1/*
2 * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved.
4 * Copyright (C) 2010 Google Inc. All rights reserved.
5 * Copyright (C) 2013 ChangSeok Oh <shivamidow@gmail.com>
6 * Copyright (C) 2013 Adobe Systems Inc. All right reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25#pragma once
26
27#include "RenderLayer.h"
28
29namespace WebCore {
30
31enum WhitespacePosition { LeadingWhitespace, TrailingWhitespace };
32
33inline bool hasInlineDirectionBordersPaddingOrMargin(const RenderInline& flow)
34{
35 // Where an empty inline is split across anonymous blocks we should only give lineboxes to the 'sides' of the
36 // inline that have borders, padding or margin.
37 bool shouldApplyStartBorderPaddingOrMargin = !flow.parent()->isAnonymousBlock() || !flow.isContinuation();
38 if (shouldApplyStartBorderPaddingOrMargin && (flow.borderStart() || flow.marginStart() || flow.paddingStart()))
39 return true;
40
41 bool shouldApplyEndBorderPaddingOrMargin = !flow.parent()->isAnonymousBlock() || flow.isContinuation() || !flow.inlineContinuation();
42 return shouldApplyEndBorderPaddingOrMargin && (flow.borderEnd() || flow.marginEnd() || flow.paddingEnd());
43}
44
45inline const RenderStyle& lineStyle(const RenderObject& renderer, const LineInfo& lineInfo)
46{
47 return lineInfo.isFirstLine() ? renderer.firstLineStyle() : renderer.style();
48}
49
50inline bool requiresLineBoxForContent(const RenderInline& flow, const LineInfo& lineInfo)
51{
52 RenderElement* parent = flow.parent();
53 if (flow.document().inNoQuirksMode()) {
54 const RenderStyle& flowStyle = lineStyle(flow, lineInfo);
55 const RenderStyle& parentStyle = lineStyle(*parent, lineInfo);
56 if (flowStyle.lineHeight() != parentStyle.lineHeight()
57 || flowStyle.verticalAlign() != parentStyle.verticalAlign()
58 || !parentStyle.fontCascade().fontMetrics().hasIdenticalAscentDescentAndLineGap(flowStyle.fontCascade().fontMetrics()))
59 return true;
60 }
61 return false;
62}
63
64inline bool shouldCollapseWhiteSpace(const RenderStyle* style, const LineInfo& lineInfo, WhitespacePosition whitespacePosition)
65{
66 // CSS2 16.6.1
67 // If a space (U+0020) at the beginning of a line has 'white-space' set to 'normal', 'nowrap', or 'pre-line', it is removed.
68 // If a space (U+0020) at the end of a line has 'white-space' set to 'normal', 'nowrap', or 'pre-line', it is also removed.
69 // If spaces (U+0020) or tabs (U+0009) at the end of a line have 'white-space' set to 'pre-wrap', UAs may visually collapse them.
70 return style->collapseWhiteSpace()
71 || (whitespacePosition == TrailingWhitespace && style->whiteSpace() == WhiteSpace::PreWrap && (!lineInfo.isEmpty() || !lineInfo.previousLineBrokeCleanly()));
72}
73
74inline bool skipNonBreakingSpace(const InlineIterator& it, const LineInfo& lineInfo)
75{
76 if (it.renderer()->style().nbspMode() != NBSPMode::Space || it.current() != noBreakSpace)
77 return false;
78
79 // FIXME: This is bad. It makes nbsp inconsistent with space and won't work correctly
80 // with m_minWidth/m_maxWidth.
81 // Do not skip a non-breaking space if it is the first character
82 // on a line after a clean line break (or on the first line, since previousLineBrokeCleanly starts off
83 // |true|).
84 if (lineInfo.isEmpty() && lineInfo.previousLineBrokeCleanly())
85 return false;
86
87 return true;
88}
89
90inline bool alwaysRequiresLineBox(const RenderInline& flow)
91{
92 // FIXME: Right now, we only allow line boxes for inlines that are truly empty.
93 // We need to fix this, though, because at the very least, inlines containing only
94 // ignorable whitespace should should also have line boxes.
95 return isEmptyInline(flow) && hasInlineDirectionBordersPaddingOrMargin(flow);
96}
97
98inline bool requiresLineBox(const InlineIterator& it, const LineInfo& lineInfo = LineInfo(), WhitespacePosition whitespacePosition = LeadingWhitespace)
99{
100 if (it.renderer()->isFloatingOrOutOfFlowPositioned())
101 return false;
102
103 if (it.renderer()->isBR())
104 return true;
105
106 bool rendererIsEmptyInline = false;
107 if (is<RenderInline>(*it.renderer())) {
108 const auto& inlineRenderer = downcast<RenderInline>(*it.renderer());
109 if (!alwaysRequiresLineBox(inlineRenderer) && !requiresLineBoxForContent(inlineRenderer, lineInfo))
110 return false;
111 rendererIsEmptyInline = isEmptyInline(inlineRenderer);
112 }
113
114 if (!shouldCollapseWhiteSpace(&it.renderer()->style(), lineInfo, whitespacePosition))
115 return true;
116
117 UChar current = it.current();
118 bool notJustWhitespace = current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || it.renderer()->preservesNewline()) && !skipNonBreakingSpace(it, lineInfo);
119 return notJustWhitespace || rendererIsEmptyInline;
120}
121
122inline void setStaticPositions(RenderBlockFlow& block, RenderBox& child, IndentTextOrNot shouldIndentText)
123{
124 // FIXME: The math here is actually not really right. It's a best-guess approximation that
125 // will work for the common cases
126 RenderElement* containerBlock = child.container();
127 LayoutUnit blockHeight = block.logicalHeight();
128 if (is<RenderInline>(*containerBlock)) {
129 // A relative positioned inline encloses us. In this case, we also have to determine our
130 // position as though we were an inline. Set |staticInlinePosition| and |staticBlockPosition| on the relative positioned
131 // inline so that we can obtain the value later.
132 downcast<RenderInline>(*containerBlock).layer()->setStaticInlinePosition(block.startAlignedOffsetForLine(blockHeight, DoNotIndentText));
133 downcast<RenderInline>(*containerBlock).layer()->setStaticBlockPosition(blockHeight);
134 }
135 block.updateStaticInlinePositionForChild(child, blockHeight, shouldIndentText);
136 child.layer()->setStaticBlockPosition(blockHeight);
137}
138
139} // namespace WebCore
140