#include "config.h"
#if ENABLE(VIDEO_TRACK)
#include "VTTCue.h"
#include "CSSPropertyNames.h"
#include "CSSValueKeywords.h"
#include "DocumentFragment.h"
#include "Event.h"
#include "HTMLDivElement.h"
#include "HTMLSpanElement.h"
#include "Logging.h"
#include "NodeTraversal.h"
#include "RenderVTTCue.h"
#include "Text.h"
#include "TextTrack.h"
#include "TextTrackCueList.h"
#include "VTTScanner.h"
#include "WebVTTElement.h"
#include "WebVTTParser.h"
#include <wtf/MathExtras.h>
#include <wtf/text/StringBuilder.h>
#if ENABLE(WEBVTT_REGIONS)
#include "VTTRegionList.h"
#endif
namespace WebCore {
const static double DEFAULTCAPTIONFONTSIZEPERCENTAGE = 5;
static const int undefinedPosition = -1;
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()
{
DEPRECATED_DEFINE_STATIC_LOCAL(const String, start, (ASCIILiteral("start")));
return start;
}
static const String& middleKeyword()
{
DEPRECATED_DEFINE_STATIC_LOCAL(const String, middle, (ASCIILiteral("middle")));
return middle;
}
static const String& endKeyword()
{
DEPRECATED_DEFINE_STATIC_LOCAL(const String, end, (ASCIILiteral("end")));
return end;
}
static const String& leftKeyword()
{
DEPRECATED_DEFINE_STATIC_LOCAL(const String, left, ("left"));
return left;
}
static const String& rightKeyword()
{
DEPRECATED_DEFINE_STATIC_LOCAL(const String, right, ("right"));
return right;
}
static const String& horizontalKeyword()
{
return emptyString();
}
static const String& verticalGrowingLeftKeyword()
{
DEPRECATED_DEFINE_STATIC_LOCAL(const String, verticalrl, (ASCIILiteral("rl")));
return verticalrl;
}
static const String& verticalGrowingRightKeyword()
{
DEPRECATED_DEFINE_STATIC_LOCAL(const String, verticallr, (ASCIILiteral("lr")));
return verticallr;
}
PassRefPtr<VTTCueBox> VTTCueBox::create(Document& document, VTTCue& cue)
{
VTTCueBox* cueBox = new VTTCueBox(document, cue);
cueBox->setPseudo(VTTCueBox::vttCueBoxShadowPseudoId());
return adoptRef(cueBox);
}
VTTCueBox::VTTCueBox(Document& document, VTTCue& cue)
: HTMLElement(divTag, document)
, m_cue(cue)
{
setPseudo(vttCueBoxShadowPseudoId());
}
VTTCue* VTTCueBox::getCue() const
{
return &m_cue;
}
void VTTCueBox::applyCSSProperties(const IntSize& videoSize)
{
#if ENABLE(WEBVTT_REGIONS)
if (!m_cue.regionId().isEmpty()) {
setInlineStyleProperty(CSSPropertyPosition, CSSValueRelative);
return;
}
#endif
setInlineStyleProperty(CSSPropertyPosition, CSSValueAbsolute);
setInlineStyleProperty(CSSPropertyUnicodeBidi, CSSValueWebkitPlaintext);
setInlineStyleProperty(CSSPropertyDirection, m_cue.getCSSWritingDirection());
setInlineStyleProperty(CSSPropertyWebkitWritingMode, m_cue.getCSSWritingMode(), false);
std::pair<float, float> position = m_cue.getCSSPosition();
setInlineStyleProperty(CSSPropertyTop, static_cast<double>(position.second), CSSPrimitiveValue::CSS_PERCENTAGE);
setInlineStyleProperty(CSSPropertyLeft, static_cast<double>(position.first), CSSPrimitiveValue::CSS_PERCENTAGE);
double authorFontSize = std::min(videoSize.width(), videoSize.height()) * DEFAULTCAPTIONFONTSIZEPERCENTAGE / 100.0;
double multiplier = 1.0;
if (authorFontSize)
multiplier = m_fontSizeFromCaptionUserPrefs / authorFontSize;
double textPosition = m_cue.position();
double maxSize = 100.0;
CSSValueID alignment = m_cue.getCSSAlignment();
if (alignment == CSSValueEnd || alignment == CSSValueRight)
maxSize = textPosition;
else if (alignment == CSSValueStart || alignment == CSSValueLeft)
maxSize = 100.0 - textPosition;
double newCueSize = std::min(m_cue.getCSSSize() * multiplier, 100.0);
if (m_cue.vertical() == horizontalKeyword()) {
setInlineStyleProperty(CSSPropertyWidth, newCueSize, CSSPrimitiveValue::CSS_PERCENTAGE);
setInlineStyleProperty(CSSPropertyHeight, CSSValueAuto);
setInlineStyleProperty(CSSPropertyMinWidth, "-webkit-min-content");
setInlineStyleProperty(CSSPropertyMaxWidth, maxSize, CSSPrimitiveValue::CSS_PERCENTAGE);
if ((alignment == CSSValueMiddle || alignment == CSSValueCenter) && multiplier != 1.0)
setInlineStyleProperty(CSSPropertyLeft, static_cast<double>(position.first - (newCueSize - m_cue.getCSSSize()) / 2), CSSPrimitiveValue::CSS_PERCENTAGE);
} else {
setInlineStyleProperty(CSSPropertyWidth, CSSValueAuto);
setInlineStyleProperty(CSSPropertyHeight, newCueSize, CSSPrimitiveValue::CSS_PERCENTAGE);
setInlineStyleProperty(CSSPropertyMinHeight, "-webkit-min-content");
setInlineStyleProperty(CSSPropertyMaxHeight, maxSize, CSSPrimitiveValue::CSS_PERCENTAGE);
if ((alignment == CSSValueMiddle || alignment == CSSValueCenter) && multiplier != 1.0)
setInlineStyleProperty(CSSPropertyTop, static_cast<double>(position.second - (newCueSize - m_cue.getCSSSize()) / 2), CSSPrimitiveValue::CSS_PERCENTAGE);
}
setInlineStyleProperty(CSSPropertyTextAlign, m_cue.getCSSAlignment());
if (!m_cue.snapToLines()) {
setInlineStyleProperty(CSSPropertyWebkitTransform,
String::format("translate(-%.2f%%, -%.2f%%)", position.first, position.second));
setInlineStyleProperty(CSSPropertyWhiteSpace, CSSValuePre);
}
}
const AtomicString& VTTCueBox::vttCueBoxShadowPseudoId()
{
DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, trackDisplayBoxShadowPseudoId, ("-webkit-media-text-track-display", AtomicString::ConstructFromLiteral));
return trackDisplayBoxShadowPseudoId;
}
RenderPtr<RenderElement> VTTCueBox::createElementRenderer(PassRef<RenderStyle> style)
{
return createRenderer<RenderVTTCue>(*this, WTF::move(style));
}
const AtomicString& VTTCue::cueBackdropShadowPseudoId()
{
DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, cueBackdropShadowPseudoId, ("-webkit-media-text-track-display-backdrop", AtomicString::ConstructFromLiteral));
return cueBackdropShadowPseudoId;
}
PassRefPtr<VTTCue> VTTCue::create(ScriptExecutionContext& context, const WebVTTCueData& data)
{
return adoptRef(new VTTCue(context, data));
}
VTTCue::VTTCue(ScriptExecutionContext& context, const MediaTime& start, const MediaTime& end, const String& content)
: TextTrackCue(context, start, end)
, m_content(content)
{
initialize(context);
}
VTTCue::VTTCue(ScriptExecutionContext& context, const WebVTTCueData& cueData)
: TextTrackCue(context, MediaTime::zeroTime(), MediaTime::zeroTime())
{
initialize(context);
setText(cueData.content());
setStartTime(cueData.startTime());
setEndTime(cueData.endTime());
setId(cueData.id());
setCueSettings(cueData.settings());
m_originalStartTime = cueData.originalStartTime();
}
VTTCue::~VTTCue()
{
if (!hasDisplayTree())
return;
displayTreeInternal()->remove(ASSERT_NO_EXCEPTION);
}
void VTTCue::initialize(ScriptExecutionContext& context)
{
m_linePosition = undefinedPosition;
m_computedLinePosition = undefinedPosition;
m_textPosition = 50;
m_cueSize = 100;
m_writingDirection = Horizontal;
m_cueAlignment = Middle;
m_webVTTNodeTree = nullptr;
m_cueBackdropBox = HTMLDivElement::create(toDocument(context));
m_cueHighlightBox = HTMLSpanElement::create(spanTag, toDocument(context));
m_displayDirection = CSSValueLtr;
m_displaySize = 0;
m_snapToLines = true;
m_displayTreeShouldChange = true;
m_notifyRegion = true;
m_originalStartTime = MediaTime::zeroTime();
}
PassRefPtr<VTTCueBox> VTTCue::createDisplayTree()
{
return VTTCueBox::create(ownerDocument(), *this);
}
VTTCueBox* VTTCue::displayTreeInternal()
{
if (!m_displayTree)
m_displayTree = createDisplayTree();
return m_displayTree.get();
}
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();
}
}
void VTTCue::setVertical(const String& value, ExceptionCode& ec)
{
WritingDirection direction = m_writingDirection;
if (value == horizontalKeyword())
direction = Horizontal;
else if (value == verticalGrowingLeftKeyword())
direction = VerticalGrowingLeft;
else if (value == verticalGrowingRightKeyword())
direction = VerticalGrowingRight;
else
ec = SYNTAX_ERR;
if (direction == m_writingDirection)
return;
willChange();
m_writingDirection = direction;
didChange();
}
void VTTCue::setSnapToLines(bool value)
{
if (m_snapToLines == value)
return;
willChange();
m_snapToLines = value;
didChange();
}
void VTTCue::setLine(double position, ExceptionCode& ec)
{
if (!m_snapToLines && (position < 0 || position > 100)) {
ec = INDEX_SIZE_ERR;
return;
}
if (m_linePosition == position)
return;
willChange();
m_linePosition = position;
m_computedLinePosition = calculateComputedLinePosition();
didChange();
}
void VTTCue::setPosition(double position, ExceptionCode& ec)
{
if (position < 0 || position > 100) {
ec = INDEX_SIZE_ERR;
return;
}
if (m_textPosition == position)
return;
willChange();
m_textPosition = position;
didChange();
}
void VTTCue::setSize(int size, ExceptionCode& ec)
{
if (size < 0 || size > 100) {
ec = INDEX_SIZE_ERR;
return;
}
if (m_cueSize == size)
return;
willChange();
m_cueSize = size;
didChange();
}
const String& VTTCue::align() const
{
switch (m_cueAlignment) {
case Start:
return startKeyword();
case Middle:
return middleKeyword();
case End:
return endKeyword();
case Left:
return leftKeyword();
case Right:
return rightKeyword();
default:
ASSERT_NOT_REACHED();
return emptyString();
}
}
void VTTCue::setAlign(const String& value, ExceptionCode& ec)
{
CueAlignment alignment = m_cueAlignment;
if (value == startKeyword())
alignment = Start;
else if (value == middleKeyword())
alignment = Middle;
else if (value == endKeyword())
alignment = End;
else if (value == leftKeyword())
alignment = Left;
else if (value == rightKeyword())
alignment = Right;
else
ec = SYNTAX_ERR;
if (alignment == m_cueAlignment)
return;
willChange();
m_cueAlignment = alignment;
didChange();
}
void VTTCue::setText(const String& text)
{
if (m_content == text)
return;
willChange();
m_webVTTNodeTree = 0;
m_content = text;
didChange();
}
void VTTCue::createWebVTTNodeTree()
{
if (!m_webVTTNodeTree)
m_webVTTNodeTree = WebVTTParser::createDocumentFragmentFromCueText(ownerDocument(), m_content);
}
void VTTCue::copyWebVTTNodeToDOMTree(ContainerNode* webVTTNode, ContainerNode* parent)
{
for (Node* node = webVTTNode->firstChild(); node; node = node->nextSibling()) {
RefPtr<Node> clonedNode;
if (node->isWebVTTElement())
clonedNode = toWebVTTElement(node)->createEquivalentHTMLElement(ownerDocument());
else
clonedNode = node->cloneNode(false);
parent->appendChild(clonedNode, ASSERT_NO_EXCEPTION);
if (node->isContainerNode())
copyWebVTTNodeToDOMTree(toContainerNode(node), toContainerNode(clonedNode.get()));
}
}
PassRefPtr<DocumentFragment> VTTCue::getCueAsHTML()
{
createWebVTTNodeTree();
if (!m_webVTTNodeTree)
return 0;
RefPtr<DocumentFragment> clonedFragment = DocumentFragment::create(ownerDocument());
copyWebVTTNodeToDOMTree(m_webVTTNodeTree.get(), clonedFragment.get());
return clonedFragment.release();
}
PassRefPtr<DocumentFragment> VTTCue::createCueRenderingTree()
{
RefPtr<DocumentFragment> clonedFragment;
createWebVTTNodeTree();
if (!m_webVTTNodeTree)
return 0;
clonedFragment = DocumentFragment::create(ownerDocument());
m_webVTTNodeTree->cloneChildNodes(clonedFragment.get());
return clonedFragment.release();
}
#if ENABLE(WEBVTT_REGIONS)
void VTTCue::setRegionId(const String& regionId)
{
if (m_regionId == regionId)
return;
willChange();
m_regionId = regionId;
didChange();
}
void VTTCue::notifyRegionWhenRemovingDisplayTree(bool notifyRegion)
{
m_notifyRegion = notifyRegion;
}
#endif
void VTTCue::setIsActive(bool active)
{
TextTrackCue::setIsActive(active);
if (!active) {
if (!hasDisplayTree())
return;
removeDisplayTree();
}
}
int VTTCue::calculateComputedLinePosition()
{
if (m_linePosition != undefinedPosition)
return m_linePosition;
if (!m_snapToLines)
return 100;
if (!track())
return -1;
int n = track()->trackIndexRelativeToRenderedTracks();
n++;
n = -n;
return n;
}
static bool isCueParagraphSeparator(UChar character)
{
return u_charType(character) == U_PARAGRAPH_SEPARATOR;
}
void VTTCue::determineTextDirection()
{
DEPRECATED_DEFINE_STATIC_LOCAL(const String, rtTag, (ASCIILiteral("rt")));
createWebVTTNodeTree();
if (!m_webVTTNodeTree)
return;
StringBuilder paragraphBuilder;
for (Node* node = m_webVTTNodeTree->firstChild(); node; node = NodeTraversal::next(node, m_webVTTNodeTree.get())) {
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;
}
}
}
}
void VTTCue::calculateDisplayParameters()
{
determineTextDirection();
int maximumSize = m_textPosition;
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 - m_textPosition;
} 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 = m_textPosition;
} else if (m_cueAlignment == Middle) {
maximumSize = m_textPosition <= 50 ? m_textPosition : (100 - m_textPosition);
maximumSize = maximumSize * 2;
} else
ASSERT_NOT_REACHED();
m_displaySize = std::min(m_cueSize, maximumSize);
if (m_writingDirection == Horizontal) {
switch (m_cueAlignment) {
case Start:
if (m_displayDirection == CSSValueLtr)
m_displayPosition.first = m_textPosition;
else
m_displayPosition.first = 100 - m_textPosition - m_displaySize;
break;
case End:
if (m_displayDirection == CSSValueRtl)
m_displayPosition.first = 100 - m_textPosition;
else
m_displayPosition.first = m_textPosition - m_displaySize;
break;
case Left:
if (m_displayDirection == CSSValueLtr)
m_displayPosition.first = m_textPosition;
else
m_displayPosition.first = 100 - m_textPosition;
break;
case Right:
if (m_displayDirection == CSSValueLtr)
m_displayPosition.first = m_textPosition - m_displaySize;
else
m_displayPosition.first = 100 - m_textPosition - m_displaySize;
break;
case Middle:
if (m_displayDirection == CSSValueLtr)
m_displayPosition.first = m_textPosition - m_displaySize / 2;
else
m_displayPosition.first = 100 - m_textPosition - m_displaySize / 2;
break;
case NumberOfAlignments:
ASSERT_NOT_REACHED();
}
}
m_computedLinePosition = calculateComputedLinePosition();
if (m_snapToLines && m_displayPosition.second == undefinedPosition && m_writingDirection == Horizontal)
m_displayPosition.second = 0;
if (!m_snapToLines && m_displayPosition.second == undefinedPosition && m_writingDirection == Horizontal)
m_displayPosition.second = m_computedLinePosition;
if (m_snapToLines && m_displayPosition.first == undefinedPosition
&& (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)
{
DEPRECATED_DEFINE_STATIC_LOCAL(const String, timestampTag, (ASCIILiteral("timestamp")));
bool isPastNode = true;
MediaTime currentTimestamp = previousTimestamp;
if (currentTimestamp > movieTime)
isPastNode = false;
for (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 (child->isWebVTTElement()) {
toWebVTTElement(child)->setIsPastNode(isPastNode);
if (!id().isEmpty())
toElement(child)->setIdAttribute(id());
}
}
}
void VTTCue::updateDisplayTree(const MediaTime& movieTime)
{
if (!track()->isRendered())
return;
m_cueHighlightBox->removeChildren();
RefPtr<DocumentFragment> referenceTree = createCueRenderingTree();
if (!referenceTree)
return;
markFutureAndPastNodes(referenceTree.get(), startMediaTime(), movieTime);
m_cueHighlightBox->appendChild(referenceTree);
}
VTTCueBox* VTTCue::getDisplayTree(const IntSize& videoSize, int fontSize)
{
RefPtr<VTTCueBox> displayTree = displayTreeInternal();
if (!m_displayTreeShouldChange || !track()->isRendered())
return displayTree.get();
calculateDisplayParameters();
displayTree->removeChildren();
m_cueHighlightBox->setPseudo(cueShadowPseudoId());
m_cueBackdropBox->setPseudo(cueBackdropShadowPseudoId());
m_cueBackdropBox->appendChild(m_cueHighlightBox, ASSERT_NO_EXCEPTION);
displayTree->appendChild(m_cueBackdropBox, ASSERT_NO_EXCEPTION);
displayTree->setFontSizeFromCaptionUserPrefs(fontSize);
displayTree->applyCSSProperties(videoSize);
m_displayTreeShouldChange = false;
return displayTree.get();
}
void VTTCue::removeDisplayTree()
{
#if ENABLE(WEBVTT_REGIONS)
if (m_notifyRegion && track()) {
if (VTTRegionList* regions = track()->regions()) {
if (VTTRegion* region = regions->getRegionById(m_regionId))
region->willRemoveTextTrackCueBox(m_displayTree.get());
}
}
#endif
if (!hasDisplayTree())
return;
displayTreeInternal()->remove(ASSERT_NO_EXCEPTION);
}
std::pair<double, double> VTTCue::getPositionCoordinates() const
{
std::pair<double, double> coordinates;
if (m_writingDirection == Horizontal && m_displayDirection == CSSValueLtr) {
coordinates.first = m_textPosition;
coordinates.second = m_computedLinePosition;
return coordinates;
}
if (m_writingDirection == Horizontal && m_displayDirection == CSSValueRtl) {
coordinates.first = 100 - m_textPosition;
coordinates.second = m_computedLinePosition;
return coordinates;
}
if (m_writingDirection == VerticalGrowingLeft) {
coordinates.first = 100 - m_computedLinePosition;
coordinates.second = m_textPosition;
return coordinates;
}
if (m_writingDirection == VerticalGrowingRight) {
coordinates.first = m_computedLinePosition;
coordinates.second = m_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;
#if ENABLE(WEBVTT_REGIONS)
else if (input.scan("region"))
parsedSetting = RegionId;
#endif
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()) {
input.skipWhile<WebVTTParser::isValidSettingDelimiter>();
if (input.isAtEnd())
break;
CueSetting name = settingName(input);
VTTScanner::Run valueRun = input.collectUntil<WebVTTParser::isValidSettingDelimiter>();
switch (name) {
case Vertical: {
if (input.scanRun(valueRun, verticalGrowingLeftKeyword()))
m_writingDirection = VerticalGrowingLeft;
else if (input.scanRun(valueRun, verticalGrowingRightKeyword()))
m_writingDirection = VerticalGrowingRight;
else
LOG(Media, "VTTCue::setCueSettings, invalid Vertical");
break;
}
case Line: {
bool isValid = false;
do {
float linePosition;
bool isNegative;
if (!input.scanFloat(linePosition, &isNegative))
break;
bool isPercentage = input.scan('%');
if (!input.isAt(valueRun.end()))
break;
if (isPercentage && isNegative)
break;
if (isPercentage) {
if (linePosition < 0 || linePosition > 100)
break;
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: {
if (input.scanRun(valueRun, startKeyword()))
m_cueAlignment = Start;
else if (input.scanRun(valueRun, middleKeyword()))
m_cueAlignment = Middle;
else if (input.scanRun(valueRun, endKeyword()))
m_cueAlignment = End;
else if (input.scanRun(valueRun, leftKeyword()))
m_cueAlignment = Left;
else if (input.scanRun(valueRun, rightKeyword()))
m_cueAlignment = Right;
else
LOG(Media, "VTTCue::setCueSettings, invalid Align");
break;
}
#if ENABLE(WEBVTT_REGIONS)
case RegionId:
m_regionId = input.extractString(valueRun);
break;
#endif
case None:
break;
}
input.skipRun(valueRun);
}
#if ENABLE(WEBVTT_REGIONS)
if (m_regionId.isEmpty())
return;
if (m_linePosition != undefinedPosition || m_cueSize != 100 || m_writingDirection != Horizontal)
m_regionId = emptyString();
#endif
}
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;
}
std::pair<double, double> VTTCue::getCSSPosition() const
{
if (!m_snapToLines)
return getPositionCoordinates();
return m_displayPosition;
}
bool VTTCue::cueContentsMatch(const TextTrackCue& cue) const
{
const VTTCue* vttCue = toVTTCue(&cue);
if (text() != vttCue->text())
return false;
if (cueSettings() != vttCue->cueSettings())
return false;
if (position() != vttCue->position())
return false;
if (line() != vttCue->line())
return false;
if (size() != vttCue->size())
return false;
if (align() != vttCue->align())
return false;
return true;
}
bool VTTCue::isEqual(const TextTrackCue& cue, TextTrackCue::CueMatchRules match) const
{
if (!TextTrackCue::isEqual(cue, match))
return false;
if (cue.cueType() != WebVTT)
return false;
return cueContentsMatch(cue);
}
bool VTTCue::doesExtendCue(const TextTrackCue& cue) const
{
if (!cueContentsMatch(cue))
return false;
return TextTrackCue::doesExtendCue(cue);
}
void VTTCue::setFontSize(int fontSize, const IntSize&, bool important)
{
if (!hasDisplayTree() || !fontSize)
return;
LOG(Media, "TextTrackCue::setFontSize - setting cue font size to %i", fontSize);
m_displayTreeShouldChange = true;
displayTreeInternal()->setInlineStyleProperty(CSSPropertyFontSize, fontSize, CSSPrimitiveValue::CSS_PX, important);
}
VTTCue* toVTTCue(TextTrackCue* cue)
{
return const_cast<VTTCue*>(toVTTCue(const_cast<const TextTrackCue*>(cue)));
}
const VTTCue* toVTTCue(const TextTrackCue* cue)
{
ASSERT_WITH_SECURITY_IMPLICATION(cue->isRenderable());
return static_cast<const VTTCue*>(cue);
}
}
#endif