Line data Source code
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/. */
6 : #include "mozilla/dom/DesktopNotification.h"
7 : #include "mozilla/dom/DesktopNotificationBinding.h"
8 : #include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
9 : #include "mozilla/dom/ToJSValue.h"
10 : #include "nsComponentManagerUtils.h"
11 : #include "nsContentPermissionHelper.h"
12 : #include "nsXULAppAPI.h"
13 : #include "mozilla/dom/PBrowserChild.h"
14 : #include "mozilla/Preferences.h"
15 : #include "nsGlobalWindow.h"
16 : #include "nsIScriptSecurityManager.h"
17 : #include "nsServiceManagerUtils.h"
18 : #include "PermissionMessageUtils.h"
19 : #include "nsILoadContext.h"
20 :
21 : namespace mozilla {
22 : namespace dom {
23 :
24 : /*
25 : * Simple Request
26 : */
27 : class DesktopNotificationRequest : public nsIContentPermissionRequest
28 : , public Runnable
29 : {
30 0 : virtual ~DesktopNotificationRequest()
31 0 : {
32 0 : }
33 :
34 : nsCOMPtr<nsIContentPermissionRequester> mRequester;
35 : public:
36 : NS_DECL_ISUPPORTS_INHERITED
37 : NS_DECL_NSICONTENTPERMISSIONREQUEST
38 :
39 0 : explicit DesktopNotificationRequest(DesktopNotification* aNotification)
40 0 : : Runnable("dom::DesktopNotificationRequest")
41 0 : , mDesktopNotification(aNotification)
42 : {
43 0 : mRequester = new nsContentPermissionRequester(mDesktopNotification->GetOwner());
44 0 : }
45 :
46 0 : NS_IMETHOD Run() override
47 : {
48 0 : nsCOMPtr<nsPIDOMWindowInner> window = mDesktopNotification->GetOwner();
49 0 : nsContentPermissionUtils::AskPermission(this, window);
50 0 : return NS_OK;
51 : }
52 :
53 : RefPtr<DesktopNotification> mDesktopNotification;
54 : };
55 :
56 : /* ------------------------------------------------------------------------ */
57 : /* AlertServiceObserver */
58 : /* ------------------------------------------------------------------------ */
59 :
60 0 : NS_IMPL_ISUPPORTS(AlertServiceObserver, nsIObserver)
61 :
62 : /* ------------------------------------------------------------------------ */
63 : /* DesktopNotification */
64 : /* ------------------------------------------------------------------------ */
65 :
66 : uint32_t DesktopNotification::sCount = 0;
67 :
68 : nsresult
69 0 : DesktopNotification::PostDesktopNotification()
70 : {
71 0 : if (!mObserver) {
72 0 : mObserver = new AlertServiceObserver(this);
73 : }
74 :
75 0 : nsCOMPtr<nsIAlertsService> alerts = do_GetService("@mozilla.org/alerts-service;1");
76 0 : if (!alerts) {
77 0 : return NS_ERROR_NOT_IMPLEMENTED;
78 : }
79 :
80 : // Generate a unique name (which will also be used as a cookie) because
81 : // the nsIAlertsService will coalesce notifications with the same name.
82 : // In the case of IPC, the parent process will use the cookie to map
83 : // to nsIObservers, thus cookies must be unique to differentiate observers.
84 0 : nsString uniqueName = NS_LITERAL_STRING("desktop-notification:");
85 0 : uniqueName.AppendInt(sCount++);
86 0 : nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
87 0 : if (!owner) {
88 0 : return NS_ERROR_FAILURE;
89 : }
90 0 : nsCOMPtr<nsIDocument> doc = owner->GetDoc();
91 0 : nsIPrincipal* principal = doc->NodePrincipal();
92 0 : nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
93 0 : bool inPrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
94 : nsCOMPtr<nsIAlertNotification> alert =
95 0 : do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
96 0 : NS_ENSURE_TRUE(alert, NS_ERROR_FAILURE);
97 0 : nsresult rv = alert->Init(uniqueName, mIconURL, mTitle,
98 : mDescription,
99 : true,
100 : uniqueName,
101 0 : NS_LITERAL_STRING("auto"),
102 0 : EmptyString(),
103 0 : EmptyString(),
104 : principal,
105 : inPrivateBrowsing,
106 0 : false /* requireInteraction */);
107 0 : NS_ENSURE_SUCCESS(rv, rv);
108 0 : return alerts->ShowAlert(alert, mObserver);
109 : }
110 :
111 0 : DesktopNotification::DesktopNotification(const nsAString & title,
112 : const nsAString & description,
113 : const nsAString & iconURL,
114 : nsPIDOMWindowInner* aWindow,
115 0 : nsIPrincipal* principal)
116 : : DOMEventTargetHelper(aWindow)
117 : , mTitle(title)
118 : , mDescription(description)
119 : , mIconURL(iconURL)
120 : , mPrincipal(principal)
121 : , mAllow(false)
122 0 : , mShowHasBeenCalled(false)
123 : {
124 0 : if (Preferences::GetBool("notification.disabled", false)) {
125 0 : return;
126 : }
127 :
128 : // If we are in testing mode (running mochitests, for example)
129 : // and we are suppose to allow requests, then just post an allow event.
130 0 : if (Preferences::GetBool("notification.prompt.testing", false) &&
131 0 : Preferences::GetBool("notification.prompt.testing.allow", true)) {
132 0 : mAllow = true;
133 : }
134 : }
135 :
136 : void
137 0 : DesktopNotification::Init()
138 : {
139 0 : RefPtr<DesktopNotificationRequest> request = new DesktopNotificationRequest(this);
140 :
141 0 : NS_DispatchToMainThread(request);
142 0 : }
143 :
144 0 : DesktopNotification::~DesktopNotification()
145 : {
146 0 : if (mObserver) {
147 0 : mObserver->Disconnect();
148 : }
149 0 : }
150 :
151 : void
152 0 : DesktopNotification::DispatchNotificationEvent(const nsString& aName)
153 : {
154 0 : if (NS_FAILED(CheckInnerWindowCorrectness())) {
155 0 : return;
156 : }
157 :
158 0 : RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
159 : // it doesn't bubble, and it isn't cancelable
160 0 : event->InitEvent(aName, false, false);
161 0 : event->SetTrusted(true);
162 0 : DispatchDOMEvent(nullptr, event, nullptr, nullptr);
163 : }
164 :
165 : nsresult
166 0 : DesktopNotification::SetAllow(bool aAllow)
167 : {
168 0 : mAllow = aAllow;
169 :
170 : // if we have called Show() already, lets go ahead and post a notification
171 0 : if (mShowHasBeenCalled && aAllow) {
172 0 : return PostDesktopNotification();
173 : }
174 :
175 0 : return NS_OK;
176 : }
177 :
178 : void
179 0 : DesktopNotification::HandleAlertServiceNotification(const char *aTopic)
180 : {
181 0 : if (NS_FAILED(CheckInnerWindowCorrectness())) {
182 0 : return;
183 : }
184 :
185 0 : if (!strcmp("alertclickcallback", aTopic)) {
186 0 : DispatchNotificationEvent(NS_LITERAL_STRING("click"));
187 0 : } else if (!strcmp("alertfinished", aTopic)) {
188 0 : DispatchNotificationEvent(NS_LITERAL_STRING("close"));
189 : }
190 : }
191 :
192 : void
193 0 : DesktopNotification::Show(ErrorResult& aRv)
194 : {
195 0 : mShowHasBeenCalled = true;
196 :
197 0 : if (!mAllow) {
198 0 : return;
199 : }
200 :
201 0 : aRv = PostDesktopNotification();
202 : }
203 :
204 : JSObject*
205 0 : DesktopNotification::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
206 : {
207 0 : return DesktopNotificationBinding::Wrap(aCx, this, aGivenProto);
208 : }
209 :
210 : /* ------------------------------------------------------------------------ */
211 : /* DesktopNotificationCenter */
212 : /* ------------------------------------------------------------------------ */
213 :
214 0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(DesktopNotificationCenter)
215 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(DesktopNotificationCenter)
216 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(DesktopNotificationCenter)
217 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DesktopNotificationCenter)
218 0 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
219 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
220 0 : NS_INTERFACE_MAP_END
221 :
222 : already_AddRefed<DesktopNotification>
223 0 : DesktopNotificationCenter::CreateNotification(const nsAString& aTitle,
224 : const nsAString& aDescription,
225 : const nsAString& aIconURL)
226 : {
227 0 : MOZ_ASSERT(mOwner);
228 :
229 : RefPtr<DesktopNotification> notification =
230 : new DesktopNotification(aTitle,
231 : aDescription,
232 : aIconURL,
233 : mOwner,
234 0 : mPrincipal);
235 0 : notification->Init();
236 0 : return notification.forget();
237 : }
238 :
239 : JSObject*
240 0 : DesktopNotificationCenter::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
241 : {
242 0 : return DesktopNotificationCenterBinding::Wrap(aCx, this, aGivenProto);
243 : }
244 :
245 : /* ------------------------------------------------------------------------ */
246 : /* DesktopNotificationRequest */
247 : /* ------------------------------------------------------------------------ */
248 :
249 0 : NS_IMPL_ISUPPORTS_INHERITED(DesktopNotificationRequest, Runnable,
250 : nsIContentPermissionRequest)
251 :
252 : NS_IMETHODIMP
253 0 : DesktopNotificationRequest::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
254 : {
255 0 : if (!mDesktopNotification) {
256 0 : return NS_ERROR_NOT_INITIALIZED;
257 : }
258 :
259 0 : NS_IF_ADDREF(*aRequestingPrincipal = mDesktopNotification->mPrincipal);
260 0 : return NS_OK;
261 : }
262 :
263 : NS_IMETHODIMP
264 0 : DesktopNotificationRequest::GetWindow(mozIDOMWindow** aRequestingWindow)
265 : {
266 0 : if (!mDesktopNotification) {
267 0 : return NS_ERROR_NOT_INITIALIZED;
268 : }
269 :
270 0 : NS_IF_ADDREF(*aRequestingWindow = mDesktopNotification->GetOwner());
271 0 : return NS_OK;
272 : }
273 :
274 : NS_IMETHODIMP
275 0 : DesktopNotificationRequest::GetElement(nsIDOMElement * *aElement)
276 : {
277 0 : NS_ENSURE_ARG_POINTER(aElement);
278 0 : *aElement = nullptr;
279 0 : return NS_OK;
280 : }
281 :
282 : NS_IMETHODIMP
283 0 : DesktopNotificationRequest::Cancel()
284 : {
285 0 : nsresult rv = mDesktopNotification->SetAllow(false);
286 0 : mDesktopNotification = nullptr;
287 0 : return rv;
288 : }
289 :
290 : NS_IMETHODIMP
291 0 : DesktopNotificationRequest::Allow(JS::HandleValue aChoices)
292 : {
293 0 : MOZ_ASSERT(aChoices.isUndefined());
294 0 : nsresult rv = mDesktopNotification->SetAllow(true);
295 0 : mDesktopNotification = nullptr;
296 0 : return rv;
297 : }
298 :
299 : NS_IMETHODIMP
300 0 : DesktopNotificationRequest::GetRequester(nsIContentPermissionRequester** aRequester)
301 : {
302 0 : NS_ENSURE_ARG_POINTER(aRequester);
303 :
304 0 : nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
305 0 : requester.forget(aRequester);
306 0 : return NS_OK;
307 : }
308 :
309 : NS_IMETHODIMP
310 0 : DesktopNotificationRequest::GetTypes(nsIArray** aTypes)
311 : {
312 0 : nsTArray<nsString> emptyOptions;
313 0 : return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
314 0 : NS_LITERAL_CSTRING("unused"),
315 : emptyOptions,
316 0 : aTypes);
317 : }
318 :
319 : } // namespace dom
320 : } // namespace mozilla
|