mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-03 02:21:05 +08:00
It uses a `.rawresponse` file that forcibly sets the `Last-Modified` header. We then open the network panel, add the `Last-Modified` column to the data-grid and then introspect its values. As part of this patch, each context submenu now gets an `aria-label`. These attributes are redundant here, since they mirror the computed accessible name of these elements. The reason they're added is that we use this label in the selectors of the end-to-end test. This replaces https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/web_tests/http/tests/devtools/startup/resources/cached-resource-metadata.html;drc=77578ccb4082ae20a9326d9e673225f1189ebb63?originalUrl=https:%2F%2Fcs.chromium.org%2F as an e2etest. Bug: 1047671 Change-Id: I89efc145c915f1a083dbcfb3765674122d9748fe Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2039270 Commit-Queue: Tim van der Lippe <tvanderlippe@chromium.org> Reviewed-by: Mathias Bynens <mathias@chromium.org>
202 lines
7.0 KiB
JavaScript
202 lines
7.0 KiB
JavaScript
// Copyright (c) 2016 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.
|
|
var fs = require('fs');
|
|
var http = require('http');
|
|
var path = require('path');
|
|
var parseURL = require('url').parse;
|
|
|
|
var utils = require('../utils');
|
|
|
|
const remoteDebuggingPort = parseInt(process.env.REMOTE_DEBUGGING_PORT, 10) || 9222;
|
|
const serverPort = parseInt(process.env.PORT, 10) || 8090;
|
|
const localProtocolPath = process.env.LOCAL_PROTOCOL_PATH;
|
|
const devtoolsFolder = path.resolve(path.join(__dirname, '../..'));
|
|
|
|
http.createServer(requestHandler).listen(serverPort);
|
|
console.log(`Started hosted mode server at http://localhost:${serverPort}\n`);
|
|
console.log('For info on using the hosted mode server, see our contributing docs:');
|
|
console.log('https://bit.ly/devtools-contribution-guide');
|
|
console.log('Tip: Look for the \'Development server options\' section\n');
|
|
|
|
function requestHandler(request, response) {
|
|
var filePath = parseURL(request.url).pathname;
|
|
if (filePath === '/') {
|
|
var landingURL = `http://localhost:${remoteDebuggingPort}#custom=true`;
|
|
sendResponse(200, `<html>Please go to <a href="${landingURL}">${landingURL}</a></html>`);
|
|
return;
|
|
}
|
|
|
|
var proxiedFile = proxy(filePath, sendResponse);
|
|
if (proxiedFile) {
|
|
proxiedFile.then(data => sendResponse(200, data)).catch(handleProxyError);
|
|
return;
|
|
}
|
|
|
|
function handleProxyError(err) {
|
|
console.log(`Error serving the file ${filePath}:`, err);
|
|
console.log(`Make sure you opened Chrome with the flag "--remote-debugging-port=${remoteDebuggingPort}"`);
|
|
sendResponse(500, '500 - Internal Server Error');
|
|
}
|
|
|
|
var absoluteFilePath = path.join(devtoolsFolder, filePath);
|
|
if (!path.resolve(absoluteFilePath).startsWith(devtoolsFolder)) {
|
|
console.log(`File requested (${absoluteFilePath}) is outside of devtools folder: ${devtoolsFolder}`);
|
|
sendResponse(403, `403 - Access denied. File requested is outside of devtools folder: ${devtoolsFolder}`);
|
|
return;
|
|
}
|
|
|
|
fs.exists(absoluteFilePath, fsExistsCallback);
|
|
|
|
function fsExistsCallback(fileExists) {
|
|
if (!fileExists) {
|
|
console.log(`Cannot find file ${absoluteFilePath}`);
|
|
sendResponse(404, '404 - File not found');
|
|
return;
|
|
}
|
|
fs.readFile(absoluteFilePath, 'binary', readFileCallback);
|
|
}
|
|
|
|
function readFileCallback(err, file) {
|
|
if (err) {
|
|
console.log(`Unable to read local file ${absoluteFilePath}:`, err);
|
|
sendResponse(500, '500 - Internal Server Error');
|
|
return;
|
|
}
|
|
sendResponse(200, file);
|
|
}
|
|
|
|
function sendResponse(statusCode, data) {
|
|
const path = parseURL(request.url).pathname;
|
|
|
|
if (path.endsWith('.rawresponse')) {
|
|
sendRawResponse(data);
|
|
return;
|
|
}
|
|
|
|
if (path.endsWith('.js')) {
|
|
response.setHeader('Content-Type', 'text/javascript');
|
|
}
|
|
|
|
if (path.endsWith('.wasm')) {
|
|
response.setHeader('Content-Type', 'application/wasm');
|
|
}
|
|
|
|
if (path.endsWith('.svg')) {
|
|
response.setHeader('Content-Type', 'image/svg+xml');
|
|
}
|
|
|
|
response.writeHead(statusCode);
|
|
response.write(data, 'binary');
|
|
response.end();
|
|
}
|
|
|
|
function sendRawResponse(data) {
|
|
const lines = data.split('\n');
|
|
|
|
let isHeader = true;
|
|
let line = lines.shift();
|
|
const statusCode = parseInt(line);
|
|
|
|
while ((line = lines.shift()) !== undefined) {
|
|
if (line.trim() === '') {
|
|
isHeader = false;
|
|
response.writeHead(statusCode);
|
|
continue;
|
|
}
|
|
|
|
if (isHeader) {
|
|
const firstColon = line.indexOf(':');
|
|
response.setHeader(line.substring(0, firstColon), line.substring(firstColon + 1));
|
|
} else {
|
|
response.write(line);
|
|
}
|
|
}
|
|
|
|
response.end();
|
|
}
|
|
}
|
|
|
|
var proxyFilePathToURL = {
|
|
'/front_end/SupportedCSSProperties.js': cloudURL.bind(null, 'SupportedCSSProperties.js'),
|
|
'/front_end/InspectorBackendCommands.js': cloudURL.bind(null, 'InspectorBackendCommands.js'),
|
|
'/favicon.ico': () => 'https://chrome-devtools-frontend.appspot.com/favicon.ico',
|
|
'/front_end/accessibility/ARIAProperties.js': cloudURL.bind(null, 'accessibility/ARIAProperties.js'),
|
|
};
|
|
|
|
function cloudURL(path, commitHash) {
|
|
return `https://chrome-devtools-frontend.appspot.com/serve_file/@${commitHash}/${path}`;
|
|
}
|
|
|
|
var proxyFileCache = new Map();
|
|
|
|
function proxy(filePath) {
|
|
if (!(filePath in proxyFilePathToURL))
|
|
return null;
|
|
if (localProtocolPath && filePath === '/front_end/InspectorBackendCommands.js')
|
|
return serveLocalProtocolFile();
|
|
if (process.env.CHROMIUM_COMMIT)
|
|
return onProxyFileURL(proxyFilePathToURL[filePath](process.env.CHROMIUM_COMMIT));
|
|
return utils.fetch(`http://localhost:${remoteDebuggingPort}/json/version`)
|
|
.then(onBrowserMetadata)
|
|
.then(onProxyFileURL);
|
|
|
|
function serveLocalProtocolFile() {
|
|
return new Promise((resolve, reject) => {
|
|
fs.exists(localProtocolPath, fsExistsCallback);
|
|
function fsExistsCallback(fileExists) {
|
|
if (!fileExists) {
|
|
reject(new Error(`Cannot find local protocol file ${localProtocolPath}`));
|
|
return;
|
|
}
|
|
fs.readFile(localProtocolPath, 'binary', readFileCallback);
|
|
}
|
|
function readFileCallback(err, file) {
|
|
if (err) {
|
|
reject(new Error(`Unable to read local protocol file ${localProtocolPath}`));
|
|
return;
|
|
}
|
|
return resolve(file);
|
|
}
|
|
});
|
|
}
|
|
|
|
function onBrowserMetadata(metadata) {
|
|
var metadataObject = JSON.parse(metadata);
|
|
var match = metadataObject['WebKit-Version'].match(/\s\(@(\b[0-9a-f]{5,40}\b)/);
|
|
var commitHash = match[1];
|
|
var proxyFileURL = proxyFilePathToURL[filePath](commitHash);
|
|
return proxyFileURL;
|
|
}
|
|
|
|
function onProxyFileURL(proxyFileURL) {
|
|
if (proxyFileCache.has(proxyFileURL))
|
|
return Promise.resolve(proxyFileCache.get(proxyFileURL));
|
|
return utils.fetch(proxyFileURL).then(cacheProxyFile.bind(null, proxyFileURL)).catch(onMissingFile);
|
|
}
|
|
|
|
function onMissingFile() {
|
|
var isFullCheckout = utils.shellOutput('git config --get remote.origin.url') ===
|
|
'https://chromium.googlesource.com/chromium/src.git';
|
|
var earlierCommitHash;
|
|
var gitLogCommand = `git log --max-count=1 --grep="Commit-Position" --before="12 hours ago"`;
|
|
if (isFullCheckout) {
|
|
earlierCommitHash = utils.shellOutput(`${gitLogCommand} --pretty=format:"%H"`);
|
|
} else {
|
|
var commitMessage = utils.shellOutput(`${gitLogCommand}`);
|
|
earlierCommitHash = commitMessage.match(/Cr-Mirrored-Commit: (.*)/)[1];
|
|
}
|
|
var fallbackURL = proxyFilePathToURL[filePath](earlierCommitHash);
|
|
console.log('WARNING: Unable to fetch generated file based on browser\'s revision');
|
|
console.log('Fetching earlier revision of same file as fallback');
|
|
console.log('There may be a mismatch between the front-end and back-end');
|
|
console.log(fallbackURL, '\n');
|
|
return utils.fetch(fallbackURL).then(cacheProxyFile.bind(null, fallbackURL));
|
|
}
|
|
|
|
function cacheProxyFile(proxyFileURL, data) {
|
|
proxyFileCache.set(proxyFileURL, data);
|
|
return data;
|
|
}
|
|
}
|