| /* |
| * Copyright (C) 2011 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| |
| #if ENABLE(VIDEO_TRACK) |
| |
| #include "TextTrackCue.h" |
| |
| #include "Event.h" |
| #include "DocumentFragment.h" |
| #include "TextTrack.h" |
| #include "WebVTTParser.h" |
| #include <wtf/text/StringBuilder.h> |
| |
| namespace WebCore { |
| |
| static const AtomicString& startKeyword() |
| { |
| DEFINE_STATIC_LOCAL(const AtomicString, start, ("start")); |
| return start; |
| } |
| |
| static const AtomicString& middleKeyword() |
| { |
| DEFINE_STATIC_LOCAL(const AtomicString, middle, ("middle")); |
| return middle; |
| } |
| |
| static const AtomicString& endKeyword() |
| { |
| DEFINE_STATIC_LOCAL(const AtomicString, end, ("end")); |
| return end; |
| } |
| |
| static const AtomicString& horizontalKeyword() |
| { |
| DEFINE_STATIC_LOCAL(const AtomicString, horizontal, ("horizontal")); |
| return horizontal; |
| } |
| |
| static const AtomicString& verticalKeyword() |
| { |
| DEFINE_STATIC_LOCAL(const AtomicString, vertical, ("vertical")); |
| return vertical; |
| } |
| static const AtomicString& verticallrKeyword() |
| { |
| DEFINE_STATIC_LOCAL(const AtomicString, verticallr, ("vertical-lr")); |
| return verticallr; |
| } |
| |
| TextTrackCue::TextTrackCue(ScriptExecutionContext* context, const String& id, double start, double end, const String& content, const String& settings, bool pauseOnExit) |
| : m_id(id) |
| , m_startTime(start) |
| , m_endTime(end) |
| , m_content(content) |
| , m_writingDirection(Horizontal) |
| , m_linePosition(-1) |
| , m_textPosition(50) |
| , m_cueSize(100) |
| , m_cueAlignment(Middle) |
| , m_scriptExecutionContext(context) |
| , m_isActive(false) |
| , m_pauseOnExit(pauseOnExit) |
| , m_snapToLines(true) |
| { |
| parseSettings(settings); |
| } |
| |
| TextTrackCue::~TextTrackCue() |
| { |
| } |
| |
| void TextTrackCue::cueWillChange() |
| { |
| if (m_track) |
| m_track->cueWillChange(this); |
| } |
| |
| void TextTrackCue::cueDidChange() |
| { |
| if (m_track) |
| m_track->cueDidChange(this); |
| } |
| |
| TextTrack* TextTrackCue::track() const |
| { |
| return m_track.get(); |
| } |
| |
| void TextTrackCue::setTrack(PassRefPtr<TextTrack>track) |
| { |
| m_track = track; |
| } |
| |
| void TextTrackCue::setId(const String& id) |
| { |
| if (m_id == id) |
| return; |
| |
| cueWillChange(); |
| m_id = id; |
| cueDidChange(); |
| } |
| |
| void TextTrackCue::setStartTime(double value) |
| { |
| if (m_startTime == value) |
| return; |
| |
| cueWillChange(); |
| m_startTime = value; |
| cueDidChange(); |
| } |
| |
| void TextTrackCue::setEndTime(double value) |
| { |
| if (m_endTime == value) |
| return; |
| |
| cueWillChange(); |
| m_endTime = value; |
| cueDidChange(); |
| } |
| |
| void TextTrackCue::setPauseOnExit(bool value) |
| { |
| if (m_pauseOnExit == value) |
| return; |
| |
| cueWillChange(); |
| m_pauseOnExit = value; |
| cueDidChange(); |
| } |
| |
| String TextTrackCue::direction() const |
| { |
| switch (m_writingDirection) { |
| case Horizontal: |
| return horizontalKeyword(); |
| case VerticalGrowingLeft: |
| return verticalKeyword(); |
| case VerticalGrowingRight: |
| return verticallrKeyword(); |
| default: |
| ASSERT_NOT_REACHED(); |
| return ""; |
| } |
| } |
| |
| void TextTrackCue::setDirection(const String& value, ExceptionCode& ec) |
| { |
| // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-direction |
| // On setting, the text track cue writing direction must be set to the value given |
| // in the first cell of the row in the table above whose second cell is a |
| // case-sensitive match for the new value, if any. If none of the values match, then |
| // the user agent must instead throw a SyntaxError exception. |
| |
| Direction direction = m_writingDirection; |
| if (value == horizontalKeyword()) |
| direction = Horizontal; |
| else if (value == verticalKeyword()) |
| direction = VerticalGrowingLeft; |
| else if (value == verticallrKeyword()) |
| direction = VerticalGrowingRight; |
| else |
| ec = SYNTAX_ERR; |
| |
| if (direction == m_writingDirection) |
| return; |
| |
| cueWillChange(); |
| m_writingDirection = direction; |
| cueDidChange(); |
| } |
| |
| void TextTrackCue::setSnapToLines(bool value) |
| { |
| if (m_snapToLines == value) |
| return; |
| |
| cueWillChange(); |
| m_snapToLines = value; |
| cueDidChange(); |
| } |
| |
| void TextTrackCue::setLinePosition(int position, ExceptionCode& ec) |
| { |
| // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-lineposition |
| // On setting, if the text track cue snap-to-lines flag is not set, and the new |
| // value is negative or greater than 100, then throw an IndexSizeError exception. |
| if (!m_snapToLines && (position < 0 || position > 100)) { |
| ec = INDEX_SIZE_ERR; |
| return; |
| } |
| |
| // Otherwise, set the text track cue line position to the new value. |
| if (m_linePosition == position) |
| return; |
| |
| cueWillChange(); |
| m_linePosition = position; |
| cueDidChange(); |
| } |
| |
| void TextTrackCue::setTextPosition(int position, ExceptionCode& ec) |
| { |
| // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-lineposition |
| // On setting, if the new value is negative or greater than 100, then throw an IndexSizeError exception. |
| // Otherwise, set the text track cue text position to the new value. |
| if (position < 0 || position > 100) { |
| ec = INDEX_SIZE_ERR; |
| return; |
| } |
| |
| // Otherwise, set the text track cue line position to the new value. |
| if (m_textPosition == position) |
| return; |
| |
| cueWillChange(); |
| m_textPosition = position; |
| cueDidChange(); |
| } |
| |
| void TextTrackCue::setSize(int size, ExceptionCode& ec) |
| { |
| // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-size |
| // On setting, if the new value is negative or greater than 100, then throw an IndexSizeError |
| // exception. Otherwise, set the text track cue size to the new value. |
| if (size < 0 || size > 100) { |
| ec = INDEX_SIZE_ERR; |
| return; |
| } |
| |
| // Otherwise, set the text track cue line position to the new value. |
| if (m_cueSize == size) |
| return; |
| |
| cueWillChange(); |
| m_cueSize = size; |
| cueDidChange(); |
| } |
| |
| String TextTrackCue::alignment() const |
| { |
| switch (m_cueAlignment) { |
| case Start: |
| return startKeyword(); |
| case Middle: |
| return middleKeyword(); |
| case End: |
| return endKeyword(); |
| default: |
| ASSERT_NOT_REACHED(); |
| return ""; |
| } |
| } |
| |
| void TextTrackCue::setAlignment(const String& value, ExceptionCode& ec) |
| { |
| // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-alignment |
| // On setting, the text track cue alignment must be set to the value given in the |
| // first cell of the row in the table above whose second cell is a case-sensitive |
| // match for the new value, if any. If none of the values match, then the user |
| // agent must instead throw a SyntaxError exception. |
| |
| Alignment alignment = m_cueAlignment; |
| if (value == startKeyword()) |
| alignment = Start; |
| else if (value == middleKeyword()) |
| alignment = Middle; |
| else if (value == endKeyword()) |
| alignment = End; |
| else |
| ec = SYNTAX_ERR; |
| |
| if (alignment == m_cueAlignment) |
| return; |
| |
| cueWillChange(); |
| m_cueAlignment = alignment; |
| cueDidChange(); |
| } |
| |
| String TextTrackCue::getCueAsSource() |
| { |
| return m_content; |
| } |
| |
| PassRefPtr<DocumentFragment> TextTrackCue::getCueAsHTML() |
| { |
| return m_documentFragment; |
| } |
| |
| void TextTrackCue::setCueHTML(PassRefPtr<DocumentFragment> fragment) |
| { |
| m_documentFragment = fragment; |
| } |
| |
| bool TextTrackCue::isActive() |
| { |
| return m_isActive && track() && track()->mode() != TextTrack::DISABLED; |
| } |
| |
| void TextTrackCue::setIsActive(bool active) |
| { |
| m_isActive = active; |
| |
| // When a TextTrack's mode is disabled: No cues are active, no events are fired ... |
| if (!track() || track()->mode() == TextTrack::DISABLED) |
| return; |
| |
| ExceptionCode ec = 0; |
| if (active) |
| dispatchEvent(Event::create(eventNames().enterEvent, false, false), ec); |
| else |
| dispatchEvent(Event::create(eventNames().exitEvent, false, false), ec); |
| |
| if (m_track) |
| m_track->fireCueChangeEvent(); |
| } |
| |
| void TextTrackCue::parseSettings(const String& input) |
| { |
| // 4.8.10.13.3 Parse the WebVTT settings. |
| // 1 - Initial setup. |
| unsigned position = 0; |
| while (position < input.length()) { |
| // Discard any space characters between or after settings (not in the spec, but we think it should be). |
| while (position < input.length() && WebVTTParser::isASpace(input[position])) |
| position++; |
| |
| // 2-4 Settings - get the next character representing a settings. |
| char setting = input[position++]; |
| if (position >= input.length()) |
| return; |
| |
| // 5-7 - If the character at position is not ':', set setting to empty string. |
| if (input[position++] != ':') |
| setting = '\0'; |
| if (position >= input.length()) |
| return; |
| |
| // 8 - Gather settings based on value of setting. |
| switch (setting) { |
| case 'D': |
| { |
| // 1-3 - Collect the next word and set the writing direction accordingly. |
| String writingDirection = WebVTTParser::collectWord(input, &position); |
| if (writingDirection == verticalKeyword()) |
| m_writingDirection = VerticalGrowingLeft; |
| else if (writingDirection == verticallrKeyword()) |
| m_writingDirection = VerticalGrowingRight; |
| } |
| break; |
| case 'L': |
| { |
| // 1-2 - Collect chars that are either '-', '%', or a digit. |
| StringBuilder linePositionBuilder; |
| while (position < input.length() && (input[position] == '-' || input[position] == '%' || isASCIIDigit(input[position]))) |
| linePositionBuilder.append(input[position++]); |
| if (position < input.length() && !WebVTTParser::isASpace(input[position])) |
| goto Otherwise; |
| String linePosition = linePositionBuilder.toString(); |
| |
| // 3-5 - If there is not at least one digit character, |
| // a '-' occurs anywhere other than the front, or |
| // a '%' occurs anywhere other than the end, then |
| // ignore this setting and keep looking. |
| if (linePosition.find('-', 1) != notFound || linePosition.reverseFind("%", linePosition.length() - 2) != notFound) |
| break; |
| |
| // 6 - If the first char is a '-' and the last char is a '%', ignore and keep looking. |
| if (linePosition[0] == '-' && linePosition[linePosition.length() - 1] == '%') |
| break; |
| |
| // 7 - Interpret as number (toInt ignores trailing non-digit characters, |
| // such as a possible '%'). |
| bool validNumber; |
| int number = linePosition.toInt(&validNumber); |
| if (!validNumber) |
| break; |
| |
| // 8 - If the last char is a '%' and the value is not between 0 and 100, ignore and keep looking. |
| if (linePosition[linePosition.length() - 1] == '%') { |
| if (number < 0 || number > 100) |
| break; |
| |
| // 10 - If '%' then set snap-to-lines flag to false. |
| m_snapToLines = false; |
| } |
| |
| // 9 - Set cue line position to the number found. |
| m_linePosition = number; |
| } |
| break; |
| case 'T': |
| { |
| // 1-2 - Collect characters that are digits. |
| String textPosition = WebVTTParser::collectDigits(input, &position); |
| if (position >= input.length()) |
| break; |
| |
| // 3 - Character at end must be '%', otherwise ignore and keep looking. |
| if (input[position++] != '%') |
| goto Otherwise; |
| |
| // 4-6 - Ensure no other chars in this setting and setting is not empty. |
| if (position < input.length() && !WebVTTParser::isASpace(input[position])) |
| goto Otherwise; |
| if (textPosition.isEmpty()) |
| break; |
| |
| // 7-8 - Interpret as number and make sure it is between 0 and 100 |
| // (toInt ignores trailing non-digit characters, such as a possible '%'). |
| bool validNumber; |
| int number = textPosition.toInt(&validNumber); |
| if (!validNumber) |
| break; |
| if (number < 0 || number > 100) |
| break; |
| |
| // 9 - Set cue text position to the number found. |
| m_textPosition = number; |
| } |
| break; |
| case 'S': |
| { |
| // 1-2 - Collect characters that are digits. |
| String cueSize = WebVTTParser::collectDigits(input, &position); |
| if (position >= input.length()) |
| break; |
| |
| // 3 - Character at end must be '%', otherwise ignore and keep looking. |
| if (input[position++] != '%') |
| goto Otherwise; |
| |
| // 4-6 - Ensure no other chars in this setting and setting is not empty. |
| if (position < input.length() && !WebVTTParser::isASpace(input[position])) |
| goto Otherwise; |
| if (cueSize.isEmpty()) |
| break; |
| |
| // 7-8 - Interpret as number and make sure it is between 0 and 100. |
| bool validNumber; |
| int number = cueSize.toInt(&validNumber); |
| if (!validNumber) |
| break; |
| if (number < 0 || number > 100) |
| break; |
| |
| // 9 - Set cue size to the number found. |
| m_cueSize = number; |
| } |
| break; |
| case 'A': |
| { |
| // 1-4 - Collect the next word and set the cue alignment accordingly. |
| String cueAlignment = WebVTTParser::collectWord(input, &position); |
| if (cueAlignment == startKeyword()) |
| m_cueAlignment = Start; |
| else if (cueAlignment == middleKeyword()) |
| m_cueAlignment = Middle; |
| else if (cueAlignment == endKeyword()) |
| m_cueAlignment = End; |
| } |
| break; |
| } |
| |
| continue; |
| |
| Otherwise: |
| // Collect a sequence of characters that are not space characters and discard them. |
| WebVTTParser::collectWord(input, &position); |
| } |
| } |
| |
| const AtomicString& TextTrackCue::interfaceName() const |
| { |
| return eventNames().interfaceForTextTrackCue; |
| } |
| |
| ScriptExecutionContext* TextTrackCue::scriptExecutionContext() const |
| { |
| return m_scriptExecutionContext; |
| } |
| |
| EventTargetData* TextTrackCue::eventTargetData() |
| { |
| return &m_eventTargetData; |
| } |
| |
| EventTargetData* TextTrackCue::ensureEventTargetData() |
| { |
| return &m_eventTargetData; |
| } |
| |
| } // namespace WebCore |
| |
| #endif |