简体   繁体   中英

How to call a function from injected script?

This is code from my 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

Is there is a way to call this function without modifying 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). 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. 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. If you don't to access any JavaScript objects from the page itself, then you don't need to inject a script. 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 ). 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.

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 ).

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+)

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.

     // 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 .

As long as the someFunctionFromMySuperScript function is global you can call it, however you need to wait for the code actually be loaded.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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