2008-11-06 3 views
7

Ich baue eine wirklich einfache Cocoa-Anwendung mit WebKit, um eine Flash/Silverlight-Anwendung darin anzuzeigen. Sehr einfach, keine Absicht, selbst ein Browser zu sein.Cocoa/WebKit, mit "window.open()" JavaScript Links Öffnen in einer Instanz von Safari

Bisher habe ich in der Lage gewesen, es zu erhalten grundlegende HTML-Links zu öffnen (<a href="..." />) in einer neuen Instanz von Safari mit

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

Nun meine Schwierigkeit wird auf einen Link in einer neuen Instanz von Safari zu öffnen, wenn window.open() wird in JavaScript verwendet. Ich „denken“ (und dies, habe ich den Code zu hacken, und bin nicht sicher, ob ich eigentlich oder nicht) habe ich diese Art von Arbeit durch die WebView der Einstellung policyDelegate und seine

-webView:decidePolicyForNavigationAction:request:frame:decisionListener: 

Delegatmethode Umsetzung . Dies führte jedoch zu unberechenbarem Verhalten.

Also die einfache Frage, was muss ich tun, damit, wenn window.open() aufgerufen wird, der Link in einer neuen Instanz von Safari geöffnet wird.

Dank

Big Punkt, ich bin normalerweise ein .NET Entwickler, und haben mit Cocoa/WebKit arbeiten für ein paar Tage nur worden.

+0

Ich habe genau das gleiche Problem. Es scheint ein Fehler zu sein, dass 'webView: decidePolicyForNewWindowAction: request: newFrameName: decisionListener' nicht aufgerufen wird. –

Antwort

2

Sie erwähnen nicht, welche Art von unberechenbarem Verhalten Sie sehen. Eine schnelle Möglichkeit ist, dass Sie beim Implementieren der Delegate-Methode vergessen haben, dem Webview zu sagen, dass Sie den Klick ignorieren, indem Sie die ignore-Methode der WebPolicyDecisionListener aufrufen, die an Ihren Delegaten übergeben wurde, was die Dinge in einen merkwürdigen Zustand versetzt haben könnte.

Wenn das nicht das Problem ist, wie viel Kontrolle haben Sie dann über den Inhalt, den Sie anzeigen? Der Policy-Delegate bietet Ihnen einfache Mechanismen zum Filtern aller Ressourcenladungen (wie Sie festgestellt haben), und alle neuen Fenster werden über webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: geöffnet. Alle Aufrufe von window.open sollten sich durch dieses Fenster ziehen, ebenso wie alles andere, was ein neues Fenster auslöst.

Wenn andere Fenster geöffnet werden, die Sie in Ihrer App behalten möchten, werden Sie ein wenig mehr arbeiten. Eines der Argumente, das an den Delegaten übergeben wird, ist ein Wörterbuch, das information über das Ereignis enthält. In diesem Wörterbuch wird der WebActionElementKey ein Wörterbuch enthalten, das eine Nummer details enthält, einschließlich des ursprünglichen dom-Inhalts des Links. Wenn Sie dort herumstochern wollen, können Sie sich das eigentliche DOM-Element ansehen und den Text der href überprüfen, um zu sehen, ob es mit window.open beginnt. Das ist ein bisschen schwer, aber wenn Sie feinkörnige Kontrolle wollen, wird es Ihnen geben.

+0

Vielen Dank für Ihr Feedback Louis, ich habe die letzten beiden Links überprüft, und ich habe sie überprüft, leider glaube ich nicht, dass das funktionieren wird, da window.open nicht in einem Anker-Tag eingebunden ist. Es wird über eine Javascript-Methode aufgerufen, die von Silverlight aufgerufen wird. – FireWire

9

Ich machte letzte Nacht Fortschritte und fixierte einen Teil meines Problems.

