Rive em uma extensão do Chrome (Manifest V3): empacote o WASM
Por equipe Charmotion · Atualizado em

O Rive falha em uma extensão Manifest V3 porque o runtime web baixa o rive.wasm do unpkg por padrão, e o Chrome proíbe código hospedado remotamente, incluindo WebAssembly. Empacote o @rive-app/canvas, copie os arquivos WASM para o pacote, chame RuntimeLoader.setWasmUrl() com chrome.runtime.getURL() antes de criar a animação, inclua o .riv dentro da extensão e adicione 'wasm-unsafe-eval' à CSP.
Principais pontos
- O Chrome define código hospedado remotamente como qualquer coisa executada que seja carregada de fora dos arquivos da própria extensão, e cita WASM explicitamente.
- A CSP padrão das extensões desativa o WebAssembly; a política de que você precisa é
script-src 'self' 'wasm-unsafe-eval'; object-src 'self';. - O runtime busca o
rive.wasmno unpkg e um fallback no jsDelivr; substitua os dois por URLs locais. - Mantenha os arquivos WASM e o pacote npm exatamente na mesma versão; segundo o Rive, uma diferença pode quebrar a renderização.
- Electron e Tauri precisam do mesmo WASM hospedado localmente, além de
'wasm-unsafe-eval'em qualquer CSP que você definir.
Já tem um personagem ou logo? Veja seu personagem em telas reais de app: telas de splash, onboarding, carregamento e estado vazio. É grátis, roda no navegador e não envia nada.
Por que o Rive não carrega na minha extensão do Chrome?
Duas regras do Manifest V3 entram em conflito com os padrões do runtime.
1. Código remoto é proibido. O Chrome define código hospedado remotamente como "qualquer coisa executada pelo navegador que seja carregada de algum lugar que não sejam os arquivos da própria extensão" e cita "coisas como JavaScript e WASM" como exemplos. As extensões devem "empacotar todo o código que usam dentro da própria extensão". O runtime web do Rive, por padrão, baixa o motor de uma CDN. No @rive-app/canvas 2.47.1 o padrão é https://unpkg.com/@rive-app/canvas@2.47.1/rive.wasm e, se isso falhar, ele tenta o rive_fallback.wasm do jsDelivr.
2. A CSP padrão desativa o WebAssembly. Sem uma chave content_security_policy, as páginas da extensão recebem script-src 'self'; object-src 'self';, e a documentação do Chrome diz que, com ela, "o WebAssembly será desativado". A política mais permissiva que o Chrome aceita para páginas de extensão é script-src 'self' 'wasm-unsafe-eval'; object-src 'self';.
Então os sintomas são uma requisição de rede bloqueada para o rive.wasm ou um erro de CSP sobre a compilação do WebAssembly, seguidos de um canvas em branco. Corrija os dois.
Qual CSP o manifest precisa para o WebAssembly?
Adicione 'wasm-unsafe-eval' a extension_pages. Essa é a política mínima da documentação do Chrome, então também é o máximo que você pode afrouxá-la:
{
"manifest_version": 3,
"name": "Mascot",
"version": "1.0.0",
"action": { "default_popup": "popup.html" },
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"
}
}
- Não adicione
'unsafe-eval'nem um host de CDN; o Chrome rejeita na instalação políticas mais frouxas que a mínima. - Essa política vale para as páginas da extensão: o popup, o painel lateral, a página de opções e a substituição da nova guia.
- O próprio .riv é buscado na origem da extensão, o que
'self'permite.
Como empacotar o rive.wasm e o .riv na extensão?
Instale uma versão exata do runtime, copie os dois arquivos WASM para a pasta da extensão e aponte o runtime para eles antes de criar a primeira animação. O pacote npm traz os dois arquivos (na 2.47.1, o rive.wasm tem 2.165.388 bytes e o rive_fallback.wasm, 2.191.884 bytes).
npm install --save-exact @rive-app/canvas@2.47.1
mkdir -p extension/vendor
cp node_modules/@rive-app/canvas/rive.wasm extension/vendor/
cp node_modules/@rive-app/canvas/rive_fallback.wasm extension/vendor/
Depois, no script do popup (empacotado com Vite, esbuild, Rollup ou webpack, já que as páginas da extensão não resolvem imports npm diretos):
import { Rive, RuntimeLoader } from "@rive-app/canvas";
// Must run before the first new Rive(): no CDN requests from now on.
RuntimeLoader.setWasmUrl(chrome.runtime.getURL("vendor/rive.wasm"));
RuntimeLoader.setWasmFallbackUrl(chrome.runtime.getURL("vendor/rive_fallback.wasm"));
const canvas = document.getElementById("mascot");
const r = new Rive({
src: chrome.runtime.getURL("assets/character.riv"), // shipped in the package
canvas,
stateMachine: "CharacterViewModelMachine",
autoplay: !matchMedia("(prefers-reduced-motion: reduce)").matches,
autoBind: true,
onLoad: () => r.resizeDrawingSurfaceToCanvas(),
});
document.addEventListener("visibilitychange", () => {
if (document.hidden) r.pause(); else r.play();
});
setWasmUrlé a chamada que o Rive documenta para hospedagem própria. A documentação do Rive acrescenta que a versão do WASM "deve corresponder à versão do pacote@rive-app", porque os dois são gerados em par; atualize os arquivos copiados no mesmo commit de qualquer mudança de versão.setWasmFallbackUrlexiste no código-fonte do runtime a partir da 2.40.1 (verificamos da 2.40.1 até a 2.47.1). Apontá-la para o fallback empacotado faz com que até a nova tentativa continue local; em versões mais antigas, essa chamada não existe.- Inclua o .riv no pacote e carregue-o com
chrome.runtime.getURL(). Assim ele funciona offline e não há nada remoto para um revisor questionar.
O código da biblioteca ainda contém as URLs padrão da CDN como strings, embora elas nunca sejam usadas depois que você as substitui. O guia do Chrome sugere procurar https:// no código gerado para encontrar violações; então, se um revisor apontar isso, mostre a chamada setWasmUrl.
Onde uma animação Rive pode rodar: popup, painel lateral ou content script?
As páginas da extensão são o caso fácil: popup, painel lateral, opções e nova guia rodam sob a CSP da sua extensão, então a configuração acima funciona sem mudanças.
- Popup: o Chrome ajusta o tamanho ao conteúdo, entre 25×25 e 800×600 pixels. Defina largura e altura explícitas em CSS para o canvas, senão o popup se fecha em volta de um canvas vazio.
- Painel lateral e opções: o mesmo código; redimensione a superfície de desenho quando a largura do painel mudar.
- Em páginas web: em vez de rodar o runtime dentro de um content script na página de outra pessoa, injete um iframe que aponte para uma página da sua extensão. Liste essa página em
web_accessible_resourcespara que os sites possam exibi-la em frame; assim a animação roda na origem da sua extensão, sob a sua CSP, isolada dos scripts e estilos do site.
"web_accessible_resources": [
{
"resources": ["mascot-frame.html"],
"matches": ["https://*.example.com/*"]
}
]
Use a lista matches mais restrita possível; ela controla quais sites podem carregar o recurso.
Como manter rápido um mascote na nova guia?
Uma substituição da nova guia (chrome_url_overrides) carrega toda vez que alguém abre uma guia, então pequenos custos se acumulam.
- Pré-carregue o WASM. O guia de pré-carregamento do Rive usa
<link rel="preload" as="fetch" crossorigin>; a URL do preload precisa ser exatamente igual à URL desetWasmUrl(), senão o arquivo é baixado duas vezes. Um caminho relativovendor/rive.wasmem uma página da extensão resolve para a mesma URL quechrome.runtime.getURL("vendor/rive.wasm"). - Pause quando a página estiver oculta. O exemplo pausa no
visibilitychange, para que guias em segundo plano não continuem animando. - Uma instância. Use um único personagem com humores, não vários canvas.
- Respeite o movimento reduzido. A reprodução automática fica desligada quando o usuário pede menos movimento; mostre o primeiro frame ou uma imagem estática no lugar.
O Rive também recomenda @rive-app/webgl2 para a maioria dos projetos web; as mesmas chamadas de RuntimeLoader valem, mas copie os arquivos WASM desse pacote.
A mesma configuração funciona no Firefox?
Sim, com as mesmas regras. A referência de manifest do MDN diz que, no Manifest V3, os únicos valores permitidos em script-src para páginas de extensão são 'self', 'none' e 'wasm-unsafe-eval', e que "o Manifest V3 não permite URLs remotas em script-src". Também observa que as políticas de add-ons do Firefox não permitem scripts remotos. O WASM incluído no pacote, o .riv local e a mesma string de CSP continuam valendo.
Para add-ons em Manifest V2, o MDN diz que o Firefox atualmente consegue rodar WebAssembly sem 'wasm-unsafe-eval' por compatibilidade retroativa, mas que "esse comportamento não é garantido", e recomenda declará-lo.
Apps em Electron e Tauri precisam da mesma correção?
O princípio é idêntico: inclua o rive.wasm no app, chame RuntimeLoader.setWasmUrl() com uma URL local e permita WebAssembly na sua CSP.
- Electron. O checklist de segurança do Electron alerta contra carregar e executar código remoto e recomenda servir páginas locais a partir de um protocolo personalizado em vez de
file://. ApontesetWasmUrlpara uma URL nesse protocolo. Se você definir uma CSP (com uma meta tag para páginas locais), inclua'wasm-unsafe-eval'emscript-src. - Tauri. A CSP do Tauri só é aplicada se você a definir na configuração; quando você faz isso, a documentação diz para "incluir
'wasm-unsafe-eval'comoscript-src" e desaconselha scripts de CDN. O runtime baixa o WASM e o .riv comfetch, então garanta queconnect-src(oudefault-src) permita a origem de onde você os serve.
O que muda em um .riv do Charmotion dentro de uma extensão?
Nada no arquivo. O .riv do Charmotion (17 animações a partir de uma imagem, com um view model Character: um número mood de 0 a 9 e os triggers success, error, yes, no, enter e exit) é autossuficiente, então funciona offline a partir do pacote. O código para web que vem com ele carrega o runtime de uma CDN; para uma extensão, substitua essa tag script pelo import empacotado e pelas duas chamadas de RuntimeLoader mostradas acima. O .riv faz parte de todos os planos pagos (a partir de US$ 20/mês).

