2 * Copyright (C) 2012 Victor Carbune (victor@rosedu.org)
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "core/layout/LayoutVTTCue.h"
29 #include "core/html/shadow/MediaControls.h"
30 #include "core/layout/LayoutInline.h"
31 #include "core/layout/LayoutState.h"
35 LayoutVTTCue::LayoutVTTCue(ContainerNode
* node
, float snapToLinesPosition
)
36 : LayoutBlockFlow(node
)
37 , m_snapToLinesPosition(snapToLinesPosition
)
41 class SnapToLinesLayouter
{
44 SnapToLinesLayouter(LayoutVTTCue
& cueBox
, const IntRect
& controlsRect
)
46 , m_controlsRect(controlsRect
)
53 bool isOutside() const;
54 bool isOverlapping() const;
55 LayoutUnit
computeInitialPositionAdjustment(LayoutUnit
&) const;
56 bool shouldSwitchDirection(InlineFlowBox
*, LayoutUnit
) const;
58 void moveBoxesBy(LayoutUnit distance
)
60 m_cueBox
.setLogicalTop(m_cueBox
.logicalTop() + distance
);
63 InlineFlowBox
* findFirstLineBox() const;
65 LayoutPoint m_specifiedPosition
;
66 LayoutVTTCue
& m_cueBox
;
67 IntRect m_controlsRect
;
70 InlineFlowBox
* SnapToLinesLayouter::findFirstLineBox() const
72 if (!m_cueBox
.firstChild()->isLayoutInline())
74 return toLayoutInline(m_cueBox
.firstChild())->firstLineBox();
77 LayoutUnit
SnapToLinesLayouter::computeInitialPositionAdjustment(LayoutUnit
& step
) const
79 ASSERT(std::isfinite(m_cueBox
.snapToLinesPosition()));
81 // 6. Let line be cue's computed line.
82 // 7. Round line to an integer by adding 0.5 and then flooring it.
83 LayoutUnit linePosition
= floorf(m_cueBox
.snapToLinesPosition() + 0.5f
);
85 WritingMode writingMode
= m_cueBox
.style()->writingMode();
86 // 8. Vertical Growing Left: Add one to line then negate it.
87 if (writingMode
== RightToLeftWritingMode
)
88 linePosition
= -(linePosition
+ 1);
90 // 9. Let position be the result of multiplying step and line offset.
91 LayoutUnit position
= step
* linePosition
;
93 // 10. Vertical Growing Left: Decrease position by the width of the
94 // bounding box of the boxes in boxes, then increase position by step.
95 if (writingMode
== RightToLeftWritingMode
) {
96 position
-= m_cueBox
.size().width();
100 // 11. If line is less than zero...
101 if (linePosition
< 0) {
102 LayoutBlock
* parentBlock
= m_cueBox
.containingBlock();
104 // ... then increase position by max dimension ...
105 position
+= blink::isHorizontalWritingMode(writingMode
) ? parentBlock
->size().height() : parentBlock
->size().width();
107 // ... and negate step.
113 bool SnapToLinesLayouter::isOutside() const
115 return !m_cueBox
.containingBlock()->absoluteBoundingBoxRect().contains(m_cueBox
.absoluteContentBox());
118 bool SnapToLinesLayouter::isOverlapping() const
120 IntRect cueBoxRect
= m_cueBox
.absoluteBoundingBoxRect();
121 for (LayoutObject
* box
= m_cueBox
.previousSibling(); box
; box
= box
->previousSibling()) {
122 IntRect boxRect
= box
->absoluteBoundingBoxRect();
124 if (cueBoxRect
.intersects(boxRect
))
128 if (cueBoxRect
.intersects(m_controlsRect
))
134 bool SnapToLinesLayouter::shouldSwitchDirection(InlineFlowBox
* firstLineBox
, LayoutUnit step
) const
136 // 17. Horizontal: If step is negative and the top of the first line box in
137 // boxes is now above the top of the title area, or if step is positive and
138 // the bottom of the first line box in boxes is now below the bottom of the
139 // title area, jump to the step labeled switch direction.
140 // Vertical: If step is negative and the left edge of the first line
141 // box in boxes is now to the left of the left edge of the title area, or
142 // if step is positive and the right edge of the first line box in boxes is
143 // now to the right of the right edge of the title area, jump to the step
144 // labeled switch direction.
145 LayoutUnit logicalTop
= m_cueBox
.logicalTop();
146 if (step
< 0 && logicalTop
< 0)
148 if (step
> 0 && logicalTop
+ firstLineBox
->logicalHeight() > m_cueBox
.containingBlock()->logicalHeight())
153 void SnapToLinesLayouter::layout()
155 // http://dev.w3.org/html5/webvtt/#dfn-apply-webvtt-cue-settings
156 // Step 13, "If cue's text track cue snap-to-lines flag is set".
158 InlineFlowBox
* firstLineBox
= findFirstLineBox();
162 // Steps 1-3 skipped.
163 // 4. Horizontal: Let step be the height of the first line box in boxes.
164 // Vertical: Let step be the width of the first line box in boxes.
165 LayoutUnit step
= firstLineBox
->logicalHeight();
167 // 5. If step is zero, then jump to the step labeled done positioning below.
172 LayoutUnit positionAdjustment
= computeInitialPositionAdjustment(step
);
174 // 12. Move all boxes in boxes ...
175 // Horizontal: ... down by the distance given by position
176 // Vertical: ... right by the distance given by position
177 moveBoxesBy(positionAdjustment
);
179 // 13. Remember the position of all the boxes in boxes as their specified
181 m_specifiedPosition
= m_cueBox
.location();
183 // XX. Let switched be false.
184 bool switched
= false;
186 // Step 14 skipped. (margin == 0; title area == video area)
188 // 15. Step loop: If none of the boxes in boxes would overlap any of the
189 // boxes in output, and all of the boxes in output are entirely within the
190 // title area box, then jump to the step labeled done positioning below.
191 while (isOutside() || isOverlapping()) {
192 // 16. Let current position score be the percentage of the area of the
193 // bounding box of the boxes in boxes that is outside the title area
195 if (!shouldSwitchDirection(firstLineBox
, step
)) {
196 // 18. Horizontal: Move all the boxes in boxes down by the distance
197 // given by step. (If step is negative, then this will actually
198 // result in an upwards movement of the boxes in absolute terms.)
199 // Vertical: Move all the boxes in boxes right by the distance
200 // given by step. (If step is negative, then this will actually
201 // result in a leftwards movement of the boxes in absolute terms.)
204 // 19. Jump back to the step labeled step loop.
208 // 20. Switch direction: If switched is true, then remove all the boxes
209 // in boxes, and jump to the step labeled done positioning below.
211 // This does not "remove" the boxes, but rather just pushes them
212 // out of the viewport. Otherwise we'd need to mutate the layout
213 // tree during layout.
214 m_cueBox
.setLogicalTop(m_cueBox
.containingBlock()->logicalHeight() + 1);
218 // 21. Otherwise, move all the boxes in boxes back to their specified
219 // position as determined in the earlier step.
220 m_cueBox
.setLocation(m_specifiedPosition
);
225 // 23. Set switched to true.
228 // 24. Jump back to the step labeled step loop.
232 void LayoutVTTCue::repositionCueSnapToLinesNotSet()
234 // FIXME: Implement overlapping detection when snap-to-lines is not set. http://wkb.ug/84296
236 // http://dev.w3.org/html5/webvtt/#dfn-apply-webvtt-cue-settings
237 // Step 13, "If cue's text track cue snap-to-lines flag is not set".
239 // 1. Let bounding box be the bounding box of the boxes in boxes.
241 // 2. Run the appropriate steps from the following list:
242 // If the text track cue writing direction is horizontal
243 // If the text track cue line alignment is middle alignment
244 // Move all the boxes in boxes up by half of the height of
246 // If the text track cue line alignment is end alignment
247 // Move all the boxes in boxes up by the height of bounding box.
249 // If the text track cue writing direction is vertical growing left or
250 // vertical growing right
251 // If the text track cue line alignment is middle alignment
252 // Move all the boxes in boxes left by half of the width of
254 // If the text track cue line alignment is end alignment
255 // Move all the boxes in boxes left by the width of bounding box.
257 // 3. If none of the boxes in boxes would overlap any of the boxes in
258 // output, and all the boxes in output are within the video's rendering
259 // area, then jump to the step labeled done positioning below.
261 // 4. If there is a position to which the boxes in boxes can be moved while
262 // maintaining the relative positions of the boxes in boxes to each other
263 // such that none of the boxes in boxes would overlap any of the boxes in
264 // output, and all the boxes in output would be within the video's
265 // rendering area, then move the boxes in boxes to the closest such
266 // position to their current position, and then jump to the step labeled
267 // done positioning below. If there are multiple such positions that are
268 // equidistant from their current position, use the highest one amongst
269 // them; if there are several at that height, then use the leftmost one
272 // 5. Otherwise, jump to the step labeled done positioning below. (The
273 // boxes will unfortunately overlap.)
276 void LayoutVTTCue::layout()
278 LayoutBlockFlow::layout();
280 ASSERT(firstChild());
282 LayoutState
state(*this, locationOffset());
284 // Determine the area covered by the media controls, if any. If the controls
285 // are present, they are the next sibling of the text track container, which
286 // is our parent. (LayoutMedia ensures that the media controls are laid out
287 // before text tracks, so that the layout is up-to-date here.)
288 ASSERT(parent()->node()->isTextTrackContainer());
289 IntRect controlsRect
;
290 if (LayoutObject
* parentSibling
= parent()->nextSibling()) {
291 // Only a part of the media controls is used for overlap avoidance.
292 MediaControls
* controls
= toMediaControls(parentSibling
->node());
293 if (LayoutObject
* controlsLayout
= controls
->layoutObjectForTextTrackLayout())
294 controlsRect
= controlsLayout
->absoluteBoundingBoxRect();
297 // http://dev.w3.org/html5/webvtt/#dfn-apply-webvtt-cue-settings - step 13.
298 if (!std::isnan(m_snapToLinesPosition
))
299 SnapToLinesLayouter(*this, controlsRect
).layout();
301 repositionCueSnapToLinesNotSet();