Open18

Chrome Extensions with Angular

kazumeatkazumeat
pnpm build
  • 拡張機能のページを開く
    • chrome://extensions/
  • パッケージ化されていない拡張機能を読み込む
    • dist/app/browser/ ディレクトリを指定して読み込む

  • 拡張機能として、dist/app/browser/index.html が表示される
kazumeatkazumeat

変更検知しながら開発する場合

pnpm watch

watch していれば、拡張機能を開き直すだけで反映される、すごい!

manifest.json を更新した時は、下記をクリックするして反映する必要がある
(ディレクトリの再読み込みは不要)

kazumeatkazumeat

拡張機能でやりたいことざっくり。

  • 拡張機能の popupメニューから下記ができる
    • ローカルの画像を選択させ、画像を LocalStrage に保存する
    • 「削除」ボタンを押したら、LocalStrageに保存した画像が削除される
  • 特定ページが開かれたら、特定の画像を、LocalStrageに保存した画像に変更する
    • LocalStrageに画像がなければ、何もしない
kazumeatkazumeat

拡張機能をインストールした後は、拡張機能アイコンを右クリックしたメニューから起動したウインドウからしか console log や、storage の内容を確認できない
Inspect Popup をクリック

background.js側のログを見るためのdevtoolは、拡張機能一覧のメニューから開ける

kazumeatkazumeat

manifest version v3 での拡張機能実装方法。かなり参考にさせてもらった。
Chrome Extension with Angular — from zero to a little hero

この図で示しているところの、contentscript.js でやろうと思ってたんだけど、どうも違うみたい。

(図、お借りしてます)

Since the service workers run in a context different than the extension’s popup we cannot just add it to the scripts section in angular.json.
Anything that’s in scripts section is compiled by Angular CLI into scripts chunk and loaded by index.html (a popup context in our case).
This is also the reason we can’t just import the background script in main.ts or any other file that is compiled as part of popup build.

background.js は別でビルドしないといけない
そのために以下を使ってる
https://github.com/just-jeb/angular-builders
公式のビルドプロセスに対するカスタムビルダーを提供し、特定のニーズに応じてビルドプロセスを拡張したりカスタマイズしたりするためのツール、という理解

angular.json の設定で不足していたのが以下

      "architect": {
        "build": {
          "builder": "@angular-builders/custom-webpack:browser",
          "options": {
            "customWebpackConfig": {
              "path": "./custom-webpack.config.ts"
            },
            "main": "src/main.ts",    // <-- angular-devkit/build-angular の時は browser だったので、main に変更した
...
kazumeatkazumeat

Chrome拡張機能の実現方法がいくつかあって混乱してきたので整理。
下記の記事が参考になった。
今までやってきたやりかたは以下。(not use Content scripts)
[Chrome拡張] Chrome拡張メニューとWebページでやりとりする方法 #JavaScript - Qiita

んだけど、やりたきこととしては、やっぱり Content scripts が必要なことがわかった。
特定のページでスクリプトを実行したい = 特定のページにスクリプトを差し込みたい

kazumeatkazumeat

Angular with Content scripts は下記を参考にした
https://github.com/janverhoeckx/angular-chrome-extension-template/tree/6780bcdcc1a92e67e22683be7a9505ef649664da

runtimeChunk: false にしないと、Content scripts を読み込んでくれなかった

custom-webpack.config.ts

import type { Configuration } from 'webpack';

module.exports = {
  entry: {
    background: { import: 'src/background.ts', runtime: false },
    contentScript: 'src/content_script.ts'
  },
  optimization: {
    runtimeChunk: false
  }
} as Configuration;

そういえばここでもチャンクの説明がなされていた
https://www.justjeb.com/post/chrome-extension-with-angular-from-zero-to-a-little-hero

As you can see our code is being pushed into webpack chunks array but no one invokes it.
So yes, the script is being loaded but nothing happens, because no one actually runs the code.
Angular CLI (Webpack) puts the code that is responsible for chunks execution in a separate chunk called runtime.
Since this code split doesn't give us much in terms of performance (remember, we have all the files available locally), we can just disable this behavior for background.ts entry

全然理解できてないんだけど、チャンクをruntimeに置くだけで実行されてないから、runtimeに置くのをやめたってこと?(読み込まれたら即実行

kazumeatkazumeat

WIP

run_at: 'document_end' にしてみたけど、DOMが構築されたあとに Content scripts を実行できない
(ので、sleep入れてる
デフォルトで、window.onload 後の挿入が保証されてるはずなので、run_at は必要ないはず
原因としては、対象のDOMが生成されるのはまた別のタイミングということ。。

別のAPI叩いて部分的なDOMを構築してる
上記のDOMを取得したいけど、API実行前にスクリプト動いちゃってで取得できてない

kazumeatkazumeat

CSPのエラーが出る件

Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' http://localhost:* http://127.0.0.1:*". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution. Note that hashes do not apply to event handlers, style attributes and javascript: navigations unless the 'unsafe-hashes' keyword is present.

buildした index.html には下記のように出力されている

  <link rel="stylesheet" href="styles.css" media="print" onload="this.media='all'">

inlineでスクリプトが定義されてしまっているのでエラーになる。
また、こちらで設定している onload イベントが実行されない。
理由と回避方法は以下参考記事参照

参考