1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
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/. */
6 #include "nsXULAlerts.h"
9 #include "nsComponentManagerUtils.h"
11 #include "mozilla/ClearOnShutdown.h"
12 #include "mozilla/EventForwards.h"
13 #include "mozilla/LookAndFeel.h"
14 #include "mozilla/dom/Notification.h"
15 #include "nsISupportsPrimitives.h"
16 #include "nsPIDOMWindow.h"
17 #include "nsIWindowWatcher.h"
19 using namespace mozilla
;
22 StaticRefPtr
<nsXULAlerts
> gXULAlerts
;
23 } // anonymous namespace
25 NS_IMPL_CYCLE_COLLECTION(nsXULAlertObserver
, mAlertWindow
)
27 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULAlertObserver
)
28 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
29 NS_INTERFACE_MAP_ENTRY(nsISupports
)
32 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULAlertObserver
)
33 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULAlertObserver
)
36 nsXULAlertObserver::Observe(nsISupports
* aSubject
, const char* aTopic
,
37 const char16_t
* aData
) {
38 if (!strcmp("alertfinished", aTopic
)) {
39 mozIDOMWindowProxy
* currentAlert
=
40 mXULAlerts
->mNamedWindows
.GetWeak(mAlertName
);
41 // The window in mNamedWindows might be a replacement, thus it should only
42 // be removed if it is the same window that is associated with this
44 if (currentAlert
== mAlertWindow
) {
45 mXULAlerts
->mNamedWindows
.Remove(mAlertName
);
48 mXULAlerts
->PersistentAlertFinished();
55 rv
= mObserver
->Observe(aSubject
, aTopic
, aData
);
60 // We don't cycle collect nsXULAlerts since gXULAlerts will keep the instance
61 // alive till shutdown anyway.
62 NS_IMPL_ISUPPORTS(nsXULAlerts
, nsIAlertsService
, nsIAlertsDoNotDisturb
,
66 already_AddRefed
<nsXULAlerts
> nsXULAlerts::GetInstance() {
67 // Gecko on Android does not fully support XUL windows.
68 #ifndef MOZ_WIDGET_ANDROID
70 gXULAlerts
= new nsXULAlerts();
71 ClearOnShutdown(&gXULAlerts
);
73 #endif // MOZ_WIDGET_ANDROID
74 RefPtr
<nsXULAlerts
> instance
= gXULAlerts
.get();
75 return instance
.forget();
78 void nsXULAlerts::PersistentAlertFinished() {
79 MOZ_ASSERT(mPersistentAlertCount
);
80 mPersistentAlertCount
--;
82 // Show next pending persistent alert if any.
83 if (!mPendingPersistentAlerts
.IsEmpty()) {
84 ShowAlertWithIconURI(mPendingPersistentAlerts
[0].mAlert
,
85 mPendingPersistentAlerts
[0].mListener
, nullptr);
86 mPendingPersistentAlerts
.RemoveElementAt(0);
91 nsXULAlerts::ShowAlertNotification(
92 const nsAString
& aImageUrl
, const nsAString
& aAlertTitle
,
93 const nsAString
& aAlertText
, bool aAlertTextClickable
,
94 const nsAString
& aAlertCookie
, nsIObserver
* aAlertListener
,
95 const nsAString
& aAlertName
, const nsAString
& aBidi
, const nsAString
& aLang
,
96 const nsAString
& aData
, nsIPrincipal
* aPrincipal
, bool aInPrivateBrowsing
,
97 bool aRequireInteraction
) {
98 nsCOMPtr
<nsIAlertNotification
> alert
=
99 do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID
);
100 NS_ENSURE_TRUE(alert
, NS_ERROR_FAILURE
);
101 // vibrate is unused for now
102 nsTArray
<uint32_t> vibrate
;
103 nsresult rv
= alert
->Init(aAlertName
, aImageUrl
, aAlertTitle
, aAlertText
,
104 aAlertTextClickable
, aAlertCookie
, aBidi
, aLang
,
105 aData
, aPrincipal
, aInPrivateBrowsing
,
106 aRequireInteraction
, false, vibrate
);
107 NS_ENSURE_SUCCESS(rv
, rv
);
108 return ShowAlert(alert
, aAlertListener
);
112 nsXULAlerts::ShowPersistentNotification(const nsAString
& aPersistentData
,
113 nsIAlertNotification
* aAlert
,
114 nsIObserver
* aAlertListener
) {
115 return ShowAlert(aAlert
, aAlertListener
);
119 nsXULAlerts::ShowAlert(nsIAlertNotification
* aAlert
,
120 nsIObserver
* aAlertListener
) {
122 nsresult rv
= aAlert
->GetName(name
);
123 NS_ENSURE_SUCCESS(rv
, rv
);
125 // If there is a pending alert with the same name in the list of
126 // pending alerts, replace it.
127 if (!mPendingPersistentAlerts
.IsEmpty()) {
128 for (uint32_t i
= 0; i
< mPendingPersistentAlerts
.Length(); i
++) {
129 nsAutoString pendingAlertName
;
130 nsCOMPtr
<nsIAlertNotification
> pendingAlert
=
131 mPendingPersistentAlerts
[i
].mAlert
;
132 rv
= pendingAlert
->GetName(pendingAlertName
);
133 NS_ENSURE_SUCCESS(rv
, rv
);
135 if (pendingAlertName
.Equals(name
)) {
137 rv
= pendingAlert
->GetCookie(cookie
);
138 NS_ENSURE_SUCCESS(rv
, rv
);
140 if (mPendingPersistentAlerts
[i
].mListener
) {
141 rv
= mPendingPersistentAlerts
[i
].mListener
->Observe(
142 nullptr, "alertfinished", cookie
.get());
143 NS_ENSURE_SUCCESS(rv
, rv
);
146 mPendingPersistentAlerts
[i
].Init(aAlert
, aAlertListener
);
152 bool requireInteraction
;
153 rv
= aAlert
->GetRequireInteraction(&requireInteraction
);
154 NS_ENSURE_SUCCESS(rv
, rv
);
156 if (requireInteraction
&& !mNamedWindows
.Contains(name
) &&
157 static_cast<int32_t>(mPersistentAlertCount
) >=
158 Preferences::GetInt("dom.webnotifications.requireinteraction.count",
160 PendingAlert
* pa
= mPendingPersistentAlerts
.AppendElement();
161 pa
->Init(aAlert
, aAlertListener
);
164 return ShowAlertWithIconURI(aAlert
, aAlertListener
, nullptr);
168 nsXULAlerts::ShowAlertWithIconURI(nsIAlertNotification
* aAlert
,
169 nsIObserver
* aAlertListener
,
171 bool inPrivateBrowsing
;
172 nsresult rv
= aAlert
->GetInPrivateBrowsing(&inPrivateBrowsing
);
173 NS_ENSURE_SUCCESS(rv
, rv
);
176 rv
= aAlert
->GetCookie(cookie
);
177 NS_ENSURE_SUCCESS(rv
, rv
);
180 if (aAlertListener
) {
181 aAlertListener
->Observe(nullptr, "alertfinished", cookie
.get());
187 rv
= aAlert
->GetName(name
);
188 NS_ENSURE_SUCCESS(rv
, rv
);
190 nsAutoString imageUrl
;
191 rv
= aAlert
->GetImageURL(imageUrl
);
192 NS_ENSURE_SUCCESS(rv
, rv
);
195 rv
= aAlert
->GetTitle(title
);
196 NS_ENSURE_SUCCESS(rv
, rv
);
199 rv
= aAlert
->GetText(text
);
200 NS_ENSURE_SUCCESS(rv
, rv
);
203 rv
= aAlert
->GetTextClickable(&textClickable
);
204 NS_ENSURE_SUCCESS(rv
, rv
);
207 rv
= aAlert
->GetDir(bidi
);
208 NS_ENSURE_SUCCESS(rv
, rv
);
211 rv
= aAlert
->GetLang(lang
);
212 NS_ENSURE_SUCCESS(rv
, rv
);
215 rv
= aAlert
->GetSource(source
);
216 NS_ENSURE_SUCCESS(rv
, rv
);
218 bool requireInteraction
;
219 rv
= aAlert
->GetRequireInteraction(&requireInteraction
);
220 NS_ENSURE_SUCCESS(rv
, rv
);
222 nsCOMPtr
<nsIWindowWatcher
> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID
));
224 nsCOMPtr
<nsIMutableArray
> argsArray
= nsArray::Create();
226 // create scriptable versions of our strings that we can store in our
227 // nsIMutableArray....
228 nsCOMPtr
<nsISupportsString
> scriptableImageUrl(
229 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID
));
230 NS_ENSURE_TRUE(scriptableImageUrl
, NS_ERROR_FAILURE
);
232 scriptableImageUrl
->SetData(imageUrl
);
233 rv
= argsArray
->AppendElement(scriptableImageUrl
);
234 NS_ENSURE_SUCCESS(rv
, rv
);
236 nsCOMPtr
<nsISupportsString
> scriptableAlertTitle(
237 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID
));
238 NS_ENSURE_TRUE(scriptableAlertTitle
, NS_ERROR_FAILURE
);
240 scriptableAlertTitle
->SetData(title
);
241 rv
= argsArray
->AppendElement(scriptableAlertTitle
);
242 NS_ENSURE_SUCCESS(rv
, rv
);
244 nsCOMPtr
<nsISupportsString
> scriptableAlertText(
245 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID
));
246 NS_ENSURE_TRUE(scriptableAlertText
, NS_ERROR_FAILURE
);
248 scriptableAlertText
->SetData(text
);
249 rv
= argsArray
->AppendElement(scriptableAlertText
);
250 NS_ENSURE_SUCCESS(rv
, rv
);
252 nsCOMPtr
<nsISupportsPRBool
> scriptableIsClickable(
253 do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID
));
254 NS_ENSURE_TRUE(scriptableIsClickable
, NS_ERROR_FAILURE
);
256 scriptableIsClickable
->SetData(textClickable
);
257 rv
= argsArray
->AppendElement(scriptableIsClickable
);
258 NS_ENSURE_SUCCESS(rv
, rv
);
260 nsCOMPtr
<nsISupportsString
> scriptableAlertCookie(
261 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID
));
262 NS_ENSURE_TRUE(scriptableAlertCookie
, NS_ERROR_FAILURE
);
264 scriptableAlertCookie
->SetData(cookie
);
265 rv
= argsArray
->AppendElement(scriptableAlertCookie
);
266 NS_ENSURE_SUCCESS(rv
, rv
);
268 nsCOMPtr
<nsISupportsPRInt32
> scriptableOrigin(
269 do_CreateInstance(NS_SUPPORTS_PRINT32_CONTRACTID
));
270 NS_ENSURE_TRUE(scriptableOrigin
, NS_ERROR_FAILURE
);
273 LookAndFeel::GetInt(LookAndFeel::IntID::AlertNotificationOrigin
);
274 scriptableOrigin
->SetData(origin
);
276 rv
= argsArray
->AppendElement(scriptableOrigin
);
277 NS_ENSURE_SUCCESS(rv
, rv
);
279 nsCOMPtr
<nsISupportsString
> scriptableBidi(
280 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID
));
281 NS_ENSURE_TRUE(scriptableBidi
, NS_ERROR_FAILURE
);
283 scriptableBidi
->SetData(bidi
);
284 rv
= argsArray
->AppendElement(scriptableBidi
);
285 NS_ENSURE_SUCCESS(rv
, rv
);
287 nsCOMPtr
<nsISupportsString
> scriptableLang(
288 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID
));
289 NS_ENSURE_TRUE(scriptableLang
, NS_ERROR_FAILURE
);
291 scriptableLang
->SetData(lang
);
292 rv
= argsArray
->AppendElement(scriptableLang
);
293 NS_ENSURE_SUCCESS(rv
, rv
);
295 nsCOMPtr
<nsISupportsPRBool
> scriptableRequireInteraction(
296 do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID
));
297 NS_ENSURE_TRUE(scriptableRequireInteraction
, NS_ERROR_FAILURE
);
299 scriptableRequireInteraction
->SetData(requireInteraction
);
300 rv
= argsArray
->AppendElement(scriptableRequireInteraction
);
301 NS_ENSURE_SUCCESS(rv
, rv
);
303 // Alerts with the same name should replace the old alert in the same
304 // position. Provide the new alert window with a pointer to the replaced
305 // window so that it may take the same position.
306 nsCOMPtr
<nsISupportsInterfacePointer
> replacedWindow
=
307 do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID
, &rv
);
308 NS_ENSURE_TRUE(replacedWindow
, NS_ERROR_FAILURE
);
309 mozIDOMWindowProxy
* previousAlert
= mNamedWindows
.GetWeak(name
);
310 replacedWindow
->SetData(previousAlert
);
311 replacedWindow
->SetDataIID(&NS_GET_IID(mozIDOMWindowProxy
));
312 rv
= argsArray
->AppendElement(replacedWindow
);
313 NS_ENSURE_SUCCESS(rv
, rv
);
315 if (requireInteraction
) {
316 mPersistentAlertCount
++;
319 // Add an observer (that wraps aAlertListener) to remove the window from
320 // mNamedWindows when it is closed.
321 nsCOMPtr
<nsISupportsInterfacePointer
> ifptr
=
322 do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID
, &rv
);
323 NS_ENSURE_SUCCESS(rv
, rv
);
324 RefPtr
<nsXULAlertObserver
> alertObserver
=
325 new nsXULAlertObserver(this, name
, aAlertListener
, requireInteraction
);
326 nsCOMPtr
<nsISupports
> iSupports(do_QueryInterface(alertObserver
));
327 ifptr
->SetData(iSupports
);
328 ifptr
->SetDataIID(&NS_GET_IID(nsIObserver
));
329 rv
= argsArray
->AppendElement(ifptr
);
330 NS_ENSURE_SUCCESS(rv
, rv
);
332 // The source contains the host and port of the site that sent the
333 // notification. It is empty for system alerts.
334 nsCOMPtr
<nsISupportsString
> scriptableAlertSource(
335 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID
));
336 NS_ENSURE_TRUE(scriptableAlertSource
, NS_ERROR_FAILURE
);
337 scriptableAlertSource
->SetData(source
);
338 rv
= argsArray
->AppendElement(scriptableAlertSource
);
339 NS_ENSURE_SUCCESS(rv
, rv
);
341 nsCOMPtr
<nsISupportsCString
> scriptableIconURL(
342 do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID
));
343 NS_ENSURE_TRUE(scriptableIconURL
, NS_ERROR_FAILURE
);
345 nsAutoCString iconURL
;
346 rv
= aIconURI
->GetSpec(iconURL
);
347 NS_ENSURE_SUCCESS(rv
, rv
);
348 scriptableIconURL
->SetData(iconURL
);
350 rv
= argsArray
->AppendElement(scriptableIconURL
);
351 NS_ENSURE_SUCCESS(rv
, rv
);
353 nsCOMPtr
<mozIDOMWindowProxy
> newWindow
;
354 nsAutoCString
features("chrome,dialog=yes,alert=yes,titlebar=no");
355 if (inPrivateBrowsing
) {
356 features
.AppendLiteral(",private");
358 rv
= wwatch
->OpenWindow(
359 nullptr, "chrome://global/content/alerts/alert.xhtml"_ns
, "_blank"_ns
,
360 features
, argsArray
, getter_AddRefs(newWindow
));
361 NS_ENSURE_SUCCESS(rv
, rv
);
363 mNamedWindows
.InsertOrUpdate(name
, newWindow
);
364 alertObserver
->SetAlertWindow(newWindow
);
370 nsXULAlerts::SetManualDoNotDisturb(bool aDoNotDisturb
) {
371 mDoNotDisturb
= aDoNotDisturb
;
376 nsXULAlerts::GetManualDoNotDisturb(bool* aRetVal
) {
377 *aRetVal
= mDoNotDisturb
;
382 nsXULAlerts::GetSuppressForScreenSharing(bool* aRetVal
) {
383 NS_ENSURE_ARG(aRetVal
);
384 *aRetVal
= mSuppressForScreenSharing
;
389 nsXULAlerts::SetSuppressForScreenSharing(bool aSuppress
) {
390 mSuppressForScreenSharing
= aSuppress
;
395 nsXULAlerts::CloseAlert(const nsAString
& aAlertName
, bool aContextClosed
) {
396 mozIDOMWindowProxy
* alert
= mNamedWindows
.GetWeak(aAlertName
);
397 if (nsCOMPtr
<nsPIDOMWindowOuter
> domWindow
=
398 nsPIDOMWindowOuter::From(alert
)) {
399 domWindow
->DispatchCustomEvent(u
"XULAlertClose"_ns
,
400 ChromeOnlyDispatch::eYes
);