Bug 1880550 - Propagate explicit heights to scrolled table cells as min-heights....
[gecko.git] / toolkit / components / alerts / nsXULAlerts.cpp
blob0a8c9ea752f6922bbb08dd916e5bbc099c250747
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"
8 #include "nsArray.h"
9 #include "nsComponentManagerUtils.h"
10 #include "nsCOMPtr.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;
21 namespace {
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)
30 NS_INTERFACE_MAP_END
32 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULAlertObserver)
33 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULAlertObserver)
35 NS_IMETHODIMP
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
43 // listener.
44 if (currentAlert == mAlertWindow) {
45 mXULAlerts->mNamedWindows.Remove(mAlertName);
47 if (mIsPersistent) {
48 mXULAlerts->PersistentAlertFinished();
53 nsresult rv = NS_OK;
54 if (mObserver) {
55 rv = mObserver->Observe(aSubject, aTopic, aData);
57 return rv;
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,
63 nsIAlertsIconURI)
65 /* static */
66 already_AddRefed<nsXULAlerts> nsXULAlerts::GetInstance() {
67 // Gecko on Android does not fully support XUL windows.
68 #ifndef MOZ_WIDGET_ANDROID
69 if (!gXULAlerts) {
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);
90 NS_IMETHODIMP
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);
111 NS_IMETHODIMP
112 nsXULAlerts::ShowPersistentNotification(const nsAString& aPersistentData,
113 nsIAlertNotification* aAlert,
114 nsIObserver* aAlertListener) {
115 return ShowAlert(aAlert, aAlertListener);
118 NS_IMETHODIMP
119 nsXULAlerts::ShowAlert(nsIAlertNotification* aAlert,
120 nsIObserver* aAlertListener) {
121 nsAutoString name;
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)) {
136 nsAutoString cookie;
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);
147 return NS_OK;
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",
159 0)) {
160 PendingAlert* pa = mPendingPersistentAlerts.AppendElement();
161 pa->Init(aAlert, aAlertListener);
162 return NS_OK;
164 return ShowAlertWithIconURI(aAlert, aAlertListener, nullptr);
167 NS_IMETHODIMP
168 nsXULAlerts::ShowAlertWithIconURI(nsIAlertNotification* aAlert,
169 nsIObserver* aAlertListener,
170 nsIURI* aIconURI) {
171 bool inPrivateBrowsing;
172 nsresult rv = aAlert->GetInPrivateBrowsing(&inPrivateBrowsing);
173 NS_ENSURE_SUCCESS(rv, rv);
175 nsAutoString cookie;
176 rv = aAlert->GetCookie(cookie);
177 NS_ENSURE_SUCCESS(rv, rv);
179 if (mDoNotDisturb) {
180 if (aAlertListener) {
181 aAlertListener->Observe(nullptr, "alertfinished", cookie.get());
183 return NS_OK;
186 nsAutoString name;
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);
194 nsAutoString title;
195 rv = aAlert->GetTitle(title);
196 NS_ENSURE_SUCCESS(rv, rv);
198 nsAutoString text;
199 rv = aAlert->GetText(text);
200 NS_ENSURE_SUCCESS(rv, rv);
202 bool textClickable;
203 rv = aAlert->GetTextClickable(&textClickable);
204 NS_ENSURE_SUCCESS(rv, rv);
206 nsAutoString bidi;
207 rv = aAlert->GetDir(bidi);
208 NS_ENSURE_SUCCESS(rv, rv);
210 nsAutoString lang;
211 rv = aAlert->GetLang(lang);
212 NS_ENSURE_SUCCESS(rv, rv);
214 nsAutoString source;
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);
272 int32_t origin =
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);
344 if (aIconURI) {
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);
366 return NS_OK;
369 NS_IMETHODIMP
370 nsXULAlerts::SetManualDoNotDisturb(bool aDoNotDisturb) {
371 mDoNotDisturb = aDoNotDisturb;
372 return NS_OK;
375 NS_IMETHODIMP
376 nsXULAlerts::GetManualDoNotDisturb(bool* aRetVal) {
377 *aRetVal = mDoNotDisturb;
378 return NS_OK;
381 NS_IMETHODIMP
382 nsXULAlerts::GetSuppressForScreenSharing(bool* aRetVal) {
383 NS_ENSURE_ARG(aRetVal);
384 *aRetVal = mSuppressForScreenSharing;
385 return NS_OK;
388 NS_IMETHODIMP
389 nsXULAlerts::SetSuppressForScreenSharing(bool aSuppress) {
390 mSuppressForScreenSharing = aSuppress;
391 return NS_OK;
394 NS_IMETHODIMP
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);
402 return NS_OK;