Notification.cpp 75 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557
  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/Notification.h"
  6. #include "mozilla/JSONWriter.h"
  7. #include "mozilla/Move.h"
  8. #include "mozilla/OwningNonNull.h"
  9. #include "mozilla/Preferences.h"
  10. #include "mozilla/Services.h"
  11. #include "mozilla/Unused.h"
  12. #include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
  13. #include "mozilla/dom/BindingUtils.h"
  14. #include "mozilla/dom/ContentChild.h"
  15. #include "mozilla/dom/NotificationEvent.h"
  16. #include "mozilla/dom/PermissionMessageUtils.h"
  17. #include "mozilla/dom/Promise.h"
  18. #include "mozilla/dom/PromiseWorkerProxy.h"
  19. #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
  20. #include "nsAlertsUtils.h"
  21. #include "nsComponentManagerUtils.h"
  22. #include "nsContentPermissionHelper.h"
  23. #include "nsContentUtils.h"
  24. #include "nsCRTGlue.h"
  25. #include "nsDOMJSUtils.h"
  26. #include "nsGlobalWindow.h"
  27. #include "nsIAlertsService.h"
  28. #include "nsIContentPermissionPrompt.h"
  29. #include "nsIDocument.h"
  30. #include "nsILoadContext.h"
  31. #include "nsINotificationStorage.h"
  32. #include "nsIPermissionManager.h"
  33. #include "nsIPermission.h"
  34. #include "nsIPushService.h"
  35. #include "nsIScriptSecurityManager.h"
  36. #include "nsIServiceWorkerManager.h"
  37. #include "nsISimpleEnumerator.h"
  38. #include "nsIUUIDGenerator.h"
  39. #include "nsIXPConnect.h"
  40. #include "nsNetUtil.h"
  41. #include "nsProxyRelease.h"
  42. #include "nsServiceManagerUtils.h"
  43. #include "nsStructuredCloneContainer.h"
  44. #include "nsThreadUtils.h"
  45. #include "nsToolkitCompsCID.h"
  46. #include "nsXULAppAPI.h"
  47. #include "ServiceWorkerManager.h"
  48. #include "WorkerPrivate.h"
  49. #include "WorkerRunnable.h"
  50. #include "WorkerScope.h"
  51. namespace mozilla {
  52. namespace dom {
  53. using namespace workers;
  54. struct NotificationStrings
  55. {
  56. const nsString mID;
  57. const nsString mTitle;
  58. const nsString mDir;
  59. const nsString mLang;
  60. const nsString mBody;
  61. const nsString mTag;
  62. const nsString mIcon;
  63. const nsString mData;
  64. const nsString mBehavior;
  65. const nsString mServiceWorkerRegistrationScope;
  66. };
  67. class ScopeCheckingGetCallback : public nsINotificationStorageCallback
  68. {
  69. const nsString mScope;
  70. public:
  71. explicit ScopeCheckingGetCallback(const nsAString& aScope)
  72. : mScope(aScope)
  73. {}
  74. NS_IMETHOD Handle(const nsAString& aID,
  75. const nsAString& aTitle,
  76. const nsAString& aDir,
  77. const nsAString& aLang,
  78. const nsAString& aBody,
  79. const nsAString& aTag,
  80. const nsAString& aIcon,
  81. const nsAString& aData,
  82. const nsAString& aBehavior,
  83. const nsAString& aServiceWorkerRegistrationScope) final
  84. {
  85. AssertIsOnMainThread();
  86. MOZ_ASSERT(!aID.IsEmpty());
  87. // Skip scopes that don't match when called from getNotifications().
  88. if (!mScope.IsEmpty() && !mScope.Equals(aServiceWorkerRegistrationScope)) {
  89. return NS_OK;
  90. }
  91. NotificationStrings strings = {
  92. nsString(aID),
  93. nsString(aTitle),
  94. nsString(aDir),
  95. nsString(aLang),
  96. nsString(aBody),
  97. nsString(aTag),
  98. nsString(aIcon),
  99. nsString(aData),
  100. nsString(aBehavior),
  101. nsString(aServiceWorkerRegistrationScope),
  102. };
  103. mStrings.AppendElement(Move(strings));
  104. return NS_OK;
  105. }
  106. NS_IMETHOD Done() override = 0;
  107. protected:
  108. virtual ~ScopeCheckingGetCallback()
  109. {}
  110. nsTArray<NotificationStrings> mStrings;
  111. };
  112. class NotificationStorageCallback final : public ScopeCheckingGetCallback
  113. {
  114. public:
  115. NS_DECL_CYCLE_COLLECTING_ISUPPORTS
  116. NS_DECL_CYCLE_COLLECTION_CLASS(NotificationStorageCallback)
  117. NotificationStorageCallback(nsIGlobalObject* aWindow, const nsAString& aScope,
  118. Promise* aPromise)
  119. : ScopeCheckingGetCallback(aScope),
  120. mWindow(aWindow),
  121. mPromise(aPromise)
  122. {
  123. AssertIsOnMainThread();
  124. MOZ_ASSERT(aWindow);
  125. MOZ_ASSERT(aPromise);
  126. }
  127. NS_IMETHOD Done() final
  128. {
  129. ErrorResult result;
  130. AutoTArray<RefPtr<Notification>, 5> notifications;
  131. for (uint32_t i = 0; i < mStrings.Length(); ++i) {
  132. RefPtr<Notification> n =
  133. Notification::ConstructFromFields(mWindow,
  134. mStrings[i].mID,
  135. mStrings[i].mTitle,
  136. mStrings[i].mDir,
  137. mStrings[i].mLang,
  138. mStrings[i].mBody,
  139. mStrings[i].mTag,
  140. mStrings[i].mIcon,
  141. mStrings[i].mData,
  142. /* mStrings[i].mBehavior, not
  143. * supported */
  144. mStrings[i].mServiceWorkerRegistrationScope,
  145. result);
  146. n->SetStoredState(true);
  147. Unused << NS_WARN_IF(result.Failed());
  148. if (!result.Failed()) {
  149. notifications.AppendElement(n.forget());
  150. }
  151. }
  152. mPromise->MaybeResolve(notifications);
  153. return NS_OK;
  154. }
  155. private:
  156. virtual ~NotificationStorageCallback()
  157. {}
  158. nsCOMPtr<nsIGlobalObject> mWindow;
  159. RefPtr<Promise> mPromise;
  160. const nsString mScope;
  161. };
  162. NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationStorageCallback)
  163. NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationStorageCallback)
  164. NS_IMPL_CYCLE_COLLECTION(NotificationStorageCallback, mWindow, mPromise);
  165. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationStorageCallback)
  166. NS_INTERFACE_MAP_ENTRY(nsINotificationStorageCallback)
  167. NS_INTERFACE_MAP_ENTRY(nsISupports)
  168. NS_INTERFACE_MAP_END
  169. class NotificationGetRunnable final : public Runnable
  170. {
  171. const nsString mOrigin;
  172. const nsString mTag;
  173. nsCOMPtr<nsINotificationStorageCallback> mCallback;
  174. public:
  175. NotificationGetRunnable(const nsAString& aOrigin,
  176. const nsAString& aTag,
  177. nsINotificationStorageCallback* aCallback)
  178. : mOrigin(aOrigin), mTag(aTag), mCallback(aCallback)
  179. {}
  180. NS_IMETHOD
  181. Run() override
  182. {
  183. nsresult rv;
  184. nsCOMPtr<nsINotificationStorage> notificationStorage =
  185. do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
  186. if (NS_WARN_IF(NS_FAILED(rv))) {
  187. return rv;
  188. }
  189. rv = notificationStorage->Get(mOrigin, mTag, mCallback);
  190. //XXXnsm Is it guaranteed mCallback will be called in case of failure?
  191. Unused << NS_WARN_IF(NS_FAILED(rv));
  192. return rv;
  193. }
  194. };
  195. class NotificationPermissionRequest : public nsIContentPermissionRequest,
  196. public nsIRunnable
  197. {
  198. public:
  199. NS_DECL_CYCLE_COLLECTING_ISUPPORTS
  200. NS_DECL_NSICONTENTPERMISSIONREQUEST
  201. NS_DECL_NSIRUNNABLE
  202. NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(NotificationPermissionRequest,
  203. nsIContentPermissionRequest)
  204. NotificationPermissionRequest(nsIPrincipal* aPrincipal,
  205. nsPIDOMWindowInner* aWindow, Promise* aPromise,
  206. NotificationPermissionCallback* aCallback)
  207. : mPrincipal(aPrincipal), mWindow(aWindow),
  208. mPermission(NotificationPermission::Default),
  209. mPromise(aPromise),
  210. mCallback(aCallback)
  211. {
  212. MOZ_ASSERT(aPromise);
  213. mRequester = new nsContentPermissionRequester(mWindow);
  214. }
  215. protected:
  216. virtual ~NotificationPermissionRequest() {}
  217. nsresult ResolvePromise();
  218. nsresult DispatchResolvePromise();
  219. nsCOMPtr<nsIPrincipal> mPrincipal;
  220. nsCOMPtr<nsPIDOMWindowInner> mWindow;
  221. NotificationPermission mPermission;
  222. RefPtr<Promise> mPromise;
  223. RefPtr<NotificationPermissionCallback> mCallback;
  224. nsCOMPtr<nsIContentPermissionRequester> mRequester;
  225. };
  226. namespace {
  227. class ReleaseNotificationControlRunnable final : public MainThreadWorkerControlRunnable
  228. {
  229. Notification* mNotification;
  230. public:
  231. explicit ReleaseNotificationControlRunnable(Notification* aNotification)
  232. : MainThreadWorkerControlRunnable(aNotification->mWorkerPrivate)
  233. , mNotification(aNotification)
  234. { }
  235. bool
  236. WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
  237. {
  238. mNotification->ReleaseObject();
  239. return true;
  240. }
  241. };
  242. class GetPermissionRunnable final : public WorkerMainThreadRunnable
  243. {
  244. NotificationPermission mPermission;
  245. public:
  246. explicit GetPermissionRunnable(WorkerPrivate* aWorker)
  247. : WorkerMainThreadRunnable(aWorker,
  248. NS_LITERAL_CSTRING("Notification :: Get Permission"))
  249. , mPermission(NotificationPermission::Denied)
  250. { }
  251. bool
  252. MainThreadRun() override
  253. {
  254. ErrorResult result;
  255. mPermission =
  256. Notification::GetPermissionInternal(mWorkerPrivate->GetPrincipal(),
  257. result);
  258. return true;
  259. }
  260. NotificationPermission
  261. GetPermission()
  262. {
  263. return mPermission;
  264. }
  265. };
  266. class FocusWindowRunnable final : public Runnable
  267. {
  268. nsMainThreadPtrHandle<nsPIDOMWindowInner> mWindow;
  269. public:
  270. explicit FocusWindowRunnable(const nsMainThreadPtrHandle<nsPIDOMWindowInner>& aWindow)
  271. : mWindow(aWindow)
  272. { }
  273. NS_IMETHOD
  274. Run() override
  275. {
  276. AssertIsOnMainThread();
  277. if (!mWindow->IsCurrentInnerWindow()) {
  278. // Window has been closed, this observer is not valid anymore
  279. return NS_OK;
  280. }
  281. nsIDocument* doc = mWindow->GetExtantDoc();
  282. if (doc) {
  283. // Browser UI may use DOMWebNotificationClicked to focus the tab
  284. // from which the event was dispatched.
  285. nsContentUtils::DispatchChromeEvent(doc, mWindow->GetOuterWindow(),
  286. NS_LITERAL_STRING("DOMWebNotificationClicked"),
  287. true, true);
  288. }
  289. return NS_OK;
  290. }
  291. };
  292. nsresult
  293. CheckScope(nsIPrincipal* aPrincipal, const nsACString& aScope)
  294. {
  295. AssertIsOnMainThread();
  296. MOZ_ASSERT(aPrincipal);
  297. nsCOMPtr<nsIURI> scopeURI;
  298. nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
  299. if (NS_WARN_IF(NS_FAILED(rv))) {
  300. return rv;
  301. }
  302. return aPrincipal->CheckMayLoad(scopeURI, /* report = */ true,
  303. /* allowIfInheritsPrincipal = */ false);
  304. }
  305. } // anonymous namespace
  306. // Subclass that can be directly dispatched to child workers from the main
  307. // thread.
  308. class NotificationWorkerRunnable : public MainThreadWorkerRunnable
  309. {
  310. protected:
  311. explicit NotificationWorkerRunnable(WorkerPrivate* aWorkerPrivate)
  312. : MainThreadWorkerRunnable(aWorkerPrivate)
  313. {
  314. }
  315. bool
  316. WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
  317. {
  318. aWorkerPrivate->AssertIsOnWorkerThread();
  319. aWorkerPrivate->ModifyBusyCountFromWorker(true);
  320. WorkerRunInternal(aWorkerPrivate);
  321. return true;
  322. }
  323. void
  324. PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
  325. bool aRunResult) override
  326. {
  327. aWorkerPrivate->ModifyBusyCountFromWorker(false);
  328. }
  329. virtual void
  330. WorkerRunInternal(WorkerPrivate* aWorkerPrivate) = 0;
  331. };
  332. // Overrides dispatch and run handlers so we can directly dispatch from main
  333. // thread to child workers.
  334. class NotificationEventWorkerRunnable final : public NotificationWorkerRunnable
  335. {
  336. Notification* mNotification;
  337. const nsString mEventName;
  338. public:
  339. NotificationEventWorkerRunnable(Notification* aNotification,
  340. const nsString& aEventName)
  341. : NotificationWorkerRunnable(aNotification->mWorkerPrivate)
  342. , mNotification(aNotification)
  343. , mEventName(aEventName)
  344. {}
  345. void
  346. WorkerRunInternal(WorkerPrivate* aWorkerPrivate) override
  347. {
  348. mNotification->DispatchTrustedEvent(mEventName);
  349. }
  350. };
  351. class ReleaseNotificationRunnable final : public NotificationWorkerRunnable
  352. {
  353. Notification* mNotification;
  354. public:
  355. explicit ReleaseNotificationRunnable(Notification* aNotification)
  356. : NotificationWorkerRunnable(aNotification->mWorkerPrivate)
  357. , mNotification(aNotification)
  358. {}
  359. void
  360. WorkerRunInternal(WorkerPrivate* aWorkerPrivate) override
  361. {
  362. mNotification->ReleaseObject();
  363. }
  364. };
  365. // Create one whenever you require ownership of the notification. Use with
  366. // UniquePtr<>. See Notification.h for details.
  367. class NotificationRef final {
  368. friend class WorkerNotificationObserver;
  369. private:
  370. Notification* mNotification;
  371. bool mInited;
  372. // Only useful for workers.
  373. void
  374. Forget()
  375. {
  376. mNotification = nullptr;
  377. }
  378. public:
  379. explicit NotificationRef(Notification* aNotification)
  380. : mNotification(aNotification)
  381. {
  382. MOZ_ASSERT(mNotification);
  383. if (mNotification->mWorkerPrivate) {
  384. mNotification->mWorkerPrivate->AssertIsOnWorkerThread();
  385. } else {
  386. AssertIsOnMainThread();
  387. }
  388. mInited = mNotification->AddRefObject();
  389. }
  390. // This is only required because Gecko runs script in a worker's onclose
  391. // handler (non-standard, Bug 790919) where calls to HoldWorker() will
  392. // fail. Due to non-standardness and added complications if we decide to
  393. // support this, attempts to create a Notification in onclose just throw
  394. // exceptions.
  395. bool
  396. Initialized()
  397. {
  398. return mInited;
  399. }
  400. ~NotificationRef()
  401. {
  402. if (Initialized() && mNotification) {
  403. Notification* notification = mNotification;
  404. mNotification = nullptr;
  405. if (notification->mWorkerPrivate && NS_IsMainThread()) {
  406. // Try to pass ownership back to the worker. If the dispatch succeeds we
  407. // are guaranteed this runnable will run, and that it will run after queued
  408. // event runnables, so event runnables will have a safe pointer to the
  409. // Notification.
  410. //
  411. // If the dispatch fails, the worker isn't running anymore and the event
  412. // runnables have already run or been canceled. We can use a control
  413. // runnable to release the reference.
  414. RefPtr<ReleaseNotificationRunnable> r =
  415. new ReleaseNotificationRunnable(notification);
  416. if (!r->Dispatch()) {
  417. RefPtr<ReleaseNotificationControlRunnable> r =
  418. new ReleaseNotificationControlRunnable(notification);
  419. MOZ_ALWAYS_TRUE(r->Dispatch());
  420. }
  421. } else {
  422. notification->AssertIsOnTargetThread();
  423. notification->ReleaseObject();
  424. }
  425. }
  426. }
  427. // XXXnsm, is it worth having some sort of WeakPtr like wrapper instead of
  428. // a rawptr that the NotificationRef can invalidate?
  429. Notification*
  430. GetNotification()
  431. {
  432. MOZ_ASSERT(Initialized());
  433. return mNotification;
  434. }
  435. };
  436. class NotificationTask : public Runnable
  437. {
  438. public:
  439. enum NotificationAction {
  440. eShow,
  441. eClose
  442. };
  443. NotificationTask(UniquePtr<NotificationRef> aRef, NotificationAction aAction)
  444. : mNotificationRef(Move(aRef)), mAction(aAction)
  445. {}
  446. NS_IMETHOD
  447. Run() override;
  448. protected:
  449. virtual ~NotificationTask() {}
  450. UniquePtr<NotificationRef> mNotificationRef;
  451. NotificationAction mAction;
  452. };
  453. uint32_t Notification::sCount = 0;
  454. NS_IMPL_CYCLE_COLLECTION(NotificationPermissionRequest, mWindow, mPromise,
  455. mCallback)
  456. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationPermissionRequest)
  457. NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
  458. NS_INTERFACE_MAP_ENTRY(nsIRunnable)
  459. NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
  460. NS_INTERFACE_MAP_END
  461. NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationPermissionRequest)
  462. NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationPermissionRequest)
  463. NS_IMETHODIMP
  464. NotificationPermissionRequest::Run()
  465. {
  466. if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
  467. mPermission = NotificationPermission::Granted;
  468. } else {
  469. // File are automatically granted permission.
  470. nsCOMPtr<nsIURI> uri;
  471. mPrincipal->GetURI(getter_AddRefs(uri));
  472. if (uri) {
  473. bool isFile;
  474. uri->SchemeIs("file", &isFile);
  475. if (isFile) {
  476. mPermission = NotificationPermission::Granted;
  477. }
  478. }
  479. }
  480. // Grant permission if pref'ed on.
  481. if (Preferences::GetBool("notification.prompt.testing", false)) {
  482. if (Preferences::GetBool("notification.prompt.testing.allow", true)) {
  483. mPermission = NotificationPermission::Granted;
  484. } else {
  485. mPermission = NotificationPermission::Denied;
  486. }
  487. }
  488. if (mPermission != NotificationPermission::Default) {
  489. return DispatchResolvePromise();
  490. }
  491. return nsContentPermissionUtils::AskPermission(this, mWindow);
  492. }
  493. NS_IMETHODIMP
  494. NotificationPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
  495. {
  496. NS_ADDREF(*aRequestingPrincipal = mPrincipal);
  497. return NS_OK;
  498. }
  499. NS_IMETHODIMP
  500. NotificationPermissionRequest::GetWindow(mozIDOMWindow** aRequestingWindow)
  501. {
  502. NS_ADDREF(*aRequestingWindow = mWindow);
  503. return NS_OK;
  504. }
  505. NS_IMETHODIMP
  506. NotificationPermissionRequest::GetElement(nsIDOMElement** aElement)
  507. {
  508. NS_ENSURE_ARG_POINTER(aElement);
  509. *aElement = nullptr;
  510. return NS_OK;
  511. }
  512. NS_IMETHODIMP
  513. NotificationPermissionRequest::Cancel()
  514. {
  515. // `Cancel` is called if the user denied permission or dismissed the
  516. // permission request. To distinguish between the two, we set the
  517. // permission to "default" and query the permission manager in
  518. // `ResolvePromise`.
  519. mPermission = NotificationPermission::Default;
  520. return DispatchResolvePromise();
  521. }
  522. NS_IMETHODIMP
  523. NotificationPermissionRequest::Allow(JS::HandleValue aChoices)
  524. {
  525. MOZ_ASSERT(aChoices.isUndefined());
  526. mPermission = NotificationPermission::Granted;
  527. return DispatchResolvePromise();
  528. }
  529. NS_IMETHODIMP
  530. NotificationPermissionRequest::GetRequester(nsIContentPermissionRequester** aRequester)
  531. {
  532. NS_ENSURE_ARG_POINTER(aRequester);
  533. nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
  534. requester.forget(aRequester);
  535. return NS_OK;
  536. }
  537. inline nsresult
  538. NotificationPermissionRequest::DispatchResolvePromise()
  539. {
  540. return NS_DispatchToMainThread(NewRunnableMethod(this,
  541. &NotificationPermissionRequest::ResolvePromise));
  542. }
  543. nsresult
  544. NotificationPermissionRequest::ResolvePromise()
  545. {
  546. nsresult rv = NS_OK;
  547. if (mPermission == NotificationPermission::Default) {
  548. // This will still be "default" if the user dismissed the doorhanger,
  549. // or "denied" otherwise.
  550. mPermission = Notification::TestPermission(mPrincipal);
  551. }
  552. if (mCallback) {
  553. ErrorResult error;
  554. mCallback->Call(mPermission, error);
  555. rv = error.StealNSResult();
  556. }
  557. mPromise->MaybeResolve(mPermission);
  558. return rv;
  559. }
  560. NS_IMETHODIMP
  561. NotificationPermissionRequest::GetTypes(nsIArray** aTypes)
  562. {
  563. nsTArray<nsString> emptyOptions;
  564. return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
  565. NS_LITERAL_CSTRING("unused"),
  566. emptyOptions,
  567. aTypes);
  568. }
  569. // Observer that the alert service calls to do common tasks and/or dispatch to the
  570. // specific observer for the context e.g. main thread, worker, or service worker.
  571. class NotificationObserver final : public nsIObserver
  572. {
  573. public:
  574. nsCOMPtr<nsIObserver> mObserver;
  575. nsCOMPtr<nsIPrincipal> mPrincipal;
  576. bool mInPrivateBrowsing;
  577. NS_DECL_ISUPPORTS
  578. NS_DECL_NSIOBSERVER
  579. NotificationObserver(nsIObserver* aObserver, nsIPrincipal* aPrincipal,
  580. bool aInPrivateBrowsing)
  581. : mObserver(aObserver), mPrincipal(aPrincipal),
  582. mInPrivateBrowsing(aInPrivateBrowsing)
  583. {
  584. AssertIsOnMainThread();
  585. MOZ_ASSERT(mObserver);
  586. MOZ_ASSERT(mPrincipal);
  587. }
  588. protected:
  589. virtual ~NotificationObserver()
  590. {
  591. AssertIsOnMainThread();
  592. }
  593. nsresult AdjustPushQuota(const char* aTopic);
  594. };
  595. NS_IMPL_ISUPPORTS(NotificationObserver, nsIObserver)
  596. class MainThreadNotificationObserver : public nsIObserver
  597. {
  598. public:
  599. UniquePtr<NotificationRef> mNotificationRef;
  600. NS_DECL_ISUPPORTS
  601. NS_DECL_NSIOBSERVER
  602. explicit MainThreadNotificationObserver(UniquePtr<NotificationRef> aRef)
  603. : mNotificationRef(Move(aRef))
  604. {
  605. AssertIsOnMainThread();
  606. }
  607. protected:
  608. virtual ~MainThreadNotificationObserver()
  609. {
  610. AssertIsOnMainThread();
  611. }
  612. };
  613. NS_IMPL_ISUPPORTS(MainThreadNotificationObserver, nsIObserver)
  614. NS_IMETHODIMP
  615. NotificationTask::Run()
  616. {
  617. AssertIsOnMainThread();
  618. // Get a pointer to notification before the notification takes ownership of
  619. // the ref (it owns itself temporarily, with ShowInternal() and
  620. // CloseInternal() passing on the ownership appropriately.)
  621. Notification* notif = mNotificationRef->GetNotification();
  622. notif->mTempRef.swap(mNotificationRef);
  623. if (mAction == eShow) {
  624. notif->ShowInternal();
  625. } else if (mAction == eClose) {
  626. notif->CloseInternal();
  627. } else {
  628. MOZ_CRASH("Invalid action");
  629. }
  630. MOZ_ASSERT(!mNotificationRef);
  631. return NS_OK;
  632. }
  633. bool
  634. Notification::RequireInteractionEnabled(JSContext* aCx, JSObject* aOjb)
  635. {
  636. if (NS_IsMainThread()) {
  637. return Preferences::GetBool("dom.webnotifications.requireinteraction.enabled", false);
  638. }
  639. WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
  640. if (!workerPrivate) {
  641. return false;
  642. }
  643. return workerPrivate->DOMWorkerNotificationRIEnabled();
  644. }
  645. // static
  646. bool
  647. Notification::PrefEnabled(JSContext* aCx, JSObject* aObj)
  648. {
  649. if (NS_IsMainThread()) {
  650. return Preferences::GetBool("dom.webnotifications.enabled", false);
  651. }
  652. WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
  653. if (!workerPrivate) {
  654. return false;
  655. }
  656. if (workerPrivate->IsServiceWorker()) {
  657. return workerPrivate->DOMServiceWorkerNotificationEnabled();
  658. }
  659. return workerPrivate->DOMWorkerNotificationEnabled();
  660. }
  661. // static
  662. bool
  663. Notification::IsGetEnabled(JSContext* aCx, JSObject* aObj)
  664. {
  665. return NS_IsMainThread();
  666. }
  667. Notification::Notification(nsIGlobalObject* aGlobal, const nsAString& aID,
  668. const nsAString& aTitle, const nsAString& aBody,
  669. NotificationDirection aDir, const nsAString& aLang,
  670. const nsAString& aTag, const nsAString& aIconUrl,
  671. bool aRequireInteraction,
  672. const NotificationBehavior& aBehavior)
  673. : DOMEventTargetHelper(),
  674. mWorkerPrivate(nullptr), mObserver(nullptr),
  675. mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang),
  676. mTag(aTag), mIconUrl(aIconUrl), mRequireInteraction(aRequireInteraction),
  677. mBehavior(aBehavior), mData(JS::NullValue()),
  678. mIsClosed(false), mIsStored(false), mTaskCount(0)
  679. {
  680. if (NS_IsMainThread()) {
  681. // We can only call this on the main thread because
  682. // Event::SetEventType() called down the call chain when dispatching events
  683. // using DOMEventTargetHelper::DispatchTrustedEvent() will assume the event
  684. // is a main thread event if it has a valid owner. It will then attempt to
  685. // fetch the atom for the event name which asserts main thread only.
  686. BindToOwner(aGlobal);
  687. } else {
  688. mWorkerPrivate = GetCurrentThreadWorkerPrivate();
  689. MOZ_ASSERT(mWorkerPrivate);
  690. }
  691. }
  692. nsresult
  693. Notification::Init()
  694. {
  695. if (!mWorkerPrivate) {
  696. nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  697. NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
  698. nsresult rv = obs->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, true);
  699. NS_ENSURE_SUCCESS(rv, rv);
  700. rv = obs->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC, true);
  701. NS_ENSURE_SUCCESS(rv, rv);
  702. }
  703. return NS_OK;
  704. }
  705. void
  706. Notification::SetAlertName()
  707. {
  708. AssertIsOnMainThread();
  709. if (!mAlertName.IsEmpty()) {
  710. return;
  711. }
  712. nsAutoString alertName;
  713. nsresult rv = GetOrigin(GetPrincipal(), alertName);
  714. if (NS_WARN_IF(NS_FAILED(rv))) {
  715. return;
  716. }
  717. // Get the notification name that is unique per origin + tag/ID.
  718. // The name of the alert is of the form origin#tag/ID.
  719. alertName.Append('#');
  720. if (!mTag.IsEmpty()) {
  721. alertName.AppendLiteral("tag:");
  722. alertName.Append(mTag);
  723. } else {
  724. alertName.AppendLiteral("notag:");
  725. alertName.Append(mID);
  726. }
  727. mAlertName = alertName;
  728. }
  729. // May be called on any thread.
  730. // static
  731. already_AddRefed<Notification>
  732. Notification::Constructor(const GlobalObject& aGlobal,
  733. const nsAString& aTitle,
  734. const NotificationOptions& aOptions,
  735. ErrorResult& aRv)
  736. {
  737. // FIXME(nsm): If the sticky flag is set, throw an error.
  738. RefPtr<ServiceWorkerGlobalScope> scope;
  739. UNWRAP_OBJECT(ServiceWorkerGlobalScope, aGlobal.Get(), scope);
  740. if (scope) {
  741. aRv.ThrowTypeError<MSG_NOTIFICATION_NO_CONSTRUCTOR_IN_SERVICEWORKER>();
  742. return nullptr;
  743. }
  744. nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
  745. RefPtr<Notification> notification =
  746. CreateAndShow(aGlobal.Context(), global, aTitle, aOptions,
  747. EmptyString(), aRv);
  748. if (NS_WARN_IF(aRv.Failed())) {
  749. return nullptr;
  750. }
  751. // This is be ok since we are on the worker thread where this function will
  752. // run to completion before the Notification has a chance to go away.
  753. return notification.forget();
  754. }
  755. // static
  756. already_AddRefed<Notification>
  757. Notification::ConstructFromFields(
  758. nsIGlobalObject* aGlobal,
  759. const nsAString& aID,
  760. const nsAString& aTitle,
  761. const nsAString& aDir,
  762. const nsAString& aLang,
  763. const nsAString& aBody,
  764. const nsAString& aTag,
  765. const nsAString& aIcon,
  766. const nsAString& aData,
  767. const nsAString& aServiceWorkerRegistrationScope,
  768. ErrorResult& aRv)
  769. {
  770. MOZ_ASSERT(aGlobal);
  771. RootedDictionary<NotificationOptions> options(RootingCx());
  772. options.mDir = Notification::StringToDirection(nsString(aDir));
  773. options.mLang = aLang;
  774. options.mBody = aBody;
  775. options.mTag = aTag;
  776. options.mIcon = aIcon;
  777. RefPtr<Notification> notification = CreateInternal(aGlobal, aID, aTitle,
  778. options);
  779. notification->InitFromBase64(aData, aRv);
  780. if (NS_WARN_IF(aRv.Failed())) {
  781. return nullptr;
  782. }
  783. notification->SetScope(aServiceWorkerRegistrationScope);
  784. return notification.forget();
  785. }
  786. nsresult
  787. Notification::PersistNotification()
  788. {
  789. AssertIsOnMainThread();
  790. nsresult rv;
  791. nsCOMPtr<nsINotificationStorage> notificationStorage =
  792. do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
  793. if (NS_FAILED(rv)) {
  794. return rv;
  795. }
  796. nsString origin;
  797. rv = GetOrigin(GetPrincipal(), origin);
  798. if (NS_WARN_IF(NS_FAILED(rv))) {
  799. return rv;
  800. }
  801. nsString id;
  802. GetID(id);
  803. nsString alertName;
  804. GetAlertName(alertName);
  805. nsAutoString behavior;
  806. if (!mBehavior.ToJSON(behavior)) {
  807. return NS_ERROR_FAILURE;
  808. }
  809. rv = notificationStorage->Put(origin,
  810. id,
  811. mTitle,
  812. DirectionToString(mDir),
  813. mLang,
  814. mBody,
  815. mTag,
  816. mIconUrl,
  817. alertName,
  818. mDataAsBase64,
  819. behavior,
  820. mScope);
  821. if (NS_FAILED(rv)) {
  822. return rv;
  823. }
  824. SetStoredState(true);
  825. return NS_OK;
  826. }
  827. void
  828. Notification::UnpersistNotification()
  829. {
  830. AssertIsOnMainThread();
  831. if (IsStored()) {
  832. nsCOMPtr<nsINotificationStorage> notificationStorage =
  833. do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID);
  834. if (notificationStorage) {
  835. nsString origin;
  836. nsresult rv = GetOrigin(GetPrincipal(), origin);
  837. if (NS_SUCCEEDED(rv)) {
  838. notificationStorage->Delete(origin, mID);
  839. }
  840. }
  841. SetStoredState(false);
  842. }
  843. }
  844. already_AddRefed<Notification>
  845. Notification::CreateInternal(nsIGlobalObject* aGlobal,
  846. const nsAString& aID,
  847. const nsAString& aTitle,
  848. const NotificationOptions& aOptions)
  849. {
  850. nsresult rv;
  851. nsString id;
  852. if (!aID.IsEmpty()) {
  853. id = aID;
  854. } else {
  855. nsCOMPtr<nsIUUIDGenerator> uuidgen =
  856. do_GetService("@mozilla.org/uuid-generator;1");
  857. NS_ENSURE_TRUE(uuidgen, nullptr);
  858. nsID uuid;
  859. rv = uuidgen->GenerateUUIDInPlace(&uuid);
  860. NS_ENSURE_SUCCESS(rv, nullptr);
  861. char buffer[NSID_LENGTH];
  862. uuid.ToProvidedString(buffer);
  863. NS_ConvertASCIItoUTF16 convertedID(buffer);
  864. id = convertedID;
  865. }
  866. RefPtr<Notification> notification = new Notification(aGlobal, id, aTitle,
  867. aOptions.mBody,
  868. aOptions.mDir,
  869. aOptions.mLang,
  870. aOptions.mTag,
  871. aOptions.mIcon,
  872. aOptions.mRequireInteraction,
  873. aOptions.mMozbehavior);
  874. rv = notification->Init();
  875. NS_ENSURE_SUCCESS(rv, nullptr);
  876. return notification.forget();
  877. }
  878. Notification::~Notification()
  879. {
  880. mData.setUndefined();
  881. mozilla::DropJSObjects(this);
  882. AssertIsOnTargetThread();
  883. MOZ_ASSERT(!mWorkerHolder);
  884. MOZ_ASSERT(!mTempRef);
  885. }
  886. NS_IMPL_CYCLE_COLLECTION_CLASS(Notification)
  887. NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Notification, DOMEventTargetHelper)
  888. tmp->mData.setUndefined();
  889. NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  890. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Notification, DOMEventTargetHelper)
  891. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  892. NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(Notification, DOMEventTargetHelper)
  893. NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mData)
  894. NS_IMPL_CYCLE_COLLECTION_TRACE_END
  895. NS_IMPL_ADDREF_INHERITED(Notification, DOMEventTargetHelper)
  896. NS_IMPL_RELEASE_INHERITED(Notification, DOMEventTargetHelper)
  897. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Notification)
  898. NS_INTERFACE_MAP_ENTRY(nsIObserver)
  899. NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
  900. NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
  901. nsIPrincipal*
  902. Notification::GetPrincipal()
  903. {
  904. AssertIsOnMainThread();
  905. if (mWorkerPrivate) {
  906. return mWorkerPrivate->GetPrincipal();
  907. } else {
  908. nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetOwner());
  909. NS_ENSURE_TRUE(sop, nullptr);
  910. return sop->GetPrincipal();
  911. }
  912. }
  913. class WorkerNotificationObserver final : public MainThreadNotificationObserver
  914. {
  915. public:
  916. NS_DECL_ISUPPORTS_INHERITED
  917. NS_DECL_NSIOBSERVER
  918. explicit WorkerNotificationObserver(UniquePtr<NotificationRef> aRef)
  919. : MainThreadNotificationObserver(Move(aRef))
  920. {
  921. AssertIsOnMainThread();
  922. MOZ_ASSERT(mNotificationRef->GetNotification()->mWorkerPrivate);
  923. }
  924. void
  925. ForgetNotification()
  926. {
  927. AssertIsOnMainThread();
  928. mNotificationRef->Forget();
  929. }
  930. protected:
  931. virtual ~WorkerNotificationObserver()
  932. {
  933. AssertIsOnMainThread();
  934. MOZ_ASSERT(mNotificationRef);
  935. Notification* notification = mNotificationRef->GetNotification();
  936. if (notification) {
  937. notification->mObserver = nullptr;
  938. }
  939. }
  940. };
  941. NS_IMPL_ISUPPORTS_INHERITED0(WorkerNotificationObserver, MainThreadNotificationObserver)
  942. class ServiceWorkerNotificationObserver final : public nsIObserver
  943. {
  944. public:
  945. NS_DECL_ISUPPORTS
  946. NS_DECL_NSIOBSERVER
  947. ServiceWorkerNotificationObserver(const nsAString& aScope,
  948. nsIPrincipal* aPrincipal,
  949. const nsAString& aID,
  950. const nsAString& aTitle,
  951. const nsAString& aDir,
  952. const nsAString& aLang,
  953. const nsAString& aBody,
  954. const nsAString& aTag,
  955. const nsAString& aIcon,
  956. const nsAString& aData,
  957. const nsAString& aBehavior)
  958. : mScope(aScope), mID(aID), mPrincipal(aPrincipal), mTitle(aTitle)
  959. , mDir(aDir), mLang(aLang), mBody(aBody), mTag(aTag), mIcon(aIcon)
  960. , mData(aData), mBehavior(aBehavior)
  961. {
  962. AssertIsOnMainThread();
  963. MOZ_ASSERT(aPrincipal);
  964. }
  965. private:
  966. ~ServiceWorkerNotificationObserver()
  967. {}
  968. const nsString mScope;
  969. const nsString mID;
  970. nsCOMPtr<nsIPrincipal> mPrincipal;
  971. const nsString mTitle;
  972. const nsString mDir;
  973. const nsString mLang;
  974. const nsString mBody;
  975. const nsString mTag;
  976. const nsString mIcon;
  977. const nsString mData;
  978. const nsString mBehavior;
  979. };
  980. NS_IMPL_ISUPPORTS(ServiceWorkerNotificationObserver, nsIObserver)
  981. // For ServiceWorkers.
  982. bool
  983. Notification::DispatchNotificationClickEvent()
  984. {
  985. MOZ_ASSERT(mWorkerPrivate);
  986. MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
  987. mWorkerPrivate->AssertIsOnWorkerThread();
  988. NotificationEventInit options;
  989. options.mNotification = this;
  990. ErrorResult result;
  991. RefPtr<EventTarget> target = mWorkerPrivate->GlobalScope();
  992. RefPtr<NotificationEvent> event =
  993. NotificationEvent::Constructor(target,
  994. NS_LITERAL_STRING("notificationclick"),
  995. options,
  996. result);
  997. if (NS_WARN_IF(result.Failed())) {
  998. return false;
  999. }
  1000. event->SetTrusted(true);
  1001. WantsPopupControlCheck popupControlCheck(event);
  1002. target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
  1003. // We always return false since in case of dispatching on the serviceworker,
  1004. // there is no well defined window to focus. The script may use the
  1005. // Client.focus() API if it wishes.
  1006. return false;
  1007. }
  1008. bool
  1009. Notification::DispatchClickEvent()
  1010. {
  1011. AssertIsOnTargetThread();
  1012. RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
  1013. event->InitEvent(NS_LITERAL_STRING("click"), false, true);
  1014. event->SetTrusted(true);
  1015. WantsPopupControlCheck popupControlCheck(event);
  1016. bool doDefaultAction = true;
  1017. DispatchEvent(event, &doDefaultAction);
  1018. return doDefaultAction;
  1019. }
  1020. // Overrides dispatch and run handlers so we can directly dispatch from main
  1021. // thread to child workers.
  1022. class NotificationClickWorkerRunnable final : public NotificationWorkerRunnable
  1023. {
  1024. Notification* mNotification;
  1025. // Optional window that gets focused if click event is not
  1026. // preventDefault()ed.
  1027. nsMainThreadPtrHandle<nsPIDOMWindowInner> mWindow;
  1028. public:
  1029. NotificationClickWorkerRunnable(Notification* aNotification,
  1030. const nsMainThreadPtrHandle<nsPIDOMWindowInner>& aWindow)
  1031. : NotificationWorkerRunnable(aNotification->mWorkerPrivate)
  1032. , mNotification(aNotification)
  1033. , mWindow(aWindow)
  1034. {
  1035. MOZ_ASSERT_IF(mWorkerPrivate->IsServiceWorker(), !mWindow);
  1036. }
  1037. void
  1038. WorkerRunInternal(WorkerPrivate* aWorkerPrivate) override
  1039. {
  1040. bool doDefaultAction = mNotification->DispatchClickEvent();
  1041. MOZ_ASSERT_IF(mWorkerPrivate->IsServiceWorker(), !doDefaultAction);
  1042. if (doDefaultAction) {
  1043. RefPtr<FocusWindowRunnable> r = new FocusWindowRunnable(mWindow);
  1044. NS_DispatchToMainThread(r);
  1045. }
  1046. }
  1047. };
  1048. NS_IMETHODIMP
  1049. NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
  1050. const char16_t* aData)
  1051. {
  1052. AssertIsOnMainThread();
  1053. if (!strcmp("alertdisablecallback", aTopic)) {
  1054. if (XRE_IsParentProcess()) {
  1055. return Notification::RemovePermission(mPrincipal);
  1056. }
  1057. // Permissions can't be removed from the content process. Send a message
  1058. // to the parent; `ContentParent::RecvDisableNotifications` will call
  1059. // `RemovePermission`.
  1060. ContentChild::GetSingleton()->SendDisableNotifications(
  1061. IPC::Principal(mPrincipal));
  1062. return NS_OK;
  1063. } else if (!strcmp("alertsettingscallback", aTopic)) {
  1064. if (XRE_IsParentProcess()) {
  1065. return Notification::OpenSettings(mPrincipal);
  1066. }
  1067. // `ContentParent::RecvOpenNotificationSettings` notifies observers in the
  1068. // parent process.
  1069. ContentChild::GetSingleton()->SendOpenNotificationSettings(
  1070. IPC::Principal(mPrincipal));
  1071. return NS_OK;
  1072. } else if (!strcmp("alertshow", aTopic) ||
  1073. !strcmp("alertfinished", aTopic)) {
  1074. Unused << NS_WARN_IF(NS_FAILED(AdjustPushQuota(aTopic)));
  1075. }
  1076. return mObserver->Observe(aSubject, aTopic, aData);
  1077. }
  1078. nsresult
  1079. NotificationObserver::AdjustPushQuota(const char* aTopic)
  1080. {
  1081. nsCOMPtr<nsIPushQuotaManager> pushQuotaManager =
  1082. do_GetService("@mozilla.org/push/Service;1");
  1083. if (!pushQuotaManager) {
  1084. return NS_ERROR_FAILURE;
  1085. }
  1086. nsAutoCString origin;
  1087. nsresult rv = mPrincipal->GetOrigin(origin);
  1088. if (NS_FAILED(rv)) {
  1089. return rv;
  1090. }
  1091. if (!strcmp("alertshow", aTopic)) {
  1092. return pushQuotaManager->NotificationForOriginShown(origin.get());
  1093. }
  1094. return pushQuotaManager->NotificationForOriginClosed(origin.get());
  1095. }
  1096. NS_IMETHODIMP
  1097. MainThreadNotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
  1098. const char16_t* aData)
  1099. {
  1100. AssertIsOnMainThread();
  1101. MOZ_ASSERT(mNotificationRef);
  1102. Notification* notification = mNotificationRef->GetNotification();
  1103. MOZ_ASSERT(notification);
  1104. if (!strcmp("alertclickcallback", aTopic)) {
  1105. nsCOMPtr<nsPIDOMWindowInner> window = notification->GetOwner();
  1106. if (NS_WARN_IF(!window || !window->IsCurrentInnerWindow())) {
  1107. // Window has been closed, this observer is not valid anymore
  1108. return NS_ERROR_FAILURE;
  1109. }
  1110. bool doDefaultAction = notification->DispatchClickEvent();
  1111. if (doDefaultAction) {
  1112. nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
  1113. if (doc) {
  1114. // Browser UI may use DOMWebNotificationClicked to focus the tab
  1115. // from which the event was dispatched.
  1116. nsContentUtils::DispatchChromeEvent(doc, window->GetOuterWindow(),
  1117. NS_LITERAL_STRING("DOMWebNotificationClicked"),
  1118. true, true);
  1119. }
  1120. }
  1121. } else if (!strcmp("alertfinished", aTopic)) {
  1122. notification->UnpersistNotification();
  1123. notification->mIsClosed = true;
  1124. notification->DispatchTrustedEvent(NS_LITERAL_STRING("close"));
  1125. } else if (!strcmp("alertshow", aTopic)) {
  1126. notification->DispatchTrustedEvent(NS_LITERAL_STRING("show"));
  1127. }
  1128. return NS_OK;
  1129. }
  1130. NS_IMETHODIMP
  1131. WorkerNotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
  1132. const char16_t* aData)
  1133. {
  1134. AssertIsOnMainThread();
  1135. MOZ_ASSERT(mNotificationRef);
  1136. // For an explanation of why it is OK to pass this rawptr to the event
  1137. // runnables, see the Notification class comment.
  1138. Notification* notification = mNotificationRef->GetNotification();
  1139. // We can't assert notification here since the feature could've unset it.
  1140. if (NS_WARN_IF(!notification)) {
  1141. return NS_ERROR_FAILURE;
  1142. }
  1143. MOZ_ASSERT(notification->mWorkerPrivate);
  1144. RefPtr<WorkerRunnable> r;
  1145. if (!strcmp("alertclickcallback", aTopic)) {
  1146. nsPIDOMWindowInner* window = nullptr;
  1147. if (!notification->mWorkerPrivate->IsServiceWorker()) {
  1148. WorkerPrivate* top = notification->mWorkerPrivate;
  1149. while (top->GetParent()) {
  1150. top = top->GetParent();
  1151. }
  1152. window = top->GetWindow();
  1153. if (NS_WARN_IF(!window || !window->IsCurrentInnerWindow())) {
  1154. // Window has been closed, this observer is not valid anymore
  1155. return NS_ERROR_FAILURE;
  1156. }
  1157. }
  1158. // Instead of bothering with adding features and other worker lifecycle
  1159. // management, we simply hold strongrefs to the window and document.
  1160. nsMainThreadPtrHandle<nsPIDOMWindowInner> windowHandle(
  1161. new nsMainThreadPtrHolder<nsPIDOMWindowInner>(window));
  1162. r = new NotificationClickWorkerRunnable(notification, windowHandle);
  1163. } else if (!strcmp("alertfinished", aTopic)) {
  1164. notification->UnpersistNotification();
  1165. notification->mIsClosed = true;
  1166. r = new NotificationEventWorkerRunnable(notification,
  1167. NS_LITERAL_STRING("close"));
  1168. } else if (!strcmp("alertshow", aTopic)) {
  1169. r = new NotificationEventWorkerRunnable(notification,
  1170. NS_LITERAL_STRING("show"));
  1171. }
  1172. MOZ_ASSERT(r);
  1173. if (!r->Dispatch()) {
  1174. NS_WARNING("Could not dispatch event to worker notification");
  1175. }
  1176. return NS_OK;
  1177. }
  1178. NS_IMETHODIMP
  1179. ServiceWorkerNotificationObserver::Observe(nsISupports* aSubject,
  1180. const char* aTopic,
  1181. const char16_t* aData)
  1182. {
  1183. AssertIsOnMainThread();
  1184. nsAutoCString originSuffix;
  1185. nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
  1186. if (NS_WARN_IF(NS_FAILED(rv))) {
  1187. return rv;
  1188. }
  1189. nsCOMPtr<nsIServiceWorkerManager> swm =
  1190. mozilla::services::GetServiceWorkerManager();
  1191. if (NS_WARN_IF(!swm)) {
  1192. return NS_ERROR_FAILURE;
  1193. }
  1194. if (!strcmp("alertclickcallback", aTopic)) {
  1195. rv = swm->SendNotificationClickEvent(originSuffix,
  1196. NS_ConvertUTF16toUTF8(mScope),
  1197. mID,
  1198. mTitle,
  1199. mDir,
  1200. mLang,
  1201. mBody,
  1202. mTag,
  1203. mIcon,
  1204. mData,
  1205. mBehavior);
  1206. Unused << NS_WARN_IF(NS_FAILED(rv));
  1207. return NS_OK;
  1208. }
  1209. if (!strcmp("alertfinished", aTopic)) {
  1210. nsString origin;
  1211. nsresult rv = Notification::GetOrigin(mPrincipal, origin);
  1212. if (NS_WARN_IF(NS_FAILED(rv))) {
  1213. return rv;
  1214. }
  1215. // Remove closed or dismissed persistent notifications.
  1216. nsCOMPtr<nsINotificationStorage> notificationStorage =
  1217. do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID);
  1218. if (notificationStorage) {
  1219. notificationStorage->Delete(origin, mID);
  1220. }
  1221. rv = swm->SendNotificationCloseEvent(originSuffix,
  1222. NS_ConvertUTF16toUTF8(mScope),
  1223. mID,
  1224. mTitle,
  1225. mDir,
  1226. mLang,
  1227. mBody,
  1228. mTag,
  1229. mIcon,
  1230. mData,
  1231. mBehavior);
  1232. Unused << NS_WARN_IF(NS_FAILED(rv));
  1233. return NS_OK;
  1234. }
  1235. return NS_OK;
  1236. }
  1237. bool
  1238. Notification::IsInPrivateBrowsing()
  1239. {
  1240. AssertIsOnMainThread();
  1241. nsIDocument* doc = nullptr;
  1242. if (mWorkerPrivate) {
  1243. doc = mWorkerPrivate->GetDocument();
  1244. } else if (GetOwner()) {
  1245. doc = GetOwner()->GetExtantDoc();
  1246. }
  1247. if (doc) {
  1248. nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
  1249. return loadContext && loadContext->UsePrivateBrowsing();
  1250. }
  1251. if (mWorkerPrivate) {
  1252. // Not all workers may have a document, but with Bug 1107516 fixed, they
  1253. // should all have a loadcontext.
  1254. nsCOMPtr<nsILoadGroup> loadGroup = mWorkerPrivate->GetLoadGroup();
  1255. nsCOMPtr<nsILoadContext> loadContext;
  1256. NS_QueryNotificationCallbacks(nullptr, loadGroup, NS_GET_IID(nsILoadContext),
  1257. getter_AddRefs(loadContext));
  1258. return loadContext && loadContext->UsePrivateBrowsing();
  1259. }
  1260. //XXXnsm Should this default to true?
  1261. return false;
  1262. }
  1263. namespace {
  1264. struct StringWriteFunc : public JSONWriteFunc
  1265. {
  1266. nsAString& mBuffer; // This struct must not outlive this buffer
  1267. explicit StringWriteFunc(nsAString& buffer) : mBuffer(buffer) {}
  1268. void Write(const char* aStr)
  1269. {
  1270. mBuffer.Append(NS_ConvertUTF8toUTF16(aStr));
  1271. }
  1272. };
  1273. }
  1274. void
  1275. Notification::ShowInternal()
  1276. {
  1277. AssertIsOnMainThread();
  1278. MOZ_ASSERT(mTempRef, "Notification should take ownership of itself before"
  1279. "calling ShowInternal!");
  1280. // A notification can only have one observer and one call to ShowInternal.
  1281. MOZ_ASSERT(!mObserver);
  1282. // Transfer ownership to local scope so we can either release it at the end
  1283. // of this function or transfer it to the observer.
  1284. UniquePtr<NotificationRef> ownership;
  1285. mozilla::Swap(ownership, mTempRef);
  1286. MOZ_ASSERT(ownership->GetNotification() == this);
  1287. nsresult rv = PersistNotification();
  1288. if (NS_FAILED(rv)) {
  1289. NS_WARNING("Could not persist Notification");
  1290. }
  1291. nsCOMPtr<nsIAlertsService> alertService =
  1292. do_GetService(NS_ALERTSERVICE_CONTRACTID);
  1293. ErrorResult result;
  1294. NotificationPermission permission = NotificationPermission::Denied;
  1295. if (mWorkerPrivate) {
  1296. permission = GetPermissionInternal(mWorkerPrivate->GetPrincipal(), result);
  1297. } else {
  1298. permission = GetPermissionInternal(GetOwner(), result);
  1299. }
  1300. // We rely on GetPermissionInternal returning Denied on all failure codepaths.
  1301. MOZ_ASSERT_IF(result.Failed(), permission == NotificationPermission::Denied);
  1302. result.SuppressException();
  1303. if (permission != NotificationPermission::Granted || !alertService) {
  1304. if (mWorkerPrivate) {
  1305. RefPtr<NotificationEventWorkerRunnable> r =
  1306. new NotificationEventWorkerRunnable(this,
  1307. NS_LITERAL_STRING("error"));
  1308. if (!r->Dispatch()) {
  1309. NS_WARNING("Could not dispatch event to worker notification");
  1310. }
  1311. } else {
  1312. DispatchTrustedEvent(NS_LITERAL_STRING("error"));
  1313. }
  1314. return;
  1315. }
  1316. nsAutoString iconUrl;
  1317. nsAutoString soundUrl;
  1318. ResolveIconAndSoundURL(iconUrl, soundUrl);
  1319. bool isPersistent = false;
  1320. nsCOMPtr<nsIObserver> observer;
  1321. if (mScope.IsEmpty()) {
  1322. // Ownership passed to observer.
  1323. if (mWorkerPrivate) {
  1324. // Scope better be set on ServiceWorker initiated requests.
  1325. MOZ_ASSERT(!mWorkerPrivate->IsServiceWorker());
  1326. // Keep a pointer so that the feature can tell the observer not to release
  1327. // the notification.
  1328. mObserver = new WorkerNotificationObserver(Move(ownership));
  1329. observer = mObserver;
  1330. } else {
  1331. observer = new MainThreadNotificationObserver(Move(ownership));
  1332. }
  1333. } else {
  1334. isPersistent = true;
  1335. // This observer does not care about the Notification. It will be released
  1336. // at the end of this function.
  1337. //
  1338. // The observer is wholly owned by the NotificationObserver passed to the alert service.
  1339. nsAutoString behavior;
  1340. if (NS_WARN_IF(!mBehavior.ToJSON(behavior))) {
  1341. behavior.Truncate();
  1342. }
  1343. observer = new ServiceWorkerNotificationObserver(mScope,
  1344. GetPrincipal(),
  1345. mID,
  1346. mTitle,
  1347. DirectionToString(mDir),
  1348. mLang,
  1349. mBody,
  1350. mTag,
  1351. iconUrl,
  1352. mDataAsBase64,
  1353. behavior);
  1354. }
  1355. MOZ_ASSERT(observer);
  1356. nsCOMPtr<nsIObserver> alertObserver = new NotificationObserver(observer,
  1357. GetPrincipal(),
  1358. IsInPrivateBrowsing());
  1359. // In the case of IPC, the parent process uses the cookie to map to
  1360. // nsIObserver. Thus the cookie must be unique to differentiate observers.
  1361. nsString uniqueCookie = NS_LITERAL_STRING("notification:");
  1362. uniqueCookie.AppendInt(sCount++);
  1363. bool inPrivateBrowsing = IsInPrivateBrowsing();
  1364. bool requireInteraction = mRequireInteraction;
  1365. if (!Preferences::GetBool("dom.webnotifications.requireinteraction.enabled", false)) {
  1366. requireInteraction = false;
  1367. }
  1368. nsAutoString alertName;
  1369. GetAlertName(alertName);
  1370. nsCOMPtr<nsIAlertNotification> alert =
  1371. do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
  1372. NS_ENSURE_TRUE_VOID(alert);
  1373. nsIPrincipal* principal = GetPrincipal();
  1374. rv = alert->Init(alertName, iconUrl, mTitle, mBody,
  1375. true,
  1376. uniqueCookie,
  1377. DirectionToString(mDir),
  1378. mLang,
  1379. mDataAsBase64,
  1380. GetPrincipal(),
  1381. inPrivateBrowsing,
  1382. requireInteraction);
  1383. NS_ENSURE_SUCCESS_VOID(rv);
  1384. if (isPersistent) {
  1385. nsAutoString persistentData;
  1386. JSONWriter w(MakeUnique<StringWriteFunc>(persistentData));
  1387. w.Start();
  1388. nsAutoString origin;
  1389. Notification::GetOrigin(principal, origin);
  1390. w.StringProperty("origin", NS_ConvertUTF16toUTF8(origin).get());
  1391. w.StringProperty("id", NS_ConvertUTF16toUTF8(mID).get());
  1392. nsAutoCString originSuffix;
  1393. principal->GetOriginSuffix(originSuffix);
  1394. w.StringProperty("originSuffix", originSuffix.get());
  1395. w.End();
  1396. alertService->ShowPersistentNotification(persistentData, alert, alertObserver);
  1397. } else {
  1398. alertService->ShowAlert(alert, alertObserver);
  1399. }
  1400. }
  1401. /* static */ bool
  1402. Notification::RequestPermissionEnabledForScope(JSContext* aCx, JSObject* /* unused */)
  1403. {
  1404. // requestPermission() is not allowed on workers. The calling page should ask
  1405. // for permission on the worker's behalf. This is to prevent 'which window
  1406. // should show the browser pop-up'. See discussion:
  1407. // http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2013-October/041272.html
  1408. return NS_IsMainThread();
  1409. }
  1410. already_AddRefed<Promise>
  1411. Notification::RequestPermission(const GlobalObject& aGlobal,
  1412. const Optional<OwningNonNull<NotificationPermissionCallback> >& aCallback,
  1413. ErrorResult& aRv)
  1414. {
  1415. // Get principal from global to make permission request for notifications.
  1416. nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
  1417. nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal.GetAsSupports());
  1418. if (!sop) {
  1419. aRv.Throw(NS_ERROR_UNEXPECTED);
  1420. return nullptr;
  1421. }
  1422. nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
  1423. nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window);
  1424. RefPtr<Promise> promise = Promise::Create(global, aRv);
  1425. if (aRv.Failed()) {
  1426. return nullptr;
  1427. }
  1428. NotificationPermissionCallback* permissionCallback = nullptr;
  1429. if (aCallback.WasPassed()) {
  1430. permissionCallback = &aCallback.Value();
  1431. }
  1432. nsCOMPtr<nsIRunnable> request =
  1433. new NotificationPermissionRequest(principal, window, promise, permissionCallback);
  1434. NS_DispatchToMainThread(request);
  1435. return promise.forget();
  1436. }
  1437. // static
  1438. NotificationPermission
  1439. Notification::GetPermission(const GlobalObject& aGlobal, ErrorResult& aRv)
  1440. {
  1441. nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
  1442. return GetPermission(global, aRv);
  1443. }
  1444. // static
  1445. NotificationPermission
  1446. Notification::GetPermission(nsIGlobalObject* aGlobal, ErrorResult& aRv)
  1447. {
  1448. if (NS_IsMainThread()) {
  1449. return GetPermissionInternal(aGlobal, aRv);
  1450. } else {
  1451. WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
  1452. MOZ_ASSERT(worker);
  1453. RefPtr<GetPermissionRunnable> r =
  1454. new GetPermissionRunnable(worker);
  1455. r->Dispatch(Terminating, aRv);
  1456. if (aRv.Failed()) {
  1457. return NotificationPermission::Denied;
  1458. }
  1459. return r->GetPermission();
  1460. }
  1461. }
  1462. /* static */ NotificationPermission
  1463. Notification::GetPermissionInternal(nsISupports* aGlobal, ErrorResult& aRv)
  1464. {
  1465. // Get principal from global to check permission for notifications.
  1466. nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal);
  1467. if (!sop) {
  1468. aRv.Throw(NS_ERROR_UNEXPECTED);
  1469. return NotificationPermission::Denied;
  1470. }
  1471. nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
  1472. return GetPermissionInternal(principal, aRv);
  1473. }
  1474. /* static */ NotificationPermission
  1475. Notification::GetPermissionInternal(nsIPrincipal* aPrincipal,
  1476. ErrorResult& aRv)
  1477. {
  1478. AssertIsOnMainThread();
  1479. MOZ_ASSERT(aPrincipal);
  1480. if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
  1481. return NotificationPermission::Granted;
  1482. } else {
  1483. // Allow files to show notifications by default.
  1484. nsCOMPtr<nsIURI> uri;
  1485. aPrincipal->GetURI(getter_AddRefs(uri));
  1486. if (uri) {
  1487. bool isFile;
  1488. uri->SchemeIs("file", &isFile);
  1489. if (isFile) {
  1490. return NotificationPermission::Granted;
  1491. }
  1492. }
  1493. }
  1494. // We also allow notifications is they are pref'ed on.
  1495. if (Preferences::GetBool("notification.prompt.testing", false)) {
  1496. if (Preferences::GetBool("notification.prompt.testing.allow", true)) {
  1497. return NotificationPermission::Granted;
  1498. } else {
  1499. return NotificationPermission::Denied;
  1500. }
  1501. }
  1502. return TestPermission(aPrincipal);
  1503. }
  1504. /* static */ NotificationPermission
  1505. Notification::TestPermission(nsIPrincipal* aPrincipal)
  1506. {
  1507. AssertIsOnMainThread();
  1508. uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
  1509. nsCOMPtr<nsIPermissionManager> permissionManager =
  1510. services::GetPermissionManager();
  1511. if (!permissionManager) {
  1512. return NotificationPermission::Default;
  1513. }
  1514. permissionManager->TestExactPermissionFromPrincipal(aPrincipal,
  1515. "desktop-notification",
  1516. &permission);
  1517. // Convert the result to one of the enum types.
  1518. switch (permission) {
  1519. case nsIPermissionManager::ALLOW_ACTION:
  1520. return NotificationPermission::Granted;
  1521. case nsIPermissionManager::DENY_ACTION:
  1522. return NotificationPermission::Denied;
  1523. default:
  1524. return NotificationPermission::Default;
  1525. }
  1526. }
  1527. nsresult
  1528. Notification::ResolveIconAndSoundURL(nsString& iconUrl, nsString& soundUrl)
  1529. {
  1530. AssertIsOnMainThread();
  1531. nsresult rv = NS_OK;
  1532. nsCOMPtr<nsIURI> baseUri;
  1533. // XXXnsm If I understand correctly, the character encoding for resolving
  1534. // URIs in new specs is dictated by the URL spec, which states that unless
  1535. // the URL parser is passed an override encoding, the charset to be used is
  1536. // UTF-8. The new Notification icon/sound specification just says to use the
  1537. // Fetch API, where the Request constructor defers to URL parsing specifying
  1538. // the API base URL and no override encoding. So we've to use UTF-8 on
  1539. // workers, but for backwards compat keeping it document charset on main
  1540. // thread.
  1541. const char* charset = "UTF-8";
  1542. if (mWorkerPrivate) {
  1543. baseUri = mWorkerPrivate->GetBaseURI();
  1544. } else {
  1545. nsIDocument* doc = GetOwner() ? GetOwner()->GetExtantDoc() : nullptr;
  1546. if (doc) {
  1547. baseUri = doc->GetBaseURI();
  1548. charset = doc->GetDocumentCharacterSet().get();
  1549. } else {
  1550. NS_WARNING("No document found for main thread notification!");
  1551. return NS_ERROR_FAILURE;
  1552. }
  1553. }
  1554. if (baseUri) {
  1555. if (mIconUrl.Length() > 0) {
  1556. nsCOMPtr<nsIURI> srcUri;
  1557. rv = NS_NewURI(getter_AddRefs(srcUri), mIconUrl, charset, baseUri);
  1558. if (NS_SUCCEEDED(rv)) {
  1559. nsAutoCString src;
  1560. srcUri->GetSpec(src);
  1561. iconUrl = NS_ConvertUTF8toUTF16(src);
  1562. }
  1563. }
  1564. if (mBehavior.mSoundFile.Length() > 0) {
  1565. nsCOMPtr<nsIURI> srcUri;
  1566. rv = NS_NewURI(getter_AddRefs(srcUri), mBehavior.mSoundFile, charset, baseUri);
  1567. if (NS_SUCCEEDED(rv)) {
  1568. nsAutoCString src;
  1569. srcUri->GetSpec(src);
  1570. soundUrl = NS_ConvertUTF8toUTF16(src);
  1571. }
  1572. }
  1573. }
  1574. return rv;
  1575. }
  1576. already_AddRefed<Promise>
  1577. Notification::Get(nsPIDOMWindowInner* aWindow,
  1578. const GetNotificationOptions& aFilter,
  1579. const nsAString& aScope,
  1580. ErrorResult& aRv)
  1581. {
  1582. MOZ_ASSERT(aWindow);
  1583. nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
  1584. if (!doc) {
  1585. aRv.Throw(NS_ERROR_UNEXPECTED);
  1586. return nullptr;
  1587. }
  1588. nsString origin;
  1589. aRv = GetOrigin(doc->NodePrincipal(), origin);
  1590. if (aRv.Failed()) {
  1591. return nullptr;
  1592. }
  1593. nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aWindow);
  1594. RefPtr<Promise> promise = Promise::Create(global, aRv);
  1595. if (aRv.Failed()) {
  1596. return nullptr;
  1597. }
  1598. nsCOMPtr<nsINotificationStorageCallback> callback =
  1599. new NotificationStorageCallback(global, aScope, promise);
  1600. RefPtr<NotificationGetRunnable> r =
  1601. new NotificationGetRunnable(origin, aFilter.mTag, callback);
  1602. aRv = NS_DispatchToMainThread(r);
  1603. if (NS_WARN_IF(aRv.Failed())) {
  1604. return nullptr;
  1605. }
  1606. return promise.forget();
  1607. }
  1608. already_AddRefed<Promise>
  1609. Notification::Get(const GlobalObject& aGlobal,
  1610. const GetNotificationOptions& aFilter,
  1611. ErrorResult& aRv)
  1612. {
  1613. AssertIsOnMainThread();
  1614. nsCOMPtr<nsIGlobalObject> global =
  1615. do_QueryInterface(aGlobal.GetAsSupports());
  1616. MOZ_ASSERT(global);
  1617. nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
  1618. return Get(window, aFilter, EmptyString(), aRv);
  1619. }
  1620. class WorkerGetResultRunnable final : public NotificationWorkerRunnable
  1621. {
  1622. RefPtr<PromiseWorkerProxy> mPromiseProxy;
  1623. const nsTArray<NotificationStrings> mStrings;
  1624. public:
  1625. WorkerGetResultRunnable(WorkerPrivate* aWorkerPrivate,
  1626. PromiseWorkerProxy* aPromiseProxy,
  1627. const nsTArray<NotificationStrings>&& aStrings)
  1628. : NotificationWorkerRunnable(aWorkerPrivate)
  1629. , mPromiseProxy(aPromiseProxy)
  1630. , mStrings(Move(aStrings))
  1631. {
  1632. }
  1633. void
  1634. WorkerRunInternal(WorkerPrivate* aWorkerPrivate) override
  1635. {
  1636. RefPtr<Promise> workerPromise = mPromiseProxy->WorkerPromise();
  1637. ErrorResult result;
  1638. AutoTArray<RefPtr<Notification>, 5> notifications;
  1639. for (uint32_t i = 0; i < mStrings.Length(); ++i) {
  1640. RefPtr<Notification> n =
  1641. Notification::ConstructFromFields(aWorkerPrivate->GlobalScope(),
  1642. mStrings[i].mID,
  1643. mStrings[i].mTitle,
  1644. mStrings[i].mDir,
  1645. mStrings[i].mLang,
  1646. mStrings[i].mBody,
  1647. mStrings[i].mTag,
  1648. mStrings[i].mIcon,
  1649. mStrings[i].mData,
  1650. /* mStrings[i].mBehavior, not
  1651. * supported */
  1652. mStrings[i].mServiceWorkerRegistrationScope,
  1653. result);
  1654. n->SetStoredState(true);
  1655. Unused << NS_WARN_IF(result.Failed());
  1656. if (!result.Failed()) {
  1657. notifications.AppendElement(n.forget());
  1658. }
  1659. }
  1660. workerPromise->MaybeResolve(notifications);
  1661. mPromiseProxy->CleanUp();
  1662. }
  1663. };
  1664. class WorkerGetCallback final : public ScopeCheckingGetCallback
  1665. {
  1666. RefPtr<PromiseWorkerProxy> mPromiseProxy;
  1667. public:
  1668. NS_DECL_ISUPPORTS
  1669. WorkerGetCallback(PromiseWorkerProxy* aProxy, const nsAString& aScope)
  1670. : ScopeCheckingGetCallback(aScope), mPromiseProxy(aProxy)
  1671. {
  1672. AssertIsOnMainThread();
  1673. MOZ_ASSERT(aProxy);
  1674. }
  1675. NS_IMETHOD Done() final
  1676. {
  1677. AssertIsOnMainThread();
  1678. MOZ_ASSERT(mPromiseProxy, "Was Done() called twice?");
  1679. RefPtr<PromiseWorkerProxy> proxy = mPromiseProxy.forget();
  1680. MutexAutoLock lock(proxy->Lock());
  1681. if (proxy->CleanedUp()) {
  1682. return NS_OK;
  1683. }
  1684. RefPtr<WorkerGetResultRunnable> r =
  1685. new WorkerGetResultRunnable(proxy->GetWorkerPrivate(),
  1686. proxy,
  1687. Move(mStrings));
  1688. r->Dispatch();
  1689. return NS_OK;
  1690. }
  1691. private:
  1692. ~WorkerGetCallback()
  1693. {}
  1694. };
  1695. NS_IMPL_ISUPPORTS(WorkerGetCallback, nsINotificationStorageCallback)
  1696. class WorkerGetRunnable final : public Runnable
  1697. {
  1698. RefPtr<PromiseWorkerProxy> mPromiseProxy;
  1699. const nsString mTag;
  1700. const nsString mScope;
  1701. public:
  1702. WorkerGetRunnable(PromiseWorkerProxy* aProxy,
  1703. const nsAString& aTag,
  1704. const nsAString& aScope)
  1705. : mPromiseProxy(aProxy), mTag(aTag), mScope(aScope)
  1706. {
  1707. MOZ_ASSERT(mPromiseProxy);
  1708. }
  1709. NS_IMETHOD
  1710. Run() override
  1711. {
  1712. AssertIsOnMainThread();
  1713. nsCOMPtr<nsINotificationStorageCallback> callback =
  1714. new WorkerGetCallback(mPromiseProxy, mScope);
  1715. nsresult rv;
  1716. nsCOMPtr<nsINotificationStorage> notificationStorage =
  1717. do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
  1718. if (NS_WARN_IF(NS_FAILED(rv))) {
  1719. callback->Done();
  1720. return rv;
  1721. }
  1722. MutexAutoLock lock(mPromiseProxy->Lock());
  1723. if (mPromiseProxy->CleanedUp()) {
  1724. return NS_OK;
  1725. }
  1726. nsString origin;
  1727. rv =
  1728. Notification::GetOrigin(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(),
  1729. origin);
  1730. if (NS_WARN_IF(NS_FAILED(rv))) {
  1731. callback->Done();
  1732. return rv;
  1733. }
  1734. rv = notificationStorage->Get(origin, mTag, callback);
  1735. if (NS_WARN_IF(NS_FAILED(rv))) {
  1736. callback->Done();
  1737. return rv;
  1738. }
  1739. return NS_OK;
  1740. }
  1741. private:
  1742. ~WorkerGetRunnable()
  1743. {}
  1744. };
  1745. already_AddRefed<Promise>
  1746. Notification::WorkerGet(WorkerPrivate* aWorkerPrivate,
  1747. const GetNotificationOptions& aFilter,
  1748. const nsAString& aScope,
  1749. ErrorResult& aRv)
  1750. {
  1751. MOZ_ASSERT(aWorkerPrivate);
  1752. aWorkerPrivate->AssertIsOnWorkerThread();
  1753. RefPtr<Promise> p = Promise::Create(aWorkerPrivate->GlobalScope(), aRv);
  1754. if (NS_WARN_IF(aRv.Failed())) {
  1755. return nullptr;
  1756. }
  1757. RefPtr<PromiseWorkerProxy> proxy =
  1758. PromiseWorkerProxy::Create(aWorkerPrivate, p);
  1759. if (!proxy) {
  1760. aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
  1761. return nullptr;
  1762. }
  1763. RefPtr<WorkerGetRunnable> r =
  1764. new WorkerGetRunnable(proxy, aFilter.mTag, aScope);
  1765. // Since this is called from script via
  1766. // ServiceWorkerRegistration::GetNotifications, we can assert dispatch.
  1767. MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
  1768. return p.forget();
  1769. }
  1770. JSObject*
  1771. Notification::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
  1772. {
  1773. return mozilla::dom::NotificationBinding::Wrap(aCx, this, aGivenProto);
  1774. }
  1775. void
  1776. Notification::Close()
  1777. {
  1778. AssertIsOnTargetThread();
  1779. auto ref = MakeUnique<NotificationRef>(this);
  1780. if (!ref->Initialized()) {
  1781. return;
  1782. }
  1783. nsCOMPtr<nsIRunnable> closeNotificationTask =
  1784. new NotificationTask(Move(ref), NotificationTask::eClose);
  1785. nsresult rv = NS_DispatchToMainThread(closeNotificationTask);
  1786. if (NS_FAILED(rv)) {
  1787. DispatchTrustedEvent(NS_LITERAL_STRING("error"));
  1788. // If dispatch fails, NotificationTask will release the ref when it goes
  1789. // out of scope at the end of this function.
  1790. }
  1791. }
  1792. void
  1793. Notification::CloseInternal()
  1794. {
  1795. AssertIsOnMainThread();
  1796. // Transfer ownership (if any) to local scope so we can release it at the end
  1797. // of this function. This is relevant when the call is from
  1798. // NotificationTask::Run().
  1799. UniquePtr<NotificationRef> ownership;
  1800. mozilla::Swap(ownership, mTempRef);
  1801. SetAlertName();
  1802. UnpersistNotification();
  1803. if (!mIsClosed) {
  1804. nsCOMPtr<nsIAlertsService> alertService =
  1805. do_GetService(NS_ALERTSERVICE_CONTRACTID);
  1806. if (alertService) {
  1807. nsAutoString alertName;
  1808. GetAlertName(alertName);
  1809. alertService->CloseAlert(alertName, GetPrincipal());
  1810. }
  1811. }
  1812. }
  1813. nsresult
  1814. Notification::GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin)
  1815. {
  1816. if (!aPrincipal) {
  1817. return NS_ERROR_FAILURE;
  1818. }
  1819. nsresult rv = nsContentUtils::GetUTFOrigin(aPrincipal, aOrigin);
  1820. NS_ENSURE_SUCCESS(rv, rv);
  1821. return NS_OK;
  1822. }
  1823. bool
  1824. Notification::RequireInteraction() const
  1825. {
  1826. return mRequireInteraction;
  1827. }
  1828. void
  1829. Notification::GetData(JSContext* aCx,
  1830. JS::MutableHandle<JS::Value> aRetval)
  1831. {
  1832. if (mData.isNull() && !mDataAsBase64.IsEmpty()) {
  1833. nsresult rv;
  1834. RefPtr<nsStructuredCloneContainer> container =
  1835. new nsStructuredCloneContainer();
  1836. rv = container->InitFromBase64(mDataAsBase64, JS_STRUCTURED_CLONE_VERSION);
  1837. if (NS_WARN_IF(NS_FAILED(rv))) {
  1838. aRetval.setNull();
  1839. return;
  1840. }
  1841. JS::Rooted<JS::Value> data(aCx);
  1842. rv = container->DeserializeToJsval(aCx, &data);
  1843. if (NS_WARN_IF(NS_FAILED(rv))) {
  1844. aRetval.setNull();
  1845. return;
  1846. }
  1847. if (data.isGCThing()) {
  1848. mozilla::HoldJSObjects(this);
  1849. }
  1850. mData = data;
  1851. }
  1852. if (mData.isNull()) {
  1853. aRetval.setNull();
  1854. return;
  1855. }
  1856. aRetval.set(mData);
  1857. }
  1858. void
  1859. Notification::InitFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aData,
  1860. ErrorResult& aRv)
  1861. {
  1862. if (!mDataAsBase64.IsEmpty() || aData.isNull()) {
  1863. return;
  1864. }
  1865. RefPtr<nsStructuredCloneContainer> dataObjectContainer =
  1866. new nsStructuredCloneContainer();
  1867. aRv = dataObjectContainer->InitFromJSVal(aData, aCx);
  1868. if (NS_WARN_IF(aRv.Failed())) {
  1869. return;
  1870. }
  1871. dataObjectContainer->GetDataAsBase64(mDataAsBase64);
  1872. }
  1873. void Notification::InitFromBase64(const nsAString& aData, ErrorResult& aRv)
  1874. {
  1875. if (!mDataAsBase64.IsEmpty() || aData.IsEmpty()) {
  1876. return;
  1877. }
  1878. // To and fro to ensure it is valid base64.
  1879. RefPtr<nsStructuredCloneContainer> container =
  1880. new nsStructuredCloneContainer();
  1881. aRv = container->InitFromBase64(aData, JS_STRUCTURED_CLONE_VERSION);
  1882. if (NS_WARN_IF(aRv.Failed())) {
  1883. return;
  1884. }
  1885. container->GetDataAsBase64(mDataAsBase64);
  1886. }
  1887. bool
  1888. Notification::AddRefObject()
  1889. {
  1890. AssertIsOnTargetThread();
  1891. MOZ_ASSERT_IF(mWorkerPrivate && !mWorkerHolder, mTaskCount == 0);
  1892. MOZ_ASSERT_IF(mWorkerPrivate && mWorkerHolder, mTaskCount > 0);
  1893. if (mWorkerPrivate && !mWorkerHolder) {
  1894. if (!RegisterWorkerHolder()) {
  1895. return false;
  1896. }
  1897. }
  1898. AddRef();
  1899. ++mTaskCount;
  1900. return true;
  1901. }
  1902. void
  1903. Notification::ReleaseObject()
  1904. {
  1905. AssertIsOnTargetThread();
  1906. MOZ_ASSERT(mTaskCount > 0);
  1907. MOZ_ASSERT_IF(mWorkerPrivate, mWorkerHolder);
  1908. --mTaskCount;
  1909. if (mWorkerPrivate && mTaskCount == 0) {
  1910. UnregisterWorkerHolder();
  1911. }
  1912. Release();
  1913. }
  1914. NotificationWorkerHolder::NotificationWorkerHolder(Notification* aNotification)
  1915. : mNotification(aNotification)
  1916. {
  1917. MOZ_ASSERT(mNotification->mWorkerPrivate);
  1918. mNotification->mWorkerPrivate->AssertIsOnWorkerThread();
  1919. }
  1920. /*
  1921. * Called from the worker, runs on main thread, blocks worker.
  1922. *
  1923. * We can freely access mNotification here because the feature supplied it and
  1924. * the Notification owns the feature.
  1925. */
  1926. class CloseNotificationRunnable final
  1927. : public WorkerMainThreadRunnable
  1928. {
  1929. Notification* mNotification;
  1930. bool mHadObserver;
  1931. public:
  1932. explicit CloseNotificationRunnable(Notification* aNotification)
  1933. : WorkerMainThreadRunnable(aNotification->mWorkerPrivate,
  1934. NS_LITERAL_CSTRING("Notification :: Close Notification"))
  1935. , mNotification(aNotification)
  1936. , mHadObserver(false)
  1937. {}
  1938. bool
  1939. MainThreadRun() override
  1940. {
  1941. if (mNotification->mObserver) {
  1942. // The Notify() take's responsibility of releasing the Notification.
  1943. mNotification->mObserver->ForgetNotification();
  1944. mNotification->mObserver = nullptr;
  1945. mHadObserver = true;
  1946. }
  1947. mNotification->CloseInternal();
  1948. return true;
  1949. }
  1950. bool
  1951. HadObserver()
  1952. {
  1953. return mHadObserver;
  1954. }
  1955. };
  1956. bool
  1957. NotificationWorkerHolder::Notify(Status aStatus)
  1958. {
  1959. if (aStatus >= Canceling) {
  1960. // CloseNotificationRunnable blocks the worker by pushing a sync event loop
  1961. // on the stack. Meanwhile, WorkerControlRunnables dispatched to the worker
  1962. // can still continue running. One of these is
  1963. // ReleaseNotificationControlRunnable that releases the notification,
  1964. // invalidating the notification and this feature. We hold this reference to
  1965. // keep the notification valid until we are done with it.
  1966. //
  1967. // An example of when the control runnable could get dispatched to the
  1968. // worker is if a Notification is created and the worker is immediately
  1969. // closed, but there is no permission to show it so that the main thread
  1970. // immediately drops the NotificationRef. In this case, this function blocks
  1971. // on the main thread, but the main thread dispatches the control runnable,
  1972. // invalidating mNotification.
  1973. RefPtr<Notification> kungFuDeathGrip = mNotification;
  1974. // Dispatched to main thread, blocks on closing the Notification.
  1975. RefPtr<CloseNotificationRunnable> r =
  1976. new CloseNotificationRunnable(kungFuDeathGrip);
  1977. ErrorResult rv;
  1978. r->Dispatch(Killing, rv);
  1979. // XXXbz I'm told throwing and returning false from here is pointless (and
  1980. // also that doing sync stuff from here is really weird), so I guess we just
  1981. // suppress the exception on rv, if any.
  1982. rv.SuppressException();
  1983. // Only call ReleaseObject() to match the observer's NotificationRef
  1984. // ownership (since CloseNotificationRunnable asked the observer to drop the
  1985. // reference to the notification).
  1986. if (r->HadObserver()) {
  1987. kungFuDeathGrip->ReleaseObject();
  1988. }
  1989. // From this point we cannot touch properties of this feature because
  1990. // ReleaseObject() may have led to the notification going away and the
  1991. // notification owns this feature!
  1992. }
  1993. return true;
  1994. }
  1995. bool
  1996. Notification::RegisterWorkerHolder()
  1997. {
  1998. MOZ_ASSERT(mWorkerPrivate);
  1999. mWorkerPrivate->AssertIsOnWorkerThread();
  2000. MOZ_ASSERT(!mWorkerHolder);
  2001. mWorkerHolder = MakeUnique<NotificationWorkerHolder>(this);
  2002. if (NS_WARN_IF(!mWorkerHolder->HoldWorker(mWorkerPrivate, Canceling))) {
  2003. return false;
  2004. }
  2005. return true;
  2006. }
  2007. void
  2008. Notification::UnregisterWorkerHolder()
  2009. {
  2010. MOZ_ASSERT(mWorkerPrivate);
  2011. mWorkerPrivate->AssertIsOnWorkerThread();
  2012. MOZ_ASSERT(mWorkerHolder);
  2013. mWorkerHolder = nullptr;
  2014. }
  2015. /*
  2016. * Checks:
  2017. * 1) Is aWorker allowed to show a notification for scope?
  2018. * 2) Is aWorker an active worker?
  2019. *
  2020. * If it is not an active worker, Result() will be NS_ERROR_NOT_AVAILABLE.
  2021. */
  2022. class CheckLoadRunnable final : public WorkerMainThreadRunnable
  2023. {
  2024. nsresult mRv;
  2025. nsCString mScope;
  2026. public:
  2027. explicit CheckLoadRunnable(WorkerPrivate* aWorker, const nsACString& aScope)
  2028. : WorkerMainThreadRunnable(aWorker,
  2029. NS_LITERAL_CSTRING("Notification :: Check Load"))
  2030. , mRv(NS_ERROR_DOM_SECURITY_ERR)
  2031. , mScope(aScope)
  2032. { }
  2033. bool
  2034. MainThreadRun() override
  2035. {
  2036. nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
  2037. mRv = CheckScope(principal, mScope);
  2038. if (NS_FAILED(mRv)) {
  2039. return true;
  2040. }
  2041. RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
  2042. if (!swm) {
  2043. // browser shutdown began
  2044. mRv = NS_ERROR_FAILURE;
  2045. return true;
  2046. }
  2047. RefPtr<ServiceWorkerRegistrationInfo> registration =
  2048. swm->GetRegistration(principal, mScope);
  2049. // This is coming from a ServiceWorkerRegistration.
  2050. MOZ_ASSERT(registration);
  2051. if (!registration->GetActive() ||
  2052. registration->GetActive()->ID() != mWorkerPrivate->ServiceWorkerID()) {
  2053. mRv = NS_ERROR_NOT_AVAILABLE;
  2054. }
  2055. return true;
  2056. }
  2057. nsresult
  2058. Result()
  2059. {
  2060. return mRv;
  2061. }
  2062. };
  2063. /* static */
  2064. already_AddRefed<Promise>
  2065. Notification::ShowPersistentNotification(JSContext* aCx,
  2066. nsIGlobalObject *aGlobal,
  2067. const nsAString& aScope,
  2068. const nsAString& aTitle,
  2069. const NotificationOptions& aOptions,
  2070. ErrorResult& aRv)
  2071. {
  2072. MOZ_ASSERT(aGlobal);
  2073. // Validate scope.
  2074. // XXXnsm: This may be slow due to blocking the worker and waiting on the main
  2075. // thread. On calls from content, we can be sure the scope is valid since
  2076. // ServiceWorkerRegistrations have their scope set correctly. Can this be made
  2077. // debug only? The problem is that there would be different semantics in
  2078. // debug and non-debug builds in such a case.
  2079. if (NS_IsMainThread()) {
  2080. nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aGlobal);
  2081. if (NS_WARN_IF(!sop)) {
  2082. aRv.Throw(NS_ERROR_UNEXPECTED);
  2083. return nullptr;
  2084. }
  2085. nsIPrincipal* principal = sop->GetPrincipal();
  2086. if (NS_WARN_IF(!principal)) {
  2087. aRv.Throw(NS_ERROR_UNEXPECTED);
  2088. return nullptr;
  2089. }
  2090. aRv = CheckScope(principal, NS_ConvertUTF16toUTF8(aScope));
  2091. if (NS_WARN_IF(aRv.Failed())) {
  2092. aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  2093. return nullptr;
  2094. }
  2095. } else {
  2096. WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
  2097. MOZ_ASSERT(worker);
  2098. worker->AssertIsOnWorkerThread();
  2099. RefPtr<CheckLoadRunnable> loadChecker =
  2100. new CheckLoadRunnable(worker, NS_ConvertUTF16toUTF8(aScope));
  2101. loadChecker->Dispatch(Terminating, aRv);
  2102. if (aRv.Failed()) {
  2103. return nullptr;
  2104. }
  2105. if (NS_WARN_IF(NS_FAILED(loadChecker->Result()))) {
  2106. if (loadChecker->Result() == NS_ERROR_NOT_AVAILABLE) {
  2107. aRv.ThrowTypeError<MSG_NO_ACTIVE_WORKER>(aScope);
  2108. } else {
  2109. aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  2110. }
  2111. return nullptr;
  2112. }
  2113. }
  2114. RefPtr<Promise> p = Promise::Create(aGlobal, aRv);
  2115. if (NS_WARN_IF(aRv.Failed())) {
  2116. return nullptr;
  2117. }
  2118. // We check permission here rather than pass the Promise to NotificationTask
  2119. // which leads to uglier code.
  2120. NotificationPermission permission = GetPermission(aGlobal, aRv);
  2121. // "If permission for notification's origin is not "granted", reject promise with a TypeError exception, and terminate these substeps."
  2122. if (NS_WARN_IF(aRv.Failed()) || permission == NotificationPermission::Denied) {
  2123. ErrorResult result;
  2124. result.ThrowTypeError<MSG_NOTIFICATION_PERMISSION_DENIED>();
  2125. p->MaybeReject(result);
  2126. return p.forget();
  2127. }
  2128. // "Otherwise, resolve promise with undefined."
  2129. // The Notification may still not be shown due to other errors, but the spec
  2130. // is not concerned with those.
  2131. p->MaybeResolveWithUndefined();
  2132. RefPtr<Notification> notification =
  2133. CreateAndShow(aCx, aGlobal, aTitle, aOptions, aScope, aRv);
  2134. if (NS_WARN_IF(aRv.Failed())) {
  2135. return nullptr;
  2136. }
  2137. return p.forget();
  2138. }
  2139. /* static */ already_AddRefed<Notification>
  2140. Notification::CreateAndShow(JSContext* aCx,
  2141. nsIGlobalObject* aGlobal,
  2142. const nsAString& aTitle,
  2143. const NotificationOptions& aOptions,
  2144. const nsAString& aScope,
  2145. ErrorResult& aRv)
  2146. {
  2147. MOZ_ASSERT(aGlobal);
  2148. RefPtr<Notification> notification = CreateInternal(aGlobal, EmptyString(),
  2149. aTitle, aOptions);
  2150. // Make a structured clone of the aOptions.mData object
  2151. JS::Rooted<JS::Value> data(aCx, aOptions.mData);
  2152. notification->InitFromJSVal(aCx, data, aRv);
  2153. if (NS_WARN_IF(aRv.Failed())) {
  2154. return nullptr;
  2155. }
  2156. notification->SetScope(aScope);
  2157. auto ref = MakeUnique<NotificationRef>(notification);
  2158. if (NS_WARN_IF(!ref->Initialized())) {
  2159. aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
  2160. return nullptr;
  2161. }
  2162. // Queue a task to show the notification.
  2163. nsCOMPtr<nsIRunnable> showNotificationTask =
  2164. new NotificationTask(Move(ref), NotificationTask::eShow);
  2165. nsresult rv = NS_DispatchToMainThread(showNotificationTask);
  2166. if (NS_WARN_IF(NS_FAILED(rv))) {
  2167. notification->DispatchTrustedEvent(NS_LITERAL_STRING("error"));
  2168. }
  2169. return notification.forget();
  2170. }
  2171. /* static */ nsresult
  2172. Notification::RemovePermission(nsIPrincipal* aPrincipal)
  2173. {
  2174. MOZ_ASSERT(XRE_IsParentProcess());
  2175. nsCOMPtr<nsIPermissionManager> permissionManager =
  2176. mozilla::services::GetPermissionManager();
  2177. if (!permissionManager) {
  2178. return NS_ERROR_FAILURE;
  2179. }
  2180. permissionManager->RemoveFromPrincipal(aPrincipal, "desktop-notification");
  2181. return NS_OK;
  2182. }
  2183. /* static */ nsresult
  2184. Notification::OpenSettings(nsIPrincipal* aPrincipal)
  2185. {
  2186. MOZ_ASSERT(XRE_IsParentProcess());
  2187. nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  2188. if (!obs) {
  2189. return NS_ERROR_FAILURE;
  2190. }
  2191. // Notify other observers so they can show settings UI.
  2192. obs->NotifyObservers(aPrincipal, "notifications-open-settings", nullptr);
  2193. return NS_OK;
  2194. }
  2195. NS_IMETHODIMP
  2196. Notification::Observe(nsISupports* aSubject, const char* aTopic,
  2197. const char16_t* aData)
  2198. {
  2199. AssertIsOnMainThread();
  2200. if (!strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) ||
  2201. !strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC)) {
  2202. nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
  2203. if (SameCOMIdentity(aSubject, window)) {
  2204. nsCOMPtr<nsIObserverService> obs =
  2205. mozilla::services::GetObserverService();
  2206. if (obs) {
  2207. obs->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
  2208. obs->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
  2209. }
  2210. CloseInternal();
  2211. }
  2212. }
  2213. return NS_OK;
  2214. }
  2215. } // namespace dom
  2216. } // namespace mozilla