| /* |
| * Copyright (C) 2018 Apple 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: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" |
| #include "InlineLineBreaker.h" |
| |
| #if ENABLE(LAYOUT_FORMATTING_CONTEXT) |
| |
| #include "Hyphenation.h" |
| #include "InlineItem.h" |
| #include "InlineTextItem.h" |
| |
| namespace WebCore { |
| namespace Layout { |
| |
| LineBreaker::BreakingContext LineBreaker::breakingContext(const InlineItem& inlineItem, LayoutUnit logicalWidth, const LineContext& lineContext) |
| { |
| // First content always stays on line. |
| if (lineContext.isEmpty || logicalWidth <= lineContext.availableWidth) |
| return { BreakingBehavior::Keep, isAtBreakingOpportunity(inlineItem) }; |
| |
| if (inlineItem.isHardLineBreak()) |
| return { BreakingBehavior::Keep, isAtBreakingOpportunity(inlineItem) }; |
| |
| if (is<InlineTextItem>(inlineItem)) |
| return { wordBreakingBehavior(downcast<InlineTextItem>(inlineItem), lineContext.isEmpty), isAtBreakingOpportunity(inlineItem) }; |
| |
| // Wrap non-text boxes to the next line unless we can trim trailing whitespace. |
| auto availableWidth = lineContext.availableWidth + lineContext.trimmableWidth; |
| if (logicalWidth <= availableWidth) |
| return { BreakingBehavior::Keep, isAtBreakingOpportunity(inlineItem) }; |
| return { BreakingBehavior::Wrap, isAtBreakingOpportunity(inlineItem) }; |
| } |
| |
| LineBreaker::BreakingBehavior LineBreaker::wordBreakingBehavior(const InlineTextItem& inlineItem, bool lineIsEmpty) const |
| { |
| // Word breaking behaviour: |
| // 1. Whitesapce collapse on -> push whitespace to next line. |
| // 2. Whitespace collapse off -> whitespace is split where possible. |
| // 3. Non-whitespace -> first run on the line -> either split or kept on the line. (depends on overflow-wrap) |
| // 4. Non-whitespace -> already content on the line -> either gets split (word-break: break-all) or gets pushed to the next line. |
| // (Hyphenate when possible) |
| // 5. Non-text type -> next line |
| auto& style = inlineItem.style(); |
| |
| if (inlineItem.isWhitespace()) |
| return style.collapseWhiteSpace() ? BreakingBehavior::Wrap : BreakingBehavior::Split; |
| |
| auto shouldHypenate = !m_hyphenationIsDisabled && style.hyphens() == Hyphens::Auto && canHyphenate(style.locale()); |
| if (shouldHypenate) |
| return BreakingBehavior::Split; |
| |
| if (style.autoWrap()) { |
| // Break any word |
| if (style.wordBreak() == WordBreak::BreakAll) |
| return BreakingBehavior::Split; |
| |
| // Break first run on line. |
| if (lineIsEmpty && style.breakWords() && style.preserveNewline()) |
| return BreakingBehavior::Split; |
| } |
| |
| // Non-breakable non-whitespace run. |
| return lineIsEmpty ? BreakingBehavior::Keep : BreakingBehavior::Wrap; |
| } |
| |
| bool LineBreaker::isAtBreakingOpportunity(const InlineItem& inlineItem) |
| { |
| if (inlineItem.isHardLineBreak()) |
| return true; |
| |
| if (is<InlineTextItem>(inlineItem)) |
| return downcast<InlineTextItem>(inlineItem).isWhitespace(); |
| return !inlineItem.isFloat() && !inlineItem.isContainerStart() && !inlineItem.isContainerEnd(); |
| } |
| |
| } |
| } |
| #endif |