safari.application in Safari App Extension

Hi,


When I developed with Safari Extension, I could listen beforeNavigate event in global page, such as:


<!DOCTYPE html>

<head>

</head>

<body>

<script type="text/javascript" charset="utf-8">

safari.application.addEventListener("beforeNavigate", performCommand, true);

function performCommand(event) {

alert("The URL" + event.url);

}

</script>

</body>

</html>


I could receive this event, and handle it.


According to : https://developer.apple.com/documentation/safariservices/safari_app_extensions/converting_a_legacy_safari_extension_to_a_safari_app_extension?language=objc

Working in the native app extension. A Safari App Extension doesn’t have a global HTML page. Instead, pass messages from your injected code into the app extension.


But when I develop with Safari App Extension, in the SFSafariContentScript, the similar code is not worked.

It seems the safari.application object is NULL.


if(safari.application){

safari.application.addEventListener("beforeNavigate", performCommand, true);

function performCommand(event) {

alert("The URL" + event.url);

}

}

else{

alert("no application"); ----this is alerted

}


What should I do to listen beforeNavigate event with Safari App Extension?


Thak you very much!

Replies

I'm having exactly the same problem, has anyone found a solution to this?


I found a stackoverflow article (https://stackoverflow.com/questions/44916917/safari-app-extension-how-to-capture-beforenavigate-event) that suggests a solution might involve overriding the beginRequest method of the SFSafariExtensionHandler object but that article has no answers either.

Can you please tell me what you'd like to do with the beforeNavigate event? Specific use cases and extensions you would like to write that need this API would be the most helpful.

I don't know about weichao199 - but I do know for our specific use case. We're making a shopping app that allows users to raise money for charity using the cashback / affilate commission that a lot of retailers and affiliate networks subscribe to. Part of our apps requirements is that we adhere to the affliate network rules about not showing donation reminder alerts if users have already been redirected via other networks.


We need to use the onBeforeNavigate event to see when users have arrived via other networks by watching the redirect chain and not show our notification alerts.


Please free to contact us at dev@easyfundraising.org.uk if you require any further assistence.

We have the same situation in which we must be aware of the redirect chain in order to determine if our extension should "stand down" and not inject any content within the page for complaince with our partners terms.


"7. Software Publishers must recognize and respect publisher-driven traffic immediately prior to the software dropping a cookie. Specifically, all software must recognize CJ domains and afsrc=1 codes in affiliate links. When a CJ domain or the afsrc=1 code is detected, the software may not operate or redirect the consumer to the advertiser site using the Software Publisher's PID."


It appears that there are no longer any events (specifically navigation events) coming from the browser beyond just messages that can be dispatched from the injected script which occur too late in the flow as injections do not occur during server side redirects. We have investigated the use of validateToolbarItem(in:validationHandler) with SFSafariExtensionHanlding however this does not fire during redirects even if it did it is only passes the window and not the tab that the navigation occured in, being a user could click a link and immediately focus another tab it is important to know which tab redirected through an affiliate chain so that the final destination can stand down per our agreement with partners.


We also investigated the use of NEFilterDataProvider to watch the network with the use of urlAppendStringVerdict(withMapKey:) although hackish and overkill in my opinion could have possibly worked, however this NetworkExtension type is only available on iOS and not macOS making it unusable for this use case.


Was the removal of the SafariNavigateEvent and SafariBeforeNavigateEvent from Safari Extensions JS when transitioning to Safari App Extensions intentional or just an oversight and are there plans to reimplement similar capabilities or any known workarounds?

Hi Steve,

I've actually raised a TSI with Apple over this issue and here's what we got back.


"Thank you for contacting Apple Developer Technical Support (DTS). We have reviewed your request and have concluded that there is no supported way to achieve the desired functionality given the currently shipping system configurations.
If you would like for Apple to consider adding support for such features in the future, please submit an enhancement request via the Apple Bug Reporter tool at <https://developer.apple.com/bug-reporting/>.


In the meantime, you can try injecting a script into the webpage that checks the window.location.href and redirects after the fact if there is no affiliate code on the URL to start with.


Hopefully this helps."


The problem with his potential solution (as I understand it) is that the redirect has already happened by the time the code is injected and therefore it get's missed.


I notice from your post that your refering specifically about CJ affilates, we've even contacted CJ to see if there's another way round this and we're waiting to hear back from them.


Another thing that makes the situation even worse is that we rely on cookies to keep track of which retailers we've activated and Safari 12 has also blocked access to cookies as well so we can't currently sync our extension to the website.


I think it's very telling that none of the big players in the browser extension / affiliate marketing space have yet to launch native MacOS app's - they're obviously all having the same problems.

Thanks for the update, I agree that the proposed solution will not work being you cannot intercept the redirect. You can be injected on the page where the click occured or the final landing page unfortunately you cannot gaurantee how the click or redirect occurs to intercept even if you could you would have to be aware of every publisher site, and the affiliate domain and afsrc parameters would happen as an intermediate step that we would not be injected during. Leaving only the injection on the landing page, unfortunately by then its too late the current url does not provide the necessary information, and the browsers history API will not give you URLs in the history.


Please do keep me posted if you learn anything further as I also suspect this will be an issue across the entire industry who will all be looking for potential solutions.

For everyone that has posted on this thread - would being able to get the redirect chain of an SFSafariPage (via the SFSafariPageProperties) suit your needs? Would that give you the information you need to be able to make the determination whether or not you should add your content?

I believe this should work just fine, the idea is that when you land on a particiular page we need to know the URLs you've been redirected through so that we can inspect if you traveled through any affiliate links.


The trick here is that some affiliate networks may do their redirects through javascript or a meta refresh, Its unclear if these would appear in the redirect chain your describing.

Thanks Steve!

A few more follow-up questions, as this is behavior we'd like to continue to support:

1) Are you only looking at query string of the URL for this functionality? Or could this information be anywhere in the URL?

