【问题标题】:Cocoa/WebKit, having "window.open()" JavaScript links opening in an instance of SafariCocoa/WebKit,在 Safari 实例中打开“window.open()”JavaScript 链接
【发布时间】:2008-11-06 21:53:29
【问题描述】:

我正在使用 WebKit 构建一个非常基本的 Cocoa 应用程序,以在其中显示一个 Flash/Silverlight 应用程序。非常基础,不打算将其作为浏览器本身。

到目前为止,我已经能够使用

在新的 Safari 实例中打开基本的 html 链接 (<a href="..." />)
[[NSWorkspace sharedWorkspace] openURL:[request URL]];

现在我的困难是在 JavaScript 中使用 window.open() 时在新的 Safari 实例中打开链接。我“认为”(通过这个,我一直在修改代码并且不确定我是否真的这样做了)我通过设置 WebView 的 policyDelegate 并实现它来实现这种工作

-webView:decidePolicyForNavigationAction:request:frame:decisionListener:

委托方法。然而,这导致了一些不稳定的行为。

那么简单的问题,我需要做什么才能在调用 window.open() 时,在新的 Safari 实例中打开链接。

谢谢

重要的是,我通常是一名 .NET 开发人员,并且只使用 Cocoa/WebKit 几天。

【问题讨论】:

  • 我遇到了完全相同的问题。好像是webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener没有被调用的bug。

标签: objective-c cocoa macos webkit


【解决方案1】:

我昨晚取得了进步,并解决了我的部分问题。

我已经在使用 webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: 并且我已经让它与锚标记一起使用,但是在调用 JavaScript 时该方法似乎永远不会被调用。

然而,当window.open() 被调用webView:createWebViewWithRequest:request 被调用时,我试图在这里强制在Safari 中打开窗口,但是请求始终为空。所以我永远无法读出 URL。

我已经进行了一些搜索,这似乎是一个已知的“misfeature”,但是我无法找到解决它的方法。

据我了解,createWebViewWithRequest 使您能够创建新的 webview,然后将请求的 url 发送到新的 webview 以进行加载。 This is the best explanation I have been able to find so far.

因此,虽然很多人都指出了这个问题,但我还没有看到任何适合我需要的解决方案。我将尝试再次深入研究decidePolicyForNewWindowAction

谢谢!

【讨论】:

    【解决方案2】:

    好吧,我通过创建一个虚拟 webView 来处理它,将它的 frameLoad 委托设置为一个自定义类来处理

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

    并在那里打开一个新窗口。

    代码:

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

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

    【讨论】:

      【解决方案3】:

      webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: 似乎存在一个错误,即请求始终为 nil,但有一个强大的解决方案可用于普通的 target="_blank" 链接和 javascript 链接。

      基本上,我使用另一个临时 WebView 来处理新页面的加载。类似于 Yoni Shalom,但语法糖要多一些。

      要使用它,首先为您的 WebView 设置一个委托对象,在这种情况下,我将自己设置为委托:

      webView.UIDelegate = self;
      

      然后只需实现 webView:createWebViewWithRequest: 委托方法并使用我的基于块的 API 在加载新页面时执行某些操作,在这种情况下,我在外部浏览器中打开页面:

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

      差不多就是这样。这是我的课程的代码。标题:

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

      实现:

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

      授权为 Apache 2。

      【讨论】:

      • 旧但很棒,这个错误似乎仍然存在,这个答案正是我所需要的。
      【解决方案4】:

      你没有提到你看到什么样的不稳定行为。一个快速的可能性是,在实现委托方法时,您忘记通过调用传递给您的委托的 WebPolicyDecisionListener 的忽略方法来告诉 webview 您忽略了单击,这可能会使事情进入一个奇怪的状态。

      如果这不是问题,那么您对所显示的内容有多少控制权?策略委托为您提供了过滤所有资源负载的简单机制(正如您所发现的那样),并且所有新窗口都通过 webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: 打开。所有 window.open 调用都应该通过它,触发新窗口的任何其他调用也是如此。

      如果您希望在您的应用程序中保留其他打开的窗口,您需要做更多的工作。传递给委托的参数之一是包含有关事件的information 的字典。 Insie 那个字典的 WebActionElementKey 将有一个包含多个details 的字典,包括链接的原始 dom 内容。如果你想在那里四处寻找,你可以获取实际的 DOM 元素,并检查 href 的文本以查看它是否以 window.open 开头。这有点重,但如果你想要细粒度的控制,它会给你。

      【讨论】:

      • 感谢您的反馈 Louis,我检查了最后两个链接,并查看了它们,不幸的是,我认为这不起作用,因为 window.open 不是内联在锚标记中。它是通过从 Silverlight 调用的 Javascript 方法调用的。
      【解决方案5】:

      通过阅读所有帖子,我想出了我的简单解决方案,所有功能都在同一个类中,在这里,用浏览器打开一个链接。

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

      【讨论】:

        【解决方案6】:

        解释:

        通过 window.open 从 JavaScript 创建的窗口通过 createWebViewWithRequest。 所有 window.open 调用都会导致 createWebViewWithRequest: 请求为空,然后在该 WebView 上发生位置更改。

        如需更多信息,请在 WebKit 邮件列表中see this old post

        【讨论】:

          【解决方案7】:

          除了返回一个新的 WebView 并等待其 loadRequest: 方法被调用之外,我最终覆盖了 WebView 的 JSContext 中的 window.open 函数:

          首先,我将我的控制器设置为 WebView 的 WebFrameLoadDelegate:

          myWebView.frameLoadDelegate = self;
          

          然后,在委托方法中,我覆盖了window.open 函数,我可以在那里处理URL。

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

          这让我可以处理我需要的请求,而无需创建额外的 WebView。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-01-16
            • 2013-01-07
            • 2023-04-11
            • 2023-03-10
            • 1970-01-01
            • 2013-05-18
            • 2021-08-13
            相关资源
            最近更新 更多