WebKit 對於開啟新視窗事件的處理

如果想要在網頁中插入開啟新的瀏覽器視窗的語法,有兩種簡單的作法,一種是直接在連結語法中加入 target=”_blank”,例如 <a href="http://zonble.net/" target="_blank">,另外一種方法則是呼叫 Javascript 的 window.open() 函式。

而雖然這兩者的目的是開啟新的瀏覽器視窗,但是在當你在寫一個使用了 WebKit 的 Cocoa 應用程式的時候,對於這兩個事件,則需要分別給予不同的實作-在 WebView 物件中觸發前者時,WebView 會去呼叫 Policy DelegatewebView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: 這個 Method,但是後者-Javascript 的 window.open()-則會跳過 Policy Delegate,直接向 UI Delegate 呼叫 webView:createWebViewWithRequest:

意思就是,在使用者在你的應用程式中的 WebView 中,點選了一個包含 target=”_black” 的連結的時候,WebView 首先會詢問你所指定的 Delegate Object,然後你可以根據狀況判斷,而做出對應的處理-應該開新視窗、開新的瀏覽器分頁(如果你寫的是一套具備分頁功能的瀏覽器應用程式)、或是使用系統預設的瀏覽器開啟。

但是遇到 window.open() 則不然,這個狀況是-事件發生的 WebView 已經決定要因此產生新的 WebView 物件,你需要在 webView:createWebViewWithRequest: 的實作中,回傳一個 WebView 物件,而這個 WebView 物件就會開始載入 window.open() 所傳入的、那個要開啟的 URL。而我們會在這個地方遇到一個問題-基本上我覺得應該算是蘋果的 Bug-在 webView:createWebViewWithRequest: method 中所傳入的那個 NSURLRequest 物件,居然是 NULL!

假使我們現在要做的事情是,無論遇到哪一種開新視窗的事件,統統都丟到外部瀏覽器開啟。在處理 target=”_blank” 這種狀況時,只要這麼寫,就可以達到想要的效果:

- (void)webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request newFrameName:(NSString *)frameName decisionListener:(id < WebPolicyDecisionListener >)listener
{
	NSURL *URL = [request URL];
	[[NSWorkspace sharedWorkspace] openURL:URL];
}

然後我們可以試試看,在 webView:createWebViewWithRequest: 中,也採用相同的實作:

- (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request
{
	NSURL *URL = [request URL];
	[[NSWorkspace sharedWorkspace] openURL:URL];
	return nil;
}

完全沒有用。因為 request 物件是 NULL,所以自然沒有辦法取得對應的 URL 物件。查了一下網路上面的討論,看到的說法是-WebView 的確不會傳入 request 物件,因為在遇到 window.open() 要開啟新視窗時,只要靶新的 WebView 準備好,之後就會自動往這個新的 WebView 的 mainFrame 送出 loadRequest:-既然如此,webView:createWebViewWithRequest: 中設計要傳入 request 物件,這個設計到底是做心酸的還是做好玩的…。而網路上發問的時間是 2004 年,而到了 2009 年這個問題還是始終存在。

後來決定使用醜陋的作法-既然在 code>webView:createWebViewWithRequest: 收不到 request,那麼就回傳一個隱藏的 WebView,而這個 WebView 要開始載入內容的時候,總可以取得 URL 吧?

- (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request
{
	return _hiddenWebView;
}

- (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame
{
	if ([sender isEqual:_hiddenWebView]) {
		NSString *URLString = [sender mainFrameURL];
		NSURL *URL = [NSURL URLWithString:URLString];
		[[NSWorkspace sharedWorkspace] openURL:URL];
		[_hiddenWebView stopLoading:self];
		return;
	}
}

為了要在外部開啟網頁,居然要另外準備一個 WebView 物件…。有什麼更好的作法嗎?

One thought on “WebKit 對於開啟新視窗事件的處理

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.