Search code examples
javascriptgoogle-chrome-extensionchrome-extension-manifest-v3chrome-extension-manifest-v2

chrome.debugger.attach() not attaching to the current tab


I am trying to attach a debugger to the current tab but it gives me this error

Uncaught (in promise) Error: Cannot access a chrome-extension:// URL of different extension

Here is my background script file that is handling this

chrome.runtime.onMessage.addListener(async (req, sender, sendResponse) => {
  if (req.name == "test") {
    const [tab] = await chrome.tabs.query({
      active: true
    })

    console.log(tab)
    const tabId = tab.id
    console.log(tabId)
    const text = "Hello there mate"

    await chrome.debugger.detach({ tabId: tabId })
    console.log("before attach")
    await chrome.debugger.attach({ tabId }, "1.2")
    console.log("after attach")

    await chrome.debugger.sendCommand({ tabId }, "Runtime.enable")
    await chrome.debugger.sendCommand({ tabId }, "DOM.enable")

    for (const char of text) {
      console.log(char)
      await chrome.debugger.sendCommand({ tabId }, "Input.dispatchKeyEvent", {
        type: "keyDown",
        text: char
      })
      await sleep(50)
      await chrome.debugger.sendCommand({ tabId }, "Input.dispatchKeyEvent", {
        type: "keyUp",
        text: char
      })
      await sleep(50)
    }
    sendResponse(tabId)
  }
})

I am sending the a message from my content script 5 seconds after the content script loads.

Why is this not working ?

I was expecting it to attach the debugger and run the dispatch event in order to write in a input field.


Solution

  • The error means you're attaching to a chrome-extension:// tab of a different extension or to a normal tab that contains an iframe of a different extension.

    • The tab case is solved by removing chrome.tabs.query and using sender.tab.id instead.

    • The iframe case is a bug in Chrome and the workaround is to remove all such iframes from the tab in your content script. There's no 100% reliable method of finding them, but in most cases it's sufficient to check the src attribute:

      if (window.length) for (const el of document.querySelectorAll(
        `iframe[src^="chrome-extension://"]:not([src^="${chrome.runtime.getURL('')}"])`))
          el.remove();
      

      Some extensions add their frames inside a closed Shadow DOM, though, so in that case you'll need to check all elements in the document:

      (function removeExtensionFrames(root) {
        if (root || window.length)
          for (const el of (root || document).querySelectorAll(
          `iframe[src^="chrome-extension:"]:not([src^="${chrome.runtime.getURL('')}"])`))
            el.remove();
        for (let el of root.querySelectorAll('*'))
          if (!el.shadowRoot && (el = chrome.dom.openOrClosedShadowRoot(el)))
            removeExtensionFrames(el);
      })();
      

      You may want to store the elements somewhere to re-add them afterwards.

    P.S. Either patch your onMessage listener or don't use async, more info.