Executing host script from an injected page

2019-03-31 05:22发布

问题:

I'm writing a Safari Extension that adds some functionality to a web page that I don't own. I would like for my injected JavaScript page to be able to fire some JavaScript functions the host page uses, but it doesn't work. On the Console I get the message ReferenceError: Can't find variable: function_name.

My script is specified to be an End Script, so the whole page should be loaded. The function is also called from an onclick() handler on the page, like so:

onclick="function_name($(this).up());"

I can get a reference to that page element, but when I call element.onclick() I get another error: TypeError: 'undefined' is not a function (evaluating '$(this).up()').

Oddly, when I call the JavaScript function from an AppleScript (do JavaScript "function_name()" in page) it works fine. How can I trigger these functions?

回答1:

I can extend answer from canisbos. You can communicate with the inserted script with the PostMessage function.

injected script:

//insert script to page
var myScriptElement = document.createElement('script'); 
myScriptElement.innerHTML =
  'window.addEventListener("message", function(e) {' +
  '  if (e.data.msg == "do") {' +
  '    foo(e.data.info);' +
  '    postMessage({msg: "done", info: "answer"}, "*");' +
  '  };' +
  '}, false);'
document.querySelector('head').appendChild(myScriptElement);

//add answers listener
window.addEventListener('message', function(e) {
  if (e.data.msg == 'done') {
    console.log(e.data.info);
  };
}, false);

//add the testing function on the body click
document.addEventListener('click', function (e) {
  //call inserted script
  postMessage({msg: 'do', info: 'from inject'}, '*');
}, false);

Test html page:

<html>
<script>
  function foo(text) {
    console.log(text);
  };
</script>
<body>
  <button id='button' onclick='foo("from page")'>test</button>
</body>
</html>


回答2:

It's not working because the extension's injected script is sandboxed; it can't see the page's global objects except for the DOM (and vice versa). A way around this security limitation is to have the injected script create a <script> element with the statements you want and insert it into the document. For example:

var myScriptElement = document.createElement('script');
myScriptElement.innerHTML = 'alert("Page is using jQuery " + $.fn.jquery)';
document.querySelector('head').appendChild(myScriptElement);

However, the inserted script will not have access to the injected script's objects either. So, for example, if you try to access the extension's safari object from the inserted script, you will get a reference error.