summaryrefslogtreecommitdiffstats
path: root/dom/media/utils/MediaElementEventRunners.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/utils/MediaElementEventRunners.cpp')
-rw-r--r--dom/media/utils/MediaElementEventRunners.cpp140
1 files changed, 140 insertions, 0 deletions
diff --git a/dom/media/utils/MediaElementEventRunners.cpp b/dom/media/utils/MediaElementEventRunners.cpp
new file mode 100644
index 0000000000..57be04528c
--- /dev/null
+++ b/dom/media/utils/MediaElementEventRunners.cpp
@@ -0,0 +1,140 @@
+/* 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<RefPtr<PlayPromise>>&& 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