2008-11-06 17 views
7

Estoy construyendo una aplicación Cocoa realmente básica usando WebKit, para mostrar una aplicación Flash/Silverlight dentro de ella. Muy básico, sin intenciones de que sea un navegador en sí mismo.Cocoa/WebKit, con enlaces de apertura de ventana "window.open()" en una instancia de Safari

Hasta ahora no han sido capaces de conseguir que se abra enlaces HTML básico (<a href="..." />) en una nueva instancia de Safari usando

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

Ahora mi dificultad es la apertura de un enlace en una nueva instancia de Safari cuando window.open() se usa en JavaScript. I "pienso" (y con esto, he estado una solución en el código y estoy seguro de si realmente hice o no) Tengo este tipo de trabajo mediante el establecimiento de la vista Web de policyDelegate y aplicar su método

-webView:decidePolicyForNavigationAction:request:frame:decisionListener: 

delegado . Sin embargo, esto condujo a un comportamiento errático.

Entonces la pregunta simple, ¿qué debo hacer para que cuando se llame a window.open(), el enlace se abra en una nueva instancia de Safari.

Gracias

grande de la punta, normalmente soy un desarrollador de .NET, y sólo han estado trabajando con Cacao/WebKit durante unos días.

+0

Estoy teniendo exactamente el mismo problema. Parece ser un error que 'webView: decidePolicyForNewWindowAction: request: newFrameName: decisionListener' no se llama. –

Respuesta

2

No menciona qué tipo de comportamiento errático está viendo. Una posibilidad rápida es que al implementar el método de delegado se olvidó de decirle a la vista web que está ignorando el clic llamando al método de ignorar WebPolicyDecisionListener que se pasó a su delegado, lo que puede haber puesto las cosas en un estado extraño.

Si ese no es el problema, ¿cuánto control tiene sobre el contenido que está mostrando? El delegado de políticas le brinda mecanismos fáciles para filtrar todas las cargas de recursos (como ha descubierto), y todas las ventanas nuevas se abren a través del webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener:. Todas las llamadas a window.open deberían canalizarse a través de eso, al igual que cualquier otra cosa que active una nueva ventana.

Si hay otras ventanas abiertas que desea mantener dentro de su aplicación, deberá hacer un poco más de trabajo. Uno de los argumentos pasados ​​al delegado es un diccionario que contiene information sobre el evento. Si se incluye ese diccionario, WebActionElementKey tendrá un diccionario que contiene un número de details, incluido el contenido original del enlace. Si quiere hurgar, puede tomar el elemento DOM real y verificar el texto del href para ver si comienza con window.open. Eso es un poco pesado, pero si quieres control de grano fino te lo dará.

+0

Gracias por sus comentarios Louis, me registré en los dos últimos enlaces, y los he examinado, lamentablemente no creo que eso funcione porque window.open no está en línea dentro de una etiqueta de anclaje. Se invoca a través de un método Javascript que se llama desde Silverlight. – FireWire

9

Hice el progreso anoche y anoté parte de mi problema.

Ya estoy usando webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: y he conseguido que funcione con las etiquetas de anclaje, sin embargo, parece que el método nunca se llama cuando se invoca JavaScript.

Sin embargo cuando se llama window.open() se llama webView:createWebViewWithRequest:request, he intentado forzar que la ventana se abra en Safari aquí, sin embargo la solicitud siempre es nula. Entonces nunca puedo leer la URL.

He hecho algunas búsquedas alrededor, y esto parece ser una "mala característica conocida" sin embargo, no he podido encontrar una manera de evitarlo.

Por lo que entiendo createWebViewWithRequest le da la posibilidad de crear la nueva vista web, la url solicitada se envía a la nueva webView para ser cargada. This is the best explanation I have been able to find so far.

Así que, aunque muchas personas han señalado este problema, todavía no he visto ninguna solución que se ajuste a mis necesidades. Trataré de ahondar un poco más en el decidePolicyForNewWindowAction nuevamente.

Gracias!

+0

Esto es muy útil. Gran trabajo, hombre! :) – Kimpoy

6

Bueno, yo estoy manejando mediante la creación de una web View ficticia, estableciendo que es frameLoad delegado a una clase personalizada que maneja

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

y abre una nueva ventana de allí.

código:

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

y 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

Parece que hay un error con webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: en que la solicitud es siempre nil, pero no es una solución robusta que funciona con normalidad target="_blank" enlaces, así como los javascript.

Básicamente utilizo otra WebView efímera para manejar la carga de la nueva página. Similar a Yoni Shalom pero con un poco más de azúcar sintáctica.

para usarlo primero estableció un objeto delegado para su WebView, en este caso estoy configurando mi mismo como el delegado:

webView.UIDelegate = self; 

A continuación, sólo poner en práctica el método delegado webView:createWebViewWithRequest: y utilizar mi API basada en bloque para hacer algo cuando se carga una nueva página, en este caso estoy de abrir la página en un navegador externo:

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

Eso es más o menos la misma. Aquí está el código para mi clase. Cabecera:

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

Implementación:

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

Licenciado como Apache 2.

+0

Viejo pero impresionante, este error todavía parece estar presente y esta respuesta era exactamente lo que necesitaba. –

+0

Todavía es una buena solución. –

2

Mediante la lectura de todos los mensajes, he llegado con mi solución sencilla, todos los funcs se encuentran en una misma clase, aquí es, abre un enlace con el navegador.

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

Explicación:

de Windows creados a partir de JavaScript a través de ir a través window.open createWebViewWithRequest. Todas las llamadas de window.open dan como resultado un createWebViewWithRequest: con una solicitud nula, luego un cambio de ubicación en ese WebView.

Para obtener más información, see this old post en la lista de correo de WebKit.

0

Una alternativa a devolver una nueva vista Web ya la espera de su método loadRequest: a ser llamado, terminé sobrescribir la función window.open en la vista Web JSContext:

En primer lugar, me puse mi controlador de ser el WebFrameLoadDelegate de la vista Web :

myWebView.frameLoadDelegate = self; 

Luego, en el método delegado, que sobreescribí la función window.open, y puede procesar la dirección URL en su lugar.

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

Esto me permite manejar la solicitud sin importar lo que necesite sin la incómoda necesidad de crear WebViews adicionales.

Cuestiones relacionadas