Chrome Extension (Manifest V3) में Rive: WASM को बंडल करें
Charmotion टीम द्वारा · अपडेट:

Manifest V3 extension में Rive इसलिए फ़ेल होता है क्योंकि web runtime डिफ़ॉल्ट रूप से rive.wasm को unpkg से डाउनलोड करता है, और Chrome remotely hosted code पर रोक लगाता है, जिसमें WebAssembly भी शामिल है। @rive-app/canvas को bundle करें, उसकी WASM फ़ाइलें package में कॉपी करें, एनिमेशन बनाने से पहले chrome.runtime.getURL() के साथ RuntimeLoader.setWasmUrl() कॉल करें, .riv को package के अंदर ही रखें, और CSP में 'wasm-unsafe-eval' जोड़ें।
मुख्य बातें
- Chrome के अनुसार remotely hosted code वह कुछ भी है जो execute होता है और extension की अपनी फ़ाइलों के बाहर से लोड होता है, और इसमें WASM का नाम साफ़ तौर पर लिया गया है।
- डिफ़ॉल्ट extension CSP, WebAssembly को बंद कर देता है; आपको
script-src 'self' 'wasm-unsafe-eval'; object-src 'self';policy चाहिए। - runtime
rive.wasmको unpkg से और एक fallback को jsDelivr से fetch करता है; दोनों को local URLs से override करें। - WASM फ़ाइलें और npm package बिल्कुल एक ही version पर रखें; Rive के अनुसार version मेल न खाने पर rendering टूट सकती है।
- Electron और Tauri में भी यही self-hosted WASM चाहिए, और आप जो भी CSP सेट करें उसमें
'wasm-unsafe-eval'होना चाहिए।
पहले से कोई कैरेक्टर या लोगो है? अपने कैरेक्टर को असली ऐप स्क्रीन में देखें: स्प्लैश, ऑनबोर्डिंग, लोडिंग और एम्प्टी स्टेट स्क्रीन। यह फ़्री है, आपके ब्राउज़र में चलता है और कुछ भी अपलोड नहीं होता।
मेरे Chrome extension में Rive लोड क्यों नहीं हो रहा?
Manifest V3 के दो नियम runtime की डिफ़ॉल्ट सेटिंग्स से टकराते हैं।
1. Remote code पर रोक है। Chrome remotely hosted code को "ऐसी कोई भी चीज़ जो browser execute करता है और जो extension की अपनी फ़ाइलों के अलावा कहीं और से लोड होती है" के रूप में परिभाषित करता है, और उदाहरण के तौर पर "JavaScript और WASM जैसी चीज़ें" बताता है। Extensions को "इस्तेमाल होने वाला सारा code extension के अंदर ही bundle करना होगा।" Rive का web runtime डिफ़ॉल्ट रूप से अपना इंजन एक CDN से डाउनलोड करता है। @rive-app/canvas 2.47.1 में डिफ़ॉल्ट https://unpkg.com/@rive-app/canvas@2.47.1/rive.wasm है, और अगर वह फ़ेल हो जाए तो यह jsDelivr से rive_fallback.wasm आज़माता है।
2. डिफ़ॉल्ट CSP, WebAssembly को बंद कर देता है। content_security_policy key न होने पर extension pages को script-src 'self'; object-src 'self'; मिलता है, और Chrome के docs कहते हैं कि इसके तहत "WebAssembly बंद रहेगा।" Chrome extension pages के लिए जो सबसे ढीली policy स्वीकार करता है, वह script-src 'self' 'wasm-unsafe-eval'; object-src 'self'; है।
इसलिए लक्षण या तो rive.wasm के लिए blocked network request होते हैं या WebAssembly compilation से जुड़ी CSP error, और उसके बाद खाली कैनवस। दोनों ठीक करें।
WebAssembly के लिए manifest में कौन सा CSP चाहिए?
extension_pages में 'wasm-unsafe-eval' जोड़ें। यह Chrome के docs में दी गई minimum policy है, इसलिए आप इसे इससे ज़्यादा ढीला नहीं कर सकते:
{
"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';"
}
}
'unsafe-eval'या कोई CDN host न जोड़ें; minimum से ढीली policies को Chrome install के समय ही reject कर देता है।- यह policy extension pages पर लागू होती है: popup, side panel, options page और new tab override।
- .riv खुद extension के अपने origin से fetch होती है, जिसकी अनुमति
'self'देता है।
extension में rive.wasm और .riv को bundle कैसे करें?
runtime का एक exact version install करें, उसकी दोनों WASM फ़ाइलें extension folder में कॉपी करें, और पहला एनिमेशन बनने से पहले runtime को इन फ़ाइलों की ओर point करें। npm package में दोनों फ़ाइलें आती हैं (2.47.1 में rive.wasm 2,165,388 bytes की है और 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/
फिर popup की script में (Vite, esbuild, Rollup या webpack से bundle करके, क्योंकि extension pages bare npm imports resolve नहीं कर सकते):
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वह कॉल है जिसे Rive self-hosting के लिए document करता है। Rive के docs आगे कहते हैं कि WASM version "@rive-apppackage version से मेल खाना चाहिए," क्योंकि दोनों एक जोड़ी के रूप में build होते हैं; जब भी version बढ़ाएँ, कॉपी की गई फ़ाइलें उसी commit में अपडेट करें।setWasmFallbackUrlruntime source में 2.40.1 से मौजूद है (हमने 2.40.1 से 2.47.1 तक चेक किया)। इसे bundled fallback की ओर point करने से retry भी local ही रहता है; पुराने versions में यह कॉल मौजूद नहीं है।- .riv को package में ही रखें और उसे
chrome.runtime.getURL()से लोड करें। तब यह offline भी चलती है और reviewer के पूछने लायक कुछ भी remote नहीं रहता।
library code में डिफ़ॉल्ट CDN URLs अब भी strings के रूप में मौजूद रहते हैं, भले ही override करने के बाद उनका कभी इस्तेमाल न हो। Chrome की गाइड violations ढूँढने के लिए built code में https:// खोजने की सलाह देती है, इसलिए अगर reviewer यह मुद्दा उठाए, तो setWasmUrl कॉल दिखाएँ।
Rive एनिमेशन कहाँ चल सकता है: popup, side panel या content script?
Extension pages सबसे आसान हैं: popup, side panel, options और new tab pages सभी आपके extension के CSP के तहत चलते हैं, इसलिए ऊपर वाला सेटअप बिना बदलाव के काम करता है।
- Popup: Chrome इसका साइज़ content के हिसाब से 25×25 से 800×600 pixels के बीच तय करता है। कैनवस को साफ़ CSS width और height दें, वरना popup खाली कैनवस के चारों ओर सिकुड़ जाता है।
- Side panel और options: वही code; panel की width बदलने पर drawing surface का साइज़ बदलें।
- Web pages पर: किसी और के पेज पर content script के अंदर runtime चलाने के बजाय, एक iframe inject करें जो आपके extension के किसी पेज की ओर point करे। उस पेज को
web_accessible_resourcesमें लिस्ट करें ताकि websites उसे frame कर सकें; तब एनिमेशन आपके extension के origin पर, आपके CSP के तहत चलता है और साइट की scripts और styles से अलग रहता है।
"web_accessible_resources": [
{
"resources": ["mascot-frame.html"],
"matches": ["https://*.example.com/*"]
}
]
जितनी हो सके उतनी छोटी matches list इस्तेमाल करें; यही तय करती है कि कौन सी साइटें resource लोड कर सकती हैं।
new tab वाले मैस्कॉट को तेज़ कैसे रखें?
new tab override (chrome_url_overrides) हर बार तब लोड होता है जब कोई tab खोलता है, इसलिए छोटी-छोटी देरी भी जुड़ती जाती है।
- WASM को preload करें। Rive की preloading गाइड
<link rel="preload" as="fetch" crossorigin>इस्तेमाल करती है; preload URL कोsetWasmUrl()वाले URL से बिल्कुल मेल खाना चाहिए, वरना फ़ाइल दो बार डाउनलोड होगी। extension page पर relativevendor/rive.wasmउसी URL पर resolve होता है जिस परchrome.runtime.getURL("vendor/rive.wasm")। - छिपे होने पर pause करें। sample
visibilitychangeपर pause हो जाता है, ताकि background tabs में एनिमेशन चलता न रहे। - एक ही instance। कई कैनवस की जगह मूड वाला एक ही कैरेक्टर इस्तेमाल करें।
- मोशन कम करने की सेटिंग का सम्मान करें। जब यूज़र कम मोशन चाहता है, तब ऑटोप्ले बंद रहता है। इसकी जगह पहला फ़्रेम या कोई स्थिर इमेज दिखाएँ।
Rive ज़्यादातर वेब प्रोजेक्ट के लिए @rive-app/webgl2 की भी सलाह देता है। इसमें भी वही RuntimeLoader calls लागू होते हैं, बस WASM फ़ाइलें उसी पैकेज से कॉपी करें।
क्या यही सेटअप Firefox में भी काम करता है?
हाँ, उन्हीं नियमों के साथ। MDN के manifest reference के अनुसार Manifest V3 में extension pages के लिए script-src में केवल 'self', 'none' और 'wasm-unsafe-eval' वैल्यू की अनुमति है, और "Manifest V3 script-src में remote URLs की अनुमति नहीं देता।" इसमें यह भी बताया गया है कि Firefox की add-on नीतियाँ remote scripts की अनुमति नहीं देतीं। बंडल किया गया WASM, लोकल .riv और वही CSP string यहाँ भी वैसे ही काम करते हैं।
Manifest V2 add-ons के लिए MDN बताता है कि backward compatibility के कारण Firefox फ़िलहाल 'wasm-unsafe-eval' के बिना भी WebAssembly चला सकता है, लेकिन "इस व्यवहार की गारंटी नहीं है", इसलिए इसे declare करने की सलाह दी जाती है।
क्या Electron और Tauri ऐप्स में भी यही समाधान चाहिए?
सिद्धांत बिल्कुल वही है: rive.wasm को ऐप के साथ शिप करें, लोकल URL के साथ RuntimeLoader.setWasmUrl() कॉल करें, और अपने CSP में WebAssembly की अनुमति दें।
- Electron. Electron की security checklist remote code लोड और execute करने के खिलाफ़ चेतावनी देती है और लोकल पेजों को
file://के बजाय एक custom protocol से serve करने की सलाह देती है।setWasmUrlको उसी protocol वाले URL पर पॉइंट करें। अगर आप CSP सेट करते हैं (लोकल पेजों के लिए meta tag के ज़रिए), तोscript-srcमें'wasm-unsafe-eval'शामिल करें। - Tauri. Tauri का CSP तभी लागू होता है जब आप उसे config में सेट करें। ऐसा करने पर इसके docs कहते हैं कि "
'wasm-unsafe-eval'कोscript-srcके रूप में शामिल करें", और वे CDN scripts से बचने की सलाह देते हैं। रनटाइम WASM और .riv कोfetchसे डाउनलोड करता है, इसलिए पक्का करें किconnect-src(याdefault-src) उस origin की अनुमति देता है जहाँ से आप इन्हें serve करते हैं।
Extension में Charmotion .riv के लिए क्या बदलता है?
फ़ाइल में कुछ नहीं। Charmotion की .riv (एक इमेज से 17 एनिमेशन, Character व्यू मॉडल के साथ: 0 से 9 तक एक mood नंबर और success, error, yes, no, enter और exit ट्रिगर) अपने आप में पूरी है, इसलिए यह पैकेज से ऑफ़लाइन काम करती है। इसके साथ आने वाला वेब कोड रनटाइम को CDN से लोड करता है; एक्सटेंशन के लिए उस स्क्रिप्ट टैग को बंडल किए गए इम्पोर्ट और ऊपर दिखाए गए दो RuntimeLoader कॉल से बदलें। .riv हर पेड प्लान का हिस्सा है ($20/माह से)।

Manifest V3 Chrome extension में Rive एनिमेशन कैसे चलाएँ
- रनटाइम इंस्टॉल करें. @rive-app/canvas का एक exact version इंस्टॉल करें और अपनी popup script को बंडल करें।
- WASM कॉपी करें. rive.wasm और rive_fallback.wasm को node_modules से extension फ़ोल्डर में कॉपी करें।
- रनटाइम को उनकी ओर पॉइंट करें. new Rive() से पहले chrome.runtime.getURL के साथ RuntimeLoader.setWasmUrl और setWasmFallbackUrl कॉल करें।
- .riv को bundle करें. .riv को पैकेज में रखें और chrome.runtime.getURL से लोड करें।
- CSP सेट करें. content_security_policy.extension_pages के अंदर script-src 'self' 'wasm-unsafe-eval'; object-src 'self'; जोड़ें।
- नेटवर्क जाँचें. Popup का DevTools खोलें और पक्का करें कि कोई भी request unpkg या jsDelivr पर नहीं जा रही।
अक्सर पूछे जाने वाले सवाल
क्या मेरा extension सर्वर से .riv फ़ाइलें डाउनलोड कर सकता है?
Chrome के remote code नियम execute होने वाले कोड पर लागू होते हैं, और इसकी गाइड कहती है कि JSON या CSS जैसा डेटा remote code नहीं है। .riv ज़्यादातर डेटा होती है, लेकिन Rive scripting इस्तेमाल करने वाली फ़ाइलों में कोड होता है। बंडल की गई फ़ाइल ऑफ़लाइन काम करती है और review के सवालों से बचाती है। जब तक आपको सच में remote content updates की ज़रूरत न हो, इसे बंडल करें।
मेरा Rive popup बहुत छोटा या खाली क्यों है?
Chrome popup का साइज़ उसके content के हिसाब से तय करता है, 25×25 से 800×600 pixels के बीच। CSS dimensions के बिना canvas के पास मापने के लिए ज़्यादा कुछ नहीं होता। Canvas पर साफ़ width और height सेट करें, फिर onLoad में resizeDrawingSurfaceToCanvas() कॉल करें ताकि drawing buffer मेल खाए।
क्या rive.wasm के लिए web_accessible_resources चाहिए?
Popup या side panel जैसे extension pages के लिए नहीं, क्योंकि वे अपनी पैकेज की हुई फ़ाइलें खुद लोड करते हैं। web_accessible_resources की ज़रूरत तब होती है जब किसी वेबसाइट को आपके extension से कुछ लोड करना हो, जैसे किसी साइट में iframe के रूप में inject किया गया extension page।
इस गाइड से झटपट जवाब
स्रोत
- 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