HTMLTrackElement.cpp [plain text]
#include "config.h"
#include "HTMLTrackElement.h"
#if ENABLE(VIDEO_TRACK)
#include "ContentSecurityPolicy.h"
#include "Event.h"
#include "EventNames.h"
#include "HTMLMediaElement.h"
#include "HTMLNames.h"
#include "Logging.h"
#include "RuntimeEnabledFeatures.h"
#include <wtf/text/CString.h>
namespace WebCore {
using namespace HTMLNames;
#if !LOG_DISABLED
static String urlForLoggingTrack(const URL& url)
{
static const unsigned maximumURLLengthForLogging = 128;
if (url.string().length() < maximumURLLengthForLogging)
return url.string();
return url.string().substring(0, maximumURLLengthForLogging) + "...";
}
#endif
inline HTMLTrackElement::HTMLTrackElement(const QualifiedName& tagName, Document& document)
: HTMLElement(tagName, document)
, m_loadTimer(*this, &HTMLTrackElement::loadTimerFired)
{
LOG(Media, "HTMLTrackElement::HTMLTrackElement - %p", this);
ASSERT(hasTagName(trackTag));
}
HTMLTrackElement::~HTMLTrackElement()
{
if (m_track) {
m_track->clearElement();
m_track->clearClient();
}
}
Ref<HTMLTrackElement> HTMLTrackElement::create(const QualifiedName& tagName, Document& document)
{
return adoptRef(*new HTMLTrackElement(tagName, document));
}
Node::InsertedIntoAncestorResult HTMLTrackElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
{
HTMLElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
if (parentNode() == &parentOfInsertedTree && is<HTMLMediaElement>(parentOfInsertedTree)) {
downcast<HTMLMediaElement>(parentOfInsertedTree).didAddTextTrack(*this);
scheduleLoad();
}
return InsertedIntoAncestorResult::Done;
}
void HTMLTrackElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
{
HTMLElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
if (!parentNode() && is<HTMLMediaElement>(oldParentOfRemovedTree))
downcast<HTMLMediaElement>(oldParentOfRemovedTree).didRemoveTextTrack(*this);
}
void HTMLTrackElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
{
if (name == srcAttr) {
scheduleLoad();
} else if (name == kindAttr)
track().setKindKeywordIgnoringASCIICase(value.string());
else if (name == labelAttr)
track().setLabel(value);
else if (name == srclangAttr)
track().setLanguage(value);
else if (name == defaultAttr)
track().setIsDefault(!value.isNull());
HTMLElement::parseAttribute(name, value);
}
const AtomicString& HTMLTrackElement::kind()
{
return track().kindKeyword();
}
void HTMLTrackElement::setKind(const AtomicString& kind)
{
setAttributeWithoutSynchronization(kindAttr, kind);
}
const AtomicString& HTMLTrackElement::srclang() const
{
return attributeWithoutSynchronization(srclangAttr);
}
const AtomicString& HTMLTrackElement::label() const
{
return attributeWithoutSynchronization(labelAttr);
}
bool HTMLTrackElement::isDefault() const
{
return hasAttributeWithoutSynchronization(defaultAttr);
}
LoadableTextTrack& HTMLTrackElement::track()
{
if (!m_track) {
String kind = attributeWithoutSynchronization(kindAttr).convertToASCIILowercase();
if (!TextTrack::isValidKindKeyword(kind))
kind = TextTrack::subtitlesKeyword();
m_track = LoadableTextTrack::create(*this, kind, label(), srclang());
}
ASSERT(m_track->trackElement() == this);
return *m_track;
}
bool HTMLTrackElement::isURLAttribute(const Attribute& attribute) const
{
return attribute.name() == srcAttr || HTMLElement::isURLAttribute(attribute);
}
void HTMLTrackElement::scheduleLoad()
{
if (m_loadTimer.isActive())
return;
if (track().mode() != TextTrack::Mode::Hidden && track().mode() != TextTrack::Mode::Showing)
return;
if (!mediaElement())
return;
m_loadTimer.startOneShot(0_s);
}
void HTMLTrackElement::loadTimerFired()
{
if (!hasAttributeWithoutSynchronization(srcAttr)) {
track().removeAllCues();
return;
}
setReadyState(HTMLTrackElement::LOADING);
URL url = getNonEmptyURLAttribute(srcAttr);
if (url.isEmpty() || !canLoadURL(url)) {
track().removeAllCues();
didCompleteLoad(HTMLTrackElement::Failure);
return;
}
track().scheduleLoad(url);
}
bool HTMLTrackElement::canLoadURL(const URL& url)
{
auto parent = mediaElement();
if (!parent)
return false;
if (url.isEmpty())
return false;
ASSERT(document().contentSecurityPolicy());
if (!isInUserAgentShadowTree() && !document().contentSecurityPolicy()->allowMediaFromSource(url)) {
LOG(Media, "HTMLTrackElement::canLoadURL(%s) -> rejected by Content Security Policy", urlForLoggingTrack(url).utf8().data());
return false;
}
return dispatchBeforeLoadEvent(url.string());
}
void HTMLTrackElement::didCompleteLoad(LoadStatus status)
{
if (status == Failure) {
setReadyState(HTMLTrackElement::TRACK_ERROR);
dispatchEvent(Event::create(eventNames().errorEvent, false, false));
return;
}
setReadyState(HTMLTrackElement::LOADED);
dispatchEvent(Event::create(eventNames().loadEvent, false, false));
}
COMPILE_ASSERT(HTMLTrackElement::NONE == static_cast<HTMLTrackElement::ReadyState>(TextTrack::NotLoaded), TextTrackEnumNotLoaded_Is_Wrong_Should_Be_HTMLTrackElementEnumNONE);
COMPILE_ASSERT(HTMLTrackElement::LOADING == static_cast<HTMLTrackElement::ReadyState>(TextTrack::Loading), TextTrackEnumLoadingIsWrong_ShouldBe_HTMLTrackElementEnumLOADING);
COMPILE_ASSERT(HTMLTrackElement::LOADED == static_cast<HTMLTrackElement::ReadyState>(TextTrack::Loaded), TextTrackEnumLoaded_Is_Wrong_Should_Be_HTMLTrackElementEnumLOADED);
COMPILE_ASSERT(HTMLTrackElement::TRACK_ERROR == static_cast<HTMLTrackElement::ReadyState>(TextTrack::FailedToLoad), TextTrackEnumFailedToLoad_Is_Wrong_Should_Be_HTMLTrackElementEnumTRACK_ERROR);
void HTMLTrackElement::setReadyState(ReadyState state)
{
track().setReadinessState(static_cast<TextTrack::ReadinessState>(state));
if (auto parent = mediaElement())
parent->textTrackReadyStateChanged(m_track.get());
}
HTMLTrackElement::ReadyState HTMLTrackElement::readyState()
{
return static_cast<ReadyState>(track().readinessState());
}
const AtomicString& HTMLTrackElement::mediaElementCrossOriginAttribute() const
{
if (auto parent = mediaElement())
return parent->attributeWithoutSynchronization(HTMLNames::crossoriginAttr);
return nullAtom();
}
void HTMLTrackElement::textTrackKindChanged(TextTrack& track)
{
if (auto parent = mediaElement())
parent->textTrackKindChanged(track);
}
void HTMLTrackElement::textTrackModeChanged(TextTrack& track)
{
if (readyState() == HTMLTrackElement::NONE)
scheduleLoad();
if (auto parent = mediaElement())
parent->textTrackModeChanged(track);
}
void HTMLTrackElement::textTrackAddCues(TextTrack& track, const TextTrackCueList& cues)
{
if (auto parent = mediaElement())
parent->textTrackAddCues(track, cues);
}
void HTMLTrackElement::textTrackRemoveCues(TextTrack& track, const TextTrackCueList& cues)
{
if (auto parent = mediaElement())
parent->textTrackRemoveCues(track, cues);
}
void HTMLTrackElement::textTrackAddCue(TextTrack& track, TextTrackCue& cue)
{
if (auto parent = mediaElement())
parent->textTrackAddCue(track, cue);
}
void HTMLTrackElement::textTrackRemoveCue(TextTrack& track, TextTrackCue& cue)
{
if (auto parent = mediaElement())
parent->textTrackRemoveCue(track, cue);
}
RefPtr<HTMLMediaElement> HTMLTrackElement::mediaElement() const
{
auto parent = makeRefPtr(parentElement());
if (!is<HTMLMediaElement>(parent))
return nullptr;
return downcast<HTMLMediaElement>(parent.get());
}
}
#endif