简体   繁体   English

如何从注入的脚本调用函数?

[英]How to call a function from injected script?

This is code from my contentScript.js : 这是我的contentScript.js中的代码:

function loadScript(script_url)
  {
      var head= document.getElementsByTagName('head')[0];
      var script= document.createElement('script');
      script.type= 'text/javascript';
      script.src= chrome.extension.getURL('mySuperScript.js');
      head.appendChild(script);
      someFunctionFromMySuperScript(request.widgetFrame);// ReferenceError: someFunctionFromMySuperScript is not defined
  }

but i got an error when calling a function from injected script: 但是从注入的脚本调用函数时出现错误:

ReferenceError: someFunctionFromMySuperScript is not defined ReferenceError:未定义someFunctionFromMySuperScript

Is there is a way to call this function without modifying mySuperScript.js ? 有没有办法在不修改mySuperScript.js的情况下调用此函数?

Your code suffers from multiple problems: 您的代码存在多个问题:

  1. As you've noticed, the functions and variables from the injected script (mySuperScript.js) are not directly visible to the content script (contentScript.js). 正如您所注意到的,注入脚本(mySuperScript.js)中的函数和变量对内容脚本(contentScript.js)不是直接可见的。 That is because the two scripts run in different execution environments . 这是因为这两个脚本在不同的执行环境中运行。
  2. Inserting a <script> element with a script referenced through a src attribute does not immediately cause the script to execute. 使用通过src属性引用的脚本插入<script>元素不会立即导致脚本执行。 Therefore, even if the scripts were to run in the same environment, then you can still not access it. 因此,即使脚本在同一环境中运行,您仍然无法访问它。

To solve the issue, first consider whether it is really necessary to run mySuperScript.js in the page. 要解决此问题,首先要考虑是否确实需要在页面中运行mySuperScript.js If you don't to access any JavaScript objects from the page itself, then you don't need to inject a script. 如果您不从页面本身访问任何JavaScript对象,则无需注入脚本。 You should try to minimize the amount of code that runs in the page itself to avoid conflicts. 您应该尝试最小化页面本身中运行的代码量以避免冲突。

If you don't have to run the code in the page, then run mySuperScript.js before contentScript.js , and then any functions and variables are immediately available (as usual, via the manifest or by programmatic injection ). 如果您不必在页面中运行代码,则在contentScript.js之前运行mySuperScript.js ,然后立即可以使用任何函数和变量(通常, 通过清单或通过编程注入 )。 If for some reason the script really needs to be loaded dynamically, then you could declare it in web_accessible_resources and use fetch or XMLHttpRequest to load the script, and then eval to run it in your content script's context. 如果由于某种原因,脚本确实需要动态加载,那么您可以在web_accessible_resources声明它并使用fetchXMLHttpRequest加载脚本,然后使用eval在内容脚本的上下文中运行它。

For example: 例如:

function loadScript(scriptUrl, callback) {
    var scriptUrl = chrome.runtime.getURL(scriptUrl);
    fetch(scriptUrl).then(function(response) {
        return response.text();
    }).then(function(responseText) {
        // Optional: Set sourceURL so that the debugger can correctly
        // map the source code back to the original script URL.
        responseText += '\n//# sourceURL=' + scriptUrl;
        // eval is normally frowned upon, but we are executing static
        // extension scripts, so that is safe.
        window.eval(responseText);
        callback();
    });
}

// Usage:
loadScript('mySuperScript.js', function() {
    someFunctionFromMySuperScript();
});

If you really have to call a function in the page from the script (ie mySuperScript.js must absolutely run in the context of the page), then you could inject another script (via any of the techniques from Building a Chrome Extension - Inject code in a page using a Content script ) and then pass the message back to the content script (eg using custom events ). 如果你真的必须从脚本调用页面中的函数(即mySuperScript.js必须绝对在页面上下文中运行),那么你可以注入另一个脚本(通过构建Chrome扩展中的任何技术- 注入代码在使用内容脚本的页面中 ),然后将消息传递回内容脚本(例如, 使用自定义事件 )。

For example: 例如:

var script = document.createElement('script');
script.src = chrome.runtime.getURL('mySuperScript.js');
// We have to use .onload to wait until the script has loaded and executed.
script.onload = function() {
    this.remove(); // Clean-up previous script tag
    var s = document.createElement('script');
    s.addEventListener('my-event-todo-rename', function(e) {
        // TODO: Do something with e.detail
        // (= result of someFunctionFromMySuperScript() in page)
        console.log('Potentially untrusted result: ', e.detail);
        // ^ Untrusted because anything in the page can spoof the event.
    });
    s.textContent = `(function() {
        var currentScript = document.currentScript;
        var result = someFunctionFromMySuperScript();
        currentScript.dispatchEvent(new CustomEvent('my-event-todo-rename', {
            detail: result,
        }));
    })()`;

    // Inject to run above script in the page.
    (document.head || document.documentElement).appendChild(s);
    // Because we use .textContent, the script is synchronously executed.
    // So now we can safely remove the script (to clean up).
    s.remove();
};
(document.head || document.documentElement).appendChild(script);

(in the above example I'm using template literals , which are supported in Chrome 41+) (在上面的示例中,我使用的是Chrome 41+支持的模板文字

This doesn't work, because your content script and the injected script live in different contexts : what you inject into the page is in the page context instead. 这不起作用,因为您的内容脚本和注入的脚本存在于不同的上下文中 :您注入页面的内容是在页面上下文中。

  1. If you just want to load code dynamically into the content script context, you can't do it from the content script - you need to ask a background page to do executeScript on your behalf. 如果您只想动态地将代码加载到内容脚本上下文中,则无法通过内容脚本执行此操作 - 您需要请求后台页面代表您执行executeScript

     // Content script chrome.runtime.sendMessage({injectScript: "mySuperScript.js"}, function(response) { // You can use someFunctionFromMySuperScript here }); // Background script chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) { if (message.injectScript) { chrome.tabs.executeScript( sender.tab.id, { frameId: sender.frameId}, file: message.injectScript }, function() { sendResponse(true); } ); return true; // Since sendResponse is called asynchronously } }); 
  2. If you need to inject code in the page context, then your method is correct but you can't call it directly. 如果您需要在页面上下文中注入代码,那么您的方法是正确的,但您无法直接调用它。 Use other methods to communicate with it, such as custom DOM events . 使用其他方法与其进行通信,例如自定义DOM事件

As long as the someFunctionFromMySuperScript function is global you can call it, however you need to wait for the code actually be loaded. 只要someFunctionFromMySuperScript函数是全局的,您就可以调用它,但是您需要等待实际加载的代码。

function loadScript(script_url)
  {
      var head= document.getElementsByTagName('head')[0];
      var script= document.createElement('script');
      script.type= 'text/javascript';
      script.src= chrome.extension.getURL('mySuperScript.js');
      script.onload = function () {
          someFunctionFromMySuperScript(request.widgetFrame);         
      }
      head.appendChild(script);
  }

You can also use jQuery's getScript method. 您还可以使用jQuery的getScript方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM