зеркало из https://github.com/mozilla/gecko-dev.git
497 строки
15 KiB
C++
497 строки
15 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "WorkerLoadInfo.h"
|
|
#include "WorkerPrivate.h"
|
|
|
|
#include "mozilla/dom/TabChild.h"
|
|
#include "mozilla/ipc/BackgroundUtils.h"
|
|
#include "mozilla/ipc/PBackgroundSharedTypes.h"
|
|
#include "mozilla/LoadContext.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsIContentSecurityPolicy.h"
|
|
#include "nsINetworkInterceptController.h"
|
|
#include "nsIProtocolHandler.h"
|
|
#include "nsITabChild.h"
|
|
#include "nsScriptSecurityManager.h"
|
|
#include "nsNetUtil.h"
|
|
|
|
namespace mozilla {
|
|
|
|
using namespace ipc;
|
|
|
|
namespace dom {
|
|
|
|
namespace {
|
|
|
|
class MainThreadReleaseRunnable final : public Runnable
|
|
{
|
|
nsTArray<nsCOMPtr<nsISupports>> mDoomed;
|
|
nsCOMPtr<nsILoadGroup> mLoadGroupToCancel;
|
|
|
|
public:
|
|
MainThreadReleaseRunnable(nsTArray<nsCOMPtr<nsISupports>>& aDoomed,
|
|
nsCOMPtr<nsILoadGroup>& aLoadGroupToCancel)
|
|
: mozilla::Runnable("MainThreadReleaseRunnable")
|
|
{
|
|
mDoomed.SwapElements(aDoomed);
|
|
mLoadGroupToCancel.swap(aLoadGroupToCancel);
|
|
}
|
|
|
|
NS_INLINE_DECL_REFCOUNTING_INHERITED(MainThreadReleaseRunnable, Runnable)
|
|
|
|
NS_IMETHOD
|
|
Run() override
|
|
{
|
|
if (mLoadGroupToCancel) {
|
|
mLoadGroupToCancel->Cancel(NS_BINDING_ABORTED);
|
|
mLoadGroupToCancel = nullptr;
|
|
}
|
|
|
|
mDoomed.Clear();
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
~MainThreadReleaseRunnable()
|
|
{ }
|
|
};
|
|
|
|
// Specialize this if there's some class that has multiple nsISupports bases.
|
|
template <class T>
|
|
struct ISupportsBaseInfo
|
|
{
|
|
typedef T ISupportsBase;
|
|
};
|
|
|
|
template <template <class> class SmartPtr, class T>
|
|
inline void
|
|
SwapToISupportsArray(SmartPtr<T>& aSrc,
|
|
nsTArray<nsCOMPtr<nsISupports> >& aDest)
|
|
{
|
|
nsCOMPtr<nsISupports>* dest = aDest.AppendElement();
|
|
|
|
T* raw = nullptr;
|
|
aSrc.swap(raw);
|
|
|
|
nsISupports* rawSupports =
|
|
static_cast<typename ISupportsBaseInfo<T>::ISupportsBase*>(raw);
|
|
dest->swap(rawSupports);
|
|
}
|
|
|
|
} // anonymous
|
|
|
|
WorkerLoadInfoData::WorkerLoadInfoData()
|
|
: mLoadFlags(nsIRequest::LOAD_NORMAL)
|
|
, mWindowID(UINT64_MAX)
|
|
, mReferrerPolicy(net::RP_Unset)
|
|
, mFromWindow(false)
|
|
, mEvalAllowed(false)
|
|
, mReportCSPViolations(false)
|
|
, mXHRParamsAllowed(false)
|
|
, mPrincipalIsSystem(false)
|
|
, mStorageAllowed(false)
|
|
, mFirstPartyStorageAccessGranted(false)
|
|
, mServiceWorkersTestingInWindow(false)
|
|
, mSecureContext(eNotSet)
|
|
{}
|
|
|
|
nsresult
|
|
WorkerLoadInfo::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
|
|
nsILoadGroup* aLoadGroup)
|
|
{
|
|
AssertIsOnMainThread();
|
|
MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadGroup, aPrincipal));
|
|
|
|
mPrincipal = aPrincipal;
|
|
mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
|
|
|
|
nsresult rv = aPrincipal->GetCsp(getter_AddRefs(mCSP));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (mCSP) {
|
|
mCSP->GetAllowsEval(&mReportCSPViolations, &mEvalAllowed);
|
|
} else {
|
|
mEvalAllowed = true;
|
|
mReportCSPViolations = false;
|
|
}
|
|
|
|
mLoadGroup = aLoadGroup;
|
|
|
|
mPrincipalInfo = new PrincipalInfo();
|
|
mOriginAttributes = nsContentUtils::GetOriginAttributes(aLoadGroup);
|
|
|
|
rv = PrincipalToPrincipalInfo(aPrincipal, mPrincipalInfo);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = nsContentUtils::GetUTFOrigin(aPrincipal, mOrigin);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
WorkerLoadInfo::GetPrincipalAndLoadGroupFromChannel(nsIChannel* aChannel,
|
|
nsIPrincipal** aPrincipalOut,
|
|
nsILoadGroup** aLoadGroupOut)
|
|
{
|
|
AssertIsOnMainThread();
|
|
MOZ_DIAGNOSTIC_ASSERT(aChannel);
|
|
MOZ_DIAGNOSTIC_ASSERT(aPrincipalOut);
|
|
MOZ_DIAGNOSTIC_ASSERT(aLoadGroupOut);
|
|
|
|
// Initial triggering principal should be set
|
|
NS_ENSURE_TRUE(mLoadingPrincipal, NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
|
|
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
|
MOZ_DIAGNOSTIC_ASSERT(ssm);
|
|
|
|
nsCOMPtr<nsIPrincipal> channelPrincipal;
|
|
nsresult rv = ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(channelPrincipal));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Every time we call GetChannelResultPrincipal() it will return a different
|
|
// null principal for a data URL. We don't want to change the worker's
|
|
// principal again, though. Instead just keep the original null principal we
|
|
// first got from the channel.
|
|
//
|
|
// Note, we don't do this by setting principalToInherit on the channel's
|
|
// load info because we don't yet have the first null principal when we
|
|
// create the channel.
|
|
if (mPrincipal && mPrincipal->GetIsNullPrincipal() &&
|
|
channelPrincipal->GetIsNullPrincipal()) {
|
|
channelPrincipal = mPrincipal;
|
|
}
|
|
|
|
nsCOMPtr<nsILoadGroup> channelLoadGroup;
|
|
rv = aChannel->GetLoadGroup(getter_AddRefs(channelLoadGroup));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
MOZ_ASSERT(channelLoadGroup);
|
|
|
|
// If the loading principal is the system principal then the channel
|
|
// principal must also be the system principal (we do not allow chrome
|
|
// code to create workers with non-chrome scripts, and if we ever decide
|
|
// to change this we need to make sure we don't always set
|
|
// mPrincipalIsSystem to true in WorkerPrivate::GetLoadInfo()). Otherwise
|
|
// this channel principal must be same origin with the load principal (we
|
|
// check again here in case redirects changed the location of the script).
|
|
if (nsContentUtils::IsSystemPrincipal(mLoadingPrincipal)) {
|
|
if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
|
|
nsCOMPtr<nsIURI> finalURI;
|
|
rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// See if this is a resource URI. Since JSMs usually come from
|
|
// resource:// URIs we're currently considering all URIs with the
|
|
// URI_IS_UI_RESOURCE flag as valid for creating privileged workers.
|
|
bool isResource;
|
|
rv = NS_URIChainHasFlags(finalURI,
|
|
nsIProtocolHandler::URI_IS_UI_RESOURCE,
|
|
&isResource);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (isResource) {
|
|
// Assign the system principal to the resource:// worker only if it
|
|
// was loaded from code using the system principal.
|
|
channelPrincipal = mLoadingPrincipal;
|
|
} else {
|
|
return NS_ERROR_DOM_BAD_URI;
|
|
}
|
|
}
|
|
}
|
|
|
|
// The principal can change, but it should still match the original
|
|
// load group's appId and browser element flag.
|
|
MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(channelLoadGroup, channelPrincipal));
|
|
|
|
channelPrincipal.forget(aPrincipalOut);
|
|
channelLoadGroup.forget(aLoadGroupOut);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
WorkerLoadInfo::SetPrincipalFromChannel(nsIChannel* aChannel)
|
|
{
|
|
AssertIsOnMainThread();
|
|
|
|
nsCOMPtr<nsIPrincipal> principal;
|
|
nsCOMPtr<nsILoadGroup> loadGroup;
|
|
nsresult rv = GetPrincipalAndLoadGroupFromChannel(aChannel,
|
|
getter_AddRefs(principal),
|
|
getter_AddRefs(loadGroup));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return SetPrincipalOnMainThread(principal, loadGroup);
|
|
}
|
|
|
|
bool
|
|
WorkerLoadInfo::FinalChannelPrincipalIsValid(nsIChannel* aChannel)
|
|
{
|
|
AssertIsOnMainThread();
|
|
|
|
nsCOMPtr<nsIPrincipal> principal;
|
|
nsCOMPtr<nsILoadGroup> loadGroup;
|
|
nsresult rv = GetPrincipalAndLoadGroupFromChannel(aChannel,
|
|
getter_AddRefs(principal),
|
|
getter_AddRefs(loadGroup));
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
|
|
// Verify that the channel is still a null principal. We don't care
|
|
// if these are the exact same null principal object, though. From
|
|
// the worker's perspective its the same effect.
|
|
if (principal->GetIsNullPrincipal() && mPrincipal->GetIsNullPrincipal()) {
|
|
return true;
|
|
}
|
|
|
|
// Otherwise we require exact equality. Redirects can happen, but they
|
|
// are not allowed to change our principal.
|
|
if (principal->Equals(mPrincipal)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
bool
|
|
WorkerLoadInfo::PrincipalIsValid() const
|
|
{
|
|
return mPrincipal && mPrincipalInfo &&
|
|
mPrincipalInfo->type() != PrincipalInfo::T__None &&
|
|
mPrincipalInfo->type() <= PrincipalInfo::T__Last;
|
|
}
|
|
|
|
bool
|
|
WorkerLoadInfo::PrincipalURIMatchesScriptURL()
|
|
{
|
|
AssertIsOnMainThread();
|
|
|
|
nsAutoCString scheme;
|
|
nsresult rv = mBaseURI->GetScheme(scheme);
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
// A system principal must either be a blob URL or a resource JSM.
|
|
if (mPrincipal->GetIsSystemPrincipal()) {
|
|
if (scheme == NS_LITERAL_CSTRING("blob")) {
|
|
return true;
|
|
}
|
|
|
|
bool isResource = false;
|
|
nsresult rv = NS_URIChainHasFlags(mBaseURI,
|
|
nsIProtocolHandler::URI_IS_UI_RESOURCE,
|
|
&isResource);
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
return isResource;
|
|
}
|
|
|
|
// A null principal can occur for a data URL worker script or a blob URL
|
|
// worker script from a sandboxed iframe.
|
|
if (mPrincipal->GetIsNullPrincipal()) {
|
|
return scheme == NS_LITERAL_CSTRING("data") ||
|
|
scheme == NS_LITERAL_CSTRING("blob");
|
|
}
|
|
|
|
// The principal for a blob: URL worker script does not have a matching URL.
|
|
// This is likely a bug in our referer setting logic, but exempt it for now.
|
|
// This is another reason we should fix bug 1340694 so that referer does not
|
|
// depend on the principal URI.
|
|
if (scheme == NS_LITERAL_CSTRING("blob")) {
|
|
return true;
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> principalURI;
|
|
rv = mPrincipal->GetURI(getter_AddRefs(principalURI));
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
NS_ENSURE_TRUE(principalURI, false);
|
|
|
|
if (nsScriptSecurityManager::SecurityCompareURIs(mBaseURI, principalURI)) {
|
|
return true;
|
|
}
|
|
|
|
// If strict file origin policy is in effect, local files will always fail
|
|
// SecurityCompareURIs unless they are identical. Explicitly check file origin
|
|
// policy, in that case.
|
|
if (nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
|
|
NS_URIIsLocalFile(mBaseURI) &&
|
|
NS_RelaxStrictFileOriginPolicy(mBaseURI, principalURI)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
|
|
bool
|
|
WorkerLoadInfo::ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate)
|
|
{
|
|
nsCOMPtr<nsILoadGroup> nullLoadGroup;
|
|
return ProxyReleaseMainThreadObjects(aWorkerPrivate, nullLoadGroup);
|
|
}
|
|
|
|
bool
|
|
WorkerLoadInfo::ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate,
|
|
nsCOMPtr<nsILoadGroup>& aLoadGroupToCancel)
|
|
{
|
|
|
|
static const uint32_t kDoomedCount = 10;
|
|
nsTArray<nsCOMPtr<nsISupports>> doomed(kDoomedCount);
|
|
|
|
SwapToISupportsArray(mWindow, doomed);
|
|
SwapToISupportsArray(mScriptContext, doomed);
|
|
SwapToISupportsArray(mBaseURI, doomed);
|
|
SwapToISupportsArray(mResolvedScriptURI, doomed);
|
|
SwapToISupportsArray(mPrincipal, doomed);
|
|
SwapToISupportsArray(mLoadingPrincipal, doomed);
|
|
SwapToISupportsArray(mChannel, doomed);
|
|
SwapToISupportsArray(mCSP, doomed);
|
|
SwapToISupportsArray(mLoadGroup, doomed);
|
|
SwapToISupportsArray(mInterfaceRequestor, doomed);
|
|
// Before adding anything here update kDoomedCount above!
|
|
|
|
MOZ_ASSERT(doomed.Length() == kDoomedCount);
|
|
|
|
RefPtr<MainThreadReleaseRunnable> runnable =
|
|
new MainThreadReleaseRunnable(doomed, aLoadGroupToCancel);
|
|
return NS_SUCCEEDED(aWorkerPrivate->DispatchToMainThread(runnable.forget()));
|
|
}
|
|
|
|
WorkerLoadInfo::
|
|
InterfaceRequestor::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<nsILoadContext> baseContext;
|
|
if (aLoadGroup) {
|
|
nsCOMPtr<nsIInterfaceRequestor> callbacks;
|
|
aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
|
|
if (callbacks) {
|
|
callbacks->GetInterface(NS_GET_IID(nsILoadContext),
|
|
getter_AddRefs(baseContext));
|
|
}
|
|
mOuterRequestor = callbacks;
|
|
}
|
|
|
|
mLoadContext = new LoadContext(aPrincipal, baseContext);
|
|
}
|
|
|
|
void
|
|
WorkerLoadInfo::
|
|
InterfaceRequestor::MaybeAddTabChild(nsILoadGroup* aLoadGroup)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (!aLoadGroup) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIInterfaceRequestor> callbacks;
|
|
aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
|
|
if (!callbacks) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsITabChild> 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_IMETHODIMP
|
|
WorkerLoadInfo::
|
|
InterfaceRequestor::GetInterface(const nsIID& aIID, void** aSink)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(mLoadContext);
|
|
|
|
if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
|
|
nsCOMPtr<nsILoadContext> 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<nsITabChild> tabChild = GetAnyLiveTabChild();
|
|
if (!tabChild) {
|
|
return NS_NOINTERFACE;
|
|
}
|
|
tabChild.forget(aSink);
|
|
return NS_OK;
|
|
}
|
|
|
|
if (aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) &&
|
|
mOuterRequestor) {
|
|
// If asked for the network intercept controller, ask the outer requestor,
|
|
// which could be the docshell.
|
|
return mOuterRequestor->GetInterface(aIID, aSink);
|
|
}
|
|
|
|
return NS_NOINTERFACE;
|
|
}
|
|
|
|
already_AddRefed<nsITabChild>
|
|
WorkerLoadInfo::
|
|
InterfaceRequestor::GetAnyLiveTabChild()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// Search our list of known TabChild objects for one that still exists.
|
|
while (!mTabChildList.IsEmpty()) {
|
|
nsCOMPtr<nsITabChild> tabChild =
|
|
do_QueryReferent(mTabChildList.LastElement());
|
|
|
|
// Does this tab child still exist? If so, return it. We are done. If the
|
|
// PBrowser actor is no longer useful, don't bother returning this tab.
|
|
if (tabChild && !static_cast<TabChild*>(tabChild.get())->IsDestroyed()) {
|
|
return tabChild.forget();
|
|
}
|
|
|
|
// Otherwise remove the stale weak reference and check the next one
|
|
mTabChildList.RemoveLastElement();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
NS_IMPL_ADDREF(WorkerLoadInfo::InterfaceRequestor)
|
|
NS_IMPL_RELEASE(WorkerLoadInfo::InterfaceRequestor)
|
|
NS_IMPL_QUERY_INTERFACE(WorkerLoadInfo::InterfaceRequestor,
|
|
nsIInterfaceRequestor)
|
|
|
|
WorkerLoadInfo::WorkerLoadInfo()
|
|
{
|
|
MOZ_COUNT_CTOR(WorkerLoadInfo);
|
|
}
|
|
|
|
WorkerLoadInfo::WorkerLoadInfo(WorkerLoadInfo&& aOther) noexcept
|
|
: WorkerLoadInfoData(std::move(aOther))
|
|
{
|
|
MOZ_COUNT_CTOR(WorkerLoadInfo);
|
|
}
|
|
|
|
WorkerLoadInfo::~WorkerLoadInfo()
|
|
{
|
|
MOZ_COUNT_DTOR(WorkerLoadInfo);
|
|
}
|
|
|
|
} // dom namespace
|
|
} // mozilla namespace
|