Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / rendering / RenderLineBoxList.cpp
1 /*
2  * Copyright (C) 2008 Apple 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
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "core/rendering/RenderLineBoxList.h"
31
32 #include "core/rendering/HitTestResult.h"
33 #include "core/rendering/InlineTextBox.h"
34 #include "core/rendering/PaintInfo.h"
35 #include "core/rendering/RenderInline.h"
36 #include "core/rendering/RenderView.h"
37 #include "core/rendering/RootInlineBox.h"
38
39 namespace blink {
40
41 #if ENABLE(ASSERT)
42 RenderLineBoxList::~RenderLineBoxList()
43 {
44     ASSERT(!m_firstLineBox);
45     ASSERT(!m_lastLineBox);
46 }
47 #endif
48
49 void RenderLineBoxList::appendLineBox(InlineFlowBox* box)
50 {
51     checkConsistency();
52
53     if (!m_firstLineBox)
54         m_firstLineBox = m_lastLineBox = box;
55     else {
56         m_lastLineBox->setNextLineBox(box);
57         box->setPreviousLineBox(m_lastLineBox);
58         m_lastLineBox = box;
59     }
60
61     checkConsistency();
62 }
63
64 void RenderLineBoxList::deleteLineBoxTree()
65 {
66     InlineFlowBox* line = m_firstLineBox;
67     InlineFlowBox* nextLine;
68     while (line) {
69         nextLine = line->nextLineBox();
70         line->deleteLine();
71         line = nextLine;
72     }
73     m_firstLineBox = m_lastLineBox = 0;
74 }
75
76 void RenderLineBoxList::extractLineBox(InlineFlowBox* box)
77 {
78     checkConsistency();
79
80     m_lastLineBox = box->prevLineBox();
81     if (box == m_firstLineBox)
82         m_firstLineBox = 0;
83     if (box->prevLineBox())
84         box->prevLineBox()->setNextLineBox(0);
85     box->setPreviousLineBox(0);
86     for (InlineFlowBox* curr = box; curr; curr = curr->nextLineBox())
87         curr->setExtracted();
88
89     checkConsistency();
90 }
91
92 void RenderLineBoxList::attachLineBox(InlineFlowBox* box)
93 {
94     checkConsistency();
95
96     if (m_lastLineBox) {
97         m_lastLineBox->setNextLineBox(box);
98         box->setPreviousLineBox(m_lastLineBox);
99     } else
100         m_firstLineBox = box;
101     InlineFlowBox* last = box;
102     for (InlineFlowBox* curr = box; curr; curr = curr->nextLineBox()) {
103         curr->setExtracted(false);
104         last = curr;
105     }
106     m_lastLineBox = last;
107
108     checkConsistency();
109 }
110
111 void RenderLineBoxList::removeLineBox(InlineFlowBox* box)
112 {
113     checkConsistency();
114
115     if (box == m_firstLineBox)
116         m_firstLineBox = box->nextLineBox();
117     if (box == m_lastLineBox)
118         m_lastLineBox = box->prevLineBox();
119     if (box->nextLineBox())
120         box->nextLineBox()->setPreviousLineBox(box->prevLineBox());
121     if (box->prevLineBox())
122         box->prevLineBox()->setNextLineBox(box->nextLineBox());
123
124     checkConsistency();
125 }
126
127 void RenderLineBoxList::deleteLineBoxes()
128 {
129     if (m_firstLineBox) {
130         InlineFlowBox* next;
131         for (InlineFlowBox* curr = m_firstLineBox; curr; curr = next) {
132             next = curr->nextLineBox();
133             curr->destroy();
134         }
135         m_firstLineBox = 0;
136         m_lastLineBox = 0;
137     }
138 }
139
140 void RenderLineBoxList::dirtyLineBoxes()
141 {
142     for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox())
143         curr->dirtyLineBoxes();
144 }
145
146 bool RenderLineBoxList::rangeIntersectsRect(RenderBoxModelObject* renderer, LayoutUnit logicalTop, LayoutUnit logicalBottom, const LayoutRect& rect, const LayoutPoint& offset) const
147 {
148     RenderBox* block;
149     if (renderer->isBox())
150         block = toRenderBox(renderer);
151     else
152         block = renderer->containingBlock();
153     LayoutUnit physicalStart = block->flipForWritingMode(logicalTop);
154     LayoutUnit physicalEnd = block->flipForWritingMode(logicalBottom);
155     LayoutUnit physicalExtent = absoluteValue(physicalEnd - physicalStart);
156     physicalStart = std::min(physicalStart, physicalEnd);
157
158     if (renderer->style()->isHorizontalWritingMode()) {
159         physicalStart += offset.y();
160         if (physicalStart >= rect.maxY() || physicalStart + physicalExtent <= rect.y())
161             return false;
162     } else {
163         physicalStart += offset.x();
164         if (physicalStart >= rect.maxX() || physicalStart + physicalExtent <= rect.x())
165             return false;
166     }
167
168     return true;
169 }
170
171 bool RenderLineBoxList::anyLineIntersectsRect(RenderBoxModelObject* renderer, const LayoutRect& rect, const LayoutPoint& offset) const
172 {
173     // We can check the first box and last box and avoid painting/hit testing if we don't
174     // intersect.  This is a quick short-circuit that we can take to avoid walking any lines.
175     // FIXME: This check is flawed in the following extremely obscure way:
176     // if some line in the middle has a huge overflow, it might actually extend below the last line.
177     RootInlineBox& firstRootBox = firstLineBox()->root();
178     RootInlineBox& lastRootBox = lastLineBox()->root();
179     LayoutUnit firstLineTop = firstLineBox()->logicalTopVisualOverflow(firstRootBox.lineTop());
180     LayoutUnit lastLineBottom = lastLineBox()->logicalBottomVisualOverflow(lastRootBox.lineBottom());
181
182     return rangeIntersectsRect(renderer, firstLineTop, lastLineBottom, rect, offset);
183 }
184
185 bool RenderLineBoxList::lineIntersectsDirtyRect(RenderBoxModelObject* renderer, InlineFlowBox* box, const PaintInfo& paintInfo, const LayoutPoint& offset) const
186 {
187     RootInlineBox& root = box->root();
188     LayoutUnit logicalTop = std::min<LayoutUnit>(box->logicalTopVisualOverflow(root.lineTop()), root.selectionTop());
189     LayoutUnit logicalBottom = box->logicalBottomVisualOverflow(root.lineBottom());
190
191     return rangeIntersectsRect(renderer, logicalTop, logicalBottom, paintInfo.rect, offset);
192 }
193
194 void RenderLineBoxList::paint(RenderBoxModelObject* renderer, PaintInfo& paintInfo, const LayoutPoint& paintOffset) const
195 {
196     // Only paint during the foreground/selection phases.
197     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseOutline
198         && paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines && paintInfo.phase != PaintPhaseTextClip
199         && paintInfo.phase != PaintPhaseMask)
200         return;
201
202     ASSERT(renderer->isRenderBlock() || (renderer->isRenderInline() && renderer->hasLayer())); // The only way an inline could paint like this is if it has a layer.
203
204     // If we have no lines then we have no work to do.
205     if (!firstLineBox())
206         return;
207
208     if (!anyLineIntersectsRect(renderer, paintInfo.rect, paintOffset))
209         return;
210
211     PaintInfo info(paintInfo);
212     ListHashSet<RenderInline*> outlineObjects;
213     info.setOutlineObjects(&outlineObjects);
214
215     // See if our root lines intersect with the dirty rect.  If so, then we paint
216     // them.  Note that boxes can easily overlap, so we can't make any assumptions
217     // based off positions of our first line box or our last line box.
218     for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
219         if (lineIntersectsDirtyRect(renderer, curr, info, paintOffset)) {
220             RootInlineBox& root = curr->root();
221             curr->paint(info, paintOffset, root.lineTop(), root.lineBottom());
222         }
223     }
224
225     if (info.phase == PaintPhaseOutline || info.phase == PaintPhaseSelfOutline || info.phase == PaintPhaseChildOutlines) {
226         ListHashSet<RenderInline*>::iterator end = info.outlineObjects()->end();
227         for (ListHashSet<RenderInline*>::iterator it = info.outlineObjects()->begin(); it != end; ++it) {
228             RenderInline* flow = *it;
229             flow->paintOutline(info, paintOffset);
230         }
231         info.outlineObjects()->clear();
232     }
233 }
234
235 bool RenderLineBoxList::hitTest(RenderBoxModelObject* renderer, const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) const
236 {
237     if (hitTestAction != HitTestForeground)
238         return false;
239
240     ASSERT(renderer->isRenderBlock() || (renderer->isRenderInline() && renderer->hasLayer())); // The only way an inline could hit test like this is if it has a layer.
241
242     // If we have no lines then we have no work to do.
243     if (!firstLineBox())
244         return false;
245
246     LayoutPoint point = locationInContainer.point();
247     LayoutRect rect = firstLineBox()->isHorizontal() ?
248         IntRect(point.x(), point.y() - locationInContainer.topPadding(), 1, locationInContainer.topPadding() + locationInContainer.bottomPadding() + 1) :
249         IntRect(point.x() - locationInContainer.leftPadding(), point.y(), locationInContainer.rightPadding() + locationInContainer.leftPadding() + 1, 1);
250
251     if (!anyLineIntersectsRect(renderer, rect, accumulatedOffset))
252         return false;
253
254     // See if our root lines contain the point.  If so, then we hit test
255     // them further.  Note that boxes can easily overlap, so we can't make any assumptions
256     // based off positions of our first line box or our last line box.
257     for (InlineFlowBox* curr = lastLineBox(); curr; curr = curr->prevLineBox()) {
258         RootInlineBox& root = curr->root();
259         if (rangeIntersectsRect(renderer, curr->logicalTopVisualOverflow(root.lineTop()), curr->logicalBottomVisualOverflow(root.lineBottom()), rect, accumulatedOffset)) {
260             bool inside = curr->nodeAtPoint(request, result, locationInContainer, accumulatedOffset, root.lineTop(), root.lineBottom());
261             if (inside) {
262                 renderer->updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset));
263                 return true;
264             }
265         }
266     }
267
268     return false;
269 }
270
271 void RenderLineBoxList::dirtyLinesFromChangedChild(RenderObject* container, RenderObject* child)
272 {
273     if (!container->parent() || (container->isRenderBlock() && (container->selfNeedsLayout() || !container->isRenderBlockFlow())))
274         return;
275
276     RenderInline* inlineContainer = container->isRenderInline() ? toRenderInline(container) : 0;
277     InlineBox* firstBox = inlineContainer ? inlineContainer->firstLineBoxIncludingCulling() : firstLineBox();
278
279     // If we have no first line box, then just bail early.
280     if (!firstBox) {
281         // For an empty inline, go ahead and propagate the check up to our parent, unless the parent
282         // is already dirty.
283         if (container->isInline() && !container->ancestorLineBoxDirty()) {
284             container->parent()->dirtyLinesFromChangedChild(container);
285             container->setAncestorLineBoxDirty(); // Mark the container to avoid dirtying the same lines again across multiple destroy() calls of the same subtree.
286         }
287         return;
288     }
289
290     // Try to figure out which line box we belong in.  First try to find a previous
291     // line box by examining our siblings.  If we didn't find a line box, then use our
292     // parent's first line box.
293     RootInlineBox* box = 0;
294     RenderObject* curr = 0;
295     ListHashSet<RenderObject*, 16> potentialLineBreakObjects;
296     potentialLineBreakObjects.add(child);
297     for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) {
298         potentialLineBreakObjects.add(curr);
299
300         if (curr->isFloatingOrOutOfFlowPositioned())
301             continue;
302
303         if (curr->isReplaced()) {
304             InlineBox* wrapper = toRenderBox(curr)->inlineBoxWrapper();
305             if (wrapper)
306                 box = &wrapper->root();
307         } else if (curr->isText()) {
308             InlineTextBox* textBox = toRenderText(curr)->lastTextBox();
309             if (textBox)
310                 box = &textBox->root();
311         } else if (curr->isRenderInline()) {
312             InlineBox* lastSiblingBox = toRenderInline(curr)->lastLineBoxIncludingCulling();
313             if (lastSiblingBox)
314                 box = &lastSiblingBox->root();
315         }
316
317         if (box)
318             break;
319     }
320     if (!box) {
321         if (inlineContainer && !inlineContainer->alwaysCreateLineBoxes()) {
322             // https://bugs.webkit.org/show_bug.cgi?id=60778
323             // We may have just removed a <br> with no line box that was our first child. In this case
324             // we won't find a previous sibling, but firstBox can be pointing to a following sibling.
325             // This isn't good enough, since we won't locate the root line box that encloses the removed
326             // <br>. We have to just over-invalidate a bit and go up to our parent.
327             if (!inlineContainer->ancestorLineBoxDirty()) {
328                 inlineContainer->parent()->dirtyLinesFromChangedChild(inlineContainer);
329                 inlineContainer->setAncestorLineBoxDirty(); // Mark the container to avoid dirtying the same lines again across multiple destroy() calls of the same subtree.
330             }
331             return;
332         }
333         box = &firstBox->root();
334     }
335
336     // If we found a line box, then dirty it.
337     if (box) {
338         RootInlineBox* adjacentBox;
339         box->markDirty();
340
341         // dirty the adjacent lines that might be affected
342         // NOTE: we dirty the previous line because RootInlineBox objects cache
343         // the address of the first object on the next line after a BR, which we may be
344         // invalidating here.  For more info, see how RenderBlock::layoutInlineChildren
345         // calls setLineBreakInfo with the result of findNextLineBreak.  findNextLineBreak,
346         // despite the name, actually returns the first RenderObject after the BR.
347         // <rdar://problem/3849947> "Typing after pasting line does not appear until after window resize."
348         adjacentBox = box->prevRootBox();
349         if (adjacentBox)
350             adjacentBox->markDirty();
351         adjacentBox = box->nextRootBox();
352         // If |child| or any of its immediately previous siblings with culled lineboxes is the object after a line-break in |box| or the linebox after it
353         // then that means |child| actually sits on the linebox after |box| (or is its line-break object) and so we need to dirty it as well.
354         if (adjacentBox && (potentialLineBreakObjects.contains(box->lineBreakObj()) || potentialLineBreakObjects.contains(adjacentBox->lineBreakObj()) || child->isBR() || isIsolated(container->style()->unicodeBidi())))
355             adjacentBox->markDirty();
356     }
357 }
358
359 #if ENABLE(ASSERT)
360
361 void RenderLineBoxList::checkConsistency() const
362 {
363 #ifdef CHECK_CONSISTENCY
364     const InlineFlowBox* prev = 0;
365     for (const InlineFlowBox* child = m_firstLineBox; child != 0; child = child->nextLineBox()) {
366         ASSERT(child->prevLineBox() == prev);
367         prev = child;
368     }
369     ASSERT(prev == m_lastLineBox);
370 #endif
371 }
372
373 #endif
374
375 }