import changes from `dev' branch of rmottola/Arctic-Fox:

- partial of  Bug 1153658 - browser_compartments.js logspam. r=yoric (8e2c21aa5)
- Bug 1178653 - Refactor construction code to use an interface consistent with the spec, with the one exception using an out-of-the-way, differently-named method. r=efaust (d316259d7)
- Bug 1175098 - PerformanceStats for e10s. r=felipe, r=mconley (515acb8d7)
- Bug 1147664 - Detailed mode for PerformanceStats (low-level). r=jandem (dda8d84de)
- Bug 1147664 - Detailed mode for PerformanceStats (high-level). r=mossop (b86076568)
- Bug 1164304 - Run all fetch tests in the service worker context as well; r=nsm (e20fa8bfd)
- Bug 1143981 - Reroute all fetch tests through a transparent service worker; r=nsm (5196acc57)
- Bug 1122161 - Redirected channels should respect skip service worker flag. r=nsm (f4288392e)
- Bug 1170937 - Set the URL on the Response object created from a fetch() properly if the underlying channel gets redirected; r=baku (45febabb3)
- Bug 1173029 - Remove mFinalURL from InternalResponse; r=baku a=KWierso (6bdc1083b)
- Bug 1137683 - Use a loadgroup derived from the document's when updating a ServiceWorker; r=bkelly (fabaa2602)
- Bug 1164397 - Part 1: Use the original channel URI for constructing the cache entry key when we're dealing with an intercepted channel; r=mcmanus (b20ab36c7)
- Bug 1164397 - Part 2: Add an API for overriding the original URI on HttpChannelBase; r=mcmanus (20021722f)
- Bug 1164397 - Part 3: Add an API for overriding the original URI on nsJARChannel; r=jdm (492b6fd6f)
- Bug 1164397 - Part 4: Add infromation about whether a channel was redirected to ChannelInfo; r=jdm (e2ce84660)
- Bug 1164397 - Part 5: Save the redirected flag and the redirected URI in the DOM cache; r=bkelly (7d2d1fc92)
- Bug 1162018 - Add an automated test to ensure that a redirected Request won't be visible to a service worker if it had triggered the original fetch(); r=jdm (0397a073f)
- Bug 1164397 - Part 6: Add a test case for the service worker responding with a redirected Response; r=jdm (e83e0bee4)
- Bug 1164397 - Part 7: Add a test case for the redirected Response object being stored in the DOM Cache; r=jdm (7a82916d8)
- Bug 1169296 - Intercepting top-level document loads is not working with JAR channels. Tests. r=jdm (fe8f128c5)
- Bug 1171285 - Part 1: Add a script for regenerating the application.zip used by test_app_protocol.html; r=jdm (ec303b3b2)
- Bug 1171285 - Part 2: Fix test_app_protocol.html to finish both index.html and controlled.html tests; r=jdm (2e68e6665)
- Bug 1169613 - Use content type of synthesized response for JAR channel requests if available. Tests. r=jdm (b0095fc3b)
- Bug 1164397 - Part 8: Add a test case for the service worker for an app:// URI responding with a redirected Response; r=jdm (460e834c9)
- Bug 1169044 - Patch 3 - Store and set principal with script URI on ServiceWorkers. r=ehsan (6e0b0102a)
- Bug 1164397 - Part 9: Add a test case for the service worker for an app:// URI responding with a redirected HTTPS response; r=jdm (1be195f5a)
- Bug 1164397 - Part 10: Add a test case for the service worker for an app:// URI responding with cached HTTP and HTTPS responses; r=jdm# Please enter the commit message for your changes. Lines starting (56432b7b5)
- Bug 1164397 - Part 11: Add a test case for the service worker responding to HTTPS normal and cached Responses; r=jdm (6ec238455)
- Bug 1164397 - Part 12: Add a test case for the service worker responding to normal and cached HTTP->HTTPS responses; r=jdm (925a1970f)
- Bug 1190074 - PerformanceGroup now uses mozilla::RefPtr;r=jandem (53dc0a640)
- Bug 1169086 followup: Add missing 'override' annotation to VerifyTraceProtoAndIfaceCacheCalledTracer::trace() method decl. rs=ehsan (cdedce447)
- Bug 1172824: Initialize a few members in CompartmentCheckTracer (CID 1304705); r=terrence (3db40160c)
- missing bit of 1166678 (9fb0cceeb)
- Bug 1139473: File some metadata for the js/src/ subdirectory; r=jorendorff (9dc4a29a7)
- Bug 1173889 - Strongly type the CallbackTracer dispatch function; r=jonco, r=mccr8 (19b47dc70)
This commit is contained in:
2021-04-25 13:38:44 +08:00
parent b4028f07d5
commit 516d358f36
129 changed files with 3326 additions and 747 deletions
Binary file not shown.
Binary file not shown.
+1
View File
@@ -96,6 +96,7 @@ http://example.net:80 privileged
http://prefixexample.com:80
https://example.com:443 privileged
https://example.org:443 privileged
https://test1.example.com:443 privileged
https://test2.example.com:443 privileged
https://sub1.test1.example.com:443 privileged
+17
View File
@@ -115,6 +115,23 @@ OriginAttributes::PopulateFromSuffix(const nsACString& aStr)
return params->ForEach(iterator);
}
bool
OriginAttributes::PopulateFromOrigin(const nsACString& aOrigin,
nsACString& aOriginNoSuffix)
{
// RFindChar is only available on nsCString.
nsCString origin(aOrigin);
int32_t pos = origin.RFindChar('!');
if (pos == kNotFound) {
aOriginNoSuffix = origin;
return true;
}
aOriginNoSuffix = Substring(origin, 0, pos);
return PopulateFromSuffix(Substring(origin, pos));
}
void
OriginAttributes::CookieJar(nsACString& aStr)
{
+5
View File
@@ -48,6 +48,11 @@ public:
bool PopulateFromSuffix(const nsACString& aStr);
void CookieJar(nsACString& aStr);
// Populates the attributes from a string like
// |uri!key1=value1&key2=value2| and returns the uri without the suffix.
bool PopulateFromOrigin(const nsACString& aOrigin,
nsACString& aOriginNoSuffix);
};
/*
+1 -1
View File
@@ -523,7 +523,7 @@ struct VerifyTraceProtoAndIfaceCacheCalledTracer : public JS::CallbackTracer
: JS::CallbackTracer(rt), ok(false)
{}
void trace(void** thingp, JS::TraceKind kind) {
void onChild(const JS::GCCellPtr&) override {
// We don't do anything here, we only want to verify that
// TraceProtoAndIfaceCache was called.
}
+2
View File
@@ -7,6 +7,7 @@ include protocol PCachePushStream;
include protocol PCacheStreamControl;
include InputStreamParams;
include ChannelInfo;
include PBackgroundSharedTypes;
using HeadersGuardEnum from "mozilla/dom/cache/IPCUtils.h";
using RequestCredentials from "mozilla/dom/cache/IPCUtils.h";
@@ -83,6 +84,7 @@ struct CacheResponse
HeadersGuardEnum headersGuard;
CacheReadStreamOrVoid body;
IPCChannelInfo channelInfo;
OptionalPrincipalInfo principalInfo;
};
union CacheResponseOrVoid
+73 -3
View File
@@ -19,6 +19,7 @@
#include "nsCRT.h"
#include "nsHttp.h"
#include "nsICryptoHash.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/dom/HeadersBinding.h"
#include "mozilla/dom/RequestBinding.h"
#include "mozilla/dom/ResponseBinding.h"
@@ -29,11 +30,11 @@ namespace dom {
namespace cache {
namespace db {
const int32_t kMaxWipeSchemaVersion = 10;
const int32_t kMaxWipeSchemaVersion = 11;
namespace {
const int32_t kLatestSchemaVersion = 10;
const int32_t kLatestSchemaVersion = 11;
const int32_t kMaxEntriesPerStatement = 255;
const uint32_t kPageSize = 4 * 1024;
@@ -301,6 +302,11 @@ CreateSchema(mozIStorageConnection* aConn)
"response_headers_guard INTEGER NOT NULL, "
"response_body_id TEXT NULL, "
"response_security_info_id INTEGER NULL REFERENCES security_info(id), "
"response_principal_info TEXT NOT NULL, "
"response_redirected INTEGER NOT NULL, "
// Note that response_redirected_url is either going to be empty, or
// it's going to be a URL different than response_url.
"response_redirected_url TEXT NOT NULL, "
"cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE"
");"
));
@@ -1524,6 +1530,9 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
"response_headers_guard, "
"response_body_id, "
"response_security_info_id, "
"response_principal_info, "
"response_redirected, "
"response_redirected_url, "
"cache_id "
") VALUES ("
":request_method, "
@@ -1544,6 +1553,9 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
":response_headers_guard, "
":response_body_id, "
":response_security_info_id, "
":response_principal_info, "
":response_redirected, "
":response_redirected_url, "
":cache_id "
");"
), getter_AddRefs(state));
@@ -1623,6 +1635,36 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
}
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsAutoCString serializedInfo;
// We only allow content serviceworkers right now.
if (aResponse.principalInfo().type() == mozilla::ipc::OptionalPrincipalInfo::TPrincipalInfo) {
const mozilla::ipc::PrincipalInfo& principalInfo =
aResponse.principalInfo().get_PrincipalInfo();
MOZ_ASSERT(principalInfo.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
const mozilla::ipc::ContentPrincipalInfo& cInfo =
principalInfo.get_ContentPrincipalInfo();
serializedInfo.Append(cInfo.spec());
MOZ_ASSERT(cInfo.appId() != nsIScriptSecurityManager::UNKNOWN_APP_ID);
OriginAttributes attrs(cInfo.appId(), cInfo.isInBrowserElement());
nsAutoCString suffix;
attrs.CreateSuffix(suffix);
serializedInfo.Append(suffix);
}
rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("response_principal_info"),
serializedInfo);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = state->BindInt32ByName(NS_LITERAL_CSTRING("response_redirected"),
aResponse.channelInfo().redirected() ? 1 : 0);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("response_redirected_url"),
aResponse.channelInfo().redirectedURI());
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -1714,6 +1756,9 @@ ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
"entries.response_status_text, "
"entries.response_headers_guard, "
"entries.response_body_id, "
"entries.response_principal_info, "
"entries.response_redirected, "
"entries.response_redirected_url, "
"security_info.data "
"FROM entries "
"LEFT OUTER JOIN security_info "
@@ -1761,7 +1806,32 @@ ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
}
rv = state->GetBlobAsUTF8String(6, aSavedResponseOut->mValue.channelInfo().securityInfo());
nsAutoCString serializedInfo;
rv = state->GetUTF8String(6, serializedInfo);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
aSavedResponseOut->mValue.principalInfo() = void_t();
if (!serializedInfo.IsEmpty()) {
nsAutoCString originNoSuffix;
OriginAttributes attrs;
fprintf(stderr, "\n%s\n", serializedInfo.get());
if (!attrs.PopulateFromOrigin(serializedInfo, originNoSuffix)) {
NS_WARNING("Something went wrong parsing a serialized principal!");
return NS_ERROR_FAILURE;
}
aSavedResponseOut->mValue.principalInfo() =
mozilla::ipc::ContentPrincipalInfo(attrs.mAppId, attrs.mInBrowser, originNoSuffix);
}
int32_t redirected;
rv = state->GetInt32(7, &redirected);
aSavedResponseOut->mValue.channelInfo().redirected() = !!redirected;
rv = state->GetUTF8String(8, aSavedResponseOut->mValue.channelInfo().redirectedURI());
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = state->GetBlobAsUTF8String(9, aSavedResponseOut->mValue.channelInfo().securityInfo());
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
+9
View File
@@ -225,6 +225,11 @@ TypeUtils::ToCacheResponseWithoutBody(CacheResponse& aOut,
ToHeadersEntryList(aOut.headers(), headers);
aOut.headersGuard() = headers->Guard();
aOut.channelInfo() = aIn.GetChannelInfo().AsIPCChannelInfo();
if (aIn.GetPrincipalInfo()) {
aOut.principalInfo() = *aIn.GetPrincipalInfo();
} else {
aOut.principalInfo() = void_t();
}
}
void
@@ -291,6 +296,10 @@ TypeUtils::ToResponse(const CacheResponse& aIn)
MOZ_ASSERT(!result.Failed());
ir->InitChannelInfo(aIn.channelInfo());
if (aIn.principalInfo().type() == mozilla::ipc::OptionalPrincipalInfo::TPrincipalInfo) {
UniquePtr<mozilla::ipc::PrincipalInfo> info(new mozilla::ipc::PrincipalInfo(aIn.principalInfo().get_PrincipalInfo()));
ir->SetPrincipalInfo(Move(info));
}
nsCOMPtr<nsIInputStream> stream = ReadStream::Create(aIn.body());
ir->SetBody(stream);
+52 -4
View File
@@ -13,6 +13,7 @@
#include "mozilla/ipc/ChannelInfo.h"
#include "nsIJARChannel.h"
#include "nsJARChannel.h"
#include "nsNetUtil.h"
using namespace mozilla;
using namespace mozilla::dom;
@@ -20,6 +21,7 @@ using namespace mozilla::dom;
void
ChannelInfo::InitFromChannel(nsIChannel* aChannel)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mInited, "Cannot initialize the object twice");
nsCOMPtr<nsISupports> securityInfo;
@@ -28,6 +30,20 @@ ChannelInfo::InitFromChannel(nsIChannel* aChannel)
SetSecurityInfo(securityInfo);
}
nsLoadFlags loadFlags = 0;
aChannel->GetLoadFlags(&loadFlags);
mRedirected = (loadFlags & nsIChannel::LOAD_REPLACE);
if (mRedirected) {
// Save the spec and not the nsIURI object itself, since those objects are
// not thread-safe, and releasing them somewhere other than the main thread
// is not possible.
nsCOMPtr<nsIURI> redirectedURI;
aChannel->GetURI(getter_AddRefs(redirectedURI));
if (redirectedURI) {
redirectedURI->GetSpec(mRedirectedURISpec);
}
}
mInited = true;
}
@@ -37,6 +53,8 @@ ChannelInfo::InitFromIPCChannelInfo(const ipc::IPCChannelInfo& aChannelInfo)
MOZ_ASSERT(!mInited, "Cannot initialize the object twice");
mSecurityInfo = aChannelInfo.securityInfo();
mRedirectedURISpec = aChannelInfo.redirectedURI();
mRedirected = aChannelInfo.redirected();
mInited = true;
}
@@ -56,16 +74,22 @@ ChannelInfo::SetSecurityInfo(nsISupports* aSecurityInfo)
nsresult
ChannelInfo::ResurrectInfoOnChannel(nsIChannel* aChannel)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mInited);
// These pointers may be null at this point. They must be checked before
// being dereferenced.
nsCOMPtr<nsIHttpChannel> httpChannel =
do_QueryInterface(aChannel);
nsCOMPtr<nsIJARChannel> jarChannel =
do_QueryInterface(aChannel);
if (!mSecurityInfo.IsEmpty()) {
nsCOMPtr<nsISupports> infoObj;
nsresult rv = NS_DeserializeObject(mSecurityInfo, getter_AddRefs(infoObj));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIHttpChannel> httpChannel =
do_QueryInterface(aChannel);
if (httpChannel) {
net::HttpBaseChannel* httpBaseChannel =
static_cast<net::HttpBaseChannel*>(httpChannel.get());
@@ -74,8 +98,6 @@ ChannelInfo::ResurrectInfoOnChannel(nsIChannel* aChannel)
return rv;
}
} else {
nsCOMPtr<nsIJARChannel> jarChannel =
do_QueryInterface(aChannel);
if (NS_WARN_IF(!jarChannel)) {
return NS_ERROR_FAILURE;
}
@@ -84,6 +106,30 @@ ChannelInfo::ResurrectInfoOnChannel(nsIChannel* aChannel)
}
}
if (mRedirected) {
nsLoadFlags flags = 0;
aChannel->GetLoadFlags(&flags);
flags |= nsIChannel::LOAD_REPLACE;
aChannel->SetLoadFlags(flags);
nsCOMPtr<nsIURI> redirectedURI;
nsresult rv = NS_NewURI(getter_AddRefs(redirectedURI),
mRedirectedURISpec);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (httpChannel) {
net::HttpBaseChannel* httpBaseChannel =
static_cast<net::HttpBaseChannel*>(httpChannel.get());
httpBaseChannel->OverrideURI(redirectedURI);
} else {
if (NS_WARN_IF(!jarChannel)) {
return NS_ERROR_FAILURE;
}
static_cast<nsJARChannel*>(jarChannel.get())->OverrideURI(redirectedURI);
}
}
return NS_OK;
}
@@ -98,6 +144,8 @@ ChannelInfo::AsIPCChannelInfo() const
IPCChannelInfo ipcInfo;
ipcInfo.securityInfo() = mSecurityInfo;
ipcInfo.redirectedURI() = mRedirectedURISpec;
ipcInfo.redirected() = mRedirected;
return ipcInfo;
}
+9
View File
@@ -8,8 +8,10 @@
#define mozilla_dom_ChannelInfo_h
#include "nsString.h"
#include "nsCOMPtr.h"
class nsIChannel;
class nsIURI;
namespace mozilla {
namespace ipc {
@@ -42,12 +44,15 @@ public:
ChannelInfo()
: mInited(false)
, mRedirected(false)
{
}
ChannelInfo(const ChannelInfo& aRHS)
: mSecurityInfo(aRHS.mSecurityInfo)
, mRedirectedURISpec(aRHS.mRedirectedURISpec)
, mInited(aRHS.mInited)
, mRedirected(aRHS.mRedirected)
{
}
@@ -55,7 +60,9 @@ public:
operator=(const ChannelInfo& aRHS)
{
mSecurityInfo = aRHS.mSecurityInfo;
mRedirectedURISpec = aRHS.mRedirectedURISpec;
mInited = aRHS.mInited;
mRedirected = aRHS.mRedirected;
return *this;
}
@@ -78,7 +85,9 @@ private:
private:
nsCString mSecurityInfo;
nsCString mRedirectedURISpec;
bool mInited;
bool mRedirected;
};
} // namespace dom
+2
View File
@@ -8,6 +8,8 @@ namespace ipc {
struct IPCChannelInfo
{
nsCString securityInfo;
nsCString redirectedURI;
bool redirected;
};
} // namespace ipc
+21 -11
View File
@@ -433,18 +433,18 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
// "If request's referrer is a URL, let referrerSource be request's
// referrer."
//
// This allows ServiceWorkers to function transparently when the referrer
// of the intercepted request is already set.
// XXXnsm - We never actually hit this from a fetch() call since both
// fetch and Request() create a new internal request whose referrer is
// always set to about:client. Should we just crash here instead until
// someone tries to use FetchDriver for non-fetch() APIs?
nsCOMPtr<nsIURI> referrerURI;
rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return FailWithNetworkError();
}
// FIXME(nsm): Can we assert that this case can only happen in
// ServiceWorkers and assume null mDocument?
rv =
httpChan->SetReferrerWithPolicy(nullptr,
httpChan->SetReferrerWithPolicy(referrerURI,
mDocument ? mDocument->GetReferrerPolicy() :
net::RP_Default);
if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -564,14 +564,16 @@ FetchDriver::ContinueHttpFetchAfterNetworkFetch()
}
already_AddRefed<InternalResponse>
FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse)
FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse, nsIURI* aFinalURI)
{
MOZ_ASSERT(aResponse);
if (!aResponse->FinalURL()) {
nsAutoCString reqURL;
nsAutoCString reqURL;
if (aFinalURI) {
aFinalURI->GetSpec(reqURL);
} else {
mRequest->GetURL(reqURL);
aResponse->SetUrl(reqURL);
}
aResponse->SetUrl(reqURL);
// FIXME(nsm): Handle mixed content check, step 7 of fetch.
@@ -600,7 +602,7 @@ FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse)
void
FetchDriver::BeginResponse(InternalResponse* aResponse)
{
nsRefPtr<InternalResponse> r = BeginAndGetFilteredResponse(aResponse);
nsRefPtr<InternalResponse> r = BeginAndGetFilteredResponse(aResponse, nullptr);
// Release the ref.
}
@@ -723,9 +725,17 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest,
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
response->InitChannelInfo(channel);
nsCOMPtr<nsIURI> channelURI;
rv = channel->GetURI(getter_AddRefs(channelURI));
if (NS_WARN_IF(NS_FAILED(rv))) {
FailWithNetworkError();
// Cancel request.
return rv;
}
// Resolves fetch() promise which may trigger code running in a worker. Make
// sure the Response is fully initialized before calling this.
mResponse = BeginAndGetFilteredResponse(response);
mResponse = BeginAndGetFilteredResponse(response, channelURI);
nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
+2 -1
View File
@@ -88,8 +88,9 @@ private:
nsresult HttpFetch(bool aCORSFlag = false, bool aCORSPreflightFlag = false, bool aAuthenticationFlag = false);
nsresult ContinueHttpFetchAfterNetworkFetch();
// Returns the filtered response sent to the observer.
// Callers who don't have access to a channel can pass null for aFinalURI.
already_AddRefed<InternalResponse>
BeginAndGetFilteredResponse(InternalResponse* aResponse);
BeginAndGetFilteredResponse(InternalResponse* aResponse, nsIURI* aFinalURI);
// Utility since not all cases need to do any post processing of the filtered
// response.
void BeginResponse(InternalResponse* aResponse);
+42 -1
View File
@@ -7,6 +7,8 @@
#include "InternalResponse.h"
#include "mozilla/dom/InternalHeaders.h"
#include "mozilla/dom/cache/CacheTypes.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "nsStreamUtils.h"
namespace mozilla {
@@ -14,13 +16,16 @@ namespace dom {
InternalResponse::InternalResponse(uint16_t aStatus, const nsACString& aStatusText)
: mType(ResponseType::Default)
, mFinalURL(false)
, mStatus(aStatus)
, mStatusText(aStatusText)
, mHeaders(new InternalHeaders(HeadersGuardEnum::Response))
{
}
InternalResponse::~InternalResponse()
{
}
already_AddRefed<InternalResponse>
InternalResponse::Clone()
{
@@ -74,5 +79,41 @@ InternalResponse::CORSResponse()
return cors.forget();
}
void
InternalResponse::SetPrincipalInfo(UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo)
{
mPrincipalInfo = Move(aPrincipalInfo);
}
already_AddRefed<InternalResponse>
InternalResponse::OpaqueResponse()
{
MOZ_ASSERT(!mWrappedResponse, "Can't OpaqueResponse a already wrapped response");
nsRefPtr<InternalResponse> response = new InternalResponse(0, EmptyCString());
response->mType = ResponseType::Opaque;
response->mTerminationReason = mTerminationReason;
response->mURL = mURL;
response->mChannelInfo = mChannelInfo;
if (mPrincipalInfo) {
response->mPrincipalInfo = MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo);
}
response->mWrappedResponse = this;
return response.forget();
}
already_AddRefed<InternalResponse>
InternalResponse::CreateIncompleteCopy()
{
nsRefPtr<InternalResponse> copy = new InternalResponse(mStatus, mStatusText);
copy->mType = mType;
copy->mTerminationReason = mTerminationReason;
copy->mURL = mURL;
copy->mChannelInfo = mChannelInfo;
if (mPrincipalInfo) {
copy->mPrincipalInfo = MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo);
}
return copy.forget();
}
} // namespace dom
} // namespace mozilla
+19 -37
View File
@@ -12,8 +12,13 @@
#include "mozilla/dom/ResponseBinding.h"
#include "mozilla/dom/ChannelInfo.h"
#include "mozilla/UniquePtr.h"
namespace mozilla {
namespace ipc {
class PrincipalInfo;
}
namespace dom {
class InternalHeaders;
@@ -41,18 +46,7 @@ public:
}
already_AddRefed<InternalResponse>
OpaqueResponse()
{
MOZ_ASSERT(!mWrappedResponse, "Can't OpaqueResponse a already wrapped response");
nsRefPtr<InternalResponse> response = new InternalResponse(0, EmptyCString());
response->mType = ResponseType::Opaque;
response->mTerminationReason = mTerminationReason;
response->mURL = mURL;
response->mFinalURL = mFinalURL;
response->mChannelInfo = mChannelInfo;
response->mWrappedResponse = this;
return response.forget();
}
OpaqueResponse();
already_AddRefed<InternalResponse>
BasicResponse();
@@ -90,18 +84,6 @@ public:
mURL.Assign(aURL);
}
bool
FinalURL() const
{
return mFinalURL;
}
void
SetFinalURL(bool aFinalURL)
{
mFinalURL = aFinalURL;
}
uint16_t
GetStatus() const
{
@@ -181,9 +163,18 @@ public:
return mChannelInfo;
}
const UniquePtr<mozilla::ipc::PrincipalInfo>&
GetPrincipalInfo() const
{
return mPrincipalInfo;
}
// Takes ownership of the principal info.
void
SetPrincipalInfo(UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo);
private:
~InternalResponse()
{ }
~InternalResponse();
explicit InternalResponse(const InternalResponse& aOther) = delete;
InternalResponse& operator=(const InternalResponse&) = delete;
@@ -191,26 +182,17 @@ private:
// Returns an instance of InternalResponse which is a copy of this
// InternalResponse, except headers, body and wrapped response (if any) which
// are left uninitialized. Used for cloning and filtering.
already_AddRefed<InternalResponse> CreateIncompleteCopy()
{
nsRefPtr<InternalResponse> copy = new InternalResponse(mStatus, mStatusText);
copy->mType = mType;
copy->mTerminationReason = mTerminationReason;
copy->mURL = mURL;
copy->mFinalURL = mFinalURL;
copy->mChannelInfo = mChannelInfo;
return copy.forget();
}
already_AddRefed<InternalResponse> CreateIncompleteCopy();
ResponseType mType;
nsCString mTerminationReason;
nsCString mURL;
bool mFinalURL;
const uint16_t mStatus;
const nsCString mStatusText;
nsRefPtr<InternalHeaders> mHeaders;
nsCOMPtr<nsIInputStream> mBody;
ChannelInfo mChannelInfo;
UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
// For filtered responses.
// Cache, and SW interception should always serialize/access the underlying
+10
View File
@@ -17,6 +17,10 @@
#include "InternalResponse.h"
namespace mozilla {
namespace ipc {
class PrincipalInfo;
}
namespace dom {
class Headers;
@@ -90,6 +94,12 @@ public:
return mInternalResponse->GetChannelInfo();
}
const UniquePtr<mozilla::ipc::PrincipalInfo>&
GetPrincipalInfo() const
{
return mInternalResponse->GetPrincipalInfo();
}
Headers* Headers_();
void
@@ -1,12 +1,25 @@
function testScript(script) {
function setupPrefs() {
return new Promise(function(resolve, reject) {
SpecialPowers.pushPrefEnv({
"set": [["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
["dom.serviceWorkers.exemptFromPerDomainMax", true]]
}, resolve);
});
}
function workerTest() {
return new Promise(function(resolve, reject) {
var worker = new Worker("worker_wrapper.js");
worker.onmessage = function(event) {
if (event.data.context != "Worker") {
return;
}
if (event.data.type == 'finish') {
resolve();
} else if (event.data.type == 'status') {
ok(event.data.status, "Worker fetch test: " + event.data.msg);
ok(event.data.status, event.data.context + ": " + event.data.msg);
}
}
worker.onerror = function(event) {
@@ -17,6 +30,60 @@ function testScript(script) {
});
}
function serviceWorkerTest() {
var isB2G = !navigator.userAgent.includes("Android") &&
/Mobile|Tablet/.test(navigator.userAgent);
if (isB2G) {
// TODO B2G doesn't support running service workers for now due to bug 1137683.
dump("Skipping running the test in SW until bug 1137683 gets fixed.\n");
return Promise.resolve();
}
return new Promise(function(resolve, reject) {
function setupSW(registration) {
var worker = registration.waiting ||
registration.active;
window.addEventListener("message",function onMessage(event) {
if (event.data.context != "ServiceWorker") {
return;
}
if (event.data.type == 'finish') {
window.removeEventListener("message", onMessage);
registration.unregister()
.then(resolve)
.catch(reject);
} else if (event.data.type == 'status') {
ok(event.data.status, event.data.context + ": " + event.data.msg);
}
}, false);
worker.onerror = reject;
var iframe = document.createElement("iframe");
iframe.src = "message_receiver.html";
iframe.onload = function() {
worker.postMessage({ script: script });
};
document.body.appendChild(iframe);
}
navigator.serviceWorker.register("worker_wrapper.js", {scope: "."})
.then(function(registration) {
if (registration.installing) {
var done = false;
registration.installing.onstatechange = function() {
if (!done) {
done = true;
setupSW(registration);
}
};
} else {
setupSW(registration);
}
});
});
}
function windowTest() {
return new Promise(function(resolve, reject) {
var scriptEl = document.createElement("script");
@@ -29,13 +96,19 @@ function testScript(script) {
}
SimpleTest.waitForExplicitFinish();
// We have to run the window and worker tests sequentially since some tests
// set and compare cookies and running in parallel can lead to conflicting
// values.
windowTest()
// We have to run the window, worker and service worker tests sequentially
// since some tests set and compare cookies and running in parallel can lead
// to conflicting values.
setupPrefs()
.then(function() {
return windowTest();
})
.then(function() {
return workerTest();
})
.then(function() {
return serviceWorkerTest();
})
.catch(function(e) {
ok(false, "Some test failed in " + script);
info(e);
@@ -43,7 +116,11 @@ function testScript(script) {
return Promise.resolve();
})
.then(function() {
SimpleTest.finish();
if (parent && parent.finishTest) {
parent.finishTest();
} else {
SimpleTest.finish();
}
});
}
@@ -0,0 +1,6 @@
<!DOCTYPE html>
<script>
navigator.serviceWorker.onmessage = function(e) {
window.parent.postMessage(e.data, "*");
};
</script>
+19
View File
@@ -11,13 +11,32 @@ support-files =
test_response.js
utils.js
worker_wrapper.js
message_receiver.html
reroute.html
reroute.js
reroute.js^headers^
sw_reroute.js
[test_headers.html]
[test_headers_sw_reroute.html]
skip-if = buildapp == 'b2g' # Bug 1137683
[test_headers_mainthread.html]
[test_fetch_app_protocol.html]
[test_fetch_basic.html]
[test_fetch_basic_sw_reroute.html]
skip-if = buildapp == 'b2g' # Bug 1137683
[test_fetch_basic_http.html]
[test_fetch_basic_http_sw_reroute.html]
skip-if = e10s || buildapp == 'b2g' # Bug 1173163 for e10s, bug 1137683 for b2g
[test_fetch_cors.html]
[test_fetch_cors_sw_reroute.html]
skip-if = e10s || buildapp == 'b2g' # Bug 1173163 for e10s, bug 1137683 for b2g
[test_formdataparsing.html]
[test_formdataparsing_sw_reroute.html]
skip-if = buildapp == 'b2g' # Bug 1137683
[test_request.html]
[test_request_sw_reroute.html]
skip-if = buildapp == 'b2g' # Bug 1137683
[test_response.html]
[test_response_sw_reroute.html]
skip-if = buildapp == 'b2g' # Bug 1137683
+10
View File
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<script>
["SimpleTest", "ok", "info", "is", "$"]
.forEach((v) => window[v] = window.parent[v]);
</script>
<script type="text/javascript" src="utils.js"> </script>
<script type="text/javascript" src="fetch_test_framework.js"> </script>
<script>
testScript(location.search.substring(1) + ".js");
</script>
+3
View File
@@ -0,0 +1,3 @@
onfetch = function(e) {
e.respondWith(fetch(e.request));
};
@@ -0,0 +1 @@
Service-Worker-Allowed: /
+29
View File
@@ -0,0 +1,29 @@
var gRegistration;
function testScript(script) {
function setupSW(registration) {
gRegistration = registration;
var iframe = document.createElement("iframe");
iframe.src = "reroute.html?" + script.replace(".js", "");
document.body.appendChild(iframe);
}
SpecialPowers.pushPrefEnv({
"set": [["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
["dom.serviceWorkers.exemptFromPerDomainMax", true]]
}, function() {
navigator.serviceWorker.ready.then(setupSW);
navigator.serviceWorker.register("reroute.js", {scope: "/"});
});
}
function finishTest() {
gRegistration.unregister().then(SimpleTest.finish, function(e) {
dump("unregistration failed: " + e + "\n");
SimpleTest.finish();
});
}
SimpleTest.waitForExplicitFinish();
@@ -0,0 +1,22 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1039846 - Test fetch() http fetching in worker</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script type="text/javascript" src="sw_reroute.js"> </script>
<script class="testbody" type="text/javascript">
testScript("test_fetch_basic_http.js");
</script>
</body>
</html>
@@ -0,0 +1,22 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1039846 - Test fetch() function in worker</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script type="text/javascript" src="sw_reroute.js"> </script>
<script class="testbody" type="text/javascript">
testScript("test_fetch_basic.js");
</script>
</body>
</html>
@@ -0,0 +1,22 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1039846 - Test fetch() CORS mode</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script type="text/javascript" src="sw_reroute.js"> </script>
<script class="testbody" type="text/javascript">
testScript("test_fetch_cors.js");
</script>
</body>
</html>
@@ -0,0 +1,22 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1109751 - Test FormData parsing</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script type="text/javascript" src="sw_reroute.js"> </script>
<script class="testbody" type="text/javascript">
testScript("test_formdataparsing.js");
</script>
</body>
</html>
@@ -0,0 +1,16 @@
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<!DOCTYPE HTML>
<html>
<head>
<title>Test Fetch Headers - Basic</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<script type="text/javascript" src="sw_reroute.js"> </script>
<script class="testbody" type="text/javascript">
testScript("test_headers_common.js");
</script>
</body>
</html>
+1 -1
View File
@@ -5,7 +5,7 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug XXXXXX - Test Request object in worker</title>
<title>Test Request object in worker</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
@@ -0,0 +1,22 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test Request object in worker</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script type="text/javascript" src="sw_reroute.js"> </script>
<script class="testbody" type="text/javascript">
testScript("test_request.js");
</script>
</body>
</html>
@@ -0,0 +1,22 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1039846 - Test Response object in worker</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script type="text/javascript" src="sw_reroute.js"> </script>
<script class="testbody" type="text/javascript">
testScript("test_response.js");
</script>
</body>
</html>
+41 -14
View File
@@ -1,31 +1,58 @@
importScripts("utils.js");
var client;
var context;
function ok(a, msg) {
postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
client.postMessage({type: 'status', status: !!a,
msg: a + ": " + msg, context: context});
}
function is(a, b, msg) {
postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
client.postMessage({type: 'status', status: a === b,
msg: a + " === " + b + ": " + msg, context: context});
}
addEventListener('message', function workerWrapperOnMessage(e) {
removeEventListener('message', workerWrapperOnMessage);
var data = e.data;
var done = function() {
postMessage({ type: 'finish' });
function loadTest() {
var done = function() {
client.postMessage({ type: 'finish', context: context });
}
try {
importScripts(data.script);
// runTest() is provided by the test.
runTest().then(done, done);
} catch(e) {
client.postMessage({
type: 'status',
status: false,
msg: 'worker failed to import ' + data.script + "; error: " + e.message,
context: context
});
done();
}
}
try {
importScripts(data.script);
// runTest() is provided by the test.
runTest().then(done, done);
} catch(e) {
postMessage({
type: 'status',
status: false,
msg: 'worker failed to import ' + data.script + "; error: " + e.message
if ("ServiceWorker" in self) {
self.clients.matchAll().then(function(clients) {
for (var i = 0; i < clients.length; ++i) {
if (clients[i].url.indexOf("message_receiver.html") > -1) {
client = clients[i];
break;
}
}
if (!client) {
dump("We couldn't find the message_receiver window, the test will fail\n");
}
context = "ServiceWorker";
loadTest();
});
done();
} else {
client = self;
context = "Worker";
loadTest();
}
});
+43 -6
View File
@@ -38,6 +38,7 @@
#include "mozilla/LoadContext.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/dom/CacheBinding.h"
#include "mozilla/dom/cache/CacheTypes.h"
#include "mozilla/dom/cache/Cache.h"
#include "mozilla/dom/cache/CacheStorage.h"
#include "mozilla/dom/Exceptions.h"
@@ -422,6 +423,7 @@ private:
nsCOMPtr<nsIInputStreamPump> mPump;
nsCOMPtr<nsIURI> mBaseURI;
ChannelInfo mChannelInfo;
UniquePtr<PrincipalInfo> mPrincipalInfo;
};
NS_IMPL_ISUPPORTS(CacheScriptLoader, nsIStreamLoaderObserver)
@@ -592,6 +594,27 @@ private:
// saved in the cache.
ir->InitChannelInfo(channel);
// Save the principal of the channel since its URI encodes the script URI
// rather than the ServiceWorkerRegistrationInfo URI.
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
NS_ASSERTION(ssm, "Should never be null!");
nsCOMPtr<nsIPrincipal> channelPrincipal;
nsresult rv = ssm->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal));
if (NS_WARN_IF(NS_FAILED(rv))) {
channel->Cancel(rv);
return rv;
}
UniquePtr<PrincipalInfo> principalInfo(new PrincipalInfo());
rv = PrincipalToPrincipalInfo(channelPrincipal, principalInfo.get());
if (NS_WARN_IF(NS_FAILED(rv))) {
channel->Cancel(rv);
return rv;
}
ir->SetPrincipalInfo(Move(principalInfo));
nsRefPtr<Response> response = new Response(mCacheCreator->Global(), ir);
RequestOrUSVString request;
@@ -1046,7 +1069,8 @@ private:
void
DataReceivedFromCache(uint32_t aIndex, const uint8_t* aString,
uint32_t aStringLen,
const ChannelInfo& aChannelInfo)
const ChannelInfo& aChannelInfo,
UniquePtr<PrincipalInfo> aPrincipalInfo)
{
AssertIsOnMainThread();
MOZ_ASSERT(aIndex < mLoadInfos.Length());
@@ -1074,10 +1098,15 @@ private:
MOZ_ASSERT(principal);
nsILoadGroup* loadGroup = mWorkerPrivate->GetLoadGroup();
MOZ_ASSERT(loadGroup);
nsCOMPtr<nsIPrincipal> responsePrincipal =
PrincipalInfoToPrincipal(*aPrincipalInfo);
DebugOnly<bool> equal = false;
MOZ_ASSERT(responsePrincipal && NS_SUCCEEDED(responsePrincipal->Equals(principal, &equal)));
MOZ_ASSERT(equal);
mWorkerPrivate->InitChannelInfo(aChannelInfo);
// Needed to initialize the principal info. This is fine because
// the cache principal cannot change, unlike the channel principal.
mWorkerPrivate->SetPrincipal(principal, loadGroup);
mWorkerPrivate->SetPrincipal(responsePrincipal, loadGroup);
}
if (NS_SUCCEEDED(rv)) {
@@ -1429,10 +1458,16 @@ CacheScriptLoader::ResolvedCallback(JSContext* aCx,
nsCOMPtr<nsIInputStream> inputStream;
response->GetBody(getter_AddRefs(inputStream));
mChannelInfo = response->GetChannelInfo();
const UniquePtr<mozilla::ipc::PrincipalInfo>& pInfo =
response->GetPrincipalInfo();
if (pInfo) {
mPrincipalInfo = MakeUnique<mozilla::ipc::PrincipalInfo>(*pInfo);
}
if (!inputStream) {
mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
mRunnable->DataReceivedFromCache(mIndex, (uint8_t*)"", 0, mChannelInfo);
mRunnable->DataReceivedFromCache(mIndex, (uint8_t*)"", 0, mChannelInfo,
Move(mPrincipalInfo));
return;
}
@@ -1488,7 +1523,9 @@ CacheScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aCont
mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
mRunnable->DataReceivedFromCache(mIndex, aString, aStringLen, mChannelInfo);
MOZ_ASSERT(mPrincipalInfo);
mRunnable->DataReceivedFromCache(mIndex, aString, aStringLen, mChannelInfo,
Move(mPrincipalInfo));
return NS_OK;
}
+21 -4
View File
@@ -873,6 +873,7 @@ class ServiceWorkerRegisterJob final : public ServiceWorkerJob,
nsRefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsRefPtr<ServiceWorkerInfo> mUpdateAndInstallInfo;
nsCOMPtr<nsILoadGroup> mLoadGroup;
~ServiceWorkerRegisterJob()
{ }
@@ -893,15 +894,19 @@ public:
const nsCString& aScope,
const nsCString& aScriptSpec,
ServiceWorkerUpdateFinishCallback* aCallback,
nsIPrincipal* aPrincipal)
nsIPrincipal* aPrincipal,
nsILoadGroup* aLoadGroup)
: ServiceWorkerJob(aQueue)
, mScope(aScope)
, mScriptSpec(aScriptSpec)
, mCallback(aCallback)
, mPrincipal(aPrincipal)
, mLoadGroup(aLoadGroup)
, mJobType(REGISTER_JOB)
, mCanceled(false)
{ }
{
MOZ_ASSERT(mLoadGroup);
}
// [[Update]]
ServiceWorkerRegisterJob(ServiceWorkerJobQueue* aQueue,
@@ -1223,7 +1228,7 @@ private:
nsresult rv =
serviceWorkerScriptCache::Compare(mRegistration->mPrincipal, cacheName,
NS_ConvertUTF8toUTF16(mRegistration->mScriptSpec),
this);
this, mLoadGroup);
if (NS_WARN_IF(NS_FAILED(rv))) {
return Fail(rv);
}
@@ -1495,8 +1500,20 @@ ServiceWorkerManager::Register(nsIDOMWindow* aWindow,
nsRefPtr<ServiceWorkerResolveWindowPromiseOnUpdateCallback> cb =
new ServiceWorkerResolveWindowPromiseOnUpdateCallback(window, promise);
nsCOMPtr<nsILoadGroup> docLoadGroup = doc->GetDocumentLoadGroup();
nsRefPtr<WorkerLoadInfo::InterfaceRequestor> ir =
new WorkerLoadInfo::InterfaceRequestor(documentPrincipal, docLoadGroup);
ir->MaybeAddTabChild(docLoadGroup);
// Create a load group that is separate from, yet related to, the document's load group.
// This allows checks for interfaces like nsILoadContext to yield the values used by the
// the document, yet will not cancel the update job if the document's load group is cancelled.
nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
rv = loadGroup->SetNotificationCallbacks(ir);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
nsRefPtr<ServiceWorkerRegisterJob> job =
new ServiceWorkerRegisterJob(queue, cleanedScope, spec, cb, documentPrincipal);
new ServiceWorkerRegisterJob(queue, cleanedScope, spec, cb, documentPrincipal, loadGroup);
queue->Append(job);
AssertIsOnMainThread();
+47 -6
View File
@@ -9,6 +9,8 @@
#include "mozilla/dom/CacheBinding.h"
#include "mozilla/dom/cache/CacheStorage.h"
#include "mozilla/dom/cache/Cache.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "nsIThreadRetargetableRequest.h"
#include "nsIPrincipal.h"
@@ -72,7 +74,7 @@ public:
}
nsresult
Initialize(nsIPrincipal* aPrincipal, const nsAString& aURL)
Initialize(nsIPrincipal* aPrincipal, const nsAString& aURL, nsILoadGroup* aLoadGroup)
{
MOZ_ASSERT(aPrincipal);
AssertIsOnMainThread();
@@ -83,10 +85,17 @@ public:
return rv;
}
nsCOMPtr<nsILoadGroup> loadGroup;
rv = NS_NewLoadGroup(getter_AddRefs(loadGroup), aPrincipal);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = NS_NewChannel(getter_AddRefs(mChannel),
uri, aPrincipal,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_SCRIPT); // FIXME(nsm): TYPE_SERVICEWORKER
nsIContentPolicy::TYPE_SCRIPT, // FIXME(nsm): TYPE_SERVICEWORKER
loadGroup);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@@ -275,7 +284,7 @@ public:
nsresult
Initialize(nsIPrincipal* aPrincipal, const nsAString& aURL,
const nsAString& aCacheName)
const nsAString& aCacheName, nsILoadGroup* aLoadGroup)
{
AssertIsOnMainThread();
MOZ_ASSERT(aPrincipal);
@@ -292,7 +301,7 @@ public:
}
mCN = new CompareNetwork(this);
nsresult rv = mCN->Initialize(aPrincipal, aURL);
nsresult rv = mCN->Initialize(aPrincipal, aURL, aLoadGroup);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@@ -445,6 +454,28 @@ public:
mChannelInfo.InitFromChannel(aChannel);
}
nsresult
SetPrincipalInfo(nsIChannel* aChannel)
{
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
NS_ASSERTION(ssm, "Should never be null!");
nsCOMPtr<nsIPrincipal> channelPrincipal;
nsresult rv = ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(channelPrincipal));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
UniquePtr<mozilla::ipc::PrincipalInfo> principalInfo(new mozilla::ipc::PrincipalInfo());
rv = PrincipalToPrincipalInfo(channelPrincipal, principalInfo.get());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mPrincipalInfo = Move(principalInfo);
return NS_OK;
}
private:
~CompareManager()
{
@@ -540,6 +571,9 @@ private:
ir->SetBody(body);
ir->InitChannelInfo(mChannelInfo);
if (mPrincipalInfo) {
ir->SetPrincipalInfo(Move(mPrincipalInfo));
}
nsRefPtr<Response> response = new Response(aCache->GetGlobalObject(), ir);
@@ -572,6 +606,8 @@ private:
ChannelInfo mChannelInfo;
UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
nsCString mMaxScope;
enum {
@@ -600,6 +636,10 @@ CompareNetwork::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
#endif
mManager->InitChannelInfo(mChannel);
nsresult rv = mManager->SetPrincipalInfo(mChannel);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
@@ -930,7 +970,8 @@ GenerateCacheName(nsAString& aName)
nsresult
Compare(nsIPrincipal* aPrincipal, const nsAString& aCacheName,
const nsAString& aURL, CompareCallback* aCallback)
const nsAString& aURL, CompareCallback* aCallback,
nsILoadGroup* aLoadGroup)
{
AssertIsOnMainThread();
MOZ_ASSERT(aPrincipal);
@@ -939,7 +980,7 @@ Compare(nsIPrincipal* aPrincipal, const nsAString& aCacheName,
nsRefPtr<CompareManager> cm = new CompareManager(aCallback);
nsresult rv = cm->Initialize(aPrincipal, aURL, aCacheName);
nsresult rv = cm->Initialize(aPrincipal, aURL, aCacheName, aLoadGroup);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
+1 -1
View File
@@ -44,7 +44,7 @@ public:
nsresult
Compare(nsIPrincipal* aPrincipal, const nsAString& aCacheName,
const nsAString& aURL, CompareCallback* aCallback);
const nsAString& aURL, CompareCallback* aCallback, nsILoadGroup* aLoadGroup);
} // namespace serviceWorkerScriptCache
@@ -1,2 +1,3 @@
application.zip contains foo.txt, index.html, sw.js and manifest.webapp.
Any change to one of these three files should be added to application.zip as well.
application.list contains a list of files that are in application.zip.
To update application.zip when changing one of those files, run makezip.sh.
@@ -0,0 +1,7 @@
controlled.html
foo.txt
index.html
manifest.webapp
sw.js
test.js
test_doc_load_interception.js
@@ -3,12 +3,24 @@
<head>
<title>Test app for bug 1161684</title>
<script src='test.js'></script>
<script src='test_doc_load_interception.js'></script>
<script type='application/javascript;version=1.7'>
function runTests() {
return Promise.resolve()
.then(navigator.serviceWorker.ready)
.then(() => { return testFetchAppResource('swresponse'); })
.then(() => {
return testFetchAppResource('foo.txt',
'swresponse', 'text/plain');
})
.then(() => {
return testFetchAppResource('test_custom_content_type',
'customContentType', 'text/html');
})
.then(testRedirectedResponse)
.then(testRedirectedHttpsResponse)
.then(testCachedRedirectedResponse)
.then(testCachedRedirectedHttpsResponse)
.then(done);
}
</script>
@@ -7,7 +7,6 @@
function registerServiceWorker() {
return new Promise((resolve, reject) => {
navigator.serviceWorker.ready.then(() => {
ready();
resolve();
});
navigator.serviceWorker.register('sw.js', {scope: '.'})
@@ -20,9 +19,10 @@ function registerServiceWorker() {
function runTests() {
return Promise.resolve()
.then(() => { return testFetchAppResource('networkresponse'); })
.then(() => { return testFetchAppResource('foo.txt',
'networkresponse'); })
.then(registerServiceWorker)
.then(done);
.then(ready);
}
</script>
</head>
@@ -0,0 +1,4 @@
#!/bin/sh
rm application.zip
zip application.zip `cat application.list`
@@ -0,0 +1,2 @@
<!DOCTYPE html>
real index
@@ -0,0 +1 @@
Access-Control-Allow-Origin: *
@@ -0,0 +1,5 @@
function handleRequest(request, response) {
response.setStatusLine(null, 308, "Permanent Redirect");
response.setHeader("Access-Control-Allow-Origin", "*", false);
response.setHeader("Location", "https://example.org/tests/dom/workers/test/serviceworkers/app-protocol/realindex.html", false);
}
@@ -0,0 +1,5 @@
function handleRequest(request, response) {
response.setStatusLine(null, 308, "Permanent Redirect");
response.setHeader("Access-Control-Allow-Origin", "*", false);
response.setHeader("Location", "http://example.org/tests/dom/workers/test/serviceworkers/app-protocol/realindex.html", false);
}
@@ -1,8 +1,63 @@
const kHTTPRedirect = "http://example.com/tests/dom/workers/test/serviceworkers/app-protocol/redirect.sjs";
const kHTTPSRedirect = "https://example.com/tests/dom/workers/test/serviceworkers/app-protocol/redirect-https.sjs";
self.addEventListener('install', (event) => {
event.waitUntil(
self.caches.open("origin-app-cache")
.then(c => {
return Promise.all(
[
c.add(kHTTPRedirect),
c.add(kHTTPSRedirect),
]
);
})
);
});
self.addEventListener('fetch', (event) => {
if (event.request.url.indexOf('foo.txt') >= 0) {
var body = 'swresponse';
event.respondWith(new Response(body, {
event.respondWith(new Response('swresponse', {
headers: {'Content-Type': 'text/plain'}
}));
}
if (event.request.url.indexOf('test_doc_load_interception.js') >= 0 ) {
var scriptContent = 'alert("OK: Script modified by service worker")';
event.respondWith(new Response(scriptContent, {
headers: {'Content-Type': 'application/javascript'}
}));
}
if (event.request.url.indexOf('test_custom_content_type') >= 0) {
event.respondWith(new Response('customContentType', {
headers: {'Content-Type': 'text/html'}
}));
}
if (event.request.url.indexOf('redirected.html') >= 0) {
event.respondWith(fetch(kHTTPRedirect));
}
if (event.request.url.indexOf('redirected-https.html') >= 0) {
event.respondWith(fetch(kHTTPSRedirect));
}
if (event.request.url.indexOf('redirected-cached.html') >= 0) {
event.respondWith(
self.caches.open("origin-app-cache")
.then(c => {
return c.match(kHTTPRedirect);
})
);
}
if (event.request.url.indexOf('redirected-https-cached.html') >= 0) {
event.respondWith(
self.caches.open("origin-app-cache")
.then(c => {
return c.match(kHTTPSRedirect);
})
);
}
});
@@ -14,16 +14,59 @@ function done() {
alert('DONE');
}
function testFetchAppResource(aExpectedResponse) {
return fetch('foo.txt').then(res => {
function testFetchAppResource(aUrl,
aExpectedResponse,
aExpectedContentType) {
return fetch(aUrl).then(res => {
ok(true, 'fetch should resolve');
if (res.type == 'error') {
ok(false, 'fetch failed');
}
ok(res.status == 200, 'status should be 200');
ok(res.statusText == 'OK', 'statusText should be OK');
if (aExpectedContentType) {
var headers = res.headers.getAll('Content-Type');
ok(headers.length, "Headers length");
var contentType = res.headers.get('Content-Type');
ok(contentType == aExpectedContentType, ('content type ' +
contentType + ' should match with ' + aExpectedContentType));
}
return res.text().then(body => {
ok(body == aExpectedResponse, 'body should match');
ok(body == aExpectedResponse, 'body ' + body +
' should match with ' + aExpectedResponse);
});
});
}
function testRedirectedResponse() {
return testRedirectedResponseWorker("redirected", "IFRAMELOADED");
}
function testRedirectedHttpsResponse() {
return testRedirectedResponseWorker("redirected-https", "HTTPSIFRAMELOADED");
}
function testCachedRedirectedResponse() {
return testRedirectedResponseWorker("redirected-cached", "IFRAMELOADED");
}
function testCachedRedirectedHttpsResponse() {
return testRedirectedResponseWorker("redirected-https-cached", "HTTPSIFRAMELOADED");
}
function testRedirectedResponseWorker(aFrameId, aAlert) {
// Because of the CSP policies applied to privileged apps, we cannot run
// inline script inside realindex.html, and loading a script from the app://
// URI is also not an option, so we let the parent iframe which has access
// to the SpecialPowers API use those privileges to access the document.
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.src = aFrameId + ".html";
iframe.id = aFrameId;
return new Promise(resolve => {
iframe.addEventListener("load", event => {
alert(aAlert);
resolve();
}, false);
});
}
@@ -0,0 +1 @@
alert('KO: Should not load this file, but the sw modified version instead');
@@ -123,6 +123,25 @@ fetchXHR('http://example.com/tests/dom/base/test/file_CrossSiteXHR_server.sjs?st
finish();
});
// Test that when the page fetches a url the controlling SW forces a redirect to
// another location. This other location fetch should also be intercepted by
// the SW.
fetchXHR('something.txt', function(xhr) {
my_ok(xhr.status == 200, "load should be successful");
my_ok(xhr.responseText == "something else response body", "load should have something else");
finish();
});
// Test fetch will internally get it's SkipServiceWorker flag set. The request is
// made from the SW through fetch(). fetch() fetches a server-side JavaScript
// file that force a redirect. The redirect location fetch does not go through
// the SW.
fetchXHR('redirect_serviceworker.sjs', function(xhr) {
my_ok(xhr.status == 200, "load should be successful");
my_ok(xhr.responseText == "// empty worker, always succeed!\n", "load should have redirection content");
finish();
});
expectAsyncResult();
fetch('http://example.com/tests/dom/base/test/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*')
.then(function(res) {
@@ -0,0 +1,4 @@
function handleRequest(request, response) {
response.setStatusLine(null, 308, "Permanent Redirect");
response.setHeader("Location", "https://example.org/tests/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html", false);
}
@@ -0,0 +1,23 @@
var prefix = "/tests/dom/workers/test/serviceworkers/fetch/origin/https/";
self.addEventListener("install", function(event) {
event.waitUntil(
self.caches.open("origin-cache")
.then(c => {
return c.add(prefix + 'index-https.sjs');
})
);
});
self.addEventListener("fetch", function(event) {
if (event.request.url.indexOf("index-cached-https.sjs") >= 0) {
event.respondWith(
self.caches.open("origin-cache")
.then(c => {
return c.match(prefix + 'index-https.sjs');
})
);
} else {
event.respondWith(fetch(event.request));
}
});
@@ -0,0 +1,6 @@
<!DOCTYPE html>
<script>
window.opener.postMessage({status: "domain", data: document.domain}, "*");
window.opener.postMessage({status: "origin", data: location.origin}, "*");
window.opener.postMessage({status: "done"}, "*");
</script>
@@ -0,0 +1 @@
Access-Control-Allow-Origin: https://example.com
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<script>
function ok(v, msg) {
window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
}
function done(reg) {
ok(reg.active, "The active worker should be available.");
window.parent.postMessage({status: "registrationdone"}, "*");
}
navigator.serviceWorker.ready.then(done);
navigator.serviceWorker.register("origin_test.js", {scope: "."});
</script>
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<script>
navigator.serviceWorker.getRegistration(".").then(function(registration) {
registration.unregister().then(function(success) {
if (success) {
window.parent.postMessage({status: "unregistrationdone"}, "*");
}
}, function(e) {
dump("Unregistering the SW failed with " + e + "\n");
});
});
</script>
@@ -0,0 +1,4 @@
function handleRequest(request, response) {
response.setStatusLine(null, 308, "Permanent Redirect");
response.setHeader("Location", "https://example.org/tests/dom/workers/test/serviceworkers/fetch/origin/realindex.html", false);
}
@@ -0,0 +1,4 @@
function handleRequest(request, response) {
response.setStatusLine(null, 308, "Permanent Redirect");
response.setHeader("Location", "http://example.org/tests/dom/workers/test/serviceworkers/fetch/origin/realindex.html", false);
}
@@ -0,0 +1,35 @@
var prefix = "/tests/dom/workers/test/serviceworkers/fetch/origin/";
self.addEventListener("install", function(event) {
event.waitUntil(
self.caches.open("origin-cache")
.then(c => {
return Promise.all(
[
c.add(prefix + 'index.sjs'),
c.add(prefix + 'index-to-https.sjs'),
]
);
})
);
});
self.addEventListener("fetch", function(event) {
if (event.request.url.indexOf("index-cached.sjs") >= 0) {
event.respondWith(
self.caches.open("origin-cache")
.then(c => {
return c.match(prefix + 'index.sjs');
})
);
} else if (event.request.url.indexOf("index-to-https-cached.sjs") >= 0) {
event.respondWith(
self.caches.open("origin-cache")
.then(c => {
return c.match(prefix + 'index-to-https.sjs');
})
);
} else {
event.respondWith(fetch(event.request));
}
});
@@ -0,0 +1,6 @@
<!DOCTYPE html>
<script>
window.opener.postMessage({status: "domain", data: document.domain}, "*");
window.opener.postMessage({status: "origin", data: location.origin}, "*");
window.opener.postMessage({status: "done"}, "*");
</script>
@@ -0,0 +1 @@
Access-Control-Allow-Origin: http://mochi.test:8888
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<script>
function ok(v, msg) {
window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
}
function done(reg) {
ok(reg.active, "The active worker should be available.");
window.parent.postMessage({status: "registrationdone"}, "*");
}
navigator.serviceWorker.ready.then(done);
navigator.serviceWorker.register("origin_test.js", {scope: "."});
</script>
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<script>
navigator.serviceWorker.getRegistration(".").then(function(registration) {
registration.unregister().then(function(success) {
if (success) {
window.parent.postMessage({status: "unregistrationdone"}, "*");
}
}, function(e) {
dump("Unregistering the SW failed with " + e + "\n");
});
});
</script>
@@ -0,0 +1,7 @@
<!DOCTYPE html>
<script>
navigator.serviceWorker.onmessage = window.onmessage = e => {
window.parent.postMessage(e.data, "*");
};
</script>
<iframe src="redirector.html"></iframe>
@@ -0,0 +1,4 @@
function handleRequest(request, response) {
response.setStatusLine(null, 308, "Permanent Redirect");
response.setHeader("Location", "http://example.org/tests/dom/workers/test/serviceworkers/fetch/requesturl/secret.html", false);
}
@@ -0,0 +1,2 @@
<!DOCTYPE html>
<meta http-equiv="refresh" content="3;URL=/tests/dom/workers/test/serviceworkers/fetch/requesturl/redirect.sjs">
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<script>
function ok(v, msg) {
window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
}
function done(reg) {
ok(reg.active, "The active worker should be available.");
window.parent.postMessage({status: "registrationdone"}, "*");
}
navigator.serviceWorker.ready.then(done);
navigator.serviceWorker.register("requesturl_test.js", {scope: "."});
</script>
@@ -0,0 +1,17 @@
addEventListener("fetch", event => {
var url = event.request.url;
var badURL = url.indexOf("secret.html") > -1;
event.respondWith(
new Promise(resolve => {
clients.matchAll().then(clients => {
for (var client of clients) {
if (client.url.indexOf("index.html") > -1) {
client.postMessage({status: "ok", result: !badURL, message: "Should not find a bad URL (" + url + ")"});
break;
}
}
resolve(fetch(event.request));
});
})
);
});
@@ -0,0 +1,5 @@
<!DOCTYPE html>
secret stuff
<script>
window.parent.postMessage({status: "done"}, "*");
</script>
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<script>
navigator.serviceWorker.getRegistration(".").then(function(registration) {
registration.unregister().then(function(success) {
if (success) {
window.parent.postMessage({status: "unregistrationdone"}, "*");
}
}, function(e) {
dump("Unregistering the SW failed with " + e + "\n");
});
});
</script>
@@ -179,4 +179,20 @@ onfetch = function(ev) {
return new Response(body + body);
}));
}
}
else if (ev.request.url.includes('something.txt')) {
ev.respondWith(Response.redirect('fetch/somethingelse.txt'));
}
else if (ev.request.url.includes('somethingelse.txt')) {
ev.respondWith(new Response('something else response body', {}));
}
else if (ev.request.url.includes('redirect_serviceworker.sjs')) {
// The redirect_serviceworker.sjs server-side JavaScript file redirects to
// 'http://mochi.test:8888/tests/dom/workers/test/serviceworkers/worker.js'
// The redirected fetch should not go through the SW since the original
// fetch was initiated from a SW.
ev.respondWith(fetch('redirect_serviceworker.sjs'));
}
};
@@ -46,6 +46,26 @@ support-files =
fetch/https/clonedresponse/register.html
fetch/https/clonedresponse/unregister.html
fetch/https/clonedresponse/https_test.js
fetch/origin/index.sjs
fetch/origin/index-to-https.sjs
fetch/origin/realindex.html
fetch/origin/realindex.html^headers^
fetch/origin/register.html
fetch/origin/unregister.html
fetch/origin/origin_test.js
fetch/origin/https/index-https.sjs
fetch/origin/https/realindex.html
fetch/origin/https/realindex.html^headers^
fetch/origin/https/register.html
fetch/origin/https/unregister.html
fetch/origin/https/origin_test.js
fetch/requesturl/index.html
fetch/requesturl/redirect.sjs
fetch/requesturl/redirector.html
fetch/requesturl/register.html
fetch/requesturl/requesturl_test.js
fetch/requesturl/secret.html
fetch/requesturl/unregister.html
fetch/sandbox/index.html
fetch/sandbox/intercepted_index.html
fetch/sandbox/register.html
@@ -150,3 +170,10 @@ support-files =
[test_force_refresh.html]
[test_skip_waiting.html]
[test_strict_mode_error.html]
[test_cross_origin_url_after_redirect.html]
[test_origin_after_redirect.html]
[test_origin_after_redirect_cached.html]
[test_origin_after_redirect_to_https.html]
[test_origin_after_redirect_to_https_cached.html]
[test_https_origin_after_redirect.html]
[test_https_origin_after_redirect_cached.html]
@@ -22,14 +22,14 @@ const appManifestURL =
let gApp;
function setup() {
info('Setting up');
return new Promise((resolve, reject) => {
SpecialPowers.setAllAppsLaunchable(true);
SpecialPowers.pushPrefEnv({'set': [
['dom.mozBrowserFramesEnabled', true],
['dom.serviceWorkers.exemptFromPerDomainMax', true],
['dom.serviceWorkers.enabled', true],
['dom.serviceWorkers.testing.enabled', true]
['dom.serviceWorkers.testing.enabled', true],
['dom.caches.enabled', true],
]}, () => {
SpecialPowers.pushPermissions([
{ 'type': 'webapps-manage', 'allow': 1, 'context': document },
@@ -75,9 +75,15 @@ function launchApp() {
iframe.setAttribute('mozapp', gApp.manifestURL);
iframe.addEventListener('mozbrowsershowmodalprompt', function listener(e) {
let message = e.detail.message;
if (/READY/.exec(message)) {
if (/OK/.exec(message)) {
ok(true, "Message from app: " + message);
} else if (/KO/.exec(message)) {
ok(false, "Message from app: " + message);
} else if (/READY/.exec(message)) {
ok(true, "Message from app: " + message);
resolve();
} else {
ok(false, "Unexpected message received: " + message);
}
}, false);
let domParent = document.getElementById('container');
@@ -88,31 +94,42 @@ function launchApp() {
}
function loadControlled() {
info("reloading");
return new Promise((resolve, reject) => {
let iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', 'true');
iframe.setAttribute('mozapp', gApp.manifestURL);
iframe.addEventListener('mozbrowsershowmodalprompt', function listener(e) {
let message = e.detail.message;
info(message);
if (/OK/.exec(message)) {
ok(true, "Message from app: " + message);
} else if (/KO/.exec(message)) {
ok(false, "Message from app: " + message);
} else if (/HTTPSIFRAMELOADED/.exec(message)) {
let doc = SpecialPowers.wrap(iframe).contentDocument;
let innerDoc = SpecialPowers.wrap(doc.getElementById("redirected-https").contentDocument);
let innerLocation = innerDoc.defaultView.location;
is(innerDoc.domain, "example.org", "Correct domain expected (https)");
is(innerLocation.origin, "https://example.org", "Correct origin expected (https)");
} else if (/IFRAMELOADED/.exec(message)) {
let doc = SpecialPowers.wrap(iframe).contentDocument;
let innerDoc = SpecialPowers.wrap(doc.getElementById("redirected").contentDocument);
let innerLocation = innerDoc.defaultView.location;
is(innerDoc.domain, "example.org", "Correct domain expected");
is(innerLocation.origin, "http://example.org", "Correct origin expected");
} else if (/DONE/.exec(message)) {
ok(true, "Messaging from app complete");
iframe.removeEventListener('mozbrowsershowmodalprompt', listener);
let domParent = document.getElementById('container');
domParent.removeChild(iframe);
resolve();
} else {
ok(false, "Unexpected message received: " + message);
}
}, false);
let domParent = document.getElementById('container');
domParent.appendChild(iframe);
SpecialPowers.wrap(iframe.contentWindow).location =
gApp.origin + '/controlled.html';
info("reloaded");
});
}
@@ -0,0 +1,50 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test access to a cross origin Request.url property from a service worker for a redirected intercepted iframe</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
<iframe></iframe>
</div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
var iframe;
function runTest() {
iframe = document.querySelector("iframe");
iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/requesturl/register.html";
window.onmessage = function(e) {
if (e.data.status == "ok") {
ok(e.data.result, e.data.message);
} else if (e.data.status == "registrationdone") {
iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/requesturl/index.html";
} else if (e.data.status == "done") {
iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/requesturl/unregister.html";
} else if (e.data.status == "unregistrationdone") {
window.onmessage = null;
ok(true, "Test finished successfully");
SimpleTest.finish();
}
};
}
SimpleTest.waitForExplicitFinish();
onload = function() {
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
]}, runTest);
};
</script>
</pre>
</body>
</html>
@@ -0,0 +1,56 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test the origin of a redirected response from a service worker</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
<iframe></iframe>
</div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
var iframe;
function runTest() {
iframe = document.querySelector("iframe");
iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/register.html";
var win;
window.onmessage = function(e) {
if (e.data.status == "ok") {
ok(e.data.result, e.data.message);
} else if (e.data.status == "registrationdone") {
win = window.open("https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/index-https.sjs", "mywindow", "width=100,height=100");
} else if (e.data.status == "domain") {
is(e.data.data, "example.org", "Correct domain expected");
} else if (e.data.status == "origin") {
is(e.data.data, "https://example.org", "Correct origin expected");
} else if (e.data.status == "done") {
win.close();
iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/unregister.html";
} else if (e.data.status == "unregistrationdone") {
window.onmessage = null;
ok(true, "Test finished successfully");
SimpleTest.finish();
}
};
}
SimpleTest.waitForExplicitFinish();
onload = function() {
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.caches.enabled", true],
]}, runTest);
};
</script>
</pre>
</body>
</html>
@@ -0,0 +1,56 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test the origin of a redirected response from a service worker</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
<iframe></iframe>
</div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
var iframe;
function runTest() {
iframe = document.querySelector("iframe");
iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/register.html";
var win;
window.onmessage = function(e) {
if (e.data.status == "ok") {
ok(e.data.result, e.data.message);
} else if (e.data.status == "registrationdone") {
win = window.open("https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/index-cached-https.sjs", "mywindow", "width=100,height=100");
} else if (e.data.status == "domain") {
is(e.data.data, "example.org", "Correct domain expected");
} else if (e.data.status == "origin") {
is(e.data.data, "https://example.org", "Correct origin expected");
} else if (e.data.status == "done") {
win.close();
iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/unregister.html";
} else if (e.data.status == "unregistrationdone") {
window.onmessage = null;
ok(true, "Test finished successfully");
SimpleTest.finish();
}
};
}
SimpleTest.waitForExplicitFinish();
onload = function() {
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.caches.enabled", true],
]}, runTest);
};
</script>
</pre>
</body>
</html>
@@ -0,0 +1,57 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test the origin of a redirected response from a service worker</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
<iframe></iframe>
</div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
var iframe;
function runTest() {
iframe = document.querySelector("iframe");
iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/register.html";
var win;
window.onmessage = function(e) {
if (e.data.status == "ok") {
ok(e.data.result, e.data.message);
} else if (e.data.status == "registrationdone") {
win = window.open("/tests/dom/workers/test/serviceworkers/fetch/origin/index.sjs", "mywindow", "width=100,height=100");
} else if (e.data.status == "domain") {
is(e.data.data, "example.org", "Correct domain expected");
} else if (e.data.status == "origin") {
is(e.data.data, "http://example.org", "Correct origin expected");
} else if (e.data.status == "done") {
win.close();
iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/unregister.html";
} else if (e.data.status == "unregistrationdone") {
window.onmessage = null;
ok(true, "Test finished successfully");
SimpleTest.finish();
}
};
}
SimpleTest.waitForExplicitFinish();
onload = function() {
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
["dom.caches.enabled", true],
]}, runTest);
};
</script>
</pre>
</body>
</html>
@@ -0,0 +1,57 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test the origin of a redirected response from a service worker</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
<iframe></iframe>
</div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
var iframe;
function runTest() {
iframe = document.querySelector("iframe");
iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/register.html";
var win;
window.onmessage = function(e) {
if (e.data.status == "ok") {
ok(e.data.result, e.data.message);
} else if (e.data.status == "registrationdone") {
win = window.open("/tests/dom/workers/test/serviceworkers/fetch/origin/index-cached.sjs", "mywindow", "width=100,height=100");
} else if (e.data.status == "domain") {
is(e.data.data, "example.org", "Correct domain expected");
} else if (e.data.status == "origin") {
is(e.data.data, "http://example.org", "Correct origin expected");
} else if (e.data.status == "done") {
win.close();
iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/unregister.html";
} else if (e.data.status == "unregistrationdone") {
window.onmessage = null;
ok(true, "Test finished successfully");
SimpleTest.finish();
}
};
}
SimpleTest.waitForExplicitFinish();
onload = function() {
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
["dom.caches.enabled", true],
]}, runTest);
};
</script>
</pre>
</body>
</html>
@@ -0,0 +1,57 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test the origin of a redirected response from a service worker</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
<iframe></iframe>
</div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
var iframe;
function runTest() {
iframe = document.querySelector("iframe");
iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/register.html";
var win;
window.onmessage = function(e) {
if (e.data.status == "ok") {
ok(e.data.result, e.data.message);
} else if (e.data.status == "registrationdone") {
win = window.open("/tests/dom/workers/test/serviceworkers/fetch/origin/index-to-https.sjs", "mywindow", "width=100,height=100");
} else if (e.data.status == "domain") {
is(e.data.data, "example.org", "Correct domain expected");
} else if (e.data.status == "origin") {
is(e.data.data, "https://example.org", "Correct origin expected");
} else if (e.data.status == "done") {
win.close();
iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/unregister.html";
} else if (e.data.status == "unregistrationdone") {
window.onmessage = null;
ok(true, "Test finished successfully");
SimpleTest.finish();
}
};
}
SimpleTest.waitForExplicitFinish();
onload = function() {
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
["dom.caches.enabled", true],
]}, runTest);
};
</script>
</pre>
</body>
</html>
@@ -0,0 +1,57 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test the origin of a redirected response from a service worker</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
<iframe></iframe>
</div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
var iframe;
function runTest() {
iframe = document.querySelector("iframe");
iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/register.html";
var win;
window.onmessage = function(e) {
if (e.data.status == "ok") {
ok(e.data.result, e.data.message);
} else if (e.data.status == "registrationdone") {
win = window.open("/tests/dom/workers/test/serviceworkers/fetch/origin/index-to-https-cached.sjs", "mywindow", "width=100,height=100");
} else if (e.data.status == "domain") {
is(e.data.data, "example.org", "Correct domain expected");
} else if (e.data.status == "origin") {
is(e.data.data, "https://example.org", "Correct origin expected");
} else if (e.data.status == "done") {
win.close();
iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/unregister.html";
} else if (e.data.status == "unregistrationdone") {
window.onmessage = null;
ok(true, "Test finished successfully");
SimpleTest.finish();
}
};
}
SimpleTest.waitForExplicitFinish();
onload = function() {
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
["dom.caches.enabled", true],
]}, runTest);
};
</script>
</pre>
</body>
</html>
+4 -1
View File
@@ -9,7 +9,9 @@
#include <limits.h>
#include "js/TracingAPI.h"
#include "jspubtd.h"
#include "js/TraceKind.h"
#include "js/Utility.h"
/* These values are private to the JS engine. */
@@ -165,6 +167,7 @@ class JS_FRIEND_API(GCCellPtr)
explicit GCCellPtr(JSFunction* fun) : ptr(checkedCast(fun, JS::TraceKind::Object)) { }
explicit GCCellPtr(JSString* str) : ptr(checkedCast(str, JS::TraceKind::String)) { }
explicit GCCellPtr(JSFlatString* str) : ptr(checkedCast(str, JS::TraceKind::String)) { }
explicit GCCellPtr(JS::Symbol* sym) : ptr(checkedCast(sym, JS::TraceKind::Symbol)) { }
explicit GCCellPtr(JSScript* script) : ptr(checkedCast(script, JS::TraceKind::Script)) { }
explicit GCCellPtr(const Value& v);
+52
View File
@@ -0,0 +1,52 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/. */
#ifndef js_TraceKind_h
#define js_TraceKind_h
namespace JS {
// When tracing a thing, the GC needs to know about the layout of the object it
// is looking at. There are a fixed number of different layouts that the GC
// knows about. The "trace kind" is a static map which tells which layout a GC
// thing has.
//
// Although this map is public, the details are completely hidden. Not all of
// the matching C++ types are exposed, and those that are, are opaque.
//
// See Value::gcKind() and JSTraceCallback in Tracer.h for more details.
enum class TraceKind
{
// These trace kinds have a publicly exposed, although opaque, C++ type.
// Note: The order here is determined by our Value packing. Other users
// should sort alphabetically, for consistency.
Object = 0x00,
String = 0x01,
Symbol = 0x02,
Script = 0x03,
// Shape details are exposed through JS_TraceShapeCycleCollectorChildren.
Shape = 0x04,
// ObjectGroup details are exposed through JS_TraceObjectGroupCycleCollectorChildren.
ObjectGroup = 0x05,
// The kind associated with a nullptr.
Null = 0x06,
// The following kinds do not have an exposed C++ idiom.
BaseShape = 0x0F,
JitCode = 0x1F,
LazyScript = 0x2F
};
const static uintptr_t OutOfLineTraceKindMask = 0x07;
static_assert(uintptr_t(JS::TraceKind::BaseShape) & OutOfLineTraceKindMask, "mask bits are set");
static_assert(uintptr_t(JS::TraceKind::JitCode) & OutOfLineTraceKindMask, "mask bits are set");
static_assert(uintptr_t(JS::TraceKind::LazyScript) & OutOfLineTraceKindMask, "mask bits are set");
} // namespace JS
#endif // js_TraceKind_h
+53 -57
View File
@@ -8,9 +8,10 @@
#define js_TracingAPI_h
#include "jsalloc.h"
#include "jspubtd.h"
#include "js/HashTable.h"
#include "js/HeapAPI.h"
#include "js/TraceKind.h"
class JS_PUBLIC_API(JSTracer);
@@ -19,66 +20,20 @@ class JS_PUBLIC_API(CallbackTracer);
template <typename T> class Heap;
template <typename T> class TenuredHeap;
// When tracing a thing, the GC needs to know about the layout of the object it
// is looking at. There are a fixed number of different layouts that the GC
// knows about. The "trace kind" is a static map which tells which layout a GC
// thing has.
//
// Although this map is public, the details are completely hidden. Not all of
// the matching C++ types are exposed, and those that are, are opaque.
//
// See Value::gcKind() and JSTraceCallback in Tracer.h for more details.
enum class TraceKind
{
// These trace kinds have a publicly exposed, although opaque, C++ type.
// Note: The order here is determined by our Value packing. Other users
// should sort alphabetically, for consistency.
Object = 0x00,
String = 0x01,
Symbol = 0x02,
Script = 0x03,
// Shape details are exposed through JS_TraceShapeCycleCollectorChildren.
Shape = 0x04,
// ObjectGroup details are exposed through JS_TraceObjectGroupCycleCollectorChildren.
ObjectGroup = 0x05,
// The kind associated with a nullptr.
Null = 0x06,
// The following kinds do not have an exposed C++ idiom.
BaseShape = 0x0F,
JitCode = 0x1F,
LazyScript = 0x2F
};
const static uintptr_t OutOfLineTraceKindMask = 0x07;
static_assert(uintptr_t(JS::TraceKind::BaseShape) & OutOfLineTraceKindMask, "mask bits are set");
static_assert(uintptr_t(JS::TraceKind::JitCode) & OutOfLineTraceKindMask, "mask bits are set");
static_assert(uintptr_t(JS::TraceKind::LazyScript) & OutOfLineTraceKindMask, "mask bits are set");
// Returns a static string equivalent of |kind|.
JS_FRIEND_API(const char*)
GCTraceKindToAscii(JS::TraceKind kind);
} // namespace JS
// Tracer callback, called for each traceable thing directly referenced by a
// particular object or runtime structure. It is the callback responsibility
// to ensure the traversal of the full object graph via calling eventually
// JS_TraceChildren on the passed thing. In this case the callback must be
// prepared to deal with cycles in the traversal graph.
//
// kind argument is one of JS::TraceKind::Object, JS::TraceKind::String or a
// tag denoting internal implementation-specific traversal kind. In the latter
// case the only operations on thing that the callback can do is to call
// JS_TraceChildren or JS_GetTraceThingInfo.
//
// If eagerlyTraceWeakMaps is true, when we trace a WeakMap visit all
// of its mappings. This should be used in cases where the tracer
// wants to use the existing liveness of entries.
typedef void
(* JSTraceCallback)(JS::CallbackTracer* trc, void** thingp, JS::TraceKind kind);
namespace js {
class BaseShape;
class LazyScript;
class ObjectGroup;
namespace jit {
class JitCode;
} // namespace jit
} // namespace js
enum WeakMapTraceKind {
DoNotTraceWeakMaps = 0,
@@ -132,8 +87,34 @@ class JS_PUBLIC_API(CallbackTracer) : public JSTracer
contextName_(nullptr), contextIndex_(InvalidIndex), contextFunctor_(nullptr)
{}
// Override this method to receive notification when an edge is visited.
virtual void trace(void** thing, JS::TraceKind kind) = 0;
// Override these methods to receive notification when an edge is visited
// with the type contained in the callback. The default implementation
// dispatches to the fully-generic onChild implementation, so for cases that
// do not care about boxing overhead and do not need the actual edges,
// just override the generic onChild.
virtual void onObjectEdge(JSObject** objp) { onChild(JS::GCCellPtr(*objp)); }
virtual void onStringEdge(JSString** strp) { onChild(JS::GCCellPtr(*strp)); }
virtual void onSymbolEdge(JS::Symbol** symp) { onChild(JS::GCCellPtr(*symp)); }
virtual void onScriptEdge(JSScript** scriptp) { onChild(JS::GCCellPtr(*scriptp)); }
virtual void onShapeEdge(js::Shape** shapep) {
onChild(JS::GCCellPtr(*shapep, JS::TraceKind::Shape));
}
virtual void onObjectGroupEdge(js::ObjectGroup** groupp) {
onChild(JS::GCCellPtr(*groupp, JS::TraceKind::ObjectGroup));
}
virtual void onBaseShapeEdge(js::BaseShape** basep) {
onChild(JS::GCCellPtr(*basep, JS::TraceKind::BaseShape));
}
virtual void onJitCodeEdge(js::jit::JitCode** codep) {
onChild(JS::GCCellPtr(*codep, JS::TraceKind::JitCode));
}
virtual void onLazyScriptEdge(js::LazyScript** lazyp) {
onChild(JS::GCCellPtr(*lazyp, JS::TraceKind::LazyScript));
}
// Override this method to receive notification when a node in the GC
// heap graph is visited.
virtual void onChild(const JS::GCCellPtr& thing) = 0;
// Access to the tracing context:
// When tracing with a JS::CallbackTracer, we invoke the callback with the
@@ -185,6 +166,21 @@ class JS_PUBLIC_API(CallbackTracer) : public JSTracer
virtual TracerKind getTracerKind() const { return TracerKind::DoNotCare; }
#endif
// In C++, overriding a method hides all methods in the base class with
// that name, not just methods with that signature. Thus, the typed edge
// methods have to have distinct names to allow us to override them
// individually, which is freqently useful if, for example, we only want to
// process only one type of edge.
void dispatchToOnEdge(JSObject** objp) { onObjectEdge(objp); }
void dispatchToOnEdge(JSString** strp) { onStringEdge(strp); }
void dispatchToOnEdge(JS::Symbol** symp) { onSymbolEdge(symp); }
void dispatchToOnEdge(JSScript** scriptp) { onScriptEdge(scriptp); }
void dispatchToOnEdge(js::Shape** shapep) { onShapeEdge(shapep); }
void dispatchToOnEdge(js::ObjectGroup** groupp) { onObjectGroupEdge(groupp); }
void dispatchToOnEdge(js::BaseShape** basep) { onBaseShapeEdge(basep); }
void dispatchToOnEdge(js::jit::JitCode** codep) { onJitCodeEdge(codep); }
void dispatchToOnEdge(js::LazyScript** lazyp) { onLazyScriptEdge(lazyp); }
private:
friend class AutoTracingName;
const char* contextName_;
+1 -1
View File
@@ -331,7 +331,7 @@ class JS_FRIEND_API(Node) {
// JS::ubi::Node are both essentially tagged references to other sorts of
// objects, so letting conversions happen automatically is appropriate.
MOZ_IMPLICIT Node(JS::HandleValue value);
Node(JS::TraceKind kind, void* ptr);
explicit Node(const JS::GCCellPtr& thing);
// copy construction and copy assignment just use memcpy, since we know
// instances contain nothing but a vtable pointer and a data pointer.
+7 -12
View File
@@ -24,8 +24,9 @@ using namespace js;
* The elementTypes argument is not supported. The result list is
* pushed to *args.
*/
template <class InvokeArgs>
static bool
InitArgsFromArrayLike(JSContext* cx, HandleValue v, InvokeArgs* args, bool construct)
InitArgsFromArrayLike(JSContext* cx, HandleValue v, InvokeArgs* args)
{
// Step 3.
RootedObject obj(cx, NonNullObject(cx, v));
@@ -42,7 +43,7 @@ InitArgsFromArrayLike(JSContext* cx, HandleValue v, InvokeArgs* args, bool const
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TOO_MANY_FUN_APPLY_ARGS);
return false;
}
if (!args->init(len, construct))
if (!args->init(len))
return false;
// Steps 6-8.
@@ -71,7 +72,7 @@ Reflect_apply(JSContext* cx, unsigned argc, Value* vp)
// Steps 2-3.
FastInvokeGuard fig(cx, args.get(0));
InvokeArgs& invokeArgs = fig.args();
if (!InitArgsFromArrayLike(cx, args.get(2), &invokeArgs, false))
if (!InitArgsFromArrayLike(cx, args.get(2), &invokeArgs))
return false;
invokeArgs.setCallee(args.get(0));
invokeArgs.setThis(args.get(1));
@@ -108,18 +109,12 @@ Reflect_construct(JSContext* cx, unsigned argc, Value* vp)
}
// Step 4-5.
InvokeArgs invokeArgs(cx);
if (!InitArgsFromArrayLike(cx, args.get(1), &invokeArgs, true))
ConstructArgs constructArgs(cx);
if (!InitArgsFromArrayLike(cx, args.get(1), &constructArgs))
return false;
invokeArgs.setCallee(args.get(0));
invokeArgs.setThis(MagicValue(JS_THIS_POISON));
invokeArgs.newTarget().set(newTarget);
// Step 6.
if (!InvokeConstructor(cx, invokeArgs))
return false;
args.rval().set(invokeArgs.rval());
return true;
return Construct(cx, args.get(0), constructArgs, newTarget, args.rval());
}
/* ES6 26.1.3 Reflect.defineProperty(target, propertyKey, attributes) */
+2 -2
View File
@@ -800,8 +800,8 @@ class HasChildTracer : public JS::CallbackTracer
RootedValue child_;
bool found_;
void trace(void** thingp, JS::TraceKind kind) {
if (*thingp == child_.toGCThing())
void onChild(const JS::GCCellPtr& thing) override {
if (thing.asCell() == child_.toGCThing())
found_ = true;
}
+8 -2
View File
@@ -138,9 +138,15 @@ void
CheckHashTablesAfterMovingGC(JSRuntime* rt);
#endif
struct MovingTracer : JS::CallbackTracer {
struct MovingTracer : JS::CallbackTracer
{
explicit MovingTracer(JSRuntime* rt) : CallbackTracer(rt, TraceWeakMapKeysValues) {}
void trace(void** thingp, JS::TraceKind kind) override;
void onObjectEdge(JSObject** objp) override;
void onChild(const JS::GCCellPtr& thing) override {
MOZ_ASSERT(!RelocationOverlay::isCellForwarded(thing.asCell()));
}
#ifdef DEBUG
TracerKind getTracerKind() const override { return TracerKind::Moving; }
#endif
+11 -11
View File
@@ -2303,9 +2303,9 @@ TypeSet::MarkTypeUnbarriered(JSTracer* trc, TypeSet::Type* v, const char* name)
#ifdef DEBUG
struct AssertNonGrayTracer : public JS::CallbackTracer {
explicit AssertNonGrayTracer(JSRuntime* rt) : JS::CallbackTracer(rt) {}
void trace(void** thingp, JS::TraceKind kind) override {
DebugOnly<Cell*> thing(static_cast<Cell*>(*thingp));
MOZ_ASSERT_IF(thing->isTenured(), !thing->asTenured().isMarked(js::gc::GRAY));
void onChild(const JS::GCCellPtr& thing) override {
MOZ_ASSERT_IF(thing.asCell()->isTenured(),
!thing.asCell()->asTenured().isMarked(js::gc::GRAY));
}
};
#endif
@@ -2330,7 +2330,7 @@ struct UnmarkGrayTracer : public JS::CallbackTracer
unmarkedAny(false)
{}
void trace(void** thingp, JS::TraceKind kind) override;
void onChild(const JS::GCCellPtr& thing) override;
/* True iff we are tracing the immediate children of a shape. */
bool tracingShape;
@@ -2373,7 +2373,7 @@ struct UnmarkGrayTracer : public JS::CallbackTracer
* containers.
*/
void
UnmarkGrayTracer::trace(void** thingp, JS::TraceKind kind)
UnmarkGrayTracer::onChild(const JS::GCCellPtr& thing)
{
int stackDummy;
if (!JS_CHECK_STACK_SIZE(runtime()->mainThread.nativeStackLimit[StackForSystemCode],
@@ -2387,14 +2387,14 @@ UnmarkGrayTracer::trace(void** thingp, JS::TraceKind kind)
return;
}
Cell* cell = static_cast<Cell*>(*thingp);
Cell* cell = thing.asCell();
// Cells in the nursery cannot be gray, and therefore must necessarily point
// to only black edges.
if (!cell->isTenured()) {
#ifdef DEBUG
AssertNonGrayTracer nongray(runtime());
TraceChildren(&nongray, cell, kind);
TraceChildren(&nongray, cell, thing.kind());
#endif
return;
}
@@ -2411,16 +2411,16 @@ UnmarkGrayTracer::trace(void** thingp, JS::TraceKind kind)
// The parent will later trace |tenured|. This is done to avoid increasing
// the stack depth during shape tracing. It is safe to do because a shape
// can only have one child that is a shape.
UnmarkGrayTracer childTracer(this, kind == JS::TraceKind::Shape);
UnmarkGrayTracer childTracer(this, thing.kind() == JS::TraceKind::Shape);
if (kind != JS::TraceKind::Shape) {
TraceChildren(&childTracer, &tenured, kind);
if (thing.kind() != JS::TraceKind::Shape) {
TraceChildren(&childTracer, &tenured, thing.kind());
MOZ_ASSERT(!childTracer.previousShape);
unmarkedAny |= childTracer.unmarkedAny;
return;
}
MOZ_ASSERT(kind == JS::TraceKind::Shape);
MOZ_ASSERT(thing.kind() == JS::TraceKind::Shape);
Shape* shape = static_cast<Shape*>(&tenured);
if (tracingShape) {
MOZ_ASSERT(!previousShape);
+6 -6
View File
@@ -484,7 +484,7 @@ class BufferGrayRootsTracer : public JS::CallbackTracer
// Set to false if we OOM while buffering gray roots.
bool bufferingGrayRootsFailed;
void trace(void** thingp, JS::TraceKind kind) override;
void onChild(const JS::GCCellPtr& thing) override;
public:
explicit BufferGrayRootsTracer(JSRuntime* rt)
@@ -537,24 +537,24 @@ struct SetMaybeAliveFunctor {
};
void
BufferGrayRootsTracer::trace(void** thingp, JS::TraceKind kind)
BufferGrayRootsTracer::onChild(const JS::GCCellPtr& thing)
{
MOZ_ASSERT(runtime()->isHeapBusy());
if (bufferingGrayRootsFailed)
return;
gc::TenuredCell* thing = gc::TenuredCell::fromPointer(*thingp);
gc::TenuredCell* tenured = gc::TenuredCell::fromPointer(thing.asCell());
Zone* zone = thing->zone();
Zone* zone = tenured->zone();
if (zone->isCollecting()) {
// See the comment on SetMaybeAliveFlag to see why we only do this for
// objects and scripts. We rely on gray root buffering for this to work,
// but we only need to worry about uncollected dead compartments during
// incremental GCs (when we do gray root buffering).
CallTyped(SetMaybeAliveFunctor(), thing, kind);
CallTyped(SetMaybeAliveFunctor(), tenured, thing.kind());
if (!zone->gcGrayRoots.append(thing))
if (!zone->gcGrayRoots.append(tenured))
bufferingGrayRootsFailed = true;
}
}
+4 -7
View File
@@ -47,9 +47,8 @@ T
DoCallback(JS::CallbackTracer* trc, T* thingp, const char* name)
{
CheckTracedThing(trc, *thingp);
JS::TraceKind kind = MapTypeToTraceKind<typename mozilla::RemovePointer<T>::Type>::kind;
JS::AutoTracingName ctx(trc, name);
trc->trace(reinterpret_cast<void**>(thingp), kind);
trc->dispatchToOnEdge(thingp);
return *thingp;
}
#define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(name, type, _) \
@@ -316,21 +315,19 @@ struct ObjectGroupCycleCollectorTracer : public JS::CallbackTracer
innerTracer(innerTracer)
{}
void trace(void** thingp, JS::TraceKind kind) override;
void onChild(const JS::GCCellPtr& thing) override;
JS::CallbackTracer* innerTracer;
Vector<ObjectGroup*, 4, SystemAllocPolicy> seen, worklist;
};
void
ObjectGroupCycleCollectorTracer::trace(void** thingp, JS::TraceKind kind)
ObjectGroupCycleCollectorTracer::onChild(const JS::GCCellPtr& thing)
{
JS::GCCellPtr thing(*thingp, kind);
if (thing.isObject() || thing.isScript()) {
// Invoke the inner cycle collector callback on this child. It will not
// recurse back into TraceChildren.
innerTracer->trace(thingp, kind);
innerTracer->onChild(thing);
return;
}
+9 -9
View File
@@ -81,7 +81,7 @@ class js::VerifyPreTracer : public JS::CallbackTracer
{
JS::AutoDisableGenerationalGC noggc;
void trace(void** thingp, JS::TraceKind kind) override;
void onChild(const JS::GCCellPtr& thing) override;
public:
/* The gcNumber when the verification began. */
@@ -111,9 +111,9 @@ class js::VerifyPreTracer : public JS::CallbackTracer
* node.
*/
void
VerifyPreTracer::trace(void** thingp, JS::TraceKind kind)
VerifyPreTracer::onChild(const JS::GCCellPtr& thing)
{
MOZ_ASSERT(!IsInsideNursery(*reinterpret_cast<Cell**>(thingp)));
MOZ_ASSERT(!IsInsideNursery(thing.asCell()));
edgeptr += sizeof(EdgeValue);
if (edgeptr >= term) {
@@ -124,8 +124,8 @@ VerifyPreTracer::trace(void** thingp, JS::TraceKind kind)
VerifyNode* node = curnode;
uint32_t i = node->count;
node->edges[i].thing = *thingp;
node->edges[i].kind = kind;
node->edges[i].thing = thing.asCell();
node->edges[i].kind = thing.kind();
node->edges[i].label = contextName();
node->count++;
}
@@ -251,7 +251,7 @@ IsMarkedOrAllocated(TenuredCell* cell)
struct CheckEdgeTracer : public JS::CallbackTracer {
VerifyNode* node;
explicit CheckEdgeTracer(JSRuntime* rt) : JS::CallbackTracer(rt), node(nullptr) {}
void trace(void** thingp, JS::TraceKind kind) override;
void onChild(const JS::GCCellPtr& thing) override;
};
static const uint32_t MAX_VERIFIER_EDGES = 1000;
@@ -264,15 +264,15 @@ static const uint32_t MAX_VERIFIER_EDGES = 1000;
* been modified) must point to marked objects.
*/
void
CheckEdgeTracer::trace(void** thingp, JS::TraceKind kind)
CheckEdgeTracer::onChild(const JS::GCCellPtr& thing)
{
/* Avoid n^2 behavior. */
if (node->count > MAX_VERIFIER_EDGES)
return;
for (uint32_t i = 0; i < node->count; i++) {
if (node->edges[i].thing == *thingp) {
MOZ_ASSERT(node->edges[i].kind == kind);
if (node->edges[i].thing == thing.asCell()) {
MOZ_ASSERT(node->edges[i].kind == thing.kind());
node->edges[i].thing = nullptr;
return;
}
+3 -2
View File
@@ -3,6 +3,7 @@ var t = gen.throw;
try {
new t;
} catch (e) {
actual = "" + e;
actual = e;
}
assertEq(actual, "TypeError: t is not a constructor");
assertEq(actual.name, "TypeError");
assertEq(/is not a constructor/.test(actual.message), true);
+19 -1
View File
@@ -8882,7 +8882,25 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint
}
if (op == JSOP_NEW) {
if (!InvokeConstructor(cx, callee, argc, args, true, res))
// Callees from the stack could have any old non-constructor callee.
if (!IsConstructor(callee)) {
ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, callee, nullptr);
return false;
}
ConstructArgs cargs(cx);
if (!cargs.init(argc))
return false;
for (uint32_t i = 0; i < argc; i++)
cargs[i].set(args[i]);
RootedValue newTarget(cx, args[argc]);
MOZ_ASSERT(IsConstructor(newTarget),
"either callee == newTarget, or the initial |new| checked "
"that IsConstructor(newTarget)");
if (!Construct(cx, callee, cargs, newTarget, res))
return false;
} else if ((op == JSOP_EVAL || op == JSOP_STRICTEVAL) &&
frame->scopeChain()->global().valueIsEval(callee))
+28 -7
View File
@@ -63,16 +63,37 @@ InvokeFunction(JSContext* cx, HandleObject obj, bool constructing, uint32_t argc
AutoArrayRooter argvRoot(cx, argc + 1 + constructing, argv);
// Data in the argument vector is arranged for a JIT -> JIT call.
Value thisv = argv[0];
RootedValue thisv(cx, argv[0]);
Value* argvWithoutThis = argv + 1;
// For constructing functions, |this| is constructed at caller side and we can just call Invoke.
// When creating this failed / is impossible at caller site, i.e. MagicValue(JS_IS_CONSTRUCTING),
// we use InvokeConstructor that creates it at the callee side.
if (thisv.isMagic(JS_IS_CONSTRUCTING))
return InvokeConstructor(cx, ObjectValue(*obj), argc, argvWithoutThis, true, rval);
RootedValue fval(cx, ObjectValue(*obj));
if (constructing) {
if (!IsConstructor(fval)) {
ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval, nullptr);
return false;
}
return Invoke(cx, thisv, ObjectValue(*obj), argc, argvWithoutThis, rval);
ConstructArgs cargs(cx);
if (!cargs.init(argc))
return false;
for (uint32_t i = 0; i < argc; i++)
cargs[i].set(argvWithoutThis[i]);
RootedValue newTarget(cx, argvWithoutThis[argc]);
// If |this| hasn't been created, we can use normal construction code.
if (thisv.isMagic(JS_IS_CONSTRUCTING))
return Construct(cx, fval, cargs, newTarget, rval);
// Otherwise the default |this| has already been created. We could
// almost perform a *call* at this point, but we'd break |new.target|
// in the function. So in this one weird case we call a one-off
// construction path that *won't* set |this| to JS_IS_CONSTRUCTING.
return InternalConstructWithProvidedThis(cx, fval, thisv, cargs, newTarget, rval);
}
return Invoke(cx, thisv, fval, argc, argvWithoutThis, rval);
}
bool
+4 -4
View File
@@ -8,16 +8,16 @@
#include "jsapi-tests/tests.h"
class CCWTestTracer : public JS::CallbackTracer {
void trace(void** thingp, JS::TraceKind kind) {
void onChild(const JS::GCCellPtr& thing) override {
numberOfThingsTraced++;
printf("*thingp = %p\n", *thingp);
printf("*thingp = %p\n", thing.asCell());
printf("*expectedThingp = %p\n", *expectedThingp);
printf("kind = %d\n", static_cast<int>(kind));
printf("kind = %d\n", static_cast<int>(thing.kind()));
printf("expectedKind = %d\n", static_cast<int>(expectedKind));
if (*thingp != *expectedThingp || kind != expectedKind)
if (thing.asCell() != *expectedThingp || thing.kind() != expectedKind)
okay = false;
}
+75 -49
View File
@@ -318,16 +318,21 @@ IterPerformanceStats(JSContext* cx,
}
JSRuntime* rt = JS_GetRuntime(cx);
for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
// First report the shared groups
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
JSCompartment* compartment = c.get();
if (!compartment->performanceMonitoring.isLinked()) {
if (!c->principals()) {
// Compartments without principals could show up here, but
// reporting them doesn't really make sense.
continue;
}
if (!c->performanceMonitoring.hasSharedGroup()) {
// Don't report compartments that do not even have a PerformanceGroup.
continue;
}
js::AutoCompartment autoCompartment(cx, compartment);
PerformanceGroup* group = compartment->performanceMonitoring.getGroup(cx);
PerformanceGroup* group = compartment->performanceMonitoring.getSharedGroup(cx);
if (group->data.ticks == 0) {
// Don't report compartments that have never been used.
continue;
@@ -339,7 +344,9 @@ IterPerformanceStats(JSContext* cx,
continue;
}
if (!(*walker)(cx, group->data, group->uid, closure)) {
if (!(*walker)(cx,
group->data, group->uid, nullptr,
closure)) {
// Issue in callback
return false;
}
@@ -348,7 +355,36 @@ IterPerformanceStats(JSContext* cx,
return false;
}
}
*processStats = rt->stopwatch.performance;
// Then report the own groups
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
JSCompartment* compartment = c.get();
if (!c->principals()) {
// Compartments without principals could show up here, but
// reporting them doesn't really make sense.
continue;
}
if (!c->performanceMonitoring.hasOwnGroup()) {
// Don't report compartments that do not even have a PerformanceGroup.
continue;
}
js::AutoCompartment autoCompartment(cx, compartment);
mozilla::RefPtr<PerformanceGroup> ownGroup = compartment->performanceMonitoring.getOwnGroup();
if (ownGroup->data.ticks == 0) {
// Don't report compartments that have never been used.
continue;
}
mozilla::RefPtr<PerformanceGroup> sharedGroup = compartment->performanceMonitoring.getSharedGroup(cx);
if (!(*walker)(cx,
ownGroup->data, ownGroup->uid, &sharedGroup->uid,
closure)) {
// Issue in callback
return false;
}
}
// Finally, report the process stats
*processStats = rt->stopwatch.performance.getOwnGroup()->data;
return true;
}
@@ -4604,7 +4640,16 @@ JS::Construct(JSContext* cx, HandleValue fval, const JS::HandleValueArray& args,
assertSameCompartment(cx, fval, args);
AutoLastFrameCheck lfc(cx);
return InvokeConstructor(cx, fval, args.length(), args.begin(), false, rval);
if (!IsConstructor(fval)) {
ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval, nullptr);
return false;
}
ConstructArgs cargs(cx);
if (!FillArgumentsFromArraylike(cx, cargs, args))
return false;
return js::Construct(cx, fval, cargs, fval, rval);
}
JS_PUBLIC_API(bool)
@@ -4616,26 +4661,22 @@ JS::Construct(JSContext* cx, HandleValue fval, HandleObject newTarget, const JS:
assertSameCompartment(cx, fval, newTarget, args);
AutoLastFrameCheck lfc(cx);
// Reflect.construct ensures that the supplied new.target value is a
// constructor. Frankly, this makes good sense, so we reproduce the check.
if (!newTarget->isConstructor()) {
RootedValue val(cx, ObjectValue(*newTarget));
ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, val, nullptr);
if (!IsConstructor(fval)) {
ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval, nullptr);
return false;
}
// This is a littlesilly, but we need to convert from what's useful for our
// consumers to what we can actually handle internally.
AutoValueVector argv(cx);
unsigned argc = args.length();
if (!argv.reserve(argc + 1))
RootedValue newTargetVal(cx, ObjectValue(*newTarget));
if (!IsConstructor(newTargetVal)) {
ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, newTargetVal, nullptr);
return false;
for (unsigned i = 0; i < argc; i++) {
argv.infallibleAppend(args[i]);
}
argv.infallibleAppend(ObjectValue(*newTarget));
return InvokeConstructor(cx, fval, argc, argv.begin(), true, rval);
ConstructArgs cargs(cx);
if (!FillArgumentsFromArraylike(cx, cargs, args))
return false;
return js::Construct(cx, fval, cargs, newTargetVal, rval);
}
static JSObject*
@@ -4645,36 +4686,21 @@ JS_NewHelper(JSContext* cx, HandleObject ctor, const JS::HandleValueArray& input
CHECK_REQUEST(cx);
assertSameCompartment(cx, ctor, inputArgs);
// This is not a simple variation of JS_CallFunctionValue because JSOP_NEW
// is not a simple variation of JSOP_CALL. We have to determine what class
// of object to create, create it, and clamp the return value to an object,
// among other details. InvokeConstructor does the hard work.
InvokeArgs args(cx);
if (!args.init(inputArgs.length(), true))
return nullptr;
args.setCallee(ObjectValue(*ctor));
args.setThis(NullValue());
PodCopy(args.array(), inputArgs.begin(), inputArgs.length());
args.newTarget().setObject(*ctor);
if (!InvokeConstructor(cx, args))
return nullptr;
if (!args.rval().isObject()) {
/*
* Although constructors may return primitives (via proxies), this
* API is asking for an object, so we report an error.
*/
JSAutoByteString bytes;
if (ValueToPrintable(cx, args.rval(), &bytes)) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_NEW_RESULT,
bytes.ptr());
}
RootedValue ctorVal(cx, ObjectValue(*ctor));
if (!IsConstructor(ctorVal)) {
ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, ctorVal, nullptr);
return nullptr;
}
return &args.rval().toObject();
ConstructArgs args(cx);
if (!FillArgumentsFromArraylike(cx, args, inputArgs))
return nullptr;
RootedValue rval(cx);
if (!js::Construct(cx, ctorVal, args, ctorVal, &rval))
return nullptr;
return &rval.toObject();
}
JS_PUBLIC_API(JSObject*)
+51 -33
View File
@@ -13,6 +13,7 @@
#include "mozilla/MemoryReporting.h"
#include "mozilla/Range.h"
#include "mozilla/RangedPtr.h"
#include "mozilla/RefPtr.h"
#include <stdarg.h>
#include <stddef.h>
@@ -5428,7 +5429,7 @@ BuildStackString(JSContext *cx, HandleObject stack, MutableHandleString stringp)
namespace js {
struct AutoStopwatch;
class AutoStopwatch;
// Container for performance data
// All values are monotonic.
@@ -5522,15 +5523,22 @@ struct PerformanceGroup {
stopwatch_ = nullptr;
}
explicit PerformanceGroup(JSContext* cx, void* key);
~PerformanceGroup()
{
MOZ_ASSERT(refCount_ == 0);
}
private:
// Refcounting. For use with mozilla::RefPtr.
void AddRef();
void Release();
// Construct a PerformanceGroup for a single compartment.
explicit PerformanceGroup(JSRuntime* rt);
// Construct a PerformanceGroup for a group of compartments.
explicit PerformanceGroup(JSContext* rt, void* key);
private:
PerformanceGroup& operator=(const PerformanceGroup&) = delete;
PerformanceGroup(const PerformanceGroup&) = delete;
JSRuntime* runtime_;
// The stopwatch currently monitoring the group,
// or `nullptr` if none. Used ony for comparison.
const AutoStopwatch* stopwatch_;
@@ -5542,38 +5550,41 @@ struct PerformanceGroup {
// The hash key for this PerformanceGroup.
void* const key_;
// Increment/decrement the refcounter, return the updated value.
uint64_t incRefCount() {
MOZ_ASSERT(refCount_ + 1 > 0);
return ++refCount_;
}
uint64_t decRefCount() {
MOZ_ASSERT(refCount_ > 0);
return --refCount_;
}
friend struct PerformanceGroupHolder;
private:
// A reference counter. Maintained by PerformanceGroupHolder.
// A reference counter.
uint64_t refCount_;
// `true` if this PerformanceGroup may be shared by several
// compartments, `false` if it is dedicated to a single
// compartment.
const bool isSharedGroup_;
};
//
// Indirection towards a PerformanceGroup.
// This structure handles reference counting for instances of PerformanceGroup.
// Each PerformanceGroupHolder handles:
// - a reference-counted indirection towards a PerformanceGroup shared
// by several compartments
// - a owned PerformanceGroup representing the performance of a single
// compartment.
//
struct PerformanceGroupHolder {
// Get the group.
// Get the shared group.
// On first call, this causes a single Hashtable lookup.
// Successive calls do not require further lookups.
js::PerformanceGroup* getGroup(JSContext*);
js::PerformanceGroup* getSharedGroup(JSContext*);
// `true` if the this holder is currently associated to a
// Get the own group.
js::PerformanceGroup* getOwnGroup();
// `true` if the this holder is currently associated to a shared
// PerformanceGroup, `false` otherwise. Use this method to avoid
// instantiating a PerformanceGroup if you only need to get
// available performance data.
inline bool isLinked() const {
return group_ != nullptr;
inline bool hasSharedGroup() const {
return sharedGroup_ != nullptr;
}
inline bool hasOwnGroup() const {
return ownGroup_ != nullptr;
}
// Remove the link to the PerformanceGroup. This method is designed
@@ -5583,10 +5594,10 @@ struct PerformanceGroupHolder {
explicit PerformanceGroupHolder(JSRuntime* runtime)
: runtime_(runtime)
, group_(nullptr)
{ }
~PerformanceGroupHolder();
private:
private:
// Return the key representing this PerformanceGroup in
// Runtime::Stopwatch.
// Do not deallocate the key.
@@ -5594,10 +5605,11 @@ private:
JSRuntime *runtime_;
// The PerformanceGroup held by this object.
// Initially set to `nullptr` until the first cal to `getGroup`.
// The PerformanceGroups held by this object.
// Initially set to `nullptr` until the first call to `getGroup`.
// May be reset to `nullptr` by a call to `unlink`.
js::PerformanceGroup* group_;
mozilla::RefPtr<js::PerformanceGroup> sharedGroup_;
mozilla::RefPtr<js::PerformanceGroup> ownGroup_;
};
/**
@@ -5623,6 +5635,10 @@ extern JS_PUBLIC_API(bool)
SetStopwatchIsMonitoringJank(JSRuntime*, bool);
extern JS_PUBLIC_API(bool)
GetStopwatchIsMonitoringJank(JSRuntime*);
extern JS_PUBLIC_API(bool)
SetStopwatchIsMonitoringPerCompartment(JSRuntime*, bool);
extern JS_PUBLIC_API(bool)
GetStopwatchIsMonitoringPerCompartment(JSRuntime*);
extern JS_PUBLIC_API(bool)
IsStopwatchActive(JSRuntime*);
@@ -5634,7 +5650,9 @@ extern JS_PUBLIC_API(PerformanceData*)
GetPerformanceData(JSRuntime*);
typedef bool
(PerformanceStatsWalker)(JSContext* cx, const PerformanceData& stats, uint64_t uid, void* closure);
(PerformanceStatsWalker)(JSContext* cx,
const PerformanceData& stats, uint64_t uid,
const uint64_t* parentId, void* closure);
/**
* Extract the performance statistics.
+8 -5
View File
@@ -3152,13 +3152,16 @@ array_of(JSContext* cx, unsigned argc, Value* vp)
// Step 4.
RootedObject obj(cx);
{
ConstructArgs cargs(cx);
if (!cargs.init(1))
return false;
cargs[0].setNumber(args.length());
RootedValue v(cx);
Value argv[1] = {NumberValue(args.length())};
if (!InvokeConstructor(cx, args.thisv(), 1, argv, false, &v))
return false;
obj = ToObject(cx, v);
if (!obj)
if (!Construct(cx, args.thisv(), cargs, args.thisv(), &v))
return false;
obj = &v.toObject();
}
// Step 8.
+4 -4
View File
@@ -853,7 +853,7 @@ struct DumpHeapTracer : public JS::CallbackTracer, public WeakMapTracer
map, key.asCell(), kdelegate, value.asCell());
}
void trace(void** thingp, JS::TraceKind kind) override;
void onChild(const JS::GCCellPtr& thing) override;
};
static char
@@ -907,14 +907,14 @@ DumpHeapVisitCell(JSRuntime* rt, void* data, void* thing,
}
void
DumpHeapTracer::trace(void** thingp, JS::TraceKind kind)
DumpHeapTracer::onChild(const JS::GCCellPtr& thing)
{
if (gc::IsInsideNursery((js::gc::Cell*)*thingp))
if (gc::IsInsideNursery(thing.asCell()))
return;
char buffer[1024];
getTracingEdgeName(buffer, sizeof(buffer));
fprintf(output, "%s%p %c %s\n", prefix, *thingp, MarkDescriptor(*thingp), buffer);
fprintf(output, "%s%p %c %s\n", prefix, thing.asCell(), MarkDescriptor(thing.asCell()), buffer);
}
void

Some files were not shown because too many files have changed in this diff Show More