зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1128959 - Implement the WHATWG Streams spec - part 16 - report stream errors during consumption r=bkelly
This commit is contained in:
Родитель
76f69afc03
Коммит
6e330b0360
|
@ -41,6 +41,8 @@
|
|||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/CustomElementRegistry.h"
|
||||
#include "mozilla/dom/DocumentFragment.h"
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
#include "mozilla/dom/DOMExceptionBinding.h"
|
||||
#include "mozilla/dom/DOMTypes.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/FileSystemSecurity.h"
|
||||
|
@ -10750,3 +10752,73 @@ nsContentUtils::IsOverridingWindowName(const nsAString& aName)
|
|||
!aName.LowerCaseEqualsLiteral("_parent") &&
|
||||
!aName.LowerCaseEqualsLiteral("_self");
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsContentUtils::ExtractErrorValues(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
nsACString& aSourceSpecOut,
|
||||
uint32_t* aLineOut,
|
||||
uint32_t* aColumnOut,
|
||||
nsString& aMessageOut)
|
||||
{
|
||||
MOZ_ASSERT(aLineOut);
|
||||
MOZ_ASSERT(aColumnOut);
|
||||
|
||||
if (aValue.isObject()) {
|
||||
JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
|
||||
RefPtr<dom::DOMException> domException;
|
||||
|
||||
// Try to process as an Error object. Use the file/line/column values
|
||||
// from the Error as they will be more specific to the root cause of
|
||||
// the problem.
|
||||
JSErrorReport* err = obj ? JS_ErrorFromException(aCx, obj) : nullptr;
|
||||
if (err) {
|
||||
// Use xpc to extract the error message only. We don't actually send
|
||||
// this report anywhere.
|
||||
RefPtr<xpc::ErrorReport> report = new xpc::ErrorReport();
|
||||
report->Init(err,
|
||||
"<unknown>", // toString result
|
||||
false, // chrome
|
||||
0); // window ID
|
||||
|
||||
if (!report->mFileName.IsEmpty()) {
|
||||
CopyUTF16toUTF8(report->mFileName, aSourceSpecOut);
|
||||
*aLineOut = report->mLineNumber;
|
||||
*aColumnOut = report->mColumn;
|
||||
}
|
||||
aMessageOut.Assign(report->mErrorMsg);
|
||||
}
|
||||
|
||||
// Next, try to unwrap the rejection value as a DOMException.
|
||||
else if(NS_SUCCEEDED(UNWRAP_OBJECT(DOMException, obj, domException))) {
|
||||
|
||||
nsAutoString filename;
|
||||
domException->GetFilename(aCx, filename);
|
||||
if (!filename.IsEmpty()) {
|
||||
CopyUTF16toUTF8(filename, aSourceSpecOut);
|
||||
*aLineOut = domException->LineNumber(aCx);
|
||||
*aColumnOut = domException->ColumnNumber();
|
||||
}
|
||||
|
||||
domException->GetName(aMessageOut);
|
||||
aMessageOut.AppendLiteral(": ");
|
||||
|
||||
nsAutoString message;
|
||||
domException->GetMessageMoz(message);
|
||||
aMessageOut.Append(message);
|
||||
}
|
||||
}
|
||||
|
||||
// If we could not unwrap a specific error type, then perform default safe
|
||||
// string conversions on primitives. Objects will result in "[Object]"
|
||||
// unfortunately.
|
||||
if (aMessageOut.IsEmpty()) {
|
||||
nsAutoJSString jsString;
|
||||
if (jsString.init(aCx, aValue)) {
|
||||
aMessageOut = jsString;
|
||||
} else {
|
||||
JS_ClearPendingException(aCx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1077,6 +1077,11 @@ public:
|
|||
|
||||
static bool PrefetchPreloadEnabled(nsIDocShell* aDocShell);
|
||||
|
||||
static void
|
||||
ExtractErrorValues(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
||||
nsACString& aSourceSpecOut, uint32_t *aLineOut,
|
||||
uint32_t *aColumnOut, nsString& aMessageOut);
|
||||
|
||||
/**
|
||||
* Fill (with the parameters given) the localized string named |aKey| in
|
||||
* properties file |aFile|.
|
||||
|
|
|
@ -211,7 +211,7 @@ public:
|
|||
// reporting way.
|
||||
//
|
||||
// Exceptions generated when reading from the ReadableStream are directly sent
|
||||
// to the Console (NOTE FOR THE REVIEWER: this is part of patch 16)
|
||||
// to the Console.
|
||||
void
|
||||
SetBodyUsed(JSContext* aCx, ErrorResult& aRv);
|
||||
|
||||
|
|
|
@ -7,6 +7,11 @@
|
|||
#include "FetchStreamReader.h"
|
||||
#include "InternalResponse.h"
|
||||
#include "mozilla/dom/PromiseBinding.h"
|
||||
#include "mozilla/SystemGroup.h"
|
||||
#include "mozilla/TaskCategory.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -297,8 +302,57 @@ void
|
|||
FetchStreamReader::RejectedCallback(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
ReportErrorToConsole(aCx, aValue);
|
||||
CloseAndRelease(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
void
|
||||
FetchStreamReader::ReportErrorToConsole(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
nsCString sourceSpec;
|
||||
uint32_t line = 0;
|
||||
uint32_t column = 0;
|
||||
nsString valueString;
|
||||
|
||||
nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line,
|
||||
&column, valueString);
|
||||
|
||||
nsTArray<nsString> params;
|
||||
params.AppendElement(valueString);
|
||||
|
||||
RefPtr<ConsoleReportCollector> reporter = new ConsoleReportCollector();
|
||||
reporter->AddConsoleReport(nsIScriptError::errorFlag,
|
||||
NS_LITERAL_CSTRING("ReadableStreamReader.read"),
|
||||
nsContentUtils::eDOM_PROPERTIES,
|
||||
sourceSpec, line, column,
|
||||
NS_LITERAL_CSTRING("ReadableStreamReadingFailed"),
|
||||
params);
|
||||
|
||||
uint64_t innerWindowId = 0;
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
|
||||
if (window) {
|
||||
innerWindowId = window->WindowID();
|
||||
}
|
||||
reporter->FlushReportsToConsole(innerWindowId);
|
||||
return;
|
||||
}
|
||||
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
|
||||
if (workerPrivate) {
|
||||
innerWindowId = workerPrivate->WindowID();
|
||||
}
|
||||
|
||||
RefPtr<Runnable> r = NS_NewRunnableFunction(
|
||||
"FetchStreamReader::ReportErrorToConsole",
|
||||
[reporter, innerWindowId] () {
|
||||
reporter->FlushReportsToConsole(innerWindowId);
|
||||
});
|
||||
|
||||
workerPrivate->DispatchToMainThread(r.forget());
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
|
|
@ -55,6 +55,9 @@ private:
|
|||
nsresult
|
||||
WriteBuffer();
|
||||
|
||||
void
|
||||
ReportErrorToConsole(JSContext* aCx, JS::Handle<JS::Value> aValue);
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
nsCOMPtr<nsIEventTarget> mOwningEventTarget;
|
||||
|
||||
|
|
|
@ -345,3 +345,5 @@ ScriptSourceMalformed=<script> source URI is malformed: “%S”.
|
|||
ScriptSourceNotAllowed=<script> source URI is not allowed in this document: “%S”.
|
||||
# LOCALIZATION NOTE: %1$S is the invalid property value and %2$S is the property name.
|
||||
InvalidKeyframePropertyValue=Keyframe property value “%1$S” is invalid according to the syntax for “%2$S”.
|
||||
# LOCALIZATION NOTE: Do not translate "ReadableStream".
|
||||
ReadableStreamReadingFailed=Failed to read data from the ReadableStream: “%S”.
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "ServiceWorkerEvents.h"
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIConsoleReportCollector.h"
|
||||
#include "nsIHttpChannelInternal.h"
|
||||
#include "nsINetworkInterceptController.h"
|
||||
|
@ -30,8 +31,6 @@
|
|||
#include "mozilla/LoadInfo.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/BodyUtil.h"
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
#include "mozilla/dom/DOMExceptionBinding.h"
|
||||
#include "mozilla/dom/FetchEventBinding.h"
|
||||
#include "mozilla/dom/MessagePort.h"
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
|
@ -414,76 +413,6 @@ void RespondWithCopyComplete(void* aClosure, nsresult aStatus)
|
|||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void
|
||||
ExtractErrorValues(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
||||
nsACString& aSourceSpecOut, uint32_t *aLineOut,
|
||||
uint32_t *aColumnOut, nsString& aMessageOut)
|
||||
{
|
||||
MOZ_ASSERT(aLineOut);
|
||||
MOZ_ASSERT(aColumnOut);
|
||||
|
||||
if (aValue.isObject()) {
|
||||
JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
|
||||
RefPtr<DOMException> domException;
|
||||
|
||||
// Try to process as an Error object. Use the file/line/column values
|
||||
// from the Error as they will be more specific to the root cause of
|
||||
// the problem.
|
||||
JSErrorReport* err = obj ? JS_ErrorFromException(aCx, obj) : nullptr;
|
||||
if (err) {
|
||||
// Use xpc to extract the error message only. We don't actually send
|
||||
// this report anywhere.
|
||||
RefPtr<xpc::ErrorReport> report = new xpc::ErrorReport();
|
||||
report->Init(err,
|
||||
"<unknown>", // toString result
|
||||
false, // chrome
|
||||
0); // window ID
|
||||
|
||||
if (!report->mFileName.IsEmpty()) {
|
||||
CopyUTF16toUTF8(report->mFileName, aSourceSpecOut);
|
||||
*aLineOut = report->mLineNumber;
|
||||
*aColumnOut = report->mColumn;
|
||||
}
|
||||
aMessageOut.Assign(report->mErrorMsg);
|
||||
}
|
||||
|
||||
// Next, try to unwrap the rejection value as a DOMException.
|
||||
else if(NS_SUCCEEDED(UNWRAP_OBJECT(DOMException, obj, domException))) {
|
||||
|
||||
nsAutoString filename;
|
||||
domException->GetFilename(aCx, filename);
|
||||
if (!filename.IsEmpty()) {
|
||||
CopyUTF16toUTF8(filename, aSourceSpecOut);
|
||||
*aLineOut = domException->LineNumber(aCx);
|
||||
*aColumnOut = domException->ColumnNumber();
|
||||
}
|
||||
|
||||
domException->GetName(aMessageOut);
|
||||
aMessageOut.AppendLiteral(": ");
|
||||
|
||||
nsAutoString message;
|
||||
domException->GetMessageMoz(message);
|
||||
aMessageOut.Append(message);
|
||||
}
|
||||
}
|
||||
|
||||
// If we could not unwrap a specific error type, then perform default safe
|
||||
// string conversions on primitives. Objects will result in "[Object]"
|
||||
// unfortunately.
|
||||
if (aMessageOut.IsEmpty()) {
|
||||
nsAutoJSString jsString;
|
||||
if (jsString.init(aCx, aValue)) {
|
||||
aMessageOut = jsString;
|
||||
} else {
|
||||
JS_ClearPendingException(aCx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class MOZ_STACK_CLASS AutoCancel
|
||||
{
|
||||
RefPtr<RespondWithHandler> mOwner;
|
||||
|
@ -606,7 +535,8 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
|
|||
uint32_t line = 0;
|
||||
uint32_t column = 0;
|
||||
nsString valueString;
|
||||
ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, valueString);
|
||||
nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
|
||||
valueString);
|
||||
|
||||
autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column,
|
||||
NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"),
|
||||
|
@ -621,7 +551,8 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
|
|||
uint32_t line = 0;
|
||||
uint32_t column = 0;
|
||||
nsString valueString;
|
||||
ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, valueString);
|
||||
nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
|
||||
valueString);
|
||||
|
||||
autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column,
|
||||
NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"),
|
||||
|
@ -771,7 +702,8 @@ RespondWithHandler::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
|
|||
|
||||
mInterceptedChannel->SetFinishResponseStart(TimeStamp::Now());
|
||||
|
||||
ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, valueString);
|
||||
nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
|
||||
valueString);
|
||||
|
||||
::AsyncLog(mInterceptedChannel, sourceSpec, line, column,
|
||||
NS_LITERAL_CSTRING("InterceptionRejectedResponseWithURL"),
|
||||
|
@ -917,7 +849,8 @@ public:
|
|||
nsCString spec;
|
||||
uint32_t line = 0;
|
||||
uint32_t column = 0;
|
||||
ExtractErrorValues(aCx, aValue, spec, &line, &column, mRejectValue);
|
||||
nsContentUtils::ExtractErrorValues(aCx, aValue, spec, &line, &column,
|
||||
mRejectValue);
|
||||
|
||||
// only use the extracted location if we found one
|
||||
if (!spec.IsEmpty()) {
|
||||
|
|
|
@ -149,6 +149,14 @@ fetchXHR('readable-stream-with-exception.txt', function(xhr) {
|
|||
finish();
|
||||
});
|
||||
|
||||
fetchXHR('readable-stream-with-exception2.txt', function(xhr) {
|
||||
my_ok(false, "This should not be called!");
|
||||
finish();
|
||||
}, function() {
|
||||
my_ok(true, "The exception has been correctly handled!");
|
||||
finish();
|
||||
});
|
||||
|
||||
fetchXHR('readable-stream-already-consumed.txt', function(xhr) {
|
||||
my_ok(false, "This should not be called!");
|
||||
finish();
|
||||
|
|
|
@ -137,6 +137,22 @@ onfetch = function(ev) {
|
|||
));
|
||||
}
|
||||
|
||||
else if (ev.request.url.includes("readable-stream-with-exception2.txt")) {
|
||||
ev.respondWith(
|
||||
new Response(
|
||||
new ReadableStream({
|
||||
_controller: null,
|
||||
_count: 0,
|
||||
|
||||
start(controller) { this._controller = controller; },
|
||||
pull() {
|
||||
if (++this._count == 5) { throw "EXCEPTION 2!"; }
|
||||
this._controller.enqueue(new Uint8Array([this._count]));
|
||||
}
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
else if (ev.request.url.includes("readable-stream-already-consumed.txt")) {
|
||||
let r = new Response(
|
||||
new ReadableStream({
|
||||
|
|
Загрузка…
Ссылка в новой задаче