| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. | |
| 3 * | |
| 4 * Redistribution and use in source and binary forms, with or without | |
| 5 * modification, are permitted provided that the following conditions are | |
| 6 * met: | |
| 7 * | |
| 8 * * Redistributions of source code must retain the above copyright | |
| 9 * notice, this list of conditions and the following disclaimer. | |
| 10 * * Redistributions in binary form must reproduce the above | |
| 11 * copyright notice, this list of conditions and the following disclaimer | |
| 12 * in the documentation and/or other materials provided with the | |
| 13 * distribution. | |
| 14 * * Neither the name of Google Inc. nor the names of its | |
| 15 * contributors may be used to endorse or promote products derived from | |
| 16 * this software without specific prior written permission. | |
| 17 * | |
| 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 29 */ | |
| 30 | |
| 31 #include "config.h" | |
| 32 #include "core/html/track/TextTrackRegion.h" | |
| 33 | |
| 34 #include "bindings/v8/ExceptionState.h" | |
| 35 #include "bindings/v8/ExceptionStatePlaceholder.h" | |
| 36 #include "core/dom/ClientRect.h" | |
| 37 #include "core/dom/DOMTokenList.h" | |
| 38 #include "core/html/HTMLDivElement.h" | |
| 39 #include "core/html/track/WebVTTParser.h" | |
| 40 #include "platform/Logging.h" | |
| 41 #include "core/rendering/RenderInline.h" | |
| 42 #include "core/rendering/RenderObject.h" | |
| 43 #include "wtf/MathExtras.h" | |
| 44 #include "wtf/text/StringBuilder.h" | |
| 45 | |
| 46 namespace WebCore { | |
| 47 | |
| 48 // The following values default values are defined within the WebVTT Regions Spe
c. | |
| 49 // https://dvcs.w3.org/hg/text-tracks/raw-file/default/608toVTT/region.html | |
| 50 | |
| 51 // The region occupies by default 100% of the width of the video viewport. | |
| 52 static const float defaultWidth = 100; | |
| 53 | |
| 54 // The region has, by default, 3 lines of text. | |
| 55 static const long defaultHeightInLines = 3; | |
| 56 | |
| 57 // The region and viewport are anchored in the bottom left corner. | |
| 58 static const float defaultAnchorPointX = 0; | |
| 59 static const float defaultAnchorPointY = 100; | |
| 60 | |
| 61 // The region doesn't have scrolling text, by default. | |
| 62 static const bool defaultScroll = false; | |
| 63 | |
| 64 // Default region line-height (vh units) | |
| 65 static const float lineHeight = 5.33; | |
| 66 | |
| 67 // Default scrolling animation time period (s). | |
| 68 static const float scrollTime = 0.433; | |
| 69 | |
| 70 TextTrackRegion::TextTrackRegion() | |
| 71 : m_id(emptyString()) | |
| 72 , m_width(defaultWidth) | |
| 73 , m_heightInLines(defaultHeightInLines) | |
| 74 , m_regionAnchor(FloatPoint(defaultAnchorPointX, defaultAnchorPointY)) | |
| 75 , m_viewportAnchor(FloatPoint(defaultAnchorPointX, defaultAnchorPointY)) | |
| 76 , m_scroll(defaultScroll) | |
| 77 , m_track(0) | |
| 78 , m_currentTop(0) | |
| 79 , m_scrollTimer(this, &TextTrackRegion::scrollTimerFired) | |
| 80 { | |
| 81 } | |
| 82 | |
| 83 TextTrackRegion::~TextTrackRegion() | |
| 84 { | |
| 85 } | |
| 86 | |
| 87 void TextTrackRegion::setTrack(TextTrack* track) | |
| 88 { | |
| 89 m_track = track; | |
| 90 } | |
| 91 | |
| 92 void TextTrackRegion::setId(const String& id) | |
| 93 { | |
| 94 m_id = id; | |
| 95 } | |
| 96 | |
| 97 void TextTrackRegion::setWidth(double value, ExceptionState& es) | |
| 98 { | |
| 99 if (std::isinf(value) || std::isnan(value)) { | |
| 100 es.throwUninformativeAndGenericTypeError(); | |
| 101 return; | |
| 102 } | |
| 103 | |
| 104 if (value < 0 || value > 100) { | |
| 105 es.throwUninformativeAndGenericDOMException(IndexSizeError); | |
| 106 return; | |
| 107 } | |
| 108 | |
| 109 m_width = value; | |
| 110 } | |
| 111 | |
| 112 void TextTrackRegion::setHeight(long value, ExceptionState& es) | |
| 113 { | |
| 114 if (value < 0) { | |
| 115 es.throwUninformativeAndGenericDOMException(IndexSizeError); | |
| 116 return; | |
| 117 } | |
| 118 | |
| 119 m_heightInLines = value; | |
| 120 } | |
| 121 | |
| 122 void TextTrackRegion::setRegionAnchorX(double value, ExceptionState& es) | |
| 123 { | |
| 124 if (std::isinf(value) || std::isnan(value)) { | |
| 125 es.throwUninformativeAndGenericTypeError(); | |
| 126 return; | |
| 127 } | |
| 128 | |
| 129 if (value < 0 || value > 100) { | |
| 130 es.throwUninformativeAndGenericDOMException(IndexSizeError); | |
| 131 return; | |
| 132 } | |
| 133 | |
| 134 m_regionAnchor.setX(value); | |
| 135 } | |
| 136 | |
| 137 void TextTrackRegion::setRegionAnchorY(double value, ExceptionState& es) | |
| 138 { | |
| 139 if (std::isinf(value) || std::isnan(value)) { | |
| 140 es.throwUninformativeAndGenericTypeError(); | |
| 141 return; | |
| 142 } | |
| 143 | |
| 144 if (value < 0 || value > 100) { | |
| 145 es.throwUninformativeAndGenericDOMException(IndexSizeError); | |
| 146 return; | |
| 147 } | |
| 148 | |
| 149 m_regionAnchor.setY(value); | |
| 150 } | |
| 151 | |
| 152 void TextTrackRegion::setViewportAnchorX(double value, ExceptionState& es) | |
| 153 { | |
| 154 if (std::isinf(value) || std::isnan(value)) { | |
| 155 es.throwUninformativeAndGenericTypeError(); | |
| 156 return; | |
| 157 } | |
| 158 | |
| 159 if (value < 0 || value > 100) { | |
| 160 es.throwUninformativeAndGenericDOMException(IndexSizeError); | |
| 161 return; | |
| 162 } | |
| 163 | |
| 164 m_viewportAnchor.setX(value); | |
| 165 } | |
| 166 | |
| 167 void TextTrackRegion::setViewportAnchorY(double value, ExceptionState& es) | |
| 168 { | |
| 169 if (std::isinf(value) || std::isnan(value)) { | |
| 170 es.throwUninformativeAndGenericTypeError(); | |
| 171 return; | |
| 172 } | |
| 173 | |
| 174 if (value < 0 || value > 100) { | |
| 175 es.throwUninformativeAndGenericDOMException(IndexSizeError); | |
| 176 return; | |
| 177 } | |
| 178 | |
| 179 m_viewportAnchor.setY(value); | |
| 180 } | |
| 181 | |
| 182 const AtomicString TextTrackRegion::scroll() const | |
| 183 { | |
| 184 DEFINE_STATIC_LOCAL(const AtomicString, upScrollValueKeyword, ("up", AtomicS
tring::ConstructFromLiteral)); | |
| 185 | |
| 186 if (m_scroll) | |
| 187 return upScrollValueKeyword; | |
| 188 | |
| 189 return ""; | |
| 190 } | |
| 191 | |
| 192 void TextTrackRegion::setScroll(const AtomicString& value, ExceptionState& es) | |
| 193 { | |
| 194 DEFINE_STATIC_LOCAL(const AtomicString, upScrollValueKeyword, ("up", AtomicS
tring::ConstructFromLiteral)); | |
| 195 | |
| 196 if (value != emptyString() && value != upScrollValueKeyword) { | |
| 197 es.throwUninformativeAndGenericDOMException(SyntaxError); | |
| 198 return; | |
| 199 } | |
| 200 | |
| 201 m_scroll = value == upScrollValueKeyword; | |
| 202 } | |
| 203 | |
| 204 void TextTrackRegion::updateParametersFromRegion(TextTrackRegion* region) | |
| 205 { | |
| 206 m_heightInLines = region->height(); | |
| 207 m_width = region->width(); | |
| 208 | |
| 209 m_regionAnchor = FloatPoint(region->regionAnchorX(), region->regionAnchorY()
); | |
| 210 m_viewportAnchor = FloatPoint(region->viewportAnchorX(), region->viewportAnc
horY()); | |
| 211 | |
| 212 setScroll(region->scroll(), ASSERT_NO_EXCEPTION); | |
| 213 } | |
| 214 | |
| 215 void TextTrackRegion::setRegionSettings(const String& input) | |
| 216 { | |
| 217 m_settings = input; | |
| 218 unsigned position = 0; | |
| 219 | |
| 220 while (position < input.length()) { | |
| 221 while (position < input.length() && WebVTTParser::isValidSettingDelimite
r(input[position])) | |
| 222 position++; | |
| 223 | |
| 224 if (position >= input.length()) | |
| 225 break; | |
| 226 | |
| 227 parseSetting(input, &position); | |
| 228 } | |
| 229 } | |
| 230 | |
| 231 TextTrackRegion::RegionSetting TextTrackRegion::getSettingFromString(const Strin
g& setting) | |
| 232 { | |
| 233 DEFINE_STATIC_LOCAL(const AtomicString, idKeyword, ("id", AtomicString::Cons
tructFromLiteral)); | |
| 234 DEFINE_STATIC_LOCAL(const AtomicString, heightKeyword, ("height", AtomicStri
ng::ConstructFromLiteral)); | |
| 235 DEFINE_STATIC_LOCAL(const AtomicString, widthKeyword, ("width", AtomicString
::ConstructFromLiteral)); | |
| 236 DEFINE_STATIC_LOCAL(const AtomicString, regionAnchorKeyword, ("regionanchor"
, AtomicString::ConstructFromLiteral)); | |
| 237 DEFINE_STATIC_LOCAL(const AtomicString, viewportAnchorKeyword, ("viewportanc
hor", AtomicString::ConstructFromLiteral)); | |
| 238 DEFINE_STATIC_LOCAL(const AtomicString, scrollKeyword, ("scroll", AtomicStri
ng::ConstructFromLiteral)); | |
| 239 | |
| 240 if (setting == idKeyword) | |
| 241 return Id; | |
| 242 if (setting == heightKeyword) | |
| 243 return Height; | |
| 244 if (setting == widthKeyword) | |
| 245 return Width; | |
| 246 if (setting == viewportAnchorKeyword) | |
| 247 return ViewportAnchor; | |
| 248 if (setting == regionAnchorKeyword) | |
| 249 return RegionAnchor; | |
| 250 if (setting == scrollKeyword) | |
| 251 return Scroll; | |
| 252 | |
| 253 return None; | |
| 254 } | |
| 255 | |
| 256 void TextTrackRegion::parseSettingValue(RegionSetting setting, const String& val
ue) | |
| 257 { | |
| 258 DEFINE_STATIC_LOCAL(const AtomicString, scrollUpValueKeyword, ("up", AtomicS
tring::ConstructFromLiteral)); | |
| 259 | |
| 260 bool isValidSetting; | |
| 261 String numberAsString; | |
| 262 int number; | |
| 263 unsigned position; | |
| 264 FloatPoint anchorPosition; | |
| 265 | |
| 266 switch (setting) { | |
| 267 case Id: | |
| 268 if (value.find("-->") == kNotFound) | |
| 269 m_id = value; | |
| 270 break; | |
| 271 case Width: | |
| 272 number = WebVTTParser::parseFloatPercentageValue(value, isValidSetting); | |
| 273 if (isValidSetting) | |
| 274 m_width = number; | |
| 275 else | |
| 276 LOG(Media, "TextTrackRegion::parseSettingValue, invalid Width"); | |
| 277 break; | |
| 278 case Height: | |
| 279 position = 0; | |
| 280 | |
| 281 numberAsString = WebVTTParser::collectDigits(value, &position); | |
| 282 number = value.toInt(&isValidSetting); | |
| 283 | |
| 284 if (isValidSetting && number >= 0) | |
| 285 m_heightInLines = number; | |
| 286 else | |
| 287 LOG(Media, "TextTrackRegion::parseSettingValue, invalid Height"); | |
| 288 break; | |
| 289 case RegionAnchor: | |
| 290 anchorPosition = WebVTTParser::parseFloatPercentageValuePair(value, ',',
isValidSetting); | |
| 291 if (isValidSetting) | |
| 292 m_regionAnchor = anchorPosition; | |
| 293 else | |
| 294 LOG(Media, "TextTrackRegion::parseSettingValue, invalid RegionAnchor
"); | |
| 295 break; | |
| 296 case ViewportAnchor: | |
| 297 anchorPosition = WebVTTParser::parseFloatPercentageValuePair(value, ',',
isValidSetting); | |
| 298 if (isValidSetting) | |
| 299 m_viewportAnchor = anchorPosition; | |
| 300 else | |
| 301 LOG(Media, "TextTrackRegion::parseSettingValue, invalid ViewportAnch
or"); | |
| 302 break; | |
| 303 case Scroll: | |
| 304 if (value == scrollUpValueKeyword) | |
| 305 m_scroll = true; | |
| 306 else | |
| 307 LOG(Media, "TextTrackRegion::parseSettingValue, invalid Scroll"); | |
| 308 break; | |
| 309 case None: | |
| 310 break; | |
| 311 } | |
| 312 } | |
| 313 | |
| 314 void TextTrackRegion::parseSetting(const String& input, unsigned* position) | |
| 315 { | |
| 316 String setting = WebVTTParser::collectWord(input, position); | |
| 317 | |
| 318 size_t equalOffset = setting.find('=', 1); | |
| 319 if (equalOffset == kNotFound || !equalOffset || equalOffset == setting.lengt
h() - 1) | |
| 320 return; | |
| 321 | |
| 322 RegionSetting name = getSettingFromString(setting.substring(0, equalOffset))
; | |
| 323 String value = setting.substring(equalOffset + 1, setting.length() - 1); | |
| 324 | |
| 325 parseSettingValue(name, value); | |
| 326 } | |
| 327 | |
| 328 const AtomicString& TextTrackRegion::textTrackCueContainerShadowPseudoId() | |
| 329 { | |
| 330 DEFINE_STATIC_LOCAL(const AtomicString, trackRegionCueContainerPseudoId, | |
| 331 ("-webkit-media-text-track-region-container", AtomicString::ConstructFro
mLiteral)); | |
| 332 | |
| 333 return trackRegionCueContainerPseudoId; | |
| 334 } | |
| 335 | |
| 336 const AtomicString& TextTrackRegion::textTrackCueContainerScrollingClass() | |
| 337 { | |
| 338 DEFINE_STATIC_LOCAL(const AtomicString, trackRegionCueContainerScrollingClas
s, | |
| 339 ("scrolling", AtomicString::ConstructFromLiteral)); | |
| 340 | |
| 341 return trackRegionCueContainerScrollingClass; | |
| 342 } | |
| 343 | |
| 344 const AtomicString& TextTrackRegion::textTrackRegionShadowPseudoId() | |
| 345 { | |
| 346 DEFINE_STATIC_LOCAL(const AtomicString, trackRegionShadowPseudoId, | |
| 347 ("-webkit-media-text-track-region", AtomicString::ConstructFromLiteral))
; | |
| 348 | |
| 349 return trackRegionShadowPseudoId; | |
| 350 } | |
| 351 | |
| 352 PassRefPtr<HTMLDivElement> TextTrackRegion::getDisplayTree(Document& document) | |
| 353 { | |
| 354 if (!m_regionDisplayTree) { | |
| 355 m_regionDisplayTree = HTMLDivElement::create(document); | |
| 356 prepareRegionDisplayTree(); | |
| 357 } | |
| 358 | |
| 359 return m_regionDisplayTree; | |
| 360 } | |
| 361 | |
| 362 void TextTrackRegion::willRemoveTextTrackCueBox(TextTrackCueBox* box) | |
| 363 { | |
| 364 LOG(Media, "TextTrackRegion::willRemoveTextTrackCueBox"); | |
| 365 ASSERT(m_cueContainer->contains(box)); | |
| 366 | |
| 367 double boxHeight = box->getBoundingClientRect()->bottom() - box->getBounding
ClientRect()->top(); | |
| 368 | |
| 369 m_cueContainer->classList()->remove(textTrackCueContainerScrollingClass(), A
SSERT_NO_EXCEPTION); | |
| 370 | |
| 371 m_currentTop += boxHeight; | |
| 372 m_cueContainer->setInlineStyleProperty(CSSPropertyTop, m_currentTop, CSSPrim
itiveValue::CSS_PX); | |
| 373 } | |
| 374 | |
| 375 | |
| 376 void TextTrackRegion::appendTextTrackCueBox(PassRefPtr<TextTrackCueBox> displayB
ox) | |
| 377 { | |
| 378 ASSERT(m_cueContainer); | |
| 379 | |
| 380 if (m_cueContainer->contains(displayBox.get())) | |
| 381 return; | |
| 382 | |
| 383 m_cueContainer->appendChild(displayBox); | |
| 384 displayLastTextTrackCueBox(); | |
| 385 } | |
| 386 | |
| 387 void TextTrackRegion::displayLastTextTrackCueBox() | |
| 388 { | |
| 389 LOG(Media, "TextTrackRegion::displayLastTextTrackCueBox"); | |
| 390 ASSERT(m_cueContainer); | |
| 391 | |
| 392 // FIXME: This should not be causing recalc styles in a loop to set the "top
" css | |
| 393 // property to move elements. We should just scroll the text track cues on t
he | |
| 394 // compositor with an animation. | |
| 395 | |
| 396 if (m_scrollTimer.isActive()) | |
| 397 return; | |
| 398 | |
| 399 // If it's a scrolling region, add the scrolling class. | |
| 400 if (isScrollingRegion()) | |
| 401 m_cueContainer->classList()->add(textTrackCueContainerScrollingClass(),
ASSERT_NO_EXCEPTION); | |
| 402 | |
| 403 float regionBottom = m_regionDisplayTree->getBoundingClientRect()->bottom(); | |
| 404 | |
| 405 // Find first cue that is not entirely displayed and scroll it upwards. | |
| 406 for (size_t i = 0; i < m_cueContainer->childNodeCount() && !m_scrollTimer.is
Active(); ++i) { | |
| 407 float childTop = toHTMLDivElement(m_cueContainer->childNode(i))->getBoun
dingClientRect()->top(); | |
| 408 float childBottom = toHTMLDivElement(m_cueContainer->childNode(i))->getB
oundingClientRect()->bottom(); | |
| 409 | |
| 410 if (regionBottom >= childBottom) | |
| 411 continue; | |
| 412 | |
| 413 float height = childBottom - childTop; | |
| 414 | |
| 415 m_currentTop -= std::min(height, childBottom - regionBottom); | |
| 416 m_cueContainer->setInlineStyleProperty(CSSPropertyTop, m_currentTop, CSS
PrimitiveValue::CSS_PX); | |
| 417 | |
| 418 startTimer(); | |
| 419 } | |
| 420 } | |
| 421 | |
| 422 void TextTrackRegion::prepareRegionDisplayTree() | |
| 423 { | |
| 424 ASSERT(m_regionDisplayTree); | |
| 425 | |
| 426 // 7.2 Prepare region CSS boxes | |
| 427 | |
| 428 // FIXME: Change the code below to use viewport units when | |
| 429 // http://crbug/244618 is fixed. | |
| 430 | |
| 431 // Let regionWidth be the text track region width. | |
| 432 // Let width be 'regionWidth vw' ('vw' is a CSS unit) | |
| 433 m_regionDisplayTree->setInlineStyleProperty(CSSPropertyWidth, | |
| 434 m_width, CSSPrimitiveValue::CSS_PERCENTAGE); | |
| 435 | |
| 436 // Let lineHeight be '0.0533vh' ('vh' is a CSS unit) and regionHeight be | |
| 437 // the text track region height. Let height be 'lineHeight' multiplied | |
| 438 // by regionHeight. | |
| 439 double height = lineHeight * m_heightInLines; | |
| 440 m_regionDisplayTree->setInlineStyleProperty(CSSPropertyHeight, | |
| 441 height, CSSPrimitiveValue::CSS_VH); | |
| 442 | |
| 443 // Let viewportAnchorX be the x dimension of the text track region viewport | |
| 444 // anchor and regionAnchorX be the x dimension of the text track region | |
| 445 // anchor. Let leftOffset be regionAnchorX multiplied by width divided by | |
| 446 // 100.0. Let left be leftOffset subtracted from 'viewportAnchorX vw'. | |
| 447 double leftOffset = m_regionAnchor.x() * m_width / 100; | |
| 448 m_regionDisplayTree->setInlineStyleProperty(CSSPropertyLeft, | |
| 449 m_viewportAnchor.x() - leftOffset, | |
| 450 CSSPrimitiveValue::CSS_PERCENTAGE); | |
| 451 | |
| 452 // Let viewportAnchorY be the y dimension of the text track region viewport | |
| 453 // anchor and regionAnchorY be the y dimension of the text track region | |
| 454 // anchor. Let topOffset be regionAnchorY multiplied by height divided by | |
| 455 // 100.0. Let top be topOffset subtracted from 'viewportAnchorY vh'. | |
| 456 double topOffset = m_regionAnchor.y() * height / 100; | |
| 457 m_regionDisplayTree->setInlineStyleProperty(CSSPropertyTop, | |
| 458 m_viewportAnchor.y() - topOffset, | |
| 459 CSSPrimitiveValue::CSS_PERCENTAGE); | |
| 460 | |
| 461 | |
| 462 // The cue container is used to wrap the cues and it is the object which is | |
| 463 // gradually scrolled out as multiple cues are appended to the region. | |
| 464 m_cueContainer = HTMLDivElement::create(m_regionDisplayTree->document()); | |
| 465 m_cueContainer->setInlineStyleProperty(CSSPropertyTop, | |
| 466 0.0, | |
| 467 CSSPrimitiveValue::CSS_PX); | |
| 468 | |
| 469 m_cueContainer->setPart(textTrackCueContainerShadowPseudoId()); | |
| 470 m_regionDisplayTree->appendChild(m_cueContainer); | |
| 471 | |
| 472 // 7.5 Every WebVTT region object is initialised with the following CSS | |
| 473 m_regionDisplayTree->setPart(textTrackRegionShadowPseudoId()); | |
| 474 } | |
| 475 | |
| 476 void TextTrackRegion::startTimer() | |
| 477 { | |
| 478 LOG(Media, "TextTrackRegion::startTimer"); | |
| 479 | |
| 480 if (m_scrollTimer.isActive()) | |
| 481 return; | |
| 482 | |
| 483 double duration = isScrollingRegion() ? scrollTime : 0; | |
| 484 m_scrollTimer.startOneShot(duration); | |
| 485 } | |
| 486 | |
| 487 void TextTrackRegion::stopTimer() | |
| 488 { | |
| 489 LOG(Media, "TextTrackRegion::stopTimer"); | |
| 490 | |
| 491 if (m_scrollTimer.isActive()) | |
| 492 m_scrollTimer.stop(); | |
| 493 } | |
| 494 | |
| 495 void TextTrackRegion::scrollTimerFired(Timer<TextTrackRegion>*) | |
| 496 { | |
| 497 LOG(Media, "TextTrackRegion::scrollTimerFired"); | |
| 498 | |
| 499 stopTimer(); | |
| 500 displayLastTextTrackCueBox(); | |
| 501 } | |
| 502 | |
| 503 } // namespace WebCore | |
| OLD | NEW |