1

As a follow-up to How do I get the selected text from a WKWebView from objective-C, I have a related question but using Swift.

I need to obtain the URL on mouse over, in prelude to contextual menu appearance, so I can inject it into the menu item's respresentedObject - or otherwise save. The built-in action(s) for some items are not being properly handled.

I find that menu item action for 'Open Link' works fine but not for 'Open Link in New Window'; neither of these work via the built-in contextual menu item actions. I wanted to support a middle button click to be the latter menu item.

So, using the original post as a base I have this - I also sub-class WkWebView, adding

class MyWebView : WKWebView {
    var selectedText : String?
    var selectedURL : URL?
}

then in view controller

func viewDidLoad() {
    //  Watch javascript selection messages
    let controller = webView.configuration.userContentController
    controller.add(self, name: "newSelectionDetected")
    let js = """
function getSelectionAndSendMessage()
{
    var txt = document.getSelection().toString() ;
    window.webkit.messageHandlers.newSelectionDetected.postMessage(txt) ;
}
document.onmouseup   = getSelectionAndSendMessage ;
document.onkeyup     = getSelectionAndSendMessage ;
"""

    let script = WKUserScript.init(source: js, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
    controller.addUserScript(script)
}

// MARK: Javascript

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    Swift.print("userContentController")

    // A new selected text has been received
    if let urlString : String = message.body as? String
    {
        webView.selectedText = urlString
        Swift.print("ucc: str -> \(urlString)")
    }
    if let url = message.frameInfo.request.url {
        webView.selectedURL = url
        Swift.print("ucc: url -> \(url.absoluteString)")
    }
}

// MARK: callbacks
override func willOpenMenu(_ menu: NSMenu, with event: NSEvent) {

    //  Pick off javascript items we want to ignore or handle
    for title in ["Open Link", "Open Link in New Window", "Download Linked File"] {
        if let item = menu.item(withTitle: title) {
            if title == "Download Linked File" {
                menu.removeItem(item)
            }
            else
            if title == "Open Link"
            {
                item.representedObject = self.window
                item.action = #selector(MyWebView.openLinkInWindow(_:))
                item.target = self
            }
            else
            {
                item.action = #selector(MyWebView.openLinkNewWindow(_:))
                item.target = self
            }
        }
    }
}

In my action handler, I'm only getting the frame's URL, not what was highlighted on mouse over.

What I need, is to get the URL etc as shown in a web browser's status bar on mouse overs to be cached for a contextual menu item's action.

slashlos
  • 913
  • 9
  • 17

1 Answers1

1

Well, I guess that, in your javascript, you need to add a onmouseover event handler to all your links, and this event handler should send the link back to the swift world.

Building upon your code, I would add in the Javascript something like:

function sendLink()
{
    window.webkit.messageHandlers.newUrlDetected.postMessage(this.href) ;
}

var allLinks = document.links;
for(var i=0; i< allLinks.length; i++)
{
    allLinks[i].onmouseover = sendLink ;
}

And of course, in the Swift world, you need to catch the "newUrlDetected"

controller.add(self, name: "newUrlDetected")

And in the Swift message handler, you need to switch upon the WKScriptMessage name property.

  • If it is "newSelectionDetected", then set webView.selectedText with the message body
  • If it is "newUrlDetected", then set webView.selectedURL with the message body
AirXygène
  • 2,409
  • 15
  • 34