|
1 /* |
|
2 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |
|
3 * |
|
4 * This library is free software; you can redistribute it and/or |
|
5 * modify it under the terms of the GNU Library General Public |
|
6 * License as published by the Free Software Foundation; either |
|
7 * version 2 of the License, or (at your option) any later version. |
|
8 * |
|
9 * This library is distributed in the hope that it will be useful, |
|
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
12 * Library General Public License for more details. |
|
13 * |
|
14 * You should have received a copy of the GNU Library General Public License |
|
15 * along with this library; see the file COPYING.LIB. If not, write to |
|
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
|
17 * Boston, MA 02110-1301, USA. |
|
18 */ |
|
19 |
|
20 #include "config.h" |
|
21 #include "InlineFlowBox.h" |
|
22 |
|
23 #include "CachedImage.h" |
|
24 #include "CSSPropertyNames.h" |
|
25 #include "Document.h" |
|
26 #include "EllipsisBox.h" |
|
27 #include "GraphicsContext.h" |
|
28 #include "InlineTextBox.h" |
|
29 #include "HitTestResult.h" |
|
30 #include "RootInlineBox.h" |
|
31 #include "RenderBlock.h" |
|
32 #include "RenderInline.h" |
|
33 #include "RenderLayer.h" |
|
34 #include "RenderListMarker.h" |
|
35 #include "RenderTableCell.h" |
|
36 #include "RootInlineBox.h" |
|
37 #include "Text.h" |
|
38 |
|
39 #include <math.h> |
|
40 |
|
41 using namespace std; |
|
42 |
|
43 namespace WebCore { |
|
44 |
|
45 #ifndef NDEBUG |
|
46 |
|
47 InlineFlowBox::~InlineFlowBox() |
|
48 { |
|
49 if (!m_hasBadChildList) |
|
50 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) |
|
51 child->setHasBadParent(); |
|
52 } |
|
53 |
|
54 #endif |
|
55 |
|
56 int InlineFlowBox::getFlowSpacingWidth() |
|
57 { |
|
58 int totWidth = marginBorderPaddingLeft() + marginBorderPaddingRight(); |
|
59 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { |
|
60 if (curr->isInlineFlowBox()) |
|
61 totWidth += static_cast<InlineFlowBox*>(curr)->getFlowSpacingWidth(); |
|
62 } |
|
63 return totWidth; |
|
64 } |
|
65 |
|
66 void InlineFlowBox::addToLine(InlineBox* child) |
|
67 { |
|
68 ASSERT(!child->parent()); |
|
69 ASSERT(!child->nextOnLine()); |
|
70 ASSERT(!child->prevOnLine()); |
|
71 checkConsistency(); |
|
72 |
|
73 child->setParent(this); |
|
74 if (!m_firstChild) { |
|
75 m_firstChild = child; |
|
76 m_lastChild = child; |
|
77 } else { |
|
78 m_lastChild->setNextOnLine(child); |
|
79 child->setPrevOnLine(m_lastChild); |
|
80 m_lastChild = child; |
|
81 } |
|
82 child->setFirstLineStyleBit(m_firstLine); |
|
83 if (child->isText()) |
|
84 m_hasTextChildren = true; |
|
85 if (child->renderer()->selectionState() != RenderObject::SelectionNone) |
|
86 root()->setHasSelectedChildren(true); |
|
87 |
|
88 checkConsistency(); |
|
89 } |
|
90 |
|
91 void InlineFlowBox::removeChild(InlineBox* child) |
|
92 { |
|
93 checkConsistency(); |
|
94 |
|
95 if (!m_dirty) |
|
96 dirtyLineBoxes(); |
|
97 |
|
98 root()->childRemoved(child); |
|
99 |
|
100 if (child == m_firstChild) |
|
101 m_firstChild = child->nextOnLine(); |
|
102 if (child == m_lastChild) |
|
103 m_lastChild = child->prevOnLine(); |
|
104 if (child->nextOnLine()) |
|
105 child->nextOnLine()->setPrevOnLine(child->prevOnLine()); |
|
106 if (child->prevOnLine()) |
|
107 child->prevOnLine()->setNextOnLine(child->nextOnLine()); |
|
108 |
|
109 child->setParent(0); |
|
110 |
|
111 checkConsistency(); |
|
112 } |
|
113 |
|
114 void InlineFlowBox::deleteLine(RenderArena* arena) |
|
115 { |
|
116 InlineBox* child = firstChild(); |
|
117 InlineBox* next = 0; |
|
118 while (child) { |
|
119 ASSERT(this == child->parent()); |
|
120 next = child->nextOnLine(); |
|
121 #ifndef NDEBUG |
|
122 child->setParent(0); |
|
123 #endif |
|
124 child->deleteLine(arena); |
|
125 child = next; |
|
126 } |
|
127 #ifndef NDEBUG |
|
128 m_firstChild = 0; |
|
129 m_lastChild = 0; |
|
130 #endif |
|
131 |
|
132 removeLineBoxFromRenderObject(); |
|
133 destroy(arena); |
|
134 } |
|
135 |
|
136 void InlineFlowBox::removeLineBoxFromRenderObject() |
|
137 { |
|
138 toRenderInline(renderer())->lineBoxes()->removeLineBox(this); |
|
139 } |
|
140 |
|
141 void InlineFlowBox::extractLine() |
|
142 { |
|
143 if (!m_extracted) |
|
144 extractLineBoxFromRenderObject(); |
|
145 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) |
|
146 child->extractLine(); |
|
147 } |
|
148 |
|
149 void InlineFlowBox::extractLineBoxFromRenderObject() |
|
150 { |
|
151 toRenderInline(renderer())->lineBoxes()->extractLineBox(this); |
|
152 } |
|
153 |
|
154 void InlineFlowBox::attachLine() |
|
155 { |
|
156 if (m_extracted) |
|
157 attachLineBoxToRenderObject(); |
|
158 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) |
|
159 child->attachLine(); |
|
160 } |
|
161 |
|
162 void InlineFlowBox::attachLineBoxToRenderObject() |
|
163 { |
|
164 toRenderInline(renderer())->lineBoxes()->attachLineBox(this); |
|
165 } |
|
166 |
|
167 void InlineFlowBox::adjustPosition(int dx, int dy) |
|
168 { |
|
169 InlineBox::adjustPosition(dx, dy); |
|
170 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) |
|
171 child->adjustPosition(dx, dy); |
|
172 if (m_overflow) |
|
173 m_overflow->move(dx, dy); |
|
174 } |
|
175 |
|
176 RenderLineBoxList* InlineFlowBox::rendererLineBoxes() const |
|
177 { |
|
178 return toRenderInline(renderer())->lineBoxes(); |
|
179 } |
|
180 |
|
181 bool InlineFlowBox::onEndChain(RenderObject* endObject) |
|
182 { |
|
183 if (!endObject) |
|
184 return false; |
|
185 |
|
186 if (endObject == renderer()) |
|
187 return true; |
|
188 |
|
189 RenderObject* curr = endObject; |
|
190 RenderObject* parent = curr->parent(); |
|
191 while (parent && !parent->isRenderBlock()) { |
|
192 if (parent->lastChild() != curr || parent == renderer()) |
|
193 return false; |
|
194 |
|
195 curr = parent; |
|
196 parent = curr->parent(); |
|
197 } |
|
198 |
|
199 return true; |
|
200 } |
|
201 |
|
202 void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject* endObject) |
|
203 { |
|
204 // All boxes start off open. They will not apply any margins/border/padding on |
|
205 // any side. |
|
206 bool includeLeftEdge = false; |
|
207 bool includeRightEdge = false; |
|
208 |
|
209 // The root inline box never has borders/margins/padding. |
|
210 if (parent()) { |
|
211 bool ltr = renderer()->style()->direction() == LTR; |
|
212 |
|
213 // Check to see if all initial lines are unconstructed. If so, then |
|
214 // we know the inline began on this line (unless we are a continuation). |
|
215 RenderLineBoxList* lineBoxList = rendererLineBoxes(); |
|
216 if (!lineBoxList->firstLineBox()->isConstructed() && !renderer()->isInlineElementContinuation()) { |
|
217 if (ltr && lineBoxList->firstLineBox() == this) |
|
218 includeLeftEdge = true; |
|
219 else if (!ltr && lineBoxList->lastLineBox() == this) |
|
220 includeRightEdge = true; |
|
221 } |
|
222 |
|
223 // In order to determine if the inline ends on this line, we check three things: |
|
224 // (1) If we are the last line and we don't have a continuation(), then we can |
|
225 // close up. |
|
226 // (2) If the last line box for the flow has an object following it on the line (ltr, |
|
227 // reverse for rtl), then the inline has closed. |
|
228 // (3) The line may end on the inline. If we are the last child (climbing up |
|
229 // the end object's chain), then we just closed as well. |
|
230 if (!lineBoxList->lastLineBox()->isConstructed()) { |
|
231 RenderInline* inlineFlow = toRenderInline(renderer()); |
|
232 if (ltr) { |
|
233 if (!nextLineBox() && |
|
234 ((lastLine && !inlineFlow->continuation()) || nextOnLineExists() || onEndChain(endObject))) |
|
235 includeRightEdge = true; |
|
236 } else { |
|
237 if ((!prevLineBox() || prevLineBox()->isConstructed()) && |
|
238 ((lastLine && !inlineFlow->continuation()) || prevOnLineExists() || onEndChain(endObject))) |
|
239 includeLeftEdge = true; |
|
240 } |
|
241 } |
|
242 } |
|
243 |
|
244 setEdges(includeLeftEdge, includeRightEdge); |
|
245 |
|
246 // Recur into our children. |
|
247 for (InlineBox* currChild = firstChild(); currChild; currChild = currChild->nextOnLine()) { |
|
248 if (currChild->isInlineFlowBox()) { |
|
249 InlineFlowBox* currFlow = static_cast<InlineFlowBox*>(currChild); |
|
250 currFlow->determineSpacingForFlowBoxes(lastLine, endObject); |
|
251 } |
|
252 } |
|
253 } |
|
254 |
|
255 int InlineFlowBox::placeBoxesHorizontally(int xPos, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) |
|
256 { |
|
257 // Set our x position. |
|
258 setX(xPos); |
|
259 |
|
260 int leftLayoutOverflow = xPos; |
|
261 int rightLayoutOverflow = xPos; |
|
262 int leftVisualOverflow = xPos; |
|
263 int rightVisualOverflow = xPos; |
|
264 |
|
265 int boxShadowLeft; |
|
266 int boxShadowRight; |
|
267 renderer()->style(m_firstLine)->getBoxShadowHorizontalExtent(boxShadowLeft, boxShadowRight); |
|
268 |
|
269 leftVisualOverflow = min(xPos + boxShadowLeft, leftVisualOverflow); |
|
270 |
|
271 int startX = xPos; |
|
272 xPos += borderLeft() + paddingLeft(); |
|
273 |
|
274 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { |
|
275 if (curr->renderer()->isText()) { |
|
276 InlineTextBox* text = static_cast<InlineTextBox*>(curr); |
|
277 RenderText* rt = toRenderText(text->renderer()); |
|
278 if (rt->textLength()) { |
|
279 if (needsWordSpacing && isSpaceOrNewline(rt->characters()[text->start()])) |
|
280 xPos += rt->style(m_firstLine)->font().wordSpacing(); |
|
281 needsWordSpacing = !isSpaceOrNewline(rt->characters()[text->end()]); |
|
282 } |
|
283 text->setX(xPos); |
|
284 |
|
285 int strokeOverflow = static_cast<int>(ceilf(rt->style()->textStrokeWidth() / 2.0f)); |
|
286 |
|
287 // If letter-spacing is negative, we should factor that into right layout overflow. (Even in RTL, letter-spacing is |
|
288 // applied to the right, so this is not an issue with left overflow. |
|
289 int letterSpacing = min(0, (int)rt->style(m_firstLine)->font().letterSpacing()); |
|
290 rightLayoutOverflow = max(xPos + text->width() - letterSpacing, rightLayoutOverflow); |
|
291 |
|
292 GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(static_cast<InlineTextBox*>(curr)); |
|
293 GlyphOverflow* glyphOverflow = it == textBoxDataMap.end() ? 0 : &it->second.second; |
|
294 |
|
295 int leftGlyphOverflow = -strokeOverflow - (glyphOverflow ? glyphOverflow->left : 0); |
|
296 int rightGlyphOverflow = strokeOverflow - letterSpacing + (glyphOverflow ? glyphOverflow->right : 0); |
|
297 |
|
298 int childOverflowLeft = leftGlyphOverflow; |
|
299 int childOverflowRight = rightGlyphOverflow; |
|
300 for (const ShadowData* shadow = rt->style()->textShadow(); shadow; shadow = shadow->next()) { |
|
301 childOverflowLeft = min(childOverflowLeft, shadow->x() - shadow->blur() + leftGlyphOverflow); |
|
302 childOverflowRight = max(childOverflowRight, shadow->x() + shadow->blur() + rightGlyphOverflow); |
|
303 } |
|
304 |
|
305 leftVisualOverflow = min(xPos + childOverflowLeft, leftVisualOverflow); |
|
306 rightVisualOverflow = max(xPos + text->width() + childOverflowRight, rightVisualOverflow); |
|
307 |
|
308 xPos += text->width(); |
|
309 } else { |
|
310 if (curr->renderer()->isPositioned()) { |
|
311 if (curr->renderer()->parent()->style()->direction() == LTR) |
|
312 curr->setX(xPos); |
|
313 else |
|
314 // Our offset that we cache needs to be from the edge of the right border box and |
|
315 // not the left border box. We have to subtract |x| from the width of the block |
|
316 // (which can be obtained from the root line box). |
|
317 curr->setX(root()->block()->width() - xPos); |
|
318 continue; // The positioned object has no effect on the width. |
|
319 } |
|
320 if (curr->renderer()->isRenderInline()) { |
|
321 InlineFlowBox* flow = static_cast<InlineFlowBox*>(curr); |
|
322 xPos += flow->marginLeft(); |
|
323 xPos = flow->placeBoxesHorizontally(xPos, needsWordSpacing, textBoxDataMap); |
|
324 xPos += flow->marginRight(); |
|
325 leftLayoutOverflow = min(leftLayoutOverflow, flow->leftLayoutOverflow()); |
|
326 rightLayoutOverflow = max(rightLayoutOverflow, flow->rightLayoutOverflow()); |
|
327 leftVisualOverflow = min(leftVisualOverflow, flow->leftVisualOverflow()); |
|
328 rightVisualOverflow = max(rightVisualOverflow, flow->rightVisualOverflow()); |
|
329 } else if (!curr->renderer()->isListMarker() || toRenderListMarker(curr->renderer())->isInside()) { |
|
330 xPos += curr->boxModelObject()->marginLeft(); |
|
331 curr->setX(xPos); |
|
332 |
|
333 RenderBox* box = toRenderBox(curr->renderer()); |
|
334 int childLeftOverflow = box->hasOverflowClip() ? 0 : box->leftLayoutOverflow(); |
|
335 int childRightOverflow = box->hasOverflowClip() ? curr->width() : box->rightLayoutOverflow(); |
|
336 |
|
337 leftLayoutOverflow = min(xPos + childLeftOverflow, leftLayoutOverflow); |
|
338 rightLayoutOverflow = max(xPos + childRightOverflow, rightLayoutOverflow); |
|
339 |
|
340 leftVisualOverflow = min(xPos + box->leftVisualOverflow(), leftVisualOverflow); |
|
341 rightVisualOverflow = max(xPos + box->rightVisualOverflow(), rightVisualOverflow); |
|
342 |
|
343 xPos += curr->width() + curr->boxModelObject()->marginRight(); |
|
344 } |
|
345 } |
|
346 } |
|
347 |
|
348 xPos += borderRight() + paddingRight(); |
|
349 setWidth(xPos - startX); |
|
350 rightVisualOverflow = max(x() + width() + boxShadowRight, rightVisualOverflow); |
|
351 rightLayoutOverflow = max(x() + width(), rightLayoutOverflow); |
|
352 |
|
353 setHorizontalOverflowPositions(leftLayoutOverflow, rightLayoutOverflow, leftVisualOverflow, rightVisualOverflow); |
|
354 return xPos; |
|
355 } |
|
356 |
|
357 void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, |
|
358 int maxPositionTop, int maxPositionBottom) |
|
359 { |
|
360 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { |
|
361 // The computed lineheight needs to be extended for the |
|
362 // positioned elements |
|
363 if (curr->renderer()->isPositioned()) |
|
364 continue; // Positioned placeholders don't affect calculations. |
|
365 if (curr->y() == PositionTop || curr->y() == PositionBottom) { |
|
366 int lineHeight = curr->lineHeight(false); |
|
367 if (curr->y() == PositionTop) { |
|
368 if (maxAscent + maxDescent < lineHeight) |
|
369 maxDescent = lineHeight - maxAscent; |
|
370 } |
|
371 else { |
|
372 if (maxAscent + maxDescent < lineHeight) |
|
373 maxAscent = lineHeight - maxDescent; |
|
374 } |
|
375 |
|
376 if (maxAscent + maxDescent >= max(maxPositionTop, maxPositionBottom)) |
|
377 break; |
|
378 } |
|
379 |
|
380 if (curr->isInlineFlowBox()) |
|
381 static_cast<InlineFlowBox*>(curr)->adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom); |
|
382 } |
|
383 } |
|
384 |
|
385 static int verticalPositionForBox(InlineBox* curr, bool firstLine) |
|
386 { |
|
387 if (curr->renderer()->isText()) |
|
388 return curr->parent()->y(); |
|
389 if (curr->renderer()->isBox()) |
|
390 return toRenderBox(curr->renderer())->verticalPosition(firstLine); |
|
391 return toRenderInline(curr->renderer())->verticalPositionFromCache(firstLine); |
|
392 } |
|
393 |
|
394 void InlineFlowBox::computeLogicalBoxHeights(int& maxPositionTop, int& maxPositionBottom, |
|
395 int& maxAscent, int& maxDescent, bool strictMode, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) |
|
396 { |
|
397 if (isRootInlineBox()) { |
|
398 // Examine our root box. |
|
399 int height = lineHeight(true); |
|
400 int baseline = baselinePosition(true); |
|
401 if (hasTextChildren() || strictMode) { |
|
402 int ascent = baseline; |
|
403 int descent = height - ascent; |
|
404 if (maxAscent < ascent) |
|
405 maxAscent = ascent; |
|
406 if (maxDescent < descent) |
|
407 maxDescent = descent; |
|
408 } |
|
409 } |
|
410 |
|
411 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { |
|
412 if (curr->renderer()->isPositioned()) |
|
413 continue; // Positioned placeholders don't affect calculations. |
|
414 |
|
415 bool isInlineFlow = curr->isInlineFlowBox(); |
|
416 |
|
417 int lineHeight; |
|
418 int baseline; |
|
419 Vector<const SimpleFontData*>* usedFonts = 0; |
|
420 if (curr->isInlineTextBox()) { |
|
421 GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(static_cast<InlineTextBox*>(curr)); |
|
422 usedFonts = it == textBoxDataMap.end() ? 0 : &it->second.first; |
|
423 } |
|
424 |
|
425 if (usedFonts) { |
|
426 usedFonts->append(curr->renderer()->style(m_firstLine)->font().primaryFont()); |
|
427 Length parentLineHeight = curr->renderer()->parent()->style()->lineHeight(); |
|
428 if (parentLineHeight.isNegative()) { |
|
429 int baselineToBottom = 0; |
|
430 baseline = 0; |
|
431 for (size_t i = 0; i < usedFonts->size(); ++i) { |
|
432 int halfLeading = (usedFonts->at(i)->lineSpacing() - usedFonts->at(i)->ascent() - usedFonts->at(i)->descent()) / 2; |
|
433 baseline = max(baseline, halfLeading + usedFonts->at(i)->ascent()); |
|
434 baselineToBottom = max(baselineToBottom, usedFonts->at(i)->lineSpacing() - usedFonts->at(i)->ascent() - usedFonts->at(i)->descent() - halfLeading); |
|
435 } |
|
436 lineHeight = baseline + baselineToBottom; |
|
437 } else if (parentLineHeight.isPercent()) { |
|
438 lineHeight = parentLineHeight.calcMinValue(curr->renderer()->style()->fontSize()); |
|
439 baseline = 0; |
|
440 for (size_t i = 0; i < usedFonts->size(); ++i) { |
|
441 int halfLeading = (lineHeight - usedFonts->at(i)->ascent() - usedFonts->at(i)->descent()) / 2; |
|
442 baseline = max(baseline, halfLeading + usedFonts->at(i)->ascent()); |
|
443 } |
|
444 } else { |
|
445 lineHeight = parentLineHeight.value(); |
|
446 baseline = 0; |
|
447 for (size_t i = 0; i < usedFonts->size(); ++i) { |
|
448 int halfLeading = (lineHeight - usedFonts->at(i)->ascent() - usedFonts->at(i)->descent()) / 2; |
|
449 baseline = max(baseline, halfLeading + usedFonts->at(i)->ascent()); |
|
450 } |
|
451 } |
|
452 } else { |
|
453 lineHeight = curr->lineHeight(false); |
|
454 baseline = curr->baselinePosition(false); |
|
455 } |
|
456 |
|
457 curr->setY(verticalPositionForBox(curr, m_firstLine)); |
|
458 if (curr->y() == PositionTop) { |
|
459 if (maxPositionTop < lineHeight) |
|
460 maxPositionTop = lineHeight; |
|
461 } else if (curr->y() == PositionBottom) { |
|
462 if (maxPositionBottom < lineHeight) |
|
463 maxPositionBottom = lineHeight; |
|
464 } else if ((!isInlineFlow || static_cast<InlineFlowBox*>(curr)->hasTextChildren()) || curr->boxModelObject()->hasHorizontalBordersOrPadding() || strictMode) { |
|
465 int ascent = baseline - curr->y(); |
|
466 int descent = lineHeight - ascent; |
|
467 if (maxAscent < ascent) |
|
468 maxAscent = ascent; |
|
469 if (maxDescent < descent) |
|
470 maxDescent = descent; |
|
471 } |
|
472 |
|
473 if (curr->isInlineFlowBox()) |
|
474 static_cast<InlineFlowBox*>(curr)->computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, strictMode, textBoxDataMap); |
|
475 } |
|
476 } |
|
477 |
|
478 void InlineFlowBox::placeBoxesVertically(int yPos, int maxHeight, int maxAscent, bool strictMode, int& selectionTop, int& selectionBottom) |
|
479 { |
|
480 if (isRootInlineBox()) |
|
481 setY(yPos + maxAscent - baselinePosition(true)); // Place our root box. |
|
482 |
|
483 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { |
|
484 if (curr->renderer()->isPositioned()) |
|
485 continue; // Positioned placeholders don't affect calculations. |
|
486 |
|
487 // Adjust boxes to use their real box y/height and not the logical height (as dictated by |
|
488 // line-height). |
|
489 bool isInlineFlow = curr->isInlineFlowBox(); |
|
490 if (isInlineFlow) |
|
491 static_cast<InlineFlowBox*>(curr)->placeBoxesVertically(yPos, maxHeight, maxAscent, strictMode, selectionTop, selectionBottom); |
|
492 |
|
493 bool childAffectsTopBottomPos = true; |
|
494 if (curr->y() == PositionTop) |
|
495 curr->setY(yPos); |
|
496 else if (curr->y() == PositionBottom) |
|
497 curr->setY(yPos + maxHeight - curr->lineHeight(false)); |
|
498 else { |
|
499 if ((isInlineFlow && !static_cast<InlineFlowBox*>(curr)->hasTextChildren()) && !curr->boxModelObject()->hasHorizontalBordersOrPadding() && !strictMode) |
|
500 childAffectsTopBottomPos = false; |
|
501 int posAdjust = maxAscent - curr->baselinePosition(false); |
|
502 curr->setY(curr->y() + yPos + posAdjust); |
|
503 } |
|
504 |
|
505 int newY = curr->y(); |
|
506 if (curr->isText() || curr->isInlineFlowBox()) { |
|
507 const Font& font = curr->renderer()->style(m_firstLine)->font(); |
|
508 newY += curr->baselinePosition(false) - font.ascent(); |
|
509 if (curr->isInlineFlowBox()) |
|
510 newY -= curr->boxModelObject()->borderTop() + curr->boxModelObject()->paddingTop(); |
|
511 } else if (!curr->renderer()->isBR()) { |
|
512 RenderBox* box = toRenderBox(curr->renderer()); |
|
513 newY += box->marginTop(); |
|
514 } |
|
515 |
|
516 curr->setY(newY); |
|
517 |
|
518 if (childAffectsTopBottomPos) { |
|
519 int boxHeight = curr->height(); |
|
520 selectionTop = min(selectionTop, newY); |
|
521 selectionBottom = max(selectionBottom, newY + boxHeight); |
|
522 } |
|
523 } |
|
524 |
|
525 if (isRootInlineBox()) { |
|
526 const Font& font = renderer()->style(m_firstLine)->font(); |
|
527 setY(y() + baselinePosition(true) - font.ascent()); |
|
528 if (hasTextChildren() || strictMode) { |
|
529 selectionTop = min(selectionTop, y()); |
|
530 selectionBottom = max(selectionBottom, y() + height()); |
|
531 } |
|
532 } |
|
533 } |
|
534 |
|
535 void InlineFlowBox::computeVerticalOverflow(int lineTop, int lineBottom, bool strictMode, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) |
|
536 { |
|
537 int boxHeight = height(); |
|
538 |
|
539 // Any spillage outside of the line top and bottom is not considered overflow. We just ignore this, since it only happens |
|
540 // from the "your ascent/descent don't affect the line" quirk. |
|
541 int topOverflow = max(y(), lineTop); |
|
542 int bottomOverflow = min(y() + boxHeight, lineBottom); |
|
543 |
|
544 int topLayoutOverflow = topOverflow; |
|
545 int bottomLayoutOverflow = bottomOverflow; |
|
546 |
|
547 int topVisualOverflow = topOverflow; |
|
548 int bottomVisualOverflow = bottomOverflow; |
|
549 |
|
550 // box-shadow on root line boxes is applying to the block and not to the lines. |
|
551 if (parent()) { |
|
552 int boxShadowTop; |
|
553 int boxShadowBottom; |
|
554 renderer()->style(m_firstLine)->getBoxShadowVerticalExtent(boxShadowTop, boxShadowBottom); |
|
555 |
|
556 topVisualOverflow = min(y() + boxShadowTop, topVisualOverflow); |
|
557 bottomVisualOverflow = max(y() + boxHeight + boxShadowBottom, bottomVisualOverflow); |
|
558 } |
|
559 |
|
560 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { |
|
561 if (curr->renderer()->isPositioned()) |
|
562 continue; // Positioned placeholders don't affect calculations. |
|
563 |
|
564 if (curr->renderer()->isText()) { |
|
565 InlineTextBox* text = static_cast<InlineTextBox*>(curr); |
|
566 RenderText* rt = toRenderText(text->renderer()); |
|
567 if (rt->isBR()) |
|
568 continue; |
|
569 |
|
570 int strokeOverflow = static_cast<int>(ceilf(rt->style()->textStrokeWidth() / 2.0f)); |
|
571 |
|
572 GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(static_cast<InlineTextBox*>(curr)); |
|
573 GlyphOverflow* glyphOverflow = it == textBoxDataMap.end() ? 0 : &it->second.second; |
|
574 |
|
575 int topGlyphOverflow = -strokeOverflow - (glyphOverflow ? glyphOverflow->top : 0); |
|
576 int bottomGlyphOverflow = strokeOverflow + (glyphOverflow ? glyphOverflow->bottom : 0); |
|
577 |
|
578 int childOverflowTop = topGlyphOverflow; |
|
579 int childOverflowBottom = bottomGlyphOverflow; |
|
580 for (const ShadowData* shadow = rt->style()->textShadow(); shadow; shadow = shadow->next()) { |
|
581 childOverflowTop = min(childOverflowTop, shadow->y() - shadow->blur() + topGlyphOverflow); |
|
582 childOverflowBottom = max(childOverflowBottom, shadow->y() + shadow->blur() + bottomGlyphOverflow); |
|
583 } |
|
584 |
|
585 topVisualOverflow = min(curr->y() + childOverflowTop, topVisualOverflow); |
|
586 bottomVisualOverflow = max(curr->y() + text->height() + childOverflowBottom, bottomVisualOverflow); |
|
587 } else if (curr->renderer()->isRenderInline()) { |
|
588 InlineFlowBox* flow = static_cast<InlineFlowBox*>(curr); |
|
589 flow->computeVerticalOverflow(lineTop, lineBottom, strictMode, textBoxDataMap); |
|
590 topLayoutOverflow = min(topLayoutOverflow, flow->topLayoutOverflow()); |
|
591 bottomLayoutOverflow = max(bottomLayoutOverflow, flow->bottomLayoutOverflow()); |
|
592 topVisualOverflow = min(topVisualOverflow, flow->topVisualOverflow()); |
|
593 bottomVisualOverflow = max(bottomVisualOverflow, flow->bottomVisualOverflow()); |
|
594 } else if (!curr->boxModelObject()->hasSelfPaintingLayer()){ |
|
595 // Only include overflow from replaced inlines if they do not paint themselves. |
|
596 RenderBox* box = toRenderBox(curr->renderer()); |
|
597 int boxY = curr->y(); |
|
598 int childTopOverflow = box->hasOverflowClip() ? 0 : box->topLayoutOverflow(); |
|
599 int childBottomOverflow = box->hasOverflowClip() ? curr->height() : box->bottomLayoutOverflow(); |
|
600 topLayoutOverflow = min(boxY + childTopOverflow, topLayoutOverflow); |
|
601 bottomLayoutOverflow = max(boxY + childBottomOverflow, bottomLayoutOverflow); |
|
602 topVisualOverflow = min(boxY + box->topVisualOverflow(), topVisualOverflow); |
|
603 bottomVisualOverflow = max(boxY + box->bottomVisualOverflow(), bottomVisualOverflow); |
|
604 } |
|
605 } |
|
606 |
|
607 setVerticalOverflowPositions(topLayoutOverflow, bottomLayoutOverflow, topVisualOverflow, bottomVisualOverflow, boxHeight); |
|
608 } |
|
609 |
|
610 bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty) |
|
611 { |
|
612 IntRect overflowRect(visibleOverflowRect()); |
|
613 overflowRect.move(tx, ty); |
|
614 if (!overflowRect.intersects(result.rectFromPoint(x, y))) |
|
615 return false; |
|
616 |
|
617 // Check children first. |
|
618 for (InlineBox* curr = lastChild(); curr; curr = curr->prevOnLine()) { |
|
619 if ((curr->renderer()->isText() || !curr->boxModelObject()->hasSelfPaintingLayer()) && curr->nodeAtPoint(request, result, x, y, tx, ty)) { |
|
620 renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); |
|
621 return true; |
|
622 } |
|
623 } |
|
624 |
|
625 // Now check ourselves. |
|
626 IntRect rect(tx + m_x, ty + m_y, m_width, height()); |
|
627 if (visibleToHitTesting() && rect.intersects(result.rectFromPoint(x, y))) { |
|
628 renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); // Don't add in m_x or m_y here, we want coords in the containing block's space. |
|
629 if (!result.addNodeToRectBasedTestResult(renderer()->node(), x, y, rect)) |
|
630 return true; |
|
631 } |
|
632 |
|
633 return false; |
|
634 } |
|
635 |
|
636 void InlineFlowBox::paint(PaintInfo& paintInfo, int tx, int ty) |
|
637 { |
|
638 IntRect overflowRect(visibleOverflowRect()); |
|
639 overflowRect.inflate(renderer()->maximalOutlineSize(paintInfo.phase)); |
|
640 overflowRect.move(tx, ty); |
|
641 |
|
642 if (!paintInfo.rect.intersects(overflowRect)) |
|
643 return; |
|
644 |
|
645 if (paintInfo.phase != PaintPhaseChildOutlines) { |
|
646 if (paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) { |
|
647 // Add ourselves to the paint info struct's list of inlines that need to paint their |
|
648 // outlines. |
|
649 if (renderer()->style()->visibility() == VISIBLE && renderer()->hasOutline() && !isRootInlineBox()) { |
|
650 RenderInline* inlineFlow = toRenderInline(renderer()); |
|
651 |
|
652 RenderBlock* cb = 0; |
|
653 bool containingBlockPaintsContinuationOutline = inlineFlow->continuation() || inlineFlow->isInlineElementContinuation(); |
|
654 if (containingBlockPaintsContinuationOutline) { |
|
655 cb = renderer()->containingBlock()->containingBlock(); |
|
656 |
|
657 for (RenderBoxModelObject* box = boxModelObject(); box != cb; box = box->parent()->enclosingBoxModelObject()) { |
|
658 if (box->hasSelfPaintingLayer()) { |
|
659 containingBlockPaintsContinuationOutline = false; |
|
660 break; |
|
661 } |
|
662 } |
|
663 } |
|
664 |
|
665 if (containingBlockPaintsContinuationOutline) { |
|
666 // Add ourselves to the containing block of the entire continuation so that it can |
|
667 // paint us atomically. |
|
668 cb->addContinuationWithOutline(toRenderInline(renderer()->node()->renderer())); |
|
669 } else if (!inlineFlow->isInlineElementContinuation()) |
|
670 paintInfo.outlineObjects->add(inlineFlow); |
|
671 } |
|
672 } else if (paintInfo.phase == PaintPhaseMask) { |
|
673 paintMask(paintInfo, tx, ty); |
|
674 return; |
|
675 } else { |
|
676 // 1. Paint our background, border and box-shadow. |
|
677 paintBoxDecorations(paintInfo, tx, ty); |
|
678 |
|
679 // 2. Paint our underline and overline. |
|
680 paintTextDecorations(paintInfo, tx, ty, false); |
|
681 } |
|
682 } |
|
683 |
|
684 if (paintInfo.phase == PaintPhaseMask) |
|
685 return; |
|
686 |
|
687 PaintPhase paintPhase = paintInfo.phase == PaintPhaseChildOutlines ? PaintPhaseOutline : paintInfo.phase; |
|
688 PaintInfo childInfo(paintInfo); |
|
689 childInfo.phase = paintPhase; |
|
690 childInfo.updatePaintingRootForChildren(renderer()); |
|
691 |
|
692 // 3. Paint our children. |
|
693 if (paintPhase != PaintPhaseSelfOutline) { |
|
694 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { |
|
695 if (curr->renderer()->isText() || !curr->boxModelObject()->hasSelfPaintingLayer()) |
|
696 curr->paint(childInfo, tx, ty); |
|
697 } |
|
698 } |
|
699 |
|
700 // 4. Paint our strike-through |
|
701 if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection) |
|
702 paintTextDecorations(paintInfo, tx, ty, true); |
|
703 } |
|
704 |
|
705 void InlineFlowBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int _tx, int _ty, int w, int h, CompositeOperator op) |
|
706 { |
|
707 if (!fillLayer) |
|
708 return; |
|
709 paintFillLayers(paintInfo, c, fillLayer->next(), _tx, _ty, w, h, op); |
|
710 paintFillLayer(paintInfo, c, fillLayer, _tx, _ty, w, h, op); |
|
711 } |
|
712 |
|
713 void InlineFlowBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int w, int h, CompositeOperator op) |
|
714 { |
|
715 StyleImage* img = fillLayer->image(); |
|
716 bool hasFillImage = img && img->canRender(renderer()->style()->effectiveZoom()); |
|
717 if ((!hasFillImage && !renderer()->style()->hasBorderRadius()) || (!prevLineBox() && !nextLineBox()) || !parent()) |
|
718 boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, tx, ty, w, h, this, op); |
|
719 else { |
|
720 // We have a fill image that spans multiple lines. |
|
721 // We need to adjust _tx and _ty by the width of all previous lines. |
|
722 // Think of background painting on inlines as though you had one long line, a single continuous |
|
723 // strip. Even though that strip has been broken up across multiple lines, you still paint it |
|
724 // as though you had one single line. This means each line has to pick up the background where |
|
725 // the previous line left off. |
|
726 // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right, |
|
727 // but it isn't even clear how this should work at all. |
|
728 int xOffsetOnLine = 0; |
|
729 for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox()) |
|
730 xOffsetOnLine += curr->width(); |
|
731 int startX = tx - xOffsetOnLine; |
|
732 int totalWidth = xOffsetOnLine; |
|
733 for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox()) |
|
734 totalWidth += curr->width(); |
|
735 paintInfo.context->save(); |
|
736 paintInfo.context->clip(IntRect(tx, ty, width(), height())); |
|
737 boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, startX, ty, totalWidth, h, this, op); |
|
738 paintInfo.context->restore(); |
|
739 } |
|
740 } |
|
741 |
|
742 void InlineFlowBox::paintBoxShadow(GraphicsContext* context, RenderStyle* s, ShadowStyle shadowStyle, int tx, int ty, int w, int h) |
|
743 { |
|
744 if ((!prevLineBox() && !nextLineBox()) || !parent()) |
|
745 boxModelObject()->paintBoxShadow(context, tx, ty, w, h, s, shadowStyle); |
|
746 else { |
|
747 // FIXME: We can do better here in the multi-line case. We want to push a clip so that the shadow doesn't |
|
748 // protrude incorrectly at the edges, and we want to possibly include shadows cast from the previous/following lines |
|
749 boxModelObject()->paintBoxShadow(context, tx, ty, w, h, s, shadowStyle, includeLeftEdge(), includeRightEdge()); |
|
750 } |
|
751 } |
|
752 |
|
753 void InlineFlowBox::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) |
|
754 { |
|
755 if (!paintInfo.shouldPaintWithinRoot(renderer()) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseForeground) |
|
756 return; |
|
757 |
|
758 int x = m_x; |
|
759 int y = m_y; |
|
760 int w = width(); |
|
761 int h = height(); |
|
762 |
|
763 // Constrain our background/border painting to the line top and bottom if necessary. |
|
764 bool strictMode = renderer()->document()->inStrictMode(); |
|
765 if (!hasTextChildren() && !strictMode) { |
|
766 RootInlineBox* rootBox = root(); |
|
767 int bottom = min(rootBox->lineBottom(), y + h); |
|
768 y = max(rootBox->lineTop(), y); |
|
769 h = bottom - y; |
|
770 } |
|
771 |
|
772 // Move x/y to our coordinates. |
|
773 tx += x; |
|
774 ty += y; |
|
775 |
|
776 GraphicsContext* context = paintInfo.context; |
|
777 |
|
778 // You can use p::first-line to specify a background. If so, the root line boxes for |
|
779 // a line may actually have to paint a background. |
|
780 RenderStyle* styleToUse = renderer()->style(m_firstLine); |
|
781 if ((!parent() && m_firstLine && styleToUse != renderer()->style()) || (parent() && renderer()->hasBoxDecorations())) { |
|
782 // Shadow comes first and is behind the background and border. |
|
783 if (styleToUse->boxShadow()) |
|
784 paintBoxShadow(context, styleToUse, Normal, tx, ty, w, h); |
|
785 |
|
786 Color c = styleToUse->visitedDependentColor(CSSPropertyBackgroundColor); |
|
787 paintFillLayers(paintInfo, c, styleToUse->backgroundLayers(), tx, ty, w, h); |
|
788 |
|
789 if (styleToUse->boxShadow()) |
|
790 paintBoxShadow(context, styleToUse, Inset, tx, ty, w, h); |
|
791 |
|
792 // :first-line cannot be used to put borders on a line. Always paint borders with our |
|
793 // non-first-line style. |
|
794 if (parent() && renderer()->style()->hasBorder()) { |
|
795 StyleImage* borderImage = renderer()->style()->borderImage().image(); |
|
796 bool hasBorderImage = borderImage && borderImage->canRender(styleToUse->effectiveZoom()); |
|
797 if (hasBorderImage && !borderImage->isLoaded()) |
|
798 return; // Don't paint anything while we wait for the image to load. |
|
799 |
|
800 // The simple case is where we either have no border image or we are the only box for this object. In those |
|
801 // cases only a single call to draw is required. |
|
802 if (!hasBorderImage || (!prevLineBox() && !nextLineBox())) |
|
803 boxModelObject()->paintBorder(context, tx, ty, w, h, renderer()->style(), includeLeftEdge(), includeRightEdge()); |
|
804 else { |
|
805 // We have a border image that spans multiple lines. |
|
806 // We need to adjust _tx and _ty by the width of all previous lines. |
|
807 // Think of border image painting on inlines as though you had one long line, a single continuous |
|
808 // strip. Even though that strip has been broken up across multiple lines, you still paint it |
|
809 // as though you had one single line. This means each line has to pick up the image where |
|
810 // the previous line left off. |
|
811 // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right, |
|
812 // but it isn't even clear how this should work at all. |
|
813 int xOffsetOnLine = 0; |
|
814 for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox()) |
|
815 xOffsetOnLine += curr->width(); |
|
816 int startX = tx - xOffsetOnLine; |
|
817 int totalWidth = xOffsetOnLine; |
|
818 for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox()) |
|
819 totalWidth += curr->width(); |
|
820 context->save(); |
|
821 context->clip(IntRect(tx, ty, w, h)); |
|
822 boxModelObject()->paintBorder(context, startX, ty, totalWidth, h, renderer()->style()); |
|
823 context->restore(); |
|
824 } |
|
825 } |
|
826 } |
|
827 } |
|
828 |
|
829 void InlineFlowBox::paintMask(PaintInfo& paintInfo, int tx, int ty) |
|
830 { |
|
831 if (!paintInfo.shouldPaintWithinRoot(renderer()) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) |
|
832 return; |
|
833 |
|
834 int x = m_x; |
|
835 int y = m_y; |
|
836 int w = width(); |
|
837 int h = height(); |
|
838 |
|
839 // Constrain our background/border painting to the line top and bottom if necessary. |
|
840 bool strictMode = renderer()->document()->inStrictMode(); |
|
841 if (!hasTextChildren() && !strictMode) { |
|
842 RootInlineBox* rootBox = root(); |
|
843 int bottom = min(rootBox->lineBottom(), y + h); |
|
844 y = max(rootBox->lineTop(), y); |
|
845 h = bottom - y; |
|
846 } |
|
847 |
|
848 // Move x/y to our coordinates. |
|
849 tx += x; |
|
850 ty += y; |
|
851 |
|
852 const NinePieceImage& maskNinePieceImage = renderer()->style()->maskBoxImage(); |
|
853 StyleImage* maskBoxImage = renderer()->style()->maskBoxImage().image(); |
|
854 |
|
855 // Figure out if we need to push a transparency layer to render our mask. |
|
856 bool pushTransparencyLayer = false; |
|
857 bool compositedMask = renderer()->hasLayer() && boxModelObject()->layer()->hasCompositedMask(); |
|
858 CompositeOperator compositeOp = CompositeSourceOver; |
|
859 if (!compositedMask) { |
|
860 if ((maskBoxImage && renderer()->style()->maskLayers()->hasImage()) || renderer()->style()->maskLayers()->next()) |
|
861 pushTransparencyLayer = true; |
|
862 |
|
863 compositeOp = CompositeDestinationIn; |
|
864 if (pushTransparencyLayer) { |
|
865 paintInfo.context->setCompositeOperation(CompositeDestinationIn); |
|
866 paintInfo.context->beginTransparencyLayer(1.0f); |
|
867 compositeOp = CompositeSourceOver; |
|
868 } |
|
869 } |
|
870 |
|
871 paintFillLayers(paintInfo, Color(), renderer()->style()->maskLayers(), tx, ty, w, h, compositeOp); |
|
872 |
|
873 bool hasBoxImage = maskBoxImage && maskBoxImage->canRender(renderer()->style()->effectiveZoom()); |
|
874 if (!hasBoxImage || !maskBoxImage->isLoaded()) |
|
875 return; // Don't paint anything while we wait for the image to load. |
|
876 |
|
877 // The simple case is where we are the only box for this object. In those |
|
878 // cases only a single call to draw is required. |
|
879 if (!prevLineBox() && !nextLineBox()) { |
|
880 boxModelObject()->paintNinePieceImage(paintInfo.context, tx, ty, w, h, renderer()->style(), maskNinePieceImage, compositeOp); |
|
881 } else { |
|
882 // We have a mask image that spans multiple lines. |
|
883 // We need to adjust _tx and _ty by the width of all previous lines. |
|
884 int xOffsetOnLine = 0; |
|
885 for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox()) |
|
886 xOffsetOnLine += curr->width(); |
|
887 int startX = tx - xOffsetOnLine; |
|
888 int totalWidth = xOffsetOnLine; |
|
889 for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox()) |
|
890 totalWidth += curr->width(); |
|
891 paintInfo.context->save(); |
|
892 paintInfo.context->clip(IntRect(tx, ty, w, h)); |
|
893 boxModelObject()->paintNinePieceImage(paintInfo.context, startX, ty, totalWidth, h, renderer()->style(), maskNinePieceImage, compositeOp); |
|
894 paintInfo.context->restore(); |
|
895 } |
|
896 |
|
897 if (pushTransparencyLayer) |
|
898 paintInfo.context->endTransparencyLayer(); |
|
899 } |
|
900 |
|
901 static bool shouldDrawTextDecoration(RenderObject* obj) |
|
902 { |
|
903 for (RenderObject* curr = obj->firstChild(); curr; curr = curr->nextSibling()) { |
|
904 if (curr->isRenderInline()) |
|
905 return true; |
|
906 if (curr->isText() && !curr->isBR()) { |
|
907 if (!curr->style()->collapseWhiteSpace()) |
|
908 return true; |
|
909 Node* currElement = curr->node(); |
|
910 if (!currElement) |
|
911 return true; |
|
912 if (!currElement->isTextNode()) |
|
913 return true; |
|
914 if (!static_cast<Text*>(currElement)->containsOnlyWhitespace()) |
|
915 return true; |
|
916 } |
|
917 } |
|
918 return false; |
|
919 } |
|
920 |
|
921 void InlineFlowBox::paintTextDecorations(PaintInfo& paintInfo, int tx, int ty, bool paintedChildren) |
|
922 { |
|
923 // Paint text decorations like underlines/overlines. We only do this if we aren't in quirks mode (i.e., in |
|
924 // almost-strict mode or strict mode). |
|
925 if (renderer()->style()->htmlHacks() || !paintInfo.shouldPaintWithinRoot(renderer()) || |
|
926 renderer()->style()->visibility() != VISIBLE) |
|
927 return; |
|
928 |
|
929 // We don't want underlines or other decorations when we're trying to draw nothing but the selection as white text. |
|
930 if (paintInfo.phase == PaintPhaseSelection && paintInfo.forceBlackText) |
|
931 return; |
|
932 |
|
933 GraphicsContext* context = paintInfo.context; |
|
934 tx += m_x; |
|
935 ty += m_y; |
|
936 RenderStyle* styleToUse = renderer()->style(m_firstLine); |
|
937 int deco = parent() ? styleToUse->textDecoration() : styleToUse->textDecorationsInEffect(); |
|
938 if (deco != TDNONE && |
|
939 ((!paintedChildren && ((deco & UNDERLINE) || (deco & OVERLINE))) || (paintedChildren && (deco & LINE_THROUGH))) && |
|
940 shouldDrawTextDecoration(renderer())) { |
|
941 int x = m_x + borderLeft() + paddingLeft(); |
|
942 int w = m_width - (borderLeft() + paddingLeft() + borderRight() + paddingRight()); |
|
943 RootInlineBox* rootLine = root(); |
|
944 if (rootLine->ellipsisBox()) { |
|
945 int ellipsisX = m_x + rootLine->ellipsisBox()->x(); |
|
946 int ellipsisWidth = rootLine->ellipsisBox()->width(); |
|
947 bool ltr = renderer()->style()->direction() == LTR; |
|
948 if (rootLine == this) { |
|
949 // Trim w and x so that the underline isn't drawn underneath the ellipsis. |
|
950 // ltr: is our right edge farther right than the right edge of the ellipsis. |
|
951 // rtl: is the left edge of our box farther left than the left edge of the ellipsis. |
|
952 bool ltrTruncation = ltr && (x + w >= ellipsisX + ellipsisWidth); |
|
953 bool rtlTruncation = !ltr && (x <= ellipsisX + ellipsisWidth); |
|
954 if (ltrTruncation) |
|
955 w -= (x + w) - (ellipsisX + ellipsisWidth); |
|
956 else if (rtlTruncation) { |
|
957 int dx = m_x - ((ellipsisX - m_x) + ellipsisWidth); |
|
958 tx -= dx; |
|
959 w += dx; |
|
960 } |
|
961 } else { |
|
962 bool ltrPastEllipsis = ltr && x >= ellipsisX; |
|
963 bool rtlPastEllipsis = !ltr && (x + w) <= (ellipsisX + ellipsisWidth); |
|
964 if (ltrPastEllipsis || rtlPastEllipsis) |
|
965 return; |
|
966 |
|
967 bool ltrTruncation = ltr && x + w >= ellipsisX; |
|
968 bool rtlTruncation = !ltr && x <= ellipsisX; |
|
969 if (ltrTruncation) |
|
970 w -= (x + w - ellipsisX); |
|
971 else if (rtlTruncation) { |
|
972 int dx = m_x - ((ellipsisX - m_x) + ellipsisWidth); |
|
973 tx -= dx; |
|
974 w += dx; |
|
975 } |
|
976 } |
|
977 } |
|
978 |
|
979 // We must have child boxes and have decorations defined. |
|
980 tx += borderLeft() + paddingLeft(); |
|
981 |
|
982 Color underline, overline, linethrough; |
|
983 underline = overline = linethrough = styleToUse->visitedDependentColor(CSSPropertyColor); |
|
984 if (!parent()) |
|
985 renderer()->getTextDecorationColors(deco, underline, overline, linethrough); |
|
986 |
|
987 bool isPrinting = renderer()->document()->printing(); |
|
988 context->setStrokeThickness(1.0f); // FIXME: We should improve this rule and not always just assume 1. |
|
989 |
|
990 bool paintUnderline = deco & UNDERLINE && !paintedChildren; |
|
991 bool paintOverline = deco & OVERLINE && !paintedChildren; |
|
992 bool paintLineThrough = deco & LINE_THROUGH && paintedChildren; |
|
993 |
|
994 bool linesAreOpaque = !isPrinting && (!paintUnderline || underline.alpha() == 255) && (!paintOverline || overline.alpha() == 255) && (!paintLineThrough || linethrough.alpha() == 255); |
|
995 |
|
996 int baselinePos = renderer()->style(m_firstLine)->font().ascent(); |
|
997 if (!isRootInlineBox()) |
|
998 baselinePos += borderTop() + paddingTop(); |
|
999 |
|
1000 bool setClip = false; |
|
1001 int extraOffset = 0; |
|
1002 const ShadowData* shadow = styleToUse->textShadow(); |
|
1003 if (!linesAreOpaque && shadow && shadow->next()) { |
|
1004 IntRect clipRect(tx, ty, w, baselinePos + 2); |
|
1005 for (const ShadowData* s = shadow; s; s = s->next()) { |
|
1006 IntRect shadowRect(tx, ty, w, baselinePos + 2); |
|
1007 shadowRect.inflate(s->blur()); |
|
1008 shadowRect.move(s->x(), s->y()); |
|
1009 clipRect.unite(shadowRect); |
|
1010 extraOffset = max(extraOffset, max(0, s->y()) + s->blur()); |
|
1011 } |
|
1012 context->save(); |
|
1013 context->clip(clipRect); |
|
1014 extraOffset += baselinePos + 2; |
|
1015 ty += extraOffset; |
|
1016 setClip = true; |
|
1017 } |
|
1018 |
|
1019 ColorSpace colorSpace = renderer()->style()->colorSpace(); |
|
1020 bool setShadow = false; |
|
1021 do { |
|
1022 if (shadow) { |
|
1023 if (!shadow->next()) { |
|
1024 // The last set of lines paints normally inside the clip. |
|
1025 ty -= extraOffset; |
|
1026 extraOffset = 0; |
|
1027 } |
|
1028 context->setShadow(IntSize(shadow->x(), shadow->y() - extraOffset), shadow->blur(), shadow->color(), colorSpace); |
|
1029 setShadow = true; |
|
1030 shadow = shadow->next(); |
|
1031 } |
|
1032 |
|
1033 if (paintUnderline) { |
|
1034 context->setStrokeColor(underline, colorSpace); |
|
1035 context->setStrokeStyle(SolidStroke); |
|
1036 // Leave one pixel of white between the baseline and the underline. |
|
1037 context->drawLineForText(IntPoint(tx, ty + baselinePos + 1), w, isPrinting); |
|
1038 } |
|
1039 if (paintOverline) { |
|
1040 context->setStrokeColor(overline, colorSpace); |
|
1041 context->setStrokeStyle(SolidStroke); |
|
1042 context->drawLineForText(IntPoint(tx, ty), w, isPrinting); |
|
1043 } |
|
1044 if (paintLineThrough) { |
|
1045 context->setStrokeColor(linethrough, colorSpace); |
|
1046 context->setStrokeStyle(SolidStroke); |
|
1047 context->drawLineForText(IntPoint(tx, ty + 2 * baselinePos / 3), w, isPrinting); |
|
1048 } |
|
1049 } while (shadow); |
|
1050 |
|
1051 if (setClip) |
|
1052 context->restore(); |
|
1053 else if (setShadow) |
|
1054 context->clearShadow(); |
|
1055 } |
|
1056 } |
|
1057 |
|
1058 InlineBox* InlineFlowBox::firstLeafChild() const |
|
1059 { |
|
1060 InlineBox* leaf = 0; |
|
1061 for (InlineBox* child = firstChild(); child && !leaf; child = child->nextOnLine()) |
|
1062 leaf = child->isLeaf() ? child : static_cast<InlineFlowBox*>(child)->firstLeafChild(); |
|
1063 return leaf; |
|
1064 } |
|
1065 |
|
1066 InlineBox* InlineFlowBox::lastLeafChild() const |
|
1067 { |
|
1068 InlineBox* leaf = 0; |
|
1069 for (InlineBox* child = lastChild(); child && !leaf; child = child->prevOnLine()) |
|
1070 leaf = child->isLeaf() ? child : static_cast<InlineFlowBox*>(child)->lastLeafChild(); |
|
1071 return leaf; |
|
1072 } |
|
1073 |
|
1074 RenderObject::SelectionState InlineFlowBox::selectionState() |
|
1075 { |
|
1076 return RenderObject::SelectionNone; |
|
1077 } |
|
1078 |
|
1079 bool InlineFlowBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) |
|
1080 { |
|
1081 for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) { |
|
1082 if (!box->canAccommodateEllipsis(ltr, blockEdge, ellipsisWidth)) |
|
1083 return false; |
|
1084 } |
|
1085 return true; |
|
1086 } |
|
1087 |
|
1088 int InlineFlowBox::placeEllipsisBox(bool ltr, int blockLeftEdge, int blockRightEdge, int ellipsisWidth, bool& foundBox) |
|
1089 { |
|
1090 int result = -1; |
|
1091 // We iterate over all children, the foundBox variable tells us when we've found the |
|
1092 // box containing the ellipsis. All boxes after that one in the flow are hidden. |
|
1093 // If our flow is ltr then iterate over the boxes from left to right, otherwise iterate |
|
1094 // from right to left. Varying the order allows us to correctly hide the boxes following the ellipsis. |
|
1095 InlineBox *box = ltr ? firstChild() : lastChild(); |
|
1096 |
|
1097 // NOTE: these will cross after foundBox = true. |
|
1098 int visibleLeftEdge = blockLeftEdge; |
|
1099 int visibleRightEdge = blockRightEdge; |
|
1100 |
|
1101 while (box) { |
|
1102 int currResult = box->placeEllipsisBox(ltr, visibleLeftEdge, visibleRightEdge, ellipsisWidth, foundBox); |
|
1103 if (currResult != -1 && result == -1) |
|
1104 result = currResult; |
|
1105 |
|
1106 if (ltr) { |
|
1107 visibleLeftEdge += box->width(); |
|
1108 box = box->nextOnLine(); |
|
1109 } |
|
1110 else { |
|
1111 visibleRightEdge -= box->width(); |
|
1112 box = box->prevOnLine(); |
|
1113 } |
|
1114 } |
|
1115 return result; |
|
1116 } |
|
1117 |
|
1118 void InlineFlowBox::clearTruncation() |
|
1119 { |
|
1120 for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) |
|
1121 box->clearTruncation(); |
|
1122 } |
|
1123 |
|
1124 #ifndef NDEBUG |
|
1125 |
|
1126 void InlineFlowBox::checkConsistency() const |
|
1127 { |
|
1128 #ifdef CHECK_CONSISTENCY |
|
1129 ASSERT(!m_hasBadChildList); |
|
1130 const InlineBox* prev = 0; |
|
1131 for (const InlineBox* child = m_firstChild; child; child = child->nextOnLine()) { |
|
1132 ASSERT(child->parent() == this); |
|
1133 ASSERT(child->prevOnLine() == prev); |
|
1134 prev = child; |
|
1135 } |
|
1136 ASSERT(prev == m_lastChild); |
|
1137 #endif |
|
1138 } |
|
1139 |
|
1140 #endif |
|
1141 |
|
1142 } // namespace WebCore |