LCOV - code coverage report
Current view: top level - dom/broadcastchannel - BroadcastChannel.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 245 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 57 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "BroadcastChannel.h"
       8             : #include "BroadcastChannelChild.h"
       9             : #include "mozilla/dom/BroadcastChannelBinding.h"
      10             : #include "mozilla/dom/Navigator.h"
      11             : #include "mozilla/dom/File.h"
      12             : #include "mozilla/dom/StructuredCloneHolder.h"
      13             : #include "mozilla/dom/ipc/StructuredCloneData.h"
      14             : #include "mozilla/ipc/BackgroundChild.h"
      15             : #include "mozilla/ipc/BackgroundUtils.h"
      16             : #include "mozilla/ipc/PBackgroundChild.h"
      17             : #include "nsContentUtils.h"
      18             : #include "WorkerPrivate.h"
      19             : #include "WorkerRunnable.h"
      20             : 
      21             : #include "nsIBFCacheEntry.h"
      22             : #include "nsIDocument.h"
      23             : #include "nsISupportsPrimitives.h"
      24             : 
      25             : #ifdef XP_WIN
      26             : #undef PostMessage
      27             : #endif
      28             : 
      29             : namespace mozilla {
      30             : 
      31             : using namespace ipc;
      32             : 
      33             : namespace dom {
      34             : 
      35             : using namespace workers;
      36             : using namespace ipc;
      37             : 
      38             : class BroadcastChannelMessage final : public StructuredCloneDataNoTransfers
      39             : {
      40             : public:
      41           0 :   NS_INLINE_DECL_REFCOUNTING(BroadcastChannelMessage)
      42             : 
      43           0 :   BroadcastChannelMessage()
      44           0 :     : StructuredCloneDataNoTransfers()
      45           0 :   {}
      46             : 
      47             : private:
      48           0 :   ~BroadcastChannelMessage()
      49           0 :   {}
      50             : };
      51             : 
      52             : namespace {
      53             : 
      54             : nsIPrincipal*
      55           0 : GetPrincipalFromWorkerPrivate(WorkerPrivate* aWorkerPrivate)
      56             : {
      57           0 :   nsIPrincipal* principal = aWorkerPrivate->GetPrincipal();
      58           0 :   if (principal) {
      59           0 :     return principal;
      60             :   }
      61             : 
      62             :   // Walk up to our containing page
      63           0 :   WorkerPrivate* wp = aWorkerPrivate;
      64           0 :   while (wp->GetParent()) {
      65           0 :     wp = wp->GetParent();
      66             :   }
      67             : 
      68           0 :   return wp->GetPrincipal();
      69             : }
      70             : 
      71           0 : class InitializeRunnable final : public WorkerMainThreadRunnable
      72             : {
      73             : public:
      74           0 :   InitializeRunnable(WorkerPrivate* aWorkerPrivate, nsACString& aOrigin,
      75             :                      PrincipalInfo& aPrincipalInfo, ErrorResult& aRv)
      76           0 :     : WorkerMainThreadRunnable(aWorkerPrivate,
      77           0 :                                NS_LITERAL_CSTRING("BroadcastChannel :: Initialize"))
      78           0 :     , mWorkerPrivate(GetCurrentThreadWorkerPrivate())
      79             :     , mOrigin(aOrigin)
      80             :     , mPrincipalInfo(aPrincipalInfo)
      81           0 :     , mRv(aRv)
      82             :   {
      83           0 :     MOZ_ASSERT(mWorkerPrivate);
      84           0 :   }
      85             : 
      86           0 :   bool MainThreadRun() override
      87             :   {
      88           0 :     MOZ_ASSERT(NS_IsMainThread());
      89             : 
      90           0 :     nsIPrincipal* principal = GetPrincipalFromWorkerPrivate(mWorkerPrivate);
      91           0 :     if (!principal) {
      92           0 :       mRv.Throw(NS_ERROR_FAILURE);
      93           0 :       return true;
      94             :     }
      95             : 
      96           0 :     mRv = PrincipalToPrincipalInfo(principal, &mPrincipalInfo);
      97           0 :     if (NS_WARN_IF(mRv.Failed())) {
      98           0 :       return true;
      99             :     }
     100             : 
     101           0 :     mRv = principal->GetOrigin(mOrigin);
     102           0 :     if (NS_WARN_IF(mRv.Failed())) {
     103           0 :       return true;
     104             :     }
     105             : 
     106             :     // Walk up to our containing page
     107           0 :     WorkerPrivate* wp = mWorkerPrivate;
     108           0 :     while (wp->GetParent()) {
     109           0 :       wp = wp->GetParent();
     110             :     }
     111             : 
     112             :     // Window doesn't exist for some kind of workers (eg: SharedWorkers)
     113           0 :     nsPIDOMWindowInner* window = wp->GetWindow();
     114           0 :     if (!window) {
     115           0 :       return true;
     116             :     }
     117             : 
     118           0 :     return true;
     119             :   }
     120             : 
     121             : private:
     122             :   WorkerPrivate* mWorkerPrivate;
     123             :   nsACString& mOrigin;
     124             :   PrincipalInfo& mPrincipalInfo;
     125             :   ErrorResult& mRv;
     126             : };
     127             : 
     128             : class BCPostMessageRunnable final : public nsIRunnable,
     129             :                                     public nsICancelableRunnable
     130             : {
     131             : public:
     132             :   NS_DECL_ISUPPORTS
     133             : 
     134           0 :   BCPostMessageRunnable(BroadcastChannelChild* aActor,
     135             :                         BroadcastChannelMessage* aData)
     136           0 :     : mActor(aActor)
     137           0 :     , mData(aData)
     138             :   {
     139           0 :     MOZ_ASSERT(mActor);
     140           0 :   }
     141             : 
     142           0 :   NS_IMETHOD Run() override
     143             :   {
     144           0 :     MOZ_ASSERT(mActor);
     145           0 :     if (mActor->IsActorDestroyed()) {
     146           0 :       return NS_OK;
     147             :     }
     148             : 
     149           0 :     ClonedMessageData message;
     150           0 :     mData->BuildClonedMessageDataForBackgroundChild(mActor->Manager(), message);
     151           0 :     mActor->SendPostMessage(message);
     152           0 :     return NS_OK;
     153             :   }
     154             : 
     155           0 :   nsresult Cancel() override
     156             :   {
     157           0 :     mActor = nullptr;
     158           0 :     return NS_OK;
     159             :   }
     160             : 
     161             : private:
     162           0 :   ~BCPostMessageRunnable() {}
     163             : 
     164             :   RefPtr<BroadcastChannelChild> mActor;
     165             :   RefPtr<BroadcastChannelMessage> mData;
     166             : };
     167             : 
     168           0 : NS_IMPL_ISUPPORTS(BCPostMessageRunnable, nsICancelableRunnable, nsIRunnable)
     169             : 
     170             : class CloseRunnable final : public nsIRunnable,
     171             :                             public nsICancelableRunnable
     172             : {
     173             : public:
     174             :   NS_DECL_ISUPPORTS
     175             : 
     176           0 :   explicit CloseRunnable(BroadcastChannel* aBC)
     177           0 :     : mBC(aBC)
     178             :   {
     179           0 :     MOZ_ASSERT(mBC);
     180           0 :   }
     181             : 
     182           0 :   NS_IMETHOD Run() override
     183             :   {
     184           0 :     mBC->Shutdown();
     185           0 :     return NS_OK;
     186             :   }
     187             : 
     188           0 :   nsresult Cancel() override
     189             :   {
     190           0 :     mBC = nullptr;
     191           0 :     return NS_OK;
     192             :   }
     193             : 
     194             : private:
     195           0 :   ~CloseRunnable() {}
     196             : 
     197             :   RefPtr<BroadcastChannel> mBC;
     198             : };
     199             : 
     200           0 : NS_IMPL_ISUPPORTS(CloseRunnable, nsICancelableRunnable, nsIRunnable)
     201             : 
     202             : class TeardownRunnable final : public nsIRunnable,
     203             :                                public nsICancelableRunnable
     204             : {
     205             : public:
     206             :   NS_DECL_ISUPPORTS
     207             : 
     208           0 :   explicit TeardownRunnable(BroadcastChannelChild* aActor)
     209           0 :     : mActor(aActor)
     210             :   {
     211           0 :     MOZ_ASSERT(mActor);
     212           0 :   }
     213             : 
     214           0 :   NS_IMETHOD Run() override
     215             :   {
     216           0 :     MOZ_ASSERT(mActor);
     217           0 :     if (!mActor->IsActorDestroyed()) {
     218           0 :       mActor->SendClose();
     219             :     }
     220           0 :     return NS_OK;
     221             :   }
     222             : 
     223           0 :   nsresult Cancel() override
     224             :   {
     225           0 :     mActor = nullptr;
     226           0 :     return NS_OK;
     227             :   }
     228             : 
     229             : private:
     230           0 :   ~TeardownRunnable() {}
     231             : 
     232             :   RefPtr<BroadcastChannelChild> mActor;
     233             : };
     234             : 
     235           0 : NS_IMPL_ISUPPORTS(TeardownRunnable, nsICancelableRunnable, nsIRunnable)
     236             : 
     237             : class BroadcastChannelWorkerHolder final : public workers::WorkerHolder
     238             : {
     239             :   BroadcastChannel* mChannel;
     240             : 
     241             : public:
     242           0 :   explicit BroadcastChannelWorkerHolder(BroadcastChannel* aChannel)
     243           0 :     : mChannel(aChannel)
     244             :   {
     245           0 :     MOZ_COUNT_CTOR(BroadcastChannelWorkerHolder);
     246           0 :   }
     247             : 
     248           0 :   virtual bool Notify(workers::Status aStatus) override
     249             :   {
     250           0 :     if (aStatus >= Closing) {
     251           0 :       mChannel->Shutdown();
     252             :     }
     253             : 
     254           0 :     return true;
     255             :   }
     256             : 
     257             : private:
     258           0 :   ~BroadcastChannelWorkerHolder()
     259           0 :   {
     260           0 :     MOZ_COUNT_DTOR(BroadcastChannelWorkerHolder);
     261           0 :   }
     262             : };
     263             : 
     264             : } // namespace
     265             : 
     266           0 : BroadcastChannel::BroadcastChannel(nsPIDOMWindowInner* aWindow,
     267             :                                    const PrincipalInfo& aPrincipalInfo,
     268             :                                    const nsACString& aOrigin,
     269           0 :                                    const nsAString& aChannel)
     270             :   : DOMEventTargetHelper(aWindow)
     271             :   , mWorkerHolder(nullptr)
     272           0 :   , mPrincipalInfo(new PrincipalInfo(aPrincipalInfo))
     273             :   , mOrigin(aOrigin)
     274             :   , mChannel(aChannel)
     275             :   , mInnerID(0)
     276           0 :   , mState(StateActive)
     277             : {
     278             :   // Window can be null in workers
     279             : 
     280           0 :   KeepAliveIfHasListenersFor(NS_LITERAL_STRING("message"));
     281           0 : }
     282             : 
     283           0 : BroadcastChannel::~BroadcastChannel()
     284             : {
     285           0 :   Shutdown();
     286           0 :   MOZ_ASSERT(!mWorkerHolder);
     287           0 : }
     288             : 
     289             : JSObject*
     290           0 : BroadcastChannel::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
     291             : {
     292           0 :   return BroadcastChannelBinding::Wrap(aCx, this, aGivenProto);
     293             : }
     294             : 
     295             : /* static */ already_AddRefed<BroadcastChannel>
     296           0 : BroadcastChannel::Constructor(const GlobalObject& aGlobal,
     297             :                               const nsAString& aChannel,
     298             :                               ErrorResult& aRv)
     299             : {
     300             :   nsCOMPtr<nsPIDOMWindowInner> window =
     301           0 :     do_QueryInterface(aGlobal.GetAsSupports());
     302             :   // Window is null in workers.
     303             : 
     304           0 :   nsAutoCString origin;
     305           0 :   PrincipalInfo principalInfo;
     306           0 :   WorkerPrivate* workerPrivate = nullptr;
     307             : 
     308           0 :   if (NS_IsMainThread()) {
     309           0 :     nsCOMPtr<nsIGlobalObject> incumbent = mozilla::dom::GetIncumbentGlobal();
     310             : 
     311           0 :     if (!incumbent) {
     312           0 :       aRv.Throw(NS_ERROR_FAILURE);
     313           0 :       return nullptr;
     314             :     }
     315             : 
     316           0 :     nsIPrincipal* principal = incumbent->PrincipalOrNull();
     317           0 :     if (!principal) {
     318           0 :       aRv.Throw(NS_ERROR_UNEXPECTED);
     319           0 :       return nullptr;
     320             :     }
     321             : 
     322           0 :     aRv = principal->GetOrigin(origin);
     323           0 :     if (NS_WARN_IF(aRv.Failed())) {
     324           0 :       return nullptr;
     325             :     }
     326             : 
     327           0 :     aRv = PrincipalToPrincipalInfo(principal, &principalInfo);
     328           0 :     if (NS_WARN_IF(aRv.Failed())) {
     329           0 :       return nullptr;
     330             :     }
     331             :   } else {
     332           0 :     JSContext* cx = aGlobal.Context();
     333           0 :     workerPrivate = GetWorkerPrivateFromContext(cx);
     334           0 :     MOZ_ASSERT(workerPrivate);
     335             : 
     336             :     RefPtr<InitializeRunnable> runnable =
     337           0 :       new InitializeRunnable(workerPrivate, origin, principalInfo, aRv);
     338           0 :     runnable->Dispatch(Closing, aRv);
     339             :   }
     340             : 
     341           0 :   if (aRv.Failed()) {
     342           0 :     return nullptr;
     343             :   }
     344             : 
     345             :   RefPtr<BroadcastChannel> bc =
     346           0 :     new BroadcastChannel(window, principalInfo, origin, aChannel);
     347             : 
     348             :   // Register this component to PBackground.
     349           0 :   PBackgroundChild* actor = BackgroundChild::GetForCurrentThread();
     350           0 :   if (actor) {
     351           0 :     bc->ActorCreated(actor);
     352             :   } else {
     353           0 :     BackgroundChild::GetOrCreateForCurrentThread(bc);
     354             :   }
     355             : 
     356           0 :   if (!workerPrivate) {
     357           0 :     MOZ_ASSERT(window);
     358           0 :     MOZ_ASSERT(window->IsInnerWindow());
     359           0 :     bc->mInnerID = window->WindowID();
     360             : 
     361             :     // Register as observer for inner-window-destroyed.
     362           0 :     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     363           0 :     if (obs) {
     364           0 :       obs->AddObserver(bc, "inner-window-destroyed", false);
     365             :     }
     366             :   } else {
     367           0 :     bc->mWorkerHolder = new BroadcastChannelWorkerHolder(bc);
     368           0 :     if (NS_WARN_IF(!bc->mWorkerHolder->HoldWorker(workerPrivate, Closing))) {
     369           0 :       bc->mWorkerHolder = nullptr;
     370           0 :       aRv.Throw(NS_ERROR_FAILURE);
     371           0 :       return nullptr;
     372             :     }
     373             :   }
     374             : 
     375           0 :   return bc.forget();
     376             : }
     377             : 
     378             : void
     379           0 : BroadcastChannel::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
     380             :                               ErrorResult& aRv)
     381             : {
     382           0 :   if (mState != StateActive) {
     383           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     384           0 :     return;
     385             :   }
     386             : 
     387           0 :   PostMessageInternal(aCx, aMessage, aRv);
     388             : }
     389             : 
     390             : void
     391           0 : BroadcastChannel::PostMessageInternal(JSContext* aCx,
     392             :                                       JS::Handle<JS::Value> aMessage,
     393             :                                       ErrorResult& aRv)
     394             : {
     395           0 :   RefPtr<BroadcastChannelMessage> data = new BroadcastChannelMessage();
     396             : 
     397           0 :   data->Write(aCx, aMessage, aRv);
     398           0 :   if (NS_WARN_IF(aRv.Failed())) {
     399           0 :     return;
     400             :   }
     401             : 
     402           0 :   PostMessageData(data);
     403             : }
     404             : 
     405             : void
     406           0 : BroadcastChannel::PostMessageData(BroadcastChannelMessage* aData)
     407             : {
     408           0 :   RemoveDocFromBFCache();
     409             : 
     410           0 :   if (mActor) {
     411             :     RefPtr<BCPostMessageRunnable> runnable =
     412           0 :       new BCPostMessageRunnable(mActor, aData);
     413             : 
     414           0 :     if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
     415           0 :       NS_WARNING("Failed to dispatch to the current thread!");
     416             :     }
     417             : 
     418           0 :     return;
     419             :   }
     420             : 
     421           0 :   mPendingMessages.AppendElement(aData);
     422             : }
     423             : 
     424             : void
     425           0 : BroadcastChannel::Close()
     426             : {
     427           0 :   if (mState != StateActive) {
     428           0 :     return;
     429             :   }
     430             : 
     431           0 :   if (mPendingMessages.IsEmpty()) {
     432             :     // We cannot call Shutdown() immediatelly because we could have some
     433             :     // postMessage runnable already dispatched. Instead, we change the state to
     434             :     // StateClosed and we shutdown the actor asynchrounsly.
     435             : 
     436           0 :     mState = StateClosed;
     437           0 :     RefPtr<CloseRunnable> runnable = new CloseRunnable(this);
     438             : 
     439           0 :     if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
     440           0 :       NS_WARNING("Failed to dispatch to the current thread!");
     441             :     }
     442             :   } else {
     443           0 :     MOZ_ASSERT(!mActor);
     444           0 :     mState = StateClosing;
     445             :   }
     446             : }
     447             : 
     448             : void
     449           0 : BroadcastChannel::ActorFailed()
     450             : {
     451           0 :   MOZ_CRASH("Failed to create a PBackgroundChild actor!");
     452             : }
     453             : 
     454             : void
     455           0 : BroadcastChannel::ActorCreated(PBackgroundChild* aActor)
     456             : {
     457           0 :   MOZ_ASSERT(aActor);
     458             : 
     459           0 :   if (mState == StateClosed) {
     460           0 :     return;
     461             :   }
     462             : 
     463             :   PBroadcastChannelChild* actor =
     464           0 :     aActor->SendPBroadcastChannelConstructor(*mPrincipalInfo, mOrigin, mChannel);
     465             : 
     466           0 :   mActor = static_cast<BroadcastChannelChild*>(actor);
     467           0 :   MOZ_ASSERT(mActor);
     468             : 
     469           0 :   mActor->SetParent(this);
     470             : 
     471             :   // Flush pending messages.
     472           0 :   for (uint32_t i = 0; i < mPendingMessages.Length(); ++i) {
     473           0 :     PostMessageData(mPendingMessages[i]);
     474             :   }
     475             : 
     476           0 :   mPendingMessages.Clear();
     477             : 
     478           0 :   if (mState == StateClosing) {
     479           0 :     Shutdown();
     480             :   }
     481             : }
     482             : 
     483             : void
     484           0 : BroadcastChannel::Shutdown()
     485             : {
     486           0 :   mState = StateClosed;
     487             : 
     488             :   // The DTOR of this WorkerHolder will release the worker for us.
     489           0 :   mWorkerHolder = nullptr;
     490             : 
     491           0 :   if (mActor) {
     492           0 :     mActor->SetParent(nullptr);
     493             : 
     494           0 :     RefPtr<TeardownRunnable> runnable = new TeardownRunnable(mActor);
     495           0 :     NS_DispatchToCurrentThread(runnable);
     496             : 
     497           0 :     mActor = nullptr;
     498             :   }
     499             : 
     500           0 :   IgnoreKeepAliveIfHasListenersFor(NS_LITERAL_STRING("message"));
     501           0 : }
     502             : 
     503             : NS_IMETHODIMP
     504           0 : BroadcastChannel::Observe(nsISupports* aSubject, const char* aTopic,
     505             :                           const char16_t* aData)
     506             : {
     507           0 :   MOZ_ASSERT(NS_IsMainThread());
     508           0 :   MOZ_ASSERT(!strcmp(aTopic, "inner-window-destroyed"));
     509             : 
     510             :   // If the window is destroyed we have to release the reference that we are
     511             :   // keeping.
     512           0 :   nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
     513           0 :   NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
     514             : 
     515             :   uint64_t innerID;
     516           0 :   nsresult rv = wrapper->GetData(&innerID);
     517           0 :   NS_ENSURE_SUCCESS(rv, rv);
     518             : 
     519           0 :   if (innerID == mInnerID) {
     520           0 :     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     521           0 :     if (obs) {
     522           0 :       obs->RemoveObserver(this, "inner-window-destroyed");
     523             :     }
     524             : 
     525           0 :     Shutdown();
     526             :   }
     527             : 
     528           0 :   return NS_OK;
     529             : }
     530             : 
     531             : void
     532           0 : BroadcastChannel::RemoveDocFromBFCache()
     533             : {
     534           0 :   if (!NS_IsMainThread()) {
     535           0 :     return;
     536             :   }
     537             : 
     538           0 :   nsPIDOMWindowInner* window = GetOwner();
     539           0 :   if (!window) {
     540           0 :     return;
     541             :   }
     542             : 
     543           0 :   nsIDocument* doc = window->GetExtantDoc();
     544           0 :   if (!doc) {
     545           0 :     return;
     546             :   }
     547             : 
     548           0 :   nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry();
     549           0 :   if (!bfCacheEntry) {
     550           0 :     return;
     551             :   }
     552             : 
     553           0 :   bfCacheEntry->RemoveFromBFCacheSync();
     554             : }
     555             : 
     556             : NS_IMPL_CYCLE_COLLECTION_CLASS(BroadcastChannel)
     557             : 
     558           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BroadcastChannel,
     559             :                                                   DOMEventTargetHelper)
     560           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     561             : 
     562           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BroadcastChannel,
     563             :                                                 DOMEventTargetHelper)
     564           0 :   tmp->Shutdown();
     565           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     566             : 
     567           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BroadcastChannel)
     568           0 :   NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
     569           0 :   NS_INTERFACE_MAP_ENTRY(nsIObserver)
     570           0 : NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
     571             : 
     572           0 : NS_IMPL_ADDREF_INHERITED(BroadcastChannel, DOMEventTargetHelper)
     573           0 : NS_IMPL_RELEASE_INHERITED(BroadcastChannel, DOMEventTargetHelper)
     574             : 
     575             : } // namespace dom
     576             : } // namespace mozilla

Generated by: LCOV version 1.13