# Overriding Upstream WebUIs Currently, there are a number of ways to override upstream WebUIs. Which one you want will depend on what you're trying to do, and how the upstream WebUI is implemented. ## chromium_src overrides This is probably the simplest approach. Similarly to our C++ overrides just add a new file at the same path but in the `chromium_src` directory and it will replace the upstream version of the file. The upstream version can be imported via `./-chromium.` if you need to reexport something from it. **Note:** You don't need to reexport upstream stuff if you're completely replacing it. Similarly to C++ `chromium_src` overrides should be as minimal as possible to provide the hooks Brave needs. When you need to do something more substantial you should [add a new target](#adding-new-files) and make most of your changes there. This makes code easier to follow, and means you don't have to jump back and forward between `chromium_src` and `brave` files. As a bonus, it means you won't need `chromium_src` approvals most of the time! ### Example Typescript ```ts // in chromium_src/path/to/file.ts import { frob as frobChromium } from './file-chromium.js' // export everything from upstream export * from './file-chromium.js' // in Brave, all frobs are twice as good. export function frob() { return frobChromium() * 2 } ``` ### Example CSS Note: This is slightly different depending on whether you're overriding a Lit CSS file or a Polymer CSS file (you need to make sure `type` is correct in the `css_wrapper_metadata`) ```css /* in chromium_src/path/to/file.css */ /* #css_wrapper_metadata_start * #type=style-lit * #import=./file-chromium.css.js * #scheme=relative * #css_wrapper_metadata_end */ :host { background: hotpink; } ``` ## Lit Functions Adding a new function to a Lit component can be complicated because the types for the HTML template depend on what's exported, so naively super classing the upstream component does not work. The best approach is to use [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) and modify the prototype of the upstream class. ```ts import { FancyElement } from './fancy-chromium.js' declare module './fancy-chromium.js' { interface FancyElement { isBraveAndFancy: () => boolean } } FancyElement.prototype.isBraveAndFancy = () => true; export * from './fancy-chromium.js' ``` See [this PR](https://github.com/brave/brave-core/pull/29598/files) for a real example. ## Lit Mangling Unfortunately, the above strategies don't work for modifying Lit HTML. To modify a Lit HTML template we need a way to directly modify the source file. This is done with a Lit mangler. Create a file in `chromium_src` for `path/to/your/file.html.ts` at `chromium_src/path/to/your/file.html.ts.lit_mangler.ts` ```ts import mangle from 'lit-mangler' mangle(element => { element.textContent = element.textContent.replaceAll("Chrome", "Brave") }, literal => literal.text.includes("id='thing-i-want-to-edit'")) ``` The `mangle` function extracts all templates from the `.html.ts` file and loads them into an HTMLElement, so you can manipulate them using the DOM APIs. The second argument to this function is optional but lets you select what template you want to edit (in the case of multiple nested templates). For example: ```ts // this template literal from upstream: html`
${this.children.map(c => html`
  • ${c.text}
  • `)}
    ` // would have two templates // 1. For the container // 2. For the children, iterated over the array. // in our mangler: // We'll make the following changes with a Lit Mangler: // 1. Add a `.brave` class to the `.container` element // 2. Add an `id` attribute to the `li` elements // 3. Wrap the child text in a span import mangle from 'lit-mangler' mangle(e => { const container = e.querySelector('.container') // Its good practise to throw an error when the mangler can't find something // - it will cause the build to fail, so we'll know quickly that something // went wrong. if (!container) throw new Error("Couldn't find the container") // Note: We use this predicate to select the right template. }, t => t.text.includes('class="container"')) mangle(e => { // Note: The element passed to the mangler is a document fragment, as there // could be multiple root elements in a template. const li = e.querySelector('li') // Assuming our children have an id element. Note: We set this to a string // literal, because we're editing the source text of the .html.ts file. li.setAttribute('id', '${c.id}'); // Similarly, we need to escape the template interpolation here, because we // want it to happen at runtime, not now! // If we mess something up we'll get a typescript error (as the mangled source // file still runs through the type checker)! li.innerHTML = ` \${c.text} ` // select the list items }, t => t.text.startsWith('