OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2013 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 "sky/engine/config.h" | |
32 #include "sky/engine/core/rendering/RenderBlockFlow.h" | |
33 | |
34 #include "sky/engine/core/frame/FrameView.h" | |
35 #include "sky/engine/core/frame/LocalFrame.h" | |
36 #include "sky/engine/core/frame/Settings.h" | |
37 #include "sky/engine/core/rendering/BidiRun.h" | |
38 #include "sky/engine/core/rendering/HitTestLocation.h" | |
39 #include "sky/engine/core/rendering/RenderLayer.h" | |
40 #include "sky/engine/core/rendering/RenderText.h" | |
41 #include "sky/engine/core/rendering/RenderView.h" | |
42 #include "sky/engine/core/rendering/line/LineWidth.h" | |
43 #include "sky/engine/platform/text/BidiTextRun.h" | |
44 | |
45 namespace blink { | |
46 | |
47 RenderBlockFlow::RenderBlockFlow(ContainerNode* node) | |
48 : RenderBlock(node) | |
49 { | |
50 } | |
51 | |
52 RenderBlockFlow::~RenderBlockFlow() | |
53 { | |
54 } | |
55 | |
56 RenderBlockFlow* RenderBlockFlow::createAnonymous(Document* document) | |
57 { | |
58 RenderBlockFlow* renderer = new RenderBlockFlow(0); | |
59 renderer->setDocumentForAnonymous(document); | |
60 return renderer; | |
61 } | |
62 | |
63 void RenderBlockFlow::layout() | |
64 { | |
65 ASSERT(needsLayout()); | |
66 ASSERT(isInlineBlock() || !isInline()); | |
67 | |
68 if (simplifiedLayout()) | |
69 return; | |
70 | |
71 SubtreeLayoutScope layoutScope(*this); | |
72 | |
73 layoutBlockFlow(layoutScope); | |
74 | |
75 updateLayerTransformAfterLayout(); | |
76 | |
77 clearNeedsLayout(); | |
78 } | |
79 | |
80 inline void RenderBlockFlow::layoutBlockFlow(SubtreeLayoutScope& layoutScope) | |
81 { | |
82 LayoutUnit oldLeft = logicalLeft(); | |
83 bool logicalWidthChanged = updateLogicalWidthAndColumnWidth(); | |
84 bool relayoutChildren = logicalWidthChanged; | |
85 | |
86 LayoutUnit beforeEdge = borderBefore() + paddingBefore(); | |
87 LayoutUnit afterEdge = borderAfter() + paddingAfter(); | |
88 LayoutUnit previousHeight = logicalHeight(); | |
89 setLogicalHeight(beforeEdge); | |
90 | |
91 layoutChildren(relayoutChildren, layoutScope, beforeEdge, afterEdge); | |
92 | |
93 LayoutUnit oldClientAfterEdge = clientLogicalBottom(); | |
94 | |
95 updateLogicalHeight(); | |
96 | |
97 if (previousHeight != logicalHeight()) | |
98 relayoutChildren = true; | |
99 | |
100 layoutPositionedObjects(relayoutChildren, oldLeft != logicalLeft() ? ForcedL
ayoutAfterContainingBlockMoved : DefaultLayout); | |
101 | |
102 // Add overflow from children (unless we're multi-column, since in that case
all our child overflow is clipped anyway). | |
103 computeOverflow(oldClientAfterEdge); | |
104 } | |
105 | |
106 void RenderBlockFlow::determineLogicalLeftPositionForChild(RenderBox* child) | |
107 { | |
108 LayoutUnit startPosition = borderStart() + paddingStart(); | |
109 LayoutUnit totalAvailableLogicalWidth = borderAndPaddingLogicalWidth() + ava
ilableLogicalWidth(); | |
110 | |
111 LayoutUnit childMarginStart = marginStartForChild(child); | |
112 LayoutUnit newPosition = startPosition + childMarginStart; | |
113 | |
114 // If the child has an offset from the content edge to avoid floats then use
that, otherwise let any negative | |
115 // margin pull it back over the content edge or any positive margin push it
out. | |
116 if (child->style()->marginStartUsing(style()).isAuto()) | |
117 newPosition = std::max(newPosition, childMarginStart); | |
118 | |
119 child->setX(style()->isLeftToRightDirection() ? newPosition : totalAvailable
LogicalWidth - newPosition - logicalWidthForChild(child)); | |
120 } | |
121 | |
122 void RenderBlockFlow::layoutBlockChild(RenderBox* child) | |
123 { | |
124 child->computeAndSetBlockDirectionMargins(this); | |
125 LayoutUnit marginBefore = marginBeforeForChild(child); | |
126 child->setY(logicalHeight() + marginBefore); | |
127 child->layoutIfNeeded(); | |
128 determineLogicalLeftPositionForChild(child); | |
129 setLogicalHeight(logicalHeight() + marginBefore + logicalHeightForChild(chil
d) + marginAfterForChild(child)); | |
130 } | |
131 | |
132 void RenderBlockFlow::layoutChildren(bool relayoutChildren, SubtreeLayoutScope&
layoutScope, LayoutUnit beforeEdge, LayoutUnit afterEdge) | |
133 { | |
134 dirtyForLayoutFromPercentageHeightDescendants(layoutScope); | |
135 | |
136 RenderBox* next = firstChildBox(); | |
137 RenderBox* lastNormalFlowChild = 0; | |
138 | |
139 while (next) { | |
140 RenderBox* child = next; | |
141 next = child->nextSiblingBox(); | |
142 | |
143 updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, child); | |
144 | |
145 if (child->isOutOfFlowPositioned()) { | |
146 child->containingBlock()->insertPositionedObject(child); | |
147 continue; | |
148 } | |
149 | |
150 // Lay out the child. | |
151 layoutBlockChild(child); | |
152 lastNormalFlowChild = child; | |
153 } | |
154 | |
155 // Negative margins can cause our height to shrink below our minimal height
(border/padding). | |
156 // If this happens, ensure that the computed height is increased to the mini
mal height. | |
157 setLogicalHeight(std::max(logicalHeight() + afterEdge, beforeEdge + afterEdg
e)); | |
158 } | |
159 | |
160 RootInlineBox* RenderBlockFlow::createAndAppendRootInlineBox() | |
161 { | |
162 RootInlineBox* rootBox = createRootInlineBox(); | |
163 m_lineBoxes.appendLineBox(rootBox); | |
164 | |
165 return rootBox; | |
166 } | |
167 | |
168 void RenderBlockFlow::deleteLineBoxTree() | |
169 { | |
170 m_lineBoxes.deleteLineBoxTree(); | |
171 } | |
172 | |
173 void RenderBlockFlow::addChild(RenderObject* newChild, RenderObject* beforeChild
) | |
174 { | |
175 RenderBlock::addChild(newChild, beforeChild); | |
176 } | |
177 | |
178 LayoutUnit RenderBlockFlow::logicalLeftSelectionOffset(RenderBlock* rootBlock, L
ayoutUnit position) | |
179 { | |
180 LayoutUnit logicalLeft = logicalLeftOffsetForLine(false); | |
181 if (logicalLeft == logicalLeftOffsetForContent()) | |
182 return RenderBlock::logicalLeftSelectionOffset(rootBlock, position); | |
183 | |
184 RenderBlock* cb = this; | |
185 while (cb != rootBlock) { | |
186 logicalLeft += cb->logicalLeft(); | |
187 cb = cb->containingBlock(); | |
188 } | |
189 return logicalLeft; | |
190 } | |
191 | |
192 LayoutUnit RenderBlockFlow::logicalRightSelectionOffset(RenderBlock* rootBlock,
LayoutUnit position) | |
193 { | |
194 LayoutUnit logicalRight = logicalRightOffsetForLine(false); | |
195 if (logicalRight == logicalRightOffsetForContent()) | |
196 return RenderBlock::logicalRightSelectionOffset(rootBlock, position); | |
197 | |
198 RenderBlock* cb = this; | |
199 while (cb != rootBlock) { | |
200 logicalRight += cb->logicalLeft(); | |
201 cb = cb->containingBlock(); | |
202 } | |
203 return logicalRight; | |
204 } | |
205 | |
206 RootInlineBox* RenderBlockFlow::createRootInlineBox() | |
207 { | |
208 return new RootInlineBox(*this); | |
209 } | |
210 | |
211 static void updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection, B
idiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float av
ailableLogicalWidth) | |
212 { | |
213 // The direction of the block should determine what happens with wide lines. | |
214 // In particular with RTL blocks, wide lines should still spill out to the l
eft. | |
215 if (isLeftToRightDirection) { | |
216 if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) | |
217 trailingSpaceRun->m_box->setLogicalWidth(std::max<float>(0, trailing
SpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); | |
218 return; | |
219 } | |
220 | |
221 if (trailingSpaceRun) | |
222 trailingSpaceRun->m_box->setLogicalWidth(0); | |
223 else if (totalLogicalWidth > availableLogicalWidth) | |
224 logicalLeft -= (totalLogicalWidth - availableLogicalWidth); | |
225 } | |
226 | |
227 static void updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection,
BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float a
vailableLogicalWidth) | |
228 { | |
229 // Wide lines spill out of the block based off direction. | |
230 // So even if text-align is right, if direction is LTR, wide lines should ov
erflow out of the right | |
231 // side of the block. | |
232 if (isLeftToRightDirection) { | |
233 if (trailingSpaceRun) { | |
234 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); | |
235 trailingSpaceRun->m_box->setLogicalWidth(0); | |
236 } | |
237 if (totalLogicalWidth < availableLogicalWidth) | |
238 logicalLeft += availableLogicalWidth - totalLogicalWidth; | |
239 return; | |
240 } | |
241 | |
242 if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) { | |
243 trailingSpaceRun->m_box->setLogicalWidth(std::max<float>(0, trailingSpac
eRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); | |
244 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); | |
245 } else | |
246 logicalLeft += availableLogicalWidth - totalLogicalWidth; | |
247 } | |
248 | |
249 static void updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection,
BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float
availableLogicalWidth) | |
250 { | |
251 float trailingSpaceWidth = 0; | |
252 if (trailingSpaceRun) { | |
253 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); | |
254 trailingSpaceWidth = std::min(trailingSpaceRun->m_box->logicalWidth(), (
availableLogicalWidth - totalLogicalWidth + 1) / 2); | |
255 trailingSpaceRun->m_box->setLogicalWidth(std::max<float>(0, trailingSpac
eWidth)); | |
256 } | |
257 if (isLeftToRightDirection) | |
258 logicalLeft += std::max<float>((availableLogicalWidth - totalLogicalWidt
h) / 2, 0); | |
259 else | |
260 logicalLeft += totalLogicalWidth > availableLogicalWidth ? (availableLog
icalWidth - totalLogicalWidth) : (availableLogicalWidth - totalLogicalWidth) / 2
- trailingSpaceWidth; | |
261 } | |
262 | |
263 void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign
, const RootInlineBox* rootInlineBox, BidiRun* trailingSpaceRun, float& logicalL
eft, float& totalLogicalWidth, float& availableLogicalWidth, unsigned expansionO
pportunityCount) | |
264 { | |
265 TextDirection direction; | |
266 if (rootInlineBox && rootInlineBox->renderer().style()->unicodeBidi() == Pla
intext) | |
267 direction = rootInlineBox->direction(); | |
268 else | |
269 direction = style()->direction(); | |
270 | |
271 // Armed with the total width of the line (without justification), | |
272 // we now examine our text-align property in order to determine where to pos
ition the | |
273 // objects horizontally. The total width of the line can be increased if we
end up | |
274 // justifying text. | |
275 switch (textAlign) { | |
276 case LEFT: | |
277 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(),
trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); | |
278 break; | |
279 case RIGHT: | |
280 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection()
, trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); | |
281 break; | |
282 case CENTER: | |
283 updateLogicalWidthForCenterAlignedBlock(style()->isLeftToRightDirection(
), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); | |
284 break; | |
285 case JUSTIFY: | |
286 adjustInlineDirectionLineBounds(expansionOpportunityCount, logicalLeft,
availableLogicalWidth); | |
287 if (expansionOpportunityCount) { | |
288 if (trailingSpaceRun) { | |
289 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); | |
290 trailingSpaceRun->m_box->setLogicalWidth(0); | |
291 } | |
292 break; | |
293 } | |
294 // Fall through | |
295 case TASTART: | |
296 if (direction == LTR) | |
297 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirectio
n(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); | |
298 else | |
299 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirecti
on(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); | |
300 break; | |
301 case TAEND: | |
302 if (direction == LTR) | |
303 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirecti
on(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); | |
304 else | |
305 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirectio
n(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); | |
306 break; | |
307 } | |
308 } | |
309 | |
310 LayoutUnit RenderBlockFlow::startAlignedOffsetForLine(bool firstLine) | |
311 { | |
312 ETextAlign textAlign = style()->textAlign(); | |
313 | |
314 if (textAlign == TASTART) // FIXME: Handle TAEND here | |
315 return startOffsetForLine(firstLine); | |
316 | |
317 // updateLogicalWidthForAlignment() handles the direction of the block so no
need to consider it here | |
318 float totalLogicalWidth = 0; | |
319 float logicalLeft = logicalLeftOffsetForLine(false).toFloat(); | |
320 float availableLogicalWidth = logicalRightOffsetForLine(false) - logicalLeft
; | |
321 updateLogicalWidthForAlignment(textAlign, 0, 0, logicalLeft, totalLogicalWid
th, availableLogicalWidth, 0); | |
322 | |
323 if (!style()->isLeftToRightDirection()) | |
324 return logicalWidth() - logicalLeft; | |
325 return logicalLeft; | |
326 } | |
327 | |
328 } // namespace blink | |
OLD | NEW |