|
|
|
@@ -47,49 +47,9 @@ export default defineContentScript({
|
|
|
|
|
|
|
|
|
|
When defining multiple content scripts, content script entrypoints that have the same set of options will be merged into a single `content_script` item in the manifest.
|
|
|
|
|
|
|
|
|
|
## CSS
|
|
|
|
|
|
|
|
|
|
To include CSS with your content script, import the CSS file at the top of your entrypoint.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
<srcDir>
|
|
|
|
|
└─ entrypoints/
|
|
|
|
|
└─ overlay.content/
|
|
|
|
|
├─ index.ts
|
|
|
|
|
└─ style.css
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
// entrypoints/overlay.content/index.ts
|
|
|
|
|
import './style.css';
|
|
|
|
|
|
|
|
|
|
export default defineContentScript({
|
|
|
|
|
matches: ['*://google.com/*', '*://duckduckgo.com/*'],
|
|
|
|
|
main(ctx) {
|
|
|
|
|
// ...
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Any styles imported in your content script will be added to that content script's `css` array in your `manifest.json`:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
// .output/chrome-mv3/manifest.json
|
|
|
|
|
{
|
|
|
|
|
"content_scripts": [
|
|
|
|
|
{
|
|
|
|
|
"matches": ["*://google.com/*", "*://duckduckgo.com/*"],
|
|
|
|
|
"js": ["content-scripts/overlay.js"],
|
|
|
|
|
"css": ["content-scripts/overlay.css"]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Context
|
|
|
|
|
|
|
|
|
|
Old content scripts are not automatically stopped when an extension updates and restarts. Often, this leads to "Invalidated context" errors in production when a content script from an old version of your extension tries to use a web extension API. Since it's not connected to the latest version of your extension, the browser decides to throw an error.
|
|
|
|
|
Old content scripts are not automatically stopped when an extension updates and reloads. Often, this leads to "Invalidated context" errors in production when a content script from an old version of your extension tries to use a extension API.
|
|
|
|
|
|
|
|
|
|
WXT provides a utility for managing this process: `ContentScriptContext`. An instance of this class is provided to you automatically inside the `main` function of your content script.
|
|
|
|
|
|
|
|
|
@@ -123,3 +83,210 @@ When working with content scripts, **you should always use the `ctx` object to s
|
|
|
|
|
|
|
|
|
|
This prevents old content scripts from interfering with new content scripts, and prevents error messages from the console in production.
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
|
|
## CSS
|
|
|
|
|
|
|
|
|
|
To include CSS with your content script, import the CSS file at the top of your entrypoint.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
<srcDir>
|
|
|
|
|
└─ entrypoints/
|
|
|
|
|
└─ overlay.content/
|
|
|
|
|
├─ index.ts
|
|
|
|
|
└─ style.css
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
// entrypoints/overlay.content/index.ts
|
|
|
|
|
import './style.css';
|
|
|
|
|
|
|
|
|
|
export default defineContentScript({
|
|
|
|
|
matches: ['*://google.com/*', '*://duckduckgo.com/*'],
|
|
|
|
|
|
|
|
|
|
main(ctx) {
|
|
|
|
|
// ...
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Any styles imported in your content script will be added to that content script's `css` array in your `manifest.json`:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
// .output/chrome-mv3/manifest.json
|
|
|
|
|
{
|
|
|
|
|
"content_scripts": [
|
|
|
|
|
{
|
|
|
|
|
"matches": ["*://google.com/*", "*://duckduckgo.com/*"],
|
|
|
|
|
"js": ["content-scripts/overlay.js"],
|
|
|
|
|
"css": ["content-scripts/overlay.css"]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
To disable this behavior, set `cssInjectionMode` to `"manual"` or `"ui"`.
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
export default defineContentScript({
|
|
|
|
|
matches: ['*://google.com/*', '*://duckduckgo.com/*'],
|
|
|
|
|
cssInjectionMode: 'manual',
|
|
|
|
|
|
|
|
|
|
main(ctx) {
|
|
|
|
|
// ...
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## UI
|
|
|
|
|
|
|
|
|
|
WXT provides a utility function, `createContentScriptUi` to simplify mounting a UI from a content script. Internally, it uses the `ShadowRoot` API to isolate your CSS from the webpages.
|
|
|
|
|
|
|
|
|
|
`createContentScriptUi` requires a `ContentScriptContext` so that when the context is invalidated, the UI is automatically removed from the webpage.
|
|
|
|
|
|
|
|
|
|
:::details When to use `createContentScriptUi`
|
|
|
|
|
You should only use `createContentScriptUi` if you want your UI's styles isolated from the webpages. If you want to create a more "integrated" UI that uses the page's styles, you can just use the regular JS API's to append your UI to the page.
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
const ui = document.createElement('div');
|
|
|
|
|
const anchor = document.querySelector('#anchor-selector');
|
|
|
|
|
anchor.append(ui);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
|
|
### Usage
|
|
|
|
|
|
|
|
|
|
To use `createContentScriptUi`, follow these steps:
|
|
|
|
|
|
|
|
|
|
1. Import your CSS file at the top of your content script
|
|
|
|
|
2. Set `cssInjectionMode: "ui"` inside `defineContentScript`
|
|
|
|
|
3. Call `createContentScriptUi`
|
|
|
|
|
4. Call `ui.mount()` to add the UI to the webpage
|
|
|
|
|
|
|
|
|
|
Here's a basic example:
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
// entrypoints/ui.content/index.ts
|
|
|
|
|
import './style.css';
|
|
|
|
|
|
|
|
|
|
export default defineContentScript({
|
|
|
|
|
// ...
|
|
|
|
|
cssInjectionMode: 'ui',
|
|
|
|
|
|
|
|
|
|
async main(ctx) {
|
|
|
|
|
const ui = await createContentScriptUi(ctx, {
|
|
|
|
|
name: 'example-ui',
|
|
|
|
|
type: 'inline',
|
|
|
|
|
anchor: '#some-element',
|
|
|
|
|
append: 'after',
|
|
|
|
|
mount(container) {
|
|
|
|
|
// Mount UI inside `container`...
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Yoy must call `mount` to add the UI to the page.
|
|
|
|
|
ui.mount();
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
If you're using a frontend framework, you'll also need to include an `onRemoved` callback:
|
|
|
|
|
|
|
|
|
|
:::code-group
|
|
|
|
|
|
|
|
|
|
```ts [Vue]
|
|
|
|
|
import { createApp } from 'vue';
|
|
|
|
|
|
|
|
|
|
createContentScriptUi(ctx, {
|
|
|
|
|
// ...
|
|
|
|
|
mount(container) {
|
|
|
|
|
// Create a new app and mount it inside the container
|
|
|
|
|
const app = createApp(...);
|
|
|
|
|
app.mount(container);
|
|
|
|
|
return app;
|
|
|
|
|
},
|
|
|
|
|
onRemove(app) {
|
|
|
|
|
// When the UI is removed from the DOM, call unmount to stop the app
|
|
|
|
|
app.unmount();
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```ts [React]
|
|
|
|
|
import ReactDOM from 'react-dom/client';
|
|
|
|
|
|
|
|
|
|
createContentScriptUi(ctx, {
|
|
|
|
|
// ...
|
|
|
|
|
mount(container) {
|
|
|
|
|
// Create a root using the container and render your app
|
|
|
|
|
const root = ReactDOM.createRoot(container);
|
|
|
|
|
root.render(...);
|
|
|
|
|
return root;
|
|
|
|
|
},
|
|
|
|
|
onRemove(root) {
|
|
|
|
|
// When the UI is removed from the DOM, call unmount to stop the app
|
|
|
|
|
root.unmount();
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```ts [Svelte]
|
|
|
|
|
import App from './App.svelte';
|
|
|
|
|
|
|
|
|
|
createContentScriptUi(ctx, {
|
|
|
|
|
// ...
|
|
|
|
|
mount(container) {
|
|
|
|
|
// Mount your app component inside the container
|
|
|
|
|
return new App({
|
|
|
|
|
target: container,
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
onRemove(app) {
|
|
|
|
|
// When the UI is removed from the DOM, call $destroy to stop the app
|
|
|
|
|
app.$destroy();
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```ts [Solid]
|
|
|
|
|
import { render } from 'solid-js/web';
|
|
|
|
|
|
|
|
|
|
createContentScriptUi(ctx, {
|
|
|
|
|
// ...
|
|
|
|
|
mount(container) {
|
|
|
|
|
// Render your app component into the container
|
|
|
|
|
return render(() => ..., container)
|
|
|
|
|
},
|
|
|
|
|
onRemove(unmount) {
|
|
|
|
|
// When the UI is removed from the DOM, call unmount to stop the app
|
|
|
|
|
unmount();
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
|
|
### `anchor`
|
|
|
|
|
|
|
|
|
|
The anchor dictates where the UI will be mounted.
|
|
|
|
|
|
|
|
|
|
### `append`
|
|
|
|
|
|
|
|
|
|
Customize where the UI get's appended to the DOM, relative to the `anchor` element.
|
|
|
|
|
|
|
|
|
|
### `type`
|
|
|
|
|
|
|
|
|
|
There are 3 types of UI's you can mount.
|
|
|
|
|
|
|
|
|
|
- `inline`: Shows up inline based on the `anchor` and `append` options
|
|
|
|
|
- `overlay`: Shows up inline, but styled to be 0px by 0px, with overflow visible. This causes the UI to overlay on top of the webpage's content
|
|
|
|
|
- `modal`: A fullscreen overlay that covers the entire screen, regardless of where it's anchored.
|
|
|
|
|
|
|
|
|
|
> TODO: Add visualization of the different UI types.
|
|
|
|
|
|
|
|
|
|
### Overlay `alignment`
|
|
|
|
|
|
|
|
|
|
Because the overlay UI type results in a 0px by 0px container being added to the webpage, the `alignment` option allows you to configure which corner of your UI is aligned with the 0x0 element.
|
|
|
|
|
|
|
|
|
|
> TODO: Add visualization of the different alignments.
|
|
|
|
|