/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "MediaElementEventRunners.h" #include "mozilla/dom/HTMLMediaElement.h" extern mozilla::LazyLogModule gMediaElementEventsLog; #define LOG_EVENT(type, msg) MOZ_LOG(gMediaElementEventsLog, type, msg) namespace mozilla::dom { nsMediaEventRunner::nsMediaEventRunner(const nsAString& aName, HTMLMediaElement* aElement, const nsAString& aEventName) : mElement(aElement), mName(aName), mEventName(aEventName), mLoadID(mElement->GetCurrentLoadID()) {} bool nsMediaEventRunner::IsCancelled() { return !mElement || mElement->GetCurrentLoadID() != mLoadID; } nsresult nsMediaEventRunner::DispatchEvent(const nsAString& aName) { return mElement ? mElement->DispatchEvent(aName) : NS_OK; } NS_IMPL_CYCLE_COLLECTION(nsMediaEventRunner, mElement) NS_IMPL_CYCLE_COLLECTING_ADDREF(nsMediaEventRunner) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsMediaEventRunner) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsMediaEventRunner) NS_INTERFACE_MAP_ENTRY(nsINamed) NS_INTERFACE_MAP_ENTRY(nsIRunnable) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable) NS_INTERFACE_MAP_END NS_IMETHODIMP nsAsyncEventRunner::Run() { // Silently cancel if our load has been cancelled or element has been CCed. return IsCancelled() ? NS_OK : DispatchEvent(mEventName); } nsResolveOrRejectPendingPlayPromisesRunner:: nsResolveOrRejectPendingPlayPromisesRunner( HTMLMediaElement* aElement, nsTArray>&& aPromises, nsresult aError) : nsMediaEventRunner(u"nsResolveOrRejectPendingPlayPromisesRunner"_ns, aElement), mPromises(std::move(aPromises)), mError(aError) { mElement->mPendingPlayPromisesRunners.AppendElement(this); } void nsResolveOrRejectPendingPlayPromisesRunner::ResolveOrReject() { if (NS_SUCCEEDED(mError)) { PlayPromise::ResolvePromisesWithUndefined(mPromises); } else { PlayPromise::RejectPromises(mPromises, mError); } } NS_IMETHODIMP nsResolveOrRejectPendingPlayPromisesRunner::Run() { if (!IsCancelled()) { ResolveOrReject(); } mElement->mPendingPlayPromisesRunners.RemoveElement(this); return NS_OK; } NS_IMETHODIMP nsNotifyAboutPlayingRunner::Run() { if (!IsCancelled()) { DispatchEvent(u"playing"_ns); } return nsResolveOrRejectPendingPlayPromisesRunner::Run(); } NS_IMPL_CYCLE_COLLECTION_INHERITED(nsResolveOrRejectPendingPlayPromisesRunner, nsMediaEventRunner, mPromises) NS_IMPL_ADDREF_INHERITED(nsResolveOrRejectPendingPlayPromisesRunner, nsMediaEventRunner) NS_IMPL_RELEASE_INHERITED(nsResolveOrRejectPendingPlayPromisesRunner, nsMediaEventRunner) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION( nsResolveOrRejectPendingPlayPromisesRunner) NS_INTERFACE_MAP_END_INHERITING(nsMediaEventRunner) NS_IMETHODIMP nsSourceErrorEventRunner::Run() { // Silently cancel if our load has been cancelled. if (IsCancelled()) { return NS_OK; } LOG_EVENT(LogLevel::Debug, ("%p Dispatching simple event source error", mElement.get())); return nsContentUtils::DispatchTrustedEvent(mElement->OwnerDoc(), mSource, u"error"_ns, CanBubble::eNo, Cancelable::eNo); } NS_IMPL_CYCLE_COLLECTION_INHERITED(nsSourceErrorEventRunner, nsMediaEventRunner, mSource) NS_IMPL_ADDREF_INHERITED(nsSourceErrorEventRunner, nsMediaEventRunner) NS_IMPL_RELEASE_INHERITED(nsSourceErrorEventRunner, nsMediaEventRunner) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSourceErrorEventRunner) NS_INTERFACE_MAP_END_INHERITING(nsMediaEventRunner) NS_IMETHODIMP nsTimeupdateRunner::Run() { if (IsCancelled() || !ShouldDispatchTimeupdate()) { return NS_OK; } // After dispatching `timeupdate`, if the timeupdate event listener takes lots // of time then we end up spending all time handling just timeupdate events. // The spec is vague in this situation, so we choose to update time after we // dispatch the event in order to solve that issue. nsresult rv = DispatchEvent(mEventName); if (NS_WARN_IF(NS_FAILED(rv))) { LOG_EVENT(LogLevel::Debug, ("%p Failed to dispatch 'timeupdate'", mElement.get())); } else { mElement->UpdateLastTimeupdateDispatchTime(); } return rv; } bool nsTimeupdateRunner::ShouldDispatchTimeupdate() const { if (mIsMandatory) { return true; } // If the main thread is busy, tasks may be delayed and dispatched at // unexpected times. Ensure we don't dispatch `timeupdate` more often // than once per `TIMEUPDATE_MS`. const TimeStamp& lastTime = mElement->LastTimeupdateDispatchTime(); return lastTime.IsNull() || TimeStamp::Now() - lastTime > TimeDuration::FromMilliseconds(TIMEUPDATE_MS); } #undef LOG_EVENT } // namespace mozilla::dom