blob: bd4929bb198ed8cbd84ff8893ee9eec86d2737f2 [file] [log] [blame]
/*
* Copyright (C) 2009 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_ruby_base.h"
#include "third_party/blink/renderer/core/layout/layout_ruby_run.h"
#include "third_party/blink/renderer/core/layout/ng/layout_ng_ruby_base.h"
namespace blink {
LayoutRubyBase::LayoutRubyBase(Element* element) : LayoutBlockFlow(nullptr) {
DCHECK(!element);
SetInline(false);
}
LayoutRubyBase::~LayoutRubyBase() = default;
LayoutRubyBase* LayoutRubyBase::CreateAnonymous(Document* document,
const LayoutRubyRun& ruby_run) {
LayoutRubyBase* layout_object;
if (ruby_run.IsLayoutNGObject()) {
layout_object = new LayoutNGRubyBase();
} else {
layout_object = new LayoutRubyBase(nullptr);
}
layout_object->SetDocumentForAnonymous(document);
return layout_object;
}
bool LayoutRubyBase::IsChildAllowed(LayoutObject* child,
const ComputedStyle&) const {
NOT_DESTROYED();
return child->IsInline();
}
void LayoutRubyBase::MoveChildren(LayoutRubyBase* to_base,
LayoutObject* before_child) {
NOT_DESTROYED();
// This function removes all children that are before (!) beforeChild
// and appends them to toBase.
DCHECK(to_base);
// Callers should have handled the percent height descendant map.
DCHECK(!HasPercentHeightDescendants());
if (before_child && before_child->Parent() != this)
before_child = SplitAnonymousBoxesAroundChild(before_child);
if (ChildrenInline())
MoveInlineChildren(to_base, before_child);
else
MoveBlockChildren(to_base, before_child);
SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
layout_invalidation_reason::kUnknown);
to_base->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
layout_invalidation_reason::kUnknown);
}
void LayoutRubyBase::MoveInlineChildren(LayoutRubyBase* to_base,
LayoutObject* before_child) {
NOT_DESTROYED();
DCHECK(ChildrenInline());
DCHECK(to_base);
if (!FirstChild())
return;
LayoutBlock* to_block;
if (to_base->ChildrenInline()) {
// The standard and easy case: move the children into the target base
to_block = to_base;
} else {
// We need to wrap the inline objects into an anonymous block.
// If toBase has a suitable block, we re-use it, otherwise create a new one.
LayoutObject* last_child = to_base->LastChild();
if (last_child && last_child->IsAnonymousBlock() &&
last_child->ChildrenInline()) {
to_block = To<LayoutBlock>(last_child);
} else {
to_block = to_base->CreateAnonymousBlock();
to_base->Children()->AppendChildNode(to_base, to_block);
}
}
// Move our inline children into the target block we determined above.
MoveChildrenTo(to_block, FirstChild(), before_child);
}
void LayoutRubyBase::MoveBlockChildren(LayoutRubyBase* to_base,
LayoutObject* before_child) {
NOT_DESTROYED();
DCHECK(!ChildrenInline());
DCHECK(to_base);
if (!FirstChild())
return;
if (to_base->ChildrenInline())
to_base->MakeChildrenNonInline();
// If an anonymous block would be put next to another such block, then merge
// those.
LayoutObject* first_child_here = FirstChild();
LayoutObject* last_child_there = to_base->LastChild();
if (first_child_here->IsAnonymousBlock() &&
first_child_here->ChildrenInline() && last_child_there &&
last_child_there->IsAnonymousBlock() &&
last_child_there->ChildrenInline()) {
auto* anon_block_here = To<LayoutBlockFlow>(first_child_here);
auto* anon_block_there = To<LayoutBlockFlow>(last_child_there);
anon_block_here->MoveAllChildrenTo(anon_block_there,
anon_block_there->Children());
anon_block_here->DeleteLineBoxTree();
anon_block_here->Destroy();
}
// Move all remaining children normally. If moving all children, include our
// float list.
if (!before_child) {
bool full_remove_insert = to_base->HasLayer() || HasLayer();
// TODO(kojii): |this| is |!ChildrenInline()| when we enter this function,
// but it may turn to |ChildrenInline()| when |anon_block_here| is destroyed
// above. Probably the correct fix is to do it earlier and switch to
// |MoveInlineChildren()| if this happens. For the short term safe fix,
// using |full_remove_insert| can prevent inconsistent LayoutObject tree
// that leads to CHECK failures.
full_remove_insert |= ChildrenInline();
MoveAllChildrenIncludingFloatsTo(to_base, full_remove_insert);
} else {
MoveChildrenTo(to_base, FirstChild(), before_child);
RemoveFloatingObjectsFromDescendants();
}
}
ETextAlign LayoutRubyBase::TextAlignmentForLine(
bool /* endsWithSoftBreak */) const {
return ETextAlign::kJustify;
}
void LayoutRubyBase::AdjustInlineDirectionLineBounds(
unsigned expansion_opportunity_count,
LayoutUnit& logical_left,
LayoutUnit& logical_width) const {
NOT_DESTROYED();
int max_preferred_logical_width = PreferredLogicalWidths().max_size.ToInt();
if (max_preferred_logical_width >= logical_width)
return;
unsigned max_count = static_cast<unsigned>(LayoutUnit::Max().Floor());
if (expansion_opportunity_count > max_count)
expansion_opportunity_count = max_count;
// Inset the ruby base by half the inter-ideograph expansion amount.
LayoutUnit inset = (logical_width - max_preferred_logical_width) /
(expansion_opportunity_count + 1);
logical_left += inset / 2;
logical_width -= inset;
}
} // namespace blink