0

WebUI: Add syntax highlighting to docs/webui/ code snippets.

Bug: None
Change-Id: Ib88ad238b6a2e756e9050876b37a984dd88d6c04
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6388416
Auto-Submit: Demetrios Papadopoulos <dpapad@chromium.org>
Commit-Queue: Demetrios Papadopoulos <dpapad@chromium.org>
Reviewed-by: Teresa Mao <temao@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1437215}
This commit is contained in:
dpapad
2025-03-24 17:21:43 -07:00
committed by Chromium LUCI CQ
parent ede58b1274
commit 994a93969c
8 changed files with 76 additions and 78 deletions

@ -5,7 +5,7 @@
In order to build with a fast configuration, try setting these options in your
GN args:
```
```python
optimize_webui = true
is_debug = false
```

@ -71,11 +71,10 @@ class FooBarBrowserTest : public WebUIMochaBrowserTest {
// Necessary if you have features (or the UI itself) behind a feature flag.
base::test::ScopedFeatureList scoped_feature_list_{foo_bar::kSomeNewFeature};
};
```
Note: If testing a `chrome-untrusted://` UI, also call:
```
```c++
set_test_loader_scheme(content::kChromeUIUntrustedScheme);
```
in the constructor.
@ -89,7 +88,6 @@ IN_PROC_BROWSER_TEST_F(FooBarAppTest, All) {
// Run all Mocha test suites found in app_test
RunTest("foo_bar/app_test.js", "mocha.run();");
}
```
Should you write a large number of tests in a single file (e.g., for
one particular custom element), it may be necessary to run different suites or
@ -205,7 +203,7 @@ suite('SearchEmpty', function() {
To build your tests as part of the `browser_tests` target, add a BUILD.gn file
in your folder:
```
```python
# chrome/test/data/webui/foo_bar/BUILD.gn
import("../build_webui_tests.gni")
@ -239,7 +237,7 @@ importing shared test files from `chrome-untrusted://webui-test/` paths.
You then need to hook up the targets generated by this file, and list your
browsertest.cc source file, in `chrome/test/data/webui/BUILD.gn`:
```
```python
# chrome/test/data/webui/BUILD.gn
source_set("browser_tests") {
@ -282,8 +280,8 @@ generate_grd("build_grd") {
# Conditionally added (e.g. platform-specific) grdps and deps go here
}
```
### Unusual Test Setups
Some UIs may need to rely on loading a particular URL, such that using the
default `chrome://foo-bar/test_loader.html` URL that is used to dynamically

@ -25,7 +25,7 @@ in the WebUI renderer such as memory corruption bugs.
should have an if statement to check the Trusted Types support before using
methods/properties under `window.trustedTypes` :**
```
```ts
if (window.trustedTypes) {
// Trusted Types is supported, let's use Trusted Types 😎
elem.innerHTML = trustedTypes.emptyHTML;
@ -39,7 +39,7 @@ if (window.trustedTypes) {
Example code:
```
```ts
document.body.innerHTML = '';
```
@ -47,7 +47,7 @@ This will be a Trusted Types violation because the value we are assigning to
a dangerous sink is not a Trusted Type.
This can be converted to:
```
```ts
document.body.innerHTML = trustedTypes.emptyHTML;
```
@ -57,13 +57,13 @@ There is also `trustedTypes.emptyScript` to clear script contents.
Example code:
```
```ts
document.body.innerHTML = 'Hello Guest!';
```
Because this is just a text assignment, this can be converted to:
```
```ts
document.body.textContent = 'Hello Guest!';
```
@ -73,14 +73,14 @@ document.body.textContent = 'Hello Guest!';
Example code:
```
```ts
document.body.innerHTML = '<div><p>' + loadTimeData.getString('foo') + '</p>
</div>';
```
This can be converted by adding _template_ element to HTML file:
```
```html
<template id="foo-template">
<div>
<p></p>
@ -90,7 +90,7 @@ This can be converted by adding _template_ element to HTML file:
And then adding following JS code to JS file:
```
```ts
// body might already have some contents, so let's clear those first 😊
document.body.innerHTML = trustedTypes.emptyHTML;
@ -105,13 +105,13 @@ In cases where you don't have control over the HTML file (e.g. converting common
JS libraries), you can use DOM APIs.
Example code:
```
```ts
document.body.innerHTML += '<p>' + loadTimeData.getString('foo') + '</p>';
```
And then adding following JS code to JS file:
```
```ts
const p = document.createElement('p');
p.textContent = loadTimeData.getString('foo');
document.body.appendChild(p);
@ -124,7 +124,7 @@ readability, you can [use `trustedTypes.createPolicy`]
(https://web.dev/trusted-types/#create-a-trusted-type-policy).
Example code:
```
```ts
document.body.innerHTML = '<div class="tree-row">' +
'<span class="expand-icon"></span>' +
'<span class="tree-label-icon"></span>' +
@ -135,7 +135,7 @@ document.body.innerHTML = '<div class="tree-row">' +
This can be converted to:
```
```ts
const htmlString = '<div class="tree-row">' +
'<span class="expand-icon"></span>' +
'<span class="tree-label-icon"></span>' +
@ -155,7 +155,7 @@ This case also requires changes in C++, as we need to allow the `foo-static`
Trusted Type policy (created above in `trustedTypes.createPolicy`) in the CSP
header.
```
```c++
source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::TrustedTypes,
"trusted-types foo-static;");
@ -165,7 +165,7 @@ source->OverrideContentSecurityPolicy(
Example code:
```
```ts
const script = document.createElement('script');
script.src = 'chrome://resources/foo.js';
document.body.appendChild(script);
@ -173,7 +173,7 @@ document.body.appendChild(script);
This can be converted to:
```
```ts
const staticUrlPolicy = trustedTypes.createPolicy(
'foo-js-static',
{createScriptURL: () => 'chrome://resources/foo.js'});
@ -189,7 +189,7 @@ This case also requires changes in C++, as we need to allow the `foo-js-static`
Trusted Type policy (created above in `trustedTypes.createPolicy`) in the CSP
header.
```
```c++
source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::TrustedTypes,
"trusted-types foo-js-static;");
@ -200,7 +200,7 @@ source->OverrideContentSecurityPolicy(
In case there is no way to support Trusted Types in a WebUI page, you can
disable Trusted Types with following code:
```
```c++
source->DisableTrustedTypesCSP();
```

@ -72,7 +72,7 @@ scheme: One of ['chrome', 'relative']. Defaults to 'relative'. Specifies whether
```
#### **Example**
```
```python
import("//tools/polymer/html_to_wrapper.gni")
import("//tools/polymer/css_to_wrapper.gni")
@ -115,7 +115,7 @@ defines: Optional parameter. Specifies additional variables that can be used in
conditional expressions.
```
#### **Example:**
```
```python
import("//tools/grit/preprocess_if_expr.gni")
# Preprocesses "my_web_component.html.ts" and my_style_module.css.ts in
@ -223,7 +223,7 @@ enable_source_maps: Defaults to the value of the enable_webui_inline_sourcemaps
#### **Example**
```
```python
import("//tools/typescript/ts_library.gni")
# Compiles and outputs my_webui.js, my_web_component.js and
@ -362,7 +362,7 @@ out_folder: The location where bundled files will be placed in. Defaults to
```
#### **Example**
```
```python
import("//ui/webui/resources/tools/bundle_js.gni")
import ("//chrome/common/features.gni")
@ -406,7 +406,7 @@ deps: Targets generating any files being minified.
```
#### **Example**
```
```python
import("//ui/webui/resources/tools/minify_js.gni")
import ("//chrome/common/features.gni")
@ -444,7 +444,7 @@ deps: Targets generating any files being minified.
```
#### **Example**
```
```python
import("//ui/webui/resources/tools/generate_code_cache.gni")
import ("//chrome/common/features.gni")
@ -497,7 +497,7 @@ resource_path_rewrites: Paths to rewrite. In general, the path in input_files,
```
#### **Example**
```
```python
import("//ui/webui/resources/tools/generate_grd.gni")
generate_grd("build_grd") {
@ -539,7 +539,7 @@ deps: Names of any targets generating the grd file, and names of any targets
```
#### **Example**
```
```python
import("//tools/grit/grit_rule.gni")
grit("resources") {
@ -715,7 +715,7 @@ enable_type_aware_eslint_checks: Defaults to "true". Turns on additional
```
#### **Example**
```
```python
import("//ui/webui/resources/tools/build_webui.gni")
build_webui("build") {
@ -821,7 +821,7 @@ enable_type_aware_eslint_checks: Defaults to "true". Turns on additional
```
#### **Example**
```
```python
import("//chrome/test/data/webui/settings/tools/build_webui_tests.gni")
build_webui_tests("build") {
@ -862,7 +862,7 @@ use any `<if expr>`.
```
#### **BUILD.gn file**
```
```python
import("//chrome/common/features.gni")
import("//tools/grit/grit_rule.gni")
import("//tools/typescript/ts_library.gni")
@ -915,7 +915,7 @@ for the top level index. None of these files use any `<if expr>`.
```
#### **BUILD.gn file**
```
```python
import("//chrome/common/features.gni")
import("//tools/grit/grit_rule.gni")
import("//tools/typescript/ts_library.gni")

@ -124,7 +124,7 @@ Third, **add dependencies and corresponding path mappings** in the build
targets that depend on the new shared library. This is best demonstrated with
an example:
```
```python
build_webui("build_my_webui") {
grd_prefix = "my_webui"
# Other params here

@ -512,7 +512,7 @@ mojom() is the build rule used to generate mojo bindings. It can be set up as
follows:
**chrome/browser/ui/webui/donuts/BUILD.gn**
```
```python
import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojo_bindings") {
@ -673,7 +673,7 @@ added to the build and served from the root (e.g.
`build_webui()`:
**chrome/browser/resources/donuts/BUILD.gn**
```
```python
import("//ui/webui/resources/tools/build_webui.gni")
build_webui("build") {
@ -743,7 +743,7 @@ calling the method returns a promise. The promise will be resolved with the
response from the browser.
**chrome/browser/resources/donuts/donuts.ts**
```js
```ts
import {BrowserProxyImpl} from './browser_proxy.js';
let numDonutsBaked: number = 0;
@ -864,7 +864,7 @@ void OvenHandler::OnPilotLightExtinguished() {
This works by crafting a string to be evaluated in the renderer. Any arguments
to the call are serialized to JSON and the parameter list is wrapped with
```
```c++
// See WebUI::GetJavascriptCall() for specifics:
"functionCallName(" + argumentsAsJson + ")"
```

@ -73,7 +73,7 @@ body {
```
`chrome/browser/resources/hello_world/app.html.ts`
```js
```ts
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@ -89,7 +89,7 @@ export function getHtml(this: HelloWorldAppElement) {
```
`chrome/browser/resources/hello_world/app.ts`
```js
```ts
import './strings.m.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
@ -517,7 +517,7 @@ HelloWorldUI::HelloWorldUI(content::WebUI* web_ui) {
It is suggested to turn off any optimizations during WebUI development by adding
the following in the args.gn file:
```
```py
optimize_webui = false
```
@ -541,7 +541,7 @@ iterate faster by **not having to do the above steps**. In order to use this
flow, follow the steps below:
**Step 1:** Add the following in your args.gn file and build the `chrome` target.
```
```py
optimize_webui = false # explained in previous section
load_webui_from_disk = true
```

@ -84,7 +84,7 @@ will be necessary to check for changes to the property in `changedProperties`.
This is also demonstrated in the example below.
Suppose the Lit child has a property with `notify: true` as follows:
```
```ts
static override get properties() {
return {
foo: {
@ -97,14 +97,14 @@ static override get properties() {
This property is also bound to a parent element that listens for the
`-changed` event as follows:
```
```html
<foo-child ?foo="${this.foo_}" on-foo-changed="${this.onFooChanged_}">
</foo-child>
<demo-child id="demo"></demo-child>
```
The parent TypeScript code could look like this:
```
```ts
static override get properties() {
return {
foo_: {type: Boolean},
@ -148,7 +148,7 @@ empty at startup. The following example would reproduce this bug and
have an empty `<select>` displayed at startup.
`.html.ts` file with `<select>` bug:
```
```html
<select .value="${this.mySelectValue}" @change="${this.onSelectChange_}">
<option value="${MyEnum.FIRST}">Option 1</option>
<option value="${MyEnum.SECOND}">Option 2</option>
@ -157,7 +157,7 @@ have an empty `<select>` displayed at startup.
Corresponding `.ts`. Note that the bug manifests even though `mySelectValue`
is being initialized to a valid option.
```
```ts
static get properties() {
return {
mySelectValue: {type: String},
@ -175,7 +175,7 @@ The current recommended workaround is to instead bind to the `selected`
attribute on each `<option>`, i.e.:
`.html.ts` file:
```
```html
<select @change="${this.onSelectChange_}">
<option value="${MyEnum.FIRST}"
?selected="${this.isSelected_(MyEnum.FIRST)}">
@ -189,7 +189,7 @@ attribute on each `<option>`, i.e.:
```
Corresponding `.ts` file:
```
```ts
static get properties() {
return {
mySelectValue: {type: String},
@ -313,7 +313,7 @@ template and its styling, and a `.ts` file containing the element definition.
***
Example `.ts` file:
```
```ts
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@ -371,7 +371,7 @@ customElements.define(MyExampleElement.is, MyExampleElement);
```
Example CSS file:
```
```css
/* Copyright 2024 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */
@ -406,7 +406,7 @@ generate the wrapper `.css.ts` file.
***
Example `.html.ts `file:
```
```ts
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@ -425,7 +425,7 @@ export function getHtml(this: MyExampleElement) {
```
`BUILD.gn` file configuration:
```
```python
build_webui("build") {
# Use non_web_component_files since the .html.ts file is checked in.
@ -493,12 +493,12 @@ in these cases the computation method can be used directly in the template
without specifying parameters. An example of this follows.
Polymer HTML template snippet:
```
```html
<cr-button hidden="[[hideButton_]]">Click Me</cr-button>
```
In the Polymer element definition:
```
```ts
static get properties() {
return {
loading: Boolean,
@ -519,12 +519,12 @@ private computeHideButton_(): boolean {
This could be rewritten in Lit, omitting the `hideButton_` property entirely.
Equivalent Lit HTML template snippet:
```
```html
<cr-button ?hidden="${this.computeHideButton_()}">Click Me</cr-button>
```
Equivalent Lit element definition:
```
```ts
static get properties() {
return {
loading: {type: Boolean},
@ -543,7 +543,7 @@ In other cases, where computed properties are bound to other elements, used as
attributes, or are needed for other internal logic, they can be computed in the
`willUpdate()` lifecycle callback when the properties that they depend on change
as in the following example:
```
```ts
override willUpdate(changedProperties: PropertyValues<this>) {
super.willUpdate(changedProperties);
@ -567,7 +567,7 @@ internal logic or requires accessing the elements DOM:
than triggering a second round of updates.
Consider the following Polymer code, with a complex observer:
```
```ts
static get properties() {
return {
max: Number,
@ -591,7 +591,7 @@ private onValueSet_() {
The Lit migrated code would look as follows, with the observer code split
into `willUpdate()` and `updated()` based on whether it accesses the DOM:
```
```ts
static override get properties() {
return {
max: {type: Number},
@ -630,7 +630,7 @@ statements in the `.html.ts` file of the form
`cr-toolbar`:
Polymer `cr_toolbar.html`:
```
```html
<div id="content">
<template is="dom-if" if="[[showMenu]]" restamp>
<cr-icon-button id="menuButton" class="no-overlap"
@ -642,7 +642,7 @@ Polymer `cr_toolbar.html`:
```
Lit `cr_toolbar.html.ts`:
```
```html
<div id="content">
${this.showMenu ? html`
<cr-icon-button id="menuButton" class="no-overlap"
@ -677,7 +677,7 @@ One possibility is to set the index or item as data attributes on elements that
fire events, as seen in the example that follows.
From the Polymer element template:
```
```html
<template is="dom-repeat" items="[[listItems]]">
<div class="item-container [[getSelectedClass_(item, selectedItem)]]">
<cr-button id="[[getItemId_(index)]]" on-click="onItemClick_">
@ -688,7 +688,7 @@ From the Polymer element template:
```
From the Polymer element definition:
```
```ts
private getItemId_(index: number): string {
return 'listItemId' + index;
}
@ -709,7 +709,7 @@ private onItemClick_(e: DomRepeatEvent<ListItemType>) {
```
Lit template:
```
```ts
${this.listItems.map((item, index) => html`
<div class="item-container ${this.getSelectedClass_(item)}">
<cr-button id="${this.getItemId_(index)}"
@ -725,7 +725,7 @@ Note the `data-index` setting the `data` attribute on the
***
From the Lit element definition file:
```
```ts
protected getItemId_(index: number): string {
return 'listItemId' + index;
}
@ -763,7 +763,7 @@ An example based on a simplified form of `cr-url-list-item`, which uses
composition, follows.
From the Polymer `.html` template:
```
```html
<div class="folder-and-count">
<template is="dom-if" if="[[shouldShowFolderImages_(size)]]" restamp>
<template is="dom-repeat" items="[[imageUrls]]"
@ -778,7 +778,7 @@ From the Polymer `.html` template:
```
From the Polymer element definition:
```
```ts
private shouldShowImageUrl_(_url: string, index: number) {
return index <= 1;
}
@ -798,7 +798,7 @@ private getDisplayedCount_() {
```
From the Lit `.html.ts` template file:
```
```ts
import {html} from '//resources/lit/v3_0/lit.rollup.js';
import type {CrUrlListItemElement} from './cr_url_list_item.js';
@ -842,7 +842,7 @@ export function getHtml(this: CrUrlListItemElement) {
```
From the Lit element definition:
```
```ts
protected getDisplayedCount_(): string {
if (this.count && this.count > 999) {
// The square to display the count only fits 3 characters.
@ -891,7 +891,7 @@ their `updated()` lifecycle callback whenever any property that may impact
their height has changed. See example below:
From the `list_parent.html` template (`iron-list` client so must be Polymer)
```
```html
<iron-list id="list" items="[[listItems_]]" as="item">
<template>
<custom-item description="[[item.description]]" name="[[item.name]]"
@ -902,7 +902,7 @@ From the `list_parent.html` template (`iron-list` client so must be Polymer)
```
From the child `custom_item.html.ts` template:
```
```html
<div class="name">${this.name}</div>
<div class="description" ?hidden="${!this.description}">
${this.description}
@ -916,7 +916,7 @@ display gaps or overlap in the list. To prevent this, the child item should
fire `iron-resize` in `updated()` if its `description` property changes.
From `custom_item.ts`:
```
```ts
override updated(changedProperties: PropertyValues<this>) {
super.updated(changedProperties);
if (changedProperties.has('description')) {
@ -929,7 +929,7 @@ override updated(changedProperties: PropertyValues<this>) {
### Testing
A large number of unit tests do something like the following:
```
```ts
// Validate that the input is disabled when invalid is set.
myTestElement.invalid = true;
assertTrue(myTestElement.$.input.disabled);
@ -948,7 +948,7 @@ to do this:
render cycle).
Updated example:
```
```ts
// Validate that the input is disabled when invalid is set.
myTestElement.invalid = true;
await microtasksFinished();