1 /* 2 * Copyright (C) 2012 Victor Carbune (victor (at) rosedu.org) 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 * 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. 12 * 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. 24 */ 25 26 #include "config.h" 27 #include "core/rendering/RenderVTTCue.h" 28 29 #include "core/html/track/vtt/VTTCue.h" 30 #include "core/rendering/RenderView.h" 31 32 namespace WebCore { 33 34 RenderVTTCue::RenderVTTCue(VTTCueBox* element) 35 : RenderBlockFlow(element) 36 , m_cue(element->getCue()) 37 { 38 } 39 40 void RenderVTTCue::layout() 41 { 42 RenderBlockFlow::layout(); 43 44 // If WebVTT Regions are used, the regular WebVTT layout algorithm is no 45 // longer necessary, since cues having the region parameter set do not have 46 // any positioning parameters. Also, in this case, the regions themselves 47 // have positioning information. 48 if (!m_cue->regionId().isEmpty()) 49 return; 50 51 LayoutState state(*this, locationOffset()); 52 53 if (m_cue->snapToLines()) 54 repositionCueSnapToLinesSet(); 55 else 56 repositionCueSnapToLinesNotSet(); 57 } 58 59 bool RenderVTTCue::findFirstLineBox(InlineFlowBox*& firstLineBox) 60 { 61 if (firstChild()->isRenderInline()) 62 firstLineBox = toRenderInline(firstChild())->firstLineBox(); 63 else 64 return false; 65 66 return true; 67 } 68 69 bool RenderVTTCue::initializeLayoutParameters(InlineFlowBox* firstLineBox, LayoutUnit& step, LayoutUnit& position) 70 { 71 ASSERT(firstChild()); 72 73 RenderBlock* parentBlock = containingBlock(); 74 75 // 1. Horizontal: Let step be the height of the first line box in boxes. 76 // Vertical: Let step be the width of the first line box in boxes. 77 step = m_cue->getWritingDirection() == VTTCue::Horizontal ? firstLineBox->height() : firstLineBox->width(); 78 79 // 2. If step is zero, then jump to the step labeled done positioning below. 80 if (!step) 81 return false; 82 83 // 3. Let line position be the text track cue computed line position. 84 int linePosition = m_cue->calculateComputedLinePosition(); 85 86 // 4. Vertical Growing Left: Add one to line position then negate it. 87 if (m_cue->getWritingDirection() == VTTCue::VerticalGrowingLeft) 88 linePosition = -(linePosition + 1); 89 90 // 5. Let position be the result of multiplying step and line position. 91 position = step * linePosition; 92 93 // 6. 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 (m_cue->getWritingDirection() == VTTCue::VerticalGrowingLeft) { 96 position -= width(); 97 position += step; 98 } 99 100 // 7. If line position is less than zero... 101 if (linePosition < 0) { 102 // Horizontal / Vertical: ... then increase position by the 103 // height / width of the video's rendering area ... 104 position += m_cue->getWritingDirection() == VTTCue::Horizontal ? parentBlock->height() : parentBlock->width(); 105 106 // ... and negate step. 107 step = -step; 108 } 109 110 return true; 111 } 112 113 void RenderVTTCue::placeBoxInDefaultPosition(LayoutUnit position, bool& switched) 114 { 115 // 8. Move all boxes in boxes ... 116 if (m_cue->getWritingDirection() == VTTCue::Horizontal) { 117 // Horizontal: ... down by the distance given by position 118 setY(y() + position); 119 } else { 120 // Vertical: ... right by the distance given by position 121 setX(x() + position); 122 } 123 124 // 9. Default: Remember the position of all the boxes in boxes as their 125 // default position. 126 // FIXME: Why the direct conversion between float and LayoutUnit? crbug.com/350474 127 m_fallbackPosition = FloatPoint(location()); 128 129 // 10. Let switched be false. 130 switched = false; 131 } 132 133 bool RenderVTTCue::isOutside() const 134 { 135 return !containingBlock()->absoluteBoundingBoxRect().contains(absoluteContentBox()); 136 } 137 138 bool RenderVTTCue::isOverlapping() const 139 { 140 for (RenderObject* box = previousSibling(); box; box = box->previousSibling()) { 141 IntRect boxRect = box->absoluteBoundingBoxRect(); 142 143 if (absoluteBoundingBoxRect().intersects(boxRect)) 144 return true; 145 } 146 147 return false; 148 } 149 150 bool RenderVTTCue::shouldSwitchDirection(InlineFlowBox* firstLineBox, LayoutUnit step) const 151 { 152 LayoutUnit top = y(); 153 LayoutUnit left = x(); 154 LayoutUnit bottom = top + firstLineBox->height(); 155 LayoutUnit right = left + firstLineBox->width(); 156 157 // 12. Horizontal: If step is negative and the top of the first line 158 // box in boxes is now above the top of the video's rendering area, 159 // or if step is positive and the bottom of the first line box in 160 // boxes is now below the bottom of the video's rendering area, jump 161 // to the step labeled switch direction. 162 LayoutUnit parentHeight = containingBlock()->height(); 163 if (m_cue->getWritingDirection() == VTTCue::Horizontal && ((step < 0 && top < 0) || (step > 0 && bottom > parentHeight))) 164 return true; 165 166 // 12. Vertical: If step is negative and the left edge of the first line 167 // box in boxes is now to the left of the left edge of the video's 168 // rendering area, or if step is positive and the right edge of the 169 // first line box in boxes is now to the right of the right edge of 170 // the video's rendering area, jump to the step labeled switch direction. 171 LayoutUnit parentWidth = containingBlock()->width(); 172 if (m_cue->getWritingDirection() != VTTCue::Horizontal && ((step < 0 && left < 0) || (step > 0 && right > parentWidth))) 173 return true; 174 175 return false; 176 } 177 178 void RenderVTTCue::moveBoxesByStep(LayoutUnit step) 179 { 180 // 13. Horizontal: Move all the boxes in boxes down by the distance 181 // given by step. (If step is negative, then this will actually 182 // result in an upwards movement of the boxes in absolute terms.) 183 if (m_cue->getWritingDirection() == VTTCue::Horizontal) 184 setY(y() + step); 185 186 // 13. Vertical: Move all the boxes in boxes right by the distance 187 // given by step. (If step is negative, then this will actually 188 // result in a leftwards movement of the boxes in absolute terms.) 189 else 190 setX(x() + step); 191 } 192 193 bool RenderVTTCue::switchDirection(bool& switched, LayoutUnit& step) 194 { 195 // 15. Switch direction: Move all the boxes in boxes back to their 196 // default position as determined in the step above labeled default. 197 setX(m_fallbackPosition.x()); 198 setY(m_fallbackPosition.y()); 199 200 // 16. If switched is true, jump to the step labeled done 201 // positioning below. 202 if (switched) 203 return false; 204 205 // 17. Negate step. 206 step = -step; 207 208 // 18. Set switched to true. 209 switched = true; 210 return true; 211 } 212 213 void RenderVTTCue::repositionCueSnapToLinesSet() 214 { 215 InlineFlowBox* firstLineBox; 216 LayoutUnit step; 217 LayoutUnit position; 218 219 if (!findFirstLineBox(firstLineBox)) 220 return; 221 222 if (!initializeLayoutParameters(firstLineBox, step, position)) 223 return; 224 225 bool switched; 226 placeBoxInDefaultPosition(position, switched); 227 228 // 11. Step loop: If none of the boxes in boxes would overlap any of the boxes 229 // in output and all the boxes in output are within the video's rendering area 230 // then jump to the step labeled done positioning. 231 while (isOutside() || isOverlapping()) { 232 if (!shouldSwitchDirection(firstLineBox, step)) { 233 // 13. Move all the boxes in boxes ... 234 // 14. Jump back to the step labeled step loop. 235 moveBoxesByStep(step); 236 } else if (!switchDirection(switched, step)) { 237 break; 238 } 239 240 // 19. Jump back to the step labeled step loop. 241 } 242 243 // Acommodate extra top and bottom padding, border or margin. 244 // Note: this is supported only for internal UA styling, not through the cue selector. 245 if (hasInlineDirectionBordersPaddingOrMargin()) { 246 IntRect containerRect = containingBlock()->absoluteBoundingBoxRect(); 247 IntRect cueRect = absoluteBoundingBoxRect(); 248 249 int topOverflow = cueRect.y() - containerRect.y(); 250 int bottomOverflow = containerRect.y() + containerRect.height() - cueRect.y() - cueRect.height(); 251 252 int adjustment = 0; 253 if (topOverflow < 0) 254 adjustment = -topOverflow; 255 else if (bottomOverflow < 0) 256 adjustment = bottomOverflow; 257 258 if (adjustment) 259 setY(y() + adjustment); 260 } 261 } 262 263 void RenderVTTCue::repositionCueSnapToLinesNotSet() 264 { 265 // FIXME: Implement overlapping detection when snap-to-lines is not set. http://wkb.ug/84296 266 } 267 268 } // namespace WebCore 269 270