Backed out 2 changesets (bug 1827651) for causing TestAUSHelper related bustages...
[gecko.git] / dom / push / PushNotifier.cpp
blobe36714c932ca5d65ddf7c6c3e5d88aa1cd803a78
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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/. */
7 #include "PushNotifier.h"
9 #include "nsContentUtils.h"
10 #include "nsCOMPtr.h"
11 #include "nsICategoryManager.h"
12 #include "nsIXULRuntime.h"
13 #include "nsNetUtil.h"
14 #include "nsXPCOM.h"
15 #include "mozilla/dom/ServiceWorkerManager.h"
16 #include "mozilla/BasePrincipal.h"
17 #include "mozilla/Preferences.h"
18 #include "mozilla/Services.h"
19 #include "mozilla/Unused.h"
21 #include "mozilla/dom/BodyUtil.h"
22 #include "mozilla/dom/ContentChild.h"
23 #include "mozilla/dom/ContentParent.h"
25 namespace mozilla::dom {
27 PushNotifier::PushNotifier() = default;
29 PushNotifier::~PushNotifier() = default;
31 NS_IMPL_CYCLE_COLLECTION_0(PushNotifier)
33 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushNotifier)
34 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPushNotifier)
35 NS_INTERFACE_MAP_ENTRY(nsIPushNotifier)
36 NS_INTERFACE_MAP_END
38 NS_IMPL_CYCLE_COLLECTING_ADDREF(PushNotifier)
39 NS_IMPL_CYCLE_COLLECTING_RELEASE(PushNotifier)
41 NS_IMETHODIMP
42 PushNotifier::NotifyPushWithData(const nsACString& aScope,
43 nsIPrincipal* aPrincipal,
44 const nsAString& aMessageId,
45 const nsTArray<uint8_t>& aData) {
46 NS_ENSURE_ARG(aPrincipal);
47 // We still need to do this copying business, if we want the copy to be
48 // fallible. Just passing Some(aData) would do an infallible copy at the
49 // point where the Some() call happens.
50 nsTArray<uint8_t> data;
51 if (!data.AppendElements(aData, fallible)) {
52 return NS_ERROR_OUT_OF_MEMORY;
54 PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId,
55 Some(std::move(data)));
56 return Dispatch(dispatcher);
59 NS_IMETHODIMP
60 PushNotifier::NotifyPush(const nsACString& aScope, nsIPrincipal* aPrincipal,
61 const nsAString& aMessageId) {
62 NS_ENSURE_ARG(aPrincipal);
63 PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
64 return Dispatch(dispatcher);
67 NS_IMETHODIMP
68 PushNotifier::NotifySubscriptionChange(const nsACString& aScope,
69 nsIPrincipal* aPrincipal) {
70 NS_ENSURE_ARG(aPrincipal);
71 PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
72 return Dispatch(dispatcher);
75 NS_IMETHODIMP
76 PushNotifier::NotifySubscriptionModified(const nsACString& aScope,
77 nsIPrincipal* aPrincipal) {
78 NS_ENSURE_ARG(aPrincipal);
79 PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
80 return Dispatch(dispatcher);
83 NS_IMETHODIMP
84 PushNotifier::NotifyError(const nsACString& aScope, nsIPrincipal* aPrincipal,
85 const nsAString& aMessage, uint32_t aFlags) {
86 NS_ENSURE_ARG(aPrincipal);
87 PushErrorDispatcher dispatcher(aScope, aPrincipal, aMessage, aFlags);
88 return Dispatch(dispatcher);
91 nsresult PushNotifier::Dispatch(PushDispatcher& aDispatcher) {
92 if (XRE_IsParentProcess()) {
93 // Always notify XPCOM observers in the parent process.
94 Unused << NS_WARN_IF(NS_FAILED(aDispatcher.NotifyObservers()));
96 // e10s is disabled; notify workers in the parent.
97 return aDispatcher.NotifyWorkers();
100 // Otherwise, we're in the content process, so e10s must be enabled. Notify
101 // observers and workers, then send a message to notify observers in the
102 // parent.
103 MOZ_ASSERT(XRE_IsContentProcess());
105 nsresult rv = aDispatcher.NotifyObserversAndWorkers();
107 ContentChild* parentActor = ContentChild::GetSingleton();
108 if (!NS_WARN_IF(!parentActor)) {
109 Unused << NS_WARN_IF(!aDispatcher.SendToParent(parentActor));
112 return rv;
115 PushData::PushData(const nsTArray<uint8_t>& aData) : mData(aData.Clone()) {}
117 PushData::~PushData() = default;
119 NS_IMPL_CYCLE_COLLECTION_0(PushData)
121 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushData)
122 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPushData)
123 NS_INTERFACE_MAP_ENTRY(nsIPushData)
124 NS_INTERFACE_MAP_END
126 NS_IMPL_CYCLE_COLLECTING_ADDREF(PushData)
127 NS_IMPL_CYCLE_COLLECTING_RELEASE(PushData)
129 nsresult PushData::EnsureDecodedText() {
130 if (mData.IsEmpty() || !mDecodedText.IsEmpty()) {
131 return NS_OK;
133 nsresult rv = BodyUtil::ConsumeText(
134 mData.Length(), reinterpret_cast<uint8_t*>(mData.Elements()),
135 mDecodedText);
136 if (NS_WARN_IF(NS_FAILED(rv))) {
137 mDecodedText.Truncate();
138 return rv;
140 return NS_OK;
143 NS_IMETHODIMP
144 PushData::Text(nsAString& aText) {
145 nsresult rv = EnsureDecodedText();
146 if (NS_WARN_IF(NS_FAILED(rv))) {
147 return rv;
149 aText = mDecodedText;
150 return NS_OK;
153 NS_IMETHODIMP
154 PushData::Json(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) {
155 nsresult rv = EnsureDecodedText();
156 if (NS_WARN_IF(NS_FAILED(rv))) {
157 return rv;
159 ErrorResult error;
160 BodyUtil::ConsumeJson(aCx, aResult, mDecodedText, error);
161 return error.StealNSResult();
164 NS_IMETHODIMP
165 PushData::Binary(nsTArray<uint8_t>& aData) {
166 aData = mData.Clone();
167 return NS_OK;
170 PushMessage::PushMessage(nsIPrincipal* aPrincipal, nsIPushData* aData)
171 : mPrincipal(aPrincipal), mData(aData) {}
173 PushMessage::~PushMessage() = default;
175 NS_IMPL_CYCLE_COLLECTION(PushMessage, mPrincipal, mData)
177 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushMessage)
178 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPushMessage)
179 NS_INTERFACE_MAP_ENTRY(nsIPushMessage)
180 NS_INTERFACE_MAP_END
182 NS_IMPL_CYCLE_COLLECTING_ADDREF(PushMessage)
183 NS_IMPL_CYCLE_COLLECTING_RELEASE(PushMessage)
185 NS_IMETHODIMP
186 PushMessage::GetPrincipal(nsIPrincipal** aPrincipal) {
187 NS_ENSURE_ARG_POINTER(aPrincipal);
189 nsCOMPtr<nsIPrincipal> principal = mPrincipal;
190 principal.forget(aPrincipal);
191 return NS_OK;
194 NS_IMETHODIMP
195 PushMessage::GetData(nsIPushData** aData) {
196 NS_ENSURE_ARG_POINTER(aData);
198 nsCOMPtr<nsIPushData> data = mData;
199 data.forget(aData);
200 return NS_OK;
203 PushDispatcher::PushDispatcher(const nsACString& aScope,
204 nsIPrincipal* aPrincipal)
205 : mScope(aScope), mPrincipal(aPrincipal) {}
207 PushDispatcher::~PushDispatcher() = default;
209 nsresult PushDispatcher::HandleNoChildProcesses() { return NS_OK; }
211 nsresult PushDispatcher::NotifyObserversAndWorkers() {
212 Unused << NS_WARN_IF(NS_FAILED(NotifyObservers()));
213 return NotifyWorkers();
216 bool PushDispatcher::ShouldNotifyWorkers() {
217 if (NS_WARN_IF(!mPrincipal)) {
218 return false;
221 // System subscriptions use observer notifications instead of service worker
222 // events. The `testing.notifyWorkers` pref disables worker events for
223 // non-system subscriptions.
224 if (mPrincipal->IsSystemPrincipal() ||
225 !Preferences::GetBool("dom.push.testing.notifyWorkers", true)) {
226 return false;
229 // If e10s is off, no need to worry about processes.
230 if (!BrowserTabsRemoteAutostart()) {
231 return true;
234 // We only want to notify in the parent process.
235 bool isContentProcess = XRE_GetProcessType() == GeckoProcessType_Content;
236 return !isContentProcess;
239 nsresult PushDispatcher::DoNotifyObservers(nsISupports* aSubject,
240 const char* aTopic,
241 const nsACString& aScope) {
242 nsCOMPtr<nsIObserverService> obsService =
243 mozilla::services::GetObserverService();
244 if (!obsService) {
245 return NS_ERROR_FAILURE;
247 // If there's a service for this push category, make sure it is alive.
248 nsCOMPtr<nsICategoryManager> catMan =
249 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
250 if (catMan) {
251 nsCString contractId;
252 nsresult rv = catMan->GetCategoryEntry("push", mScope, contractId);
253 if (NS_SUCCEEDED(rv)) {
254 // Ensure the service is created - we don't need to do anything with
255 // it though - we assume the service constructor attaches a listener.
256 nsCOMPtr<nsISupports> service = do_GetService(contractId.get());
259 return obsService->NotifyObservers(aSubject, aTopic,
260 NS_ConvertUTF8toUTF16(mScope).get());
263 PushMessageDispatcher::PushMessageDispatcher(
264 const nsACString& aScope, nsIPrincipal* aPrincipal,
265 const nsAString& aMessageId, const Maybe<nsTArray<uint8_t>>& aData)
266 : PushDispatcher(aScope, aPrincipal),
267 mMessageId(aMessageId),
268 mData(aData ? Some(aData->Clone()) : Nothing()) {}
270 PushMessageDispatcher::~PushMessageDispatcher() = default;
272 nsresult PushMessageDispatcher::NotifyObservers() {
273 nsCOMPtr<nsIPushData> data;
274 if (mData) {
275 data = new PushData(mData.ref());
277 nsCOMPtr<nsIPushMessage> message = new PushMessage(mPrincipal, data);
278 return DoNotifyObservers(message, OBSERVER_TOPIC_PUSH, mScope);
281 nsresult PushMessageDispatcher::NotifyWorkers() {
282 if (!ShouldNotifyWorkers()) {
283 return NS_OK;
285 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
286 if (!swm) {
287 return NS_ERROR_FAILURE;
289 nsAutoCString originSuffix;
290 nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
291 if (NS_WARN_IF(NS_FAILED(rv))) {
292 return rv;
294 return swm->SendPushEvent(originSuffix, mScope, mMessageId, mData);
297 bool PushMessageDispatcher::SendToParent(ContentChild* aParentActor) {
298 if (mData) {
299 return aParentActor->SendNotifyPushObserversWithData(
300 mScope, mPrincipal, mMessageId, mData.ref());
302 return aParentActor->SendNotifyPushObservers(mScope, mPrincipal, mMessageId);
305 bool PushMessageDispatcher::SendToChild(ContentParent* aContentActor) {
306 if (mData) {
307 return aContentActor->SendPushWithData(mScope, mPrincipal, mMessageId,
308 mData.ref());
310 return aContentActor->SendPush(mScope, mPrincipal, mMessageId);
313 PushSubscriptionChangeDispatcher::PushSubscriptionChangeDispatcher(
314 const nsACString& aScope, nsIPrincipal* aPrincipal)
315 : PushDispatcher(aScope, aPrincipal) {}
317 PushSubscriptionChangeDispatcher::~PushSubscriptionChangeDispatcher() = default;
319 nsresult PushSubscriptionChangeDispatcher::NotifyObservers() {
320 return DoNotifyObservers(mPrincipal, OBSERVER_TOPIC_SUBSCRIPTION_CHANGE,
321 mScope);
324 nsresult PushSubscriptionChangeDispatcher::NotifyWorkers() {
325 if (!ShouldNotifyWorkers()) {
326 return NS_OK;
328 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
329 if (!swm) {
330 return NS_ERROR_FAILURE;
332 nsAutoCString originSuffix;
333 nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
334 if (NS_WARN_IF(NS_FAILED(rv))) {
335 return rv;
337 return swm->SendPushSubscriptionChangeEvent(originSuffix, mScope);
340 bool PushSubscriptionChangeDispatcher::SendToParent(
341 ContentChild* aParentActor) {
342 return aParentActor->SendNotifyPushSubscriptionChangeObservers(mScope,
343 mPrincipal);
346 bool PushSubscriptionChangeDispatcher::SendToChild(
347 ContentParent* aContentActor) {
348 return aContentActor->SendPushSubscriptionChange(mScope, mPrincipal);
351 PushSubscriptionModifiedDispatcher::PushSubscriptionModifiedDispatcher(
352 const nsACString& aScope, nsIPrincipal* aPrincipal)
353 : PushDispatcher(aScope, aPrincipal) {}
355 PushSubscriptionModifiedDispatcher::~PushSubscriptionModifiedDispatcher() =
356 default;
358 nsresult PushSubscriptionModifiedDispatcher::NotifyObservers() {
359 return DoNotifyObservers(mPrincipal, OBSERVER_TOPIC_SUBSCRIPTION_MODIFIED,
360 mScope);
363 nsresult PushSubscriptionModifiedDispatcher::NotifyWorkers() { return NS_OK; }
365 bool PushSubscriptionModifiedDispatcher::SendToParent(
366 ContentChild* aParentActor) {
367 return aParentActor->SendNotifyPushSubscriptionModifiedObservers(mScope,
368 mPrincipal);
371 bool PushSubscriptionModifiedDispatcher::SendToChild(
372 ContentParent* aContentActor) {
373 return aContentActor->SendNotifyPushSubscriptionModifiedObservers(mScope,
374 mPrincipal);
377 PushErrorDispatcher::PushErrorDispatcher(const nsACString& aScope,
378 nsIPrincipal* aPrincipal,
379 const nsAString& aMessage,
380 uint32_t aFlags)
381 : PushDispatcher(aScope, aPrincipal), mMessage(aMessage), mFlags(aFlags) {}
383 PushErrorDispatcher::~PushErrorDispatcher() = default;
385 nsresult PushErrorDispatcher::NotifyObservers() { return NS_OK; }
387 nsresult PushErrorDispatcher::NotifyWorkers() {
388 if (!ShouldNotifyWorkers() &&
389 (!mPrincipal || mPrincipal->IsSystemPrincipal())) {
390 // For system subscriptions, log the error directly to the browser console.
391 return nsContentUtils::ReportToConsoleNonLocalized(
392 mMessage, mFlags, "Push"_ns, nullptr, /* aDocument */
393 SourceLocation());
396 // For service worker subscriptions, report the error to all clients.
397 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
398 if (swm) {
399 swm->ReportToAllClients(mScope, mMessage, mScope, /* aFilename */
400 u""_ns, /* aLine */
401 0, /* aLineNumber */
402 0, /* aColumnNumber */
403 mFlags);
405 return NS_OK;
408 bool PushErrorDispatcher::SendToParent(ContentChild* aContentActor) {
409 return aContentActor->SendPushError(mScope, mPrincipal, mMessage, mFlags);
412 bool PushErrorDispatcher::SendToChild(ContentParent* aContentActor) {
413 return aContentActor->SendPushError(mScope, mPrincipal, mMessage, mFlags);
416 nsresult PushErrorDispatcher::HandleNoChildProcesses() {
417 // Report to the console if no content processes are active.
418 nsCOMPtr<nsIURI> scopeURI;
419 nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), mScope);
420 if (NS_WARN_IF(NS_FAILED(rv))) {
421 return rv;
423 return nsContentUtils::ReportToConsoleNonLocalized(
424 mMessage, mFlags, "Push"_ns, /* aDocument = */ nullptr,
425 SourceLocation(scopeURI.get()));
428 } // namespace mozilla::dom