summaryrefslogtreecommitdiff
path: root/components/alerts/src
diff options
context:
space:
mode:
Diffstat (limited to 'components/alerts/src')
-rw-r--r--components/alerts/src/AlertNotification.cpp361
-rw-r--r--components/alerts/src/AlertNotification.h81
-rw-r--r--components/alerts/src/AlertNotificationIPCSerializer.h122
-rw-r--r--components/alerts/src/nsAlertsService.cpp301
-rw-r--r--components/alerts/src/nsAlertsService.h31
-rw-r--r--components/alerts/src/nsAlertsUtils.cpp43
-rw-r--r--components/alerts/src/nsAlertsUtils.h32
-rw-r--r--components/alerts/src/nsXULAlerts.cpp380
-rw-r--r--components/alerts/src/nsXULAlerts.h84
9 files changed, 1435 insertions, 0 deletions
diff --git a/components/alerts/src/AlertNotification.cpp b/components/alerts/src/AlertNotification.cpp
new file mode 100644
index 000000000..b828f1100
--- /dev/null
+++ b/components/alerts/src/AlertNotification.cpp
@@ -0,0 +1,361 @@
+/* This Source Code Form is subject to the terms of the Mozilla Pub
+ * License, v. 2.0. If a copy of the MPL was not distributed with t
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/AlertNotification.h"
+
+#include "imgIContainer.h"
+#include "imgINotificationObserver.h"
+#include "imgIRequest.h"
+#include "imgLoader.h"
+#include "nsAlertsUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsContentUtils.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(AlertNotification, nsIAlertNotification)
+
+AlertNotification::AlertNotification()
+ : mTextClickable(false)
+ , mInPrivateBrowsing(false)
+{}
+
+AlertNotification::~AlertNotification()
+{}
+
+NS_IMETHODIMP
+AlertNotification::Init(const nsAString& aName, const nsAString& aImageURL,
+ const nsAString& aTitle, const nsAString& aText,
+ bool aTextClickable, const nsAString& aCookie,
+ const nsAString& aDir, const nsAString& aLang,
+ const nsAString& aData, nsIPrincipal* aPrincipal,
+ bool aInPrivateBrowsing, bool aRequireInteraction)
+{
+ mName = aName;
+ mImageURL = aImageURL;
+ mTitle = aTitle;
+ mText = aText;
+ mTextClickable = aTextClickable;
+ mCookie = aCookie;
+ mDir = aDir;
+ mLang = aLang;
+ mData = aData;
+ mPrincipal = aPrincipal;
+ mInPrivateBrowsing = aInPrivateBrowsing;
+ mRequireInteraction = aRequireInteraction;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetName(nsAString& aName)
+{
+ aName = mName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetImageURL(nsAString& aImageURL)
+{
+ aImageURL = mImageURL;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetTitle(nsAString& aTitle)
+{
+ aTitle = mTitle;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetText(nsAString& aText)
+{
+ aText = mText;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetTextClickable(bool* aTextClickable)
+{
+ *aTextClickable = mTextClickable;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetCookie(nsAString& aCookie)
+{
+ aCookie = mCookie;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetDir(nsAString& aDir)
+{
+ aDir = mDir;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetLang(nsAString& aLang)
+{
+ aLang = mLang;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetRequireInteraction(bool* aRequireInteraction)
+{
+ *aRequireInteraction = mRequireInteraction;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetData(nsAString& aData)
+{
+ aData = mData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetPrincipal(nsIPrincipal** aPrincipal)
+{
+ NS_IF_ADDREF(*aPrincipal = mPrincipal);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetURI(nsIURI** aURI)
+{
+ if (!nsAlertsUtils::IsActionablePrincipal(mPrincipal)) {
+ *aURI = nullptr;
+ return NS_OK;
+ }
+ return mPrincipal->GetURI(aURI);
+}
+
+NS_IMETHODIMP
+AlertNotification::GetInPrivateBrowsing(bool* aInPrivateBrowsing)
+{
+ *aInPrivateBrowsing = mInPrivateBrowsing;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetActionable(bool* aActionable)
+{
+ *aActionable = nsAlertsUtils::IsActionablePrincipal(mPrincipal);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::GetSource(nsAString& aSource)
+{
+ nsAlertsUtils::GetSourceHostPort(mPrincipal, aSource);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertNotification::LoadImage(uint32_t aTimeout,
+ nsIAlertNotificationImageListener* aListener,
+ nsISupports* aUserData,
+ nsICancelable** aRequest)
+{
+ NS_ENSURE_ARG(aListener);
+ NS_ENSURE_ARG_POINTER(aRequest);
+ *aRequest = nullptr;
+
+ // Exit early if this alert doesn't have an image.
+ if (mImageURL.IsEmpty()) {
+ return aListener->OnImageMissing(aUserData);
+ }
+ nsCOMPtr<nsIURI> imageURI;
+ NS_NewURI(getter_AddRefs(imageURI), mImageURL);
+ if (!imageURI) {
+ return aListener->OnImageMissing(aUserData);
+ }
+
+ RefPtr<AlertImageRequest> request = new AlertImageRequest(imageURI, mPrincipal,
+ mInPrivateBrowsing,
+ aTimeout, aListener,
+ aUserData);
+ nsresult rv = request->Start();
+ request.forget(aRequest);
+ return rv;
+}
+
+NS_IMPL_CYCLE_COLLECTION(AlertImageRequest, mURI, mPrincipal, mListener,
+ mUserData)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AlertImageRequest)
+ NS_INTERFACE_MAP_ENTRY(imgINotificationObserver)
+ NS_INTERFACE_MAP_ENTRY(nsICancelable)
+ NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, imgINotificationObserver)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(AlertImageRequest)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(AlertImageRequest)
+
+AlertImageRequest::AlertImageRequest(nsIURI* aURI, nsIPrincipal* aPrincipal,
+ bool aInPrivateBrowsing, uint32_t aTimeout,
+ nsIAlertNotificationImageListener* aListener,
+ nsISupports* aUserData)
+ : mURI(aURI)
+ , mPrincipal(aPrincipal)
+ , mInPrivateBrowsing(aInPrivateBrowsing)
+ , mTimeout(aTimeout)
+ , mListener(aListener)
+ , mUserData(aUserData)
+{}
+
+AlertImageRequest::~AlertImageRequest()
+{
+ if (mRequest) {
+ mRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
+ }
+}
+
+NS_IMETHODIMP
+AlertImageRequest::Notify(imgIRequest* aRequest, int32_t aType,
+ const nsIntRect* aData)
+{
+ MOZ_ASSERT(aRequest == mRequest);
+
+ uint32_t imgStatus = imgIRequest::STATUS_ERROR;
+ nsresult rv = aRequest->GetImageStatus(&imgStatus);
+ if (NS_WARN_IF(NS_FAILED(rv)) ||
+ (imgStatus & imgIRequest::STATUS_ERROR)) {
+ return NotifyMissing();
+ }
+
+ // If the image is already decoded, `FRAME_COMPLETE` will fire before
+ // `LOAD_COMPLETE`, so we can notify the listener immediately. Otherwise,
+ // we'll need to request a decode when `LOAD_COMPLETE` fires, and wait
+ // for the first frame.
+
+ if (aType == imgINotificationObserver::LOAD_COMPLETE) {
+ if (!(imgStatus & imgIRequest::STATUS_FRAME_COMPLETE)) {
+ nsCOMPtr<imgIContainer> image;
+ rv = aRequest->GetImage(getter_AddRefs(image));
+ if (NS_WARN_IF(NS_FAILED(rv) || !image)) {
+ return NotifyMissing();
+ }
+
+ // Ask the image to decode at its intrinsic size.
+ int32_t width = 0, height = 0;
+ image->GetWidth(&width);
+ image->GetHeight(&height);
+ image->RequestDecodeForSize(gfx::IntSize(width, height), imgIContainer::FLAG_NONE);
+ }
+ return NS_OK;
+ }
+
+ if (aType == imgINotificationObserver::FRAME_COMPLETE) {
+ return NotifyComplete();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AlertImageRequest::Notify(nsITimer* aTimer)
+{
+ MOZ_ASSERT(aTimer == mTimer);
+ return NotifyMissing();
+}
+
+NS_IMETHODIMP
+AlertImageRequest::Cancel(nsresult aReason)
+{
+ if (mRequest) {
+ mRequest->Cancel(aReason);
+ }
+ // We call `NotifyMissing` here because we won't receive a `LOAD_COMPLETE`
+ // notification if we cancel the request before it loads (bug 1233086,
+ // comment 33). Once that's fixed, `nsIAlertNotification::loadImage` could
+ // return the underlying `imgIRequest` instead of the wrapper.
+ return NotifyMissing();
+}
+
+nsresult
+AlertImageRequest::Start()
+{
+ // Keep the request alive until we notify the image listener.
+ NS_ADDREF_THIS();
+
+ nsresult rv;
+ if (mTimeout > 0) {
+ mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+ if (NS_WARN_IF(!mTimer)) {
+ return NotifyMissing();
+ }
+ rv = mTimer->InitWithCallback(this, mTimeout,
+ nsITimer::TYPE_ONE_SHOT);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NotifyMissing();
+ }
+ }
+
+ // Begin loading the image.
+ imgLoader* il = imgLoader::NormalLoader();
+ if (!il) {
+ return NotifyMissing();
+ }
+
+ // Bug 1237405: `LOAD_ANONYMOUS` disables cookies, but we want to use a
+ // temporary cookie jar instead. We should also use
+ // `imgLoader::PrivateBrowsingLoader()` instead of the normal loader.
+ // Unfortunately, the PB loader checks the load group, and asserts if its
+ // load context's PB flag isn't set. The fix is to pass the load group to
+ // `nsIAlertNotification::loadImage`.
+ int32_t loadFlags = mInPrivateBrowsing ? nsIRequest::LOAD_ANONYMOUS :
+ nsIRequest::LOAD_NORMAL;
+
+ rv = il->LoadImageXPCOM(mURI, nullptr, nullptr,
+ NS_LITERAL_STRING("default"), mPrincipal, nullptr,
+ this, nullptr, loadFlags, nullptr,
+ nsIContentPolicy::TYPE_INTERNAL_IMAGE,
+ getter_AddRefs(mRequest));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NotifyMissing();
+ }
+
+ return NS_OK;
+}
+
+nsresult
+AlertImageRequest::NotifyMissing()
+{
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+ if (nsCOMPtr<nsIAlertNotificationImageListener> listener = mListener.forget()) {
+ nsresult rv = listener->OnImageMissing(mUserData);
+ NS_RELEASE_THIS();
+ return rv;
+ }
+ return NS_OK;
+}
+
+nsresult
+AlertImageRequest::NotifyComplete()
+{
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+ if (nsCOMPtr<nsIAlertNotificationImageListener> listener = mListener.forget()) {
+ nsresult rv = listener->OnImageReady(mUserData, mRequest);
+ NS_RELEASE_THIS();
+ return rv;
+ }
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/components/alerts/src/AlertNotification.h b/components/alerts/src/AlertNotification.h
new file mode 100644
index 000000000..c0bcc0ba9
--- /dev/null
+++ b/components/alerts/src/AlertNotification.h
@@ -0,0 +1,81 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_AlertNotification_h__
+#define mozilla_AlertNotification_h__
+
+#include "imgINotificationObserver.h"
+#include "nsIAlertsService.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsICancelable.h"
+#include "nsIPrincipal.h"
+#include "nsString.h"
+#include "nsITimer.h"
+
+namespace mozilla {
+
+class AlertImageRequest final : public imgINotificationObserver,
+ public nsICancelable,
+ public nsITimerCallback
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(AlertImageRequest,
+ imgINotificationObserver)
+ NS_DECL_IMGINOTIFICATIONOBSERVER
+ NS_DECL_NSICANCELABLE
+ NS_DECL_NSITIMERCALLBACK
+
+ AlertImageRequest(nsIURI* aURI, nsIPrincipal* aPrincipal,
+ bool aInPrivateBrowsing, uint32_t aTimeout,
+ nsIAlertNotificationImageListener* aListener,
+ nsISupports* aUserData);
+
+ nsresult Start();
+
+private:
+ virtual ~AlertImageRequest();
+
+ nsresult NotifyMissing();
+ nsresult NotifyComplete();
+
+ nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsIPrincipal> mPrincipal;
+ bool mInPrivateBrowsing;
+ uint32_t mTimeout;
+ nsCOMPtr<nsIAlertNotificationImageListener> mListener;
+ nsCOMPtr<nsISupports> mUserData;
+ nsCOMPtr<nsITimer> mTimer;
+ nsCOMPtr<imgIRequest> mRequest;
+};
+
+class AlertNotification final : public nsIAlertNotification
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIALERTNOTIFICATION
+ AlertNotification();
+
+protected:
+ virtual ~AlertNotification();
+
+private:
+ nsString mName;
+ nsString mImageURL;
+ nsString mTitle;
+ nsString mText;
+ bool mTextClickable;
+ nsString mCookie;
+ nsString mDir;
+ nsString mLang;
+ bool mRequireInteraction;
+ nsString mData;
+ nsCOMPtr<nsIPrincipal> mPrincipal;
+ bool mInPrivateBrowsing;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_AlertNotification_h__ */
diff --git a/components/alerts/src/AlertNotificationIPCSerializer.h b/components/alerts/src/AlertNotificationIPCSerializer.h
new file mode 100644
index 000000000..9544f9633
--- /dev/null
+++ b/components/alerts/src/AlertNotificationIPCSerializer.h
@@ -0,0 +1,122 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_AlertNotificationIPCSerializer_h__
+#define mozilla_AlertNotificationIPCSerializer_h__
+
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsIAlertsService.h"
+#include "nsIPrincipal.h"
+#include "nsString.h"
+
+#include "ipc/IPCMessageUtils.h"
+
+#include "mozilla/dom/PermissionMessageUtils.h"
+
+typedef nsIAlertNotification* AlertNotificationType;
+
+namespace IPC {
+
+template <>
+struct ParamTraits<AlertNotificationType>
+{
+ typedef AlertNotificationType paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ bool isNull = !aParam;
+ if (isNull) {
+ WriteParam(aMsg, isNull);
+ return;
+ }
+
+ nsString name, imageURL, title, text, cookie, dir, lang, data;
+ bool textClickable, inPrivateBrowsing, requireInteraction;
+ nsCOMPtr<nsIPrincipal> principal;
+
+ if (NS_WARN_IF(NS_FAILED(aParam->GetName(name))) ||
+ NS_WARN_IF(NS_FAILED(aParam->GetImageURL(imageURL))) ||
+ NS_WARN_IF(NS_FAILED(aParam->GetTitle(title))) ||
+ NS_WARN_IF(NS_FAILED(aParam->GetText(text))) ||
+ NS_WARN_IF(NS_FAILED(aParam->GetTextClickable(&textClickable))) ||
+ NS_WARN_IF(NS_FAILED(aParam->GetCookie(cookie))) ||
+ NS_WARN_IF(NS_FAILED(aParam->GetDir(dir))) ||
+ NS_WARN_IF(NS_FAILED(aParam->GetLang(lang))) ||
+ NS_WARN_IF(NS_FAILED(aParam->GetData(data))) ||
+ NS_WARN_IF(NS_FAILED(aParam->GetPrincipal(getter_AddRefs(principal)))) ||
+ NS_WARN_IF(NS_FAILED(aParam->GetInPrivateBrowsing(&inPrivateBrowsing))) ||
+ NS_WARN_IF(NS_FAILED(aParam->GetRequireInteraction(&requireInteraction)))) {
+
+ // Write a `null` object if any getter returns an error. Otherwise, the
+ // receiver will try to deserialize an incomplete object and crash.
+ WriteParam(aMsg, /* isNull */ true);
+ return;
+ }
+
+ WriteParam(aMsg, isNull);
+ WriteParam(aMsg, name);
+ WriteParam(aMsg, imageURL);
+ WriteParam(aMsg, title);
+ WriteParam(aMsg, text);
+ WriteParam(aMsg, textClickable);
+ WriteParam(aMsg, cookie);
+ WriteParam(aMsg, dir);
+ WriteParam(aMsg, lang);
+ WriteParam(aMsg, data);
+ WriteParam(aMsg, IPC::Principal(principal));
+ WriteParam(aMsg, inPrivateBrowsing);
+ WriteParam(aMsg, requireInteraction);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ bool isNull;
+ NS_ENSURE_TRUE(ReadParam(aMsg, aIter, &isNull), false);
+ if (isNull) {
+ *aResult = nullptr;
+ return true;
+ }
+
+ nsString name, imageURL, title, text, cookie, dir, lang, data;
+ bool textClickable, inPrivateBrowsing, requireInteraction;
+ IPC::Principal principal;
+
+ if (!ReadParam(aMsg, aIter, &name) ||
+ !ReadParam(aMsg, aIter, &imageURL) ||
+ !ReadParam(aMsg, aIter, &title) ||
+ !ReadParam(aMsg, aIter, &text) ||
+ !ReadParam(aMsg, aIter, &textClickable) ||
+ !ReadParam(aMsg, aIter, &cookie) ||
+ !ReadParam(aMsg, aIter, &dir) ||
+ !ReadParam(aMsg, aIter, &lang) ||
+ !ReadParam(aMsg, aIter, &data) ||
+ !ReadParam(aMsg, aIter, &principal) ||
+ !ReadParam(aMsg, aIter, &inPrivateBrowsing) ||
+ !ReadParam(aMsg, aIter, &requireInteraction)) {
+
+ return false;
+ }
+
+ nsCOMPtr<nsIAlertNotification> alert =
+ do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
+ if (NS_WARN_IF(!alert)) {
+ *aResult = nullptr;
+ return true;
+ }
+ nsresult rv = alert->Init(name, imageURL, title, text, textClickable,
+ cookie, dir, lang, data, principal,
+ inPrivateBrowsing, requireInteraction);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ *aResult = nullptr;
+ return true;
+ }
+ alert.forget(aResult);
+ return true;
+ }
+};
+
+} // namespace IPC
+
+#endif /* mozilla_AlertNotificationIPCSerializer_h__ */
diff --git a/components/alerts/src/nsAlertsService.cpp b/components/alerts/src/nsAlertsService.cpp
new file mode 100644
index 000000000..4f248c861
--- /dev/null
+++ b/components/alerts/src/nsAlertsService.cpp
@@ -0,0 +1,301 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/PermissionMessageUtils.h"
+#include "mozilla/Preferences.h"
+#include "nsXULAppAPI.h"
+
+#include "nsAlertsService.h"
+
+#include "nsXPCOM.h"
+#include "nsIServiceManager.h"
+#include "nsIDOMWindow.h"
+#include "nsPromiseFlatString.h"
+#include "nsToolkitCompsCID.h"
+
+#ifdef MOZ_PLACES
+#include "mozIAsyncFavicons.h"
+#include "nsIFaviconService.h"
+#endif // MOZ_PLACES
+
+#ifdef XP_WIN
+#include <shellapi.h>
+#endif
+
+using namespace mozilla;
+
+using mozilla::dom::ContentChild;
+
+namespace {
+
+#ifdef MOZ_PLACES
+
+class IconCallback final : public nsIFaviconDataCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ IconCallback(nsIAlertsService* aBackend,
+ nsIAlertNotification* aAlert,
+ nsIObserver* aAlertListener)
+ : mBackend(aBackend)
+ , mAlert(aAlert)
+ , mAlertListener(aAlertListener)
+ {}
+
+ NS_IMETHOD
+ OnComplete(nsIURI *aIconURI, uint32_t aIconSize, const uint8_t *aIconData,
+ const nsACString &aMimeType) override
+ {
+ nsresult rv = NS_ERROR_FAILURE;
+ if (aIconSize > 0) {
+ nsCOMPtr<nsIAlertsIconData> alertsIconData(do_QueryInterface(mBackend));
+ if (alertsIconData) {
+ rv = alertsIconData->ShowAlertWithIconData(mAlert, mAlertListener,
+ aIconSize, aIconData);
+ }
+ } else if (aIconURI) {
+ nsCOMPtr<nsIAlertsIconURI> alertsIconURI(do_QueryInterface(mBackend));
+ if (alertsIconURI) {
+ rv = alertsIconURI->ShowAlertWithIconURI(mAlert, mAlertListener,
+ aIconURI);
+ }
+ }
+ if (NS_FAILED(rv)) {
+ rv = mBackend->ShowAlert(mAlert, mAlertListener);
+ }
+ return rv;
+ }
+
+private:
+ virtual ~IconCallback() {}
+
+ nsCOMPtr<nsIAlertsService> mBackend;
+ nsCOMPtr<nsIAlertNotification> mAlert;
+ nsCOMPtr<nsIObserver> mAlertListener;
+};
+
+NS_IMPL_ISUPPORTS(IconCallback, nsIFaviconDataCallback)
+
+#endif // MOZ_PLACES
+
+nsresult
+ShowWithIconBackend(nsIAlertsService* aBackend, nsIAlertNotification* aAlert,
+ nsIObserver* aAlertListener)
+{
+#ifdef MOZ_PLACES
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = aAlert->GetURI(getter_AddRefs(uri));
+ if (NS_FAILED(rv) || !uri) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Ensure the backend supports favicons.
+ nsCOMPtr<nsIAlertsIconData> alertsIconData(do_QueryInterface(aBackend));
+ nsCOMPtr<nsIAlertsIconURI> alertsIconURI;
+ if (!alertsIconData) {
+ alertsIconURI = do_QueryInterface(aBackend);
+ }
+ if (!alertsIconData && !alertsIconURI) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ nsCOMPtr<mozIAsyncFavicons> favicons(do_GetService(
+ "@mozilla.org/browser/favicon-service;1"));
+ NS_ENSURE_TRUE(favicons, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIFaviconDataCallback> callback =
+ new IconCallback(aBackend, aAlert, aAlertListener);
+ if (alertsIconData) {
+ return favicons->GetFaviconDataForPage(uri, callback);
+ }
+ return favicons->GetFaviconURLForPage(uri, callback);
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif // !MOZ_PLACES
+}
+
+nsresult
+ShowWithBackend(nsIAlertsService* aBackend, nsIAlertNotification* aAlert,
+ nsIObserver* aAlertListener, const nsAString& aPersistentData)
+{
+ if (!aPersistentData.IsEmpty()) {
+ return aBackend->ShowPersistentNotification(
+ aPersistentData, aAlert, aAlertListener);
+ }
+
+ if (Preferences::GetBool("alerts.showFavicons")) {
+ nsresult rv = ShowWithIconBackend(aBackend, aAlert, aAlertListener);
+ if (NS_SUCCEEDED(rv)) {
+ return rv;
+ }
+ }
+
+ // If favicons are disabled, or the backend doesn't support them, show the
+ // alert without one.
+ return aBackend->ShowAlert(aAlert, aAlertListener);
+}
+
+} // anonymous namespace
+
+NS_IMPL_ISUPPORTS(nsAlertsService, nsIAlertsService, nsIAlertsDoNotDisturb)
+
+nsAlertsService::nsAlertsService() :
+ mBackend(nullptr)
+{
+ mBackend = do_GetService(NS_SYSTEMALERTSERVICE_CONTRACTID);
+}
+
+nsAlertsService::~nsAlertsService()
+{}
+
+bool nsAlertsService::ShouldShowAlert()
+{
+ bool result = true;
+
+#ifdef XP_WIN
+ QUERY_USER_NOTIFICATION_STATE qstate;
+ if (SUCCEEDED(SHQueryUserNotificationState(&qstate))) {
+ if (qstate != QUNS_ACCEPTS_NOTIFICATIONS) {
+ result = false;
+ }
+ }
+#endif
+
+ return result;
+}
+
+NS_IMETHODIMP nsAlertsService::ShowAlertNotification(const nsAString & aImageUrl, const nsAString & aAlertTitle,
+ const nsAString & aAlertText, bool aAlertTextClickable,
+ const nsAString & aAlertCookie,
+ nsIObserver * aAlertListener,
+ const nsAString & aAlertName,
+ const nsAString & aBidi,
+ const nsAString & aLang,
+ const nsAString & aData,
+ nsIPrincipal * aPrincipal,
+ bool aInPrivateBrowsing,
+ bool aRequireInteraction)
+{
+ nsCOMPtr<nsIAlertNotification> alert =
+ do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
+ NS_ENSURE_TRUE(alert, NS_ERROR_FAILURE);
+ nsresult rv = alert->Init(aAlertName, aImageUrl, aAlertTitle,
+ aAlertText, aAlertTextClickable,
+ aAlertCookie, aBidi, aLang, aData,
+ aPrincipal, aInPrivateBrowsing,
+ aRequireInteraction);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ShowAlert(alert, aAlertListener);
+}
+
+
+NS_IMETHODIMP nsAlertsService::ShowAlert(nsIAlertNotification * aAlert,
+ nsIObserver * aAlertListener)
+{
+ return ShowPersistentNotification(EmptyString(), aAlert, aAlertListener);
+}
+
+NS_IMETHODIMP nsAlertsService::ShowPersistentNotification(const nsAString & aPersistentData,
+ nsIAlertNotification * aAlert,
+ nsIObserver * aAlertListener)
+{
+ NS_ENSURE_ARG(aAlert);
+
+ nsAutoString cookie;
+ nsresult rv = aAlert->GetCookie(cookie);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (XRE_IsContentProcess()) {
+ ContentChild* cpc = ContentChild::GetSingleton();
+
+ if (aAlertListener)
+ cpc->AddRemoteAlertObserver(cookie, aAlertListener);
+
+ cpc->SendShowAlert(aAlert);
+ return NS_OK;
+ }
+
+ // Check if there is an optional service that handles system-level notifications
+ if (mBackend) {
+ rv = ShowWithBackend(mBackend, aAlert, aAlertListener, aPersistentData);
+ if (NS_SUCCEEDED(rv)) {
+ return rv;
+ }
+ // If the system backend failed to show the alert, clear the backend and
+ // retry with XUL notifications. Future alerts will always use XUL.
+ mBackend = nullptr;
+ }
+
+ if (!ShouldShowAlert()) {
+ // Do not display the alert. Instead call alertfinished and get out.
+ if (aAlertListener)
+ aAlertListener->Observe(nullptr, "alertfinished", cookie.get());
+ return NS_OK;
+ }
+
+ // Use XUL notifications as a fallback if above methods have failed.
+ nsCOMPtr<nsIAlertsService> xulBackend(nsXULAlerts::GetInstance());
+ NS_ENSURE_TRUE(xulBackend, NS_ERROR_FAILURE);
+ return ShowWithBackend(xulBackend, aAlert, aAlertListener, aPersistentData);
+}
+
+NS_IMETHODIMP nsAlertsService::CloseAlert(const nsAString& aAlertName,
+ nsIPrincipal* aPrincipal)
+{
+ if (XRE_IsContentProcess()) {
+ ContentChild* cpc = ContentChild::GetSingleton();
+ cpc->SendCloseAlert(nsAutoString(aAlertName), IPC::Principal(aPrincipal));
+ return NS_OK;
+ }
+
+ nsresult rv;
+ // Try the system notification service.
+ if (mBackend) {
+ rv = mBackend->CloseAlert(aAlertName, aPrincipal);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // If the system backend failed to close the alert, fall back to XUL for
+ // future alerts.
+ mBackend = nullptr;
+ }
+ } else {
+ nsCOMPtr<nsIAlertsService> xulBackend(nsXULAlerts::GetInstance());
+ NS_ENSURE_TRUE(xulBackend, NS_ERROR_FAILURE);
+ rv = xulBackend->CloseAlert(aAlertName, aPrincipal);
+ }
+ return rv;
+}
+
+
+// nsIAlertsDoNotDisturb
+NS_IMETHODIMP nsAlertsService::GetManualDoNotDisturb(bool* aRetVal)
+{
+ nsCOMPtr<nsIAlertsDoNotDisturb> alertsDND(GetDNDBackend());
+ NS_ENSURE_TRUE(alertsDND, NS_ERROR_NOT_IMPLEMENTED);
+ return alertsDND->GetManualDoNotDisturb(aRetVal);
+}
+
+NS_IMETHODIMP nsAlertsService::SetManualDoNotDisturb(bool aDoNotDisturb)
+{
+ nsCOMPtr<nsIAlertsDoNotDisturb> alertsDND(GetDNDBackend());
+ NS_ENSURE_TRUE(alertsDND, NS_ERROR_NOT_IMPLEMENTED);
+
+ nsresult rv = alertsDND->SetManualDoNotDisturb(aDoNotDisturb);
+ return rv;
+}
+
+already_AddRefed<nsIAlertsDoNotDisturb>
+nsAlertsService::GetDNDBackend()
+{
+ // Try the system notification service.
+ nsCOMPtr<nsIAlertsService> backend = mBackend;
+ if (!backend) {
+ backend = nsXULAlerts::GetInstance();
+ }
+
+ nsCOMPtr<nsIAlertsDoNotDisturb> alertsDND(do_QueryInterface(backend));
+ return alertsDND.forget();
+}
diff --git a/components/alerts/src/nsAlertsService.h b/components/alerts/src/nsAlertsService.h
new file mode 100644
index 000000000..d2b2e1e6c
--- /dev/null
+++ b/components/alerts/src/nsAlertsService.h
@@ -0,0 +1,31 @@
+// /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAlertsService_h__
+#define nsAlertsService_h__
+
+#include "nsIAlertsService.h"
+#include "nsCOMPtr.h"
+#include "nsXULAlerts.h"
+
+class nsAlertsService : public nsIAlertsService,
+ public nsIAlertsDoNotDisturb
+{
+public:
+ NS_DECL_NSIALERTSDONOTDISTURB
+ NS_DECL_NSIALERTSSERVICE
+ NS_DECL_ISUPPORTS
+
+ nsAlertsService();
+
+protected:
+ virtual ~nsAlertsService();
+
+ bool ShouldShowAlert();
+ already_AddRefed<nsIAlertsDoNotDisturb> GetDNDBackend();
+ nsCOMPtr<nsIAlertsService> mBackend;
+};
+
+#endif /* nsAlertsService_h__ */
diff --git a/components/alerts/src/nsAlertsUtils.cpp b/components/alerts/src/nsAlertsUtils.cpp
new file mode 100644
index 000000000..5f7d92d2a
--- /dev/null
+++ b/components/alerts/src/nsAlertsUtils.cpp
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAlertsUtils.h"
+
+#include "nsCOMPtr.h"
+#include "nsContentUtils.h"
+#include "nsIStringBundle.h"
+#include "nsIURI.h"
+#include "nsXPIDLString.h"
+
+/* static */
+bool
+nsAlertsUtils::IsActionablePrincipal(nsIPrincipal* aPrincipal)
+{
+ return aPrincipal &&
+ !nsContentUtils::IsSystemOrExpandedPrincipal(aPrincipal) &&
+ !aPrincipal->GetIsNullPrincipal();
+}
+
+/* static */
+void
+nsAlertsUtils::GetSourceHostPort(nsIPrincipal* aPrincipal,
+ nsAString& aHostPort)
+{
+ if (!IsActionablePrincipal(aPrincipal)) {
+ return;
+ }
+ nsCOMPtr<nsIURI> principalURI;
+ if (NS_WARN_IF(NS_FAILED(
+ aPrincipal->GetURI(getter_AddRefs(principalURI))))) {
+ return;
+ }
+ if (!principalURI) {
+ return;
+ }
+ nsAutoCString hostPort;
+ if (NS_WARN_IF(NS_FAILED(principalURI->GetHostPort(hostPort)))) {
+ return;
+ }
+ CopyUTF8toUTF16(hostPort, aHostPort);
+}
diff --git a/components/alerts/src/nsAlertsUtils.h b/components/alerts/src/nsAlertsUtils.h
new file mode 100644
index 000000000..bc11f6351
--- /dev/null
+++ b/components/alerts/src/nsAlertsUtils.h
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAlertsUtils_h
+#define nsAlertsUtils_h
+
+#include "nsIPrincipal.h"
+#include "nsString.h"
+
+class nsAlertsUtils final
+{
+private:
+ nsAlertsUtils() = delete;
+
+public:
+ /**
+ * Indicates whether an alert from |aPrincipal| should include the source
+ * string and action buttons. Returns false if |aPrincipal| is |nullptr|, or
+ * a system, expanded, or null principal.
+ */
+ static bool
+ IsActionablePrincipal(nsIPrincipal* aPrincipal);
+
+ /**
+ * Sets |aHostPort| to the host and port from |aPrincipal|'s URI, or an
+ * empty string if |aPrincipal| is not actionable.
+ */
+ static void
+ GetSourceHostPort(nsIPrincipal* aPrincipal, nsAString& aHostPort);
+};
+#endif /* nsAlertsUtils_h */
diff --git a/components/alerts/src/nsXULAlerts.cpp b/components/alerts/src/nsXULAlerts.cpp
new file mode 100644
index 000000000..47e72a27f
--- /dev/null
+++ b/components/alerts/src/nsXULAlerts.cpp
@@ -0,0 +1,380 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsXULAlerts.h"
+
+#include "nsArray.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/dom/Notification.h"
+#include "mozilla/Unused.h"
+#include "nsIServiceManager.h"
+#include "nsISupportsPrimitives.h"
+#include "nsPIDOMWindow.h"
+#include "nsIWindowWatcher.h"
+
+using namespace mozilla;
+
+#define ALERT_CHROME_URL "chrome://global/content/alerts/alert.xul"
+
+namespace {
+StaticRefPtr<nsXULAlerts> gXULAlerts;
+} // anonymous namespace
+
+NS_IMPL_CYCLE_COLLECTION(nsXULAlertObserver, mAlertWindow)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULAlertObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULAlertObserver)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULAlertObserver)
+
+NS_IMETHODIMP
+nsXULAlertObserver::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ if (!strcmp("alertfinished", aTopic)) {
+ mozIDOMWindowProxy* currentAlert = mXULAlerts->mNamedWindows.GetWeak(mAlertName);
+ // The window in mNamedWindows might be a replacement, thus it should only
+ // be removed if it is the same window that is associated with this listener.
+ if (currentAlert == mAlertWindow) {
+ mXULAlerts->mNamedWindows.Remove(mAlertName);
+
+ if (mIsPersistent) {
+ mXULAlerts->PersistentAlertFinished();
+ }
+ }
+ }
+
+ nsresult rv = NS_OK;
+ if (mObserver) {
+ rv = mObserver->Observe(aSubject, aTopic, aData);
+ }
+ return rv;
+}
+
+// We don't cycle collect nsXULAlerts since gXULAlerts will keep the instance
+// alive till shutdown anyway.
+NS_IMPL_ISUPPORTS(nsXULAlerts, nsIAlertsService, nsIAlertsDoNotDisturb, nsIAlertsIconURI)
+
+/* static */ already_AddRefed<nsXULAlerts>
+nsXULAlerts::GetInstance()
+{
+ if (!gXULAlerts) {
+ gXULAlerts = new nsXULAlerts();
+ ClearOnShutdown(&gXULAlerts);
+ }
+ RefPtr<nsXULAlerts> instance = gXULAlerts.get();
+ return instance.forget();
+}
+
+void
+nsXULAlerts::PersistentAlertFinished()
+{
+ MOZ_ASSERT(mPersistentAlertCount);
+ mPersistentAlertCount--;
+
+ // Show next pending persistent alert if any.
+ if (!mPendingPersistentAlerts.IsEmpty()) {
+ ShowAlertWithIconURI(mPendingPersistentAlerts[0].mAlert,
+ mPendingPersistentAlerts[0].mListener,
+ nullptr);
+ mPendingPersistentAlerts.RemoveElementAt(0);
+ }
+}
+
+NS_IMETHODIMP
+nsXULAlerts::ShowAlertNotification(const nsAString& aImageUrl, const nsAString& aAlertTitle,
+ const nsAString& aAlertText, bool aAlertTextClickable,
+ const nsAString& aAlertCookie, nsIObserver* aAlertListener,
+ const nsAString& aAlertName, const nsAString& aBidi,
+ const nsAString& aLang, const nsAString& aData,
+ nsIPrincipal* aPrincipal, bool aInPrivateBrowsing,
+ bool aRequireInteraction)
+{
+ nsCOMPtr<nsIAlertNotification> alert =
+ do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
+ NS_ENSURE_TRUE(alert, NS_ERROR_FAILURE);
+ nsresult rv = alert->Init(aAlertName, aImageUrl, aAlertTitle,
+ aAlertText, aAlertTextClickable,
+ aAlertCookie, aBidi, aLang, aData,
+ aPrincipal, aInPrivateBrowsing,
+ aRequireInteraction);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ShowAlert(alert, aAlertListener);
+}
+
+NS_IMETHODIMP
+nsXULAlerts::ShowPersistentNotification(const nsAString& aPersistentData,
+ nsIAlertNotification* aAlert,
+ nsIObserver* aAlertListener)
+{
+ return ShowAlert(aAlert, aAlertListener);
+}
+
+NS_IMETHODIMP
+nsXULAlerts::ShowAlert(nsIAlertNotification* aAlert,
+ nsIObserver* aAlertListener)
+{
+ nsAutoString name;
+ nsresult rv = aAlert->GetName(name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If there is a pending alert with the same name in the list of
+ // pending alerts, replace it.
+ if (!mPendingPersistentAlerts.IsEmpty()) {
+ for (uint32_t i = 0; i < mPendingPersistentAlerts.Length(); i++) {
+ nsAutoString pendingAlertName;
+ nsCOMPtr<nsIAlertNotification> pendingAlert = mPendingPersistentAlerts[i].mAlert;
+ rv = pendingAlert->GetName(pendingAlertName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (pendingAlertName.Equals(name)) {
+ nsAutoString cookie;
+ rv = pendingAlert->GetCookie(cookie);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mPendingPersistentAlerts[i].mListener) {
+ rv = mPendingPersistentAlerts[i].mListener->Observe(nullptr, "alertfinished", cookie.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ mPendingPersistentAlerts[i].Init(aAlert, aAlertListener);
+ return NS_OK;
+ }
+ }
+ }
+
+ bool requireInteraction;
+ rv = aAlert->GetRequireInteraction(&requireInteraction);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (requireInteraction &&
+ !mNamedWindows.Contains(name) &&
+ static_cast<int32_t>(mPersistentAlertCount) >=
+ Preferences::GetInt("dom.webnotifications.requireinteraction.count", 0)) {
+ PendingAlert* pa = mPendingPersistentAlerts.AppendElement();
+ pa->Init(aAlert, aAlertListener);
+ return NS_OK;
+ } else {
+ return ShowAlertWithIconURI(aAlert, aAlertListener, nullptr);
+ }
+}
+
+NS_IMETHODIMP
+nsXULAlerts::ShowAlertWithIconURI(nsIAlertNotification* aAlert,
+ nsIObserver* aAlertListener,
+ nsIURI* aIconURI)
+{
+ bool inPrivateBrowsing;
+ nsresult rv = aAlert->GetInPrivateBrowsing(&inPrivateBrowsing);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString cookie;
+ rv = aAlert->GetCookie(cookie);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mDoNotDisturb) {
+ if (aAlertListener)
+ aAlertListener->Observe(nullptr, "alertfinished", cookie.get());
+ return NS_OK;
+ }
+
+ nsAutoString name;
+ rv = aAlert->GetName(name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString imageUrl;
+ rv = aAlert->GetImageURL(imageUrl);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString title;
+ rv = aAlert->GetTitle(title);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString text;
+ rv = aAlert->GetText(text);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool textClickable;
+ rv = aAlert->GetTextClickable(&textClickable);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString bidi;
+ rv = aAlert->GetDir(bidi);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString lang;
+ rv = aAlert->GetLang(lang);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString source;
+ rv = aAlert->GetSource(source);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool requireInteraction;
+ rv = aAlert->GetRequireInteraction(&requireInteraction);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+
+ nsCOMPtr<nsIMutableArray> argsArray = nsArray::Create();
+
+ // create scriptable versions of our strings that we can store in our nsIMutableArray....
+ nsCOMPtr<nsISupportsString> scriptableImageUrl (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+ NS_ENSURE_TRUE(scriptableImageUrl, NS_ERROR_FAILURE);
+
+ scriptableImageUrl->SetData(imageUrl);
+ rv = argsArray->AppendElement(scriptableImageUrl, /*weak =*/ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupportsString> scriptableAlertTitle (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+ NS_ENSURE_TRUE(scriptableAlertTitle, NS_ERROR_FAILURE);
+
+ scriptableAlertTitle->SetData(title);
+ rv = argsArray->AppendElement(scriptableAlertTitle, /*weak =*/ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupportsString> scriptableAlertText (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+ NS_ENSURE_TRUE(scriptableAlertText, NS_ERROR_FAILURE);
+
+ scriptableAlertText->SetData(text);
+ rv = argsArray->AppendElement(scriptableAlertText, /*weak =*/ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupportsPRBool> scriptableIsClickable (do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID));
+ NS_ENSURE_TRUE(scriptableIsClickable, NS_ERROR_FAILURE);
+
+ scriptableIsClickable->SetData(textClickable);
+ rv = argsArray->AppendElement(scriptableIsClickable, /*weak =*/ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupportsString> scriptableAlertCookie (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+ NS_ENSURE_TRUE(scriptableAlertCookie, NS_ERROR_FAILURE);
+
+ scriptableAlertCookie->SetData(cookie);
+ rv = argsArray->AppendElement(scriptableAlertCookie, /*weak =*/ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupportsPRInt32> scriptableOrigin (do_CreateInstance(NS_SUPPORTS_PRINT32_CONTRACTID));
+ NS_ENSURE_TRUE(scriptableOrigin, NS_ERROR_FAILURE);
+
+ int32_t origin =
+ LookAndFeel::GetInt(LookAndFeel::eIntID_AlertNotificationOrigin);
+ scriptableOrigin->SetData(origin);
+
+ rv = argsArray->AppendElement(scriptableOrigin, /*weak =*/ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupportsString> scriptableBidi (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+ NS_ENSURE_TRUE(scriptableBidi, NS_ERROR_FAILURE);
+
+ scriptableBidi->SetData(bidi);
+ rv = argsArray->AppendElement(scriptableBidi, /*weak =*/ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupportsString> scriptableLang (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+ NS_ENSURE_TRUE(scriptableLang, NS_ERROR_FAILURE);
+
+ scriptableLang->SetData(lang);
+ rv = argsArray->AppendElement(scriptableLang, /*weak =*/ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupportsPRBool> scriptableRequireInteraction (do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID));
+ NS_ENSURE_TRUE(scriptableRequireInteraction, NS_ERROR_FAILURE);
+
+ scriptableRequireInteraction->SetData(requireInteraction);
+ rv = argsArray->AppendElement(scriptableRequireInteraction, /*weak =*/ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Alerts with the same name should replace the old alert in the same position.
+ // Provide the new alert window with a pointer to the replaced window so that
+ // it may take the same position.
+ nsCOMPtr<nsISupportsInterfacePointer> replacedWindow = do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
+ NS_ENSURE_TRUE(replacedWindow, NS_ERROR_FAILURE);
+ mozIDOMWindowProxy* previousAlert = mNamedWindows.GetWeak(name);
+ replacedWindow->SetData(previousAlert);
+ replacedWindow->SetDataIID(&NS_GET_IID(mozIDOMWindowProxy));
+ rv = argsArray->AppendElement(replacedWindow, /*weak =*/ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (requireInteraction) {
+ mPersistentAlertCount++;
+ }
+
+ // Add an observer (that wraps aAlertListener) to remove the window from
+ // mNamedWindows when it is closed.
+ nsCOMPtr<nsISupportsInterfacePointer> ifptr = do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ RefPtr<nsXULAlertObserver> alertObserver = new nsXULAlertObserver(this, name, aAlertListener, requireInteraction);
+ nsCOMPtr<nsISupports> iSupports(do_QueryInterface(alertObserver));
+ ifptr->SetData(iSupports);
+ ifptr->SetDataIID(&NS_GET_IID(nsIObserver));
+ rv = argsArray->AppendElement(ifptr, /*weak =*/ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // The source contains the host and port of the site that sent the
+ // notification. It is empty for system alerts.
+ nsCOMPtr<nsISupportsString> scriptableAlertSource (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+ NS_ENSURE_TRUE(scriptableAlertSource, NS_ERROR_FAILURE);
+ scriptableAlertSource->SetData(source);
+ rv = argsArray->AppendElement(scriptableAlertSource, /*weak =*/ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupportsCString> scriptableIconURL (do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
+ NS_ENSURE_TRUE(scriptableIconURL, NS_ERROR_FAILURE);
+ if (aIconURI) {
+ nsAutoCString iconURL;
+ rv = aIconURI->GetSpec(iconURL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ scriptableIconURL->SetData(iconURL);
+ }
+ rv = argsArray->AppendElement(scriptableIconURL, /*weak =*/ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<mozIDOMWindowProxy> newWindow;
+ nsAutoCString features("chrome,dialog=yes,titlebar=no,popup=yes");
+ if (inPrivateBrowsing) {
+ features.AppendLiteral(",private");
+ }
+ rv = wwatch->OpenWindow(nullptr, ALERT_CHROME_URL, "_blank", features.get(),
+ argsArray, getter_AddRefs(newWindow));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mNamedWindows.Put(name, newWindow);
+ alertObserver->SetAlertWindow(newWindow);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULAlerts::SetManualDoNotDisturb(bool aDoNotDisturb)
+{
+ mDoNotDisturb = aDoNotDisturb;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULAlerts::GetManualDoNotDisturb(bool* aRetVal)
+{
+ *aRetVal = mDoNotDisturb;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULAlerts::CloseAlert(const nsAString& aAlertName,
+ nsIPrincipal* aPrincipal)
+{
+ mozIDOMWindowProxy* alert = mNamedWindows.GetWeak(aAlertName);
+ if (nsCOMPtr<nsPIDOMWindowOuter> domWindow = nsPIDOMWindowOuter::From(alert)) {
+ domWindow->DispatchCustomEvent(NS_LITERAL_STRING("XULAlertClose"));
+ }
+ return NS_OK;
+}
+
diff --git a/components/alerts/src/nsXULAlerts.h b/components/alerts/src/nsXULAlerts.h
new file mode 100644
index 000000000..557716ee6
--- /dev/null
+++ b/components/alerts/src/nsXULAlerts.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsXULAlerts_h__
+#define nsXULAlerts_h__
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsInterfaceHashtable.h"
+
+#include "mozIDOMWindow.h"
+#include "nsIObserver.h"
+
+struct PendingAlert
+{
+ void Init(nsIAlertNotification* aAlert, nsIObserver* aListener)
+ {
+ mAlert = aAlert;
+ mListener = aListener;
+ }
+ nsCOMPtr<nsIAlertNotification> mAlert;
+ nsCOMPtr<nsIObserver> mListener;
+};
+
+class nsXULAlerts : public nsIAlertsService,
+ public nsIAlertsDoNotDisturb,
+ public nsIAlertsIconURI
+{
+ friend class nsXULAlertObserver;
+public:
+ NS_DECL_NSIALERTSICONURI
+ NS_DECL_NSIALERTSDONOTDISTURB
+ NS_DECL_NSIALERTSSERVICE
+ NS_DECL_ISUPPORTS
+
+ nsXULAlerts()
+ {
+ }
+
+ static already_AddRefed<nsXULAlerts> GetInstance();
+
+protected:
+ virtual ~nsXULAlerts() {}
+ void PersistentAlertFinished();
+
+ nsInterfaceHashtable<nsStringHashKey, mozIDOMWindowProxy> mNamedWindows;
+ uint32_t mPersistentAlertCount = 0;
+ nsTArray<PendingAlert> mPendingPersistentAlerts;
+ bool mDoNotDisturb = false;
+};
+
+/**
+ * This class wraps observers for alerts and watches
+ * for the "alertfinished" event in order to release
+ * the reference on the nsIDOMWindow of the XUL alert.
+ */
+class nsXULAlertObserver : public nsIObserver {
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ NS_DECL_CYCLE_COLLECTION_CLASS(nsXULAlertObserver)
+
+ nsXULAlertObserver(nsXULAlerts* aXULAlerts, const nsAString& aAlertName,
+ nsIObserver* aObserver, bool aIsPersistent)
+ : mXULAlerts(aXULAlerts), mAlertName(aAlertName),
+ mObserver(aObserver), mIsPersistent(aIsPersistent) {}
+
+ void SetAlertWindow(mozIDOMWindowProxy* aWindow) { mAlertWindow = aWindow; }
+
+protected:
+ virtual ~nsXULAlertObserver() {}
+
+ RefPtr<nsXULAlerts> mXULAlerts;
+ nsString mAlertName;
+ nsCOMPtr<mozIDOMWindowProxy> mAlertWindow;
+ nsCOMPtr<nsIObserver> mObserver;
+ bool mIsPersistent;
+};
+
+#endif /* nsXULAlerts_h__ */
+