11

I've done my research and struggled with this for a while, but I need your help.

I'm building a Chrome DevTools extension. It should should pass the currently selected element from the 'Elements' panel as a reference to a JS object defined in a content script.

It is important that I pass the reference to the selected element, or some other way of identifying the element from the content script.

I understand the workflow with 'isolated worlds' in Chrome DevTools. I also understand messaging between extension pages, background page and content scripts. This only happens with JSON primitives, hence no JS scope passing.

How can I pass the element selected in devtools Elements panel to the content script that lives in the inspected page?

Edit

Here's what I know so far:

Getting a reference to the selected element:

chrome.devtools.inspectedWindow.eval("(" + function(){ console.log($0) }.toString() + ")()")
  • That function expression will run in the context of the inspected page, not in the context of the devtools extension and not in the context of the 'isolated world' of the content script. I don't believe it is possible to pass in a reference to a different context using closures.

  • The reference to the selected DOM element $0 can't be returned because it can't be serialized to JSON due to circular references.

  • The chrome.devtools namespace isn't available outside the devtools extension page. The $0 reference can't be used outside the evaluated expression in chrome.devtools.inspectedWindow

Workaround

As a workaround, I chose to use the shared DOM to mark the selected element with a data attribute and use that to re-select it in the context of the content script. Messaging is used to pass the data attribute marker around.

Here's a simplified version of the code:

In the devtools extension page:

// setup a communication port
port = chrome.runtime.connect({name: "devtools"});

chrome.devtools.panels.elements.onSelectionChanged.addListener(function(){

  // expression to run in the context of the inspected page
  var expression = "(" + mark.toString() + ")()"

  // evaluate the expression and handle the result
  chrome.devtools.inspectedWindow.eval(expression, dispatchToContentScript)
});


function mark(){

  // mark the currently selected element
  $0.setAttribute('data-selected')

  // send the marker to the callback
  return { marker: 'data-selected' }
}

function dispatchToContentScript(data){

  // dispatch data to the content script which is listening to the same port.
  port.postMessage(data)
}

In the content script:

var port = chrome.runtime.connect({name: "devtools"});

port.onMessage.addListener(function(data) {

  // re-select the element in the context of the content script
  var el = document.querySelector('['+ data.marker +']')
})

It's not a clean solution but I can use it for my needs.

Is there a simpler way to achieve the same result - identify from a content script the element selected in the devtools 'Elements' panel?

4

2 回答 2

9

chrome.devtools.inspectedWindow 已更新API以支持在内容脚本的上下文中执行脚本。

官方 Chrome API 中的此更新淘汰了我们上面描述的黑客。我们现在可以通过以下方式实现预期结果:

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

$0参数将引用在“元素”面板中选择的元素。

于 2014-01-20T11:16:28.113 回答
3

我这样做的方式也是一种 hack .. 而不是注入在您的扩展中定义的内容脚本,您可以注入一个脚本标签,指向您的在线文件(或本地,相对于检查的 html ):

//devtools.js
var str = "var s = document.createElement('script');" +
      "s.src = 'http://extentionDomain/extentionFile.js';" +
      "document.body.appendChild(s);";
chrome.devtools.inspectedWindow.eval(str);

在线文件定义了一个全局:

var myExtention = { doStuff: function(selectedElement){ ..}}

devtools 可以调用它并将所选元素传递给它:

chrome.devtools.panels.elements.onSelectionChanged.addListener(function(){
    chrome.devtools.inspectedWindow.eval('myExtention.doStuff($0)');});

但是,我还没有找到一种方法可以使用此设置将参考从检查的窗口发送回 devtools 扩展。

于 2014-01-19T22:23:15.580 回答