5

I'm trying to build a simple safari app extension. On about:blank I load a html page from resources folder of extension. Following is the code for that.

//script.js - injected script
if(window.location.href=="about:blank"){
    window.location.href = safari.extension.baseURI + "page.html";
}

I want communication between this loaded page and safari app. Apparently injected script script.js is not available on loaded html page.

I tried linking the script.js to html page inline but the safari object itself is not available for safari.extension.dispatchMessage or safari.self.addEventListener.

EDIT:
with this ( injected script script.js is not available on loaded html page/tab the page is loaded in) I mean that on opening web inspector in resources tab we can't see any extension scripts

moghya
  • 854
  • 9
  • 18

4 Answers4

2

If I understand your question correctly, you need to communicate between the page's javascript and the Safari app extension's native host app. The host app can only be contacted using API safari.extension.dispatchMessage which is accessible only from the injected script.js script but not from the page's JS.

You can solve this by sending and a request event from the page and registering the response event. Your HTML page would contain script:

function sendEventToInjected() {
    var storeEvent = new CustomEvent('myCustomEvent', {"detail": "testData"});
    document.dispatchEvent(storeEvent);

    var responseEventID = 'myResponseEvent';
    document.addEventListener(responseEventID, function respListener(event)
    {
        console.log("Got data from injected script: " + event.detail);
        document.removeEventListener(responseEventID, respListener);
    });
}

Your script.js will catch this event by registering the event listener and can then pass the data over to the extension's native host app. After native host's response is obtained, response event can be dispatched back to the page.

document.addEventListener("myCustomEvent", function(event) {
    console.log("myCustomEvent:" + event.detail);
    safari.extension.dispatchMessage(event.detail);
});

// Listens for messages sent from the app extension's Swift code.
safari.self.addEventListener("message", messageHandler);

function messageHandler(event)
{
    var resp = {detail: "respData"};
    var respEvent = new CustomEvent('myResponseEvent', resp);
    document.dispatchEvent(respEvent);
}
Marek B.
  • 113
  • 7
  • thank you very much for your answer but you missed the fact that ( injected script `script.js` is not available on loaded html page/tab the page is loaded in) and hence it can't really listen to the events dispatched by js in page or vice versa – moghya Sep 27 '18 at 08:09
  • You are right the injected script is not available to the page as such. However listening to the events from the page to injected JS and back works (Safari 11, not tested 12 yet). I did a proof-of-concent because I am just working on a similar problem. – Marek B. Sep 27 '18 at 08:57
  • well it with this ( injected script script.js is not available on loaded html page/tab the page is loaded in) I mean that on opening web inspector in resources tab we can't see any `extension scripts` , I tried what you said, but It's not working. There's no `script.js` present when I'm on a html page from by extension's resources i.e with `safari-extensions://` protocol in url – moghya Sep 27 '18 at 10:11
  • OK, maybe I missed your use case. I have a normal HTML page (opened by by URL e.g. http://localhost/test.html) that would like to communicate with the extension (in my case I need to invoke the native code as a replacement of NPAPI). For this case the communication works as I had written. Can you specify your use case - how you open the page from the extension, provide some more example code to simulate the problem? – Marek B. Sep 27 '18 at 14:09
0

You may not "relocate" the page to a safari-extensions:// url but get the HTML code to display from the Safari App Extension (using messaging) and then insert the code in the about:blank page. Result will be the same, except the page url will remain about:blank

Emmanuel Sellier
  • 526
  • 1
  • 5
  • 13
0

This looks like a bug with Apple. I have filed one with BugReporter.

dimitrirostavo
  • 318
  • 1
  • 13
0

You can add zero height iframe to your HTML to "force inject" extension script.

<!DOCTYPE html>
<html lang=en>
   <head>
      <title>My Extension!</title>
   </head>
   <body>
      <noscript><strong>We're sorry but App doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript>
      <p>Hi!</p>
      <iframe id="myID" src="https://example.com" height="0" style="border: none;"></iframe>
   </body>
</html>

Here is possible JS:

function handleMessage(event) {
    console.log(event.name);
    console.log(event.message);
}

if (window.top === window) {
   document.addEventListener("DOMContentLoaded", function(event) {
       console.log("[app] Content loaded: main");
       safari.extension.dispatchMessage("Hello from Main!");
       safari.self.addEventListener("message", handleMessage);
   });
} else {
   document.addEventListener("DOMContentLoaded", function(event) {
       console.log("[app] Content loaded: iframe");
       safari.extension.dispatchMessage("Hello from iframe!");
       safari.self.addEventListener("message", handleMessage);
   });
}

Result:

Image 1 Image 2 Image 3

Vlad
  • 6,402
  • 1
  • 60
  • 74