Merge mozilla-central to autoland r=merge a=merge on a CLOSED TREE

This commit is contained in:
Brindusan Cristian 2018-01-12 13:32:23 +02:00
Родитель 8d9a650ed6 4de0807d09
Коммит b102ef421c
82 изменённых файлов: 2004 добавлений и 582 удалений

Просмотреть файл

@ -68,7 +68,3 @@ groupbox description {
menulist label {
font-weight: unset;
}
.dialog-button-box {
padding: 0;
}

Просмотреть файл

@ -28,11 +28,6 @@
padding: 0;
}
.prefwindow[type="child"] > .prefpane {
-moz-box-flex: 1;
overflow: -moz-hidden-unscrollable;
}
.prefpane > groupbox + groupbox {
margin-top: 16px;
}

6
config/external/nspr/pr/moz.build поставляемый
Просмотреть файл

@ -40,7 +40,11 @@ elif CONFIG['OS_TARGET'] in ('FreeBSD', 'OpenBSD', 'NetBSD'):
SOURCES += ['/nsprpub/pr/src/md/unix/%s.c' % CONFIG['OS_TARGET'].lower()]
elif CONFIG['OS_TARGET'] == 'Darwin':
OS_LIBS += ['-framework CoreServices']
if CONFIG['HOST_MAJOR_VERSION'] >= '15':
if not CONFIG['HOST_MAJOR_VERSION']:
DEFINES.update(
HAS_CONNECTX=True,
)
elif CONFIG['HOST_MAJOR_VERSION'] >= '15':
DEFINES.update(
HAS_CONNECTX=True,
)

Просмотреть файл

@ -348,9 +348,9 @@ public:
virtual bool IsFrozen() const override;
void SyncStateFromParentWindow();
mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const override;
mozilla::Maybe<mozilla::dom::ClientState> GetClientState() const;
mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const;
mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const override;
void NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope);

Просмотреть файл

@ -9,6 +9,10 @@
#include "nsThreadUtils.h"
#include "nsHostObjectProtocolHandler.h"
using mozilla::Maybe;
using mozilla::dom::ClientInfo;
using mozilla::dom::ServiceWorkerDescriptor;
nsIGlobalObject::~nsIGlobalObject()
{
UnlinkHostObjectURIs();
@ -112,3 +116,19 @@ nsIGlobalObject::TraverseHostObjectURIs(nsCycleCollectionTraversalCallback &aCb)
nsHostObjectProtocolHandler::Traverse(mHostObjectURIs[index], aCb);
}
}
Maybe<ClientInfo>
nsIGlobalObject::GetClientInfo() const
{
// By default globals do not expose themselves as a client. Only real
// window and worker globals are currently considered clients.
return Maybe<ClientInfo>();
}
Maybe<ServiceWorkerDescriptor>
nsIGlobalObject::GetController() const
{
// By default globals do not have a service worker controller. Only real
// window and worker globals can currently be controlled as a client.
return Maybe<ServiceWorkerDescriptor>();
}

Просмотреть файл

@ -7,7 +7,10 @@
#ifndef nsIGlobalObject_h__
#define nsIGlobalObject_h__
#include "mozilla/Maybe.h"
#include "mozilla/dom/ClientInfo.h"
#include "mozilla/dom/DispatcherTrait.h"
#include "mozilla/dom/ServiceWorkerDescriptor.h"
#include "nsISupports.h"
#include "nsStringFwd.h"
#include "nsTArray.h"
@ -76,6 +79,12 @@ public:
virtual bool IsInSyncOperation() { return false; }
virtual mozilla::Maybe<mozilla::dom::ClientInfo>
GetClientInfo() const;
virtual mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor>
GetController() const;
protected:
virtual ~nsIGlobalObject();

Просмотреть файл

@ -1087,25 +1087,37 @@ FetchDriver::OnDataAvailable(nsIRequest* aRequest,
}
}
uint32_t aRead;
// Needs to be initialized to 0 because in some cases nsStringInputStream may
// not write to aRead.
uint32_t aRead = 0;
MOZ_ASSERT(mResponse);
MOZ_ASSERT(mPipeOutputStream);
// From "Main Fetch" step 19: SRI-part2.
// Note: Avoid checking the hidden opaque body.
nsresult rv;
if (mResponse->Type() != ResponseType::Opaque &&
ShouldCheckSRI(mRequest, mResponse)) {
MOZ_ASSERT(mSRIDataVerifier);
SRIVerifierAndOutputHolder holder(mSRIDataVerifier, mPipeOutputStream);
nsresult rv = aInputStream->ReadSegments(CopySegmentToStreamAndSRI,
&holder, aCount, &aRead);
return rv;
rv = aInputStream->ReadSegments(CopySegmentToStreamAndSRI,
&holder, aCount, &aRead);
} else {
rv = aInputStream->ReadSegments(NS_CopySegmentToStream,
mPipeOutputStream,
aCount, &aRead);
}
nsresult rv = aInputStream->ReadSegments(NS_CopySegmentToStream,
mPipeOutputStream,
aCount, &aRead);
// If no data was read, it's possible the output stream is closed but the
// ReadSegments call followed its contract of returning NS_OK despite write
// errors. Unfortunately, nsIOutputStream has an ill-conceived contract when
// taken together with ReadSegments' contract, because the pipe will just
// NS_OK if we try and invoke its Write* functions ourselves with a 0 count.
// So we must just assume the pipe is broken.
if (aRead == 0 && aCount != 0) {
return NS_BASE_STREAM_CLOSED;
}
return rv;
}

Просмотреть файл

