2014-10-01 153 views
2

我有一个自己的脚本和变量的网页,我需要执行并从我的扩展的Background.js中检索返回值。从Background.js在页面级执行代码并返回值

我明白(我想!)为了与网页交互,它必须通过chrome.tabs.executeScript或ContentScript来完成,但因为代码必须在原始页面的上下文中执行(在为了使脚本和变量具有作用域),它需要首先注入页面。

在此great post by Rob W之后,我可以调用页面级脚本/变量,但我很努力去理解如何以这种方式返回值。

这里就是我这么远......

网页代码(我想与互动):

<html> 
<head> 
<script> 
    var favColor = "Blue"; 

    function getURL() { 
     return window.location.href; 
    } 
</script> 
</head> 

<body> 
    <p>Example web page with script content I want interact with...</p> 
</body> 
</html> 

manifest.json的

{ 
    // Extension ID: behakphdmjpjhhbilolgcfgpnpcoamaa 
    "name": "MyExtension", 
    "version": "1.0", 
    "manifest_version": 2, 
    "description": "My Desc Here", 
    "background": { 
    "scripts": ["background.js"] 
    }, 
    "icons": { 
    "128": "icon-128px.png" 
    }, 
    "permissions": [ 
    "background", 
    "tabs", 
    "http://*/", 
    "https://*/", 
    "file://*/",   //### (DEBUG ONLY) 
    "nativeMessaging" 
    ] 
} 

background.js

codeToExec = ['var actualCode = "alert(favColor)";', 
        'var script = document.createElement("script");', 
        ' script.textContent = actualCode;', 
        '(document.head||document.documentElement).appendChild(script);', 
        'script.parentNode.removeChild(script);'].join('\n'); 
chrome.tabs.executeScript(tab.id, {code:codeToExec}, function(result) { 
    console.log('Result = ' + result); 
}); 

我意识到代码目前只是“提醒”favColor变量(这只是一个测试,以确保我能看到它的工作)。但是,如果我尝试返回该变量(通过将其保留为最后一个语句或通过说“return favColor”),executeScript回调从不具有该值。

因此,似乎有(至少)三个级别的位置:

  1. background.js
  2. 内容脚本
  3. 实际网页(含脚本/变量)

...我想知道什么是rec从1级到3级(上图)进行交谈并返回值?

在此先感谢:o)

+0

你理解上下文的划分方式非常正确,赞誉和+1。即将回答。 – Xan 2014-10-01 11:40:20

+0

(...感谢您的信心助推器!) – 2014-10-01 14:45:12

回答

6

您对理解3层上下文分离非常正确。

  • 背景页面是一个单独的页面,因此不会与可见页面共享JS或DOM。
  • 内容脚本与网页的JS上下文分离,但共享DOM。
  • 您可以使用共享DOM将代码注入页面的上下文中。它可以访问JS上下文,但不能访问Chrome API。

为了通信,这些层使用不同的方法:

背景< - >内容谈话通过铬API。
最原始的是executeScript的回调,但对于除单线之外的任何东西都是不切实际的。
常用的方法是使用Messaging
不常见,但可以使用chrome.storage及其onChanged事件进行通信。

页面< - >分机不能使用相同的技术。
由于注入的页面上下文脚本在技术上与页面自己的脚本没有区别,因此您正在寻找网页与扩展对话的方法。有可用的2种方法:

  1. 虽然页面都非常,非常有限的访问chrome.*的API,他们仍然可以使用邮件联系扩展。这通过"externally_connectable" method实现。

    我最近详细描述了它this answer。简而言之,如果您的扩展程序声明允许域与其进行通信,并且域知道该扩展的ID,则它可以向该扩展发送外部消息。

    好处是直接与扩展对话,但缺点是要求将您使用的域从明确列入白名单,并且您需要跟踪您的扩展ID(但由于您正在注入代码,您可以提供带有ID的代码)。如果你需要在任何域上使用它,这是不合适的。

  2. 另一种解决方案是使用DOM事件。由于DOM在内容脚本和页面脚本之间共享,因此一个事件生成的事件对另一个事件可见。

    该文档演示了如何为此效果use window.postMessage;使用自定义事件在概念上更清晰。

    再次,我answered about this before

    该方法的不足之处在于要求内容脚本充当代理。这些方针的东西必须在内容脚本:

    window.addEventListener("PassToBackground", function(evt) { 
        chrome.runtime.sendMessage(evt.detail); 
    }, false); 
    

    而后台脚本处理这与chrome.runtime.onMessage听众。

我鼓励你写一个单独的内容脚本和一个file属性,而不是code调用executeScript,而不是依赖于它的回调。消息传递更清晰并允许多次将数据返回到后台脚本。

+0

谢谢Xan提供这样一个详细的答案,那太棒了! 我已经得到它的工作(尽管更多的箍比我本来喜欢的,但至少它的工作!):) – 2014-10-01 14:43:17

3

Xan的答案中的方法(使用事件进行通信)是推荐的方法。然而,实施这个概念(并且以一种安全的方式!)是比较困难的。

所以我会指出,这是可能同步从页面返回值的内容脚本。当在页面中插入带有内联脚本的<script>标签时,立即同步执行脚本(在.appendChild(script)方法返回之前)。

通过使用注入的脚本将结果分配给可由内容脚本访问的DOM对象,可以充分利用此行为。例如,通过覆盖当前活动的<script>标签的文本内容。 <script>标记中的代码仅执行一次,因此您可以将任何垃圾分配给<script>标记的内容,因为它不会再被解析为代码。例如:

// background script 
// The next code will run as a content script (via chrome.tabs.executeScript) 
var codeToExec = [ 
    // actualCode will run in the page's context 
    'var actualCode = "document.currentScript.textContent = favColor;";', 
    'var script = document.createElement("script");', 
    'script.textContent = actualCode;', 
    '(document.head||document.documentElement).appendChild(script);', 
    'script.remove();', 
    'script.textContent;' 
].join('\n'); 

chrome.tabs.executeScript(tab.id, { 
    code: codeToExec 
}, function(result) { 
    // NOTE: result is an array of results. It is usually an array with size 1, 
    // unless an error occurs (e.g. no permission to access page), or 
    // when you're executing in multiple frames (via allFrames:true). 
    console.log('Result = ' + result[0]); 
}); 

这个例子是可用的,但并不完美。在你的代码中使用它之前,请确保你实现了正确的错误处理。目前,当favColor未定义时,脚本将引发错误。因此脚本文本不会更新,并且返回的值不正确。在实施适当的错误处理之后,这个例子将会非常稳固。

而且该示例几乎不可读,因为该脚本是由字符串构造的。如果逻辑相当大,但内容脚本在单独的文件中并使用chrome.tabs.executeScript(tab.id, {file: ...}, ...);

actualCode变得比几行时间越长,我建议包装在一个函数的代码文本并与'('')();串连它,让您更轻松地编写代码,而无需添加引号和反斜线在actualCode(基本上"Method 2b" of the answer that you've cited in the question )。

+0

有趣的信息,插入'