DesktopNotification.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. /* -*- Mode: C++; tab-width: 8; 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/. */
  5. #include "mozilla/dom/DesktopNotification.h"
  6. #include "mozilla/dom/DesktopNotificationBinding.h"
  7. #include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
  8. #include "mozilla/dom/ToJSValue.h"
  9. #include "nsComponentManagerUtils.h"
  10. #include "nsContentPermissionHelper.h"
  11. #include "nsXULAppAPI.h"
  12. #include "mozilla/dom/PBrowserChild.h"
  13. #include "mozilla/Preferences.h"
  14. #include "nsGlobalWindow.h"
  15. #include "nsIScriptSecurityManager.h"
  16. #include "nsServiceManagerUtils.h"
  17. #include "PermissionMessageUtils.h"
  18. #include "nsILoadContext.h"
  19. namespace mozilla {
  20. namespace dom {
  21. /*
  22. * Simple Request
  23. */
  24. class DesktopNotificationRequest : public nsIContentPermissionRequest
  25. , public Runnable
  26. {
  27. virtual ~DesktopNotificationRequest()
  28. {
  29. }
  30. nsCOMPtr<nsIContentPermissionRequester> mRequester;
  31. public:
  32. NS_DECL_ISUPPORTS_INHERITED
  33. NS_DECL_NSICONTENTPERMISSIONREQUEST
  34. explicit DesktopNotificationRequest(DesktopNotification* aNotification)
  35. : mDesktopNotification(aNotification)
  36. {
  37. mRequester = new nsContentPermissionRequester(mDesktopNotification->GetOwner());
  38. }
  39. NS_IMETHOD Run() override
  40. {
  41. nsCOMPtr<nsPIDOMWindowInner> window = mDesktopNotification->GetOwner();
  42. nsContentPermissionUtils::AskPermission(this, window);
  43. return NS_OK;
  44. }
  45. RefPtr<DesktopNotification> mDesktopNotification;
  46. };
  47. /* ------------------------------------------------------------------------ */
  48. /* AlertServiceObserver */
  49. /* ------------------------------------------------------------------------ */
  50. NS_IMPL_ISUPPORTS(AlertServiceObserver, nsIObserver)
  51. /* ------------------------------------------------------------------------ */
  52. /* DesktopNotification */
  53. /* ------------------------------------------------------------------------ */
  54. uint32_t DesktopNotification::sCount = 0;
  55. nsresult
  56. DesktopNotification::PostDesktopNotification()
  57. {
  58. if (!mObserver) {
  59. mObserver = new AlertServiceObserver(this);
  60. }
  61. nsCOMPtr<nsIAlertsService> alerts = do_GetService("@mozilla.org/alerts-service;1");
  62. if (!alerts) {
  63. return NS_ERROR_NOT_IMPLEMENTED;
  64. }
  65. // Generate a unique name (which will also be used as a cookie) because
  66. // the nsIAlertsService will coalesce notifications with the same name.
  67. // In the case of IPC, the parent process will use the cookie to map
  68. // to nsIObservers, thus cookies must be unique to differentiate observers.
  69. nsString uniqueName = NS_LITERAL_STRING("desktop-notification:");
  70. uniqueName.AppendInt(sCount++);
  71. nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
  72. if (!owner) {
  73. return NS_ERROR_FAILURE;
  74. }
  75. nsCOMPtr<nsIDocument> doc = owner->GetDoc();
  76. nsIPrincipal* principal = doc->NodePrincipal();
  77. nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
  78. bool inPrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
  79. nsCOMPtr<nsIAlertNotification> alert =
  80. do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
  81. NS_ENSURE_TRUE(alert, NS_ERROR_FAILURE);
  82. nsresult rv = alert->Init(uniqueName, mIconURL, mTitle,
  83. mDescription,
  84. true,
  85. uniqueName,
  86. NS_LITERAL_STRING("auto"),
  87. EmptyString(),
  88. EmptyString(),
  89. principal,
  90. inPrivateBrowsing,
  91. false /* requireInteraction */);
  92. NS_ENSURE_SUCCESS(rv, rv);
  93. return alerts->ShowAlert(alert, mObserver);
  94. }
  95. DesktopNotification::DesktopNotification(const nsAString & title,
  96. const nsAString & description,
  97. const nsAString & iconURL,
  98. nsPIDOMWindowInner* aWindow,
  99. nsIPrincipal* principal)
  100. : DOMEventTargetHelper(aWindow)
  101. , mTitle(title)
  102. , mDescription(description)
  103. , mIconURL(iconURL)
  104. , mPrincipal(principal)
  105. , mAllow(false)
  106. , mShowHasBeenCalled(false)
  107. {
  108. if (Preferences::GetBool("notification.disabled", false)) {
  109. return;
  110. }
  111. // If we are in testing mode (running mochitests, for example)
  112. // and we are suppose to allow requests, then just post an allow event.
  113. if (Preferences::GetBool("notification.prompt.testing", false) &&
  114. Preferences::GetBool("notification.prompt.testing.allow", true)) {
  115. mAllow = true;
  116. }
  117. }
  118. void
  119. DesktopNotification::Init()
  120. {
  121. RefPtr<DesktopNotificationRequest> request = new DesktopNotificationRequest(this);
  122. NS_DispatchToMainThread(request);
  123. }
  124. DesktopNotification::~DesktopNotification()
  125. {
  126. if (mObserver) {
  127. mObserver->Disconnect();
  128. }
  129. }
  130. void
  131. DesktopNotification::DispatchNotificationEvent(const nsString& aName)
  132. {
  133. if (NS_FAILED(CheckInnerWindowCorrectness())) {
  134. return;
  135. }
  136. RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
  137. // it doesn't bubble, and it isn't cancelable
  138. event->InitEvent(aName, false, false);
  139. event->SetTrusted(true);
  140. DispatchDOMEvent(nullptr, event, nullptr, nullptr);
  141. }
  142. nsresult
  143. DesktopNotification::SetAllow(bool aAllow)
  144. {
  145. mAllow = aAllow;
  146. // if we have called Show() already, lets go ahead and post a notification
  147. if (mShowHasBeenCalled && aAllow) {
  148. return PostDesktopNotification();
  149. }
  150. return NS_OK;
  151. }
  152. void
  153. DesktopNotification::HandleAlertServiceNotification(const char *aTopic)
  154. {
  155. if (NS_FAILED(CheckInnerWindowCorrectness())) {
  156. return;
  157. }
  158. if (!strcmp("alertclickcallback", aTopic)) {
  159. DispatchNotificationEvent(NS_LITERAL_STRING("click"));
  160. } else if (!strcmp("alertfinished", aTopic)) {
  161. DispatchNotificationEvent(NS_LITERAL_STRING("close"));
  162. }
  163. }
  164. void
  165. DesktopNotification::Show(ErrorResult& aRv)
  166. {
  167. mShowHasBeenCalled = true;
  168. if (!mAllow) {
  169. return;
  170. }
  171. aRv = PostDesktopNotification();
  172. }
  173. JSObject*
  174. DesktopNotification::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
  175. {
  176. return DesktopNotificationBinding::Wrap(aCx, this, aGivenProto);
  177. }
  178. /* ------------------------------------------------------------------------ */
  179. /* DesktopNotificationCenter */
  180. /* ------------------------------------------------------------------------ */
  181. NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(DesktopNotificationCenter)
  182. NS_IMPL_CYCLE_COLLECTING_ADDREF(DesktopNotificationCenter)
  183. NS_IMPL_CYCLE_COLLECTING_RELEASE(DesktopNotificationCenter)
  184. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DesktopNotificationCenter)
  185. NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  186. NS_INTERFACE_MAP_ENTRY(nsISupports)
  187. NS_INTERFACE_MAP_END
  188. already_AddRefed<DesktopNotification>
  189. DesktopNotificationCenter::CreateNotification(const nsAString& aTitle,
  190. const nsAString& aDescription,
  191. const nsAString& aIconURL)
  192. {
  193. MOZ_ASSERT(mOwner);
  194. RefPtr<DesktopNotification> notification =
  195. new DesktopNotification(aTitle,
  196. aDescription,
  197. aIconURL,
  198. mOwner,
  199. mPrincipal);
  200. notification->Init();
  201. return notification.forget();
  202. }
  203. JSObject*
  204. DesktopNotificationCenter::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
  205. {
  206. return DesktopNotificationCenterBinding::Wrap(aCx, this, aGivenProto);
  207. }
  208. /* ------------------------------------------------------------------------ */
  209. /* DesktopNotificationRequest */
  210. /* ------------------------------------------------------------------------ */
  211. NS_IMPL_ISUPPORTS_INHERITED(DesktopNotificationRequest, Runnable,
  212. nsIContentPermissionRequest)
  213. NS_IMETHODIMP
  214. DesktopNotificationRequest::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
  215. {
  216. if (!mDesktopNotification) {
  217. return NS_ERROR_NOT_INITIALIZED;
  218. }
  219. NS_IF_ADDREF(*aRequestingPrincipal = mDesktopNotification->mPrincipal);
  220. return NS_OK;
  221. }
  222. NS_IMETHODIMP
  223. DesktopNotificationRequest::GetWindow(mozIDOMWindow** aRequestingWindow)
  224. {
  225. if (!mDesktopNotification) {
  226. return NS_ERROR_NOT_INITIALIZED;
  227. }
  228. NS_IF_ADDREF(*aRequestingWindow = mDesktopNotification->GetOwner());
  229. return NS_OK;
  230. }
  231. NS_IMETHODIMP
  232. DesktopNotificationRequest::GetElement(nsIDOMElement * *aElement)
  233. {
  234. NS_ENSURE_ARG_POINTER(aElement);
  235. *aElement = nullptr;
  236. return NS_OK;
  237. }
  238. NS_IMETHODIMP
  239. DesktopNotificationRequest::Cancel()
  240. {
  241. nsresult rv = mDesktopNotification->SetAllow(false);
  242. mDesktopNotification = nullptr;
  243. return rv;
  244. }
  245. NS_IMETHODIMP
  246. DesktopNotificationRequest::Allow(JS::HandleValue aChoices)
  247. {
  248. MOZ_ASSERT(aChoices.isUndefined());
  249. nsresult rv = mDesktopNotification->SetAllow(true);
  250. mDesktopNotification = nullptr;
  251. return rv;
  252. }
  253. NS_IMETHODIMP
  254. DesktopNotificationRequest::GetRequester(nsIContentPermissionRequester** aRequester)
  255. {
  256. NS_ENSURE_ARG_POINTER(aRequester);
  257. nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
  258. requester.forget(aRequester);
  259. return NS_OK;
  260. }
  261. NS_IMETHODIMP
  262. DesktopNotificationRequest::GetTypes(nsIArray** aTypes)
  263. {
  264. nsTArray<nsString> emptyOptions;
  265. return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
  266. NS_LITERAL_CSTRING("unused"),
  267. emptyOptions,
  268. aTypes);
  269. }
  270. } // namespace dom
  271. } // namespace mozilla