Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(339)

Side by Side Diff: Source/core/html/track/vtt/VTTCue.cpp

Issue 77853002: Move VTT functionality from TextTrackCue.* to VTTCue.* (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Address comments in https://codereview.chromium.org/77853002/#msg2. Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 /* 1 /*
2 * Copyright (c) 2013, Opera Software ASA. All rights reserved. 2 * Copyright (c) 2013, Opera Software ASA. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 12 matching lines...) Expand all
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
27 * OF THE POSSIBILITY OF SUCH DAMAGE. 27 * OF THE POSSIBILITY OF SUCH DAMAGE.
28 */ 28 */
29 29
30 #include "config.h" 30 #include "config.h"
31 #include "core/html/track/vtt/VTTCue.h" 31 #include "core/html/track/vtt/VTTCue.h"
32 32
33 #include "CSSPropertyNames.h"
34 #include "CSSValueKeywords.h"
35 #include "RuntimeEnabledFeatures.h"
36 #include "bindings/v8/ExceptionStatePlaceholder.h"
37 #include "core/dom/DocumentFragment.h"
38 #include "core/dom/NodeTraversal.h"
39 #include "core/events/Event.h"
40 #include "core/html/HTMLDivElement.h"
41 #include "core/html/track/TextTrack.h"
42 #include "core/html/track/TextTrackCueList.h"
43 #include "core/html/track/vtt/VTTElement.h"
44 #include "core/html/track/vtt/VTTParser.h"
45 #include "core/html/track/vtt/VTTRegionList.h"
46 #include "core/rendering/RenderVTTCue.h"
47 #include "wtf/MathExtras.h"
48 #include "wtf/text/StringBuilder.h"
49
33 namespace WebCore { 50 namespace WebCore {
34 51
52 static const int undefinedPosition = -1;
53
54 static const CSSValueID displayWritingModeMap[] = {
55 CSSValueHorizontalTb, CSSValueVerticalRl, CSSValueVerticalLr
56 };
57 COMPILE_ASSERT(WTF_ARRAY_LENGTH(displayWritingModeMap) == VTTCue::NumberOfWritin gDirections,
58 displayWritingModeMap_has_wrong_size);
59
60 static const CSSValueID displayAlignmentMap[] = {
61 CSSValueStart, CSSValueCenter, CSSValueEnd, CSSValueLeft, CSSValueRight
62 };
63 COMPILE_ASSERT(WTF_ARRAY_LENGTH(displayAlignmentMap) == VTTCue::NumberOfAlignmen ts,
64 displayAlignmentMap_has_wrong_size);
65
66 static const String& startKeyword()
67 {
68 DEFINE_STATIC_LOCAL(const String, start, ("start"));
69 return start;
70 }
71
72 static const String& middleKeyword()
73 {
74 DEFINE_STATIC_LOCAL(const String, middle, ("middle"));
75 return middle;
76 }
77
78 static const String& endKeyword()
79 {
80 DEFINE_STATIC_LOCAL(const String, end, ("end"));
81 return end;
82 }
83
84 static const String& leftKeyword()
85 {
86 DEFINE_STATIC_LOCAL(const String, left, ("left"));
87 return left;
88 }
89
90 static const String& rightKeyword()
91 {
92 DEFINE_STATIC_LOCAL(const String, right, ("right"));
93 return right;
94 }
95
96 static const String& horizontalKeyword()
97 {
98 return emptyString();
99 }
100
101 static const String& verticalGrowingLeftKeyword()
102 {
103 DEFINE_STATIC_LOCAL(const String, verticalrl, ("rl"));
104 return verticalrl;
105 }
106
107 static const String& verticalGrowingRightKeyword()
108 {
109 DEFINE_STATIC_LOCAL(const String, verticallr, ("lr"));
110 return verticallr;
111 }
112
113 // ----------------------------
114
115 VTTCueBox::VTTCueBox(Document& document, VTTCue* cue)
116 : TextTrackCueBox(document)
117 , m_cue(cue)
118 {
119 setPart(textTrackCueBoxShadowPseudoId());
120 }
121
122 void VTTCueBox::applyCSSProperties(const IntSize&)
123 {
124 // FIXME: Apply all the initial CSS positioning properties. http://wkb.ug/79 916
125 if (!m_cue->regionId().isEmpty()) {
126 setInlineStyleProperty(CSSPropertyPosition, CSSValueRelative);
127 return;
128 }
129
130 // 3.5.1 On the (root) List of WebVTT Node Objects:
131
132 // the 'position' property must be set to 'absolute'
133 setInlineStyleProperty(CSSPropertyPosition, CSSValueAbsolute);
134
135 // the 'unicode-bidi' property must be set to 'plaintext'
136 setInlineStyleProperty(CSSPropertyUnicodeBidi, CSSValueWebkitPlaintext);
137
138 // the 'direction' property must be set to direction
139 setInlineStyleProperty(CSSPropertyDirection, m_cue->getCSSWritingDirection() );
140
141 // the 'writing-mode' property must be set to writing-mode
142 setInlineStyleProperty(CSSPropertyWebkitWritingMode, m_cue->getCSSWritingMod e());
143
144 std::pair<float, float> position = m_cue->getCSSPosition();
145
146 // the 'top' property must be set to top,
147 setInlineStyleProperty(CSSPropertyTop, position.second, CSSPrimitiveValue::C SS_PERCENTAGE);
148
149 // the 'left' property must be set to left
150 setInlineStyleProperty(CSSPropertyLeft, position.first, CSSPrimitiveValue::C SS_PERCENTAGE);
151
152 // the 'width' property must be set to width, and the 'height' property mus t be set to height
153 if (m_cue->vertical() == horizontalKeyword()) {
154 setInlineStyleProperty(CSSPropertyWidth, static_cast<double>(m_cue->getC SSSize()), CSSPrimitiveValue::CSS_PERCENTAGE);
155 setInlineStyleProperty(CSSPropertyHeight, CSSValueAuto);
156 } else {
157 setInlineStyleProperty(CSSPropertyWidth, CSSValueAuto);
158 setInlineStyleProperty(CSSPropertyHeight, static_cast<double>(m_cue->get CSSSize()), CSSPrimitiveValue::CSS_PERCENTAGE);
159 }
160
161 // The 'text-align' property on the (root) List of WebVTT Node Objects must
162 // be set to the value in the second cell of the row of the table below
163 // whose first cell is the value of the corresponding cue's text track cue
164 // alignment:
165 setInlineStyleProperty(CSSPropertyTextAlign, m_cue->getCSSAlignment());
166
167 if (!m_cue->snapToLines()) {
168 // 10.13.1 Set up x and y:
169 // Note: x and y are set through the CSS left and top above.
170
171 // 10.13.2 Position the boxes in boxes such that the point x% along the
172 // width of the bounding box of the boxes in boxes is x% of the way
173 // across the width of the video's rendering area, and the point y%
174 // along the height of the bounding box of the boxes in boxes is y%
175 // of the way across the height of the video's rendering area, while
176 // maintaining the relative positions of the boxes in boxes to each
177 // other.
178 setInlineStyleProperty(CSSPropertyWebkitTransform,
179 String::format("translate(-%.2f%%, -%.2f%%)", position.first, positi on.second));
180
181 setInlineStyleProperty(CSSPropertyWhiteSpace, CSSValuePre);
182 }
183 }
184
185 RenderObject* VTTCueBox::createRenderer(RenderStyle*)
186 {
187 return new RenderVTTCue(this);
188 }
189
190 // ----------------------------
191
35 VTTCue::VTTCue(Document& document, double startTime, double endTime, const Strin g& text) 192 VTTCue::VTTCue(Document& document, double startTime, double endTime, const Strin g& text)
36 : TextTrackCue(document, startTime, endTime, text) 193 : TextTrackCue(startTime, endTime)
194 , m_text(text)
195 , m_linePosition(undefinedPosition)
196 , m_computedLinePosition(undefinedPosition)
197 , m_textPosition(50)
198 , m_cueSize(100)
199 , m_writingDirection(Horizontal)
200 , m_cueAlignment(Middle)
201 , m_webVTTNodeTree(0)
202 , m_snapToLines(true)
203 , m_cueBackgroundBox(HTMLDivElement::create(document))
204 , m_displayTreeShouldChange(true)
205 , m_displayDirection(CSSValueLtr)
206 , m_notifyRegion(true)
37 { 207 {
38 ScriptWrappable::init(this); 208 ScriptWrappable::init(this);
39 } 209 }
40 210
41 PassRefPtr<VTTCue> VTTCue::create(Document& document, double startTime, double e ndTime, const String& text) 211 VTTCue::~VTTCue()
42 { 212 {
43 return adoptRef(new VTTCue(document, startTime, endTime, text)); 213 displayTreeInternal()->remove(ASSERT_NO_EXCEPTION);
214 }
215
216 PassRefPtr<VTTCueBox> VTTCue::displayTreeInternal()
217 {
218 if (!m_displayTree)
219 m_displayTree = VTTCueBox::create(document(), this);
220 return m_displayTree;
221 }
222
223 void VTTCue::cueDidChange()
224 {
225 TextTrackCue::cueDidChange();
226 m_displayTreeShouldChange = true;
227 }
228
229 const String& VTTCue::vertical() const
230 {
231 switch (m_writingDirection) {
232 case Horizontal:
233 return horizontalKeyword();
234 case VerticalGrowingLeft:
235 return verticalGrowingLeftKeyword();
236 case VerticalGrowingRight:
237 return verticalGrowingRightKeyword();
238 default:
239 ASSERT_NOT_REACHED();
240 return emptyString();
241 }
242 }
243
244 void VTTCue::setVertical(const String& value, ExceptionState& exceptionState)
245 {
246 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-ele ment.html#dom-texttrackcue-vertical
247 // On setting, the text track cue writing direction must be set to the value given
248 // in the first cell of the row in the table above whose second cell is a
249 // case-sensitive match for the new value, if any. If none of the values mat ch, then
250 // the user agent must instead throw a SyntaxError exception.
251
252 WritingDirection direction = m_writingDirection;
253 if (value == horizontalKeyword())
254 direction = Horizontal;
255 else if (value == verticalGrowingLeftKeyword())
256 direction = VerticalGrowingLeft;
257 else if (value == verticalGrowingRightKeyword())
258 direction = VerticalGrowingRight;
259 else
260 exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
261
262 if (direction == m_writingDirection)
263 return;
264
265 cueWillChange();
266 m_writingDirection = direction;
267 cueDidChange();
268 }
269
270 void VTTCue::setSnapToLines(bool value)
271 {
272 if (m_snapToLines == value)
273 return;
274
275 cueWillChange();
276 m_snapToLines = value;
277 cueDidChange();
278 }
279
280 void VTTCue::setLine(int position, ExceptionState& exceptionState)
281 {
282 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-ele ment.html#dom-texttrackcue-line
283 // On setting, if the text track cue snap-to-lines flag is not set, and the new
284 // value is negative or greater than 100, then throw an IndexSizeError excep tion.
285 if (!m_snapToLines && (position < 0 || position > 100)) {
286 exceptionState.throwUninformativeAndGenericDOMException(IndexSizeError);
287 return;
288 }
289
290 // Otherwise, set the text track cue line position to the new value.
291 if (m_linePosition == position)
292 return;
293
294 cueWillChange();
295 m_linePosition = position;
296 m_computedLinePosition = calculateComputedLinePosition();
297 cueDidChange();
298 }
299
300 void VTTCue::setPosition(int position, ExceptionState& exceptionState)
301 {
302 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-ele ment.html#dom-texttrackcue-position
303 // On setting, if the new value is negative or greater than 100, then throw an IndexSizeError exception.
304 // Otherwise, set the text track cue text position to the new value.
305 if (position < 0 || position > 100) {
306 exceptionState.throwUninformativeAndGenericDOMException(IndexSizeError);
307 return;
308 }
309
310 // Otherwise, set the text track cue line position to the new value.
311 if (m_textPosition == position)
312 return;
313
314 cueWillChange();
315 m_textPosition = position;
316 cueDidChange();
317 }
318
319 void VTTCue::setSize(int size, ExceptionState& exceptionState)
320 {
321 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-ele ment.html#dom-texttrackcue-size
322 // On setting, if the new value is negative or greater than 100, then throw an IndexSizeError
323 // exception. Otherwise, set the text track cue size to the new value.
324 if (size < 0 || size > 100) {
325 exceptionState.throwUninformativeAndGenericDOMException(IndexSizeError);
326 return;
327 }
328
329 // Otherwise, set the text track cue line position to the new value.
330 if (m_cueSize == size)
331 return;
332
333 cueWillChange();
334 m_cueSize = size;
335 cueDidChange();
336 }
337
338 const String& VTTCue::align() const
339 {
340 switch (m_cueAlignment) {
341 case Start:
342 return startKeyword();
343 case Middle:
344 return middleKeyword();
345 case End:
346 return endKeyword();
347 case Left:
348 return leftKeyword();
349 case Right:
350 return rightKeyword();
351 default:
352 ASSERT_NOT_REACHED();
353 return emptyString();
354 }
355 }
356
357 void VTTCue::setAlign(const String& value, ExceptionState& exceptionState)
358 {
359 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-ele ment.html#dom-texttrackcue-align
360 // On setting, the text track cue alignment must be set to the value given i n the
361 // first cell of the row in the table above whose second cell is a case-sens itive
362 // match for the new value, if any. If none of the values match, then the us er
363 // agent must instead throw a SyntaxError exception.
364
365 CueAlignment alignment = m_cueAlignment;
366 if (value == startKeyword())
367 alignment = Start;
368 else if (value == middleKeyword())
369 alignment = Middle;
370 else if (value == endKeyword())
371 alignment = End;
372 else if (value == leftKeyword())
373 alignment = Left;
374 else if (value == rightKeyword())
375 alignment = Right;
376 else
377 exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
378
379 if (alignment == m_cueAlignment)
380 return;
381
382 cueWillChange();
383 m_cueAlignment = alignment;
384 cueDidChange();
385 }
386
387 void VTTCue::setText(const String& text)
388 {
389 if (m_text == text)
390 return;
391
392 cueWillChange();
393 // Clear the document fragment but don't bother to create it again just yet as we can do that
394 // when it is requested.
395 m_webVTTNodeTree = 0;
396 m_text = text;
397 cueDidChange();
398 }
399
400 void VTTCue::createVTTNodeTree()
401 {
402 if (!m_webVTTNodeTree)
403 m_webVTTNodeTree = VTTParser::createDocumentFragmentFromCueText(document (), m_text);
404 }
405
406 void VTTCue::copyVTTNodeToDOMTree(ContainerNode* webVTTNode, ContainerNode* pare nt)
407 {
408 for (Node* node = webVTTNode->firstChild(); node; node = node->nextSibling() ) {
409 RefPtr<Node> clonedNode;
410 if (node->isVTTElement())
411 clonedNode = toVTTElement(node)->createEquivalentHTMLElement(documen t());
412 else
413 clonedNode = node->cloneNode(false);
414 parent->appendChild(clonedNode);
415 if (node->isContainerNode())
416 copyVTTNodeToDOMTree(toContainerNode(node), toContainerNode(clonedNo de));
417 }
418 }
419
420 PassRefPtr<DocumentFragment> VTTCue::getCueAsHTML()
421 {
422 createVTTNodeTree();
423 RefPtr<DocumentFragment> clonedFragment = DocumentFragment::create(document( ));
424 copyVTTNodeToDOMTree(m_webVTTNodeTree.get(), clonedFragment.get());
425 return clonedFragment.release();
426 }
427
428 PassRefPtr<DocumentFragment> VTTCue::createCueRenderingTree()
429 {
430 RefPtr<DocumentFragment> clonedFragment;
431 createVTTNodeTree();
432 clonedFragment = DocumentFragment::create(document());
433 m_webVTTNodeTree->cloneChildNodes(clonedFragment.get());
434 return clonedFragment.release();
435 }
436
437 void VTTCue::setRegionId(const String& regionId)
438 {
439 if (m_regionId == regionId)
440 return;
441
442 cueWillChange();
443 m_regionId = regionId;
444 cueDidChange();
445 }
446
447 void VTTCue::notifyRegionWhenRemovingDisplayTree(bool notifyRegion)
448 {
449 m_notifyRegion = notifyRegion;
450 }
451
452 int VTTCue::calculateComputedLinePosition()
453 {
454 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-ele ment.html#text-track-cue-computed-line-position
455
456 // If the text track cue line position is numeric, then that is the text
457 // track cue computed line position.
458 if (m_linePosition != undefinedPosition)
459 return m_linePosition;
460
461 // If the text track cue snap-to-lines flag of the text track cue is not
462 // set, the text track cue computed line position is the value 100;
463 if (!m_snapToLines)
464 return 100;
465
466 // Otherwise, it is the value returned by the following algorithm:
467
468 // If cue is not associated with a text track, return -1 and abort these
469 // steps.
470 if (!track())
471 return -1;
472
473 // Let n be the number of text tracks whose text track mode is showing or
474 // showing by default and that are in the media element's list of text
475 // tracks before track.
476 int n = track()->trackIndexRelativeToRenderedTracks();
477
478 // Increment n by one.
479 n++;
480
481 // Negate n.
482 n = -n;
483
484 return n;
485 }
486
487 static bool isCueParagraphSeparator(UChar character)
488 {
489 // Within a cue, paragraph boundaries are only denoted by Type B characters,
490 // such as U+000A LINE FEED (LF), U+0085 NEXT LINE (NEL), and U+2029 PARAGRA PH SEPARATOR.
491 return WTF::Unicode::category(character) & WTF::Unicode::Separator_Paragraph ;
492 }
493
494 void VTTCue::determineTextDirection()
495 {
496 DEFINE_STATIC_LOCAL(const String, rtTag, ("rt"));
497 createVTTNodeTree();
498
499 // Apply the Unicode Bidirectional Algorithm's Paragraph Level steps to the
500 // concatenation of the values of each WebVTT Text Object in nodes, in a
501 // pre-order, depth-first traversal, excluding WebVTT Ruby Text Objects and
502 // their descendants.
503 StringBuilder paragraphBuilder;
504 for (Node* node = m_webVTTNodeTree->firstChild(); node; node = NodeTraversal ::next(*node, m_webVTTNodeTree.get())) {
505 if (!node->isTextNode() || node->localName() == rtTag)
506 continue;
507
508 paragraphBuilder.append(node->nodeValue());
509 }
510
511 String paragraph = paragraphBuilder.toString();
512 if (!paragraph.length())
513 return;
514
515 for (size_t i = 0; i < paragraph.length(); ++i) {
516 UChar current = paragraph[i];
517 if (!current || isCueParagraphSeparator(current))
518 return;
519
520 if (UChar current = paragraph[i]) {
521 WTF::Unicode::Direction charDirection = WTF::Unicode::direction(curr ent);
522 if (charDirection == WTF::Unicode::LeftToRight) {
523 m_displayDirection = CSSValueLtr;
524 return;
525 }
526 if (charDirection == WTF::Unicode::RightToLeft
527 || charDirection == WTF::Unicode::RightToLeftArabic) {
528 m_displayDirection = CSSValueRtl;
529 return;
530 }
531 }
532 }
533 }
534
535 void VTTCue::calculateDisplayParameters()
536 {
537 // Steps 10.2, 10.3
538 determineTextDirection();
539
540 // 10.4 If the text track cue writing direction is horizontal, then let
541 // block-flow be 'tb'. Otherwise, if the text track cue writing direction is
542 // vertical growing left, then let block-flow be 'lr'. Otherwise, the text
543 // track cue writing direction is vertical growing right; let block-flow be
544 // 'rl'.
545
546 // The above step is done through the writing direction static map.
547
548 // 10.5 Determine the value of maximum size for cue as per the appropriate
549 // rules from the following list:
550 int maximumSize = m_textPosition;
551 if ((m_writingDirection == Horizontal && m_cueAlignment == Start && m_displa yDirection == CSSValueLtr)
552 || (m_writingDirection == Horizontal && m_cueAlignment == End && m_displ ayDirection == CSSValueRtl)
553 || (m_writingDirection == Horizontal && m_cueAlignment == Left)
554 || (m_writingDirection == VerticalGrowingLeft && (m_cueAlignment == Star t || m_cueAlignment == Left))
555 || (m_writingDirection == VerticalGrowingRight && (m_cueAlignment == Sta rt || m_cueAlignment == Left))) {
556 maximumSize = 100 - m_textPosition;
557 } else if ((m_writingDirection == Horizontal && m_cueAlignment == End && m_d isplayDirection == CSSValueLtr)
558 || (m_writingDirection == Horizontal && m_cueAlignment == Start && m_dis playDirection == CSSValueRtl)
559 || (m_writingDirection == Horizontal && m_cueAlignment == Right)
560 || (m_writingDirection == VerticalGrowingLeft && (m_cueAlignment == End || m_cueAlignment == Right))
561 || (m_writingDirection == VerticalGrowingRight && (m_cueAlignment == End || m_cueAlignment == Right))) {
562 maximumSize = m_textPosition;
563 } else if (m_cueAlignment == Middle) {
564 maximumSize = m_textPosition <= 50 ? m_textPosition : (100 - m_textPosit ion);
565 maximumSize = maximumSize * 2;
566 } else {
567 ASSERT_NOT_REACHED();
568 }
569
570 // 10.6 If the text track cue size is less than maximum size, then let size
571 // be text track cue size. Otherwise, let size be maximum size.
572 m_displaySize = std::min(m_cueSize, maximumSize);
573
574 // FIXME: Understand why step 10.7 is missing (just a copy/paste error?)
575 // Could be done within a spec implementation check - http://crbug.com/30158 0
576
577 // 10.8 Determine the value of x-position or y-position for cue as per the
578 // appropriate rules from the following list:
579 if (m_writingDirection == Horizontal) {
580 switch (m_cueAlignment) {
581 case Start:
582 if (m_displayDirection == CSSValueLtr)
583 m_displayPosition.first = m_textPosition;
584 else
585 m_displayPosition.first = 100 - m_textPosition - m_displaySize;
586 break;
587 case End:
588 if (m_displayDirection == CSSValueRtl)
589 m_displayPosition.first = 100 - m_textPosition;
590 else
591 m_displayPosition.first = m_textPosition - m_displaySize;
592 break;
593 case Left:
594 if (m_displayDirection == CSSValueLtr)
595 m_displayPosition.first = m_textPosition;
596 else
597 m_displayPosition.first = 100 - m_textPosition;
598 break;
599 case Right:
600 if (m_displayDirection == CSSValueLtr)
601 m_displayPosition.first = m_textPosition - m_displaySize;
602 else
603 m_displayPosition.first = 100 - m_textPosition - m_displaySize;
604 break;
605 case Middle:
606 if (m_displayDirection == CSSValueLtr)
607 m_displayPosition.first = m_textPosition - m_displaySize / 2;
608 else
609 m_displayPosition.first = 100 - m_textPosition - m_displaySize / 2;
610 break;
611 case NumberOfAlignments:
612 ASSERT_NOT_REACHED();
613 }
614 } else {
615 // Cases for m_writingDirection being VerticalGrowing{Left|Right}
616 switch (m_cueAlignment) {
617 case Start:
618 case Left:
619 m_displayPosition.second = m_textPosition;
620 break;
621 case End:
622 case Right:
623 m_displayPosition.second = m_textPosition - m_displaySize;
624 break;
625 case Middle:
626 m_displayPosition.second = m_textPosition - m_displaySize / 2;
627 break;
628 case NumberOfAlignments:
629 ASSERT_NOT_REACHED();
630 }
631 }
632
633 // A text track cue has a text track cue computed line position whose value
634 // is defined in terms of the other aspects of the cue.
635 m_computedLinePosition = calculateComputedLinePosition();
636
637 // 10.9 Determine the value of whichever of x-position or y-position is not
638 // yet calculated for cue as per the appropriate rules from the following
639 // list:
640 if (m_snapToLines && m_displayPosition.second == undefinedPosition && m_writ ingDirection == Horizontal)
641 m_displayPosition.second = 0;
642
643 if (!m_snapToLines && m_displayPosition.second == undefinedPosition && m_wri tingDirection == Horizontal)
644 m_displayPosition.second = m_computedLinePosition;
645
646 if (m_snapToLines && m_displayPosition.first == undefinedPosition
647 && (m_writingDirection == VerticalGrowingLeft || m_writingDirection == V erticalGrowingRight))
648 m_displayPosition.first = 0;
649
650 if (!m_snapToLines && (m_writingDirection == VerticalGrowingLeft || m_writin gDirection == VerticalGrowingRight))
651 m_displayPosition.first = m_computedLinePosition;
652 }
653
654 void VTTCue::markFutureAndPastNodes(ContainerNode* root, double previousTimestam p, double movieTime)
655 {
656 DEFINE_STATIC_LOCAL(const String, timestampTag, ("timestamp"));
657
658 bool isPastNode = true;
659 double currentTimestamp = previousTimestamp;
660 if (currentTimestamp > movieTime)
661 isPastNode = false;
662
663 for (Node* child = root->firstChild(); child; child = NodeTraversal::next(*c hild, root)) {
664 if (child->nodeName() == timestampTag) {
665 unsigned position = 0;
666 String timestamp = child->nodeValue();
667 double currentTimestamp = VTTParser::collectTimeStamp(timestamp, &po sition);
668 ASSERT(currentTimestamp != -1);
669
670 if (currentTimestamp > movieTime)
671 isPastNode = false;
672 }
673
674 if (child->isVTTElement()) {
675 toVTTElement(child)->setIsPastNode(isPastNode);
676 // Make an elemenet id match a cue id for style matching purposes.
677 if (!id().isEmpty())
678 toElement(child)->setIdAttribute(id());
679 }
680 }
681 }
682
683 void VTTCue::updateDisplayTree(double movieTime)
684 {
685 // The display tree may contain WebVTT timestamp objects representing
686 // timestamps (processing instructions), along with displayable nodes.
687
688 if (!track()->isRendered())
689 return;
690
691 // Clear the contents of the set.
692 m_cueBackgroundBox->removeChildren();
693
694 // Update the two sets containing past and future WebVTT objects.
695 RefPtr<DocumentFragment> referenceTree = createCueRenderingTree();
696 markFutureAndPastNodes(referenceTree.get(), startTime(), movieTime);
697 m_cueBackgroundBox->appendChild(referenceTree, ASSERT_NO_EXCEPTION);
698 }
699
700 PassRefPtr<TextTrackCueBox> VTTCue::getDisplayTree(const IntSize& videoSize)
701 {
702 RefPtr<VTTCueBox> displayTree = displayTreeInternal();
703 if (!m_displayTreeShouldChange || !track()->isRendered())
704 return displayTree;
705
706 // 10.1 - 10.10
707 calculateDisplayParameters();
708
709 // 10.11. Apply the terms of the CSS specifications to nodes within the
710 // following constraints, thus obtaining a set of CSS boxes positioned
711 // relative to an initial containing block:
712 displayTree->removeChildren();
713
714 // The document tree is the tree of WebVTT Node Objects rooted at nodes.
715
716 // The children of the nodes must be wrapped in an anonymous box whose
717 // 'display' property has the value 'inline'. This is the WebVTT cue
718 // background box.
719
720 // Note: This is contained by default in m_cueBackgroundBox.
721 m_cueBackgroundBox->setPart(cueShadowPseudoId());
722 displayTree->appendChild(m_cueBackgroundBox);
723
724 // FIXME(BUG 79916): Runs of children of WebVTT Ruby Objects that are not
725 // WebVTT Ruby Text Objects must be wrapped in anonymous boxes whose
726 // 'display' property has the value 'ruby-base'.
727
728 // FIXME(BUG 79916): Text runs must be wrapped according to the CSS
729 // line-wrapping rules, except that additionally, regardless of the value of
730 // the 'white-space' property, lines must be wrapped at the edge of their
731 // containing blocks, even if doing so requires splitting a word where there
732 // is no line breaking opportunity. (Thus, normally text wraps as needed,
733 // but if there is a particularly long word, it does not overflow as it
734 // normally would in CSS, it is instead forcibly wrapped at the box's edge.)
735 displayTree->applyCSSProperties(videoSize);
736
737 m_displayTreeShouldChange = false;
738
739 // 10.15. Let cue's text track cue display state have the CSS boxes in
740 // boxes.
741 return displayTree;
742 }
743
744 void VTTCue::removeDisplayTree()
745 {
746 if (m_notifyRegion && track()->regions()) {
747 // The region needs to be informed about the cue removal.
748 VTTRegion* region = track()->regions()->getRegionById(m_regionId);
749 if (region)
750 region->willRemoveTextTrackCueBox(m_displayTree.get());
751 }
752
753 displayTreeInternal()->remove(ASSERT_NO_EXCEPTION);
754 }
755
756 void VTTCue::updateDisplay(const IntSize& videoSize, HTMLDivElement* container)
757 {
758 RefPtr<TextTrackCueBox> displayBox = getDisplayTree(videoSize);
759 VTTRegion* region = 0;
760 if (track()->regions())
761 region = track()->regions()->getRegionById(regionId());
762
763 if (!region) {
764 // If cue has an empty text track cue region identifier or there is no
765 // WebVTT region whose region identifier is identical to cue's text
766 // track cue region identifier, run the following substeps:
767 if (displayBox->hasChildNodes() && !container->contains(displayBox.get() )) {
768 // Note: the display tree of a cue is removed when the active flag o f the cue is unset.
769 container->appendChild(displayBox);
770 }
771 } else {
772 // Let region be the WebVTT region whose region identifier
773 // matches the text track cue region identifier of cue.
774 RefPtr<HTMLDivElement> regionNode = region->getDisplayTree(document());
775
776 // Append the region to the viewport, if it was not already.
777 if (!container->contains(regionNode.get()))
778 container->appendChild(regionNode);
779
780 region->appendTextTrackCueBox(displayBox);
781 }
782 }
783
784 std::pair<double, double> VTTCue::getPositionCoordinates() const
785 {
786 // This method is used for setting x and y when snap to lines is not set.
787 std::pair<double, double> coordinates;
788
789 if (m_writingDirection == Horizontal && m_displayDirection == CSSValueLtr) {
790 coordinates.first = m_textPosition;
791 coordinates.second = m_computedLinePosition;
792
793 return coordinates;
794 }
795
796 if (m_writingDirection == Horizontal && m_displayDirection == CSSValueRtl) {
797 coordinates.first = 100 - m_textPosition;
798 coordinates.second = m_computedLinePosition;
799
800 return coordinates;
801 }
802
803 if (m_writingDirection == VerticalGrowingLeft) {
804 coordinates.first = 100 - m_computedLinePosition;
805 coordinates.second = m_textPosition;
806
807 return coordinates;
808 }
809
810 if (m_writingDirection == VerticalGrowingRight) {
811 coordinates.first = m_computedLinePosition;
812 coordinates.second = m_textPosition;
813
814 return coordinates;
815 }
816
817 ASSERT_NOT_REACHED();
818
819 return coordinates;
820 }
821
822 VTTCue::CueSetting VTTCue::settingName(const String& name)
823 {
824 DEFINE_STATIC_LOCAL(const String, verticalKeyword, ("vertical"));
825 DEFINE_STATIC_LOCAL(const String, lineKeyword, ("line"));
826 DEFINE_STATIC_LOCAL(const String, positionKeyword, ("position"));
827 DEFINE_STATIC_LOCAL(const String, sizeKeyword, ("size"));
828 DEFINE_STATIC_LOCAL(const String, alignKeyword, ("align"));
829 DEFINE_STATIC_LOCAL(const String, regionIdKeyword, ("region"));
830
831 if (name == verticalKeyword)
832 return Vertical;
833 if (name == lineKeyword)
834 return Line;
835 if (name == positionKeyword)
836 return Position;
837 if (name == sizeKeyword)
838 return Size;
839 if (name == alignKeyword)
840 return Align;
841 if (RuntimeEnabledFeatures::webVTTRegionsEnabled() && name == regionIdKeywor d)
842 return RegionId;
843
844 return None;
845 }
846
847 void VTTCue::parseSettings(const String& input)
848 {
849 unsigned position = 0;
850
851 while (position < input.length()) {
852
853 // The WebVTT cue settings part of a WebVTT cue consists of zero or more of the following components, in any order,
854 // separated from each other by one or more U+0020 SPACE characters or U +0009 CHARACTER TABULATION (tab) characters.
855 while (position < input.length() && VTTParser::isValidSettingDelimiter(i nput[position]))
856 position++;
857 if (position >= input.length())
858 break;
859
860 // When the user agent is to parse the WebVTT settings given by a string input for a text track cue cue,
861 // the user agent must run the following steps:
862 // 1. Let settings be the result of splitting input on spaces.
863 // 2. For each token setting in the list settings, run the following sub steps:
864 // 1. If setting does not contain a U+003A COLON character (:), or if the first U+003A COLON character (:)
865 // in setting is either the first or last character of setting, th en jump to the step labeled next setting.
866 unsigned endOfSetting = position;
867 String setting = VTTParser::collectWord(input, &endOfSetting);
868 CueSetting name;
869 size_t colonOffset = setting.find(':', 1);
870 if (colonOffset == kNotFound || !colonOffset || colonOffset == setting.l ength() - 1)
871 goto NextSetting;
872
873 // 2. Let name be the leading substring of setting up to and excluding t he first U+003A COLON character (:) in that string.
874 name = settingName(setting.substring(0, colonOffset));
875
876 // 3. Let value be the trailing substring of setting starting from the c haracter immediately after the first U+003A COLON character (:) in that string.
877 position += colonOffset + 1;
878 if (position >= input.length())
879 break;
880
881 // 4. Run the appropriate substeps that apply for the value of name, as follows:
882 switch (name) {
883 case Vertical:
884 {
885 // If name is a case-sensitive match for "vertical"
886 // 1. If value is a case-sensitive match for the string "rl", then l et cue's text track cue writing direction
887 // be vertical growing left.
888 String writingDirection = VTTParser::collectWord(input, &position);
889 if (writingDirection == verticalGrowingLeftKeyword())
890 m_writingDirection = VerticalGrowingLeft;
891
892 // 2. Otherwise, if value is a case-sensitive match for the string " lr", then let cue's text track cue writing
893 // direction be vertical growing right.
894 else if (writingDirection == verticalGrowingRightKeyword())
895 m_writingDirection = VerticalGrowingRight;
896 }
897 break;
898 case Line:
899 {
900 // 1-2 - Collect chars that are either '-', '%', or a digit.
901 // 1. If value contains any characters other than U+002D HYPHEN-MINU S characters (-), U+0025 PERCENT SIGN
902 // characters (%), and characters in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9), then jump
903 // to the step labeled next setting.
904 StringBuilder linePositionBuilder;
905 while (position < input.length() && (input[position] == '-' || input [position] == '%' || isASCIIDigit(input[position])))
906 linePositionBuilder.append(input[position++]);
907 if (position < input.length() && !VTTParser::isValidSettingDelimiter (input[position]))
908 break;
909
910 // 2. If value does not contain at least one character in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT
911 // NINE (9), then jump to the step labeled next setting.
912 // 3. If any character in value other than the first character is a U+002D HYPHEN-MINUS character (-), then
913 // jump to the step labeled next setting.
914 // 4. If any character in value other than the last character is a U +0025 PERCENT SIGN character (%), then
915 // jump to the step labeled next setting.
916 String linePosition = linePositionBuilder.toString();
917 if (linePosition.find('-', 1) != kNotFound || linePosition.reverseFi nd("%", linePosition.length() - 2) != kNotFound)
918 break;
919
920 // 5. If the first character in value is a U+002D HYPHEN-MINUS chara cter (-) and the last character in value is a
921 // U+0025 PERCENT SIGN character (%), then jump to the step label ed next setting.
922 if (linePosition[0] == '-' && linePosition[linePosition.length() - 1 ] == '%')
923 break;
924
925 // 6. Ignoring the trailing percent sign, if any, interpret value as a (potentially signed) integer, and
926 // let number be that number.
927 // NOTE: toInt ignores trailing non-digit characters, such as '%'.
928 bool validNumber;
929 int number = linePosition.toInt(&validNumber);
930 if (!validNumber)
931 break;
932
933 // 7. If the last character in value is a U+0025 PERCENT SIGN charac ter (%), but number is not in the range
934 // 0 ≤ number ≤ 100, then jump to the step labeled next setting.
935 // 8. Let cue's text track cue line position be number.
936 // 9. If the last character in value is a U+0025 PERCENT SIGN charac ter (%), then let cue's text track cue
937 // snap-to-lines flag be false. Otherwise, let it be true.
938 if (linePosition[linePosition.length() - 1] == '%') {
939 if (number < 0 || number > 100)
940 break;
941
942 // 10 - If '%' then set snap-to-lines flag to false.
943 m_snapToLines = false;
944 }
945
946 m_linePosition = number;
947 }
948 break;
949 case Position:
950 {
951 // 1. If value contains any characters other than U+0025 PERCENT SIG N characters (%) and characters in the range
952 // U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9), then jump to t he step labeled next setting.
953 // 2. If value does not contain at least one character in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9),
954 // then jump to the step labeled next setting.
955 String textPosition = VTTParser::collectDigits(input, &position);
956 if (textPosition.isEmpty())
957 break;
958 if (position >= input.length())
959 break;
960
961 // 3. If any character in value other than the last character is a U +0025 PERCENT SIGN character (%), then jump
962 // to the step labeled next setting.
963 // 4. If the last character in value is not a U+0025 PERCENT SIGN ch aracter (%), then jump to the step labeled
964 // next setting.
965 if (input[position++] != '%')
966 break;
967 if (position < input.length() && !VTTParser::isValidSettingDelimiter (input[position]))
968 break;
969
970 // 5. Ignoring the trailing percent sign, interpret value as an inte ger, and let number be that number.
971 // 6. If number is not in the range 0 ≤ number ≤ 100, then jump to t he step labeled next setting.
972 // NOTE: toInt ignores trailing non-digit characters, such as '%'.
973 bool validNumber;
974 int number = textPosition.toInt(&validNumber);
975 if (!validNumber)
976 break;
977 if (number < 0 || number > 100)
978 break;
979
980 // 7. Let cue's text track cue text position be number.
981 m_textPosition = number;
982 }
983 break;
984 case Size:
985 {
986 // 1. If value contains any characters other than U+0025 PERCENT SIG N characters (%) and characters in the
987 // range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9), then jum p to the step labeled next setting.
988 // 2. If value does not contain at least one character in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT
989 // NINE (9), then jump to the step labeled next setting.
990 String cueSize = VTTParser::collectDigits(input, &position);
991 if (cueSize.isEmpty())
992 break;
993 if (position >= input.length())
994 break;
995
996 // 3. If any character in value other than the last character is a U +0025 PERCENT SIGN character (%),
997 // then jump to the step labeled next setting.
998 // 4. If the last character in value is not a U+0025 PERCENT SIGN ch aracter (%), then jump to the step
999 // labeled next setting.
1000 if (input[position++] != '%')
1001 break;
1002 if (position < input.length() && !VTTParser::isValidSettingDelimiter (input[position]))
1003 break;
1004
1005 // 5. Ignoring the trailing percent sign, interpret value as an inte ger, and let number be that number.
1006 // 6. If number is not in the range 0 ≤ number ≤ 100, then jump to t he step labeled next setting.
1007 bool validNumber;
1008 int number = cueSize.toInt(&validNumber);
1009 if (!validNumber)
1010 break;
1011 if (number < 0 || number > 100)
1012 break;
1013
1014 // 7. Let cue's text track cue size be number.
1015 m_cueSize = number;
1016 }
1017 break;
1018 case Align:
1019 {
1020 String cueAlignment = VTTParser::collectWord(input, &position);
1021
1022 // 1. If value is a case-sensitive match for the string "start", the n let cue's text track cue alignment be start alignment.
1023 if (cueAlignment == startKeyword())
1024 m_cueAlignment = Start;
1025
1026 // 2. If value is a case-sensitive match for the string "middle", th en let cue's text track cue alignment be middle alignment.
1027 else if (cueAlignment == middleKeyword())
1028 m_cueAlignment = Middle;
1029
1030 // 3. If value is a case-sensitive match for the string "end", then let cue's text track cue alignment be end alignment.
1031 else if (cueAlignment == endKeyword())
1032 m_cueAlignment = End;
1033
1034 // 4. If value is a case-sensitive match for the string "left", then let cue's text track cue alignment be left alignment.
1035 else if (cueAlignment == leftKeyword())
1036 m_cueAlignment = Left;
1037
1038 // 5. If value is a case-sensitive match for the string "right", the n let cue's text track cue alignment be right alignment.
1039 else if (cueAlignment == rightKeyword())
1040 m_cueAlignment = Right;
1041 }
1042 break;
1043 case RegionId:
1044 m_regionId = VTTParser::collectWord(input, &position);
1045 break;
1046 case None:
1047 break;
1048 }
1049
1050 NextSetting:
1051 position = endOfSetting;
1052 }
1053
1054 // If cue's line position is not auto or cue's size is not 100 or cue's
1055 // writing direction is not horizontal, but cue's region identifier is not
1056 // the empty string, let cue's region identifier be the empty string.
1057 if (m_regionId.isEmpty())
1058 return;
1059
1060 if (m_linePosition != undefinedPosition || m_cueSize != 100 || m_writingDire ction != Horizontal)
1061 m_regionId = emptyString();
1062 }
1063
1064 CSSValueID VTTCue::getCSSAlignment() const
1065 {
1066 return displayAlignmentMap[m_cueAlignment];
1067 }
1068
1069 CSSValueID VTTCue::getCSSWritingDirection() const
1070 {
1071 return m_displayDirection;
1072 }
1073
1074 CSSValueID VTTCue::getCSSWritingMode() const
1075 {
1076 return displayWritingModeMap[m_writingDirection];
1077 }
1078
1079 int VTTCue::getCSSSize() const
1080 {
1081 return m_displaySize;
1082 }
1083
1084 std::pair<double, double> VTTCue::getCSSPosition() const
1085 {
1086 if (!m_snapToLines)
1087 return getPositionCoordinates();
1088
1089 return m_displayPosition;
1090 }
1091
1092 ExecutionContext* VTTCue::executionContext() const
1093 {
1094 ASSERT(m_cueBackgroundBox);
1095 return m_cueBackgroundBox->executionContext();
1096 }
1097
1098 Document& VTTCue::document() const
1099 {
1100 ASSERT(m_cueBackgroundBox);
1101 return m_cueBackgroundBox->document();
44 } 1102 }
45 1103
46 } // namespace WebCore 1104 } // namespace WebCore
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698