Add WTF::move()
[WebKit-https.git] / Source / WebCore / rendering / RenderVTTCue.cpp
1 /*
2  * Copyright (C) 2012 Victor Carbune (victor@rosedu.org)
3  * Copyright (C) 2014 Apple Inc.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28
29 #if ENABLE(VIDEO_TRACK)
30 #include "RenderVTTCue.h"
31
32 #include "RenderView.h"
33 #include "TextTrackCueGeneric.h"
34 #include "VTTCue.h"
35 #include <wtf/StackStats.h>
36
37 namespace WebCore {
38
39 RenderVTTCue::RenderVTTCue(VTTCueBox& element, PassRef<RenderStyle> style)
40     : RenderBlockFlow(element, WTF::move(style))
41     , m_cue(element.getCue())
42 {
43 }
44
45 void RenderVTTCue::layout()
46 {
47     StackStats::LayoutCheckPoint layoutCheckPoint;
48     RenderBlockFlow::layout();
49
50 #if ENABLE(WEBVTT_REGIONS)
51     // If WebVTT Regions are used, the regular WebVTT layout algorithm is no
52     // longer necessary, since cues having the region parameter set do not have
53     // any positioning parameters. Also, in this case, the regions themselves
54     // have positioning information.
55     if (!m_cue->regionId().isEmpty())
56         return;
57 #endif
58
59     LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
60     
61     if (m_cue->cueType()== TextTrackCue::WebVTT) {
62         if (toVTTCue(m_cue)->snapToLines())
63             repositionCueSnapToLinesSet();
64         else
65             repositionCueSnapToLinesNotSet();
66     } else
67         repositionGenericCue();
68
69     statePusher.pop();
70 }
71
72 bool RenderVTTCue::initializeLayoutParameters(InlineFlowBox*& firstLineBox, LayoutUnit& step, LayoutUnit& position)
73 {
74     ASSERT(firstChild());
75
76     RenderBlock* parentBlock = containingBlock();
77     firstLineBox = toRenderInline(firstChild())->firstLineBox();
78     if (!firstLineBox)
79         firstLineBox = this->firstRootBox();
80
81     // 1. Horizontal: Let step be the height of the first line box in boxes.
82     //    Vertical: Let step be the width of the first line box in boxes.
83     step = m_cue->getWritingDirection() == VTTCue::Horizontal ? firstLineBox->height() : firstLineBox->width();
84
85     // 2. If step is zero, then jump to the step labeled done positioning below.
86     if (!step)
87         return false;
88
89     // 3. Let line position be the text track cue computed line position.
90     int linePosition = m_cue->calculateComputedLinePosition();
91
92     // 4. Vertical Growing Left: Add one to line position then negate it.
93     if (m_cue->getWritingDirection() == VTTCue::VerticalGrowingLeft)
94         linePosition = -(linePosition + 1);
95
96     // 5. Let position be the result of multiplying step and line position.
97     position = step * linePosition;
98
99     // 6. Vertical Growing Left: Decrease position by the width of the
100     // bounding box of the boxes in boxes, then increase position by step.
101     if (m_cue->getWritingDirection() == VTTCue::VerticalGrowingLeft) {
102         position -= width();
103         position += step;
104     }
105
106     // 7. If line position is less than zero...
107     if (linePosition < 0) {
108         // Horizontal / Vertical: ... then increase position by the
109         // height / width of the video's rendering area ...
110         position += m_cue->getWritingDirection() == VTTCue::Horizontal ? parentBlock->height() : parentBlock->width();
111
112         // ... and negate step.
113         step = -step;
114     }
115
116     return true;
117 }
118
119 void RenderVTTCue::placeBoxInDefaultPosition(LayoutUnit position, bool& switched)
120 {
121     // 8. Move all boxes in boxes ...
122     if (m_cue->getWritingDirection() == VTTCue::Horizontal)
123         // Horizontal: ... down by the distance given by position
124         setY(y() + position);
125     else
126         // Vertical: ... right by the distance given by position
127         setX(x() + position);
128
129     // 9. Default: Remember the position of all the boxes in boxes as their
130     // default position.
131     m_fallbackPosition = FloatPoint(x(), y());
132
133     // 10. Let switched be false.
134     switched = false;
135 }
136
137 bool RenderVTTCue::isOutside() const
138 {
139     return !rectIsWithinContainer(absoluteContentBox());
140 }
141
142 bool RenderVTTCue::rectIsWithinContainer(const IntRect& rect) const
143 {
144     return containingBlock()->absoluteBoundingBoxRect().contains(rect);
145 }
146
147
148 bool RenderVTTCue::isOverlapping() const
149 {
150     return overlappingObject();
151 }
152
153 RenderObject* RenderVTTCue::overlappingObject() const
154 {
155     return overlappingObjectForRect(absoluteBoundingBoxRect());
156 }
157
158 RenderObject* RenderVTTCue::overlappingObjectForRect(const IntRect& rect) const
159 {
160     for (RenderObject* box = previousSibling(); box; box = box->previousSibling()) {
161         IntRect boxRect = box->absoluteBoundingBoxRect();
162
163         if (rect.intersects(boxRect))
164             return box;
165     }
166
167     return 0;
168 }
169
170 bool RenderVTTCue::shouldSwitchDirection(InlineFlowBox* firstLineBox, LayoutUnit step) const
171 {
172     LayoutUnit top = y();
173     LayoutUnit left = x();
174     LayoutUnit bottom = top + firstLineBox->height();
175     LayoutUnit right = left + firstLineBox->width();
176
177     // 12. Horizontal: If step is negative and the top of the first line
178     // box in boxes is now above the top of the video's rendering area,
179     // or if step is positive and the bottom of the first line box in
180     // boxes is now below the bottom of the video's rendering area, jump
181     // to the step labeled switch direction.
182     LayoutUnit parentHeight = containingBlock()->height();
183     if (m_cue->getWritingDirection() == VTTCue::Horizontal && ((step < 0 && top < 0) || (step > 0 && bottom > parentHeight)))
184         return true;
185
186     // 12. Vertical: If step is negative and the left edge of the first line
187     // box in boxes is now to the left of the left edge of the video's
188     // rendering area, or if step is positive and the right edge of the
189     // first line box in boxes is now to the right of the right edge of
190     // the video's rendering area, jump to the step labeled switch direction.
191     LayoutUnit parentWidth = containingBlock()->width();
192     if (m_cue->getWritingDirection() != VTTCue::Horizontal && ((step < 0 && left < 0) || (step > 0 && right > parentWidth)))
193         return true;
194
195     return false;
196 }
197
198 void RenderVTTCue::moveBoxesByStep(LayoutUnit step)
199 {
200     // 13. Horizontal: Move all the boxes in boxes down by the distance
201     // given by step. (If step is negative, then this will actually
202     // result in an upwards movement of the boxes in absolute terms.)
203     if (m_cue->getWritingDirection() == VTTCue::Horizontal)
204         setY(y() + step);
205
206     // 13. Vertical: Move all the boxes in boxes right by the distance
207     // given by step. (If step is negative, then this will actually
208     // result in a leftwards movement of the boxes in absolute terms.)
209     else
210         setX(x() + step);
211 }
212
213 bool RenderVTTCue::switchDirection(bool& switched, LayoutUnit& step)
214 {
215     // 15. Switch direction: Move all the boxes in boxes back to their
216     // default position as determined in the step above labeled default.
217     setX(m_fallbackPosition.x());
218     setY(m_fallbackPosition.y());
219
220     // 16. If switched is true, jump to the step labeled done
221     // positioning below.
222     if (switched)
223         return false;
224
225     // 17. Negate step.
226     step = -step;
227
228     // 18. Set switched to true.
229     switched = true;
230     return true;
231 }
232
233 void RenderVTTCue::moveIfNecessaryToKeepWithinContainer()
234 {
235     IntRect containerRect = containingBlock()->absoluteBoundingBoxRect();
236     IntRect cueRect = absoluteBoundingBoxRect();
237
238     int topOverflow = cueRect.y() - containerRect.y();
239     int bottomOverflow = containerRect.maxY() - cueRect.maxY();
240
241     int verticalAdjustment = 0;
242     if (topOverflow < 0)
243         verticalAdjustment = -topOverflow;
244     else if (bottomOverflow < 0)
245         verticalAdjustment = bottomOverflow;
246
247     if (verticalAdjustment)
248         setY(y() + verticalAdjustment);
249
250     int leftOverflow = cueRect.x() - containerRect.x();
251     int rightOverflow = containerRect.maxX() - cueRect.maxX();
252
253     int horizontalAdjustment = 0;
254     if (leftOverflow < 0)
255         horizontalAdjustment = -leftOverflow;
256     else if (rightOverflow < 0)
257         horizontalAdjustment = rightOverflow;
258
259     if (horizontalAdjustment)
260         setX(x() + horizontalAdjustment);
261 }
262
263 bool RenderVTTCue::findNonOverlappingPosition(int& newX, int& newY) const
264 {
265     newX = x();
266     newY = y();
267     IntRect srcRect = absoluteBoundingBoxRect();
268     IntRect destRect = srcRect;
269
270     // Move the box up, looking for a non-overlapping position:
271     while (RenderObject* box = overlappingObjectForRect(destRect)) {
272         if (m_cue->getWritingDirection() == VTTCue::Horizontal)
273             destRect.setY(box->absoluteBoundingBoxRect().y() - destRect.height());
274         else
275             destRect.setX(box->absoluteBoundingBoxRect().x() - destRect.width());
276     }
277
278     if (rectIsWithinContainer(destRect)) {
279         newX += destRect.x() - srcRect.x();
280         newY += destRect.y() - srcRect.y();
281         return true;
282     }
283
284     destRect = srcRect;
285
286     // Move the box down, looking for a non-overlapping position:
287     while (RenderObject* box = overlappingObjectForRect(destRect)) {
288         if (m_cue->getWritingDirection() == VTTCue::Horizontal)
289             destRect.setY(box->absoluteBoundingBoxRect().maxY());
290         else
291             destRect.setX(box->absoluteBoundingBoxRect().maxX());
292     }
293
294     if (rectIsWithinContainer(destRect)) {
295         newX += destRect.x() - srcRect.x();
296         newY += destRect.y() - srcRect.y();
297         return true;
298     }
299
300     return false;
301 }
302
303 void RenderVTTCue::repositionCueSnapToLinesSet()
304 {
305     InlineFlowBox* firstLineBox;
306     LayoutUnit step;
307     LayoutUnit position;
308     if (!initializeLayoutParameters(firstLineBox, step, position))
309         return;
310
311     bool switched;
312     placeBoxInDefaultPosition(position, switched);
313
314     // 11. Step loop: If none of the boxes in boxes would overlap any of the boxes
315     // in output and all the boxes in output are within the video's rendering area
316     // then jump to the step labeled done positioning.
317     while (isOutside() || isOverlapping()) {
318         if (!shouldSwitchDirection(firstLineBox, step))
319             // 13. Move all the boxes in boxes ...
320             // 14. Jump back to the step labeled step loop.
321             moveBoxesByStep(step);
322         else if (!switchDirection(switched, step))
323             break;
324
325         // 19. Jump back to the step labeled step loop.
326     }
327
328     // Acommodate extra top and bottom padding, border or margin.
329     // Note: this is supported only for internal UA styling, not through the cue selector.
330     if (hasInlineDirectionBordersPaddingOrMargin())
331         moveIfNecessaryToKeepWithinContainer();
332 }
333
334 void RenderVTTCue::repositionGenericCue()
335 {
336     ASSERT(firstChild());
337     InlineFlowBox* firstLineBox = toRenderInline(firstChild())->firstLineBox();
338     if (static_cast<TextTrackCueGeneric*>(m_cue)->useDefaultPosition() && firstLineBox) {
339         LayoutUnit parentWidth = containingBlock()->logicalWidth();
340         LayoutUnit width = firstLineBox->width();
341         LayoutUnit right = (parentWidth / 2) - (width / 2);
342         setX(right);
343     }
344     repositionCueSnapToLinesNotSet();
345 }
346
347 void RenderVTTCue::repositionCueSnapToLinesNotSet()
348 {
349     // 3. If none of the boxes in boxes would overlap any of the boxes in output, and all the boxes in
350     // output are within the video's rendering area, then jump to the step labeled done positioning below.
351     if (!isOutside() && !isOverlapping())
352         return;
353
354     // 4. If there is a position to which the boxes in boxes can be moved while maintaining the relative
355     // positions of the boxes in boxes to each other such that none of the boxes in boxes would overlap
356     // any of the boxes in output, and all the boxes in output would be within the video's rendering area,
357     // then move the boxes in boxes to the closest such position to their current position, and then jump
358     // to the step labeled done positioning below. If there are multiple such positions that are equidistant
359     // from their current position, use the highest one amongst them; if there are several at that height,
360     // then use the leftmost one amongst them.
361     moveIfNecessaryToKeepWithinContainer();
362     int x = 0;
363     int y = 0;
364     if (!findNonOverlappingPosition(x, y))
365         return;
366
367     setX(x);
368     setY(y);
369 }
370
371 } // namespace WebCore
372
373 #endif