@ -34,7 +34,10 @@ public:
{
if (!mWasNotified) {
mWasNotified = true;
mReader->CloseAndRelease(NS_ERROR_DOM_INVALID_STATE_ERR);
// The WorkerPrivate does have a context available, and we could pass it
// here to trigger cancellation of the reader, but the author of this
// comment chickened out.
mReader->CloseAndRelease(nullptr, NS_ERROR_DOM_INVALID_STATE_ERR);
}
return true;
@ -124,11 +127,17 @@ FetchStreamReader::FetchStreamReader(nsIGlobalObject* aGlobal)
FetchStreamReader::~FetchStreamReader()
{
CloseAndRelease(NS_BASE_STREAM_CLOSED);
CloseAndRelease(nullptr, NS_BASE_STREAM_CLOSED);
}
// If a context is provided, an attempt will be made to cancel the reader. The
// only situation where we don't expect to have a context is when closure is
// being triggered from the destructor or the WorkerHolder is notifying. If
// we're at the destructor, it's far too late to cancel anything. And if the
// WorkerHolder is being notified, the global is going away, so there's also
// no need to do further JS work.
void
FetchStreamReader::CloseAndRelease(nsresult aStatus)
FetchStreamReader::CloseAndRelease(JSContext* aCx, nsresult aStatus)
{
NS_ASSERT_OWNINGTHREAD(FetchStreamReader);
@ -139,6 +148,22 @@ FetchStreamReader::CloseAndRelease(nsresult aStatus)
RefPtr<FetchStreamReader> kungFuDeathGrip = this;
if (aCx) {
MOZ_ASSERT(mReader);
RefPtr<DOMException> error = DOMException::Create(aStatus);
JS::Rooted<JS::Value> errorValue(aCx);
if (ToJSValue(aCx, error, &errorValue)) {
JS::Rooted<JSObject*> reader(aCx, mReader);
// It's currently safe to cancel an already closed reader because, per the
// comments in ReadableStream::cancel() conveying the spec, step 2 of
// 3.4.3 that specified ReadableStreamCancel is: If stream.[[state]] is
// "closed", return a new promise resolved with undefined.
JS::ReadableStreamReaderCancel(aCx, reader, errorValue);
}
}
mStreamClosed = true;
mGlobal = nullptr;
@ -166,7 +191,7 @@ FetchStreamReader::StartConsuming(JSContext* aCx,
JS::ReadableStreamReaderMode::Default));
if (!reader) {
aRv.StealExceptionFromJSContext(aCx);
CloseAndRelease(NS_ERROR_DOM_INVALID_STATE_ERR);
CloseAndRelease(aCx, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
@ -206,14 +231,14 @@ FetchStreamReader::OnOutputStreamReady(nsIAsyncOutputStream* aStream)
reader));
if (NS_WARN_IF(!promise)) {
// Let's close the stream.
CloseAndRelease(NS_ERROR_DOM_INVALID_STATE_ERR);
CloseAndRelease(aes.cx(), NS_ERROR_DOM_INVALID_STATE_ERR);
return NS_ERROR_FAILURE;
}
RefPtr<Promise> domPromise = Promise::CreateFromExisting(mGlobal, promise);
if (NS_WARN_IF(!domPromise)) {
// Let's close the stream.
CloseAndRelease(NS_ERROR_DOM_INVALID_STATE_ERR);
CloseAndRelease(aes.cx(), NS_ERROR_DOM_INVALID_STATE_ERR);
return NS_ERROR_FAILURE;
}
@ -240,13 +265,13 @@ FetchStreamReader::ResolvedCallback(JSContext* aCx,
FetchReadableStreamReadDataDone valueDone;
if (!valueDone.Init(aCx, aValue)) {
JS_ClearPendingException(aCx);
CloseAndRelease(NS_ERROR_DOM_INVALID_STATE_ERR);
CloseAndRelease(aCx, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
if (valueDone.mDone) {
// Stream is completed.
CloseAndRelease(NS_BASE_STREAM_CLOSED);
CloseAndRelease(aCx, NS_BASE_STREAM_CLOSED);
return;
}
@ -254,7 +279,7 @@ FetchStreamReader::ResolvedCallback(JSContext* aCx,
new FetchReadableStreamReadDataArray);
if (!value->Init(aCx, aValue) || !value->mValue.WasPassed()) {
JS_ClearPendingException(aCx);
CloseAndRelease(NS_ERROR_DOM_INVALID_STATE_ERR);
CloseAndRelease(aCx, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
@ -274,7 +299,12 @@ FetchStreamReader::ResolvedCallback(JSContext* aCx,
mBufferOffset = 0;
mBufferRemaining = len;
WriteBuffer();
nsresult rv = WriteBuffer();
if (NS_FAILED(rv)) {
// DOMException only understands errors from domerr.msg, so we normalize to
// identifying an abort if the write fails.
CloseAndRelease(aCx, NS_ERROR_DOM_ABORT_ERR);
}
}
nsresult
@ -296,7 +326,6 @@ FetchStreamReader::WriteBuffer()
}
if (NS_WARN_IF(NS_FAILED(rv))) {
CloseAndRelease(rv);
return rv;
}
@ -312,7 +341,6 @@ FetchStreamReader::WriteBuffer()
nsresult rv = mPipeOut->AsyncWait(this, 0, 0, mOwningEventTarget);
if (NS_WARN_IF(NS_FAILED(rv))) {
CloseAndRelease(rv);
return rv;
}
@ -324,7 +352,7 @@ FetchStreamReader::RejectedCallback(JSContext* aCx,
JS::Handle<JS::Value> aValue)
{
ReportErrorToConsole(aCx, aValue);
CloseAndRelease(NS_ERROR_FAILURE);
CloseAndRelease(aCx, NS_ERROR_FAILURE);
}
void

Просмотреть файл

@ -40,8 +40,12 @@ public:
void
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
// Idempotently close the output stream and null out all state. If aCx is
// provided, the reader will also be canceled. aStatus must be a DOM error
// as understood by DOMException because it will be provided as the
// cancellation reason.
void
CloseAndRelease(nsresult aStatus);
CloseAndRelease(JSContext* aCx, nsresult aStatus);
void
StartConsuming(JSContext* aCx,

Просмотреть файл

@ -1380,7 +1380,7 @@ DeleteFilesRunnable::Open()
quotaManager->OpenDirectory(mFileManager->Type(),
mFileManager->Group(),
mFileManager->Origin(),
Client::IDB,
quota::Client::IDB,
/* aExclusive */ false,
this);

Просмотреть файл

@ -1286,199 +1286,179 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement* aElement)
}
// Step 15. and later in the HTML5 spec
nsresult rv = NS_OK;
RefPtr<ScriptLoadRequest> request;
mozilla::net::ReferrerPolicy ourRefPolicy = mDocument->GetReferrerPolicy();
if (aElement->GetScriptExternal()) {
// external script
nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
if (!scriptURI) {
// Asynchronously report the failure to create a URI object
return ProcessExternalScript(aElement, scriptKind, type, scriptContent);
}
return ProcessInlineScript(aElement, scriptKind);
}
bool
ScriptLoader::ProcessExternalScript(nsIScriptElement* aElement,
ScriptKind aScriptKind,
nsAutoString aTypeAttr,
nsIContent* aScriptContent)
{
nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
if (!scriptURI) {
// Asynchronously report the failure to create a URI object
NS_DispatchToCurrentThread(
NewRunnableMethod("nsIScriptElement::FireErrorEvent",
aElement,
&nsIScriptElement::FireErrorEvent));
return false;
}
RefPtr<ScriptLoadRequest> request = LookupPreloadRequest(aElement, aScriptKind);
if (request && NS_FAILED(CheckContentPolicy(mDocument, aElement, request->mURI,
aTypeAttr, false))) {
// Probably plans have changed; even though the preload was allowed seems
// like the actual load is not; let's cancel the preload request.
request->Cancel();
return false;
}
if (request) {
// Use the preload request.
// It's possible these attributes changed since we started the preload so
// update them here.
request->SetScriptMode(aElement->GetScriptDeferred(),
aElement->GetScriptAsync());
} else {
// No usable preload found.
SRIMetadata sriMetadata;
{
nsAutoString integrity;
aScriptContent->AsElement()->GetAttr(kNameSpaceID_None,
nsGkAtoms::integrity,
integrity);
GetSRIMetadata(integrity, &sriMetadata);
}
nsCOMPtr<nsIPrincipal> principal = aElement->GetScriptURITriggeringPrincipal();
if (!principal) {
principal = aScriptContent->NodePrincipal();
}
CORSMode ourCORSMode = aElement->GetCORSMode();
mozilla::net::ReferrerPolicy ourRefPolicy = mDocument->GetReferrerPolicy();
request = CreateLoadRequest(aScriptKind, scriptURI, aElement,
ourCORSMode, sriMetadata, ourRefPolicy);
request->mTriggeringPrincipal = Move(principal);
request->mIsInline = false;
request->SetScriptMode(aElement->GetScriptDeferred(),
aElement->GetScriptAsync());
// keep request->mScriptFromHead to false so we don't treat non preloaded
// scripts as blockers for full page load. See bug 792438.
nsresult rv = StartLoad(request);
if (NS_FAILED(rv)) {
ReportErrorToConsole(request, rv);
// Asynchronously report the load failure
NS_DispatchToCurrentThread(
NewRunnableMethod("nsIScriptElement::FireErrorEvent",
aElement,
&nsIScriptElement::FireErrorEvent));
return false;
}
}
// Double-check that the preload matches what we're asked to load now.
CORSMode ourCORSMode = aElement->GetCORSMode();
nsTArray<PreloadInfo>::index_type i =
mPreloads.IndexOf(scriptURI.get(), 0, PreloadURIComparator());
if (i != nsTArray<PreloadInfo>::NoIndex) {
// preloaded
// note that a script-inserted script can steal a preload!
request = mPreloads[i].mRequest;
request->mElement = aElement;
nsString preloadCharset(mPreloads[i].mCharset);
mPreloads.RemoveElementAt(i);
// Should still be in loading stage of script.
NS_ASSERTION(!request->InCompilingStage(),
"Request should not yet be in compiling stage.");
// Double-check that the charset the preload used is the same as
// the charset we have now.
nsAutoString elementCharset;
aElement->GetScriptCharset(elementCharset);
if (elementCharset.Equals(preloadCharset) &&
ourCORSMode == request->mCORSMode &&
ourRefPolicy == request->mReferrerPolicy &&
scriptKind == request->mKind) {
rv = CheckContentPolicy(mDocument, aElement, request->mURI, type, false);
if (NS_FAILED(rv)) {
// probably plans have changed; even though the preload was allowed seems
// like the actual load is not; let's cancel the preload request.
request->Cancel();
return false;
}
} else {
// Drop the preload
request = nullptr;
}
}
if (request->IsAsyncScript()) {
AddAsyncRequest(request);
if (request->IsReadyToRun()) {
// The script is available already. Run it ASAP when the event
// loop gets a chance to spin.
if (request) {
// Use a preload request.
// It's possible these attributes changed since we started the preload so
// update them here.
request->SetScriptMode(aElement->GetScriptDeferred(),
aElement->GetScriptAsync());
} else {
// No usable preload found.
SRIMetadata sriMetadata;
{
nsAutoString integrity;
scriptContent->AsElement()->GetAttr(kNameSpaceID_None,
nsGkAtoms::integrity,
integrity);
if (!integrity.IsEmpty()) {
MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
("ScriptLoader::ProcessScriptElement, integrity=%s",
NS_ConvertUTF16toUTF8(integrity).get()));
nsAutoCString sourceUri;
if (mDocument->GetDocumentURI()) {
mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
}
SRICheck::IntegrityMetadata(integrity, sourceUri, mReporter,
&sriMetadata);
}
}
nsCOMPtr<nsIPrincipal> principal = aElement->GetScriptURITriggeringPrincipal();
if (!principal) {
principal = scriptContent->NodePrincipal();
}
request = CreateLoadRequest(scriptKind, scriptURI, aElement, ourCORSMode,
sriMetadata, ourRefPolicy);
request->mTriggeringPrincipal = Move(principal);
request->mIsInline = false;
request->SetScriptMode(aElement->GetScriptDeferred(),
aElement->GetScriptAsync());
// keep request->mScriptFromHead to false so we don't treat non preloaded
// scripts as blockers for full page load. See bug 792438.
rv = StartLoad(request);
if (NS_FAILED(rv)) {
ReportErrorToConsole(request, rv);
// Asynchronously report the load failure
NS_DispatchToCurrentThread(
NewRunnableMethod("nsIScriptElement::FireErrorEvent",
aElement,
&nsIScriptElement::FireErrorEvent));
return false;
}
}
// Should still be in loading stage of script.
NS_ASSERTION(!request->InCompilingStage(),
"Request should not yet be in compiling stage.");
if (request->IsAsyncScript()) {
AddAsyncRequest(request);
if (request->IsReadyToRun()) {
// The script is available already. Run it ASAP when the event
// loop gets a chance to spin.
// KVKV TODO: Instead of processing immediately, try off-thread-parsing
// it and only schedule a pending ProcessRequest if that fails.
ProcessPendingRequestsAsync();
}
return false;
}
if (!aElement->GetParserCreated()) {
// Violate the HTML5 spec in order to make LABjs and the "order" plug-in
// for RequireJS work with their Gecko-sniffed code path. See
// http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html
request->mIsNonAsyncScriptInserted = true;
mNonAsyncExternalScriptInsertedRequests.AppendElement(request);
if (request->IsReadyToRun()) {
// The script is available already. Run it ASAP when the event
// loop gets a chance to spin.
ProcessPendingRequestsAsync();
}
return false;
}
// we now have a parser-inserted request that may or may not be still
// loading
if (request->IsDeferredScript()) {
// We don't want to run this yet.
// If we come here, the script is a parser-created script and it has
// the defer attribute but not the async attribute. Since a
// a parser-inserted script is being run, we came here by the parser
// running the script, which means the parser is still alive and the
// parse is ongoing.
NS_ASSERTION(mDocument->GetCurrentContentSink() ||
aElement->GetParserCreated() == FROM_PARSER_XSLT,
"Non-XSLT Defer script on a document without an active parser; bug 592366.");
AddDeferRequest(request);
return false;
}
if (aElement->GetParserCreated() == FROM_PARSER_XSLT) {
// Need to maintain order for XSLT-inserted scripts
NS_ASSERTION(!mParserBlockingRequest,
"Parser-blocking scripts and XSLT scripts in the same doc!");
request->mIsXSLT = true;
mXSLTRequests.AppendElement(request);
if (request->IsReadyToRun()) {
// The script is available already. Run it ASAP when the event
// loop gets a chance to spin.
ProcessPendingRequestsAsync();
}
return true;
}
if (request->IsReadyToRun() && ReadyToExecuteParserBlockingScripts()) {
// The request has already been loaded and there are no pending style
// sheets. If the script comes from the network stream, cheat for
// performance reasons and avoid a trip through the event loop.
if (aElement->GetParserCreated() == FROM_PARSER_NETWORK) {
return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
}
// Otherwise, we've got a document.written script, make a trip through
// the event loop to hide the preload effects from the scripts on the
// Web page.
NS_ASSERTION(!mParserBlockingRequest,
"There can be only one parser-blocking script at a time");
NS_ASSERTION(mXSLTRequests.isEmpty(),
"Parser-blocking scripts and XSLT scripts in the same doc!");
mParserBlockingRequest = request;
// KVKV TODO: Instead of processing immediately, try off-thread-parsing
// it and only schedule a pending ProcessRequest if that fails.
ProcessPendingRequestsAsync();
return true;
}
return false;
}
if (!aElement->GetParserCreated()) {
// Violate the HTML5 spec in order to make LABjs and the "order" plug-in
// for RequireJS work with their Gecko-sniffed code path. See
// http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html
request->mIsNonAsyncScriptInserted = true;
mNonAsyncExternalScriptInsertedRequests.AppendElement(request);
if (request->IsReadyToRun()) {
// The script is available already. Run it ASAP when the event
// loop gets a chance to spin.
ProcessPendingRequestsAsync();
}
return false;
}
// we now have a parser-inserted request that may or may not be still
// loading
if (request->IsDeferredScript()) {
// We don't want to run this yet.
// If we come here, the script is a parser-created script and it has
// the defer attribute but not the async attribute. Since a
// a parser-inserted script is being run, we came here by the parser
// running the script, which means the parser is still alive and the
// parse is ongoing.
NS_ASSERTION(mDocument->GetCurrentContentSink() ||
aElement->GetParserCreated() == FROM_PARSER_XSLT,
"Non-XSLT Defer script on a document without an active parser; bug 592366.");
AddDeferRequest(request);
return false;
}
// The script hasn't loaded yet or there's a style sheet blocking it.
// The script will be run when it loads or the style sheet loads.
if (aElement->GetParserCreated() == FROM_PARSER_XSLT) {
// Need to maintain order for XSLT-inserted scripts
NS_ASSERTION(!mParserBlockingRequest,
"Parser-blocking scripts and XSLT scripts in the same doc!");
request->mIsXSLT = true;
mXSLTRequests.AppendElement(request);
if (request->IsReadyToRun()) {
// The script is available already. Run it ASAP when the event
// loop gets a chance to spin.
ProcessPendingRequestsAsync();
}
return true;
}
if (request->IsReadyToRun() && ReadyToExecuteParserBlockingScripts()) {
// The request has already been loaded and there are no pending style
// sheets. If the script comes from the network stream, cheat for
// performance reasons and avoid a trip through the event loop.
if (aElement->GetParserCreated() == FROM_PARSER_NETWORK) {
return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
}
// Otherwise, we've got a document.written script, make a trip through
// the event loop to hide the preload effects from the scripts on the
// Web page.
NS_ASSERTION(!mParserBlockingRequest,
"There can be only one parser-blocking script at a time");
NS_ASSERTION(mXSLTRequests.isEmpty(),
"Parser-blocking scripts and XSLT scripts in the same doc!");
mParserBlockingRequest = request;
ProcessPendingRequestsAsync();
return true;
}
// inline script
// The script hasn't loaded yet or there's a style sheet blocking it.
// The script will be run when it loads or the style sheet loads.
NS_ASSERTION(!mParserBlockingRequest,
"There can be only one parser-blocking script at a time");
NS_ASSERTION(mXSLTRequests.isEmpty(),
"Parser-blocking scripts and XSLT scripts in the same doc!");
mParserBlockingRequest = request;
return true;
}
bool
ScriptLoader::ProcessInlineScript(nsIScriptElement* aElement,
ScriptKind aScriptKind)
{
// Is this document sandboxed without 'allow-scripts'?
if (mDocument->HasScriptsBlockedBySandbox()) {
return false;
@ -1491,14 +1471,15 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement* aElement)
// Inline classic scripts ignore their CORS mode and are always CORS_NONE.
CORSMode corsMode = CORS_NONE;
if (scriptKind == ScriptKind::eModule) {
if (aScriptKind == ScriptKind::eModule) {
corsMode = aElement->GetCORSMode();
}
request = CreateLoadRequest(scriptKind, mDocument->GetDocumentURI(), aElement,
corsMode,
SRIMetadata(), // SRI doesn't apply
ourRefPolicy);
RefPtr<ScriptLoadRequest> request =
CreateLoadRequest(aScriptKind, mDocument->GetDocumentURI(), aElement,
corsMode,
SRIMetadata(), // SRI doesn't apply
mDocument->GetReferrerPolicy());
request->mIsInline = true;
request->mTriggeringPrincipal = mDocument->NodePrincipal();
request->mLineNo = aElement->GetScriptLineNumber();
@ -1574,6 +1555,60 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement* aElement)
return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
}
ScriptLoadRequest*
ScriptLoader::LookupPreloadRequest(nsIScriptElement* aElement,
ScriptKind aScriptKind)
{
nsTArray<PreloadInfo>::index_type i =
mPreloads.IndexOf(aElement->GetScriptURI(), 0, PreloadURIComparator());
if (i == nsTArray<PreloadInfo>::NoIndex) {
return nullptr;
}
// Found preloaded request. Note that a script-inserted script can steal a
// preload!
RefPtr<ScriptLoadRequest> request = mPreloads[i].mRequest;
request->mElement = aElement;
nsString preloadCharset(mPreloads[i].mCharset);
mPreloads.RemoveElementAt(i);
// Double-check that the charset the preload used is the same as the charset
// we have now.
nsAutoString elementCharset;
aElement->GetScriptCharset(elementCharset);
if (!elementCharset.Equals(preloadCharset) ||
aElement->GetCORSMode() != request->mCORSMode ||
mDocument->GetReferrerPolicy() != request->mReferrerPolicy ||
aScriptKind != request->mKind) {
// Drop the preload.
return nullptr;
}
return request;
}
void
ScriptLoader::GetSRIMetadata(const nsAString& aIntegrityAttr,
SRIMetadata *aMetadataOut)
{
MOZ_ASSERT(aMetadataOut->IsEmpty());
if (aIntegrityAttr.IsEmpty()) {
return;
}
MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
("ScriptLoader::GetSRIMetadata, integrity=%s",
NS_ConvertUTF16toUTF8(aIntegrityAttr).get()));
nsAutoCString sourceUri;
if (mDocument->GetDocumentURI()) {
mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
}
SRICheck::IntegrityMetadata(aIntegrityAttr, sourceUri, mReporter,
aMetadataOut);
}
namespace {
class NotifyOffThreadScriptLoadCompletedRunnable : public Runnable
@ -3129,16 +3164,7 @@ ScriptLoader::PreloadURI(nsIURI* aURI, const nsAString& aCharset,
}
SRIMetadata sriMetadata;
if (!aIntegrity.IsEmpty()) {
MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
("ScriptLoader::PreloadURI, integrity=%s",
NS_ConvertUTF16toUTF8(aIntegrity).get()));
nsAutoCString sourceUri;
if (mDocument->GetDocumentURI()) {
mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
}
SRICheck::IntegrityMetadata(aIntegrity, sourceUri, mReporter, &sriMetadata);
}
GetSRIMetadata(aIntegrity, &sriMetadata);
RefPtr<ScriptLoadRequest> request =
CreateLoadRequest(ScriptKind::eClassic, aURI, nullptr,

Просмотреть файл

@ -357,6 +357,20 @@ private:
void ContinueParserAsync(ScriptLoadRequest* aParserBlockingRequest);
bool ProcessExternalScript(nsIScriptElement* aElement,
ScriptKind aScriptKind,
nsAutoString aTypeAttr,
nsIContent* aScriptContent);
bool ProcessInlineScript(nsIScriptElement* aElement,
ScriptKind aScriptKind);
ScriptLoadRequest* LookupPreloadRequest(nsIScriptElement* aElement,
ScriptKind aScriptKind);
void GetSRIMetadata(const nsAString& aIntegrityAttr,
SRIMetadata *aMetadataOut);
/**
* Helper function to check the content policy for a given request.
*/

Просмотреть файл

@ -35,15 +35,20 @@ public:
// WebIDL
already_AddRefed<SVGAnimatedLength> TextLength();
already_AddRefed<SVGAnimatedEnumeration> LengthAdjust();
int32_t GetNumberOfChars();
float GetComputedTextLength();
MOZ_CAN_RUN_SCRIPT int32_t GetNumberOfChars();
MOZ_CAN_RUN_SCRIPT float GetComputedTextLength();
MOZ_CAN_RUN_SCRIPT
void SelectSubString(uint32_t charnum, uint32_t nchars, ErrorResult& rv);
MOZ_CAN_RUN_SCRIPT
float GetSubStringLength(uint32_t charnum, uint32_t nchars, ErrorResult& rv);
MOZ_CAN_RUN_SCRIPT
already_AddRefed<nsISVGPoint> GetStartPositionOfChar(uint32_t charnum, ErrorResult& rv);
MOZ_CAN_RUN_SCRIPT
already_AddRefed<nsISVGPoint> GetEndPositionOfChar(uint32_t charnum, ErrorResult& rv);
MOZ_CAN_RUN_SCRIPT
already_AddRefed<SVGIRect> GetExtentOfChar(uint32_t charnum, ErrorResult& rv);
float GetRotationOfChar(uint32_t charnum, ErrorResult& rv);
int32_t GetCharNumAtPosition(nsISVGPoint& point);
MOZ_CAN_RUN_SCRIPT float GetRotationOfChar(uint32_t charnum, ErrorResult& rv);
MOZ_CAN_RUN_SCRIPT int32_t GetCharNumAtPosition(nsISVGPoint& point);
protected:
@ -51,9 +56,9 @@ protected:
: SVGTextContentElementBase(aNodeInfo)
{}
SVGTextFrame* GetSVGTextFrame();
SVGTextFrame* GetSVGTextFrameForNonLayoutDependentQuery();
mozilla::Maybe<int32_t> GetNonLayoutDependentNumberOfChars();
MOZ_CAN_RUN_SCRIPT SVGTextFrame* GetSVGTextFrame();
MOZ_CAN_RUN_SCRIPT SVGTextFrame* GetSVGTextFrameForNonLayoutDependentQuery();
MOZ_CAN_RUN_SCRIPT mozilla::Maybe<int32_t> GetNonLayoutDependentNumberOfChars();
enum { LENGTHADJUST };
virtual nsSVGEnum* EnumAttributes() = 0;

Просмотреть файл

@ -37,6 +37,7 @@ public:
already_AddRefed<SVGAnimatedTransformList> Transform();
nsSVGElement* GetNearestViewportElement();
nsSVGElement* GetFarthestViewportElement();
MOZ_CAN_RUN_SCRIPT
already_AddRefed<SVGIRect> GetBBox(const SVGBoundingBoxOptions& aOptions,
ErrorResult& rv);
already_AddRefed<SVGMatrix> GetCTM();

Просмотреть файл

@ -49,6 +49,7 @@
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/ClientManager.h"
#include "mozilla/dom/ClientSource.h"
#include "mozilla/dom/ClientState.h"
#include "mozilla/dom/Console.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/ErrorEvent.h"
@ -5333,6 +5334,24 @@ WorkerPrivate::GetClientInfo() const
return mClientSource->Info();
}
const ClientState
WorkerPrivate::GetClientState() const
{
AssertIsOnWorkerThread();
MOZ_DIAGNOSTIC_ASSERT(mClientSource);
ClientState state;
mClientSource->SnapshotState(&state);
return Move(state);
}
const Maybe<ServiceWorkerDescriptor>
WorkerPrivate::GetController() const
{
AssertIsOnWorkerThread();
MOZ_DIAGNOSTIC_ASSERT(mClientSource);
return mClientSource->GetController();
}
void
WorkerPrivate::Control(const ServiceWorkerDescriptor& aServiceWorker)
{

Просмотреть файл

@ -1494,6 +1494,12 @@ public:
const ClientInfo&
GetClientInfo() const;
const ClientState
GetClientState() const;
const Maybe<ServiceWorkerDescriptor>
GetController() const;
void
Control(const ServiceWorkerDescriptor& aServiceWorker);

Просмотреть файл

@ -515,6 +515,28 @@ WorkerGlobalScope::AbstractMainThreadFor(TaskCategory aCategory)
MOZ_CRASH("AbstractMainThreadFor not supported for workers.");
}
Maybe<ClientInfo>
WorkerGlobalScope::GetClientInfo() const
{
Maybe<ClientInfo> info;
info.emplace(mWorkerPrivate->GetClientInfo());
return Move(info);
}
Maybe<ClientState>
WorkerGlobalScope::GetClientState() const
{
Maybe<ClientState> state;
state.emplace(mWorkerPrivate->GetClientState());
return Move(state);
}
Maybe<ServiceWorkerDescriptor>
WorkerGlobalScope::GetController() const
{
return mWorkerPrivate->GetController();
}
DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate,
const nsString& aName)
: WorkerGlobalScope(aWorkerPrivate)

Просмотреть файл

@ -19,7 +19,9 @@ namespace dom {
class AnyCallback;
struct ChannelPixelLayout;
class ClientInfo;
class Clients;
class ClientState;
class Console;
class Crypto;
class Function;
@ -221,6 +223,15 @@ public:
AbstractThread*
AbstractMainThreadFor(TaskCategory aCategory) override;
Maybe<ClientInfo>
GetClientInfo() const override;
Maybe<ClientState>
GetClientState() const;
Maybe<ServiceWorkerDescriptor>
GetController() const override;
};
class DedicatedWorkerGlobalScope final : public WorkerGlobalScope

Просмотреть файл

@ -125,6 +125,7 @@ MOCHITEST_CHROME_MANIFESTS += [
BROWSER_CHROME_MANIFESTS += [
'test/serviceworkers/browser.ini',
'test/serviceworkers/isolated/multi-e10s-update/browser.ini',
]
XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']

Просмотреть файл

@ -4,19 +4,20 @@ support-files =
browser_cached_force_refresh.html
download/window.html
download/worker.js
download_canceled/page_download_canceled.html
download_canceled/server-stream-download.sjs
download_canceled/sw_download_canceled.js
fetch.js
file_multie10s_update.html
file_userContextId_openWindow.js
force_refresh_browser_worker.js
empty.html
empty.js
server_multie10s_update.sjs
utils.js
[browser_devtools_serviceworker_interception.js]
[browser_force_refresh.js]
[browser_download.js]
[browser_multie10s_update.js]
skip-if = !e10s || os != "win" # Bug 1404914
[browser_download_canceled.js]
[browser_storage_permission.js]
[browser_unregister_with_containers.js]
[browser_userContextId_openWindow.js]

Просмотреть файл

@ -0,0 +1,166 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/*
* Test cancellation of a download in order to test edge-cases related to
* channel diversion. Channel diversion occurs in cases of file (and PSM cert)
* downloads where we realize in the child that we really want to consume the
* channel data in the parent. For data "sourced" by the parent, like network
* data, data streaming to the child is suspended and the parent waits for the
* child to send back the data it already received, then the channel is resumed.
* For data generated by the child, such as (the current, to be mooted by
* parent-intercept) child-side intercept, the data (currently) stream is
* continually pumped up to the parent.
*
* In particular, we want to reproduce the circumstances of Bug 1418795 where
* the child-side input-stream pump attempts to send data to the parent process
* but the parent has canceled the channel and so the IPC Actor has been torn
* down. Diversion begins once the nsURILoader receives the OnStartRequest
* notification with the headers, so there are two ways to produce
*/
Cu.import('resource://gre/modules/Services.jsm');
const { Downloads } = Cu.import("resource://gre/modules/Downloads.jsm", {});
/**
* Clear the downloads list so other tests don't see our byproducts.
*/
async function clearDownloads() {
const downloads = await Downloads.getList(Downloads.ALL);
downloads.removeFinished();
}
/**
* Returns a Promise that will be resolved once the download dialog shows up and
* we have clicked the given button.
*
* Derived from browser/components/downloads/test/browser/head.js's
* self-contained promiseAlertDialogOpen helper, but modified to work on the
* download dialog instead of commonDialog.xul.
*/
function promiseClickDownloadDialogButton(buttonAction) {
return new Promise(resolve => {
Services.ww.registerNotification(function onOpen(win, topic, data) {
if (topic === "domwindowopened" && win instanceof Ci.nsIDOMWindow) {
// The test listens for the "load" event which guarantees that the alert
// class has already been added (it is added when "DOMContentLoaded" is
// fired).
win.addEventListener("load", function() {
info(`found window of type: ${win.document.documentURI}`);
if (win.document.documentURI ===
"chrome://mozapps/content/downloads/unknownContentType.xul") {
Services.ww.unregisterNotification(onOpen);
// nsHelperAppDlg.js currently uses an eval-based setTimeout(0) to
// invoke its postShowCallback that results in a misleading error to
// the console if we close the dialog before it gets a chance to
// run. Just a setTimeout is not sufficient because it appears we
// get our "load" listener before the document's, so we use
// executeSoon to defer until after its load handler runs, then
// use setTimeout(0) to end up after its eval.
executeSoon(function() {
setTimeout(function() {
const button = win.document.documentElement.getButton(buttonAction);
button.disabled = false;
info(`clicking ${buttonAction} button`);
button.click();
resolve();
}, 0);
});
}
}, {once: true});
}
});
});
}
async function performCanceledDownload(tab, path) {
// Start waiting for the download dialog before triggering the download.
info("watching for download popup");
const cancelDownload = promiseClickDownloadDialogButton("cancel");
// Trigger the download.
info(`triggering download of "${path}"`);
await ContentTask.spawn(
tab.linkedBrowser,
path,
function(path) {
// Put a Promise in place that we can wait on for stream closure.
content.wrappedJSObject.trackStreamClosure(path);
// Create the link and trigger the download.
const link = content.document.createElement('a');
link.href = path;
link.download = path;
content.document.body.appendChild(link);
link.click();
});
// Wait for the cancelation to have been triggered.
info("waiting for download popup");
await cancelDownload;
ok(true, "canceled download");
// Wait for confirmation that the stream stopped.
info(`wait for the ${path} stream to close.`);
const why = await ContentTask.spawn(
tab.linkedBrowser,
path,
function(path) {
return content.wrappedJSObject.streamClosed[path].promise;
});
is(why.why, "canceled", "Ensure the stream canceled instead of timing out.");
// Note that for the "sw-stream-download" case, we end up with a bogus
// reason of "'close' may only be called on a stream in the 'readable' state."
// Since we aren't actually invoking close(), I'm assuming this is an
// implementation bug that will be corrected in the web platform tests.
info(`Cancellation reason: ${why.message} after ${why.ticks} ticks`);
}
const gTestRoot = getRootDirectory(gTestPath)
.replace("chrome://mochitests/content/", "http://mochi.test:8888/");
const PAGE_URL = `${gTestRoot}download_canceled/page_download_canceled.html`;
add_task(async function interruptedDownloads() {
await SpecialPowers.pushPrefEnv({'set': [
['dom.serviceWorkers.enabled', true],
['dom.serviceWorkers.exemptFromPerDomainMax', true],
['dom.serviceWorkers.testing.enabled', true],
["javascript.options.streams", true],
["dom.streams.enabled", true],
]});
// Open the tab
const tab = await BrowserTestUtils.openNewForegroundTab({
gBrowser,
opening: PAGE_URL
});
// Wait for it to become controlled. Check that it was a promise that
// resolved as expected rather than undefined by checking the return value.
const controlled = await ContentTask.spawn(
tab.linkedBrowser,
null,
function() {
// This is a promise set up by the page during load, and we are post-load.
return content.wrappedJSObject.controlled;
});
is(controlled, "controlled", "page became controlled");
// Download a pass-through fetch stream.
await performCanceledDownload(tab, "sw-passthrough-download");
// Download a SW-generated stream
await performCanceledDownload(tab, "sw-stream-download");
// Cleanup
await ContentTask.spawn(
tab.linkedBrowser,
null,
function() {
return content.wrappedJSObject.registration.unregister();
});
await BrowserTestUtils.removeTab(tab);
await clearDownloads();
});

Просмотреть файл

@ -36,15 +36,21 @@ function test() {
function() {
var url = gTestRoot + 'browser_base_force_refresh.html';
var tab = BrowserTestUtils.addTab(gBrowser);
var tabBrowser = gBrowser.getBrowserForTab(tab);
gBrowser.selectedTab = tab;
tab.linkedBrowser.messageManager.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
gBrowser.loadURI(url);
function done() {
async function done() {
tab.linkedBrowser.messageManager.removeMessageListener("test:event", eventHandler);
gBrowser.removeTab(tab);
await ContentTask.spawn(tabBrowser, null, async function() {
const swr = await content.navigator.serviceWorker.getRegistration();
await swr.unregister();
});
await BrowserTestUtils.removeTab(tab);
executeSoon(finish);
}

Просмотреть файл

@ -1,80 +0,0 @@
"use strict";
const { classes: Cc, interfaces: Ci, results: Cr } = Components;
// Testing if 2 child processes are correctly managed when they both try to do
// an SW update.
const BASE_URI = "http://mochi.test:8888/browser/dom/workers/test/serviceworkers/";
add_task(async function test_update() {
info("Setting the prefs to having multi-e10s enabled");
await SpecialPowers.pushPrefEnv({"set": [
["dom.ipc.processCount", 4],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
]});
let url = BASE_URI + "file_multie10s_update.html";
info("Creating the first tab...");
let tab1 = BrowserTestUtils.addTab(gBrowser, url);
let browser1 = gBrowser.getBrowserForTab(tab1);
await BrowserTestUtils.browserLoaded(browser1);
info("Creating the second tab...");
let tab2 = BrowserTestUtils.addTab(gBrowser, url);
let browser2 = gBrowser.getBrowserForTab(tab2);
await BrowserTestUtils.browserLoaded(browser2);
let sw = BASE_URI + "server_multie10s_update.sjs";
info("Let's start the test...");
let status = await ContentTask.spawn(browser1, sw, function(url) {
// Registration of the SW
return content.navigator.serviceWorker.register(url)
// Activation
.then(function(r) {
return new content.window.Promise(resolve => {
let worker = r.installing;
worker.addEventListener('statechange', () => {
if (worker.state === 'installed') {
resolve(true);
}
});
});
})
// Waiting for the result.
.then(() => {
return new content.window.Promise(resolve => {
let results = [];
let bc = new content.window.BroadcastChannel('result');
bc.onmessage = function(e) {
results.push(e.data);
if (results.length != 2) {
return;
}
resolve(results[0] + results[1]);
}
// Let's inform the tabs.
bc = new content.window.BroadcastChannel('start');
bc.postMessage('go');
});
});
});
if (status == 0) {
ok(false, "both succeeded. This is wrong.");
} else if (status == 1) {
ok(true, "one succeded, one failed. This is good.");
} else {
ok(false, "both failed. This is definitely wrong.");
}
await BrowserTestUtils.removeTab(tab1);
await BrowserTestUtils.removeTab(tab2);
});

Просмотреть файл

@ -0,0 +1,58 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script src="../utils.js"></script>
<script type="text/javascript">
function wait_until_controlled() {
return new Promise(function(resolve) {
if (navigator.serviceWorker.controller) {
return resolve('controlled');
}
navigator.serviceWorker.addEventListener('controllerchange', function onController() {
if (navigator.serviceWorker.controller) {
navigator.serviceWorker.removeEventListener('controllerchange', onController);
return resolve('controlled');
}
});
});
}
addEventListener('load', async function(event) {
window.controlled = wait_until_controlled();
window.registration =
await navigator.serviceWorker.register('sw_download_canceled.js');
let sw = registration.installing || registration.waiting ||
registration.active;
await waitForState(sw, 'activated');
sw.postMessage('claim');
});
// Place to hold promises for stream closures reported by the SW.
window.streamClosed = {};
// The ServiceWorker will postMessage to this BroadcastChannel when the streams
// are closed. (Alternately, the SW could have used the clients API to post at
// us, but the mechanism by which that operates would be different when this
// test is uplifted, and it's desirable to avoid timing changes.)
//
// The browser test will use this promise to wait on stream shutdown.
window.swStreamChannel = new BroadcastChannel("stream-closed");
function trackStreamClosure(path) {
let resolve;
const promise = new Promise(r => { resolve = r });
window.streamClosed[path] = { promise, resolve };
}
window.swStreamChannel.onmessage = ({ data }) => {
window.streamClosed[data.what].resolve(data);
};
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,123 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
// stolen from file_blocked_script.sjs
function setGlobalState(data, key)
{
x = { data: data, QueryInterface: function(iid) { return this } };
x.wrappedJSObject = x;
setObjectState(key, x);
}
function getGlobalState(key)
{
var data;
getObjectState(key, function(x) {
data = x && x.wrappedJSObject.data;
});
return data;
}
/*
* We want to let the sw_download_canceled.js service worker know when the
* stream was canceled. To this end, we let it issue a monitor request which we
* fulfill when the stream has been canceled. In order to coordinate between
* multiple requests, we use the getObjectState/setObjectState mechanism that
* httpd.js exposes to let data be shared and/or persist between requests. We
* handle both possible orderings of the requests because we currently don't
* try and impose an ordering between the two requests as issued by the SW, and
* file_blocked_script.sjs encourages us to do this, but we probably could order
* them.
*/
const MONITOR_KEY = "stream-monitor";
function completeMonitorResponse(response, data) {
response.write(JSON.stringify(data));
response.finish();
}
function handleMonitorRequest(request, response) {
response.setHeader("Content-Type", "application/json");
response.setStatusLine(request.httpVersion, 200, "Found");
response.processAsync();
// Necessary to cause the headers to be flushed; that or touching the
// bodyOutputStream getter.
response.write("");
dump("server-stream-download.js: monitor headers issued\n");
const alreadyCompleted = getGlobalState(MONITOR_KEY);
if (alreadyCompleted) {
completeMonitorResponse(response, alreadyCompleted);
setGlobalState(null, MONITOR_KEY);
} else {
setGlobalState(response, MONITOR_KEY);
}
}
const MAX_TICK_COUNT = 3000;
const TICK_INTERVAL = 2;
function handleStreamRequest(request, response) {
const name = "server-stream-download";
// Create some payload to send.
let strChunk =
'Static routes are the future of ServiceWorkers! So say we all!\n';
while (strChunk.length < 1024) {
strChunk += strChunk;
}
response.setHeader("Content-Disposition", `attachment; filename="${name}"`);
response.setHeader("Content-Type", `application/octet-stream; name="${name}"`);
response.setHeader("Content-Length", `${strChunk.length * MAX_TICK_COUNT}`);
response.setStatusLine(request.httpVersion, 200, "Found");
response.processAsync();
response.write(strChunk);
dump("server-stream-download.js: stream headers + first payload issued\n");
let count = 0;
let intervalId;
function closeStream(why, message) {
dump("server-stream-download.js: closing stream: " + why + "\n");
clearInterval(intervalId);
response.finish();
const data = { why, message };
const monitorResponse = getGlobalState(MONITOR_KEY);
if (monitorResponse) {
completeMonitorResponse(monitorResponse, data);
setGlobalState(null, MONITOR_KEY);
} else {
setGlobalState(data, MONITOR_KEY);
}
}
function tick() {
try {
// bound worst-case behavior.
if (count++ > MAX_TICK_COUNT) {
closeStream("timeout", "timeout");
return;
}
response.write(strChunk);
} catch(e) {
closeStream("canceled", e.message);
}
}
intervalId = setInterval(tick, TICK_INTERVAL);
}
Components.utils.importGlobalProperties(["URLSearchParams"]);
function handleRequest(request, response) {
dump("server-stream-download.js: processing request for " + request.path +
"?" + request.queryString + "\n");
const query = new URLSearchParams(request.queryString);
if (query.has("monitor")) {
handleMonitorRequest(request, response);
} else {
handleStreamRequest(request, response);
}
}

Просмотреть файл

@ -0,0 +1,136 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// This file is derived from :bkelly's https://glitch.com/edit/#!/html-sw-stream
addEventListener("install", evt => {
evt.waitUntil(self.skipWaiting());
});
// Create a BroadcastChannel to notify when we have closed our streams.
const channel = new BroadcastChannel("stream-closed");
const MAX_TICK_COUNT = 3000;
const TICK_INTERVAL = 4;
/**
* Generate a continuous stream of data at a sufficiently high frequency that a
* there"s a good chance of racing channel cancellation.
*/
function handleStream(evt, filename) {
// Create some payload to send.
const encoder = new TextEncoder();
let strChunk =
"Static routes are the future of ServiceWorkers! So say we all!\n";
while (strChunk.length < 1024) {
strChunk += strChunk;
}
const dataChunk = encoder.encode(strChunk);
evt.waitUntil(new Promise(resolve => {
let body = new ReadableStream({
start: controller => {
const closeStream = (why) => {
console.log("closing stream: " + JSON.stringify(why) + "\n");
clearInterval(intervalId);
resolve();
// In event of error, the controller will automatically have closed.
if (why.why != "canceled") {
try {
controller.close();
} catch(ex) {
// If we thought we should cancel but experienced a problem,
// that's a different kind of failure and we need to report it.
// (If we didn't catch the exception here, we'd end up erroneously
// in the tick() method's canceled handler.)
channel.postMessage({
what: filename,
why: "close-failure",
message: ex.message,
ticks: why.ticks
});
return;
}
}
// Post prior to performing any attempt to close...
channel.postMessage(why);
};
controller.enqueue(dataChunk);
let count = 0;
let intervalId;
function tick() {
try {
// bound worst-case behavior.
if (count++ > MAX_TICK_COUNT) {
closeStream({
what: filename, why: "timeout", message: "timeout", ticks: count
});
return;
}
controller.enqueue(dataChunk);
} catch(e) {
closeStream({
what: filename, why: "canceled", message: e.message, ticks: count
});
}
}
// Alternately, streams' pull mechanism could be used here, but this
// test doesn't so much want to saturate the stream as to make sure the
// data is at least flowing a little bit. (Also, the author had some
// concern about slowing down the test by overwhelming the event loop
// and concern that we might not have sufficent back-pressure plumbed
// through and an infinite pipe might make bad things happen.)
intervalId = setInterval(tick, TICK_INTERVAL);
tick();
},
});
evt.respondWith(new Response(body, {
headers: {
"Content-Disposition": `attachment; filename="${filename}"`,
"Content-Type": "application/octet-stream"
}
}));
}));
}
/**
* Use an .sjs to generate a similar stream of data to the above, passing the
* response through directly. Because we're handing off the response but also
* want to be able to report when cancellation occurs, we create a second,
* overlapping long-poll style fetch that will not finish resolving until the
* .sjs experiences closure of its socket and terminates the payload stream.
*/
function handlePassThrough(evt, filename) {
evt.waitUntil((async () => {
console.log("issuing monitor fetch request");
const response = await fetch("server-stream-download.sjs?monitor");
console.log("monitor headers received, awaiting body");
const data = await response.json();
console.log("passthrough monitor fetch completed, notifying.");
channel.postMessage({
what: filename,
why: data.why,
message: data.message
});
})());
evt.respondWith(fetch("server-stream-download.sjs").then(response => {
console.log("server-stream-download.sjs Response received, propagating");
return response;
}));
}
addEventListener("fetch", evt => {
console.log(`SW processing fetch of ${evt.request.url}`);
if (evt.request.url.indexOf("sw-stream-download") >= 0) {
return handleStream(evt, "sw-stream-download");
}
if (evt.request.url.indexOf("sw-passthrough-download") >= 0) {
return handlePassThrough(evt, "sw-passthrough-download");
}
})
addEventListener("message", evt => {
if (evt.data === "claim") {
evt.waitUntil(clients.claim());
}
});

Просмотреть файл

@ -0,0 +1,19 @@
This directory contains tests that are flaky when run with other tests
but that we don't want to disable and where it's not trivial to make
the tests not flaky at this time, but we have a plan to fix them via
systemic fixes that are improving the codebase rather than hacking a
test until it works.
This directory and ugly hack structure needs to exist because of
multi-e10s propagation races that will go away when we finish
implementing the multi-e10s overhaul for ServiceWorkers. Most
specifically, unregister() calls need to propagate across all
content processes. There are fixes on bug 1318142, but they're
ugly and complicate things.
Specific test notes and rationalizations:
- multi-e10s-update: This test relies on there being no registrations
existing at its start. The preceding test that induces the breakage
(`browser_force_refresh.js`) was made to clean itself up, but the
unregister() race issue is not easily/cleanly hacked around and this
test will itself become moot when the multi-e10s changes land.

Просмотреть файл

@ -0,0 +1,8 @@
[DEFAULT]
support-files =
file_multie10s_update.html
server_multie10s_update.sjs
[browser_multie10s_update.js]
skip-if = true # bug 1429794 is to re-enable, then un-comment the below.
#skip-if = !e10s # this is an e10s-only test.

Просмотреть файл

@ -0,0 +1,135 @@
"use strict";
const { classes: Cc, interfaces: Ci, results: Cr } = Components;
// Testing if 2 child processes are correctly managed when they both try to do
// an SW update.
const BASE_URI =
"http://mochi.test:8888/browser/dom/workers/test/serviceworkers/isolated/multi-e10s-update/";
add_task(async function test_update() {
info("Setting the prefs to having multi-e10s enabled");
await SpecialPowers.pushPrefEnv({"set": [
["dom.ipc.processCount", 4],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
]});
let url = BASE_URI + "file_multie10s_update.html";
info("Creating the first tab...");
let tab1 = BrowserTestUtils.addTab(gBrowser, url);
let browser1 = gBrowser.getBrowserForTab(tab1);
await BrowserTestUtils.browserLoaded(browser1);
info("Creating the second tab...");
let tab2 = BrowserTestUtils.addTab(gBrowser, url);
let browser2 = gBrowser.getBrowserForTab(tab2);
await BrowserTestUtils.browserLoaded(browser2);
let sw = BASE_URI + "server_multie10s_update.sjs";
info("Let's make sure there are no existing registrations...");
let existingCount = await ContentTask.spawn(browser1, null, async function() {
const regs = await content.navigator.serviceWorker.getRegistrations();
return regs.length;
});
is(existingCount, 0, "Previous tests should have cleaned up!");
info("Let's start the test...");
let status = await ContentTask.spawn(browser1, sw, function(url) {
// Let the SW be served immediately once by triggering a relase immediately.
// We don't need to await this. We do this from a frame script because
// it has fetch.
content.fetch(url + "?release");
// Registration of the SW
return content.navigator.serviceWorker.register(url)
// Activation
.then(function(r) {
content.registration = r;
return new content.window.Promise(resolve => {
let worker = r.installing;
worker.addEventListener('statechange', () => {
if (worker.state === 'installed') {
resolve(true);
}
});
});
})
// Waiting for the result.
.then(() => {
return new content.window.Promise(resolveResults => {
// Once both updates have been issued and a single update has failed, we
// can tell the .sjs to release a single copy of the SW script.
let updateCount = 0;
const uc = new content.window.BroadcastChannel('update');
// This promise tracks the updates tally.
const updatesIssued = new Promise(resolveUpdatesIssued => {
uc.onmessage = function(e) {
updateCount++;
console.log("got update() number", updateCount);
if (updateCount === 2) {
resolveUpdatesIssued();
}
};
});
let results = [];
const rc = new content.window.BroadcastChannel('result');
// This promise resolves when an update has failed.
const oneFailed = new Promise(resolveOneFailed => {
rc.onmessage = function(e) {
console.log("got result", e.data);
results.push(e.data);
if (e.data === 1) {
resolveOneFailed();
}
if (results.length != 2) {
return;
}
resolveResults(results[0] + results[1]);
}
});
Promise.all([updatesIssued, oneFailed]).then(() => {
console.log("releasing update");
content.fetch(url + "?release").catch((ex) => {
console.error("problem releasing:", ex);
});
});
// Let's inform the tabs.
const sc = new content.window.BroadcastChannel('start');
sc.postMessage('go');
});
});
});
if (status == 0) {
ok(false, "both succeeded. This is wrong.");
} else if (status == 1) {
ok(true, "one succeded, one failed. This is good.");
} else {
ok(false, "both failed. This is definitely wrong.");
}
// let's clean up the registration and get the fetch count. The count
// should be 1 for the initial fetch and 1 for the update.
const count = await ContentTask.spawn(browser1, sw, async function(url) {
// We stored the registration on the frame script's wrapper, hence directly
// accesss content without using wrappedJSObject.
await content.registration.unregister();
const { count } =
await content.fetch(url + "?get-and-clear-count").then(r => r.json());
return count;
});
is(count, 2, "SW should have been fetched only twice");
await BrowserTestUtils.removeTab(tab1);
await BrowserTestUtils.removeTab(tab2);
});

Просмотреть файл

@ -24,6 +24,12 @@ bc.onmessage = function(e) {
}, () => {
bc.postMessage(1);
});
// Tell the coordinating frame script that we've kicked off our update
// call so that the SW script can be released once both instances of us
// have triggered update() and 1 has failed.
const blockingChannel = new BroadcastChannel('update');
blockingChannel.postMessage(true);
});
}

Просмотреть файл

@ -0,0 +1,97 @@
// stolen from file_blocked_script.sjs
function setGlobalState(data, key)
{
x = { data: data, QueryInterface: function(iid) { return this } };
x.wrappedJSObject = x;
setObjectState(key, x);
}
function getGlobalState(key)
{
var data;
getObjectState(key, function(x) {
data = x && x.wrappedJSObject.data;
});
return data;
}
function completeBlockingRequest(response)
{
response.write("42");
response.finish();
}
// This stores the response that's currently blocking, or true if the release
// got here before the blocking request.
const BLOCKING_KEY = "multie10s-update-release";
// This tracks the number of blocking requests we received up to this point in
// time. This value will be cleared when fetched. It's on the caller to make
// sure that all the blocking requests that might occurr have already occurred.
const COUNT_KEY = "multie10s-update-count";
/**
* Serve a request that will only be completed when the ?release variant of this
* .sjs is fetched. This allows us to avoid using a timer, which slows down the
* tests and is brittle under slow hardware.
*/
function handleBlockingRequest(request, response)
{
response.processAsync();
response.setHeader("Content-Type", "application/javascript", false);
const existingCount = getGlobalState(COUNT_KEY) || 0;
setGlobalState(existingCount + 1, COUNT_KEY);
const alreadyReleased = getGlobalState(BLOCKING_KEY);
if (alreadyReleased === true) {
completeBlockingRequest(response);
setGlobalState(null, BLOCKING_KEY);
} else if (alreadyReleased) {
// If we've got another response stacked up, this means something is wrong
// with the test. The count mechanism will detect this, so just let this
// one through so we fail fast rather than hanging.
dump("we got multiple blocking requests stacked up!!\n");
completeBlockingRequest(response);
} else {
setGlobalState(response, BLOCKING_KEY);
}
}
function handleReleaseRequest(request, response)
{
const blockingResponse = getGlobalState(BLOCKING_KEY);
if (blockingResponse) {
completeBlockingRequest(blockingResponse);
setGlobalState(null, BLOCKING_KEY);
} else {
setGlobalState(true, BLOCKING_KEY);
}
response.setHeader("Content-Type", "application/json", false);
response.write(JSON.stringify({ released: true }));
}
function handleCountRequest(request, response)
{
const count = getGlobalState(COUNT_KEY) || 0;
// --verify requires that we clear this so the test can be re-run.
setGlobalState(0, COUNT_KEY);
response.setHeader("Content-Type", "application/json", false);
response.write(JSON.stringify({ count: count }));
}
Components.utils.importGlobalProperties(["URLSearchParams"]);
function handleRequest(request, response) {
dump("server_multie10s_update.sjs: processing request for " + request.path +
"?" + request.queryString + "\n");
const query = new URLSearchParams(request.queryString);
if (query.has("release")) {
handleReleaseRequest(request, response);
} else if (query.has("get-and-clear-count")) {
handleCountRequest(request, response);
} else {
handleBlockingRequest(request, response);
}
}

Просмотреть файл

@ -1,12 +0,0 @@
function handleRequest(request, response)
{
response.processAsync();
response.setHeader("Content-Type", "application/javascript", false);
timer = Components.classes["@mozilla.org/timer;1"].
createInstance(Components.interfaces.nsITimer);
timer.init(function() {
response.write("42");
response.finish();
}, 3000, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
}

Просмотреть файл

@ -194,7 +194,7 @@ for (let type of ['f32', 'f64']) {
(func (export "") (call $foo))
)`,
WebAssembly.RuntimeError,
["", ">", "1,>", "0,1,>", "interstitial,0,1,>", "trap handling,0,1,>", "", ">", ""]);
["", ">", "1,>", "0,1,>", "1,>", "", ">", ""]);
testError(
`(module

Просмотреть файл

@ -12350,7 +12350,7 @@ CodeGenerator::visitWasmTrap(LWasmTrap* lir)
MOZ_ASSERT(gen->compilingWasm());
const MWasmTrap* mir = lir->mir();
masm.jump(oldTrap(mir, mir->trap()));
masm.wasmTrap(mir->trap(), mir->bytecodeOffset());
}
void

Просмотреть файл

@ -2930,6 +2930,12 @@ MacroAssembler::maybeBranchTestType(MIRType type, MDefinition* maybeDef, Registe
}
}
void
MacroAssembler::wasmTrap(wasm::Trap trap, wasm::BytecodeOffset bytecodeOffset)
{
append(trap, wasm::TrapSite(illegalInstruction().offset(), bytecodeOffset));
}
void
MacroAssembler::wasmCallImport(const wasm::CallSiteDesc& desc, const wasm::CalleeDesc& callee)
{

Просмотреть файл

@ -1399,6 +1399,9 @@ class MacroAssembler : public MacroAssemblerSpecific
// ========================================================================
// wasm support
CodeOffset illegalInstruction() PER_SHARED_ARCH;
void wasmTrap(wasm::Trap trap, wasm::BytecodeOffset bytecodeOffset);
// Emit a bounds check against the wasm heap limit, jumping to 'label' if 'cond' holds.
// Required when WASM_HUGE_MEMORY is not defined.
template <class L>

Просмотреть файл

@ -3005,6 +3005,14 @@ Assembler::as_bkpt()
hit++;
}
BufferOffset
Assembler::as_illegal_trap()
{
// Encoding of the permanently-undefined 'udf' instruction, with the imm16
// set to 0.
return writeInst(0xe7f000f0);
}
void
Assembler::flushBuffer()
{

Просмотреть файл

@ -1733,6 +1733,7 @@ class Assembler : public AssemblerShared
static void Bind(uint8_t* rawCode, CodeOffset label, CodeOffset target);
void as_bkpt();
BufferOffset as_illegal_trap();
public:
static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);

Просмотреть файл

@ -4911,6 +4911,12 @@ template void
MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
const BaseIndex& dest, MIRType slotType);
CodeOffset
MacroAssembler::illegalInstruction()
{
return CodeOffset(as_illegal_trap().getOffset());
}
void
MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry)
{

Просмотреть файл

@ -260,6 +260,11 @@ class SimInstruction {
return typeValue() == 7 && bit(24) == 1 && svcValue() >= kStopCode;
}
// Test for a udf instruction, which falls under type 3.
inline bool isUDF() const {
return (instructionBits() & 0xfff000f0) == 0xe7f000f0;
}
// Special accessors that test for existence of a value.
inline bool hasS() const { return sValue() == 1; }
inline bool hasB() const { return bValue() == 1; }
@ -1583,6 +1588,16 @@ Simulator::handleWasmInterrupt()
set_pc(int32_t(cs->interruptCode()));
}
static inline JitActivation*
GetJitActivation(JSContext* cx)
{
if (!wasm::CodeExists)
return nullptr;
if (!cx->activation() || !cx->activation()->isJit())
return nullptr;
return cx->activation()->asJit();
}
// WebAssembly memories contain an extra region of guard pages (see
// WasmArrayRawBuffer comment). The guard pages catch out-of-bounds accesses
// using a signal handler that redirects PC to a stub that safely reports an
@ -1590,13 +1605,11 @@ Simulator::handleWasmInterrupt()
// and cannot be redirected. Therefore, we must avoid hitting the handler by
// redirecting in the simulator before the real handler would have been hit.
bool
Simulator::handleWasmFault(int32_t addr, unsigned numBytes)
Simulator::handleWasmSegFault(int32_t addr, unsigned numBytes)
{
if (!wasm::CodeExists)
JitActivation* act = GetJitActivation(cx_);
if (!act)
return false;
if (!cx_->activation() || !cx_->activation()->isJit())
return false;
JitActivation* act = cx_->activation()->asJit();
void* pc = reinterpret_cast<void*>(get_pc());
uint8_t* fp = reinterpret_cast<uint8_t*>(get_register(r11));
@ -1623,10 +1636,34 @@ Simulator::handleWasmFault(int32_t addr, unsigned numBytes)
return true;
}
bool
Simulator::handleWasmIllFault()
{
JitActivation* act = GetJitActivation(cx_);
if (!act)
return false;
void* pc = reinterpret_cast<void*>(get_pc());
uint8_t* fp = reinterpret_cast<uint8_t*>(get_register(r11));
const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc);
if (!segment)
return false;
wasm::Trap trap;
wasm::BytecodeOffset bytecode;
if (!segment->code().lookupTrap(pc, &trap, &bytecode))
return false;
act->startWasmTrap(trap, bytecode.offset, pc, fp);
set_pc(int32_t(segment->trapCode()));
return true;
}
uint64_t
Simulator::readQ(int32_t addr, SimInstruction* instr, UnalignedPolicy f)
{
if (handleWasmFault(addr, 8))
if (handleWasmSegFault(addr, 8))
return UINT64_MAX;
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
@ -1649,7 +1686,7 @@ Simulator::readQ(int32_t addr, SimInstruction* instr, UnalignedPolicy f)
void
Simulator::writeQ(int32_t addr, uint64_t value, SimInstruction* instr, UnalignedPolicy f)
{
if (handleWasmFault(addr, 8))
if (handleWasmSegFault(addr, 8))
return;
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
@ -1672,7 +1709,7 @@ Simulator::writeQ(int32_t addr, uint64_t value, SimInstruction* instr, Unaligned
int
Simulator::readW(int32_t addr, SimInstruction* instr, UnalignedPolicy f)
{
if (handleWasmFault(addr, 4))
if (handleWasmSegFault(addr, 4))
return -1;
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
@ -1698,7 +1735,7 @@ Simulator::readW(int32_t addr, SimInstruction* instr, UnalignedPolicy f)
void
Simulator::writeW(int32_t addr, int value, SimInstruction* instr, UnalignedPolicy f)
{
if (handleWasmFault(addr, 4))
if (handleWasmSegFault(addr, 4))
return;
if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
@ -1743,7 +1780,7 @@ Simulator::readExW(int32_t addr, SimInstruction* instr)
if (addr & 3)
MOZ_CRASH("Unaligned exclusive read");
if (handleWasmFault(addr, 4))
if (handleWasmSegFault(addr, 4))
return -1;
SharedMem<int32_t*> ptr = SharedMem<int32_t*>::shared(reinterpret_cast<int32_t*>(addr));
@ -1758,7 +1795,7 @@ Simulator::writeExW(int32_t addr, int value, SimInstruction* instr)
if (addr & 3)
MOZ_CRASH("Unaligned exclusive write");
if (handleWasmFault(addr, 4))
if (handleWasmSegFault(addr, 4))
return -1;
SharedMem<int32_t*> ptr = SharedMem<int32_t*>::shared(reinterpret_cast<int32_t*>(addr));
@ -1773,7 +1810,7 @@ Simulator::writeExW(int32_t addr, int value, SimInstruction* instr)
uint16_t
Simulator::readHU(int32_t addr, SimInstruction* instr)
{
if (handleWasmFault(addr, 2))
if (handleWasmSegFault(addr, 2))
return UINT16_MAX;
// The regexp engine emits unaligned loads, so we don't check for them here
@ -1799,7 +1836,7 @@ Simulator::readHU(int32_t addr, SimInstruction* instr)
int16_t
Simulator::readH(int32_t addr, SimInstruction* instr)
{
if (handleWasmFault(addr, 2))
if (handleWasmSegFault(addr, 2))
return -1;
if ((addr & 1) == 0 || !HasAlignmentFault()) {
@ -1823,7 +1860,7 @@ Simulator::readH(int32_t addr, SimInstruction* instr)
void
Simulator::writeH(int32_t addr, uint16_t value, SimInstruction* instr)
{
if (handleWasmFault(addr, 2))
if (handleWasmSegFault(addr, 2))
return;
if ((addr & 1) == 0 || !HasAlignmentFault()) {
@ -1846,7 +1883,7 @@ Simulator::writeH(int32_t addr, uint16_t value, SimInstruction* instr)
void
Simulator::writeH(int32_t addr, int16_t value, SimInstruction* instr)
{
if (handleWasmFault(addr, 2))
if (handleWasmSegFault(addr, 2))
return;
if ((addr & 1) == 0 || !HasAlignmentFault()) {
@ -1872,7 +1909,7 @@ Simulator::readExHU(int32_t addr, SimInstruction* instr)
if (addr & 1)
MOZ_CRASH("Unaligned exclusive read");
if (handleWasmFault(addr, 2))
if (handleWasmSegFault(addr, 2))
return UINT16_MAX;
SharedMem<uint16_t*> ptr = SharedMem<uint16_t*>::shared(reinterpret_cast<uint16_t*>(addr));
@ -1887,7 +1924,7 @@ Simulator::writeExH(int32_t addr, uint16_t value, SimInstruction* instr)
if (addr & 1)
MOZ_CRASH("Unaligned exclusive write");
if (handleWasmFault(addr, 2))
if (handleWasmSegFault(addr, 2))
return -1;
SharedMem<uint16_t*> ptr = SharedMem<uint16_t*>::shared(reinterpret_cast<uint16_t*>(addr));
@ -1902,7 +1939,7 @@ Simulator::writeExH(int32_t addr, uint16_t value, SimInstruction* instr)
uint8_t
Simulator::readBU(int32_t addr)
{
if (handleWasmFault(addr, 1))
if (handleWasmSegFault(addr, 1))
return UINT8_MAX;
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
@ -1912,7 +1949,7 @@ Simulator::readBU(int32_t addr)
uint8_t
Simulator::readExBU(int32_t addr)
{
if (handleWasmFault(addr, 1))
if (handleWasmSegFault(addr, 1))
return UINT8_MAX;
SharedMem<uint8_t*> ptr = SharedMem<uint8_t*>::shared(reinterpret_cast<uint8_t*>(addr));
@ -1924,7 +1961,7 @@ Simulator::readExBU(int32_t addr)
int32_t
Simulator::writeExB(int32_t addr, uint8_t value)
{
if (handleWasmFault(addr, 1))
if (handleWasmSegFault(addr, 1))
return -1;
SharedMem<uint8_t*> ptr = SharedMem<uint8_t*>::shared(reinterpret_cast<uint8_t*>(addr));
@ -1939,7 +1976,7 @@ Simulator::writeExB(int32_t addr, uint8_t value)
int8_t
Simulator::readB(int32_t addr)
{
if (handleWasmFault(addr, 1))
if (handleWasmSegFault(addr, 1))
return -1;
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
@ -1949,7 +1986,7 @@ Simulator::readB(int32_t addr)
void
Simulator::writeB(int32_t addr, uint8_t value)
{
if (handleWasmFault(addr, 1))
if (handleWasmSegFault(addr, 1))
return;
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
@ -1959,7 +1996,7 @@ Simulator::writeB(int32_t addr, uint8_t value)
void
Simulator::writeB(int32_t addr, int8_t value)
{
if (handleWasmFault(addr, 1))
if (handleWasmSegFault(addr, 1))
return;
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
@ -1969,7 +2006,7 @@ Simulator::writeB(int32_t addr, int8_t value)
int32_t*
Simulator::readDW(int32_t addr)
{
if (handleWasmFault(addr, 8))
if (handleWasmSegFault(addr, 8))
return nullptr;
if ((addr & 3) == 0) {
@ -1984,7 +2021,7 @@ Simulator::readDW(int32_t addr)
void
Simulator::writeDW(int32_t addr, int32_t value1, int32_t value2)
{
if (handleWasmFault(addr, 8))
if (handleWasmSegFault(addr, 8))
return;
if ((addr & 3) == 0) {
@ -2004,7 +2041,7 @@ Simulator::readExDW(int32_t addr, int32_t* hibits)
if (addr & 3)
MOZ_CRASH("Unaligned exclusive read");
if (handleWasmFault(addr, 8))
if (handleWasmSegFault(addr, 8))
return -1;
SharedMem<uint64_t*> ptr = SharedMem<uint64_t*>::shared(reinterpret_cast<uint64_t*>(addr));
@ -2025,7 +2062,7 @@ Simulator::writeExDW(int32_t addr, int32_t value1, int32_t value2)
if (addr & 3)
MOZ_CRASH("Unaligned exclusive write");
if (handleWasmFault(addr, 8))
if (handleWasmSegFault(addr, 8))
return -1;
SharedMem<uint64_t*> ptr = SharedMem<uint64_t*>::shared(reinterpret_cast<uint64_t*>(addr));
@ -3551,6 +3588,12 @@ rotateBytes(uint32_t val, int32_t rotate)
void
Simulator::decodeType3(SimInstruction* instr)
{
if (MOZ_UNLIKELY(instr->isUDF())) {
if (handleWasmIllFault())
return;
MOZ_CRASH("illegal instruction encountered");
}
int rd = instr->rdValue();
int rn = instr->rnValue();
int32_t rn_val = get_register(rn);

Просмотреть файл

@ -296,7 +296,8 @@ class Simulator
void startWasmInterrupt(JitActivation* act);
// Handle any wasm faults, returning true if the fault was handled.
bool handleWasmFault(int32_t addr, unsigned numBytes);
bool handleWasmSegFault(int32_t addr, unsigned numBytes);
bool handleWasmIllFault();
// Read and write memory.
inline uint8_t readBU(int32_t addr);

Просмотреть файл

@ -852,6 +852,12 @@ MacroAssembler::comment(const char* msg)
// ========================================================================
// wasm support
CodeOffset
MacroAssembler::illegalInstruction()
{
MOZ_CRASH("NYI");
}
void
MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry)
{

Просмотреть файл

@ -1863,6 +1863,14 @@ MacroAssembler::comment(const char* msg)
Assembler::comment(msg);
}
// ===============================================================
// WebAssembly
CodeOffset
MacroAssembler::illegalInstruction()
{
MOZ_CRASH("NYI");
}
void
MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry)

Просмотреть файл

@ -871,6 +871,7 @@ class AssemblerShared
{
wasm::CallSiteVector callSites_;
wasm::CallSiteTargetVector callSiteTargets_;
wasm::TrapSiteVectorArray trapSites_;
wasm::OldTrapSiteVector oldTrapSites_;
wasm::OldTrapFarJumpVector oldTrapFarJumps_;
wasm::CallFarJumpVector callFarJumps_;
@ -930,6 +931,9 @@ class AssemblerShared
enoughMemory_ &= callSites_.emplaceBack(desc, retAddr.offset());
enoughMemory_ &= callSiteTargets_.emplaceBack(mozilla::Forward<Args>(args)...);
}
void append(wasm::Trap trap, wasm::TrapSite site) {
enoughMemory_ &= trapSites_[trap].append(site);
}
void append(wasm::OldTrapSite trapSite) {
enoughMemory_ &= oldTrapSites_.append(trapSite);
}
@ -966,6 +970,7 @@ class AssemblerShared
wasm::CallSiteVector& callSites() { return callSites_; }
wasm::CallSiteTargetVector& callSiteTargets() { return callSiteTargets_; }
wasm::TrapSiteVectorArray& trapSites() { return trapSites_; }
wasm::OldTrapSiteVector& oldTrapSites() { return oldTrapSites_; }
wasm::OldTrapFarJumpVector& oldTrapFarJumps() { return oldTrapFarJumps_; }
wasm::CallFarJumpVector& callFarJumps() { return callFarJumps_; }

Просмотреть файл

@ -1101,6 +1101,11 @@ class AssemblerX86Shared : public AssemblerShared
void breakpoint() {
masm.int3();
}
CodeOffset ud2() {
CodeOffset off(masm.currentOffset());
masm.ud2();
return off;
}
static bool HasSSE2() { return CPUInfo::IsSSE2Present(); }
static bool HasSSE3() { return CPUInfo::IsSSE3Present(); }

Просмотреть файл

@ -668,7 +668,14 @@ MacroAssembler::pushFakeReturnAddress(Register scratch)
return retAddr;
}
// wasm specific methods, used in both the wasm baseline compiler and ion.
// ===============================================================
// WebAssembly
CodeOffset
MacroAssembler::illegalInstruction()
{
return ud2();
}
// RAII class that generates the jumps to traps when it's destructed, to
// prevent some code duplication in the outOfLineWasmTruncateXtoY methods.

Просмотреть файл

@ -20,9 +20,6 @@
#include "vm/ErrorReporting.h"
#include "vm/MallocProvider.h"
#include "vm/Runtime.h"
#ifdef XP_DARWIN
# include "wasm/WasmSignalHandlers.h"
#endif
#ifdef _MSC_VER
#pragma warning(push)

Просмотреть файл

@ -193,11 +193,13 @@ ArgumentsGetterImpl(JSContext* cx, const CallArgs& args)
if (!argsobj)
return false;
#ifndef JS_CODEGEN_NONE
// Disabling compiling of this script in IonMonkey. IonMonkey doesn't
// guarantee |f.arguments| can be fully recovered, so we try to mitigate
// observing this behavior by detecting its use early.
JSScript* script = iter.script();
jit::ForbidCompilation(cx, script);
#endif
args.rval().setObject(*argsobj);
return true;

Просмотреть файл

@ -11,6 +11,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/DoublyLinkedList.h"
#include "mozilla/LinkedList.h"
#include "mozilla/MaybeOneOf.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/PodOperations.h"
#include "mozilla/Scoped.h"
@ -49,6 +50,7 @@
#include "vm/Stack.h"
#include "vm/Stopwatch.h"
#include "vm/Symbol.h"
#include "wasm/WasmSignalHandlers.h"
#ifdef _MSC_VER
#pragma warning(push)
@ -1049,34 +1051,17 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
public:
js::RuntimeCaches& caches() { return caches_.ref(); }
private:
// When wasm is interrupted, the pc at which we should return if the
// interrupt hasn't stopped execution of the current running code. Since
// this is used only by the interrupt handler and the latter is not
// reentrant, this value can't be clobbered so there is at most one
// resume PC at a time.
js::ActiveThreadData<void*> wasmResumePC_;
// When wasm traps or is interrupted, the signal handler records some data
// for unwinding purposes. Wasm code can't interrupt or trap reentrantly.
js::ActiveThreadData<
mozilla::MaybeOneOf<js::wasm::TrapData, js::wasm::InterruptData>
> wasmUnwindData;
// To ensure a consistent state of fp/pc, the unwound pc might be
// different from the resumePC, especially at call boundaries.
js::ActiveThreadData<void*> wasmUnwindPC_;
public:
void startWasmInterrupt(void* resumePC, void* unwindPC) {
MOZ_ASSERT(resumePC && unwindPC);
wasmResumePC_ = resumePC;
wasmUnwindPC_ = unwindPC;
js::wasm::TrapData& wasmTrapData() {
return wasmUnwindData.ref().ref<js::wasm::TrapData>();
}
void finishWasmInterrupt() {
MOZ_ASSERT(wasmResumePC_ && wasmUnwindPC_);
wasmResumePC_ = nullptr;
wasmUnwindPC_ = nullptr;
}
void* wasmResumePC() const {
return wasmResumePC_;
}
void* wasmUnwindPC() const {
return wasmUnwindPC_;
js::wasm::InterruptData& wasmInterruptData() {
return wasmUnwindData.ref().ref<js::wasm::InterruptData>();
}
public:

Просмотреть файл

@ -1552,6 +1552,7 @@ jit::JitActivation::~JitActivation()
MOZ_ASSERT(!bailoutData_);
MOZ_ASSERT(!isWasmInterrupted());
MOZ_ASSERT(!isWasmTrapping());
clearRematerializedFrames();
js_delete(rematerializedFrames_);
@ -1725,9 +1726,8 @@ jit::JitActivation::startWasmInterrupt(const JS::ProfilingFrameIterator::Registe
MOZ_ASSERT(state.fp);
// Execution can only be interrupted in function code. Afterwards, control
// flow does not reenter function code and thus there should be no
// flow does not reenter function code and thus there can be no
// interrupt-during-interrupt.
MOZ_ASSERT(!isWasmInterrupted());
bool ignoredUnwound;
wasm::UnwindState unwindState;
@ -1736,7 +1736,7 @@ jit::JitActivation::startWasmInterrupt(const JS::ProfilingFrameIterator::Registe
void* pc = unwindState.pc;
MOZ_ASSERT(wasm::LookupCode(pc)->lookupRange(pc)->isFunction());
cx_->runtime()->startWasmInterrupt(state.pc, pc);
cx_->runtime()->wasmUnwindData.ref().construct<wasm::InterruptData>(pc, state.pc);
setWasmExitFP(unwindState.fp);
MOZ_ASSERT(compartment() == unwindState.fp->tls->instance->compartment());
@ -1746,18 +1746,17 @@ jit::JitActivation::startWasmInterrupt(const JS::ProfilingFrameIterator::Registe
void
jit::JitActivation::finishWasmInterrupt()
{
MOZ_ASSERT(hasWasmExitFP());
MOZ_ASSERT(isWasmInterrupted());
cx_->runtime()->finishWasmInterrupt();
cx_->runtime()->wasmUnwindData.ref().destroy();
packedExitFP_ = nullptr;
}
bool
jit::JitActivation::isWasmInterrupted() const
{
void* pc = cx_->runtime()->wasmUnwindPC();
if (!pc)
JSRuntime* rt = cx_->runtime();
if (!rt->wasmUnwindData.ref().constructed<wasm::InterruptData>())
return false;
Activation* act = cx_->activation();
@ -1768,24 +1767,76 @@ jit::JitActivation::isWasmInterrupted() const
return false;
DebugOnly<const wasm::Frame*> fp = wasmExitFP();
MOZ_ASSERT(fp && fp->instance()->code().containsCodePC(pc));
DebugOnly<void*> unwindPC = rt->wasmInterruptData().unwindPC;
MOZ_ASSERT(fp->instance()->code().containsCodePC(unwindPC));
return true;
}
void*
jit::JitActivation::wasmUnwindPC() const
jit::JitActivation::wasmInterruptUnwindPC() const
{
MOZ_ASSERT(hasWasmExitFP());
MOZ_ASSERT(isWasmInterrupted());
return cx_->runtime()->wasmUnwindPC();
return cx_->runtime()->wasmInterruptData().unwindPC;
}
void*
jit::JitActivation::wasmResumePC() const
jit::JitActivation::wasmInterruptResumePC() const
{
MOZ_ASSERT(hasWasmExitFP());
MOZ_ASSERT(isWasmInterrupted());
return cx_->runtime()->wasmResumePC();
return cx_->runtime()->wasmInterruptData().resumePC;
}
void
jit::JitActivation::startWasmTrap(wasm::Trap trap, uint32_t bytecodeOffset, void* pc, void* fp)
{
MOZ_ASSERT(pc);
MOZ_ASSERT(fp);
cx_->runtime()->wasmUnwindData.ref().construct<wasm::TrapData>(pc, trap, bytecodeOffset);
setWasmExitFP((wasm::Frame*)fp);
}
void
jit::JitActivation::finishWasmTrap()
{
MOZ_ASSERT(isWasmTrapping());
cx_->runtime()->wasmUnwindData.ref().destroy();
packedExitFP_ = nullptr;
}
bool
jit::JitActivation::isWasmTrapping() const
{
JSRuntime* rt = cx_->runtime();
if (!rt->wasmUnwindData.ref().constructed<wasm::TrapData>())
return false;
Activation* act = cx_->activation();
while (act && !act->hasWasmExitFP())
act = act->prev();
if (act != this)
return false;
DebugOnly<const wasm::Frame*> fp = wasmExitFP();
DebugOnly<void*> unwindPC = rt->wasmTrapData().pc;
MOZ_ASSERT(fp->instance()->code().containsCodePC(unwindPC));
return true;
}
void*
jit::JitActivation::wasmTrapPC() const
{
MOZ_ASSERT(isWasmTrapping());
return cx_->runtime()->wasmTrapData().pc;
}
uint32_t
jit::JitActivation::wasmTrapBytecodeOffset() const
{
MOZ_ASSERT(isWasmTrapping());
return cx_->runtime()->wasmTrapData().bytecodeOffset;
}
InterpreterFrameIterator&

Просмотреть файл

@ -1666,11 +1666,18 @@ class JitActivation : public Activation
// Interrupts are started from the interrupt signal handler (or the ARM
// simulator) and cleared by WasmHandleExecutionInterrupt or WasmHandleThrow
// when the interrupt is handled.
void startWasmInterrupt(const JS::ProfilingFrameIterator::RegisterState& state);
void finishWasmInterrupt();
bool isWasmInterrupted() const;
void* wasmUnwindPC() const;
void* wasmResumePC() const;
void* wasmInterruptUnwindPC() const;
void* wasmInterruptResumePC() const;
void startWasmTrap(wasm::Trap trap, uint32_t bytecodeOffset, void* pc, void* fp);
void finishWasmTrap();
bool isWasmTrapping() const;
void* wasmTrapPC() const;
uint32_t wasmTrapBytecodeOffset() const;
};
// A filtering of the ActivationIterator to only stop at JitActivations.

Просмотреть файл

@ -3807,15 +3807,6 @@ class BaseCompiler final : public BaseCompilerInterface
#endif
}
void unreachableTrap()
{
masm.jump(oldTrap(Trap::Unreachable));
#ifdef DEBUG
masm.breakpoint();
#endif
}
MOZ_MUST_USE bool
supportsRoundInstruction(RoundingMode mode)
{
@ -4905,6 +4896,10 @@ class BaseCompiler final : public BaseCompilerInterface
return iter_.bytecodeOffset();
}
void trap(Trap t) const {
masm.wasmTrap(t, bytecodeOffset());
}
OldTrapDesc oldTrap(Trap t) const {
// Use masm.framePushed() because the value needed by the trap machinery
// is the size of the frame overall, not the height of the stack area of
@ -8533,7 +8528,7 @@ BaseCompiler::emitBody()
case uint16_t(Op::Unreachable):
CHECK(iter_.readUnreachable());
if (!deadCode_) {
unreachableTrap();
trap(Trap::Unreachable);
deadCode_ = true;
}
NEXT();

Просмотреть файл

@ -87,7 +87,7 @@ WasmHandleExecutionInterrupt()
// If CheckForInterrupt succeeded, then execution can proceed and the
// interrupt is over.
void* resumePC = activation->wasmResumePC();
void* resumePC = activation->wasmInterruptResumePC();
activation->finishWasmInterrupt();
return resumePC;
}
@ -225,6 +225,7 @@ wasm::HandleThrow(JSContext* cx, WasmFrameIter& iter)
}
MOZ_ASSERT(!cx->activation()->asJit()->isWasmInterrupted(), "unwinding clears the interrupt");
MOZ_ASSERT(!cx->activation()->asJit()->isWasmTrapping(), "unwinding clears the trapping state");
return iter.unwoundAddressOfReturnAddress();
}
@ -288,6 +289,13 @@ WasmOldReportTrap(int32_t trapIndex)
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
}
static void
WasmReportTrap()
{
Trap trap = TlsContext.get()->runtime()->wasmTrapData().trap;
WasmOldReportTrap(int32_t(trap));
}
static void
WasmReportOutOfBounds()
{
@ -444,6 +452,9 @@ AddressOf(SymbolicAddress imm, ABIFunctionType* abiType)
case SymbolicAddress::HandleThrow:
*abiType = Args_General0;
return FuncCast(WasmHandleThrow, *abiType);
case SymbolicAddress::ReportTrap:
*abiType = Args_General0;
return FuncCast(WasmReportTrap, *abiType);
case SymbolicAddress::OldReportTrap:
*abiType = Args_General1;
return FuncCast(WasmOldReportTrap, *abiType);
@ -600,6 +611,7 @@ wasm::NeedsBuiltinThunk(SymbolicAddress sym)
case SymbolicAddress::HandleExecutionInterrupt: // GenerateInterruptExit
case SymbolicAddress::HandleDebugTrap: // GenerateDebugTrapStub
case SymbolicAddress::HandleThrow: // GenerateThrowStub
case SymbolicAddress::ReportTrap: // GenerateTrapExit
case SymbolicAddress::OldReportTrap: // GenerateOldTrapExit
case SymbolicAddress::ReportOutOfBounds: // GenerateOutOfBoundsExit
case SymbolicAddress::ReportUnalignedAccess: // GeneratesUnalignedExit
@ -896,6 +908,7 @@ wasm::EnsureBuiltinThunksInitialized()
MOZ_ASSERT(masm.callSites().empty());
MOZ_ASSERT(masm.callSiteTargets().empty());
MOZ_ASSERT(masm.callFarJumps().empty());
MOZ_ASSERT(masm.trapSites().empty());
MOZ_ASSERT(masm.oldTrapSites().empty());
MOZ_ASSERT(masm.oldTrapFarJumps().empty());
MOZ_ASSERT(masm.callFarJumps().empty());

Просмотреть файл

@ -242,9 +242,6 @@ CodeSegment::create(Tier tier,
const Metadata& metadata)
{
// These should always exist and should never be first in the code segment.
MOZ_ASSERT(linkData.interruptOffset != 0);
MOZ_ASSERT(linkData.outOfBoundsOffset != 0);
MOZ_ASSERT(linkData.unalignedAccessOffset != 0);
auto cs = js::MakeUnique<CodeSegment>();
if (!cs)
@ -268,6 +265,7 @@ CodeSegment::initialize(Tier tier,
MOZ_ASSERT(linkData.interruptOffset);
MOZ_ASSERT(linkData.outOfBoundsOffset);
MOZ_ASSERT(linkData.unalignedAccessOffset);
MOZ_ASSERT(linkData.trapOffset);
tier_ = tier;
bytes_ = Move(codeBytes);
@ -275,6 +273,7 @@ CodeSegment::initialize(Tier tier,
interruptCode_ = bytes_.get() + linkData.interruptOffset;
outOfBoundsCode_ = bytes_.get() + linkData.outOfBoundsOffset;
unalignedAccessCode_ = bytes_.get() + linkData.unalignedAccessOffset;
trapCode_ = bytes_.get() + linkData.trapOffset;
if (!StaticallyLink(*this, linkData))
return false;
@ -459,6 +458,7 @@ MetadataTier::serializedSize() const
return SerializedPodVectorSize(memoryAccesses) +
SerializedPodVectorSize(codeRanges) +
SerializedPodVectorSize(callSites) +
trapSites.serializedSize() +
SerializedVectorSize(funcImports) +
SerializedVectorSize(funcExports);
}
@ -469,6 +469,7 @@ MetadataTier::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
return memoryAccesses.sizeOfExcludingThis(mallocSizeOf) +
codeRanges.sizeOfExcludingThis(mallocSizeOf) +
callSites.sizeOfExcludingThis(mallocSizeOf) +
trapSites.sizeOfExcludingThis(mallocSizeOf) +
SizeOfVectorExcludingThis(funcImports, mallocSizeOf) +
SizeOfVectorExcludingThis(funcExports, mallocSizeOf);
}
@ -480,6 +481,7 @@ MetadataTier::serialize(uint8_t* cursor) const
cursor = SerializePodVector(cursor, memoryAccesses);
cursor = SerializePodVector(cursor, codeRanges);
cursor = SerializePodVector(cursor, callSites);
cursor = trapSites.serialize(cursor);
cursor = SerializeVector(cursor, funcImports);
cursor = SerializeVector(cursor, funcExports);
return cursor;
@ -491,6 +493,7 @@ MetadataTier::deserialize(const uint8_t* cursor)
(cursor = DeserializePodVector(cursor, &memoryAccesses)) &&
(cursor = DeserializePodVector(cursor, &codeRanges)) &&
(cursor = DeserializePodVector(cursor, &callSites)) &&
(cursor = trapSites.deserialize(cursor)) &&
(cursor = DeserializeVector(cursor, &funcImports)) &&
(cursor = DeserializeVector(cursor, &funcExports));
debugTrapFarJumpOffsets.clear();
@ -750,7 +753,7 @@ Code::segment(Tier tier) const
bool
Code::containsCodePC(const void* pc) const
{
for (auto t : tiers()) {
for (Tier t : tiers()) {
const CodeSegment& cs = segment(t);
if (cs.containsCodePC(pc))
return true;
@ -770,7 +773,7 @@ struct CallSiteRetAddrOffset
const CallSite*
Code::lookupCallSite(void* returnAddress) const
{
for (auto t : tiers()) {
for (Tier t : tiers()) {
uint32_t target = ((uint8_t*)returnAddress) - segment(t).base();
size_t lowerBound = 0;
size_t upperBound = metadata(t).callSites.length();
@ -787,7 +790,7 @@ Code::lookupCallSite(void* returnAddress) const
const CodeRange*
Code::lookupRange(void* pc) const
{
for (auto t : tiers()) {
for (Tier t : tiers()) {
CodeRange::OffsetInCode target((uint8_t*)pc - segment(t).base());
const CodeRange* result = LookupInSorted(metadata(t).codeRanges, target);
if (result)
@ -809,7 +812,7 @@ struct MemoryAccessOffset
const MemoryAccess*
Code::lookupMemoryAccess(void* pc) const
{
for (auto t : tiers()) {
for (Tier t : tiers()) {
const MemoryAccessVector& memoryAccesses = metadata(t).memoryAccesses;
uint32_t target = ((uint8_t*)pc) - segment(t).base();
@ -827,6 +830,40 @@ Code::lookupMemoryAccess(void* pc) const
return nullptr;
}
struct TrapSitePCOffset
{
const TrapSiteVector& trapSites;
explicit TrapSitePCOffset(const TrapSiteVector& trapSites) : trapSites(trapSites) {}
uint32_t operator[](size_t index) const {
return trapSites[index].pcOffset;
}
};
bool
Code::lookupTrap(void* pc, Trap* trapOut, BytecodeOffset* bytecode) const
{
for (Tier t : tiers()) {
const TrapSiteVectorArray& trapSitesArray = metadata(t).trapSites;
for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
const TrapSiteVector& trapSites = trapSitesArray[trap];
uint32_t target = ((uint8_t*)pc) - segment(t).base();
size_t lowerBound = 0;
size_t upperBound = trapSites.length();
size_t match;
if (BinarySearch(TrapSitePCOffset(trapSites), lowerBound, upperBound, target, &match)) {
MOZ_ASSERT(segment(t).containsCodePC(pc));
*trapOut = trap;
*bytecode = trapSites[match].bytecode;
return true;
}
}
}
return false;
}
// When enabled, generate profiling labels for every name in funcNames_ that is
// the name of some Function CodeRange. This involves malloc() so do it now
// since, once we start sampling, we'll be in a signal-handing context where we

Просмотреть файл

@ -81,6 +81,7 @@ class CodeSegment
uint8_t* interruptCode_;
uint8_t* outOfBoundsCode_;
uint8_t* unalignedAccessCode_;
uint8_t* trapCode_;
bool registered_;
@ -108,6 +109,7 @@ class CodeSegment
interruptCode_(nullptr),
outOfBoundsCode_(nullptr),
unalignedAccessCode_(nullptr),
trapCode_(nullptr),
registered_(false)
{}
@ -139,6 +141,7 @@ class CodeSegment
uint8_t* interruptCode() const { return interruptCode_; }
uint8_t* outOfBoundsCode() const { return outOfBoundsCode_; }
uint8_t* unalignedAccessCode() const { return unalignedAccessCode_; }
uint8_t* trapCode() const { return trapCode_; }
bool containsCodePC(const void* pc) const {
return pc >= base() && pc < (base() + length_);
@ -351,6 +354,7 @@ struct MetadataTier
MemoryAccessVector memoryAccesses;
CodeRangeVector codeRanges;
CallSiteVector callSites;
TrapSiteVectorArray trapSites;
FuncImportVector funcImports;
FuncExportVector funcExports;
@ -488,6 +492,7 @@ class Code : public ShareableBase<Code>
const CodeRange* lookupRange(void* pc) const;
const MemoryAccess* lookupMemoryAccess(void* pc) const;
bool containsCodePC(const void* pc) const;
bool lookupTrap(void* pc, Trap* trap, BytecodeOffset* bytecode) const;
// To save memory, profilingLabels_ are generated lazily when profiling mode
// is enabled.

Просмотреть файл

@ -42,6 +42,23 @@ WasmFrameIter::WasmFrameIter(JitActivation* activation, wasm::Frame* fp)
{
MOZ_ASSERT(fp_);
// When the stack is captured during a trap (viz., to create the .stack
// for an Error object), use the pc/bytecode information captured by the
// signal handler in the runtime.
if (activation->isWasmTrapping()) {
code_ = &fp_->tls->instance->code();
MOZ_ASSERT(code_ == LookupCode(activation->wasmTrapPC()));
codeRange_ = code_->lookupRange(activation->wasmTrapPC());
MOZ_ASSERT(codeRange_->kind() == CodeRange::Function);
lineOrBytecode_ = activation->wasmTrapBytecodeOffset();
MOZ_ASSERT(!done());
return;
}
// When asynchronously interrupted, exitFP is set to the interrupted frame
// itself and so we do not want to skip it. Instead, we can recover the
// Code and CodeRange from the JitActivation, which are set when control
@ -52,9 +69,9 @@ WasmFrameIter::WasmFrameIter(JitActivation* activation, wasm::Frame* fp)
if (activation->isWasmInterrupted()) {
code_ = &fp_->tls->instance->code();
MOZ_ASSERT(code_ == LookupCode(activation->wasmUnwindPC()));
MOZ_ASSERT(code_ == LookupCode(activation->wasmInterruptUnwindPC()));
codeRange_ = code_->lookupRange(activation->wasmUnwindPC());
codeRange_ = code_->lookupRange(activation->wasmInterruptUnwindPC());
MOZ_ASSERT(codeRange_->kind() == CodeRange::Function);
lineOrBytecode_ = codeRange_->funcLineOrBytecode();
@ -98,6 +115,8 @@ WasmFrameIter::operator++()
if (unwind_ == Unwind::True) {
if (activation_->isWasmInterrupted())
activation_->finishWasmInterrupt();
else if (activation_->isWasmTrapping())
activation_->finishWasmTrap();
activation_->setWasmExitFP(fp_);
}
@ -624,6 +643,7 @@ ProfilingFrameIterator::initFromExitFP(const Frame* fp)
case CodeRange::ImportJitExit:
case CodeRange::ImportInterpExit:
case CodeRange::BuiltinThunk:
case CodeRange::TrapExit:
case CodeRange::OldTrapExit:
case CodeRange::DebugTrap:
case CodeRange::OutOfBoundsExit:
@ -794,6 +814,7 @@ js::wasm::StartUnwinding(const RegisterState& registers, UnwindState* unwindStat
break;
}
break;
case CodeRange::TrapExit:
case CodeRange::OutOfBoundsExit:
case CodeRange::UnalignedExit:
// These code stubs execute after the prologue/epilogue have completed
@ -902,6 +923,7 @@ ProfilingFrameIterator::operator++()
case CodeRange::ImportJitExit:
case CodeRange::ImportInterpExit:
case CodeRange::BuiltinThunk:
case CodeRange::TrapExit:
case CodeRange::OldTrapExit:
case CodeRange::DebugTrap:
case CodeRange::OutOfBoundsExit:
@ -930,6 +952,7 @@ ThunkedNativeToDescription(SymbolicAddress func)
case SymbolicAddress::HandleExecutionInterrupt:
case SymbolicAddress::HandleDebugTrap:
case SymbolicAddress::HandleThrow:
case SymbolicAddress::ReportTrap:
case SymbolicAddress::OldReportTrap:
case SymbolicAddress::ReportOutOfBounds:
case SymbolicAddress::ReportUnalignedAccess:
@ -1060,6 +1083,7 @@ ProfilingFrameIterator::label() const
case CodeRange::ImportJitExit: return importJitDescription;
case CodeRange::BuiltinThunk: return builtinNativeDescription;
case CodeRange::ImportInterpExit: return importInterpDescription;
case CodeRange::TrapExit: return trapDescription;
case CodeRange::OldTrapExit: return trapDescription;
case CodeRange::DebugTrap: return debugTrapDescription;
case CodeRange::OutOfBoundsExit: return "out-of-bounds stub (in wasm)";

Просмотреть файл

@ -47,6 +47,7 @@ CompiledCode::swap(MacroAssembler& masm)
callSites.swap(masm.callSites());
callSiteTargets.swap(masm.callSiteTargets());
trapSites.swap(masm.trapSites());
oldTrapSites.swap(masm.oldTrapSites());
callFarJumps.swap(masm.callFarJumps());
oldTrapFarJumps.swap(masm.oldTrapFarJumps());
@ -380,7 +381,7 @@ InRange(uint32_t caller, uint32_t callee)
}
typedef HashMap<uint32_t, uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy> OffsetMap;
typedef EnumeratedArray<Trap, Trap::Limit, Maybe<uint32_t>> TrapOffsetArray;
typedef EnumeratedArray<Trap, Trap::Limit, Maybe<uint32_t>> TrapMaybeOffsetArray;
bool
ModuleGenerator::linkCallSites()
@ -399,7 +400,7 @@ ModuleGenerator::linkCallSites()
if (!existingCallFarJumps.init())
return false;
TrapOffsetArray existingTrapFarJumps;
TrapMaybeOffsetArray existingTrapFarJumps;
for (; lastPatchedCallSite_ < metadataTier_->callSites.length(); lastPatchedCallSite_++) {
const CallSite& callSite = metadataTier_->callSites[lastPatchedCallSite_];
@ -523,6 +524,10 @@ ModuleGenerator::noteCodeRange(uint32_t codeRangeIndex, const CodeRange& codeRan
MOZ_ASSERT(!linkDataTier_->interruptOffset);
linkDataTier_->interruptOffset = codeRange.begin();
break;
case CodeRange::TrapExit:
MOZ_ASSERT(!linkDataTier_->trapOffset);
linkDataTier_->trapOffset = codeRange.begin();
break;
case CodeRange::Throw:
// Jumped to by other stubs, so nothing to do.
break;
@ -580,6 +585,12 @@ ModuleGenerator::linkCompiledCode(const CompiledCode& code)
if (!callSiteTargets_.appendAll(code.callSiteTargets))
return false;
for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
auto trapSiteOp = [=](uint32_t, TrapSite* ts) { ts->offsetBy(offsetInModule); };
if (!AppendForEach(&metadataTier_->trapSites[trap], code.trapSites[trap], trapSiteOp))
return false;
}
MOZ_ASSERT(code.oldTrapSites.empty());
auto trapFarJumpOp = [=](uint32_t, OldTrapFarJump* tfj) { tfj->offsetBy(offsetInModule); };
@ -798,6 +809,7 @@ ModuleGenerator::finishCode()
MOZ_ASSERT(masm_.callSites().empty());
MOZ_ASSERT(masm_.callSiteTargets().empty());
MOZ_ASSERT(masm_.trapSites().empty());
MOZ_ASSERT(masm_.oldTrapSites().empty());
MOZ_ASSERT(masm_.oldTrapFarJumps().empty());
MOZ_ASSERT(masm_.callFarJumps().empty());
@ -812,19 +824,32 @@ ModuleGenerator::finishCode()
bool
ModuleGenerator::finishMetadata(const ShareableBytes& bytecode)
{
// Assert all sorted metadata is sorted.
#ifdef DEBUG
// Assert CodeRanges are sorted.
uint32_t lastEnd = 0;
uint32_t last = 0;
for (const CodeRange& codeRange : metadataTier_->codeRanges) {
MOZ_ASSERT(codeRange.begin() >= lastEnd);
lastEnd = codeRange.end();
MOZ_ASSERT(codeRange.begin() >= last);
last = codeRange.end();
}
// Assert debugTrapFarJumpOffsets are sorted.
uint32_t lastOffset = 0;
last = 0;
for (const CallSite& callSite : metadataTier_->callSites) {
MOZ_ASSERT(callSite.returnAddressOffset() >= last);
last = callSite.returnAddressOffset();
}
for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
last = 0;
for (const TrapSite& trapSite : metadataTier_->trapSites[trap]) {
MOZ_ASSERT(trapSite.pcOffset >= last);
last = trapSite.pcOffset;
}
}
last = 0;
for (uint32_t debugTrapFarJumpOffset : metadataTier_->debugTrapFarJumpOffsets) {
MOZ_ASSERT(debugTrapFarJumpOffset >= lastOffset);
lastOffset = debugTrapFarJumpOffset;
MOZ_ASSERT(debugTrapFarJumpOffset >= last);
last = debugTrapFarJumpOffset;
}
#endif
@ -849,7 +874,7 @@ ModuleGenerator::finishMetadata(const ShareableBytes& bytecode)
metadataTier_->memoryAccesses.podResizeToFit();
metadataTier_->codeRanges.podResizeToFit();
metadataTier_->callSites.podResizeToFit();
metadataTier_->trapSites.podResizeToFit();
metadataTier_->debugTrapFarJumpOffsets.podResizeToFit();
metadataTier_->debugFuncToCodeRange.podResizeToFit();

Просмотреть файл

@ -64,6 +64,7 @@ struct CompiledCode
CodeRangeVector codeRanges;
CallSiteVector callSites;
CallSiteTargetVector callSiteTargets;
TrapSiteVectorArray trapSites;
OldTrapSiteVector oldTrapSites;
OldTrapFarJumpVector oldTrapFarJumps;
CallFarJumpVector callFarJumps;
@ -78,6 +79,7 @@ struct CompiledCode
codeRanges.clear();
callSites.clear();
callSiteTargets.clear();
trapSites.clear();
oldTrapSites.clear();
oldTrapFarJumps.clear();
callFarJumps.clear();
@ -92,6 +94,7 @@ struct CompiledCode
codeRanges.empty() &&
callSites.empty() &&
callSiteTargets.empty() &&
trapSites.empty() &&
oldTrapSites.empty() &&
oldTrapFarJumps.empty() &&
callFarJumps.empty() &&

Просмотреть файл

@ -45,6 +45,7 @@ struct LinkDataTierCacheablePod
uint32_t interruptOffset;
uint32_t outOfBoundsOffset;
uint32_t unalignedAccessOffset;
uint32_t trapOffset;
LinkDataTierCacheablePod() { mozilla::PodZero(this); }
};

Просмотреть файл

@ -421,27 +421,31 @@ struct macos_arm_context {
# define LR_sig(p) R31_sig(p)
#endif
#if defined(FP_sig) && defined(SP_sig) && defined(SP_sig)
# define KNOWS_MACHINE_STATE
#endif
static uint8_t**
ContextToPC(CONTEXT* context)
{
#ifdef JS_CODEGEN_NONE
MOZ_CRASH();
#else
#ifdef KNOWS_MACHINE_STATE
return reinterpret_cast<uint8_t**>(&PC_sig(context));
#else
MOZ_CRASH();
#endif
}
static uint8_t*
ContextToFP(CONTEXT* context)
{
#ifdef JS_CODEGEN_NONE
MOZ_CRASH();
#else
#ifdef KNOWS_MACHINE_STATE
return reinterpret_cast<uint8_t*>(FP_sig(context));
#else
MOZ_CRASH();
#endif
}
#ifndef JS_CODEGEN_NONE
#ifdef KNOWS_MACHINE_STATE
static uint8_t*
ContextToSP(CONTEXT* context)
{
@ -455,7 +459,7 @@ ContextToLR(CONTEXT* context)
return reinterpret_cast<uint8_t*>(LR_sig(context));
}
# endif
#endif // JS_CODEGEN_NONE
#endif // KNOWS_MACHINE_STATE
#if defined(XP_DARWIN)
@ -532,9 +536,7 @@ ToRegisterState(EMULATOR_CONTEXT* context)
static JS::ProfilingFrameIterator::RegisterState
ToRegisterState(CONTEXT* context)
{
#ifdef JS_CODEGEN_NONE
MOZ_CRASH();
#else
#ifdef KNOWS_MACHINE_STATE
JS::ProfilingFrameIterator::RegisterState state;
state.fp = ContextToFP(context);
state.pc = *ContextToPC(context);
@ -543,6 +545,8 @@ ToRegisterState(CONTEXT* context)
state.lr = ContextToLR(context);
# endif
return state;
#else
MOZ_CRASH();
#endif
}
@ -975,15 +979,15 @@ HandleFault(PEXCEPTION_POINTERS exception)
EXCEPTION_RECORD* record = exception->ExceptionRecord;
CONTEXT* context = exception->ContextRecord;
if (record->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
if (record->ExceptionCode != EXCEPTION_ACCESS_VIOLATION &&
record->ExceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION)
{
return false;
}
uint8_t** ppc = ContextToPC(context);
uint8_t* pc = *ppc;
if (record->NumberParameters < 2)
return false;
const CodeSegment* codeSegment = LookupCodeSegment(pc);
if (!codeSegment)
return false;
@ -994,7 +998,7 @@ HandleFault(PEXCEPTION_POINTERS exception)
const Instance* instance = LookupFaultingInstance(*codeSegment, pc, ContextToFP(context));
if (!instance) {
// On Windows, it is possible for InterruptRunningJitCode to execute
// between a faulting heap access and the handling of the fault due
// between a faulting instruction and the handling of the fault due
// to InterruptRunningJitCode's use of SuspendThread. When this happens,
// after ResumeThread, the exception handler is called with pc equal to
// CodeSegment.interrupt, which is logically wrong. The Right Thing would
@ -1004,9 +1008,36 @@ HandleFault(PEXCEPTION_POINTERS exception)
// retrigger after the interrupt jumps back to resumePC).
return activation->isWasmInterrupted() &&
pc == codeSegment->interruptCode() &&
codeSegment->containsCodePC(activation->wasmResumePC());
codeSegment->containsCodePC(activation->wasmInterruptResumePC());
}
// In the same race-with-interrupt situation above, it's *also* possible
// that the reported 'pc' is the pre-interrupt pc, not post-interrupt
// codeSegment->interruptCode (this may be windows-version-specific). In
// this case, lookupTrap()/lookupMemoryAccess() will all succeed causing the
// pc to be redirected *again* (to a trap stub), leading to the interrupt
// stub never being called. Since the goal of the async interrupt is to break
// out iloops and trapping does just that, this is fine, we just clear the
// "interrupted" state.
if (activation->isWasmInterrupted()) {
MOZ_ASSERT(activation->wasmInterruptResumePC() == pc);
activation->finishWasmInterrupt();
}
if (record->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) {
Trap trap;
BytecodeOffset bytecode;
if (!codeSegment->code().lookupTrap(pc, &trap, &bytecode))
return false;
activation->startWasmTrap(trap, bytecode.offset, pc, ContextToFP(context));
*ppc = codeSegment->trapCode();
return true;
}
if (record->NumberParameters < 2)
return false;
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(record->ExceptionInformation[1]);
// This check isn't necessary, but, since we can, check anyway to make
@ -1016,18 +1047,6 @@ HandleFault(PEXCEPTION_POINTERS exception)
MOZ_ASSERT(activation->compartment() == instance->compartment());
// Similar to the non-atomic situation above, on Windows, an OOB fault at a
// PC can trigger *after* an async interrupt observed that PC and attempted
// to redirect to the async stub. In this unique case, isWasmInterrupted() is
// already true when the OOB handler is called. Since the point of the async
// interrupt is to get out of an iloop and the OOB trap will do just that,
// we can simply clear the interrupt. (The update to CONTEXT.pc made by
// HandleMemoryAccess will clobber the interrupt's previous update.)
if (activation->isWasmInterrupted()) {
MOZ_ASSERT(activation->wasmResumePC() == pc);
activation->finishWasmInterrupt();
}
HandleMemoryAccess(context, pc, faultingAddress, codeSegment, *instance, activation, ppc);
return true;
}
@ -1113,8 +1132,11 @@ HandleMachException(JSContext* cx, const ExceptionRequest& request)
uint8_t** ppc = ContextToPC(&context);
uint8_t* pc = *ppc;
if (request.body.exception != EXC_BAD_ACCESS || request.body.codeCnt != 2)
if (request.body.exception != EXC_BAD_ACCESS &&
request.body.exception != EXC_BAD_INSTRUCTION)
{
return false;
}
// The faulting thread is suspended so we can access cx fields that can
// normally only be accessed by the cx's active thread.
@ -1128,17 +1150,31 @@ HandleMachException(JSContext* cx, const ExceptionRequest& request)
if (!instance)
return false;
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(request.body.code[1]);
// This check isn't necessary, but, since we can, check anyway to make
// sure we aren't covering up a real bug.
if (!IsHeapAccessAddress(*instance, faultingAddress))
return false;
JitActivation* activation = cx->activation()->asJit();
MOZ_ASSERT(activation->compartment() == instance->compartment());
HandleMemoryAccess(&context, pc, faultingAddress, codeSegment, *instance, activation, ppc);
if (request.body.exception == EXC_BAD_INSTRUCTION) {
Trap trap;
BytecodeOffset bytecode;
if (!codeSegment->code().lookupTrap(pc, &trap, &bytecode))
return false;
activation->startWasmTrap(trap, bytecode.offset, pc, ContextToFP(&context));
*ppc = codeSegment->trapCode();
} else {
MOZ_ASSERT(request.body.exception == EXC_BAD_ACCESS);
if (request.body.codeCnt != 2)
return false;
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(request.body.code[1]);
// This check isn't necessary, but, since we can, check anyway to make
// sure we aren't covering up a real bug.
if (!IsHeapAccessAddress(*instance, faultingAddress))
return false;
HandleMemoryAccess(&context, pc, faultingAddress, codeSegment, *instance, activation, ppc);
}
// Update the thread state with the new pc and register values.
kret = thread_set_state(cxThread, float_state, (thread_state_t)&context.float_, float_state_count);
@ -1221,7 +1257,7 @@ MachExceptionHandler::uninstall()
if (installed_) {
thread_port_t thread = mach_thread_self();
kern_return_t kret = thread_set_exception_ports(thread,
EXC_MASK_BAD_ACCESS,
EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION,
MACH_PORT_NULL,
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
THREAD_STATE_NONE);
@ -1286,7 +1322,7 @@ MachExceptionHandler::install(JSContext* cx)
// exception, so we should be fine.
thread = mach_thread_self();
kret = thread_set_exception_ports(thread,
EXC_MASK_BAD_ACCESS,
EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION,
port_,
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
THREAD_STATE_NONE);
@ -1311,7 +1347,7 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
return false;
AutoSignalHandler ash;
MOZ_RELEASE_ASSERT(signum == SIGSEGV || signum == SIGBUS);
MOZ_RELEASE_ASSERT(signum == SIGSEGV || signum == SIGBUS || signum == SIGILL);
CONTEXT* context = (CONTEXT*)ctx;
uint8_t** ppc = ContextToPC(context);
@ -1325,6 +1361,20 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
if (!instance)
return false;
JitActivation* activation = TlsContext.get()->activation()->asJit();
MOZ_ASSERT(activation->compartment() == instance->compartment());
if (signum == SIGILL) {
Trap trap;
BytecodeOffset bytecode;
if (!segment->code().lookupTrap(pc, &trap, &bytecode))
return false;
activation->startWasmTrap(trap, bytecode.offset, pc, ContextToFP(context));
*ppc = segment->trapCode();
return true;
}
uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(info->si_addr);
// Although it's not strictly necessary, to make sure we're not covering up
@ -1346,9 +1396,6 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
return false;
}
JitActivation* activation = TlsContext.get()->activation()->asJit();
MOZ_ASSERT(activation->compartment() == instance->compartment());
#ifdef JS_CODEGEN_ARM
if (signum == SIGBUS) {
// TODO: We may see a bus error for something that is an unaligned access that
@ -1367,6 +1414,7 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
static struct sigaction sPrevSEGVHandler;
static struct sigaction sPrevSIGBUSHandler;
static struct sigaction sPrevSIGILLHandler;
static void
WasmFaultHandler(int signum, siginfo_t* info, void* context)
@ -1374,9 +1422,13 @@ WasmFaultHandler(int signum, siginfo_t* info, void* context)
if (HandleFault(signum, info, context))
return;
struct sigaction* previousSignal = signum == SIGSEGV
? &sPrevSEGVHandler
: &sPrevSIGBUSHandler;
struct sigaction* previousSignal = nullptr;
switch (signum) {
case SIGSEGV: previousSignal = &sPrevSEGVHandler; break;
case SIGBUS: previousSignal = &sPrevSIGBUSHandler; break;
case SIGILL: previousSignal = &sPrevSIGILLHandler; break;
}
MOZ_ASSERT(previousSignal);
// This signal is not for any asm.js code we expect, so we need to forward
// the signal to the next handler. If there is no next handler (SIG_IGN or
@ -1617,6 +1669,15 @@ ProcessHasSignalHandlers()
if (sigaction(SIGBUS, &busHandler, &sPrevSIGBUSHandler))
MOZ_CRASH("unable to install sigbus handler");
# endif
// Install a SIGILL handler to handle the ud2 instructions that are emitted
// to implement wasm traps.
struct sigaction illegalHandler;
illegalHandler.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
illegalHandler.sa_sigaction = WasmFaultHandler;
sigemptyset(&illegalHandler.sa_mask);
if (sigaction(SIGILL, &illegalHandler, &sPrevSIGILLHandler))
MOZ_CRASH("unable to install segv handler");
# endif
sHaveSignalHandlers = true;

Просмотреть файл

@ -25,6 +25,7 @@
# include <mach/mach.h>
#endif
#include "threading/Thread.h"
#include "wasm/WasmTypes.h"
struct JSContext;
struct JSRuntime;
@ -79,6 +80,35 @@ class MachExceptionHandler
};
#endif
// Typed wrappers encapsulating the data saved by the signal handler on async
// interrupt or trap. On interrupt, the PC at which to resume is saved. On trap,
// the bytecode offset to be reported in callstacks is saved.
struct InterruptData
{
// The pc to use for unwinding purposes which is kept consistent with fp at
// call boundaries.
void* unwindPC;
// The pc at which we should return if the interrupt doesn't stop execution.
void* resumePC;
InterruptData(void* unwindPC, void* resumePC)
: unwindPC(unwindPC), resumePC(resumePC)
{}
};
struct TrapData
{
void* pc;
Trap trap;
uint32_t bytecodeOffset;
TrapData(void* pc, Trap trap, uint32_t bytecodeOffset)
: pc(pc), trap(trap), bytecodeOffset(bytecodeOffset)
{}
};
} // namespace wasm
} // namespace js

Просмотреть файл

@ -1001,6 +1001,29 @@ wasm::GenerateBuiltinThunk(MacroAssembler& masm, ABIFunctionType abiType, ExitRe
return FinishOffsets(masm, offsets);
}
// Generate a stub which calls WasmReportTrap() and can be executed by having
// the signal handler redirect PC from any trapping instruction.
static bool
GenerateTrapExit(MacroAssembler& masm, Label* throwLabel, Offsets* offsets)
{
masm.haltingAlign(CodeAlignment);
offsets->begin = masm.currentOffset();
// We know that StackPointer is word-aligned, but not necessarily
// stack-aligned, so we need to align it dynamically.
masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
if (ShadowStackSpace)
masm.subFromStackPtr(Imm32(ShadowStackSpace));
masm.assertStackAlignment(ABIStackAlignment);
masm.call(SymbolicAddress::ReportTrap);
masm.jump(throwLabel);
return FinishOffsets(masm, offsets);
}
// Generate a stub that calls into WasmOldReportTrap with the right trap reason.
// This stub is called with ABIStackAlignment by a trap out-of-line path. An
// exit prologue/epilogue is used so that stack unwinding picks up the
@ -1368,11 +1391,30 @@ wasm::GenerateStubs(const ModuleEnvironment& env, const FuncImportVector& import
}
for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
CallableOffsets offsets;
if (!GenerateOldTrapExit(masm, trap, &throwLabel, &offsets))
return false;
if (!code->codeRanges.emplaceBack(trap, offsets))
return false;
switch (trap) {
case Trap::Unreachable:
break;
// The TODO list of "old" traps to convert to new traps:
case Trap::IntegerOverflow:
case Trap::InvalidConversionToInteger:
case Trap::IntegerDivideByZero:
case Trap::OutOfBounds:
case Trap::UnalignedAccess:
case Trap::IndirectCallToNull:
case Trap::IndirectCallBadSig:
case Trap::ImpreciseSimdConversion:
case Trap::StackOverflow:
case Trap::ThrowReported: {
CallableOffsets offsets;
if (!GenerateOldTrapExit(masm, trap, &throwLabel, &offsets))
return false;
if (!code->codeRanges.emplaceBack(trap, offsets))
return false;
break;
}
case Trap::Limit:
MOZ_CRASH("impossible");
}
}
Offsets offsets;
@ -1387,6 +1429,11 @@ wasm::GenerateStubs(const ModuleEnvironment& env, const FuncImportVector& import
if (!code->codeRanges.emplaceBack(CodeRange::UnalignedExit, offsets))
return false;
if (!GenerateTrapExit(masm, &throwLabel, &offsets))
return false;
if (!code->codeRanges.emplaceBack(CodeRange::TrapExit, offsets))
return false;
if (!GenerateInterruptExit(masm, &throwLabel, &offsets))
return false;
if (!code->codeRanges.emplaceBack(CodeRange::Interrupt, offsets))

Просмотреть файл

@ -30,6 +30,7 @@ using namespace js::jit;
using namespace js::wasm;
using mozilla::IsPowerOfTwo;
using mozilla::MakeEnumeratedRange;
// A sanity check. We have only tested WASM_HUGE_MEMORY on x64, and only tested
// x64 with WASM_HUGE_MEMORY.
@ -690,6 +691,75 @@ DebugFrame::leave(JSContext* cx)
}
}
bool
TrapSiteVectorArray::empty() const
{
for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
if (!(*this)[trap].empty())
return false;
}
return true;
}
void
TrapSiteVectorArray::clear()
{
for (Trap trap : MakeEnumeratedRange(Trap::Limit))
(*this)[trap].clear();
}
void
TrapSiteVectorArray::swap(TrapSiteVectorArray& rhs)
{
for (Trap trap : MakeEnumeratedRange(Trap::Limit))
(*this)[trap].swap(rhs[trap]);
}
void
TrapSiteVectorArray::podResizeToFit()
{
for (Trap trap : MakeEnumeratedRange(Trap::Limit))
(*this)[trap].podResizeToFit();
}
size_t
TrapSiteVectorArray::serializedSize() const
{
size_t ret = 0;
for (Trap trap : MakeEnumeratedRange(Trap::Limit))
ret += SerializedPodVectorSize((*this)[trap]);
return ret;
}
uint8_t*
TrapSiteVectorArray::serialize(uint8_t* cursor) const
{
for (Trap trap : MakeEnumeratedRange(Trap::Limit))
cursor = SerializePodVector(cursor, (*this)[trap]);
return cursor;
}
const uint8_t*
TrapSiteVectorArray::deserialize(const uint8_t* cursor)
{
for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
cursor = DeserializePodVector(cursor, &(*this)[trap]);
if (!cursor)
return nullptr;
}
return cursor;
}
size_t
TrapSiteVectorArray::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
{
size_t ret = 0;
for (Trap trap : MakeEnumeratedRange(Trap::Limit))
ret += (*this)[trap].sizeOfExcludingThis(mallocSizeOf);
return ret;
}
CodeRange::CodeRange(Kind kind, Offsets offsets)
: begin_(offsets.begin),
ret_(0),
@ -703,6 +773,7 @@ CodeRange::CodeRange(Kind kind, Offsets offsets)
case FarJumpIsland:
case OutOfBoundsExit:
case UnalignedExit:
case TrapExit:
case Throw:
case Interrupt:
break;

Просмотреть файл

@ -935,6 +935,52 @@ enum class Trap
Limit
};
// A wrapper around the bytecode offset of a wasm instruction within a whole
// module, used for trap offsets or call offsets. These offsets should refer to
// the first byte of the instruction that triggered the trap / did the call and
// should ultimately derive from OpIter::bytecodeOffset.
struct BytecodeOffset
{
static const uint32_t INVALID = -1;
uint32_t offset;
BytecodeOffset() : offset(INVALID) {}
explicit BytecodeOffset(uint32_t offset) : offset(offset) {}
bool isValid() const { return offset != INVALID; }
};
// A TrapSite (in the TrapSiteVector for a given Trap code) represents a wasm
// instruction at a given bytecode offset that can fault at the given pc offset.
// When such a fault occurs, a signal/exception handler looks up the TrapSite to
// confirm the fault is intended/safe and redirects pc to the trap stub.
struct TrapSite
{
uint32_t pcOffset;
BytecodeOffset bytecode;
TrapSite() : pcOffset(-1), bytecode() {}
TrapSite(uint32_t pcOffset, BytecodeOffset bytecode) : pcOffset(pcOffset), bytecode(bytecode) {}
void offsetBy(uint32_t offset) {
pcOffset += offset;
}
};
WASM_DECLARE_POD_VECTOR(TrapSite, TrapSiteVector)
struct TrapSiteVectorArray : EnumeratedArray<Trap, Trap::Limit, TrapSiteVector>
{
bool empty() const;
void clear();
void swap(TrapSiteVectorArray& rhs);
void podResizeToFit();
WASM_DECLARE_SERIALIZABLE(TrapSiteVectorArray)
};
// The (,Callable,Func)Offsets classes are used to record the offsets of
// different key points in a CodeRange during compilation.
@ -1011,6 +1057,7 @@ class CodeRange
ImportJitExit, // fast-path calling from wasm into JIT code
ImportInterpExit, // slow-path calling from wasm into C++ interp
BuiltinThunk, // fast-path calling from wasm into a C++ native
TrapExit, // calls C++ to report and jumps to throw stub
OldTrapExit, // calls C++ to report and jumps to throw stub
DebugTrap, // calls C++ to handle debug event
FarJumpIsland, // inserted to connect otherwise out-of-range insns
@ -1087,7 +1134,7 @@ class CodeRange
return kind() == ImportJitExit;
}
bool isTrapExit() const {
return kind() == OldTrapExit;
return kind() == OldTrapExit || kind() == TrapExit;
}
bool isDebugTrap() const {
return kind() == DebugTrap;
@ -1101,7 +1148,7 @@ class CodeRange
// the return instruction to calculate the frame pointer.
bool hasReturn() const {
return isFunction() || isImportExit() || isTrapExit() || isDebugTrap();
return isFunction() || isImportExit() || kind() == OldTrapExit || isDebugTrap();
}
uint32_t ret() const {
MOZ_ASSERT(hasReturn());
@ -1179,22 +1226,6 @@ WASM_DECLARE_POD_VECTOR(CodeRange, CodeRangeVector)
extern const CodeRange*
LookupInSorted(const CodeRangeVector& codeRanges, CodeRange::OffsetInCode target);
// A wrapper around the bytecode offset of a wasm instruction within a whole
// module, used for trap offsets or call offsets. These offsets should refer to
// the first byte of the instruction that triggered the trap / did the call and
// should ultimately derive from OpIter::bytecodeOffset.
struct BytecodeOffset
{
static const uint32_t INVALID = -1;
uint32_t offset;
BytecodeOffset() : offset(INVALID) {}
explicit BytecodeOffset(uint32_t offset) : offset(offset) {}
bool isValid() const { return offset != INVALID; }
};
// While the frame-pointer chain allows the stack to be unwound without
// metadata, Error.stack still needs to know the line/column of every call in
// the chain. A CallSiteDesc describes a single callsite to which CallSite adds
@ -1333,6 +1364,7 @@ enum class SymbolicAddress
HandleExecutionInterrupt,
HandleDebugTrap,
HandleThrow,
ReportTrap,
OldReportTrap,
ReportOutOfBounds,
ReportUnalignedAccess,

Просмотреть файл

@ -17,7 +17,6 @@
// PR LOGGING
#include "mozilla/Logging.h"
#define DUMP_LAYOUT_LEVEL 9 // this turns on the dumping of each doucment's layout info
static mozilla::LazyLogModule gPrintingLog("printing");
#define PR_PL(_p1) MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug, _p1);

Просмотреть файл

@ -136,7 +136,8 @@ using namespace mozilla::dom;
//#define EXTENDED_DEBUG_PRINTING
#endif
#define DUMP_LAYOUT_LEVEL 9 // this turns on the dumping of each doucment's layout info
// this log level turns on the dumping of each document's layout info
#define DUMP_LAYOUT_LEVEL (static_cast<mozilla::LogLevel>(9))
#ifndef PR_PL
static mozilla::LazyLogModule gPrintingLog("printing")
@ -162,13 +163,15 @@ static nsresult DeleteUnselectedNodes(nsIDocument* aOrigDoc, nsIDocument* aDoc);
#ifdef EXTENDED_DEBUG_PRINTING
// Forward Declarations
static void DumpPrintObjectsListStart(const char * aStr, nsTArray<nsPrintObject*> * aDocList);
static void DumpPrintObjectsListStart(const char * aStr, const nsTArray<nsPrintObject*>& aDocList);
static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel= 0, FILE* aFD = nullptr);
static void DumpPrintObjectsTreeLayout(nsPrintObject * aPO,nsDeviceContext * aDC, int aLevel= 0, FILE * aFD = nullptr);
static void DumpPrintObjectsTreeLayout(const UniquePtr<nsPrintObject>& aPO,
nsDeviceContext * aDC, int aLevel = 0,
FILE * aFD = nullptr);
#define DUMP_DOC_LIST(_title) DumpPrintObjectsListStart((_title), mPrt->mPrintDocList);
#define DUMP_DOC_TREE DumpPrintObjectsTree(mPrt->mPrintObject.get());
#define DUMP_DOC_TREELAYOUT DumpPrintObjectsTreeLayout(mPrt->mPrintObject.get(), mPrt->mPrintDC);
#define DUMP_DOC_TREELAYOUT DumpPrintObjectsTreeLayout(mPrt->mPrintObject, mPrt->mPrintDC);
#else
#define DUMP_DOC_LIST(_title)
#define DUMP_DOC_TREE
@ -600,13 +603,18 @@ nsPrintJob::GetSeqFrameAndCountPages(nsIFrame*& aSeqFrame, int32_t& aCount)
// Foward decl for Debug Helper Functions
#ifdef EXTENDED_DEBUG_PRINTING
#ifdef XP_WIN
static int RemoveFilesInDir(const char * aDir);
static void GetDocTitleAndURL(nsPrintObject* aPO, char *& aDocStr, char *& aURLStr);
#endif
static void GetDocTitleAndURL(const UniquePtr<nsPrintObject>& aPO,
nsACString& aDocStr,
nsACString& aURLStr);
static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel, FILE* aFD);
static void DumpPrintObjectsList(nsTArray<nsPrintObject*> * aDocList);
static void RootFrameList(nsPresContext* aPresContext, FILE* out, int32_t aIndent);
static void DumpPrintObjectsList(const nsTArray<nsPrintObject*>& aDocList);
static void RootFrameList(nsPresContext* aPresContext, FILE* out,
const char* aPrefix);
static void DumpViews(nsIDocShell* aDocShell, FILE* out);
static void DumpLayoutData(char* aTitleStr, char* aURLStr,
static void DumpLayoutData(const char* aTitleStr, const char* aURLStr,
nsPresContext* aPresContext,
nsDeviceContext * aDC, nsIFrame * aRootFrame,
nsIDocShell * aDocShell, FILE* aFD);
@ -1664,7 +1672,7 @@ nsPrintJob::ReconstructAndReflow(bool doSetPixelScale)
#if defined(XP_WIN) && defined(EXTENDED_DEBUG_PRINTING)
// We need to clear all the output files here
// because they will be re-created with second reflow of the docs
if (kPrintingLogMod && kPrintingLogMod->level == DUMP_LAYOUT_LEVEL) {
if (MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
RemoveFilesInDir(".\\");
gDumpFileNameCnt = 0;
gDumpLOFileNameCnt = 0;
@ -2409,7 +2417,7 @@ nsPrintJob::ReflowPrintObject(const UniquePtr<nsPrintObject>& aPO)
NS_ENSURE_SUCCESS(rv, rv);
#ifdef EXTENDED_DEBUG_PRINTING
if (kPrintingLogMod && kPrintingLogMod->level == DUMP_LAYOUT_LEVEL) {
if (MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
nsAutoCString docStr;
nsAutoCString urlStr;
GetDocTitleAndURL(aPO, docStr, urlStr);
@ -2435,9 +2443,9 @@ nsPrintJob::ReflowPrintObject(const UniquePtr<nsPrintObject>& aPO)
} else {
printf("View is null!\n");
}
if (docShell) {
if (aPO->mDocShell) {
fprintf(fd, "--------------- All Views ----------------\n");
DumpViews(docShell, fd);
DumpViews(aPO->mDocShell, fd);
fprintf(fd, "---------------------------------------\n\n");
}
fclose(fd);
@ -2661,7 +2669,7 @@ nsPrintJob::DoPrint(const UniquePtr<nsPrintObject>& aPO)
nsAutoCString urlStr;
GetDocTitleAndURL(aPO, docStr, urlStr);
DumpLayoutData(docStr.get(), urlStr.get(), poPresContext,
printData->mPrintDocDC, rootFrame, docShell, nullptr);
printData->mPrintDC, rootFrame, aPO->mDocShell, nullptr);
}
#endif
@ -3639,7 +3647,8 @@ int RemoveFilesInDir(const char * aDir)
/** ---------------------------------------------------
* Dumps Frames for Printing
*/
static void RootFrameList(nsPresContext* aPresContext, FILE* out, int32_t aIndent)
static void RootFrameList(nsPresContext* aPresContext, FILE* out,
const char* aPrefix)
{
if (!aPresContext || !out)
return;
@ -3648,7 +3657,7 @@ static void RootFrameList(nsPresContext* aPresContext, FILE* out, int32_t aInden
if (shell) {
nsIFrame* frame = shell->FrameManager()->GetRootFrame();
if (frame) {
frame->List(aPresContext, out, aIndent);
frame->List(out, aPrefix);
}
}
}
@ -3676,7 +3685,7 @@ static void DumpFrames(FILE* out,
child->GetFrameName(tmp);
fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
bool isSelected;
if (NS_SUCCEEDED(child->IsVisibleForPainting(aPresContext, *aRendContext, true, &isSelected))) {
if (child->IsVisibleForPainting()) {
fprintf(out, " %p %s", child, isSelected?"VIS":"UVS");
nsRect rect = child->GetRect();
fprintf(out, "[%d,%d,%d,%d] ", rect.x, rect.y, rect.width, rect.height);
@ -3700,7 +3709,7 @@ DumpViews(nsIDocShell* aDocShell, FILE* out)
if (nullptr != aDocShell) {
fprintf(out, "docshell=%p \n", aDocShell);
nsIPresShell* shell = nsPrintJob::GetPresShellFor(aDocShell);
nsIPresShell* shell = aDocShell->GetPresShell();
if (shell) {
nsViewManager* vm = shell->GetViewManager();
if (vm) {
@ -3731,15 +3740,17 @@ DumpViews(nsIDocShell* aDocShell, FILE* out)
/** ---------------------------------------------------
* Dumps the Views and Frames
*/
void DumpLayoutData(char* aTitleStr,
char* aURLStr,
nsPresContext* aPresContext,
nsDeviceContext * aDC,
nsIFrame * aRootFrame,
nsIDocShekk * aDocShell,
void DumpLayoutData(const char* aTitleStr,
const char* aURLStr,
nsPresContext* aPresContext,
nsDeviceContext* aDC,
nsIFrame* aRootFrame,
nsIDocShell* aDocShell,
FILE* aFD = nullptr)
{
if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return;
if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
return;
}
if (aPresContext == nullptr || aDC == nullptr) {
return;
@ -3765,7 +3776,7 @@ void DumpLayoutData(char* aTitleStr,
fprintf(fd, "--------------- Frames ----------------\n");
//RefPtr<gfxContext> renderingContext =
// aDC->CreateRenderingContext();
RootFrameList(aPresContext, fd, 0);
RootFrameList(aPresContext, fd, "");
//DumpFrames(fd, aPresContext, renderingContext, aRootFrame, 0);
fprintf(fd, "---------------------------------------\n\n");
fprintf(fd, "--------------- Views From Root Frame----------------\n");
@ -3787,18 +3798,16 @@ void DumpLayoutData(char* aTitleStr,
}
//-------------------------------------------------------------
static void DumpPrintObjectsList(nsTArray<nsPrintObject*> * aDocList)
static void DumpPrintObjectsList(const nsTArray<nsPrintObject*>& aDocList)
{
if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return;
NS_ASSERTION(aDocList, "Pointer is null!");
if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
return;
}
const char types[][3] = {"DC", "FR", "IF", "FS"};
PR_PL(("Doc List\n***************************************************\n"));
PR_PL(("T P A H PO DocShell Seq Page Root Page# Rect\n"));
int32_t cnt = aDocList->Length();
for (int32_t i=0;i<cnt;i++) {
nsPrintObject* po = aDocList->ElementAt(i);
for (nsPrintObject* po : aDocList) {
NS_ASSERTION(po, "nsPrintObject can't be null!");
nsIFrame* rootFrame = nullptr;
if (po->mPresShell) {
@ -3812,16 +3821,18 @@ static void DumpPrintObjectsList(nsTArray<nsPrintObject*> * aDocList)
}
}
PR_PL(("%s %d %d %d %p %p %p %p %p %d %d,%d,%d,%d\n", types[po->mFrameType],
po->IsPrintable(), po->mPrintAsIs, po->mHasBeenPrinted, po, po->mDocShell.get(), po->mSeqFrame,
po->mPageFrame, rootFrame, po->mPageNum, po->mRect.x, po->mRect.y, po->mRect.width, po->mRect.height));
PR_PL(("%s %d %d %d %p %p %p\n", types[po->mFrameType],
po->IsPrintable(), po->mPrintAsIs, po->mHasBeenPrinted, po,
po->mDocShell.get(), rootFrame));
}
}
//-------------------------------------------------------------
static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel, FILE* aFD)
{
if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return;
if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
return;
}
NS_ASSERTION(aPO, "Pointer is null!");
@ -3831,13 +3842,11 @@ static void DumpPrintObjectsTree(nsPrintObject * aPO, int aLevel, FILE* aFD)
fprintf(fd, "DocTree\n***************************************************\n");
fprintf(fd, "T PO DocShell Seq Page Page# Rect\n");
}
int32_t cnt = aPO->mKids.Length();
for (int32_t i=0;i<cnt;i++) {
nsPrintObject* po = aPO->mKids.ElementAt(i);
for (const auto& po : aPO->mKids) {
NS_ASSERTION(po, "nsPrintObject can't be null!");
for (int32_t k=0;k<aLevel;k++) fprintf(fd, " ");
fprintf(fd, "%s %p %p %p %p %d %d,%d,%d,%d\n", types[po->mFrameType], po, po->mDocShell.get(), po->mSeqFrame,
po->mPageFrame, po->mPageNum, po->mRect.x, po->mRect.y, po->mRect.width, po->mRect.height);
fprintf(fd, "%s %p %p\n", types[po->mFrameType], po.get(),
po->mDocShell.get());
}
}
@ -3848,19 +3857,19 @@ static void GetDocTitleAndURL(const UniquePtr<nsPrintObject>& aPO,
{
nsAutoString docTitleStr;
nsAutoString docURLStr;
nsPrintJob::GetDisplayTitleAndURL(aPO,
docTitleStr, docURLStr,
nsPrintJob::eDocTitleDefURLDoc);
GetDocumentTitleAndURL(aPO->mDocument, docTitleStr, docURLStr);
aDocStr = NS_ConvertUTF16toUTF8(docTitleStr);
aURLStr = NS_ConvertUTF16toUTF8(docURLStr);
}
//-------------------------------------------------------------
static void DumpPrintObjectsTreeLayout(nsPrintObject * aPO,
static void DumpPrintObjectsTreeLayout(const UniquePtr<nsPrintObject>& aPO,
nsDeviceContext * aDC,
int aLevel, FILE * aFD)
{
if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return;
if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
return;
}
NS_ASSERTION(aPO, "Pointer is null!");
NS_ASSERTION(aDC, "Pointer is null!");
@ -3881,8 +3890,8 @@ static void DumpPrintObjectsTreeLayout(nsPrintObject * aPO,
rootFrame = aPO->mPresShell->FrameManager()->GetRootFrame();
}
for (int32_t k=0;k<aLevel;k++) fprintf(fd, " ");
fprintf(fd, "%s %p %p %p %p %d %d,%d,%d,%d\n", types[aPO->mFrameType], aPO, aPO->mDocShell.get(), aPO->mSeqFrame,
aPO->mPageFrame, aPO->mPageNum, aPO->mRect.x, aPO->mRect.y, aPO->mRect.width, aPO->mRect.height);
fprintf(fd, "%s %p %p\n", types[aPO->mFrameType], aPO.get(),
aPO->mDocShell.get());
if (aPO->IsPrintable()) {
nsAutoCString docStr;
nsAutoCString urlStr;
@ -3891,9 +3900,7 @@ static void DumpPrintObjectsTreeLayout(nsPrintObject * aPO,
}
fprintf(fd, "<***************************************************>\n");
int32_t cnt = aPO->mKids.Length();
for (int32_t i=0;i<cnt;i++) {
nsPrintObject* po = aPO->mKids.ElementAt(i);
for (const auto& po : aPO->mKids) {
NS_ASSERTION(po, "nsPrintObject can't be null!");
DumpPrintObjectsTreeLayout(po, aDC, aLevel+1, fd);
}
@ -3904,25 +3911,19 @@ static void DumpPrintObjectsTreeLayout(nsPrintObject * aPO,
}
//-------------------------------------------------------------
static void DumpPrintObjectsListStart(const char * aStr, nsTArray<nsPrintObject*> * aDocList)
static void DumpPrintObjectsListStart(const char * aStr,
const nsTArray<nsPrintObject*>& aDocList)
{
if (!kPrintingLogMod || kPrintingLogMod->level != DUMP_LAYOUT_LEVEL) return;
if (!MOZ_LOG_TEST(gPrintingLog, DUMP_LAYOUT_LEVEL)) {
return;
}
NS_ASSERTION(aStr, "Pointer is null!");
NS_ASSERTION(aDocList, "Pointer is null!");
PR_PL(("%s\n", aStr));
DumpPrintObjectsList(aDocList);
}
#define DUMP_DOC_LIST(_title) DumpPrintObjectsListStart((_title), mPrt->mPrintDocList);
#define DUMP_DOC_TREE DumpPrintObjectsTree(mPrt->mPrintObject.get());
#define DUMP_DOC_TREELAYOUT DumpPrintObjectsTreeLayout(mPrt->mPrintObject.get(), mPrt->mPrintDC);
#else
#define DUMP_DOC_LIST(_title)
#define DUMP_DOC_TREE
#define DUMP_DOC_TREELAYOUT
#endif
//---------------------------------------------------------------

Просмотреть файл

@ -3254,12 +3254,11 @@ PeerConnectionImpl::IceGatheringStateChange(
return;
}
WrappableJSErrorResult rv;
RUN_ON_THREAD(mThread,
WrapRunnable(pco,
&PeerConnectionObserver::OnStateChange,
PCObserverStateType::IceGatheringState,
rv, static_cast<JSCompartment*>(nullptr)),
NS_DISPATCH_NORMAL);
mThread->Dispatch(WrapRunnable(pco,
&PeerConnectionObserver::OnStateChange,
PCObserverStateType::IceGatheringState,
rv, static_cast<JSCompartment*>(nullptr)),
NS_DISPATCH_NORMAL);
if (mIceGatheringState == PCImplIceGatheringState::Complete) {
SendLocalIceCandidateToContent(0, "", "");

Просмотреть файл

@ -159,6 +159,11 @@ public final class GeckoSessionSettings implements Parcelable {
return mBundle;
}
@Override
public String toString() {
return mBundle.toString();
}
private <T> boolean valueChangedLocked(final Key<T> key, T value) {
if (key.initOnly && mSession != null && mSession.isOpen()) {
throw new IllegalStateException("Read-only property");

Просмотреть файл

@ -123,9 +123,12 @@ public class GeckoViewActivity extends Activity {
}
private void loadSettings(final Intent intent) {
mGeckoView.getSettings().setBoolean(
final GeckoSessionSettings settings = mGeckoView.getSettings();
settings.setBoolean(
GeckoSessionSettings.USE_REMOTE_DEBUGGER,
intent.getBooleanExtra(USE_REMOTE_DEBUGGER_EXTRA, false));
Log.i(LOGTAG, "Load with settings " + settings);
}
@Override

Просмотреть файл

@ -37,6 +37,21 @@ public:
// Suspend()/Resume() functions.
virtual nsresult SuspendMessageDiversion() = 0;
virtual nsresult ResumeMessageDiversion() = 0;
// Cancel an ongoing diversion by using IPC to invoke Cancel() in the child.
// This is necessary because most of the channel's state machine is suspended
// during diversion, so an explicit action must be taken to interrupt the
// diversion process so cancellation can be fully processed.
//
// Historically, diversions were assumed to be shortlived, where it was merely
// a question of diverting some small amount of network traffic back to the
// parent. However, Service Worker child interception made it possible for
// the data to entirely be sourced from the child, which makes diversion
// potentially long-lived. Especially when large files are involved.
//
// This mechanism is expected to be removed when ServiceWorkers move from
// child intercept to parent intercept (in the short to medium term).
virtual nsresult CancelDiversion() = 0;
};
} // namespace net

Просмотреть файл

@ -737,6 +737,15 @@ FTPChannelParent::ResumeMessageDiversion()
return NS_OK;
}
nsresult
FTPChannelParent::CancelDiversion()
{
// Only HTTP channels can have child-process-sourced-data that's long-lived
// so this isn't currently relevant for FTP channels and there is nothing to
// do.
return NS_OK;
}
void
FTPChannelParent::DivertTo(nsIStreamListener *aListener)
{

Просмотреть файл

@ -54,6 +54,8 @@ public:
nsresult SuspendForDiversion() override;
nsresult SuspendMessageDiversion() override;
nsresult ResumeMessageDiversion() override;
nsresult CancelDiversion() override;
// Calls OnStartRequest for "DivertTo" listener, then notifies child channel
// that it should divert OnDataAvailable and OnStopRequest calls to this

Просмотреть файл

@ -615,8 +615,6 @@ HttpChannelChild::OnStartRequest(const nsresult& channelStatus,
DoOnStartRequest(this, mListenerContext);
}
namespace {
class SyntheticDiversionListener final : public nsIStreamListener
{
RefPtr<HttpChannelChild> mChannel;
@ -643,7 +641,10 @@ public:
OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
nsresult aStatus) override
{
mChannel->SendDivertOnStopRequest(aStatus);
if (mChannel->mIPCOpen) {
mChannel->SendDivertOnStopRequest(aStatus);
mChannel->SendDivertComplete();
}
return NS_OK;
}
@ -652,6 +653,11 @@ public:
nsIInputStream* aInputStream, uint64_t aOffset,
uint32_t aCount) override
{
if (!mChannel->mIPCOpen) {
aRequest->Cancel(NS_ERROR_ABORT);
return NS_ERROR_ABORT;
}
nsAutoCString data;
nsresult rv = NS_ConsumeStream(aInputStream, aCount, data);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -668,8 +674,6 @@ public:
NS_IMPL_ISUPPORTS(SyntheticDiversionListener, nsIStreamListener);
} // anonymous namespace
void
HttpChannelChild::DoOnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
{
@ -1884,7 +1888,11 @@ HttpChannelChild::FlushedForDiversion()
// received from the parent channel, nor dequeued from the ChannelEventQueue.
mFlushedForDiversion = true;
SendDivertComplete();
// If we're synthesized, it's up to the SyntheticDiversionListener to invoke
// SendDivertComplete after it has sent the DivertOnStopRequestMessage.
if (!mSynthesizedResponse) {
SendDivertComplete();
}
}
void
@ -3860,6 +3868,21 @@ HttpChannelChild::RecvAttachStreamFilter(Endpoint<extensions::PStreamFilterParen
return IPC_OK();
}
mozilla::ipc::IPCResult
HttpChannelChild::RecvCancelDiversion()
{
MOZ_ASSERT(NS_IsMainThread());
// This method is a very special case for cancellation of a diverted
// intercepted channel. Normally we would go through the mEventQ in order to
// serialize event execution in the face of sync XHR and now background
// channels. However, similar to how CancelOnMainThread describes, Cancel()
// pre-empts everything. (And frankly, we want this stack frame on the stack
// if a crash happens.)
Cancel(NS_ERROR_ABORT);
return IPC_OK();
}
void
HttpChannelChild::ActorDestroy(ActorDestroyReason aWhy)
{

Просмотреть файл

@ -47,6 +47,7 @@ namespace net {
class HttpBackgroundChannelChild;
class InterceptedChannelContent;
class InterceptStreamListener;
class SyntheticDiversionListener;
class HttpChannelChild final : public PHttpChannelChild
, public HttpBaseChannel
@ -168,6 +169,8 @@ protected:
mozilla::ipc::IPCResult RecvAttachStreamFilter(Endpoint<extensions::PStreamFilterParent>&& aEndpoint) override;
mozilla::ipc::IPCResult RecvCancelDiversion() override;
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
MOZ_MUST_USE bool
@ -472,6 +475,7 @@ private:
friend class HttpAsyncAborter<HttpChannelChild>;
friend class InterceptStreamListener;
friend class InterceptedChannelContent;
friend class SyntheticDiversionListener;
friend class HttpBackgroundChannelChild;
friend class NeckoTargetChannelEvent<HttpChannelChild>;
};

Просмотреть файл

@ -2001,6 +2001,16 @@ HttpChannelParent::ResumeMessageDiversion()
return NS_OK;
}
nsresult
HttpChannelParent::CancelDiversion()
{
LOG(("HttpChannelParent::CancelDiversion [this=%p]", this));
if (!mIPCClosed) {
Unused << SendCancelDiversion();
}
return NS_OK;
}
/* private, supporting function for ADivertableParentChannel */
nsresult
HttpChannelParent::ResumeForDiversion()

Просмотреть файл

@ -83,6 +83,7 @@ public:
MOZ_MUST_USE nsresult SuspendForDiversion() override;
MOZ_MUST_USE nsresult SuspendMessageDiversion() override;
MOZ_MUST_USE nsresult ResumeMessageDiversion() override;
MOZ_MUST_USE nsresult CancelDiversion() override;
// Calls OnStartRequest for "DivertTo" listener, then notifies child channel
// that it should divert OnDataAvailable and OnStopRequest calls to this

Просмотреть файл

@ -33,6 +33,7 @@ InterceptedHttpChannel::InterceptedHttpChannel(PRTime aCreationTime,
, mResumeStartPos(0)
, mSynthesizedOrReset(Invalid)
, mCallingStatusAndProgress(false)
, mDiverting(false)
{
// Pre-set the creation and AsyncOpen times based on the original channel
// we are intercepting. We don't want our extra internal redirect to mask
@ -501,6 +502,15 @@ InterceptedHttpChannel::Cancel(nsresult aStatus)
mStatus = aStatus;
}
// Everything is suspended during diversion until it completes. Since the
// intercepted channel could be a long-running stream, we need to request that
// cancellation be triggered in the child, completing the diversion and
// allowing cancellation to run to completion.
if (mDiverting) {
Unused << mParentChannel->CancelDiversion();
// (We want the pump to be canceled as well, so don't directly return.)
}
if (mPump) {
return mPump->Cancel(mStatus);
}
@ -1114,6 +1124,7 @@ InterceptedHttpChannel::MessageDiversionStarted(ADivertableParentChannel* aParen
{
MOZ_ASSERT(!mParentChannel);
mParentChannel = aParentChannel;
mDiverting = true;
uint32_t suspendCount = mSuspendCount;
while(suspendCount--) {
mParentChannel->SuspendMessageDiversion();
@ -1126,6 +1137,7 @@ InterceptedHttpChannel::MessageDiversionStop()
{
MOZ_ASSERT(mParentChannel);
mParentChannel = nullptr;
mDiverting = false;
return NS_OK;
}

Просмотреть файл

@ -94,6 +94,7 @@ private:
Reset
} mSynthesizedOrReset;
Atomic<bool> mCallingStatusAndProgress;
bool mDiverting;
InterceptedHttpChannel(PRTime aCreationTime,
const TimeStamp& aCreationTimestamp,

Просмотреть файл

@ -149,6 +149,9 @@ child:
async AttachStreamFilter(Endpoint<PStreamFilterParent> aEndpoint);
// See ADivertableParentChannel::CancelDiversion
async CancelDiversion();
both:
// After receiving this message, the parent also calls
// SendFinishInterceptedRedirect, and makes sure not to send any more messages

Просмотреть файл

@ -135,7 +135,11 @@ EXTRA_COMPONENTS += [
]
if CONFIG['OS_TARGET'] == 'Darwin':
if CONFIG['HOST_MAJOR_VERSION'] >= '15':
if not CONFIG['HOST_MAJOR_VERSION']:
DEFINES.update(
HAS_CONNECTX=True,
)
elif CONFIG['HOST_MAJOR_VERSION'] >= '15':
DEFINES.update(
HAS_CONNECTX=True,
)

Просмотреть файл

@ -192,11 +192,15 @@ nsMenuItemIconX::GetIconURI(nsIURI** aIconURI)
// Return NS_ERROR_FAILURE if the image region is invalid so the image
// is not drawn, and behavior is similar to XUL menus.
if (r.X() < 0 || r.Y() < 0 || r.IsEmpty()) {
if (r.X() < 0 || r.Y() < 0 || r.Width() < 0 || r.Height() < 0) {
return NS_ERROR_FAILURE;
}
mImageRegionRect = r.ToNearestPixels(mozilla::AppUnitsPerCSSPixel());
// 'auto' is represented by a [0, 0, 0, 0] rect. Only set mImageRegionRect
// if we have some other value.
if (!r.IsEmpty()) {
mImageRegionRect = r.ToNearestPixels(mozilla::AppUnitsPerCSSPixel());
}
}
return NS_OK;

Просмотреть файл

@ -1365,6 +1365,12 @@ GfxInfo::GetGfxDriverInfo()
nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions, "FEATURE_FAILURE_BUG_1359416");
// bug 1419264
APPEND_TO_DRIVER_BLOCKLIST(OperatingSystem::Windows7,
(nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
nsIGfxInfo::FEATURE_ADVANCED_LAYERS, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
DRIVER_GREATER_THAN_OR_EQUAL, V(23,21,13,8813),
"FEATURE_FAILURE_BUG_1419264", "Windows 10");
}
return *mDriverInfo;
}