[sourcemaps] Report error message on resourceLoad error
This CL adds reporting of a human readable string which includes the
internal error code if a source map load fails. We could think about
providing a web.dev page with an explanation of the internal error
codes. In any case, this fine-grained distinction should be an
improvement for referencing errors on sites like stack-overflow. If
all else fails, the internal error code can be looked up in
net_error_list.h in the chromium sources.
Screenshot: https://imgur.com/a/dWjG1GS
Bug: chromium:1030746
Change-Id: I04ddbc3f98ab2d6c54fb067a89408cbb60217477
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/1998761
Reviewed-by: Peter Marshall <[email protected]>
Reviewed-by: Tim van der Lippe <[email protected]>
Commit-Queue: Sigurd Schneider <[email protected]>
diff --git a/front_end/Tests.js b/front_end/Tests.js
index c9e7139..cc74a7b 100644
--- a/front_end/Tests.js
+++ b/front_end/Tests.js
@@ -1397,8 +1397,8 @@
return new Promise(fulfill => {
Host.ResourceLoader.load(url, headers, callback);
- function callback(statusCode, headers, content) {
- test.assertEquals(expectedStatus, statusCode);
+ function callback(success, headers, content, errorDescription) {
+ test.assertEquals(expectedStatus, errorDescription.statusCode);
const headersArray = [];
for (const name in headers) {
diff --git a/front_end/externs.js b/front_end/externs.js
index cbe1f23..46b5282 100644
--- a/front_end/externs.js
+++ b/front_end/externs.js
@@ -1503,7 +1503,11 @@
/** @typedef
{{
statusCode: number,
- headers: (!Object.<string, string>|undefined)
+ headers: (!Object.<string, string>|undefined),
+ netError: (number|undefined),
+ netErrorName: (string|undefined),
+ urlValid: (boolean|undefined),
+ messageOverride: (string|undefined)
}} */
InspectorFrontendHostAPI.LoadNetworkResourceResult;
diff --git a/front_end/host/ResourceLoader.js b/front_end/host/ResourceLoader.js
index 9f33bbc..6e6b79b 100644
--- a/front_end/host/ResourceLoader.js
+++ b/front_end/host/ResourceLoader.js
@@ -36,30 +36,118 @@
_boundStreams[id].write(chunk);
};
+/** @typedef
+{{
+ statusCode: number,
+ netError: (number|undefined),
+ netErrorName: (string|undefined),
+ urlValid: (boolean|undefined),
+ message: (string|undefined)
+}} */
+ResourceLoader.LoadErrorDescription;
+
/**
* @param {string} url
* @param {?Object.<string, string>} headers
- * @param {function(number, !Object.<string, string>, string, number)} callback
+ * @param {function(boolean, !Object.<string, string>, string, !ResourceLoader.LoadErrorDescription)} callback
*/
export function load(url, headers, callback) {
const stream = new Common.StringOutputStream();
loadAsStream(url, headers, stream, mycallback);
/**
- * @param {number} statusCode
+ * @param {boolean} success
* @param {!Object.<string, string>} headers
- * @param {number} netError
+ * @param {!ResourceLoader.LoadErrorDescription} errorDescription
*/
- function mycallback(statusCode, headers, netError) {
- callback(statusCode, headers, stream.data(), netError);
+ function mycallback(success, headers, errorDescription) {
+ callback(success, headers, stream.data(), errorDescription);
}
}
/**
+ * @param {number} netError
+ * Keep this function in sync with `net_error_list.h` on the Chromium side.
+ * @returns {string}
+ */
+function getNetErrorCategory(netError) {
+ if (netError > -100) {
+ return ls`System error`;
+ }
+ if (netError > -200) {
+ return ls`Connection error`;
+ }
+ if (netError > -300) {
+ return ls`Certificate error`;
+ }
+ if (netError > -400) {
+ return ls`HTTP error`;
+ }
+ if (netError > -500) {
+ return ls`Cache error`;
+ }
+ if (netError > -600) {
+ return ls`Signed Exchange error`;
+ }
+ if (netError > -700) {
+ return ls`FTP error`;
+ }
+ if (netError > -800) {
+ return ls`Certificate manager error`;
+ }
+ if (netError > -900) {
+ return ls`DNS resolver error`;
+ }
+ return ls`Unknown error`;
+}
+
+/**
+ * @param {number} netError
+ * @returns {boolean}
+ */
+function isHTTPError(netError) {
+ return netError <= -300 && netError > -400;
+}
+
+/**
+ * @param {!InspectorFrontendHostAPI.LoadNetworkResourceResult} response
+ * @returns {!{success:boolean, description: !ResourceLoader.LoadErrorDescription}}
+ */
+function createErrorMessageFromResponse(response) {
+ const {statusCode, netError, netErrorName, urlValid, messageOverride} = response;
+ let message = '';
+ const success = statusCode >= 200 && statusCode < 300;
+ if (typeof messageOverride === 'string') {
+ message = messageOverride;
+ } else if (!success) {
+ if (typeof netError === 'undefined') {
+ if (urlValid === false) {
+ message = ls`Invalid URL`;
+ } else {
+ message = ls`Unknown error`;
+ }
+ } else {
+ if (netError !== 0) {
+ if (isHTTPError(netError)) {
+ message += ls`HTTP error: status code ${statusCode}, ${netErrorName}`;
+ } else {
+ const errorCategory = getNetErrorCategory(netError);
+ // We don't localize here, as `errorCategory` is already localized,
+ // and `netErrorName` is an error code like 'net::ERR_CERT_AUTHORITY_INVALID'.
+ message = `${errorCategory}: ${netErrorName}`;
+ }
+ }
+ }
+ }
+ console.assert(success === (message.length === 0));
+ return {success, description: {statusCode, netError, netErrorName, urlValid, message}};
+}
+
+/**
* @param {string} url
* @param {?Object.<string, string>} headers
* @param {!Common.OutputStream} stream
- * @param {function(number, !Object.<string, string>, number)=} callback
+ * @param {function(boolean, !Object.<string, string>, !ResourceLoader.LoadErrorDescription)=} callback
*/
export const loadAsStream = function(url, headers, stream, callback) {
const streamId = _bindOutputStream(stream);
@@ -82,7 +170,8 @@
*/
function finishedCallback(response) {
if (callback) {
- callback(response.statusCode, response.headers || {}, response.netError || 0);
+ const {success, description} = createErrorMessageFromResponse(response);
+ callback(success, response.headers || {}, description);
}
_discardOutputStream(streamId);
}
@@ -95,7 +184,9 @@
finishedCallback(/** @type {!InspectorFrontendHostAPI.LoadNetworkResourceResult} */ ({statusCode: 200}));
}
- function dataURLDecodeFailed() {
- finishedCallback(/** @type {!InspectorFrontendHostAPI.LoadNetworkResourceResult} */ ({statusCode: 404}));
+ function dataURLDecodeFailed(xhrStatus) {
+ const messageOverride = ls`Decoding Data URL failed`;
+ finishedCallback(
+ /** @type {!InspectorFrontendHostAPI.LoadNetworkResourceResult} */ ({statusCode: 404, messageOverride}));
}
};
diff --git a/front_end/host/host-legacy.js b/front_end/host/host-legacy.js
index dd892f7..fee065d 100644
--- a/front_end/host/host-legacy.js
+++ b/front_end/host/host-legacy.js
@@ -27,7 +27,7 @@
/**
* @param {string} url
* @param {?Object.<string, string>} headers
- * @param {function(number, !Object.<string, string>, string, number)} callback
+ * @param {function(boolean, !Object.<string, string>, string, !HostModule.ResourceLoader.ResourceLoader.LoadErrorDescription)} callback
*/
Host.ResourceLoader.load = HostModule.ResourceLoader.load;
@@ -35,7 +35,7 @@
* @param {string} url
* @param {?Object.<string, string>} headers
* @param {!Common.OutputStream} stream
- * @param {function(number, !Object.<string, string>, number)=} callback
+ * @param {function(boolean, !Object.<string, string>, !HostModule.ResourceLoader.ResourceLoader.LoadErrorDescription)=} callback
*/
Host.ResourceLoader.loadAsStream = HostModule.ResourceLoader.loadAsStream;
diff --git a/front_end/host/host_strings.grdp b/front_end/host/host_strings.grdp
index 020a2ff..e4f3d22 100644
--- a/front_end/host/host_strings.grdp
+++ b/front_end/host/host_strings.grdp
@@ -1,6 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<grit-part>
+ <message name="IDS_DEVTOOLS_1f651ab3d2499f9ec45d86440334683e" desc="Name of an error category used in error messages">
+ HTTP error
+ </message>
+ <message name="IDS_DEVTOOLS_2e92ae79ff32b37fee4368a594792183" desc="Name of an error category used in error messages">
+ Connection error
+ </message>
<message name="IDS_DEVTOOLS_92bb7e733a12d24684262f046afcc2fd" desc="Document title in Inspector Frontend Host of the DevTools window">
<ph name="LOCKED_1">DevTools</ph> - <ph name="URL_REPLACE___HTTPS____________">$1s<ex>example.com</ex></ph>
</message>
-</grit-part>
\ No newline at end of file
+ <message name="IDS_DEVTOOLS_92e49dde8a851253ae32a85677e834df" desc="Phrase used in error messages that carry a network error name">
+ HTTP error: status code <ph name="STATUSCODE">$1s<ex>404</ex></ph>, <ph name="NETERRORNAME">$2s<ex>net::ERR_INSUFFICIENT_RESOURCES</ex></ph>
+ </message>
+ <message name="IDS_DEVTOOLS_9c9fc26f84098ba85cdf3397fd8184c7" desc="Name of an error category used in error messages">
+ Cache error
+ </message>
+ <message name="IDS_DEVTOOLS_a1bd59ccc3b0879f006fc81bce5cbde9" desc="Name of an error category used in error messages">
+ System error
+ </message>
+ <message name="IDS_DEVTOOLS_a811fec555556024e2175b487a9e6c21" desc="Name of an error category used in error messages">
+ DNS resolver error
+ </message>
+ <message name="IDS_DEVTOOLS_a900fc2e67bbbc74a2ce79242906b842" desc="Name of an error category used in error messages">
+ FTP error
+ </message>
+ <message name="IDS_DEVTOOLS_aee9784c03b80d38d3271cde2b252b8d" desc="Name of an error category used in error messages">
+ Unknown error
+ </message>
+ <message name="IDS_DEVTOOLS_bf945532466a63b144bf9ad4dc26bde9" desc="Name of an error category used in error messages">
+ Decoding Data URL failed
+ </message>
+ <message name="IDS_DEVTOOLS_c97758a89070ad6146a7f57496d58122" desc="Name of an error category used in error messages">
+ Certificate error
+ </message>
+ <message name="IDS_DEVTOOLS_d8075977a0e561aef111639b8cd9e409" desc="Name of an error category used in error messages">
+ Certificate manager error
+ </message>
+ <message name="IDS_DEVTOOLS_e9dc27efa61d173b27795e7a83db3b7d" desc="Name of an error category used in error messages">
+ Signed Exchange error
+ </message>
+ <message name="IDS_DEVTOOLS_f9c7939a8397ee022fefee2bdb3407af" desc="Name of an error category used in error messages">
+ Invalid URL
+ </message>
+</grit-part>
diff --git a/front_end/main/MainImpl.js b/front_end/main/MainImpl.js
index 3862661..3513135 100644
--- a/front_end/main/MainImpl.js
+++ b/front_end/main/MainImpl.js
@@ -133,8 +133,6 @@
Root.Runtime.experiments.register('nativeHeapProfiler', 'Native memory sampling heap profiler', true);
Root.Runtime.experiments.register('protocolMonitor', 'Protocol Monitor');
Root.Runtime.experiments.register(
- 'reportInternalNetErrorOnSourceMapLoadFail', 'Report internal net error code when a SourceMap fails to load');
- Root.Runtime.experiments.register(
'recordCoverageWithPerformanceTracing', 'Record coverage while performance tracing');
Root.Runtime.experiments.register('samplingHeapProfilerTimeline', 'Sampling heap profiler timeline', true);
Root.Runtime.experiments.register('sourceDiff', 'Source diff');
diff --git a/front_end/sdk/CompilerSourceMappingContentProvider.js b/front_end/sdk/CompilerSourceMappingContentProvider.js
index 5bcf1dd..1e98d04 100644
--- a/front_end/sdk/CompilerSourceMappingContentProvider.js
+++ b/front_end/sdk/CompilerSourceMappingContentProvider.js
@@ -72,24 +72,15 @@
*/
requestContent() {
return new Promise(resolve => {
- SDK.multitargetNetworkManager.loadResource(
- this._sourceURL,
- /**
- * @param {number} statusCode
- * @param {!Object.<string, string>} _headers (unused)
- * @param {string} content
- * @this {CompilerSourceMappingContentProvider}
- */
- (statusCode, _headers, content, netError) => {
- if (statusCode >= 400) {
- const error = ls`Could not load content for ${this._sourceURL} (HTTP status code: ${
- statusCode}, net error code ${netError})`;
- console.error(error);
- resolve({error, isEncoded: false});
- } else {
- resolve({content, isEncoded: false});
- }
- });
+ SDK.multitargetNetworkManager.loadResource(this._sourceURL, (success, _headers, content, errorDescription) => {
+ if (!success) {
+ const error = ls`Could not load content for ${this._sourceURL} (${errorDescription.message})`;
+ console.error(error);
+ resolve({error, isEncoded: false});
+ } else {
+ resolve({content, isEncoded: false});
+ }
+ });
});
}
diff --git a/front_end/sdk/NetworkManager.js b/front_end/sdk/NetworkManager.js
index ef8a4cc..a28f921 100644
--- a/front_end/sdk/NetworkManager.js
+++ b/front_end/sdk/NetworkManager.js
@@ -28,6 +28,8 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+import {ResourceLoader} from '../host/ResourceLoader.js'; // eslint-disable-line no-unused-vars
+
import {Cookie} from './Cookie.js';
import {Events as NetworkRequestEvents, NetworkRequest} from './NetworkRequest.js';
import {Capability, SDKModel, SDKModelObserver, Target} from './SDKModel.js'; // eslint-disable-line no-unused-vars
@@ -1333,7 +1335,7 @@
/**
* @param {string} url
- * @param {function(number, !Object.<string, string>, string, number)} callback
+ * @param {function(boolean, !Object.<string, string>, string, !ResourceLoader.LoadErrorDescription)} callback
*/
loadResource(url, callback) {
const headers = {};
diff --git a/front_end/sdk/SourceMap.js b/front_end/sdk/SourceMap.js
index 0fc4434..f7dd03b 100644
--- a/front_end/sdk/SourceMap.js
+++ b/front_end/sdk/SourceMap.js
@@ -226,12 +226,9 @@
*/
static async load(sourceMapURL, compiledURL) {
let content = await new Promise((resolve, reject) => {
- SDK.multitargetNetworkManager.loadResource(sourceMapURL, (statusCode, _headers, content, netError) => {
- if (!content || statusCode >= 400) {
- const showInternalError = Root.Runtime.experiments.isEnabled('reportInternalNetErrorOnSourceMapLoadFail');
- const internalError =
- showInternalError ? ls` (HTTP status code: ${statusCode}, net error code ${netError})` : ``;
- const error = new Error(ls`Could not load content for ${sourceMapURL}${internalError}`);
+ SDK.multitargetNetworkManager.loadResource(sourceMapURL, (success, _headers, content, errorDescription) => {
+ if (!content || !success) {
+ const error = new Error(ls`Could not load content for ${sourceMapURL}: ${errorDescription.message}`);
reject(error);
} else {
resolve(content);
diff --git a/front_end/sdk/sdk_strings.grdp b/front_end/sdk/sdk_strings.grdp
index bd00b6e..31e7d06 100644
--- a/front_end/sdk/sdk_strings.grdp
+++ b/front_end/sdk/sdk_strings.grdp
@@ -81,9 +81,6 @@
<message name="IDS_DEVTOOLS_5e53467e9b005376370c28e92b42b6f5" desc="Title of a setting under the Rendering drawer that can be invoked through the Command Menu">
Emulate CSS prefers-reduced-motion: reduce
</message>
- <message name="IDS_DEVTOOLS_66c8faee22c98da5169a40a63299f993" desc="Error message when failing to load a source map text via the network">
- Could not load content for <ph name="SOURCEMAPURL">$1s<ex>https://example.com</ex></ph><ph name="INTERNALERROR">$2s<ex> error code 15</ex></ph>
- </message>
<message name="IDS_DEVTOOLS_685c645cb0cb322c5b9989eb12bada19" desc="Title of a setting under the Rendering drawer that can be invoked through the Command Menu">
Emulate CSS media feature prefers-color-scheme
</message>
@@ -207,6 +204,9 @@
<message name="IDS_DEVTOOLS_7121afd196f5c52bef488d5a0f4c097b" desc="Text in the Event Listener Breakpoints Panel of the JavaScript Debugger in the Sources Panel">
Script First Statement
</message>
+ <message name="IDS_DEVTOOLS_716ddf802d9b9ee6899a47025e38bff7" desc="Error message when failing to fetch a resource referenced in a source map">
+ Could not load content for <ph name="THIS__SOURCEURL">$1s<ex>https://example.com/sourcemap.map</ex></ph> (<ph name="ERRORMESSAGE">$2s<ex>An error occurred</ex></ph>)
+ </message>
<message name="IDS_DEVTOOLS_73329564760013a7824ff9d5d1af91ff" desc="Service worker version status displayed in the Threads view of the Debugging side pane in the Sources panel">
installed
</message>
@@ -279,9 +279,6 @@
<message name="IDS_DEVTOOLS_a1595abbb4c3a326636dd178757cd6c1" desc="Text in DOMDebugger Model">
Control
</message>
- <message name="IDS_DEVTOOLS_a4fde38e56094c9fa87b2d9079dad5b4" desc="Error message when failing to load a source map text via the network">
- ''' (HTTP status code: <ph name="STATUSCODE"><ex>404</ex>$1s</ph>, net error code <ph name="NETERROR"><ex>-202</ex>$2s</ph>)
- </message>
<message name="IDS_DEVTOOLS_a720fc15eb9ec85751969e8615ace9e1" desc="Text in Server Timing">
Duplicate parameter "<ph name="PARAMNAME">$1s<ex>https</ex></ph>" ignored.
</message>
@@ -390,9 +387,6 @@
<message name="IDS_DEVTOOLS_edb020d2175281d94054136e09a3e132" desc="Title of a setting under the Debugger category that can be invoked through the Command Menu">
Do not pause on exceptions
</message>
- <message name="IDS_DEVTOOLS_ef2d5ca3248aaeb464b086e633f79d5b" desc="Error message when failing to load a script source text via the network">
- Could not load content for <ph name="THIS__SOURCEURL"><ex>https://example.com</ex>$1s</ph> (HTTP status code: <ph name="STATUSCODE"><ex>404</ex>$2s</ph>, net error code <ph name="NETERROR"><ex>-202</ex>$3s</ph>)
- </message>
<message name="IDS_DEVTOOLS_efa547f7d0b9924fdc7b301838c99fad" desc="A drop-down menu option to do not emulate css media type">
No emulation
</message>
@@ -417,6 +411,9 @@
<message name="IDS_DEVTOOLS_f7531e2d0ea27233ce00b5f01c5bf335" desc="A drop-down menu option to emulate css print media type">
print
</message>
+ <message name="IDS_DEVTOOLS_b507642e29a193b6853843992eef8c10" desc="Error message when failing to load a source map text via the network">
+ Could not load content for <ph name="SOURCEMAPURL">$1s<ex>https://example.com/sourcemap.map</ex></ph>: <ph name="ERRORMESSAGE">$2s<ex>A certificate error occured</ex></ph>
+ </message>
<message name="IDS_DEVTOOLS_f9778c8e7464e4bb037ec2463879588f" desc="Text in DOMDebugger Model">
DOM Mutation
</message>