Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / core / layout / LayoutVTTCue.cpp
blob319a5b6a2a6280685ad553bf350aed1e65fcdc79
1 /*
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
6 * are met:
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.
26 #include "config.h"
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"
33 namespace blink {
35 LayoutVTTCue::LayoutVTTCue(ContainerNode* node, float snapToLinesPosition)
36 : LayoutBlockFlow(node)
37 , m_snapToLinesPosition(snapToLinesPosition)
41 class SnapToLinesLayouter {
42 STACK_ALLOCATED();
43 public:
44 SnapToLinesLayouter(LayoutVTTCue& cueBox, const IntRect& controlsRect)
45 : m_cueBox(cueBox)
46 , m_controlsRect(controlsRect)
50 void layout();
52 private:
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())
73 return nullptr;
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();
97 position += step;
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.
108 step = -step;
110 return position;
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))
125 return true;
128 if (cueBoxRect.intersects(m_controlsRect))
129 return true;
131 return false;
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)
147 return true;
148 if (step > 0 && logicalTop + firstLineBox->logicalHeight() > m_cueBox.containingBlock()->logicalHeight())
149 return true;
150 return false;
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();
159 if (!firstLineBox)
160 return;
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.
168 if (!step)
169 return;
171 // Steps 6-11.
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
180 // position.
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
194 // box.
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.)
202 moveBoxesBy(step);
204 // 19. Jump back to the step labeled step loop.
205 continue;
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.
210 if (switched) {
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);
215 break;
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);
222 // 22. Negate step.
223 step = -step;
225 // 23. Set switched to true.
226 switched = 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
245 // bounding box.
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
253 // bounding box.
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
270 // amongst them.
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();
300 else
301 repositionCueSnapToLinesNotSet();
304 } // namespace blink