obscuritas
すべてのウェブページを強制的にダークテーマへ変更する拡張機能です。
Debugging Web Extensions
Developer Tools で拡張機能のデバッグを有効化
Developer Tools の設定から Enable browser chrome and add-on debuggin toolboxes を有効化すると、Debugger タブに拡張機能のソースコードが表示されるようになります。
スタイル変更の検知
JS 実行の検知
動的にスタイルを変更するのは JS しかないので、ページが読み込まれた後に限って言えば (ページの読み込みは DOMContentLoaded などあるのであまり気にしていません)、click や scroll などのユーザーインタラクションイベントをフックすることで、JS の実行を検知できるのではと考えました。
そんなとき、Object.observe() を調べていたら Angular のデータバインドでは、オブジェクトのプロパティーが変更されたことを自動検出してビューに反映していることを知りました。これは、DOM イベントや XHR、タイマーなどをフックすることで JS 実行をすべて検出しているようです (Zone.js というのがその役割をになっています)。
なるほど方向性はあっていたのかもと安心したのでこれを参考に JS 実行を検知します。
参考
できなかったこと
ページ内の CSS の参照
拡張機能からページ内の link 要素で指定されてる CSS テキストを参照しようとしたら、CORS がどーたらどうとかでブロックされてしまいました。
どうせ style プロパティーを参照しないと完璧に見た目を調整することはできないから諦めました。
Content scripts からページ上のグローバル変数の書き換え
Monkey patch のために content scripts 内で window.alert などのグローバル変数を書き換えようとしましたが、セキュリティー上の制限からこの操作はできませんでした。ページ内スクリプトと content scripts では実行コンテキストが違っていて、content scripts 内でグローバル変数を書き換えてもページ内のグローバル変数には何の影響も与えないようです。
この問題の解決策として、manifest.json に "web_accessible_resources" キーを足してページに追加するスクリプトファイルのパスを指定し、ページにそのファイルを script 要素で追加することにより回避できます。
参考
メモ
見えてないノードは後回しにしたい
一度追加されて色変換かけた後、ホバーやクリックがない限り色の変化は起きなさそう
メインループでは一度処理したノードは独自属性付加して二度と処理しないようにした
ホバーやクリックされたノードの子孫と祖先は変化しそう
getComputedStyle() だと -webkit-text-fill-color' がデフォルトっぽい値になってて、例えば color: blue だとしても #333 とかが返ってきちゃう 彩度が高い色はたぶんブランドカラーだろうという方針
彩度でコントラストを作ったら見づらくなってしまった。彩度は控えめに
白背景の場合、影だけで重なりを表現する場合があるが、ダークモードでは背景の明度をあげないといけない
querySelectorAll('*') では pseudo element を取得できない。ふつーの element とは違うから。getComputedStyle(element, ':before') のようにするとスタイルを取得できる
現在はドラフト段階だが、将来的には pseudo-elements-4 という仕様が各ブラウザーで実装されるかもしれない。そうなると pseudo-elements をふつーの Element のように扱えるかも
Pseudo element に対して JavaScript からスタイルを適用する方法は style 要素でのスタイルの追加しかない
要素ごとに style 要素を作ってツリーに追加すると極端なパフォーマンス劣化が発生する
1 つだけ style 要素を作って、innerHTML を書き換えるほうがお得
領域大きいやつ優先探索
window.getComputedStyle(element, '::before').content === 'none' なら pseudo element は存在しないと判定できる
セレクターによっては (例えば ::selection とか) none ではなく normal になる
画像も Canvas で色変換している。すごい
Pseudo-elements のセレクターごとに style 要素を作ると重すぎて使い物にならない
なので 1 個だけ style 要素を作って、contentText を書き換えるのが良い