2) Are the list of the relevant URL parameters something that you could provide to Safari ahead of time? Or is this something you would need to compute per URL?

  1. I'm not sure about other use cases outside affiliate marketing but we must check 2 things
    1. That you did not redirect through a URL containing an afsrc or u1 parameter.
    2. That you did not redirect through a URL containing a known affiliate hostname.
  2. Same I'm not sure if anyone else has another use case but we definitely know the hostname and parameters in head of time, although we may update the hostnames periodically so would need to do so without requiring an update to the app. Other then that I think it would be acceptable to provide a list and ask Safari if the current page met them, I'm assuming you want to avoid providing the URLs of the redirect chain?

Hello bweinstein,
I hope that you are still following this thread.

For us, we have a similar problem. In our current extension we are observing the 'beforeSearch' event on the safari.application object before inserting our content.
It's important for us that we can do that before any loading is made.
Is that possible somehow? Will that functionality be re-implemented in the future?

Cheers,
Paul

Sorry for the late reply. In response to bweinstein's first question about accessing the redirect chain via the SFPageProperties. Yes that would be an acceptable solution.


With regards our use cases - they are identical to stevestmartin's in so much as we need to be able to detect when a url contains the afsrc=1 parameter ie. http://www.someffiliateurl.com/path?afsrc=1 (this is something we can already do).


We also need to be be able to detect when we detect the presence of one (or more) urls that are known ahead of time. I also agree with stevestmartin in that it would be acceptable to make requests to check for the presence of a named url rather than having access to the complete chain should you wish to restrict access.


Thanks for considering this request.

Hi folks!


I've been doing some more thinking about this API and I'd like to reach out to see if an idea I had would work for you for developers that need this functionality.


Each time an extension performs a navigation (even if it is part of a redirect), Safari would call a method on your extension like:


- (void)pageDidNavigate:(SFSafariPage *)page toURL:(NSURL *)url


Your extension would only get this message if it has access to the URL that the page navigated to.


Is this something that would help with the use case you need for beforeNavigate?


Thanks!

Can you define what has access to the URL means? I'm assuming your referring to SFSafariWebsiteAccess, which in our case we could just choose All if we don't have a finite list of domains we are looking for?


Also would SFSafariPage be the page that triggered the redirect and the NSURL be the URL that you were redirected to and what would this look like for a redirect change where a click occured on one page and you were redirected multiple times before that. Either way I think it could work I'm just curious what the API is.


I'll try to clarify the usecase. Ultimately when you hit a particular site (usually a retailer) we need to know if you arrived there by means of an affiliate tracking link. To do this we must examine a list of known hostnames and/or url parameters, these are known in advance and don't change often so needing to specify them in a plist would not be out of reason, although it would be preferred if it could be dynamic. The caveat here is that this check cannot just be done on the referrer as you could have been redirected through a chain of redirects and one of them contained it (for example if you went to a coupon site and clicked on a link they may have redirected through their own tracking url, which then redirected to the affiliate tracking url which is what we need to see, then you could have redirected through a series of tracking urls for the retailer site before hitting your final destination).


Now whether we get access to a callback everytime the browser navigates and I keep track of the chain myself (which is what we currently do), or if Safari keeps track of the chain and just allows me to define a pattern and ask Safari if the current page matches that pattern (hostname and/or url parameters) isn't a huge concern on my part and I suspect the same for others on this thread. So I feel we are likely open to whatever API you feel solves our usecase and potentially others, just as long as when you arrive on a page we have some ability to know if you redirected through a specific pattern of urls.


I've tried to throw together some example code from the legacy API to help reinforce what we are trying, although there are no promises it is fully functional as its mostly for reference https://gist.github.com/stevestmartin/7dcd7766c8bbc55e40ae151824f6964f.


I hope this helps, we are pretty anxious to get a working solution.

I'd like just echo what stevestmartin said. I know his use case and it's pretty much the same as ours and his gist echo's the same software pattern we used to keep track of competing affiliate links.


At this point any solution that allow's us visibility of the redirect chain that resulted in a user arriving at a given url would be acceptable.


Thanks.