| /* |
| * Copyright (C) 2011, 2012 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 "core/html/track/TextTrackList.h" |
| |
| #include "bindings/v8/ExceptionStatePlaceholder.h" |
| #include "core/events/GenericEventQueue.h" |
| #include "core/events/ThreadLocalEventNames.h" |
| #include "core/html/HTMLMediaElement.h" |
| #include "core/html/track/InbandTextTrack.h" |
| #include "core/html/track/LoadableTextTrack.h" |
| #include "core/html/track/TextTrack.h" |
| #include "core/html/track/TrackEvent.h" |
| |
| using namespace WebCore; |
| |
| TextTrackList::TextTrackList(HTMLMediaElement* owner) |
| : m_owner(owner) |
| , m_asyncEventQueue(GenericEventQueue::create(this)) |
| { |
| ScriptWrappable::init(this); |
| } |
| |
| TextTrackList::~TextTrackList() |
| { |
| m_asyncEventQueue->close(); |
| } |
| |
| unsigned TextTrackList::length() const |
| { |
| return m_addTrackTracks.size() + m_elementTracks.size() + m_inbandTracks.size(); |
| } |
| |
| int TextTrackList::getTrackIndex(TextTrack *textTrack) |
| { |
| if (textTrack->trackType() == TextTrack::TrackElement) |
| return static_cast<LoadableTextTrack*>(textTrack)->trackElementIndex(); |
| |
| 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); |
| |
| ASSERT_NOT_REACHED(); |
| |
| return -1; |
| } |
| |
| 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 (!m_elementTracks[i]->isRendered()) |
| continue; |
| |
| if (m_elementTracks[i] == textTrack) |
| return trackIndex; |
| ++trackIndex; |
| } |
| |
| for (size_t i = 0; i < m_addTrackTracks.size(); ++i) { |
| if (!m_addTrackTracks[i]->isRendered()) |
| continue; |
| |
| if (m_addTrackTracks[i] == textTrack) |
| return trackIndex; |
| ++trackIndex; |
| } |
| |
| for (size_t i = 0; i < m_inbandTracks.size(); ++i) { |
| if (!m_inbandTracks[i]->isRendered()) |
| continue; |
| |
| if (m_inbandTracks[i] == textTrack) |
| return trackIndex; |
| ++trackIndex; |
| } |
| |
| ASSERT_NOT_REACHED(); |
| |
| return -1; |
| } |
| |
| TextTrack* TextTrackList::item(unsigned index) |
| { |
| // 4.8.10.12.1 Text track model |
| // The text tracks are sorted as follows: |
| // 1. The text tracks corresponding to track element children of the media element, in tree order. |
| // 2. Any text tracks added using the addTextTrack() method, in the order they were added, oldest first. |
| // 3. Any media-resource-specific text tracks (text tracks corresponding to data in the media |
| // resource), in the order defined by the media resource's format specification. |
| |
| if (index < m_elementTracks.size()) |
| return m_elementTracks[index].get(); |
| |
| index -= m_elementTracks.size(); |
| if (index < m_addTrackTracks.size()) |
| return m_addTrackTracks[index].get(); |
| |
| index -= m_addTrackTracks.size(); |
| if (index < m_inbandTracks.size()) |
| return m_inbandTracks[index].get(); |
| |
| return 0; |
| } |
| |
| TextTrack* TextTrackList::getTrackById(const AtomicString& id) |
| { |
| // 4.8.10.12.5 Text track API |
| // The getTrackById(id) method must return the first TextTrack in the |
| // 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; |
| } |
| |
| // When no tracks match the given argument, the method must return null. |
| return 0; |
| } |
| |
| void TextTrackList::invalidateTrackIndexesAfterTrack(TextTrack* track) |
| { |
| Vector<RefPtr<TextTrack> >* tracks = 0; |
| |
| if (track->trackType() == TextTrack::TrackElement) { |
| tracks = &m_elementTracks; |
| for (size_t i = 0; i < m_addTrackTracks.size(); ++i) |
| m_addTrackTracks[i]->invalidateTrackIndex(); |
| for (size_t i = 0; i < m_inbandTracks.size(); ++i) |
| m_inbandTracks[i]->invalidateTrackIndex(); |
| } else if (track->trackType() == TextTrack::AddTrack) { |
| tracks = &m_addTrackTracks; |
| for (size_t i = 0; i < m_inbandTracks.size(); ++i) |
| m_inbandTracks[i]->invalidateTrackIndex(); |
| } else if (track->trackType() == TextTrack::InBand) |
| tracks = &m_inbandTracks; |
| else |
| ASSERT_NOT_REACHED(); |
| |
| size_t index = tracks->find(track); |
| if (index == kNotFound) |
| return; |
| |
| for (size_t i = index; i < tracks->size(); ++i) |
| tracks->at(index)->invalidateTrackIndex(); |
| } |
| |
| void TextTrackList::append(PassRefPtr<TextTrack> prpTrack) |
| { |
| RefPtr<TextTrack> track = prpTrack; |
| |
| if (track->trackType() == TextTrack::AddTrack) |
| m_addTrackTracks.append(track); |
| else if (track->trackType() == TextTrack::TrackElement) { |
| // Insert tracks added for <track> element in tree order. |
| size_t index = static_cast<LoadableTextTrack*>(track.get())->trackElementIndex(); |
| m_elementTracks.insert(index, track); |
| } 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); |
| } else |
| ASSERT_NOT_REACHED(); |
| |
| invalidateTrackIndexesAfterTrack(track.get()); |
| |
| ASSERT(!track->mediaElement() || track->mediaElement() == m_owner); |
| track->setMediaElement(m_owner); |
| |
| scheduleAddTrackEvent(track.release()); |
| } |
| |
| void TextTrackList::remove(TextTrack* track) |
| { |
| Vector<RefPtr<TextTrack> >* tracks = 0; |
| RefPtr<InbandTextTrack> inbandTrack; |
| |
| if (track->trackType() == TextTrack::TrackElement) { |
| tracks = &m_elementTracks; |
| } else if (track->trackType() == TextTrack::AddTrack) { |
| tracks = &m_addTrackTracks; |
| } else if (track->trackType() == TextTrack::InBand) { |
| tracks = &m_inbandTracks; |
| inbandTrack = static_cast<InbandTextTrack*>(track); |
| } else { |
| ASSERT_NOT_REACHED(); |
| } |
| |
| size_t index = tracks->find(track); |
| if (index == kNotFound) |
| return; |
| |
| invalidateTrackIndexesAfterTrack(track); |
| |
| ASSERT(track->mediaElement() == m_owner); |
| track->setMediaElement(0); |
| |
| tracks->remove(index); |
| |
| if (inbandTrack) |
| inbandTrack->trackRemoved(); |
| |
| scheduleRemoveTrackEvent(track); |
| } |
| |
| bool TextTrackList::contains(TextTrack* track) const |
| { |
| const Vector<RefPtr<TextTrack> >* tracks = 0; |
| |
| if (track->trackType() == TextTrack::TrackElement) |
| tracks = &m_elementTracks; |
| else if (track->trackType() == TextTrack::AddTrack) |
| tracks = &m_addTrackTracks; |
| else if (track->trackType() == TextTrack::InBand) |
| tracks = &m_inbandTracks; |
| else |
| ASSERT_NOT_REACHED(); |
| |
| return tracks->find(track) != kNotFound; |
| } |
| |
| const AtomicString& TextTrackList::interfaceName() const |
| { |
| return EventTargetNames::TextTrackList; |
| } |
| |
| ExecutionContext* TextTrackList::executionContext() const |
| { |
| ASSERT(m_owner); |
| return m_owner->executionContext(); |
| } |
| |
| void TextTrackList::scheduleTrackEvent(const AtomicString& eventName, PassRefPtr<TextTrack> track) |
| { |
| TrackEventInit initializer; |
| initializer.track = track; |
| initializer.bubbles = false; |
| initializer.cancelable = false; |
| |
| m_asyncEventQueue->enqueueEvent(TrackEvent::create(eventName, initializer)); |
| } |
| |
| void TextTrackList::scheduleAddTrackEvent(PassRefPtr<TextTrack> track) |
| { |
| // 4.8.10.12.3 Sourcing out-of-band text tracks |
| // 4.8.10.12.4 Text track API |
| // ... then queue a task to fire an event with the name addtrack, that does not |
| // 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(EventTypeNames::addtrack, track); |
| } |
| |
| void TextTrackList::scheduleChangeEvent() |
| { |
| // 4.8.10.12.1 Text track model |
| // Whenever a text track that is in a media element's list of text tracks |
| // has its text track mode change value, the user agent must run the |
| // following steps for the media element: |
| // ... |
| // Fire a simple event named change at the media element's textTracks |
| // attribute's TextTrackList object. |
| |
| EventInit initializer; |
| initializer.bubbles = false; |
| initializer.cancelable = false; |
| |
| m_asyncEventQueue->enqueueEvent(Event::create(EventTypeNames::change, initializer)); |
| } |
| |
| void TextTrackList::scheduleRemoveTrackEvent(PassRefPtr<TextTrack> track) |
| { |
| // 4.8.10.12.3 Sourcing out-of-band text tracks |
| // When a track element's parent element changes and the old parent was a |
| // media element, then the user agent must remove the track element's |
| // corresponding text track from the media element's list of text tracks, |
| // and then queue a task to fire a trusted event with the name removetrack, |
| // that does not 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(EventTypeNames::removetrack, track); |
| } |
| |
| Node* TextTrackList::owner() const |
| { |
| return m_owner; |
| } |