diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index e3ae9c9870ea..e9353b3b9145 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -2260,7 +2260,9 @@ RuntimeService::CreateSharedWorkerInternal(const GlobalObject& aGlobal, WorkerPrivate::LoadInfo loadInfo; nsresult rv = WorkerPrivate::GetLoadInfo(cx, window, nullptr, aScriptURL, - false, &loadInfo); + false, + WorkerPrivate::OverrideLoadGroup, + &loadInfo); NS_ENSURE_SUCCESS(rv, rv); return CreateSharedWorkerFromLoadInfo(cx, &loadInfo, aScriptURL, aName, aType, @@ -2314,6 +2316,11 @@ RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx, NS_ENSURE_TRUE(workerPrivate, rv.ErrorCode()); created = true; + } else { + // If we're attaching to an existing SharedWorker private, then we + // must update the overriden load group to account for our document's + // load group. + workerPrivate->UpdateOverridenLoadGroup(aLoadInfo->mLoadGroup); } nsRefPtr sharedWorker = new SharedWorker(window, workerPrivate); diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 35b0abb9d949..3332469552c1 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -2483,10 +2483,7 @@ ServiceWorkerManager::CreateServiceWorker(nsIPrincipal* aPrincipal, // - use remote tabs = false // Alternatively we could persist the original load group values and use // them here. - rv = NS_NewLoadGroup(getter_AddRefs(info.mLoadGroup), info.mPrincipal); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + WorkerPrivate::OverrideLoadInfoLoadGroup(info); nsRefPtr serviceWorker; RuntimeService* rs = RuntimeService::GetOrCreateService(); diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index c360c10a398e..d3c268726c1b 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -15,16 +15,19 @@ #include "nsIDOMMessageEvent.h" #include "nsIDocument.h" #include "nsIDocShell.h" +#include "nsIInterfaceRequestor.h" #include "nsIMemoryReporter.h" #include "nsIPermissionManager.h" #include "nsIScriptError.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptSecurityManager.h" +#include "nsITabChild.h" #include "nsITextToSubURI.h" #include "nsIThreadInternal.h" #include "nsITimer.h" #include "nsIURI.h" #include "nsIURL.h" +#include "nsIWeakReferenceUtils.h" #include "nsIWorkerDebugger.h" #include "nsIXPConnect.h" #include "nsPerformance.h" @@ -704,13 +707,16 @@ class MainThreadReleaseRunnable MOZ_FINAL : public nsRunnable { nsTArray> mDoomed; nsTArray mHostObjectURIs; + nsCOMPtr mLoadGroupToCancel; public: MainThreadReleaseRunnable(nsTArray>& aDoomed, - nsTArray& aHostObjectURIs) + nsTArray& aHostObjectURIs, + nsCOMPtr& aLoadGroupToCancel) { mDoomed.SwapElements(aDoomed); mHostObjectURIs.SwapElements(aHostObjectURIs); + mLoadGroupToCancel.swap(aLoadGroupToCancel); } NS_DECL_ISUPPORTS_INHERITED @@ -718,6 +724,11 @@ public: NS_IMETHOD Run() MOZ_OVERRIDE { + if (mLoadGroupToCancel) { + mLoadGroupToCancel->Cancel(NS_BINDING_ABORTED); + mLoadGroupToCancel = nullptr; + } + mDoomed.Clear(); for (uint32_t index = 0; index < mHostObjectURIs.Length(); index++) { @@ -761,6 +772,9 @@ private: virtual bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE { + nsCOMPtr loadGroupToCancel; + mFinishedWorker->ForgetOverridenLoadGroup(loadGroupToCancel); + nsTArray> doomed; mFinishedWorker->ForgetMainThreadObjects(doomed); @@ -768,7 +782,7 @@ private: mFinishedWorker->StealHostObjectURIs(hostObjectURIs); nsRefPtr runnable = - new MainThreadReleaseRunnable(doomed, hostObjectURIs); + new MainThreadReleaseRunnable(doomed, hostObjectURIs, loadGroupToCancel); if (NS_FAILED(NS_DispatchToMainThread(runnable))) { NS_WARNING("Failed to dispatch, going to leak!"); } @@ -816,6 +830,9 @@ private: runtime->UnregisterWorker(cx, mFinishedWorker); + nsCOMPtr loadGroupToCancel; + mFinishedWorker->ForgetOverridenLoadGroup(loadGroupToCancel); + nsTArray > doomed; mFinishedWorker->ForgetMainThreadObjects(doomed); @@ -823,7 +840,7 @@ private: mFinishedWorker->StealHostObjectURIs(hostObjectURIs); nsRefPtr runnable = - new MainThreadReleaseRunnable(doomed, hostObjectURIs); + new MainThreadReleaseRunnable(doomed, hostObjectURIs, loadGroupToCancel); if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { NS_WARNING("Failed to dispatch, going to leak!"); } @@ -1982,6 +1999,129 @@ private: } }; +template +class WorkerPrivateParent::InterfaceRequestor MOZ_FINAL + : public nsIInterfaceRequestor +{ + NS_DECL_ISUPPORTS + +public: + InterfaceRequestor(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup) + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + + // Look for an existing LoadContext. This is optional and it's ok if + // we don't find one. + nsCOMPtr baseContext; + if (aLoadGroup) { + nsCOMPtr callbacks; + aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks)); + if (callbacks) { + callbacks->GetInterface(NS_GET_IID(nsILoadContext), + getter_AddRefs(baseContext)); + } + } + + mLoadContext = new LoadContext(aPrincipal, baseContext); + } + + void + MaybeAddTabChild(nsILoadGroup* aLoadGroup) + { + MOZ_ASSERT(NS_IsMainThread()); + + if (!aLoadGroup) { + return; + } + + nsCOMPtr callbacks; + aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks)); + if (!callbacks) { + return; + } + + nsCOMPtr tabChild; + callbacks->GetInterface(NS_GET_IID(nsITabChild), getter_AddRefs(tabChild)); + if (!tabChild) { + return; + } + + // Use weak references to the tab child. Holding a strong reference will + // not prevent an ActorDestroy() from being called on the TabChild. + // Therefore, we should let the TabChild destroy itself as soon as possible. + mTabChildList.AppendElement(do_GetWeakReference(tabChild)); + } + + NS_IMETHOD + GetInterface(const nsIID& aIID, void** aSink) MOZ_OVERRIDE + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mLoadContext); + + if (aIID.Equals(NS_GET_IID(nsILoadContext))) { + nsCOMPtr ref = mLoadContext; + ref.forget(aSink); + return NS_OK; + } + + // If we still have an active nsITabChild, then return it. Its possible, + // though, that all of the TabChild objects have been destroyed. In that + // case we return NS_NOINTERFACE. + if(aIID.Equals(NS_GET_IID(nsITabChild))) { + nsCOMPtr tabChild = GetAnyLiveTabChild(); + if (!tabChild) { + return NS_NOINTERFACE; + } + tabChild.forget(aSink); + return NS_OK; + } + + return NS_NOINTERFACE; + } + +private: + ~InterfaceRequestor() { } + + already_AddRefed + GetAnyLiveTabChild() + { + MOZ_ASSERT(NS_IsMainThread()); + + // Search our list of known TabChild objects for one that still exists. + while (!mTabChildList.IsEmpty()) { + nsCOMPtr tabChild = + do_QueryReferent(mTabChildList.LastElement()); + + // Does this tab child still exist? If so, return it. We are done. + if (tabChild) { + return tabChild.forget(); + } + + // Otherwise remove the stale weak reference and check the next one + mTabChildList.RemoveElementAt(mTabChildList.Length() - 1); + } + + return nullptr; + } + + nsCOMPtr mLoadContext; + + // Array of weak references to nsITabChild. We do not want to keep TabChild + // actors alive for long after their ActorDestroy() methods are called. + nsTArray mTabChildList; +}; + +template +NS_IMPL_ADDREF(WorkerPrivateParent::InterfaceRequestor) + +template +NS_IMPL_RELEASE(WorkerPrivateParent::InterfaceRequestor) + +template +NS_IMPL_QUERY_INTERFACE(WorkerPrivateParent::InterfaceRequestor, + nsIInterfaceRequestor) + template class WorkerPrivateParent::EventTarget MOZ_FINAL : public nsIEventTarget @@ -2833,6 +2973,22 @@ WorkerPrivateParent::ModifyBusyCount(JSContext* aCx, bool aIncrease) return true; } +template +void +WorkerPrivateParent::ForgetOverridenLoadGroup( + nsCOMPtr& aLoadGroupOut) +{ + AssertIsOnParentThread(); + + // If we're not overriden, then do nothing here. Let the load group get + // handled in ForgetMainThreadObjects(). + if (!mLoadInfo.mInterfaceRequestor) { + return; + } + + mLoadInfo.mLoadGroup.swap(aLoadGroupOut); +} + template void WorkerPrivateParent::ForgetMainThreadObjects( @@ -2841,7 +2997,7 @@ WorkerPrivateParent::ForgetMainThreadObjects( AssertIsOnParentThread(); MOZ_ASSERT(!mMainThreadObjectsForgotten); - static const uint32_t kDoomedCount = 8; + static const uint32_t kDoomedCount = 9; aDoomed.SetCapacity(kDoomedCount); @@ -2853,6 +3009,7 @@ WorkerPrivateParent::ForgetMainThreadObjects( SwapToISupportsArray(mLoadInfo.mChannel, aDoomed); SwapToISupportsArray(mLoadInfo.mCSP, aDoomed); SwapToISupportsArray(mLoadInfo.mLoadGroup, aDoomed); + SwapToISupportsArray(mLoadInfo.mInterfaceRequestor, aDoomed); // Before adding anything here update kDoomedCount above! MOZ_ASSERT(aDoomed.Length() == kDoomedCount); @@ -3631,6 +3788,16 @@ WorkerPrivateParent::StealHostObjectURIs(nsTArray& aArray) aArray.SwapElements(mHostObjectURIs); } +template +void +WorkerPrivateParent::UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup) +{ + AssertIsOnMainThread(); + + // The load group should have been overriden at init time. + mLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aBaseLoadGroup); +} + template NS_IMPL_ADDREF_INHERITED(WorkerPrivateParent, DOMEventTargetHelper) @@ -4062,7 +4229,8 @@ WorkerPrivate::Constructor(JSContext* aCx, stackLoadInfo.emplace(); nsresult rv = GetLoadInfo(aCx, nullptr, parent, aScriptURL, - aIsChromeWorker, stackLoadInfo.ptr()); + aIsChromeWorker, InheritLoadGroup, + stackLoadInfo.ptr()); if (NS_FAILED(rv)) { scriptloader::ReportLoadError(aCx, aScriptURL, rv, !parent); aRv.Throw(rv); @@ -4117,7 +4285,9 @@ WorkerPrivate::Constructor(JSContext* aCx, nsresult WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, WorkerPrivate* aParent, const nsAString& aScriptURL, - bool aIsChromeWorker, LoadInfo* aLoadInfo) + bool aIsChromeWorker, + LoadGroupBehavior aLoadGroupBehavior, + LoadInfo* aLoadInfo) { using namespace mozilla::dom::workers::scriptloader; using mozilla::dom::indexedDB::IDBFactory; @@ -4350,10 +4520,8 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, loadInfo.mReportCSPViolations = false; } - if (!loadInfo.mLoadGroup) { - rv = NS_NewLoadGroup(getter_AddRefs(loadInfo.mLoadGroup), - loadInfo.mPrincipal); - NS_ENSURE_SUCCESS(rv, rv); + if (!loadInfo.mLoadGroup || aLoadGroupBehavior == OverrideLoadGroup) { + OverrideLoadInfoLoadGroup(loadInfo); } MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup, loadInfo.mPrincipal)); @@ -4373,6 +4541,26 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, return NS_OK; } +// static +void +WorkerPrivate::OverrideLoadInfoLoadGroup(LoadInfo& aLoadInfo) +{ + MOZ_ASSERT(!aLoadInfo.mInterfaceRequestor); + + aLoadInfo.mInterfaceRequestor = new InterfaceRequestor(aLoadInfo.mPrincipal, + aLoadInfo.mLoadGroup); + aLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aLoadInfo.mLoadGroup); + + nsCOMPtr loadGroup = + do_CreateInstance(NS_LOADGROUP_CONTRACTID); + + nsresult rv = + loadGroup->SetNotificationCallbacks(aLoadInfo.mInterfaceRequestor); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv)); + + aLoadInfo.mLoadGroup = loadGroup.forget(); +} + void WorkerPrivate::DoRunLoop(JSContext* aCx) { diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 430886f4fe4a..9551a466501f 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -132,6 +132,7 @@ class WorkerPrivateParent : public DOMEventTargetHelper class SynchronizeAndResumeRunnable; protected: + class InterfaceRequestor; class EventTarget; friend class EventTarget; @@ -163,6 +164,9 @@ public: nsCOMPtr mChannel; nsCOMPtr mLoadGroup; + // Only set if we have a custom overriden load group + nsRefPtr mInterfaceRequestor; + nsAutoPtr mPrincipalInfo; nsCString mDomain; @@ -207,6 +211,9 @@ public: MOZ_ASSERT(!mLoadGroup); aOther.mLoadGroup.swap(mLoadGroup); + MOZ_ASSERT(!mInterfaceRequestor); + aOther.mInterfaceRequestor.swap(mInterfaceRequestor); + MOZ_ASSERT(!mPrincipalInfo); mPrincipalInfo = aOther.mPrincipalInfo.forget(); @@ -393,6 +400,9 @@ public: bool ModifyBusyCount(JSContext* aCx, bool aIncrease); + void + ForgetOverridenLoadGroup(nsCOMPtr& aLoadGroupOut); + void ForgetMainThreadObjects(nsTArray >& aDoomed); @@ -763,6 +773,9 @@ public: void StealHostObjectURIs(nsTArray& aArray); + void + UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup); + IMPL_EVENT_HANDLER(message) IMPL_EVENT_HANDLER(error) @@ -921,10 +934,19 @@ public: static bool WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */); + enum LoadGroupBehavior + { + InheritLoadGroup, + OverrideLoadGroup + }; + static nsresult GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker, - LoadInfo* aLoadInfo); + LoadGroupBehavior aLoadGroupBehavior, LoadInfo* aLoadInfo); + + static void + OverrideLoadInfoLoadGroup(LoadInfo& aLoadInfo); WorkerDebugger* Debugger() const