DevTools の拡張

概要

DevTools 拡張機能は、Chrome DevTools に機能を追加します。新しい UI パネルやサイドバーの追加、検査対象ページの操作、ネットワーク リクエストに関する情報の取得などを行うことができます。DevTools の注目の拡張機能を確認する。DevTools 拡張機能では、DevTools 固有の拡張機能 API のセットにアクセスできます。

DevTools 拡張機能は他の拡張機能と同様に構成されており、バックグラウンド ページ、コンテンツ スクリプト、その他のアイテムを含めることができます。また、各 DevTools 拡張機能には、DevTools API にアクセスできる DevTools ページがあります。

DevTools ページと検査対象ウィンドウおよびバックグラウンド ページ間の通信を示すアーキテクチャ図。バックグラウンド ページが、コンテンツ スクリプトと通信し、拡張機能 API にアクセスしている様子が表示されています。[DevTools] ページから、パネルの作成など、DevTools API にアクセスできます。

[DevTools] ページ

DevTools ウィンドウが開くたびに、拡張機能の DevTools ページのインスタンスが作成されます。[DevTools] ページは、[DevTools] ウィンドウの存続期間中は存在します。[DevTools] ページから、DevTools API と一部の拡張機能 API にアクセスできます。[DevTools] ページでは次のことができます。

  • devtools.panels API を使用してパネルを作成し、操作します。
  • devtools.inspectedWindow API を使用して、検査対象ウィンドウに関する情報を取得し、検査対象ウィンドウでコードを評価します。
  • devtools.network API を使用して、ネットワーク リクエストに関する情報を取得します。

DevTools ページでは、ほとんどの拡張機能 API を直接使用することはできません。コンテンツ スクリプトがアクセスできる extension API と runtime API のサブセットにアクセスできます。コンテンツ スクリプトと同様に、DevTools ページはメッセージ パッシングを使用してバックグラウンド ページと通信できます。例については、Content Script の挿入をご覧ください。

DevTools 拡張機能の作成

拡張機能の DevTools ページを作成するには、拡張機能のマニフェストに devtools_page フィールドを追加します。

{
  "name": ...
  "version": "1.0",
  "minimum_chrome_version": "10.0",
  "devtools_page": "devtools.html",
  ...
}

拡張機能のマニフェストで指定された devtools_page のインスタンスが、DevTools ウィンドウが開かれるたびに作成されます。このページでは、devtools.panels API を使用して、他の拡張機能ページをパネルやサイドバーとして [DevTools] ウィンドウに追加できます。

chrome.devtools.* API モジュールは、[DevTools] ウィンドウ内で読み込まれたページでのみ使用できます。コンテンツ スクリプトなどの拡張機能のページには、これらの API がありません。そのため、API は DevTools ウィンドウの存続期間中のみ利用できます。

また、試験運用版の DevTools API もあります。参照: chrome.experimental.* API をご覧ください。

DevTools UI 要素: パネルとサイドバー ペイン

DevTools 拡張機能では、ブラウザのアクション、コンテキスト メニュー、ポップアップなどの通常の拡張機能 UI 要素に加えて、UI 要素を DevTools ウィンドウに追加できます。

  • パネルは、[要素]、[ソース]、[ネットワーク] の各パネルと同様に最上位のタブです。
  • サイドバー ペインは、パネルに関連する補足の UI を表示します。[要素] パネルの [スタイル]、[計算済みスタイル]、[イベント リスナー] ペインは、サイドバー ペインの例です。(使用している Chrome のバージョンや、DevTools ウィンドウがドッキングされている場所によっては、サイドバー ペインの外観が画像と一致しない場合があります)。

[要素] パネルと [スタイル] サイドバー ペインが表示されている DevTools ウィンドウ。

各パネルは独自の HTML ファイルで、そのファイルには他のリソース(JavaScript、CSS、画像など)を含めることができます。基本的なパネルを作成する場合は次のようになります。

chrome.devtools.panels.create("My Panel",
    "MyPanelIcon.png",
    "Panel.html",
    function(panel) {
      // code invoked on panel creation
    }
);

パネルまたはサイドバー ペインで実行される JavaScript は、DevTools ページと同じ API にアクセスできます。

[要素] パネルの基本的なサイドバー ペインを作成するには、次のようにします。

chrome.devtools.panels.elements.createSidebarPane("My Sidebar",
    function(sidebar) {
        // sidebar initialization code here
        sidebar.setObject({ some_data: "Some data to show" });
});

サイドバー ペインにコンテンツを表示するには、いくつかの方法があります。

  • HTML コンテンツ。setPage を呼び出して、ペインに表示する HTML ページを指定します。
  • JSON データです。JSON オブジェクトを setObject に渡します。
  • JavaScript の式です。setExpression に式を渡します。DevTools は検査対象のページのコンテキストで式を評価し、戻り値を表示します。

setObjectsetExpression の両方で、DevTools コンソールに表示されるとおりに値がペインに表示されます。ただし、setExpression では DOM 要素と任意の JavaScript オブジェクトを表示できますが、setObject では JSON オブジェクトのみがサポートされます。

