1

Related to

I'm trying to have my sub-classed WkWebView act on middle button URL click to present the link in a new window/webView. WkWebView's contextual menu features an item to do this - whose target/action I alter to my view, but I'd like the simplicity as in web browsers to do the same; middle button yields new window.

I'd have this so far:

viewDidLoad() {
    //  Watch javascript selection messages
    let controller = webView.configuration.userContentController
    controller.add(self, name: "newWindowWithUrlDetected")

    let js = """
//  https://stackoverflow.com/questions/50846404/how-do-i-get-the-selected-text-from-a-wkwebview-from-objective-c
function getSelectionAndSendMessage()
{
    var txt = document.getSelection().toString() ;
    window.webkit.messageHandlers.newSelectionDetected.postMessage(txt) ;
}
document.onmouseup   = getSelectionAndSendMessage ;
document.onkeyup     = getSelectionAndSendMessage ;

//  https://stackoverflow.com/questions/21224327/how-to-detect-middle-mouse-button-click/21224428
document.body.onclick = function (e) {
  if (e && (e.which == 2 || e.button == 4 )) {
    middleLink;
  }
}
function middleLink()
{
   window.webkit.messageHandlers.newWindowWithUrlDetected.postMessage(this.href) ;
}

//  https://stackoverflow.com/questions/51894733/how-to-get-mouse-over-urls-into-wkwebview-with-swift/51899392#51899392
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 ;
}
"""
    let script = WKUserScript.init(source: js, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
    controller.addUserScript(script)
}

The idea was that on a mouse over, the link would be reported but when the action takes place I'd like to know which button was sent to the view.

The trouble is that I'm not seeing the action, not in my decision handler; even an override on load(URLRequest:) itself

override func load(_ request: URLRequest) -> WKNavigation? {
    Swift.print("we got \(request)")
    return super.load(request)
}

Since I'm not getting the action I suppose the issue is the javascrpt is not "sending" such an event and being handled entirely within the webpage?

How can this event's button be make known to its view?

Nav delegate apparently not working?

func webView(_ webView: WKWebView,
             decidePolicyFor navigationAction: WKNavigationAction,
             decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {

    guard let event = NSApp.currentEvent, event.buttonNumber <= 1 else {
        if let url = navigationAction.request.url {
            Swift.print("newWindow with url:\(String(describing: url))")
            DispatchQueue.main.async {
                self.appDelegate.openURLInNewWindow(url)
            }
        }
        decisionHandler(WKNavigationActionPolicy.cancel)
        return
    }
    decisionHandler(WKNavigationActionPolicy.allow)
    return
}
slashlos
  • 913
  • 9
  • 17
  • 1
    Did you try to make the web view do what you want by setting the delegates and implementing methods of `WKNavigationDelegate` and `WKUIDelegate`? – Willeke Aug 20 '18 at 10:46
  • I do have a nav delegate but they're all "did" not "will" events. it appears the webView is already has the new url at didStartProvisionalNavigation (1st), commit (2nd) events, and I'm not seeing how/if WKNavigation object can communicate back - ie., stop as I'll load a new one? While I can see the mouse button number in the current event, and can launch a new webView is not the current one already in progress? I cant see a way to affect loading early - looking for like "willBeginNavigation" I thought would happen at decide policy ? – slashlos Aug 20 '18 at 12:19
  • Implement `func webView(WKWebView, decidePolicyFor: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void)` and call `decisionHandler(.cancel)` to cancel or `decisionHandler(.allow)` to allow. – Willeke Aug 20 '18 at 13:50
  • I do have one, been iteratively revising it; posted latest. If I step thru the debugger after break in dispatch it works but not otherwise? – slashlos Aug 20 '18 at 22:38
  • Try `navigationAction.buttonNumber` instead of `NSApp.currentEvent.buttonNumber`. – Willeke Aug 20 '18 at 23:00
  • Bingo! THANK YOU – slashlos Aug 20 '18 at 23:34

1 Answers1

0

With @Willeke's help this fixed it:

func webView(_ webView: WKWebView,
             decidePolicyFor navigationAction: WKNavigationAction,
             decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    guard navigationAction.buttonNumber <= 1 else {
        if let url = navigationAction.request.url {
            Swift.print("newWindow with url:\(String(describing: url))")
            DispatchQueue.main.async {
                self.appDelegate.openURLInNewWindow(url: url)
            }
        }
        decisionHandler(WKNavigationActionPolicy.cancel)
        return
    }
    decisionHandler(WKNavigationActionPolicy.allow)
}
slashlos
  • 913
  • 9
  • 17