ブラウザ拡張を作り始めた
from JSとCSSをサクッと書いて、特定のURLで動かして、他人に共有したい
作り始めた2022/5/14
Safari Web Extension、XCodeでテンプレート作ったらSwiftファイルがいっぱい作られて邪魔だった
どうやらJS(Safari)→Swift(アプリ)というメッセージングAPIがあるらしい
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging
面白いけど、こんな機能使わんので、リポジトリに含めたくない
Web Extensions、標準化されたと思いきや、V2とV3で大きな乖離がある
https://developer.chrome.com/docs/extensions/mv3/intro/mv3-migration/
SafariとFireFoxはV2のみサポートしており、V3をサポートする予定もなさそう
Chrome Web Storeは2022年のうちにV2がEOLになるので、もはやV3一択
V3=Chrome Web Extensionと思った方が良さそう
2022/5/14
chromeの型付け
https://github.com/GoogleChrome/chrome-types
npm i -D chrome-typesするだけでVSCodeが解釈してくれる
onClickedイベント
https://github.com/GoogleChrome/chrome-extensions-samples/tree/main/examples/page-redder
manifestに"action": {}が必要
JavaScriptの実行
https://developer.chrome.com/docs/extensions/reference/scripting/#method-executeScript
デフォルトではCSP制限があり、chrome-extension:// 以下ではinline scriptを実行できない
https://developer.chrome.com/docs/extensions/mv3/intro/mv3-migration/#content-security-policy
inlineじゃなければよいのでは?でもscript srcで開けるのか
executeScript()だと動的なコードは実行できないっぽい
https://gyazo.com/dc93e82d6464b6173af3e343f0c97335
これはscriptタグを作ってappendChildした場合
CSPが設定されていないサイトでもこうなる
world: "MAIN"にすると上手くいった
https://developer.chrome.com/docs/extensions/reference/scripting/#type-ExecutionWorld
ISOLATEDの説明は https://developer.chrome.com/docs/extensions/mv3/content_scripts/#isolated_world にある
2022/5/15
monaco-editorを搭載した
Webpackでビルドするのがやや大変だった
https://github.com/microsoft/monaco-editor/tree/main/samples/browser-esm-webpack-small を元にした
import "monaco-editor/esm/vs/editor/..."以下のパスが変わっていたので勘で直した
Webpack5からflie-loaderではなくAsset Modulesになった
devtool: "eval"(default)だとCSPの制限で実行できないので、devtool: falseにした
どうやってWebページを開いた時にScriptを実行する?
onCommittedかonDOMContentLoadedかonCompletedイベントをハンドルする
URLパターンマッチ
backgroundからexecuteScriptする
タイミング的にウェブサイトにScriptより遅くなってしまう
https://gyazo.com/c25719be866c58c044e0501482825b83
ただしwindow.onloadのタイミングよりは早く実行できる
元からあるウェブサイトのJSは何らかの方法で阻害できたら便利そう
matchの文法どうしようか
macOS/ChromeでCmd+Nをキーバインドできない
2022/6/4
コンテキストメニューから開けると、「このDOMをいじる」みたいな事が出来て面白そう
background.jsからchrome.contextMenus.createを呼んでみたんだけど、コンテキストメニューが追加されない
MV3で仕様が変わったせい?
エラーが出ているっぽいのだが、なぜかchrome://extensionsでエラーを開くと画面が表示されない
これはchrome://extensionsのバグっぽい
まだアイコンを設定してないからかも知れない
もしかしたらcontent scriptから呼ぶのかも知れない
言語のロゴを表示するようにした
2022/6/8
変なバグ
コードをJSにコピペして保存する前にCSSを保存すると、コピペしていたJSが消えるバグ
Extensionのstrorageは、巨大なJSONとして使った方がいいかも知れない
code__{id}みたいなkeyは地味に扱いが難しい
結局全てのキーを取得することになる
型付けもしづらい
codesみたいなキーでArrayを格納した方が扱いやすそう
2022/6/16 任意のコードを実行する場合、CSP制限を回避するのが難しい
executeScriptはCSPの制限を回避できるっぽい
background側でstring→Functionの変換をすると、今度はChrome Extensionの制約でダメ
よく分からないが、Background Scriptではevalに相当することは出来ないらしい
Tampermonkey、MV3移行無理なのでは?
つまり任意のコードを実行するには、executeScriptからscriptタグを入れるかevalする必要がある
ここでscriptタグを挿入すると、CSP制限に引っかかる
https://gyazo.com/db356d76508e299406147f31f40680b5
https://gyazo.com/39c7f2a100fb8e9bf1419529622b7120
これはgithub.comのCSPなんだけど、よく見ると'unsafe-eval'は許可してるぞ…?
evalにしたら動いた
Stacktraceが見づらくなりそうだが、まあ仕方ない
2022/6/22 evalだと動かなくなった
https://gyazo.com/03bd66757342b5cdbf908597e24e74be
github.comのCSPは変わってない
発想を変えて、popupの画面を作れるようにするのはどうだろう?
Proto ExtensionのJavaScriptをsandboxed pageで実行する
2022/6/16 URL matchで実行するのとSPAは相性が悪い
ランディングページは違うURLだったけど、遷移してそのURLに来たりする
history.pushState()されたタイミングでもう一回評価する必要がある?
あるいは、もう何らかのアクションで実行させるか
特定のキーを押す
右クリックしてコンテキストメニューを開く
MV3のcontextMenus再挑戦
エラーが出る
Unchecked runtime.lastError: Extensions using event pages or Service Workers must pass an id parameter to chrome.contextMenus.create
idを設定したら上手くいった!
onclick callbackは指定できないらしい
Unchecked runtime.lastError: Extensions using event pages or Service Workers cannot pass an onclick parameter to chrome.contextMenus.create. Instead, use the chrome.contextMenus.onClicked event.
chrome.contextMenus.onClickを使ったらcallbackも取れた
https://gyazo.com/ffcc447d2fb55bcbc863a2b0cd0d3bd8
2022/6/18 SPAに対応した
onHistoryStateUpdatedを使うと、history.pushを検知できる
同じコードを2回実行しないように、このように実行する
code:js
const results = await chrome.scripting.executeScript({
target: {
tabId: details.tabId,
},
args: [code, ${key}_HAS_ALREADY_EXECUTED],
func: (code, executedKey) => {
// @ts-ignore
if (windowexecutedKey) return false;
eval(code);
// @ts-ignore
return (windowexecutedKey = true);
},
world: "MAIN", // Webページと同じコンテキストでコードを実行する
});
results[0].resultを見れば、今回実行したのか、それとも実行済みだったのかが分かる