拡張機能コンポーネント間の通信

以降のセクションでは、DevTools 拡張機能のさまざまなコンポーネント間の通信の一般的なシナリオについて説明します。

コンテンツ スクリプトの挿入

DevTools ページから tabs.executeScript を直接呼び出すことはできません。DevTools ページからコンテンツ スクリプトを挿入するには、inspectedWindow.tabId プロパティを使用して検査対象ウィンドウのタブの ID を取得し、バックグラウンド ページにメッセージを送信する必要があります。バックグラウンド ページから tabs.executeScript を呼び出して、スクリプトを挿入します。

次のコード スニペットは、executeScript を使用してコンテンツ スクリプトを挿入する方法を示しています。

// DevTools page -- devtools.js
// Create a connection to the background page
var backgroundPageConnection = chrome.runtime.connect({
    name: "devtools-page"
});

backgroundPageConnection.onMessage.addListener(function (message) {
    // Handle responses from the background page, if any
});

// Relay the tab ID to the background page
chrome.runtime.sendMessage({
    tabId: chrome.devtools.inspectedWindow.tabId,
    scriptToInject: "content_script.js"
});

バックグラウンド ページのコード:

// Background page -- background.js
chrome.runtime.onConnect.addListener(function(devToolsConnection) {
    // assign the listener function to a variable so we can remove it later
    var devToolsListener = function(message, sender, sendResponse) {
        // Inject a content script into the identified tab
        chrome.tabs.executeScript(message.tabId,
            { file: message.scriptToInject });
    }
    // add the listener
    devToolsConnection.onMessage.addListener(devToolsListener);

    devToolsConnection.onDisconnect.addListener(function() {
         devToolsConnection.onMessage.removeListener(devToolsListener);
    });
});

検査対象ウィンドウで JavaScript を評価する

inspectedWindow.eval メソッドを使用すると、検査対象のページ内で JavaScript コードを実行できます。eval メソッドは、DevTools のページ、パネル、またはサイドバー ペインから呼び出すことができます。

デフォルトでは、式はページのメインフレームのコンテキストで評価されます。要素検査(inspect(elem))、関数での互換性の問題(debug(fn))、クリップボードへのコピー(copy())など、DevTools のコマンドライン API の機能にはすでに習熟しているはずです。inspectedWindow.eval() は、DevTools コンソールで入力されたコードと同じスクリプト実行コンテキストとオプションを使用します。これにより、eval 内でこれらの API にアクセスできます。たとえば、SOAK ではこの要素を使用して要素の検査を行います。

chrome.devtools.inspectedWindow.eval(
  "inspect($$('head script[data-soak=main]')[0])",
  function(result, isException) { }
);

または、inspectedWindow.eval()useContentScriptContext: true オプションを使用して、コンテンツ スクリプトと同じコンテキストで式を評価します。useContentScriptContext: true を指定して eval を呼び出しても、コンテンツ スクリプトのコンテキストは作成されないため、executeScript を呼び出すか、manifest.json ファイルでコンテンツ スクリプトを指定して、eval を呼び出す前にコンテキスト スクリプトを読み込む必要があります。

コンテキスト スクリプトのコンテキストが存在する場合、このオプションを使用して追加のコンテンツ スクリプトを挿入できます。

eval メソッドは、適切なコンテキストで使用すると強力になり、不適切に使用すると危険になります。検査対象ページの JavaScript コンテキストにアクセスする必要がない場合は、tabs.executeScript メソッドを使用します。2 つの方法の詳細な注意事項と比較については、inspectedWindow をご覧ください。

選択した要素をコンテンツ スクリプトに渡す

コンテンツ スクリプトには、現在選択されている要素に直接アクセスできません。ただし、inspectedWindow.eval を使用して実行するコードはすべて、DevTools コンソールとコマンドライン API にアクセスできます。たとえば、評価済みコードでは、$0 を使用して選択された要素にアクセスできます。

選択した要素をコンテンツ スクリプトに渡すには:

  • 選択した要素を引数として受け取るメソッドをコンテンツ スクリプト内に作成します。
  • DevTools ページから、useContentScriptContext: true オプション付きで inspectedWindow.eval を使用して、メソッドを呼び出します。

コンテンツ スクリプトのコードは次のようになります。

function setSelectedElement(el) {
    // do something with the selected element
}

次のように、DevTools ページからメソッドを呼び出します。

chrome.devtools.inspectedWindow.eval("setSelectedElement($0)",
    { useContentScriptContext: true });

useContentScriptContext: true オプションは、コンテンツ スクリプトと同じコンテキストで式を評価する必要があることを指定し、setSelectedElement メソッドにアクセスできるようにします。

参照パネルの window を取得する

DevTools パネルから postMessage するには、その window オブジェクトへの参照が必要です。パネルの iframe ウィンドウを panel.onShown イベント ハンドラから取得します。

