Search code examples
swiftmacoswebkitaccessibility

Select Text in Webkit applications via macOS accessibility API


I need to select text in a WebKit view of another application (Apple Mail) using accessibility APIs.

For regular text fields, I do something like this:

func selectText(withRange range: CFRange) throws {
    var range = range
    guard let newValue: AXValue = AXValueCreate(AXValueType.cfRange, &range) else { return }
    AXUIElementSetAttributeValue(self, kAXSelectedTextRangeAttribute as CFString, newValue)
}

However, in the composing window of Apple Mail every text seems to be of type Static Text which doesn't come with the necessary AXSelectedTextRange

It has AXSelectedTextMarkerRange, though, which requires an AXTextMarker. I just don't get how to create one of these. I have no trouble reading the text from a user created selection using this here, but I'm unable to select text via the accessibility APIs.


Solution

  • Thanks to the hint from Willeke I was able to figure it out. It is indeed possible to do it using AXTextMarkerForIndex. Knowing that it's actually pretty straightforward.

    Here's my code:

    func getTextMarker(forIndex index: CFIndex) throws -> AXTextMarker? {
            var textMarker: AnyObject?
            guard AXUIElementCopyParameterizedAttributeValue(self,"AXTextMarkerForIndex" as CFString, index as AnyObject, &textMarker) == .success else { return nil }
            return textMarker as! AXTextMarker
    }
    
    func selectStaticText(withRange range: CFRange) throws {
            guard let textMarkerStart = try? getTextMarker(forIndex: range.location) else { return }
            guard let textMarkerEnd = try? getTextMarker(forIndex: range.location + range.length) else { return }
            let textMarkerRange = AXTextMarkerRangeCreate(kCFAllocatorDefault, textMarkerStart, textMarkerEnd)
    
            AXUIElementSetAttributeValue(self, "AXSelectedTextMarkerRange" as CFString, textMarkerRange)
    }