diff options
Diffstat (limited to 'dom/notification/DesktopNotification.cpp')
-rw-r--r-- | dom/notification/DesktopNotification.cpp | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/dom/notification/DesktopNotification.cpp b/dom/notification/DesktopNotification.cpp new file mode 100644 index 000000000..76f1c5afb --- /dev/null +++ b/dom/notification/DesktopNotification.cpp @@ -0,0 +1,319 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/DesktopNotification.h" +#include "mozilla/dom/DesktopNotificationBinding.h" +#include "mozilla/dom/AppNotificationServiceOptionsBinding.h" +#include "mozilla/dom/ToJSValue.h" +#include "nsComponentManagerUtils.h" +#include "nsContentPermissionHelper.h" +#include "nsXULAppAPI.h" +#include "mozilla/dom/PBrowserChild.h" +#include "mozilla/Preferences.h" +#include "nsGlobalWindow.h" +#include "nsIScriptSecurityManager.h" +#include "nsServiceManagerUtils.h" +#include "PermissionMessageUtils.h" +#include "nsILoadContext.h" + +namespace mozilla { +namespace dom { + +/* + * Simple Request + */ +class DesktopNotificationRequest : public nsIContentPermissionRequest + , public Runnable +{ + virtual ~DesktopNotificationRequest() + { + } + + nsCOMPtr<nsIContentPermissionRequester> mRequester; +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSICONTENTPERMISSIONREQUEST + + explicit DesktopNotificationRequest(DesktopNotification* aNotification) + : mDesktopNotification(aNotification) + { + mRequester = new nsContentPermissionRequester(mDesktopNotification->GetOwner()); + } + + NS_IMETHOD Run() override + { + nsCOMPtr<nsPIDOMWindowInner> window = mDesktopNotification->GetOwner(); + nsContentPermissionUtils::AskPermission(this, window); + return NS_OK; + } + + RefPtr<DesktopNotification> mDesktopNotification; +}; + +/* ------------------------------------------------------------------------ */ +/* AlertServiceObserver */ +/* ------------------------------------------------------------------------ */ + +NS_IMPL_ISUPPORTS(AlertServiceObserver, nsIObserver) + +/* ------------------------------------------------------------------------ */ +/* DesktopNotification */ +/* ------------------------------------------------------------------------ */ + +uint32_t DesktopNotification::sCount = 0; + +nsresult +DesktopNotification::PostDesktopNotification() +{ + if (!mObserver) { + mObserver = new AlertServiceObserver(this); + } + + nsCOMPtr<nsIAlertsService> alerts = do_GetService("@mozilla.org/alerts-service;1"); + if (!alerts) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + // Generate a unique name (which will also be used as a cookie) because + // the nsIAlertsService will coalesce notifications with the same name. + // In the case of IPC, the parent process will use the cookie to map + // to nsIObservers, thus cookies must be unique to differentiate observers. + nsString uniqueName = NS_LITERAL_STRING("desktop-notification:"); + uniqueName.AppendInt(sCount++); + nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner(); + if (!owner) { + return NS_ERROR_FAILURE; + } + nsCOMPtr<nsIDocument> doc = owner->GetDoc(); + nsIPrincipal* principal = doc->NodePrincipal(); + nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext(); + bool inPrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing(); + nsCOMPtr<nsIAlertNotification> alert = + do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID); + NS_ENSURE_TRUE(alert, NS_ERROR_FAILURE); + nsresult rv = alert->Init(uniqueName, mIconURL, mTitle, + mDescription, + true, + uniqueName, + NS_LITERAL_STRING("auto"), + EmptyString(), + EmptyString(), + principal, + inPrivateBrowsing, + false /* requireInteraction */); + NS_ENSURE_SUCCESS(rv, rv); + return alerts->ShowAlert(alert, mObserver); +} + +DesktopNotification::DesktopNotification(const nsAString & title, + const nsAString & description, + const nsAString & iconURL, + nsPIDOMWindowInner* aWindow, + nsIPrincipal* principal) + : DOMEventTargetHelper(aWindow) + , mTitle(title) + , mDescription(description) + , mIconURL(iconURL) + , mPrincipal(principal) + , mAllow(false) + , mShowHasBeenCalled(false) +{ + if (Preferences::GetBool("notification.disabled", false)) { + return; + } + + // If we are in testing mode (running mochitests, for example) + // and we are suppose to allow requests, then just post an allow event. + if (Preferences::GetBool("notification.prompt.testing", false) && + Preferences::GetBool("notification.prompt.testing.allow", true)) { + mAllow = true; + } +} + +void +DesktopNotification::Init() +{ + RefPtr<DesktopNotificationRequest> request = new DesktopNotificationRequest(this); + + NS_DispatchToMainThread(request); +} + +DesktopNotification::~DesktopNotification() +{ + if (mObserver) { + mObserver->Disconnect(); + } +} + +void +DesktopNotification::DispatchNotificationEvent(const nsString& aName) +{ + if (NS_FAILED(CheckInnerWindowCorrectness())) { + return; + } + + RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr); + // it doesn't bubble, and it isn't cancelable + event->InitEvent(aName, false, false); + event->SetTrusted(true); + DispatchDOMEvent(nullptr, event, nullptr, nullptr); +} + +nsresult +DesktopNotification::SetAllow(bool aAllow) +{ + mAllow = aAllow; + + // if we have called Show() already, lets go ahead and post a notification + if (mShowHasBeenCalled && aAllow) { + return PostDesktopNotification(); + } + + return NS_OK; +} + +void +DesktopNotification::HandleAlertServiceNotification(const char *aTopic) +{ + if (NS_FAILED(CheckInnerWindowCorrectness())) { + return; + } + + if (!strcmp("alertclickcallback", aTopic)) { + DispatchNotificationEvent(NS_LITERAL_STRING("click")); + } else if (!strcmp("alertfinished", aTopic)) { + DispatchNotificationEvent(NS_LITERAL_STRING("close")); + } +} + +void +DesktopNotification::Show(ErrorResult& aRv) +{ + mShowHasBeenCalled = true; + + if (!mAllow) { + return; + } + + aRv = PostDesktopNotification(); +} + +JSObject* +DesktopNotification::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return DesktopNotificationBinding::Wrap(aCx, this, aGivenProto); +} + +/* ------------------------------------------------------------------------ */ +/* DesktopNotificationCenter */ +/* ------------------------------------------------------------------------ */ + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(DesktopNotificationCenter) +NS_IMPL_CYCLE_COLLECTING_ADDREF(DesktopNotificationCenter) +NS_IMPL_CYCLE_COLLECTING_RELEASE(DesktopNotificationCenter) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DesktopNotificationCenter) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +already_AddRefed<DesktopNotification> +DesktopNotificationCenter::CreateNotification(const nsAString& aTitle, + const nsAString& aDescription, + const nsAString& aIconURL) +{ + MOZ_ASSERT(mOwner); + + RefPtr<DesktopNotification> notification = + new DesktopNotification(aTitle, + aDescription, + aIconURL, + mOwner, + mPrincipal); + notification->Init(); + return notification.forget(); +} + +JSObject* +DesktopNotificationCenter::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return DesktopNotificationCenterBinding::Wrap(aCx, this, aGivenProto); +} + +/* ------------------------------------------------------------------------ */ +/* DesktopNotificationRequest */ +/* ------------------------------------------------------------------------ */ + +NS_IMPL_ISUPPORTS_INHERITED(DesktopNotificationRequest, Runnable, + nsIContentPermissionRequest) + +NS_IMETHODIMP +DesktopNotificationRequest::GetPrincipal(nsIPrincipal * *aRequestingPrincipal) +{ + if (!mDesktopNotification) { + return NS_ERROR_NOT_INITIALIZED; + } + + NS_IF_ADDREF(*aRequestingPrincipal = mDesktopNotification->mPrincipal); + return NS_OK; +} + +NS_IMETHODIMP +DesktopNotificationRequest::GetWindow(mozIDOMWindow** aRequestingWindow) +{ + if (!mDesktopNotification) { + return NS_ERROR_NOT_INITIALIZED; + } + + NS_IF_ADDREF(*aRequestingWindow = mDesktopNotification->GetOwner()); + return NS_OK; +} + +NS_IMETHODIMP +DesktopNotificationRequest::GetElement(nsIDOMElement * *aElement) +{ + NS_ENSURE_ARG_POINTER(aElement); + *aElement = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +DesktopNotificationRequest::Cancel() +{ + nsresult rv = mDesktopNotification->SetAllow(false); + mDesktopNotification = nullptr; + return rv; +} + +NS_IMETHODIMP +DesktopNotificationRequest::Allow(JS::HandleValue aChoices) +{ + MOZ_ASSERT(aChoices.isUndefined()); + nsresult rv = mDesktopNotification->SetAllow(true); + mDesktopNotification = nullptr; + return rv; +} + +NS_IMETHODIMP +DesktopNotificationRequest::GetRequester(nsIContentPermissionRequester** aRequester) +{ + NS_ENSURE_ARG_POINTER(aRequester); + + nsCOMPtr<nsIContentPermissionRequester> requester = mRequester; + requester.forget(aRequester); + return NS_OK; +} + +NS_IMETHODIMP +DesktopNotificationRequest::GetTypes(nsIArray** aTypes) +{ + nsTArray<nsString> emptyOptions; + return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"), + NS_LITERAL_CSTRING("unused"), + emptyOptions, + aTypes); +} + +} // namespace dom +} // namespace mozilla |