1 /* vim:set ts=2 sw=2 sts=2 et cindent: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "BackgroundVideoDecodingPermissionObserver.h"
7 
8 #include "mozilla/AsyncEventDispatcher.h"
9 #include "mozilla/StaticPrefs_media.h"
10 #include "MediaDecoder.h"
11 #include "nsContentUtils.h"
12 #include "mozilla/dom/Document.h"
13 
14 namespace mozilla {
15 
16 BackgroundVideoDecodingPermissionObserver::
BackgroundVideoDecodingPermissionObserver(MediaDecoder * aDecoder)17     BackgroundVideoDecodingPermissionObserver(MediaDecoder* aDecoder)
18     : mDecoder(aDecoder), mIsRegisteredForEvent(false) {
19   MOZ_ASSERT(mDecoder);
20 }
21 
22 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)23 BackgroundVideoDecodingPermissionObserver::Observe(nsISupports* aSubject,
24                                                    const char* aTopic,
25                                                    const char16_t* aData) {
26   if (!StaticPrefs::media_resume_bkgnd_video_on_tabhover()) {
27     return NS_OK;
28   }
29 
30   if (!IsValidEventSender(aSubject)) {
31     return NS_OK;
32   }
33 
34   if (strcmp(aTopic, "unselected-tab-hover") == 0) {
35     bool allowed = !NS_strcmp(aData, u"true");
36     mDecoder->SetIsBackgroundVideoDecodingAllowed(allowed);
37   }
38   return NS_OK;
39 }
40 
RegisterEvent()41 void BackgroundVideoDecodingPermissionObserver::RegisterEvent() {
42   MOZ_ASSERT(!mIsRegisteredForEvent);
43   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
44   if (observerService) {
45     observerService->AddObserver(this, "unselected-tab-hover", false);
46     mIsRegisteredForEvent = true;
47     if (nsContentUtils::IsInStableOrMetaStableState()) {
48       // Events shall not be fired synchronously to prevent anything visible
49       // from the scripts while we are in stable state.
50       if (nsCOMPtr<dom::Document> doc = GetOwnerDoc()) {
51         doc->Dispatch(
52             TaskCategory::Other,
53             NewRunnableMethod(
54                 "BackgroundVideoDecodingPermissionObserver::"
55                 "EnableEvent",
56                 this, &BackgroundVideoDecodingPermissionObserver::EnableEvent));
57       }
58     } else {
59       EnableEvent();
60     }
61   }
62 }
63 
UnregisterEvent()64 void BackgroundVideoDecodingPermissionObserver::UnregisterEvent() {
65   MOZ_ASSERT(mIsRegisteredForEvent);
66   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
67   if (observerService) {
68     observerService->RemoveObserver(this, "unselected-tab-hover");
69     mIsRegisteredForEvent = false;
70     mDecoder->SetIsBackgroundVideoDecodingAllowed(false);
71     if (nsContentUtils::IsInStableOrMetaStableState()) {
72       // Events shall not be fired synchronously to prevent anything visible
73       // from the scripts while we are in stable state.
74       if (nsCOMPtr<dom::Document> doc = GetOwnerDoc()) {
75         doc->Dispatch(
76             TaskCategory::Other,
77             NewRunnableMethod(
78                 "BackgroundVideoDecodingPermissionObserver::"
79                 "DisableEvent",
80                 this,
81                 &BackgroundVideoDecodingPermissionObserver::DisableEvent));
82       }
83     } else {
84       DisableEvent();
85     }
86   }
87 }
88 
89 BackgroundVideoDecodingPermissionObserver::
~BackgroundVideoDecodingPermissionObserver()90     ~BackgroundVideoDecodingPermissionObserver() {
91   MOZ_ASSERT(!mIsRegisteredForEvent);
92 }
93 
EnableEvent() const94 void BackgroundVideoDecodingPermissionObserver::EnableEvent() const {
95   dom::Document* doc = GetOwnerDoc();
96   if (!doc) {
97     return;
98   }
99 
100   nsCOMPtr<nsPIDOMWindowOuter> ownerTop = GetOwnerWindow();
101   if (!ownerTop) {
102     return;
103   }
104 
105   RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
106       doc, NS_LITERAL_STRING("UnselectedTabHover:Enable"), CanBubble::eYes,
107       ChromeOnlyDispatch::eYes);
108   asyncDispatcher->PostDOMEvent();
109 }
110 
DisableEvent() const111 void BackgroundVideoDecodingPermissionObserver::DisableEvent() const {
112   dom::Document* doc = GetOwnerDoc();
113   if (!doc) {
114     return;
115   }
116 
117   nsCOMPtr<nsPIDOMWindowOuter> ownerTop = GetOwnerWindow();
118   if (!ownerTop) {
119     return;
120   }
121 
122   RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
123       doc, NS_LITERAL_STRING("UnselectedTabHover:Disable"), CanBubble::eYes,
124       ChromeOnlyDispatch::eYes);
125   asyncDispatcher->PostDOMEvent();
126 }
127 
128 already_AddRefed<nsPIDOMWindowOuter>
GetOwnerWindow() const129 BackgroundVideoDecodingPermissionObserver::GetOwnerWindow() const {
130   dom::Document* doc = GetOwnerDoc();
131   if (!doc) {
132     return nullptr;
133   }
134 
135   nsCOMPtr<nsPIDOMWindowInner> innerWin = doc->GetInnerWindow();
136   if (!innerWin) {
137     return nullptr;
138   }
139 
140   nsCOMPtr<nsPIDOMWindowOuter> outerWin = innerWin->GetOuterWindow();
141   if (!outerWin) {
142     return nullptr;
143   }
144 
145   nsCOMPtr<nsPIDOMWindowOuter> topWin = outerWin->GetInProcessTop();
146   return topWin.forget();
147 }
148 
GetOwnerDoc() const149 dom::Document* BackgroundVideoDecodingPermissionObserver::GetOwnerDoc() const {
150   if (!mDecoder->GetOwner()) {
151     return nullptr;
152   }
153 
154   return mDecoder->GetOwner()->GetDocument();
155 }
156 
IsValidEventSender(nsISupports * aSubject) const157 bool BackgroundVideoDecodingPermissionObserver::IsValidEventSender(
158     nsISupports* aSubject) const {
159   nsCOMPtr<nsPIDOMWindowInner> senderInner(do_QueryInterface(aSubject));
160   if (!senderInner) {
161     return false;
162   }
163 
164   nsCOMPtr<nsPIDOMWindowOuter> senderOuter = senderInner->GetOuterWindow();
165   if (!senderOuter) {
166     return false;
167   }
168 
169   nsCOMPtr<nsPIDOMWindowOuter> senderTop = senderOuter->GetInProcessTop();
170   if (!senderTop) {
171     return false;
172   }
173 
174   nsCOMPtr<nsPIDOMWindowOuter> ownerTop = GetOwnerWindow();
175   if (!ownerTop) {
176     return false;
177   }
178 
179   return ownerTop == senderTop;
180 }
181 
182 NS_IMPL_ISUPPORTS(BackgroundVideoDecodingPermissionObserver, nsIObserver)
183 
184 }  // namespace mozilla
185