1 /*
2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "third_party/blink/renderer/core/layout/layout_ruby_run.h"
32
33 #include "third_party/blink/renderer/core/layout/layout_ruby_base.h"
34 #include "third_party/blink/renderer/core/layout/layout_ruby_text.h"
35 #include "third_party/blink/renderer/core/layout/layout_text.h"
36 #include "third_party/blink/renderer/core/layout/ng/layout_ng_ruby_run.h"
37
38 namespace blink {
39
LayoutRubyRun(Element * element)40 LayoutRubyRun::LayoutRubyRun(Element* element) : LayoutBlockFlow(nullptr) {
41 DCHECK(!element);
42 SetInline(true);
43 SetIsAtomicInlineLevel(true);
44 }
45
46 LayoutRubyRun::~LayoutRubyRun() = default;
47
HasRubyText() const48 bool LayoutRubyRun::HasRubyText() const {
49 NOT_DESTROYED();
50 // The only place where a ruby text can be is in the first position
51 // Note: As anonymous blocks, ruby runs do not have ':before' or ':after'
52 // content themselves.
53 return FirstChild() && FirstChild()->IsRubyText();
54 }
55
HasRubyBase() const56 bool LayoutRubyRun::HasRubyBase() const {
57 NOT_DESTROYED();
58 // The only place where a ruby base can be is in the last position
59 // Note: As anonymous blocks, ruby runs do not have ':before' or ':after'
60 // content themselves.
61 return LastChild() && LastChild()->IsRubyBase();
62 }
63
RubyText() const64 LayoutRubyText* LayoutRubyRun::RubyText() const {
65 NOT_DESTROYED();
66 LayoutObject* child = FirstChild();
67 // If in future it becomes necessary to support floating or positioned ruby
68 // text, layout will have to be changed to handle them properly.
69 DCHECK(!child || !child->IsRubyText() ||
70 !child->IsFloatingOrOutOfFlowPositioned());
71 return child && child->IsRubyText() ? static_cast<LayoutRubyText*>(child)
72 : nullptr;
73 }
74
RubyBase() const75 LayoutRubyBase* LayoutRubyRun::RubyBase() const {
76 NOT_DESTROYED();
77 LayoutObject* child = LastChild();
78 return child && child->IsRubyBase() ? static_cast<LayoutRubyBase*>(child)
79 : nullptr;
80 }
81
RubyBaseSafe()82 LayoutRubyBase* LayoutRubyRun::RubyBaseSafe() {
83 NOT_DESTROYED();
84 LayoutRubyBase* base = RubyBase();
85 if (!base) {
86 base = CreateRubyBase();
87 LayoutBlockFlow::AddChild(base);
88 }
89 return base;
90 }
91
IsChildAllowed(LayoutObject * child,const ComputedStyle &) const92 bool LayoutRubyRun::IsChildAllowed(LayoutObject* child,
93 const ComputedStyle&) const {
94 NOT_DESTROYED();
95 return child->IsRubyText() || child->IsInline();
96 }
97
AddChild(LayoutObject * child,LayoutObject * before_child)98 void LayoutRubyRun::AddChild(LayoutObject* child, LayoutObject* before_child) {
99 NOT_DESTROYED();
100 DCHECK(child);
101
102 if (child->IsRubyText()) {
103 if (!before_child) {
104 // LayoutRuby has already ascertained that we can add the child here.
105 DCHECK(!HasRubyText());
106 // prepend ruby texts as first child
107 LayoutBlockFlow::AddChild(child, FirstChild());
108 } else if (before_child->IsRubyText()) {
109 // New text is inserted just before another.
110 // In this case the new text takes the place of the old one, and
111 // the old text goes into a new run that is inserted as next sibling.
112 DCHECK_EQ(before_child->Parent(), this);
113 LayoutObject* ruby = Parent();
114 DCHECK(ruby->IsRuby());
115 LayoutBlock* new_run = StaticCreateRubyRun(ruby, *ContainingBlock());
116 ruby->AddChild(new_run, NextSibling());
117 // Add the new ruby text and move the old one to the new run
118 // Note: Doing it in this order and not using LayoutRubyRun's methods,
119 // in order to avoid automatic removal of the ruby run in case there is no
120 // other child besides the old ruby text.
121 LayoutBlockFlow::AddChild(child, before_child);
122 LayoutBlockFlow::RemoveChild(before_child);
123 new_run->AddChild(before_child);
124 } else if (HasRubyBase()) {
125 // Insertion before a ruby base object.
126 // In this case we need insert a new run before the current one and split
127 // the base.
128 LayoutObject* ruby = Parent();
129 LayoutRubyRun* new_run = StaticCreateRubyRun(ruby, *ContainingBlock());
130 ruby->AddChild(new_run, this);
131 new_run->AddChild(child);
132
133 // Make sure we don't leave anything in the percentage descendant
134 // map before moving the children to the new base.
135 if (HasPercentHeightDescendants())
136 ClearPercentHeightDescendants();
137 RubyBaseSafe()->MoveChildren(new_run->RubyBaseSafe(), before_child);
138 }
139 } else {
140 // child is not a text -> insert it into the base
141 // (append it instead if beforeChild is the ruby text)
142 LayoutRubyBase* base = RubyBaseSafe();
143 if (before_child == base)
144 before_child = base->FirstChild();
145 if (before_child && before_child->IsRubyText())
146 before_child = nullptr;
147 DCHECK(!before_child || before_child->IsDescendantOf(base));
148 base->AddChild(child, before_child);
149 }
150 }
151
RemoveChild(LayoutObject * child)152 void LayoutRubyRun::RemoveChild(LayoutObject* child) {
153 NOT_DESTROYED();
154 // If the child is a ruby text, then merge the ruby base with the base of
155 // the right sibling run, if possible.
156 if (!BeingDestroyed() && !DocumentBeingDestroyed() && child->IsRubyText()) {
157 LayoutRubyBase* base = RubyBase();
158 LayoutObject* right_neighbour = NextSibling();
159 if (base && right_neighbour && right_neighbour->IsRubyRun()) {
160 // Ruby run without a base can happen only at the first run.
161 auto* right_run = To<LayoutRubyRun>(right_neighbour);
162 if (right_run->HasRubyBase()) {
163 LayoutRubyBase* right_base = right_run->RubyBaseSafe();
164 // Collect all children in a single base, then swap the bases.
165 right_base->MoveChildren(base);
166 MoveChildTo(right_run, base);
167 right_run->MoveChildTo(this, right_base);
168 // The now empty ruby base will be removed below.
169 DCHECK(!RubyBase()->FirstChild());
170 }
171 }
172 }
173
174 LayoutBlockFlow::RemoveChild(child);
175
176 if (!BeingDestroyed() && !DocumentBeingDestroyed()) {
177 // Check if our base (if any) is now empty. If so, destroy it.
178 LayoutBlockFlow* base = RubyBase();
179 if (base && !base->FirstChild()) {
180 LayoutBlockFlow::RemoveChild(base);
181 base->DeleteLineBoxTree();
182 base->Destroy();
183 }
184
185 // If any of the above leaves the run empty, destroy it as well.
186 if (!HasRubyText() && !HasRubyBase()) {
187 DeleteLineBoxTree();
188 Destroy();
189 }
190 }
191 }
192
CreateRubyBase() const193 LayoutRubyBase* LayoutRubyRun::CreateRubyBase() const {
194 NOT_DESTROYED();
195 LayoutRubyBase* layout_object =
196 LayoutRubyBase::CreateAnonymous(&GetDocument(), *this);
197 scoped_refptr<ComputedStyle> new_style =
198 ComputedStyle::CreateAnonymousStyleWithDisplay(StyleRef(),
199 EDisplay::kBlock);
200 new_style->SetTextAlign(ETextAlign::kCenter); // FIXME: use WEBKIT_CENTER?
201 layout_object->SetStyle(std::move(new_style));
202 return layout_object;
203 }
204
StaticCreateRubyRun(const LayoutObject * parent_ruby,const LayoutBlock & containing_block)205 LayoutRubyRun* LayoutRubyRun::StaticCreateRubyRun(
206 const LayoutObject* parent_ruby,
207 const LayoutBlock& containing_block) {
208 DCHECK(parent_ruby);
209 DCHECK(parent_ruby->IsRuby());
210 LayoutRubyRun* rr;
211 if (containing_block.IsLayoutNGObject()) {
212 rr = new LayoutNGRubyRun();
213 } else {
214 rr = new LayoutRubyRun(nullptr);
215 }
216 rr->SetDocumentForAnonymous(&parent_ruby->GetDocument());
217 scoped_refptr<ComputedStyle> new_style =
218 ComputedStyle::CreateAnonymousStyleWithDisplay(parent_ruby->StyleRef(),
219 EDisplay::kInlineBlock);
220 rr->SetStyle(std::move(new_style));
221 return rr;
222 }
223
LayoutSpecialExcludedChild(bool relayout_children,SubtreeLayoutScope & layout_scope)224 LayoutObject* LayoutRubyRun::LayoutSpecialExcludedChild(
225 bool relayout_children,
226 SubtreeLayoutScope& layout_scope) {
227 NOT_DESTROYED();
228 // Don't bother positioning the LayoutRubyRun yet.
229 LayoutRubyText* rt = RubyText();
230 if (!rt)
231 return nullptr;
232 if (relayout_children)
233 layout_scope.SetChildNeedsLayout(rt);
234 rt->LayoutIfNeeded();
235 return rt;
236 }
237
UpdateLayout()238 void LayoutRubyRun::UpdateLayout() {
239 NOT_DESTROYED();
240 LayoutBlockFlow::UpdateLayout();
241
242 LayoutRubyText* rt = RubyText();
243 if (!rt)
244 return;
245
246 rt->SetLogicalLeft(LayoutUnit());
247
248 // Place the LayoutRubyText such that its bottom is flush with the lineTop of
249 // the first line of the LayoutRubyBase.
250 LayoutUnit last_line_ruby_text_bottom = rt->LogicalHeight();
251 LayoutUnit first_line_ruby_text_top;
252 if (RootInlineBox* root_box = rt->LastRootBox()) {
253 // In order to align, we have to ignore negative leading.
254 first_line_ruby_text_top = rt->FirstRootBox()->LogicalTopLayoutOverflow();
255 last_line_ruby_text_bottom = root_box->LogicalBottomLayoutOverflow();
256 }
257
258 RubyPosition block_start_position = StyleRef().IsFlippedLinesWritingMode()
259 ? RubyPosition::kAfter
260 : RubyPosition::kBefore;
261 if (StyleRef().GetRubyPosition() == block_start_position) {
262 LayoutUnit first_line_top;
263 if (LayoutRubyBase* rb = RubyBase()) {
264 RootInlineBox* root_box = rb->FirstRootBox();
265 if (root_box)
266 first_line_top = root_box->LogicalTopLayoutOverflow();
267 first_line_top += rb->LogicalTop();
268 }
269
270 rt->SetLogicalTop(-last_line_ruby_text_bottom + first_line_top);
271 } else {
272 LayoutUnit last_line_bottom = LogicalHeight();
273 if (LayoutRubyBase* rb = RubyBase()) {
274 RootInlineBox* root_box = rb->LastRootBox();
275 if (root_box)
276 last_line_bottom = root_box->LogicalBottomLayoutOverflow();
277 last_line_bottom += rb->LogicalTop();
278 }
279
280 rt->SetLogicalTop(-first_line_ruby_text_top + last_line_bottom);
281 }
282
283 // Update our overflow to account for the new LayoutRubyText position.
284 ComputeLayoutOverflow(ClientLogicalBottom());
285 }
286
GetOverhang(bool first_line,LayoutObject * start_layout_object,LayoutObject * end_layout_object,int & start_overhang,int & end_overhang) const287 void LayoutRubyRun::GetOverhang(bool first_line,
288 LayoutObject* start_layout_object,
289 LayoutObject* end_layout_object,
290 int& start_overhang,
291 int& end_overhang) const {
292 NOT_DESTROYED();
293 DCHECK(!NeedsLayout());
294
295 start_overhang = 0;
296 end_overhang = 0;
297
298 LayoutRubyBase* ruby_base = RubyBase();
299 LayoutRubyText* ruby_text = RubyText();
300
301 if (!ruby_base || !ruby_text)
302 return;
303
304 if (!ruby_base->FirstRootBox())
305 return;
306
307 int logical_width = LogicalWidth().ToInt();
308 int logical_left_overhang = std::numeric_limits<int>::max();
309 int logical_right_overhang = std::numeric_limits<int>::max();
310 for (RootInlineBox* root_inline_box = ruby_base->FirstRootBox();
311 root_inline_box; root_inline_box = root_inline_box->NextRootBox()) {
312 logical_left_overhang = std::min<int>(
313 logical_left_overhang, root_inline_box->LogicalLeft().ToInt());
314 logical_right_overhang = std::min<int>(
315 logical_right_overhang,
316 (logical_width - root_inline_box->LogicalRight()).ToInt());
317 }
318
319 start_overhang = StyleRef().IsLeftToRightDirection() ? logical_left_overhang
320 : logical_right_overhang;
321 end_overhang = StyleRef().IsLeftToRightDirection() ? logical_right_overhang
322 : logical_left_overhang;
323
324 if (!start_layout_object || !start_layout_object->IsText() ||
325 start_layout_object->Style(first_line)->FontSize() >
326 ruby_base->Style(first_line)->FontSize())
327 start_overhang = 0;
328
329 if (!end_layout_object || !end_layout_object->IsText() ||
330 end_layout_object->Style(first_line)->FontSize() >
331 ruby_base->Style(first_line)->FontSize())
332 end_overhang = 0;
333
334 // We overhang a ruby only if the neighboring layout object is a text.
335 // We can overhang the ruby by no more than half the width of the neighboring
336 // text and no more than half the font size.
337 int half_width_of_font_size = ruby_text->Style(first_line)->FontSize() / 2;
338 if (start_overhang)
339 start_overhang = std::min<int>(
340 start_overhang,
341 std::min<int>(To<LayoutText>(start_layout_object)->MinLogicalWidth(),
342 half_width_of_font_size));
343 if (end_overhang)
344 end_overhang = std::min<int>(
345 end_overhang,
346 std::min<int>(To<LayoutText>(end_layout_object)->MinLogicalWidth(),
347 half_width_of_font_size));
348 }
349
CanBreakBefore(const LazyLineBreakIterator & iterator) const350 bool LayoutRubyRun::CanBreakBefore(
351 const LazyLineBreakIterator& iterator) const {
352 NOT_DESTROYED();
353 // TODO(kojii): It would be nice to improve this so that it isn't just
354 // hard-coded, but lookahead in this case is particularly problematic.
355 // See crbug.com/522826.
356
357 if (!iterator.PriorContextLength())
358 return true;
359 UChar ch = iterator.LastCharacter();
360 ULineBreak line_break =
361 static_cast<ULineBreak>(u_getIntPropertyValue(ch, UCHAR_LINE_BREAK));
362 // UNICODE LINE BREAKING ALGORITHM
363 // http://www.unicode.org/reports/tr14/
364 // And Requirements for Japanese Text Layout, 3.1.7 Characters Not Starting a
365 // Line
366 // http://www.w3.org/TR/2012/NOTE-jlreq-20120403/#characters_not_starting_a_line
367 switch (line_break) {
368 case U_LB_WORD_JOINER:
369 case U_LB_GLUE:
370 case U_LB_OPEN_PUNCTUATION:
371 return false;
372 default:
373 break;
374 }
375 return true;
376 }
377
378 } // namespace blink
379