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