Hello,
I've encoutered an issue with Safari App Extensions. My extension prints lots of suspect error logs in the Xcode console and inside Console.app. This happens basically whenever I make any interaction with the App Extension or with Safari. The most common and predictable error log I get is:
No current extension context; trying most recent context
(Subsystem: com.apple.SafariServices)
However, I also sometimes get the following error messages, albeit less frequently, which may be related:
No extension context for best match
No extension context for remote object
Error connecting back to host for remote object: NSCocoaErrorDomain, code: 4099
No known extension contexts for profile 00000000-0000-0000-0000-000000000000
Most recent extension context B7223E12-B563-45E0-97F8-50500BC6B994 does not have connection back to host; trying best match context
I haven't been able to find anything about these error logs in Apple documentation or on the Internet, so I did a bit of empirical investigation.
I reproduced the bug in the following basic scenario: I've created a new Safari App Extension project in Xcode by going to File > New > Project > Safari Extension App. I've selected "Safari App extension" for the type and "Swift" for the language. The project comes by default with a "SafariExtensionHandler.swift" file, which includes the following code:
override func validateToolbarItem(in window: SFSafariWindow, validationHandler: @escaping ((Bool, String) -> Void)) {
validationHandler(true, "")
}
No issues so far.
If I add the following call:
override func validateToolbarItem(in window: SFSafariWindow, validationHandler: @escaping ((Bool, String) -> Void)) {
validationHandler(true, "")
SFSafariApplication.getActiveWindow { window in
// code
}
}
There are still no error messages logged in the Console.
However, if I do this instead:
override func validateToolbarItem(in window: SFSafariWindow, validationHandler: @escaping ((Bool, String) -> Void)) {
validationHandler(true, "")
DispatchQueue.main.async {
SFSafariApplication.getActiveWindow { window in
// code
}
}
}
Then my Xcode console starts being spammed with "No current extension context; trying most recent context" error logs.
With some more testing, it seems that the most common/predictable situation that causes the error log seems to be when calling any Safari API (e.g. SFSafariApplication.getActiveWindow{} or even SFSafariApplication.setToolbarItemsNeedUpdate()) outside of a direct method call provided by the Safari App Extension API. So making API calls directly from inside validateToolbarItem(in:, validationHandler:) or messageReceived(withName:from:userInfo:) calls is fine, but anything else causes "No extension context" logs. The bug even reproduces if you make a Safari API call directly inside of an @IBAction method call caused by a button click inside the Safari popover of the Safari App Extension.
With this being the case, it seems to be impossible to make clean Safari API calls in an asynchronous or proactive way, which is problematic for our app extension use case and which seems to defeat the purpose of some of the API calls like SFSafariApplication.setToolbarItemsNeedUpdate(). Also, this seems to be a new issue.
I've tested these scenarios on various macOS versions that I had on hand (specifically, on macOS 10.15 Catalina, macOS 13 Ventura and macOS 14 Sonoma) and the bug seems to reproduce only on macOS 14 Sonoma. The Safari App Extension behaves as expected on previous macOS versions, with no suspect error logs.
Does anyone know what this issue is about?
Post
Replies
Boosts
Views
Activity
Hello,
I've been working with system extensions on macOS Catalina / Big Sur (Endpoint Security extensions to be precise) and it seems that there is no 'right' way to check whether a system extension has already been approved by the user or not. You can of course use an activation OSSystemExtensionRequest and determine through the OSSystemExtensionRequestDelegate whether the user needs to approve it (a 'requestNeedsUserApproval:' message is passed) or if the extension has been loaded into the system (a 'request:didFinishWithResult:' message is passed). That's great but the major drawback is that making such a request when the extension is not loaded also starts the process of loading the system extension: The user is shown a system popup window and the 'Allow' request shows up in System Preferences > Security & Privacy.
I'm looking for a non-intrusive way of checking the load status of the system extension.
I think being able to do this is very useful. Say, for instance, you have an app with optional features, one of which requires the activation of a system extension. If we could check the load status of that system extension, we could display to the user a proper UI that can either suggest that it could be activated or show to the user that it is already approved and working.
There are some ways to achieve this that I've thought about, but they don't seem the proper way of doing things:
1) Trying to parse the output of 'systemextensionsctl list'. It seems problematic since an extension can appear multiple times in here (based on succesive activations / deactivations) and also since the output isn't particularly documented.
2) Opening an NSXPCListener from inside the system extension and determining the activation status of the extension by whether or not a process can connect to this.
3) Attempting to look for a TeamId.com.mybundle.identifier process in the output of a 'launchctl' command, such as 'sudo launchctl list TeamId.com.mybundle.identifier 2>&1 | grep PID'. I've been using this method and it seems consistent for now.
Is there a recommended way of achieving this?
I've encountered what appears to be an unideal situation regarding EndpointSecurity System Extensions when using the NSEndpointSecurityEarlyBoot flag. The documentation (man EndpointSecurity) mentions about this flag that:
NSEndpointSecurityEarlyBoot
Type: Boolean
If set to TRUE, the ES subsystem will hold up all third party executions (anything that is not a platform binary) until all early boot ES extensions make their first subscription. This does work exactly as described. However, it seems that there are situations in which an ES system extension simply can't get to the point of making an es_subscribe() call, by far the most common one being when the user has yet to give Full Disk Access / Transparency, Consent, and Control rights to the extension. In a situation like that, the extension can't get past the es_new_client() call, as it will fail repeatedly with the ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED return code and without a valid es_client_t * value, es_subscribe() can't be called. So the system is stuck at boot time waiting what seems to be close to a minute for an extension that can't make an es_subcribe().
I was hoping to avoid this hangup when possible, as it doesn't appear to provide the best user experience, by having the system extension tell the OS to proceed whenever it detects a situation that probably won't fix itself immediately (such as the extension having no TCC rights given), but this doesn't always seem possible.
Am I missing something?