ServiceWorkerWindowClient.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  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 file,
  4. * You can obtain one at http://mozilla.org/MPL/2.0/.
  5. */
  6. #include "ServiceWorkerWindowClient.h"
  7. #include "js/Value.h"
  8. #include "mozilla/Mutex.h"
  9. #include "mozilla/dom/ClientBinding.h"
  10. #include "mozilla/dom/Promise.h"
  11. #include "mozilla/dom/PromiseWorkerProxy.h"
  12. #include "mozilla/UniquePtr.h"
  13. #include "nsContentUtils.h"
  14. #include "nsGlobalWindow.h"
  15. #include "nsIDocShell.h"
  16. #include "nsIDocShellLoadInfo.h"
  17. #include "nsIDocument.h"
  18. #include "nsIGlobalObject.h"
  19. #include "nsIInterfaceRequestor.h"
  20. #include "nsIPrincipal.h"
  21. #include "nsIScriptSecurityManager.h"
  22. #include "nsIWebNavigation.h"
  23. #include "nsIWebProgress.h"
  24. #include "nsIWebProgressListener.h"
  25. #include "nsNetUtil.h"
  26. #include "nsString.h"
  27. #include "nsWeakReference.h"
  28. #include "ServiceWorker.h"
  29. #include "ServiceWorkerInfo.h"
  30. #include "ServiceWorkerManager.h"
  31. #include "ServiceWorkerPrivate.h"
  32. #include "WorkerPrivate.h"
  33. #include "WorkerScope.h"
  34. using namespace mozilla;
  35. using namespace mozilla::dom;
  36. using namespace mozilla::dom::workers;
  37. using mozilla::UniquePtr;
  38. JSObject*
  39. ServiceWorkerWindowClient::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
  40. {
  41. return WindowClientBinding::Wrap(aCx, this, aGivenProto);
  42. }
  43. namespace {
  44. class ResolveOrRejectPromiseRunnable final : public WorkerRunnable
  45. {
  46. RefPtr<PromiseWorkerProxy> mPromiseProxy;
  47. UniquePtr<ServiceWorkerClientInfo> mClientInfo;
  48. nsresult mRv;
  49. public:
  50. // Passing a null clientInfo will resolve the promise with a null value.
  51. ResolveOrRejectPromiseRunnable(
  52. WorkerPrivate* aWorkerPrivate, PromiseWorkerProxy* aPromiseProxy,
  53. UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
  54. : WorkerRunnable(aWorkerPrivate)
  55. , mPromiseProxy(aPromiseProxy)
  56. , mClientInfo(Move(aClientInfo))
  57. , mRv(NS_OK)
  58. {
  59. AssertIsOnMainThread();
  60. }
  61. // Reject the promise with passed nsresult.
  62. ResolveOrRejectPromiseRunnable(WorkerPrivate* aWorkerPrivate,
  63. PromiseWorkerProxy* aPromiseProxy,
  64. nsresult aRv)
  65. : WorkerRunnable(aWorkerPrivate)
  66. , mPromiseProxy(aPromiseProxy)
  67. , mClientInfo(nullptr)
  68. , mRv(aRv)
  69. {
  70. MOZ_ASSERT(NS_FAILED(aRv));
  71. AssertIsOnMainThread();
  72. }
  73. bool
  74. WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
  75. {
  76. MOZ_ASSERT(aWorkerPrivate);
  77. aWorkerPrivate->AssertIsOnWorkerThread();
  78. RefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
  79. MOZ_ASSERT(promise);
  80. if (NS_WARN_IF(NS_FAILED(mRv))) {
  81. promise->MaybeReject(mRv);
  82. } else if (mClientInfo) {
  83. RefPtr<ServiceWorkerWindowClient> client =
  84. new ServiceWorkerWindowClient(promise->GetParentObject(), *mClientInfo);
  85. promise->MaybeResolve(client);
  86. } else {
  87. promise->MaybeResolve(JS::NullHandleValue);
  88. }
  89. // Release the reference on the worker thread.
  90. mPromiseProxy->CleanUp();
  91. return true;
  92. }
  93. };
  94. class ClientFocusRunnable final : public Runnable
  95. {
  96. uint64_t mWindowId;
  97. RefPtr<PromiseWorkerProxy> mPromiseProxy;
  98. public:
  99. ClientFocusRunnable(uint64_t aWindowId, PromiseWorkerProxy* aPromiseProxy)
  100. : mWindowId(aWindowId)
  101. , mPromiseProxy(aPromiseProxy)
  102. {
  103. MOZ_ASSERT(mPromiseProxy);
  104. }
  105. NS_IMETHOD
  106. Run() override
  107. {
  108. AssertIsOnMainThread();
  109. nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
  110. UniquePtr<ServiceWorkerClientInfo> clientInfo;
  111. if (window) {
  112. nsCOMPtr<nsIDocument> doc = window->GetDocument();
  113. if (doc) {
  114. nsContentUtils::DispatchFocusChromeEvent(window->GetOuterWindow());
  115. clientInfo.reset(new ServiceWorkerClientInfo(doc));
  116. }
  117. }
  118. DispatchResult(Move(clientInfo));
  119. return NS_OK;
  120. }
  121. private:
  122. void
  123. DispatchResult(UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
  124. {
  125. AssertIsOnMainThread();
  126. MutexAutoLock lock(mPromiseProxy->Lock());
  127. if (mPromiseProxy->CleanedUp()) {
  128. return;
  129. }
  130. RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable;
  131. if (aClientInfo) {
  132. resolveRunnable = new ResolveOrRejectPromiseRunnable(
  133. mPromiseProxy->GetWorkerPrivate(), mPromiseProxy, Move(aClientInfo));
  134. } else {
  135. resolveRunnable = new ResolveOrRejectPromiseRunnable(
  136. mPromiseProxy->GetWorkerPrivate(), mPromiseProxy,
  137. NS_ERROR_DOM_INVALID_ACCESS_ERR);
  138. }
  139. resolveRunnable->Dispatch();
  140. }
  141. };
  142. } // namespace
  143. already_AddRefed<Promise>
  144. ServiceWorkerWindowClient::Focus(ErrorResult& aRv) const
  145. {
  146. WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
  147. MOZ_ASSERT(workerPrivate);
  148. workerPrivate->AssertIsOnWorkerThread();
  149. nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
  150. MOZ_ASSERT(global);
  151. RefPtr<Promise> promise = Promise::Create(global, aRv);
  152. if (NS_WARN_IF(aRv.Failed())) {
  153. return nullptr;
  154. }
  155. if (workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
  156. RefPtr<PromiseWorkerProxy> promiseProxy =
  157. PromiseWorkerProxy::Create(workerPrivate, promise);
  158. if (promiseProxy) {
  159. RefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
  160. promiseProxy);
  161. MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
  162. } else {
  163. promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
  164. }
  165. } else {
  166. promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
  167. }
  168. return promise.forget();
  169. }
  170. class WebProgressListener final : public nsIWebProgressListener,
  171. public nsSupportsWeakReference
  172. {
  173. public:
  174. NS_DECL_CYCLE_COLLECTING_ISUPPORTS
  175. NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(WebProgressListener,
  176. nsIWebProgressListener)
  177. WebProgressListener(PromiseWorkerProxy* aPromiseProxy,
  178. ServiceWorkerPrivate* aServiceWorkerPrivate,
  179. nsPIDOMWindowOuter* aWindow, nsIURI* aBaseURI)
  180. : mPromiseProxy(aPromiseProxy)
  181. , mServiceWorkerPrivate(aServiceWorkerPrivate)
  182. , mWindow(aWindow)
  183. , mBaseURI(aBaseURI)
  184. {
  185. MOZ_ASSERT(aPromiseProxy);
  186. MOZ_ASSERT(aServiceWorkerPrivate);
  187. MOZ_ASSERT(aWindow);
  188. MOZ_ASSERT(aWindow->IsOuterWindow());
  189. MOZ_ASSERT(aBaseURI);
  190. AssertIsOnMainThread();
  191. mServiceWorkerPrivate->StoreISupports(static_cast<nsIWebProgressListener*>(this));
  192. }
  193. NS_IMETHOD
  194. OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
  195. uint32_t aStateFlags, nsresult aStatus) override
  196. {
  197. if (!(aStateFlags & STATE_IS_DOCUMENT) ||
  198. !(aStateFlags & (STATE_STOP | STATE_TRANSFERRING))) {
  199. return NS_OK;
  200. }
  201. // This is safe because our caller holds a strong ref.
  202. mServiceWorkerPrivate->RemoveISupports(static_cast<nsIWebProgressListener*>(this));
  203. aWebProgress->RemoveProgressListener(this);
  204. WorkerPrivate* workerPrivate;
  205. {
  206. MutexAutoLock lock(mPromiseProxy->Lock());
  207. if (mPromiseProxy->CleanedUp()) {
  208. return NS_OK;
  209. }
  210. workerPrivate = mPromiseProxy->GetWorkerPrivate();
  211. }
  212. nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
  213. RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable;
  214. UniquePtr<ServiceWorkerClientInfo> clientInfo;
  215. if (!doc) {
  216. resolveRunnable = new ResolveOrRejectPromiseRunnable(
  217. workerPrivate, mPromiseProxy, NS_ERROR_TYPE_ERR);
  218. resolveRunnable->Dispatch();
  219. return NS_OK;
  220. }
  221. // Check same origin.
  222. nsCOMPtr<nsIScriptSecurityManager> securityManager =
  223. nsContentUtils::GetSecurityManager();
  224. nsresult rv = securityManager->CheckSameOriginURI(doc->GetOriginalURI(),
  225. mBaseURI, false);
  226. if (NS_SUCCEEDED(rv)) {
  227. nsContentUtils::DispatchFocusChromeEvent(mWindow->GetOuterWindow());
  228. clientInfo.reset(new ServiceWorkerClientInfo(doc));
  229. }
  230. resolveRunnable = new ResolveOrRejectPromiseRunnable(
  231. workerPrivate, mPromiseProxy, Move(clientInfo));
  232. resolveRunnable->Dispatch();
  233. return NS_OK;
  234. }
  235. NS_IMETHOD
  236. OnProgressChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
  237. int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
  238. int32_t aCurTotalProgress,
  239. int32_t aMaxTotalProgress) override
  240. {
  241. MOZ_CRASH("Unexpected notification.");
  242. return NS_OK;
  243. }
  244. NS_IMETHOD
  245. OnLocationChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
  246. nsIURI* aLocation, uint32_t aFlags) override
  247. {
  248. MOZ_CRASH("Unexpected notification.");
  249. return NS_OK;
  250. }
  251. NS_IMETHOD
  252. OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
  253. nsresult aStatus, const char16_t* aMessage) override
  254. {
  255. MOZ_CRASH("Unexpected notification.");
  256. return NS_OK;
  257. }
  258. NS_IMETHOD
  259. OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
  260. uint32_t aState) override
  261. {
  262. MOZ_CRASH("Unexpected notification.");
  263. return NS_OK;
  264. }
  265. private:
  266. ~WebProgressListener() {}
  267. RefPtr<PromiseWorkerProxy> mPromiseProxy;
  268. RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
  269. nsCOMPtr<nsPIDOMWindowOuter> mWindow;
  270. nsCOMPtr<nsIURI> mBaseURI;
  271. };
  272. NS_IMPL_CYCLE_COLLECTING_ADDREF(WebProgressListener)
  273. NS_IMPL_CYCLE_COLLECTING_RELEASE(WebProgressListener)
  274. NS_IMPL_CYCLE_COLLECTION(WebProgressListener, mPromiseProxy,
  275. mServiceWorkerPrivate, mWindow)
  276. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebProgressListener)
  277. NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
  278. NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
  279. NS_INTERFACE_MAP_END
  280. class ClientNavigateRunnable final : public Runnable
  281. {
  282. uint64_t mWindowId;
  283. nsString mUrl;
  284. nsCString mBaseUrl;
  285. nsString mScope;
  286. RefPtr<PromiseWorkerProxy> mPromiseProxy;
  287. MOZ_INIT_OUTSIDE_CTOR WorkerPrivate* mWorkerPrivate;
  288. public:
  289. ClientNavigateRunnable(uint64_t aWindowId, const nsAString& aUrl,
  290. const nsAString& aScope,
  291. PromiseWorkerProxy* aPromiseProxy)
  292. : mWindowId(aWindowId)
  293. , mUrl(aUrl)
  294. , mScope(aScope)
  295. , mPromiseProxy(aPromiseProxy)
  296. , mWorkerPrivate(nullptr)
  297. {
  298. MOZ_ASSERT(aPromiseProxy);
  299. MOZ_ASSERT(aPromiseProxy->GetWorkerPrivate());
  300. aPromiseProxy->GetWorkerPrivate()->AssertIsOnWorkerThread();
  301. }
  302. NS_IMETHOD
  303. Run() override
  304. {
  305. AssertIsOnMainThread();
  306. nsCOMPtr<nsIPrincipal> principal;
  307. {
  308. MutexAutoLock lock(mPromiseProxy->Lock());
  309. if (mPromiseProxy->CleanedUp()) {
  310. return NS_OK;
  311. }
  312. mWorkerPrivate = mPromiseProxy->GetWorkerPrivate();
  313. WorkerPrivate::LocationInfo& info = mWorkerPrivate->GetLocationInfo();
  314. mBaseUrl = info.mHref;
  315. principal = mWorkerPrivate->GetPrincipal();
  316. MOZ_DIAGNOSTIC_ASSERT(principal);
  317. }
  318. nsCOMPtr<nsIURI> baseUrl;
  319. nsCOMPtr<nsIURI> url;
  320. nsresult rv = ParseUrl(getter_AddRefs(baseUrl), getter_AddRefs(url));
  321. if (NS_WARN_IF(NS_FAILED(rv))) {
  322. return RejectPromise(NS_ERROR_TYPE_ERR);
  323. }
  324. rv = principal->CheckMayLoad(url, true, false);
  325. if (NS_WARN_IF(NS_FAILED(rv))) {
  326. return RejectPromise(rv);
  327. }
  328. nsGlobalWindow* window;
  329. rv = Navigate(url, principal, &window);
  330. if (NS_WARN_IF(NS_FAILED(rv))) {
  331. return RejectPromise(rv);
  332. }
  333. nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
  334. nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
  335. if (NS_WARN_IF(!webProgress)) {
  336. return NS_ERROR_FAILURE;
  337. }
  338. RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
  339. if (!swm) {
  340. return NS_ERROR_FAILURE;
  341. }
  342. RefPtr<ServiceWorkerRegistrationInfo> registration =
  343. swm->GetRegistration(principal, NS_ConvertUTF16toUTF8(mScope));
  344. if (NS_WARN_IF(!registration)) {
  345. return NS_ERROR_FAILURE;
  346. }
  347. RefPtr<ServiceWorkerInfo> serviceWorkerInfo =
  348. registration->GetServiceWorkerInfoById(mWorkerPrivate->ServiceWorkerID());
  349. if (NS_WARN_IF(!serviceWorkerInfo)) {
  350. return NS_ERROR_FAILURE;
  351. }
  352. nsCOMPtr<nsIWebProgressListener> listener =
  353. new WebProgressListener(mPromiseProxy, serviceWorkerInfo->WorkerPrivate(),
  354. window->GetOuterWindow(), baseUrl);
  355. rv = webProgress->AddProgressListener(
  356. listener, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
  357. if (NS_WARN_IF(NS_FAILED(rv))) {
  358. return RejectPromise(rv);
  359. }
  360. return NS_OK;
  361. }
  362. private:
  363. nsresult
  364. RejectPromise(nsresult aRv)
  365. {
  366. MOZ_ASSERT(mWorkerPrivate);
  367. RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable =
  368. new ResolveOrRejectPromiseRunnable(mWorkerPrivate, mPromiseProxy, aRv);
  369. resolveRunnable->Dispatch();
  370. return NS_OK;
  371. }
  372. nsresult
  373. ResolvePromise(UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
  374. {
  375. MOZ_ASSERT(mWorkerPrivate);
  376. RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable =
  377. new ResolveOrRejectPromiseRunnable(mWorkerPrivate, mPromiseProxy,
  378. Move(aClientInfo));
  379. resolveRunnable->Dispatch();
  380. return NS_OK;
  381. }
  382. nsresult
  383. ParseUrl(nsIURI** aBaseUrl, nsIURI** aUrl)
  384. {
  385. MOZ_ASSERT(aBaseUrl);
  386. MOZ_ASSERT(aUrl);
  387. AssertIsOnMainThread();
  388. nsCOMPtr<nsIURI> baseUrl;
  389. nsresult rv = NS_NewURI(getter_AddRefs(baseUrl), mBaseUrl);
  390. NS_ENSURE_SUCCESS(rv, rv);
  391. nsCOMPtr<nsIURI> url;
  392. rv = NS_NewURI(getter_AddRefs(url), mUrl, nullptr, baseUrl);
  393. NS_ENSURE_SUCCESS(rv, rv);
  394. baseUrl.forget(aBaseUrl);
  395. url.forget(aUrl);
  396. return NS_OK;
  397. }
  398. nsresult
  399. Navigate(nsIURI* aUrl, nsIPrincipal* aPrincipal, nsGlobalWindow** aWindow)
  400. {
  401. MOZ_ASSERT(aWindow);
  402. nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
  403. if (NS_WARN_IF(!window)) {
  404. return NS_ERROR_TYPE_ERR;
  405. }
  406. nsCOMPtr<nsIDocument> doc = window->GetDocument();
  407. if (NS_WARN_IF(!doc)) {
  408. return NS_ERROR_TYPE_ERR;
  409. }
  410. if (NS_WARN_IF(!doc->IsActive())) {
  411. return NS_ERROR_TYPE_ERR;
  412. }
  413. nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
  414. if (NS_WARN_IF(!docShell)) {
  415. return NS_ERROR_TYPE_ERR;
  416. }
  417. nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
  418. nsresult rv = docShell->CreateLoadInfo(getter_AddRefs(loadInfo));
  419. if (NS_WARN_IF(NS_FAILED(rv))) {
  420. return rv;
  421. }
  422. loadInfo->SetTriggeringPrincipal(aPrincipal);
  423. loadInfo->SetReferrer(doc->GetOriginalURI());
  424. loadInfo->SetReferrerPolicy(doc->GetReferrerPolicy());
  425. loadInfo->SetLoadType(nsIDocShellLoadInfo::loadStopContentAndReplace);
  426. loadInfo->SetSourceDocShell(docShell);
  427. rv =
  428. docShell->LoadURI(aUrl, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, true);
  429. if (NS_WARN_IF(NS_FAILED(rv))) {
  430. return rv;
  431. }
  432. *aWindow = window;
  433. return NS_OK;
  434. }
  435. };
  436. already_AddRefed<Promise>
  437. ServiceWorkerWindowClient::Navigate(const nsAString& aUrl, ErrorResult& aRv)
  438. {
  439. WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
  440. MOZ_ASSERT(workerPrivate);
  441. workerPrivate->AssertIsOnWorkerThread();
  442. nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
  443. MOZ_ASSERT(global);
  444. RefPtr<Promise> promise = Promise::Create(global, aRv);
  445. if (NS_WARN_IF(aRv.Failed())) {
  446. return nullptr;
  447. }
  448. if (aUrl.EqualsLiteral("about:blank")) {
  449. promise->MaybeReject(NS_ERROR_TYPE_ERR);
  450. return promise.forget();
  451. }
  452. ServiceWorkerGlobalScope* globalScope =
  453. static_cast<ServiceWorkerGlobalScope*>(workerPrivate->GlobalScope());
  454. nsString scope;
  455. globalScope->GetScope(scope);
  456. RefPtr<PromiseWorkerProxy> promiseProxy =
  457. PromiseWorkerProxy::Create(workerPrivate, promise);
  458. if (promiseProxy) {
  459. RefPtr<ClientNavigateRunnable> r =
  460. new ClientNavigateRunnable(mWindowId, aUrl, scope, promiseProxy);
  461. MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
  462. } else {
  463. promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
  464. }
  465. return promise.forget();
  466. }