mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-01 17:57:36 +08:00
This CL enables a new CDP-based resource loading mechanism for developer resources such as source maps. DISABLE_THIRD_PARTY_CHECK=required for dependency Bug: chromium:1069378 Change-Id: I54d7c1639605440cb74a367f9b7889da01bb2211 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2428892 Commit-Queue: Sigurd Schneider <sigurds@chromium.org> Reviewed-by: Peter Marshall <petermarshall@chromium.org>
250 lines
6.8 KiB
JavaScript
250 lines
6.8 KiB
JavaScript
// Copyright (c) 2015 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import * as Common from '../common/common.js';
|
|
|
|
import {InspectorFrontendHostInstance} from './InspectorFrontendHost.js';
|
|
import {LoadNetworkResourceResult} from './InspectorFrontendHostAPI.js'; // eslint-disable-line no-unused-vars
|
|
|
|
export const ResourceLoader = {};
|
|
|
|
let _lastStreamId = 0;
|
|
|
|
/** @type {!Object.<number, !Common.StringOutputStream.OutputStream>} */
|
|
const _boundStreams = {};
|
|
|
|
/**
|
|
* @param {!Common.StringOutputStream.OutputStream} stream
|
|
* @return {number}
|
|
*/
|
|
const _bindOutputStream = function(stream) {
|
|
_boundStreams[++_lastStreamId] = stream;
|
|
return _lastStreamId;
|
|
};
|
|
|
|
/**
|
|
* @param {number} id
|
|
*/
|
|
const _discardOutputStream = function(id) {
|
|
_boundStreams[id].close();
|
|
delete _boundStreams[id];
|
|
};
|
|
|
|
/**
|
|
* @param {number} id
|
|
* @param {string} chunk
|
|
*/
|
|
export const streamWrite = function(id, chunk) {
|
|
_boundStreams[id].write(chunk);
|
|
};
|
|
|
|
/** @typedef
|
|
{{
|
|
statusCode: number,
|
|
netError: (number|undefined),
|
|
netErrorName: (string|undefined),
|
|
urlValid: (boolean|undefined),
|
|
message: (string|undefined)
|
|
}} */
|
|
// @ts-ignore typedef.
|
|
export let LoadErrorDescription;
|
|
|
|
/**
|
|
* @param {string} url
|
|
* @param {?Object.<string, string>} headers
|
|
* @param {function(boolean, !Object.<string, string>, string, !LoadErrorDescription):void} callback
|
|
*/
|
|
export let load = function(url, headers, callback) {
|
|
const stream = new Common.StringOutputStream.StringOutputStream();
|
|
loadAsStream(url, headers, stream, mycallback);
|
|
|
|
/**
|
|
* @param {boolean} success
|
|
* @param {!Object.<string, string>} headers
|
|
* @param {!LoadErrorDescription} errorDescription
|
|
*/
|
|
function mycallback(success, headers, errorDescription) {
|
|
callback(success, headers, stream.data(), errorDescription);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {function(string, ?Object<string, string>, function(boolean, !Object<string, string>, string, !LoadErrorDescription): void): void} newLoad
|
|
*/
|
|
export function setLoadForTest(newLoad) {
|
|
load = newLoad;
|
|
}
|
|
|
|
/**
|
|
* @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 {number|undefined} netError
|
|
* @param {number|undefined} httpStatusCode
|
|
* @param {string|undefined} netErrorName
|
|
*/
|
|
export function netErrorToMessage(netError, httpStatusCode, netErrorName) {
|
|
if (netError === undefined || httpStatusCode === undefined || netErrorName === undefined) {
|
|
return null;
|
|
}
|
|
if (netError !== 0) {
|
|
if (isHTTPError(netError)) {
|
|
return ls`HTTP error: status code ${httpStatusCode}, ${netErrorName}`;
|
|
}
|
|
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'.
|
|
return `${errorCategory}: ${netErrorName}`;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param {!LoadNetworkResourceResult} response
|
|
* @returns {!{success:boolean, description: !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 {
|
|
const maybeMessage = netErrorToMessage(netError, statusCode, netErrorName);
|
|
if (maybeMessage) {
|
|
message = maybeMessage;
|
|
}
|
|
}
|
|
}
|
|
console.assert(success === (message.length === 0));
|
|
return {success, description: {statusCode, netError, netErrorName, urlValid, message}};
|
|
}
|
|
|
|
/**
|
|
* @param {string} url
|
|
* @return {!Promise<string>}
|
|
*/
|
|
const loadXHR = url => {
|
|
return new Promise((successCallback, failureCallback) => {
|
|
function onReadyStateChanged() {
|
|
if (xhr.readyState !== XMLHttpRequest.DONE) {
|
|
return;
|
|
}
|
|
if (xhr.status !== 200) {
|
|
xhr.onreadystatechange = null;
|
|
failureCallback(new Error(String(xhr.status)));
|
|
return;
|
|
}
|
|
xhr.onreadystatechange = null;
|
|
successCallback(xhr.responseText);
|
|
}
|
|
|
|
const xhr = new XMLHttpRequest();
|
|
xhr.withCredentials = false;
|
|
xhr.open('GET', url, true);
|
|
xhr.onreadystatechange = onReadyStateChanged;
|
|
xhr.send(null);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* @param {string} url
|
|
* @param {?Object.<string, string>} headers
|
|
* @param {!Common.StringOutputStream.OutputStream} stream
|
|
* @param {function(boolean, !Object.<string, string>, !LoadErrorDescription):void=} callback
|
|
*/
|
|
export const loadAsStream = function(url, headers, stream, callback) {
|
|
const streamId = _bindOutputStream(stream);
|
|
const parsedURL = new Common.ParsedURL.ParsedURL(url);
|
|
if (parsedURL.isDataURL()) {
|
|
loadXHR(url).then(dataURLDecodeSuccessful).catch(dataURLDecodeFailed);
|
|
return;
|
|
}
|
|
|
|
const rawHeaders = [];
|
|
if (headers) {
|
|
for (const key in headers) {
|
|
rawHeaders.push(key + ': ' + headers[key]);
|
|
}
|
|
}
|
|
InspectorFrontendHostInstance.loadNetworkResource(url, rawHeaders.join('\r\n'), streamId, finishedCallback);
|
|
|
|
/**
|
|
* @param {!LoadNetworkResourceResult} response
|
|
*/
|
|
function finishedCallback(response) {
|
|
if (callback) {
|
|
const {success, description} = createErrorMessageFromResponse(response);
|
|
callback(success, response.headers || {}, description);
|
|
}
|
|
_discardOutputStream(streamId);
|
|
}
|
|
|
|
/**
|
|
* @param {string} text
|
|
*/
|
|
function dataURLDecodeSuccessful(text) {
|
|
streamWrite(streamId, text);
|
|
finishedCallback(/** @type {!LoadNetworkResourceResult} */ ({statusCode: 200}));
|
|
}
|
|
|
|
/**
|
|
* @param {*} xhrStatus
|
|
*/
|
|
function dataURLDecodeFailed(xhrStatus) {
|
|
const messageOverride = ls`Decoding Data URL failed`;
|
|
finishedCallback(
|
|
/** @type {!LoadNetworkResourceResult} */ ({statusCode: 404, messageOverride}));
|
|
}
|
|
};
|