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