diff --git a/dom/media/BackgroundVideoDecodingPermissionObserver.cpp b/dom/media/BackgroundVideoDecodingPermissionObserver.cpp new file mode 100644 index 000000000000..0a1a3e405c74 --- /dev/null +++ b/dom/media/BackgroundVideoDecodingPermissionObserver.cpp @@ -0,0 +1,198 @@ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 "BackgroundVideoDecodingPermissionObserver.h" + +#include "mozilla/AsyncEventDispatcher.h" +#include "MediaDecoder.h" +#include "MediaPrefs.h" +#include "nsContentUtils.h" +#include "nsIDocument.h" + +namespace mozilla { + +BackgroundVideoDecodingPermissionObserver:: + BackgroundVideoDecodingPermissionObserver(MediaDecoder* aDecoder) + : mDecoder(aDecoder) + , mIsRegisteredForEvent(false) +{ + MOZ_ASSERT(mDecoder); +} + +NS_IMETHODIMP +BackgroundVideoDecodingPermissionObserver::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + if (!MediaPrefs::ResumeVideoDecodingOnTabHover()) { + return NS_OK; + } + + if (!IsValidEventSender(aSubject)) { + return NS_OK; + } + + if (strcmp(aTopic, "unselected-tab-hover") == 0) { + bool allowed = !NS_strcmp(aData, u"true"); + mDecoder->SetIsBackgroundVideoDecodingAllowed(allowed); + } + return NS_OK; +} + +void +BackgroundVideoDecodingPermissionObserver::RegisterEvent() +{ + MOZ_ASSERT(!mIsRegisteredForEvent); + nsCOMPtr observerService = services::GetObserverService(); + if (observerService) { + observerService->AddObserver(this, "unselected-tab-hover", false); + mIsRegisteredForEvent = true; + if (nsContentUtils::IsInStableOrMetaStableState()) { + // Events shall not be fired synchronously to prevent anything visible + // from the scripts while we are in stable state. + if (nsCOMPtr doc = GetOwnerDoc()) { + doc->Dispatch( + TaskCategory::Other, + NewRunnableMethod( + "BackgroundVideoDecodingPermissionObserver::" + "EnableEvent", + this, + &BackgroundVideoDecodingPermissionObserver:: + EnableEvent)); + } + } else { + EnableEvent(); + } + } +} + +void +BackgroundVideoDecodingPermissionObserver::UnregisterEvent() +{ + MOZ_ASSERT(mIsRegisteredForEvent); + nsCOMPtr observerService = services::GetObserverService(); + if (observerService) { + observerService->RemoveObserver(this, "unselected-tab-hover"); + mIsRegisteredForEvent = false; + mDecoder->SetIsBackgroundVideoDecodingAllowed(false); + if (nsContentUtils::IsInStableOrMetaStableState()) { + // Events shall not be fired synchronously to prevent anything visible + // from the scripts while we are in stable state. + if (nsCOMPtr doc = GetOwnerDoc()) { + doc->Dispatch( + TaskCategory::Other, + NewRunnableMethod( + "BackgroundVideoDecodingPermissionObserver::" + "DisableEvent", + this, + &BackgroundVideoDecodingPermissionObserver:: + DisableEvent)); + } + } else { + DisableEvent(); + } + } +} + +BackgroundVideoDecodingPermissionObserver:: + ~BackgroundVideoDecodingPermissionObserver() +{ + MOZ_ASSERT(!mIsRegisteredForEvent); +} + +void +BackgroundVideoDecodingPermissionObserver::EnableEvent() const +{ + nsIDocument* doc = GetOwnerDoc(); + if (!doc) { + return; + } + + RefPtr asyncDispatcher = + new AsyncEventDispatcher(doc, + NS_LITERAL_STRING("UnselectedTabHover:Enable"), + /* Bubbles */ true, + /* OnlyChromeDispatch */ true); + asyncDispatcher->PostDOMEvent(); +} + +void +BackgroundVideoDecodingPermissionObserver::DisableEvent() const +{ + nsIDocument* doc = GetOwnerDoc(); + if (!doc) { + return; + } + + RefPtr asyncDispatcher = + new AsyncEventDispatcher(doc, + NS_LITERAL_STRING("UnselectedTabHover:Disable"), + /* Bubbles */ true, + /* OnlyChromeDispatch */ true); + asyncDispatcher->PostDOMEvent(); +} + +already_AddRefed +BackgroundVideoDecodingPermissionObserver::GetOwnerWindow() const +{ + nsIDocument* doc = GetOwnerDoc(); + if (!doc) { + return nullptr; + } + + nsCOMPtr innerWin = doc->GetInnerWindow(); + if (!innerWin) { + return nullptr; + } + + nsCOMPtr outerWin = innerWin->GetOuterWindow(); + if (!outerWin) { + return nullptr; + } + + nsCOMPtr topWin = outerWin->GetTop(); + return topWin.forget(); +} + +nsIDocument* +BackgroundVideoDecodingPermissionObserver::GetOwnerDoc() const +{ + if (!mDecoder->GetOwner()) { + return nullptr; + } + + return mDecoder->GetOwner()->GetDocument(); +} + +bool +BackgroundVideoDecodingPermissionObserver::IsValidEventSender( + nsISupports* aSubject) const +{ + nsCOMPtr senderInner(do_QueryInterface(aSubject)); + if (!senderInner) { + return false; + } + + nsCOMPtr senderOuter = senderInner->GetOuterWindow(); + if (!senderOuter) { + return false; + } + + nsCOMPtr senderTop = senderOuter->GetTop(); + if (!senderTop) { + return false; + } + + nsCOMPtr ownerTop = GetOwnerWindow(); + if (!ownerTop) { + return false; + } + + return ownerTop == senderTop; +} + +NS_IMPL_ISUPPORTS(BackgroundVideoDecodingPermissionObserver, nsIObserver) + +} // namespace mozilla diff --git a/dom/media/BackgroundVideoDecodingPermissionObserver.h b/dom/media/BackgroundVideoDecodingPermissionObserver.h new file mode 100644 index 000000000000..a3f32020cb03 --- /dev/null +++ b/dom/media/BackgroundVideoDecodingPermissionObserver.h @@ -0,0 +1,48 @@ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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/. */ + +#if !defined(BackgroundVideoDecodingPermissionObserver_h_) +#define BackgroundVideoDecodingPermissionObserver_h_ + +#include "nsIObserver.h" +#include "nsISupportsImpl.h" + +class nsIDocument; +class nsISupports; +class nsPIDOMWindowOuter; + +namespace mozilla { + +class MediaDecoder; + +class BackgroundVideoDecodingPermissionObserver final + : public nsIObserver +{ + public: + NS_DECL_ISUPPORTS + + explicit BackgroundVideoDecodingPermissionObserver(MediaDecoder* aDecoder); + + NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) override; + void RegisterEvent(); + void UnregisterEvent(); + private: + ~BackgroundVideoDecodingPermissionObserver(); + void EnableEvent() const; + void DisableEvent() const; + already_AddRefed GetOwnerWindow() const; + nsIDocument* GetOwnerDoc() const; + bool IsValidEventSender(nsISupports* aSubject) const; + + // The life cycle of observer would always be shorter than decoder, so we + // use raw pointer here. + MediaDecoder* mDecoder; + bool mIsRegisteredForEvent; +}; + +} // namespace mozilla + +#endif // BackgroundVideoDecodingPermissionObserver_h_ diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index ea48115514b3..6fe3173bf682 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -15,7 +15,6 @@ #include "VideoFrameContainer.h" #include "VideoUtils.h" #include "mozilla/AbstractThread.h" -#include "mozilla/AsyncEventDispatcher.h" #include "mozilla/FloatingPoint.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/Preferences.h" @@ -123,177 +122,6 @@ public: } }; -class MediaDecoder::BackgroundVideoDecodingPermissionObserver final : - public nsIObserver -{ - public: - NS_DECL_ISUPPORTS - - explicit BackgroundVideoDecodingPermissionObserver(MediaDecoder* aDecoder) - : mDecoder(aDecoder) - , mIsRegisteredForEvent(false) - { - MOZ_ASSERT(mDecoder); - } - - NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, - const char16_t* aData) override - { - if (!MediaPrefs::ResumeVideoDecodingOnTabHover()) { - return NS_OK; - } - - if (!IsValidEventSender(aSubject)) { - return NS_OK; - } - - if (strcmp(aTopic, "unselected-tab-hover") == 0) { - mDecoder->mIsBackgroundVideoDecodingAllowed = !NS_strcmp(aData, u"true"); - mDecoder->UpdateVideoDecodeMode(); - } - return NS_OK; - } - - void RegisterEvent() { - MOZ_ASSERT(!mIsRegisteredForEvent); - nsCOMPtr observerService = services::GetObserverService(); - if (observerService) { - observerService->AddObserver(this, "unselected-tab-hover", false); - mIsRegisteredForEvent = true; - if (nsContentUtils::IsInStableOrMetaStableState()) { - // Events shall not be fired synchronously to prevent anything visible - // from the scripts while we are in stable state. - if (nsCOMPtr doc = GetOwnerDoc()) { - doc->Dispatch(TaskCategory::Other, - NewRunnableMethod( - "MediaDecoder::BackgroundVideoDecodingPermissionObserver::EnableEvent", - this, &MediaDecoder::BackgroundVideoDecodingPermissionObserver::EnableEvent)); - } - } else { - EnableEvent(); - } - } - } - - void UnregisterEvent() { - MOZ_ASSERT(mIsRegisteredForEvent); - nsCOMPtr observerService = services::GetObserverService(); - if (observerService) { - observerService->RemoveObserver(this, "unselected-tab-hover"); - mIsRegisteredForEvent = false; - mDecoder->mIsBackgroundVideoDecodingAllowed = false; - mDecoder->UpdateVideoDecodeMode(); - if (nsContentUtils::IsInStableOrMetaStableState()) { - // Events shall not be fired synchronously to prevent anything visible - // from the scripts while we are in stable state. - if (nsCOMPtr doc = GetOwnerDoc()) { - doc->Dispatch(TaskCategory::Other, - NewRunnableMethod( - "MediaDecoder::BackgroundVideoDecodingPermissionObserver::DisableEvent", - this, &MediaDecoder::BackgroundVideoDecodingPermissionObserver::DisableEvent)); - } - } else { - DisableEvent(); - } - } - } - private: - ~BackgroundVideoDecodingPermissionObserver() { - MOZ_ASSERT(!mIsRegisteredForEvent); - } - - void EnableEvent() const - { - nsIDocument* doc = GetOwnerDoc(); - if (!doc) { - return; - } - - RefPtr asyncDispatcher = - new AsyncEventDispatcher(doc, - NS_LITERAL_STRING("UnselectedTabHover:Enable"), - /* Bubbles */ true, - /* OnlyChromeDispatch */ true); - asyncDispatcher->PostDOMEvent(); - } - - void DisableEvent() const - { - nsIDocument* doc = GetOwnerDoc(); - if (!doc) { - return; - } - - RefPtr asyncDispatcher = - new AsyncEventDispatcher(doc, - NS_LITERAL_STRING("UnselectedTabHover:Disable"), - /* Bubbles */ true, - /* OnlyChromeDispatch */ true); - asyncDispatcher->PostDOMEvent(); - } - - already_AddRefed GetOwnerWindow() const - { - nsIDocument* doc = GetOwnerDoc(); - if (!doc) { - return nullptr; - } - - nsCOMPtr innerWin = doc->GetInnerWindow(); - if (!innerWin) { - return nullptr; - } - - nsCOMPtr outerWin = innerWin->GetOuterWindow(); - if (!outerWin) { - return nullptr; - } - - nsCOMPtr topWin = outerWin->GetTop(); - return topWin.forget(); - } - - nsIDocument* GetOwnerDoc() const - { - if (!mDecoder->mOwner) { - return nullptr; - } - - return mDecoder->mOwner->GetDocument(); - } - - bool IsValidEventSender(nsISupports* aSubject) const - { - nsCOMPtr senderInner(do_QueryInterface(aSubject)); - if (!senderInner) { - return false; - } - - nsCOMPtr senderOuter = senderInner->GetOuterWindow(); - if (!senderOuter) { - return false; - } - - nsCOMPtr senderTop = senderOuter->GetTop(); - if (!senderTop) { - return false; - } - - nsCOMPtr ownerTop = GetOwnerWindow(); - if (!ownerTop) { - return false; - } - - return ownerTop == senderTop; - } - // The life cycle of observer would always be shorter than decoder, so we - // use raw pointer here. - MediaDecoder* mDecoder; - bool mIsRegisteredForEvent; -}; - -NS_IMPL_ISUPPORTS(MediaDecoder::BackgroundVideoDecodingPermissionObserver, nsIObserver) - StaticRefPtr MediaMemoryTracker::sUniqueInstance; LazyLogModule gMediaTimerLog("MediaTimer"); @@ -1200,6 +1028,13 @@ MediaDecoder::UpdateVideoDecodeMode() } } +void +MediaDecoder::SetIsBackgroundVideoDecodingAllowed(bool aAllowed) +{ + mIsBackgroundVideoDecodingAllowed = aAllowed; + UpdateVideoDecodeMode(); +} + bool MediaDecoder::HasSuspendTaint() const { diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index 2c10aa734120..e07840dd1298 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -7,6 +7,7 @@ #if !defined(MediaDecoder_h_) #define MediaDecoder_h_ +#include "BackgroundVideoDecodingPermissionObserver.h" #include "DecoderDoctorDiagnostics.h" #include "MediaContainerType.h" #include "MediaDecoderOwner.h" @@ -306,6 +307,8 @@ private: void UpdateVideoDecodeMode(); + void SetIsBackgroundVideoDecodingAllowed(bool aAllowed); + /****** * The following methods must only be called on the main * thread. @@ -641,7 +644,6 @@ protected: // We can allow video decoding in background when we match some special // conditions, eg. when the cursor is hovering over the tab. This observer is // used to listen the related events. - class BackgroundVideoDecodingPermissionObserver; RefPtr mVideoDecodingOberver; // True if we want to resume video decoding even the media element is in the diff --git a/dom/media/moz.build b/dom/media/moz.build index a34b66ed30b7..0c0208573e8b 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -99,6 +99,7 @@ EXPORTS += [ 'AudioSegment.h', 'AudioStream.h', 'AutoplayPolicy.h', + 'BackgroundVideoDecodingPermissionObserver.h', 'Benchmark.h', 'BitReader.h', 'BufferMediaResource.h', @@ -215,6 +216,7 @@ UNIFIED_SOURCES += [ 'AudioTrack.cpp', 'AudioTrackList.cpp', 'AutoplayPolicy.cpp', + 'BackgroundVideoDecodingPermissionObserver.cpp', 'BaseMediaResource.cpp', 'Benchmark.cpp', 'BitReader.cpp',