Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "PresentationAvailability.h"
8 :
9 : #include "mozilla/dom/PresentationAvailabilityBinding.h"
10 : #include "mozilla/dom/Promise.h"
11 : #include "mozilla/Unused.h"
12 : #include "nsCycleCollectionParticipant.h"
13 : #include "nsIPresentationDeviceManager.h"
14 : #include "nsIPresentationService.h"
15 : #include "nsServiceManagerUtils.h"
16 : #include "PresentationLog.h"
17 :
18 : using namespace mozilla;
19 : using namespace mozilla::dom;
20 :
21 : NS_IMPL_CYCLE_COLLECTION_CLASS(PresentationAvailability)
22 :
23 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PresentationAvailability, DOMEventTargetHelper)
24 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromises)
25 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
26 :
27 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PresentationAvailability, DOMEventTargetHelper)
28 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromises);
29 0 : tmp->Shutdown();
30 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
31 :
32 0 : NS_IMPL_ADDREF_INHERITED(PresentationAvailability, DOMEventTargetHelper)
33 0 : NS_IMPL_RELEASE_INHERITED(PresentationAvailability, DOMEventTargetHelper)
34 :
35 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationAvailability)
36 0 : NS_INTERFACE_MAP_ENTRY(nsIPresentationAvailabilityListener)
37 0 : NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
38 :
39 : /* static */ already_AddRefed<PresentationAvailability>
40 0 : PresentationAvailability::Create(nsPIDOMWindowInner* aWindow,
41 : const nsTArray<nsString>& aUrls,
42 : RefPtr<Promise>& aPromise)
43 : {
44 : RefPtr<PresentationAvailability> availability =
45 0 : new PresentationAvailability(aWindow, aUrls);
46 0 : return NS_WARN_IF(!availability->Init(aPromise)) ? nullptr
47 0 : : availability.forget();
48 : }
49 :
50 0 : PresentationAvailability::PresentationAvailability(nsPIDOMWindowInner* aWindow,
51 0 : const nsTArray<nsString>& aUrls)
52 : : DOMEventTargetHelper(aWindow)
53 : , mIsAvailable(false)
54 0 : , mUrls(aUrls)
55 : {
56 0 : for (uint32_t i = 0; i < mUrls.Length(); ++i) {
57 0 : mAvailabilityOfUrl.AppendElement(false);
58 : }
59 0 : }
60 :
61 0 : PresentationAvailability::~PresentationAvailability()
62 : {
63 0 : Shutdown();
64 0 : }
65 :
66 : bool
67 0 : PresentationAvailability::Init(RefPtr<Promise>& aPromise)
68 : {
69 : nsCOMPtr<nsIPresentationService> service =
70 0 : do_GetService(PRESENTATION_SERVICE_CONTRACTID);
71 0 : if (NS_WARN_IF(!service)) {
72 0 : return false;
73 : }
74 :
75 0 : nsresult rv = service->RegisterAvailabilityListener(mUrls, this);
76 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
77 : // If the user agent is unable to monitor available device,
78 : // Resolve promise with |value| set to false.
79 0 : mIsAvailable = false;
80 0 : aPromise->MaybeResolve(this);
81 0 : return true;
82 : }
83 :
84 0 : EnqueuePromise(aPromise);
85 :
86 0 : AvailabilityCollection* collection = AvailabilityCollection::GetSingleton();
87 0 : if (collection) {
88 0 : collection->Add(this);
89 : }
90 :
91 0 : return true;
92 : }
93 :
94 0 : void PresentationAvailability::Shutdown()
95 : {
96 0 : AvailabilityCollection* collection = AvailabilityCollection::GetSingleton();
97 0 : if (collection ) {
98 0 : collection->Remove(this);
99 : }
100 :
101 : nsCOMPtr<nsIPresentationService> service =
102 0 : do_GetService(PRESENTATION_SERVICE_CONTRACTID);
103 0 : if (NS_WARN_IF(!service)) {
104 0 : return;
105 : }
106 :
107 : Unused <<
108 0 : NS_WARN_IF(NS_FAILED(service->UnregisterAvailabilityListener(mUrls,
109 : this)));
110 : }
111 :
112 : /* virtual */ void
113 0 : PresentationAvailability::DisconnectFromOwner()
114 : {
115 0 : Shutdown();
116 0 : DOMEventTargetHelper::DisconnectFromOwner();
117 0 : }
118 :
119 : /* virtual */ JSObject*
120 0 : PresentationAvailability::WrapObject(JSContext* aCx,
121 : JS::Handle<JSObject*> aGivenProto)
122 : {
123 0 : return PresentationAvailabilityBinding::Wrap(aCx, this, aGivenProto);
124 : }
125 :
126 : bool
127 0 : PresentationAvailability::Equals(const uint64_t aWindowID,
128 : const nsTArray<nsString>& aUrls) const
129 : {
130 0 : if (GetOwner() && GetOwner()->WindowID() == aWindowID &&
131 0 : mUrls.Length() == aUrls.Length()) {
132 0 : for (const auto& url : aUrls) {
133 0 : if (!mUrls.Contains(url)) {
134 0 : return false;
135 : }
136 : }
137 0 : return true;
138 : }
139 :
140 0 : return false;
141 : }
142 :
143 : bool
144 0 : PresentationAvailability::IsCachedValueReady()
145 : {
146 : // All pending promises will be solved when cached value is ready and
147 : // no promise should be enqueued afterward.
148 0 : return mPromises.IsEmpty();
149 : }
150 :
151 : void
152 0 : PresentationAvailability::EnqueuePromise(RefPtr<Promise>& aPromise)
153 : {
154 0 : mPromises.AppendElement(aPromise);
155 0 : }
156 :
157 : bool
158 0 : PresentationAvailability::Value() const
159 : {
160 0 : return mIsAvailable;
161 : }
162 :
163 : NS_IMETHODIMP
164 0 : PresentationAvailability::NotifyAvailableChange(const nsTArray<nsString>& aAvailabilityUrls,
165 : bool aIsAvailable)
166 : {
167 0 : bool available = false;
168 0 : for (uint32_t i = 0; i < mUrls.Length(); ++i) {
169 0 : if (aAvailabilityUrls.Contains(mUrls[i])) {
170 0 : mAvailabilityOfUrl[i] = aIsAvailable;
171 : }
172 0 : available |= mAvailabilityOfUrl[i];
173 : }
174 :
175 0 : return NS_DispatchToCurrentThread(NewRunnableMethod<bool>(
176 : "dom::PresentationAvailability::UpdateAvailabilityAndDispatchEvent",
177 : this,
178 : &PresentationAvailability::UpdateAvailabilityAndDispatchEvent,
179 0 : available));
180 : }
181 :
182 : void
183 0 : PresentationAvailability::UpdateAvailabilityAndDispatchEvent(bool aIsAvailable)
184 : {
185 0 : PRES_DEBUG("%s\n", __func__);
186 0 : bool isChanged = (aIsAvailable != mIsAvailable);
187 :
188 0 : mIsAvailable = aIsAvailable;
189 :
190 0 : if (!mPromises.IsEmpty()) {
191 : // Use the first availability change notification to resolve promise.
192 0 : do {
193 0 : nsTArray<RefPtr<Promise>> promises = Move(mPromises);
194 0 : for (auto& promise : promises) {
195 0 : promise->MaybeResolve(this);
196 : }
197 : // more promises may have been added to mPromises, at least in theory
198 0 : } while (!mPromises.IsEmpty());
199 :
200 0 : return;
201 : }
202 :
203 0 : if (isChanged) {
204 : Unused <<
205 0 : NS_WARN_IF(NS_FAILED(DispatchTrustedEvent(NS_LITERAL_STRING("change"))));
206 : }
207 : }
|