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