0

In a legacy extension it was possible to iterate over safari.application.activeBrowserWindow.tabs to send a message to all tabs registered with the extension.

Is there any equivalent available with the new safari app extensions?

I've been trough the docs but did not find any hints on how to achieve this very basic thing.

A horrible workaround would be to have all tabs ping the Swift background, but really this is such a basic thing it seems absurd that it is not available or covered by the docs, am I missing something?

I also tried keeping a weak map of all "page" instances as seen by "messageReceived" handler in the hope the SFSafariPage reference would be kept until a tab is closed but they are instead lost almost immediately, suggesting they are more message channels than actual Safari pages.

paolo.caminiti
  • 526
  • 4
  • 15
  • Something like [this](https://stackoverflow.com/questions/28230845/communication-between-tabs-or-windows#28230846) answer might be what you're after. – elken Nov 15 '18 at 15:10
  • Unluckily is not, that's communication between user land contexts belonging to a same domain. I need to broadcast from the swift extension to all injected tabs in the browser. – paolo.caminiti Nov 16 '18 at 11:29
  • Trying using `localStorage`. Something like [this](https://codepen.io/alienslug/pen/EVdQwj) pen. – elken Nov 16 '18 at 12:08
  • This is not a user land webpage related issue, I edited the title to make clear this is about extension level messages. – paolo.caminiti Nov 16 '18 at 13:20
  • Safari extensions still support localStorage access and looks like your only option. – elken Nov 16 '18 at 13:30
  • I tested out of curiosity, it doesn't seem like injected contexes share localStorage events. Did you actually used this tecnique in a safari app extension? – paolo.caminiti Nov 21 '18 at 11:05

1 Answers1

2

The way should be next: in injected.js you send the message to your app-ext, e.g.

document.addEventListener("DOMContentLoaded", function (event) {
    safari.extension.dispatchMessage('REGISTER_PAGE')
})

And in app-ext handle it with smth like this:

var pages: [SFSafariPage] = []

class SafariExtensionHandler: SFSafariExtensionHandler {

    override func messageReceived(withName messageName: String, from page: SFSafariPage, userInfo: [String : Any]?) {
        switch messageName {
        case "REGISTER_PAGE":
            if !pages.contains(page) {
                pages.append(page)
            }
        default:
            return
        }
    }

}

Well, then you can send the message to all opened pages during runtime by smth like this:

for p in pages {
    p.dispatchMessageToScript(withName: "message name", userInfo: userInfo)
}

It looks hacky but yet workable. Enjoy :)

seclace
  • 98
  • 7
  • I had something similar collecting a map of "page" whenever a page is first seen by messageReceived. But if we don't have a way to know when to unregister a page I'm afraid this may lead to very bad memory leaking or ghost references... I tried with a weakMap but the references to messaging pages are lost almost immediately. Also when using a weakMap it appears that over time messageReceived will receive from the very same page different "page" pointers... which would mean we are really wasting memory keeping those past references. – paolo.caminiti Nov 21 '18 at 11:11
  • @paolo.caminiti yes, you absolutely right with memory leaks. To make the solution more reliable you can send the message from injected.js when `onbeforeunload` event fires. So you can delete target page from pages map and send broadcast only to active pages. – seclace Nov 22 '18 at 14:11