1 // Copyright 2017 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LINE_BREAKER_H_ 6 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LINE_BREAKER_H_ 7 8 #include "base/optional.h" 9 #include "third_party/blink/renderer/core/core_export.h" 10 #include "third_party/blink/renderer/core/layout/ng/exclusions/ng_line_layout_opportunity.h" 11 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h" 12 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" 13 #include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h" 14 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h" 15 #include "third_party/blink/renderer/platform/text/text_break_iterator.h" 16 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" 17 18 namespace blink { 19 20 class Hyphenation; 21 class NGInlineBreakToken; 22 class NGInlineItem; 23 24 // The line breaker needs to know which mode its in to properly handle floats. 25 enum class NGLineBreakerMode { kContent, kMinContent, kMaxContent }; 26 27 // Represents a line breaker. 28 // 29 // This class measures each NGInlineItem and determines items to form a line, 30 // so that NGInlineLayoutAlgorithm can build a line box from the output. 31 class CORE_EXPORT NGLineBreaker { 32 STACK_ALLOCATED(); 33 34 public: 35 NGLineBreaker(NGInlineNode, 36 NGLineBreakerMode, 37 const NGConstraintSpace&, 38 const NGLineLayoutOpportunity&, 39 const NGPositionedFloatVector& leading_floats, 40 unsigned handled_leading_floats_index, 41 const NGInlineBreakToken*, 42 NGExclusionSpace*); 43 ~NGLineBreaker(); 44 ItemsData()45 const NGInlineItemsData& ItemsData() const { return items_data_; } 46 47 // Compute the next line break point and produces NGInlineItemResults for 48 // the line. NextLine(NGLineInfo * line_info)49 inline void NextLine(NGLineInfo* line_info) { 50 NextLine(kIndefiniteSize, line_info); 51 } 52 53 // During the min/max size calculation we need a special percentage 54 // resolution block-size to pass to children/pass to children. 55 // TODO(layout-dev): Split into two methods (NextLine/NextLineForMinMax) or, 56 // better yet, subclass or templetize the line-breaker for Min/Max computation 57 // if we can do that without incurring a performance penalty 58 void NextLine(LayoutUnit percentage_resolution_block_size_for_min_max, 59 NGLineInfo*); 60 IsFinished()61 bool IsFinished() const { return item_index_ >= Items().size(); } 62 63 // Create an NGInlineBreakToken for the last line returned by NextLine(). 64 scoped_refptr<NGInlineBreakToken> CreateBreakToken(const NGLineInfo&) const; 65 66 // Computing |NGLineBreakerMode::kMinContent| with |MaxSizeCache| caches 67 // information that can help computing |kMaxContent|. It is recommended to set 68 // this when computing both |kMinContent| and |kMaxContent|. 69 using MaxSizeCache = Vector<LayoutUnit, 64>; 70 void SetMaxSizeCache(MaxSizeCache* max_size_cache); 71 72 // Compute NGInlineItemResult for an open tag item. 73 // Returns true if this item has edge and may have non-zero inline size. 74 static bool ComputeOpenTagResult(const NGInlineItem&, 75 const NGConstraintSpace&, 76 NGInlineItemResult*); 77 78 // This enum is private, except for |WhitespaceStateForTesting()|. See 79 // |whitespace_| member. 80 enum class WhitespaceState { 81 kLeading, 82 kNone, 83 kUnknown, 84 kCollapsible, 85 kCollapsed, 86 kPreserved, 87 }; TrailingWhitespaceForTesting()88 WhitespaceState TrailingWhitespaceForTesting() const { 89 return trailing_whitespace_; 90 } 91 92 private: Text()93 const String& Text() const { return text_content_; } Items()94 const Vector<NGInlineItem>& Items() const { return items_data_.items; } 95 96 String TextContentForLineBreak() const; 97 98 NGInlineItemResult* AddItem(const NGInlineItem&, 99 unsigned end_offset, 100 NGLineInfo*); 101 NGInlineItemResult* AddItem(const NGInlineItem&, NGLineInfo*); 102 103 void BreakLine(LayoutUnit percentage_resolution_block_size_for_min_max, 104 NGLineInfo*); 105 void PrepareNextLine(NGLineInfo*); 106 107 void ComputeLineLocation(NGLineInfo*) const; 108 109 enum class LineBreakState { 110 // The line breaking is complete. 111 kDone, 112 113 // Overflow is detected without any earlier break opportunities. This line 114 // should break at the earliest break opportunity. 115 kOverflow, 116 117 // Should complete the line at the earliest possible point. 118 // Trailing spaces, <br>, or close tags should be included to the line even 119 // when it is overflowing. 120 kTrailing, 121 122 // Looking for more items to fit into the current line. 123 kContinue, 124 }; 125 HandleText(const NGInlineItem & item,NGLineInfo * line_info)126 inline void HandleText(const NGInlineItem& item, NGLineInfo* line_info) { 127 DCHECK(item.TextShapeResult()); 128 HandleText(item, *item.TextShapeResult(), line_info); 129 } 130 void HandleText(const NGInlineItem& item, const ShapeResult&, NGLineInfo*); 131 enum BreakResult { kSuccess, kOverflow }; 132 BreakResult BreakText(NGInlineItemResult*, 133 const NGInlineItem&, 134 const ShapeResult&, 135 LayoutUnit available_width, 136 LayoutUnit available_width_with_hyphens, 137 NGLineInfo*); 138 bool BreakTextAtPreviousBreakOpportunity(NGInlineItemResult* item_result); 139 bool HandleTextForFastMinContent(NGInlineItemResult*, 140 const NGInlineItem&, 141 const ShapeResult&, 142 NGLineInfo*); 143 void HandleEmptyText(const NGInlineItem& item, NGLineInfo*); 144 145 scoped_refptr<ShapeResultView> TruncateLineEndResult( 146 const NGLineInfo&, 147 const NGInlineItemResult&, 148 unsigned end_offset); 149 void UpdateShapeResult(const NGLineInfo&, NGInlineItemResult*); 150 scoped_refptr<ShapeResult> ShapeText(const NGInlineItem&, 151 unsigned start, 152 unsigned end); 153 154 void HandleTrailingSpaces(const NGInlineItem&, NGLineInfo*); 155 void HandleTrailingSpaces(const NGInlineItem&, 156 const ShapeResult&, 157 NGLineInfo*); 158 void RemoveTrailingCollapsibleSpace(NGLineInfo*); 159 LayoutUnit TrailingCollapsibleSpaceWidth(NGLineInfo*); 160 void ComputeTrailingCollapsibleSpace(NGLineInfo*); 161 162 void HandleControlItem(const NGInlineItem&, NGLineInfo*); 163 void HandleBidiControlItem(const NGInlineItem&, NGLineInfo*); 164 void HandleAtomicInline( 165 const NGInlineItem&, 166 LayoutUnit percentage_resolution_block_size_for_min_max, 167 NGLineInfo*); 168 bool ShouldForceCanBreakAfter(const NGInlineItemResult& item_result) const; 169 void HandleFloat(const NGInlineItem&, 170 NGLineInfo*); 171 void HandleOutOfFlowPositioned(const NGInlineItem&, NGLineInfo*); 172 173 void HandleOpenTag(const NGInlineItem&, NGLineInfo*); 174 void HandleCloseTag(const NGInlineItem&, NGLineInfo*); 175 176 bool HandleOverflowIfNeeded(NGLineInfo*); 177 void HandleOverflow(NGLineInfo*); 178 void RewindOverflow(unsigned new_end, NGLineInfo*); 179 void Rewind(unsigned new_end, NGLineInfo*); ResetRewindLoopDetector()180 void ResetRewindLoopDetector() { 181 #if DCHECK_IS_ON() 182 last_rewind_from_item_index_ = last_rewind_to_item_index_ = 0; 183 #endif 184 } 185 186 const ComputedStyle& ComputeCurrentStyle(unsigned item_result_index, 187 NGLineInfo*) const; 188 void SetCurrentStyle(const ComputedStyle&); 189 190 void MoveToNextOf(const NGInlineItem&); 191 void MoveToNextOf(const NGInlineItemResult&); 192 193 void ComputeBaseDirection(); 194 AvailableWidth()195 LayoutUnit AvailableWidth() const { 196 DCHECK_EQ(available_width_, ComputeAvailableWidth()); 197 return available_width_; 198 } AvailableWidthToFit()199 LayoutUnit AvailableWidthToFit() const { 200 return AvailableWidth().AddEpsilon(); 201 } RemainingAvailableWidth()202 LayoutUnit RemainingAvailableWidth() const { 203 return AvailableWidthToFit() - position_; 204 } CanFitOnLine()205 bool CanFitOnLine() const { return position_ <= AvailableWidthToFit(); } 206 LayoutUnit ComputeAvailableWidth() const; 207 208 // Represents the current offset of the input. 209 LineBreakState state_; 210 unsigned item_index_ = 0; 211 unsigned offset_ = 0; 212 213 // |WhitespaceState| of the current end. When a line is broken, this indicates 214 // the state of trailing whitespaces. 215 WhitespaceState trailing_whitespace_; 216 217 // The current position from inline_start. Unlike NGInlineLayoutAlgorithm 218 // that computes position in visual order, this position in logical order. 219 LayoutUnit position_; 220 LayoutUnit available_width_; 221 NGLineLayoutOpportunity line_opportunity_; 222 223 NGInlineNode node_; 224 225 NGLineBreakerMode mode_; 226 227 // True if this line is the "first formatted line". 228 // https://www.w3.org/TR/CSS22/selector.html#first-formatted-line 229 bool is_first_formatted_line_ = false; 230 231 bool use_first_line_style_ = false; 232 233 // True when current box allows line wrapping. 234 bool auto_wrap_ = false; 235 236 // True when current box has 'word-break/word-wrap: break-word'. 237 bool break_anywhere_if_overflow_ = false; 238 239 // Force LineBreakType::kBreakCharacter by ignoring the current style if 240 // |break_anywhere_if_overflow_| is set. Set to find grapheme cluster 241 // boundaries for 'break-word' after overflow. 242 bool override_break_anywhere_ = false; 243 244 // True when breaking at soft hyphens (U+00AD) is allowed. 245 bool enable_soft_hyphen_ = true; 246 247 // True in quirks mode or limited-quirks mode, which require line-height 248 // quirks. 249 // https://quirks.spec.whatwg.org/#the-line-height-calculation-quirk 250 bool in_line_height_quirks_mode_ = false; 251 252 // True when the line we are breaking has a list marker. 253 bool has_list_marker_ = false; 254 255 // Set when the line ended with a forced break. Used to setup the states for 256 // the next line. 257 bool is_after_forced_break_ = false; 258 259 // Set in quirks mode when we're not supposed to break inside table cells 260 // between images, and between text and images. 261 bool sticky_images_quirk_ = false; 262 263 const NGInlineItemsData& items_data_; 264 265 // The text content of this node. This is same as |items_data_.text_content| 266 // except when sticky images quirk is needed. See 267 // |NGInlineNode::TextContentForContentSize|. 268 String text_content_; 269 270 const NGConstraintSpace& constraint_space_; 271 NGExclusionSpace* exclusion_space_; 272 scoped_refptr<const NGInlineBreakToken> break_token_; 273 scoped_refptr<const ComputedStyle> current_style_; 274 275 LazyLineBreakIterator break_iterator_; 276 HarfBuzzShaper shaper_; 277 ShapeResultSpacing<String> spacing_; 278 bool previous_line_had_forced_break_ = false; 279 const Hyphenation* hyphenation_ = nullptr; 280 281 // Cache the result of |ComputeTrailingCollapsibleSpace| to avoid shaping 282 // multiple times. 283 struct TrailingCollapsibleSpace { 284 NGInlineItemResult* item_result; 285 scoped_refptr<const ShapeResultView> collapsed_shape_result; 286 }; 287 base::Optional<TrailingCollapsibleSpace> trailing_collapsible_space_; 288 289 // Keep track of handled float items. See HandleFloat(). 290 const NGPositionedFloatVector& leading_floats_; 291 unsigned leading_floats_index_ = 0u; 292 unsigned handled_leading_floats_index_; 293 294 // Cache for computing |MinMaxSize|. See |MaxSizeCache|. 295 MaxSizeCache* max_size_cache_ = nullptr; 296 297 // Keep the last item |HandleTextForFastMinContent()| has handled. This is 298 // used to fallback the last word to |HandleText()|. 299 const NGInlineItem* fast_min_content_item_ = nullptr; 300 301 // The current base direction for the bidi algorithm. 302 // This is copied from NGInlineNode, then updated after each forced line break 303 // if 'unicode-bidi: plaintext'. 304 TextDirection base_direction_; 305 306 #if DCHECK_IS_ON() 307 // These fields are to detect rewind-loop. 308 unsigned last_rewind_from_item_index_ = 0; 309 unsigned last_rewind_to_item_index_ = 0; 310 #endif 311 }; 312 313 } // namespace blink 314 315 #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LINE_BREAKER_H_ 316