Cocoa / WebKit having "window.open ()" Opening JavaScript links in a Safari instance

I am creating a truly basic Cocoa application using WebKit to display a Flash / Silverlight application in it. Very simple, with no intention of being the browser itself.

So far, I have been able to open the basic html links ( <a href="..." /> ) in a new Safari instance using

 [[NSWorkspace sharedWorkspace] openURL:[request URL]]; 

Now my difficulty is opening the link in a new Safari instance when window.open() used in JavaScript. I β€œthink” (and thus I cracked the code and am not sure whether I really did it or not). I got this working by installing WebView policyDelegate and implementing it

 -webView:decidePolicyForNavigationAction:request:frame:decisionListener: 

delegate method. However, this led to some erratic behavior.

So, a simple question, what do I need to do so that when window.open() called, the link opens in a new Safari instance.

thanks

The big point, I usually am a .NET developer and have been working with Cocoa / WebKit for several days.

+7
objective-c cocoa webkit macos
source share
7 answers

I made yesterday's progress and reinforced part of my problem.

I already use webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: and I got it to work with anchor tags, however this method is never called when JavaScript is called.

However, when window.open() is called called webView:createWebViewWithRequest:request , I tried to force the window to open in Safari here, however the request is always zero. So I can never read the url.

I did a little work, and this seems to be a well-known β€œmistake”, but I could not find a way around it.

From what I understand, createWebViewWithRequest gives you the ability to create a new createWebViewWithRequest , the requested URL is then sent to the new webView for download. This is the best explanation that I have been able to find so far.

Thus, although many people have pointed out this problem, I still do not see a solution that would suit my needs. I will try to delve deeper into decidePolicyForNewWindowAction .

Thanks!

+9
source share

Well, I process it by creating a dummy webView, setting its frameLoad delegate to a custom class that processes

 - (void)webView:decidePolicyForNavigationAction:actionInformation :request:frame:decisionListener: 

and opens a new window there.

the code:

 - (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request { //this is a hack because request URL is null here due to a bug in webkit return [newWindowHandler webView]; } 

and NewWindowHandler:

 @implementation NewWindowHandler -(NewWindowHandler*)initWithWebView:(WebView*)newWebView { webView = newWebView; [webView setUIDelegate:self]; [webView setPolicyDelegate:self]; [webView setResourceLoadDelegate:self]; return self; } - (void)webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener { [[NSWorkspace sharedWorkspace] openURL:[actionInformation objectForKey:WebActionOriginalURLKey]]; } -(WebView*)webView { return webView; } 
+6
source share

It seems that the error with webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: is that the request is always nil , but there is a reliable solution that works with both regular target="_blank" links and javascript.

I mainly use another ephemeral WebView to handle a new page load. Like Yoni Shalom, but with a bit more syntactic sugar.

To use it, first set the delegate object for your WebView, in which case I set myself as a delegate:

 webView.UIDelegate = self; 

Then just implement the webView:createWebViewWithRequest: delegate method and use my block based API to do something when a new page loads, in this case I open the page in an external browser:

 -(WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request { return [GBWebViewExternalLinkHandler riggedWebViewWithLoadHandler:^(NSURL *url) { [[NSWorkspace sharedWorkspace] openURL:url]; }]; } 

This is pretty much the case. Here is the code for my class. Title:

 // GBWebViewExternalLinkHandler.h // TabApp2 // // Created by Luka Mirosevic on 13/03/2013. // Copyright (c) 2013 Goonbee. All rights reserved. // #import <Foundation/Foundation.h> @class WebView; typedef void(^NewWindowCallback)(NSURL *url); @interface GBWebViewExternalLinkHandler : NSObject +(WebView *)riggedWebViewWithLoadHandler:(NewWindowCallback)handler; @end 

implementation:

 // GBWebViewExternalLinkHandler.m // TabApp2 // // Created by Luka Mirosevic on 13/03/2013. // Copyright (c) 2013 Goonbee. All rights reserved. // #import "GBWebViewExternalLinkHandler.h" #import <WebKit/WebKit.h> @interface GBWebViewExternalLinkHandler () @property (strong, nonatomic) WebView *attachedWebView; @property (strong, nonatomic) GBWebViewExternalLinkHandler *retainedSelf; @property (copy, nonatomic) NewWindowCallback handler; @end @implementation GBWebViewExternalLinkHandler -(id)init { if (self = [super init]) { //create a new webview with self as the policyDelegate, and keep a ref to it self.attachedWebView = [WebView new]; self.attachedWebView.policyDelegate = self; } return self; } -(void)webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener { //execute handler if (self.handler) { self.handler(actionInformation[WebActionOriginalURLKey]); } //our job is done so safe to unretain yourself self.retainedSelf = nil; } +(WebView *)riggedWebViewWithLoadHandler:(NewWindowCallback)handler { //create a new handler GBWebViewExternalLinkHandler *newWindowHandler = [GBWebViewExternalLinkHandler new]; //store the block newWindowHandler.handler = handler; //retain yourself so that we persist until the webView:decidePolicyForNavigationAction:request:frame:decisionListener: method has been called newWindowHandler.retainedSelf = newWindowHandler; //return the attached webview return newWindowHandler.attachedWebView; } @end 

Licensed as Apache 2.

+4
source share

You do not indicate what kind of uncertain behavior you see. A quick possibility is that when you introduced the delegate method, you forgot to tell the web browser that you were ignoring the click by calling the ignore method WebPolicyDecisionListener , which was passed to your delegate, which might have put things in a weird state.

If this is not a problem, how much control do you have over the content that you display? The policy delegate provides you with simple mechanisms for filtering all resource loads (as you discovered), and an entire new window opens through webView: decidePolicyForNewWindowAction: request: newFrameName: decisionListener:. All calls to window.open should go through this, like everything that launches a new window.

If another window opens that you want to save in your application, you will do a little more work. One of the arguments passed to the delegate is a dictionary containing information about the event. In this dictionary, the WebActionElementKey will have a details dictionary, including the source dom content of the link. If you want to fight there, you can grab the actual DOM element and check the href text to see if it starts with window.open. It's a bit heavy weight, but if you want fine-grained control, it will give you that.

+2
source share

Reading all the messages, I came up with my simple solution, all the funcs are in the same class, here it opens a link with a browser.

 - (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request { return [self externalWebView:sender]; } - (void)webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener { [[NSWorkspace sharedWorkspace] openURL:[actionInformation objectForKey:WebActionOriginalURLKey]]; } -(WebView*)externalWebView:(WebView*)newWebView { WebView *webView = newWebView; [webView setUIDelegate:self]; [webView setPolicyDelegate:self]; [webView setResourceLoadDelegate:self]; return webView; } 
+2
source share

Explanation:

Windows created using JavaScript through window.open goes through createWebViewWithRequest. All calls to window.open lead to createWebViewWithRequest: with a null request, and then a location change in that WebView.

See this old post on the WebKit mailing list for more information.

0
source share

An alternative to returning a new WebView and waiting for its loadRequest: method, which I called, I rewrote the window.open function in JSCView's WebView:

First I installed my controller as WebFrameLoadDelegate from WebView:

 myWebView.frameLoadDelegate = self; 

Then, in the delegate method, I rewrote the window.open function, and I can handle the URL there.

 - (void)webView:(WebView *)webView didCreateJavaScriptContext:(JSContext *)context forFrame:(WebFrame *)frame{ context[@"window"][@"open"] = ^(id url){ NSLog(@"url to load: %@", url); }; } 

This allows me to process the request, however, I needed to create additional WebViews without the inconvenient need.

0
source share

All Articles