This source file includes following definitions.
- isInfiniteOrNonNumberOrNonPercentage
- m_scrollTimer
- setTrack
- setId
- setWidth
- setHeight
- setRegionAnchorX
- setRegionAnchorY
- setViewportAnchorX
- setViewportAnchorY
- scroll
- setScroll
- updateParametersFromRegion
- setRegionSettings
- scanSettingName
- parsedEntireRun
- parseSettingValue
- textTrackCueContainerShadowPseudoId
- textTrackCueContainerScrollingClass
- textTrackRegionShadowPseudoId
- getDisplayTree
- willRemoveVTTCueBox
- appendVTTCueBox
- displayLastVTTCueBox
- prepareRegionDisplayTree
- startTimer
- stopTimer
- scrollTimerFired
#include "config.h"
#include "core/html/track/vtt/VTTRegion.h"
#include "bindings/v8/ExceptionMessages.h"
#include "bindings/v8/ExceptionState.h"
#include "core/dom/ClientRect.h"
#include "core/dom/DOMTokenList.h"
#include "core/html/HTMLDivElement.h"
#include "core/html/track/vtt/VTTParser.h"
#include "core/html/track/vtt/VTTScanner.h"
#include "core/rendering/RenderInline.h"
#include "core/rendering/RenderObject.h"
#include "platform/Logging.h"
#include "wtf/MathExtras.h"
#include "wtf/text/StringBuilder.h"
namespace WebCore {
static const float defaultWidth = 100;
static const long defaultHeightInLines = 3;
static const float defaultAnchorPointX = 0;
static const float defaultAnchorPointY = 100;
static const bool defaultScroll = false;
static const float lineHeight = 5.33;
static const float scrollTime = 0.433;
static bool isInfiniteOrNonNumberOrNonPercentage(double value, const char* method, ExceptionState& exceptionState)
{
if (!std::isfinite(value)) {
exceptionState.throwTypeError(ExceptionMessages::notAFiniteNumber(value));
return true;
}
if (value < 0 || value > 100) {
exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexOutsideRange("value", value, 0.0, ExceptionMessages::InclusiveBound, 100.0, ExceptionMessages::InclusiveBound));
return true;
}
return false;
}
VTTRegion::VTTRegion()
: m_id(emptyString())
, m_width(defaultWidth)
, m_heightInLines(defaultHeightInLines)
, m_regionAnchor(FloatPoint(defaultAnchorPointX, defaultAnchorPointY))
, m_viewportAnchor(FloatPoint(defaultAnchorPointX, defaultAnchorPointY))
, m_scroll(defaultScroll)
, m_track(0)
, m_currentTop(0)
, m_scrollTimer(this, &VTTRegion::scrollTimerFired)
{
}
VTTRegion::~VTTRegion()
{
}
void VTTRegion::setTrack(TextTrack* track)
{
m_track = track;
}
void VTTRegion::setId(const String& id)
{
m_id = id;
}
void VTTRegion::setWidth(double value, ExceptionState& exceptionState)
{
if (isInfiniteOrNonNumberOrNonPercentage(value, "width", exceptionState))
return;
m_width = value;
}
void VTTRegion::setHeight(long value, ExceptionState& exceptionState)
{
if (value < 0) {
exceptionState.throwDOMException(IndexSizeError, "The height provided (" + String::number(value) + ") is negative.");
return;
}
m_heightInLines = value;
}
void VTTRegion::setRegionAnchorX(double value, ExceptionState& exceptionState)
{
if (isInfiniteOrNonNumberOrNonPercentage(value, "regionAnchorX", exceptionState))
return;
m_regionAnchor.setX(value);
}
void VTTRegion::setRegionAnchorY(double value, ExceptionState& exceptionState)
{
if (isInfiniteOrNonNumberOrNonPercentage(value, "regionAnchorY", exceptionState))
return;
m_regionAnchor.setY(value);
}
void VTTRegion::setViewportAnchorX(double value, ExceptionState& exceptionState)
{
if (isInfiniteOrNonNumberOrNonPercentage(value, "viewportAnchorX", exceptionState))
return;
m_viewportAnchor.setX(value);
}
void VTTRegion::setViewportAnchorY(double value, ExceptionState& exceptionState)
{
if (isInfiniteOrNonNumberOrNonPercentage(value, "viewportAnchorY", exceptionState))
return;
m_viewportAnchor.setY(value);
}
const AtomicString VTTRegion::scroll() const
{
DEFINE_STATIC_LOCAL(const AtomicString, upScrollValueKeyword, ("up", AtomicString::ConstructFromLiteral));
if (m_scroll)
return upScrollValueKeyword;
return "";
}
void VTTRegion::setScroll(const AtomicString& value, ExceptionState& exceptionState)
{
DEFINE_STATIC_LOCAL(const AtomicString, upScrollValueKeyword, ("up", AtomicString::ConstructFromLiteral));
if (value != emptyString() && value != upScrollValueKeyword) {
exceptionState.throwDOMException(SyntaxError, "The value provided ('" + value + "') is invalid. The 'scroll' property must be either the empty string, or 'up'.");
return;
}
m_scroll = value == upScrollValueKeyword;
}
void VTTRegion::updateParametersFromRegion(VTTRegion* region)
{
m_heightInLines = region->height();
m_width = region->width();
m_regionAnchor = FloatPoint(region->regionAnchorX(), region->regionAnchorY());
m_viewportAnchor = FloatPoint(region->viewportAnchorX(), region->viewportAnchorY());
setScroll(region->scroll(), ASSERT_NO_EXCEPTION);
}
void VTTRegion::setRegionSettings(const String& inputString)
{
m_settings = inputString;
VTTScanner input(inputString);
while (!input.isAtEnd()) {
input.skipWhile<VTTParser::isValidSettingDelimiter>();
if (input.isAtEnd())
break;
RegionSetting name = scanSettingName(input);
if (name == None || !input.scan('=')) {
input.skipUntil<VTTParser::isASpace>();
continue;
}
parseSettingValue(name, input);
}
}
VTTRegion::RegionSetting VTTRegion::scanSettingName(VTTScanner& input)
{
if (input.scan("id"))
return Id;
if (input.scan("height"))
return Height;
if (input.scan("width"))
return Width;
if (input.scan("viewportanchor"))
return ViewportAnchor;
if (input.scan("regionanchor"))
return RegionAnchor;
if (input.scan("scroll"))
return Scroll;
return None;
}
static inline bool parsedEntireRun(const VTTScanner& input, const VTTScanner::Run& run)
{
return input.isAt(run.end());
}
void VTTRegion::parseSettingValue(RegionSetting setting, VTTScanner& input)
{
DEFINE_STATIC_LOCAL(const AtomicString, scrollUpValueKeyword, ("up", AtomicString::ConstructFromLiteral));
VTTScanner::Run valueRun = input.collectUntil<VTTParser::isASpace>();
switch (setting) {
case Id: {
String stringValue = input.extractString(valueRun);
if (stringValue.find("-->") == kNotFound)
m_id = stringValue;
break;
}
case Width: {
float floatWidth;
if (VTTParser::parseFloatPercentageValue(input, floatWidth) && parsedEntireRun(input, valueRun))
m_width = floatWidth;
else
WTF_LOG(Media, "VTTRegion::parseSettingValue, invalid Width");
break;
}
case Height: {
int number;
if (input.scanDigits(number) && parsedEntireRun(input, valueRun))
m_heightInLines = number;
else
WTF_LOG(Media, "VTTRegion::parseSettingValue, invalid Height");
break;
}
case RegionAnchor: {
FloatPoint anchor;
if (VTTParser::parseFloatPercentageValuePair(input, ',', anchor) && parsedEntireRun(input, valueRun))
m_regionAnchor = anchor;
else
WTF_LOG(Media, "VTTRegion::parseSettingValue, invalid RegionAnchor");
break;
}
case ViewportAnchor: {
FloatPoint anchor;
if (VTTParser::parseFloatPercentageValuePair(input, ',', anchor) && parsedEntireRun(input, valueRun))
m_viewportAnchor = anchor;
else
WTF_LOG(Media, "VTTRegion::parseSettingValue, invalid ViewportAnchor");
break;
}
case Scroll:
if (input.scanRun(valueRun, scrollUpValueKeyword))
m_scroll = true;
else
WTF_LOG(Media, "VTTRegion::parseSettingValue, invalid Scroll");
break;
case None:
break;
}
input.skipRun(valueRun);
}
const AtomicString& VTTRegion::textTrackCueContainerShadowPseudoId()
{
DEFINE_STATIC_LOCAL(const AtomicString, trackRegionCueContainerPseudoId,
("-webkit-media-text-track-region-container", AtomicString::ConstructFromLiteral));
return trackRegionCueContainerPseudoId;
}
const AtomicString& VTTRegion::textTrackCueContainerScrollingClass()
{
DEFINE_STATIC_LOCAL(const AtomicString, trackRegionCueContainerScrollingClass,
("scrolling", AtomicString::ConstructFromLiteral));
return trackRegionCueContainerScrollingClass;
}
const AtomicString& VTTRegion::textTrackRegionShadowPseudoId()
{
DEFINE_STATIC_LOCAL(const AtomicString, trackRegionShadowPseudoId,
("-webkit-media-text-track-region", AtomicString::ConstructFromLiteral));
return trackRegionShadowPseudoId;
}
PassRefPtr<HTMLDivElement> VTTRegion::getDisplayTree(Document& document)
{
if (!m_regionDisplayTree) {
m_regionDisplayTree = HTMLDivElement::create(document);
prepareRegionDisplayTree();
}
return m_regionDisplayTree;
}
void VTTRegion::willRemoveVTTCueBox(VTTCueBox* box)
{
WTF_LOG(Media, "VTTRegion::willRemoveVTTCueBox");
ASSERT(m_cueContainer->contains(box));
double boxHeight = box->getBoundingClientRect()->bottom() - box->getBoundingClientRect()->top();
m_cueContainer->classList().remove(textTrackCueContainerScrollingClass(), ASSERT_NO_EXCEPTION);
m_currentTop += boxHeight;
m_cueContainer->setInlineStyleProperty(CSSPropertyTop, m_currentTop, CSSPrimitiveValue::CSS_PX);
}
void VTTRegion::appendVTTCueBox(PassRefPtr<VTTCueBox> displayBox)
{
ASSERT(m_cueContainer);
if (m_cueContainer->contains(displayBox.get()))
return;
m_cueContainer->appendChild(displayBox);
displayLastVTTCueBox();
}
void VTTRegion::displayLastVTTCueBox()
{
WTF_LOG(Media, "VTTRegion::displayLastVTTCueBox");
ASSERT(m_cueContainer);
if (m_scrollTimer.isActive())
return;
if (isScrollingRegion())
m_cueContainer->classList().add(textTrackCueContainerScrollingClass(), ASSERT_NO_EXCEPTION);
float regionBottom = m_regionDisplayTree->getBoundingClientRect()->bottom();
for (Element* child = ElementTraversal::firstWithin(*m_cueContainer); child && !m_scrollTimer.isActive(); child = ElementTraversal::nextSibling(*child)) {
float childTop = toHTMLDivElement(child)->getBoundingClientRect()->top();
float childBottom = toHTMLDivElement(child)->getBoundingClientRect()->bottom();
if (regionBottom >= childBottom)
continue;
float height = childBottom - childTop;
m_currentTop -= std::min(height, childBottom - regionBottom);
m_cueContainer->setInlineStyleProperty(CSSPropertyTop, m_currentTop, CSSPrimitiveValue::CSS_PX);
startTimer();
}
}
void VTTRegion::prepareRegionDisplayTree()
{
ASSERT(m_regionDisplayTree);
m_regionDisplayTree->setInlineStyleProperty(CSSPropertyWidth,
m_width, CSSPrimitiveValue::CSS_PERCENTAGE);
double height = lineHeight * m_heightInLines;
m_regionDisplayTree->setInlineStyleProperty(CSSPropertyHeight,
height, CSSPrimitiveValue::CSS_VH);
double leftOffset = m_regionAnchor.x() * m_width / 100;
m_regionDisplayTree->setInlineStyleProperty(CSSPropertyLeft,
m_viewportAnchor.x() - leftOffset,
CSSPrimitiveValue::CSS_PERCENTAGE);
double topOffset = m_regionAnchor.y() * height / 100;
m_regionDisplayTree->setInlineStyleProperty(CSSPropertyTop,
m_viewportAnchor.y() - topOffset,
CSSPrimitiveValue::CSS_PERCENTAGE);
m_cueContainer = HTMLDivElement::create(m_regionDisplayTree->document());
m_cueContainer->setInlineStyleProperty(CSSPropertyTop,
0.0,
CSSPrimitiveValue::CSS_PX);
m_cueContainer->setShadowPseudoId(textTrackCueContainerShadowPseudoId());
m_regionDisplayTree->appendChild(m_cueContainer);
m_regionDisplayTree->setShadowPseudoId(textTrackRegionShadowPseudoId());
}
void VTTRegion::startTimer()
{
WTF_LOG(Media, "VTTRegion::startTimer");
if (m_scrollTimer.isActive())
return;
double duration = isScrollingRegion() ? scrollTime : 0;
m_scrollTimer.startOneShot(duration, FROM_HERE);
}
void VTTRegion::stopTimer()
{
WTF_LOG(Media, "VTTRegion::stopTimer");
if (m_scrollTimer.isActive())
m_scrollTimer.stop();
}
void VTTRegion::scrollTimerFired(Timer<VTTRegion>*)
{
WTF_LOG(Media, "VTTRegion::scrollTimerFired");
stopTimer();
displayLastVTTCueBox();
}
}