diff options
Diffstat (limited to 'chromium/third_party/devtools-frontend/src/scripts')
26 files changed, 586 insertions, 635 deletions
diff --git a/chromium/third_party/devtools-frontend/src/scripts/build/build_debug_applications.py b/chromium/third_party/devtools-frontend/src/scripts/build/build_debug_applications.py deleted file mode 100644 index 825b6cbe85d..00000000000 --- a/chromium/third_party/devtools-frontend/src/scripts/build/build_debug_applications.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- -# -# Copyright 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. -""" -Builds applications in debug mode: -- Copies the module directories into their destinations. -- Copies app.html as-is. -""" - -from os import path -from os.path import join -import os -import shutil -import sys - -import modular_build - - -def main(argv): - try: - input_path_flag_index = argv.index('--input_path') - input_path = argv[input_path_flag_index + 1] - output_path_flag_index = argv.index('--output_path') - output_path = argv[output_path_flag_index + 1] - build_stamp_index = argv.index('--build_stamp') - build_stamp_path = argv[build_stamp_index + 1] - except: - print('Usage: %s app_1 app_2 ... app_N --input_path <input_path> --output_path <output_path>' % argv[0]) - raise - - symlink_dir_or_copy(input_path, output_path) - - with open(build_stamp_path, 'w') as file: - file.write('stamp') - - -def symlink_dir_or_copy(src, dest): - if hasattr(os, 'symlink'): - if path.exists(dest): - if os.path.islink(dest): - os.unlink(dest) - else: - shutil.rmtree(dest) - os.symlink(join(os.getcwd(), src), dest) - else: - for filename in os.listdir(src): - new_src = join(os.getcwd(), src, filename) - if os.path.isdir(new_src): - copy_dir(new_src, join(dest, filename)) - else: - copy_file(new_src, join(dest, filename), safe=True) - - -def copy_file(src, dest, safe=False): - if safe and path.exists(dest): - os.remove(dest) - shutil.copy(src, dest) - - -def copy_dir(src, dest): - if path.exists(dest): - shutil.rmtree(dest) - for src_dir, dirs, files in os.walk(src): - subpath = path.relpath(src_dir, src) - dest_dir = path.normpath(join(dest, subpath)) - os.makedirs(dest_dir) - for name in files: - src_name = join(os.getcwd(), src_dir, name) - dest_name = join(dest_dir, name) - copy_file(src_name, dest_name) - - -if __name__ == '__main__': - sys.exit(main(sys.argv)) diff --git a/chromium/third_party/devtools-frontend/src/scripts/build/build_release_applications.py b/chromium/third_party/devtools-frontend/src/scripts/build/build_release_applications.py index 9a128cc6aee..f74f92cd7dd 100644 --- a/chromium/third_party/devtools-frontend/src/scripts/build/build_release_applications.py +++ b/chromium/third_party/devtools-frontend/src/scripts/build/build_release_applications.py @@ -8,7 +8,6 @@ Builds applications in release mode: - Concatenates autostart modules, application modules' module.json descriptors, and the application loader into a single script. -- Builds app.html referencing the application script. """ from cStringIO import StringIO @@ -45,11 +44,8 @@ MODULE_LIST = [ for subfolder in os.listdir(FRONT_END_DIRECTORY) if path.isdir(os.path.join(FRONT_END_DIRECTORY, subfolder)) ] - -ROLLUP_ARGS = [ - '--no-treeshake', '--format', 'esm', '--context', 'self', '--external', - ','.join([path.abspath(module) for module in MODULE_LIST]) -] +EXTERNAL_MODULE_LIST = ','.join( + [path.abspath(module) for module in MODULE_LIST]) def main(argv): @@ -91,7 +87,6 @@ def concatenated_module_filename(module_name, output_dir): # Outputs: -# <app_name>.html # <app_name>.js # <module_name>_module.js class ReleaseBuilder(object): @@ -121,9 +116,6 @@ class ReleaseBuilder(object): return result def build_app(self): - if self.descriptors.has_html: - html_entrypoint = self.app_file('html') - write_file(join(self.output_dir, html_entrypoint), read_file(join(self.application_dir, html_entrypoint))) self._build_app_script() for module in filter(lambda desc: (not desc.get('type') or desc.get('type') == 'remote'), self.descriptors.application.values()): @@ -221,14 +213,22 @@ class ReleaseBuilder(object): js_entrypoint = join(self.application_dir, module_name, module_name + '.js') out = '' if self.use_rollup: - rollup_process = subprocess.Popen( - [devtools_paths.node_path(), devtools_paths.rollup_path()] + ROLLUP_ARGS + ['--input', js_entrypoint], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + rollup_process = subprocess.Popen([ + devtools_paths.node_path(), + devtools_paths.rollup_path(), '--config', + join(FRONT_END_DIRECTORY, 'rollup.config.js'), '--input', + js_entrypoint, '--external', EXTERNAL_MODULE_LIST + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) out, error = rollup_process.communicate() + if rollup_process.returncode != 0: + print(error) + sys.exit(rollup_process.returncode) else: out = read_file(js_entrypoint) - write_file(join(self.output_dir, module_name, module_name + '.js'), minify_js(out)) + write_file(join(self.output_dir, module_name, module_name + '.js'), + minify_js(out)) legacyFileName = module_name + '-legacy.js' if legacyFileName in modules: diff --git a/chromium/third_party/devtools-frontend/src/scripts/build/code_generator_frontend.py b/chromium/third_party/devtools-frontend/src/scripts/build/code_generator_frontend.py index 3d7d6d08812..502481fab57 100644 --- a/chromium/third_party/devtools-frontend/src/scripts/build/code_generator_frontend.py +++ b/chromium/third_party/devtools-frontend/src/scripts/build/code_generator_frontend.py @@ -159,13 +159,15 @@ class Templates: return "/".join(components) - file_header_ = ("""// Copyright (c) 2020 The Chromium Authors. All rights reserved. + file_header_ = ( + """// Copyright (c) 2020 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. -""" + "// File is generated by %s\n\n" % get_this_script_path_(sys.argv[0]) + """ +""" + "// File is generated by %s\n\n" % get_this_script_path_(sys.argv[0]) + + """ /** * @typedef {{ - * registerCommand: function(string, !Array.<!{name: string, type: string, optional: boolean}>, !Array.<string>, boolean):void, + * registerCommand: function(string, !Array.<!{name: string, type: string, optional: boolean}>, !Array.<string>):void, * registerEnum: function(string, !Object<string, string>):void, * registerEvent: function(string, !Array<string>):void, * }} @@ -220,6 +222,14 @@ class Generator: if "commands" in json_domain: for json_command in json_domain["commands"]: + if "parameters" in json_command: + for param in json_command["parameters"]: + if "enum" in param: + enum_name = "%s.%sRequest%s" % ( + domain_name, + to_title_case(json_command["name"]), + to_title_case(param["name"])) + Generator.process_enum(param, enum_name) Generator.process_command(json_command, domain_name) Generator.backend_js_domain_initializer_list.append("\n") @@ -277,14 +287,11 @@ class Generator: backend_js_reply_param_list.append("\"%s\"" % json_return_name) js_reply_list = "[%s]" % ", ".join(backend_js_reply_param_list) - if "error" in json_command: - has_error_data_param = "true" - else: - has_error_data_param = "false" Generator.backend_js_domain_initializer_list.append( - "inspectorBackend.registerCommand(\"%s.%s\", [%s], %s, %s);\n" % - (domain_name, json_command_name, js_parameters_text, js_reply_list, has_error_data_param)) + "inspectorBackend.registerCommand(\"%s.%s\", [%s], %s);\n" % + (domain_name, json_command_name, js_parameters_text, + js_reply_list)) Generator.go() diff --git a/chromium/third_party/devtools-frontend/src/scripts/build/copy_devtools_modules.py b/chromium/third_party/devtools-frontend/src/scripts/build/copy_devtools_modules.py index 4434e1a97d4..9ebb32940b3 100644 --- a/chromium/third_party/devtools-frontend/src/scripts/build/copy_devtools_modules.py +++ b/chromium/third_party/devtools-frontend/src/scripts/build/copy_devtools_modules.py @@ -36,8 +36,8 @@ def main(argv): for file_name in devtools_modules: file_content = read_file(join(input_path, file_name)) - minified = rjsmin.jsmin(file_content) - write_file(join(output_path, relpath(file_name, 'front_end')), minified) + write_file(join(output_path, relpath(file_name, 'front_end')), + file_content) if __name__ == '__main__': diff --git a/chromium/third_party/devtools-frontend/src/scripts/build/copy_to_gen.gni b/chromium/third_party/devtools-frontend/src/scripts/build/copy_to_gen.gni new file mode 100644 index 00000000000..61b37673251 --- /dev/null +++ b/chromium/third_party/devtools-frontend/src/scripts/build/copy_to_gen.gni @@ -0,0 +1,15 @@ +# Copyright 2019 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. + +template("copy_to_gen") { + copy(target_name) { + forward_variables_from(invoker, + [ + "sources", + "visibility", + "deps", + ]) + outputs = [ "{{source_gen_dir}}/{{source_file_part}}" ] + } +} diff --git a/chromium/third_party/devtools-frontend/src/scripts/build/generate_protocol_externs.py b/chromium/third_party/devtools-frontend/src/scripts/build/generate_protocol_externs.py index d579f64d552..a649fb6f325 100644 --- a/chromium/third_party/devtools-frontend/src/scripts/build/generate_protocol_externs.py +++ b/chromium/third_party/devtools-frontend/src/scripts/build/generate_protocol_externs.py @@ -84,13 +84,17 @@ def generate_enum(name, json): return "\n/** @enum {string} */\n%s = {\n%s\n};\n" % (name, (",\n".join(enum_members))) -def param_type(domain_name, param): +def param_type(domain_name, param, command=None): + if "enum" in param and command: + return "Protocol.%s.%sRequest%s" % (domain_name, + to_title_case(command["name"]), + to_title_case(param_name(param))) if "type" in param: if param["type"] == "array": items = param["items"] return "!Array<%s>" % param_type(domain_name, items) else: - return type_traits[param["type"]] + return "Protocol.%s" % param["type"] if "$ref" in param: type_id = full_qualified_type_id(domain_name, param["$ref"]) if type_id in ref_types: @@ -118,8 +122,15 @@ def generate_protocol_externs(output_path, file1, file2): load_schema(file2, domains) output_file = open(output_path, "w") output_file.write("var ProtocolProxyApi = {};\n") - output_file.write("/** @typedef {string} */") - output_file.write("Protocol.binary;") + + # Add basic types from protocol to closure + for protocolName, closureName in type_traits.items(): + output_file.write("/** @typedef {%s} */\n" % closureName) + output_file.write("Protocol.%s;\n" % protocolName) + + # Used for enforcing whether a dispatcher uses an object or not + output_file.write("/** @typedef {boolean} */\n") + output_file.write("Protocol.UsesObjectNotation;\n") for domain in domains: domain_name = domain["domain"] @@ -139,11 +150,19 @@ def generate_protocol_externs(output_path, file1, file2): if "commands" in domain: for command in domain["commands"]: - output_file.write("\n/**\n") params = [] in_param_to_type = {} out_param_to_type = {} has_return_value = "returns" in command + + if "parameters" in command: + for param in command["parameters"]: + if "enum" in param: + enum_name = param_type(domain_name, param, command) + output_file.write(generate_enum(enum_name, param)) + + output_file.write("\n/**\n") + if "parameters" in command: # Only declare trailing optional parameters as optional in # JSDoc annotations. @@ -158,13 +177,18 @@ def generate_protocol_externs(output_path, file1, file2): real_in_param_name = "opt_" + in_param_name if in_param_name in trailing_optional else in_param_name params.append(real_in_param_name) if "optional" in in_param: - in_param_to_type[in_param_name] = "(%s|undefined)" % param_type(domain_name, in_param) + in_param_to_type[ + in_param_name] = "(%s|undefined)" % param_type( + domain_name, in_param, command) annotation_suffix = "=" if in_param_name in trailing_optional else "|undefined" else: - in_param_to_type[in_param_name] = param_type(domain_name, in_param) + in_param_to_type[in_param_name] = param_type( + domain_name, in_param, command) annotation_suffix = "" output_file.write( - " * @param {%s%s} %s\n" % (param_type(domain_name, in_param), annotation_suffix, real_in_param_name)) + " * @param {%s%s} %s\n" % + (param_type(domain_name, in_param, command), + annotation_suffix, real_in_param_name)) returns = [] returns.append("?Protocol.Error") if ("error" in command): @@ -192,7 +216,8 @@ def generate_protocol_externs(output_path, file1, file2): "Protocol.%sAgent.prototype.%s = function(%s) {};\n" % (domain_name, command["name"], ", ".join(params))) request_object_properties = [] - request_type = "Protocol.%sAgent.%sRequest" % (domain_name, to_title_case(command["name"])) + request_type = "Protocol.%s.%sRequest" % ( + domain_name, to_title_case(command["name"])) for param in in_param_to_type: request_object_properties.append("%s: %s" % (param, in_param_to_type[param])) if request_object_properties: @@ -202,7 +227,8 @@ def generate_protocol_externs(output_path, file1, file2): output_file.write("%s;\n" % request_type) response_object_properties = [] - response_type = "Protocol.%sAgent.%sResponse" % (domain_name, to_title_case(command["name"])) + response_type = "Protocol.%s.%sResponse" % ( + domain_name, to_title_case(command["name"])) for param in out_param_to_type: response_object_properties.append("%s: %s" % (param, out_param_to_type[param])) if response_object_properties: @@ -251,6 +277,13 @@ def generate_protocol_externs(output_path, file1, file2): else: output_file.write("/** @interface */\n") output_file.write("Protocol.%sDispatcher = function() {};\n" % domain_name) + output_file.write("/** @interface */\n") + output_file.write("ProtocolProxyApi.%sDispatcher = function() {};\n" % + domain_name) + # Include the workaround for https://github.com/microsoft/TypeScript/issues/38640 + output_file.write( + "var ProtocolProxyApiWorkaround_%sDispatcher = ProtocolProxyApi.%sDispatcher;\n" + % (domain_name, domain_name)) if "events" in domain: for event in domain["events"]: params = [] @@ -267,6 +300,33 @@ def generate_protocol_externs(output_path, file1, file2): output_file.write( "Protocol.%sDispatcher.prototype.%s = function(%s) {};\n" % (domain_name, event["name"], ", ".join(params))) + # Generate a Event typedef that is used in dispatchers + if ("parameters" in event): + output_file.write("/**\n") + output_file.write("* @typedef {{") + for param in event["parameters"]: + full_param_type = param_type(domain_name, param) + if ("optional" in param): + full_param_type = "(%s|undefined)" % full_param_type + output_file.write("%s: %s," % + (param["name"], full_param_type)) + output_file.write("}} */\n") + else: + output_file.write("/**\n") + output_file.write("* @typedef {Object}") + output_file.write("*/\n") + output_file.write("Protocol.%s.%sEvent;\n" % + (domain_name, to_title_case(event["name"]))) + + # Add the interface method to the dispatcher type, which takes 1 event as argument + output_file.write("/**\n") + output_file.write("@param {!Protocol.%s.%sEvent} request\n" % + (domain_name, to_title_case(event["name"]))) + output_file.write("*/\n") + output_file.write( + "ProtocolProxyApi.%sDispatcher.prototype.%s = function(request) {};\n" + % (domain_name, event["name"])) + for domain in domains: domain_name = domain["domain"] uppercase_length = 0 @@ -277,7 +337,9 @@ def generate_protocol_externs(output_path, file1, file2): output_file.write("ProtocolClient.TargetBase.prototype.%s = function(){};\n" % (domain_name[:uppercase_length].lower() + domain_name[uppercase_length:] + "Agent")) - output_file.write("/**\n * @param {!Protocol.%sDispatcher} dispatcher\n */\n" % domain_name) + output_file.write( + "/**\n * @param {!Protocol.%sDispatcher|!ProtocolProxyApi.%sDispatcher} dispatcher\n */\n" + % (domain_name, domain_name)) output_file.write("ProtocolClient.TargetBase.prototype.register%sDispatcher = function(dispatcher) {}\n" % domain_name) output_file.close() diff --git a/chromium/third_party/devtools-frontend/src/scripts/build/generate_supported_css.py b/chromium/third_party/devtools-frontend/src/scripts/build/generate_supported_css.py index beb6ab9e3d6..a20ff587375 100755 --- a/chromium/third_party/devtools-frontend/src/scripts/build/generate_supported_css.py +++ b/chromium/third_party/devtools-frontend/src/scripts/build/generate_supported_css.py @@ -64,10 +64,16 @@ def properties_from_file(file_name): if "alias_for" in entry: aliases_for.append([entry["name"], entry["alias_for"]]) continue + # Filter out internal properties. + if entry["name"].startswith("-internal-"): + continue properties.append(_keep_only_required_keys(entry)) property_names[entry["name"]] = entry if "keywords" in entry: - property_values[entry["name"]] = {"values": entry["keywords"]} + keywords = list( + filter(lambda keyword: not keyword.startswith("-internal-"), + entry["keywords"])) + property_values[entry["name"]] = {"values": keywords} properties.sort(key=lambda entry: entry["name"]) aliases_for.sort(key=lambda entry: entry[0]) diff --git a/chromium/third_party/devtools-frontend/src/scripts/build/modular_build.py b/chromium/third_party/devtools-frontend/src/scripts/build/modular_build.py index 4f7a2e47169..fa65a213c0a 100644 --- a/chromium/third_party/devtools-frontend/src/scripts/build/modular_build.py +++ b/chromium/third_party/devtools-frontend/src/scripts/build/modular_build.py @@ -56,20 +56,19 @@ def concatenate_scripts(file_names, module_dir, output_dir, output): class Descriptors: - def __init__(self, application_name, application_dir, application_descriptor, module_descriptors, extends, has_html, worker): + def __init__(self, application_name, application_dir, + application_descriptor, module_descriptors, extends, worker): self.application_name = application_name self.application_dir = application_dir self.application = application_descriptor self._cached_sorted_modules = None self.modules = module_descriptors self.extends = extends - self.has_html = has_html self.worker = worker def application_json(self): result = dict() result['modules'] = self.application.values() - result['has_html'] = self.has_html return json.dumps(result) def all_compiled_files(self): @@ -178,7 +177,9 @@ class DescriptorLoader: all_module_descriptors[name] = descriptors[name] for name in result.application: all_application_descriptors[name] = result.application[name] - return Descriptors('all', self.application_dir, all_application_descriptors, all_module_descriptors, None, False, False) + return Descriptors('all', self.application_dir, + all_application_descriptors, all_module_descriptors, + None, False) def _load_application(self, application_descriptor_name, all_module_descriptors): module_descriptors = {} @@ -188,7 +189,6 @@ class DescriptorLoader: extends = descriptor_json['extends'] if 'extends' in descriptor_json else None if extends: extends = self._load_application(extends, all_module_descriptors) - has_html = True if 'has_html' in descriptor_json and descriptor_json['has_html'] else False worker = True if 'worker' in descriptor_json and descriptor_json['worker'] else False for (module_name, module) in application_descriptor.items(): @@ -203,8 +203,9 @@ class DescriptorLoader: bail_error('Module "%s" (dependency of "%s") not listed in application descriptor %s' % (dep, module['name'], application_descriptor_filename)) - return Descriptors(application_descriptor_name, self.application_dir, application_descriptor, module_descriptors, extends, - has_html, worker) + return Descriptors(application_descriptor_name, self.application_dir, + application_descriptor, module_descriptors, extends, + worker) def _read_module_descriptor(self, module_name, application_descriptor_filename): json_filename = path.join(self.application_dir, module_name, 'module.json') diff --git a/chromium/third_party/devtools-frontend/src/scripts/component_server/README.md b/chromium/third_party/devtools-frontend/src/scripts/component_server/README.md new file mode 100644 index 00000000000..59189a4bc0f --- /dev/null +++ b/chromium/third_party/devtools-frontend/src/scripts/component_server/README.md @@ -0,0 +1,13 @@ +# Components dev server + +This server serves up examples of the web components built within DevTools and provides a useful way to document various states of components. + +For more background, see the [initial proposal and design doc](https://docs.google.com/document/d/1P6qtACf4aryfT9OSHxNFI3okMKt9oUrtzOKCws5bOec/edit?pli=1). + +To add an example for your component: + +1. Create `front_end/component_docs/DIR` where `DIR` is the name of your component. +2. Within the new `DIR`, add HTML files. Each one of these is a standalone example. You should name the HTML file based on what it's documenting, e.g. `basic.html` or `data-missing.html`. This file should import your component and render it with the data you need. +3. Update `front_end/component_docs/BUILD.gn` to add each of your `*.html` files to the `sources` array. You will also need to update the `deps` array to ensure the component examples depend on your component's source file. +4. Run `npm run components-server` to fire up a server on `localhost:8090`. You should now see your component listed and you can click on the link to view the examples. +5. **Note**: the server assumes your built target is `Default`. If not, run the server and pass the `--target` flag: `npm run components-server -- --target=Release`. diff --git a/chromium/third_party/devtools-frontend/src/scripts/component_server/server.js b/chromium/third_party/devtools-frontend/src/scripts/component_server/server.js new file mode 100644 index 00000000000..7dfb0b9f6f5 --- /dev/null +++ b/chromium/third_party/devtools-frontend/src/scripts/component_server/server.js @@ -0,0 +1,165 @@ +// Copyright (c) 2020 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. + +const fs = require('fs'); +const http = require('http'); +const path = require('path'); +const parseURL = require('url').parse; +const {argv} = require('yargs'); + +const serverPort = parseInt(process.env.PORT, 10) || 8090; + +const target = argv.target || 'Default'; +const devtoolsFrontendFolder = path.resolve(path.join(__dirname, '..', '..', 'out', target, 'gen', 'front_end')); + +if (!fs.existsSync(devtoolsFrontendFolder)) { + console.error(`ERROR: Generated front_end folder (${devtoolsFrontendFolder}) does not exist.`); + console.log( + 'The components server works from the built Ninja output; you may need to run Ninja to update your built DevTools.'); + console.log('If you build to a target other than default, you need to pass --target=X as an argument'); + process.exit(1); +} + +http.createServer(requestHandler).listen(serverPort); +console.log(`Started components server at http://localhost:${serverPort}\n`); + +function createComponentIndexFile(componentPath, componentExamples) { + const componentName = componentPath.replace('/', ''); + // clang-format off + return `<!DOCTYPE html> + <html> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width" /> + <title>DevTools component: ${componentName}</title> + <style> + h1 { text-transform: capitalize; } + + .example { + padding: 5px; + margin: 10px; + } + iframe { display: block; width: 100%; } + </style> + </head> + <body> + <h1>${componentName}</h1> + ${componentExamples.map(example => { + const fullPath = path.join('component_docs', componentName, example); + return `<div class="example"> + <h3><a href="${fullPath}">${example}</a></h3> + <iframe src="${fullPath}"></iframe> + </div>`; + }).join('\n')} + </body> + </html>`; + // clang-format on +} + +function createServerIndexFile(componentNames) { + // clang-format off + return `<!DOCTYPE html> + <html> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width" /> + <title>DevTools components</title> + <style> + a { text-transform: capitalize; } + </style> + </head> + <body> + <h1>DevTools components</h1> + <ul> + ${componentNames.map(name => { + return `<li><a href='/${name}'>${name}</a></li>`; + }).join('\n')} + </ul> + </body> + </html>`; + // clang-format on +} + +async function getExamplesForPath(filePath) { + const componentDirectory = path.join(devtoolsFrontendFolder, 'component_docs', filePath); + const contents = await fs.promises.readdir(componentDirectory); + + return createComponentIndexFile(filePath, contents); +} + +function respondWithHtml(response, html) { + response.setHeader('Content-Type', 'text/html; charset=utf-8'); + response.writeHead(200); + response.write(html, 'utf8'); + response.end(); +} + +function send404(response, message) { + response.writeHead(404); + response.write(message, 'utf8'); + response.end(); +} + +async function requestHandler(request, response) { + const filePath = parseURL(request.url).pathname; + + if (filePath === '/favicon.ico') { + send404(response, '404, no favicon'); + return; + } + + if (filePath === '/' || filePath === '/index.html') { + const components = await fs.promises.readdir(path.join(devtoolsFrontendFolder, 'component_docs')); + const html = createServerIndexFile(components); + respondWithHtml(response, html); + } else if (path.extname(filePath) === '') { + // This means it's a component path like /breadcrumbs. + const componentHtml = await getExamplesForPath(filePath); + respondWithHtml(response, componentHtml); + } else { + // This means it's an asset like a JS file. + const fullPath = path.join(devtoolsFrontendFolder, filePath); + + if (!fullPath.startsWith(devtoolsFrontendFolder)) { + console.error(`Path ${fullPath} is outside the DevTools Frontend root dir.`); + process.exit(1); + } + const errorsAccesingFile = await fs.promises.access(fullPath, fs.constants.R_OK); + + if (errorsAccesingFile) { + console.error(`File ${fullPath} does not exist.`); + send404(response, '404, File not found'); + return; + } + + let encoding = 'utf8'; + if (fullPath.endsWith('.wasm') || fullPath.endsWith('.png') || fullPath.endsWith('.jpg')) { + encoding = 'binary'; + } + + const fileContents = await fs.promises.readFile(fullPath, encoding); + + encoding = 'utf8'; + if (fullPath.endsWith('.js')) { + response.setHeader('Content-Type', 'text/javascript; charset=utf-8'); + } else if (fullPath.endsWith('.css')) { + response.setHeader('Content-Type', 'text/css; charset=utf-8'); + } else if (fullPath.endsWith('.wasm')) { + response.setHeader('Content-Type', 'application/wasm'); + encoding = 'binary'; + } else if (fullPath.endsWith('.svg')) { + response.setHeader('Content-Type', 'image/svg+xml; charset=utf-8'); + } else if (fullPath.endsWith('.png')) { + response.setHeader('Content-Type', 'image/png'); + encoding = 'binary'; + } else if (fullPath.endsWith('.jpg')) { + response.setHeader('Content-Type', 'image/jpg'); + encoding = 'binary'; + } + + response.writeHead(200); + response.write(fileContents, encoding); + response.end(); + } +} diff --git a/chromium/third_party/devtools-frontend/src/scripts/deps/ensure_symlink.py b/chromium/third_party/devtools-frontend/src/scripts/deps/ensure_symlink.py new file mode 100644 index 00000000000..1db12df864d --- /dev/null +++ b/chromium/third_party/devtools-frontend/src/scripts/deps/ensure_symlink.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# +# Copyright 2020 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. +""" +Ensure Devtools-frontend is properly symlinked on gclient sync + +In order to use: +Ensure your .gclient file that manages chromium.src contains this hook +after your list of solutions: + +hooks = [ + { + # Ensure devtools-frontend is symlinked in the correct location + 'name': 'Symlink devtools-frontend', + 'pattern': '.', + 'action': [ + 'python', + '<path>/<to>/devtools-frontend/scripts/deps/ensure_symlink.py', + '<path>/<to>/src', + '<path>/<to>/devtools-frontend' + ], + } +] +""" + +import argparse +import os +import sys +import subprocess + +DEVTOOLS_FRONTEND_CHROMIUM_LOCATION = './third_party/devtools-frontend/src' + + +def symlink(src, dst): + os_symlink = getattr(os, 'symlink', None) + if callable(os_symlink): + # symlink is only available on Unix + os_symlink(src, dst) + else: + # use mklink on windows + subprocess.check_call(['mklink', '/D', dst, src], shell=True) + + +def parse_options(cli_args): + parser = argparse.ArgumentParser( + description='Ensure Devtools is symlinked in a full checkout.') + parser.add_argument('chromium_dir', help='Root of the Chromium Directory') + parser.add_argument('devtools_dir', help='Root of the DevTools directory') + return parser.parse_args(cli_args) + + +def ensure_symlink(options): + chromium_devtools_path = os.path.normpath( + os.path.join(options.chromium_dir, + DEVTOOLS_FRONTEND_CHROMIUM_LOCATION)) + if not os.path.exists(chromium_devtools_path): + symlink(os.path.normpath(options.devtools_dir), chromium_devtools_path) + + +if __name__ == '__main__': + OPTIONS = parse_options(sys.argv[1:]) + ensure_symlink(OPTIONS) diff --git a/chromium/third_party/devtools-frontend/src/scripts/deps/manage_node_deps.py b/chromium/third_party/devtools-frontend/src/scripts/deps/manage_node_deps.py index 3350c039ec5..1cd41896fce 100755 --- a/chromium/third_party/devtools-frontend/src/scripts/deps/manage_node_deps.py +++ b/chromium/third_party/devtools-frontend/src/scripts/deps/manage_node_deps.py @@ -27,12 +27,14 @@ LICENSES = [ "BSD-3-Clause", "CC0-1.0", "CC-BY-3.0", + "CC-BY-4.0", "ISC", ] # List all DEPS here. DEPS = { "@types/chai": "4.2.0", + "@types/estree": "0.0.45", "@types/filesystem": "0.0.29", "@types/mocha": "5.2.7", "@types/puppeteer": "2.0.0", @@ -53,12 +55,15 @@ DEPS = { "karma-sourcemap-loader": "0.3.0", "license-checker": "25.0.1", "mocha": "7.1.1", - "puppeteer": "3.0.3", + "puppeteer": "4.0.0", "recast": "0.18.2", "rimraf": "3.0.2", "rollup": "2.3.3", - "typescript": "3.9.2-insiders.20200509", - "yargs": "15.3.1" + "rollup-plugin-terser": "5.3.0", + "stylelint": "13.5.0", + "stylelint-config-standard": "20.0.0", + "typescript": "3.9.3", + "yargs": "15.3.1", } def exec_command(cmd): @@ -104,6 +109,7 @@ def strip_private_fields(): pkg_file.truncate(0) pkg_file.seek(0) json.dump(pkg_data, pkg_file, indent=2, sort_keys=True, separators=(',', ': ')) + pkg_file.write('\n') except: print('Unable to fix: %s' % pkg) return True @@ -148,6 +154,7 @@ def append_package_json_entries(): pkg_file.truncate(0) pkg_file.seek(0) json.dump(pkg_data, pkg_file, indent=2, sort_keys=True, separators=(',', ': ')) + pkg_file.write('\n') except: print('Unable to fix: %s' % sys.exc_info()[0]) @@ -169,6 +176,7 @@ def remove_package_json_entries(): pkg_file.truncate(0) pkg_file.seek(0) json.dump(pkg_data, pkg_file, indent=2, sort_keys=True, separators=(',', ': ')) + pkg_file.write('\n') except: print('Unable to fix: %s' % pkg) return True @@ -185,6 +193,18 @@ def addClangFormat(): return False +def addOwnersFile(): + with open(path.join(devtools_paths.node_modules_path(), 'OWNERS'), + 'w+') as owners_file: + try: + owners_file.write('file://INFRA_OWNERS') + owners_file.write('') + except: + print('Unable to write OWNERS file') + return True + return False + + def run_npm_command(npm_command_args=None): for (name, version) in DEPS.items(): if (version.find(u'^') == 0): @@ -215,6 +235,9 @@ def run_npm_command(npm_command_args=None): if addClangFormat(): return True + if addOwnersFile(): + return True + if run_custom_command: return custom_command_result diff --git a/chromium/third_party/devtools-frontend/src/scripts/deps/roll_codemirror.py b/chromium/third_party/devtools-frontend/src/scripts/deps/roll_codemirror.py deleted file mode 100755 index 01de71fc975..00000000000 --- a/chromium/third_party/devtools-frontend/src/scripts/deps/roll_codemirror.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 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. -""" -Helper script to update CodeMirror from upstream. -""" - -import argparse -import glob -import os -import shutil -import subprocess -import sys - - -def parse_options(cli_args): - parser = argparse.ArgumentParser(description='Roll CodeMirror') - parser.add_argument('cm_dir', help='CodeMirror directory') - parser.add_argument('devtools_dir', help='DevTools directory') - return parser.parse_args(cli_args) - - -def run_npm(options): - print 'Building CodeMirror in %s' % os.path.abspath(options.cm_dir) - subprocess.check_output(['npm', 'install'], cwd=options.cm_dir, stderr=subprocess.PIPE) - subprocess.check_output(['npm', 'run', 'build'], cwd=options.cm_dir, stderr=subprocess.PIPE) - - -def copy_lib_files(options): - print 'Copying codemirror.js and codemirror.css' - result = '' - target_dir = os.path.join(options.devtools_dir, 'front_end', 'cm') - - with open(os.path.join(options.cm_dir, 'lib', 'codemirror.js'), 'r') as read: - lines = read.readlines() - with open(os.path.join(target_dir, 'codemirror.js'), 'w') as write: - for line in lines: - if 'CodeMirror.version =' in line: - result = line.strip() - write.write(line) - - with open(os.path.join(options.cm_dir, 'lib', 'codemirror.css'), 'r') as read: - lines = read.readlines() - found_stop = False - with open(os.path.join(target_dir, 'codemirror.css'), 'w') as write: - for line in lines: - if found_stop: - write.write(line) - elif '/* STOP */' in line: - found_stop = True - assert found_stop - return result - - -def find_and_copy_js_files(source_dir, target_dir, filter_fn): - for f in os.listdir(target_dir): - if not filter_fn(f): - continue - target_file = os.path.join(target_dir, f) - if not os.path.isfile(os.path.join(target_dir, f)): - continue - source = glob.glob(os.path.join(source_dir, '*', f)) - assert len(source) == 1 - source_file = source[0] - print 'Copying %s from %s' % (target_file, source_file) - shutil.copyfile(source_file, target_file) - - -def copy_cm_files(options): - source_dir = os.path.join(options.cm_dir, 'addon') - target_dir = os.path.join(options.devtools_dir, 'front_end', 'cm') - - def cm_filter(f): - return f.endswith('.js') and f != 'codemirror.js' and f != 'cm.js' - - find_and_copy_js_files(source_dir, target_dir, cm_filter) - - - -if __name__ == '__main__': - OPTIONS = parse_options(sys.argv[1:]) - run_npm(OPTIONS) - copy_cm_files(OPTIONS) - VERSION = copy_lib_files(OPTIONS) - print VERSION diff --git a/chromium/third_party/devtools-frontend/src/scripts/devtools_paths.py b/chromium/third_party/devtools-frontend/src/scripts/devtools_paths.py index 543cfa6e2dc..66b71d3388b 100644 --- a/chromium/third_party/devtools-frontend/src/scripts/devtools_paths.py +++ b/chromium/third_party/devtools-frontend/src/scripts/devtools_paths.py @@ -78,10 +78,6 @@ def typescript_compiler_path(): return path.join(node_modules_path(), 'typescript', 'bin', 'tsc') -def boot_perf_test_path(): - return path.join(devtools_root_path(), 'test', 'perf', 'bootperf.js') - - def hosted_mode_script_path(): return path.join(devtools_root_path(), 'scripts', 'hosted_mode', 'server.js') diff --git a/chromium/third_party/devtools-frontend/src/scripts/eslint_rules/lib/check_license_header.js b/chromium/third_party/devtools-frontend/src/scripts/eslint_rules/lib/check_license_header.js index b358b6a687b..018c341fb00 100644 --- a/chromium/third_party/devtools-frontend/src/scripts/eslint_rules/lib/check_license_header.js +++ b/chromium/third_party/devtools-frontend/src/scripts/eslint_rules/lib/check_license_header.js @@ -54,25 +54,10 @@ const BLOCK_REGEX = new RegExp('[\\s\\\\n\\*]*' + BLOCK_LICENSE_HEADER.join('[\\ const LICENSE_HEADER_ADDITION = LINE_LICENSE_HEADER.map(line => `// ${line}`).join('\n') + '\n\n'; const EXCLUDED_FILES = [ - // FIXME: CodeMirror bundles must be moved to third_party - 'cm/active-line.js', - 'cm/brace-fold.js', - 'cm/closebrackets.js', - 'cm/codemirror.js', - 'cm/comment.js', - 'cm/foldcode.js', - 'cm/foldgutter.js', - 'cm/mark-selection.js', - 'cm/matchbrackets.js', - 'cm/multiplex.js', - 'cm/overlay.js', - 'cm/simple.js', // FIXME: Dagre bundles must be moved to third_party 'dagre_layout/dagre.js', // FIXME: Diff bundles must be moved to third_party 'diff/diff_match_patch.js', - // FIXME: Acorn bundles must be moved to third_party - 'formatter_worker/acorn/acorn_loose.js', ]; const OTHER_LICENSE_HEADERS = [ diff --git a/chromium/third_party/devtools-frontend/src/scripts/eslint_rules/lib/es_modules_import.js b/chromium/third_party/devtools-frontend/src/scripts/eslint_rules/lib/es_modules_import.js index 4222441e452..5e943ebd6c0 100644 --- a/chromium/third_party/devtools-frontend/src/scripts/eslint_rules/lib/es_modules_import.js +++ b/chromium/third_party/devtools-frontend/src/scripts/eslint_rules/lib/es_modules_import.js @@ -11,12 +11,17 @@ const path = require('path'); const FRONT_END_DIRECTORY = path.join(__dirname, '..', '..', '..', 'front_end'); +const INSPECTOR_OVERLAY_DIRECTORY = path.join(__dirname, '..', '..', '..', 'front_end', 'inspector_overlay'); const EXEMPTED_THIRD_PARTY_MODULES = new Set([ // lit-html is exempt as it doesn't expose all its modules from the root file path.join(FRONT_END_DIRECTORY, 'third_party', 'lit-html'), // wasmparser is exempt as it doesn't expose all its modules from the root file path.join(FRONT_END_DIRECTORY, 'third_party', 'wasmparser'), + // acorn is exempt as it doesn't expose all its modules from the root file + path.join(FRONT_END_DIRECTORY, 'third_party', 'acorn'), + // acorn-loose is exempt as it doesn't expose all its modules from the root file + path.join(FRONT_END_DIRECTORY, 'third_party', 'acorn-loose'), ]); const CROSS_NAMESPACE_MESSAGE = @@ -53,7 +58,7 @@ function checkImportExtension(importPath, context, node) { return; } - if (!importPath.endsWith('.js')) { + if (!importPath.endsWith('.js') && !importPath.endsWith('.mjs')) { context.report({ node, message: 'Missing file extension for import "{{importPath}}"', @@ -149,7 +154,7 @@ module.exports = { // // Don't use `importPath` here, as `path.normalize` removes // the `./` from same-folder import paths. - if (!node.source.value.startsWith('.') && !/^\w+$/.test(node.source.value)) { + if (!node.source.value.startsWith('.') && !/^[\w\-_]+$/.test(node.source.value)) { context.report({ node, message: 'Invalid relative URL import. An import should start with either "../" or "./".', @@ -160,6 +165,10 @@ module.exports = { return; } + if (importingFileName.startsWith(INSPECTOR_OVERLAY_DIRECTORY)) { + return; + } + if (isSideEffectImportSpecifier(node.specifiers)) { return; } diff --git a/chromium/third_party/devtools-frontend/src/scripts/hosted_mode/BUILD.gn b/chromium/third_party/devtools-frontend/src/scripts/hosted_mode/BUILD.gn new file mode 100644 index 00000000000..c709a53178b --- /dev/null +++ b/chromium/third_party/devtools-frontend/src/scripts/hosted_mode/BUILD.gn @@ -0,0 +1,9 @@ +# Copyright 2020 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("../build/copy_to_gen.gni") + +copy_to_gen("hosted_mode") { + sources = [ "server.js" ] +} diff --git a/chromium/third_party/devtools-frontend/src/scripts/hosted_mode/launch_chrome.js b/chromium/third_party/devtools-frontend/src/scripts/hosted_mode/launch_chrome.js deleted file mode 100644 index 5a3c95141a2..00000000000 --- a/chromium/third_party/devtools-frontend/src/scripts/hosted_mode/launch_chrome.js +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 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. - -const childProcess = require('child_process'); -const fs = require('fs'); -const path = require('path'); -const shell = childProcess.execSync; - -const utils = require('../utils'); - -const REMOTE_DEBUGGING_PORT = parseInt(process.env.REMOTE_DEBUGGING_PORT, 10) || 9222; -const SERVER_PORT = parseInt(process.env.PORT, 10) || 8090; -const CHROMIUM_DEFAULT_PATH = path.resolve(__dirname, '..', '..', 'third_party', 'chrome', 'chrome-linux', 'chrome'); -const CHROME_PROFILE_PATH = path.resolve(__dirname, '..', '..', '.dev_profile'); - -const Flags = { - RESET_PROFILE: '--reset-profile', -}; - -if (utils.includes(process.argv, Flags.RESET_PROFILE)) { - console.log('Removing your dev profile for Chrome Canary / Chromium at:'); - console.log(CHROME_PROFILE_PATH, '\n'); - utils.removeRecursive(CHROME_PROFILE_PATH); -} - -const chromeArgs = [ - `--remote-debugging-port=${REMOTE_DEBUGGING_PORT}`, - `--custom-devtools-frontend=http://localhost:${SERVER_PORT}/front_end/`, '--no-first-run', - `http://localhost:${REMOTE_DEBUGGING_PORT}#custom=true`, 'https://devtools.chrome.com', - `--user-data-dir=${CHROME_PROFILE_PATH}` -].concat(process.argv.slice(2)); - -if (process.platform === 'win32') { - launchChromeWindows(); - return; -} -if (process.platform === 'darwin') { - launchChromeMac(); - return; -} -if (process.platform === 'linux') { - launchChromeLinux(); - return; -} - -throw new Error(`Unrecognized platform detected: ${process.platform}`); - -function launchChromeWindows() { - let chromeCanaryPath; - if (utils.isFile(process.env.CHROMIUM_PATH)) { - chromeCanaryPath = process.env.CHROMIUM_PATH; - } else { - const suffix = '\\Google\\Chrome SxS\\Application\\chrome.exe'; - const prefixes = [process.env.LOCALAPPDATA, process.env.PROGRAMFILES, process.env['PROGRAMFILES(X86)']]; - for (let i = 0; i < prefixes.length; i++) { - const prefix = prefixes[i]; - try { - chromeCanaryPath = path.join(prefix, suffix); - fs.accessSync(chromeCanaryPath); - break; - } catch (e) { - } - } - } - launchChrome(chromeCanaryPath, chromeArgs); -} - -function launchChromeMac() { - let chromeExecPath; - if (utils.isFile(process.env.CHROMIUM_PATH)) { - chromeExecPath = process.env.CHROMIUM_PATH; - } else { - const lsregister = - '/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister'; - const chromeCanaryPath = shellOutput( - `${lsregister} -dump | grep -i 'applications/google chrome canary.app$' | awk '{$1=""; print $0}' | head -n 1`); - chromeExecPath = `${chromeCanaryPath}/Contents/MacOS/Google Chrome Canary`; - } - launchChrome(chromeExecPath, chromeArgs); -} - -function launchChromeLinux() { - let chromiumPath; - if (utils.isFile(process.env.CHROMIUM_PATH)) { - chromiumPath = process.env.CHROMIUM_PATH; - } else if (utils.isFile(CHROMIUM_DEFAULT_PATH)) { - chromiumPath = CHROMIUM_DEFAULT_PATH; - } else { - onLaunchChromeError(); - return; - } - launchChrome(chromiumPath, chromeArgs); -} - -function launchChrome(filePath, chromeArgs) { - console.log(`Launching Chrome from ${filePath}`); - console.log('Chrome args:', chromeArgs.join(' '), '\n'); - let child; - try { - child = childProcess.spawn(filePath, chromeArgs, { - stdio: 'inherit', - }); - } catch (error) { - onLaunchChromeError(); - return; - } - child.on('error', onLaunchChromeError); - child.on('exit', onExit); - function onExit(code) { - console.log('Exited Chrome with code', code); - } -} - -function onLaunchChromeError() { - if (process.platform !== 'linux') { - console.log('ERROR: Cannot find Chrome Canary on your computer'); - console.log('Install Chome Canary at:'); - console.log('https://www.google.com/chrome/browser/canary.html\n'); - } else { - console.log('ERROR: Could not launch Chromium'); - console.log('The environment variable CHROMIUM_PATH must be set to executable of a build of Chromium'); - console.log('If you do not have a recent build of chromium, you can get one from:'); - console.log('https://download-chromium.appspot.com/\n'); - } -} - -function shellOutput(command) { - return shell(command).toString().trim(); -} diff --git a/chromium/third_party/devtools-frontend/src/scripts/hosted_mode/server.js b/chromium/third_party/devtools-frontend/src/scripts/hosted_mode/server.js index 84c1ae2a626..de362c6e4b3 100644 --- a/chromium/third_party/devtools-frontend/src/scripts/hosted_mode/server.js +++ b/chromium/third_party/devtools-frontend/src/scripts/hosted_mode/server.js @@ -6,11 +6,9 @@ const http = require('http'); const path = require('path'); const parseURL = require('url').parse; -const utils = require('../utils'); - const remoteDebuggingPort = parseInt(process.env.REMOTE_DEBUGGING_PORT, 10) || 9222; const serverPort = parseInt(process.env.PORT, 10) || 8090; -const devtoolsFolder = path.resolve(path.join(__dirname, '../..')); +const devtoolsFolder = path.resolve(path.join(__dirname, '..', '..')); http.createServer(requestHandler).listen(serverPort); console.log(`Started hosted mode server at http://localhost:${serverPort}\n`); @@ -26,18 +24,6 @@ function requestHandler(request, response) { return; } - const 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'); - } - const absoluteFilePath = path.join(devtoolsFolder, filePath); if (!path.resolve(absoluteFilePath).startsWith(devtoolsFolder)) { console.log(`File requested (${absoluteFilePath}) is outside of devtools folder: ${devtoolsFolder}`); @@ -80,7 +66,7 @@ function requestHandler(request, response) { } let encoding = 'utf8'; - if (path.endsWith('.js')) { + if (path.endsWith('.js') || path.endsWith('.mjs')) { response.setHeader('Content-Type', 'text/javascript; charset=utf-8'); } else if (path.endsWith('.css')) { response.setHeader('Content-Type', 'text/css; charset=utf-8'); @@ -132,60 +118,3 @@ function requestHandler(request, response) { response.end(); } } - -const proxyFilePathToURL = { - '/favicon.ico': () => 'https://chrome-devtools-frontend.appspot.com/favicon.ico', -}; - -const proxyFileCache = new Map(); - -function proxy(filePath) { - if (!(filePath in proxyFilePathToURL)) { - return null; - } - 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 onBrowserMetadata(metadata) { - const metadataObject = JSON.parse(metadata); - const match = metadataObject['WebKit-Version'].match(/\s\(@(\b[0-9a-f]{5,40}\b)/); - const commitHash = match[1]; - const 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() { - const isFullCheckout = utils.shellOutput('git config --get remote.origin.url') === - 'https://chromium.googlesource.com/chromium/src.git'; - let earlierCommitHash; - const gitLogCommand = 'git log --max-count=1 --grep="Commit-Position" --before="12 hours ago"'; - if (isFullCheckout) { - earlierCommitHash = utils.shellOutput(`${gitLogCommand} --pretty=format:"%H"`); - } else { - const commitMessage = utils.shellOutput(`${gitLogCommand}`); - earlierCommitHash = commitMessage.match(/Cr-Mirrored-Commit: (.*)/)[1]; - } - const 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; - } -} diff --git a/chromium/third_party/devtools-frontend/src/scripts/hosted_mode/start_chrome_and_server.js b/chromium/third_party/devtools-frontend/src/scripts/hosted_mode/start_chrome_and_server.js deleted file mode 100644 index d42e3e01d14..00000000000 --- a/chromium/third_party/devtools-frontend/src/scripts/hosted_mode/start_chrome_and_server.js +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 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. - -const childProcess = require('child_process'); - -const chrome = childProcess.fork('scripts/hosted_mode/launch_chrome.js', process.argv.slice(2)); -const server = childProcess.fork('scripts/hosted_mode/server.js'); - -chrome.on('exit', function() { - server.kill(); -}); diff --git a/chromium/third_party/devtools-frontend/src/scripts/localization/check_localizability.js b/chromium/third_party/devtools-frontend/src/scripts/localization/check_localizability.js index 88c1cc5b6a9..f5d90d5cc57 100644 --- a/chromium/third_party/devtools-frontend/src/scripts/localization/check_localizability.js +++ b/chromium/third_party/devtools-frontend/src/scripts/localization/check_localizability.js @@ -90,6 +90,13 @@ function includesConditionalExpression(listOfElements) { return listOfElements.filter(ele => ele !== undefined && ele.type === espreeTypes.COND_EXPR).length > 0; } +function includesGritPlaceholders(cookedValue) { + // $[0-9] is a GRIT placeholder for Chromium l10n, unfortunately it cannot be escaped. + // https://chromium-review.googlesource.com/c/chromium/src/+/1405148 + const regexPattern = /\$[0-9]+/g; + return regexPattern.test(cookedValue); +} + function addError(error, errors) { if (!errors.includes(error)) { errors.push(error); @@ -211,6 +218,15 @@ function analyzeNode(parentNode, node, filePath, errors) { code}. Please extract conditional(s) out of the localization call.`, errors); } + + if (node.arguments[0].type === espreeTypes.LITERAL && includesGritPlaceholders(node.arguments[0].value)) { + addError( + `${localizationUtils.getRelativeFilePathFromSrc(filePath)}${ + localizationUtils.getLocationMessage(node.loc)}: possible placeholder(s) found in ${ + code}. Please extract placeholders(s) out of the localization call.`, + errors); + } + break; } @@ -222,6 +238,14 @@ function analyzeNode(parentNode, node, filePath, errors) { code}. Please extract conditional(s) out of the localization call.`, errors); } + + if (includesGritPlaceholders(node.quasi.quasis[0].value.cooked)) { + addError( + `${localizationUtils.getRelativeFilePathFromSrc(filePath)}${ + localizationUtils.getLocationMessage(node.loc)}: possible placeholder(s) found in ${ + code}. Please extract placeholders(s) out of the localization call.`, + errors); + } break; } diff --git a/chromium/third_party/devtools-frontend/src/scripts/localization/check_localizable_resources.js b/chromium/third_party/devtools-frontend/src/scripts/localization/check_localizable_resources.js index dd21e7d47f1..837e69cf36e 100644 --- a/chromium/third_party/devtools-frontend/src/scripts/localization/check_localizable_resources.js +++ b/chromium/third_party/devtools-frontend/src/scripts/localization/check_localizable_resources.js @@ -85,7 +85,7 @@ async function autofix(existingError) { if (shouldAddExampleTag) { message += ' Add example tag(s) <ex> for messages that contain placeholder(s)'; } - message += '\nFor more details, see devtools/docs/langpacks/grdp_files.md'; + message += '\nFor more details, see src/docs/localization/grdp_files.md'; } if (resourceRemoved && duplicateRemoved(keysToRemoveFromGRD)) { message += '\nDuplicate <message> entries are removed. Please verify the retained descriptions are correct.'; diff --git a/chromium/third_party/devtools-frontend/src/scripts/localization/utils/check_localized_strings.js b/chromium/third_party/devtools-frontend/src/scripts/localization/utils/check_localized_strings.js index ea706c12ad3..652ad6e9c66 100644 --- a/chromium/third_party/devtools-frontend/src/scripts/localization/utils/check_localized_strings.js +++ b/chromium/third_party/devtools-frontend/src/scripts/localization/utils/check_localized_strings.js @@ -86,7 +86,9 @@ async function validateGrdFile(shouldAutoFix) { const fileLines = fileContent.split('\n'); const newLines = []; let errors = ''; - fileLines.forEach(line => errors += validateGrdLine(line, newLines)); + fileLines.forEach(line => { + errors += validateGrdLine(line, newLines); + }); if (errors !== '' && shouldAutoFix) { await writeFileAsync(localizationUtils.GRD_PATH, newLines.join('\n')); } @@ -129,9 +131,9 @@ async function validateGrdpFiles(shouldAutoFix) { let errors = ''; const renameFilePromises = []; const grdpFilesToAddToGrd = []; - frontendDirsToGrdpFiles.forEach( - (grdpFiles, dir) => errors += - validateGrdpFile(dir, grdpFiles, grdFileContent, shouldAutoFix, renameFilePromises, grdpFilesToAddToGrd)); + frontendDirsToGrdpFiles.forEach((grdpFiles, dir) => { + errors += validateGrdpFile(dir, grdpFiles, grdFileContent, shouldAutoFix, renameFilePromises, grdpFilesToAddToGrd); + }); if (grdpFilesToAddToGrd.length > 0) { await localizationUtils.addChildGRDPFilePathsToGRD(grdpFilesToAddToGrd.sort()); } @@ -582,9 +584,11 @@ function getAndReportResourcesToRemove() { // Example error message: // third_party/devtools-frontend/front_end/accessibility/accessibility_strings.grdp Line 300: IDS_DEVTOOLS_c9bbad3047af039c14d0e7ec957bb867 for (const [ids, messages] of keysToRemoveFromGRD) { - messages.forEach( - message => errorStr += `${localizationUtils.getRelativeFilePathFromSrc(message.grdpPath)}${ - localizationUtils.getLocationMessage(message.location)}: ${ids}\n\n`); + messages.forEach(message => { + const path = localizationUtils.getRelativeFilePathFromSrc(message.grdpPath); + const msg = localizationUtils.getLocationMessage(message.location); + errorStr += `${path}${msg}: ${ids}\n\n`; + }); } return errorStr; } @@ -599,10 +603,11 @@ function getAndReportIDSKeysToModify() { errorStr += 'Please update the key(s) by changing the "name" value.\n\n'; for (const [expectedIDSKey, messages] of messagesToModify) { - messages.forEach( - message => errorStr += `${localizationUtils.getRelativeFilePathFromSrc(message.grdpPath)}${ - localizationUtils.getLocationMessage( - message.location)}:\n${message.actualIDSKey} --> ${expectedIDSKey}\n\n`); + messages.forEach(message => { + const path = localizationUtils.getRelativeFilePathFromSrc(message.grdpPath); + const msg = localizationUtils.getLocationMessage(message.location); + errorStr += `${path}${msg}:\n${message.actualIDSKey} --> ${expectedIDSKey}\n\n`; + }); } return errorStr; } diff --git a/chromium/third_party/devtools-frontend/src/scripts/npm_test.js b/chromium/third_party/devtools-frontend/src/scripts/npm_test.js index a35bab85376..c2e6e44b84e 100644 --- a/chromium/third_party/devtools-frontend/src/scripts/npm_test.js +++ b/chromium/third_party/devtools-frontend/src/scripts/npm_test.js @@ -5,7 +5,6 @@ const childProcess = require('child_process'); const fs = require('fs'); const path = require('path'); -const shell = require('child_process').execSync; const utils = require('./utils'); const Flags = { @@ -19,12 +18,8 @@ const Flags = { const IS_DEBUG_ENABLED = utils.includes(process.argv, Flags.DEBUG_DEVTOOLS) || utils.includes(process.argv, Flags.DEBUG_DEVTOOLS_SHORTHAND); const CUSTOM_CHROMIUM_PATH = utils.parseArgs(process.argv)[Flags.CHROMIUM_PATH]; -const IS_FETCH_CONTENT_SHELL = utils.includes(process.argv, Flags.FETCH_CONTENT_SHELL); const TARGET = utils.parseArgs(process.argv)[Flags.TARGET] || 'Release'; -const CONTENT_SHELL_ZIP = 'content-shell.zip'; -const MAX_CONTENT_SHELLS = 10; -const PLATFORM = getPlatform(); const PYTHON = process.platform === 'win32' ? 'python.bat' : 'python'; const CURRENT_PATH = process.env.PWD || process.cwd(); // Using env.PWD to account for symlinks. @@ -34,76 +29,22 @@ const RELEASE_PATH = path.resolve(CHROMIUM_SRC_PATH, 'out', TARGET); const BLINK_TEST_PATH = path.resolve(CHROMIUM_SRC_PATH, 'third_party', 'blink', 'tools', 'run_web_tests.py'); const DEVTOOLS_PATH = path.resolve(CHROMIUM_SRC_PATH, 'third_party', 'devtools-frontend', 'src'); const CACHE_PATH = path.resolve(DEVTOOLS_PATH, '.test_cache'); -const SOURCE_PATH = path.resolve(DEVTOOLS_PATH, 'front_end'); function main() { if (!utils.isDir(CACHE_PATH)) { fs.mkdirSync(CACHE_PATH); } - deleteOldContentShells(); const hasUserCompiledContentShell = utils.isFile(getContentShellBinaryPath(RELEASE_PATH)); - if (!IS_FETCH_CONTENT_SHELL && hasUserCompiledContentShell) { - const outDir = path.resolve(RELEASE_PATH, '..'); - if (!IS_DEBUG_ENABLED) { - compileFrontend(); - } - - runTests(outDir, IS_DEBUG_ENABLED); + if (!hasUserCompiledContentShell) { return; } + const outDir = path.resolve(RELEASE_PATH, '..'); - findPreviousUploadedPosition(findMostRecentChromiumCommit()) - .then(onUploadedCommitPosition) - .catch(onError); - - function onError(error) { - console.log('Unable to run tests because of error:', error); - console.log(`Try removing the .test_cache folder [${CACHE_PATH}] and retrying`); - } + runTests(outDir, IS_DEBUG_ENABLED); } main(); -function compileFrontend() { - console.log('Compiling devtools frontend'); - try { - shell(`ninja -C ${RELEASE_PATH} devtools_frontend_resources`, {cwd: CHROMIUM_SRC_PATH}); - } catch (err) { - console.log(err.stdout.toString()); - console.log('ERROR: Cannot compile frontend\n' + err); - process.exit(1); - } -} - -function onUploadedCommitPosition(commitPosition) { - const contentShellDirPath = path.resolve(CACHE_PATH, commitPosition, 'out', TARGET); - const contentShellResourcesPath = path.resolve(contentShellDirPath, 'resources'); - const contentShellPath = path.resolve(CACHE_PATH, commitPosition, 'out'); - - const hasCachedContentShell = utils.isFile(getContentShellBinaryPath(contentShellDirPath)); - if (hasCachedContentShell) { - console.log(`Using cached content shell at: ${contentShellPath}`); - copyFrontend(contentShellResourcesPath); - return runTests(contentShellPath, true); - } - const url = `http://commondatastorage.googleapis.com/chromium-browser-snapshots/${PLATFORM}/${commitPosition - }/${CONTENT_SHELL_ZIP}`; - return prepareContentShellDirectory(commitPosition) - .then(() => downloadContentShell(url, commitPosition)) - .then(extractContentShell) - .then(() => copyFrontend(contentShellResourcesPath)) - .then(() => runTests(contentShellPath, true)); -} - -function copyFrontend(contentShellResourcesPath) { - const devtoolsResourcesPath = path.resolve(contentShellResourcesPath, 'inspector'); - const copiedFrontendPath = path.resolve(devtoolsResourcesPath, 'front_end'); - const debugFrontendPath = path.resolve(devtoolsResourcesPath, 'debug'); - utils.removeRecursive(copiedFrontendPath); - utils.removeRecursive(debugFrontendPath); - utils.copyRecursive(SOURCE_PATH, devtoolsResourcesPath); - fs.renameSync(copiedFrontendPath, debugFrontendPath); -} function getChromiumSrcPath(isThirdParty) { if (isThirdParty) @@ -124,118 +65,6 @@ function getChromiumSrcPath(isThirdParty) { return srcPath; } -function getPlatform() { - if (process.platform === 'linux') { - return 'Linux_x64'; - } - if (process.platform === 'win32') { - return 'Win_x64'; - } - if (process.platform === 'darwin') { - return 'Mac'; - } - - throw new Error(`Unrecognized platform detected: ${process.platform}`); -} - -function findMostRecentChromiumCommit() { - const commitMessage = shell('git log --max-count=1 --grep="Cr-Commit-Position"').toString().trim(); - const commitPosition = commitMessage.match(/Cr-Commit-Position: refs\/heads\/master@\{#([0-9]+)\}/)[1]; - return commitPosition; -} - -function deleteOldContentShells() { - const files = fs.readdirSync(CACHE_PATH); - if (files.length < MAX_CONTENT_SHELLS) { - return; - } - files.sort((a, b) => parseInt(b, 10) - parseInt(a, 10)); - const remainingNumberOfContentShells = MAX_CONTENT_SHELLS / 2; - const oldContentShellDirs = files.slice(remainingNumberOfContentShells); - for (let i = 0; i < oldContentShellDirs.length; i++) { - utils.removeRecursive(path.resolve(CACHE_PATH, oldContentShellDirs[i])); - } - console.log(`Removed old content shells: ${oldContentShellDirs}`); -} - -function findPreviousUploadedPosition(commitPosition) { - const previousPosition = commitPosition - 100; - const positionsListURL = - `http://commondatastorage.googleapis.com/chromium-browser-snapshots/?delimiter=/&prefix=${PLATFORM - }/&marker=${PLATFORM}/${previousPosition}/`; - return utils.fetch(positionsListURL).then(onPositionsList).catch(onError); - - function onPositionsList(buffer) { - const positions = buffer.toString('binary') - .match(/([^<>]+)(?=<\/Prefix><\/CommonPrefixes>)/g) - .map(prefixedPosition => prefixedPosition.split('/')[1]) - .map(positionString => parseInt(positionString, 10)); - const positionSet = new Set(positions); - let previousUploadedPosition = commitPosition; - while (commitPosition - previousUploadedPosition < 100) { - if (positionSet.has(previousUploadedPosition)) { - return previousUploadedPosition.toString(); - } - previousUploadedPosition--; - } - onError(); - } - - function onError(error) { - if (error) { - console.log(`Received error: ${error} trying to fetch positions list from url: ${positionsListURL}`); - } - throw new Error(`Unable to find a previous upload position for commit position: ${commitPosition}`); - } -} - -async function prepareContentShellDirectory(folder) { - const contentShellPath = path.join(CACHE_PATH, folder); - if (utils.isDir(contentShellPath)) { - utils.removeRecursive(contentShellPath); - } - fs.mkdirSync(contentShellPath); - return folder; -} - -function downloadContentShell(url, folder) { - console.log('Downloading content shell from:', url); - console.log('NOTE: Download is ~35-65 MB depending on OS'); - return utils.fetch(url).then(writeZip).catch(onError); - - function writeZip(buffer) { - console.log('Completed download of content shell'); - const contentShellZipPath = path.join(CACHE_PATH, folder, CONTENT_SHELL_ZIP); - fs.writeFileSync(contentShellZipPath, buffer); - return contentShellZipPath; - } - - function onError(error) { - console.log(`Received error: ${error} trying to download content shell from url: ${url}`); - throw new Error('Unable to download content shell'); - } -} - -function extractContentShell(contentShellZipPath) { - console.log(`Extracting content shell zip: ${contentShellZipPath}`); - const unzipScriptPath = path.resolve(__dirname, 'unzip.py'); - const src = contentShellZipPath; - const dest = path.resolve(path.dirname(src), 'out'); - shell(`${PYTHON} ${unzipScriptPath} ${src} ${dest}`); - fs.unlinkSync(src); - const originalDirPath = path.resolve(dest, 'content-shell'); - const newDirPath = path.resolve(dest, TARGET); - fs.renameSync(originalDirPath, newDirPath); - fs.chmodSync(getContentShellBinaryPath(newDirPath), '755'); - if (process.platform === 'darwin') { - const helperPath = path.resolve( - newDirPath, 'Content Shell.app', 'Contents', 'Frameworks', 'Content Shell Helper.app', 'Contents', 'MacOS', - 'Content Shell Helper'); - fs.chmodSync(helperPath, '755'); - } - return dest; -} - function getContentShellBinaryPath(dirPath) { if (process.platform === 'linux') { return path.resolve(dirPath, 'content_shell'); diff --git a/chromium/third_party/devtools-frontend/src/scripts/protocol_typescript/protocol_dts_generator.ts b/chromium/third_party/devtools-frontend/src/scripts/protocol_typescript/protocol_dts_generator.ts index f489ef74dfe..b0de18245b1 100644 --- a/chromium/third_party/devtools-frontend/src/scripts/protocol_typescript/protocol_dts_generator.ts +++ b/chromium/third_party/devtools-frontend/src/scripts/protocol_typescript/protocol_dts_generator.ts @@ -81,6 +81,13 @@ const emitGlobalTypeDefs = () => { emitLine('getError(): string|undefined;'); numIndents--; emitLine('}'); + emitLine('export type UsesObjectNotation = true;'); + emitLine('export interface Dispatcher {'); + numIndents++; + emitLine('/** This dispatcher requires objects as parameters, rather than multiple arguments */'); + emitLine('usesObjectNotation(): UsesObjectNotation;'); + numIndents--; + emitLine('}'); }; const emitDomain = (domain: Protocol.Domain) => { @@ -368,7 +375,7 @@ const emitDomainApi = (domain: Protocol.Domain, modulePrefix: string) => { domain.commands.forEach(c => emitApiCommand(c, domainName, modulePrefix)); } emitCloseBlock(); - emitOpenBlock(`export interface ${domainName}Dispatcher`); + emitOpenBlock(`export interface ${domainName}Dispatcher extends Protocol.Dispatcher`); if (domain.events) { domain.events.forEach(e => emitApiEvent(e, domainName, modulePrefix)); } @@ -395,6 +402,12 @@ const emitApi = (moduleName: string, protocolModuleName: string, domains: Protoc domains.forEach(d => emitDomainApi(d, protocolModulePrefix)); emitCloseBlock(); emitLine(); + + emitLine('// Include the workaround for https://github.com/microsoft/TypeScript/issues/38640'); + domains.forEach(d => { + emitLine( + `interface ProtocolProxyApiWorkaround_${d.domain}Dispatcher extends ProtocolProxyApi.${d.domain}Dispatcher {}`); + }); }; const flushEmitToFile = (path: string) => { diff --git a/chromium/third_party/devtools-frontend/src/scripts/suggest_owners.js b/chromium/third_party/devtools-frontend/src/scripts/suggest_owners.js new file mode 100644 index 00000000000..becf8781b64 --- /dev/null +++ b/chromium/third_party/devtools-frontend/src/scripts/suggest_owners.js @@ -0,0 +1,102 @@ +// Copyright 2020 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. + +// Suggest owners based on authoring and reviewing contributions. +// Usage: node suggest-owners.js <since-date> <path> +const {promisify} = require('util'); +const exec = promisify(require('child_process').exec); +const readline = require('readline'); +const fs = require('fs'); +const path = require('path'); +const file_path = process.argv[3]; +const date = process.argv[2]; + +readline.Interface.prototype.question[promisify.custom] = function(prompt) { + return new Promise( + resolve => readline.Interface.prototype.question.call(this, prompt, resolve), + ); +}; +readline.Interface.prototype.questionAsync = promisify( + readline.Interface.prototype.question, +); + +(async function() { + const {stdout} = await exec(`git log --numstat --since=${date} ${file_path}`, {maxBuffer: 1024 * 1024 * 128}); + const structured_log = []; + // Parse git log into a list of commit objects. + for (const line of stdout.split('\n')) { + if (line.startsWith('commit')) { + // Start of a new commit + const match = /^commit (?<hash>\p{ASCII_Hex_Digit}+)/u.exec(line); + structured_log.push({ + commit: match.groups.hash, + contributors: new Set(), + }); + continue; + } + const commit = structured_log[structured_log.length - 1]; + let match; + if ((match = line.match(/^Author: .*<(?<author>.+@.+\..+)>$/))) { + commit.contributors.add(match.groups.author); + } else if ((match = line.match(/Reviewed-by: .*<(?<reviewer>.+@.+\..+)>$/))) { + commit.contributors.add(match.groups.reviewer); + } + } + + // Attribute commits to contributors. + const contributor_to_commits = new Map(); + for (commit of structured_log) { + for (const contributor of commit.contributors) { + if (!contributor_to_commits.has(contributor)) { + contributor_to_commits.set(contributor, 1); + } else { + contributor_to_commits.set(contributor, contributor_to_commits.get(contributor) + 1); + } + } + } + + // Output contributors. + let list = []; + for (const [contributor, commits] of contributor_to_commits) { + list.push({contributor, commits}); + } + list.sort((a, b) => b.commits - a.commits); + console.log('Contributions'); + for (const {contributor, commits} of list) { + console.log(` ${contributor.padEnd(30)}: ${String(commits).padStart(3)}`); + } + + const owners_path = path.join(file_path, 'OWNERS'); + // Output existing OWNERS file if exists. + if (fs.existsSync(owners_path)) { + console.log('Content of existing OWNERS file\n'); + console.log(fs.readFileSync(owners_path).toString()); + } + + // Prompt cut off value to suggest owners. + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + const input = await rl.questionAsync('Cut off at: '); + const cutoff = parseInt(input, 10); + if (isNaN(cutoff)) { + return; + } + + console.log('Proposed owners'); + list = list.filter(item => item.commits >= cutoff) + .sort((a, b) => a.contributor.toLowerCase().localeCompare(b.contributor.toLowerCase())); + for (const {contributor} of list) { + console.log(' ' + contributor); + } + + // Prompt to write to OWNERS file. + if ((await rl.questionAsync(`Write to ${owners_path} ?`)).toLowerCase() === 'y') { + fs.writeFileSync(owners_path, list.map(e => e.contributor).join('\n') + '\n'); + await exec(`git add ${owners_path}`); + } + + rl.close(); +})(); |