Ich benutze bereits webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: und ich habe es mit Anchor-Tags arbeiten, aber die Methode scheint nie aufgerufen, wenn JavaScript aufgerufen wird.

Wenn jedoch window.open() aufgerufen wird webView:createWebViewWithRequest:request heißt, habe ich versucht, das Fenster in Safari hier zu öffnen, aber Anfrage ist immer Null. Also kann ich die URL nie auslesen.

Ich habe einige Suche herum getan, und dies scheint eine bekannte "Fehlfunktion", aber ich habe nicht in der Lage, einen Weg zu finden, um es zu umgehen.

Von dem, was ich verstehe createWebViewWithRequest gibt Ihnen die Möglichkeit, das neue Webview zu erstellen, die angeforderte URL wird dann an das neue WebView zum Laden gesendet. This is the best explanation I have been able to find so far.

Obwohl viele Leute auf dieses Problem hingewiesen haben, habe ich noch keine Lösung gefunden, die meinen Bedürfnissen entspricht. Ich werde versuchen, noch ein wenig tiefer in die decidePolicyForNewWindowAction einzutauchen.

Danke!

+0

Dies ist sehr hilfreich. Tolle Arbeit, Mann! :) – Kimpoy

6

Nun, ich bin die Handhabung durch ein Dummy-webView zu schaffen, ist es Einstellung sind frameLoad Delegierten zu einer benutzerdefinierten Klasse, die ein neues Fenster dort

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

und öffnet behandelt.

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]; 
} 

und 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; 
} 
4

Es scheint mit webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener:, dass ein Fehler zu sein, die Anforderung immer nil ist, aber es ist eine robuste Lösung, die sowohl mit normalen arbeitet target="_blank" Links sowie Javascript.

Grundsätzlich benutze ich eine andere ephemere WebView, um die neue Seite zu laden. Ähnlich wie Yoni Shalom, aber mit ein wenig mehr syntaktischen Zucker.

Um es zu nutzen zunächst einen Delegatobjekt für Ihren WebView, in diesem Fall habe ich mir als Delegierter bin Einstellung:

webView.UIDelegate = self; 

implementieren Dann einfach die webView:createWebViewWithRequest: Methode delegieren und mein Block-basierte API verwenden zu tun etwas, wenn eine neue Seite geladen wird, in diesem Fall bin ich das Öffnen der Seite in einem externen Browser:

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

das ist ziemlich viel es ist. Hier ist der Code für meine Klasse. Rubrik:

// 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 

Implemetierung:

// 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 

Zulassung als Apache 2.

+0

Alt aber toll, dieser Bug scheint immer noch vorhanden zu sein und diese Antwort war genau das, was ich brauchte. –

+0

Immer noch eine gute Lösung. –

2

Durch alle Beiträge zu lesen, ich habe mit meiner einfachen Lösung zu kommen, alle funcs in derselben Klasse sind, hier ist, öffnet eine Verknüpfung mit dem 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; 
} 
0

Erläuterung:

Windows von JavaScript erstellt über window.open durch createWebViewWithRequest gehen. Alle Aufrufe von window.open führen zu einem createWebViewWithRequest: mit einer NULL-Anfrage und später einem Standortwechsel in diesem WebView.

Für weitere Informationen, see this old post auf der WebKit-Mailing-Liste.

0

Eine Alternative eine neue WebView und wartet auf seine loadRequest: Methode zur Rückkehr aufgerufen werden, ich habe die window.open Funktion in der JSContext des WebView überschreiben endete:

Zuerst stelle ich meinen Controller die WebFrameLoadDelegate der WebView zu sein :

myWebView.frameLoadDelegate = self; 

Dann wird in der Delegatmethode, überschrieb ich die window.open Funktion, und ich kann die URL dort stattdessen verarbeiten.

Dies ließ mich die Anfrage bearbeiten, aber ich brauchte, ohne die peinliche Notwendigkeit, zusätzliche WebViews zu erstellen.

Verwandte Themen