Bug 1688354 [wpt PR 27298] - Treat 'rem' as an absolute unit for font size, a=testonly
[gecko.git] / dom / push / PushNotifier.cpp
blobd74e101cb17af8e707bdc5b081f9785fa8d0f2aa
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 {
26 namespace dom {
28 PushNotifier::PushNotifier() = default;
30 PushNotifier::~PushNotifier() = default;
32 NS_IMPL_CYCLE_COLLECTION_0(PushNotifier)
34 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushNotifier)
35 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPushNotifier)
36 NS_INTERFACE_MAP_ENTRY(nsIPushNotifier)
37 NS_INTERFACE_MAP_END
39 NS_IMPL_CYCLE_COLLECTING_ADDREF(PushNotifier)
40 NS_IMPL_CYCLE_COLLECTING_RELEASE(PushNotifier)
42 NS_IMETHODIMP
43 PushNotifier::NotifyPushWithData(const nsACString& aScope,
44 nsIPrincipal* aPrincipal,
45 const nsAString& aMessageId,
46 const nsTArray<uint8_t>& aData) {
47 NS_ENSURE_ARG(aPrincipal);
48 // We still need to do this copying business, if we want the copy to be
49 // fallible. Just passing Some(aData) would do an infallible copy at the
50 // point where the Some() call happens.
51 nsTArray<uint8_t> data;
52 if (!data.AppendElements(aData, fallible)) {
53 return NS_ERROR_OUT_OF_MEMORY;
55 PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId,
56 Some(std::move(data)));
57 return Dispatch(dispatcher);
60 NS_IMETHODIMP
61 PushNotifier::NotifyPush(const nsACString& aScope, nsIPrincipal* aPrincipal,
62 const nsAString& aMessageId) {
63 NS_ENSURE_ARG(aPrincipal);
64 PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
65 return Dispatch(dispatcher);
68 NS_IMETHODIMP
69 PushNotifier::NotifySubscriptionChange(const nsACString& aScope,
70 nsIPrincipal* aPrincipal) {
71 NS_ENSURE_ARG(aPrincipal);
72 PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
73 return Dispatch(dispatcher);
76 NS_IMETHODIMP
77 PushNotifier::NotifySubscriptionModified(const nsACString& aScope,
78 nsIPrincipal* aPrincipal) {
79 NS_ENSURE_ARG(aPrincipal);
80 PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
81 return Dispatch(dispatcher);
84 NS_IMETHODIMP
85 PushNotifier::NotifyError(const nsACString& aScope, nsIPrincipal* aPrincipal,
86 const nsAString& aMessage, uint32_t aFlags) {
87 NS_ENSURE_ARG(aPrincipal);
88 PushErrorDispatcher dispatcher(aScope, aPrincipal, aMessage, aFlags);
89 return Dispatch(dispatcher);
92 nsresult PushNotifier::Dispatch(PushDispatcher& aDispatcher) {
93 if (XRE_IsParentProcess()) {
94 // Always notify XPCOM observers in the parent process.
95 Unused << NS_WARN_IF(NS_FAILED(aDispatcher.NotifyObservers()));
97 nsTArray<ContentParent*> contentActors;
98 ContentParent::GetAll(contentActors);
99 if (!contentActors.IsEmpty() && !ServiceWorkerParentInterceptEnabled()) {
100 // At least one content process is active, so e10s must be enabled.
101 // Broadcast a message to notify observers and service workers.
102 for (uint32_t i = 0; i < contentActors.Length(); ++i) {
103 // We need to filter based on process type, only "web" AKA the default
104 // remote type is acceptable. This should not run when Fission is
105 // enabled, and we specifically don't want this for
106 // LARGE_ALLOCATION_REMOTE_TYPE, so don't use IsWebRemoteType().
107 if (contentActors[i]->GetRemoteType() != DEFAULT_REMOTE_TYPE) {
108 continue;
111 // Ensure that the content actor has the permissions avaliable for the
112 // principal the push is being sent for before sending the push message
113 // down.
114 Unused << contentActors[i]->TransmitPermissionsForPrincipal(
115 aDispatcher.GetPrincipal());
116 if (aDispatcher.SendToChild(contentActors[i])) {
117 // Only send the push message to the first content process to avoid
118 // multiple SWs showing the same notification. See bug 1300112.
119 break;
122 return NS_OK;
125 if (BrowserTabsRemoteAutostart() &&
126 !ServiceWorkerParentInterceptEnabled()) {
127 // e10s is enabled, but no content processes are active.
128 return aDispatcher.HandleNoChildProcesses();
131 // e10s is disabled; notify workers in the parent.
132 return aDispatcher.NotifyWorkers();
135 // Otherwise, we're in the content process, so e10s must be enabled. Notify
136 // observers and workers, then send a message to notify observers in the
137 // parent.
138 MOZ_ASSERT(XRE_IsContentProcess());
140 nsresult rv = aDispatcher.NotifyObserversAndWorkers();
142 ContentChild* parentActor = ContentChild::GetSingleton();
143 if (!NS_WARN_IF(!parentActor)) {
144 Unused << NS_WARN_IF(!aDispatcher.SendToParent(parentActor));
147 return rv;
150 PushData::PushData(const nsTArray<uint8_t>& aData) : mData(aData.Clone()) {}
152 PushData::~PushData() = default;
154 NS_IMPL_CYCLE_COLLECTION_0(PushData)
156 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushData)
157 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPushData)
158 NS_INTERFACE_MAP_ENTRY(nsIPushData)
159 NS_INTERFACE_MAP_END
161 NS_IMPL_CYCLE_COLLECTING_ADDREF(PushData)
162 NS_IMPL_CYCLE_COLLECTING_RELEASE(PushData)
164 nsresult PushData::EnsureDecodedText() {
165 if (mData.IsEmpty() || !mDecodedText.IsEmpty()) {
166 return NS_OK;
168 nsresult rv = BodyUtil::ConsumeText(
169 mData.Length(), reinterpret_cast<uint8_t*>(mData.Elements()),
170 mDecodedText);
171 if (NS_WARN_IF(NS_FAILED(rv))) {
172 mDecodedText.Truncate();
173 return rv;
175 return NS_OK;
178 NS_IMETHODIMP
179 PushData::Text(nsAString& aText) {
180 nsresult rv = EnsureDecodedText();
181 if (NS_WARN_IF(NS_FAILED(rv))) {
182 return rv;
184 aText = mDecodedText;
185 return NS_OK;
188 NS_IMETHODIMP
189 PushData::Json(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) {
190 nsresult rv = EnsureDecodedText();
191 if (NS_WARN_IF(NS_FAILED(rv))) {
192 return rv;
194 ErrorResult error;
195 BodyUtil::ConsumeJson(aCx, aResult, mDecodedText, error);
196 return error.StealNSResult();
199 NS_IMETHODIMP
200 PushData::Binary(nsTArray<uint8_t>& aData) {
201 aData = mData.Clone();
202 return NS_OK;
205 PushMessage::PushMessage(nsIPrincipal* aPrincipal, nsIPushData* aData)
206 : mPrincipal(aPrincipal), mData(aData) {}
208 PushMessage::~PushMessage() = default;
210 NS_IMPL_CYCLE_COLLECTION(PushMessage, mPrincipal, mData)
212 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushMessage)
213 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPushMessage)
214 NS_INTERFACE_MAP_ENTRY(nsIPushMessage)
215 NS_INTERFACE_MAP_END
217 NS_IMPL_CYCLE_COLLECTING_ADDREF(PushMessage)
218 NS_IMPL_CYCLE_COLLECTING_RELEASE(PushMessage)
220 NS_IMETHODIMP
221 PushMessage::GetPrincipal(nsIPrincipal** aPrincipal) {
222 NS_ENSURE_ARG_POINTER(aPrincipal);
224 nsCOMPtr<nsIPrincipal> principal = mPrincipal;
225 principal.forget(aPrincipal);
226 return NS_OK;
229 NS_IMETHODIMP
230 PushMessage::GetData(nsIPushData** aData) {
231 NS_ENSURE_ARG_POINTER(aData);
233 nsCOMPtr<nsIPushData> data = mData;
234 data.forget(aData);
235 return NS_OK;
238 PushDispatcher::PushDispatcher(const nsACString& aScope,
239 nsIPrincipal* aPrincipal)
240 : mScope(aScope), mPrincipal(aPrincipal) {}
242 PushDispatcher::~PushDispatcher() = default;
244 nsresult PushDispatcher::HandleNoChildProcesses() { return NS_OK; }
246 nsresult PushDispatcher::NotifyObserversAndWorkers() {
247 Unused << NS_WARN_IF(NS_FAILED(NotifyObservers()));
248 return NotifyWorkers();
251 bool PushDispatcher::ShouldNotifyWorkers() {
252 if (NS_WARN_IF(!mPrincipal)) {
253 return false;
256 // System subscriptions use observer notifications instead of service worker
257 // events. The `testing.notifyWorkers` pref disables worker events for
258 // non-system subscriptions.
259 if (mPrincipal->IsSystemPrincipal() ||
260 !Preferences::GetBool("dom.push.testing.notifyWorkers", true)) {
261 return false;
264 // If e10s is off, no need to worry about processes.
265 if (!BrowserTabsRemoteAutostart()) {
266 return true;
269 // If parent intercept is enabled, then we only want to notify in the parent
270 // process. Otherwise, we only want to notify in the child process.
271 bool isContentProcess = XRE_GetProcessType() == GeckoProcessType_Content;
272 bool parentInterceptEnabled = ServiceWorkerParentInterceptEnabled();
273 if (parentInterceptEnabled) {
274 return !isContentProcess;
277 return isContentProcess;
280 nsresult PushDispatcher::DoNotifyObservers(nsISupports* aSubject,
281 const char* aTopic,
282 const nsACString& aScope) {
283 nsCOMPtr<nsIObserverService> obsService =
284 mozilla::services::GetObserverService();
285 if (!obsService) {
286 return NS_ERROR_FAILURE;
288 // If there's a service for this push category, make sure it is alive.
289 nsCOMPtr<nsICategoryManager> catMan =
290 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
291 if (catMan) {
292 nsCString contractId;
293 nsresult rv = catMan->GetCategoryEntry("push", mScope, contractId);
294 if (NS_SUCCEEDED(rv)) {
295 // Ensure the service is created - we don't need to do anything with
296 // it though - we assume the service constructor attaches a listener.
297 nsCOMPtr<nsISupports> service = do_GetService(contractId.get());
300 return obsService->NotifyObservers(aSubject, aTopic,
301 NS_ConvertUTF8toUTF16(mScope).get());
304 PushMessageDispatcher::PushMessageDispatcher(
305 const nsACString& aScope, nsIPrincipal* aPrincipal,
306 const nsAString& aMessageId, const Maybe<nsTArray<uint8_t>>& aData)
307 : PushDispatcher(aScope, aPrincipal),
308 mMessageId(aMessageId),
309 mData(aData ? Some(aData->Clone()) : Nothing()) {}
311 PushMessageDispatcher::~PushMessageDispatcher() = default;
313 nsresult PushMessageDispatcher::NotifyObservers() {
314 nsCOMPtr<nsIPushData> data;
315 if (mData) {
316 data = new PushData(mData.ref());
318 nsCOMPtr<nsIPushMessage> message = new PushMessage(mPrincipal, data);
319 return DoNotifyObservers(message, OBSERVER_TOPIC_PUSH, mScope);
322 nsresult PushMessageDispatcher::NotifyWorkers() {
323 if (!ShouldNotifyWorkers()) {
324 return NS_OK;
326 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
327 if (!swm) {
328 return NS_ERROR_FAILURE;
330 nsAutoCString originSuffix;
331 nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
332 if (NS_WARN_IF(NS_FAILED(rv))) {
333 return rv;
335 return swm->SendPushEvent(originSuffix, mScope, mMessageId, mData);
338 bool PushMessageDispatcher::SendToParent(ContentChild* aParentActor) {
339 if (mData) {
340 return aParentActor->SendNotifyPushObserversWithData(
341 mScope, IPC::Principal(mPrincipal), mMessageId, mData.ref());
343 return aParentActor->SendNotifyPushObservers(
344 mScope, IPC::Principal(mPrincipal), mMessageId);
347 bool PushMessageDispatcher::SendToChild(ContentParent* aContentActor) {
348 if (mData) {
349 return aContentActor->SendPushWithData(mScope, IPC::Principal(mPrincipal),
350 mMessageId, mData.ref());
352 return aContentActor->SendPush(mScope, IPC::Principal(mPrincipal),
353 mMessageId);
356 PushSubscriptionChangeDispatcher::PushSubscriptionChangeDispatcher(
357 const nsACString& aScope, nsIPrincipal* aPrincipal)
358 : PushDispatcher(aScope, aPrincipal) {}
360 PushSubscriptionChangeDispatcher::~PushSubscriptionChangeDispatcher() = default;
362 nsresult PushSubscriptionChangeDispatcher::NotifyObservers() {
363 return DoNotifyObservers(mPrincipal, OBSERVER_TOPIC_SUBSCRIPTION_CHANGE,
364 mScope);
367 nsresult PushSubscriptionChangeDispatcher::NotifyWorkers() {
368 if (!ShouldNotifyWorkers()) {
369 return NS_OK;
371 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
372 if (!swm) {
373 return NS_ERROR_FAILURE;
375 nsAutoCString originSuffix;
376 nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
377 if (NS_WARN_IF(NS_FAILED(rv))) {
378 return rv;
380 return swm->SendPushSubscriptionChangeEvent(originSuffix, mScope);
383 bool PushSubscriptionChangeDispatcher::SendToParent(
384 ContentChild* aParentActor) {
385 return aParentActor->SendNotifyPushSubscriptionChangeObservers(
386 mScope, IPC::Principal(mPrincipal));
389 bool PushSubscriptionChangeDispatcher::SendToChild(
390 ContentParent* aContentActor) {
391 return aContentActor->SendPushSubscriptionChange(mScope,
392 IPC::Principal(mPrincipal));
395 PushSubscriptionModifiedDispatcher::PushSubscriptionModifiedDispatcher(
396 const nsACString& aScope, nsIPrincipal* aPrincipal)
397 : PushDispatcher(aScope, aPrincipal) {}
399 PushSubscriptionModifiedDispatcher::~PushSubscriptionModifiedDispatcher() =
400 default;
402 nsresult PushSubscriptionModifiedDispatcher::NotifyObservers() {
403 return DoNotifyObservers(mPrincipal, OBSERVER_TOPIC_SUBSCRIPTION_MODIFIED,
404 mScope);
407 nsresult PushSubscriptionModifiedDispatcher::NotifyWorkers() { return NS_OK; }
409 bool PushSubscriptionModifiedDispatcher::SendToParent(
410 ContentChild* aParentActor) {
411 return aParentActor->SendNotifyPushSubscriptionModifiedObservers(
412 mScope, IPC::Principal(mPrincipal));
415 bool PushSubscriptionModifiedDispatcher::SendToChild(
416 ContentParent* aContentActor) {
417 return aContentActor->SendNotifyPushSubscriptionModifiedObservers(
418 mScope, IPC::Principal(mPrincipal));
421 PushErrorDispatcher::PushErrorDispatcher(const nsACString& aScope,
422 nsIPrincipal* aPrincipal,
423 const nsAString& aMessage,
424 uint32_t aFlags)
425 : PushDispatcher(aScope, aPrincipal), mMessage(aMessage), mFlags(aFlags) {}
427 PushErrorDispatcher::~PushErrorDispatcher() = default;
429 nsresult PushErrorDispatcher::NotifyObservers() { return NS_OK; }
431 nsresult PushErrorDispatcher::NotifyWorkers() {
432 if (!ShouldNotifyWorkers() &&
433 (!mPrincipal || mPrincipal->IsSystemPrincipal())) {
434 // For system subscriptions, log the error directly to the browser console.
435 return nsContentUtils::ReportToConsoleNonLocalized(
436 mMessage, mFlags, "Push"_ns, nullptr, /* aDocument */
437 nullptr, /* aURI */
438 u""_ns, /* aLine */
439 0, /* aLineNumber */
440 0, /* aColumnNumber */
441 nsContentUtils::eOMIT_LOCATION);
444 // For service worker subscriptions, report the error to all clients.
445 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
446 if (swm) {
447 swm->ReportToAllClients(mScope, mMessage,
448 NS_ConvertUTF8toUTF16(mScope), /* aFilename */
449 u""_ns, /* aLine */
450 0, /* aLineNumber */
451 0, /* aColumnNumber */
452 mFlags);
454 return NS_OK;
457 bool PushErrorDispatcher::SendToParent(ContentChild* aContentActor) {
458 return aContentActor->SendPushError(mScope, IPC::Principal(mPrincipal),
459 mMessage, mFlags);
462 bool PushErrorDispatcher::SendToChild(ContentParent* aContentActor) {
463 return aContentActor->SendPushError(mScope, IPC::Principal(mPrincipal),
464 mMessage, mFlags);
467 nsresult PushErrorDispatcher::HandleNoChildProcesses() {
468 // Report to the console if no content processes are active.
469 nsCOMPtr<nsIURI> scopeURI;
470 nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), mScope);
471 if (NS_WARN_IF(NS_FAILED(rv))) {
472 return rv;
474 return nsContentUtils::ReportToConsoleNonLocalized(
475 mMessage, mFlags, "Push"_ns, nullptr, /* aDocument */
476 scopeURI, /* aURI */
477 u""_ns, /* aLine */
478 0, /* aLineNumber */
479 0, /* aColumnNumber */
480 nsContentUtils::eOMIT_LOCATION);
483 } // namespace dom
484 } // namespace mozilla