utamaro’s blog

誰かの役に立つ情報を発信するブログ

material-iconsをchrome拡張のcontent-scriptで使用する方法

環境を整える

開発環境を整えます。

今回使うライブラリは以下のものを使います。

"dependencies": {
  "css-loader": "^2.1.1",
  "file-loader": "^3.0.1",
  "jquery": "^3.4.1",
  "material-icons": "^0.3.1",
  "webpack": "^4.32.2",
  "sass-loader": "^7.1.0",
  "style-loader": "^0.23.1",
  "ts-loader": "^6.2.1",
  "typescript": "^3.7.3",
  "url-loader": "^1.1.2",
}

他にも依存関係を使っているのですが、細かいところは省いています。

webpackを使ってtypescriptで書かれたスクリプトをビルドして、それを拡張機能として利用します。

また、content-scriptを使って画面を拡張してmaterial-iconsのアイコンを表示します。

webpackの設定を行う

module.exports = {
  module: {
    rules: [
      {
        test: /\.woff2?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        use: [
          {
            loader: 'url-loader?limit=10000&name=../fonts/[name].[ext]',
          }
        ]
      },
      {
        test: /\.(ttf|eot|svg)(\?[\s\S]+)?$/,
        use: [
          {
            loader: 'file-loader?name=../fonts/[name].[ext]',
          }
        ]
      },
    ]
  }
}

ここで大事なのはloaderの部分です。

nameを指定することで出力時のwoffファイルがfonts/MaterialIcons-Regular.woffとのようになります。

出力時のpathはwebpack.configのあるディレクトリがルートとなります。

これを行わない場合、ファイル名がhashとなります。

そうすると、画面上でwoff等を読み込むのが難しくなります。 ※ build時に再度生成されるため、毎回書き直す必要が出てきます。

manifest.jsonを設定する

{
  "content_scripts": [
    {
      "run_at": "document_end",
      "matches": ["*://*/*"],
      "js": [ 
        "./assets/js/content.bundle.js"
      ]
    }
  ],
  "web_accessible_resources": [
    "assets/fonts/MaterialIcons-Regular.eot",
    "assets/fonts/MaterialIcons-Regular.ttf",
    "assets/fonts/MaterialIcons-Regular.woff",
    "assets/fonts/MaterialIcons-Regular.woff2"
  ],
}

このようになります。

ここで重要なのは、web_accessible_resourcesにフォント用のファイルを書いていることです。

(ワイルドカード*を使っても良いですが、使えるリソースを把握するのにすべて書いたほうが良いと考えてこのようにしています。)

これを行うことで、画面上からリソースを使用することができます。

このパラメータについてはManifest - Web Accessible Resourcesを参照するのが良いです。

リソースを扱う場合は以下のようにurlを書かなければなりません。

chrome-extension://[PACKAGE ID]/[PATH]

web_accessible_resourcesを使わない場合material-iconsで使用するwoffなどが以下のように読み込まれます。

https://[domain]/~.woff

もちろんそのページで.woffを使用していない限り、これはエラー(404)となります。

もう一点重要な点があります。

"assets/fonts/MaterialIcons-Regular.eot",

リソースの書き方です。

最初は以下のように相対パスで実装していました。

{
  "web_accessible_resources": [
    "./assets/fonts/MaterialIcons-Regular.eot",
  ]
}

これは動きませんでした。

原因はわかりませんでした。

予想ではリソースの読み込み時に利用するurlが異なるからです。

画面に@font-faceを埋め込む

manifestの設定時に解説した通り、リソースを利用する場合は以下のように使用する必要があります。

chrome-extension://[PACKAGE ID]/[PATH]

このパスを作成するのに必要なのが、拡張機能のIDです。

IDは開発時と配信時に別のIDが付けられるため、ハードコーディングでは対応できません。

googleはこのurlを作成するための機能を用意しています。

以下を使用します。

chrome.extension.getURL("");

getURL("hoge.png")を利用することでchrome-extension://[PACKAGE ID]/hoge.pngとうurlを取得できます。

以上を踏まえて、画面上に@font-face<style>として埋め込みます。

function makeFontFace() {
  let woff2 = chrome.extension.getURL("assets/fonts/MaterialIcons-Regular.woff2");
  let woff = chrome.extension.getURL("assets/fonts/MaterialIcons-Regular.woff2");
  let ttf = chrome.extension.getURL("assets/fonts/MaterialIcons-Regular.woff2");
  let newStyle = document.createElement('style');
  newStyle.textContent = '\
  @font-face {\
      font-family: "Material Icons TestExtension";\
      font-style: normal;\
      src: url("' + woff2 + '") format("woff2"),\
        url("' + woff + '") format("woff"),\
        url("' + ttf + '") format("truetype");\
  }'
  document.head.appendChild(newStyle);
}

これで画面上からリソースを利用できるようになります。

jquery使ってないというツッコミはなしでお願いします。

ここで重要なのはfont-familyの値として、Material Icons TestExtensionとしていることです。

もし画面上でMaterial Iconsを使っていた場合、コンフリクトが発生する可能性があります。

あとはこの関数を実行するだけです。

cssを追加する

私の場合はscssを使っています。が、sassの場合は読み替えてください。

.test-extension {
  .material-icons {
    font-family: "Material Icons TestExtension";
    font-weight: normal;
    font-style: normal;
    font-size: 24px;
    display: inline-block;
    line-height: 1;
    text-transform: none;
    letter-spacing: normal;
    word-wrap: normal;
    white-space: nowrap;
    direction: ltr;
    /* Support for all WebKit browsers. */
    -webkit-font-smoothing: antialiased;
    /* Support for Safari and Chrome. */
    text-rendering: optimizeLegibility;
    /* Support for Firefox. */
    -moz-osx-font-smoothing: grayscale;
    /* Support for IE. */
    font-feature-settings: 'liga';
  }
}

重要なのは、Material Iconsに書かれているように以下のように使用しないことです。

$material-icons-font-path: '~material-icons/iconfont/';
@import '~material-icons/iconfont/material-icons.scss';

このようにすると、せっかく参照できるようにしたリソースファイルを利用してくれません。

ちなみに、上記のスタイルは以下のファイルを参考にしました。

node_modules/material-icons/iconfont/material-icons.css

まとめ

やることは以下のことです。

  • リソースを登録する
  • @font-faceを埋め込む
  • material-iconsのスタイルを独自に用意する

以上です。

これを解決するのに1日かかってしまいました。

だれかの参考になれば幸いです。