diff --git a/toolkit/components/alerts/AlertNotification.cpp b/toolkit/components/alerts/AlertNotification.cpp index ef443f714a8..b72e5746adb 100644 --- a/toolkit/components/alerts/AlertNotification.cpp +++ b/toolkit/components/alerts/AlertNotification.cpp @@ -117,6 +117,16 @@ AlertNotification::GetPrincipal(nsIPrincipal** aPrincipal) 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) { diff --git a/toolkit/components/alerts/nsAlertsService.cpp b/toolkit/components/alerts/nsAlertsService.cpp index d6765770ecb..345f6075406 100644 --- a/toolkit/components/alerts/nsAlertsService.cpp +++ b/toolkit/components/alerts/nsAlertsService.cpp @@ -22,10 +22,118 @@ #endif // !MOZ_WIDGET_ANDROID +#ifdef MOZ_PLACES +#include "mozIAsyncFavicons.h" +#include "nsIFaviconService.h" +#endif // MOZ_PLACES + 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 alertsIconData(do_QueryInterface(mBackend)); + if (alertsIconData) { + rv = alertsIconData->ShowAlertWithIconData(mAlert, mAlertListener, + aIconSize, aIconData); + } + } else if (aIconURI) { + nsCOMPtr 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 mBackend; + nsCOMPtr mAlert; + nsCOMPtr mAlertListener; +}; + +NS_IMPL_ISUPPORTS(IconCallback, nsIFaviconDataCallback) + +#endif // MOZ_PLACES + +nsresult +ShowWithIconBackend(nsIAlertsService* aBackend, nsIAlertNotification* aAlert, + nsIObserver* aAlertListener) +{ +#ifdef MOZ_PLACES + nsCOMPtr uri; + nsresult rv = aAlert->GetURI(getter_AddRefs(uri)); + if (NS_FAILED(rv) || !uri) { + return NS_ERROR_FAILURE; + } + + // Ensure the backend supports favicons. + nsCOMPtr alertsIconData(do_QueryInterface(aBackend)); + nsCOMPtr alertsIconURI; + if (!alertsIconData) { + alertsIconURI = do_QueryInterface(aBackend); + } + if (!alertsIconData && !alertsIconURI) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + nsCOMPtr favicons(do_GetService( + "@mozilla.org/browser/favicon-service;1")); + NS_ENSURE_TRUE(favicons, NS_ERROR_FAILURE); + + nsCOMPtr 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) +{ + nsresult rv = ShowWithIconBackend(aBackend, aAlert, aAlertListener); + if (NS_SUCCEEDED(rv)) { + return rv; + } + // If the backend doesn't support favicons, show the alert without one. + return aBackend->ShowAlert(aAlert, aAlertListener); +} + +} // anonymous namespace + NS_IMPL_ISUPPORTS(nsAlertsService, nsIAlertsService, nsIAlertsDoNotDisturb, nsIAlertsProgressListener) nsAlertsService::nsAlertsService() : @@ -66,7 +174,7 @@ bool nsAlertsService::ShouldShowAlert() return result; } -NS_IMETHODIMP nsAlertsService::ShowAlertNotification(const nsAString & aImageUrl, const nsAString & aAlertTitle, +NS_IMETHODIMP nsAlertsService::ShowAlertNotification(const nsAString & aImageUrl, const nsAString & aAlertTitle, const nsAString & aAlertText, bool aAlertTextClickable, const nsAString & aAlertCookie, nsIObserver * aAlertListener, @@ -135,7 +243,7 @@ NS_IMETHODIMP nsAlertsService::ShowAlert(nsIAlertNotification * aAlert, #else // Check if there is an optional service that handles system-level notifications if (mBackend) { - rv = mBackend->ShowAlert(aAlert, aAlertListener); + rv = ShowWithBackend(mBackend, aAlert, aAlertListener); if (NS_SUCCEEDED(rv)) { return rv; } @@ -154,7 +262,7 @@ NS_IMETHODIMP nsAlertsService::ShowAlert(nsIAlertNotification * aAlert, // Use XUL notifications as a fallback if above methods have failed. nsCOMPtr xulBackend(nsXULAlerts::GetInstance()); NS_ENSURE_TRUE(xulBackend, NS_ERROR_FAILURE); - return xulBackend->ShowAlert(aAlert, aAlertListener); + return ShowWithBackend(xulBackend, aAlert, aAlertListener); #endif // !MOZ_WIDGET_ANDROID } diff --git a/toolkit/components/alerts/nsIAlertsService.idl b/toolkit/components/alerts/nsIAlertsService.idl index f11848f566e..d34061ff21b 100644 --- a/toolkit/components/alerts/nsIAlertsService.idl +++ b/toolkit/components/alerts/nsIAlertsService.idl @@ -7,12 +7,13 @@ #include "nsIObserver.idl" interface nsIPrincipal; +interface nsIURI; %{C++ #define ALERT_NOTIFICATION_CONTRACTID "@mozilla.org/alert-notification;1" %} -[scriptable, uuid(9e87fc34-8dbb-4b14-a724-b27be6822ec8)] +[scriptable, uuid(1650a064-79d5-4eb6-8c9e-57dd6522b6ac)] interface nsIAlertNotification : nsISupports { /** Initializes an alert notification. */ @@ -85,6 +86,12 @@ interface nsIAlertNotification : nsISupports */ readonly attribute nsIPrincipal principal; + /** + * The URI of the page that created the alert. |null| if the alert is not + * actionable. + */ + readonly attribute nsIURI URI; + /** * Controls the image loading behavior. If true, the image URL will be loaded * in private browsing mode. @@ -224,3 +231,30 @@ interface nsIAlertsProgressListener : nsISupports void onCancel(in AString name); }; +[scriptable, uuid(fc6d7f0a-0cf6-4268-8c71-ab640842b9b1)] +interface nsIAlertsIconData : nsISupports +{ + /** + * Shows an alert with an icon. Web notifications use the favicon of the + * page that created the alert. If the favicon is not in the Places database, + * |iconSize| will be zero. + */ + void showAlertWithIconData(in nsIAlertNotification alert, + [optional] in nsIObserver alertListener, + [optional] in uint32_t iconSize, + [const, array, size_is(iconSize)] in uint8_t + iconData); +}; + +[scriptable, uuid(f3c82915-bf60-41ea-91ce-6c46b22e381a)] +interface nsIAlertsIconURI : nsISupports +{ + /** + * Shows an alert with an icon URI. Web notifications use |moz-anno:| + * URIs to reference favicons from Places. If the page doesn't have a + * favicon, |iconURI| will be |null|. + */ + void showAlertWithIconURI(in nsIAlertNotification alert, + [optional] in nsIObserver alertListener, + [optional] in nsIURI iconURI); +};