2018年2月16日金曜日

強力なポップアップブロックをChrome拡張で作ってみた (2)

強力なポップアップブロックをChrome拡張で作ってみた (1) の続きです。

前回は若干使いにくかったスクリプトの改善案として挙げた、(3) URLを判定したポップアップの停止について今回は話します。 もう少し具体的に言うと、閲覧中のページとはホストの異なるサイトを勝手に開こうとした場合、 それは鬱陶しいポップアップである可能性が非常に高いはずです。 そのようなポップアップだけ削除すれば、快適なブラウジングができると考えられます。

ただしこれを実現する場合、前回のファイルに多少修正を加える必要があります。 manifest.jsonを以下のように編集します。 まずは "run_at": "document_start" の指定でサイトのスクリプトより早く実行できるようにします。
{
  "name": "Minimal User CSS & JavaScript",
  "version": "0.0.1",
  "manifest_version": 2,
  "description": "Minimal User CSS & JavaScript",
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "css": ["user.css"],
      "js": ["user.js"],
      "run_at": "document_start"  <-- 追加
    }
  ],
  "web_accessible_resources": ["popup_blocker.js"]
}

user.jsではscriptタグを付ける場所をbodyから文書の先頭に変更します。 これは "run_at": "document_start" で呼び出した際にはDOMの解析は終わっていても構築が終わっていないため、bodyタグが存在しないからです。
function loadScript(file, tag) {
  // var node = document.getElementsByTagName(tag)[0];
  var s = document.createElement('script');
  s.setAttribute('type', 'text/javascript');
  s.setAttribute('src', file);
  // return node.appendChild(s);
  return document.documentElement.appendChild(s);  <-- 変更
};
loadScript(chrome.runtime.getURL('popup_blocker.js'), 'body');

popup_blocker.jsではURLを判定したポップアップの停止をできるようにします。 これにはlocation.replaceやwindow.openといったURLを開くための関数を上書きする必要があり、 以前とは異なりオブジェクトの凍結や削除では不可能です。 サイトのスクリプトより早く関数の中身を変えておく必要もあり、これは以下のように書く事ができます。 window.openを上書きされないようオブジェクト化しておいて独自関数のcallで呼び出すのがポイント。
Object.freeze(document.location);
// delete window.open;

var window_open = window.open;
window.open = function(url_str, name, options) {
  try {
    url = new URL(url_str);  // not support IE
    if (location.host == url.host) {
      return window_open.call(window, url_str, name, options);
    } else {
      if (confirm('window.open [ '+ url_str + ' ], ok?')) {
        return window_open.call(window, url_str, name, options);
      }
    }
  } catch(e) {  // 同ドメインのURLパラメータなど
    return window_open.call(window, url_str, name, options);
  }
};

あとはこの拡張を読み込むだけです。chrome://extensions にアクセスし、上部に表示されている「デベロッパーモード」をONにします。 そして「パッケージ化されていない拡張機能を読み込む...」を押して、usercssjsディレクトリを選択すれば完了です。 普通の拡張のようにパッケージ化していないので、Chrome拡張のファイルを編集後に設定をリロードする事ができます。


ホスト判定だとGoogleのようにたくさんのホスト名を持っているサイトでは誤爆の危険性はありますが、 ホストが異なる時には開くかどうかの確認画面を出すようにしたので、 何の警告もなしに停止する2行のスクリプトよりは使いやすいかも知れません。

ほとんどのページで問題なく動いているのですが、たまに動かなくなるページがありますね。 例えばBloggerのプレビューボタン。 これは一体どういう処理をしているのだろう…。
返り値を返すように修正した事で今のところ手元の環境ではすべてのページで動いていますが、動作不良はあるかも知れません。(最終更新: 2018-10-06)


ところでこのスクリプトを作ってみてわかったのですが、 location.assignやlocation.replaceといったメソッドはoverrideできないようで、 できるのはせいぜいlocation.hrefの凍結という事がわかりました。 つまりポップアップは防げるけどリダイレクトは防げないという事のようです (駄目じゃん…)。

さらにこのスクリプトだけではiframeを利用したケースに対応できていません。 iframeと組み合わせたポップアップはクリックジャッキング対策をさらに施す必要があるようです。

細かな動作不良が気になる方は前回書いたようなサイトごとの適用のほうが良いかも知れません。 より使いやすい解決案が浮かんだら追記していこうと思います。

0 件のコメント:

コメントを投稿