これはなに
Develop chrome extension with ruby.wasm - RubyKaigi 2023 で発表された、 unloosen (Ruby + wasm で Chrome extension を作るフレームワーク) を使ってみたので、設定方法と使用感を紹介する記事です。 (多分これが一番早いと思います)
作ったサンプル実装は↓にあります。
unloosen とはなにか
unloosen は ruby.wasm を利用して Chrome extension を作るフレームワークです。単純な Chrome extension のラッパーになるだけでなく、書きやすくするためのヘルパーメソッド等を提供していて、以下の通りに、1つの Ruby ファイルにまとめることが出来たりします。
require "unloosen"
content_script site: "www.example.com" do
h1 = document.querySelector("h1")
h1.innerText = "Hello unloosen!"
chrome.runtime.onMessage.addListener do |message|
h1 = document.querySelector("h1")
h1.innerText = message
true
end
end
popup do
submit_button = document.querySelector("button#submit")
submit_button.addEventListener "click" do |e|
form_text = document.querySelector("input#title").value
# This is workaround: in popup, JS.eval cannot be used.
query_object = Utils.build_js_object(active: true, currentWindow: true)
chrome.tabs.query(query_object) do |tabs|
tab = tabs.at(0)
chrome.tabs.sendMessage(
tab[:id],
form_text,
)
end
e.preventDefault
end
end
unloosen はどのように Ruby で Chrome Extension を書けるようにしているか
unloosen は以下の図の流れで、 Ruby コードを上手く Chrome Extension として使えるようにしています。
大まかに起動の流れを説明すると、以下の通りになります。
- manifest.json (Chrome 拡張の設定ファイル) で unloosen の提供する起動用コードを指定
- unloosen の提供する起動用コードが、 ruby.wasm, unloosen gem (Chrome 拡張用の DSL などを提供) を読み込み
- unloosen の提供する起動用コードが、拡張の作者が書いた Ruby コード (
app.rb
) を読み込み - ruby.wasm が app.rb 内の処理を実行
ユーザーが書く必要があるのは、 manifest.json と app.rb (あと popup するときは表示用の HTML ファイル) だけです。 こういう感じで、js を1行も書かずに Chrome Extension を作ることが出来ます。
unloosen を作った Chrome Extension の作り方
今回サンプルとして、超簡単な Chrome Extension を作ってみました。
popup を表示してページを書き換えるだけの超簡単な Chrome Extension です。
とりあえずこの extension をいじることで Chrome Extension を動かすことは出来ます。
詳しく知りたい人向けに、これを元に説明します。
1. unloosen の起動用ファイルをダウンロードして配置する
unloosen を使う Chrome 拡張には、以下の起動用ファイルが必要になります。
- entrypoint となるコード群
loader-content-script.esm.js
module-content-script.esm.js
module-popup.esm.js
module-sandbox.esm.js
module-background.esm.js
- unloosen gem を含んだ ruby.wasm
これらは unloosen-ruby-loader
という npm package にまとめられているので、これをダウンロードするのが良いでしょう。
2. 各種設定ファイルを書く
- でダウンロードしたファイルを参照するように、
manifest.json
(Chrome 拡張の設定ファイル) ,unloosen.config.json
(unloosen 用の設定ファイル) を書きます。
{
"manifest_version": 3,
"name": "unloosen-example",
"description": "A demo extension of unloosen (https://github.com/aaaa777/unloosen)",
"version": "0.0.1",
"content_scripts": [
{
"js": [
"node_modules/unloosen-ruby-loader/dist/entry/loader-content-script.esm.js"
],
"matches": ["http://www.example.com/"]
}
],
"action": {
"default_popup":"popup.html"
},
"permissions": [
"activeTab"
],
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"
},
"web_accessible_resources": [
{
"resources": [
"*",
"node_modules/unloosen-ruby-loader/dist/**"
],
"matches": ["<all_urls>"]
}
]
}
{
"application": "app.rb",
"ruby.wasm": "node_modules/unloosen-ruby-loader/dist/ruby.wasm",
"content-script-entry": "node_modules/unloosen-ruby-loader/dist/entry/module-content-script.esm.js",
"sandbox-entry": "node_modules/unloosen-ruby-loader/dist/entry/module-sandbox.esm.js",
"popup-entry": "node_modules/unloosen-ruby-loader/dist/entry/module-popup.esm.js"
}
popup 機能を利用したい場合は、以下の通りに
<html>
<head>
<script src="/node_modules/unloosen-ruby-loader/dist/entry/module-popup.esm.js" type="module"></script>
<style>
body{
width: 300px;
height: 300px;
}
</style>
</head>
<body>
<p id="tabnum"></p>
<form id="form">
<fieldset>
<legend>new h1 content</legend>
<label><input type="text" name="title" id="title"></label>
<button id="submit">Replace h1 content</button>
</fieldset>
</form>
</body>
</html>
3. app.rb を書く
1, 2 で必要な設定は済んだので、実装に必要なコードを app.rb
に書いていきます。
require 'unloosen' # 各種ヘルパーメソッド等を定義
content_script site: "www.example.com" do
# ページを開いたときの処理を書く
# h1 = document.querySelector("h1")
# h1.innerText = "Hello unloosen!"
end
popup do
# ツールバーの拡張のボタンを押したときの処理を書く
# submit_button = document.querySelector("button#submit")
# submit_button.addEventListener "click" do |e|
# form_text = document.querySelector("input#title").value
# ...
# end
end
4. Unpacked Extension としてインストール
https://developer.chrome.com/docs/extensions/mv3/getstarted/development-basics/#load-unpacked を元に manifest.json
があるディレクトリを 「Unpacked Extension」 として読み込みます。
これで Ruby で実装した Chrome Extension がインストールできました
触ってみての感想
まず、全く js のコードを書かずに Ruby だけで Chrome Extension を書ける、というのはかなり体験として面白いです。 js とそのビルドのことを全く考えたくないタイプの人間が Chrome Extension を書きたいときに重宝するのと、 かなり設定が簡略化されているので ruby.wasm を試しに触って見るための入り口として有用だと感じました。(最初の設定が今後ジェネレーター等で簡略化されればさらに試しやすくなりそう)
ただ、 実際に書く際に ruby.wasm のクセ, Chrome Extension の仕様でハマることもそこそこありました。(unloosen はかなり書きやすくなるようにケアしているものの)、これらの仕様を知らなくても書けるというわけではなく、ある程度これらについて知っておく必要はあります。
- Ruby 内で JS オブジェクトを使う際のクセ (例: null を表す
JS::Object
は falsy にならない、 Hash はそのまま js メソッドの引数として渡せない、 ...etc) - CSP に阻まれて popup 中で
JS.eval
が使えない、などの制約 - デバッグの難しさ (デバッガが使えたり、sourcemap が使えると…!)
ただ、 ruby.wasm を学ぶ材料として unloosen はハードルが低く、自分も今回始めて unloosen を通じて ruby.wasm を初めて使う機会になりになり、その過程で、 ruby.wasm がどういうことが出来るか、どこに課題があるかを知ることが出来て楽しかったです
ruby.wasm の入り口としては面白いですし、今後の発展も楽しみな分野なので、試しに潜ってみる感覚で使ってみるのはどうでしょうか。
--
この記事は 「【RubyKaigi 2023連動イベント】みんなでRubyの知見を共有しよう」 の参加記事です。 RubyKaigi で学んだことや、面白かった内容の共有、その他 Ruby の知見を是非投稿してみませんか?