| /* |
| * Copyright (C) 2011, 2013 Google Inc. All rights reserved. |
| * Copyright (C) 2011-2019 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "VTTCue.h" |
| |
| #if ENABLE(VIDEO) |
| |
| #include "CSSPropertyNames.h" |
| #include "CSSValueKeywords.h" |
| #include "DocumentFragment.h" |
| #include "ElementInlines.h" |
| #include "Event.h" |
| #include "HTMLDivElement.h" |
| #include "HTMLSpanElement.h" |
| #include "HTMLStyleElement.h" |
| #include "Logging.h" |
| #include "NodeTraversal.h" |
| #include "RenderVTTCue.h" |
| #include "ScriptDisallowedScope.h" |
| #include "ShadowPseudoIds.h" |
| #include "Text.h" |
| #include "TextTrack.h" |
| #include "TextTrackCueGeneric.h" |
| #include "TextTrackCueList.h" |
| #include "VTTRegionList.h" |
| #include "VTTScanner.h" |
| #include "WebVTTElement.h" |
| #include "WebVTTParser.h" |
| #include <wtf/IsoMallocInlines.h> |
| #include <wtf/MathExtras.h> |
| #include <wtf/text/StringBuilder.h> |
| #include <wtf/text/StringConcatenateNumbers.h> |
| |
| namespace WebCore { |
| |
| WTF_MAKE_ISO_ALLOCATED_IMPL(VTTCue); |
| WTF_MAKE_ISO_ALLOCATED_IMPL(VTTCueBox); |
| |
| // This constant should correspond with the percentage returned by CaptionUserPreferences::captionFontSizeScaleAndImportance. |
| constexpr double DEFAULTCAPTIONFONTSIZEPERCENTAGE = 5; |
| |
| static const CSSValueID displayWritingModeMap[] = { |
| CSSValueHorizontalTb, CSSValueVerticalRl, CSSValueVerticalLr |
| }; |
| COMPILE_ASSERT(WTF_ARRAY_LENGTH(displayWritingModeMap) == VTTCue::NumberOfWritingDirections, displayWritingModeMap_has_wrong_size); |
| |
| static const CSSValueID displayAlignmentMap[] = { |
| CSSValueStart, CSSValueCenter, CSSValueEnd, CSSValueLeft, CSSValueRight |
| }; |
| COMPILE_ASSERT(WTF_ARRAY_LENGTH(displayAlignmentMap) == VTTCue::NumberOfAlignments, displayAlignmentMap_has_wrong_size); |
| |
| static const String& startKeyword() |
| { |
| static NeverDestroyed<const String> start(MAKE_STATIC_STRING_IMPL("start")); |
| return start; |
| } |
| |
| static const String& centerKeyword() |
| { |
| static NeverDestroyed<const String> center(MAKE_STATIC_STRING_IMPL("center")); |
| return center; |
| } |
| |
| static const String& endKeyword() |
| { |
| static NeverDestroyed<const String> end(MAKE_STATIC_STRING_IMPL("end")); |
| return end; |
| } |
| |
| static const String& leftKeyword() |
| { |
| static NeverDestroyed<const String> left(MAKE_STATIC_STRING_IMPL("left")); |
| return left; |
| } |
| |
| static const String& rightKeyword() |
| { |
| static NeverDestroyed<const String> right(MAKE_STATIC_STRING_IMPL("right")); |
| return right; |
| } |
| |
| static const String& horizontalKeyword() |
| { |
| return emptyString(); |
| } |
| |
| static const String& verticalGrowingLeftKeyword() |
| { |
| static NeverDestroyed<const String> verticalrl(MAKE_STATIC_STRING_IMPL("rl")); |
| return verticalrl; |
| } |
| |
| static const String& verticalGrowingRightKeyword() |
| { |
| static NeverDestroyed<const String> verticallr(MAKE_STATIC_STRING_IMPL("lr")); |
| return verticallr; |
| } |
| |
| static const String& lineLeftKeyword() |
| { |
| static NeverDestroyed<const String> lineLeft(MAKE_STATIC_STRING_IMPL("line-left")); |
| return lineLeft; |
| } |
| |
| static const String& lineRightKeyword() |
| { |
| static NeverDestroyed<const String> lineRight(MAKE_STATIC_STRING_IMPL("line-right")); |
| return lineRight; |
| } |
| |
| static const String& autoKeyword() |
| { |
| static NeverDestroyed<const String> autoX(MAKE_STATIC_STRING_IMPL("auto")); |
| return autoX; |
| } |
| |
| // ---------------------------- |
| |
| Ref<VTTCueBox> VTTCueBox::create(Document& document, VTTCue& cue) |
| { |
| auto box = adoptRef(*new VTTCueBox(document, cue)); |
| box->initialize(); |
| return box; |
| } |
| |
| VTTCueBox::VTTCueBox(Document& document, VTTCue& cue) |
| : TextTrackCueBox(document, cue) |
| { |
| } |
| |
| void VTTCueBox::applyCSSProperties(const IntSize& videoSize) |
| { |
| auto textTrackCue = getCue(); |
| ASSERT(!textTrackCue || is<VTTCue>(textTrackCue)); |
| if (!is<VTTCue>(textTrackCue)) |
| return; |
| |
| Ref cue = downcast<VTTCue>(*textTrackCue); |
| |
| // FIXME: Apply all the initial CSS positioning properties. http://wkb.ug/79916 |
| if (!cue->regionId().isEmpty()) { |
| setInlineStyleProperty(CSSPropertyPosition, CSSValueRelative); |
| return; |
| } |
| |
| // 3.5.1 On the (root) List of WebVTT Node Objects: |
| |
| // the 'position' property must be set to 'absolute' |
| setInlineStyleProperty(CSSPropertyPosition, CSSValueAbsolute); |
| |
| // the 'unicode-bidi' property must be set to 'plaintext' |
| setInlineStyleProperty(CSSPropertyUnicodeBidi, CSSValuePlaintext); |
| |
| // the 'direction' property must be set to direction |
| setInlineStyleProperty(CSSPropertyDirection, cue->getCSSWritingDirection()); |
| |
| // the 'writing-mode' property must be set to writing-mode |
| setInlineStyleProperty(CSSPropertyWritingMode, cue->getCSSWritingMode(), false); |
| |
| auto& position = cue->getCSSPosition(); |
| |
| // the 'top' property must be set to top, |
| if (position.second) |
| setInlineStyleProperty(CSSPropertyTop, *position.second, CSSUnitType::CSS_PERCENTAGE); |
| |
| // the 'left' property must be set to left |
| if (cue->vertical() == horizontalKeyword() && position.first) |
| setInlineStyleProperty(CSSPropertyLeft, *position.first, CSSUnitType::CSS_PERCENTAGE); |
| else if (cue->vertical() == verticalGrowingRightKeyword()) { |
| // FIXME: Why use calc to do the math instead of doing the subtraction here? |
| setInlineStyleProperty(CSSPropertyLeft, makeString("calc(-", videoSize.width(), "px - ", cue->getCSSSize(), "px)")); |
| } |
| |
| double authorFontSize = std::min(videoSize.width(), videoSize.height()) * DEFAULTCAPTIONFONTSIZEPERCENTAGE / 100.0; |
| double multiplier = 1.0; |
| if (authorFontSize) |
| multiplier = m_fontSizeFromCaptionUserPrefs / authorFontSize; |
| |
| double textPosition = cue->calculateComputedTextPosition(); |
| double maxSize = 100.0; |
| CSSValueID alignment = cue->getCSSAlignment(); |
| if (alignment == CSSValueEnd || alignment == CSSValueRight) |
| maxSize = textPosition; |
| else if (alignment == CSSValueStart || alignment == CSSValueLeft) |
| maxSize = 100.0 - textPosition; |
| |
| double newCueSize = std::min(cue->getCSSSize() * multiplier, 100.0); |
| // the 'width' property must be set to width, and the 'height' property must be set to height |
| if (cue->vertical() == horizontalKeyword()) { |
| setInlineStyleProperty(CSSPropertyWidth, newCueSize, CSSUnitType::CSS_PERCENTAGE); |
| setInlineStyleProperty(CSSPropertyHeight, CSSValueAuto); |
| setInlineStyleProperty(CSSPropertyMinWidth, "min-content"); |
| setInlineStyleProperty(CSSPropertyMaxWidth, maxSize, CSSUnitType::CSS_PERCENTAGE); |
| if ((alignment == CSSValueMiddle || alignment == CSSValueCenter) && multiplier != 1.0 && position.first) |
| setInlineStyleProperty(CSSPropertyLeft, static_cast<double>(*position.first - (newCueSize - cue->getCSSSize()) / 2), CSSUnitType::CSS_PERCENTAGE); |
| } else { |
| setInlineStyleProperty(CSSPropertyWidth, CSSValueAuto); |
| setInlineStyleProperty(CSSPropertyHeight, newCueSize, CSSUnitType::CSS_PERCENTAGE); |
| setInlineStyleProperty(CSSPropertyMinHeight, "min-content"); |
| setInlineStyleProperty(CSSPropertyMaxHeight, maxSize, CSSUnitType::CSS_PERCENTAGE); |
| if ((alignment == CSSValueMiddle || alignment == CSSValueCenter) && multiplier != 1.0 && position.second) |
| setInlineStyleProperty(CSSPropertyTop, static_cast<double>(*position.second - (newCueSize - cue->getCSSSize()) / 2), CSSUnitType::CSS_PERCENTAGE); |
| } |
| |
| // The 'text-align' property on the (root) List of WebVTT Node Objects must |
| // be set to the value in the second cell of the row of the table below |
| // whose first cell is the value of the corresponding cue's text track cue |
| // alignment: |
| setInlineStyleProperty(CSSPropertyTextAlign, cue->getCSSAlignment()); |
| |
| if (!cue->snapToLines()) |
| setInlineStyleProperty(CSSPropertyWhiteSpace, CSSValuePre); |
| |
| // Make sure shadow or stroke is not clipped. |
| setInlineStyleProperty(CSSPropertyOverflow, CSSValueVisible); |
| cue->element().setInlineStyleProperty(CSSPropertyOverflow, CSSValueVisible); |
| } |
| |
| RenderPtr<RenderElement> VTTCueBox::createElementRenderer(RenderStyle&& style, const RenderTreePosition&) |
| { |
| return createRenderer<RenderVTTCue>(*this, WTFMove(style)); |
| } |
| |
| // ---------------------------- |
| |
| Ref<VTTCue> VTTCue::create(Document& document, double start, double end, String&& content) |
| { |
| auto cue = adoptRef(*new VTTCue(document, MediaTime::createWithDouble(start), MediaTime::createWithDouble(end), WTFMove(content))); |
| cue->suspendIfNeeded(); |
| return cue; |
| } |
| |
| Ref<VTTCue> VTTCue::create(Document& document, const WebVTTCueData& data) |
| { |
| auto cue = adoptRef(*new VTTCue(document, data)); |
| cue->suspendIfNeeded(); |
| return cue; |
| } |
| |
| VTTCue::VTTCue(Document& document, const MediaTime& start, const MediaTime& end, String&& content) |
| : TextTrackCue(document, start, end) |
| , m_content(WTFMove(content)) |
| , m_originalStartTime(MediaTime::zeroTime()) |
| { |
| initialize(); |
| } |
| |
| VTTCue::VTTCue(Document& document, const WebVTTCueData& cueData) |
| : TextTrackCue(document, MediaTime::zeroTime(), MediaTime::zeroTime()) |
| , m_originalStartTime(cueData.originalStartTime()) |
| { |
| initialize(); |
| setText(cueData.content()); |
| setStartTime(cueData.startTime()); |
| setEndTime(cueData.endTime()); |
| setId(cueData.id()); |
| setCueSettings(cueData.settings()); |
| } |
| |
| VTTCue::~VTTCue() |
| { |
| } |
| |
| void VTTCue::initialize() |
| { |
| m_cueBackdropBox = HTMLDivElement::create(ownerDocument()); |
| m_cueHighlightBox = HTMLSpanElement::create(spanTag, ownerDocument()); |
| m_snapToLines = true; |
| m_displayTreeShouldChange = true; |
| m_notifyRegion = true; |
| } |
| |
| Ref<VTTCueBox> VTTCue::createDisplayTree() |
| { |
| return VTTCueBox::create(ownerDocument(), *this); |
| } |
| |
| VTTCueBox& VTTCue::displayTreeInternal() |
| { |
| if (!m_displayTree) |
| m_displayTree = createDisplayTree(); |
| return *m_displayTree; |
| } |
| |
| void VTTCue::didChange() |
| { |
| TextTrackCue::didChange(); |
| m_displayTreeShouldChange = true; |
| } |
| |
| const String& VTTCue::vertical() const |
| { |
| switch (m_writingDirection) { |
| case Horizontal: |
| return horizontalKeyword(); |
| case VerticalGrowingLeft: |
| return verticalGrowingLeftKeyword(); |
| case VerticalGrowingRight: |
| return verticalGrowingRightKeyword(); |
| default: |
| ASSERT_NOT_REACHED(); |
| return emptyString(); |
| } |
| } |
| |
| ExceptionOr<void> VTTCue::setVertical(const String& value) |
| { |
| // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-vertical |
| // On setting, the text track cue writing direction must be set to the value given |
| // in the first cell of the row in the table above whose second cell is a |
| // case-sensitive match for the new value, if any. If none of the values match, then |
| // the user agent must instead throw a SyntaxError exception. |
| |
| WritingDirection direction = m_writingDirection; |
| if (value == horizontalKeyword()) |
| direction = Horizontal; |
| else if (value == verticalGrowingLeftKeyword()) |
| direction = VerticalGrowingLeft; |
| else if (value == verticalGrowingRightKeyword()) |
| direction = VerticalGrowingRight; |
| else |
| return { }; |
| |
| if (direction == m_writingDirection) |
| return { }; |
| |
| willChange(); |
| m_writingDirection = direction; |
| didChange(); |
| |
| return { }; |
| } |
| |
| void VTTCue::setSnapToLines(bool value) |
| { |
| if (m_snapToLines == value) |
| return; |
| |
| willChange(); |
| m_snapToLines = value; |
| didChange(); |
| } |
| |
| VTTCue::LineAndPositionSetting VTTCue::line() const |
| { |
| if (!m_linePosition) |
| return Auto; |
| |
| return *m_linePosition; |
| } |
| |
| ExceptionOr<void> VTTCue::setLine(const LineAndPositionSetting& position) |
| { |
| std::optional<double> linePosition; |
| |
| if (!std::holds_alternative<AutoKeyword>(position)) |
| linePosition = std::get<double>(position); |
| |
| if (m_linePosition == linePosition) |
| return { }; |
| |
| willChange(); |
| m_linePosition = linePosition; |
| m_computedLinePosition = calculateComputedLinePosition(); |
| didChange(); |
| |
| return { }; |
| } |
| |
| const String& VTTCue::lineAlign() const |
| { |
| switch (m_lineAlignment) { |
| case LignAlignmentStart: |
| return startKeyword(); |
| case LignAlignmentCenter: |
| return centerKeyword(); |
| case LignAlignmentEnd: |
| return endKeyword(); |
| default: |
| ASSERT_NOT_REACHED(); |
| return emptyString(); |
| } |
| } |
| |
| ExceptionOr<void> VTTCue::setLineAlign(const String& value) |
| { |
| CueLignAlignment lineAlignment; |
| if (value == startKeyword()) |
| lineAlignment = LignAlignmentStart; |
| else if (value == centerKeyword()) |
| lineAlignment = LignAlignmentCenter; |
| else if (value == endKeyword()) |
| lineAlignment = LignAlignmentEnd; |
| else |
| return { }; |
| |
| if (lineAlignment == m_lineAlignment) |
| return { }; |
| |
| willChange(); |
| m_lineAlignment = lineAlignment; |
| didChange(); |
| |
| return { }; |
| } |
| |
| |
| VTTCue::LineAndPositionSetting VTTCue::position() const |
| { |
| if (m_textPosition) |
| return *m_textPosition; |
| return Auto; |
| } |
| |
| ExceptionOr<void> VTTCue::setPosition(const LineAndPositionSetting& position) |
| { |
| // http://dev.w3.org/html5/webvtt/#dfn-vttcue-position |
| // On setting, if the new value is negative or greater than 100, then an |
| // IndexSizeError exception must be thrown. Otherwise, the WebVTT cue |
| // position must be set to the new value; if the new value is the string |
| // "auto", then it must be interpreted as the special value auto. |
| std::optional<double> textPosition; |
| |
| // Otherwise, set the text track cue line position to the new value. |
| if (!std::holds_alternative<AutoKeyword>(position)) { |
| textPosition = std::get<double>(position); |
| if (!(textPosition >= 0 && textPosition <= 100)) |
| return Exception { IndexSizeError }; |
| } |
| |
| if (m_textPosition == textPosition) |
| return { }; |
| |
| willChange(); |
| m_textPosition = WTFMove(textPosition); |
| didChange(); |
| |
| return { }; |
| } |
| |
| const String& VTTCue::positionAlign() const |
| { |
| switch (m_positionAlignment) { |
| case PositionAlignmentLignLeft: |
| return lineLeftKeyword(); |
| case PositionAlignmentLignCenter: |
| return centerKeyword(); |
| case PositionAlignmentLignRight: |
| return lineRightKeyword(); |
| case PositionAlignmentLignAuto: |
| return autoKeyword(); |
| default: |
| ASSERT_NOT_REACHED(); |
| return emptyString(); |
| } |
| } |
| |
| ExceptionOr<void> VTTCue::setPositionAlign(const String& value) |
| { |
| CuePositionAlignment positionAlignment; |
| if (value == lineLeftKeyword()) |
| positionAlignment = PositionAlignmentLignLeft; |
| else if (value == centerKeyword()) |
| positionAlignment = PositionAlignmentLignCenter; |
| else if (value == lineRightKeyword()) |
| positionAlignment = PositionAlignmentLignRight; |
| else if (value == autoKeyword()) |
| positionAlignment = PositionAlignmentLignAuto; |
| else |
| return { }; |
| |
| if (positionAlignment == m_positionAlignment) |
| return { }; |
| |
| willChange(); |
| m_positionAlignment = positionAlignment; |
| didChange(); |
| |
| return { }; |
| } |
| |
| ExceptionOr<void> VTTCue::setSize(int size) |
| { |
| // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-size |
| // On setting, if the new value is negative or greater than 100, then throw an IndexSizeError |
| // exception. Otherwise, set the text track cue size to the new value. |
| if (!(size >= 0 && size <= 100)) |
| return Exception { IndexSizeError }; |
| |
| // Otherwise, set the text track cue line position to the new value. |
| if (m_cueSize == size) |
| return { }; |
| |
| willChange(); |
| m_cueSize = size; |
| didChange(); |
| |
| return { }; |
| } |
| |
| const String& VTTCue::align() const |
| { |
| switch (m_cueAlignment) { |
| case Start: |
| return startKeyword(); |
| case Center: |
| return centerKeyword(); |
| case End: |
| return endKeyword(); |
| case Left: |
| return leftKeyword(); |
| case Right: |
| return rightKeyword(); |
| default: |
| ASSERT_NOT_REACHED(); |
| return emptyString(); |
| } |
| } |
| |
| ExceptionOr<void> VTTCue::setAlign(const String& value) |
| { |
| // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-align |
| // On setting, the text track cue alignment must be set to the value given in the |
| // first cell of the row in the table above whose second cell is a case-sensitive |
| // match for the new value, if any. If none of the values match, then the user |
| // agent must instead throw a SyntaxError exception. |
| |
| CueAlignment alignment; |
| if (value == startKeyword()) |
| alignment = Start; |
| else if (value == centerKeyword()) |
| alignment = Center; |
| else if (value == endKeyword()) |
| alignment = End; |
| else if (value == leftKeyword()) |
| alignment = Left; |
| else if (value == rightKeyword()) |
| alignment = Right; |
| else |
| return { }; |
| |
| if (alignment == m_cueAlignment) |
| return { }; |
| |
| willChange(); |
| m_cueAlignment = alignment; |
| didChange(); |
| |
| return { }; |
| } |
| |
| void VTTCue::setText(const String& text) |
| { |
| if (m_content == text) |
| return; |
| |
| willChange(); |
| // Clear the document fragment but don't bother to create it again just yet as we can do that |
| // when it is requested. |
| m_webVTTNodeTree = nullptr; |
| m_content = text; |
| didChange(); |
| } |
| |
| void VTTCue::createWebVTTNodeTree() |
| { |
| if (!m_webVTTNodeTree) |
| m_webVTTNodeTree = WebVTTParser::createDocumentFragmentFromCueText(ownerDocument(), m_content); |
| } |
| |
| static void copyWebVTTNodeToDOMTree(ContainerNode& webVTTNode, Node& parent) |
| { |
| for (RefPtr<Node> node = webVTTNode.firstChild(); node; node = node->nextSibling()) { |
| RefPtr<Node> clonedNode; |
| if (is<WebVTTElement>(*node)) |
| clonedNode = downcast<WebVTTElement>(*node).createEquivalentHTMLElement(parent.document()); |
| else |
| clonedNode = node->cloneNode(false); |
| parent.appendChild(*clonedNode); |
| if (is<ContainerNode>(*node)) |
| copyWebVTTNodeToDOMTree(downcast<ContainerNode>(*node), *clonedNode); |
| } |
| } |
| |
| RefPtr<DocumentFragment> VTTCue::getCueAsHTML() |
| { |
| createWebVTTNodeTree(); |
| if (!m_webVTTNodeTree) |
| return nullptr; |
| |
| auto clonedFragment = DocumentFragment::create(ownerDocument()); |
| copyWebVTTNodeToDOMTree(*m_webVTTNodeTree, clonedFragment); |
| return clonedFragment; |
| } |
| |
| RefPtr<DocumentFragment> VTTCue::createCueRenderingTree() |
| { |
| createWebVTTNodeTree(); |
| if (!m_webVTTNodeTree) |
| return nullptr; |
| |
| auto clonedFragment = DocumentFragment::create(ownerDocument()); |
| |
| // The cloned fragment is never exposed to author scripts so it's safe to dispatch events here. |
| ScriptDisallowedScope::EventAllowedScope allowedScope(clonedFragment); |
| |
| m_webVTTNodeTree->cloneChildNodes(clonedFragment); |
| return clonedFragment; |
| } |
| |
| void VTTCue::notifyRegionWhenRemovingDisplayTree(bool notifyRegion) |
| { |
| m_notifyRegion = notifyRegion; |
| } |
| |
| void VTTCue::setIsActive(bool active) |
| { |
| TextTrackCue::setIsActive(active); |
| |
| if (!active) { |
| if (!hasDisplayTree()) |
| return; |
| |
| // Remove the display tree as soon as the cue becomes inactive. |
| removeDisplayTree(); |
| } |
| } |
| |
| void VTTCue::setTrack(TextTrack* track) |
| { |
| LOG(Media, "VTTCue::setTrack"); |
| TextTrackCue::setTrack(track); |
| if (!m_parsedRegionId.isEmpty()) { |
| if (track != nullptr) { |
| if (auto* regions = track->regions()) { |
| if (auto region = regions->getRegionById(m_parsedRegionId)) |
| m_region = RefPtr<VTTRegion>(region); |
| } |
| } |
| } |
| } |
| |
| void VTTCue::setRegion(VTTRegion* region) |
| { |
| if (m_region != region) { |
| willChange(); |
| m_region = region; |
| didChange(); |
| } |
| } |
| |
| VTTRegion* VTTCue::region() |
| { |
| if (!m_region) |
| return nullptr; |
| |
| return &*m_region; |
| } |
| |
| const String& VTTCue::regionId() |
| { |
| if (!m_region) |
| return emptyString(); |
| |
| return m_region->id(); |
| } |
| |
| int VTTCue::calculateComputedLinePosition() const |
| { |
| // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-computed-line-position |
| |
| // If the text track cue line position is numeric, then that is the text |
| // track cue computed line position. |
| if (m_linePosition) |
| return *m_linePosition; |
| |
| // If the text track cue snap-to-lines flag of the text track cue is not |
| // set, the text track cue computed line position is the value 100; |
| if (!m_snapToLines) |
| return 100; |
| |
| // Otherwise, it is the value returned by the following algorithm: |
| |
| // If cue is not associated with a text track, return -1 and abort these |
| // steps. |
| if (!track()) |
| return -1; |
| |
| // Let n be the number of text tracks whose text track mode is showing or |
| // showing by default and that are in the media element's list of text |
| // tracks before track. |
| int n = track()->trackIndexRelativeToRenderedTracks(); |
| |
| // Increment n by one. |
| n++; |
| |
| // Negate n. |
| n = -n; |
| |
| return n; |
| } |
| |
| static bool isCueParagraphSeparator(UChar character) |
| { |
| // Within a cue, paragraph boundaries are only denoted by Type B characters, |
| // such as U+000A LINE FEED (LF), U+0085 NEXT LINE (NEL), and U+2029 PARAGRAPH SEPARATOR. |
| return u_charType(character) == U_PARAGRAPH_SEPARATOR; |
| } |
| |
| void VTTCue::determineTextDirection() |
| { |
| static NeverDestroyed<const String> rtTag(MAKE_STATIC_STRING_IMPL("rt")); |
| createWebVTTNodeTree(); |
| if (!m_webVTTNodeTree) |
| return; |
| |
| // Apply the Unicode Bidirectional Algorithm's Paragraph Level steps to the |
| // concatenation of the values of each WebVTT Text Object in nodes, in a |
| // pre-order, depth-first traversal, excluding WebVTT Ruby Text Objects and |
| // their descendants. |
| StringBuilder paragraphBuilder; |
| for (RefPtr<Node> node = m_webVTTNodeTree->firstChild(); node; node = NodeTraversal::next(*node, m_webVTTNodeTree.get())) { |
| // FIXME: The code does not match the comment above. This does not actually exclude Ruby Text Object descendant. |
| if (!node->isTextNode() || node->localName() == rtTag) |
| continue; |
| |
| paragraphBuilder.append(node->nodeValue()); |
| } |
| |
| String paragraph = paragraphBuilder.toString(); |
| if (!paragraph.length()) |
| return; |
| |
| for (size_t i = 0; i < paragraph.length(); ++i) { |
| UChar current = paragraph[i]; |
| if (!current || isCueParagraphSeparator(current)) |
| return; |
| |
| if (UChar current = paragraph[i]) { |
| UCharDirection charDirection = u_charDirection(current); |
| if (charDirection == U_LEFT_TO_RIGHT) { |
| m_displayDirection = CSSValueLtr; |
| return; |
| } |
| if (charDirection == U_RIGHT_TO_LEFT || charDirection == U_RIGHT_TO_LEFT_ARABIC) { |
| m_displayDirection = CSSValueRtl; |
| return; |
| } |
| } |
| } |
| } |
| |
| double VTTCue::calculateComputedTextPosition() const |
| { |
| // http://dev.w3.org/html5/webvtt/#dfn-cue-computed-position |
| |
| // 1. If the position is numeric, then return the value of the position and |
| // abort these steps. (Otherwise, the position is the special value auto.) |
| if (m_textPosition) |
| return *m_textPosition; |
| |
| switch (m_cueAlignment) { |
| case Start: |
| case Left: |
| // 2. If the cue text alignment is start or left, return 0 and abort these |
| // steps. |
| return 0; |
| case End: |
| case Right: |
| // 3. If the cue text alignment is end or right, return 100 and abort these |
| // steps. |
| return 100; |
| case Center: |
| // 4. If the cue text alignment is center, return 50 and abort these steps. |
| return 50; |
| default: |
| ASSERT_NOT_REACHED(); |
| return 0; |
| } |
| } |
| |
| void VTTCue::calculateDisplayParameters() |
| { |
| // Steps 10.2, 10.3 |
| determineTextDirection(); |
| |
| // 10.4 If the text track cue writing direction is horizontal, then let |
| // block-flow be 'tb'. Otherwise, if the text track cue writing direction is |
| // vertical growing left, then let block-flow be 'lr'. Otherwise, the text |
| // track cue writing direction is vertical growing right; let block-flow be |
| // 'rl'. |
| |
| // The above step is done through the writing direction static map. |
| |
| // 10.5 Determine the value of maximum size for cue as per the appropriate |
| // rules from the following list: |
| double computedTextPosition = calculateComputedTextPosition(); |
| int maximumSize = computedTextPosition; |
| if ((m_writingDirection == Horizontal && m_cueAlignment == Start && m_displayDirection == CSSValueLtr) |
| || (m_writingDirection == Horizontal && m_cueAlignment == End && m_displayDirection == CSSValueRtl) |
| || (m_writingDirection == Horizontal && m_cueAlignment == Left) |
| || (m_writingDirection == VerticalGrowingLeft && (m_cueAlignment == Start || m_cueAlignment == Left)) |
| || (m_writingDirection == VerticalGrowingRight && (m_cueAlignment == Start || m_cueAlignment == Left))) { |
| maximumSize = 100 - computedTextPosition; |
| } else if ((m_writingDirection == Horizontal && m_cueAlignment == End && m_displayDirection == CSSValueLtr) |
| || (m_writingDirection == Horizontal && m_cueAlignment == Start && m_displayDirection == CSSValueRtl) |
| || (m_writingDirection == Horizontal && m_cueAlignment == Right) |
| || (m_writingDirection == VerticalGrowingLeft && (m_cueAlignment == End || m_cueAlignment == Right)) |
| || (m_writingDirection == VerticalGrowingRight && (m_cueAlignment == End || m_cueAlignment == Right))) { |
| maximumSize = computedTextPosition; |
| } else if (m_cueAlignment == Center) { |
| maximumSize = computedTextPosition <= 50 ? computedTextPosition : (100 - computedTextPosition); |
| maximumSize = maximumSize * 2; |
| } else |
| ASSERT_NOT_REACHED(); |
| |
| // 10.6 If the text track cue size is less than maximum size, then let size |
| // be text track cue size. Otherwise, let size be maximum size. |
| m_displaySize = std::min(m_cueSize, maximumSize); |
| |
| // FIXME: Understand why step 10.7 is missing (just a copy/paste error?) |
| // Could be done within a spec implementation check - http://crbug.com/301580 |
| |
| // 10.8 Determine the value of x-position or y-position for cue as per the |
| // appropriate rules from the following list: |
| if (m_writingDirection == Horizontal) { |
| switch (m_cueAlignment) { |
| case Start: |
| if (m_displayDirection == CSSValueLtr) |
| m_displayPosition.first = computedTextPosition; |
| else |
| m_displayPosition.first = 100 - computedTextPosition - m_displaySize; |
| break; |
| case End: |
| if (m_displayDirection == CSSValueRtl) |
| m_displayPosition.first = 100 - computedTextPosition; |
| else |
| m_displayPosition.first = computedTextPosition - m_displaySize; |
| break; |
| case Left: |
| if (m_displayDirection == CSSValueLtr) |
| m_displayPosition.first = computedTextPosition; |
| else |
| m_displayPosition.first = 100 - computedTextPosition; |
| break; |
| case Right: |
| if (m_displayDirection == CSSValueLtr) |
| m_displayPosition.first = computedTextPosition - m_displaySize; |
| else |
| m_displayPosition.first = 100 - computedTextPosition - m_displaySize; |
| break; |
| case Center: |
| if (m_displayDirection == CSSValueLtr) |
| m_displayPosition.first = computedTextPosition - m_displaySize / 2; |
| else |
| m_displayPosition.first = 100 - computedTextPosition - m_displaySize / 2; |
| break; |
| case NumberOfAlignments: |
| ASSERT_NOT_REACHED(); |
| } |
| } |
| |
| // A text track cue has a text track cue computed line position whose value |
| // is defined in terms of the other aspects of the cue. |
| m_computedLinePosition = calculateComputedLinePosition(); |
| |
| // 10.9 Determine the value of whichever of x-position or y-position is not |
| // yet calculated for cue as per the appropriate rules from the following |
| // list: |
| if (m_snapToLines && !m_displayPosition.second && m_writingDirection == Horizontal) |
| m_displayPosition.second = 0; |
| |
| if (!m_snapToLines && !m_displayPosition.second && m_writingDirection == Horizontal) |
| m_displayPosition.second = *m_computedLinePosition; |
| |
| if (m_snapToLines && !m_displayPosition.first |
| && (m_writingDirection == VerticalGrowingLeft || m_writingDirection == VerticalGrowingRight)) |
| m_displayPosition.first = 0; |
| |
| if (!m_snapToLines && (m_writingDirection == VerticalGrowingLeft || m_writingDirection == VerticalGrowingRight)) |
| m_displayPosition.first = *m_computedLinePosition; |
| } |
| |
| void VTTCue::markFutureAndPastNodes(ContainerNode* root, const MediaTime& previousTimestamp, const MediaTime& movieTime) |
| { |
| static NeverDestroyed<const String> timestampTag(MAKE_STATIC_STRING_IMPL("timestamp")); |
| |
| bool isPastNode = true; |
| MediaTime currentTimestamp = previousTimestamp; |
| if (currentTimestamp > movieTime) |
| isPastNode = false; |
| |
| for (RefPtr<Node> child = root->firstChild(); child; child = NodeTraversal::next(*child, root)) { |
| if (child->nodeName() == timestampTag) { |
| MediaTime currentTimestamp; |
| bool check = WebVTTParser::collectTimeStamp(child->nodeValue(), currentTimestamp); |
| ASSERT_UNUSED(check, check); |
| |
| currentTimestamp += m_originalStartTime; |
| if (currentTimestamp > movieTime) |
| isPastNode = false; |
| } |
| |
| if (is<WebVTTElement>(*child)) { |
| downcast<WebVTTElement>(*child).setIsPastNode(isPastNode); |
| // Make an elemenet id match a cue id for style matching purposes. |
| if (!id().isEmpty()) |
| downcast<WebVTTElement>(*child).setIdAttribute(id()); |
| } |
| } |
| } |
| |
| void VTTCue::updateDisplayTree(const MediaTime& movieTime) |
| { |
| // The display tree may contain WebVTT timestamp objects representing |
| // timestamps (processing instructions), along with displayable nodes. |
| |
| if (!track()->isRendered()) |
| return; |
| |
| // Mutating the VTT contents is safe because it's never exposed to author scripts. |
| ScriptDisallowedScope::EventAllowedScope allowedScopeForCueHighlightBox(*m_cueHighlightBox); |
| |
| // Clear the contents of the set. |
| m_cueHighlightBox->removeChildren(); |
| |
| // Update the two sets containing past and future WebVTT objects. |
| RefPtr<DocumentFragment> referenceTree = createCueRenderingTree(); |
| if (!referenceTree) |
| return; |
| |
| ScriptDisallowedScope::EventAllowedScope allowedScopeForReferenceTree(*referenceTree); |
| |
| markFutureAndPastNodes(referenceTree.get(), startMediaTime(), movieTime); |
| m_cueHighlightBox->appendChild(*referenceTree); |
| } |
| |
| RefPtr<TextTrackCueBox> VTTCue::getDisplayTree(const IntSize& videoSize, int fontSize) |
| { |
| Ref<VTTCueBox> displayTree = displayTreeInternal(); |
| if (!m_displayTreeShouldChange || !track()->isRendered()) |
| return displayTree; |
| |
| // 10.1 - 10.10 |
| calculateDisplayParameters(); |
| |
| // 10.11. Apply the terms of the CSS specifications to nodes within the |
| // following constraints, thus obtaining a set of CSS boxes positioned |
| // relative to an initial containing block: |
| displayTree->removeChildren(); |
| |
| // The document tree is the tree of WebVTT Node Objects rooted at nodes. |
| |
| // The children of the nodes must be wrapped in an anonymous box whose |
| // 'display' property has the value 'inline'. This is the WebVTT cue |
| // background box. |
| |
| // Note: This is contained by default in m_cueHighlightBox. |
| m_cueHighlightBox->setPseudo(ShadowPseudoIds::cue()); |
| |
| m_cueBackdropBox->setPseudo(ShadowPseudoIds::webkitMediaTextTrackDisplayBackdrop()); |
| m_cueBackdropBox->appendChild(*m_cueHighlightBox); |
| displayTree->appendChild(*m_cueBackdropBox); |
| |
| // FIXME(BUG 79916): Runs of children of WebVTT Ruby Objects that are not |
| // WebVTT Ruby Text Objects must be wrapped in anonymous boxes whose |
| // 'display' property has the value 'ruby-base'. |
| |
| displayTree->setFontSizeFromCaptionUserPrefs(fontSize); |
| displayTree->applyCSSProperties(videoSize); |
| |
| if (displayTree->document().page()) { |
| auto cssString = displayTree->document().page()->captionUserPreferencesStyleSheet(); |
| auto style = HTMLStyleElement::create(HTMLNames::styleTag, displayTree->document(), false); |
| style->setTextContent(cssString); |
| displayTree->appendChild(style); |
| } |
| |
| const auto& styleSheets = track()->styleSheets(); |
| if (styleSheets) { |
| for (const auto& cssString : *styleSheets) { |
| auto style = HTMLStyleElement::create(HTMLNames::styleTag, displayTree->document(), false); |
| style->setTextContent(cssString); |
| displayTree->appendChild(style); |
| } |
| } |
| |
| if (m_fontSize) |
| displayTree->setInlineStyleProperty(CSSPropertyFontSize, m_fontSize, CSSUnitType::CSS_PX, m_fontSizeIsImportant); |
| |
| m_displayTreeShouldChange = false; |
| |
| if (track()) { |
| if (m_region) |
| m_region->cueStyleChanged(); |
| } |
| |
| // 10.15. Let cue's text track cue display state have the CSS boxes in |
| // boxes. |
| return displayTree; |
| } |
| |
| void VTTCue::removeDisplayTree() |
| { |
| if (!hasDisplayTree()) |
| return; |
| |
| // The region needs to be informed about the cue removal. |
| if (m_notifyRegion && track()) { |
| if (m_region && m_displayTree) |
| m_region->willRemoveTextTrackCueBox(m_displayTree.get()); |
| } |
| |
| // The display tree is never exposed to author scripts so it's safe to dispatch events here. |
| ScriptDisallowedScope::EventAllowedScope allowedScope(displayTreeInternal()); |
| displayTreeInternal().remove(); |
| } |
| |
| std::pair<double, double> VTTCue::getPositionCoordinates() const |
| { |
| // This method is used for setting x and y when snap to lines is not set. |
| std::pair<double, double> coordinates; |
| |
| auto textPosition = calculateComputedTextPosition(); |
| auto computedLinePosition = m_computedLinePosition ? *m_computedLinePosition : calculateComputedLinePosition(); |
| |
| if (m_writingDirection == Horizontal && m_displayDirection == CSSValueLtr) { |
| coordinates.first = textPosition; |
| coordinates.second = computedLinePosition; |
| |
| return coordinates; |
| } |
| |
| if (m_writingDirection == Horizontal && m_displayDirection == CSSValueRtl) { |
| coordinates.first = 100 - textPosition; |
| coordinates.second = computedLinePosition; |
| |
| return coordinates; |
| } |
| |
| if (m_writingDirection == VerticalGrowingLeft) { |
| coordinates.first = 100 - *m_computedLinePosition; |
| coordinates.second = textPosition; |
| |
| return coordinates; |
| } |
| |
| if (m_writingDirection == VerticalGrowingRight) { |
| coordinates.first = computedLinePosition; |
| coordinates.second = textPosition; |
| |
| return coordinates; |
| } |
| |
| ASSERT_NOT_REACHED(); |
| |
| return coordinates; |
| } |
| |
| VTTCue::CueSetting VTTCue::settingName(VTTScanner& input) |
| { |
| CueSetting parsedSetting = None; |
| if (input.scan("vertical")) |
| parsedSetting = Vertical; |
| else if (input.scan("line")) |
| parsedSetting = Line; |
| else if (input.scan("position")) |
| parsedSetting = Position; |
| else if (input.scan("size")) |
| parsedSetting = Size; |
| else if (input.scan("align")) |
| parsedSetting = Align; |
| else if (input.scan("region")) |
| parsedSetting = Region; |
| |
| // Verify that a ':' follows. |
| if (parsedSetting != None && input.scan(':')) |
| return parsedSetting; |
| |
| return None; |
| } |
| |
| void VTTCue::setCueSettings(const String& inputString) |
| { |
| if (inputString.isEmpty()) |
| return; |
| |
| VTTScanner input(inputString); |
| |
| while (!input.isAtEnd()) { |
| |
| // The WebVTT cue settings part of a WebVTT cue consists of zero or more of the following components, in any order, |
| // separated from each other by one or more U+0020 SPACE characters or U+0009 CHARACTER TABULATION (tab) characters. |
| input.skipWhile<WebVTTParser::isValidSettingDelimiter>(); |
| if (input.isAtEnd()) |
| break; |
| |
| // When the user agent is to parse the WebVTT settings given by a string input for a text track cue cue, |
| // the user agent must run the following steps: |
| // 1. Let settings be the result of splitting input on spaces. |
| // 2. For each token setting in the list settings, run the following substeps: |
| // 1. If setting does not contain a U+003A COLON character (:), or if the first U+003A COLON character (:) |
| // in setting is either the first or last character of setting, then jump to the step labeled next setting. |
| // 2. Let name be the leading substring of setting up to and excluding the first U+003A COLON character (:) in that string. |
| CueSetting name = settingName(input); |
| |
| // 3. Let value be the trailing substring of setting starting from the character immediately after the first U+003A COLON character (:) in that string. |
| VTTScanner::Run valueRun = input.collectUntil<WebVTTParser::isValidSettingDelimiter>(); |
| |
| // 4. Run the appropriate substeps that apply for the value of name, as follows: |
| switch (name) { |
| case Vertical: { |
| // If name is a case-sensitive match for "vertical" |
| // 1. If value is a case-sensitive match for the string "rl", then let cue's text track cue writing direction |
| // be vertical growing left. |
| if (input.scanRun(valueRun, verticalGrowingLeftKeyword())) |
| m_writingDirection = VerticalGrowingLeft; |
| |
| // 2. Otherwise, if value is a case-sensitive match for the string "lr", then let cue's text track cue writing |
| // direction be vertical growing right. |
| else if (input.scanRun(valueRun, verticalGrowingRightKeyword())) |
| m_writingDirection = VerticalGrowingRight; |
| |
| else |
| LOG(Media, "VTTCue::setCueSettings, invalid Vertical"); |
| break; |
| } |
| case Line: { |
| bool isValid = false; |
| do { |
| // 1-2 - Collect chars that are either '-', '%', or a digit. |
| // 1. If value contains any characters other than U+002D HYPHEN-MINUS characters (-), U+0025 PERCENT SIGN |
| // characters (%), and characters in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9), then jump |
| // to the step labeled next setting. |
| float linePosition; |
| bool isNegative; |
| if (!input.scanFloat(linePosition, &isNegative)) |
| break; |
| |
| bool isPercentage = input.scan('%'); |
| if (!input.isAt(valueRun.end())) { |
| if (!input.scan(',')) |
| break; |
| // FIXME: implement handling of line setting alignment. |
| if (!input.scan(startKeyword().characters8(), startKeyword().length()) |
| && !input.scan(centerKeyword().characters8(), centerKeyword().length()) |
| && !input.scan(endKeyword().characters8(), endKeyword().length())) { |
| LOG(Media, "VTTCue::setCueSettings, invalid line setting alignment"); |
| break; |
| } |
| } |
| |
| // 2. If value does not contain at least one character in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT |
| // NINE (9), then jump to the step labeled next setting. |
| // 3. If any character in value other than the first character is a U+002D HYPHEN-MINUS character (-), then |
| // jump to the step labeled next setting. |
| // 4. If any character in value other than the last character is a U+0025 PERCENT SIGN character (%), then |
| // jump to the step labeled next setting. |
| // 5. If the first character in value is a U+002D HYPHEN-MINUS character (-) and the last character in value is a |
| // U+0025 PERCENT SIGN character (%), then jump to the step labeled next setting. |
| if (isPercentage && isNegative) |
| break; |
| |
| // 6. Ignoring the trailing percent sign, if any, interpret value as a (potentially signed) integer, and |
| // let number be that number. |
| // 7. If the last character in value is a U+0025 PERCENT SIGN character (%), but number is not in the range |
| // 0 ≤ number ≤ 100, then jump to the step labeled next setting. |
| // 8. Let cue's text track cue line position be number. |
| // 9. If the last character in value is a U+0025 PERCENT SIGN character (%), then let cue's text track cue |
| // snap-to-lines flag be false. Otherwise, let it be true. |
| if (isPercentage) { |
| if (linePosition < 0 || linePosition > 100) |
| break; |
| |
| // 10 - If '%' then set snap-to-lines flag to false. |
| m_snapToLines = false; |
| } else { |
| if (linePosition - static_cast<int>(linePosition)) |
| break; |
| |
| m_snapToLines = true; |
| } |
| |
| m_linePosition = linePosition; |
| isValid = true; |
| } while (0); |
| |
| if (!isValid) |
| LOG(Media, "VTTCue::setCueSettings, invalid Line"); |
| |
| break; |
| } |
| case Position: { |
| float position; |
| if (WebVTTParser::parseFloatPercentageValue(input, position) && input.isAt(valueRun.end())) |
| m_textPosition = position; |
| else |
| LOG(Media, "VTTCue::setCueSettings, invalid Position"); |
| break; |
| } |
| case Size: { |
| float cueSize; |
| if (WebVTTParser::parseFloatPercentageValue(input, cueSize) && input.isAt(valueRun.end())) |
| m_cueSize = cueSize; |
| else |
| LOG(Media, "VTTCue::setCueSettings, invalid Size"); |
| break; |
| } |
| case Align: { |
| // 1. If value is a case-sensitive match for the string "start", then let cue's text track cue alignment be start alignment. |
| if (input.scanRun(valueRun, startKeyword())) |
| m_cueAlignment = Start; |
| |
| // 2. If value is a case-sensitive match for the string "center", then let cue's text track cue alignment be center alignment. |
| else if (input.scanRun(valueRun, centerKeyword())) |
| m_cueAlignment = Center; |
| |
| // 3. If value is a case-sensitive match for the string "end", then let cue's text track cue alignment be end alignment. |
| else if (input.scanRun(valueRun, endKeyword())) |
| m_cueAlignment = End; |
| |
| // 4. If value is a case-sensitive match for the string "left", then let cue's text track cue alignment be left alignment. |
| else if (input.scanRun(valueRun, leftKeyword())) |
| m_cueAlignment = Left; |
| |
| // 5. If value is a case-sensitive match for the string "right", then let cue's text track cue alignment be right alignment. |
| else if (input.scanRun(valueRun, rightKeyword())) |
| m_cueAlignment = Right; |
| |
| else |
| LOG(Media, "VTTCue::setCueSettings, invalid Align"); |
| |
| break; |
| } |
| case Region: { |
| m_parsedRegionId = input.extractString(valueRun); |
| break; |
| } |
| case None: |
| break; |
| } |
| |
| // Make sure the entire run is consumed. |
| input.skipRun(valueRun); |
| } |
| |
| } |
| |
| CSSValueID VTTCue::getCSSAlignment() const |
| { |
| return displayAlignmentMap[m_cueAlignment]; |
| } |
| |
| CSSValueID VTTCue::getCSSWritingDirection() const |
| { |
| return m_displayDirection; |
| } |
| |
| CSSValueID VTTCue::getCSSWritingMode() const |
| { |
| return displayWritingModeMap[m_writingDirection]; |
| } |
| |
| int VTTCue::getCSSSize() const |
| { |
| return m_displaySize; |
| } |
| |
| bool VTTCue::cueContentsMatch(const TextTrackCue& otherTextTrackCue) const |
| { |
| auto& other = downcast<VTTCue>(otherTextTrackCue); |
| return TextTrackCue::cueContentsMatch(other) |
| && text() == other.text() |
| && cueSettings() == other.cueSettings() |
| && position() == other.position() |
| && line() == other.line() |
| && size() == other.size() |
| && align() == other.align(); |
| } |
| |
| void VTTCue::setFontSize(int fontSize, const IntSize&, bool important) |
| { |
| if (fontSize == m_fontSize && important == m_fontSizeIsImportant) |
| return; |
| |
| m_displayTreeShouldChange = true; |
| m_fontSizeIsImportant = important; |
| m_fontSize = fontSize; |
| } |
| |
| void VTTCue::toJSON(JSON::Object& object) const |
| { |
| TextTrackCue::toJSON(object); |
| |
| // FIXME: Seems dangerous to include this based on LOG_DISABLED. Can we just include it unconditionally? |
| #if !LOG_DISABLED |
| object.setString("text"_s, text()); |
| #endif |
| |
| object.setString("vertical"_s, vertical()); |
| object.setBoolean("snapToLines"_s, snapToLines()); |
| if (m_linePosition) |
| object.setString("line"_s, "auto"_s); |
| else |
| object.setDouble("line"_s, *m_linePosition); |
| if (m_textPosition) |
| object.setDouble("position"_s, *m_textPosition); |
| else |
| object.setString("position"_s, "auto"_s); |
| object.setInteger("size"_s, m_cueSize); |
| object.setString("align"_s, align()); |
| } |
| |
| } // namespace WebCore |
| |
| #endif |