Como rodar uma animação Rive em uma extensão do Chrome com Manifest V3
- Instale o runtime. Instale uma versão exata de @rive-app/canvas e empacote o script do popup.
- Copie o WASM. Copie rive.wasm e rive_fallback.wasm de node_modules para a pasta da extensão.
- Aponte o runtime para eles. Chame RuntimeLoader.setWasmUrl e setWasmFallbackUrl com chrome.runtime.getURL antes de new Rive().
- Inclua o .riv no pacote. Coloque o .riv no pacote e carregue-o com chrome.runtime.getURL.
- Defina a CSP. Adicione script-src 'self' 'wasm-unsafe-eval'; object-src 'self'; em content_security_policy.extension_pages.
- Verifique a rede. Abra o DevTools do popup e confirme que nenhuma requisição vai para unpkg ou jsDelivr.
Perguntas frequentes
Minha extensão pode baixar arquivos .riv de um servidor?
As regras do Chrome sobre código remoto se aplicam a código executado, e o guia diz que dados como JSON ou CSS não são código remoto. Um .riv é principalmente dados, mas arquivos que usam scripting do Rive contêm código, e um arquivo incluído no pacote funciona offline e evita questionamentos na revisão. Inclua-o no pacote, a menos que você realmente precise atualizar o conteúdo remotamente.
Por que meu popup com Rive está minúsculo ou vazio?
O Chrome dimensiona o popup de acordo com o conteúdo, entre 25×25 e 800×600 pixels. Um canvas sem dimensões em CSS dá pouco para medir. Defina largura e altura explícitas no canvas e chame resizeDrawingSurfaceToCanvas() em onLoad para que o buffer de desenho corresponda.
Preciso de web_accessible_resources para o rive.wasm?
Não para páginas da extensão, como o popup ou o painel lateral, que carregam os próprios arquivos do pacote. Você precisa de web_accessible_resources quando um site precisa carregar algo da sua extensão, como uma página da extensão injetada como iframe em um site.
Respostas rápidas deste guia
Fontes
- Deal with remote hosted code violations, Chrome for Developers
- Manifest: content_security_policy, Chrome for Developers
- chrome.action (popup size limits), Chrome for Developers
- Manifest: web_accessible_resources, Chrome for Developers
- Override Chrome pages, Chrome for Developers
- Preloading WASM, Rive
- @rive-app/canvas package, npm
- content_security_policy (WebExtensions manifest), MDN Web Docs
- Security, Electron
- Content Security Policy (CSP), Tauri