🎹

Chrome拡張でページ内のwindowオブジェクトを取得してpopupやbackgroundに送信する方法

2022/06/17に公開
2

Chrome 拡張からページ内の window オブジェクトにアクセスして色々やるのに少し詰まったので書きます。

ページ内の window オブジェクトを取得する

content_scripts からアクセスできる window オブジェクトはページ内の window オブジェクトとは別物です。
そのためページ内の window オブジェクトにアクセスするためには直接ページに js を埋め込む必要があります。

manifest.json
  "manifest_version": 3,
  "content_scripts": [
    {
      "matches": ["https://example.com/*"],
      "js": ["content_scripts.js"]
    }
  ],
  "web_accessible_resources": [
    {
      "resources": ["embed.js"],
      "matches": ["https://example.com/*"]
    }
  ]

web_accessible_resourcesembed.jsを登録します。

content_scripts.js
const head = document.head
const script = document.createElement('script')
script.src = chrome.runtime.getURL('embed.js')
head.appendChild(script)
embed.js
const func = () => {
  console.log(window)
}
func()

ここで window オブジェクトを操作できます。

window オブジェクトの情報を popup や background に送信する

埋め込んだ js 内ではchrome.runtime.sendMessageが使えません。
そのため他機能と通信するためにwindow.postMessageで一度 content_scripts を経由します。

流れとしてはこんな感じです。

実装例として popup でボタンを押したら window オブジェクトを取得してみます。

popup.tsx
export const Popup = () => {
  const [state, setState] = useState()

  useEffect(() => {
    // content_scriptsからのメッセージを受け取ります
    chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
      switch(request.action) {
        case 'GET_WINDOW':
	  setState(request.data)
	  break
      }
    }
  }, [])

  const getWindowObject = async () => {
    const tabId = await getTabId() // chrome.tabs.query()でtabIdを取得
    if (tabId != null) {
      // content_scriptsにメッセージを送信
      chrome.tabs.sendMessage(tabId, { action: 'GET_WINDOW' })
    }
  }
  return <button onClick={getWindowObject}>btn</button>
}
content_scripts.js
// popupからのメッセージを受け取り、embedに送信します
chrome.runtime.onMessage.addListener((request, sender) => {
  window.postMessage(
    { type: 'FROM_CONTENT', action: request.action, data: request.data },
    '*'
  )
})

// embedからのメッセージを受け取り、popupに送信します
window.addEventListener('message', () => {
  if (event.source != window) {
    return
  }
  if (event.data.type && event.data.type === 'FROM_EMBED') {
    switch (event.data.action) {
      case 'GET_WINDOW':
        chrome.runtime.sendMessage({
          action: event.data.action,
          data: event.data.data
        })
        break
    }
  }
}, false)
embed.js
// content_scriptsからのメッセージを受け取り、windowオブジェクトを取得して送信します
window.addEventListener('message', () => {
  if (event.source != window) {
    return
  }
  if (event.data.type && event.data.type === 'FROM_CONTENT') {
    switch (event.data.action) {
      case 'GET_WINDOW':
        const data = JSON.stringify(window)
        window.postMessage(
	{ type: 'FROM_EMBED', action: 'GET_WINDOW', data },
          '*'
        )
        break
    }
  }
}, false)

これで popup や background から window オブジェクトを操作することが出来るはずです。

まとめ

調べてもあまり出てこなかったので書いてみました。
もっと良い方法があれば教えて下さい mm

GitHubで編集を提案

Discussion

BBBB

有用な記事ありがとうございます!

冒頭のcontent_script.js部分ですが
script.src = chrome.runtime.getURL('content.js')
script.src = chrome.runtime.getURL('embed.js')が正しいような気がしました。

ご確認よろしくお願いいたします!

ellrekaellreka

コメントありがとうございます!
ご指摘の通りembed.jsが正しかったので修正いたしました!