onShown.addListener(function callback)
extensionPanel.onShown.addListener(function (extPanelWindow) {
    extPanelWindow instanceof Window; // true
    extPanelWindow.postMessage( // …
});

コンテンツ スクリプトから DevTools ページへのメッセージング

DevTools ページとコンテンツ スクリプト間のメッセージは、バックグラウンド ページを経由して間接的です。

コンテンツ スクリプトにメッセージを送信する場合、バックグラウンド ページでは tabs.sendMessage メソッドを使用して、メッセージを特定のタブのコンテンツ スクリプトに送信します。詳しくは、コンテンツ スクリプトの挿入をご覧ください。

コンテンツ スクリプトからメッセージを送信する場合、現在のタブに関連付けられている適切な DevTools ページ インスタンスにメッセージを配信する既製の方法はありません。回避策として、DevTools ページでバックグラウンド ページとの長期的な接続を確立し、バックグラウンド ページでタブ ID と接続のマッピングを保持します。これにより、各メッセージを正しい接続にルーティングできます。

// background.js
var connections = {};

chrome.runtime.onConnect.addListener(function (port) {

    var extensionListener = function (message, sender, sendResponse) {

        // The original connection event doesn't include the tab ID of the
        // DevTools page, so we need to send it explicitly.
        if (message.name == "init") {
          connections[message.tabId] = port;
          return;
        }

    // other message handling
    }

    // Listen to messages sent from the DevTools page
    port.onMessage.addListener(extensionListener);

    port.onDisconnect.addListener(function(port) {
        port.onMessage.removeListener(extensionListener);

        var tabs = Object.keys(connections);
        for (var i=0, len=tabs.length; i < len; i++) {
          if (connections[tabs[i]] == port) {
            delete connections[tabs[i]]
            break;
          }
        }
    });
});

// Receive message from content script and relay to the devTools page for the
// current tab
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    // Messages from content scripts should have sender.tab set
    if (sender.tab) {
      var tabId = sender.tab.id;
      if (tabId in connections) {
        connections[tabId].postMessage(request);
      } else {
        console.log("Tab not found in connection list.");
      }
    } else {
      console.log("sender.tab not defined.");
    }
    return true;
});

DevTools ページ(またはパネルやサイドバー ペイン)では、次のような接続を確立します。

// Create a connection to the background page
var backgroundPageConnection = chrome.runtime.connect({
    name: "panel"
});

backgroundPageConnection.postMessage({
    name: 'init',
    tabId: chrome.devtools.inspectedWindow.tabId
});

挿入されたスクリプトから DevTools ページへのメッセージ

上記の解決策はコンテンツ スクリプトには有効ですが、コードを直接ページに挿入する(<script> タグの追加や inspectedWindow.eval により)コードを使用する場合は、別の戦略が必要になります。この場合、runtime.sendMessage は想定どおりバックグラウンド スクリプトにメッセージを渡しません。

回避策として、挿入されたスクリプトと、中継点として機能するコンテンツ スクリプトを組み合わせることができます。コンテンツ スクリプトにメッセージを渡すには、window.postMessage API を使用します。前のセクションのバックグラウンド スクリプトがあるとします。

// injected-script.js

window.postMessage({
  greeting: 'hello there!',
  source: 'my-devtools-extension'
}, '*');
// content-script.js

window.addEventListener('message', function(event) {
  // Only accept messages from the same frame
  if (event.source !== window) {
    return;
  }

  var message = event.data;

  // Only accept messages that we know are ours
  if (typeof message !== 'object' || message === null ||
      !message.source === 'my-devtools-extension') {
    return;
  }

  chrome.runtime.sendMessage(message);
});

これで、挿入されたスクリプトからコンテンツ スクリプト、バックグラウンド スクリプト、そして最後に DevTools ページにメッセージが流れます。

また、メッセージ受け渡しの 2 つの代替手法についてもご検討ください。

DevTools の起動と終了の検出

[DevTools] ウィンドウが開いているかどうかを拡張機能でトラッキングする必要がある場合は、onConnect リスナーをバックグラウンド ページに追加して、DevTools ページから connect を呼び出します。各タブで独自の DevTools ウィンドウを開くことができるため、複数の接続イベントを受け取ることがあります。DevTools ウィンドウが開いているかどうかを追跡するには、以下に示すように接続イベントと切断イベントをカウントする必要があります。

// background.js
var openCount = 0;
chrome.runtime.onConnect.addListener(function (port) {
    if (port.name == "devtools-page") {
      if (openCount == 0) {
        alert("DevTools window opening.");
      }
      openCount++;

      port.onDisconnect.addListener(function(port) {
          openCount--;
          if (openCount == 0) {
            alert("Last DevTools window closing.");
          }
      });
    }
});

DevTools ページでは、次のような接続が作成されます。

// devtools.js

// Create a connection to the background page
var backgroundPageConnection = chrome.runtime.connect({
    name: "devtools-page"
});

DevTools 拡張機能の例

以下に示す DevTools 拡張機能のソースを参照します。

詳細

拡張機能が使用できる標準 API については、chrome.* をご覧ください。APIウェブ API をご覧ください。

フィードバックをお寄せくださいお寄せいただいたご意見やご提案は、API の改善に役立てさせていただきます。

DevTools API の使用例については、サンプルをご覧ください。