| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2011 Google Inc. All rights reserved. | 2 * Copyright (C) 2011 Google Inc. All rights reserved. |
| 3 * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. | 3 * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions are | 6 * modification, are permitted provided that the following conditions are |
| 7 * met: | 7 * met: |
| 8 * | 8 * |
| 9 * * Redistributions of source code must retain the above copyright | 9 * * Redistributions of source code must retain the above copyright |
| 10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 */ | 30 */ |
| 31 | 31 |
| 32 #include "config.h" | 32 #include "config.h" |
| 33 #include "core/html/track/TextTrackCue.h" | 33 #include "core/html/track/TextTrackCue.h" |
| 34 | 34 |
| 35 #include "CSSPropertyNames.h" | |
| 36 #include "CSSValueKeywords.h" | |
| 37 #include "RuntimeEnabledFeatures.h" | |
| 38 #include "bindings/v8/ExceptionMessages.h" | 35 #include "bindings/v8/ExceptionMessages.h" |
| 39 #include "bindings/v8/ExceptionState.h" | |
| 40 #include "bindings/v8/ExceptionStatePlaceholder.h" | 36 #include "bindings/v8/ExceptionStatePlaceholder.h" |
| 41 #include "core/dom/DocumentFragment.h" | |
| 42 #include "core/events/Event.h" | 37 #include "core/events/Event.h" |
| 43 #include "core/dom/NodeTraversal.h" | |
| 44 #include "core/html/HTMLDivElement.h" | 38 #include "core/html/HTMLDivElement.h" |
| 45 #include "core/html/track/TextTrack.h" | 39 #include "core/html/track/TextTrack.h" |
| 46 #include "core/html/track/TextTrackCueList.h" | 40 #include "core/html/track/TextTrackCueList.h" |
| 47 #include "core/html/track/vtt/VTTElement.h" | |
| 48 #include "core/html/track/vtt/VTTParser.h" | |
| 49 #include "core/html/track/vtt/VTTRegionList.h" | |
| 50 #include "core/rendering/RenderTextTrackCue.h" | |
| 51 #include "wtf/MathExtras.h" | |
| 52 #include "wtf/text/StringBuilder.h" | |
| 53 | 41 |
| 54 namespace WebCore { | 42 namespace WebCore { |
| 55 | 43 |
| 56 static const int invalidCueIndex = -1; | 44 static const int invalidCueIndex = -1; |
| 57 static const int undefinedPosition = -1; | |
| 58 | 45 |
| 59 static const CSSValueID displayWritingModeMap[] = { | 46 // ---------------------------- |
| 60 CSSValueHorizontalTb, CSSValueVerticalRl, CSSValueVerticalLr | |
| 61 }; | |
| 62 COMPILE_ASSERT(WTF_ARRAY_LENGTH(displayWritingModeMap) == TextTrackCue::NumberOf
WritingDirections, | |
| 63 displayWritingModeMap_has_wrong_size); | |
| 64 | 47 |
| 65 static const CSSValueID displayAlignmentMap[] = { | 48 TextTrackCueBox::TextTrackCueBox(Document& document) |
| 66 CSSValueStart, CSSValueCenter, CSSValueEnd, CSSValueLeft, CSSValueRight | 49 : HTMLDivElement(document) |
| 67 }; | |
| 68 COMPILE_ASSERT(WTF_ARRAY_LENGTH(displayAlignmentMap) == TextTrackCue::NumberOfAl
ignments, | |
| 69 displayAlignmentMap_has_wrong_size); | |
| 70 | |
| 71 static const String& startKeyword() | |
| 72 { | 50 { |
| 73 DEFINE_STATIC_LOCAL(const String, start, ("start")); | |
| 74 return start; | |
| 75 } | 51 } |
| 76 | 52 |
| 77 static const String& middleKeyword() | 53 // ---------------------------- |
| 78 { | |
| 79 DEFINE_STATIC_LOCAL(const String, middle, ("middle")); | |
| 80 return middle; | |
| 81 } | |
| 82 | 54 |
| 83 static const String& endKeyword() | 55 bool TextTrackCue::isInfiniteOrNonNumber(double value, const char* method, Excep
tionState& exceptionState) |
| 84 { | |
| 85 DEFINE_STATIC_LOCAL(const String, end, ("end")); | |
| 86 return end; | |
| 87 } | |
| 88 | |
| 89 static const String& leftKeyword() | |
| 90 { | |
| 91 DEFINE_STATIC_LOCAL(const String, left, ("left")); | |
| 92 return left; | |
| 93 } | |
| 94 | |
| 95 static const String& rightKeyword() | |
| 96 { | |
| 97 DEFINE_STATIC_LOCAL(const String, right, ("right")); | |
| 98 return right; | |
| 99 } | |
| 100 | |
| 101 static const String& horizontalKeyword() | |
| 102 { | |
| 103 return emptyString(); | |
| 104 } | |
| 105 | |
| 106 static const String& verticalGrowingLeftKeyword() | |
| 107 { | |
| 108 DEFINE_STATIC_LOCAL(const String, verticalrl, ("rl")); | |
| 109 return verticalrl; | |
| 110 } | |
| 111 | |
| 112 static const String& verticalGrowingRightKeyword() | |
| 113 { | |
| 114 DEFINE_STATIC_LOCAL(const String, verticallr, ("lr")); | |
| 115 return verticallr; | |
| 116 } | |
| 117 | |
| 118 static bool isInfiniteOrNonNumber(double value, const char* method, ExceptionSta
te& exceptionState) | |
| 119 { | 56 { |
| 120 if (std::isinf(value)) { | 57 if (std::isinf(value)) { |
| 121 exceptionState.throwTypeError(ExceptionMessages::failedToSet(method, "Te
xtTrackCue", "The value provided is infinite.")); | 58 exceptionState.throwTypeError(ExceptionMessages::failedToSet(method, "Te
xtTrackCue", "The value provided is infinite.")); |
| 122 return true; | 59 return true; |
| 123 } | 60 } |
| 124 if (std::isnan(value)) { | 61 if (std::isnan(value)) { |
| 125 exceptionState.throwTypeError(ExceptionMessages::failedToSet(method, "Te
xtTrackCue", "The value provided is not a number.")); | 62 exceptionState.throwTypeError(ExceptionMessages::failedToSet(method, "Te
xtTrackCue", "The value provided is not a number.")); |
| 126 return true; | 63 return true; |
| 127 } | 64 } |
| 128 return false; | 65 return false; |
| 129 } | 66 } |
| 130 | 67 |
| 131 static bool isInvalidPercentage(double value, const char* method, ExceptionState
& exceptionState) | 68 TextTrackCue::TextTrackCue(double start, double end) |
| 132 { | |
| 133 if (isInfiniteOrNonNumber(value, method, exceptionState)) | |
| 134 return true; | |
| 135 if (value < 0 || value > 100) { | |
| 136 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::fail
edToSet(method, "TextTrackCue", "The value provided (" + String::number(value) +
") is not between 0 and 100.")); | |
| 137 return true; | |
| 138 } | |
| 139 return false; | |
| 140 } | |
| 141 | |
| 142 | |
| 143 // ---------------------------- | |
| 144 | |
| 145 TextTrackCueBox::TextTrackCueBox(Document& document, TextTrackCue* cue) | |
| 146 : HTMLDivElement(document) | |
| 147 , m_cue(cue) | |
| 148 { | |
| 149 setPart(textTrackCueBoxShadowPseudoId()); | |
| 150 } | |
| 151 | |
| 152 TextTrackCue* TextTrackCueBox::getCue() const | |
| 153 { | |
| 154 return m_cue; | |
| 155 } | |
| 156 | |
| 157 void TextTrackCueBox::applyCSSProperties(const IntSize&) | |
| 158 { | |
| 159 // FIXME: Apply all the initial CSS positioning properties. http://wkb.ug/79
916 | |
| 160 if (!m_cue->regionId().isEmpty()) { | |
| 161 setInlineStyleProperty(CSSPropertyPosition, CSSValueRelative); | |
| 162 return; | |
| 163 } | |
| 164 | |
| 165 // 3.5.1 On the (root) List of WebVTT Node Objects: | |
| 166 | |
| 167 // the 'position' property must be set to 'absolute' | |
| 168 setInlineStyleProperty(CSSPropertyPosition, CSSValueAbsolute); | |
| 169 | |
| 170 // the 'unicode-bidi' property must be set to 'plaintext' | |
| 171 setInlineStyleProperty(CSSPropertyUnicodeBidi, CSSValueWebkitPlaintext); | |
| 172 | |
| 173 // the 'direction' property must be set to direction | |
| 174 setInlineStyleProperty(CSSPropertyDirection, m_cue->getCSSWritingDirection()
); | |
| 175 | |
| 176 // the 'writing-mode' property must be set to writing-mode | |
| 177 setInlineStyleProperty(CSSPropertyWebkitWritingMode, m_cue->getCSSWritingMod
e()); | |
| 178 | |
| 179 std::pair<float, float> position = m_cue->getCSSPosition(); | |
| 180 | |
| 181 // the 'top' property must be set to top, | |
| 182 setInlineStyleProperty(CSSPropertyTop, position.second, CSSPrimitiveValue::C
SS_PERCENTAGE); | |
| 183 | |
| 184 // the 'left' property must be set to left | |
| 185 setInlineStyleProperty(CSSPropertyLeft, position.first, CSSPrimitiveValue::C
SS_PERCENTAGE); | |
| 186 | |
| 187 // the 'width' property must be set to width, and the 'height' property mus
t be set to height | |
| 188 if (m_cue->vertical() == horizontalKeyword()) { | |
| 189 setInlineStyleProperty(CSSPropertyWidth, static_cast<double>(m_cue->getC
SSSize()), CSSPrimitiveValue::CSS_PERCENTAGE); | |
| 190 setInlineStyleProperty(CSSPropertyHeight, CSSValueAuto); | |
| 191 } else { | |
| 192 setInlineStyleProperty(CSSPropertyWidth, CSSValueAuto); | |
| 193 setInlineStyleProperty(CSSPropertyHeight, static_cast<double>(m_cue->get
CSSSize()), CSSPrimitiveValue::CSS_PERCENTAGE); | |
| 194 } | |
| 195 | |
| 196 // The 'text-align' property on the (root) List of WebVTT Node Objects must | |
| 197 // be set to the value in the second cell of the row of the table below | |
| 198 // whose first cell is the value of the corresponding cue's text track cue | |
| 199 // alignment: | |
| 200 setInlineStyleProperty(CSSPropertyTextAlign, m_cue->getCSSAlignment()); | |
| 201 | |
| 202 if (!m_cue->snapToLines()) { | |
| 203 // 10.13.1 Set up x and y: | |
| 204 // Note: x and y are set through the CSS left and top above. | |
| 205 | |
| 206 // 10.13.2 Position the boxes in boxes such that the point x% along the | |
| 207 // width of the bounding box of the boxes in boxes is x% of the way | |
| 208 // across the width of the video's rendering area, and the point y% | |
| 209 // along the height of the bounding box of the boxes in boxes is y% | |
| 210 // of the way across the height of the video's rendering area, while | |
| 211 // maintaining the relative positions of the boxes in boxes to each | |
| 212 // other. | |
| 213 setInlineStyleProperty(CSSPropertyWebkitTransform, | |
| 214 String::format("translate(-%.2f%%, -%.2f%%)", position.first, po
sition.second)); | |
| 215 | |
| 216 setInlineStyleProperty(CSSPropertyWhiteSpace, CSSValuePre); | |
| 217 } | |
| 218 } | |
| 219 | |
| 220 const AtomicString& TextTrackCueBox::textTrackCueBoxShadowPseudoId() | |
| 221 { | |
| 222 DEFINE_STATIC_LOCAL(const AtomicString, trackDisplayBoxShadowPseudoId, ("-we
bkit-media-text-track-display", AtomicString::ConstructFromLiteral)); | |
| 223 return trackDisplayBoxShadowPseudoId; | |
| 224 } | |
| 225 | |
| 226 RenderObject* TextTrackCueBox::createRenderer(RenderStyle*) | |
| 227 { | |
| 228 return new RenderTextTrackCue(this); | |
| 229 } | |
| 230 | |
| 231 // ---------------------------- | |
| 232 | |
| 233 TextTrackCue::TextTrackCue(Document& document, double start, double end, const S
tring& content) | |
| 234 : m_startTime(start) | 69 : m_startTime(start) |
| 235 , m_endTime(end) | 70 , m_endTime(end) |
| 236 , m_content(content) | |
| 237 , m_linePosition(undefinedPosition) | |
| 238 , m_computedLinePosition(undefinedPosition) | |
| 239 , m_textPosition(50) | |
| 240 , m_cueSize(100) | |
| 241 , m_cueIndex(invalidCueIndex) | 71 , m_cueIndex(invalidCueIndex) |
| 242 , m_writingDirection(Horizontal) | |
| 243 , m_cueAlignment(Middle) | |
| 244 , m_webVTTNodeTree(0) | |
| 245 , m_track(0) | 72 , m_track(0) |
| 246 , m_isActive(false) | 73 , m_isActive(false) |
| 247 , m_pauseOnExit(false) | 74 , m_pauseOnExit(false) |
| 248 , m_snapToLines(true) | |
| 249 , m_cueBackgroundBox(HTMLDivElement::create(document)) | |
| 250 , m_displayTreeShouldChange(true) | |
| 251 , m_displayDirection(CSSValueLtr) | |
| 252 , m_notifyRegion(true) | |
| 253 { | 75 { |
| 254 } | 76 } |
| 255 | 77 |
| 256 TextTrackCue::~TextTrackCue() | 78 String TextTrackCue::toString() const |
| 257 { | 79 { |
| 258 displayTreeInternal()->remove(ASSERT_NO_EXCEPTION); | 80 return String::format("%p id=%s interval=%f-->%f)", this, id().utf8().data()
, startTime(), endTime()); |
| 259 } | |
| 260 | |
| 261 PassRefPtr<TextTrackCueBox> TextTrackCue::displayTreeInternal() | |
| 262 { | |
| 263 if (!m_displayTree) | |
| 264 m_displayTree = TextTrackCueBox::create(document(), this); | |
| 265 return m_displayTree; | |
| 266 } | 81 } |
| 267 | 82 |
| 268 void TextTrackCue::cueWillChange() | 83 void TextTrackCue::cueWillChange() |
| 269 { | 84 { |
| 270 if (m_track) | 85 if (m_track) |
| 271 m_track->cueWillChange(this); | 86 m_track->cueWillChange(this); |
| 272 } | 87 } |
| 273 | 88 |
| 274 void TextTrackCue::cueDidChange() | 89 void TextTrackCue::cueDidChange() |
| 275 { | 90 { |
| 276 if (m_track) | 91 if (m_track) |
| 277 m_track->cueDidChange(this); | 92 m_track->cueDidChange(this); |
| 278 | |
| 279 m_displayTreeShouldChange = true; | |
| 280 } | 93 } |
| 281 | 94 |
| 282 TextTrack* TextTrackCue::track() const | 95 TextTrack* TextTrackCue::track() const |
| 283 { | 96 { |
| 284 return m_track; | 97 return m_track; |
| 285 } | 98 } |
| 286 | 99 |
| 287 void TextTrackCue::setTrack(TextTrack* track) | 100 void TextTrackCue::setTrack(TextTrack* track) |
| 288 { | 101 { |
| 289 m_track = track; | 102 m_track = track; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 332 void TextTrackCue::setPauseOnExit(bool value) | 145 void TextTrackCue::setPauseOnExit(bool value) |
| 333 { | 146 { |
| 334 if (m_pauseOnExit == value) | 147 if (m_pauseOnExit == value) |
| 335 return; | 148 return; |
| 336 | 149 |
| 337 cueWillChange(); | 150 cueWillChange(); |
| 338 m_pauseOnExit = value; | 151 m_pauseOnExit = value; |
| 339 cueDidChange(); | 152 cueDidChange(); |
| 340 } | 153 } |
| 341 | 154 |
| 342 const String& TextTrackCue::vertical() const | |
| 343 { | |
| 344 switch (m_writingDirection) { | |
| 345 case Horizontal: | |
| 346 return horizontalKeyword(); | |
| 347 case VerticalGrowingLeft: | |
| 348 return verticalGrowingLeftKeyword(); | |
| 349 case VerticalGrowingRight: | |
| 350 return verticalGrowingRightKeyword(); | |
| 351 default: | |
| 352 ASSERT_NOT_REACHED(); | |
| 353 return emptyString(); | |
| 354 } | |
| 355 } | |
| 356 | |
| 357 void TextTrackCue::setVertical(const String& value, ExceptionState& exceptionSta
te) | |
| 358 { | |
| 359 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-ele
ment.html#dom-texttrackcue-vertical | |
| 360 // On setting, the text track cue writing direction must be set to the value
given | |
| 361 // in the first cell of the row in the table above whose second cell is a | |
| 362 // case-sensitive match for the new value, if any. If none of the values mat
ch, then | |
| 363 // the user agent must instead throw a SyntaxError exception. | |
| 364 | |
| 365 WritingDirection direction = m_writingDirection; | |
| 366 if (value == horizontalKeyword()) | |
| 367 direction = Horizontal; | |
| 368 else if (value == verticalGrowingLeftKeyword()) | |
| 369 direction = VerticalGrowingLeft; | |
| 370 else if (value == verticalGrowingRightKeyword()) | |
| 371 direction = VerticalGrowingRight; | |
| 372 else | |
| 373 exceptionState.throwDOMException(SyntaxError, ExceptionMessages::failedT
oSet("vertical", "TextTrackCue", "The value provided ('" + value + "') is invali
d. Only 'rl', 'lr', and the empty string are accepted.")); | |
| 374 | |
| 375 if (direction == m_writingDirection) | |
| 376 return; | |
| 377 | |
| 378 cueWillChange(); | |
| 379 m_writingDirection = direction; | |
| 380 cueDidChange(); | |
| 381 } | |
| 382 | |
| 383 void TextTrackCue::setSnapToLines(bool value) | |
| 384 { | |
| 385 if (m_snapToLines == value) | |
| 386 return; | |
| 387 | |
| 388 cueWillChange(); | |
| 389 m_snapToLines = value; | |
| 390 cueDidChange(); | |
| 391 } | |
| 392 | |
| 393 void TextTrackCue::setLine(int position, ExceptionState& exceptionState) | |
| 394 { | |
| 395 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-ele
ment.html#dom-texttrackcue-line | |
| 396 // On setting, if the text track cue snap-to-lines flag is not set, and the
new | |
| 397 // value is negative or greater than 100, then throw an IndexSizeError excep
tion. | |
| 398 if (!m_snapToLines && (position < 0 || position > 100)) { | |
| 399 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::fail
edToSet("line", "TextTrackCue", "The snap-to-lines flag is not set, and the valu
e provided (" + String::number(position) + ") is not between 0 and 100.")); | |
| 400 return; | |
| 401 } | |
| 402 | |
| 403 // Otherwise, set the text track cue line position to the new value. | |
| 404 if (m_linePosition == position) | |
| 405 return; | |
| 406 | |
| 407 cueWillChange(); | |
| 408 m_linePosition = position; | |
| 409 m_computedLinePosition = calculateComputedLinePosition(); | |
| 410 cueDidChange(); | |
| 411 } | |
| 412 | |
| 413 void TextTrackCue::setPosition(int position, ExceptionState& exceptionState) | |
| 414 { | |
| 415 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-ele
ment.html#dom-texttrackcue-position | |
| 416 // On setting, if the new value is negative or greater than 100, then throw
an IndexSizeError exception. | |
| 417 // Otherwise, set the text track cue text position to the new value. | |
| 418 if (isInvalidPercentage(position, "line", exceptionState)) | |
| 419 return; | |
| 420 | |
| 421 // Otherwise, set the text track cue line position to the new value. | |
| 422 if (m_textPosition == position) | |
| 423 return; | |
| 424 | |
| 425 cueWillChange(); | |
| 426 m_textPosition = position; | |
| 427 cueDidChange(); | |
| 428 } | |
| 429 | |
| 430 void TextTrackCue::setSize(int size, ExceptionState& exceptionState) | |
| 431 { | |
| 432 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-ele
ment.html#dom-texttrackcue-size | |
| 433 // On setting, if the new value is negative or greater than 100, then throw
an IndexSizeError | |
| 434 // exception. Otherwise, set the text track cue size to the new value. | |
| 435 if (isInvalidPercentage(size, "line", exceptionState)) | |
| 436 return; | |
| 437 | |
| 438 // Otherwise, set the text track cue line position to the new value. | |
| 439 if (m_cueSize == size) | |
| 440 return; | |
| 441 | |
| 442 cueWillChange(); | |
| 443 m_cueSize = size; | |
| 444 cueDidChange(); | |
| 445 } | |
| 446 | |
| 447 const String& TextTrackCue::align() const | |
| 448 { | |
| 449 switch (m_cueAlignment) { | |
| 450 case Start: | |
| 451 return startKeyword(); | |
| 452 case Middle: | |
| 453 return middleKeyword(); | |
| 454 case End: | |
| 455 return endKeyword(); | |
| 456 case Left: | |
| 457 return leftKeyword(); | |
| 458 case Right: | |
| 459 return rightKeyword(); | |
| 460 default: | |
| 461 ASSERT_NOT_REACHED(); | |
| 462 return emptyString(); | |
| 463 } | |
| 464 } | |
| 465 | |
| 466 void TextTrackCue::setAlign(const String& value, ExceptionState& exceptionState) | |
| 467 { | |
| 468 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-ele
ment.html#dom-texttrackcue-align | |
| 469 // On setting, the text track cue alignment must be set to the value given i
n the | |
| 470 // first cell of the row in the table above whose second cell is a case-sens
itive | |
| 471 // match for the new value, if any. If none of the values match, then the us
er | |
| 472 // agent must instead throw a SyntaxError exception. | |
| 473 | |
| 474 CueAlignment alignment = m_cueAlignment; | |
| 475 if (value == startKeyword()) | |
| 476 alignment = Start; | |
| 477 else if (value == middleKeyword()) | |
| 478 alignment = Middle; | |
| 479 else if (value == endKeyword()) | |
| 480 alignment = End; | |
| 481 else if (value == leftKeyword()) | |
| 482 alignment = Left; | |
| 483 else if (value == rightKeyword()) | |
| 484 alignment = Right; | |
| 485 else | |
| 486 exceptionState.throwDOMException(SyntaxError, ExceptionMessages::failedT
oSet("align", "TextTrackCue", "The value provided ('" + value + "') is invalid.
Only 'start', 'middle', 'end', 'left', and 'right' are accepted.")); | |
| 487 | |
| 488 if (alignment == m_cueAlignment) | |
| 489 return; | |
| 490 | |
| 491 cueWillChange(); | |
| 492 m_cueAlignment = alignment; | |
| 493 cueDidChange(); | |
| 494 } | |
| 495 | |
| 496 void TextTrackCue::setText(const String& text) | |
| 497 { | |
| 498 if (m_content == text) | |
| 499 return; | |
| 500 | |
| 501 cueWillChange(); | |
| 502 // Clear the document fragment but don't bother to create it again just yet
as we can do that | |
| 503 // when it is requested. | |
| 504 m_webVTTNodeTree = 0; | |
| 505 m_content = text; | |
| 506 cueDidChange(); | |
| 507 } | |
| 508 | |
| 509 int TextTrackCue::cueIndex() | 155 int TextTrackCue::cueIndex() |
| 510 { | 156 { |
| 511 if (m_cueIndex == invalidCueIndex) | 157 if (m_cueIndex == invalidCueIndex) |
| 512 m_cueIndex = track()->cues()->getCueIndex(this); | 158 m_cueIndex = track()->cues()->getCueIndex(this); |
| 513 | 159 |
| 514 return m_cueIndex; | 160 return m_cueIndex; |
| 515 } | 161 } |
| 516 | 162 |
| 517 void TextTrackCue::invalidateCueIndex() | 163 void TextTrackCue::invalidateCueIndex() |
| 518 { | 164 { |
| 519 m_cueIndex = invalidCueIndex; | 165 m_cueIndex = invalidCueIndex; |
| 520 } | 166 } |
| 521 | 167 |
| 522 void TextTrackCue::createVTTNodeTree() | |
| 523 { | |
| 524 if (!m_webVTTNodeTree) | |
| 525 m_webVTTNodeTree = VTTParser::createDocumentFragmentFromCueText(document
(), m_content); | |
| 526 } | |
| 527 | |
| 528 void TextTrackCue::copyVTTNodeToDOMTree(ContainerNode* webVTTNode, ContainerNode
* parent) | |
| 529 { | |
| 530 for (Node* node = webVTTNode->firstChild(); node; node = node->nextSibling()
) { | |
| 531 RefPtr<Node> clonedNode; | |
| 532 if (node->isVTTElement()) | |
| 533 clonedNode = toVTTElement(node)->createEquivalentHTMLElement(documen
t()); | |
| 534 else | |
| 535 clonedNode = node->cloneNode(false); | |
| 536 parent->appendChild(clonedNode); | |
| 537 if (node->isContainerNode()) | |
| 538 copyVTTNodeToDOMTree(toContainerNode(node), toContainerNode(clonedNo
de)); | |
| 539 } | |
| 540 } | |
| 541 | |
| 542 PassRefPtr<DocumentFragment> TextTrackCue::getCueAsHTML() | |
| 543 { | |
| 544 createVTTNodeTree(); | |
| 545 RefPtr<DocumentFragment> clonedFragment = DocumentFragment::create(document(
)); | |
| 546 copyVTTNodeToDOMTree(m_webVTTNodeTree.get(), clonedFragment.get()); | |
| 547 return clonedFragment.release(); | |
| 548 } | |
| 549 | |
| 550 PassRefPtr<DocumentFragment> TextTrackCue::createCueRenderingTree() | |
| 551 { | |
| 552 RefPtr<DocumentFragment> clonedFragment; | |
| 553 createVTTNodeTree(); | |
| 554 clonedFragment = DocumentFragment::create(document()); | |
| 555 m_webVTTNodeTree->cloneChildNodes(clonedFragment.get()); | |
| 556 return clonedFragment.release(); | |
| 557 } | |
| 558 | |
| 559 bool TextTrackCue::dispatchEvent(PassRefPtr<Event> event) | 168 bool TextTrackCue::dispatchEvent(PassRefPtr<Event> event) |
| 560 { | 169 { |
| 561 // When a TextTrack's mode is disabled: no cues are active, no events fired. | 170 // When a TextTrack's mode is disabled: no cues are active, no events fired. |
| 562 if (!track() || track()->mode() == TextTrack::disabledKeyword()) | 171 if (!track() || track()->mode() == TextTrack::disabledKeyword()) |
| 563 return false; | 172 return false; |
| 564 | 173 |
| 565 return EventTarget::dispatchEvent(event); | 174 return EventTarget::dispatchEvent(event); |
| 566 } | 175 } |
| 567 | 176 |
| 568 void TextTrackCue::setRegionId(const String& regionId) | |
| 569 { | |
| 570 if (m_regionId == regionId) | |
| 571 return; | |
| 572 | |
| 573 cueWillChange(); | |
| 574 m_regionId = regionId; | |
| 575 cueDidChange(); | |
| 576 } | |
| 577 | |
| 578 void TextTrackCue::notifyRegionWhenRemovingDisplayTree(bool notifyRegion) | |
| 579 { | |
| 580 m_notifyRegion = notifyRegion; | |
| 581 } | |
| 582 | |
| 583 bool TextTrackCue::isActive() | 177 bool TextTrackCue::isActive() |
| 584 { | 178 { |
| 585 return m_isActive && track() && track()->mode() != TextTrack::disabledKeywor
d(); | 179 return m_isActive && track() && track()->mode() != TextTrack::disabledKeywor
d(); |
| 586 } | 180 } |
| 587 | 181 |
| 588 void TextTrackCue::setIsActive(bool active) | 182 void TextTrackCue::setIsActive(bool active) |
| 589 { | 183 { |
| 590 m_isActive = active; | 184 m_isActive = active; |
| 591 | 185 |
| 592 // Remove the display tree as soon as the cue becomes inactive. | 186 // Remove the display tree as soon as the cue becomes inactive. |
| 593 if (!active) | 187 if (!active) |
| 594 removeDisplayTree(); | 188 removeDisplayTree(); |
| 595 } | 189 } |
| 596 | 190 |
| 597 int TextTrackCue::calculateComputedLinePosition() | |
| 598 { | |
| 599 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-ele
ment.html#text-track-cue-computed-line-position | |
| 600 | |
| 601 // If the text track cue line position is numeric, then that is the text | |
| 602 // track cue computed line position. | |
| 603 if (m_linePosition != undefinedPosition) | |
| 604 return m_linePosition; | |
| 605 | |
| 606 // If the text track cue snap-to-lines flag of the text track cue is not | |
| 607 // set, the text track cue computed line position is the value 100; | |
| 608 if (!m_snapToLines) | |
| 609 return 100; | |
| 610 | |
| 611 // Otherwise, it is the value returned by the following algorithm: | |
| 612 | |
| 613 // If cue is not associated with a text track, return -1 and abort these | |
| 614 // steps. | |
| 615 if (!track()) | |
| 616 return -1; | |
| 617 | |
| 618 // Let n be the number of text tracks whose text track mode is showing or | |
| 619 // showing by default and that are in the media element's list of text | |
| 620 // tracks before track. | |
| 621 int n = track()->trackIndexRelativeToRenderedTracks(); | |
| 622 | |
| 623 // Increment n by one. | |
| 624 n++; | |
| 625 | |
| 626 // Negate n. | |
| 627 n = -n; | |
| 628 | |
| 629 return n; | |
| 630 } | |
| 631 | |
| 632 static bool isCueParagraphSeparator(UChar character) | |
| 633 { | |
| 634 // Within a cue, paragraph boundaries are only denoted by Type B characters, | |
| 635 // such as U+000A LINE FEED (LF), U+0085 NEXT LINE (NEL), and U+2029 PARAGRA
PH SEPARATOR. | |
| 636 return WTF::Unicode::category(character) & WTF::Unicode::Separator_Paragraph
; | |
| 637 } | |
| 638 | |
| 639 void TextTrackCue::determineTextDirection() | |
| 640 { | |
| 641 DEFINE_STATIC_LOCAL(const String, rtTag, ("rt")); | |
| 642 createVTTNodeTree(); | |
| 643 | |
| 644 // Apply the Unicode Bidirectional Algorithm's Paragraph Level steps to the | |
| 645 // concatenation of the values of each WebVTT Text Object in nodes, in a | |
| 646 // pre-order, depth-first traversal, excluding WebVTT Ruby Text Objects and | |
| 647 // their descendants. | |
| 648 StringBuilder paragraphBuilder; | |
| 649 for (Node* node = m_webVTTNodeTree->firstChild(); node; node = NodeTraversal
::next(*node, m_webVTTNodeTree.get())) { | |
| 650 if (!node->isTextNode() || node->localName() == rtTag) | |
| 651 continue; | |
| 652 | |
| 653 paragraphBuilder.append(node->nodeValue()); | |
| 654 } | |
| 655 | |
| 656 String paragraph = paragraphBuilder.toString(); | |
| 657 if (!paragraph.length()) | |
| 658 return; | |
| 659 | |
| 660 for (size_t i = 0; i < paragraph.length(); ++i) { | |
| 661 UChar current = paragraph[i]; | |
| 662 if (!current || isCueParagraphSeparator(current)) | |
| 663 return; | |
| 664 | |
| 665 if (UChar current = paragraph[i]) { | |
| 666 WTF::Unicode::Direction charDirection = WTF::Unicode::direction(curr
ent); | |
| 667 if (charDirection == WTF::Unicode::LeftToRight) { | |
| 668 m_displayDirection = CSSValueLtr; | |
| 669 return; | |
| 670 } | |
| 671 if (charDirection == WTF::Unicode::RightToLeft | |
| 672 || charDirection == WTF::Unicode::RightToLeftArabic) { | |
| 673 m_displayDirection = CSSValueRtl; | |
| 674 return; | |
| 675 } | |
| 676 } | |
| 677 } | |
| 678 } | |
| 679 | |
| 680 void TextTrackCue::calculateDisplayParameters() | |
| 681 { | |
| 682 // Steps 10.2, 10.3 | |
| 683 determineTextDirection(); | |
| 684 | |
| 685 // 10.4 If the text track cue writing direction is horizontal, then let | |
| 686 // block-flow be 'tb'. Otherwise, if the text track cue writing direction is | |
| 687 // vertical growing left, then let block-flow be 'lr'. Otherwise, the text | |
| 688 // track cue writing direction is vertical growing right; let block-flow be | |
| 689 // 'rl'. | |
| 690 | |
| 691 // The above step is done through the writing direction static map. | |
| 692 | |
| 693 // 10.5 Determine the value of maximum size for cue as per the appropriate | |
| 694 // rules from the following list: | |
| 695 int maximumSize = m_textPosition; | |
| 696 if ((m_writingDirection == Horizontal && m_cueAlignment == Start && m_displa
yDirection == CSSValueLtr) | |
| 697 || (m_writingDirection == Horizontal && m_cueAlignment == End && m_d
isplayDirection == CSSValueRtl) | |
| 698 || (m_writingDirection == Horizontal && m_cueAlignment == Left) | |
| 699 || (m_writingDirection == VerticalGrowingLeft && (m_cueAlignment ==
Start || m_cueAlignment == Left)) | |
| 700 || (m_writingDirection == VerticalGrowingRight && (m_cueAlignment ==
Start || m_cueAlignment == Left))) { | |
| 701 maximumSize = 100 - m_textPosition; | |
| 702 } else if ((m_writingDirection == Horizontal && m_cueAlignment == End && m_d
isplayDirection == CSSValueLtr) | |
| 703 || (m_writingDirection == Horizontal && m_cueAlignment == Start && m
_displayDirection == CSSValueRtl) | |
| 704 || (m_writingDirection == Horizontal && m_cueAlignment == Right) | |
| 705 || (m_writingDirection == VerticalGrowingLeft && (m_cueAlignment ==
End || m_cueAlignment == Right)) | |
| 706 || (m_writingDirection == VerticalGrowingRight && (m_cueAlignment ==
End || m_cueAlignment == Right))) { | |
| 707 maximumSize = m_textPosition; | |
| 708 } else if (m_cueAlignment == Middle) { | |
| 709 maximumSize = m_textPosition <= 50 ? m_textPosition : (100 - m_textPosit
ion); | |
| 710 maximumSize = maximumSize * 2; | |
| 711 } else { | |
| 712 ASSERT_NOT_REACHED(); | |
| 713 } | |
| 714 | |
| 715 // 10.6 If the text track cue size is less than maximum size, then let size | |
| 716 // be text track cue size. Otherwise, let size be maximum size. | |
| 717 m_displaySize = std::min(m_cueSize, maximumSize); | |
| 718 | |
| 719 // FIXME: Understand why step 10.7 is missing (just a copy/paste error?) | |
| 720 // Could be done within a spec implementation check - http://crbug.com/30158
0 | |
| 721 | |
| 722 // 10.8 Determine the value of x-position or y-position for cue as per the | |
| 723 // appropriate rules from the following list: | |
| 724 if (m_writingDirection == Horizontal) { | |
| 725 switch (m_cueAlignment) { | |
| 726 case Start: | |
| 727 if (m_displayDirection == CSSValueLtr) | |
| 728 m_displayPosition.first = m_textPosition; | |
| 729 else | |
| 730 m_displayPosition.first = 100 - m_textPosition - m_displaySize; | |
| 731 break; | |
| 732 case End: | |
| 733 if (m_displayDirection == CSSValueRtl) | |
| 734 m_displayPosition.first = 100 - m_textPosition; | |
| 735 else | |
| 736 m_displayPosition.first = m_textPosition - m_displaySize; | |
| 737 break; | |
| 738 case Left: | |
| 739 if (m_displayDirection == CSSValueLtr) | |
| 740 m_displayPosition.first = m_textPosition; | |
| 741 else | |
| 742 m_displayPosition.first = 100 - m_textPosition; | |
| 743 break; | |
| 744 case Right: | |
| 745 if (m_displayDirection == CSSValueLtr) | |
| 746 m_displayPosition.first = m_textPosition - m_displaySize; | |
| 747 else | |
| 748 m_displayPosition.first = 100 - m_textPosition - m_displaySize; | |
| 749 break; | |
| 750 case Middle: | |
| 751 if (m_displayDirection == CSSValueLtr) | |
| 752 m_displayPosition.first = m_textPosition - m_displaySize / 2; | |
| 753 else | |
| 754 m_displayPosition.first = 100 - m_textPosition - m_displaySize /
2; | |
| 755 break; | |
| 756 case NumberOfAlignments: | |
| 757 ASSERT_NOT_REACHED(); | |
| 758 } | |
| 759 } else { | |
| 760 // Cases for m_writingDirection being VerticalGrowing{Left|Right} | |
| 761 switch (m_cueAlignment) { | |
| 762 case Start: | |
| 763 case Left: | |
| 764 m_displayPosition.second = m_textPosition; | |
| 765 break; | |
| 766 case End: | |
| 767 case Right: | |
| 768 m_displayPosition.second = m_textPosition - m_displaySize; | |
| 769 break; | |
| 770 case Middle: | |
| 771 m_displayPosition.second = m_textPosition - m_displaySize / 2; | |
| 772 break; | |
| 773 case NumberOfAlignments: | |
| 774 ASSERT_NOT_REACHED(); | |
| 775 } | |
| 776 } | |
| 777 | |
| 778 // A text track cue has a text track cue computed line position whose value | |
| 779 // is defined in terms of the other aspects of the cue. | |
| 780 m_computedLinePosition = calculateComputedLinePosition(); | |
| 781 | |
| 782 // 10.9 Determine the value of whichever of x-position or y-position is not | |
| 783 // yet calculated for cue as per the appropriate rules from the following | |
| 784 // list: | |
| 785 if (m_snapToLines && m_displayPosition.second == undefinedPosition && m_writ
ingDirection == Horizontal) | |
| 786 m_displayPosition.second = 0; | |
| 787 | |
| 788 if (!m_snapToLines && m_displayPosition.second == undefinedPosition && m_wri
tingDirection == Horizontal) | |
| 789 m_displayPosition.second = m_computedLinePosition; | |
| 790 | |
| 791 if (m_snapToLines && m_displayPosition.first == undefinedPosition | |
| 792 && (m_writingDirection == VerticalGrowingLeft || m_writingDirection
== VerticalGrowingRight)) | |
| 793 m_displayPosition.first = 0; | |
| 794 | |
| 795 if (!m_snapToLines && (m_writingDirection == VerticalGrowingLeft || m_writin
gDirection == VerticalGrowingRight)) | |
| 796 m_displayPosition.first = m_computedLinePosition; | |
| 797 } | |
| 798 | |
| 799 void TextTrackCue::markFutureAndPastNodes(ContainerNode* root, double previousTi
mestamp, double movieTime) | |
| 800 { | |
| 801 DEFINE_STATIC_LOCAL(const String, timestampTag, ("timestamp")); | |
| 802 | |
| 803 bool isPastNode = true; | |
| 804 double currentTimestamp = previousTimestamp; | |
| 805 if (currentTimestamp > movieTime) | |
| 806 isPastNode = false; | |
| 807 | |
| 808 for (Node* child = root->firstChild(); child; child = NodeTraversal::next(*c
hild, root)) { | |
| 809 if (child->nodeName() == timestampTag) { | |
| 810 unsigned position = 0; | |
| 811 String timestamp = child->nodeValue(); | |
| 812 double currentTimestamp = VTTParser::collectTimeStamp(timestamp, &po
sition); | |
| 813 ASSERT(currentTimestamp != -1); | |
| 814 | |
| 815 if (currentTimestamp > movieTime) | |
| 816 isPastNode = false; | |
| 817 } | |
| 818 | |
| 819 if (child->isVTTElement()) { | |
| 820 toVTTElement(child)->setIsPastNode(isPastNode); | |
| 821 // Make an elemenet id match a cue id for style matching purposes. | |
| 822 if (!m_id.isEmpty()) | |
| 823 toElement(child)->setIdAttribute(m_id); | |
| 824 } | |
| 825 } | |
| 826 } | |
| 827 | |
| 828 void TextTrackCue::updateDisplayTree(double movieTime) | |
| 829 { | |
| 830 // The display tree may contain WebVTT timestamp objects representing | |
| 831 // timestamps (processing instructions), along with displayable nodes. | |
| 832 | |
| 833 if (!track()->isRendered()) | |
| 834 return; | |
| 835 | |
| 836 // Clear the contents of the set. | |
| 837 m_cueBackgroundBox->removeChildren(); | |
| 838 | |
| 839 // Update the two sets containing past and future WebVTT objects. | |
| 840 RefPtr<DocumentFragment> referenceTree = createCueRenderingTree(); | |
| 841 markFutureAndPastNodes(referenceTree.get(), startTime(), movieTime); | |
| 842 m_cueBackgroundBox->appendChild(referenceTree, ASSERT_NO_EXCEPTION); | |
| 843 } | |
| 844 | |
| 845 PassRefPtr<TextTrackCueBox> TextTrackCue::getDisplayTree(const IntSize& videoSiz
e) | |
| 846 { | |
| 847 RefPtr<TextTrackCueBox> displayTree = displayTreeInternal(); | |
| 848 if (!m_displayTreeShouldChange || !track()->isRendered()) | |
| 849 return displayTree; | |
| 850 | |
| 851 // 10.1 - 10.10 | |
| 852 calculateDisplayParameters(); | |
| 853 | |
| 854 // 10.11. Apply the terms of the CSS specifications to nodes within the | |
| 855 // following constraints, thus obtaining a set of CSS boxes positioned | |
| 856 // relative to an initial containing block: | |
| 857 displayTree->removeChildren(); | |
| 858 | |
| 859 // The document tree is the tree of WebVTT Node Objects rooted at nodes. | |
| 860 | |
| 861 // The children of the nodes must be wrapped in an anonymous box whose | |
| 862 // 'display' property has the value 'inline'. This is the WebVTT cue | |
| 863 // background box. | |
| 864 | |
| 865 // Note: This is contained by default in m_cueBackgroundBox. | |
| 866 m_cueBackgroundBox->setPart(cueShadowPseudoId()); | |
| 867 displayTree->appendChild(m_cueBackgroundBox); | |
| 868 | |
| 869 // FIXME(BUG 79916): Runs of children of WebVTT Ruby Objects that are not | |
| 870 // WebVTT Ruby Text Objects must be wrapped in anonymous boxes whose | |
| 871 // 'display' property has the value 'ruby-base'. | |
| 872 | |
| 873 // FIXME(BUG 79916): Text runs must be wrapped according to the CSS | |
| 874 // line-wrapping rules, except that additionally, regardless of the value of | |
| 875 // the 'white-space' property, lines must be wrapped at the edge of their | |
| 876 // containing blocks, even if doing so requires splitting a word where there | |
| 877 // is no line breaking opportunity. (Thus, normally text wraps as needed, | |
| 878 // but if there is a particularly long word, it does not overflow as it | |
| 879 // normally would in CSS, it is instead forcibly wrapped at the box's edge.) | |
| 880 displayTree->applyCSSProperties(videoSize); | |
| 881 | |
| 882 m_displayTreeShouldChange = false; | |
| 883 | |
| 884 // 10.15. Let cue's text track cue display state have the CSS boxes in | |
| 885 // boxes. | |
| 886 return displayTree; | |
| 887 } | |
| 888 | |
| 889 void TextTrackCue::removeDisplayTree() | |
| 890 { | |
| 891 if (m_notifyRegion && m_track->regions()) { | |
| 892 // The region needs to be informed about the cue removal. | |
| 893 VTTRegion* region = m_track->regions()->getRegionById(m_regionId); | |
| 894 if (region) | |
| 895 region->willRemoveTextTrackCueBox(m_displayTree.get()); | |
| 896 } | |
| 897 | |
| 898 displayTreeInternal()->remove(ASSERT_NO_EXCEPTION); | |
| 899 } | |
| 900 | |
| 901 std::pair<double, double> TextTrackCue::getPositionCoordinates() const | |
| 902 { | |
| 903 // This method is used for setting x and y when snap to lines is not set. | |
| 904 std::pair<double, double> coordinates; | |
| 905 | |
| 906 if (m_writingDirection == Horizontal && m_displayDirection == CSSValueLtr) { | |
| 907 coordinates.first = m_textPosition; | |
| 908 coordinates.second = m_computedLinePosition; | |
| 909 | |
| 910 return coordinates; | |
| 911 } | |
| 912 | |
| 913 if (m_writingDirection == Horizontal && m_displayDirection == CSSValueRtl) { | |
| 914 coordinates.first = 100 - m_textPosition; | |
| 915 coordinates.second = m_computedLinePosition; | |
| 916 | |
| 917 return coordinates; | |
| 918 } | |
| 919 | |
| 920 if (m_writingDirection == VerticalGrowingLeft) { | |
| 921 coordinates.first = 100 - m_computedLinePosition; | |
| 922 coordinates.second = m_textPosition; | |
| 923 | |
| 924 return coordinates; | |
| 925 } | |
| 926 | |
| 927 if (m_writingDirection == VerticalGrowingRight) { | |
| 928 coordinates.first = m_computedLinePosition; | |
| 929 coordinates.second = m_textPosition; | |
| 930 | |
| 931 return coordinates; | |
| 932 } | |
| 933 | |
| 934 ASSERT_NOT_REACHED(); | |
| 935 | |
| 936 return coordinates; | |
| 937 } | |
| 938 | |
| 939 TextTrackCue::CueSetting TextTrackCue::settingName(const String& name) | |
| 940 { | |
| 941 DEFINE_STATIC_LOCAL(const String, verticalKeyword, ("vertical")); | |
| 942 DEFINE_STATIC_LOCAL(const String, lineKeyword, ("line")); | |
| 943 DEFINE_STATIC_LOCAL(const String, positionKeyword, ("position")); | |
| 944 DEFINE_STATIC_LOCAL(const String, sizeKeyword, ("size")); | |
| 945 DEFINE_STATIC_LOCAL(const String, alignKeyword, ("align")); | |
| 946 DEFINE_STATIC_LOCAL(const String, regionIdKeyword, ("region")); | |
| 947 | |
| 948 if (name == verticalKeyword) | |
| 949 return Vertical; | |
| 950 else if (name == lineKeyword) | |
| 951 return Line; | |
| 952 else if (name == positionKeyword) | |
| 953 return Position; | |
| 954 else if (name == sizeKeyword) | |
| 955 return Size; | |
| 956 else if (name == alignKeyword) | |
| 957 return Align; | |
| 958 else if (RuntimeEnabledFeatures::webVTTRegionsEnabled() && name == regionIdK
eyword) | |
| 959 return RegionId; | |
| 960 | |
| 961 return None; | |
| 962 } | |
| 963 | |
| 964 void TextTrackCue::parseSettings(const String& input) | |
| 965 { | |
| 966 unsigned position = 0; | |
| 967 | |
| 968 while (position < input.length()) { | |
| 969 | |
| 970 // The WebVTT cue settings part of a WebVTT cue consists of zero or more
of the following components, in any order, | |
| 971 // separated from each other by one or more U+0020 SPACE characters or U
+0009 CHARACTER TABULATION (tab) characters. | |
| 972 while (position < input.length() && VTTParser::isValidSettingDelimiter(i
nput[position])) | |
| 973 position++; | |
| 974 if (position >= input.length()) | |
| 975 break; | |
| 976 | |
| 977 // When the user agent is to parse the WebVTT settings given by a string
input for a text track cue cue, | |
| 978 // the user agent must run the following steps: | |
| 979 // 1. Let settings be the result of splitting input on spaces. | |
| 980 // 2. For each token setting in the list settings, run the following sub
steps: | |
| 981 // 1. If setting does not contain a U+003A COLON character (:), or if
the first U+003A COLON character (:) | |
| 982 // in setting is either the first or last character of setting, th
en jump to the step labeled next setting. | |
| 983 unsigned endOfSetting = position; | |
| 984 String setting = VTTParser::collectWord(input, &endOfSetting); | |
| 985 CueSetting name; | |
| 986 size_t colonOffset = setting.find(':', 1); | |
| 987 if (colonOffset == kNotFound || !colonOffset || colonOffset == setting.l
ength() - 1) | |
| 988 goto NextSetting; | |
| 989 | |
| 990 // 2. Let name be the leading substring of setting up to and excluding t
he first U+003A COLON character (:) in that string. | |
| 991 name = settingName(setting.substring(0, colonOffset)); | |
| 992 | |
| 993 // 3. Let value be the trailing substring of setting starting from the c
haracter immediately after the first U+003A COLON character (:) in that string. | |
| 994 position += colonOffset + 1; | |
| 995 if (position >= input.length()) | |
| 996 break; | |
| 997 | |
| 998 // 4. Run the appropriate substeps that apply for the value of name, as
follows: | |
| 999 switch (name) { | |
| 1000 case Vertical: | |
| 1001 { | |
| 1002 // If name is a case-sensitive match for "vertical" | |
| 1003 // 1. If value is a case-sensitive match for the string "rl", then l
et cue's text track cue writing direction | |
| 1004 // be vertical growing left. | |
| 1005 String writingDirection = VTTParser::collectWord(input, &position); | |
| 1006 if (writingDirection == verticalGrowingLeftKeyword()) | |
| 1007 m_writingDirection = VerticalGrowingLeft; | |
| 1008 | |
| 1009 // 2. Otherwise, if value is a case-sensitive match for the string "
lr", then let cue's text track cue writing | |
| 1010 // direction be vertical growing right. | |
| 1011 else if (writingDirection == verticalGrowingRightKeyword()) | |
| 1012 m_writingDirection = VerticalGrowingRight; | |
| 1013 } | |
| 1014 break; | |
| 1015 case Line: | |
| 1016 { | |
| 1017 // 1-2 - Collect chars that are either '-', '%', or a digit. | |
| 1018 // 1. If value contains any characters other than U+002D HYPHEN-MINU
S characters (-), U+0025 PERCENT SIGN | |
| 1019 // characters (%), and characters in the range U+0030 DIGIT ZERO
(0) to U+0039 DIGIT NINE (9), then jump | |
| 1020 // to the step labeled next setting. | |
| 1021 StringBuilder linePositionBuilder; | |
| 1022 while (position < input.length() && (input[position] == '-' || input
[position] == '%' || isASCIIDigit(input[position]))) | |
| 1023 linePositionBuilder.append(input[position++]); | |
| 1024 if (position < input.length() && !VTTParser::isValidSettingDelimiter
(input[position])) | |
| 1025 break; | |
| 1026 | |
| 1027 // 2. If value does not contain at least one character in the range
U+0030 DIGIT ZERO (0) to U+0039 DIGIT | |
| 1028 // NINE (9), then jump to the step labeled next setting. | |
| 1029 // 3. If any character in value other than the first character is a
U+002D HYPHEN-MINUS character (-), then | |
| 1030 // jump to the step labeled next setting. | |
| 1031 // 4. If any character in value other than the last character is a U
+0025 PERCENT SIGN character (%), then | |
| 1032 // jump to the step labeled next setting. | |
| 1033 String linePosition = linePositionBuilder.toString(); | |
| 1034 if (linePosition.find('-', 1) != kNotFound || linePosition.reverseFi
nd("%", linePosition.length() - 2) != kNotFound) | |
| 1035 break; | |
| 1036 | |
| 1037 // 5. If the first character in value is a U+002D HYPHEN-MINUS chara
cter (-) and the last character in value is a | |
| 1038 // U+0025 PERCENT SIGN character (%), then jump to the step label
ed next setting. | |
| 1039 if (linePosition[0] == '-' && linePosition[linePosition.length() - 1
] == '%') | |
| 1040 break; | |
| 1041 | |
| 1042 // 6. Ignoring the trailing percent sign, if any, interpret value as
a (potentially signed) integer, and | |
| 1043 // let number be that number. | |
| 1044 // NOTE: toInt ignores trailing non-digit characters, such as '%'. | |
| 1045 bool validNumber; | |
| 1046 int number = linePosition.toInt(&validNumber); | |
| 1047 if (!validNumber) | |
| 1048 break; | |
| 1049 | |
| 1050 // 7. If the last character in value is a U+0025 PERCENT SIGN charac
ter (%), but number is not in the range | |
| 1051 // 0 ≤ number ≤ 100, then jump to the step labeled next setting. | |
| 1052 // 8. Let cue's text track cue line position be number. | |
| 1053 // 9. If the last character in value is a U+0025 PERCENT SIGN charac
ter (%), then let cue's text track cue | |
| 1054 // snap-to-lines flag be false. Otherwise, let it be true. | |
| 1055 if (linePosition[linePosition.length() - 1] == '%') { | |
| 1056 if (number < 0 || number > 100) | |
| 1057 break; | |
| 1058 | |
| 1059 // 10 - If '%' then set snap-to-lines flag to false. | |
| 1060 m_snapToLines = false; | |
| 1061 } | |
| 1062 | |
| 1063 m_linePosition = number; | |
| 1064 } | |
| 1065 break; | |
| 1066 case Position: | |
| 1067 { | |
| 1068 // 1. If value contains any characters other than U+0025 PERCENT SIG
N characters (%) and characters in the range | |
| 1069 // U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9), then jump to t
he step labeled next setting. | |
| 1070 // 2. If value does not contain at least one character in the range
U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9), | |
| 1071 // then jump to the step labeled next setting. | |
| 1072 String textPosition = VTTParser::collectDigits(input, &position); | |
| 1073 if (textPosition.isEmpty()) | |
| 1074 break; | |
| 1075 if (position >= input.length()) | |
| 1076 break; | |
| 1077 | |
| 1078 // 3. If any character in value other than the last character is a U
+0025 PERCENT SIGN character (%), then jump | |
| 1079 // to the step labeled next setting. | |
| 1080 // 4. If the last character in value is not a U+0025 PERCENT SIGN ch
aracter (%), then jump to the step labeled | |
| 1081 // next setting. | |
| 1082 if (input[position++] != '%') | |
| 1083 break; | |
| 1084 if (position < input.length() && !VTTParser::isValidSettingDelimiter
(input[position])) | |
| 1085 break; | |
| 1086 | |
| 1087 // 5. Ignoring the trailing percent sign, interpret value as an inte
ger, and let number be that number. | |
| 1088 // 6. If number is not in the range 0 ≤ number ≤ 100, then jump to t
he step labeled next setting. | |
| 1089 // NOTE: toInt ignores trailing non-digit characters, such as '%'. | |
| 1090 bool validNumber; | |
| 1091 int number = textPosition.toInt(&validNumber); | |
| 1092 if (!validNumber) | |
| 1093 break; | |
| 1094 if (number < 0 || number > 100) | |
| 1095 break; | |
| 1096 | |
| 1097 // 7. Let cue's text track cue text position be number. | |
| 1098 m_textPosition = number; | |
| 1099 } | |
| 1100 break; | |
| 1101 case Size: | |
| 1102 { | |
| 1103 // 1. If value contains any characters other than U+0025 PERCENT SIG
N characters (%) and characters in the | |
| 1104 // range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9), then jum
p to the step labeled next setting. | |
| 1105 // 2. If value does not contain at least one character in the range
U+0030 DIGIT ZERO (0) to U+0039 DIGIT | |
| 1106 // NINE (9), then jump to the step labeled next setting. | |
| 1107 String cueSize = VTTParser::collectDigits(input, &position); | |
| 1108 if (cueSize.isEmpty()) | |
| 1109 break; | |
| 1110 if (position >= input.length()) | |
| 1111 break; | |
| 1112 | |
| 1113 // 3. If any character in value other than the last character is a U
+0025 PERCENT SIGN character (%), | |
| 1114 // then jump to the step labeled next setting. | |
| 1115 // 4. If the last character in value is not a U+0025 PERCENT SIGN ch
aracter (%), then jump to the step | |
| 1116 // labeled next setting. | |
| 1117 if (input[position++] != '%') | |
| 1118 break; | |
| 1119 if (position < input.length() && !VTTParser::isValidSettingDelimiter
(input[position])) | |
| 1120 break; | |
| 1121 | |
| 1122 // 5. Ignoring the trailing percent sign, interpret value as an inte
ger, and let number be that number. | |
| 1123 // 6. If number is not in the range 0 ≤ number ≤ 100, then jump to t
he step labeled next setting. | |
| 1124 bool validNumber; | |
| 1125 int number = cueSize.toInt(&validNumber); | |
| 1126 if (!validNumber) | |
| 1127 break; | |
| 1128 if (number < 0 || number > 100) | |
| 1129 break; | |
| 1130 | |
| 1131 // 7. Let cue's text track cue size be number. | |
| 1132 m_cueSize = number; | |
| 1133 } | |
| 1134 break; | |
| 1135 case Align: | |
| 1136 { | |
| 1137 String cueAlignment = VTTParser::collectWord(input, &position); | |
| 1138 | |
| 1139 // 1. If value is a case-sensitive match for the string "start", the
n let cue's text track cue alignment be start alignment. | |
| 1140 if (cueAlignment == startKeyword()) | |
| 1141 m_cueAlignment = Start; | |
| 1142 | |
| 1143 // 2. If value is a case-sensitive match for the string "middle", th
en let cue's text track cue alignment be middle alignment. | |
| 1144 else if (cueAlignment == middleKeyword()) | |
| 1145 m_cueAlignment = Middle; | |
| 1146 | |
| 1147 // 3. If value is a case-sensitive match for the string "end", then
let cue's text track cue alignment be end alignment. | |
| 1148 else if (cueAlignment == endKeyword()) | |
| 1149 m_cueAlignment = End; | |
| 1150 | |
| 1151 // 4. If value is a case-sensitive match for the string "left", then
let cue's text track cue alignment be left alignment. | |
| 1152 else if (cueAlignment == leftKeyword()) | |
| 1153 m_cueAlignment = Left; | |
| 1154 | |
| 1155 // 5. If value is a case-sensitive match for the string "right", the
n let cue's text track cue alignment be right alignment. | |
| 1156 else if (cueAlignment == rightKeyword()) | |
| 1157 m_cueAlignment = Right; | |
| 1158 } | |
| 1159 break; | |
| 1160 case RegionId: | |
| 1161 m_regionId = VTTParser::collectWord(input, &position); | |
| 1162 break; | |
| 1163 case None: | |
| 1164 break; | |
| 1165 } | |
| 1166 | |
| 1167 NextSetting: | |
| 1168 position = endOfSetting; | |
| 1169 } | |
| 1170 | |
| 1171 // If cue's line position is not auto or cue's size is not 100 or cue's | |
| 1172 // writing direction is not horizontal, but cue's region identifier is not | |
| 1173 // the empty string, let cue's region identifier be the empty string. | |
| 1174 if (m_regionId.isEmpty()) | |
| 1175 return; | |
| 1176 | |
| 1177 if (m_linePosition != undefinedPosition || m_cueSize != 100 || m_writingDire
ction != Horizontal) | |
| 1178 m_regionId = emptyString(); | |
| 1179 } | |
| 1180 | |
| 1181 CSSValueID TextTrackCue::getCSSAlignment() const | |
| 1182 { | |
| 1183 return displayAlignmentMap[m_cueAlignment]; | |
| 1184 } | |
| 1185 | |
| 1186 CSSValueID TextTrackCue::getCSSWritingDirection() const | |
| 1187 { | |
| 1188 return m_displayDirection; | |
| 1189 } | |
| 1190 | |
| 1191 CSSValueID TextTrackCue::getCSSWritingMode() const | |
| 1192 { | |
| 1193 return displayWritingModeMap[m_writingDirection]; | |
| 1194 } | |
| 1195 | |
| 1196 int TextTrackCue::getCSSSize() const | |
| 1197 { | |
| 1198 return m_displaySize; | |
| 1199 } | |
| 1200 | |
| 1201 std::pair<double, double> TextTrackCue::getCSSPosition() const | |
| 1202 { | |
| 1203 if (!m_snapToLines) | |
| 1204 return getPositionCoordinates(); | |
| 1205 | |
| 1206 return m_displayPosition; | |
| 1207 } | |
| 1208 | |
| 1209 const AtomicString& TextTrackCue::interfaceName() const | 191 const AtomicString& TextTrackCue::interfaceName() const |
| 1210 { | 192 { |
| 1211 return EventTargetNames::TextTrackCue; | 193 return EventTargetNames::TextTrackCue; |
| 1212 } | 194 } |
| 1213 | 195 |
| 1214 ExecutionContext* TextTrackCue::executionContext() const | |
| 1215 { | |
| 1216 ASSERT(m_cueBackgroundBox); | |
| 1217 return m_cueBackgroundBox->executionContext(); | |
| 1218 } | |
| 1219 | |
| 1220 Document& TextTrackCue::document() const | |
| 1221 { | |
| 1222 ASSERT(m_cueBackgroundBox); | |
| 1223 return m_cueBackgroundBox->document(); | |
| 1224 } | |
| 1225 | |
| 1226 } // namespace WebCore | 196 } // namespace WebCore |
| OLD | NEW |