Bug 882718 - Implement "TimeMarchesOn". r=rillian

MozReview-Commit-ID: 1RqUmgz056N
* * *
[mq]: hotfix

MozReview-Commit-ID: CPByIPsUag4

--HG--
extra : rebase_source : 4b1fea4f04553ea5b3a0c3c4ddfdb60b485b803e
This commit is contained in:
Andrew Quartey 2016-06-01 13:35:56 +08:00
Родитель 6df1d29dba
Коммит c70c946e3e
4 изменённых файлов: 320 добавлений и 3 удалений

Просмотреть файл

@ -76,7 +76,8 @@ CompareTextTracks::LessThan(TextTrack* aOne, TextTrack* aTwo) const
}
NS_IMPL_CYCLE_COLLECTION(TextTrackManager, mMediaElement, mTextTracks,
mPendingTextTracks, mNewCues)
mPendingTextTracks, mNewCues,
mLastActiveCues)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextTrackManager)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
@ -89,6 +90,9 @@ StaticRefPtr<nsIWebVTTParserWrapper> TextTrackManager::sParserWrapper;
TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement)
: mMediaElement(aMediaElement)
, mHasSeeked(false)
, mLastTimeMarchesOnCalled(0.0)
, mTimeMarchesOnDispatched(false)
, performedTrackSelection(false)
{
nsISupports* parentObject =
@ -98,6 +102,7 @@ TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement)
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(parentObject);
mNewCues = new TextTrackCueList(window);
mLastActiveCues = new TextTrackCueList(window);
mTextTracks = new TextTrackList(window, this);
mPendingTextTracks = new TextTrackList(window, this);
@ -191,6 +196,7 @@ TextTrackManager::DidSeek()
if (mTextTracks) {
mTextTracks->DidSeek();
}
mHasSeeked = true;
}
void
@ -380,5 +386,292 @@ TextTrackManager::HandleEvent(nsIDOMEvent* aEvent)
return NS_OK;
}
class SimpleTextTrackEvent : public Runnable
{
public:
friend class CompareSimpleTextTrackEvents;
SimpleTextTrackEvent(const nsAString& aEventName, double aTime,
TextTrack* aTrack, TextTrackCue* aCue)
: mName(aEventName),
mTime(aTime),
mTrack(aTrack),
mCue(aCue)
{}
NS_IMETHOD Run() {
mCue->DispatchTrustedEvent(mName);
return NS_OK;
}
private:
nsString mName;
double mTime;
TextTrack* mTrack;
RefPtr<TextTrackCue> mCue;
};
class CompareSimpleTextTrackEvents {
private:
int32_t TrackChildPosition(SimpleTextTrackEvent* aEvent) const
{
HTMLTrackElement* trackElement = aEvent->mTrack->GetTrackElement();;
if (!trackElement) {
return -1;
}
return mMediaElement->IndexOf(trackElement);
}
HTMLMediaElement* mMediaElement;
public:
explicit CompareSimpleTextTrackEvents(HTMLMediaElement* aMediaElement)
{
mMediaElement = aMediaElement;
}
bool Equals(SimpleTextTrackEvent* aOne, SimpleTextTrackEvent* aTwo) const
{
return false;
}
bool LessThan(SimpleTextTrackEvent* aOne, SimpleTextTrackEvent* aTwo) const
{
if (aOne->mTime < aTwo->mTime) {
return true;
} else if (aOne->mTime > aTwo->mTime) {
return false;
}
int32_t positionOne = TrackChildPosition(aOne);
int32_t positionTwo = TrackChildPosition(aTwo);
if (positionOne < positionTwo) {
return true;
} else if (positionOne > positionTwo) {
return false;
}
if (aOne->mName.EqualsLiteral("enter") ||
aTwo->mName.EqualsLiteral("exit")) {
return true;
}
return false;
}
};
class TextTrackListInternal
{
public:
void AddTextTrack(TextTrack* aTextTrack,
const CompareTextTracks& aCompareTT)
{
if (!mTextTracks.Contains(aTextTrack)) {
mTextTracks.InsertElementSorted(aTextTrack, aCompareTT);
}
}
uint32_t Length() const
{
return mTextTracks.Length();
}
TextTrack* operator[](uint32_t aIndex)
{
return mTextTracks.SafeElementAt(aIndex, nullptr);
}
private:
nsTArray<RefPtr<TextTrack>> mTextTracks;
};
void
TextTrackManager::DispatchTimeMarchesOn()
{
// Run the algorithm if no previous instance is still running, otherwise
// enqueue the current playback position and whether only that changed
// through its usual monotonic increase during normal playback; current
// executing call upon completion will check queue for further 'work'.
if (!mTimeMarchesOnDispatched) {
NS_DispatchToMainThread(NewRunnableMethod(this, &TextTrackManager::TimeMarchesOn));
mTimeMarchesOnDispatched = true;
}
}
// https://html.spec.whatwg.org/multipage/embedded-content.html#time-marches-on
void
TextTrackManager::TimeMarchesOn()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mTimeMarchesOnDispatched = false;
nsISupports* parentObject =
mMediaElement->OwnerDoc()->GetParentObject();
if (NS_WARN_IF(!parentObject)) {
return;
}
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(parentObject);
// Step 3.
double currentPlaybackTime = mMediaElement->CurrentTime();
bool hasNormalPlayback = !mHasSeeked;
mHasSeeked = false;
// Step 1, 2.
RefPtr<TextTrackCueList> currentCues =
new TextTrackCueList(window);
RefPtr<TextTrackCueList> otherCues =
new TextTrackCueList(window);
bool dummy;
for (uint32_t index = 0; index < mTextTracks->Length(); ++index) {
TextTrack* ttrack = mTextTracks->IndexedGetter(index, dummy);
if (ttrack && dummy) {
TextTrackCueList* activeCueList = ttrack->GetActiveCues();
if (activeCueList) {
for (uint32_t i = 0; i < activeCueList->Length(); ++i) {
currentCues->AddCue(*((*activeCueList)[i]));
}
}
}
}
// Populate otherCues with 'non-active" cues.
if (hasNormalPlayback) {
media::Interval<double> interval(mLastTimeMarchesOnCalled,
currentPlaybackTime);
otherCues = mNewCues->GetCueListByTimeInterval(interval);;
} else {
// Seek case. Put the mLastActiveCues into otherCues.
otherCues = mLastActiveCues;
}
for (uint32_t i = 0; i < currentCues->Length(); ++i) {
TextTrackCue* cue = (*currentCues)[i];
ErrorResult dummy;
otherCues->RemoveCue(*cue, dummy);
}
// Step 4.
RefPtr<TextTrackCueList> missedCues = new TextTrackCueList(window);
if (hasNormalPlayback) {
for (uint32_t i = 0; i < otherCues->Length(); ++i) {
TextTrackCue* cue = (*otherCues)[i];
if (cue->StartTime() >= mLastTimeMarchesOnCalled &&
cue->EndTime() <= currentPlaybackTime) {
missedCues->AddCue(*cue);
}
}
}
// Step 5. Empty now.
// TODO: Step 6: fire timeupdate?
// Step 7. Abort steps if condition 1, 2, 3 are satisfied.
// 1. All of the cues in current cues have their active flag set.
// 2. None of the cues in other cues have their active flag set.
// 3. Missed cues is empty.
bool c1 = true;
for (uint32_t i = 0; i < currentCues->Length(); ++i) {
if (!(*currentCues)[i]->GetActive()) {
c1 = false;
break;
}
}
bool c2 = true;
for (uint32_t i = 0; i < otherCues->Length(); ++i) {
if ((*otherCues)[i]->GetActive()) {
c2 = false;
break;
}
}
bool c3 = (missedCues->Length() == 0);
if (c1 && c2 && c3) {
mLastTimeMarchesOnCalled = currentPlaybackTime;
return;
}
// Step 8. Respect PauseOnExit flag if not seek.
if (hasNormalPlayback) {
for (uint32_t i = 0; i < otherCues->Length(); ++i) {
TextTrackCue* cue = (*otherCues)[i];
if (cue && cue->PauseOnExit() && cue->GetActive()) {
mMediaElement->Pause();
break;
}
}
for (uint32_t i = 0; i < missedCues->Length(); ++i) {
TextTrackCue* cue = (*missedCues)[i];
if (cue && cue->PauseOnExit()) {
mMediaElement->Pause();
break;
}
}
}
// Step 15.
// Sort text tracks in the same order as the text tracks appear
// in the media element's list of text tracks, and remove
// duplicates.
TextTrackListInternal affectedTracks;
// Step 13, 14.
nsTArray<RefPtr<SimpleTextTrackEvent>> eventList;
// Step 9, 10.
// For each text track cue in missed cues, prepare an event named
// enter for the TextTrackCue object with the cue start time.
for (uint32_t i = 0; i < missedCues->Length(); ++i) {
TextTrackCue* cue = (*missedCues)[i];
if (cue) {
SimpleTextTrackEvent* event =
new SimpleTextTrackEvent(NS_LITERAL_STRING("enter"),
cue->StartTime(), cue->GetTrack(),
cue);
eventList.InsertElementSorted(event,
CompareSimpleTextTrackEvents(mMediaElement));
affectedTracks.AddTextTrack(cue->GetTrack(), CompareTextTracks(mMediaElement));
}
}
// Step 11, 17.
for (uint32_t i = 0; i < otherCues->Length(); ++i) {
TextTrackCue* cue = (*otherCues)[i];
if (cue->GetActive() ||
missedCues->GetCueById(cue->Id()) != nullptr) {
double time = cue->StartTime() > cue->EndTime() ? cue->StartTime()
: cue->EndTime();
SimpleTextTrackEvent* event =
new SimpleTextTrackEvent(NS_LITERAL_STRING("exit"), time,
cue->GetTrack(), cue);
eventList.InsertElementSorted(event,
CompareSimpleTextTrackEvents(mMediaElement));
affectedTracks.AddTextTrack(cue->GetTrack(), CompareTextTracks(mMediaElement));
}
cue->SetActive(false);
}
// Step 12, 17.
for (uint32_t i = 0; i < currentCues->Length(); ++i) {
TextTrackCue* cue = (*currentCues)[i];
if (!cue->GetActive()) {
SimpleTextTrackEvent* event =
new SimpleTextTrackEvent(NS_LITERAL_STRING("enter"),
cue->StartTime(), cue->GetTrack(),
cue);
eventList.InsertElementSorted(event,
CompareSimpleTextTrackEvents(mMediaElement));
affectedTracks.AddTextTrack(cue->GetTrack(), CompareTextTracks(mMediaElement));
}
cue->SetActive(true);
}
// Fire the eventList
for (uint32_t i = 0; i < eventList.Length(); ++i) {
NS_DispatchToMainThread(eventList[i].forget());
}
// Step 16.
for (uint32_t i = 0; i < affectedTracks.Length(); ++i) {
TextTrack* ttrack = affectedTracks[i];
if (ttrack) {
ttrack->DispatchTrustedEvent(NS_LITERAL_STRING("cuechange"));
}
}
mLastTimeMarchesOnCalled = currentPlaybackTime;
mLastActiveCues = currentCues;
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -22,9 +22,9 @@ class HTMLMediaElement;
class CompareTextTracks {
private:
HTMLMediaElement* mMediaElement;
int32_t TrackChildPosition(TextTrack* aTrack) const;
public:
explicit CompareTextTracks(HTMLMediaElement* aMediaElement);
int32_t TrackChildPosition(TextTrack* aTrack) const;
bool Equals(TextTrack* aOne, TextTrack* aTwo) const;
bool LessThan(TextTrack* aOne, TextTrack* aTwo) const;
};
@ -94,13 +94,30 @@ public:
// The HTMLMediaElement that this TextTrackManager manages the TextTracks of.
RefPtr<HTMLMediaElement> mMediaElement;
void DispatchTimeMarchesOn();
private:
void TimeMarchesOn();
// List of the TextTrackManager's owning HTMLMediaElement's TextTracks.
RefPtr<TextTrackList> mTextTracks;
// List of text track objects awaiting loading.
RefPtr<TextTrackList> mPendingTextTracks;
// List of newly introduced Text Track cues.
// Contain all cues for a MediaElement.
RefPtr<TextTrackCueList> mNewCues;
// The active cues for the last TimeMarchesOn iteration.
RefPtr<TextTrackCueList> mLastActiveCues;
// True if the media player playback changed due to seeking prior to and
// during running the "Time Marches On" algorithm.
bool mHasSeeked;
// Playback position at the time of last "Time Marches On" call
double mLastTimeMarchesOnCalled;
bool mTimeMarchesOnDispatched;
static StaticRefPtr<nsIWebVTTParserWrapper> sParserWrapper;

Просмотреть файл

@ -62,6 +62,13 @@ TextTrackCueList::operator[](uint32_t aIndex)
return mList.SafeElementAt(aIndex, nullptr);
}
TextTrackCueList&
TextTrackCueList::operator=(const TextTrackCueList& aOther)
{
mList = aOther.mList;
return *this;
}
TextTrackCue*
TextTrackCueList::GetCueById(const nsAString& aId)
{

Просмотреть файл

@ -44,7 +44,7 @@ public:
TextTrackCue* IndexedGetter(uint32_t aIndex, bool& aFound);
TextTrackCue* operator[](uint32_t aIndex);
TextTrackCue* GetCueById(const nsAString& aId);
TextTrackCueList& operator=(const TextTrackCueList& aOther);
// Adds a cue to mList by performing an insertion sort on mList.
// We expect most files to already be sorted, so an insertion sort starting
// from the end of the current array should be more efficient than a general