diff options
Diffstat (limited to 'Source/WebCore/html/track')
66 files changed, 5506 insertions, 3605 deletions
diff --git a/Source/WebCore/html/track/AudioTrack.cpp b/Source/WebCore/html/track/AudioTrack.cpp index aecdb0af6..eeb05537f 100644 --- a/Source/WebCore/html/track/AudioTrack.cpp +++ b/Source/WebCore/html/track/AudioTrack.cpp @@ -1,6 +1,6 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. - * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2011-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -30,158 +30,161 @@ */ #include "config.h" +#include "AudioTrack.h" #if ENABLE(VIDEO_TRACK) -#include "AudioTrack.h" - -#include "AudioTrackList.h" -#include "Event.h" #include "HTMLMediaElement.h" +#include <wtf/NeverDestroyed.h> namespace WebCore { const AtomicString& AudioTrack::alternativeKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, alternative, ("alternative", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<AtomicString> alternative("alternative", AtomicString::ConstructFromLiteral); return alternative; } const AtomicString& AudioTrack::descriptionKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, description, ("description", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<AtomicString> description("description", AtomicString::ConstructFromLiteral); return description; } const AtomicString& AudioTrack::mainKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, main, ("main", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<AtomicString> main("main", AtomicString::ConstructFromLiteral); return main; } const AtomicString& AudioTrack::mainDescKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, mainDesc, ("main-desc", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<AtomicString> mainDesc("main-desc", AtomicString::ConstructFromLiteral); return mainDesc; } const AtomicString& AudioTrack::translationKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, translation, ("translation", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<AtomicString> translation("translation", AtomicString::ConstructFromLiteral); return translation; } const AtomicString& AudioTrack::commentaryKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, commentary, ("commentary", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<AtomicString> commentary("commentary", AtomicString::ConstructFromLiteral); return commentary; } -AudioTrack::AudioTrack(AudioTrackClient* client, PassRefPtr<AudioTrackPrivate> trackPrivate) - : TrackBase(TrackBase::AudioTrack, trackPrivate->id(), trackPrivate->label(), trackPrivate->language()) - , m_enabled(trackPrivate->enabled()) - , m_client(client) +AudioTrack::AudioTrack(AudioTrackClient& client, AudioTrackPrivate& trackPrivate) + : MediaTrackBase(MediaTrackBase::AudioTrack, trackPrivate.id(), trackPrivate.label(), trackPrivate.language()) + , m_enabled(trackPrivate.enabled()) + , m_client(&client) , m_private(trackPrivate) { m_private->setClient(this); - - switch (m_private->kind()) { - case AudioTrackPrivate::Alternative: - setKind(AudioTrack::alternativeKeyword()); - break; - case AudioTrackPrivate::Description: - setKind(AudioTrack::descriptionKeyword()); - break; - case AudioTrackPrivate::Main: - setKind(AudioTrack::mainKeyword()); - break; - case AudioTrackPrivate::MainDesc: - setKind(AudioTrack::mainDescKeyword()); - break; - case AudioTrackPrivate::Translation: - setKind(AudioTrack::translationKeyword()); - break; - case AudioTrackPrivate::Commentary: - setKind(AudioTrack::commentaryKeyword()); - break; - case AudioTrackPrivate::None: - setKind(emptyString()); - break; - default: - ASSERT_NOT_REACHED(); - break; - } + updateKindFromPrivate(); } AudioTrack::~AudioTrack() { - m_private->setClient(0); + m_private->setClient(nullptr); } -bool AudioTrack::isValidKind(const AtomicString& value) const +void AudioTrack::setPrivate(AudioTrackPrivate& trackPrivate) { - if (value == alternativeKeyword()) - return true; - if (value == descriptionKeyword()) - return true; - if (value == mainKeyword()) - return true; - if (value == mainDescKeyword()) - return true; - if (value == translationKeyword()) - return true; - if (value == commentaryKeyword()) - return true; + if (m_private.ptr() == &trackPrivate) + return; + + m_private->setClient(nullptr); + m_private = trackPrivate; + m_private->setEnabled(m_enabled); + m_private->setClient(this); - return false; + updateKindFromPrivate(); +} + +bool AudioTrack::isValidKind(const AtomicString& value) const +{ + return value == alternativeKeyword() + || value == commentaryKeyword() + || value == descriptionKeyword() + || value == mainKeyword() + || value == mainDescKeyword() + || value == translationKeyword(); } -void AudioTrack::setEnabled(const bool enabled) +void AudioTrack::setEnabled(bool enabled) { if (m_enabled == enabled) return; - m_enabled = enabled; m_private->setEnabled(enabled); - - if (m_client) - m_client->audioTrackEnabledChanged(this); } -size_t AudioTrack::inbandTrackIndex() +size_t AudioTrack::inbandTrackIndex() const { - ASSERT(m_private); return m_private->trackIndex(); } -void AudioTrack::enabledChanged(AudioTrackPrivate* trackPrivate, bool enabled) +void AudioTrack::enabledChanged(bool enabled) { - ASSERT_UNUSED(trackPrivate, trackPrivate == m_private); - setEnabled(enabled); + if (m_enabled == enabled) + return; + + m_enabled = enabled; + + if (m_client) + m_client->audioTrackEnabledChanged(*this); } -void AudioTrack::idChanged(TrackPrivateBase* trackPrivate, const String& id) +void AudioTrack::idChanged(const AtomicString& id) { - ASSERT_UNUSED(trackPrivate, trackPrivate == m_private); setId(id); } -void AudioTrack::labelChanged(TrackPrivateBase* trackPrivate, const String& label) +void AudioTrack::labelChanged(const AtomicString& label) { - ASSERT_UNUSED(trackPrivate, trackPrivate == m_private); setLabel(label); } -void AudioTrack::languageChanged(TrackPrivateBase* trackPrivate, const String& language) +void AudioTrack::languageChanged(const AtomicString& language) { - ASSERT_UNUSED(trackPrivate, trackPrivate == m_private); setLanguage(language); } -void AudioTrack::willRemove(TrackPrivateBase* trackPrivate) +void AudioTrack::willRemove() { - ASSERT_UNUSED(trackPrivate, trackPrivate == m_private); - mediaElement()->removeAudioTrack(this); + mediaElement()->removeAudioTrack(*this); +} + +void AudioTrack::updateKindFromPrivate() +{ + switch (m_private->kind()) { + case AudioTrackPrivate::Alternative: + setKind(AudioTrack::alternativeKeyword()); + break; + case AudioTrackPrivate::Description: + setKind(AudioTrack::descriptionKeyword()); + break; + case AudioTrackPrivate::Main: + setKind(AudioTrack::mainKeyword()); + break; + case AudioTrackPrivate::MainDesc: + setKind(AudioTrack::mainDescKeyword()); + break; + case AudioTrackPrivate::Translation: + setKind(AudioTrack::translationKeyword()); + break; + case AudioTrackPrivate::Commentary: + setKind(AudioTrack::commentaryKeyword()); + break; + case AudioTrackPrivate::None: + setKind(emptyString()); + break; + default: + ASSERT_NOT_REACHED(); + break; + } } } // namespace WebCore diff --git a/Source/WebCore/html/track/AudioTrack.h b/Source/WebCore/html/track/AudioTrack.h index 29c5bd4c0..07c7794fa 100644 --- a/Source/WebCore/html/track/AudioTrack.h +++ b/Source/WebCore/html/track/AudioTrack.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 Google Inc. All rights reserved. - * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2011-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,17 +24,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef AudioTrack_h -#define AudioTrack_h +#pragma once #if ENABLE(VIDEO_TRACK) #include "AudioTrackPrivate.h" -#include "ExceptionCode.h" #include "TrackBase.h" -#include <wtf/PassOwnPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/text/WTFString.h> namespace WebCore { @@ -43,14 +38,14 @@ class AudioTrack; class AudioTrackClient { public: virtual ~AudioTrackClient() { } - virtual void audioTrackEnabledChanged(AudioTrack*) = 0; + virtual void audioTrackEnabledChanged(AudioTrack&) = 0; }; -class AudioTrack : public TrackBase, public AudioTrackPrivateClient { +class AudioTrack final : public MediaTrackBase, private AudioTrackPrivateClient { public: - static PassRefPtr<AudioTrack> create(AudioTrackClient* client, PassRefPtr<AudioTrackPrivate> trackPrivate) + static Ref<AudioTrack> create(AudioTrackClient& client, AudioTrackPrivate& trackPrivate) { - return adoptRef(new AudioTrack(client, trackPrivate)); + return adoptRef(*new AudioTrack(client, trackPrivate)); } virtual ~AudioTrack(); @@ -60,41 +55,43 @@ public: static const AtomicString& mainDescKeyword(); static const AtomicString& translationKeyword(); static const AtomicString& commentaryKeyword(); - virtual const AtomicString& defaultKindKeyword() const override { return emptyAtom; } - virtual bool enabled() const override { return m_enabled; } - virtual void setEnabled(const bool); + bool enabled() const final { return m_enabled; } + void setEnabled(const bool); - virtual void clearClient() override { m_client = 0; } + void clearClient() final { m_client = nullptr; } AudioTrackClient* client() const { return m_client; } - size_t inbandTrackIndex(); + size_t inbandTrackIndex() const; -protected: - AudioTrack(AudioTrackClient*, PassRefPtr<AudioTrackPrivate>); + void setPrivate(AudioTrackPrivate&); private: - virtual bool isValidKind(const AtomicString&) const override; + AudioTrack(AudioTrackClient&, AudioTrackPrivate&); - virtual void enabledChanged(AudioTrackPrivate*, bool) override; - virtual void idChanged(TrackPrivateBase*, const String&) override; - virtual void labelChanged(TrackPrivateBase*, const String&) override; - virtual void languageChanged(TrackPrivateBase*, const String&) override; - virtual void willRemove(TrackPrivateBase*) override; + bool isValidKind(const AtomicString&) const final; + + // AudioTrackPrivateClient + void enabledChanged(bool) final; + + // TrackPrivateBaseClient + void idChanged(const AtomicString&) final; + void labelChanged(const AtomicString&) final; + void languageChanged(const AtomicString&) final; + void willRemove() final; + + void updateKindFromPrivate(); bool m_enabled; AudioTrackClient* m_client; - RefPtr<AudioTrackPrivate> m_private; + Ref<AudioTrackPrivate> m_private; }; -inline AudioTrack* toAudioTrack(TrackBase* track) -{ - ASSERT_WITH_SECURITY_IMPLICATION(track->type() == TrackBase::AudioTrack); - return static_cast<AudioTrack*>(track); -} - } // namespace WebCore -#endif +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::AudioTrack) + static bool isType(const WebCore::TrackBase& track) { return track.type() == WebCore::TrackBase::AudioTrack; } +SPECIALIZE_TYPE_TRAITS_END() + #endif diff --git a/Source/WebCore/html/track/AudioTrack.idl b/Source/WebCore/html/track/AudioTrack.idl index fefd44ea5..addf4bf83 100644 --- a/Source/WebCore/html/track/AudioTrack.idl +++ b/Source/WebCore/html/track/AudioTrack.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,7 +24,6 @@ */ [ - NoInterfaceObject, Conditional=VIDEO_TRACK, GenerateIsReachable=ImplElementRoot, JSCustomMarkFunction diff --git a/Source/WebCore/html/track/AudioTrackList.cpp b/Source/WebCore/html/track/AudioTrackList.cpp index eb8c9a73f..71155690a 100644 --- a/Source/WebCore/html/track/AudioTrackList.cpp +++ b/Source/WebCore/html/track/AudioTrackList.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -30,7 +30,6 @@ #include "AudioTrackList.h" #include "AudioTrack.h" -#include "EventNames.h" using namespace WebCore; @@ -43,36 +42,40 @@ AudioTrackList::~AudioTrackList() { } -void AudioTrackList::append(PassRefPtr<AudioTrack> prpTrack) +void AudioTrackList::append(Ref<AudioTrack>&& track) { - RefPtr<AudioTrack> track = prpTrack; - // Insert tracks in the media file order. size_t index = track->inbandTrackIndex(); - m_inbandTracks.insert(index, track); + size_t insertionIndex; + for (insertionIndex = 0; insertionIndex < m_inbandTracks.size(); ++insertionIndex) { + auto& otherTrack = downcast<AudioTrack>(*m_inbandTracks[insertionIndex]); + if (otherTrack.inbandTrackIndex() > index) + break; + } + m_inbandTracks.insert(insertionIndex, track.ptr()); + ASSERT(!track->mediaElement() || track->mediaElement() == mediaElement()); track->setMediaElement(mediaElement()); - scheduleAddTrackEvent(track.release()); + scheduleAddTrackEvent(WTFMove(track)); } AudioTrack* AudioTrackList::item(unsigned index) const { if (index < m_inbandTracks.size()) - return toAudioTrack(m_inbandTracks[index].get()); - - return 0; + return downcast<AudioTrack>(m_inbandTracks[index].get()); + return nullptr; } AudioTrack* AudioTrackList::getTrackById(const AtomicString& id) const { - for (size_t i = 0; i < m_inbandTracks.size(); ++i) { - AudioTrack* track = toAudioTrack(m_inbandTracks[i].get()); - if (track->id() == id) - return track; + for (auto& inbandTrack : m_inbandTracks) { + auto& track = downcast<AudioTrack>(*inbandTrack); + if (track.id() == id) + return &track; } - return 0; + return nullptr; } EventTargetInterface AudioTrackList::eventTargetInterface() const diff --git a/Source/WebCore/html/track/AudioTrackList.h b/Source/WebCore/html/track/AudioTrackList.h index 9104b6bfd..0c637ddc3 100644 --- a/Source/WebCore/html/track/AudioTrackList.h +++ b/Source/WebCore/html/track/AudioTrackList.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef AudioTrackList_h -#define AudioTrackList_h +#pragma once #if ENABLE(VIDEO_TRACK) @@ -34,11 +33,11 @@ namespace WebCore { class AudioTrack; -class AudioTrackList : public TrackListBase { +class AudioTrackList final : public TrackListBase { public: - static PassRefPtr<AudioTrackList> create(HTMLMediaElement* owner, ScriptExecutionContext* context) + static Ref<AudioTrackList> create(HTMLMediaElement* owner, ScriptExecutionContext* context) { - return adoptRef(new AudioTrackList(owner, context)); + return adoptRef(*new AudioTrackList(owner, context)); } virtual ~AudioTrackList(); @@ -46,10 +45,10 @@ public: AudioTrack* item(unsigned index) const; AudioTrack* lastItem() const { return item(length() - 1); } - void append(PassRefPtr<AudioTrack>); + void append(Ref<AudioTrack>&&); // EventTarget - virtual EventTargetInterface eventTargetInterface() const override; + EventTargetInterface eventTargetInterface() const override; private: AudioTrackList(HTMLMediaElement*, ScriptExecutionContext*); @@ -57,5 +56,4 @@ private: } // namespace WebCore -#endif -#endif +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/html/track/AudioTrackList.idl b/Source/WebCore/html/track/AudioTrackList.idl index 439384725..cde73f40a 100644 --- a/Source/WebCore/html/track/AudioTrackList.idl +++ b/Source/WebCore/html/track/AudioTrackList.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,26 +24,16 @@ */ [ - NoInterfaceObject, Conditional=VIDEO_TRACK, GenerateIsReachable=ImplElementRoot, - EventTarget, JSCustomMarkFunction, -] interface AudioTrackList { +] interface AudioTrackList : EventTarget { readonly attribute unsigned long length; getter AudioTrack item(unsigned long index); AudioTrack getTrackById(DOMString id); - attribute EventListener onchange; - attribute EventListener onaddtrack; - attribute EventListener onremovetrack; - - void addEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - void removeEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - [RaisesException] boolean dispatchEvent(Event evt); + attribute EventHandler onchange; + attribute EventHandler onaddtrack; + attribute EventHandler onremovetrack; }; diff --git a/Source/WebCore/html/track/BufferedLineReader.cpp b/Source/WebCore/html/track/BufferedLineReader.cpp new file mode 100644 index 000000000..8f2362512 --- /dev/null +++ b/Source/WebCore/html/track/BufferedLineReader.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2013, Opera Software ASA. 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" +#include "BufferedLineReader.h" + +#include <wtf/unicode/CharacterNames.h> + +namespace WebCore { + +std::optional<String> BufferedLineReader::nextLine() +{ + if (m_maybeSkipLF) { + // We ran out of data after a CR (U+000D), which means that we may be + // in the middle of a CRLF pair. If the next character is a LF (U+000A) + // then skip it, and then (unconditionally) return the buffered line. + if (!m_buffer.isEmpty()) { + if (m_buffer.currentCharacter() == newlineCharacter) + m_buffer.advancePastNewline(); + m_maybeSkipLF = false; + } + // If there was no (new) data available, then keep m_maybeSkipLF set, + // and fall through all the way down to the EOS check at the end of the function. + } + + bool shouldReturnLine = false; + bool checkForLF = false; + while (!m_buffer.isEmpty()) { + UChar character = m_buffer.currentCharacter(); + m_buffer.advance(); + + if (character == newlineCharacter || character == carriageReturn) { + // We found a line ending. Return the accumulated line. + shouldReturnLine = true; + checkForLF = (character == carriageReturn); + break; + } + + // NULs are transformed into U+FFFD (REPLACEMENT CHAR.) in step 1 of + // the WebVTT parser algorithm. + if (character == '\0') + character = replacementCharacter; + + m_lineBuffer.append(character); + } + + if (checkForLF) { + // May be in the middle of a CRLF pair. + if (!m_buffer.isEmpty()) { + if (m_buffer.currentCharacter() == newlineCharacter) + m_buffer.advancePastNewline(); + } else { + // Check for the newline on the next call (unless we reached EOS, in + // which case we'll return the contents of the line buffer, and + // reset state for the next line.) + m_maybeSkipLF = true; + } + } + + if (isAtEndOfStream()) { + // We've reached the end of the stream proper. Emit a line if the + // current line buffer is non-empty. (Note that if shouldReturnLine is + // set already, we want to return a line nonetheless.) + shouldReturnLine |= !m_lineBuffer.isEmpty(); + } + + if (shouldReturnLine) { + auto line = m_lineBuffer.toString(); + m_lineBuffer.clear(); + return WTFMove(line); + } + + ASSERT(m_buffer.isEmpty()); + return std::nullopt; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/track/BufferedLineReader.h b/Source/WebCore/html/track/BufferedLineReader.h new file mode 100644 index 000000000..9334ae73e --- /dev/null +++ b/Source/WebCore/html/track/BufferedLineReader.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2013, Opera Software ASA. 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. + */ + +#pragma once + +#include "SegmentedString.h" +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +// Line collection helper for the WebVTT Parser. +// +// Converts a stream of data (== a sequence of Strings) into a set of +// lines. CR, LR or CRLF are considered line breaks. Normalizes NULs (U+0000) +// to 'REPLACEMENT CHARACTER' (U+FFFD) and does not return the line breaks as +// part of the result. +class BufferedLineReader { + WTF_MAKE_NONCOPYABLE(BufferedLineReader); +public: + BufferedLineReader() = default; + void reset(); + + void append(String&& data) + { + ASSERT(!m_endOfStream); + m_buffer.append(WTFMove(data)); + } + + void appendEndOfStream() { m_endOfStream = true; } + bool isAtEndOfStream() const { return m_endOfStream && m_buffer.isEmpty(); } + + std::optional<String> nextLine(); + +private: + SegmentedString m_buffer; + StringBuilder m_lineBuffer; + bool m_endOfStream { false }; + bool m_maybeSkipLF { false }; +}; + +inline void BufferedLineReader::reset() +{ + m_buffer.clear(); + m_lineBuffer.clear(); + m_endOfStream = false; + m_maybeSkipLF = false; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/track/DataCue.cpp b/Source/WebCore/html/track/DataCue.cpp new file mode 100644 index 000000000..3bc272ad0 --- /dev/null +++ b/Source/WebCore/html/track/DataCue.cpp @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2014 Cable Television Labs Inc. All rights reserved. + * Copyright (C) 2014 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 "DataCue.h" + +#include "Logging.h" +#include "TextTrack.h" +#include "TextTrackCueList.h" +#include <runtime/JSCInlines.h> +#include <runtime/Protect.h> + +using namespace JSC; + +namespace WebCore { + +DataCue::DataCue(ScriptExecutionContext& context, const MediaTime& start, const MediaTime& end, ArrayBuffer& data, const String& type) + : TextTrackCue(context, start, end) + , m_type(type) +{ + setData(data); +} + +DataCue::DataCue(ScriptExecutionContext& context, const MediaTime& start, const MediaTime& end, const void* data, unsigned length) + : TextTrackCue(context, start, end) + , m_data(ArrayBuffer::create(data, length)) +{ +} + +DataCue::DataCue(ScriptExecutionContext& context, const MediaTime& start, const MediaTime& end, RefPtr<SerializedPlatformRepresentation>&& platformValue, const String& type) + : TextTrackCue(context, start, end) + , m_type(type) + , m_platformValue(WTFMove(platformValue)) +{ +} + +DataCue::DataCue(ScriptExecutionContext& context, const MediaTime& start, const MediaTime& end, JSC::JSValue value, const String& type) + : TextTrackCue(context, start, end) + , m_type(type) + , m_value(value) +{ + if (m_value) + JSC::gcProtect(m_value); +} + +DataCue::~DataCue() +{ + if (m_value) + JSC::gcUnprotect(m_value); +} + +RefPtr<ArrayBuffer> DataCue::data() const +{ + if (m_platformValue) + return m_platformValue->data(); + + if (!m_data) + return nullptr; + + return ArrayBuffer::create(*m_data); +} + +void DataCue::setData(ArrayBuffer& data) +{ + m_platformValue = nullptr; + if (m_value) + JSC::gcUnprotect(m_value); + m_value = JSC::JSValue(); + + m_data = ArrayBuffer::create(data); +} + +DataCue* toDataCue(TextTrackCue* cue) +{ + ASSERT_WITH_SECURITY_IMPLICATION(cue->cueType() == TextTrackCue::Data); + return static_cast<DataCue*>(cue); +} + +const DataCue* toDataCue(const TextTrackCue* cue) +{ + ASSERT_WITH_SECURITY_IMPLICATION(cue->cueType() == TextTrackCue::Data); + return static_cast<const DataCue*>(cue); +} + +bool DataCue::cueContentsMatch(const TextTrackCue& cue) const +{ + if (cue.cueType() != TextTrackCue::Data) + return false; + + const DataCue* dataCue = toDataCue(&cue); + RefPtr<ArrayBuffer> otherData = dataCue->data(); + if ((otherData && !m_data) || (!otherData && m_data)) + return false; + if (m_data && m_data->byteLength() != otherData->byteLength()) + return false; + if (m_data && m_data->data() && memcmp(m_data->data(), otherData->data(), m_data->byteLength())) + return false; + + const SerializedPlatformRepresentation* otherPlatformValue = dataCue->platformValue(); + if ((otherPlatformValue && !m_platformValue) || (!otherPlatformValue && m_platformValue)) + return false; + if (m_platformValue && !m_platformValue->isEqual(*otherPlatformValue)) + return false; + + JSC::JSValue thisValue = valueOrNull(); + JSC::JSValue otherValue = dataCue->valueOrNull(); + if ((otherValue && !thisValue) || (!otherValue && thisValue)) + return false; + if (!JSC::JSValue::strictEqual(nullptr, thisValue, otherValue)) + return false; + + return true; +} + +bool DataCue::isEqual(const TextTrackCue& cue, TextTrackCue::CueMatchRules match) const +{ + if (!TextTrackCue::isEqual(cue, match)) + return false; + + if (cue.cueType() != TextTrackCue::Data) + return false; + + return cueContentsMatch(cue); +} + +bool DataCue::doesExtendCue(const TextTrackCue& cue) const +{ + if (!cueContentsMatch(cue)) + return false; + + return TextTrackCue::doesExtendCue(cue); +} + +JSC::JSValue DataCue::value(JSC::ExecState& state) const +{ + if (m_platformValue) + return m_platformValue->deserialize(&state); + + if (m_value) + return m_value; + + return JSC::jsNull(); +} + +void DataCue::setValue(JSC::ExecState&, JSC::JSValue value) +{ + // FIXME: this should use a SerializedScriptValue. + if (m_value) + JSC::gcUnprotect(m_value); + m_value = value; + if (m_value) + JSC::gcProtect(m_value); + + m_platformValue = nullptr; + m_data = nullptr; +} + +JSValue DataCue::valueOrNull() const +{ + if (m_value) + return m_value; + + return jsNull(); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/track/DataCue.h b/Source/WebCore/html/track/DataCue.h new file mode 100644 index 000000000..ea32bccb6 --- /dev/null +++ b/Source/WebCore/html/track/DataCue.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2014 Cable Television Labs Inc. All rights reserved. + * Copyright (C) 2014 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +#pragma once + +#if ENABLE(VIDEO_TRACK) + +#include "SerializedPlatformRepresentation.h" +#include "TextTrackCue.h" +#include <runtime/ArrayBuffer.h> +#include <runtime/JSCJSValue.h> +#include <wtf/MediaTime.h> + +namespace WebCore { + +class ScriptExecutionContext; + +class DataCue final : public TextTrackCue { +public: + static Ref<DataCue> create(ScriptExecutionContext& context, const MediaTime& start, const MediaTime& end, ArrayBuffer& data) + { + return adoptRef(*new DataCue(context, start, end, data, emptyString())); + } + + static Ref<DataCue> create(ScriptExecutionContext& context, const MediaTime& start, const MediaTime& end, const void* data, unsigned length) + { + return adoptRef(*new DataCue(context, start, end, data, length)); + } + + static Ref<DataCue> create(ScriptExecutionContext& context, const MediaTime& start, const MediaTime& end, ArrayBuffer& data, const String& type) + { + return adoptRef(*new DataCue(context, start, end, data, type)); + } + + static Ref<DataCue> create(ScriptExecutionContext& context, const MediaTime& start, const MediaTime& end, RefPtr<SerializedPlatformRepresentation>&& platformValue, const String& type) + { + return adoptRef(*new DataCue(context, start, end, WTFMove(platformValue), type)); + } + + static Ref<DataCue> create(ScriptExecutionContext& context, double start, double end, ArrayBuffer& data) + { + return adoptRef(*new DataCue(context, MediaTime::createWithDouble(start), MediaTime::createWithDouble(end), data, emptyString())); + } + static Ref<DataCue> create(ScriptExecutionContext& context, double start, double end, JSC::JSValue value, const String& type) + { + return adoptRef(*new DataCue(context, MediaTime::createWithDouble(start), MediaTime::createWithDouble(end), value, type)); + } + + virtual ~DataCue(); + CueType cueType() const override { return Data; } + + RefPtr<JSC::ArrayBuffer> data() const; + void setData(JSC::ArrayBuffer&); + + const SerializedPlatformRepresentation* platformValue() const { return m_platformValue.get(); } + + JSC::JSValue value(JSC::ExecState&) const; + void setValue(JSC::ExecState&, JSC::JSValue); + + String type() const { return m_type; } + void setType(const String& type) { m_type = type; } + + bool isEqual(const TextTrackCue&, CueMatchRules) const override; + bool cueContentsMatch(const TextTrackCue&) const override; + bool doesExtendCue(const TextTrackCue&) const override; + +private: + DataCue(ScriptExecutionContext&, const MediaTime& start, const MediaTime& end, ArrayBuffer&, const String&); + DataCue(ScriptExecutionContext&, const MediaTime& start, const MediaTime& end, const void*, unsigned); + DataCue(ScriptExecutionContext&, const MediaTime& start, const MediaTime& end, RefPtr<SerializedPlatformRepresentation>&&, const String&); + DataCue(ScriptExecutionContext&, const MediaTime& start, const MediaTime& end, JSC::JSValue, const String&); + + JSC::JSValue valueOrNull() const; + + RefPtr<ArrayBuffer> m_data; + String m_type; + RefPtr<SerializedPlatformRepresentation> m_platformValue; + JSC::JSValue m_value; +}; + +DataCue* toDataCue(TextTrackCue*); +const DataCue* toDataCue(const TextTrackCue*); + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/track/DataCue.idl b/Source/WebCore/html/track/DataCue.idl new file mode 100644 index 000000000..3ebc041c1 --- /dev/null +++ b/Source/WebCore/html/track/DataCue.idl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014 Cable Television Labs Inc. All rights reserved. + * Copyright (C) 2014 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +[ + Conditional=VIDEO_TRACK, + Constructor(unrestricted double startTime, unrestricted double endTime, ArrayBuffer data), + Constructor(unrestricted double startTime, unrestricted double endTime, any value, optional DOMString type), + ConstructorCallWith=ScriptExecutionContext +] interface DataCue : TextTrackCue { + attribute ArrayBuffer data; + + // Proposed extensions. + [CallWith=ScriptState] attribute any value; + readonly attribute DOMString type; +}; diff --git a/Source/WebCore/html/track/InbandDataTextTrack.cpp b/Source/WebCore/html/track/InbandDataTextTrack.cpp new file mode 100644 index 000000000..d2d4a01dc --- /dev/null +++ b/Source/WebCore/html/track/InbandDataTextTrack.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2014 Cable Television Labs Inc. All rights reserved. + * Copyright (C) 2014-2017 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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" +#include "InbandDataTextTrack.h" + +#if ENABLE(VIDEO_TRACK) + +#include "DataCue.h" +#include "HTMLMediaElement.h" +#include "InbandTextTrackPrivate.h" +#include "Logging.h" + +namespace WebCore { + +inline InbandDataTextTrack::InbandDataTextTrack(ScriptExecutionContext& context, TextTrackClient& client, InbandTextTrackPrivate& trackPrivate) + : InbandTextTrack(context, client, trackPrivate) +{ +} + +Ref<InbandDataTextTrack> InbandDataTextTrack::create(ScriptExecutionContext& context, TextTrackClient& client, InbandTextTrackPrivate& trackPrivate) +{ + return adoptRef(*new InbandDataTextTrack(context, client, trackPrivate)); +} + +InbandDataTextTrack::~InbandDataTextTrack() +{ +} + +void InbandDataTextTrack::addDataCue(const MediaTime& start, const MediaTime& end, const void* data, unsigned length) +{ + addCue(DataCue::create(*scriptExecutionContext(), start, end, data, length)); +} + +#if ENABLE(DATACUE_VALUE) + +void InbandDataTextTrack::addDataCue(const MediaTime& start, const MediaTime& end, Ref<SerializedPlatformRepresentation>&& platformValue, const String& type) +{ + if (m_incompleteCueMap.contains(platformValue.ptr())) + return; + + auto cue = DataCue::create(*scriptExecutionContext(), start, end, platformValue.copyRef(), type); + if (hasCue(cue.ptr(), TextTrackCue::IgnoreDuration)) { + LOG(Media, "InbandDataTextTrack::addDataCue ignoring already added cue: start=%s, end=%s\n", toString(cue->startTime()).utf8().data(), toString(cue->endTime()).utf8().data()); + return; + } + + if (end.isPositiveInfinite() && mediaElement()) { + cue->setEndTime(mediaElement()->durationMediaTime()); + m_incompleteCueMap.add(WTFMove(platformValue), cue.copyRef()); + } + + addCue(WTFMove(cue)); +} + +void InbandDataTextTrack::updateDataCue(const MediaTime& start, const MediaTime& inEnd, SerializedPlatformRepresentation& platformValue) +{ + RefPtr<DataCue> cue = m_incompleteCueMap.get(&platformValue); + if (!cue) + return; + + cue->willChange(); + + MediaTime end = inEnd; + if (end.isPositiveInfinite() && mediaElement()) + end = mediaElement()->durationMediaTime(); + else + m_incompleteCueMap.remove(&platformValue); + + LOG(Media, "InbandDataTextTrack::updateDataCue: was start=%s, end=%s, will be start=%s, end=%s\n", toString(cue->startTime()).utf8().data(), toString(cue->endTime()).utf8().data(), toString(start).utf8().data(), toString(end).utf8().data()); + + cue->setStartTime(start); + cue->setEndTime(end); + + cue->didChange(); +} + +void InbandDataTextTrack::removeDataCue(const MediaTime&, const MediaTime&, SerializedPlatformRepresentation& platformValue) +{ + if (auto cue = m_incompleteCueMap.take(&platformValue)) { + LOG(Media, "InbandDataTextTrack::removeDataCue removing cue: start=%s, end=%s\n", toString(cue->startTime()).utf8().data(), toString(cue->endTime()).utf8().data()); + InbandTextTrack::removeCue(*cue); + } +} + +ExceptionOr<void> InbandDataTextTrack::removeCue(TextTrackCue& cue) +{ + ASSERT(cue.cueType() == TextTrackCue::Data); + + m_incompleteCueMap.remove(const_cast<SerializedPlatformRepresentation*>(toDataCue(&cue)->platformValue())); + + return InbandTextTrack::removeCue(cue); +} + +#endif + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/track/InbandDataTextTrack.h b/Source/WebCore/html/track/InbandDataTextTrack.h new file mode 100644 index 000000000..67aeb6d05 --- /dev/null +++ b/Source/WebCore/html/track/InbandDataTextTrack.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 Cable Television Labs Inc. All rights reserved. + * Copyright (C) 2014-2017 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#pragma once + +#if ENABLE(VIDEO_TRACK) + +#include "InbandTextTrack.h" + +namespace WebCore { + +class DataCue; + +class InbandDataTextTrack final : public InbandTextTrack { +public: + static Ref<InbandDataTextTrack> create(ScriptExecutionContext&, TextTrackClient&, InbandTextTrackPrivate&); + virtual ~InbandDataTextTrack(); + +private: + InbandDataTextTrack(ScriptExecutionContext&, TextTrackClient&, InbandTextTrackPrivate&); + + void addDataCue(const MediaTime& start, const MediaTime& end, const void*, unsigned) final; + +#if ENABLE(DATACUE_VALUE) + void addDataCue(const MediaTime& start, const MediaTime& end, Ref<SerializedPlatformRepresentation>&&, const String&) final; + void updateDataCue(const MediaTime& start, const MediaTime& end, SerializedPlatformRepresentation&) final; + void removeDataCue(const MediaTime& start, const MediaTime& end, SerializedPlatformRepresentation&) final; + ExceptionOr<void> removeCue(TextTrackCue&) final; + + HashMap<RefPtr<SerializedPlatformRepresentation>, RefPtr<DataCue>> m_incompleteCueMap; +#endif +}; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/track/InbandGenericTextTrack.cpp b/Source/WebCore/html/track/InbandGenericTextTrack.cpp index d807dc2d3..8065c8843 100644 --- a/Source/WebCore/html/track/InbandGenericTextTrack.cpp +++ b/Source/WebCore/html/track/InbandGenericTextTrack.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2012-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,170 +24,199 @@ */ #include "config.h" +#include "InbandGenericTextTrack.h" #if ENABLE(VIDEO_TRACK) -#include "InbandGenericTextTrack.h" - -#include "ExceptionCodePlaceholder.h" +#include "DataCue.h" #include "HTMLMediaElement.h" #include "InbandTextTrackPrivate.h" #include "Logging.h" +#include "VTTRegionList.h" #include <math.h> #include <wtf/text/CString.h> namespace WebCore { -GenericTextTrackCueMap::GenericTextTrackCueMap() +void GenericTextTrackCueMap::add(GenericCueData& cueData, TextTrackCueGeneric& cue) { + m_dataToCueMap.add(&cueData, &cue); + m_cueToDataMap.add(&cue, &cueData); } -GenericTextTrackCueMap::~GenericTextTrackCueMap() +TextTrackCueGeneric* GenericTextTrackCueMap::find(GenericCueData& cueData) { + return m_dataToCueMap.get(&cueData); } -void GenericTextTrackCueMap::add(GenericCueData* cueData, TextTrackCueGeneric* cue) +GenericCueData* GenericTextTrackCueMap::find(TextTrackCue& cue) { - m_dataToCueMap.add(cueData, cue); - m_cueToDataMap.add(cue, cueData); -} - -PassRefPtr<TextTrackCueGeneric> GenericTextTrackCueMap::find(GenericCueData* cueData) -{ - CueDataToCueMap::iterator iter = m_dataToCueMap.find(cueData); - if (iter == m_dataToCueMap.end()) - return 0; - - return iter->value; + return m_cueToDataMap.get(&cue); } -PassRefPtr<GenericCueData> GenericTextTrackCueMap::find(TextTrackCue* cue) +void GenericTextTrackCueMap::remove(GenericCueData& cueData) { - CueToDataMap::iterator iter = m_cueToDataMap.find(cue); - if (iter == m_cueToDataMap.end()) - return 0; - - return iter->value; -} - -void GenericTextTrackCueMap::remove(GenericCueData* cueData) -{ - RefPtr<TextTrackCueGeneric> cue = find(cueData); - - if (cue) + if (auto cue = m_dataToCueMap.take(&cueData)) m_cueToDataMap.remove(cue); - m_dataToCueMap.remove(cueData); } -void GenericTextTrackCueMap::remove(TextTrackCue* cue) +void GenericTextTrackCueMap::remove(TextTrackCue& cue) { - RefPtr<GenericCueData> genericData = find(cue); - if (genericData) { - m_dataToCueMap.remove(genericData); - m_cueToDataMap.remove(cue); - } + if (auto data = m_cueToDataMap.take(&cue)) + m_dataToCueMap.remove(data); } -PassRefPtr<InbandGenericTextTrack> InbandGenericTextTrack::create(ScriptExecutionContext* context, TextTrackClient* client, PassRefPtr<InbandTextTrackPrivate> playerPrivate) +inline InbandGenericTextTrack::InbandGenericTextTrack(ScriptExecutionContext& context, TextTrackClient& client, InbandTextTrackPrivate& trackPrivate) + : InbandTextTrack(context, client, trackPrivate) { - return adoptRef(new InbandGenericTextTrack(context, client, playerPrivate)); } -InbandGenericTextTrack::InbandGenericTextTrack(ScriptExecutionContext* context, TextTrackClient* client, PassRefPtr<InbandTextTrackPrivate> trackPrivate) - : InbandTextTrack(context, client, trackPrivate) +Ref<InbandGenericTextTrack> InbandGenericTextTrack::create(ScriptExecutionContext& context, TextTrackClient& client, InbandTextTrackPrivate& trackPrivate) { + return adoptRef(*new InbandGenericTextTrack(context, client, trackPrivate)); } InbandGenericTextTrack::~InbandGenericTextTrack() { } -void InbandGenericTextTrack::updateCueFromCueData(TextTrackCueGeneric* cue, GenericCueData* cueData) +void InbandGenericTextTrack::updateCueFromCueData(TextTrackCueGeneric& cue, GenericCueData& cueData) { - cue->willChange(); - - cue->setStartTime(cueData->startTime(), IGNORE_EXCEPTION); - double endTime = cueData->endTime(); - if (std::isinf(endTime) && mediaElement()) - endTime = mediaElement()->duration(); - cue->setEndTime(endTime, IGNORE_EXCEPTION); - cue->setText(cueData->content()); - cue->setId(cueData->id()); - cue->setBaseFontSizeRelativeToVideoHeight(cueData->baseFontSize()); - cue->setFontSizeMultiplier(cueData->relativeFontSize()); - cue->setFontName(cueData->fontName()); - - if (cueData->position() > 0) - cue->setPosition(lround(cueData->position()), IGNORE_EXCEPTION); - if (cueData->line() > 0) - cue->setLine(lround(cueData->line()), IGNORE_EXCEPTION); - if (cueData->size() > 0) - cue->setSize(lround(cueData->size()), IGNORE_EXCEPTION); - if (cueData->backgroundColor().isValid()) - cue->setBackgroundColor(cueData->backgroundColor().rgb()); - if (cueData->foregroundColor().isValid()) - cue->setForegroundColor(cueData->foregroundColor().rgb()); - if (cueData->highlightColor().isValid()) - cue->setHighlightColor(cueData->highlightColor().rgb()); - - if (cueData->align() == GenericCueData::Start) - cue->setAlign(ASCIILiteral("start"), IGNORE_EXCEPTION); - else if (cueData->align() == GenericCueData::Middle) - cue->setAlign(ASCIILiteral("middle"), IGNORE_EXCEPTION); - else if (cueData->align() == GenericCueData::End) - cue->setAlign(ASCIILiteral("end"), IGNORE_EXCEPTION); - cue->setSnapToLines(false); - - cue->didChange(); + cue.willChange(); + + cue.setStartTime(cueData.startTime()); + MediaTime endTime = cueData.endTime(); + if (endTime.isPositiveInfinite() && mediaElement()) + endTime = mediaElement()->durationMediaTime(); + cue.setEndTime(endTime); + cue.setText(cueData.content()); + cue.setId(cueData.id()); + cue.setBaseFontSizeRelativeToVideoHeight(cueData.baseFontSize()); + cue.setFontSizeMultiplier(cueData.relativeFontSize()); + cue.setFontName(cueData.fontName()); + + if (cueData.position() > 0) + cue.setPosition(std::round(cueData.position())); + if (cueData.line() > 0) + cue.setLine(std::round(cueData.line())); + if (cueData.size() > 0) + cue.setSize(std::round(cueData.size())); + if (cueData.backgroundColor().isValid()) + cue.setBackgroundColor(cueData.backgroundColor().rgb()); + if (cueData.foregroundColor().isValid()) + cue.setForegroundColor(cueData.foregroundColor().rgb()); + if (cueData.highlightColor().isValid()) + cue.setHighlightColor(cueData.highlightColor().rgb()); + + if (cueData.align() == GenericCueData::Start) + cue.setAlign(ASCIILiteral("start")); + else if (cueData.align() == GenericCueData::Middle) + cue.setAlign(ASCIILiteral("middle")); + else if (cueData.align() == GenericCueData::End) + cue.setAlign(ASCIILiteral("end")); + cue.setSnapToLines(false); + + cue.didChange(); } -void InbandGenericTextTrack::addGenericCue(InbandTextTrackPrivate* trackPrivate, PassRefPtr<GenericCueData> prpCueData) +void InbandGenericTextTrack::addGenericCue(GenericCueData& cueData) { - ASSERT_UNUSED(trackPrivate, trackPrivate == m_private); - - RefPtr<GenericCueData> cueData = prpCueData; - if (m_cueMap.find(cueData.get())) + if (m_cueMap.find(cueData)) return; - RefPtr<TextTrackCueGeneric> cue = TextTrackCueGeneric::create(*scriptExecutionContext(), cueData->startTime(), cueData->endTime(), cueData->content()); - updateCueFromCueData(cue.get(), cueData.get()); - if (hasCue(cue.get(), TextTrackCue::IgnoreDuration)) { - LOG(Media, "InbandGenericTextTrack::addGenericCue ignoring already added cue: start=%.2f, end=%.2f, content=\"%s\"\n", cueData->startTime(), cueData->endTime(), cueData->content().utf8().data()); + auto cue = TextTrackCueGeneric::create(*scriptExecutionContext(), cueData.startTime(), cueData.endTime(), cueData.content()); + updateCueFromCueData(cue.get(), cueData); + if (hasCue(cue.ptr(), TextTrackCue::IgnoreDuration)) { + LOG(Media, "InbandGenericTextTrack::addGenericCue ignoring already added cue: start=%s, end=%s, content=\"%s\"\n", toString(cueData.startTime()).utf8().data(), toString(cueData.endTime()).utf8().data(), cueData.content().utf8().data()); return; } - if (cueData->status() != GenericCueData::Complete) - m_cueMap.add(cueData.get(), cue.get()); + LOG(Media, "InbandGenericTextTrack::addGenericCue added cue: start=%.2f, end=%.2f, content=\"%s\"\n", cueData.startTime().toDouble(), cueData.endTime().toDouble(), cueData.content().utf8().data()); - addCue(cue); + if (cueData.status() != GenericCueData::Complete) + m_cueMap.add(cueData, cue); + + addCue(WTFMove(cue)); } -void InbandGenericTextTrack::updateGenericCue(InbandTextTrackPrivate*, GenericCueData* cueData) +void InbandGenericTextTrack::updateGenericCue(GenericCueData& cueData) { - RefPtr<TextTrackCueGeneric> cue = m_cueMap.find(cueData); + auto* cue = m_cueMap.find(cueData); if (!cue) return; - updateCueFromCueData(cue.get(), cueData); + updateCueFromCueData(*cue, cueData); - if (cueData->status() == GenericCueData::Complete) + if (cueData.status() == GenericCueData::Complete) m_cueMap.remove(cueData); } -void InbandGenericTextTrack::removeGenericCue(InbandTextTrackPrivate*, GenericCueData* cueData) +void InbandGenericTextTrack::removeGenericCue(GenericCueData& cueData) { - RefPtr<TextTrackCueGeneric> cue = m_cueMap.find(cueData); + auto* cue = m_cueMap.find(cueData); if (cue) { - LOG(Media, "InbandGenericTextTrack::removeGenericCue removing cue: start=%.2f, end=%.2f, content=\"%s\"\n", cueData->startTime(), cueData->endTime(), cueData->content().utf8().data()); - removeCue(cue.get(), IGNORE_EXCEPTION); - } else - m_cueMap.remove(cueData); + LOG(Media, "InbandGenericTextTrack::removeGenericCue removing cue: start=%s, end=%s, content=\"%s\"\n", toString(cueData.startTime()).utf8().data(), toString(cueData.endTime()).utf8().data(), cueData.content().utf8().data()); + removeCue(*cue); + } else { + LOG(Media, "InbandGenericTextTrack::removeGenericCue UNABLE to find cue: start=%.2f, end=%.2f, content=\"%s\"\n", cueData.startTime().toDouble(), cueData.endTime().toDouble(), cueData.content().utf8().data()); + } +} + +ExceptionOr<void> InbandGenericTextTrack::removeCue(TextTrackCue& cue) +{ + auto result = TextTrack::removeCue(cue); + if (!result.hasException()) + m_cueMap.remove(cue); + return result; +} + +WebVTTParser& InbandGenericTextTrack::parser() +{ + if (!m_webVTTParser) + m_webVTTParser = std::make_unique<WebVTTParser>(static_cast<WebVTTParserClient*>(this), scriptExecutionContext()); + return *m_webVTTParser; +} + +void InbandGenericTextTrack::parseWebVTTCueData(const ISOWebVTTCue& cueData) +{ + parser().parseCueData(cueData); +} + +void InbandGenericTextTrack::parseWebVTTFileHeader(String&& header) +{ + parser().parseFileHeader(WTFMove(header)); +} + +void InbandGenericTextTrack::newCuesParsed() +{ + Vector<RefPtr<WebVTTCueData>> cues; + parser().getNewCues(cues); + + for (auto& cueData : cues) { + auto vttCue = VTTCue::create(*scriptExecutionContext(), *cueData); + + if (hasCue(vttCue.ptr(), TextTrackCue::IgnoreDuration)) { + LOG(Media, "InbandGenericTextTrack::newCuesParsed ignoring already added cue: start=%.2f, end=%.2f, content=\"%s\"\n", vttCue->startTime(), vttCue->endTime(), vttCue->text().utf8().data()); + return; + } + addCue(WTFMove(vttCue)); + } +} + +void InbandGenericTextTrack::newRegionsParsed() +{ + Vector<RefPtr<VTTRegion>> newRegions; + parser().getNewRegions(newRegions); + + for (auto& region : newRegions) { + region->setTrack(this); + regions()->add(region.releaseNonNull()); + } } -void InbandGenericTextTrack::removeCue(TextTrackCue* cue, ExceptionCode& ec) +void InbandGenericTextTrack::fileFailedToParse() { - m_cueMap.remove(cue); - TextTrack::removeCue(cue, ec); + LOG(Media, "Error parsing WebVTT stream."); } } // namespace WebCore diff --git a/Source/WebCore/html/track/InbandGenericTextTrack.h b/Source/WebCore/html/track/InbandGenericTextTrack.h index 2456a9adc..e5feb254b 100644 --- a/Source/WebCore/html/track/InbandGenericTextTrack.h +++ b/Source/WebCore/html/track/InbandGenericTextTrack.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2012-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,64 +23,61 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef InbandGenericTextTrack_h -#define InbandGenericTextTrack_h +#pragma once #if ENABLE(VIDEO_TRACK) #include "InbandTextTrack.h" #include "TextTrackCueGeneric.h" -#include <wtf/RefPtr.h> +#include "WebVTTParser.h" namespace WebCore { -class Document; -class InbandTextTrackPrivate; -class TextTrackCue; - class GenericTextTrackCueMap { public: - GenericTextTrackCueMap(); - virtual ~GenericTextTrackCueMap(); - - void add(GenericCueData*, TextTrackCueGeneric*); + void add(GenericCueData&, TextTrackCueGeneric&); - void remove(TextTrackCue*); - void remove(GenericCueData*); + void remove(TextTrackCue&); + void remove(GenericCueData&); - PassRefPtr<GenericCueData> find(TextTrackCue*); - PassRefPtr<TextTrackCueGeneric> find(GenericCueData*); + GenericCueData* find(TextTrackCue&); + TextTrackCueGeneric* find(GenericCueData&); private: - typedef HashMap<RefPtr<TextTrackCue>, RefPtr<GenericCueData>> CueToDataMap; - typedef HashMap<RefPtr<GenericCueData>, RefPtr<TextTrackCueGeneric>> CueDataToCueMap; + using CueToDataMap = HashMap<RefPtr<TextTrackCue>, RefPtr<GenericCueData>>; + using CueDataToCueMap = HashMap<RefPtr<GenericCueData>, RefPtr<TextTrackCueGeneric>>; CueToDataMap m_cueToDataMap; CueDataToCueMap m_dataToCueMap; }; -class InbandGenericTextTrack : public InbandTextTrack { +class InbandGenericTextTrack final : public InbandTextTrack, private WebVTTParserClient { public: - static PassRefPtr<InbandGenericTextTrack> create(ScriptExecutionContext*, TextTrackClient*, PassRefPtr<InbandTextTrackPrivate>); + static Ref<InbandGenericTextTrack> create(ScriptExecutionContext&, TextTrackClient&, InbandTextTrackPrivate&); virtual ~InbandGenericTextTrack(); private: - InbandGenericTextTrack(ScriptExecutionContext*, TextTrackClient*, PassRefPtr<InbandTextTrackPrivate>); + InbandGenericTextTrack(ScriptExecutionContext&, TextTrackClient&, InbandTextTrackPrivate&); - virtual void addGenericCue(InbandTextTrackPrivate*, PassRefPtr<GenericCueData>) override; - virtual void updateGenericCue(InbandTextTrackPrivate*, GenericCueData*) override; - virtual void removeGenericCue(InbandTextTrackPrivate*, GenericCueData*) override; - virtual void removeCue(TextTrackCue*, ExceptionCode&) override; + void addGenericCue(GenericCueData&) final; + void updateGenericCue(GenericCueData&) final; + void removeGenericCue(GenericCueData&) final; + ExceptionOr<void> removeCue(TextTrackCue&) final; - virtual void parseWebVTTCueData(InbandTextTrackPrivate*, const char*, unsigned) override { ASSERT_NOT_REACHED(); } + void updateCueFromCueData(TextTrackCueGeneric&, GenericCueData&); - PassRefPtr<TextTrackCueGeneric> createCue(PassRefPtr<GenericCueData>); - void updateCueFromCueData(TextTrackCueGeneric*, GenericCueData*); + WebVTTParser& parser(); + void parseWebVTTCueData(const ISOWebVTTCue&) final; + void parseWebVTTFileHeader(String&&) final; + + void newCuesParsed() final; + void newRegionsParsed() final; + void fileFailedToParse() final; GenericTextTrackCueMap m_cueMap; + std::unique_ptr<WebVTTParser> m_webVTTParser; }; } // namespace WebCore #endif -#endif diff --git a/Source/WebCore/html/track/InbandTextTrack.cpp b/Source/WebCore/html/track/InbandTextTrack.cpp index f8772cbc2..bd65e548a 100644 --- a/Source/WebCore/html/track/InbandTextTrack.cpp +++ b/Source/WebCore/html/track/InbandTextTrack.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2012-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,160 +24,171 @@ */ #include "config.h" +#include "InbandTextTrack.h" #if ENABLE(VIDEO_TRACK) -#include "InbandTextTrack.h" - -#include "Document.h" -#include "Event.h" -#include "ExceptionCodePlaceholder.h" #include "HTMLMediaElement.h" +#include "InbandDataTextTrack.h" #include "InbandGenericTextTrack.h" #include "InbandTextTrackPrivate.h" #include "InbandWebVTTTextTrack.h" -#include "Logging.h" -#include "TextTrackCueList.h" -#include <math.h> -#include <wtf/text/CString.h> namespace WebCore { -PassRefPtr<InbandTextTrack> InbandTextTrack::create(ScriptExecutionContext* context, - TextTrackClient* client, PassRefPtr<InbandTextTrackPrivate> trackPrivate) +Ref<InbandTextTrack> InbandTextTrack::create(ScriptExecutionContext& context, TextTrackClient& client, InbandTextTrackPrivate& trackPrivate) { - switch (trackPrivate->cueFormat()) { + switch (trackPrivate.cueFormat()) { + case InbandTextTrackPrivate::Data: + return InbandDataTextTrack::create(context, client, trackPrivate); case InbandTextTrackPrivate::Generic: return InbandGenericTextTrack::create(context, client, trackPrivate); case InbandTextTrackPrivate::WebVTT: return InbandWebVTTTextTrack::create(context, client, trackPrivate); - default: - ASSERT_NOT_REACHED(); - return 0; } + ASSERT_NOT_REACHED(); + return InbandDataTextTrack::create(context, client, trackPrivate); } -InbandTextTrack::InbandTextTrack(ScriptExecutionContext* context, TextTrackClient* client, PassRefPtr<InbandTextTrackPrivate> trackPrivate) - : TextTrack(context, client, emptyString(), trackPrivate->id(), trackPrivate->label(), trackPrivate->language(), InBand) +InbandTextTrack::InbandTextTrack(ScriptExecutionContext& context, TextTrackClient& client, InbandTextTrackPrivate& trackPrivate) + : TextTrack(&context, &client, emptyAtom, trackPrivate.id(), trackPrivate.label(), trackPrivate.language(), InBand) , m_private(trackPrivate) { m_private->setClient(this); - - switch (m_private->kind()) { - case InbandTextTrackPrivate::Subtitles: - setKind(TextTrack::subtitlesKeyword()); - break; - case InbandTextTrackPrivate::Captions: - setKind(TextTrack::captionsKeyword()); - break; - case InbandTextTrackPrivate::Descriptions: - setKind(TextTrack::descriptionsKeyword()); - break; - case InbandTextTrackPrivate::Chapters: - setKind(TextTrack::chaptersKeyword()); - break; - case InbandTextTrackPrivate::Metadata: - setKind(TextTrack::metadataKeyword()); - break; - case InbandTextTrackPrivate::Forced: - setKind(TextTrack::forcedKeyword()); - break; - case InbandTextTrackPrivate::None: - default: - ASSERT_NOT_REACHED(); - break; - } + updateKindFromPrivate(); } InbandTextTrack::~InbandTextTrack() { - m_private->setClient(0); + m_private->setClient(nullptr); +} + +void InbandTextTrack::setPrivate(InbandTextTrackPrivate& trackPrivate) +{ + if (m_private.ptr() == &trackPrivate) + return; + + m_private->setClient(nullptr); + m_private = trackPrivate; + m_private->setClient(this); + + setModeInternal(mode()); + updateKindFromPrivate(); } -void InbandTextTrack::setMode(const AtomicString& mode) +void InbandTextTrack::setMode(Mode mode) { TextTrack::setMode(mode); + setModeInternal(mode); +} - if (mode == TextTrack::disabledKeyword()) - m_private->setMode(InbandTextTrackPrivate::Disabled); - else if (mode == TextTrack::hiddenKeyword()) - m_private->setMode(InbandTextTrackPrivate::Hidden); - else if (mode == TextTrack::showingKeyword()) - m_private->setMode(InbandTextTrackPrivate::Showing); - else - ASSERT_NOT_REACHED(); +static inline InbandTextTrackPrivate::Mode toPrivate(TextTrack::Mode mode) +{ + switch (mode) { + case TextTrack::Mode::Disabled: + return InbandTextTrackPrivate::Disabled; + case TextTrack::Mode::Hidden: + return InbandTextTrackPrivate::Hidden; + case TextTrack::Mode::Showing: + return InbandTextTrackPrivate::Showing; + } + ASSERT_NOT_REACHED(); + return InbandTextTrackPrivate::Disabled; } -bool InbandTextTrack::isClosedCaptions() const +void InbandTextTrack::setModeInternal(Mode mode) { - if (!m_private) - return false; + m_private->setMode(toPrivate(mode)); +} +bool InbandTextTrack::isClosedCaptions() const +{ return m_private->isClosedCaptions(); } bool InbandTextTrack::isSDH() const { - if (!m_private) - return false; - return m_private->isSDH(); } bool InbandTextTrack::containsOnlyForcedSubtitles() const { - if (!m_private) - return false; - return m_private->containsOnlyForcedSubtitles(); } bool InbandTextTrack::isMainProgramContent() const { - if (!m_private) - return false; - return m_private->isMainProgramContent(); } bool InbandTextTrack::isEasyToRead() const { - if (!m_private) - return false; - return m_private->isEasyToRead(); } size_t InbandTextTrack::inbandTrackIndex() { - ASSERT(m_private); return m_private->trackIndex(); } -void InbandTextTrack::idChanged(TrackPrivateBase* trackPrivate, const String& id) +AtomicString InbandTextTrack::inBandMetadataTrackDispatchType() const +{ + return m_private->inBandMetadataTrackDispatchType(); +} + +void InbandTextTrack::idChanged(const AtomicString& id) { - ASSERT_UNUSED(trackPrivate, trackPrivate == m_private); setId(id); } -void InbandTextTrack::labelChanged(TrackPrivateBase* trackPrivate, const String& label) +void InbandTextTrack::labelChanged(const AtomicString& label) { - ASSERT_UNUSED(trackPrivate, trackPrivate == m_private); setLabel(label); } -void InbandTextTrack::languageChanged(TrackPrivateBase* trackPrivate, const String& language) +void InbandTextTrack::languageChanged(const AtomicString& language) { - ASSERT_UNUSED(trackPrivate, trackPrivate == m_private); setLanguage(language); } -void InbandTextTrack::willRemove(TrackPrivateBase* trackPrivate) +void InbandTextTrack::willRemove() +{ + auto* element = mediaElement(); + if (!element) + return; + element->removeTextTrack(*this); +} + +void InbandTextTrack::updateKindFromPrivate() { - if (!mediaElement()) + switch (m_private->kind()) { + case InbandTextTrackPrivate::Subtitles: + setKind(Kind::Subtitles); return; - ASSERT_UNUSED(trackPrivate, trackPrivate == m_private); - mediaElement()->removeTextTrack(this); + case InbandTextTrackPrivate::Captions: + setKind(Kind::Captions); + return; + case InbandTextTrackPrivate::Descriptions: + setKind(Kind::Descriptions); + return; + case InbandTextTrackPrivate::Chapters: + setKind(Kind::Chapters); + return; + case InbandTextTrackPrivate::Metadata: + setKind(Kind::Metadata); + return; + case InbandTextTrackPrivate::Forced: + setKind(Kind::Forced); + return; + case InbandTextTrackPrivate::None: + break; + } + ASSERT_NOT_REACHED(); +} + +MediaTime InbandTextTrack::startTimeVariance() const +{ + return m_private->startTimeVariance(); } } // namespace WebCore diff --git a/Source/WebCore/html/track/InbandTextTrack.h b/Source/WebCore/html/track/InbandTextTrack.h index 1e1313bf5..6cf2687bb 100644 --- a/Source/WebCore/html/track/InbandTextTrack.h +++ b/Source/WebCore/html/track/InbandTextTrack.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2012-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,49 +23,70 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef InbandTextTrack_h -#define InbandTextTrack_h +#pragma once #if ENABLE(VIDEO_TRACK) #include "InbandTextTrackPrivateClient.h" #include "TextTrack.h" -#include "TextTrackCueGeneric.h" -#include <wtf/RefPtr.h> namespace WebCore { -class InbandTextTrack : public TextTrack, public InbandTextTrackPrivateClient { +class InbandTextTrack : public TextTrack, private InbandTextTrackPrivateClient { public: - static PassRefPtr<InbandTextTrack> create(ScriptExecutionContext*, TextTrackClient*, PassRefPtr<InbandTextTrackPrivate>); + static Ref<InbandTextTrack> create(ScriptExecutionContext&, TextTrackClient&, InbandTextTrackPrivate&); virtual ~InbandTextTrack(); - virtual bool isClosedCaptions() const override; - virtual bool isSDH() const override; - virtual bool containsOnlyForcedSubtitles() const override; - virtual bool isMainProgramContent() const override; - virtual bool isEasyToRead() const override; - virtual void setMode(const AtomicString&) override; + bool isClosedCaptions() const override; + bool isSDH() const override; + bool containsOnlyForcedSubtitles() const override; + bool isMainProgramContent() const override; + bool isEasyToRead() const override; + void setMode(Mode) override; size_t inbandTrackIndex(); + AtomicString inBandMetadataTrackDispatchType() const override; + + void setPrivate(InbandTextTrackPrivate&); + protected: - InbandTextTrack(ScriptExecutionContext*, TextTrackClient*, PassRefPtr<InbandTextTrackPrivate>); + InbandTextTrack(ScriptExecutionContext&, TextTrackClient&, InbandTextTrackPrivate&); + + void setModeInternal(Mode); + void updateKindFromPrivate(); - RefPtr<InbandTextTrackPrivate> m_private; + Ref<InbandTextTrackPrivate> m_private; private: + bool isInband() const final { return true; } + void idChanged(const AtomicString&) override; + void labelChanged(const AtomicString&) override; + void languageChanged(const AtomicString&) override; + void willRemove() override; - virtual void idChanged(TrackPrivateBase*, const String&) override; - virtual void labelChanged(TrackPrivateBase*, const String&) override; - virtual void languageChanged(TrackPrivateBase*, const String&) override; - virtual void willRemove(TrackPrivateBase*) override; + void addDataCue(const MediaTime&, const MediaTime&, const void*, unsigned) override { ASSERT_NOT_REACHED(); } -#if USE(PLATFORM_TEXT_TRACK_MENU) - virtual InbandTextTrackPrivate* privateTrack() override { return m_private.get(); } +#if ENABLE(DATACUE_VALUE) + void addDataCue(const MediaTime&, const MediaTime&, Ref<SerializedPlatformRepresentation>&&, const String&) override { ASSERT_NOT_REACHED(); } + void updateDataCue(const MediaTime&, const MediaTime&, SerializedPlatformRepresentation&) override { ASSERT_NOT_REACHED(); } + void removeDataCue(const MediaTime&, const MediaTime&, SerializedPlatformRepresentation&) override { ASSERT_NOT_REACHED(); } #endif + + void addGenericCue(GenericCueData&) override { ASSERT_NOT_REACHED(); } + void updateGenericCue(GenericCueData&) override { ASSERT_NOT_REACHED(); } + void removeGenericCue(GenericCueData&) override { ASSERT_NOT_REACHED(); } + + void parseWebVTTFileHeader(String&&) override { ASSERT_NOT_REACHED(); } + void parseWebVTTCueData(const char*, unsigned) override { ASSERT_NOT_REACHED(); } + void parseWebVTTCueData(const ISOWebVTTCue&) override { ASSERT_NOT_REACHED(); } + + MediaTime startTimeVariance() const override; }; } // namespace WebCore -#endif -#endif +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::InbandTextTrack) + static bool isType(const WebCore::TextTrack& track) { return track.isInband(); } +SPECIALIZE_TYPE_TRAITS_END() + +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/html/track/InbandWebVTTTextTrack.cpp b/Source/WebCore/html/track/InbandWebVTTTextTrack.cpp index 7e03d41a6..d0fae4e9b 100644 --- a/Source/WebCore/html/track/InbandWebVTTTextTrack.cpp +++ b/Source/WebCore/html/track/InbandWebVTTTextTrack.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2012-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,60 +24,76 @@ */ #include "config.h" +#include "InbandWebVTTTextTrack.h" #if ENABLE(VIDEO_TRACK) -#include "InbandWebVTTTextTrack.h" - #include "InbandTextTrackPrivate.h" #include "Logging.h" +#include "VTTCue.h" +#include "VTTRegionList.h" #include <wtf/text/CString.h> namespace WebCore { -PassRefPtr<InbandTextTrack> InbandWebVTTTextTrack::create(ScriptExecutionContext* context, TextTrackClient* client, PassRefPtr<InbandTextTrackPrivate> playerPrivate) +inline InbandWebVTTTextTrack::InbandWebVTTTextTrack(ScriptExecutionContext& context, TextTrackClient& client, InbandTextTrackPrivate& trackPrivate) + : InbandTextTrack(context, client, trackPrivate) { - return adoptRef(new InbandWebVTTTextTrack(context, client, playerPrivate)); } -InbandWebVTTTextTrack::InbandWebVTTTextTrack(ScriptExecutionContext* context, TextTrackClient* client, PassRefPtr<InbandTextTrackPrivate> trackPrivate) - : InbandTextTrack(context, client, trackPrivate) +Ref<InbandTextTrack> InbandWebVTTTextTrack::create(ScriptExecutionContext& context, TextTrackClient& client, InbandTextTrackPrivate& trackPrivate) { + return adoptRef(*new InbandWebVTTTextTrack(context, client, trackPrivate)); } InbandWebVTTTextTrack::~InbandWebVTTTextTrack() { } -void InbandWebVTTTextTrack::parseWebVTTCueData(InbandTextTrackPrivate* trackPrivate, const char* data, unsigned length) +WebVTTParser& InbandWebVTTTextTrack::parser() { - ASSERT_UNUSED(trackPrivate, trackPrivate == m_private); if (!m_webVTTParser) - m_webVTTParser = WebVTTParser::create(this, scriptExecutionContext()); - m_webVTTParser->parseBytes(data, length); + m_webVTTParser = std::make_unique<WebVTTParser>(static_cast<WebVTTParserClient*>(this), scriptExecutionContext()); + return *m_webVTTParser; +} + +void InbandWebVTTTextTrack::parseWebVTTCueData(const char* data, unsigned length) +{ + parser().parseBytes(data, length); +} + +void InbandWebVTTTextTrack::parseWebVTTCueData(const ISOWebVTTCue& cueData) +{ + parser().parseCueData(cueData); } void InbandWebVTTTextTrack::newCuesParsed() { Vector<RefPtr<WebVTTCueData>> cues; - m_webVTTParser->getNewCues(cues); - for (size_t i = 0; i < cues.size(); ++i) { - RefPtr<WebVTTCueData> cueData = cues[i]; - RefPtr<TextTrackCue> cue = TextTrackCue::create(*scriptExecutionContext(), cueData->startTime(), cueData->endTime(), cueData->content()); - cue->setId(cueData->id()); - cue->setCueSettings(cueData->settings()); - - if (hasCue(cue.get(), TextTrackCue::IgnoreDuration)) { - LOG(Media, "InbandWebVTTTextTrack::newCuesParsed ignoring already added cue: start=%.2f, end=%.2f, content=\"%s\"\n", cueData->startTime(), cueData->endTime(), cueData->content().utf8().data()); + parser().getNewCues(cues); + for (auto& cueData : cues) { + auto vttCue = VTTCue::create(*scriptExecutionContext(), *cueData); + if (hasCue(vttCue.ptr(), TextTrackCue::IgnoreDuration)) { + LOG(Media, "InbandWebVTTTextTrack::newCuesParsed ignoring already added cue: start=%.2f, end=%.2f, content=\"%s\"\n", vttCue->startTime(), vttCue->endTime(), vttCue->text().utf8().data()); return; } - addCue(cue.release()); + addCue(WTFMove(vttCue)); + } +} + +void InbandWebVTTTextTrack::newRegionsParsed() +{ + Vector<RefPtr<VTTRegion>> newRegions; + parser().getNewRegions(newRegions); + for (auto& region : newRegions) { + region->setTrack(this); + regions()->add(region.releaseNonNull()); } } void InbandWebVTTTextTrack::fileFailedToParse() { - LOG(Media, "Unable to parse WebVTT stream."); + LOG(Media, "Error parsing WebVTT stream."); } } // namespace WebCore diff --git a/Source/WebCore/html/track/InbandWebVTTTextTrack.h b/Source/WebCore/html/track/InbandWebVTTTextTrack.h index c0c2ef830..21d913469 100644 --- a/Source/WebCore/html/track/InbandWebVTTTextTrack.h +++ b/Source/WebCore/html/track/InbandWebVTTTextTrack.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2012-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,41 +23,35 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef InbandWebVTTTextTrack_h -#define InbandWebVTTTextTrack_h +#pragma once #if ENABLE(VIDEO_TRACK) #include "InbandTextTrack.h" #include "WebVTTParser.h" -#include <wtf/RefPtr.h> +#include <memory> namespace WebCore { -class InbandWebVTTTextTrack : public InbandTextTrack, private WebVTTParserClient { +class InbandWebVTTTextTrack final : public InbandTextTrack, private WebVTTParserClient { public: - static PassRefPtr<InbandTextTrack> create(ScriptExecutionContext*, TextTrackClient*, PassRefPtr<InbandTextTrackPrivate>); + static Ref<InbandTextTrack> create(ScriptExecutionContext&, TextTrackClient&, InbandTextTrackPrivate&); virtual ~InbandWebVTTTextTrack(); private: - InbandWebVTTTextTrack(ScriptExecutionContext*, TextTrackClient*, PassRefPtr<InbandTextTrackPrivate>); + InbandWebVTTTextTrack(ScriptExecutionContext&, TextTrackClient&, InbandTextTrackPrivate&); - virtual void parseWebVTTCueData(InbandTextTrackPrivate*, const char* data, unsigned length) override; + WebVTTParser& parser(); + void parseWebVTTCueData(const char* data, unsigned length) final; + void parseWebVTTCueData(const ISOWebVTTCue&) final; - virtual void newCuesParsed() override; -#if ENABLE(WEBVTT_REGIONS) - virtual void newRegionsParsed() override; -#endif - virtual void fileFailedToParse() override; + void newCuesParsed() final; + void newRegionsParsed() final; + void fileFailedToParse() final; - virtual void addGenericCue(InbandTextTrackPrivate*, PassRefPtr<GenericCueData>) override { ASSERT_NOT_REACHED(); } - virtual void updateGenericCue(InbandTextTrackPrivate*, GenericCueData*) override { ASSERT_NOT_REACHED(); } - virtual void removeGenericCue(InbandTextTrackPrivate*, GenericCueData*) override { ASSERT_NOT_REACHED(); } - - OwnPtr<WebVTTParser> m_webVTTParser; + std::unique_ptr<WebVTTParser> m_webVTTParser; }; } // namespace WebCore -#endif -#endif +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/html/track/LoadableTextTrack.cpp b/Source/WebCore/html/track/LoadableTextTrack.cpp index e61cffc6a..5528de17b 100644 --- a/Source/WebCore/html/track/LoadableTextTrack.cpp +++ b/Source/WebCore/html/track/LoadableTextTrack.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2011, 2013 Google Inc. All rights reserved. + * Copyright (C) 2011-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,36 +25,24 @@ */ #include "config.h" +#include "LoadableTextTrack.h" #if ENABLE(VIDEO_TRACK) -#include "LoadableTextTrack.h" - -#include "Event.h" #include "HTMLTrackElement.h" -#include "ScriptExecutionContext.h" #include "TextTrackCueList.h" +#include "VTTRegionList.h" namespace WebCore { -LoadableTextTrack::LoadableTextTrack(HTMLTrackElement* track, const String& kind, const String& label, const String& language) - : TextTrack(&track->document(), track, kind, emptyString(), label, language, TrackElement) - , m_trackElement(track) - , m_loadTimer(this, &LoadableTextTrack::loadTimerFired) +LoadableTextTrack::LoadableTextTrack(HTMLTrackElement& track, const String& kind, const String& label, const String& language) + : TextTrack(&track.document(), &track, kind, emptyString(), label, language, TrackElement) + , m_trackElement(&track) + , m_loadTimer(*this, &LoadableTextTrack::loadTimerFired) , m_isDefault(false) { } -LoadableTextTrack::~LoadableTextTrack() -{ -} - -void LoadableTextTrack::clearClient() -{ - m_trackElement = 0; - TextTrack::clearClient(); -} - void LoadableTextTrack::scheduleLoad(const URL& url) { if (url == m_url) @@ -74,14 +63,8 @@ Element* LoadableTextTrack::element() { return m_trackElement; } - -void LoadableTextTrack::setTrackElement(HTMLTrackElement* element) -{ - ASSERT(!m_trackElement || m_trackElement == element); - m_trackElement = element; -} -void LoadableTextTrack::loadTimerFired(Timer<LoadableTextTrack>&) +void LoadableTextTrack::loadTimerFired() { if (m_loader) m_loader->cancelLoad(); @@ -94,14 +77,14 @@ void LoadableTextTrack::loadTimerFired(Timer<LoadableTextTrack>&) // 4. Download: If URL is not the empty string, perform a potentially CORS-enabled fetch of URL, with the // mode being the state of the media element's crossorigin content attribute, the origin being the // origin of the media element's Document, and the default origin behaviour set to fail. - m_loader = TextTrackLoader::create(*this, static_cast<ScriptExecutionContext*>(&m_trackElement->document())); - if (!m_loader->load(m_url, m_trackElement->mediaElementCrossOriginAttribute())) + m_loader = std::make_unique<TextTrackLoader>(static_cast<TextTrackLoaderClient&>(*this), static_cast<ScriptExecutionContext*>(&m_trackElement->document())); + if (!m_loader->load(m_url, m_trackElement->mediaElementCrossOriginAttribute(), m_trackElement->isInUserAgentShadowTree())) m_trackElement->didCompleteLoad(HTMLTrackElement::Failure); } void LoadableTextTrack::newCuesAvailable(TextTrackLoader* loader) { - ASSERT_UNUSED(loader, m_loader == loader); + ASSERT_UNUSED(loader, m_loader.get() == loader); Vector<RefPtr<TextTrackCue>> newCues; m_loader->getNewCues(newCues); @@ -109,18 +92,18 @@ void LoadableTextTrack::newCuesAvailable(TextTrackLoader* loader) if (!m_cues) m_cues = TextTrackCueList::create(); - for (size_t i = 0; i < newCues.size(); ++i) { - newCues[i]->setTrack(this); - m_cues->add(newCues[i]); + for (auto& newCue : newCues) { + newCue->setTrack(this); + m_cues->add(newCue.releaseNonNull()); } if (client()) - client()->textTrackAddCues(this, m_cues.get()); + client()->textTrackAddCues(*this, *m_cues); } void LoadableTextTrack::cueLoadingCompleted(TextTrackLoader* loader, bool loadingFailed) { - ASSERT_UNUSED(loader, m_loader == loader); + ASSERT_UNUSED(loader, m_loader.get() == loader); if (!m_trackElement) return; @@ -128,26 +111,24 @@ void LoadableTextTrack::cueLoadingCompleted(TextTrackLoader* loader, bool loadin m_trackElement->didCompleteLoad(loadingFailed ? HTMLTrackElement::Failure : HTMLTrackElement::Success); } -#if ENABLE(WEBVTT_REGIONS) void LoadableTextTrack::newRegionsAvailable(TextTrackLoader* loader) { - ASSERT_UNUSED(loader, m_loader == loader); + ASSERT_UNUSED(loader, m_loader.get() == loader); - Vector<RefPtr<TextTrackRegion>> newRegions; + Vector<RefPtr<VTTRegion>> newRegions; m_loader->getNewRegions(newRegions); - for (size_t i = 0; i < newRegions.size(); ++i) { - newRegions[i]->setTrack(this); - regionList()->add(newRegions[i]); + for (auto& newRegion : newRegions) { + newRegion->setTrack(this); + regions()->add(newRegion.releaseNonNull()); } } -#endif AtomicString LoadableTextTrack::id() const { - if (m_trackElement) - return m_trackElement->getAttribute("id"); - return emptyString(); + if (!m_trackElement) + return emptyAtom; + return m_trackElement->attributeWithoutSynchronization(idAttr); } size_t LoadableTextTrack::trackElementIndex() diff --git a/Source/WebCore/html/track/LoadableTextTrack.h b/Source/WebCore/html/track/LoadableTextTrack.h index 970c3322d..ba9c76fb2 100644 --- a/Source/WebCore/html/track/LoadableTextTrack.h +++ b/Source/WebCore/html/track/LoadableTextTrack.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2011-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,61 +24,56 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef LoadableTextTrack_h -#define LoadableTextTrack_h +#pragma once #if ENABLE(VIDEO_TRACK) #include "TextTrack.h" #include "TextTrackLoader.h" -#include <wtf/Vector.h> namespace WebCore { class HTMLTrackElement; -class LoadableTextTrack; -class LoadableTextTrack : public TextTrack, private TextTrackLoaderClient { +class LoadableTextTrack final : public TextTrack, private TextTrackLoaderClient { public: - static PassRefPtr<LoadableTextTrack> create(HTMLTrackElement* track, const String& kind, const String& label, const String& language) + static Ref<LoadableTextTrack> create(HTMLTrackElement& track, const String& kind, const String& label, const String& language) { - return adoptRef(new LoadableTextTrack(track, kind, label, language)); + return adoptRef(*new LoadableTextTrack(track, kind, label, language)); } - virtual ~LoadableTextTrack(); void scheduleLoad(const URL&); - virtual void clearClient() override; - - virtual AtomicString id() const override; - size_t trackElementIndex(); - HTMLTrackElement* trackElement() { return m_trackElement; } - void setTrackElement(HTMLTrackElement*); - virtual Element* element() override; + HTMLTrackElement* trackElement() const { return m_trackElement; } + void clearElement() { m_trackElement = nullptr; } - virtual bool isDefault() const override { return m_isDefault; } - virtual void setIsDefault(bool isDefault) override { m_isDefault = isDefault; } + void setIsDefault(bool isDefault) final { m_isDefault = isDefault; } private: - // TextTrackLoaderClient - virtual void newCuesAvailable(TextTrackLoader*) override; - virtual void cueLoadingCompleted(TextTrackLoader*, bool loadingFailed) override; -#if ENABLE(WEBVTT_REGIONS) - virtual void newRegionsAvailable(TextTrackLoader*); -#endif + LoadableTextTrack(HTMLTrackElement&, const String& kind, const String& label, const String& language); + + void newCuesAvailable(TextTrackLoader*) final; + void cueLoadingCompleted(TextTrackLoader*, bool loadingFailed) final; + void newRegionsAvailable(TextTrackLoader*) final; - LoadableTextTrack(HTMLTrackElement*, const String& kind, const String& label, const String& language); + AtomicString id() const final; + bool isDefault() const final { return m_isDefault; } + Element* element() final; - void loadTimerFired(Timer<LoadableTextTrack>&); + void loadTimerFired(); HTMLTrackElement* m_trackElement; - Timer<LoadableTextTrack> m_loadTimer; - OwnPtr<TextTrackLoader> m_loader; + Timer m_loadTimer; + std::unique_ptr<TextTrackLoader> m_loader; URL m_url; - bool m_isDefault; + bool m_isDefault { false }; }; + } // namespace WebCore -#endif -#endif +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::LoadableTextTrack) + static bool isType(const WebCore::TextTrack& track) { return track.trackType() == WebCore::TextTrack::TrackElement; } +SPECIALIZE_TYPE_TRAITS_END() + +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/html/track/TextTrack.cpp b/Source/WebCore/html/track/TextTrack.cpp index 52b57b32c..0445edcdb 100644 --- a/Source/WebCore/html/track/TextTrack.cpp +++ b/Source/WebCore/html/track/TextTrack.cpp @@ -1,6 +1,6 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. - * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2013 Google Inc. All rights reserved. + * Copyright (C) 2011-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -30,130 +30,104 @@ */ #include "config.h" +#include "TextTrack.h" #if ENABLE(VIDEO_TRACK) -#include "TextTrack.h" - #include "Event.h" +#include "ExceptionCode.h" #include "HTMLMediaElement.h" #include "SourceBuffer.h" #include "TextTrackCueList.h" #include "TextTrackList.h" -#include "TextTrackRegionList.h" +#include "VTTRegion.h" +#include "VTTRegionList.h" namespace WebCore { -static const int invalidTrackIndex = -1; - const AtomicString& TextTrack::subtitlesKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, subtitles, ("subtitles", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> subtitles("subtitles", AtomicString::ConstructFromLiteral); return subtitles; } -const AtomicString& TextTrack::captionsKeyword() +static const AtomicString& captionsKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, captions, ("captions", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> captions("captions", AtomicString::ConstructFromLiteral); return captions; } -const AtomicString& TextTrack::descriptionsKeyword() +static const AtomicString& descriptionsKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, descriptions, ("descriptions", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> descriptions("descriptions", AtomicString::ConstructFromLiteral); return descriptions; } -const AtomicString& TextTrack::chaptersKeyword() +static const AtomicString& chaptersKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, chapters, ("chapters", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> chapters("chapters", AtomicString::ConstructFromLiteral); return chapters; } -const AtomicString& TextTrack::metadataKeyword() +static const AtomicString& metadataKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, metadata, ("metadata", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> metadata("metadata", AtomicString::ConstructFromLiteral); return metadata; } -const AtomicString& TextTrack::forcedKeyword() +static const AtomicString& forcedKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, forced, ("forced", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> forced("forced", AtomicString::ConstructFromLiteral); return forced; } -const AtomicString& TextTrack::disabledKeyword() -{ - DEFINE_STATIC_LOCAL(const AtomicString, open, ("disabled", AtomicString::ConstructFromLiteral)); - return open; -} - -const AtomicString& TextTrack::hiddenKeyword() -{ - DEFINE_STATIC_LOCAL(const AtomicString, closed, ("hidden", AtomicString::ConstructFromLiteral)); - return closed; -} - -const AtomicString& TextTrack::showingKeyword() -{ - DEFINE_STATIC_LOCAL(const AtomicString, ended, ("showing", AtomicString::ConstructFromLiteral)); - return ended; -} - TextTrack* TextTrack::captionMenuOffItem() { - static TextTrack* off = TextTrack::create(0, 0, "off menu item", "", "", "").leakRef(); - return off; + static TextTrack& off = TextTrack::create(nullptr, nullptr, "off menu item", emptyAtom, emptyAtom, emptyAtom).leakRef(); + return &off; } TextTrack* TextTrack::captionMenuAutomaticItem() { - static TextTrack* automatic = TextTrack::create(0, 0, "automatic menu item", "", "", "").leakRef(); - return automatic; + static TextTrack& automatic = TextTrack::create(nullptr, nullptr, "automatic menu item", emptyAtom, emptyAtom, emptyAtom).leakRef(); + return &automatic; } TextTrack::TextTrack(ScriptExecutionContext* context, TextTrackClient* client, const AtomicString& kind, const AtomicString& id, const AtomicString& label, const AtomicString& language, TextTrackType type) : TrackBase(TrackBase::TextTrack, id, label, language) - , m_cues(0) , m_scriptExecutionContext(context) -#if ENABLE(WEBVTT_REGIONS) - , m_regions(0) -#endif - , m_mode(disabledKeyword().string()) , m_client(client) , m_trackType(type) - , m_readinessState(NotLoaded) - , m_trackIndex(invalidTrackIndex) - , m_renderedTrackIndex(invalidTrackIndex) - , m_hasBeenConfigured(false) { - setKindInternal(kind); + if (kind == captionsKeyword()) + m_kind = Kind::Captions; + else if (kind == chaptersKeyword()) + m_kind = Kind::Chapters; + else if (kind == descriptionsKeyword()) + m_kind = Kind::Descriptions; + else if (kind == forcedKeyword()) + m_kind = Kind::Forced; + else if (kind == metadataKeyword()) + m_kind = Kind::Metadata; } TextTrack::~TextTrack() { if (m_cues) { if (m_client) - m_client->textTrackRemoveCues(this, m_cues.get()); - + m_client->textTrackRemoveCues(*this, *m_cues); for (size_t i = 0; i < m_cues->length(); ++i) - m_cues->item(i)->setTrack(0); -#if ENABLE(WEBVTT_REGIONS) + m_cues->item(i)->setTrack(nullptr); + } + if (m_regions) { for (size_t i = 0; i < m_regions->length(); ++i) - m_regions->item(i)->setTrack(0); -#endif + m_regions->item(i)->setTrack(nullptr); } - clearClient(); -} - -bool TextTrack::isValidKind(const AtomicString& value) const -{ - return TextTrack::isValidKindKeyword(value); } bool TextTrack::enabled() const { - return m_mode != disabledKeyword(); + return m_mode != Mode::Disabled; } bool TextTrack::isValidKindKeyword(const AtomicString& value) @@ -174,60 +148,102 @@ bool TextTrack::isValidKindKeyword(const AtomicString& value) return false; } -void TextTrack::setKind(const AtomicString& newKind) +const AtomicString& TextTrack::kindKeyword() const +{ + switch (m_kind) { + case Kind::Captions: + return captionsKeyword(); + case Kind::Chapters: + return chaptersKeyword(); + case Kind::Descriptions: + return descriptionsKeyword(); + case Kind::Forced: + return forcedKeyword(); + case Kind::Metadata: + return metadataKeyword(); + case Kind::Subtitles: + return subtitlesKeyword(); + } + ASSERT_NOT_REACHED(); + return subtitlesKeyword(); +} + +void TextTrack::setKind(Kind newKind) { - String oldKind = kind(); + auto oldKind = m_kind; -#if ENABLE(MEDIA_SOURCE) // 10.1 kind, on setting: // 1. If the value being assigned to this attribute does not match one of the text track kinds, // then abort these steps. - if (!isValidKindKeyword(newKind)) - return; // 2. Update this attribute to the new value. - setKindInternal(newKind); + m_kind = newKind; +#if ENABLE(MEDIA_SOURCE) // 3. If the sourceBuffer attribute on this track is not null, then queue a task to fire a simple // event named change at sourceBuffer.textTracks. if (m_sourceBuffer) - m_sourceBuffer->textTracks()->scheduleChangeEvent(); + m_sourceBuffer->textTracks().scheduleChangeEvent(); // 4. Queue a task to fire a simple event named change at the TextTrackList object referenced by // the textTracks attribute on the HTMLMediaElement. if (mediaElement()) - mediaElement()->textTracks()->scheduleChangeEvent(); -#else - TrackBase::setKind(newKind); + mediaElement()->textTracks().scheduleChangeEvent(); #endif - if (m_client && oldKind != kind()) - m_client->textTrackKindChanged(this); + if (m_client && oldKind != m_kind) + m_client->textTrackKindChanged(*this); } -void TextTrack::setMode(const AtomicString& mode) +void TextTrack::setKindKeywordIgnoringASCIICase(StringView keyword) { - // On setting, if the new value isn't equal to what the attribute would currently - // return, the new value must be processed as follows ... - if (mode != disabledKeyword() && mode != hiddenKeyword() && mode != showingKeyword()) + if (keyword.isNull()) { + // The missing value default is the subtitles state. + setKind(Kind::Subtitles); return; + } + if (equalLettersIgnoringASCIICase(keyword, "captions")) + setKind(Kind::Captions); + else if (equalLettersIgnoringASCIICase(keyword, "chapters")) + setKind(Kind::Chapters); + else if (equalLettersIgnoringASCIICase(keyword, "descriptions")) + setKind(Kind::Descriptions); + else if (equalLettersIgnoringASCIICase(keyword, "forced")) + setKind(Kind::Forced); + else if (equalLettersIgnoringASCIICase(keyword, "metadata")) + setKind(Kind::Metadata); + else if (equalLettersIgnoringASCIICase(keyword, "subtitles")) + setKind(Kind::Subtitles); + else { + // The invalid value default is the metadata state. + setKind(Kind::Metadata); + } +} +void TextTrack::setMode(Mode mode) +{ + // On setting, if the new value isn't equal to what the attribute would currently + // return, the new value must be processed as follows ... if (m_mode == mode) return; // If mode changes to disabled, remove this track's cues from the client // because they will no longer be accessible from the cues() function. - if (mode == disabledKeyword() && m_client && m_cues) - m_client->textTrackRemoveCues(this, m_cues.get()); - - if (mode != showingKeyword() && m_cues) - for (size_t i = 0; i < m_cues->length(); ++i) - m_cues->item(i)->removeDisplayTree(); + if (mode == Mode::Disabled && m_client && m_cues) + m_client->textTrackRemoveCues(*this, *m_cues); + + if (mode != Mode::Showing && m_cues) { + for (size_t i = 0; i < m_cues->length(); ++i) { + TextTrackCue* cue = m_cues->item(i); + if (cue->isRenderable()) + toVTTCue(cue)->removeDisplayTree(); + } + } m_mode = mode; if (m_client) - m_client->textTrackModeChanged(this); + m_client->textTrackModeChanged(*this); } TextTrackCueList* TextTrack::cues() @@ -237,9 +253,9 @@ TextTrackCueList* TextTrack::cues() // Otherwise, it must return null. When an object is returned, the // same object must be returned each time. // http://www.whatwg.org/specs/web-apps/current-work/#dom-texttrack-cues - if (m_mode != disabledKeyword()) - return ensureTextTrackCueList(); - return 0; + if (m_mode == Mode::Disabled) + return nullptr; + return &ensureTextTrackCueList(); } void TextTrack::removeAllCues() @@ -248,12 +264,12 @@ void TextTrack::removeAllCues() return; if (m_client) - m_client->textTrackRemoveCues(this, m_cues.get()); + m_client->textTrackRemoveCues(*this, *m_cues); for (size_t i = 0; i < m_cues->length(); ++i) - m_cues->item(i)->setTrack(0); + m_cues->item(i)->setTrack(nullptr); - m_cues = 0; + m_cues = nullptr; } TextTrackCueList* TextTrack::activeCues() const @@ -264,145 +280,136 @@ TextTrackCueList* TextTrack::activeCues() const // order. Otherwise, it must return null. When an object is returned, the // same object must be returned each time. // http://www.whatwg.org/specs/web-apps/current-work/#dom-texttrack-activecues - if (m_cues && m_mode != disabledKeyword()) - return m_cues->activeCues(); - return 0; + if (!m_cues || m_mode == Mode::Disabled) + return nullptr; + return &m_cues->activeCues(); } -void TextTrack::addCue(PassRefPtr<TextTrackCue> prpCue) +ExceptionOr<void> TextTrack::addCue(Ref<TextTrackCue>&& cue) { - if (!prpCue) - return; - - RefPtr<TextTrackCue> cue = prpCue; + // 4.7.10.12.6 Text tracks exposing in-band metadata + // The UA will use DataCue to expose only text track cue objects that belong to a text track that has a text + // track kind of metadata. + // If a DataCue is added to a TextTrack via the addCue() method but the text track does not have its text + // track kind set to metadata, throw a InvalidNodeTypeError exception and don't add the cue to the TextTrackList + // of the TextTrack. + if (cue->cueType() == TextTrackCue::Data && m_kind != Kind::Metadata) + return Exception { INVALID_NODE_TYPE_ERR }; // TODO(93143): Add spec-compliant behavior for negative time values. - if (std::isnan(cue->startTime()) || std::isnan(cue->endTime()) || cue->startTime() < 0 || cue->endTime() < 0) - return; + if (!cue->startMediaTime().isValid() || !cue->endMediaTime().isValid() || cue->startMediaTime() < MediaTime::zeroTime() || cue->endMediaTime() < MediaTime::zeroTime()) + return { }; // 4.8.10.12.5 Text track API // The addCue(cue) method of TextTrack objects, when invoked, must run the following steps: + auto* cueTrack = cue->track(); + if (cueTrack == this) + return { }; + // 1. If the given cue is in a text track list of cues, then remove cue from that text track // list of cues. - TextTrack* cueTrack = cue->track(); - if (cueTrack && cueTrack != this) - cueTrack->removeCue(cue.get(), ASSERT_NO_EXCEPTION); + if (cueTrack) + cueTrack->removeCue(cue.get()); // 2. Add cue to the method's TextTrack object's text track's text track list of cues. cue->setTrack(this); - ensureTextTrackCueList()->add(cue); + ensureTextTrackCueList().add(cue.copyRef()); if (m_client) - m_client->textTrackAddCue(this, cue.get()); + m_client->textTrackAddCue(*this, cue); + + return { }; } -void TextTrack::removeCue(TextTrackCue* cue, ExceptionCode& ec) +ExceptionOr<void> TextTrack::removeCue(TextTrackCue& cue) { - if (!cue) - return; - // 4.8.10.12.5 Text track API // The removeCue(cue) method of TextTrack objects, when invoked, must run the following steps: // 1. If the given cue is not currently listed in the method's TextTrack // object's text track's text track list of cues, then throw a NotFoundError exception. - if (cue->track() != this) { - ec = NOT_FOUND_ERR; - return; - } + if (cue.track() != this) + return Exception { NOT_FOUND_ERR }; + if (!m_cues) + return Exception { INVALID_STATE_ERR }; // 2. Remove cue from the method's TextTrack object's text track's text track list of cues. - if (!m_cues || !m_cues->remove(cue)) { - ec = INVALID_STATE_ERR; - return; - } - - cue->setTrack(0); + m_cues->remove(cue); + cue.setIsActive(false); + cue.setTrack(nullptr); if (m_client) - m_client->textTrackRemoveCue(this, cue); -} + m_client->textTrackRemoveCue(*this, cue); -#if ENABLE(VIDEO_TRACK) && ENABLE(WEBVTT_REGIONS) -TextTrackRegionList* TextTrack::regionList() -{ - return ensureTextTrackRegionList(); + return { }; } -TextTrackRegionList* TextTrack::ensureTextTrackRegionList() +VTTRegionList& TextTrack::ensureVTTRegionList() { if (!m_regions) - m_regions = TextTrackRegionList::create(); + m_regions = VTTRegionList::create(); - return m_regions.get(); + return *m_regions; } -TextTrackRegionList* TextTrack::regions() +VTTRegionList* TextTrack::regions() { // If the text track mode of the text track that the TextTrack object // represents is not the text track disabled mode, then the regions - // attribute must return a live TextTrackRegionList object that represents + // attribute must return a live VTTRegionList object that represents // the text track list of regions of the text track. Otherwise, it must // return null. When an object is returned, the same object must be returned // each time. - if (m_mode != disabledKeyword()) - return ensureTextTrackRegionList(); - - return 0; + if (m_mode == Mode::Disabled) + return nullptr; + return &ensureVTTRegionList(); } -void TextTrack::addRegion(PassRefPtr<TextTrackRegion> prpRegion) +void TextTrack::addRegion(RefPtr<VTTRegion>&& region) { - if (!prpRegion) + if (!region) return; - RefPtr<TextTrackRegion> region = prpRegion; - TextTrackRegionList* regionList = ensureTextTrackRegionList(); + auto& regionList = ensureVTTRegionList(); // 1. If the given region is in a text track list of regions, then remove // region from that text track list of regions. - TextTrack* regionTrack = region->track(); + auto* regionTrack = region->track(); if (regionTrack && regionTrack != this) - regionTrack->removeRegion(region.get(), ASSERT_NO_EXCEPTION); + regionTrack->removeRegion(region.get()); // 2. If the method's TextTrack object's text track list of regions contains // a region with the same identifier as region replace the values of that // region's width, height, anchor point, viewport anchor point and scroll // attributes with those of region. - TextTrackRegion* existingRegion = regionList->getRegionById(region->id()); + auto* existingRegion = regionList.getRegionById(region->id()); if (existingRegion) { - existingRegion->updateParametersFromRegion(region.get()); + existingRegion->updateParametersFromRegion(*region); return; } - // Otherwise: add region to the method's TextTrack object's text track - // list of regions. + // Otherwise: add region to the method's TextTrack object's text track list of regions. region->setTrack(this); - regionList->add(region); + regionList.add(region.releaseNonNull()); } -void TextTrack::removeRegion(TextTrackRegion* region, ExceptionCode &ec) +ExceptionOr<void> TextTrack::removeRegion(VTTRegion* region) { if (!region) - return; + return { }; // 1. If the given region is not currently listed in the method's TextTrack // object's text track list of regions, then throw a NotFoundError exception. - if (region->track() != this) { - ec = NOT_FOUND_ERR; - return; - } - - if (!m_regions || !m_regions->remove(region)) { - ec = INVALID_STATE_ERR; - return; - } + if (region->track() != this) + return Exception { NOT_FOUND_ERR }; - region->setTrack(0); + ASSERT(m_regions); + m_regions->remove(*region); + region->setTrack(nullptr); + return { }; } -#endif void TextTrack::cueWillChange(TextTrackCue* cue) { @@ -411,7 +418,7 @@ void TextTrack::cueWillChange(TextTrackCue* cue) // The cue may need to be repositioned in the media element's interval tree, may need to // be re-rendered, etc, so remove it before the modification... - m_client->textTrackRemoveCue(this, cue); + m_client->textTrackRemoveCue(*this, *cue); } void TextTrack::cueDidChange(TextTrackCue* cue) @@ -420,60 +427,50 @@ void TextTrack::cueDidChange(TextTrackCue* cue) return; // Make sure the TextTrackCueList order is up-to-date. - ensureTextTrackCueList()->updateCueIndex(cue); + ensureTextTrackCueList().updateCueIndex(*cue); // ... and add it back again. - m_client->textTrackAddCue(this, cue); + m_client->textTrackAddCue(*this, *cue); } int TextTrack::trackIndex() { ASSERT(m_mediaElement); - - if (m_trackIndex == invalidTrackIndex) - m_trackIndex = m_mediaElement->textTracks()->getTrackIndex(this); - - return m_trackIndex; + if (!m_trackIndex) + m_trackIndex = m_mediaElement->textTracks().getTrackIndex(*this); + return m_trackIndex.value(); } void TextTrack::invalidateTrackIndex() { - m_trackIndex = invalidTrackIndex; - m_renderedTrackIndex = invalidTrackIndex; + m_trackIndex = std::nullopt; + m_renderedTrackIndex = std::nullopt; } bool TextTrack::isRendered() { - if (kind() != captionsKeyword() && kind() != subtitlesKeyword() && kind() != forcedKeyword()) - return false; - - if (m_mode != showingKeyword()) - return false; - - return true; + return (m_kind == Kind::Captions || m_kind == Kind::Subtitles || m_kind == Kind::Forced) + && m_mode == Mode::Showing; } -TextTrackCueList* TextTrack::ensureTextTrackCueList() +TextTrackCueList& TextTrack::ensureTextTrackCueList() { if (!m_cues) m_cues = TextTrackCueList::create(); - - return m_cues.get(); + return *m_cues; } int TextTrack::trackIndexRelativeToRenderedTracks() { ASSERT(m_mediaElement); - - if (m_renderedTrackIndex == invalidTrackIndex) - m_renderedTrackIndex = m_mediaElement->textTracks()->getTrackIndexRelativeToRenderedTracks(this); - - return m_renderedTrackIndex; + if (!m_renderedTrackIndex) + m_renderedTrackIndex = m_mediaElement->textTracks().getTrackIndexRelativeToRenderedTracks(*this); + return m_renderedTrackIndex.value(); } bool TextTrack::hasCue(TextTrackCue* cue, TextTrackCue::CueMatchRules match) { - if (cue->startTime() < 0 || cue->endTime() < 0) + if (cue->startMediaTime() < MediaTime::zeroTime() || cue->endMediaTime() < MediaTime::zeroTime()) return false; if (!m_cues || !m_cues->length()) @@ -495,7 +492,7 @@ bool TextTrack::hasCue(TextTrackCue* cue, TextTrackCue::CueMatchRules match) // If there is more than one cue with the same start time, back up to first one so we // consider all of them. - while (searchStart >= 2 && cue->startTime() == m_cues->item(searchStart - 2)->startTime()) + while (searchStart >= 2 && cue->hasEquivalentStartTime(*m_cues->item(searchStart - 2))) --searchStart; bool firstCompare = true; @@ -507,19 +504,20 @@ bool TextTrack::hasCue(TextTrackCue* cue, TextTrackCue::CueMatchRules match) return false; existingCue = m_cues->item(searchStart - 1); - if (!existingCue || cue->startTime() > existingCue->startTime()) + if (!existingCue) return false; - if (!existingCue->isEqual(*cue, match)) - continue; - - return true; + if (cue->startMediaTime() > (existingCue->startMediaTime() + startTimeVariance())) + return false; + + if (existingCue->isEqual(*cue, match)) + return true; } } size_t index = (searchStart + searchEnd) / 2; existingCue = m_cues->item(index); - if (cue->startTime() < existingCue->startTime() || (match != TextTrackCue::IgnoreDuration && cue->startTime() == existingCue->startTime() && cue->endTime() > existingCue->endTime())) + if ((cue->startMediaTime() + startTimeVariance()) < existingCue->startMediaTime() || (match != TextTrackCue::IgnoreDuration && cue->hasEquivalentStartTime(*existingCue) && cue->endMediaTime() > existingCue->endMediaTime())) searchEnd = index; else searchStart = index + 1; @@ -529,58 +527,29 @@ bool TextTrack::hasCue(TextTrackCue* cue, TextTrackCue::CueMatchRules match) return false; } -#if USE(PLATFORM_TEXT_TRACK_MENU) -PassRefPtr<PlatformTextTrack> TextTrack::platformTextTrack() -{ - static int uniqueId = 0; - - if (m_platformTextTrack) - return m_platformTextTrack; - - PlatformTextTrack::TrackKind platformKind = PlatformTextTrack::Caption; - if (kind() == subtitlesKeyword()) - platformKind = PlatformTextTrack::Subtitle; - else if (kind() == captionsKeyword()) - platformKind = PlatformTextTrack::Caption; - else if (kind() == descriptionsKeyword()) - platformKind = PlatformTextTrack::Description; - else if (kind() == chaptersKeyword()) - platformKind = PlatformTextTrack::Chapter; - else if (kind() == metadataKeyword()) - platformKind = PlatformTextTrack::MetaData; - else if (kind() == forcedKeyword()) - platformKind = PlatformTextTrack::Forced; - - PlatformTextTrack::TrackType type = PlatformTextTrack::OutOfBand; - if (m_trackType == TrackElement) - type = PlatformTextTrack::OutOfBand; - else if (m_trackType == AddTrack) - type = PlatformTextTrack::Script; - else if (m_trackType == InBand) - type = PlatformTextTrack::InBand; - - m_platformTextTrack = PlatformTextTrack::create(this, label(), language(), platformKind, type, ++uniqueId); - - return m_platformTextTrack; -} -#endif - bool TextTrack::isMainProgramContent() const { // "Main program" content is intrinsic to the presentation of the media file, regardless of locale. Content such as // directors commentary is not "main program" because it is not essential for the presentation. HTML5 doesn't have // a way to express this in a machine-reable form, it is typically done with the track label, so we assume that caption // tracks are main content and all other track types are not. - return kind() == captionsKeyword(); + return m_kind == Kind::Captions; +} + +bool TextTrack::containsOnlyForcedSubtitles() const +{ + return m_kind == Kind::Forced; } #if ENABLE(MEDIA_SOURCE) + void TextTrack::setLanguage(const AtomicString& language) { // 11.1 language, on setting: // 1. If the value being assigned to this attribute is not an empty string or a BCP 47 language // tag[BCP47], then abort these steps. - // FIXME(123926): Validate the BCP47-ness of langague. + // BCP 47 validation is done in TrackBase::setLanguage() which is + // shared between all tracks that support setting language. // 2. Update this attribute to the new value. TrackBase::setLanguage(language); @@ -588,13 +557,14 @@ void TextTrack::setLanguage(const AtomicString& language) // 3. If the sourceBuffer attribute on this track is not null, then queue a task to fire a simple // event named change at sourceBuffer.textTracks. if (m_sourceBuffer) - m_sourceBuffer->textTracks()->scheduleChangeEvent(); + m_sourceBuffer->textTracks().scheduleChangeEvent(); // 4. Queue a task to fire a simple event named change at the TextTrackList object referenced by // the textTracks attribute on the HTMLMediaElement. if (mediaElement()) - mediaElement()->textTracks()->scheduleChangeEvent(); + mediaElement()->textTracks().scheduleChangeEvent(); } + #endif } // namespace WebCore diff --git a/Source/WebCore/html/track/TextTrack.h b/Source/WebCore/html/track/TextTrack.h index 43e788f33..8bb47b276 100644 --- a/Source/WebCore/html/track/TextTrack.h +++ b/Source/WebCore/html/track/TextTrack.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 Google Inc. All rights reserved. - * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2011-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,78 +24,68 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TextTrack_h -#define TextTrack_h +#pragma once #if ENABLE(VIDEO_TRACK) -#include "ExceptionCode.h" #include "TextTrackCue.h" #include "TrackBase.h" -#include <wtf/PassOwnPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/text/WTFString.h> - -#if USE(PLATFORM_TEXT_TRACK_MENU) -#include "PlatformTextTrack.h" -#endif namespace WebCore { class ScriptExecutionContext; class TextTrack; class TextTrackCueList; -#if ENABLE(WEBVTT_REGIONS) -class TextTrackRegion; -class TextTrackRegionList; -#endif +class VTTRegion; +class VTTRegionList; class TextTrackClient { public: virtual ~TextTrackClient() { } - virtual void textTrackKindChanged(TextTrack*) = 0; - virtual void textTrackModeChanged(TextTrack*) = 0; - virtual void textTrackAddCues(TextTrack*, const TextTrackCueList*) = 0; - virtual void textTrackRemoveCues(TextTrack*, const TextTrackCueList*) = 0; - virtual void textTrackAddCue(TextTrack*, PassRefPtr<TextTrackCue>) = 0; - virtual void textTrackRemoveCue(TextTrack*, PassRefPtr<TextTrackCue>) = 0; + virtual void textTrackKindChanged(TextTrack&) = 0; + virtual void textTrackModeChanged(TextTrack&) = 0; + virtual void textTrackAddCues(TextTrack&, const TextTrackCueList&) = 0; + virtual void textTrackRemoveCues(TextTrack&, const TextTrackCueList&) = 0; + virtual void textTrackAddCue(TextTrack&, TextTrackCue&) = 0; + virtual void textTrackRemoveCue(TextTrack&, TextTrackCue&) = 0; }; -class TextTrack : public TrackBase, public EventTargetWithInlineData -#if USE(PLATFORM_TEXT_TRACK_MENU) - , public PlatformTextTrackClient -#endif - { +class TextTrack : public TrackBase, public EventTargetWithInlineData { public: - static PassRefPtr<TextTrack> create(ScriptExecutionContext* context, TextTrackClient* client, const AtomicString& kind, const AtomicString& id, const AtomicString& label, const AtomicString& language) + static Ref<TextTrack> create(ScriptExecutionContext* context, TextTrackClient* client, const AtomicString& kind, const AtomicString& id, const AtomicString& label, const AtomicString& language) { - return adoptRef(new TextTrack(context, client, kind, id, label, language, AddTrack)); + return adoptRef(*new TextTrack(context, client, kind, id, label, language, AddTrack)); } virtual ~TextTrack(); - virtual EventTargetInterface eventTargetInterface() const override final { return TextTrackEventTargetInterfaceType; } - virtual ScriptExecutionContext* scriptExecutionContext() const override final { return m_scriptExecutionContext; } + EventTargetInterface eventTargetInterface() const final { return TextTrackEventTargetInterfaceType; } + ScriptExecutionContext* scriptExecutionContext() const final { return m_scriptExecutionContext; } static TextTrack* captionMenuOffItem(); static TextTrack* captionMenuAutomaticItem(); static const AtomicString& subtitlesKeyword(); - static const AtomicString& captionsKeyword(); - static const AtomicString& descriptionsKeyword(); - static const AtomicString& chaptersKeyword(); - static const AtomicString& metadataKeyword(); - static const AtomicString& forcedKeyword(); - virtual const AtomicString& defaultKindKeyword() const override { return subtitlesKeyword(); } static bool isValidKindKeyword(const AtomicString&); static const AtomicString& disabledKeyword(); static const AtomicString& hiddenKeyword(); static const AtomicString& showingKeyword(); - virtual void setKind(const AtomicString&) override; + enum class Kind { Subtitles, Captions, Descriptions, Chapters, Metadata, Forced }; + Kind kind() const; + void setKind(Kind); - AtomicString mode() const { return m_mode; } - virtual void setMode(const AtomicString&); + Kind kindForBindings() const; + void setKindForBindings(Kind); + + const AtomicString& kindKeyword() const; + void setKindKeywordIgnoringASCIICase(StringView); + + virtual AtomicString inBandMetadataTrackDispatchType() const { return emptyString(); } + + enum class Mode { Disabled, Hidden, Showing }; + Mode mode() const; + virtual void setMode(Mode); enum ReadinessState { NotLoaded = 0, Loading = 1, Loaded = 2, FailedToLoad = 3 }; ReadinessState readinessState() const { return m_readinessState; } @@ -104,31 +94,27 @@ public: TextTrackCueList* cues(); TextTrackCueList* activeCues() const; - virtual void clearClient() override { m_client = 0; } + void clearClient() override { m_client = nullptr; } TextTrackClient* client() { return m_client; } - void addCue(PassRefPtr<TextTrackCue>); - virtual void removeCue(TextTrackCue*, ExceptionCode&); + ExceptionOr<void> addCue(Ref<TextTrackCue>&&); + virtual ExceptionOr<void> removeCue(TextTrackCue&); bool hasCue(TextTrackCue*, TextTrackCue::CueMatchRules = TextTrackCue::MatchAllFields); -#if ENABLE(VIDEO_TRACK) && ENABLE(WEBVTT_REGIONS) - TextTrackRegionList* regions(); - void addRegion(PassRefPtr<TextTrackRegion>); - void removeRegion(TextTrackRegion*, ExceptionCode&); -#endif + VTTRegionList* regions(); + void addRegion(RefPtr<VTTRegion>&&); + ExceptionOr<void> removeRegion(VTTRegion*); void cueWillChange(TextTrackCue*); void cueDidChange(TextTrackCue*); - DEFINE_ATTRIBUTE_EVENT_LISTENER(cuechange); - enum TextTrackType { TrackElement, AddTrack, InBand }; TextTrackType trackType() const { return m_trackType; } virtual bool isClosedCaptions() const { return false; } virtual bool isSDH() const { return false; } - virtual bool containsOnlyForcedSubtitles() const { return false; } + virtual bool containsOnlyForcedSubtitles() const; virtual bool isMainProgramContent() const; virtual bool isEasyToRead() const { return false; } @@ -146,63 +132,82 @@ public: void removeAllCues(); -#if USE(PLATFORM_TEXT_TRACK_MENU) - PassRefPtr<PlatformTextTrack> platformTextTrack(); -#endif - #if ENABLE(MEDIA_SOURCE) - virtual void setLanguage(const AtomicString&) override; + void setLanguage(const AtomicString&) override; #endif + virtual bool isInband() const { return false; } + + virtual MediaTime startTimeVariance() const { return MediaTime::zeroTime(); } + using RefCounted<TrackBase>::ref; using RefCounted<TrackBase>::deref; protected: TextTrack(ScriptExecutionContext*, TextTrackClient*, const AtomicString& kind, const AtomicString& id, const AtomicString& label, const AtomicString& language, TextTrackType); -#if ENABLE(VIDEO_TRACK) && ENABLE(WEBVTT_REGIONS) - TextTrackRegionList* regionList(); -#endif RefPtr<TextTrackCueList> m_cues; private: - virtual bool isValidKind(const AtomicString&) const override; + bool enabled() const override; - virtual bool enabled() const override; + void refEventTarget() final { ref(); } + void derefEventTarget() final { deref(); } - virtual void refEventTarget() override final { ref(); } - virtual void derefEventTarget() override final { deref(); } - -#if ENABLE(VIDEO_TRACK) && ENABLE(WEBVTT_REGIONS) - TextTrackRegionList* ensureTextTrackRegionList(); - RefPtr<TextTrackRegionList> m_regions; -#endif - -#if USE(PLATFORM_TEXT_TRACK_MENU) - virtual TextTrack* publicTrack() override { return this; } - - RefPtr<PlatformTextTrack> m_platformTextTrack; -#endif + VTTRegionList& ensureVTTRegionList(); + RefPtr<VTTRegionList> m_regions; - TextTrackCueList* ensureTextTrackCueList(); + TextTrackCueList& ensureTextTrackCueList(); ScriptExecutionContext* m_scriptExecutionContext; - AtomicString m_mode; + Mode m_mode { Mode::Disabled }; + Kind m_kind { Kind::Subtitles }; TextTrackClient* m_client; TextTrackType m_trackType; - ReadinessState m_readinessState; - int m_trackIndex; - int m_renderedTrackIndex; - bool m_hasBeenConfigured; + ReadinessState m_readinessState { NotLoaded }; + std::optional<int> m_trackIndex; + std::optional<int> m_renderedTrackIndex; + bool m_hasBeenConfigured { false }; }; -inline TextTrack* toTextTrack(TrackBase* track) +inline auto TextTrack::mode() const -> Mode { - ASSERT_WITH_SECURITY_IMPLICATION(track->type() == TrackBase::TextTrack); - return static_cast<TextTrack*>(track); + return m_mode; } -} // namespace WebCore +inline auto TextTrack::kind() const -> Kind +{ + return m_kind; +} + +inline auto TextTrack::kindForBindings() const -> Kind +{ + return kind(); +} + +#if !ENABLE(MEDIA_SOURCE) + +inline void TextTrack::setKindForBindings(Kind) +{ + // FIXME: We are using kindForBindings only to implement this empty function, preserving the + // behavior of doing nothing when trying to set the kind, originally implemented in a custom setter. + // Once we no longer need this special case, we should remove kindForBindings and setKindForBindings. +} + +#else + +inline void TextTrack::setKindForBindings(Kind kind) +{ + setKind(kind); +} #endif + + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::TextTrack) + static bool isType(const WebCore::TrackBase& track) { return track.type() == WebCore::TrackBase::TextTrack; } +SPECIALIZE_TYPE_TRAITS_END() + #endif diff --git a/Source/WebCore/html/track/TextTrack.idl b/Source/WebCore/html/track/TextTrack.idl index 90f6d1b93..c09f0d105 100644 --- a/Source/WebCore/html/track/TextTrack.idl +++ b/Source/WebCore/html/track/TextTrack.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,39 +23,33 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +enum TextTrackMode { "disabled", "hidden", "showing" }; +enum TextTrackKind { "subtitles", "captions", "descriptions", "chapters", "metadata", "forced" }; + [ Conditional=VIDEO_TRACK, - EventTarget, GenerateIsReachable=ImplElementRoot, JSCustomMarkFunction, SkipVTableValidation, -] interface TextTrack { +] interface TextTrack : EventTarget { readonly attribute DOMString id; - [CustomSetter] attribute DOMString kind; + [ImplementedAs=kindForBindings] attribute TextTrackKind kind; readonly attribute DOMString label; [CustomSetter] attribute DOMString language; + readonly attribute DOMString inBandMetadataTrackDispatchType; - attribute DOMString mode; + attribute TextTrackMode mode; - readonly attribute TextTrackCueList cues; - readonly attribute TextTrackCueList activeCues; - attribute EventListener oncuechange; + readonly attribute TextTrackCueList? cues; + readonly attribute TextTrackCueList? activeCues; - void addCue(TextTrackCue cue); - [RaisesException] void removeCue(TextTrackCue cue); + [MayThrowException] void addCue(TextTrackCue cue); + [MayThrowException] void removeCue(TextTrackCue cue); -#if defined(ENABLE_WEBVTT_REGIONS) && ENABLE_WEBVTT_REGIONS - readonly attribute TextTrackRegionList regions; - void addRegion(TextTrackRegion region); - [RaisesException] void removeRegion(TextTrackRegion region); -#endif + attribute EventHandler oncuechange; - // EventTarget interface - void addEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - void removeEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - [RaisesException] boolean dispatchEvent(Event evt); + readonly attribute VTTRegionList regions; + // FIXME: region parameter should not be nullable in addRegion and removeRegion. + void addRegion(VTTRegion? region); + [MayThrowException] void removeRegion(VTTRegion? region); }; diff --git a/Source/WebCore/html/track/TextTrackCue.cpp b/Source/WebCore/html/track/TextTrackCue.cpp index 67ff91a98..339172d10 100644 --- a/Source/WebCore/html/track/TextTrackCue.cpp +++ b/Source/WebCore/html/track/TextTrackCue.cpp @@ -1,6 +1,6 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. - * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2013 Google Inc. All rights reserved. + * Copyright (C) 2011-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -30,208 +30,38 @@ */ #include "config.h" +#include "TextTrackCue.h" #if ENABLE(VIDEO_TRACK) -#include "TextTrackCue.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 "RenderTextTrackCue.h" #include "Text.h" #include "TextTrack.h" #include "TextTrackCueList.h" -#include "WebVTTElement.h" -#include "WebVTTParser.h" +#include "VTTCue.h" +#include "VTTRegionList.h" #include <wtf/MathExtras.h> -#include <wtf/text/StringBuilder.h> namespace WebCore { -static const int invalidCueIndex = -1; -static const int undefinedPosition = -1; - -static const String& startKeyword() -{ - DEFINE_STATIC_LOCAL(const String, start, (ASCIILiteral("start"))); - return start; -} - -static const String& middleKeyword() -{ - DEFINE_STATIC_LOCAL(const String, middle, (ASCIILiteral("middle"))); - return middle; -} - -static const String& endKeyword() -{ - DEFINE_STATIC_LOCAL(const String, end, (ASCIILiteral("end"))); - return end; -} - -static const String& horizontalKeyword() -{ - return emptyString(); -} - -static const String& verticalGrowingLeftKeyword() -{ - DEFINE_STATIC_LOCAL(const String, verticalrl, (ASCIILiteral("rl"))); - return verticalrl; -} - -static const String& verticalGrowingRightKeyword() -{ - DEFINE_STATIC_LOCAL(const String, verticallr, (ASCIILiteral("lr"))); - return verticallr; -} - -// ---------------------------- - -TextTrackCueBox::TextTrackCueBox(Document& document, TextTrackCue* cue) - : HTMLElement(divTag, document) - , m_cue(cue) -{ - setPseudo(textTrackCueBoxShadowPseudoId()); -} - -TextTrackCue* TextTrackCueBox::getCue() const -{ - return m_cue; -} - -void TextTrackCueBox::applyCSSProperties(const IntSize&) -{ - // FIXME: Apply all the initial CSS positioning properties. http://wkb.ug/79916 - - // 3.5.1 On the (root) List of WebVTT Node Objects: - - // the 'position' property must be set to 'absolute' - setInlineStyleProperty(CSSPropertyPosition, CSSValueAbsolute); - - // the 'unicode-bidi' property must be set to 'plaintext' - setInlineStyleProperty(CSSPropertyUnicodeBidi, CSSValueWebkitPlaintext); - - // the 'direction' property must be set to direction - setInlineStyleProperty(CSSPropertyDirection, m_cue->getCSSWritingDirection()); - - // the 'writing-mode' property must be set to writing-mode - setInlineStyleProperty(CSSPropertyWebkitWritingMode, m_cue->getCSSWritingMode(), false); - - std::pair<float, float> position = m_cue->getCSSPosition(); - - // the 'top' property must be set to top, - setInlineStyleProperty(CSSPropertyTop, static_cast<double>(position.second), CSSPrimitiveValue::CSS_PERCENTAGE); - - // the 'left' property must be set to left - setInlineStyleProperty(CSSPropertyLeft, static_cast<double>(position.first), CSSPrimitiveValue::CSS_PERCENTAGE); - - // the 'width' property must be set to width, and the 'height' property must be set to height - if (m_cue->vertical() == horizontalKeyword()) { - setInlineStyleProperty(CSSPropertyWidth, static_cast<double>(m_cue->getCSSSize()), CSSPrimitiveValue::CSS_PERCENTAGE); - setInlineStyleProperty(CSSPropertyHeight, CSSValueAuto); - } else { - setInlineStyleProperty(CSSPropertyWidth, CSSValueAuto); - setInlineStyleProperty(CSSPropertyHeight, static_cast<double>(m_cue->getCSSSize()), CSSPrimitiveValue::CSS_PERCENTAGE); - } - - // The 'text-align' property on the (root) List of WebVTT Node Objects must - // be set to the value in the second cell of the row of the table below - // whose first cell is the value of the corresponding cue's text track cue - // alignment: - if (m_cue->align() == startKeyword()) - setInlineStyleProperty(CSSPropertyTextAlign, CSSValueStart); - else if (m_cue->align() == endKeyword()) - setInlineStyleProperty(CSSPropertyTextAlign, CSSValueEnd); - else - setInlineStyleProperty(CSSPropertyTextAlign, CSSValueCenter); - - if (!m_cue->snapToLines()) { - // 10.13.1 Set up x and y: - // Note: x and y are set through the CSS left and top above. - - // 10.13.2 Position the boxes in boxes such that the point x% along the - // width of the bounding box of the boxes in boxes is x% of the way - // across the width of the video's rendering area, and the point y% - // along the height of the bounding box of the boxes in boxes is y% - // of the way across the height of the video's rendering area, while - // maintaining the relative positions of the boxes in boxes to each - // other. - setInlineStyleProperty(CSSPropertyWebkitTransform, - String::format("translate(-%.2f%%, -%.2f%%)", position.first, position.second)); - - setInlineStyleProperty(CSSPropertyWhiteSpace, CSSValuePre); - } -} - -const AtomicString& TextTrackCueBox::textTrackCueBoxShadowPseudoId() +const AtomicString& TextTrackCue::cueShadowPseudoId() { - DEFINE_STATIC_LOCAL(const AtomicString, trackDisplayBoxShadowPseudoId, ("-webkit-media-text-track-display", AtomicString::ConstructFromLiteral)); - return trackDisplayBoxShadowPseudoId; + static NeverDestroyed<const AtomicString> cue("cue", AtomicString::ConstructFromLiteral); + return cue; } -RenderPtr<RenderElement> TextTrackCueBox::createElementRenderer(PassRef<RenderStyle> style) -{ - return createRenderer<RenderTextTrackCue>(*this, std::move(style)); -} - -// ---------------------------- - -TextTrackCue::TextTrackCue(ScriptExecutionContext& context, double start, double end, const String& content) +TextTrackCue::TextTrackCue(ScriptExecutionContext& context, const MediaTime& start, const MediaTime& end) : m_startTime(start) , m_endTime(end) - , m_content(content) - , m_linePosition(undefinedPosition) - , m_computedLinePosition(undefinedPosition) - , m_textPosition(50) - , m_cueSize(100) - , m_cueIndex(invalidCueIndex) - , m_processingCueChanges(0) - , m_writingDirection(Horizontal) - , m_cueAlignment(Middle) - , m_webVTTNodeTree(0) - , m_track(0) , m_scriptExecutionContext(context) , m_isActive(false) , m_pauseOnExit(false) - , m_snapToLines(true) - , m_cueBackgroundBox(HTMLSpanElement::create(spanTag, toDocument(context))) - , m_displayTreeShouldChange(true) - , m_displayDirection(CSSValueLtr) { ASSERT(m_scriptExecutionContext.isDocument()); - - // 4. If the text track cue writing direction is horizontal, then let - // writing-mode be 'horizontal-tb'. Otherwise, if the text track cue writing - // direction is vertical growing left, then let writing-mode be - // 'vertical-rl'. Otherwise, the text track cue writing direction is - // vertical growing right; let writing-mode be 'vertical-lr'. - m_displayWritingModeMap[Horizontal] = CSSValueHorizontalTb; - m_displayWritingModeMap[VerticalGrowingLeft] = CSSValueVerticalRl; - m_displayWritingModeMap[VerticalGrowingRight] = CSSValueVerticalLr; -} - -TextTrackCue::~TextTrackCue() -{ - removeDisplayTree(); -} - -PassRefPtr<TextTrackCueBox> TextTrackCue::createDisplayTree() -{ - return TextTrackCueBox::create(ownerDocument(), this); -} - -TextTrackCueBox* TextTrackCue::displayTreeInternal() -{ - if (!m_displayTree) - m_displayTree = createDisplayTree(); - return m_displayTree.get(); } void TextTrackCue::willChange() @@ -251,8 +81,6 @@ void TextTrackCue::didChange() if (m_track) m_track->cueDidChange(this); - - m_displayTreeShouldChange = true; } TextTrack* TextTrackCue::track() const @@ -275,928 +103,116 @@ void TextTrackCue::setId(const String& id) didChange(); } -void TextTrackCue::setStartTime(double value, ExceptionCode& ec) +void TextTrackCue::setStartTime(double value) { - // NaN, Infinity and -Infinity values should trigger a TypeError. - if (std::isinf(value) || std::isnan(value)) { - ec = TypeError; - return; - } - // TODO(93143): Add spec-compliant behavior for negative time values. - if (m_startTime == value || value < 0) - return; - - willChange(); - m_startTime = value; - didChange(); -} - -void TextTrackCue::setEndTime(double value, ExceptionCode& ec) -{ - // NaN, Infinity and -Infinity values should trigger a TypeError. - if (std::isinf(value) || std::isnan(value)) { - ec = TypeError; - return; - } - - // TODO(93143): Add spec-compliant behavior for negative time values. - if (m_endTime == value || value < 0) - return; - - willChange(); - m_endTime = value; - didChange(); -} - -void TextTrackCue::setPauseOnExit(bool value) -{ - if (m_pauseOnExit == value) - return; - - m_pauseOnExit = value; -} - -const String& TextTrackCue::vertical() const -{ - switch (m_writingDirection) { - case Horizontal: - return horizontalKeyword(); - case VerticalGrowingLeft: - return verticalGrowingLeftKeyword(); - case VerticalGrowingRight: - return verticalGrowingRightKeyword(); - default: - ASSERT_NOT_REACHED(); - return emptyString(); - } -} - -void TextTrackCue::setVertical(const String& value, ExceptionCode& ec) -{ - // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-vertical - // 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. - - 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) + if (m_startTime.toDouble() == value || value < 0) return; - willChange(); - m_writingDirection = direction; - didChange(); + setStartTime(MediaTime::createWithDouble(value)); } -void TextTrackCue::setSnapToLines(bool value) +void TextTrackCue::setStartTime(const MediaTime& value) { - if (m_snapToLines == value) - return; - willChange(); - m_snapToLines = value; - didChange(); -} - -void TextTrackCue::setLine(int position, ExceptionCode& ec) -{ - // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-line - // 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; - - willChange(); - m_linePosition = position; - m_computedLinePosition = calculateComputedLinePosition(); + m_startTime = value; didChange(); } - -void TextTrackCue::setPosition(int position, ExceptionCode& ec) -{ - // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-position - // 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; - willChange(); - m_textPosition = position; - didChange(); -} - -void TextTrackCue::setSize(int size, ExceptionCode& ec) +void TextTrackCue::setEndTime(double value) { - // 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) + // TODO(93143): Add spec-compliant behavior for negative time values. + if (m_endTime.toDouble() == value || value < 0) return; - - willChange(); - m_cueSize = size; - didChange(); -} -const String& TextTrackCue::align() const -{ - switch (m_cueAlignment) { - case Start: - return startKeyword(); - case Middle: - return middleKeyword(); - case End: - return endKeyword(); - default: - ASSERT_NOT_REACHED(); - return emptyString(); - } + setEndTime(MediaTime::createWithDouble(value)); } -void TextTrackCue::setAlign(const String& value, ExceptionCode& ec) +void TextTrackCue::setEndTime(const MediaTime& value) { - // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-align - // 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. - - CueAlignment 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; - willChange(); - m_cueAlignment = alignment; + m_endTime = value; didChange(); } -void TextTrackCue::setText(const String& text) +void TextTrackCue::setPauseOnExit(bool value) { - if (m_content == text) + if (m_pauseOnExit == value) return; - willChange(); - // Clear the document fragment but don't bother to create it again just yet as we can do that - // when it is requested. - m_webVTTNodeTree = 0; - m_content = text; - didChange(); -} - -int TextTrackCue::cueIndex() -{ - if (m_cueIndex == invalidCueIndex) { - ASSERT(track()); - ASSERT(track()->cues()); - if (TextTrackCueList* cueList = track()->cues()) - m_cueIndex = cueList->getCueIndex(this); - } - - return m_cueIndex; -} - -void TextTrackCue::invalidateCueIndex() -{ - m_cueIndex = invalidCueIndex; -} - -void TextTrackCue::createWebVTTNodeTree() -{ - if (!m_webVTTNodeTree) - m_webVTTNodeTree = WebVTTParser::create(0, &m_scriptExecutionContext)->createDocumentFragmentFromCueText(m_content); -} - -void TextTrackCue::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> TextTrackCue::getCueAsHTML() -{ - createWebVTTNodeTree(); - if (!m_webVTTNodeTree) - return 0; - - RefPtr<DocumentFragment> clonedFragment = DocumentFragment::create(ownerDocument()); - copyWebVTTNodeToDOMTree(m_webVTTNodeTree.get(), clonedFragment.get()); - return clonedFragment.release(); -} - -PassRefPtr<DocumentFragment> TextTrackCue::createCueRenderingTree() -{ - RefPtr<DocumentFragment> clonedFragment; - createWebVTTNodeTree(); - if (!m_webVTTNodeTree) - return 0; - - clonedFragment = DocumentFragment::create(ownerDocument()); - m_webVTTNodeTree->cloneChildNodes(clonedFragment.get()); - return clonedFragment.release(); + m_pauseOnExit = value; } -bool TextTrackCue::dispatchEvent(PassRefPtr<Event> event) +bool TextTrackCue::dispatchEvent(Event& event) { // When a TextTrack's mode is disabled: no cues are active, no events fired. - if (!track() || track()->mode() == TextTrack::disabledKeyword()) + if (!track() || track()->mode() == TextTrack::Mode::Disabled) return false; return EventTarget::dispatchEvent(event); } -#if ENABLE(WEBVTT_REGIONS) -void TextTrackCue::setRegionId(const String& regionId) -{ - if (m_regionId == regionId) - return; - - willChange(); - m_regionId = regionId; - didChange(); -} -#endif - bool TextTrackCue::isActive() { - return m_isActive && track() && track()->mode() != TextTrack::disabledKeyword(); + return m_isActive && track() && track()->mode() != TextTrack::Mode::Disabled; } void TextTrackCue::setIsActive(bool active) { m_isActive = active; - - if (!active) { - if (!hasDisplayTree()) - return; - - // Remove the display tree as soon as the cue becomes inactive. - displayTreeInternal()->remove(ASSERT_NO_EXCEPTION); - } -} - -int TextTrackCue::calculateComputedLinePosition() -{ - // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-computed-line-position - - // If the text track cue line position is numeric, then that is the text - // track cue computed line position. - if (m_linePosition != undefinedPosition) - return m_linePosition; - - // If the text track cue snap-to-lines flag of the text track cue is not - // set, the text track cue computed line position is the value 100; - if (!m_snapToLines) - return 100; - - // Otherwise, it is the value returned by the following algorithm: - - // If cue is not associated with a text track, return -1 and abort these - // steps. - if (!track()) - return -1; - - // Let n be the number of text tracks whose text track mode is showing or - // showing by default and that are in the media element's list of text - // tracks before track. - int n = track()->trackIndexRelativeToRenderedTracks(); - - // Increment n by one. - n++; - - // Negate n. - n = -n; - - return n; -} - -static bool isCueParagraphSeparator(UChar character) -{ - // Within a cue, paragraph boundaries are only denoted by Type B characters, - // such as U+000A LINE FEED (LF), U+0085 NEXT LINE (NEL), and U+2029 PARAGRAPH SEPARATOR. - return u_charType(character) == U_PARAGRAPH_SEPARATOR; -} - -void TextTrackCue::determineTextDirection() -{ - DEFINE_STATIC_LOCAL(const String, rtTag, (ASCIILiteral("rt"))); - createWebVTTNodeTree(); - if (!m_webVTTNodeTree) - return; - - // Apply the Unicode Bidirectional Algorithm's Paragraph Level steps to the - // concatenation of the values of each WebVTT Text Object in nodes, in a - // pre-order, depth-first traversal, excluding WebVTT Ruby Text Objects and - // their descendants. - StringBuilder paragraphBuilder; - for (Node* node = m_webVTTNodeTree->firstChild(); node; node = NodeTraversal::next(node, m_webVTTNodeTree.get())) { - // FIXME: The code does not match the comment above. This does not actually exclude Ruby Text Object descendant. - 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 TextTrackCue::calculateDisplayParameters() -{ - // Steps 10.2, 10.3 - determineTextDirection(); - - // 10.4 If the text track cue writing direction is horizontal, then let - // block-flow be 'tb'. Otherwise, if the text track cue writing direction is - // vertical growing left, then let block-flow be 'lr'. Otherwise, the text - // track cue writing direction is vertical growing right; let block-flow be - // 'rl'. - m_displayWritingMode = m_displayWritingModeMap[m_writingDirection]; - - // 10.5 Determine the value of maximum size for cue as per the appropriate - // rules from the following list: - 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 == VerticalGrowingLeft && m_cueAlignment == Start) - || (m_writingDirection == VerticalGrowingRight && m_cueAlignment == Start)) { - 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 == VerticalGrowingLeft && m_cueAlignment == End) - || (m_writingDirection == VerticalGrowingRight && m_cueAlignment == End)) { - maximumSize = m_textPosition; - } else if (m_cueAlignment == Middle) { - maximumSize = m_textPosition <= 50 ? m_textPosition : (100 - m_textPosition); - maximumSize = maximumSize * 2; - } - - // 10.6 If the text track cue size is less than maximum size, then let size - // be text track cue size. Otherwise, let size be maximum size. - m_displaySize = std::min(m_cueSize, maximumSize); - - // 10.8 Determine the value of x-position or y-position for cue as per the - // appropriate rules from the following list: - if (m_writingDirection == Horizontal) { - if (m_cueAlignment == Start) { - if (m_displayDirection == CSSValueLtr) - m_displayPosition.first = m_textPosition; - else - m_displayPosition.first = 100 - m_textPosition - m_displaySize; - } else if (m_cueAlignment == End) { - if (m_displayDirection == CSSValueRtl) - m_displayPosition.first = 100 - m_textPosition; - else - m_displayPosition.first = m_textPosition - m_displaySize; - } - } - - if ((m_writingDirection == VerticalGrowingLeft && m_cueAlignment == Start) - || (m_writingDirection == VerticalGrowingRight && m_cueAlignment == Start)) { - m_displayPosition.second = m_textPosition; - } else if ((m_writingDirection == VerticalGrowingLeft && m_cueAlignment == End) - || (m_writingDirection == VerticalGrowingRight && m_cueAlignment == End)) { - m_displayPosition.second = 100 - m_textPosition; - } - - if (m_writingDirection == Horizontal && m_cueAlignment == Middle) { - if (m_displayDirection == CSSValueLtr) - m_displayPosition.first = m_textPosition - m_displaySize / 2; - else - m_displayPosition.first = 100 - m_textPosition - m_displaySize / 2; - } - - if ((m_writingDirection == VerticalGrowingLeft && m_cueAlignment == Middle) - || (m_writingDirection == VerticalGrowingRight && m_cueAlignment == Middle)) - m_displayPosition.second = m_textPosition - m_displaySize / 2; - - // 10.9 Determine the value of whichever of x-position or y-position is not - // yet calculated for cue as per the appropriate rules from the following - // list: - 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; - - // A text track cue has a text track cue computed line position whose value - // is defined in terms of the other aspects of the cue. - m_computedLinePosition = calculateComputedLinePosition(); -} - -void TextTrackCue::markFutureAndPastNodes(ContainerNode* root, double previousTimestamp, double movieTime) -{ - DEFINE_STATIC_LOCAL(const String, timestampTag, (ASCIILiteral("timestamp"))); - - bool isPastNode = true; - double currentTimestamp = previousTimestamp; - if (currentTimestamp > movieTime) - isPastNode = false; - - for (Node* child = root->firstChild(); child; child = NodeTraversal::next(child, root)) { - if (child->nodeName() == timestampTag) { - unsigned position = 0; - String timestamp = child->nodeValue(); - double currentTimestamp = WebVTTParser::create(0, &m_scriptExecutionContext)->collectTimeStamp(timestamp, &position); - ASSERT(currentTimestamp != -1); - - if (currentTimestamp > movieTime) - isPastNode = false; - } - - if (child->isWebVTTElement()) { - toWebVTTElement(child)->setIsPastNode(isPastNode); - // Make an elemenet id match a cue id for style matching purposes. - if (!m_id.isEmpty()) - toElement(child)->setIdAttribute(m_id); - } - } -} - -void TextTrackCue::updateDisplayTree(double movieTime) -{ - // The display tree may contain WebVTT timestamp objects representing - // timestamps (processing instructions), along with displayable nodes. - - if (!track()->isRendered()) - return; - - // Clear the contents of the set. - m_cueBackgroundBox->removeChildren(); - - // Update the two sets containing past and future WebVTT objects. - RefPtr<DocumentFragment> referenceTree = createCueRenderingTree(); - if (!referenceTree) - return; - - markFutureAndPastNodes(referenceTree.get(), startTime(), movieTime); - m_cueBackgroundBox->appendChild(referenceTree); -} - -TextTrackCueBox* TextTrackCue::getDisplayTree(const IntSize& videoSize) -{ - RefPtr<TextTrackCueBox> displayTree = displayTreeInternal(); - if (!m_displayTreeShouldChange || !track()->isRendered()) - return displayTree.get(); - - // 10.1 - 10.10 - calculateDisplayParameters(); - - // 10.11. Apply the terms of the CSS specifications to nodes within the - // following constraints, thus obtaining a set of CSS boxes positioned - // relative to an initial containing block: - displayTree->removeChildren(); - - // The document tree is the tree of WebVTT Node Objects rooted at nodes. - - // The children of the nodes must be wrapped in an anonymous box whose - // 'display' property has the value 'inline'. This is the WebVTT cue - // background box. - - // Note: This is contained by default in m_cueBackgroundBox. - m_cueBackgroundBox->setPseudo(cueShadowPseudoId()); - displayTree->appendChild(m_cueBackgroundBox, ASSERT_NO_EXCEPTION); - - // FIXME(BUG 79916): Runs of children of WebVTT Ruby Objects that are not - // WebVTT Ruby Text Objects must be wrapped in anonymous boxes whose - // 'display' property has the value 'ruby-base'. - - // FIXME(BUG 79916): Text runs must be wrapped according to the CSS - // line-wrapping rules, except that additionally, regardless of the value of - // the 'white-space' property, lines must be wrapped at the edge of their - // containing blocks, even if doing so requires splitting a word where there - // is no line breaking opportunity. (Thus, normally text wraps as needed, - // but if there is a particularly long word, it does not overflow as it - // normally would in CSS, it is instead forcibly wrapped at the box's edge.) - displayTree->applyCSSProperties(videoSize); - - m_displayTreeShouldChange = false; - - // 10.15. Let cue's text track cue display state have the CSS boxes in - // boxes. - return displayTree.get(); -} - -void TextTrackCue::removeDisplayTree() -{ - if (!hasDisplayTree()) - return; - displayTreeInternal()->remove(ASSERT_NO_EXCEPTION); } -std::pair<double, double> TextTrackCue::getPositionCoordinates() const -{ - // This method is used for setting x and y when snap to lines is not set. - 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; -} - -TextTrackCue::CueSetting TextTrackCue::settingName(const String& name) -{ - DEFINE_STATIC_LOCAL(const String, verticalKeyword, (ASCIILiteral("vertical"))); - DEFINE_STATIC_LOCAL(const String, lineKeyword, (ASCIILiteral("line"))); - DEFINE_STATIC_LOCAL(const String, positionKeyword, (ASCIILiteral("position"))); - DEFINE_STATIC_LOCAL(const String, sizeKeyword, (ASCIILiteral("size"))); - DEFINE_STATIC_LOCAL(const String, alignKeyword, (ASCIILiteral("align"))); -#if ENABLE(WEBVTT_REGIONS) - DEFINE_STATIC_LOCAL(const String, regionIdKeyword, (ASCIILiteral("region"))); -#endif - - if (name == verticalKeyword) - return Vertical; - else if (name == lineKeyword) - return Line; - else if (name == positionKeyword) - return Position; - else if (name == sizeKeyword) - return Size; - else if (name == alignKeyword) - return Align; -#if ENABLE(WEBVTT_REGIONS) - else if (name == regionIdKeyword) - return RegionId; -#endif - - return None; -} - -void TextTrackCue::setCueSettings(const String& input) -{ - m_settings = input; - unsigned position = 0; - - while (position < input.length()) { - - // The WebVTT cue settings part of a WebVTT cue consists of zero or more of the following components, in any order, - // separated from each other by one or more U+0020 SPACE characters or U+0009 CHARACTER TABULATION (tab) characters. - while (position < input.length() && WebVTTParser::isValidSettingDelimiter(input[position])) - position++; - if (position >= input.length()) - break; - - // When the user agent is to parse the WebVTT settings given by a string input for a text track cue cue, - // the user agent must run the following steps: - // 1. Let settings be the result of splitting input on spaces. - // 2. For each token setting in the list settings, run the following substeps: - // 1. If setting does not contain a U+003A COLON character (:), or if the first U+003A COLON character (:) - // in setting is either the first or last character of setting, then jump to the step labeled next setting. - unsigned endOfSetting = position; - String setting = WebVTTParser::collectWord(input, &endOfSetting); - CueSetting name; - size_t colonOffset = setting.find(':', 1); - if (colonOffset == notFound || colonOffset == 0 || colonOffset == setting.length() - 1) - goto NextSetting; - - // 2. Let name be the leading substring of setting up to and excluding the first U+003A COLON character (:) in that string. - name = settingName(setting.substring(0, colonOffset)); - - // 3. Let value be the trailing substring of setting starting from the character immediately after the first U+003A COLON character (:) in that string. - position += colonOffset + 1; - if (position >= input.length()) - break; - - // 4. Run the appropriate substeps that apply for the value of name, as follows: - switch (name) { - case Vertical: - { - // If name is a case-sensitive match for "vertical" - // 1. If value is a case-sensitive match for the string "rl", then let cue's text track cue writing direction - // be vertical growing left. - String writingDirection = WebVTTParser::collectWord(input, &position); - if (writingDirection == verticalGrowingLeftKeyword()) - m_writingDirection = VerticalGrowingLeft; - - // 2. Otherwise, if value is a case-sensitive match for the string "lr", then let cue's text track cue writing - // direction be vertical growing right. - else if (writingDirection == verticalGrowingRightKeyword()) - m_writingDirection = VerticalGrowingRight; - } - break; - case Line: - { - // 1-2 - Collect chars that are either '-', '%', or a digit. - // 1. If value contains any characters other than U+002D HYPHEN-MINUS characters (-), U+0025 PERCENT SIGN - // characters (%), and characters in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9), then jump - // to the step labeled next setting. - StringBuilder linePositionBuilder; - while (position < input.length() && (input[position] == '-' || input[position] == '%' || isASCIIDigit(input[position]))) - linePositionBuilder.append(input[position++]); - if (position < input.length() && !WebVTTParser::isValidSettingDelimiter(input[position])) - break; - - // 2. If value does not contain at least one character in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT - // NINE (9), then jump to the step labeled next setting. - // 3. If any character in value other than the first character is a U+002D HYPHEN-MINUS character (-), then - // jump to the step labeled next setting. - // 4. If any character in value other than the last character is a U+0025 PERCENT SIGN character (%), then - // jump to the step labeled next setting. - String linePosition = linePositionBuilder.toString(); - if (linePosition.find('-', 1) != notFound || linePosition.reverseFind("%", linePosition.length() - 2) != notFound) - break; - - // 5. If the first character in value is a U+002D HYPHEN-MINUS character (-) and the last character in value is a - // U+0025 PERCENT SIGN character (%), then jump to the step labeled next setting. - if (linePosition[0] == '-' && linePosition[linePosition.length() - 1] == '%') - break; - - // 6. Ignoring the trailing percent sign, if any, interpret value as a (potentially signed) integer, and - // let number be that number. - // NOTE: toInt ignores trailing non-digit characters, such as '%'. - bool validNumber; - int number = linePosition.toInt(&validNumber); - if (!validNumber) - break; - - // 7. If the last character in value is a U+0025 PERCENT SIGN character (%), but number is not in the range - // 0 ≤ number ≤ 100, then jump to the step labeled next setting. - // 8. Let cue's text track cue line position be number. - // 9. If the last character in value is a U+0025 PERCENT SIGN character (%), then let cue's text track cue - // snap-to-lines flag be false. Otherwise, let it be true. - if (linePosition[linePosition.length() - 1] == '%') { - if (number < 0 || number > 100) - break; - - // 10 - If '%' then set snap-to-lines flag to false. - m_snapToLines = false; - } - - m_linePosition = number; - } - break; - case Position: - { - // 1. If value contains any characters other than U+0025 PERCENT SIGN characters (%) and characters in the range - // U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9), then jump to the step labeled next setting. - // 2. If value does not contain at least one character in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9), - // then jump to the step labeled next setting. - String textPosition = WebVTTParser::collectDigits(input, &position); - if (textPosition.isEmpty()) - break; - if (position >= input.length()) - break; - - // 3. If any character in value other than the last character is a U+0025 PERCENT SIGN character (%), then jump - // to the step labeled next setting. - // 4. If the last character in value is not a U+0025 PERCENT SIGN character (%), then jump to the step labeled - // next setting. - if (input[position++] != '%') - break; - if (position < input.length() && !WebVTTParser::isValidSettingDelimiter(input[position])) - break; - - // 5. Ignoring the trailing percent sign, interpret value as an integer, and let number be that number. - // 6. If number is not in the range 0 ≤ number ≤ 100, then jump to the step labeled next setting. - // NOTE: toInt ignores trailing non-digit characters, such as '%'. - bool validNumber; - int number = textPosition.toInt(&validNumber); - if (!validNumber) - break; - if (number < 0 || number > 100) - break; - - // 7. Let cue's text track cue text position be number. - m_textPosition = number; - } - break; - case Size: - { - // 1. If value contains any characters other than U+0025 PERCENT SIGN characters (%) and characters in the - // range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9), then jump to the step labeled next setting. - // 2. If value does not contain at least one character in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT - // NINE (9), then jump to the step labeled next setting. - String cueSize = WebVTTParser::collectDigits(input, &position); - if (cueSize.isEmpty()) - break; - if (position >= input.length()) - break; - - // 3. If any character in value other than the last character is a U+0025 PERCENT SIGN character (%), - // then jump to the step labeled next setting. - // 4. If the last character in value is not a U+0025 PERCENT SIGN character (%), then jump to the step - // labeled next setting. - if (input[position++] != '%') - break; - if (position < input.length() && !WebVTTParser::isValidSettingDelimiter(input[position])) - break; - - // 5. Ignoring the trailing percent sign, interpret value as an integer, and let number be that number. - // 6. If number is not in the range 0 ≤ number ≤ 100, then jump to the step labeled next setting. - bool validNumber; - int number = cueSize.toInt(&validNumber); - if (!validNumber) - break; - if (number < 0 || number > 100) - break; - - // 7. Let cue's text track cue size be number. - m_cueSize = number; - } - break; - case Align: - { - String cueAlignment = WebVTTParser::collectWord(input, &position); - - // 1. If value is a case-sensitive match for the string "start", then let cue's text track cue alignment be start alignment. - if (cueAlignment == startKeyword()) - m_cueAlignment = Start; - - // 2. If value is a case-sensitive match for the string "middle", then let cue's text track cue alignment be middle alignment. - else if (cueAlignment == middleKeyword()) - m_cueAlignment = Middle; - - // 3. If value is a case-sensitive match for the string "end", then let cue's text track cue alignment be end alignment. - else if (cueAlignment == endKeyword()) - m_cueAlignment = End; - } - break; -#if ENABLE(WEBVTT_REGIONS) - case RegionId: - m_regionId = WebVTTParser::collectWord(input, &position); - break; -#endif - case None: - break; - } - -NextSetting: - position = endOfSetting; - } -#if ENABLE(WEBVTT_REGIONS) - // If cue's line position is not auto or cue's size is not 100 or cue's - // writing direction is not horizontal, but cue's region identifier is not - // the empty string, let cue's region identifier be the empty string. - if (m_regionId.isEmpty()) - return; - - if (m_linePosition != undefinedPosition || m_cueSize != 100 || m_writingDirection != Horizontal) - m_regionId = emptyString(); -#endif -} - -CSSValueID TextTrackCue::getCSSWritingDirection() const -{ - return m_displayDirection; -} - -CSSValueID TextTrackCue::getCSSWritingMode() const +bool TextTrackCue::isOrderedBefore(const TextTrackCue* other) const { - return m_displayWritingMode; + return startMediaTime() < other->startMediaTime() || (startMediaTime() == other->startMediaTime() && endMediaTime() > other->endMediaTime()); } -int TextTrackCue::getCSSSize() const +bool TextTrackCue::cueContentsMatch(const TextTrackCue& cue) const { - return m_displaySize; -} + if (cueType() != cue.cueType()) + return false; -std::pair<double, double> TextTrackCue::getCSSPosition() const -{ - if (!m_snapToLines) - return getPositionCoordinates(); + if (id() != cue.id()) + return false; - return m_displayPosition; + return true; } -bool TextTrackCue::isEqual(const TextTrackCue& cue, CueMatchRules match) const +bool TextTrackCue::isEqual(const TextTrackCue& cue, TextTrackCue::CueMatchRules match) const { if (cueType() != cue.cueType()) return false; - - if (match != IgnoreDuration && m_endTime != cue.endTime()) - return false; - if (m_startTime != cue.startTime()) - return false; - if (m_content != cue.text()) - return false; - if (m_settings != cue.cueSettings()) - return false; - if (m_id != cue.id()) - return false; - if (m_textPosition != cue.position()) - return false; - if (m_linePosition != cue.line()) + + if (match != IgnoreDuration && endMediaTime() != cue.endMediaTime()) return false; - if (m_cueSize != cue.size()) + if (!hasEquivalentStartTime(cue)) return false; - if (align() != cue.align()) + if (!cueContentsMatch(cue)) return false; - + return true; } -bool TextTrackCue::isOrderedBefore(const TextTrackCue* other) const +bool TextTrackCue::hasEquivalentStartTime(const TextTrackCue& cue) const { - return startTime() < other->startTime() || (startTime() == other->startTime() && endTime() > other->endTime()); + MediaTime startTimeVariance = MediaTime::zeroTime(); + if (track()) + startTimeVariance = track()->startTimeVariance(); + else if (cue.track()) + startTimeVariance = cue.track()->startTimeVariance(); + + return abs(abs(startMediaTime()) - abs(cue.startMediaTime())) <= startTimeVariance; } -void TextTrackCue::setFontSize(int fontSize, const IntSize&, bool important) +bool TextTrackCue::doesExtendCue(const TextTrackCue& cue) const { - if (!hasDisplayTree() || !fontSize) - return; - - LOG(Media, "TextTrackCue::setFontSize - setting cue font size to %i", fontSize); + if (!cueContentsMatch(cue)) + return false; - displayTreeInternal()->setInlineStyleProperty(CSSPropertyFontSize, fontSize, CSSPrimitiveValue::CSS_PX, important); + if (endMediaTime() != cue.startMediaTime()) + return false; + + return true; } } // namespace WebCore diff --git a/Source/WebCore/html/track/TextTrackCue.h b/Source/WebCore/html/track/TextTrackCue.h index 5d3cf4e9c..4907546a2 100644 --- a/Source/WebCore/html/track/TextTrackCue.h +++ b/Source/WebCore/html/track/TextTrackCue.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. - * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2012-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -29,62 +29,20 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TextTrackCue_h -#define TextTrackCue_h +#pragma once #if ENABLE(VIDEO_TRACK) -#include "EventTarget.h" -#include "HTMLElement.h" -#include <wtf/PassOwnPtr.h> -#include <wtf/RefCounted.h> +#include "Document.h" +#include <wtf/MediaTime.h> namespace WebCore { -class DocumentFragment; -class HTMLSpanElement; -class ScriptExecutionContext; class TextTrack; -class TextTrackCue; - -// ---------------------------- - -class TextTrackCueBox : public HTMLElement { -public: - static PassRefPtr<TextTrackCueBox> create(Document& document, TextTrackCue* cue) - { - return adoptRef(new TextTrackCueBox(document, cue)); - } - - TextTrackCue* getCue() const; - virtual void applyCSSProperties(const IntSize& videoSize); - - static const AtomicString& textTrackCueBoxShadowPseudoId(); - -protected: - TextTrackCueBox(Document&, TextTrackCue*); - - virtual RenderPtr<RenderElement> createElementRenderer(PassRef<RenderStyle>) override; - - TextTrackCue* m_cue; -}; - -// ---------------------------- class TextTrackCue : public RefCounted<TextTrackCue>, public EventTargetWithInlineData { public: - static PassRefPtr<TextTrackCue> create(ScriptExecutionContext& context, double start, double end, const String& content) - { - return adoptRef(new TextTrackCue(context, start, end, content)); - } - - static const AtomicString& cueShadowPseudoId() - { - DEFINE_STATIC_LOCAL(const AtomicString, cue, ("cue", AtomicString::ConstructFromLiteral)); - return cue; - } - - virtual ~TextTrackCue(); + static const AtomicString& cueShadowPseudoId(); TextTrack* track() const; void setTrack(TextTrack*); @@ -92,193 +50,73 @@ public: const String& id() const { return m_id; } void setId(const String&); - double startTime() const { return m_startTime; } - void setStartTime(double, ExceptionCode&); + double startTime() const { return startMediaTime().toDouble(); } + void setStartTime(double); - double endTime() const { return m_endTime; } - void setEndTime(double, ExceptionCode&); + double endTime() const { return endMediaTime().toDouble(); } + void setEndTime(double); bool pauseOnExit() const { return m_pauseOnExit; } void setPauseOnExit(bool); - const String& vertical() const; - void setVertical(const String&, ExceptionCode&); + MediaTime startMediaTime() const { return m_startTime; } + void setStartTime(const MediaTime&); - bool snapToLines() const { return m_snapToLines; } - void setSnapToLines(bool); - - int line() const { return m_linePosition; } - virtual void setLine(int, ExceptionCode&); - - int position() const { return m_textPosition; } - virtual void setPosition(int, ExceptionCode&); - - int size() const { return m_cueSize; } - virtual void setSize(int, ExceptionCode&); - - const String& align() const; - void setAlign(const String&, ExceptionCode&); - - const String& text() const { return m_content; } - void setText(const String&); - - const String& cueSettings() const { return m_settings; } - void setCueSettings(const String&); - - int cueIndex(); - void invalidateCueIndex(); - - PassRefPtr<DocumentFragment> getCueAsHTML(); - PassRefPtr<DocumentFragment> createCueRenderingTree(); - - using EventTarget::dispatchEvent; - virtual bool dispatchEvent(PassRefPtr<Event>) override; - -#if ENABLE(WEBVTT_REGIONS) - const String& regionId() const { return m_regionId; } - void setRegionId(const String&); -#endif + MediaTime endMediaTime() const { return m_endTime; } + void setEndTime(const MediaTime&); bool isActive(); - void setIsActive(bool); + virtual void setIsActive(bool); - bool hasDisplayTree() const { return m_displayTree; } - TextTrackCueBox* getDisplayTree(const IntSize& videoSize); - HTMLSpanElement* element() const { return m_cueBackgroundBox.get(); } - - void updateDisplayTree(double); - void removeDisplayTree(); - void markFutureAndPastNodes(ContainerNode*, double, double); - - int calculateComputedLinePosition(); - std::pair<double, double> getPositionCoordinates() const; - - virtual EventTargetInterface eventTargetInterface() const override final { return TextTrackCueEventTargetInterfaceType; } - virtual ScriptExecutionContext* scriptExecutionContext() const override final { return &m_scriptExecutionContext; } - - std::pair<double, double> getCSSPosition() const; - - int getCSSSize() const; - CSSValueID getCSSWritingDirection() const; - CSSValueID getCSSWritingMode() const; - - enum WritingDirection { - Horizontal, - VerticalGrowingLeft, - VerticalGrowingRight, - NumberOfWritingDirections - }; - WritingDirection getWritingDirection() const { return m_writingDirection; } + virtual bool isOrderedBefore(const TextTrackCue*) const; + virtual bool isPositionedAbove(const TextTrackCue* cue) const { return isOrderedBefore(cue); } - enum CueAlignment { - Start, - Middle, - End - }; - CueAlignment getAlignment() const { return m_cueAlignment; } + bool hasEquivalentStartTime(const TextTrackCue&) const; - virtual void setFontSize(int, const IntSize&, bool important); + enum CueType { Data, Generic, WebVTT }; + virtual CueType cueType() const = 0; + virtual bool isRenderable() const { return false; } - enum CueMatchRules { - MatchAllFields, - IgnoreDuration, - }; + enum CueMatchRules { MatchAllFields, IgnoreDuration }; virtual bool isEqual(const TextTrackCue&, CueMatchRules) const; - - virtual bool isOrderedBefore(const TextTrackCue*) const; - - enum CueType { - Generic, - WebVTT - }; - virtual CueType cueType() const { return WebVTT; } + virtual bool doesExtendCue(const TextTrackCue&) const; void willChange(); - void didChange(); - - DEFINE_ATTRIBUTE_EVENT_LISTENER(enter); - DEFINE_ATTRIBUTE_EVENT_LISTENER(exit); + virtual void didChange(); - using RefCounted<TextTrackCue>::ref; - using RefCounted<TextTrackCue>::deref; + using RefCounted::ref; + using RefCounted::deref; protected: - TextTrackCue(ScriptExecutionContext&, double start, double end, const String& content); - - Document& ownerDocument() { return toDocument(m_scriptExecutionContext); } + TextTrackCue(ScriptExecutionContext&, const MediaTime& start, const MediaTime& end); - virtual PassRefPtr<TextTrackCueBox> createDisplayTree(); - TextTrackCueBox* displayTreeInternal(); + Document& ownerDocument() { return downcast<Document>(m_scriptExecutionContext); } private: - void createWebVTTNodeTree(); - void copyWebVTTNodeToDOMTree(ContainerNode* WebVTTNode, ContainerNode* root); - - void parseSettings(const String&); - - void determineTextDirection(); - void calculateDisplayParameters(); - - virtual void refEventTarget() override final { ref(); } - virtual void derefEventTarget() override final { deref(); } - - enum CueSetting { - None, - Vertical, - Line, - Position, - Size, - Align, -#if ENABLE(WEBVTT_REGIONS) - RegionId -#endif - }; - CueSetting settingName(const String&); + void refEventTarget() final { ref(); } + void derefEventTarget() final { deref(); } - String m_id; - double m_startTime; - double m_endTime; - String m_content; - String m_settings; - int m_linePosition; - int m_computedLinePosition; - int m_textPosition; - int m_cueSize; - int m_cueIndex; - int m_processingCueChanges; - - WritingDirection m_writingDirection; - - CueAlignment m_cueAlignment; - - RefPtr<DocumentFragment> m_webVTTNodeTree; - TextTrack* m_track; - - ScriptExecutionContext& m_scriptExecutionContext; - - bool m_isActive; - bool m_pauseOnExit; - bool m_snapToLines; + using EventTarget::dispatchEvent; + bool dispatchEvent(Event&) final; - RefPtr<HTMLSpanElement> m_cueBackgroundBox; + EventTargetInterface eventTargetInterface() const final { return TextTrackCueEventTargetInterfaceType; } + ScriptExecutionContext* scriptExecutionContext() const final { return &m_scriptExecutionContext; } - bool m_displayTreeShouldChange; - RefPtr<TextTrackCueBox> m_displayTree; + virtual bool cueContentsMatch(const TextTrackCue&) const; - CSSValueID m_displayDirection; + String m_id; + MediaTime m_startTime; + MediaTime m_endTime; + int m_processingCueChanges { 0 }; - CSSValueID m_displayWritingModeMap[NumberOfWritingDirections]; - CSSValueID m_displayWritingMode; + TextTrack* m_track { nullptr }; - int m_displaySize; + ScriptExecutionContext& m_scriptExecutionContext; - std::pair<float, float> m_displayPosition; -#if ENABLE(WEBVTT_REGIONS) - String m_regionId; -#endif + bool m_isActive : 1; + bool m_pauseOnExit : 1; }; } // namespace WebCore #endif -#endif diff --git a/Source/WebCore/html/track/TextTrackCue.idl b/Source/WebCore/html/track/TextTrackCue.idl index e860b9765..44fe168aa 100644 --- a/Source/WebCore/html/track/TextTrackCue.idl +++ b/Source/WebCore/html/track/TextTrackCue.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * 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 @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -25,45 +25,18 @@ [ Conditional=VIDEO_TRACK, - JSGenerateToNativeObject, - Constructor(double startTime, double endTime, DOMString text), - ConstructorCallWith=ScriptExecutionContext, - EventTarget, - JSCustomMarkFunction, CustomIsReachable, + CustomToJSObject, + JSCustomMarkFunction, SkipVTableValidation, -] interface TextTrackCue { +] interface TextTrackCue : EventTarget { readonly attribute TextTrack track; attribute DOMString id; - [SetterRaisesException] attribute double startTime; - [SetterRaisesException] attribute double endTime; + attribute double startTime; + attribute double endTime; attribute boolean pauseOnExit; - [SetterRaisesException] attribute DOMString vertical; - attribute boolean snapToLines; - [SetterRaisesException] attribute long line; - [SetterRaisesException] attribute long position; - [SetterRaisesException] attribute long size; - [SetterRaisesException] attribute DOMString align; - - attribute DOMString text; - DocumentFragment getCueAsHTML(); - - attribute EventListener onenter; - attribute EventListener onexit; - - // EventTarget interface - void addEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - void removeEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - [RaisesException] boolean dispatchEvent(Event evt); - -#if defined(ENABLE_WEBVTT_REGIONS) && ENABLE_WEBVTT_REGIONS - attribute DOMString regionId; -#endif + attribute EventHandler onenter; + attribute EventHandler onexit; }; - diff --git a/Source/WebCore/html/track/TextTrackCueGeneric.cpp b/Source/WebCore/html/track/TextTrackCueGeneric.cpp index a3344dcbc..f63583924 100644 --- a/Source/WebCore/html/track/TextTrackCueGeneric.cpp +++ b/Source/WebCore/html/track/TextTrackCueGeneric.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -30,32 +30,37 @@ #include "TextTrackCueGeneric.h" #include "CSSPropertyNames.h" +#include "CSSStyleDeclaration.h" #include "CSSValueKeywords.h" -#include "HTMLNames.h" #include "HTMLSpanElement.h" #include "InbandTextTrackPrivateClient.h" #include "Logging.h" #include "RenderObject.h" #include "ScriptExecutionContext.h" +#include "StyleProperties.h" #include "TextTrackCue.h" +#include <wtf/MathExtras.h> namespace WebCore { -class TextTrackCueGenericBoxElement final : public TextTrackCueBox { +// This default value must be the same as the one specified in mediaControlsApple.css for -webkit-media-controls-closed-captions-container +const static int DEFAULTCAPTIONFONTSIZE = 10; + +class TextTrackCueGenericBoxElement final : public VTTCueBox { public: - static PassRefPtr<TextTrackCueGenericBoxElement> create(Document& document, TextTrackCueGeneric* cue) + static Ref<TextTrackCueGenericBoxElement> create(Document& document, TextTrackCueGeneric& cue) { - return adoptRef(new TextTrackCueGenericBoxElement(document, cue)); + return adoptRef(*new TextTrackCueGenericBoxElement(document, cue)); } - virtual void applyCSSProperties(const IntSize&) override; + void applyCSSProperties(const IntSize&) override; private: - TextTrackCueGenericBoxElement(Document&, TextTrackCue*); + TextTrackCueGenericBoxElement(Document&, VTTCue&); }; -TextTrackCueGenericBoxElement::TextTrackCueGenericBoxElement(Document& document, TextTrackCue* cue) - : TextTrackCueBox(document, cue) +TextTrackCueGenericBoxElement::TextTrackCueGenericBoxElement(Document& document, VTTCue& cue) + : VTTCueBox(document, cue) { } @@ -65,8 +70,9 @@ void TextTrackCueGenericBoxElement::applyCSSProperties(const IntSize& videoSize) setInlineStyleProperty(CSSPropertyUnicodeBidi, CSSValueWebkitPlaintext); TextTrackCueGeneric* cue = static_cast<TextTrackCueGeneric*>(getCue()); - RefPtr<HTMLSpanElement> cueElement = cue->element(); + Ref<HTMLSpanElement> cueElement = cue->element(); + CSSValueID alignment = cue->getCSSAlignment(); float size = static_cast<float>(cue->getCSSSize()); if (cue->useDefaultPosition()) { setInlineStyleProperty(CSSPropertyBottom, 0, CSSPrimitiveValue::CSS_PX); @@ -75,10 +81,40 @@ void TextTrackCueGenericBoxElement::applyCSSProperties(const IntSize& videoSize) setInlineStyleProperty(CSSPropertyLeft, static_cast<float>(cue->position()), CSSPrimitiveValue::CSS_PERCENTAGE); setInlineStyleProperty(CSSPropertyTop, static_cast<float>(cue->line()), CSSPrimitiveValue::CSS_PERCENTAGE); - if (cue->getWritingDirection() == TextTrackCue::Horizontal) - setInlineStyleProperty(CSSPropertyWidth, size, CSSPrimitiveValue::CSS_PERCENTAGE); - else - setInlineStyleProperty(CSSPropertyHeight, size, CSSPrimitiveValue::CSS_PERCENTAGE); + double authorFontSize = videoSize.height() * cue->baseFontSizeRelativeToVideoHeight() / 100.0; + if (!authorFontSize) + authorFontSize = DEFAULTCAPTIONFONTSIZE; + + if (cue->fontSizeMultiplier()) + authorFontSize *= cue->fontSizeMultiplier() / 100; + + double multiplier = m_fontSizeFromCaptionUserPrefs / authorFontSize; + double newCueSize = std::min(size * multiplier, 100.0); + if (cue->getWritingDirection() == VTTCue::Horizontal) { + setInlineStyleProperty(CSSPropertyWidth, newCueSize, CSSPrimitiveValue::CSS_PERCENTAGE); + if ((alignment == CSSValueMiddle || alignment == CSSValueCenter) && multiplier != 1.0) + setInlineStyleProperty(CSSPropertyLeft, static_cast<double>(cue->position() - (newCueSize - m_cue.getCSSSize()) / 2), CSSPrimitiveValue::CSS_PERCENTAGE); + } else { + setInlineStyleProperty(CSSPropertyHeight, newCueSize, CSSPrimitiveValue::CSS_PERCENTAGE); + if ((alignment == CSSValueMiddle || alignment == CSSValueCenter) && multiplier != 1.0) + setInlineStyleProperty(CSSPropertyTop, static_cast<double>(cue->line() - (newCueSize - m_cue.getCSSSize()) / 2), CSSPrimitiveValue::CSS_PERCENTAGE); + } + } + + double textPosition = m_cue.position(); + double maxSize = 100.0; + + if (alignment == CSSValueEnd || alignment == CSSValueRight) + maxSize = textPosition; + else if (alignment == CSSValueStart || alignment == CSSValueLeft) + maxSize = 100.0 - textPosition; + + if (cue->getWritingDirection() == VTTCue::Horizontal) { + setInlineStyleProperty(CSSPropertyMinWidth, "-webkit-min-content"); + setInlineStyleProperty(CSSPropertyMaxWidth, maxSize, CSSPrimitiveValue::CSS_PERCENTAGE); + } else { + setInlineStyleProperty(CSSPropertyMinHeight, "-webkit-min-content"); + setInlineStyleProperty(CSSPropertyMaxHeight, maxSize, CSSPrimitiveValue::CSS_PERCENTAGE); } if (cue->foregroundColor().isValid()) @@ -86,7 +122,7 @@ void TextTrackCueGenericBoxElement::applyCSSProperties(const IntSize& videoSize) if (cue->highlightColor().isValid()) cueElement->setInlineStyleProperty(CSSPropertyBackgroundColor, cue->highlightColor().serialized()); - if (cue->getWritingDirection() == TextTrackCue::Horizontal) + if (cue->getWritingDirection() == VTTCue::Horizontal) setInlineStyleProperty(CSSPropertyHeight, CSSValueAuto); else setInlineStyleProperty(CSSPropertyWidth, CSSValueAuto); @@ -94,43 +130,49 @@ void TextTrackCueGenericBoxElement::applyCSSProperties(const IntSize& videoSize) if (cue->baseFontSizeRelativeToVideoHeight()) cue->setFontSize(cue->baseFontSizeRelativeToVideoHeight(), videoSize, false); - if (cue->getAlignment() == TextTrackCue::Middle) + if (cue->getAlignment() == VTTCue::Middle) setInlineStyleProperty(CSSPropertyTextAlign, CSSValueCenter); - else if (cue->getAlignment() == TextTrackCue::End) + else if (cue->getAlignment() == VTTCue::End) setInlineStyleProperty(CSSPropertyTextAlign, CSSValueEnd); else setInlineStyleProperty(CSSPropertyTextAlign, CSSValueStart); if (cue->backgroundColor().isValid()) setInlineStyleProperty(CSSPropertyBackgroundColor, cue->backgroundColor().serialized()); - setInlineStyleProperty(CSSPropertyWebkitWritingMode, cue->getCSSWritingMode(), false); + setInlineStyleProperty(CSSPropertyWritingMode, cue->getCSSWritingMode(), false); setInlineStyleProperty(CSSPropertyWhiteSpace, CSSValuePreWrap); - setInlineStyleProperty(CSSPropertyWordBreak, CSSValueNormal); + + // Make sure shadow or stroke is not clipped. + setInlineStyleProperty(CSSPropertyOverflow, CSSValueVisible); + cueElement->setInlineStyleProperty(CSSPropertyOverflow, CSSValueVisible); } -TextTrackCueGeneric::TextTrackCueGeneric(ScriptExecutionContext& context, double start, double end, const String& content) - : TextTrackCue(context, start, end, content) +TextTrackCueGeneric::TextTrackCueGeneric(ScriptExecutionContext& context, const MediaTime& start, const MediaTime& end, const String& content) + : VTTCue(context, start, end, content) , m_baseFontSizeRelativeToVideoHeight(0) , m_fontSizeMultiplier(0) - , m_defaultPosition(true) { } -PassRefPtr<TextTrackCueBox> TextTrackCueGeneric::createDisplayTree() +Ref<VTTCueBox> TextTrackCueGeneric::createDisplayTree() { - return TextTrackCueGenericBoxElement::create(ownerDocument(), this); + return TextTrackCueGenericBoxElement::create(ownerDocument(), *this); } -void TextTrackCueGeneric::setLine(int line, ExceptionCode& ec) +ExceptionOr<void> TextTrackCueGeneric::setLine(double line) { - m_defaultPosition = false; - TextTrackCue::setLine(line, ec); + auto result = VTTCue::setLine(line); + if (!result.hasException()) + m_useDefaultPosition = false; + return result; } -void TextTrackCueGeneric::setPosition(int position, ExceptionCode& ec) +ExceptionOr<void> TextTrackCueGeneric::setPosition(double position) { - m_defaultPosition = false; - TextTrackCue::setPosition(position, ec); + auto result = VTTCue::setPosition(position); + if (!result.hasException()) + m_useDefaultPosition = false; + return result; } void TextTrackCueGeneric::setFontSize(int fontSize, const IntSize& videoSize, bool important) @@ -139,23 +181,25 @@ void TextTrackCueGeneric::setFontSize(int fontSize, const IntSize& videoSize, bo return; if (important || !baseFontSizeRelativeToVideoHeight()) { - TextTrackCue::setFontSize(fontSize, videoSize, important); + VTTCue::setFontSize(fontSize, videoSize, important); return; } double size = videoSize.height() * baseFontSizeRelativeToVideoHeight() / 100; if (fontSizeMultiplier()) size *= fontSizeMultiplier() / 100; - displayTreeInternal()->setInlineStyleProperty(CSSPropertyFontSize, lround(size), CSSPrimitiveValue::CSS_PX); + displayTreeInternal().setInlineStyleProperty(CSSPropertyFontSize, lround(size), CSSPrimitiveValue::CSS_PX); LOG(Media, "TextTrackCueGeneric::setFontSize - setting cue font size to %li", lround(size)); } - -bool TextTrackCueGeneric::isEqual(const TextTrackCue& cue, TextTrackCue::CueMatchRules match) const + +bool TextTrackCueGeneric::cueContentsMatch(const TextTrackCue& cue) const { - if (cue.cueType() != TextTrackCue::Generic) + // Do call the parent class cueContentsMatch here, because we want to confirm + // the content of the two cues are identical (even though the types are not the same). + if (!VTTCue::cueContentsMatch(cue)) return false; - + const TextTrackCueGeneric* other = static_cast<const TextTrackCueGeneric*>(&cue); if (m_baseFontSizeRelativeToVideoHeight != other->baseFontSizeRelativeToVideoHeight()) @@ -169,23 +213,60 @@ bool TextTrackCueGeneric::isEqual(const TextTrackCue& cue, TextTrackCue::CueMatc if (m_backgroundColor != other->backgroundColor()) return false; - return TextTrackCue::isEqual(cue, match); + return true; +} + +bool TextTrackCueGeneric::isEqual(const TextTrackCue& cue, TextTrackCue::CueMatchRules match) const +{ + // Do not call the parent class isEqual here, because we are not cueType() == VTTCue, + // and will fail that equality test. + if (!TextTrackCue::isEqual(cue, match)) + return false; + + if (cue.cueType() != TextTrackCue::Generic) + return false; + + return cueContentsMatch(cue); +} + + +bool TextTrackCueGeneric::doesExtendCue(const TextTrackCue& cue) const +{ + if (!cueContentsMatch(cue)) + return false; + + return VTTCue::doesExtendCue(cue); } bool TextTrackCueGeneric::isOrderedBefore(const TextTrackCue* that) const { - if (TextTrackCue::isOrderedBefore(that)) + if (VTTCue::isOrderedBefore(that)) return true; if (that->cueType() == Generic && startTime() == that->startTime() && endTime() == that->endTime()) { // Further order generic cues by their calculated line value. std::pair<double, double> thisPosition = getPositionCoordinates(); - std::pair<double, double> thatPosition = that->getPositionCoordinates(); + std::pair<double, double> thatPosition = toVTTCue(that)->getPositionCoordinates(); return thisPosition.second > thatPosition.second || (thisPosition.second == thatPosition.second && thisPosition.first < thatPosition.first); } return false; } + +bool TextTrackCueGeneric::isPositionedAbove(const TextTrackCue* that) const +{ + if (that->cueType() == Generic && startTime() == that->startTime() && endTime() == that->endTime()) { + // Further order generic cues by their calculated line value. + std::pair<double, double> thisPosition = getPositionCoordinates(); + std::pair<double, double> thatPosition = toVTTCue(that)->getPositionCoordinates(); + return thisPosition.second > thatPosition.second || (thisPosition.second == thatPosition.second && thisPosition.first < thatPosition.first); + } + + if (that->cueType() == Generic) + return startTime() > that->startTime(); + + return VTTCue::isOrderedBefore(that); +} } // namespace WebCore diff --git a/Source/WebCore/html/track/TextTrackCueGeneric.h b/Source/WebCore/html/track/TextTrackCueGeneric.h index 37d908013..6c281eb2c 100644 --- a/Source/WebCore/html/track/TextTrackCueGeneric.h +++ b/Source/WebCore/html/track/TextTrackCueGeneric.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,74 +23,73 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TextTrackCueGeneric_h -#define TextTrackCueGeneric_h +#pragma once #if ENABLE(VIDEO_TRACK) #include "Color.h" -#include "TextTrackCue.h" +#include "VTTCue.h" namespace WebCore { class GenericCueData; // A "generic" cue is a non-WebVTT cue, so it is not positioned/sized with the WebVTT logic. -class TextTrackCueGeneric final : public TextTrackCue { +class TextTrackCueGeneric final : public VTTCue { public: - static PassRefPtr<TextTrackCueGeneric> create(ScriptExecutionContext& context, double start, double end, const String& content) + static Ref<TextTrackCueGeneric> create(ScriptExecutionContext& context, const MediaTime& start, const MediaTime& end, const String& content) { - return adoptRef(new TextTrackCueGeneric(context, start, end, content)); + return adoptRef(*new TextTrackCueGeneric(context, start, end, content)); } - - virtual ~TextTrackCueGeneric() { } - virtual PassRefPtr<TextTrackCueBox> createDisplayTree() override; + ExceptionOr<void> setLine(double) final; + ExceptionOr<void> setPosition(double) final; - virtual void setLine(int, ExceptionCode&) override; - virtual void setPosition(int, ExceptionCode&) override; + bool useDefaultPosition() const { return m_useDefaultPosition; } - bool useDefaultPosition() const { return m_defaultPosition; } - double baseFontSizeRelativeToVideoHeight() const { return m_baseFontSizeRelativeToVideoHeight; } void setBaseFontSizeRelativeToVideoHeight(double size) { m_baseFontSizeRelativeToVideoHeight = size; } double fontSizeMultiplier() const { return m_fontSizeMultiplier; } void setFontSizeMultiplier(double size) { m_fontSizeMultiplier = size; } - String fontName() const { return m_fontName; } - void setFontName(String name) { m_fontName = name; } + const String& fontName() const { return m_fontName; } + void setFontName(const String& name) { m_fontName = name; } - Color foregroundColor() const { return m_foregroundColor; } - void setForegroundColor(RGBA32 color) { m_foregroundColor.setRGB(color); } + const Color& foregroundColor() const { return m_foregroundColor; } + void setForegroundColor(const Color& color) { m_foregroundColor = color; } - Color backgroundColor() const { return m_backgroundColor; } - void setBackgroundColor(RGBA32 color) { m_backgroundColor.setRGB(color); } + const Color& backgroundColor() const { return m_backgroundColor; } + void setBackgroundColor(const Color& color) { m_backgroundColor = color; } - Color highlightColor() const { return m_highlightColor; } - void setHighlightColor(RGBA32 color) { m_highlightColor.setRGB(color); } + const Color& highlightColor() const { return m_highlightColor; } + void setHighlightColor(const Color& color) { m_highlightColor = color; } + + void setFontSize(int, const IntSize&, bool important) final; + +private: + TextTrackCueGeneric(ScriptExecutionContext&, const MediaTime& start, const MediaTime& end, const String&); - virtual void setFontSize(int, const IntSize&, bool important) override; + bool isOrderedBefore(const TextTrackCue*) const final; + bool isPositionedAbove(const TextTrackCue*) const final; - virtual bool isEqual(const TextTrackCue&, CueMatchRules) const override; + Ref<VTTCueBox> createDisplayTree() final; - virtual TextTrackCue::CueType cueType() const override { return TextTrackCue::Generic; } + bool isEqual(const TextTrackCue&, CueMatchRules) const final; + bool cueContentsMatch(const TextTrackCue&) const final; + bool doesExtendCue(const TextTrackCue&) const final; -private: - virtual bool isOrderedBefore(const TextTrackCue*) const override; + CueType cueType() const final { return Generic; } - TextTrackCueGeneric(ScriptExecutionContext&, double start, double end, const String&); - Color m_foregroundColor; Color m_backgroundColor; Color m_highlightColor; - double m_baseFontSizeRelativeToVideoHeight; - double m_fontSizeMultiplier; + double m_baseFontSizeRelativeToVideoHeight { 0 }; + double m_fontSizeMultiplier { 0 }; String m_fontName; - bool m_defaultPosition; + bool m_useDefaultPosition { true }; }; } // namespace WebCore #endif -#endif diff --git a/Source/WebCore/html/track/TextTrackCueList.cpp b/Source/WebCore/html/track/TextTrackCueList.cpp index f5451f5f0..03827c8a7 100644 --- a/Source/WebCore/html/track/TextTrackCueList.cpp +++ b/Source/WebCore/html/track/TextTrackCueList.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -29,118 +30,100 @@ #include "TextTrackCueList.h" -namespace WebCore { +// Checking sorting is too slow for general use; turn it on explicitly when working on this class. +#undef CHECK_SORTING -TextTrackCueList::TextTrackCueList() -{ -} +#ifdef CHECK_SORTING +#define ASSERT_SORTED(begin, end) ASSERT(std::is_sorted(begin, end, compareCues)) +#else +#define ASSERT_SORTED(begin, end) ((void)0) +#endif + +namespace WebCore { -unsigned long TextTrackCueList::length() const +static inline bool compareCues(const RefPtr<TextTrackCue>& a, const RefPtr<TextTrackCue>& b) { - return m_list.size(); + return a->isOrderedBefore(b.get()); } -unsigned long TextTrackCueList::getCueIndex(TextTrackCue* cue) const +unsigned TextTrackCueList::cueIndex(TextTrackCue& cue) const { - return m_list.find(cue); + ASSERT(m_vector.contains(&cue)); + return m_vector.find(&cue); } TextTrackCue* TextTrackCueList::item(unsigned index) const { - if (index < m_list.size()) - return m_list[index].get(); - return 0; + if (index >= m_vector.size()) + return nullptr; + return m_vector[index].get(); } TextTrackCue* TextTrackCueList::getCueById(const String& id) const { - for (size_t i = 0; i < m_list.size(); ++i) { - if (m_list[i]->id() == id) - return m_list[i].get(); + for (auto& cue : m_vector) { + if (cue->id() == id) + return cue.get(); } - return 0; + return nullptr; } -TextTrackCueList* TextTrackCueList::activeCues() +TextTrackCueList& TextTrackCueList::activeCues() { if (!m_activeCues) m_activeCues = create(); - m_activeCues->clear(); - for (size_t i = 0; i < m_list.size(); ++i) { - RefPtr<TextTrackCue> cue = m_list[i]; + Vector<RefPtr<TextTrackCue>> activeCuesVector; + for (auto& cue : m_vector) { if (cue->isActive()) - m_activeCues->add(cue); - } - return m_activeCues.get(); -} - -bool TextTrackCueList::add(PassRefPtr<TextTrackCue> cue) -{ - ASSERT(cue->startTime() >= 0); - ASSERT(cue->endTime() >= 0); - - return add(cue, 0, m_list.size()); -} - -bool TextTrackCueList::add(PassRefPtr<TextTrackCue> prpCue, size_t start, size_t end) -{ - ASSERT_WITH_SECURITY_IMPLICATION(start <= m_list.size()); - ASSERT_WITH_SECURITY_IMPLICATION(end <= m_list.size()); - - // Maintain text track cue order: - // http://www.whatwg.org/specs/web-apps/current-work/#text-track-cue-order - RefPtr<TextTrackCue> cue = prpCue; - if (start == end) { - if (!m_list.isEmpty() && (start > 0) && (m_list[start - 1].get() == cue.get())) - return false; - - m_list.insert(start, cue); - invalidateCueIndexes(start); - return true; + activeCuesVector.append(cue); } + ASSERT_SORTED(activeCuesVector.begin(), activeCuesVector.end()); + m_activeCues->m_vector = WTFMove(activeCuesVector); - size_t index = (start + end) / 2; - if (cue->isOrderedBefore(m_list[index].get())) - return add(cue.release(), start, index); - - return add(cue.release(), index + 1, end); + // FIXME: This list of active cues is not updated as cues are added, removed, become active, and become inactive. + // Instead it is only updated each time this function is called again. That is not consistent with other dynamic DOM lists. + return *m_activeCues; } -bool TextTrackCueList::remove(TextTrackCue* cue) +void TextTrackCueList::add(Ref<TextTrackCue>&& cue) { - size_t index = m_list.find(cue); - if (index == notFound) - return false; - - cue->setIsActive(false); - m_list.remove(index); - return true; + ASSERT(!m_vector.contains(cue.ptr())); + ASSERT(cue->startMediaTime() >= MediaTime::zeroTime()); + ASSERT(cue->endMediaTime() >= MediaTime::zeroTime()); + + RefPtr<TextTrackCue> cueRefPtr { WTFMove(cue) }; + unsigned insertionPosition = std::upper_bound(m_vector.begin(), m_vector.end(), cueRefPtr, compareCues) - m_vector.begin(); + ASSERT_SORTED(m_vector.begin(), m_vector.end()); + m_vector.insert(insertionPosition, WTFMove(cueRefPtr)); + ASSERT_SORTED(m_vector.begin(), m_vector.end()); } -bool TextTrackCueList::contains(TextTrackCue* cue) const +void TextTrackCueList::remove(TextTrackCue& cue) { - return m_list.contains(cue); + ASSERT_SORTED(m_vector.begin(), m_vector.end()); + m_vector.remove(cueIndex(cue)); + ASSERT_SORTED(m_vector.begin(), m_vector.end()); } -bool TextTrackCueList::updateCueIndex(TextTrackCue* cue) +void TextTrackCueList::updateCueIndex(TextTrackCue& cue) { - if (!contains(cue)) - return false; - - remove(cue); - return add(cue); -} - -void TextTrackCueList::clear() -{ - m_list.clear(); -} + auto cuePosition = m_vector.begin() + cueIndex(cue); + auto afterCuePosition = cuePosition + 1; + + ASSERT_SORTED(m_vector.begin(), cuePosition); + ASSERT_SORTED(afterCuePosition, m_vector.end()); + + auto reinsertionPosition = std::upper_bound(m_vector.begin(), cuePosition, *cuePosition, compareCues); + if (reinsertionPosition != cuePosition) + std::rotate(reinsertionPosition, cuePosition, afterCuePosition); + else { + reinsertionPosition = std::upper_bound(afterCuePosition, m_vector.end(), *cuePosition, compareCues); + if (reinsertionPosition != afterCuePosition) + std::rotate(cuePosition, afterCuePosition, reinsertionPosition); + } -void TextTrackCueList::invalidateCueIndexes(size_t start) -{ - for (size_t i = start; i < m_list.size(); ++i) - m_list[i]->invalidateCueIndex(); + ASSERT_SORTED(m_vector.begin(), m_vector.end()); } } // namespace WebCore diff --git a/Source/WebCore/html/track/TextTrackCueList.h b/Source/WebCore/html/track/TextTrackCueList.h index 8478fa31f..fe04c446d 100644 --- a/Source/WebCore/html/track/TextTrackCueList.h +++ b/Source/WebCore/html/track/TextTrackCueList.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,52 +24,47 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TextTrackCueList_h -#define TextTrackCueList_h +#pragma once #if ENABLE(VIDEO_TRACK) #include "TextTrackCue.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/Vector.h> namespace WebCore { class TextTrackCueList : public RefCounted<TextTrackCueList> { public: - static PassRefPtr<TextTrackCueList> create() - { - return adoptRef(new TextTrackCueList); - } - - ~TextTrackCueList() { } - - unsigned long length() const; - unsigned long getCueIndex(TextTrackCue*) const; + static Ref<TextTrackCueList> create(); + unsigned length() const; TextTrackCue* item(unsigned index) const; TextTrackCue* getCueById(const String&) const; - TextTrackCueList* activeCues(); - bool add(PassRefPtr<TextTrackCue>); - bool remove(TextTrackCue*); - bool contains(TextTrackCue*) const; - - bool updateCueIndex(TextTrackCue*); + unsigned cueIndex(TextTrackCue&) const; + + void add(Ref<TextTrackCue>&&); + void remove(TextTrackCue&); + void updateCueIndex(TextTrackCue&); + + TextTrackCueList& activeCues(); private: - TextTrackCueList(); - bool add(PassRefPtr<TextTrackCue>, size_t, size_t); - void clear(); - void invalidateCueIndexes(size_t); + TextTrackCueList() = default; - Vector<RefPtr<TextTrackCue>> m_list; + Vector<RefPtr<TextTrackCue>> m_vector; RefPtr<TextTrackCueList> m_activeCues; - }; +inline Ref<TextTrackCueList> TextTrackCueList::create() +{ + return adoptRef(*new TextTrackCueList); +} + +inline unsigned TextTrackCueList::length() const +{ + return m_vector.size(); +} + } // namespace WebCore -#endif -#endif +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/html/track/TextTrackCueList.idl b/Source/WebCore/html/track/TextTrackCueList.idl index 3a083b91b..6aaafdb08 100644 --- a/Source/WebCore/html/track/TextTrackCueList.idl +++ b/Source/WebCore/html/track/TextTrackCueList.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 diff --git a/Source/WebCore/html/track/TextTrackList.cpp b/Source/WebCore/html/track/TextTrackList.cpp index 18d3f1bab..32f6bca0a 100644 --- a/Source/WebCore/html/track/TextTrackList.cpp +++ b/Source/WebCore/html/track/TextTrackList.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -29,7 +29,6 @@ #include "TextTrackList.h" -#include "EventNames.h" #include "HTMLMediaElement.h" #include "InbandTextTrack.h" #include "InbandTextTrackPrivate.h" @@ -44,6 +43,20 @@ TextTrackList::TextTrackList(HTMLMediaElement* element, ScriptExecutionContext* TextTrackList::~TextTrackList() { + clearElement(); +} + +void TextTrackList::clearElement() +{ + TrackListBase::clearElement(); + for (auto& track : m_elementTracks) { + track->setMediaElement(nullptr); + track->clearClient(); + } + for (auto& track : m_addTrackTracks) { + track->setMediaElement(nullptr); + track->clearClient(); + } } unsigned TextTrackList::length() const @@ -51,56 +64,51 @@ unsigned TextTrackList::length() const return m_addTrackTracks.size() + m_elementTracks.size() + m_inbandTracks.size(); } -int TextTrackList::getTrackIndex(TextTrack *textTrack) +int TextTrackList::getTrackIndex(TextTrack& textTrack) { - if (textTrack->trackType() == TextTrack::TrackElement) - return static_cast<LoadableTextTrack*>(textTrack)->trackElementIndex(); + if (is<LoadableTextTrack>(textTrack)) + return downcast<LoadableTextTrack>(textTrack).trackElementIndex(); - if (textTrack->trackType() == TextTrack::AddTrack) - return m_elementTracks.size() + m_addTrackTracks.find(textTrack); + if (textTrack.trackType() == TextTrack::AddTrack) + return m_elementTracks.size() + m_addTrackTracks.find(&textTrack); - if (textTrack->trackType() == TextTrack::InBand) - return m_elementTracks.size() + m_addTrackTracks.size() + m_inbandTracks.find(textTrack); + if (textTrack.trackType() == TextTrack::InBand) + return m_elementTracks.size() + m_addTrackTracks.size() + m_inbandTracks.find(&textTrack); ASSERT_NOT_REACHED(); return -1; } -int TextTrackList::getTrackIndexRelativeToRenderedTracks(TextTrack *textTrack) +int TextTrackList::getTrackIndexRelativeToRenderedTracks(TextTrack& textTrack) { // Calculate the "Let n be the number of text tracks whose text track mode is showing and that are in the media element's list of text tracks before track." int trackIndex = 0; - for (size_t i = 0; i < m_elementTracks.size(); ++i) { - if (!toTextTrack(m_elementTracks[i].get())->isRendered()) + for (auto& elementTrack : m_elementTracks) { + if (!downcast<TextTrack>(*elementTrack).isRendered()) continue; - - if (m_elementTracks[i] == textTrack) + if (elementTrack == &textTrack) return trackIndex; ++trackIndex; } - for (size_t i = 0; i < m_addTrackTracks.size(); ++i) { - if (!toTextTrack(m_addTrackTracks[i].get())->isRendered()) + for (auto& addTrack : m_addTrackTracks) { + if (!downcast<TextTrack>(*addTrack).isRendered()) continue; - - if (m_addTrackTracks[i] == textTrack) + if (addTrack == &textTrack) return trackIndex; ++trackIndex; } - for (size_t i = 0; i < m_inbandTracks.size(); ++i) { - if (!toTextTrack(m_inbandTracks[i].get())->isRendered()) + for (auto& inbandTrack : m_inbandTracks) { + if (!downcast<TextTrack>(*inbandTrack).isRendered()) continue; - - if (m_inbandTracks[i] == textTrack) + if (inbandTrack == &textTrack) return trackIndex; ++trackIndex; } - ASSERT_NOT_REACHED(); - return -1; } @@ -114,17 +122,17 @@ TextTrack* TextTrackList::item(unsigned index) const // resource), in the order defined by the media resource's format specification. if (index < m_elementTracks.size()) - return toTextTrack(m_elementTracks[index].get()); + return downcast<TextTrack>(m_elementTracks[index].get()); index -= m_elementTracks.size(); if (index < m_addTrackTracks.size()) - return toTextTrack(m_addTrackTracks[index].get()); + return downcast<TextTrack>(m_addTrackTracks[index].get()); index -= m_addTrackTracks.size(); if (index < m_inbandTracks.size()) - return toTextTrack(m_inbandTracks[index].get()); + return downcast<TextTrack>(m_inbandTracks[index].get()); - return 0; + return nullptr; } TextTrack* TextTrackList::getTrackById(const AtomicString& id) @@ -134,108 +142,122 @@ TextTrack* TextTrackList::getTrackById(const AtomicString& id) // TextTrackList object whose id IDL attribute would return a value equal // to the value of the id argument. for (unsigned i = 0; i < length(); ++i) { - TextTrack* track = item(i); - if (track->id() == id) - return track; + auto& track = *item(i); + if (track.id() == id) + return &track; } // When no tracks match the given argument, the method must return null. return nullptr; } -void TextTrackList::invalidateTrackIndexesAfterTrack(TextTrack* track) +void TextTrackList::invalidateTrackIndexesAfterTrack(TextTrack& track) { - Vector<RefPtr<TrackBase>>* tracks = 0; + Vector<RefPtr<TrackBase>>* tracks = nullptr; - if (track->trackType() == TextTrack::TrackElement) { + switch (track.trackType()) { + case TextTrack::TrackElement: tracks = &m_elementTracks; - for (size_t i = 0; i < m_addTrackTracks.size(); ++i) - toTextTrack(m_addTrackTracks[i].get())->invalidateTrackIndex(); - for (size_t i = 0; i < m_inbandTracks.size(); ++i) - toTextTrack(m_inbandTracks[i].get())->invalidateTrackIndex(); - } else if (track->trackType() == TextTrack::AddTrack) { + for (auto& addTrack : m_addTrackTracks) + downcast<TextTrack>(addTrack.get())->invalidateTrackIndex(); + for (auto& inbandTrack : m_inbandTracks) + downcast<TextTrack>(inbandTrack.get())->invalidateTrackIndex(); + break; + case TextTrack::AddTrack: tracks = &m_addTrackTracks; - for (size_t i = 0; i < m_inbandTracks.size(); ++i) - toTextTrack(m_inbandTracks[i].get())->invalidateTrackIndex(); - } else if (track->trackType() == TextTrack::InBand) + for (auto& inbandTrack : m_inbandTracks) + downcast<TextTrack>(inbandTrack.get())->invalidateTrackIndex(); + break; + case TextTrack::InBand: tracks = &m_inbandTracks; - else + break; + default: ASSERT_NOT_REACHED(); + } - size_t index = tracks->find(track); + size_t index = tracks->find(&track); if (index == notFound) return; for (size_t i = index; i < tracks->size(); ++i) - toTextTrack(tracks->at(index).get())->invalidateTrackIndex(); + downcast<TextTrack>(*tracks->at(index)).invalidateTrackIndex(); } -void TextTrackList::append(PassRefPtr<TextTrack> prpTrack) +void TextTrackList::append(Ref<TextTrack>&& track) { - RefPtr<TextTrack> track = prpTrack; - if (track->trackType() == TextTrack::AddTrack) - m_addTrackTracks.append(track); - else if (track->trackType() == TextTrack::TrackElement) { + m_addTrackTracks.append(track.ptr()); + else if (is<LoadableTextTrack>(track.get())) { // Insert tracks added for <track> element in tree order. - size_t index = static_cast<LoadableTextTrack*>(track.get())->trackElementIndex(); - m_elementTracks.insert(index, track); + size_t index = downcast<LoadableTextTrack>(track.get()).trackElementIndex(); + m_elementTracks.insert(index, track.ptr()); } else if (track->trackType() == TextTrack::InBand) { // Insert tracks added for in-band in the media file order. - size_t index = static_cast<InbandTextTrack*>(track.get())->inbandTrackIndex(); - m_inbandTracks.insert(index, track); + size_t index = downcast<InbandTextTrack>(track.get()).inbandTrackIndex(); + m_inbandTracks.insert(index, track.ptr()); } else ASSERT_NOT_REACHED(); - invalidateTrackIndexesAfterTrack(track.get()); + invalidateTrackIndexesAfterTrack(track); ASSERT(!track->mediaElement() || track->mediaElement() == mediaElement()); track->setMediaElement(mediaElement()); - scheduleAddTrackEvent(track.release()); + scheduleAddTrackEvent(WTFMove(track)); } -void TextTrackList::remove(TrackBase* track) +void TextTrackList::remove(TrackBase& track, bool scheduleEvent) { - TextTrack* textTrack = toTextTrack(track); - Vector<RefPtr<TrackBase>>* tracks = 0; - if (textTrack->trackType() == TextTrack::TrackElement) + auto& textTrack = downcast<TextTrack>(track); + Vector<RefPtr<TrackBase>>* tracks = nullptr; + switch (textTrack.trackType()) { + case TextTrack::TrackElement: tracks = &m_elementTracks; - else if (textTrack->trackType() == TextTrack::AddTrack) + break; + case TextTrack::AddTrack: tracks = &m_addTrackTracks; - else if (textTrack->trackType() == TextTrack::InBand) + break; + case TextTrack::InBand: tracks = &m_inbandTracks; - else + break; + default: ASSERT_NOT_REACHED(); + } - size_t index = tracks->find(track); + size_t index = tracks->find(&track); if (index == notFound) return; invalidateTrackIndexesAfterTrack(textTrack); - ASSERT(!track->mediaElement() || track->mediaElement() == element()); - track->setMediaElement(0); + ASSERT(!track.mediaElement() || !element() || track.mediaElement() == element()); + track.setMediaElement(nullptr); - RefPtr<TrackBase> trackRef = (*tracks)[index]; + Ref<TrackBase> trackRef = *(*tracks)[index]; tracks->remove(index); - scheduleRemoveTrackEvent(trackRef.release()); + + if (scheduleEvent) + scheduleRemoveTrackEvent(WTFMove(trackRef)); } -bool TextTrackList::contains(TrackBase* track) const +bool TextTrackList::contains(TrackBase& track) const { - const Vector<RefPtr<TrackBase>>* tracks = 0; - TextTrack::TextTrackType type = toTextTrack(track)->trackType(); - if (type == TextTrack::TrackElement) + const Vector<RefPtr<TrackBase>>* tracks = nullptr; + switch (downcast<TextTrack>(track).trackType()) { + case TextTrack::TrackElement: tracks = &m_elementTracks; - else if (type == TextTrack::AddTrack) + break; + case TextTrack::AddTrack: tracks = &m_addTrackTracks; - else if (type == TextTrack::InBand) + break; + case TextTrack::InBand: tracks = &m_inbandTracks; - else + break; + default: ASSERT_NOT_REACHED(); + } - return tracks->find(track) != notFound; + return tracks->find(&track) != notFound; } EventTargetInterface TextTrackList::eventTargetInterface() const diff --git a/Source/WebCore/html/track/TextTrackList.h b/Source/WebCore/html/track/TextTrackList.h index 57c8b8288..c42f1e194 100644 --- a/Source/WebCore/html/track/TextTrackList.h +++ b/Source/WebCore/html/track/TextTrackList.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TextTrackList_h -#define TextTrackList_h +#pragma once #if ENABLE(VIDEO_TRACK) @@ -34,33 +33,35 @@ namespace WebCore { class TextTrack; -class TextTrackList : public TrackListBase { +class TextTrackList final : public TrackListBase { public: - static PassRefPtr<TextTrackList> create(HTMLMediaElement* element, ScriptExecutionContext* context) + static Ref<TextTrackList> create(HTMLMediaElement* element, ScriptExecutionContext* context) { - return adoptRef(new TextTrackList(element, context)); + return adoptRef(*new TextTrackList(element, context)); } virtual ~TextTrackList(); - virtual unsigned length() const override; - int getTrackIndex(TextTrack*); - int getTrackIndexRelativeToRenderedTracks(TextTrack*); - virtual bool contains(TrackBase*) const override; + void clearElement() override; + + unsigned length() const override; + int getTrackIndex(TextTrack&); + int getTrackIndexRelativeToRenderedTracks(TextTrack&); + bool contains(TrackBase&) const override; TextTrack* item(unsigned index) const; TextTrack* getTrackById(const AtomicString&); TextTrack* lastItem() const { return item(length() - 1); } - void append(PassRefPtr<TextTrack>); - virtual void remove(TrackBase*) override; + void append(Ref<TextTrack>&&); + void remove(TrackBase&, bool scheduleEvent = true) override; // EventTarget - virtual EventTargetInterface eventTargetInterface() const override; + EventTargetInterface eventTargetInterface() const override; private: TextTrackList(HTMLMediaElement*, ScriptExecutionContext*); - void invalidateTrackIndexesAfterTrack(TextTrack*); + void invalidateTrackIndexesAfterTrack(TextTrack&); Vector<RefPtr<TrackBase>> m_addTrackTracks; Vector<RefPtr<TrackBase>> m_elementTracks; @@ -68,5 +69,4 @@ private: } // namespace WebCore -#endif -#endif +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/html/track/TextTrackList.idl b/Source/WebCore/html/track/TextTrackList.idl index 10e514fe5..fedd73b55 100644 --- a/Source/WebCore/html/track/TextTrackList.idl +++ b/Source/WebCore/html/track/TextTrackList.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -26,23 +26,14 @@ [ Conditional=VIDEO_TRACK, GenerateIsReachable=ImplElementRoot, - EventTarget, JSCustomMarkFunction, -] interface TextTrackList { +] interface TextTrackList : EventTarget { readonly attribute unsigned long length; getter TextTrack item(unsigned long index); TextTrack getTrackById(DOMString id); - attribute EventListener onaddtrack; - attribute EventListener onchange; - attribute EventListener onremovetrack; - - void addEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - void removeEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - [RaisesException] boolean dispatchEvent(Event evt); + attribute EventHandler onaddtrack; + attribute EventHandler onchange; + attribute EventHandler onremovetrack; }; diff --git a/Source/WebCore/html/track/TrackBase.cpp b/Source/WebCore/html/track/TrackBase.cpp index e93e9e908..bc95111e1 100644 --- a/Source/WebCore/html/track/TrackBase.cpp +++ b/Source/WebCore/html/track/TrackBase.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -26,20 +26,23 @@ #include "config.h" #include "TrackBase.h" -#include "HTMLMediaElement.h" +#include "Language.h" +#include <wtf/text/StringBuilder.h> #if ENABLE(VIDEO_TRACK) +#include "HTMLMediaElement.h" + namespace WebCore { +static int s_uniqueId = 0; + TrackBase::TrackBase(Type type, const AtomicString& id, const AtomicString& label, const AtomicString& language) - : m_mediaElement(0) -#if ENABLE(MEDIA_SOURCE) - , m_sourceBuffer(0) -#endif + : m_uniqueId(++s_uniqueId) , m_id(id) , m_label(label) , m_language(language) + , m_validBCP47Language(language) { ASSERT(type != BaseTrack); m_type = type; @@ -54,19 +57,99 @@ Element* TrackBase::element() return m_mediaElement; } -void TrackBase::setKind(const AtomicString& kind) +// See: https://tools.ietf.org/html/bcp47#section-2.1 +static bool isValidBCP47LanguageTag(const String& languageTag) { - setKindInternal(kind); + auto const length = languageTag.length(); + + // Max length picked as double the longest example tag in spec which is 49 characters: + // https://tools.ietf.org/html/bcp47#section-4.4.2 + if (length < 2 || length > 100) + return false; + + UChar firstChar = languageTag[0]; + + if (!isASCIIAlpha(firstChar)) + return false; + + UChar secondChar = languageTag[1]; + + if (length == 2) + return isASCIIAlpha(secondChar); + + bool grandFatheredIrregularOrPrivateUse = (firstChar == 'i' || firstChar == 'x') && secondChar == '-'; + unsigned nextCharIndexToCheck; + + if (!grandFatheredIrregularOrPrivateUse) { + if (!isASCIIAlpha(secondChar)) + return false; + + if (length == 3) + return isASCIIAlpha(languageTag[2]); + + if (isASCIIAlpha(languageTag[2])) { + if (languageTag[3] == '-') + nextCharIndexToCheck = 4; + else + return false; + } else if (languageTag[2] == '-') + nextCharIndexToCheck = 3; + else + return false; + } else + nextCharIndexToCheck = 2; + + for (; nextCharIndexToCheck < length; ++nextCharIndexToCheck) { + UChar c = languageTag[nextCharIndexToCheck]; + if (isASCIIAlphanumeric(c) || c == '-') + continue; + return false; + } + return true; +} + +void TrackBase::setLanguage(const AtomicString& language) +{ + if (!language.isEmpty() && !isValidBCP47LanguageTag(language)) { + String message; + if (language.contains((UChar)'\0')) + message = WTF::ASCIILiteral("The language contains a null character and is not a valid BCP 47 language tag."); + else { + StringBuilder stringBuilder; + stringBuilder.appendLiteral("The language '"); + stringBuilder.append(language); + stringBuilder.appendLiteral("' is not a valid BCP 47 language tag."); + message = stringBuilder.toString(); + } + if (auto element = this->element()) + element->document().addConsoleMessage(MessageSource::Rendering, MessageLevel::Warning, message); + } else + m_validBCP47Language = language; + + m_language = language; } -void TrackBase::setKindInternal(const AtomicString& kind) +AtomicString TrackBase::validBCP47Language() const { - String oldKind = m_kind; + return m_validBCP47Language; +} +MediaTrackBase::MediaTrackBase(Type type, const AtomicString& id, const AtomicString& label, const AtomicString& language) + : TrackBase(type, id, label, language) +{ +} + +void MediaTrackBase::setKind(const AtomicString& kind) +{ + setKindInternal(kind); +} + +void MediaTrackBase::setKindInternal(const AtomicString& kind) +{ if (isValidKind(kind)) m_kind = kind; else - m_kind = defaultKindKeyword(); + m_kind = emptyAtom; } } // namespace WebCore diff --git a/Source/WebCore/html/track/TrackBase.h b/Source/WebCore/html/track/TrackBase.h index 509f7a61f..16d162a13 100644 --- a/Source/WebCore/html/track/TrackBase.h +++ b/Source/WebCore/html/track/TrackBase.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,13 +23,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TrackBase_h -#define TrackBase_h +#pragma once #if ENABLE(VIDEO_TRACK) -#include "EventTarget.h" -#include <wtf/RefCounted.h> +#include <wtf/text/AtomicString.h> namespace WebCore { @@ -51,17 +49,17 @@ public: virtual AtomicString id() const { return m_id; } virtual void setId(const AtomicString& id) { m_id = id; } - AtomicString kind() const { return m_kind; } - virtual void setKind(const AtomicString&); - AtomicString label() const { return m_label; } void setLabel(const AtomicString& label) { m_label = label; } + AtomicString validBCP47Language() const; AtomicString language() const { return m_language; } - virtual void setLanguage(const AtomicString& language) { m_language = language; } + virtual void setLanguage(const AtomicString&); virtual void clearClient() = 0; + virtual int uniqueId() const { return m_uniqueId; } + #if ENABLE(MEDIA_SOURCE) SourceBuffer* sourceBuffer() const { return m_sourceBuffer; } void setSourceBuffer(SourceBuffer* buffer) { m_sourceBuffer = buffer; } @@ -72,26 +70,37 @@ public: protected: TrackBase(Type, const AtomicString& id, const AtomicString& label, const AtomicString& language); - virtual bool isValidKind(const AtomicString&) const = 0; - virtual const AtomicString& defaultKindKeyword() const = 0; - - void setKindInternal(const AtomicString&); - - HTMLMediaElement* m_mediaElement; + HTMLMediaElement* m_mediaElement { nullptr }; #if ENABLE(MEDIA_SOURCE) - SourceBuffer* m_sourceBuffer; + SourceBuffer* m_sourceBuffer { nullptr }; #endif private: Type m_type; + int m_uniqueId; AtomicString m_id; - AtomicString m_kind; AtomicString m_label; AtomicString m_language; + AtomicString m_validBCP47Language; +}; + +class MediaTrackBase : public TrackBase { +public: + const AtomicString& kind() const { return m_kind; } + virtual void setKind(const AtomicString&); + +protected: + MediaTrackBase(Type, const AtomicString& id, const AtomicString& label, const AtomicString& language); + + void setKindInternal(const AtomicString&); + +private: + virtual bool isValidKind(const AtomicString&) const = 0; + + AtomicString m_kind; }; } // namespace WebCore #endif -#endif // TrackBase_h diff --git a/Source/WebCore/html/track/TrackEvent.cpp b/Source/WebCore/html/track/TrackEvent.cpp index 16eb4d8be..7da7d5117 100644 --- a/Source/WebCore/html/track/TrackEvent.cpp +++ b/Source/WebCore/html/track/TrackEvent.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -29,22 +29,34 @@ #include "TrackEvent.h" -#include "EventNames.h" - namespace WebCore { -TrackEventInit::TrackEventInit() +static inline std::optional<TrackEvent::TrackEventTrack> convertToTrackEventTrack(Ref<TrackBase>&& track) { + switch (track->type()) { + case TrackBase::BaseTrack: + return std::nullopt; + case TrackBase::TextTrack: + return TrackEvent::TrackEventTrack { RefPtr<TextTrack>(&downcast<TextTrack>(track.get())) }; + case TrackBase::AudioTrack: + return TrackEvent::TrackEventTrack { RefPtr<AudioTrack>(&downcast<AudioTrack>(track.get())) }; + case TrackBase::VideoTrack: + return TrackEvent::TrackEventTrack { RefPtr<VideoTrack>(&downcast<VideoTrack>(track.get())) }; + } + + ASSERT_NOT_REACHED(); + return std::nullopt; } - -TrackEvent::TrackEvent() +TrackEvent::TrackEvent(const AtomicString& type, bool canBubble, bool cancelable, Ref<TrackBase>&& track) + : Event(type, canBubble, cancelable) + , m_track(convertToTrackEventTrack(WTFMove(track))) { } -TrackEvent::TrackEvent(const AtomicString& type, const TrackEventInit& initializer) - : Event(type, initializer) - , m_track(initializer.track) +TrackEvent::TrackEvent(const AtomicString& type, Init&& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) + , m_track(WTFMove(initializer.track)) { } diff --git a/Source/WebCore/html/track/TrackEvent.h b/Source/WebCore/html/track/TrackEvent.h index 3c7e43a45..8432633d2 100644 --- a/Source/WebCore/html/track/TrackEvent.h +++ b/Source/WebCore/html/track/TrackEvent.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,48 +23,48 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TrackEvent_h -#define TrackEvent_h +#pragma once #if ENABLE(VIDEO_TRACK) +#include "AudioTrack.h" #include "Event.h" -#include "TrackBase.h" +#include "TextTrack.h" +#include "VideoTrack.h" namespace WebCore { -struct TrackEventInit : public EventInit { - TrackEventInit(); - - RefPtr<TrackBase> track; -}; - -class TrackEvent : public Event { +class TrackEvent final : public Event { public: virtual ~TrackEvent(); - static PassRefPtr<TrackEvent> create() + static Ref<TrackEvent> create(const AtomicString& type, bool canBubble, bool cancelable, Ref<TrackBase>&& track) { - return adoptRef(new TrackEvent); + return adoptRef(*new TrackEvent(type, canBubble, cancelable, WTFMove(track))); } - static PassRefPtr<TrackEvent> create(const AtomicString& type, const TrackEventInit& initializer) + using TrackEventTrack = Variant<RefPtr<VideoTrack>, RefPtr<AudioTrack>, RefPtr<TextTrack>>; + + struct Init : public EventInit { + std::optional<TrackEventTrack> track; + }; + + static Ref<TrackEvent> create(const AtomicString& type, Init&& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new TrackEvent(type, initializer)); + return adoptRef(*new TrackEvent(type, WTFMove(initializer), isTrusted)); } - virtual EventInterface eventInterface() const override; - - TrackBase* track() const { return m_track.get(); } + std::optional<TrackEventTrack> track() const { return m_track; } private: - TrackEvent(); - TrackEvent(const AtomicString& type, const TrackEventInit& initializer); + TrackEvent(const AtomicString& type, bool canBubble, bool cancelable, Ref<TrackBase>&&); + TrackEvent(const AtomicString& type, Init&& initializer, IsTrusted); + + EventInterface eventInterface() const override; - RefPtr<TrackBase> m_track; + std::optional<TrackEventTrack> m_track; }; } // namespace WebCore -#endif -#endif +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/html/track/TrackEvent.idl b/Source/WebCore/html/track/TrackEvent.idl index 576761e1a..8a9330462 100644 --- a/Source/WebCore/html/track/TrackEvent.idl +++ b/Source/WebCore/html/track/TrackEvent.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -25,8 +25,11 @@ [ Conditional=VIDEO_TRACK, - ConstructorTemplate=Event + Constructor(DOMString type, optional TrackEventInit eventInitDict) ] interface TrackEvent : Event { - [InitializedByEventConstructor, CustomGetter] readonly attribute object track; + readonly attribute (VideoTrack or AudioTrack or TextTrack)? track; }; +dictionary TrackEventInit : EventInit { + (VideoTrack or AudioTrack or TextTrack)? track = null; +}; diff --git a/Source/WebCore/html/track/TrackListBase.cpp b/Source/WebCore/html/track/TrackListBase.cpp index 7fb47b68d..ef892cbb5 100644 --- a/Source/WebCore/html/track/TrackListBase.cpp +++ b/Source/WebCore/html/track/TrackListBase.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -46,6 +46,16 @@ TrackListBase::TrackListBase(HTMLMediaElement* element, ScriptExecutionContext* TrackListBase::~TrackListBase() { + clearElement(); +} + +void TrackListBase::clearElement() +{ + m_element = nullptr; + for (auto& track : m_inbandTracks) { + track->setMediaElement(nullptr); + track->clearClient(); + } } Element* TrackListBase::element() const @@ -58,37 +68,36 @@ unsigned TrackListBase::length() const return m_inbandTracks.size(); } -void TrackListBase::remove(TrackBase* track) +void TrackListBase::remove(TrackBase& track, bool scheduleEvent) { - size_t index = m_inbandTracks.find(track); - ASSERT(index != notFound); + size_t index = m_inbandTracks.find(&track); + if (index == notFound) + return; - ASSERT(track->mediaElement() == m_element); - track->setMediaElement(0); + if (track.mediaElement()) { + ASSERT(track.mediaElement() == m_element); + track.setMediaElement(nullptr); + } - RefPtr<TrackBase> trackRef = m_inbandTracks[index]; + Ref<TrackBase> trackRef = *m_inbandTracks[index]; m_inbandTracks.remove(index); - scheduleRemoveTrackEvent(trackRef.release()); + if (scheduleEvent) + scheduleRemoveTrackEvent(WTFMove(trackRef)); } -bool TrackListBase::contains(TrackBase* track) const +bool TrackListBase::contains(TrackBase& track) const { - return m_inbandTracks.find(track) != notFound; + return m_inbandTracks.find(&track) != notFound; } -void TrackListBase::scheduleTrackEvent(const AtomicString& eventName, PassRefPtr<TrackBase> track) +void TrackListBase::scheduleTrackEvent(const AtomicString& eventName, Ref<TrackBase>&& track) { - TrackEventInit initializer; - initializer.track = track; - initializer.bubbles = false; - initializer.cancelable = false; - - m_asyncEventQueue.enqueueEvent(TrackEvent::create(eventName, initializer)); + m_asyncEventQueue.enqueueEvent(TrackEvent::create(eventName, false, false, WTFMove(track))); } -void TrackListBase::scheduleAddTrackEvent(PassRefPtr<TrackBase> track) +void TrackListBase::scheduleAddTrackEvent(Ref<TrackBase>&& track) { // 4.8.10.5 Loading the media resource // ... @@ -108,10 +117,10 @@ void TrackListBase::scheduleAddTrackEvent(PassRefPtr<TrackBase> track) // bubble and is not cancelable, and that uses the TrackEvent interface, with // the track attribute initialized to the text track's TextTrack object, at // the media element's textTracks attribute's TextTrackList object. - scheduleTrackEvent(eventNames().addtrackEvent, track); + scheduleTrackEvent(eventNames().addtrackEvent, WTFMove(track)); } -void TrackListBase::scheduleRemoveTrackEvent(PassRefPtr<TrackBase> track) +void TrackListBase::scheduleRemoveTrackEvent(Ref<TrackBase>&& track) { // 4.8.10.6 Offsets into the media resource // If at any time the user agent learns that an audio or video track has @@ -135,7 +144,7 @@ void TrackListBase::scheduleRemoveTrackEvent(PassRefPtr<TrackBase> track) // interface, with the track attribute initialized to the text track's // TextTrack object, at the media element's textTracks attribute's // TextTrackList object. - scheduleTrackEvent(eventNames().removetrackEvent, track); + scheduleTrackEvent(eventNames().removetrackEvent, WTFMove(track)); } void TrackListBase::scheduleChangeEvent() @@ -148,18 +157,12 @@ void TrackListBase::scheduleChangeEvent() // Whenever a track in a VideoTrackList that was previously not selected is // selected, the user agent must queue a task to fire a simple event named // change at the VideoTrackList object. - - EventInit initializer; - initializer.bubbles = false; - initializer.cancelable = false; - - m_asyncEventQueue.enqueueEvent(Event::create(eventNames().changeEvent, initializer)); + m_asyncEventQueue.enqueueEvent(Event::create(eventNames().changeEvent, false, false)); } bool TrackListBase::isAnyTrackEnabled() const { - for (size_t i = 0; i < m_inbandTracks.size(); ++i) { - TrackBase* track = m_inbandTracks[i].get(); + for (auto& track : m_inbandTracks) { if (track->enabled()) return true; } diff --git a/Source/WebCore/html/track/TrackListBase.h b/Source/WebCore/html/track/TrackListBase.h index 87c86a479..9573a2131 100644 --- a/Source/WebCore/html/track/TrackListBase.h +++ b/Source/WebCore/html/track/TrackListBase.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TrackListBase_h -#define TrackListBase_h +#pragma once #if ENABLE(VIDEO_TRACK) @@ -32,7 +31,6 @@ #include "EventTarget.h" #include "GenericEventQueue.h" #include "Timer.h" -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/Vector.h> @@ -47,20 +45,16 @@ public: virtual ~TrackListBase(); virtual unsigned length() const; - virtual bool contains(TrackBase*) const; - virtual void remove(TrackBase*); + virtual bool contains(TrackBase&) const; + virtual void remove(TrackBase&, bool scheduleEvent = true); // EventTarget - virtual EventTargetInterface eventTargetInterface() const = 0; + EventTargetInterface eventTargetInterface() const override = 0; using RefCounted<TrackListBase>::ref; using RefCounted<TrackListBase>::deref; - virtual ScriptExecutionContext* scriptExecutionContext() const override final { return m_context; } + ScriptExecutionContext* scriptExecutionContext() const final { return m_context; } - DEFINE_ATTRIBUTE_EVENT_LISTENER(addtrack); - DEFINE_ATTRIBUTE_EVENT_LISTENER(change); - DEFINE_ATTRIBUTE_EVENT_LISTENER(removetrack); - - void clearElement() { m_element = 0; } + virtual void clearElement(); Element* element() const; HTMLMediaElement* mediaElement() const { return m_element; } @@ -72,17 +66,17 @@ public: protected: TrackListBase(HTMLMediaElement*, ScriptExecutionContext*); - void scheduleAddTrackEvent(PassRefPtr<TrackBase>); - void scheduleRemoveTrackEvent(PassRefPtr<TrackBase>); + void scheduleAddTrackEvent(Ref<TrackBase>&&); + void scheduleRemoveTrackEvent(Ref<TrackBase>&&); Vector<RefPtr<TrackBase>> m_inbandTracks; private: - void scheduleTrackEvent(const AtomicString& eventName, PassRefPtr<TrackBase>); + void scheduleTrackEvent(const AtomicString& eventName, Ref<TrackBase>&&); // EventTarget - virtual void refEventTarget() override final { ref(); } - virtual void derefEventTarget() override final { deref(); } + void refEventTarget() final { ref(); } + void derefEventTarget() final { deref(); } ScriptExecutionContext* m_context; HTMLMediaElement* m_element; @@ -92,5 +86,4 @@ private: } // namespace WebCore -#endif -#endif +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/html/track/VTTCue.cpp b/Source/WebCore/html/track/VTTCue.cpp new file mode 100644 index 000000000..4558582b4 --- /dev/null +++ b/Source/WebCore/html/track/VTTCue.cpp @@ -0,0 +1,1180 @@ +/* + * Copyright (C) 2011, 2013 Google Inc. All rights reserved. + * Copyright (C) 2011-2014 Apple 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 "VTTCue.h" + +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" +#include "DocumentFragment.h" +#include "Event.h" +#include "ExceptionCode.h" +#include "HTMLDivElement.h" +#include "HTMLSpanElement.h" +#include "Logging.h" +#include "NoEventDispatchAssertion.h" +#include "NodeTraversal.h" +#include "RenderVTTCue.h" +#include "Text.h" +#include "TextTrack.h" +#include "TextTrackCueList.h" +#include "VTTRegionList.h" +#include "VTTScanner.h" +#include "WebVTTElement.h" +#include "WebVTTParser.h" +#include <wtf/MathExtras.h> +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +// This constant should correspond with the percentage returned by CaptionUserPreferences::captionFontSizeScaleAndImportance. +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() +{ + static NeverDestroyed<const String> start(ASCIILiteral("start")); + return start; +} + +static const String& middleKeyword() +{ + static NeverDestroyed<const String> middle(ASCIILiteral("middle")); + return middle; +} + +static const String& endKeyword() +{ + static NeverDestroyed<const String> end(ASCIILiteral("end")); + return end; +} + +static const String& leftKeyword() +{ + static NeverDestroyed<const String> left("left"); + return left; +} + +static const String& rightKeyword() +{ + static NeverDestroyed<const String> right("right"); + return right; +} + +static const String& horizontalKeyword() +{ + return emptyString(); +} + +static const String& verticalGrowingLeftKeyword() +{ + static NeverDestroyed<const String> verticalrl(ASCIILiteral("rl")); + return verticalrl; +} + +static const String& verticalGrowingRightKeyword() +{ + static NeverDestroyed<const String> verticallr(ASCIILiteral("lr")); + return verticallr; +} + +// ---------------------------- + +Ref<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) +{ + // FIXME: Apply all the initial CSS positioning properties. http://wkb.ug/79916 + if (!m_cue.regionId().isEmpty()) { + setInlineStyleProperty(CSSPropertyPosition, CSSValueRelative); + return; + } + + // 3.5.1 On the (root) List of WebVTT Node Objects: + + // the 'position' property must be set to 'absolute' + setInlineStyleProperty(CSSPropertyPosition, CSSValueAbsolute); + + // the 'unicode-bidi' property must be set to 'plaintext' + setInlineStyleProperty(CSSPropertyUnicodeBidi, CSSValueWebkitPlaintext); + + // the 'direction' property must be set to direction + setInlineStyleProperty(CSSPropertyDirection, m_cue.getCSSWritingDirection()); + + // the 'writing-mode' property must be set to writing-mode + setInlineStyleProperty(CSSPropertyWritingMode, m_cue.getCSSWritingMode(), false); + + std::pair<float, float> position = m_cue.getCSSPosition(); + + // the 'top' property must be set to top, + setInlineStyleProperty(CSSPropertyTop, static_cast<double>(position.second), CSSPrimitiveValue::CSS_PERCENTAGE); + + // the 'left' property must be set to left + 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); + // the 'width' property must be set to width, and the 'height' property must be set to height + 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); + } + + // The 'text-align' property on the (root) List of WebVTT Node Objects must + // be set to the value in the second cell of the row of the table below + // whose first cell is the value of the corresponding cue's text track cue + // alignment: + setInlineStyleProperty(CSSPropertyTextAlign, m_cue.getCSSAlignment()); + + if (!m_cue.snapToLines()) { + // 10.13.1 Set up x and y: + // Note: x and y are set through the CSS left and top above. + + // 10.13.2 Position the boxes in boxes such that the point x% along the + // width of the bounding box of the boxes in boxes is x% of the way + // across the width of the video's rendering area, and the point y% + // along the height of the bounding box of the boxes in boxes is y% + // of the way across the height of the video's rendering area, while + // maintaining the relative positions of the boxes in boxes to each + // other. + setInlineStyleProperty(CSSPropertyTransform, + String::format("translate(-%.2f%%, -%.2f%%)", position.first, position.second)); + + setInlineStyleProperty(CSSPropertyWhiteSpace, CSSValuePre); + } + + // Make sure shadow or stroke is not clipped. + setInlineStyleProperty(CSSPropertyOverflow, CSSValueVisible); + m_cue.element().setInlineStyleProperty(CSSPropertyOverflow, CSSValueVisible); +} + +const AtomicString& VTTCueBox::vttCueBoxShadowPseudoId() +{ + static NeverDestroyed<const AtomicString> trackDisplayBoxShadowPseudoId("-webkit-media-text-track-display", AtomicString::ConstructFromLiteral); + return trackDisplayBoxShadowPseudoId; +} + +RenderPtr<RenderElement> VTTCueBox::createElementRenderer(RenderStyle&& style, const RenderTreePosition&) +{ + return createRenderer<RenderVTTCue>(*this, WTFMove(style)); +} + +// ---------------------------- + +const AtomicString& VTTCue::cueBackdropShadowPseudoId() +{ + static NeverDestroyed<const AtomicString> cueBackdropShadowPseudoId("-webkit-media-text-track-display-backdrop", AtomicString::ConstructFromLiteral); + return cueBackdropShadowPseudoId; +} + +Ref<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() +{ + // FIXME: We should set m_cue in VTTCueBox to nullptr instead. + if (m_displayTree && m_displayTree->document().refCount()) + m_displayTree->remove(); +} + +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(downcast<Document>(context)); + m_cueHighlightBox = HTMLSpanElement::create(spanTag, downcast<Document>(context)); + m_displayDirection = CSSValueLtr; + m_displaySize = 0; + m_snapToLines = true; + m_displayTreeShouldChange = true; + m_notifyRegion = true; + m_originalStartTime = MediaTime::zeroTime(); +} + +Ref<VTTCueBox> VTTCue::createDisplayTree() +{ + return VTTCueBox::create(ownerDocument(), *this); +} + +VTTCueBox& VTTCue::displayTreeInternal() +{ + if (!m_displayTree) + m_displayTree = createDisplayTree(); + return *m_displayTree; +} + +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(); + } +} + +ExceptionOr<void> VTTCue::setVertical(const String& value) +{ + // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-vertical + // 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. + + WritingDirection direction = m_writingDirection; + if (value == horizontalKeyword()) + direction = Horizontal; + else if (value == verticalGrowingLeftKeyword()) + direction = VerticalGrowingLeft; + else if (value == verticalGrowingRightKeyword()) + direction = VerticalGrowingRight; + else + return Exception { SYNTAX_ERR }; + + if (direction == m_writingDirection) + return { }; + + willChange(); + m_writingDirection = direction; + didChange(); + + return { }; +} + +void VTTCue::setSnapToLines(bool value) +{ + if (m_snapToLines == value) + return; + + willChange(); + m_snapToLines = value; + didChange(); +} + +ExceptionOr<void> VTTCue::setLine(double position) +{ + // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-line + // 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)) + return Exception { INDEX_SIZE_ERR }; + + // Otherwise, set the text track cue line position to the new value. + if (m_linePosition == position) + return { }; + + willChange(); + m_linePosition = position; + m_computedLinePosition = calculateComputedLinePosition(); + didChange(); + + return { }; +} + +ExceptionOr<void> VTTCue::setPosition(double position) +{ + // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-position + // 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)) + return Exception { INDEX_SIZE_ERR }; + + // Otherwise, set the text track cue line position to the new value. + if (m_textPosition == position) + return { }; + + willChange(); + m_textPosition = position; + didChange(); + + return { }; +} + +ExceptionOr<void> VTTCue::setSize(int size) +{ + // 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)) + return Exception { INDEX_SIZE_ERR }; + + // Otherwise, set the text track cue line position to the new value. + if (m_cueSize == size) + return { }; + + willChange(); + m_cueSize = size; + didChange(); + + return { }; +} + +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(); + } +} + +ExceptionOr<void> VTTCue::setAlign(const String& value) +{ + // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#dom-texttrackcue-align + // 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. + + CueAlignment alignment; + 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 + return Exception { SYNTAX_ERR }; + + if (alignment == m_cueAlignment) + return { }; + + willChange(); + m_cueAlignment = alignment; + didChange(); + + return { }; +} + +void VTTCue::setText(const String& text) +{ + if (m_content == text) + return; + + willChange(); + // Clear the document fragment but don't bother to create it again just yet as we can do that + // when it is requested. + m_webVTTNodeTree = nullptr; + 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 (is<WebVTTElement>(*node)) + clonedNode = downcast<WebVTTElement>(*node).createEquivalentHTMLElement(ownerDocument()); + else + clonedNode = node->cloneNode(false); + parent->appendChild(*clonedNode); + if (is<ContainerNode>(*node)) + copyWebVTTNodeToDOMTree(downcast<ContainerNode>(node), downcast<ContainerNode>(clonedNode.get())); + } +} + +RefPtr<DocumentFragment> VTTCue::getCueAsHTML() +{ + createWebVTTNodeTree(); + if (!m_webVTTNodeTree) + return nullptr; + + auto clonedFragment = DocumentFragment::create(ownerDocument()); + copyWebVTTNodeToDOMTree(m_webVTTNodeTree.get(), clonedFragment.ptr()); + return WTFMove(clonedFragment); +} + +RefPtr<DocumentFragment> VTTCue::createCueRenderingTree() +{ + createWebVTTNodeTree(); + if (!m_webVTTNodeTree) + return nullptr; + + auto clonedFragment = DocumentFragment::create(ownerDocument()); + + // The cloned fragment is never exposed to author scripts so it's safe to dispatch events here. + NoEventDispatchAssertion::EventAllowedScope noEventDispatchAssertionDisabledForScope(clonedFragment); + + m_webVTTNodeTree->cloneChildNodes(clonedFragment); + return WTFMove(clonedFragment); +} + +void VTTCue::setRegionId(const String& regionId) +{ + if (m_regionId == regionId) + return; + + willChange(); + m_regionId = regionId; + didChange(); +} + +void VTTCue::notifyRegionWhenRemovingDisplayTree(bool notifyRegion) +{ + m_notifyRegion = notifyRegion; +} + +void VTTCue::setIsActive(bool active) +{ + TextTrackCue::setIsActive(active); + + if (!active) { + if (!hasDisplayTree()) + return; + + // Remove the display tree as soon as the cue becomes inactive. + removeDisplayTree(); + } +} + +int VTTCue::calculateComputedLinePosition() +{ + // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-computed-line-position + + // If the text track cue line position is numeric, then that is the text + // track cue computed line position. + if (m_linePosition != undefinedPosition) + return m_linePosition; + + // If the text track cue snap-to-lines flag of the text track cue is not + // set, the text track cue computed line position is the value 100; + if (!m_snapToLines) + return 100; + + // Otherwise, it is the value returned by the following algorithm: + + // If cue is not associated with a text track, return -1 and abort these + // steps. + if (!track()) + return -1; + + // Let n be the number of text tracks whose text track mode is showing or + // showing by default and that are in the media element's list of text + // tracks before track. + int n = track()->trackIndexRelativeToRenderedTracks(); + + // Increment n by one. + n++; + + // Negate n. + n = -n; + + return n; +} + +static bool isCueParagraphSeparator(UChar character) +{ + // Within a cue, paragraph boundaries are only denoted by Type B characters, + // such as U+000A LINE FEED (LF), U+0085 NEXT LINE (NEL), and U+2029 PARAGRAPH SEPARATOR. + return u_charType(character) == U_PARAGRAPH_SEPARATOR; +} + +void VTTCue::determineTextDirection() +{ + static NeverDestroyed<const String> rtTag(ASCIILiteral("rt")); + createWebVTTNodeTree(); + if (!m_webVTTNodeTree) + return; + + // Apply the Unicode Bidirectional Algorithm's Paragraph Level steps to the + // concatenation of the values of each WebVTT Text Object in nodes, in a + // pre-order, depth-first traversal, excluding WebVTT Ruby Text Objects and + // their descendants. + StringBuilder paragraphBuilder; + for (Node* node = m_webVTTNodeTree->firstChild(); node; node = NodeTraversal::next(*node, m_webVTTNodeTree.get())) { + // FIXME: The code does not match the comment above. This does not actually exclude Ruby Text Object descendant. + 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() +{ + // Steps 10.2, 10.3 + determineTextDirection(); + + // 10.4 If the text track cue writing direction is horizontal, then let + // block-flow be 'tb'. Otherwise, if the text track cue writing direction is + // vertical growing left, then let block-flow be 'lr'. Otherwise, the text + // track cue writing direction is vertical growing right; let block-flow be + // 'rl'. + + // The above step is done through the writing direction static map. + + // 10.5 Determine the value of maximum size for cue as per the appropriate + // rules from the following list: + 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(); + + // 10.6 If the text track cue size is less than maximum size, then let size + // be text track cue size. Otherwise, let size be maximum size. + m_displaySize = std::min(m_cueSize, maximumSize); + + // FIXME: Understand why step 10.7 is missing (just a copy/paste error?) + // Could be done within a spec implementation check - http://crbug.com/301580 + + // 10.8 Determine the value of x-position or y-position for cue as per the + // appropriate rules from the following list: + 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(); + } + } + + // A text track cue has a text track cue computed line position whose value + // is defined in terms of the other aspects of the cue. + m_computedLinePosition = calculateComputedLinePosition(); + + // 10.9 Determine the value of whichever of x-position or y-position is not + // yet calculated for cue as per the appropriate rules from the following + // list: + 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) +{ + static NeverDestroyed<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 (is<WebVTTElement>(*child)) { + downcast<WebVTTElement>(*child).setIsPastNode(isPastNode); + // Make an elemenet id match a cue id for style matching purposes. + if (!id().isEmpty()) + downcast<WebVTTElement>(*child).setIdAttribute(id()); + } + } +} + +void VTTCue::updateDisplayTree(const MediaTime& movieTime) +{ + // The display tree may contain WebVTT timestamp objects representing + // timestamps (processing instructions), along with displayable nodes. + + if (!track()->isRendered()) + return; + + // Mutating the VTT contents is safe because it's never exposed to author scripts. + NoEventDispatchAssertion::EventAllowedScope allowedScopeForCueHighlightBox(*m_cueHighlightBox); + + // Clear the contents of the set. + m_cueHighlightBox->removeChildren(); + + // Update the two sets containing past and future WebVTT objects. + RefPtr<DocumentFragment> referenceTree = createCueRenderingTree(); + if (!referenceTree) + return; + + NoEventDispatchAssertion::EventAllowedScope allowedScopeForReferenceTree(*referenceTree); + + markFutureAndPastNodes(referenceTree.get(), startMediaTime(), movieTime); + m_cueHighlightBox->appendChild(*referenceTree); +} + +VTTCueBox& VTTCue::getDisplayTree(const IntSize& videoSize, int fontSize) +{ + Ref<VTTCueBox> displayTree = displayTreeInternal(); + if (!m_displayTreeShouldChange || !track()->isRendered()) + return displayTree.get(); + + // 10.1 - 10.10 + calculateDisplayParameters(); + + // 10.11. Apply the terms of the CSS specifications to nodes within the + // following constraints, thus obtaining a set of CSS boxes positioned + // relative to an initial containing block: + displayTree->removeChildren(); + + // The document tree is the tree of WebVTT Node Objects rooted at nodes. + + // The children of the nodes must be wrapped in an anonymous box whose + // 'display' property has the value 'inline'. This is the WebVTT cue + // background box. + + // Note: This is contained by default in m_cueHighlightBox. + m_cueHighlightBox->setPseudo(cueShadowPseudoId()); + + m_cueBackdropBox->setPseudo(cueBackdropShadowPseudoId()); + m_cueBackdropBox->appendChild(*m_cueHighlightBox); + displayTree->appendChild(*m_cueBackdropBox); + + // FIXME(BUG 79916): Runs of children of WebVTT Ruby Objects that are not + // WebVTT Ruby Text Objects must be wrapped in anonymous boxes whose + // 'display' property has the value 'ruby-base'. + + displayTree->setFontSizeFromCaptionUserPrefs(fontSize); + displayTree->applyCSSProperties(videoSize); + + m_displayTreeShouldChange = false; + + // 10.15. Let cue's text track cue display state have the CSS boxes in + // boxes. + return displayTree.get(); +} + +void VTTCue::removeDisplayTree() +{ + // The region needs to be informed about the cue removal. + if (m_notifyRegion && track()) { + if (VTTRegionList* regions = track()->regions()) { + if (VTTRegion* region = regions->getRegionById(m_regionId)) + if (hasDisplayTree()) + region->willRemoveTextTrackCueBox(m_displayTree.get()); + } + } + + if (!hasDisplayTree()) + return; + displayTreeInternal().remove(); +} + +std::pair<double, double> VTTCue::getPositionCoordinates() const +{ + // This method is used for setting x and y when snap to lines is not set. + 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; + else if (input.scan("region")) + parsedSetting = RegionId; + + // Verify that a ':' follows. + 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()) { + + // The WebVTT cue settings part of a WebVTT cue consists of zero or more of the following components, in any order, + // separated from each other by one or more U+0020 SPACE characters or U+0009 CHARACTER TABULATION (tab) characters. + input.skipWhile<WebVTTParser::isValidSettingDelimiter>(); + if (input.isAtEnd()) + break; + + // When the user agent is to parse the WebVTT settings given by a string input for a text track cue cue, + // the user agent must run the following steps: + // 1. Let settings be the result of splitting input on spaces. + // 2. For each token setting in the list settings, run the following substeps: + // 1. If setting does not contain a U+003A COLON character (:), or if the first U+003A COLON character (:) + // in setting is either the first or last character of setting, then jump to the step labeled next setting. + // 2. Let name be the leading substring of setting up to and excluding the first U+003A COLON character (:) in that string. + CueSetting name = settingName(input); + + // 3. Let value be the trailing substring of setting starting from the character immediately after the first U+003A COLON character (:) in that string. + VTTScanner::Run valueRun = input.collectUntil<WebVTTParser::isValidSettingDelimiter>(); + + // 4. Run the appropriate substeps that apply for the value of name, as follows: + switch (name) { + case Vertical: { + // If name is a case-sensitive match for "vertical" + // 1. If value is a case-sensitive match for the string "rl", then let cue's text track cue writing direction + // be vertical growing left. + if (input.scanRun(valueRun, verticalGrowingLeftKeyword())) + m_writingDirection = VerticalGrowingLeft; + + // 2. Otherwise, if value is a case-sensitive match for the string "lr", then let cue's text track cue writing + // direction be vertical growing right. + else if (input.scanRun(valueRun, verticalGrowingRightKeyword())) + m_writingDirection = VerticalGrowingRight; + + else + LOG(Media, "VTTCue::setCueSettings, invalid Vertical"); + break; + } + case Line: { + bool isValid = false; + do { + // 1-2 - Collect chars that are either '-', '%', or a digit. + // 1. If value contains any characters other than U+002D HYPHEN-MINUS characters (-), U+0025 PERCENT SIGN + // characters (%), and characters in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9), then jump + // to the step labeled next setting. + float linePosition; + bool isNegative; + if (!input.scanFloat(linePosition, &isNegative)) + break; + + bool isPercentage = input.scan('%'); + if (!input.isAt(valueRun.end())) + break; + + // 2. If value does not contain at least one character in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT + // NINE (9), then jump to the step labeled next setting. + // 3. If any character in value other than the first character is a U+002D HYPHEN-MINUS character (-), then + // jump to the step labeled next setting. + // 4. If any character in value other than the last character is a U+0025 PERCENT SIGN character (%), then + // jump to the step labeled next setting. + // 5. If the first character in value is a U+002D HYPHEN-MINUS character (-) and the last character in value is a + // U+0025 PERCENT SIGN character (%), then jump to the step labeled next setting. + if (isPercentage && isNegative) + break; + + // 6. Ignoring the trailing percent sign, if any, interpret value as a (potentially signed) integer, and + // let number be that number. + // 7. If the last character in value is a U+0025 PERCENT SIGN character (%), but number is not in the range + // 0 ≤ number ≤ 100, then jump to the step labeled next setting. + // 8. Let cue's text track cue line position be number. + // 9. If the last character in value is a U+0025 PERCENT SIGN character (%), then let cue's text track cue + // snap-to-lines flag be false. Otherwise, let it be true. + if (isPercentage) { + if (linePosition < 0 || linePosition > 100) + break; + + // 10 - If '%' then set snap-to-lines flag to false. + 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: { + // 1. If value is a case-sensitive match for the string "start", then let cue's text track cue alignment be start alignment. + if (input.scanRun(valueRun, startKeyword())) + m_cueAlignment = Start; + + // 2. If value is a case-sensitive match for the string "middle", then let cue's text track cue alignment be middle alignment. + else if (input.scanRun(valueRun, middleKeyword())) + m_cueAlignment = Middle; + + // 3. If value is a case-sensitive match for the string "end", then let cue's text track cue alignment be end alignment. + else if (input.scanRun(valueRun, endKeyword())) + m_cueAlignment = End; + + // 4. If value is a case-sensitive match for the string "left", then let cue's text track cue alignment be left alignment. + else if (input.scanRun(valueRun, leftKeyword())) + m_cueAlignment = Left; + + // 5. If value is a case-sensitive match for the string "right", then let cue's text track cue alignment be right alignment. + else if (input.scanRun(valueRun, rightKeyword())) + m_cueAlignment = Right; + + else + LOG(Media, "VTTCue::setCueSettings, invalid Align"); + + break; + } + case RegionId: + m_regionId = input.extractString(valueRun); + break; + case None: + break; + } + + // Make sure the entire run is consumed. + input.skipRun(valueRun); + } + + // If cue's line position is not auto or cue's size is not 100 or cue's + // writing direction is not horizontal, but cue's region identifier is not + // the empty string, let cue's region identifier be the empty string. + if (m_regionId.isEmpty()) + return; + + if (m_linePosition != undefinedPosition || m_cueSize != 100 || m_writingDirection != Horizontal) + m_regionId = emptyString(); +} + +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); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/track/VTTCue.h b/Source/WebCore/html/track/VTTCue.h new file mode 100644 index 000000000..5aa061f43 --- /dev/null +++ b/Source/WebCore/html/track/VTTCue.h @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2011, 2013 Google Inc. All rights reserved. + * Copyright (C) 2012-2014 Apple 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. + */ + +#pragma once + +#if ENABLE(VIDEO_TRACK) + +#include "EventTarget.h" +#include "HTMLElement.h" +#include "TextTrackCue.h" + +namespace WebCore { + +class DocumentFragment; +class HTMLDivElement; +class HTMLSpanElement; +class ScriptExecutionContext; +class VTTCue; +class VTTScanner; +class WebVTTCueData; + +// ---------------------------- + +class VTTCueBox : public HTMLElement { +public: + static Ref<VTTCueBox> create(Document&, VTTCue&); + + VTTCue* getCue() const; + virtual void applyCSSProperties(const IntSize& videoSize); + + static const AtomicString& vttCueBoxShadowPseudoId(); + void setFontSizeFromCaptionUserPrefs(int fontSize) { m_fontSizeFromCaptionUserPrefs = fontSize; } + +protected: + VTTCueBox(Document&, VTTCue&); + + RenderPtr<RenderElement> createElementRenderer(RenderStyle&&, const RenderTreePosition&) final; + + VTTCue& m_cue; + int m_fontSizeFromCaptionUserPrefs; +}; + +// ---------------------------- + +class VTTCue : public TextTrackCue { +public: + static Ref<VTTCue> create(ScriptExecutionContext& context, double start, double end, const String& content) + { + return create(context, MediaTime::createWithDouble(start), MediaTime::createWithDouble(end), content); + } + + static Ref<VTTCue> create(ScriptExecutionContext& context, const MediaTime& start, const MediaTime& end, const String& content) + { + return adoptRef(*new VTTCue(context, start, end, content)); + } + + static Ref<VTTCue> create(ScriptExecutionContext&, const WebVTTCueData&); + + static const AtomicString& cueBackdropShadowPseudoId(); + + virtual ~VTTCue(); + + const String& vertical() const; + ExceptionOr<void> setVertical(const String&); + + bool snapToLines() const { return m_snapToLines; } + void setSnapToLines(bool); + + double line() const { return m_linePosition; } + virtual ExceptionOr<void> setLine(double); + + double position() const { return m_textPosition; } + virtual ExceptionOr<void> setPosition(double); + + int size() const { return m_cueSize; } + ExceptionOr<void> setSize(int); + + const String& align() const; + ExceptionOr<void> setAlign(const String&); + + const String& text() const { return m_content; } + void setText(const String&); + + const String& cueSettings() const { return m_settings; } + void setCueSettings(const String&); + + RefPtr<DocumentFragment> getCueAsHTML(); + RefPtr<DocumentFragment> createCueRenderingTree(); + + const String& regionId() const { return m_regionId; } + void setRegionId(const String&); + void notifyRegionWhenRemovingDisplayTree(bool); + + void setIsActive(bool) override; + + bool hasDisplayTree() const { return m_displayTree; } + VTTCueBox& getDisplayTree(const IntSize& videoSize, int fontSize); + HTMLSpanElement& element() const { return *m_cueHighlightBox; } + + void updateDisplayTree(const MediaTime&); + void removeDisplayTree(); + void markFutureAndPastNodes(ContainerNode*, const MediaTime&, const MediaTime&); + + int calculateComputedLinePosition(); + std::pair<double, double> getPositionCoordinates() const; + + std::pair<double, double> getCSSPosition() const; + + CSSValueID getCSSAlignment() const; + int getCSSSize() const; + CSSValueID getCSSWritingDirection() const; + CSSValueID getCSSWritingMode() const; + + enum WritingDirection { + Horizontal = 0, + VerticalGrowingLeft, + VerticalGrowingRight, + NumberOfWritingDirections + }; + WritingDirection getWritingDirection() const { return m_writingDirection; } + + enum CueAlignment { + Start = 0, + Middle, + End, + Left, + Right, + NumberOfAlignments + }; + CueAlignment getAlignment() const { return m_cueAlignment; } + + virtual void setFontSize(int, const IntSize&, bool important); + + bool isEqual(const TextTrackCue&, CueMatchRules) const override; + bool cueContentsMatch(const TextTrackCue&) const override; + bool doesExtendCue(const TextTrackCue&) const override; + + CueType cueType() const override { return WebVTT; } + bool isRenderable() const final { return true; } + + void didChange() override; + +protected: + VTTCue(ScriptExecutionContext&, const MediaTime& start, const MediaTime& end, const String& content); + VTTCue(ScriptExecutionContext&, const WebVTTCueData&); + + virtual Ref<VTTCueBox> createDisplayTree(); + VTTCueBox& displayTreeInternal(); + +private: + void initialize(ScriptExecutionContext&); + void createWebVTTNodeTree(); + void copyWebVTTNodeToDOMTree(ContainerNode* WebVTTNode, ContainerNode* root); + + void parseSettings(const String&); + + void determineTextDirection(); + void calculateDisplayParameters(); + + enum CueSetting { + None, + Vertical, + Line, + Position, + Size, + Align, + RegionId + }; + CueSetting settingName(VTTScanner&); + + String m_content; + String m_settings; + double m_linePosition; + double m_computedLinePosition; + double m_textPosition; + int m_cueSize; + + WritingDirection m_writingDirection; + CueAlignment m_cueAlignment; + String m_regionId; + + RefPtr<DocumentFragment> m_webVTTNodeTree; + RefPtr<HTMLSpanElement> m_cueHighlightBox; + RefPtr<HTMLDivElement> m_cueBackdropBox; + RefPtr<VTTCueBox> m_displayTree; + + CSSValueID m_displayDirection; + int m_displaySize; + std::pair<float, float> m_displayPosition; + + MediaTime m_originalStartTime; + + bool m_snapToLines : 1; + bool m_displayTreeShouldChange : 1; + bool m_notifyRegion : 1; +}; + +VTTCue* toVTTCue(TextTrackCue*); +const VTTCue* toVTTCue(const TextTrackCue*); + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/track/VTTCue.idl b/Source/WebCore/html/track/VTTCue.idl new file mode 100644 index 000000000..9a3b78a7b --- /dev/null +++ b/Source/WebCore/html/track/VTTCue.idl @@ -0,0 +1,43 @@ +/* + * 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +[ + Conditional=VIDEO_TRACK, + Constructor(unrestricted double startTime, unrestricted double endTime, DOMString text), + ConstructorCallWith=ScriptExecutionContext, + JSGenerateToJSObject, + JSGenerateToNativeObject, +] interface VTTCue : TextTrackCue { + [SetterMayThrowException] attribute DOMString vertical; + attribute boolean snapToLines; + [SetterMayThrowException] attribute double line; + [SetterMayThrowException] attribute double position; + [SetterMayThrowException] attribute double size; + [SetterMayThrowException] attribute DOMString align; + attribute DOMString text; + DocumentFragment getCueAsHTML(); + + attribute DOMString regionId; +}; diff --git a/Source/WebCore/html/track/VTTRegion.cpp b/Source/WebCore/html/track/VTTRegion.cpp new file mode 100644 index 000000000..0578bf912 --- /dev/null +++ b/Source/WebCore/html/track/VTTRegion.cpp @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2014 Apple 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" +#include "VTTRegion.h" + +#if ENABLE(VIDEO_TRACK) + +#include "ClientRect.h" +#include "DOMTokenList.h" +#include "ElementChildIterator.h" +#include "ExceptionCode.h" +#include "HTMLDivElement.h" +#include "HTMLParserIdioms.h" +#include "Logging.h" +#include "RenderElement.h" +#include "VTTCue.h" +#include "VTTScanner.h" +#include "WebVTTParser.h" +#include <wtf/MathExtras.h> + +namespace WebCore { + +// The default values are defined within the WebVTT Regions Spec. +// https://dvcs.w3.org/hg/text-tracks/raw-file/default/608toVTT/region.html + +// Default region line-height (vh units) +static const float lineHeight = 5.33; + +// Default scrolling animation time period (s). +static const float scrollTime = 0.433; + +VTTRegion::VTTRegion(ScriptExecutionContext& context) + : ContextDestructionObserver(&context) + , m_id(emptyString()) + , m_scrollTimer(*this, &VTTRegion::scrollTimerFired) +{ +} + +VTTRegion::~VTTRegion() +{ +} + +void VTTRegion::setTrack(TextTrack* track) +{ + m_track = track; +} + +void VTTRegion::setId(const String& id) +{ + m_id = id; +} + +ExceptionOr<void> VTTRegion::setWidth(double value) +{ + if (!(value >= 0 && value <= 100)) + return Exception { INDEX_SIZE_ERR }; + m_width = value; + return { }; +} + +ExceptionOr<void> VTTRegion::setHeight(int value) +{ + if (value < 0) + return Exception { INDEX_SIZE_ERR }; + m_heightInLines = value; + return { }; +} + +ExceptionOr<void> VTTRegion::setRegionAnchorX(double value) +{ + if (!(value >= 0 && value <= 100)) + return Exception { INDEX_SIZE_ERR }; + m_regionAnchor.setX(value); + return { }; +} + +ExceptionOr<void> VTTRegion::setRegionAnchorY(double value) +{ + if (!(value >= 0 && value <= 100)) + return Exception { INDEX_SIZE_ERR }; + m_regionAnchor.setY(value); + return { }; +} + +ExceptionOr<void> VTTRegion::setViewportAnchorX(double value) +{ + if (!(value >= 0 && value <= 100)) + return Exception { INDEX_SIZE_ERR }; + m_viewportAnchor.setX(value); + return { }; +} + +ExceptionOr<void> VTTRegion::setViewportAnchorY(double value) +{ + if (!(value >= 0 && value <= 100)) + return Exception { INDEX_SIZE_ERR }; + m_viewportAnchor.setY(value); + return { }; +} + +static const AtomicString& upKeyword() +{ + static NeverDestroyed<const AtomicString> upKeyword("up", AtomicString::ConstructFromLiteral); + return upKeyword; +} + +const AtomicString& VTTRegion::scroll() const +{ + return m_scroll ? upKeyword() : emptyAtom; +} + +ExceptionOr<void> VTTRegion::setScroll(const AtomicString& value) +{ + if (value.isEmpty()) { + m_scroll = false; + return { }; + } + if (value == upKeyword()) { + m_scroll = true; + return { }; + } + return Exception { SYNTAX_ERR }; +} + +void VTTRegion::updateParametersFromRegion(const VTTRegion& other) +{ + m_heightInLines = other.m_heightInLines; + m_width = other.m_width; + m_regionAnchor = other.m_regionAnchor; + m_viewportAnchor = other.m_viewportAnchor; + m_scroll = other.m_scroll; +} + +void VTTRegion::setRegionSettings(const String& inputString) +{ + m_settings = inputString; + VTTScanner input(inputString); + + while (!input.isAtEnd()) { + input.skipWhile<WebVTTParser::isValidSettingDelimiter>(); + if (input.isAtEnd()) + break; + + // Scan the name part. + RegionSetting name = scanSettingName(input); + + // Verify that we're looking at a '='. + if (name == None || !input.scan('=')) { + input.skipUntil<isHTMLSpace<UChar>>(); + continue; + } + + // Scan the value part. + 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) +{ + VTTScanner::Run valueRun = input.collectUntil<isHTMLSpace<UChar>>(); + + switch (setting) { + case Id: { + String stringValue = input.extractString(valueRun); + if (stringValue.find("-->") == notFound) + m_id = stringValue; + break; + } + case Width: { + float floatWidth; + if (WebVTTParser::parseFloatPercentageValue(input, floatWidth) && parsedEntireRun(input, valueRun)) + m_width = floatWidth; + else + LOG(Media, "VTTRegion::parseSettingValue, invalid Width"); + break; + } + case Height: { + int number; + if (input.scanDigits(number) && parsedEntireRun(input, valueRun)) + m_heightInLines = number; + else + LOG(Media, "VTTRegion::parseSettingValue, invalid Height"); + break; + } + case RegionAnchor: { + FloatPoint anchor; + if (WebVTTParser::parseFloatPercentageValuePair(input, ',', anchor) && parsedEntireRun(input, valueRun)) + m_regionAnchor = anchor; + else + LOG(Media, "VTTRegion::parseSettingValue, invalid RegionAnchor"); + break; + } + case ViewportAnchor: { + FloatPoint anchor; + if (WebVTTParser::parseFloatPercentageValuePair(input, ',', anchor) && parsedEntireRun(input, valueRun)) + m_viewportAnchor = anchor; + else + LOG(Media, "VTTRegion::parseSettingValue, invalid ViewportAnchor"); + break; + } + case Scroll: + if (input.scanRun(valueRun, upKeyword())) + m_scroll = true; + else + LOG(Media, "VTTRegion::parseSettingValue, invalid Scroll"); + break; + case None: + break; + } + + input.skipRun(valueRun); +} + +const AtomicString& VTTRegion::textTrackCueContainerScrollingClass() +{ + static NeverDestroyed<const AtomicString> trackRegionCueContainerScrollingClass("scrolling", AtomicString::ConstructFromLiteral); + + return trackRegionCueContainerScrollingClass; +} + +const AtomicString& VTTRegion::textTrackCueContainerShadowPseudoId() +{ + static NeverDestroyed<const AtomicString> trackRegionCueContainerPseudoId("-webkit-media-text-track-region-container", AtomicString::ConstructFromLiteral); + + return trackRegionCueContainerPseudoId; +} + +const AtomicString& VTTRegion::textTrackRegionShadowPseudoId() +{ + static NeverDestroyed<const AtomicString> trackRegionShadowPseudoId("-webkit-media-text-track-region", AtomicString::ConstructFromLiteral); + + return trackRegionShadowPseudoId; +} + +void VTTRegion::appendTextTrackCueBox(Ref<VTTCueBox>&& displayBox) +{ + ASSERT(m_cueContainer); + + if (m_cueContainer->contains(displayBox.ptr())) + return; + + m_cueContainer->appendChild(displayBox); + displayLastTextTrackCueBox(); +} + +void VTTRegion::displayLastTextTrackCueBox() +{ + ASSERT(m_cueContainer); + + // The container needs to be rendered, if it is not empty and the region is not currently scrolling. + if (!m_cueContainer->renderer() || !m_cueContainer->hasChildNodes() || m_scrollTimer.isActive()) + return; + + // If it's a scrolling region, add the scrolling class. + if (isScrollingRegion()) + m_cueContainer->classList().add(textTrackCueContainerScrollingClass()); + + float regionBottom = m_regionDisplayTree->getBoundingClientRect()->bottom(); + + // Find first cue that is not entirely displayed and scroll it upwards. + for (auto& child : childrenOfType<Element>(*m_cueContainer)) { + Ref<ClientRect> rect = child.getBoundingClientRect(); + float childTop = rect->top(); + float childBottom = rect->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(); + break; + } +} + +void VTTRegion::willRemoveTextTrackCueBox(VTTCueBox* box) +{ + LOG(Media, "VTTRegion::willRemoveTextTrackCueBox"); + ASSERT(m_cueContainer->contains(box)); + + double boxHeight = box->getBoundingClientRect()->bottom() - box->getBoundingClientRect()->top(); + + m_cueContainer->classList().remove(textTrackCueContainerScrollingClass()); + + m_currentTop += boxHeight; + m_cueContainer->setInlineStyleProperty(CSSPropertyTop, m_currentTop, CSSPrimitiveValue::CSS_PX); +} + +HTMLDivElement& VTTRegion::getDisplayTree() +{ + if (!m_regionDisplayTree) { + m_regionDisplayTree = HTMLDivElement::create(downcast<Document>(*m_scriptExecutionContext)); + prepareRegionDisplayTree(); + } + + return *m_regionDisplayTree; +} + +void VTTRegion::prepareRegionDisplayTree() +{ + ASSERT(m_regionDisplayTree); + + // 7.2 Prepare region CSS boxes + + // FIXME: Change the code below to use viewport units when + // http://crbug/244618 is fixed. + + // Let regionWidth be the text track region width. + // Let width be 'regionWidth vw' ('vw' is a CSS unit) + m_regionDisplayTree->setInlineStyleProperty(CSSPropertyWidth, m_width, CSSPrimitiveValue::CSS_PERCENTAGE); + + // Let lineHeight be '0.0533vh' ('vh' is a CSS unit) and regionHeight be + // the text track region height. Let height be 'lineHeight' multiplied + // by regionHeight. + double height = lineHeight * m_heightInLines; + m_regionDisplayTree->setInlineStyleProperty(CSSPropertyHeight, height, CSSPrimitiveValue::CSS_VH); + + // Let viewportAnchorX be the x dimension of the text track region viewport + // anchor and regionAnchorX be the x dimension of the text track region + // anchor. Let leftOffset be regionAnchorX multiplied by width divided by + // 100.0. Let left be leftOffset subtracted from 'viewportAnchorX vw'. + double leftOffset = m_regionAnchor.x() * m_width / 100; + m_regionDisplayTree->setInlineStyleProperty(CSSPropertyLeft, m_viewportAnchor.x() - leftOffset, CSSPrimitiveValue::CSS_PERCENTAGE); + + // Let viewportAnchorY be the y dimension of the text track region viewport + // anchor and regionAnchorY be the y dimension of the text track region + // anchor. Let topOffset be regionAnchorY multiplied by height divided by + // 100.0. Let top be topOffset subtracted from 'viewportAnchorY vh'. + double topOffset = m_regionAnchor.y() * height / 100; + m_regionDisplayTree->setInlineStyleProperty(CSSPropertyTop, m_viewportAnchor.y() - topOffset, CSSPrimitiveValue::CSS_PERCENTAGE); + + // The cue container is used to wrap the cues and it is the object which is + // gradually scrolled out as multiple cues are appended to the region. + m_cueContainer = HTMLDivElement::create(downcast<Document>(*m_scriptExecutionContext)); + m_cueContainer->setInlineStyleProperty(CSSPropertyTop, 0.0f, CSSPrimitiveValue::CSS_PX); + + m_cueContainer->setPseudo(textTrackCueContainerShadowPseudoId()); + m_regionDisplayTree->appendChild(*m_cueContainer); + + // 7.5 Every WebVTT region object is initialised with the following CSS + m_regionDisplayTree->setPseudo(textTrackRegionShadowPseudoId()); +} + +void VTTRegion::startTimer() +{ + LOG(Media, "VTTRegion::startTimer"); + + if (m_scrollTimer.isActive()) + return; + + double duration = isScrollingRegion() ? scrollTime : 0; + m_scrollTimer.startOneShot(duration); +} + +void VTTRegion::stopTimer() +{ + LOG(Media, "VTTRegion::stopTimer"); + + if (m_scrollTimer.isActive()) + m_scrollTimer.stop(); +} + +void VTTRegion::scrollTimerFired() +{ + LOG(Media, "VTTRegion::scrollTimerFired"); + + stopTimer(); + displayLastTextTrackCueBox(); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/track/TextTrackRegion.h b/Source/WebCore/html/track/VTTRegion.h index 01b64ce4d..99c3e818e 100644 --- a/Source/WebCore/html/track/TextTrackRegion.h +++ b/Source/WebCore/html/track/VTTRegion.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -28,26 +29,28 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TextTrackRegion_h -#define TextTrackRegion_h +#pragma once -#if ENABLE(VIDEO_TRACK) && ENABLE(WEBVTT_REGIONS) +#if ENABLE(VIDEO_TRACK) +#include "ContextDestructionObserver.h" #include "FloatPoint.h" #include "TextTrack.h" -#include <wtf/PassOwnPtr.h> -#include <wtf/RefCounted.h> namespace WebCore { -class TextTrackRegion : public RefCounted<TextTrackRegion> { +class HTMLDivElement; +class VTTCueBox; +class VTTScanner; + +class VTTRegion final : public RefCounted<VTTRegion>, public ContextDestructionObserver { public: - static PassRefPtr<TextTrackRegion> create() + static Ref<VTTRegion> create(ScriptExecutionContext& context) { - return adoptRef(new TextTrackRegion()); + return adoptRef(*new VTTRegion(context)); } - virtual ~TextTrackRegion(); + virtual ~VTTRegion(); TextTrack* track() const { return m_track; } void setTrack(TextTrack*); @@ -56,33 +59,48 @@ public: void setId(const String&); double width() const { return m_width; } - void setWidth(double, ExceptionCode&); + ExceptionOr<void> setWidth(double); - long height() const { return m_heightInLines; } - void setHeight(long, ExceptionCode&); + int height() const { return m_heightInLines; } + ExceptionOr<void> setHeight(int); double regionAnchorX() const { return m_regionAnchor.x(); } - void setRegionAnchorX(double, ExceptionCode&); + ExceptionOr<void> setRegionAnchorX(double); double regionAnchorY() const { return m_regionAnchor.y(); } - void setRegionAnchorY(double, ExceptionCode&); + ExceptionOr<void> setRegionAnchorY(double); double viewportAnchorX() const { return m_viewportAnchor.x(); } - void setViewportAnchorX(double, ExceptionCode&); + ExceptionOr<void> setViewportAnchorX(double); double viewportAnchorY() const { return m_viewportAnchor.y(); } - void setViewportAnchorY(double, ExceptionCode&); + ExceptionOr<void> setViewportAnchorY(double); - const AtomicString scroll() const; - void setScroll(const AtomicString&, ExceptionCode&); + const AtomicString& scroll() const; + ExceptionOr<void> setScroll(const AtomicString&); - void updateParametersFromRegion(TextTrackRegion*); + void updateParametersFromRegion(const VTTRegion&); const String& regionSettings() const { return m_settings; } void setRegionSettings(const String&); + bool isScrollingRegion() { return m_scroll; } + + HTMLDivElement& getDisplayTree(); + + void appendTextTrackCueBox(Ref<VTTCueBox>&&); + void displayLastTextTrackCueBox(); + void willRemoveTextTrackCueBox(VTTCueBox*); + private: - TextTrackRegion(); + VTTRegion(ScriptExecutionContext&); + + void prepareRegionDisplayTree(); + + // The timer is needed to continue processing when cue scrolling ended. + void startTimer(); + void stopTimer(); + void scrollTimerFired(); enum RegionSetting { None, @@ -94,30 +112,47 @@ private: Scroll }; - RegionSetting getSettingFromString(const String&); + RegionSetting scanSettingName(VTTScanner&); - void parseSettingValue(RegionSetting, const String&); - void parseSetting(const String&, unsigned*); + void parseSettingValue(RegionSetting, VTTScanner&); + + static const AtomicString& textTrackCueContainerShadowPseudoId(); + static const AtomicString& textTrackCueContainerScrollingClass(); + static const AtomicString& textTrackRegionShadowPseudoId(); String m_id; String m_settings; - double m_width; - unsigned m_heightInLines; + double m_width { 100 }; + unsigned m_heightInLines { 3 }; + + FloatPoint m_regionAnchor { 0, 100 }; + FloatPoint m_viewportAnchor { 0, 100 }; - FloatPoint m_regionAnchor; - FloatPoint m_viewportAnchor; + bool m_scroll { false }; - bool m_scroll; + // The cue container is the container that is scrolled up to obtain the + // effect of scrolling cues when this is enabled for the regions. + RefPtr<HTMLDivElement> m_cueContainer; + RefPtr<HTMLDivElement> m_regionDisplayTree; // The member variable track can be a raw pointer as it will never // reference a destroyed TextTrack, as this member variable // is cleared in the TextTrack destructor and it is generally // set/reset within the addRegion and removeRegion methods. - TextTrack* m_track; + TextTrack* m_track { nullptr }; + + // Keep track of the current numeric value of the css "top" property. + double m_currentTop { 0 }; + + // The timer is used to display the next cue line after the current one has + // been displayed. It's main use is for scrolling regions and it triggers as + // soon as the animation for rolling out one line has finished, but + // currently it is used also for non-scrolling regions to use a single + // code path. + Timer m_scrollTimer; }; } // namespace WebCore #endif -#endif diff --git a/Source/WebCore/html/track/TextTrackRegion.idl b/Source/WebCore/html/track/VTTRegion.idl index f8f23b27b..7a24bc2ac 100644 --- a/Source/WebCore/html/track/TextTrackRegion.idl +++ b/Source/WebCore/html/track/VTTRegion.idl @@ -1,5 +1,6 @@ /* - * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,19 +25,19 @@ */ [ - Conditional=VIDEO_TRACK & WEBVTT_REGIONS, + Conditional=VIDEO_TRACK, + Constructor(), + ConstructorCallWith=ScriptExecutionContext, JSGenerateToNativeObject, - Constructor() -] interface TextTrackRegion { +] interface VTTRegion { readonly attribute TextTrack track; attribute DOMString id; - [SetterRaisesException] attribute double width; - [SetterRaisesException] attribute long height; - [SetterRaisesException] attribute double regionAnchorX; - [SetterRaisesException] attribute double regionAnchorY; - [SetterRaisesException] attribute double viewportAnchorX; - [SetterRaisesException] attribute double viewportAnchorY; - [SetterRaisesException] attribute DOMString scroll; + [SetterMayThrowException] attribute double width; + [SetterMayThrowException] attribute long height; + [SetterMayThrowException] attribute double regionAnchorX; + [SetterMayThrowException] attribute double regionAnchorY; + [SetterMayThrowException] attribute double viewportAnchorX; + [SetterMayThrowException] attribute double viewportAnchorY; + [SetterMayThrowException] attribute DOMString scroll; }; - diff --git a/Source/WebCore/html/track/VTTRegionList.cpp b/Source/WebCore/html/track/VTTRegionList.cpp new file mode 100644 index 000000000..cfc16223f --- /dev/null +++ b/Source/WebCore/html/track/VTTRegionList.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2014-2017 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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" +#include "VTTRegionList.h" + +#if ENABLE(VIDEO_TRACK) + +namespace WebCore { + +VTTRegion* VTTRegionList::item(unsigned index) const +{ + if (index >= m_vector.size()) + return nullptr; + return const_cast<VTTRegion*>(m_vector[index].ptr()); +} + +VTTRegion* VTTRegionList::getRegionById(const String& id) const +{ + // FIXME: Why is this special case needed? + if (id.isEmpty()) + return nullptr; + for (auto& region : m_vector) { + if (region->id() == id) + return const_cast<VTTRegion*>(region.ptr()); + } + return nullptr; +} + +void VTTRegionList::add(Ref<VTTRegion>&& region) +{ + m_vector.append(WTFMove(region)); +} + +void VTTRegionList::remove(VTTRegion& region) +{ + for (unsigned i = 0, size = m_vector.size(); i < size; ++i) { + if (m_vector[i].ptr() == ®ion) { + m_vector.remove(i); + return; + } + } + ASSERT_NOT_REACHED(); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/track/TextTrackRegionList.h b/Source/WebCore/html/track/VTTRegionList.h index 070e3e56e..7f9fd55dc 100644 --- a/Source/WebCore/html/track/TextTrackRegionList.h +++ b/Source/WebCore/html/track/VTTRegionList.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2014-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,43 +24,48 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TextTrackRegionList_h -#define TextTrackRegionList_h +#pragma once -#if ENABLE(VIDEO_TRACK) && ENABLE(WEBVTT_REGIONS) +#if ENABLE(VIDEO_TRACK) -#include "TextTrackRegion.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/Vector.h> +#include "VTTRegion.h" namespace WebCore { -class TextTrackRegionList : public RefCounted<TextTrackRegionList> { +class VTTRegionList : public RefCounted<VTTRegionList> { public: - static PassRefPtr<TextTrackRegionList> create() - { - return adoptRef(new TextTrackRegionList()); - } + static Ref<VTTRegionList> create(); - ~TextTrackRegionList() { } + unsigned length() const; + VTTRegion* item(unsigned index) const; + VTTRegion* getRegionById(const String&) const; - unsigned long length() const; - - TextTrackRegion* item(unsigned index) const; - TextTrackRegion* getRegionById(const String&) const; - - void add(PassRefPtr<TextTrackRegion>); - bool remove(TextTrackRegion*); + void add(Ref<VTTRegion>&&); + void remove(VTTRegion&); private: - TextTrackRegionList(); + VTTRegionList() = default; + void clear(); - Vector<RefPtr<TextTrackRegion> > m_list; + Vector<Ref<VTTRegion>> m_vector; }; +inline Ref<VTTRegionList> VTTRegionList::create() +{ + return adoptRef(*new VTTRegionList); +} + +inline unsigned VTTRegionList::length() const +{ + return m_vector.size(); +} + +inline void VTTRegionList::clear() +{ + m_vector.clear(); +} + } // namespace WebCore -#endif -#endif +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/html/track/TextTrackRegionList.idl b/Source/WebCore/html/track/VTTRegionList.idl index 31a0519d1..8431d8b51 100644 --- a/Source/WebCore/html/track/TextTrackRegionList.idl +++ b/Source/WebCore/html/track/VTTRegionList.idl @@ -1,5 +1,6 @@ /* - * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,12 +25,11 @@ */ [ - NoInterfaceObject, - Conditional=VIDEO_TRACK & WEBVTT_REGIONS, + Conditional=VIDEO_TRACK, ImplementationLacksVTable, -] interface TextTrackRegionList { + NoInterfaceObject, +] interface VTTRegionList { readonly attribute unsigned long length; - getter TextTrackRegion item(unsigned long index); - TextTrackRegion getRegionById(DOMString id); + getter VTTRegion? item(unsigned long index); + VTTRegion? getRegionById(DOMString id); }; - diff --git a/Source/WebCore/html/track/VTTScanner.cpp b/Source/WebCore/html/track/VTTScanner.cpp new file mode 100644 index 000000000..5fd4d060d --- /dev/null +++ b/Source/WebCore/html/track/VTTScanner.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2013, Opera Software ASA. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Opera Software ASA 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 HOLDER 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" +#include "VTTScanner.h" + +namespace WebCore { + +VTTScanner::VTTScanner(const String& line) + : m_is8Bit(line.is8Bit()) +{ + if (m_is8Bit) { + m_data.characters8 = line.characters8(); + m_end.characters8 = m_data.characters8 + line.length(); + } else { + m_data.characters16 = line.characters16(); + m_end.characters16 = m_data.characters16 + line.length(); + } +} + +bool VTTScanner::scan(char c) +{ + if (!match(c)) + return false; + advance(); + return true; +} + +bool VTTScanner::scan(const LChar* characters, size_t charactersCount) +{ + unsigned matchLength = m_is8Bit ? m_end.characters8 - m_data.characters8 : m_end.characters16 - m_data.characters16; + if (matchLength < charactersCount) + return false; + bool matched; + if (m_is8Bit) + matched = WTF::equal(m_data.characters8, characters, charactersCount); + else + matched = WTF::equal(m_data.characters16, characters, charactersCount); + if (matched) + advance(charactersCount); + return matched; +} + +bool VTTScanner::scanRun(const Run& run, const String& toMatch) +{ + ASSERT(run.start() == position()); + ASSERT(run.start() <= end()); + ASSERT(run.end() >= run.start()); + ASSERT(run.end() <= end()); + size_t matchLength = run.length(); + if (toMatch.length() > matchLength) + return false; + bool matched; + if (m_is8Bit) + matched = WTF::equal(toMatch.impl(), m_data.characters8, matchLength); + else + matched = WTF::equal(toMatch.impl(), m_data.characters16, matchLength); + if (matched) + seekTo(run.end()); + return matched; +} + +void VTTScanner::skipRun(const Run& run) +{ + ASSERT(run.start() <= end()); + ASSERT(run.end() >= run.start()); + ASSERT(run.end() <= end()); + seekTo(run.end()); +} + +String VTTScanner::extractString(const Run& run) +{ + ASSERT(run.start() == position()); + ASSERT(run.start() <= end()); + ASSERT(run.end() >= run.start()); + ASSERT(run.end() <= end()); + String s; + if (m_is8Bit) + s = String(m_data.characters8, run.length()); + else + s = String(m_data.characters16, run.length()); + seekTo(run.end()); + return s; +} + +String VTTScanner::restOfInputAsString() +{ + Run rest(position(), end(), m_is8Bit); + return extractString(rest); +} + +unsigned VTTScanner::scanDigits(int& number) +{ + Run runOfDigits = collectWhile<isASCIIDigit>(); + if (runOfDigits.isEmpty()) { + number = 0; + return 0; + } + bool validNumber; + size_t numDigits = runOfDigits.length(); + if (m_is8Bit) + number = charactersToIntStrict(m_data.characters8, numDigits, &validNumber); + else + number = charactersToIntStrict(m_data.characters16, numDigits, &validNumber); + + // Since we know that scanDigits only scanned valid (ASCII) digits (and + // hence that's what got passed to charactersToInt()), the remaining + // failure mode for charactersToInt() is overflow, so if |validNumber| is + // not true, then set |number| to the maximum int value. + if (!validNumber) + number = std::numeric_limits<int>::max(); + // Consume the digits. + seekTo(runOfDigits.end()); + return numDigits; +} + +bool VTTScanner::scanFloat(float& number, bool* isNegative) +{ + bool negative = scan('-'); + Run integerRun = collectWhile<isASCIIDigit>(); + + seekTo(integerRun.end()); + Run decimalRun(position(), position(), m_is8Bit); + if (scan('.')) { + decimalRun = collectWhile<isASCIIDigit>(); + seekTo(decimalRun.end()); + } + + // At least one digit required. + if (integerRun.isEmpty() && decimalRun.isEmpty()) { + // Restore to starting position. + seekTo(integerRun.start()); + return false; + } + + size_t lengthOfFloat = Run(integerRun.start(), position(), m_is8Bit).length(); + bool validNumber; + if (m_is8Bit) + number = charactersToFloat(integerRun.start(), lengthOfFloat, &validNumber); + else + number = charactersToFloat(reinterpret_cast<const UChar*>(integerRun.start()), lengthOfFloat, &validNumber); + + if (!validNumber) + number = std::numeric_limits<float>::max(); + else if (negative) + number = -number; + + if (isNegative) + *isNegative = negative; + + return true; +} + +} diff --git a/Source/WebCore/html/track/VTTScanner.h b/Source/WebCore/html/track/VTTScanner.h new file mode 100644 index 000000000..c4ae32a01 --- /dev/null +++ b/Source/WebCore/html/track/VTTScanner.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2013, Opera Software ASA. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Opera Software ASA 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 HOLDER 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. + */ + +#pragma once + +#include "ParsingUtilities.h" +#include <wtf/text/WTFString.h> + +namespace WebCore { + +// Helper class for "scanning" an input string and performing parsing of +// "micro-syntax"-like constructs. +// +// There's two primary operations: match and scan. +// +// The 'match' operation matches an explicitly or implicitly specified sequence +// against the characters ahead of the current input pointer, and returns true +// if the sequence can be matched. +// +// The 'scan' operation performs a 'match', and if the match is successful it +// advance the input pointer past the matched sequence. +class VTTScanner { + WTF_MAKE_NONCOPYABLE(VTTScanner); +public: + explicit VTTScanner(const String& line); + + typedef const LChar* Position; + + class Run { + public: + Run(Position start, Position end, bool is8Bit) + : m_start(start), m_end(end), m_is8Bit(is8Bit) { } + + Position start() const { return m_start; } + Position end() const { return m_end; } + + bool isEmpty() const { return m_start == m_end; } + size_t length() const; + + private: + Position m_start; + Position m_end; + bool m_is8Bit; + }; + + // Check if the input pointer points at the specified position. + bool isAt(Position checkPosition) const { return position() == checkPosition; } + // Check if the input pointer points at the end of the input. + bool isAtEnd() const { return position() == end(); } + // Match the character |c| against the character at the input pointer (~lookahead). + bool match(char c) const { return !isAtEnd() && currentChar() == c; } + // Scan the character |c|. + bool scan(char); + // Scan the first |charactersCount| characters of the string |characters|. + bool scan(const LChar* characters, size_t charactersCount); + + // Scan the literal |characters|. + template<unsigned charactersCount> + bool scan(const char (&characters)[charactersCount]); + + // Skip (advance the input pointer) as long as the specified + // |characterPredicate| returns true, and the input pointer is not passed + // the end of the input. + template<bool characterPredicate(UChar)> + void skipWhile(); + + // Like skipWhile, but using a negated predicate. + template<bool characterPredicate(UChar)> + void skipUntil(); + + // Return the run of characters for which the specified + // |characterPredicate| returns true. The start of the run will be the + // current input pointer. + template<bool characterPredicate(UChar)> + Run collectWhile(); + + // Like collectWhile, but using a negated predicate. + template<bool characterPredicate(UChar)> + Run collectUntil(); + + // Scan the string |toMatch|, using the specified |run| as the sequence to + // match against. + bool scanRun(const Run&, const String& toMatch); + + // Skip to the end of the specified |run|. + void skipRun(const Run&); + + // Return the String made up of the characters in |run|, and advance the + // input pointer to the end of the run. + String extractString(const Run&); + + // Return a String constructed from the rest of the input (between input + // pointer and end of input), and advance the input pointer accordingly. + String restOfInputAsString(); + + // Scan a set of ASCII digits from the input. Return the number of digits + // scanned, and set |number| to the computed value. If the digits make up a + // number that does not fit the 'int' type, |number| is set to INT_MAX. + // Note: Does not handle sign. + unsigned scanDigits(int& number); + + // Scan a floating point value on one of the forms: \d+\.? \d+\.\d+ \.\d+ + bool scanFloat(float& number, bool* isNegative = nullptr); + +protected: + Position position() const { return m_data.characters8; } + Position end() const { return m_end.characters8; } + void seekTo(Position); + UChar currentChar() const; + void advance(unsigned amount = 1); + // Adapt a UChar-predicate to an LChar-predicate. + // (For use with skipWhile/Until from ParsingUtilities.h). + template<bool characterPredicate(UChar)> + static inline bool LCharPredicateAdapter(LChar c) { return characterPredicate(c); } + union { + const LChar* characters8; + const UChar* characters16; + } m_data; + union { + const LChar* characters8; + const UChar* characters16; + } m_end; + bool m_is8Bit; +}; + +inline size_t VTTScanner::Run::length() const +{ + if (m_is8Bit) + return m_end - m_start; + return reinterpret_cast<const UChar*>(m_end) - reinterpret_cast<const UChar*>(m_start); +} + +template<unsigned charactersCount> +inline bool VTTScanner::scan(const char (&characters)[charactersCount]) +{ + return scan(reinterpret_cast<const LChar*>(characters), charactersCount - 1); +} + +template<bool characterPredicate(UChar)> +inline void VTTScanner::skipWhile() +{ + if (m_is8Bit) + WebCore::skipWhile<LChar, LCharPredicateAdapter<characterPredicate> >(m_data.characters8, m_end.characters8); + else + WebCore::skipWhile<UChar, characterPredicate>(m_data.characters16, m_end.characters16); +} + +template<bool characterPredicate(UChar)> +inline void VTTScanner::skipUntil() +{ + if (m_is8Bit) + WebCore::skipUntil<LChar, LCharPredicateAdapter<characterPredicate> >(m_data.characters8, m_end.characters8); + else + WebCore::skipUntil<UChar, characterPredicate>(m_data.characters16, m_end.characters16); +} + +template<bool characterPredicate(UChar)> +inline VTTScanner::Run VTTScanner::collectWhile() +{ + if (m_is8Bit) { + const LChar* current = m_data.characters8; + WebCore::skipWhile<LChar, LCharPredicateAdapter<characterPredicate> >(current, m_end.characters8); + return Run(position(), current, m_is8Bit); + } + const UChar* current = m_data.characters16; + WebCore::skipWhile<UChar, characterPredicate>(current, m_end.characters16); + return Run(position(), reinterpret_cast<Position>(current), m_is8Bit); +} + +template<bool characterPredicate(UChar)> +inline VTTScanner::Run VTTScanner::collectUntil() +{ + if (m_is8Bit) { + const LChar* current = m_data.characters8; + WebCore::skipUntil<LChar, LCharPredicateAdapter<characterPredicate> >(current, m_end.characters8); + return Run(position(), current, m_is8Bit); + } + const UChar* current = m_data.characters16; + WebCore::skipUntil<UChar, characterPredicate>(current, m_end.characters16); + return Run(position(), reinterpret_cast<Position>(current), m_is8Bit); +} + +inline void VTTScanner::seekTo(Position position) +{ + ASSERT(position <= end()); + m_data.characters8 = position; +} + +inline UChar VTTScanner::currentChar() const +{ + ASSERT(position() < end()); + return m_is8Bit ? *m_data.characters8 : *m_data.characters16; +} + +inline void VTTScanner::advance(unsigned amount) +{ + ASSERT(position() < end()); + if (m_is8Bit) + m_data.characters8 += amount; + else + m_data.characters16 += amount; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/track/VideoTrack.cpp b/Source/WebCore/html/track/VideoTrack.cpp index c0480fee2..f662dae3a 100644 --- a/Source/WebCore/html/track/VideoTrack.cpp +++ b/Source/WebCore/html/track/VideoTrack.cpp @@ -1,6 +1,6 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. - * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2011-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -30,12 +30,10 @@ */ #include "config.h" +#include "VideoTrack.h" #if ENABLE(VIDEO_TRACK) -#include "VideoTrack.h" - -#include "Event.h" #include "HTMLMediaElement.h" #include "VideoTrackList.h" @@ -47,97 +45,76 @@ namespace WebCore { const AtomicString& VideoTrack::alternativeKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, alternative, ("alternative", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> alternative("alternative", AtomicString::ConstructFromLiteral); return alternative; } const AtomicString& VideoTrack::captionsKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, captions, ("captions", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> captions("captions", AtomicString::ConstructFromLiteral); return captions; } const AtomicString& VideoTrack::mainKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, captions, ("main", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> captions("main", AtomicString::ConstructFromLiteral); return captions; } const AtomicString& VideoTrack::signKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, sign, ("sign", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> sign("sign", AtomicString::ConstructFromLiteral); return sign; } const AtomicString& VideoTrack::subtitlesKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, subtitles, ("subtitles", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> subtitles("subtitles", AtomicString::ConstructFromLiteral); return subtitles; } const AtomicString& VideoTrack::commentaryKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, commentary, ("commentary", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> commentary("commentary", AtomicString::ConstructFromLiteral); return commentary; } -VideoTrack::VideoTrack(VideoTrackClient* client, PassRefPtr<VideoTrackPrivate> trackPrivate) - : TrackBase(TrackBase::VideoTrack, trackPrivate->id(), trackPrivate->label(), trackPrivate->language()) - , m_selected(trackPrivate->selected()) - , m_client(client) +VideoTrack::VideoTrack(VideoTrackClient& client, VideoTrackPrivate& trackPrivate) + : MediaTrackBase(MediaTrackBase::VideoTrack, trackPrivate.id(), trackPrivate.label(), trackPrivate.language()) + , m_selected(trackPrivate.selected()) + , m_client(&client) , m_private(trackPrivate) { m_private->setClient(this); - - switch (m_private->kind()) { - case VideoTrackPrivate::Alternative: - setKindInternal(VideoTrack::alternativeKeyword()); - break; - case VideoTrackPrivate::Captions: - setKindInternal(VideoTrack::captionsKeyword()); - break; - case VideoTrackPrivate::Main: - setKindInternal(VideoTrack::mainKeyword()); - break; - case VideoTrackPrivate::Sign: - setKindInternal(VideoTrack::signKeyword()); - break; - case VideoTrackPrivate::Subtitles: - setKindInternal(VideoTrack::subtitlesKeyword()); - break; - case VideoTrackPrivate::Commentary: - setKindInternal(VideoTrack::commentaryKeyword()); - break; - case VideoTrackPrivate::None: - setKindInternal(emptyString()); - break; - default: - ASSERT_NOT_REACHED(); - break; - } + updateKindFromPrivate(); } VideoTrack::~VideoTrack() { - m_private->setClient(0); + m_private->setClient(nullptr); } -bool VideoTrack::isValidKind(const AtomicString& value) const +void VideoTrack::setPrivate(VideoTrackPrivate& trackPrivate) { - if (value == alternativeKeyword()) - return true; - if (value == captionsKeyword()) - return true; - if (value == mainKeyword()) - return true; - if (value == signKeyword()) - return true; - if (value == subtitlesKeyword()) - return true; - if (value == commentaryKeyword()) - return true; + if (m_private.ptr() == &trackPrivate) + return; - return false; + m_private->setClient(nullptr); + m_private = trackPrivate; + m_private->setClient(this); + + m_private->setSelected(m_selected); + updateKindFromPrivate(); +} + +bool VideoTrack::isValidKind(const AtomicString& value) const +{ + return value == alternativeKeyword() + || value == commentaryKeyword() + || value == captionsKeyword() + || value == mainKeyword() + || value == signKeyword() + || value == subtitlesKeyword(); } void VideoTrack::setSelected(const bool selected) @@ -149,46 +126,44 @@ void VideoTrack::setSelected(const bool selected) m_private->setSelected(selected); if (m_client) - m_client->videoTrackSelectedChanged(this); + m_client->videoTrackSelectedChanged(*this); } size_t VideoTrack::inbandTrackIndex() { - ASSERT(m_private); return m_private->trackIndex(); } -void VideoTrack::selectedChanged(VideoTrackPrivate* trackPrivate, bool selected) +void VideoTrack::selectedChanged(bool selected) { - ASSERT_UNUSED(trackPrivate, trackPrivate == m_private); setSelected(selected); } -void VideoTrack::idChanged(TrackPrivateBase* trackPrivate, const String& id) +void VideoTrack::idChanged(const AtomicString& id) { - ASSERT_UNUSED(trackPrivate, trackPrivate == m_private); setId(id); } -void VideoTrack::labelChanged(TrackPrivateBase* trackPrivate, const String& label) +void VideoTrack::labelChanged(const AtomicString& label) { - ASSERT_UNUSED(trackPrivate, trackPrivate == m_private); setLabel(label); } -void VideoTrack::languageChanged(TrackPrivateBase* trackPrivate, const String& language) +void VideoTrack::languageChanged(const AtomicString& language) { - ASSERT_UNUSED(trackPrivate, trackPrivate == m_private); setLanguage(language); } -void VideoTrack::willRemove(TrackPrivateBase* trackPrivate) +void VideoTrack::willRemove() { - ASSERT_UNUSED(trackPrivate, trackPrivate == m_private); - mediaElement()->removeVideoTrack(this); + auto* element = mediaElement(); + if (!element) + return; + element->removeVideoTrack(*this); } #if ENABLE(MEDIA_SOURCE) + void VideoTrack::setKind(const AtomicString& kind) { // 10.1 kind, on setting: @@ -203,11 +178,11 @@ void VideoTrack::setKind(const AtomicString& kind) // 3. If the sourceBuffer attribute on this track is not null, then queue a task to fire a simple // event named change at sourceBuffer.videoTracks. if (m_sourceBuffer) - m_sourceBuffer->videoTracks()->scheduleChangeEvent(); + m_sourceBuffer->videoTracks().scheduleChangeEvent(); // 4. Queue a task to fire a simple event named change at the VideoTrackList object referenced by // the videoTracks attribute on the HTMLMediaElement. - mediaElement()->videoTracks()->scheduleChangeEvent(); + mediaElement()->videoTracks().scheduleChangeEvent(); } void VideoTrack::setLanguage(const AtomicString& language) @@ -215,22 +190,52 @@ void VideoTrack::setLanguage(const AtomicString& language) // 10.1 language, on setting: // 1. If the value being assigned to this attribute is not an empty string or a BCP 47 language // tag[BCP47], then abort these steps. - // FIXME(123926): Validate the BCP47-ness of langague. + // BCP 47 validation is done in TrackBase::setLanguage() which is + // shared between all tracks that support setting language. // 2. Update this attribute to the new value. - TrackBase::setLanguage(language); + MediaTrackBase::setLanguage(language); // 3. If the sourceBuffer attribute on this track is not null, then queue a task to fire a simple // event named change at sourceBuffer.videoTracks. if (m_sourceBuffer) - m_sourceBuffer->videoTracks()->scheduleChangeEvent(); + m_sourceBuffer->videoTracks().scheduleChangeEvent(); // 4. Queue a task to fire a simple event named change at the VideoTrackList object referenced by // the videoTracks attribute on the HTMLMediaElement. - mediaElement()->videoTracks()->scheduleChangeEvent(); + mediaElement()->videoTracks().scheduleChangeEvent(); } + #endif +void VideoTrack::updateKindFromPrivate() +{ + switch (m_private->kind()) { + case VideoTrackPrivate::Alternative: + setKindInternal(VideoTrack::alternativeKeyword()); + return; + case VideoTrackPrivate::Captions: + setKindInternal(VideoTrack::captionsKeyword()); + return; + case VideoTrackPrivate::Main: + setKindInternal(VideoTrack::mainKeyword()); + return; + case VideoTrackPrivate::Sign: + setKindInternal(VideoTrack::signKeyword()); + return; + case VideoTrackPrivate::Subtitles: + setKindInternal(VideoTrack::subtitlesKeyword()); + return; + case VideoTrackPrivate::Commentary: + setKindInternal(VideoTrack::commentaryKeyword()); + return; + case VideoTrackPrivate::None: + setKindInternal(emptyString()); + return; + } + ASSERT_NOT_REACHED(); +} + } // namespace WebCore #endif diff --git a/Source/WebCore/html/track/VideoTrack.h b/Source/WebCore/html/track/VideoTrack.h index 6343a0743..ebee829e1 100644 --- a/Source/WebCore/html/track/VideoTrack.h +++ b/Source/WebCore/html/track/VideoTrack.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 Google Inc. All rights reserved. - * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2011-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,17 +24,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef VideoTrack_h -#define VideoTrack_h +#pragma once #if ENABLE(VIDEO_TRACK) -#include "ExceptionCode.h" #include "TrackBase.h" #include "VideoTrackPrivate.h" -#include <wtf/PassOwnPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/text/WTFString.h> namespace WebCore { @@ -44,14 +39,14 @@ class VideoTrack; class VideoTrackClient { public: virtual ~VideoTrackClient() { } - virtual void videoTrackSelectedChanged(VideoTrack*) = 0; + virtual void videoTrackSelectedChanged(VideoTrack&) = 0; }; -class VideoTrack : public TrackBase, public VideoTrackPrivateClient { +class VideoTrack final : public MediaTrackBase, private VideoTrackPrivateClient { public: - static PassRefPtr<VideoTrack> create(VideoTrackClient* client, PassRefPtr<VideoTrackPrivate> trackPrivate) + static Ref<VideoTrack> create(VideoTrackClient& client, VideoTrackPrivate& trackPrivate) { - return adoptRef(new VideoTrack(client, trackPrivate)); + return adoptRef(*new VideoTrack(client, trackPrivate)); } virtual ~VideoTrack(); @@ -61,50 +56,52 @@ public: static const AtomicString& signKeyword(); static const AtomicString& subtitlesKeyword(); static const AtomicString& commentaryKeyword(); - virtual const AtomicString& defaultKindKeyword() const override { return emptyAtom; } bool selected() const { return m_selected; } virtual void setSelected(const bool); - virtual void clearClient() override { m_client = 0; } + void clearClient() final { m_client = nullptr; } VideoTrackClient* client() const { return m_client; } size_t inbandTrackIndex(); #if ENABLE(MEDIA_SOURCE) - virtual void setKind(const AtomicString&) override; - virtual void setLanguage(const AtomicString&) override; + void setKind(const AtomicString&) final; + void setLanguage(const AtomicString&) final; #endif const MediaDescription& description() const; -protected: - VideoTrack(VideoTrackClient*, PassRefPtr<VideoTrackPrivate> privateTrack); + void setPrivate(VideoTrackPrivate&); private: - virtual bool isValidKind(const AtomicString&) const override; + VideoTrack(VideoTrackClient&, VideoTrackPrivate&); - virtual void selectedChanged(VideoTrackPrivate*, bool) override; - virtual void idChanged(TrackPrivateBase*, const String&) override; - virtual void labelChanged(TrackPrivateBase*, const String&) override; - virtual void languageChanged(TrackPrivateBase*, const String&) override; - virtual void willRemove(TrackPrivateBase*) override; + bool isValidKind(const AtomicString&) const final; - virtual bool enabled() const override { return selected(); } + // VideoTrackPrivateClient + void selectedChanged(bool) final; + + // TrackPrivateBaseClient + void idChanged(const AtomicString&) final; + void labelChanged(const AtomicString&) final; + void languageChanged(const AtomicString&) final; + void willRemove() final; + + bool enabled() const final { return selected(); } + + void updateKindFromPrivate(); bool m_selected; VideoTrackClient* m_client; - RefPtr<VideoTrackPrivate> m_private; + Ref<VideoTrackPrivate> m_private; }; -inline VideoTrack* toVideoTrack(TrackBase* track) -{ - ASSERT_WITH_SECURITY_IMPLICATION(track->type() == TrackBase::VideoTrack); - return static_cast<VideoTrack*>(track); -} - } // namespace WebCore -#endif +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::VideoTrack) + static bool isType(const WebCore::TrackBase& track) { return track.type() == WebCore::TrackBase::VideoTrack; } +SPECIALIZE_TYPE_TRAITS_END() + #endif diff --git a/Source/WebCore/html/track/VideoTrack.idl b/Source/WebCore/html/track/VideoTrack.idl index 53e0da62f..5627d95e7 100644 --- a/Source/WebCore/html/track/VideoTrack.idl +++ b/Source/WebCore/html/track/VideoTrack.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,7 +24,6 @@ */ [ - NoInterfaceObject, Conditional=VIDEO_TRACK, GenerateIsReachable=ImplElementRoot, JSCustomMarkFunction diff --git a/Source/WebCore/html/track/VideoTrackList.cpp b/Source/WebCore/html/track/VideoTrackList.cpp index 594ca1b3a..304c601b8 100644 --- a/Source/WebCore/html/track/VideoTrackList.cpp +++ b/Source/WebCore/html/track/VideoTrackList.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -29,7 +29,6 @@ #include "VideoTrackList.h" -#include "EventNames.h" #include "VideoTrack.h" using namespace WebCore; @@ -43,48 +42,50 @@ VideoTrackList::~VideoTrackList() { } -void VideoTrackList::append(PassRefPtr<VideoTrack> prpTrack) +void VideoTrackList::append(Ref<VideoTrack>&& track) { - RefPtr<VideoTrack> track = prpTrack; - // Insert tracks in the media file order. size_t index = track->inbandTrackIndex(); - m_inbandTracks.insert(index, track); + size_t insertionIndex; + for (insertionIndex = 0; insertionIndex < m_inbandTracks.size(); ++insertionIndex) { + auto& otherTrack = downcast<VideoTrack>(*m_inbandTracks[insertionIndex]); + if (otherTrack.inbandTrackIndex() > index) + break; + } + m_inbandTracks.insert(insertionIndex, track.ptr()); ASSERT(!track->mediaElement() || track->mediaElement() == mediaElement()); track->setMediaElement(mediaElement()); - scheduleAddTrackEvent(track.release()); + scheduleAddTrackEvent(WTFMove(track)); } VideoTrack* VideoTrackList::item(unsigned index) const { if (index < m_inbandTracks.size()) - return toVideoTrack(m_inbandTracks[index].get()); - - return 0; + return downcast<VideoTrack>(m_inbandTracks[index].get()); + return nullptr; } VideoTrack* VideoTrackList::getTrackById(const AtomicString& id) const { - for (size_t i = 0; i < length(); ++i) { - VideoTrack* track = toVideoTrack(m_inbandTracks[i].get()); - if (track->id() == id) - return track; + for (auto& inbandTracks : m_inbandTracks) { + auto& track = downcast<VideoTrack>(*inbandTracks); + if (track.id() == id) + return &track; } - return 0; + return nullptr; } -long VideoTrackList::selectedIndex() const +int VideoTrackList::selectedIndex() const { // 4.8.10.10.1 AudioTrackList and VideoTrackList objects // The VideoTrackList.selectedIndex attribute must return the index of the // currently selected track, if any. If the VideoTrackList object does not // currently represent any tracks, or if none of the tracks are selected, // it must instead return −1. - for (size_t i = 0; i < length(); ++i) { - VideoTrack* track = toVideoTrack(m_inbandTracks[i].get()); - if (track->selected()) + for (unsigned i = 0; i < length(); ++i) { + if (downcast<VideoTrack>(*m_inbandTracks[i]).selected()) return i; } return -1; diff --git a/Source/WebCore/html/track/VideoTrackList.h b/Source/WebCore/html/track/VideoTrackList.h index de7b845a1..6b5be25b4 100644 --- a/Source/WebCore/html/track/VideoTrackList.h +++ b/Source/WebCore/html/track/VideoTrackList.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef VideoTrackList_h -#define VideoTrackList_h +#pragma once #if ENABLE(VIDEO_TRACK) @@ -34,30 +33,28 @@ namespace WebCore { class VideoTrack; -class VideoTrackList : public TrackListBase { +class VideoTrackList final : public TrackListBase { public: - static PassRefPtr<VideoTrackList> create(HTMLMediaElement* owner, ScriptExecutionContext* context) + static Ref<VideoTrackList> create(HTMLMediaElement* owner, ScriptExecutionContext* context) { - return adoptRef(new VideoTrackList(owner, context)); + return adoptRef(*new VideoTrackList(owner, context)); } - ~VideoTrackList(); + virtual ~VideoTrackList(); VideoTrack* getTrackById(const AtomicString&) const; - long selectedIndex() const; + int selectedIndex() const; VideoTrack* item(unsigned) const; VideoTrack* lastItem() const { return item(length() - 1); } - void append(PassRefPtr<VideoTrack>); + void append(Ref<VideoTrack>&&); // EventTarget - virtual EventTargetInterface eventTargetInterface() const override; + EventTargetInterface eventTargetInterface() const override; private: VideoTrackList(HTMLMediaElement*, ScriptExecutionContext*); - }; } // namespace WebCore -#endif -#endif +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/html/track/VideoTrackList.idl b/Source/WebCore/html/track/VideoTrackList.idl index 1058bd97b..9c43fcd18 100644 --- a/Source/WebCore/html/track/VideoTrackList.idl +++ b/Source/WebCore/html/track/VideoTrackList.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,26 +24,17 @@ */ [ - NoInterfaceObject, Conditional=VIDEO_TRACK, GenerateIsReachable=ImplElementRoot, - EventTarget, JSCustomMarkFunction, -] interface VideoTrackList { +] interface VideoTrackList : EventTarget { readonly attribute unsigned long length; getter VideoTrack item(unsigned long index); VideoTrack getTrackById(DOMString id); + readonly attribute long selectedIndex; - attribute EventListener onchange; - attribute EventListener onaddtrack; - attribute EventListener onremovetrack; - - void addEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - void removeEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - [RaisesException] boolean dispatchEvent(Event evt); + attribute EventHandler onchange; + attribute EventHandler onaddtrack; + attribute EventHandler onremovetrack; }; diff --git a/Source/WebCore/html/track/WebVTTElement.cpp b/Source/WebCore/html/track/WebVTTElement.cpp index 6a76d6078..f43403269 100644 --- a/Source/WebCore/html/track/WebVTTElement.cpp +++ b/Source/WebCore/html/track/WebVTTElement.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,26 +24,27 @@ */ #include "config.h" +#include "WebVTTElement.h" #if ENABLE(VIDEO_TRACK) -#include "WebVTTElement.h" - -#include "HTMLElementFactory.h" +#include "HTMLSpanElement.h" +#include "RubyElement.h" +#include "RubyTextElement.h" #include "TextTrack.h" namespace WebCore { static const QualifiedName& nodeTypeToTagName(WebVTTNodeType nodeType) { - DEFINE_STATIC_LOCAL(QualifiedName, cTag, (nullAtom, "c", nullAtom)); - DEFINE_STATIC_LOCAL(QualifiedName, vTag, (nullAtom, "v", nullAtom)); - DEFINE_STATIC_LOCAL(QualifiedName, langTag, (nullAtom, "lang", nullAtom)); - DEFINE_STATIC_LOCAL(QualifiedName, bTag, (nullAtom, "b", nullAtom)); - DEFINE_STATIC_LOCAL(QualifiedName, uTag, (nullAtom, "u", nullAtom)); - DEFINE_STATIC_LOCAL(QualifiedName, iTag, (nullAtom, "i", nullAtom)); - DEFINE_STATIC_LOCAL(QualifiedName, rubyTag, (nullAtom, "ruby", nullAtom)); - DEFINE_STATIC_LOCAL(QualifiedName, rtTag, (nullAtom, "rt", nullAtom)); + static NeverDestroyed<QualifiedName> cTag(nullAtom, "c", nullAtom); + static NeverDestroyed<QualifiedName> vTag(nullAtom, "v", nullAtom); + static NeverDestroyed<QualifiedName> langTag(nullAtom, "lang", nullAtom); + static NeverDestroyed<QualifiedName> bTag(nullAtom, "b", nullAtom); + static NeverDestroyed<QualifiedName> uTag(nullAtom, "u", nullAtom); + static NeverDestroyed<QualifiedName> iTag(nullAtom, "i", nullAtom); + static NeverDestroyed<QualifiedName> rubyTag(nullAtom, "ruby", nullAtom); + static NeverDestroyed<QualifiedName> rtTag(nullAtom, "rt", nullAtom); switch (nodeType) { case WebVTTNodeTypeClass: return cTag; @@ -75,19 +76,19 @@ WebVTTElement::WebVTTElement(WebVTTNodeType nodeType, Document& document) { } -PassRefPtr<WebVTTElement> WebVTTElement::create(WebVTTNodeType nodeType, Document& document) +Ref<WebVTTElement> WebVTTElement::create(WebVTTNodeType nodeType, Document& document) { - return adoptRef(new WebVTTElement(nodeType, document)); + return adoptRef(*new WebVTTElement(nodeType, document)); } -PassRefPtr<Element> WebVTTElement::cloneElementWithoutAttributesAndChildren() +Ref<Element> WebVTTElement::cloneElementWithoutAttributesAndChildren(Document& targetDocument) { - RefPtr<WebVTTElement> clone = create(static_cast<WebVTTNodeType>(m_webVTTNodeType), document()); + Ref<WebVTTElement> clone = create(static_cast<WebVTTNodeType>(m_webVTTNodeType), targetDocument); clone->setLanguage(m_language); - return clone; + return WTFMove(clone); } -PassRefPtr<HTMLElement> WebVTTElement::createEquivalentHTMLElement(Document& document) +Ref<HTMLElement> WebVTTElement::createEquivalentHTMLElement(Document& document) { RefPtr<HTMLElement> htmlElement; @@ -95,31 +96,31 @@ PassRefPtr<HTMLElement> WebVTTElement::createEquivalentHTMLElement(Document& doc case WebVTTNodeTypeClass: case WebVTTNodeTypeLanguage: case WebVTTNodeTypeVoice: - htmlElement = HTMLElementFactory::createElement(HTMLNames::spanTag, document); - htmlElement->setAttribute(HTMLNames::titleAttr, getAttribute(voiceAttributeName())); - htmlElement->setAttribute(HTMLNames::langAttr, getAttribute(langAttributeName())); + htmlElement = HTMLSpanElement::create(document); + htmlElement->setAttributeWithoutSynchronization(HTMLNames::titleAttr, attributeWithoutSynchronization(voiceAttributeName())); + htmlElement->setAttributeWithoutSynchronization(HTMLNames::langAttr, attributeWithoutSynchronization(langAttributeName())); break; case WebVTTNodeTypeItalic: - htmlElement = HTMLElementFactory::createElement(HTMLNames::iTag, document); + htmlElement = HTMLElement::create(HTMLNames::iTag, document); break; case WebVTTNodeTypeBold: - htmlElement = HTMLElementFactory::createElement(HTMLNames::bTag, document); + htmlElement = HTMLElement::create(HTMLNames::bTag, document); break; case WebVTTNodeTypeUnderline: - htmlElement = HTMLElementFactory::createElement(HTMLNames::uTag, document); + htmlElement = HTMLElement::create(HTMLNames::uTag, document); break; case WebVTTNodeTypeRuby: - htmlElement = HTMLElementFactory::createElement(HTMLNames::rubyTag, document); + htmlElement = RubyElement::create(document); break; case WebVTTNodeTypeRubyText: - htmlElement = HTMLElementFactory::createElement(HTMLNames::rtTag, document); + htmlElement = RubyTextElement::create(document); break; } ASSERT(htmlElement); if (htmlElement) - htmlElement->setAttribute(HTMLNames::classAttr, fastGetAttribute(HTMLNames::classAttr)); - return htmlElement.release(); + htmlElement->setAttributeWithoutSynchronization(HTMLNames::classAttr, attributeWithoutSynchronization(HTMLNames::classAttr)); + return htmlElement.releaseNonNull(); } } // namespace WebCore diff --git a/Source/WebCore/html/track/WebVTTElement.h b/Source/WebCore/html/track/WebVTTElement.h index f49412c26..a08f42410 100644 --- a/Source/WebCore/html/track/WebVTTElement.h +++ b/Source/WebCore/html/track/WebVTTElement.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,9 +23,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once + #if ENABLE(VIDEO_TRACK) #include "HTMLElement.h" +#include <wtf/NeverDestroyed.h> namespace WebCore { @@ -43,10 +46,10 @@ enum WebVTTNodeType { class WebVTTElement final : public Element { public: - static PassRefPtr<WebVTTElement> create(const WebVTTNodeType, Document&); - PassRefPtr<HTMLElement> createEquivalentHTMLElement(Document&); + static Ref<WebVTTElement> create(const WebVTTNodeType, Document&); + Ref<HTMLElement> createEquivalentHTMLElement(Document&); - virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren() override; + Ref<Element> cloneElementWithoutAttributesAndChildren(Document&) override; void setWebVTTNodeType(WebVTTNodeType type) { m_webVTTNodeType = static_cast<unsigned>(type); } WebVTTNodeType webVTTNodeType() const { return static_cast<WebVTTNodeType>(m_webVTTNodeType); } @@ -59,20 +62,20 @@ public: static const QualifiedName& voiceAttributeName() { - DEFINE_STATIC_LOCAL(QualifiedName, voiceAttr, (nullAtom, "voice", nullAtom)); + static NeverDestroyed<QualifiedName> voiceAttr(nullAtom, "voice", nullAtom); return voiceAttr; } static const QualifiedName& langAttributeName() { - DEFINE_STATIC_LOCAL(QualifiedName, voiceAttr, (nullAtom, "lang", nullAtom)); + static NeverDestroyed<QualifiedName> voiceAttr(nullAtom, "lang", nullAtom); return voiceAttr; } private: WebVTTElement(WebVTTNodeType, Document&); - virtual bool isWebVTTElement() const override { return true; } + bool isWebVTTElement() const override { return true; } unsigned m_isPastNode : 1; unsigned m_webVTTNodeType : 4; @@ -80,10 +83,10 @@ private: AtomicString m_language; }; -void isWebVTTElement(const WebVTTElement&); // Catch unnecessary runtime check of type known at compile time. -inline bool isWebVTTElement(const Node& node) { return node.isWebVTTElement(); } -NODE_TYPE_CASTS(WebVTTElement) - } // namespace WebCore -#endif +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::WebVTTElement) + static bool isType(const WebCore::Node& node) { return node.isWebVTTElement(); } +SPECIALIZE_TYPE_TRAITS_END() + +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/html/track/WebVTTParser.cpp b/Source/WebCore/html/track/WebVTTParser.cpp index 48d729fb6..646110180 100644 --- a/Source/WebCore/html/track/WebVTTParser.cpp +++ b/Source/WebCore/html/track/WebVTTParser.cpp @@ -1,6 +1,7 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2011, 2013 Google Inc. All rights reserved. * Copyright (C) 2013 Cable Television Labs, Inc. + * Copyright (C) 2011-2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -35,8 +36,11 @@ #include "WebVTTParser.h" +#include "HTMLParserIdioms.h" +#include "ISOVTTCue.h" #include "ProcessingInstruction.h" #include "Text.h" +#include "VTTScanner.h" #include "WebVTTElement.h" namespace WebCore { @@ -44,83 +48,46 @@ namespace WebCore { const double secondsPerHour = 3600; const double secondsPerMinute = 60; const double secondsPerMillisecond = 0.001; -const double malformedTime = -1; -const UChar bom = 0xFEFF; const char* fileIdentifier = "WEBVTT"; const unsigned fileIdentifierLength = 6; -String WebVTTParser::collectDigits(const String& input, unsigned* position) -{ - StringBuilder digits; - while (*position < input.length() && isASCIIDigit(input[*position])) - digits.append(input[(*position)++]); - return digits.toString(); -} - -String WebVTTParser::collectWord(const String& input, unsigned* position) -{ - StringBuilder string; - while (*position < input.length() && !isASpace(input[*position])) - string.append(input[(*position)++]); - return string.toString(); -} - -#if ENABLE(WEBVTT_REGIONS) -float WebVTTParser::parseFloatPercentageValue(const String& value, bool& isValidSetting) +bool WebVTTParser::parseFloatPercentageValue(VTTScanner& valueScanner, float& percentage) { + float number; + if (!valueScanner.scanFloat(number)) + return false; // '%' must be present and at the end of the setting value. - if (value.find('%', 1) != value.length() - 1) { - isValidSetting = false; - return 0; - } - - unsigned position = 0; - - StringBuilder floatNumberAsString; - floatNumberAsString.append(WebVTTParser::collectDigits(value, &position)); - - if (value[position] == '.') { - floatNumberAsString.append("."); - position++; - - floatNumberAsString.append(WebVTTParser::collectDigits(value, &position)); - } - float number = floatNumberAsString.toString().toFloat(&isValidSetting); + if (!valueScanner.scan('%')) + return false; - if (isValidSetting && (number <= 0 || number >= 100)) - isValidSetting = false; + if (number < 0 || number > 100) + return false; - return number; + percentage = number; + return true; } -FloatPoint WebVTTParser::parseFloatPercentageValuePair(const String& value, char delimiter, bool& isValidSetting) +bool WebVTTParser::parseFloatPercentageValuePair(VTTScanner& valueScanner, char delimiter, FloatPoint& valuePair) { - // The delimiter can't be the first or second value because a pair of - // percentages (x%,y%) implies that at least the first two characters - // are the first percentage value. - size_t delimiterOffset = value.find(delimiter, 2); - if (delimiterOffset == notFound || delimiterOffset == value.length() - 1) { - isValidSetting = false; - return FloatPoint(0, 0); - } + float firstCoord; + if (!parseFloatPercentageValue(valueScanner, firstCoord)) + return false; - bool isFirstValueValid; - float firstCoord = parseFloatPercentageValue(value.substring(0, delimiterOffset), isFirstValueValid); + if (!valueScanner.scan(delimiter)) + return false; - bool isSecondValueValid; - float secondCoord = parseFloatPercentageValue(value.substring(delimiterOffset + 1, value.length() - 1), isSecondValueValid); + float secondCoord; + if (!parseFloatPercentageValue(valueScanner, secondCoord)) + return false; - isValidSetting = isFirstValueValid && isSecondValueValid; - return FloatPoint(firstCoord, secondCoord); + valuePair = FloatPoint(firstCoord, secondCoord); + return true; } -#endif WebVTTParser::WebVTTParser(WebVTTParserClient* client, ScriptExecutionContext* context) : m_scriptExecutionContext(context) , m_state(Initial) - , m_currentStartTime(0) - , m_currentEndTime(0) - , m_tokenizer(WebVTTTokenizer::create()) + , m_decoder(TextResourceDecoder::create("text/plain", UTF8Encoding())) , m_client(client) { } @@ -131,32 +98,64 @@ void WebVTTParser::getNewCues(Vector<RefPtr<WebVTTCueData>>& outputCues) m_cuelist.clear(); } -#if ENABLE(WEBVTT_REGIONS) -void WebVTTParser::getNewRegions(Vector<RefPtr<TextTrackRegion>>& outputRegions) +void WebVTTParser::getNewRegions(Vector<RefPtr<VTTRegion>>& outputRegions) { outputRegions = m_regionList; m_regionList.clear(); } -#endif + +void WebVTTParser::parseFileHeader(String&& data) +{ + m_state = Initial; + m_lineReader.reset(); + m_lineReader.append(WTFMove(data)); + parse(); +} void WebVTTParser::parseBytes(const char* data, unsigned length) { - // 4.8.10.13.3 WHATWG WebVTT Parser algorithm. - // 1-3 - Initial setup. - unsigned position = 0; - - while (position < length) { - String line = collectNextLine(data, length, &position); - if (line.isNull()) { - m_buffer.append(data + position, length - position); - return; - } + m_lineReader.append(m_decoder->decode(data, length)); + parse(); +} + +void WebVTTParser::parseCueData(const ISOWebVTTCue& data) +{ + auto cue = WebVTTCueData::create(); + + MediaTime startTime = data.presentationTime(); + cue->setStartTime(startTime); + cue->setEndTime(startTime + data.duration()); + + cue->setContent(data.cueText()); + cue->setId(data.id()); + cue->setSettings(data.settings()); + + MediaTime originalStartTime; + if (WebVTTParser::collectTimeStamp(data.originalStartTime(), originalStartTime)) + cue->setOriginalStartTime(originalStartTime); + + m_cuelist.append(WTFMove(cue)); + if (m_client) + m_client->newCuesParsed(); +} +void WebVTTParser::flush() +{ + m_lineReader.append(m_decoder->flush()); + m_lineReader.appendEndOfStream(); + parse(); + flushPendingCue(); +} + +void WebVTTParser::parse() +{ + // WebVTT parser algorithm. (5.1 WebVTT file parsing.) + // Steps 1 - 3 - Initial setup. + while (auto line = m_lineReader.nextLine()) { switch (m_state) { case Initial: - - // 4-12 - Collect the first line and check for "WEBVTT". - if (!hasRequiredFileIdentifier(line)) { + // Steps 4 - 9 - Check for a valid WebVTT signature. + if (!hasRequiredFileIdentifier(*line)) { if (m_client) m_client->fileFailedToParse(); return; @@ -166,48 +165,53 @@ void WebVTTParser::parseBytes(const char* data, unsigned length) break; case Header: - // 13-18 - Allow a header (comment area) under the WEBVTT line. -#if ENABLE(WEBVTT_REGIONS) - if (line.isEmpty()) { + collectMetadataHeader(*line); + + if (line->isEmpty()) { + // Steps 10-14 - Allow a header (comment area) under the WEBVTT line. if (m_client && m_regionList.size()) m_client->newRegionsParsed(); - m_state = Id; break; } - collectHeader(line); + // Step 15 - Break out of header loop if the line could be a timestamp line. + if (line->contains("-->")) + m_state = recoverCue(*line); - break; - - case Metadata: -#endif - if (line.isEmpty()) - m_state = Id; + // Step 16 - Line is not the empty string and does not contain "-->". break; case Id: - // 19-29 - Allow any number of line terminators, then initialize new cue values. - if (line.isEmpty()) + // Steps 17 - 20 - Allow any number of line terminators, then initialize new cue values. + if (line->isEmpty()) break; + + // Step 21 - Cue creation (start a new cue). resetCueValues(); - // 30-39 - Check if this line contains an optional identifier or timing data. - m_state = collectCueId(line); + // Steps 22 - 25 - Check if this line contains an optional identifier or timing data. + m_state = collectCueId(*line); break; case TimingsAndSettings: - // 40 - Collect cue timings and settings. - m_state = collectTimingsAndSettings(line); + // Steps 26 - 27 - Discard current cue if the line is empty. + if (line->isEmpty()) { + m_state = Id; + break; + } + + // Steps 28 - 29 - Collect cue timings and settings. + m_state = collectTimingsAndSettings(*line); break; case CueText: - // 41-53 - Collect the cue text, create a cue, and add it to the output. - m_state = collectCueText(line); + // Steps 31 - 41 - Collect the cue text, create a cue, and add it to the output. + m_state = collectCueText(*line); break; case BadCue: - // 54-62 - Collect and discard the remaining cue. - m_state = ignoreBadCue(line); + // Steps 42 - 48 - Discard lines until an empty line or a potential timing line is seen. + m_state = ignoreBadCue(*line); break; case Finished: @@ -224,55 +228,47 @@ void WebVTTParser::fileFinished() m_state = Finished; } +void WebVTTParser::flushPendingCue() +{ + ASSERT(m_lineReader.isAtEndOfStream()); + // If we're in the CueText state when we run out of data, we emit the pending cue. + if (m_state == CueText) + createNewCue(); +} + bool WebVTTParser::hasRequiredFileIdentifier(const String& line) { // A WebVTT file identifier consists of an optional BOM character, // the string "WEBVTT" followed by an optional space or tab character, // and any number of characters that are not line terminators ... - unsigned linePos = 0; - - if (line.isEmpty()) + if (!line.startsWith(fileIdentifier, fileIdentifierLength)) return false; - - if (line[0] == bom) - ++linePos; - - if (line.length() < fileIdentifierLength + linePos) - return false; - - for (unsigned i = 0; i < fileIdentifierLength; ++i, ++linePos) { - if (line[linePos] != fileIdentifier[i]) - return false; - } - - if (linePos < line.length() && line[linePos] != ' ' && line[linePos] != '\t') + if (line.length() > fileIdentifierLength && !isHTMLSpace(line[fileIdentifierLength])) return false; return true; } -#if ENABLE(WEBVTT_REGIONS) -void WebVTTParser::collectHeader(const String& line) +void WebVTTParser::collectMetadataHeader(const String& line) { - // 4.1 Extension of WebVTT header parsing (11 - 15) - DEFINE_STATIC_LOCAL(const AtomicString, regionHeaderName, ("Region", AtomicString::ConstructFromLiteral)); + // WebVTT header parsing (WebVTT parser algorithm step 12) + static NeverDestroyed<const AtomicString> regionHeaderName("Region", AtomicString::ConstructFromLiteral); - // 15.4 If line contains the character ":" (A U+003A COLON), then set metadata's + // Step 12.4 If line contains the character ":" (A U+003A COLON), then set metadata's // name to the substring of line before the first ":" character and // metadata's value to the substring after this character. - if (!line.contains(":")) + size_t colonPosition = line.find(':'); + if (colonPosition == notFound) return; - unsigned colonPosition = line.find(":"); - m_currentHeaderName = line.substring(0, colonPosition); + String headerName = line.substring(0, colonPosition); - // 15.5 If metadata's name equals "Region": - if (m_currentHeaderName == regionHeaderName) { - m_currentHeaderValue = line.substring(colonPosition + 1, line.length() - 1); - // 15.5.1 - 15.5.8 Region creation: Let region be a new text track region [...] - createNewRegion(); + // Step 12.5 If metadata's name equals "Region": + if (headerName == regionHeaderName) { + String headerValue = line.substring(colonPosition + 1, line.length() - 1); + // Steps 12.5.1 - 12.5.11 Region creation: Let region be a new text track region [...] + createNewRegion(headerValue); } } -#endif WebVTTParser::ParseState WebVTTParser::collectCueId(const String& line) { @@ -284,97 +280,126 @@ WebVTTParser::ParseState WebVTTParser::collectCueId(const String& line) WebVTTParser::ParseState WebVTTParser::collectTimingsAndSettings(const String& line) { - // 4.8.10.13.3 Collect WebVTT cue timings and settings. - // 1-3 - Let input be the string being parsed and position be a pointer into input - unsigned position = 0; - skipWhiteSpace(line, &position); - - // 4-5 - Collect a WebVTT timestamp. If that fails, then abort and return failure. Otherwise, let cue's text track cue start time be the collected time. - m_currentStartTime = collectTimeStamp(line, &position); - if (m_currentStartTime == malformedTime) - return BadCue; - if (position >= line.length()) - return BadCue; - char nextChar = line[position++]; - if (nextChar != ' ' && nextChar != '\t') + if (line.isEmpty()) return BadCue; - skipWhiteSpace(line, &position); - // 6-9 - If the next three characters are not "-->", abort and return failure. - if (line.find("-->", position) == notFound) - return BadCue; - position += 3; - if (position >= line.length()) + VTTScanner input(line); + + // Collect WebVTT cue timings and settings. (5.3 WebVTT cue timings and settings parsing.) + // Steps 1 - 3 - Let input be the string being parsed and position be a pointer into input + input.skipWhile<isHTMLSpace<UChar>>(); + + // Steps 4 - 5 - Collect a WebVTT timestamp. If that fails, then abort and return failure. Otherwise, let cue's text track cue start time be the collected time. + if (!collectTimeStamp(input, m_currentStartTime)) return BadCue; - nextChar = line[position++]; - if (nextChar != ' ' && nextChar != '\t') + + input.skipWhile<isHTMLSpace<UChar>>(); + + // Steps 6 - 9 - If the next three characters are not "-->", abort and return failure. + if (!input.scan("-->")) return BadCue; - skipWhiteSpace(line, &position); + + input.skipWhile<isHTMLSpace<UChar>>(); - // 10-11 - Collect a WebVTT timestamp. If that fails, then abort and return failure. Otherwise, let cue's text track cue end time be the collected time. - m_currentEndTime = collectTimeStamp(line, &position); - if (m_currentEndTime == malformedTime) + // Steps 10 - 11 - Collect a WebVTT timestamp. If that fails, then abort and return failure. Otherwise, let cue's text track cue end time be the collected time. + if (!collectTimeStamp(input, m_currentEndTime)) return BadCue; - skipWhiteSpace(line, &position); - // 12 - Parse the WebVTT settings for the cue (conducted in TextTrackCue). - m_currentSettings = line.substring(position, line.length()-1); + input.skipWhile<isHTMLSpace<UChar>>(); + + // Step 12 - Parse the WebVTT settings for the cue (conducted in TextTrackCue). + m_currentSettings = input.restOfInputAsString(); return CueText; } WebVTTParser::ParseState WebVTTParser::collectCueText(const String& line) { + // Step 34. if (line.isEmpty()) { createNewCue(); return Id; } + // Step 35. + if (line.contains("-->")) { + // Step 39-40. + createNewCue(); + + // Step 41 - New iteration of the cue loop. + return recoverCue(line); + } if (!m_currentContent.isEmpty()) - m_currentContent.append("\n"); + m_currentContent.append('\n'); m_currentContent.append(line); - + return CueText; } +WebVTTParser::ParseState WebVTTParser::recoverCue(const String& line) +{ + // Step 17 and 21. + resetCueValues(); + + // Step 22. + return collectTimingsAndSettings(line); +} + WebVTTParser::ParseState WebVTTParser::ignoreBadCue(const String& line) { - if (!line.isEmpty()) - return BadCue; - return Id; + if (line.isEmpty()) + return Id; + if (line.contains("-->")) + return recoverCue(line); + return BadCue; } -PassRefPtr<DocumentFragment> WebVTTParser::createDocumentFragmentFromCueText(const String& text) +// A helper class for the construction of a "cue fragment" from the cue text. +class WebVTTTreeBuilder { +public: + WebVTTTreeBuilder(Document& document) + : m_document(document) { } + + Ref<DocumentFragment> buildFromString(const String& cueText); + +private: + void constructTreeFromToken(Document&); + + WebVTTToken m_token; + RefPtr<ContainerNode> m_currentNode; + Vector<AtomicString> m_languageStack; + Document& m_document; +}; + +Ref<DocumentFragment> WebVTTTreeBuilder::buildFromString(const String& cueText) { // Cue text processing based on - // 4.8.10.13.4 WebVTT cue text parsing rules and - // 4.8.10.13.5 WebVTT cue text DOM construction rules. - - ASSERT(m_scriptExecutionContext->isDocument()); - Document* document = toDocument(m_scriptExecutionContext); - - RefPtr<DocumentFragment> fragment = DocumentFragment::create(*document); + // 5.4 WebVTT cue text parsing rules, and + // 5.5 WebVTT cue text DOM construction rules. + auto fragment = DocumentFragment::create(m_document); - if (text.isEmpty()) { - fragment->parserAppendChild(Text::create(*document, emptyString())); - return fragment.release(); + if (cueText.isEmpty()) { + fragment->parserAppendChild(Text::create(m_document, emptyString())); + return fragment; } - m_currentNode = fragment; - m_tokenizer->reset(); - m_token.clear(); - + m_currentNode = fragment.ptr(); + + WebVTTTokenizer tokenizer(cueText); m_languageStack.clear(); - SegmentedString content(text); - while (m_tokenizer->nextToken(content, m_token)) - constructTreeFromToken(document); + + while (tokenizer.nextToken(m_token)) + constructTreeFromToken(m_document); - return fragment.release(); + return fragment; } -void WebVTTParser::createNewCue() +Ref<DocumentFragment> WebVTTParser::createDocumentFragmentFromCueText(Document& document, const String& cueText) { - if (!m_currentContent.length()) - return; + WebVTTTreeBuilder treeBuilder(document); + return treeBuilder.buildFromString(cueText); +} +void WebVTTParser::createNewCue() +{ RefPtr<WebVTTCueData> cue = WebVTTCueData::create(); cue->setStartTime(m_currentStartTime); cue->setEndTime(m_currentEndTime); @@ -391,21 +416,21 @@ void WebVTTParser::resetCueValues() { m_currentId = emptyString(); m_currentSettings = emptyString(); - m_currentStartTime = 0; - m_currentEndTime = 0; + m_currentStartTime = MediaTime::zeroTime(); + m_currentEndTime = MediaTime::zeroTime(); m_currentContent.clear(); } -#if ENABLE(WEBVTT_REGIONS) -void WebVTTParser::createNewRegion() +void WebVTTParser::createNewRegion(const String& headerValue) { - if (!m_currentHeaderValue.length()) + if (headerValue.isEmpty()) return; - RefPtr<TextTrackRegion> region = TextTrackRegion::create(); - region->setRegionSettings(m_currentHeaderValue); + // Steps 12.5.1 - 12.5.9 - Construct and initialize a WebVTT Region object. + RefPtr<VTTRegion> region = VTTRegion::create(*m_scriptExecutionContext); + region->setRegionSettings(headerValue); - // 15.5.10 If the text track list of regions regions contains a region + // Step 12.5.10 If the text track list of regions regions contains a region // with the same region identifier value as region, remove that region. for (size_t i = 0; i < m_regionList.size(); ++i) if (m_regionList[i]->id() == region->id()) { @@ -413,73 +438,66 @@ void WebVTTParser::createNewRegion() break; } + // Step 12.5.11 m_regionList.append(region); } -#endif -double WebVTTParser::collectTimeStamp(const String& line, unsigned* position) +bool WebVTTParser::collectTimeStamp(const String& line, MediaTime& timeStamp) { - // 4.8.10.13.3 Collect a WebVTT timestamp. - // 1-4 - Initial checks, let most significant units be minutes. + if (line.isEmpty()) + return false; + + VTTScanner input(line); + return collectTimeStamp(input, timeStamp); +} + +bool WebVTTParser::collectTimeStamp(VTTScanner& input, MediaTime& timeStamp) +{ + // Collect a WebVTT timestamp (5.3 WebVTT cue timings and settings parsing.) + // Steps 1 - 4 - Initial checks, let most significant units be minutes. enum Mode { minutes, hours }; Mode mode = minutes; - if (*position >= line.length() || !isASCIIDigit(line[*position])) - return malformedTime; - // 5-6 - Collect a sequence of characters that are 0-9. - String digits1 = collectDigits(line, position); - int value1 = digits1.toInt(); - - // 7 - If not 2 characters or value is greater than 59, interpret as hours. - if (digits1.length() != 2 || value1 > 59) + // Steps 5 - 7 - Collect a sequence of characters that are 0-9. + // If not 2 characters or value is greater than 59, interpret as hours. + int value1; + unsigned value1Digits = input.scanDigits(value1); + if (!value1Digits) + return false; + if (value1Digits != 2 || value1 > 59) mode = hours; - // 8-12 - Collect the next sequence of 0-9 after ':' (must be 2 chars). - if (*position >= line.length() || line[(*position)++] != ':') - return malformedTime; - if (*position >= line.length() || !isASCIIDigit(line[(*position)])) - return malformedTime; - String digits2 = collectDigits(line, position); - int value2 = digits2.toInt(); - if (digits2.length() != 2) - return malformedTime; - - // 13 - Detect whether this timestamp includes hours. + // Steps 8 - 11 - Collect the next sequence of 0-9 after ':' (must be 2 chars). + int value2; + if (!input.scan(':') || input.scanDigits(value2) != 2) + return false; + + // Step 12 - Detect whether this timestamp includes hours. int value3; - if (mode == hours || (*position < line.length() && line[*position] == ':')) { - if (*position >= line.length() || line[(*position)++] != ':') - return malformedTime; - if (*position >= line.length() || !isASCIIDigit(line[*position])) - return malformedTime; - String digits3 = collectDigits(line, position); - if (digits3.length() != 2) - return malformedTime; - value3 = digits3.toInt(); + if (mode == hours || input.match(':')) { + if (!input.scan(':') || input.scanDigits(value3) != 2) + return false; } else { value3 = value2; value2 = value1; value1 = 0; } - // 14-19 - Collect next sequence of 0-9 after '.' (must be 3 chars). - if (*position >= line.length() || line[(*position)++] != '.') - return malformedTime; - if (*position >= line.length() || !isASCIIDigit(line[*position])) - return malformedTime; - String digits4 = collectDigits(line, position); - if (digits4.length() != 3) - return malformedTime; - int value4 = digits4.toInt(); + // Steps 13 - 17 - Collect next sequence of 0-9 after '.' (must be 3 chars). + int value4; + if (!input.scan('.') || input.scanDigits(value4) != 3) + return false; if (value2 > 59 || value3 > 59) - return malformedTime; + return false; - // 20-21 - Calculate result. - return (value1 * secondsPerHour) + (value2 * secondsPerMinute) + value3 + (value4 * secondsPerMillisecond); + // Steps 18 - 19 - Calculate result. + timeStamp = MediaTime::createWithDouble((value1 * secondsPerHour) + (value2 * secondsPerMinute) + value3 + (value4 * secondsPerMillisecond)); + return true; } static WebVTTNodeType tokenToNodeType(WebVTTToken& token) { - switch (token.name().size()) { + switch (token.name().length()) { case 1: if (token.name()[0] == 'c') return WebVTTNodeTypeClass; @@ -506,91 +524,77 @@ static WebVTTNodeType tokenToNodeType(WebVTTToken& token) return WebVTTNodeTypeNone; } -void WebVTTParser::constructTreeFromToken(Document* document) +void WebVTTTreeBuilder::constructTreeFromToken(Document& document) { - QualifiedName tagName(nullAtom, AtomicString(m_token.name()), xhtmlNamespaceURI); - // http://dev.w3.org/html5/webvtt/#webvtt-cue-text-dom-construction-rules switch (m_token.type()) { case WebVTTTokenTypes::Character: { - String content(m_token.characters()); // FIXME: This should be 8bit if possible. - RefPtr<Text> child = Text::create(*document, content); - m_currentNode->parserAppendChild(child); + m_currentNode->parserAppendChild(Text::create(document, m_token.characters())); break; } case WebVTTTokenTypes::StartTag: { - RefPtr<WebVTTElement> child; WebVTTNodeType nodeType = tokenToNodeType(m_token); - if (nodeType != WebVTTNodeTypeNone) - child = WebVTTElement::create(nodeType, *document); - if (child) { - if (m_token.classes().size() > 0) - child->setAttribute(classAttr, AtomicString(m_token.classes())); - - if (child->webVTTNodeType() == WebVTTNodeTypeVoice) - child->setAttribute(WebVTTElement::voiceAttributeName(), AtomicString(m_token.annotation())); - else if (child->webVTTNodeType() == WebVTTNodeTypeLanguage) { - m_languageStack.append(AtomicString(m_token.annotation())); - child->setAttribute(WebVTTElement::langAttributeName(), m_languageStack.last()); - } - if (!m_languageStack.isEmpty()) - child->setLanguage(m_languageStack.last()); - m_currentNode->parserAppendChild(child); - m_currentNode = child; + if (nodeType == WebVTTNodeTypeNone) + break; + + WebVTTNodeType currentType = is<WebVTTElement>(*m_currentNode) ? downcast<WebVTTElement>(*m_currentNode).webVTTNodeType() : WebVTTNodeTypeNone; + // <rt> is only allowed if the current node is <ruby>. + if (nodeType == WebVTTNodeTypeRubyText && currentType != WebVTTNodeTypeRuby) + break; + + auto child = WebVTTElement::create(nodeType, document); + if (!m_token.classes().isEmpty()) + child->setAttributeWithoutSynchronization(classAttr, m_token.classes()); + + if (nodeType == WebVTTNodeTypeVoice) + child->setAttributeWithoutSynchronization(WebVTTElement::voiceAttributeName(), m_token.annotation()); + else if (nodeType == WebVTTNodeTypeLanguage) { + m_languageStack.append(m_token.annotation()); + child->setAttributeWithoutSynchronization(WebVTTElement::langAttributeName(), m_languageStack.last()); } + if (!m_languageStack.isEmpty()) + child->setLanguage(m_languageStack.last()); + m_currentNode->parserAppendChild(child); + m_currentNode = WTFMove(child); break; } case WebVTTTokenTypes::EndTag: { WebVTTNodeType nodeType = tokenToNodeType(m_token); - if (nodeType != WebVTTNodeTypeNone) { - if (nodeType == WebVTTNodeTypeLanguage && m_currentNode->isWebVTTElement() && toWebVTTElement(m_currentNode.get())->webVTTNodeType() == WebVTTNodeTypeLanguage) - m_languageStack.removeLast(); - if (m_currentNode->parentNode()) - m_currentNode = m_currentNode->parentNode(); + if (nodeType == WebVTTNodeTypeNone) + break; + + // The only non-VTTElement would be the DocumentFragment root. (Text + // nodes and PIs will never appear as m_currentNode.) + if (!is<WebVTTElement>(*m_currentNode)) + break; + + WebVTTNodeType currentType = downcast<WebVTTElement>(*m_currentNode).webVTTNodeType(); + bool matchesCurrent = nodeType == currentType; + if (!matchesCurrent) { + // </ruby> auto-closes <rt> + if (currentType == WebVTTNodeTypeRubyText && nodeType == WebVTTNodeTypeRuby) { + if (m_currentNode->parentNode()) + m_currentNode = m_currentNode->parentNode(); + } else + break; } + if (nodeType == WebVTTNodeTypeLanguage) + m_languageStack.removeLast(); + if (m_currentNode->parentNode()) + m_currentNode = m_currentNode->parentNode(); break; } case WebVTTTokenTypes::TimestampTag: { - unsigned position = 0; - String charactersString(StringImpl::create8BitIfPossible(m_token.characters())); - double time = collectTimeStamp(charactersString, &position); - if (time != malformedTime) - m_currentNode->parserAppendChild(ProcessingInstruction::create(*document, "timestamp", charactersString)); + String charactersString = m_token.characters(); + MediaTime parsedTimeStamp; + if (WebVTTParser::collectTimeStamp(charactersString, parsedTimeStamp)) + m_currentNode->parserAppendChild(ProcessingInstruction::create(document, "timestamp", charactersString)); break; } default: break; } - m_token.clear(); -} - -void WebVTTParser::skipWhiteSpace(const String& line, unsigned* position) -{ - while (*position < line.length() && isASpace(line[*position])) - (*position)++; -} - -String WebVTTParser::collectNextLine(const char* data, unsigned length, unsigned* position) -{ - unsigned currentPosition = *position; - while (currentPosition < length && data[currentPosition] != '\r' && data[currentPosition] != '\n') - currentPosition++; - if (currentPosition >= length) - return String(); - String line = String::fromUTF8(data + *position , currentPosition - *position); - if (data[currentPosition] == '\r') - currentPosition++; - if (currentPosition < length && data[currentPosition] == '\n') - currentPosition++; - *position = currentPosition; - if (m_buffer.isEmpty()) - return line; - - String lineWithBuffer = String::fromUTF8(m_buffer.data(), m_buffer.size()); - lineWithBuffer.append(line); - m_buffer.clear(); - return lineWithBuffer; } } diff --git a/Source/WebCore/html/track/WebVTTParser.h b/Source/WebCore/html/track/WebVTTParser.h index eb27d7930..69ac0552a 100644 --- a/Source/WebCore/html/track/WebVTTParser.h +++ b/Source/WebCore/html/track/WebVTTParser.h @@ -1,6 +1,7 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2011, 2013 Google Inc. All rights reserved. * Copyright (C) 2013 Cable Television Labs, Inc. + * Copyright (C) 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -29,16 +30,18 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WebVTTParser_h -#define WebVTTParser_h +#pragma once #if ENABLE(VIDEO_TRACK) +#include "BufferedLineReader.h" #include "DocumentFragment.h" #include "HTMLNames.h" -#include "TextTrackRegion.h" +#include "TextResourceDecoder.h" +#include "VTTRegion.h" #include "WebVTTTokenizer.h" -#include <wtf/PassOwnPtr.h> +#include <memory> +#include <wtf/MediaTime.h> #include <wtf/text/StringBuilder.h> namespace WebCore { @@ -46,29 +49,29 @@ namespace WebCore { using namespace HTMLNames; class Document; +class ISOWebVTTCue; +class VTTScanner; class WebVTTParserClient { public: virtual ~WebVTTParserClient() { } virtual void newCuesParsed() = 0; -#if ENABLE(WEBVTT_REGIONS) virtual void newRegionsParsed() = 0; -#endif virtual void fileFailedToParse() = 0; }; -class WebVTTCueData : public RefCounted<WebVTTCueData> { +class WebVTTCueData final : public RefCounted<WebVTTCueData> { public: - static PassRefPtr<WebVTTCueData> create() { return adoptRef(new WebVTTCueData()); } - virtual ~WebVTTCueData() { } + static Ref<WebVTTCueData> create() { return adoptRef(*new WebVTTCueData()); } + ~WebVTTCueData() { } - double startTime() const { return m_startTime; } - void setStartTime(double startTime) { m_startTime = startTime; } + MediaTime startTime() const { return m_startTime; } + void setStartTime(const MediaTime& startTime) { m_startTime = startTime; } - double endTime() const { return m_endTime; } - void setEndTime(double endTime) { m_endTime = endTime; } + MediaTime endTime() const { return m_endTime; } + void setEndTime(const MediaTime& endTime) { m_endTime = endTime; } String id() const { return m_id; } void setId(String id) { m_id = id; } @@ -79,30 +82,25 @@ public: String settings() const { return m_settings; } void setSettings(String settings) { m_settings = settings; } + MediaTime originalStartTime() const { return m_originalStartTime; } + void setOriginalStartTime(const MediaTime& time) { m_originalStartTime = time; } + private: - WebVTTCueData() - : m_startTime(0) - , m_endTime(0) - { - } + WebVTTCueData() { } - double m_startTime; - double m_endTime; + MediaTime m_startTime; + MediaTime m_endTime; + MediaTime m_originalStartTime; String m_id; String m_content; String m_settings; }; -class WebVTTParser { +class WebVTTParser final { public: - virtual ~WebVTTParser() { } - enum ParseState { Initial, Header, -#if ENABLE(WEBVTT_REGIONS) - Metadata, -#endif Id, TimingsAndSettings, CueText, @@ -110,11 +108,8 @@ public: Finished }; - static OwnPtr<WebVTTParser> create(WebVTTParserClient* client, ScriptExecutionContext* context) - { - return adoptPtr(new WebVTTParser(client, context)); - } - + WebVTTParser(WebVTTParserClient*, ScriptExecutionContext*); + static inline bool isRecognizedTag(const AtomicString& tagName) { return tagName == iTag @@ -124,91 +119,68 @@ public: || tagName == rtTag; } - static inline bool isASpace(char c) - { - // WebVTT space characters are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR). - return c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r'; - } - static inline bool isValidSettingDelimiter(char c) + static inline bool isValidSettingDelimiter(UChar c) { // ... a WebVTT cue consists of zero or more of the following components, in any order, separated from each other by one or more // U+0020 SPACE characters or U+0009 CHARACTER TABULATION (tab) characters. return c == ' ' || c == '\t'; } - static String collectDigits(const String&, unsigned*); - static String collectWord(const String&, unsigned*); + static bool collectTimeStamp(const String&, MediaTime&); -#if ENABLE(WEBVTT_REGIONS) // Useful functions for parsing percentage settings. - static float parseFloatPercentageValue(const String&, bool&); - static FloatPoint parseFloatPercentageValuePair(const String&, char, bool&); -#endif + static bool parseFloatPercentageValue(VTTScanner& valueScanner, float&); + static bool parseFloatPercentageValuePair(VTTScanner& valueScanner, char, FloatPoint&); // Input data to the parser to parse. - void parseBytes(const char* data, unsigned length); + void parseBytes(const char*, unsigned); + void parseFileHeader(String&&); + void parseCueData(const ISOWebVTTCue&); + void flush(); void fileFinished(); // Transfers ownership of last parsed cues to caller. void getNewCues(Vector<RefPtr<WebVTTCueData>>&); -#if ENABLE(WEBVTT_REGIONS) - void getNewRegions(Vector<RefPtr<TextTrackRegion>>&); -#endif + void getNewRegions(Vector<RefPtr<VTTRegion>>&); - PassRefPtr<DocumentFragment> createDocumentFragmentFromCueText(const String&); - double collectTimeStamp(const String&, unsigned*); + // Create the DocumentFragment representation of the WebVTT cue text. + static Ref<DocumentFragment> createDocumentFragmentFromCueText(Document&, const String&); protected: - WebVTTParser(WebVTTParserClient*, ScriptExecutionContext*); - ScriptExecutionContext* m_scriptExecutionContext; ParseState m_state; private: + void parse(); + void flushPendingCue(); bool hasRequiredFileIdentifier(const String&); ParseState collectCueId(const String&); ParseState collectTimingsAndSettings(const String&); ParseState collectCueText(const String&); + ParseState recoverCue(const String&); ParseState ignoreBadCue(const String&); void createNewCue(); void resetCueValues(); -#if ENABLE(WEBVTT_REGIONS) - void collectHeader(const String&); - void createNewRegion(); -#endif + void collectMetadataHeader(const String&); + void createNewRegion(const String& headerValue); - void skipWhiteSpace(const String&, unsigned*); - String collectNextLine(const char* data, unsigned length, unsigned*); + static bool collectTimeStamp(VTTScanner& input, MediaTime& timeStamp); - void constructTreeFromToken(Document*); - - String m_currentHeaderName; - String m_currentHeaderValue; - - Vector<char> m_buffer; + BufferedLineReader m_lineReader; + RefPtr<TextResourceDecoder> m_decoder; String m_currentId; - double m_currentStartTime; - double m_currentEndTime; + MediaTime m_currentStartTime; + MediaTime m_currentEndTime; StringBuilder m_currentContent; String m_currentSettings; - - WebVTTToken m_token; - OwnPtr<WebVTTTokenizer> m_tokenizer; - - RefPtr<ContainerNode> m_currentNode; WebVTTParserClient* m_client; - Vector<AtomicString> m_languageStack; Vector<RefPtr<WebVTTCueData>> m_cuelist; - -#if ENABLE(WEBVTT_REGIONS) - Vector<RefPtr<TextTrackRegion>> m_regionList; -#endif + Vector<RefPtr<VTTRegion>> m_regionList; }; } // namespace WebCore -#endif -#endif +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/html/track/WebVTTToken.h b/Source/WebCore/html/track/WebVTTToken.h index 7cf1a27d3..317e1006d 100644 --- a/Source/WebCore/html/track/WebVTTToken.h +++ b/Source/WebCore/html/track/WebVTTToken.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2011, 2013 Google Inc. All rights reserved. + * Copyright (C) 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -28,8 +29,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WebVTTToken_h -#define WebVTTToken_h +#pragma once #if ENABLE(VIDEO_TRACK) @@ -43,170 +43,56 @@ public: StartTag, EndTag, TimestampTag, - EndOfFile, }; }; class WebVTTToken { - WTF_MAKE_NONCOPYABLE(WebVTTToken); - WTF_MAKE_FAST_ALLOCATED; public: typedef WebVTTTokenTypes Type; - typedef WTF::Vector<UChar, 1024> DataVector; // FIXME: Is this too large for WebVTT? - WebVTTToken() { clear(); } + WebVTTToken() + : m_type(Type::Uninitialized) { } - void appendToName(UChar character) + static WebVTTToken StringToken(const String& characterData) { - ASSERT(m_type == WebVTTTokenTypes::StartTag || m_type == WebVTTTokenTypes::EndTag); - ASSERT(character); - m_data.append(character); + return WebVTTToken(Type::Character, characterData); } - Type::Type type() const { return m_type; } - - const DataVector& name() const - { - return m_data; - } - - const DataVector& characters() const - { - ASSERT(m_type == Type::Character || m_type == Type::TimestampTag); - return m_data; - } - - // Starting a character token works slightly differently than starting - // other types of tokens because we want to save a per-character branch. - void ensureIsCharacterToken() - { - ASSERT(m_type == Type::Uninitialized || m_type == Type::Character); - m_type = Type::Character; - } - - void appendToCharacter(char character) - { - ASSERT(m_type == Type::Character); - m_data.append(character); - } - - void appendToCharacter(UChar character) - { - ASSERT(m_type == Type::Character); - m_data.append(character); - } - - void appendToCharacter(const Vector<LChar, 32>& characters) - { - ASSERT(m_type == Type::Character); - m_data.appendVector(characters); - } - - void beginEmptyStartTag() - { - ASSERT(m_type == Type::Uninitialized); - m_type = Type::StartTag; - m_data.clear(); - } - - void beginStartTag(UChar character) - { - ASSERT(character); - ASSERT(m_type == Type::Uninitialized); - m_type = Type::StartTag; - m_data.append(character); - } - - void beginEndTag(LChar character) - { - ASSERT(m_type == Type::Uninitialized); - m_type = Type::EndTag; - m_data.append(character); - } - - void beginTimestampTag(UChar character) - { - ASSERT(character); - ASSERT(m_type == Type::Uninitialized); - m_type = Type::TimestampTag; - m_data.append(character); - } - - void appendToTimestamp(UChar character) + static WebVTTToken StartTag(const String& tagName, const AtomicString& classes = emptyAtom, const AtomicString& annotation = emptyAtom) { - ASSERT(character); - ASSERT(m_type == Type::TimestampTag); - m_data.append(character); + WebVTTToken token(Type::StartTag, tagName); + token.m_classes = classes; + token.m_annotation = annotation; + return token; } - void appendToClass(UChar character) + static WebVTTToken EndTag(const String& tagName) { - appendToStartType(character); + return WebVTTToken(Type::EndTag, tagName); } - void addNewClass() - { - ASSERT(m_type == Type::StartTag); - if (!m_classes.isEmpty()) - m_classes.append(' '); - m_classes.appendVector(m_currentBuffer); - m_currentBuffer.clear(); - } - - const DataVector& classes() const + static WebVTTToken TimestampTag(const String& timestampData) { - return m_classes; + return WebVTTToken(Type::TimestampTag, timestampData); } - void appendToAnnotation(UChar character) - { - appendToStartType(character); - } - - void addNewAnnotation() - { - ASSERT(m_type == Type::StartTag); - m_annotation.clear(); - m_annotation.appendVector(m_currentBuffer); - m_currentBuffer.clear(); - } - - const DataVector& annotation() const - { - return m_annotation; - } - - void makeEndOfFile() - { - ASSERT(m_type == Type::Uninitialized); - m_type = Type::EndOfFile; - } - - void clear() - { - m_type = Type::Uninitialized; - m_data.clear(); - m_annotation.clear(); - m_classes.clear(); - m_currentBuffer.clear(); - } + Type::Type type() const { return m_type; } + const String& name() const { return m_data; } + const String& characters() const { return m_data; } + const AtomicString& classes() const { return m_classes; } + const AtomicString& annotation() const { return m_annotation; } private: - void appendToStartType(UChar character) - { - ASSERT(character); - ASSERT(m_type == Type::StartTag); - m_currentBuffer.append(character); - } + WebVTTToken(Type::Type type, const String& data) + : m_type(type) + , m_data(data) { } Type::Type m_type; - DataVector m_data; - DataVector m_annotation; - DataVector m_classes; - DataVector m_currentBuffer; + String m_data; + AtomicString m_annotation; + AtomicString m_classes; }; -} +} // namespace WebCore -#endif -#endif +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/html/track/WebVTTTokenizer.cpp b/Source/WebCore/html/track/WebVTTTokenizer.cpp index 9bde924c8..024c04a09 100644 --- a/Source/WebCore/html/track/WebVTTTokenizer.cpp +++ b/Source/WebCore/html/track/WebVTTTokenizer.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2011, 2013 Google Inc. All rights reserved. + * Copyright (C) 2014-2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -29,206 +30,201 @@ */ #include "config.h" +#include "WebVTTTokenizer.h" #if ENABLE(VIDEO_TRACK) -#include "WebVTTTokenizer.h" - #include "MarkupTokenizerInlines.h" +#include <wtf/text/StringBuilder.h> #include <wtf/unicode/CharacterNames.h> namespace WebCore { -#define WEBVTT_BEGIN_STATE(stateName) BEGIN_STATE(WebVTTTokenizerState, stateName) -#define WEBVTT_ADVANCE_TO(stateName) ADVANCE_TO(WebVTTTokenizerState, stateName) +#define WEBVTT_ADVANCE_TO(stateName) \ + do { \ + ASSERT(!m_input.isEmpty()); \ + m_preprocessor.advance(m_input); \ + character = m_preprocessor.nextInputCharacter(); \ + goto stateName; \ + } while (false) -WebVTTTokenizer::WebVTTTokenizer() - : m_inputStreamPreprocessor(this) +template<unsigned charactersCount> ALWAYS_INLINE bool equalLiteral(const StringBuilder& s, const char (&characters)[charactersCount]) { - reset(); + return WTF::equal(s, reinterpret_cast<const LChar*>(characters), charactersCount - 1); } -template <typename CharacterType> -inline bool vectorEqualsString(const Vector<CharacterType, 32>& vector, const String& string) +static void addNewClass(StringBuilder& classes, const StringBuilder& newClass) { - if (vector.size() != string.length()) - return false; - - if (!string.length()) - return true; + if (!classes.isEmpty()) + classes.append(' '); + classes.append(newClass); +} - return equal(string.impl(), vector.data(), vector.size()); +inline bool emitToken(WebVTTToken& resultToken, const WebVTTToken& token) +{ + resultToken = token; + return true; } -void WebVTTTokenizer::reset() +inline bool advanceAndEmitToken(SegmentedString& source, WebVTTToken& resultToken, const WebVTTToken& token) { - m_state = WebVTTTokenizerState::DataState; - m_token = 0; - m_buffer.clear(); + source.advance(); + return emitToken(resultToken, token); } - -bool WebVTTTokenizer::nextToken(SegmentedString& source, WebVTTToken& token) + +WebVTTTokenizer::WebVTTTokenizer(const String& input) + : m_input(input) + , m_preprocessor(*this) { - // If we have a token in progress, then we're supposed to be called back - // with the same token so we can finish it. - ASSERT(!m_token || m_token == &token || token.type() == WebVTTTokenTypes::Uninitialized); - m_token = &token; + // Append an EOF marker and close the input "stream". + ASSERT(!m_input.isClosed()); + m_input.append(String { &kEndOfFileMarker, 1 }); + m_input.close(); +} - if (source.isEmpty() || !m_inputStreamPreprocessor.peek(source)) - return haveBufferedCharacterToken(); +bool WebVTTTokenizer::nextToken(WebVTTToken& token) +{ + if (m_input.isEmpty() || !m_preprocessor.peek(m_input)) + return false; - UChar cc = m_inputStreamPreprocessor.nextInputCharacter(); + UChar character = m_preprocessor.nextInputCharacter(); + if (character == kEndOfFileMarker) { + m_preprocessor.advance(m_input); + return false; + } - // 4.8.10.13.4 WebVTT cue text tokenizer - switch (m_state) { - WEBVTT_BEGIN_STATE(DataState) { - if (cc == '&') { - m_buffer.append(static_cast<LChar>(cc)); - WEBVTT_ADVANCE_TO(EscapeState); - } else if (cc == '<') { - if (m_token->type() == WebVTTTokenTypes::Uninitialized - || vectorEqualsString<UChar>(m_token->characters(), emptyString())) - WEBVTT_ADVANCE_TO(TagState); - else - return emitAndResumeIn(source, WebVTTTokenizerState::TagState); - } else if (cc == kEndOfFileMarker) - return emitEndOfFile(source); + StringBuilder buffer; + StringBuilder result; + StringBuilder classes; + +// 4.8.10.13.4 WebVTT cue text tokenizer +DataState: + if (character == '&') { + buffer.append('&'); + WEBVTT_ADVANCE_TO(EscapeState); + } else if (character == '<') { + if (result.isEmpty()) + WEBVTT_ADVANCE_TO(TagState); else { - bufferCharacter(cc); - WEBVTT_ADVANCE_TO(DataState); - } - } - END_STATE() - - WEBVTT_BEGIN_STATE(EscapeState) { - if (cc == ';') { - if (vectorEqualsString(m_buffer, "&")) - bufferCharacter('&'); - else if (vectorEqualsString(m_buffer, "<")) - bufferCharacter('<'); - else if (vectorEqualsString(m_buffer, ">")) - bufferCharacter('>'); - else if (vectorEqualsString(m_buffer, "&lrm")) - bufferCharacter(leftToRightMark); - else if (vectorEqualsString(m_buffer, "&rlm")) - bufferCharacter(rightToLeftMark); - else if (vectorEqualsString(m_buffer, " ")) - bufferCharacter(noBreakSpace); - else { - m_buffer.append(static_cast<LChar>(cc)); - m_token->appendToCharacter(m_buffer); - } - m_buffer.clear(); - WEBVTT_ADVANCE_TO(DataState); - } else if (isASCIIAlphanumeric(cc)) { - m_buffer.append(static_cast<LChar>(cc)); - WEBVTT_ADVANCE_TO(EscapeState); - } else if (cc == kEndOfFileMarker) { - m_token->appendToCharacter(m_buffer); - return emitEndOfFile(source); - } else { - if (!vectorEqualsString(m_buffer, "&")) - m_token->appendToCharacter(m_buffer); - m_buffer.clear(); - WEBVTT_ADVANCE_TO(DataState); + // We don't want to advance input or perform a state transition - just return a (new) token. + // (On the next call to nextToken we will see '<' again, but take the other branch in this if instead.) + return emitToken(token, WebVTTToken::StringToken(result.toString())); } + } else if (character == kEndOfFileMarker) + return advanceAndEmitToken(m_input, token, WebVTTToken::StringToken(result.toString())); + else { + result.append(character); + WEBVTT_ADVANCE_TO(DataState); } - END_STATE() - - WEBVTT_BEGIN_STATE(TagState) { - if (isTokenizerWhitespace(cc)) { - m_token->beginEmptyStartTag(); - WEBVTT_ADVANCE_TO(StartTagAnnotationState); - } else if (cc == '.') { - m_token->beginEmptyStartTag(); - WEBVTT_ADVANCE_TO(StartTagClassState); - } else if (cc == '/') { - WEBVTT_ADVANCE_TO(EndTagOpenState); - } else if (WTF::isASCIIDigit(cc)) { - m_token->beginTimestampTag(cc); - WEBVTT_ADVANCE_TO(TimestampTagState); - } else if (cc == '>' || cc == kEndOfFileMarker) { - m_token->beginEmptyStartTag(); - return emitAndResumeIn(source, WebVTTTokenizerState::DataState); - } else { - m_token->beginStartTag(cc); - WEBVTT_ADVANCE_TO(StartTagState); - } - } - END_STATE() - - WEBVTT_BEGIN_STATE(StartTagState) { - if (isTokenizerWhitespace(cc)) - WEBVTT_ADVANCE_TO(StartTagAnnotationState); - else if (cc == '.') - WEBVTT_ADVANCE_TO(StartTagClassState); - else if (cc == '>' || cc == kEndOfFileMarker) - return emitAndResumeIn(source, WebVTTTokenizerState::DataState); + +EscapeState: + if (character == ';') { + if (equalLiteral(buffer, "&")) + result.append('&'); + else if (equalLiteral(buffer, "<")) + result.append('<'); + else if (equalLiteral(buffer, ">")) + result.append('>'); + else if (equalLiteral(buffer, "&lrm")) + result.append(leftToRightMark); + else if (equalLiteral(buffer, "&rlm")) + result.append(rightToLeftMark); + else if (equalLiteral(buffer, " ")) + result.append(noBreakSpace); else { - m_token->appendToName(cc); - WEBVTT_ADVANCE_TO(StartTagState); + buffer.append(character); + result.append(buffer); } - } - END_STATE() - - WEBVTT_BEGIN_STATE(StartTagClassState) { - if (isTokenizerWhitespace(cc)) { - m_token->addNewClass(); - WEBVTT_ADVANCE_TO(StartTagAnnotationState); - } else if (cc == '.') { - m_token->addNewClass(); - WEBVTT_ADVANCE_TO(StartTagClassState); - } else if (cc == '>' || cc == kEndOfFileMarker) { - m_token->addNewClass(); - return emitAndResumeIn(source, WebVTTTokenizerState::DataState); - } else { - m_token->appendToClass(cc); - WEBVTT_ADVANCE_TO(StartTagClassState); + buffer.clear(); + WEBVTT_ADVANCE_TO(DataState); + } else if (isASCIIAlphanumeric(character)) { + buffer.append(character); + WEBVTT_ADVANCE_TO(EscapeState); + } else if (character == '<') { + result.append(buffer); + return emitToken(token, WebVTTToken::StringToken(result.toString())); + } else if (character == kEndOfFileMarker) { + result.append(buffer); + return advanceAndEmitToken(m_input, token, WebVTTToken::StringToken(result.toString())); + } else { + result.append(buffer); + buffer.clear(); + + if (character == '&') { + buffer.append('&'); + WEBVTT_ADVANCE_TO(EscapeState); } - + result.append(character); + WEBVTT_ADVANCE_TO(DataState); } - END_STATE() - WEBVTT_BEGIN_STATE(StartTagAnnotationState) { - if (cc == '>' || cc == kEndOfFileMarker) { - m_token->addNewAnnotation(); - return emitAndResumeIn(source, WebVTTTokenizerState::DataState); - } - m_token->appendToAnnotation(cc); +TagState: + if (isTokenizerWhitespace(character)) { + ASSERT(result.isEmpty()); WEBVTT_ADVANCE_TO(StartTagAnnotationState); - } - END_STATE() - - WEBVTT_BEGIN_STATE(EndTagOpenState) { - if (cc == '>' || cc == kEndOfFileMarker) { - m_token->beginEndTag('\0'); - return emitAndResumeIn(source, WebVTTTokenizerState::DataState); - } - m_token->beginEndTag(cc); - WEBVTT_ADVANCE_TO(EndTagState); - } - END_STATE() - - WEBVTT_BEGIN_STATE(EndTagState) { - if (cc == '>' || cc == kEndOfFileMarker) - return emitAndResumeIn(source, WebVTTTokenizerState::DataState); - m_token->appendToName(cc); + } else if (character == '.') { + ASSERT(result.isEmpty()); + WEBVTT_ADVANCE_TO(StartTagClassState); + } else if (character == '/') { WEBVTT_ADVANCE_TO(EndTagState); + } else if (WTF::isASCIIDigit(character)) { + result.append(character); + WEBVTT_ADVANCE_TO(TimestampTagState); + } else if (character == '>' || character == kEndOfFileMarker) { + ASSERT(result.isEmpty()); + return advanceAndEmitToken(m_input, token, WebVTTToken::StartTag(result.toString())); + } else { + result.append(character); + WEBVTT_ADVANCE_TO(StartTagState); } - END_STATE() - WEBVTT_BEGIN_STATE(TimestampTagState) { - if (cc == '>' || cc == kEndOfFileMarker) - return emitAndResumeIn(source, WebVTTTokenizerState::DataState); - m_token->appendToTimestamp(cc); - WEBVTT_ADVANCE_TO(TimestampTagState); +StartTagState: + if (isTokenizerWhitespace(character)) + WEBVTT_ADVANCE_TO(StartTagAnnotationState); + else if (character == '.') + WEBVTT_ADVANCE_TO(StartTagClassState); + else if (character == '>' || character == kEndOfFileMarker) + return advanceAndEmitToken(m_input, token, WebVTTToken::StartTag(result.toString())); + else { + result.append(character); + WEBVTT_ADVANCE_TO(StartTagState); } - END_STATE() +StartTagClassState: + if (isTokenizerWhitespace(character)) { + addNewClass(classes, buffer); + buffer.clear(); + WEBVTT_ADVANCE_TO(StartTagAnnotationState); + } else if (character == '.') { + addNewClass(classes, buffer); + buffer.clear(); + WEBVTT_ADVANCE_TO(StartTagClassState); + } else if (character == '>' || character == kEndOfFileMarker) { + addNewClass(classes, buffer); + buffer.clear(); + return advanceAndEmitToken(m_input, token, WebVTTToken::StartTag(result.toString(), classes.toAtomicString())); + } else { + buffer.append(character); + WEBVTT_ADVANCE_TO(StartTagClassState); } - ASSERT_NOT_REACHED(); - return false; +StartTagAnnotationState: + if (character == '>' || character == kEndOfFileMarker) + return advanceAndEmitToken(m_input, token, WebVTTToken::StartTag(result.toString(), classes.toAtomicString(), buffer.toAtomicString())); + buffer.append(character); + WEBVTT_ADVANCE_TO(StartTagAnnotationState); + +EndTagState: + if (character == '>' || character == kEndOfFileMarker) + return advanceAndEmitToken(m_input, token, WebVTTToken::EndTag(result.toString())); + result.append(character); + WEBVTT_ADVANCE_TO(EndTagState); + +TimestampTagState: + if (character == '>' || character == kEndOfFileMarker) + return advanceAndEmitToken(m_input, token, WebVTTToken::TimestampTag(result.toString())); + result.append(character); + WEBVTT_ADVANCE_TO(TimestampTagState); } } diff --git a/Source/WebCore/html/track/WebVTTTokenizer.h b/Source/WebCore/html/track/WebVTTTokenizer.h index 79ee06b26..2b0c0c02d 100644 --- a/Source/WebCore/html/track/WebVTTTokenizer.h +++ b/Source/WebCore/html/track/WebVTTTokenizer.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2011, 2013 Google Inc. All rights reserved. + * Copyright (C) 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -28,91 +29,27 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WebVTTTokenizer_h -#define WebVTTTokenizer_h +#pragma once #if ENABLE(VIDEO_TRACK) #include "InputStreamPreprocessor.h" #include "WebVTTToken.h" -#include <wtf/PassOwnPtr.h> namespace WebCore { -class WebVTTTokenizerState { -public: - enum State { - DataState, - EscapeState, - TagState, - StartTagState, - StartTagClassState, - StartTagAnnotationState, - EndTagState, - EndTagOpenState, - TimestampTagState, - }; -}; - class WebVTTTokenizer { - WTF_MAKE_NONCOPYABLE(WebVTTTokenizer); - WTF_MAKE_FAST_ALLOCATED; public: - static OwnPtr<WebVTTTokenizer> create() { return adoptPtr(new WebVTTTokenizer); } - - typedef WebVTTTokenizerState State; - - void reset(); - - bool nextToken(SegmentedString&, WebVTTToken&); - - inline bool haveBufferedCharacterToken() - { - return m_token->type() == WebVTTToken::Type::Character; - } + explicit WebVTTTokenizer(const String&); + bool nextToken(WebVTTToken&); - inline void bufferCharacter(UChar character) - { - ASSERT(character != kEndOfFileMarker); - m_token->ensureIsCharacterToken(); - m_token->appendToCharacter(character); - } - - inline bool emitAndResumeIn(SegmentedString& source, State::State state) - { - m_state = state; - source.advanceAndUpdateLineNumber(); - return true; - } - - inline bool emitEndOfFile(SegmentedString& source) - { - if (haveBufferedCharacterToken()) - return true; - m_state = State::DataState; - source.advanceAndUpdateLineNumber(); - m_token->clear(); - m_token->makeEndOfFile(); - return true; - } - - bool shouldSkipNullCharacters() const { return true; } + static bool neverSkipNullCharacters() { return false; } private: - WebVTTTokenizer(); - - // m_token is owned by the caller. If nextToken is not on the stack, - // this member might be pointing to unallocated memory. - WebVTTToken* m_token; - WebVTTTokenizerState::State m_state; - - Vector<LChar, 32> m_buffer; - - // ://www.whatwg.org/specs/web-apps/current-work/#preprocessing-the-input-stream - InputStreamPreprocessor<WebVTTTokenizer> m_inputStreamPreprocessor; + SegmentedString m_input; + InputStreamPreprocessor<WebVTTTokenizer> m_preprocessor; }; -} +} // namespace WebCore -#endif -#endif +#endif // ENABLE(VIDEO_TRACK) |