2007-02-27 Mitz Pettel <mitz@webkit.org>
[WebKit-https.git] / WebKit / WebCoreSupport / WebChromeClient.mm
1 /*
2  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
3  * Copyright (C) 2007 Trolltech ASA
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer. 
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution. 
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission. 
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #import "WebChromeClient.h"
31
32 #import "WebDefaultUIDelegate.h"
33 #import "WebFrameInternal.h"
34 #import "WebFrameView.h"
35 #import "WebHTMLView.h"
36 #import "WebNSURLRequestExtras.h"
37 #import "WebUIDelegate.h"
38 #import "WebUIDelegatePrivate.h"
39 #import "WebView.h"
40 #import "WebViewInternal.h"
41 #import <WebCore/BlockExceptions.h>
42 #import <WebCore/FloatRect.h>
43 #import <WebCore/FrameLoadRequest.h>
44 #import <WebCore/IntRect.h>
45 #import <WebCore/PlatformString.h>
46 #import <WebCore/ResourceRequest.h>
47 #import <WebCore/Screen.h>
48 #import <wtf/PassRefPtr.h>
49
50 @interface NSView (AppKitSecretsWebBridgeKnowsAbout)
51 - (NSView *)_findLastViewInKeyViewLoop;
52 @end
53
54 using namespace WebCore;
55
56 WebChromeClient::WebChromeClient(WebView *webView) 
57     : m_webView(webView)
58 {
59 }
60
61 void WebChromeClient::chromeDestroyed()
62 {
63     delete this;
64 }
65
66 // These functions scale between window and WebView coordinates because JavaScript/DOM operations 
67 // assume that the WebView and the window share the same coordinate system.
68
69 void WebChromeClient::setWindowRect(const FloatRect& rect)
70 {
71     NSRect windowRect = toDeviceSpace(rect, [m_webView window]);
72     [[m_webView _UIDelegateForwarder] webView:m_webView setFrame:windowRect];
73 }
74
75 FloatRect WebChromeClient::windowRect()
76 {
77     NSRect windowRect = [[m_webView _UIDelegateForwarder] webViewFrame:m_webView];
78     return toUserSpace(windowRect, [m_webView window]);
79 }
80
81 // FIXME: We need to add API for setting and getting this.
82 FloatRect WebChromeClient::pageRect()
83 {
84     return [m_webView frame];
85 }
86
87 float WebChromeClient::scaleFactor()
88 {
89     if (NSWindow *window = [m_webView window])
90         return [window  userSpaceScaleFactor];
91     return [[NSScreen mainScreen] userSpaceScaleFactor];
92 }
93
94 void WebChromeClient::focus()
95 {
96     [[m_webView _UIDelegateForwarder] webViewFocus:m_webView];
97 }
98
99 void WebChromeClient::unfocus()
100 {
101     [[m_webView _UIDelegateForwarder] webViewUnfocus:m_webView];
102 }
103
104 bool WebChromeClient::canTakeFocus(FocusDirection)
105 {
106     // There's unfortunately no way to determine if we will become first responder again
107     // once we give it up, so we just have to guess that we won't.
108     return true;
109 }
110
111 void WebChromeClient::takeFocus(FocusDirection direction)
112 {
113     if (direction == FocusDirectionForward) {
114         // Since we're trying to move focus out of m_webView, and because
115         // m_webView may contain subviews within it, we ask it for the next key
116         // view of the last view in its key view loop. This makes m_webView
117         // behave as if it had no subviews, which is the behavior we want.
118         NSView *lastView = [m_webView _findLastViewInKeyViewLoop];
119         [[m_webView window] selectKeyViewFollowingView:lastView];
120     } else
121         [[m_webView window] selectKeyViewPrecedingView:m_webView];
122 }
123
124 Page* WebChromeClient::createWindow(const FrameLoadRequest& request)
125 {
126     NSURLRequest *URLRequest = nil;
127     if (!request.isEmpty())
128         URLRequest = request.resourceRequest().nsURLRequest();
129
130     WebView *newWebView;
131     id delegate = [m_webView UIDelegate];
132     if ([delegate respondsToSelector:@selector(webView:createWebViewWithRequest:)])
133         newWebView = [delegate webView:m_webView createWebViewWithRequest:URLRequest];
134     else
135         newWebView = [[WebDefaultUIDelegate sharedUIDelegate] webView:m_webView createWebViewWithRequest:URLRequest];
136
137     return core(newWebView);
138 }
139
140 Page* WebChromeClient::createModalDialog(const FrameLoadRequest& request)
141 {
142     NSURLRequest *URLRequest = nil;
143     if (!request.isEmpty())
144         URLRequest = request.resourceRequest().nsURLRequest();
145
146     WebView *newWebView = nil;
147     id delegate = [m_webView UIDelegate];
148     if ([delegate respondsToSelector:@selector(webView:createWebViewModalDialogWithRequest:)])
149         newWebView = [delegate webView:m_webView createWebViewModalDialogWithRequest:URLRequest];
150     else if ([delegate respondsToSelector:@selector(webView:createWebViewWithRequest:)])
151         newWebView = [delegate webView:m_webView createWebViewWithRequest:URLRequest];
152     else
153         newWebView = [[WebDefaultUIDelegate sharedUIDelegate] webView:m_webView createWebViewWithRequest:URLRequest];
154
155     return core(newWebView);
156 }
157
158 void WebChromeClient::show()
159 {
160     [[m_webView _UIDelegateForwarder] webViewShow:m_webView];
161 }
162
163 bool WebChromeClient::canRunModal()
164 {
165     return [[m_webView UIDelegate] respondsToSelector:@selector(webViewRunModal:)];
166 }
167
168 void WebChromeClient::runModal()
169 {
170     [[m_webView UIDelegate] webViewRunModal:m_webView];
171 }
172
173 void WebChromeClient::setToolbarsVisible(bool b)
174 {
175     [[m_webView _UIDelegateForwarder] webView:m_webView setToolbarsVisible:b];
176 }
177
178 bool WebChromeClient::toolbarsVisible()
179 {
180     id delegate = [m_webView UIDelegate];
181     if ([delegate respondsToSelector:@selector(webViewAreToolbarsVisible:)])
182         return [delegate webViewAreToolbarsVisible:m_webView];
183     return [[WebDefaultUIDelegate sharedUIDelegate] webViewAreToolbarsVisible:m_webView];
184 }
185
186 void WebChromeClient::setStatusbarVisible(bool b)
187 {
188     [[m_webView _UIDelegateForwarder] webView:m_webView setStatusBarVisible:b];
189 }
190
191 bool WebChromeClient::statusbarVisible()
192 {
193     id delegate = [m_webView UIDelegate];
194     if ([delegate respondsToSelector:@selector(webViewIsStatusBarVisible:)])
195         return [delegate webViewIsStatusBarVisible:m_webView];
196     return [[WebDefaultUIDelegate sharedUIDelegate] webViewIsStatusBarVisible:m_webView];
197 }
198
199
200 void WebChromeClient::setScrollbarsVisible(bool b)
201 {
202     [[[m_webView mainFrame] frameView] setAllowsScrolling:b];
203 }
204
205 bool WebChromeClient::scrollbarsVisible()
206 {
207     return [[[m_webView mainFrame] frameView] allowsScrolling];
208 }
209
210 void WebChromeClient::setMenubarVisible(bool)
211 {
212     // The menubar is always visible in Mac OS X.
213     return;
214 }
215
216 bool WebChromeClient::menubarVisible()
217 {
218     // The menubar is always visible in Mac OS X.
219     return true;
220 }
221
222 void WebChromeClient::setResizable(bool b)
223 {
224     [[m_webView _UIDelegateForwarder] webView:m_webView setResizable:b];
225 }
226
227 void WebChromeClient::addMessageToConsole(const String& message, unsigned int lineNumber, const String& sourceURL)
228 {
229     id wd = [m_webView UIDelegate];
230     if ([wd respondsToSelector:@selector(webView:addMessageToConsole:)]) {
231         NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
232             (NSString *)message, @"message",
233             [NSNumber numberWithInt: lineNumber], @"lineNumber",
234             (NSString *)sourceURL, @"sourceURL",
235             NULL];
236         
237         [wd webView:m_webView addMessageToConsole:dictionary];
238     }    
239 }
240
241 bool WebChromeClient::canRunBeforeUnloadConfirmPanel()
242 {
243     id wd = [m_webView UIDelegate];
244     return [wd respondsToSelector:@selector(webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:)];
245 }
246
247 bool WebChromeClient::runBeforeUnloadConfirmPanel(const String& message, Frame* frame)
248 {
249     id wd = [m_webView UIDelegate];
250     if ([wd respondsToSelector:@selector(webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:)])
251         return [wd webView:m_webView runBeforeUnloadConfirmPanelWithMessage:message initiatedByFrame:kit(frame)];
252     return true;
253 }
254
255 void WebChromeClient::closeWindowSoon()
256 {
257     // We need to remove the parent WebView from WebViewSets here, before it actually
258     // closes, to make sure that JavaScript code that executes before it closes
259     // can't find it. Otherwise, window.open will select a closed WebView instead of 
260     // opening a new one <rdar://problem/3572585>.
261
262     // We also need to stop the load to prevent further parsing or JavaScript execution
263     // after the window has torn down <rdar://problem/4161660>.
264   
265     // FIXME: This code assumes that the UI delegate will respond to a webViewClose
266     // message by actually closing the WebView. Safari guarantees this behavior, but other apps might not.
267     // This approach is an inherent limitation of not making a close execute immediately
268     // after a call to window.close.
269
270     [m_webView setGroupName:nil];
271     [m_webView stopLoading:nil];
272     [m_webView performSelector:@selector(_closeWindow) withObject:nil afterDelay:0.0];
273 }
274
275 void WebChromeClient::runJavaScriptAlert(Frame* frame, const String& message)
276 {
277     id wd = [m_webView UIDelegate];
278     // Check whether delegate implements new version, then whether delegate implements old version. If neither,
279     // fall back to shared delegate's implementation of new version.
280     if ([wd respondsToSelector:@selector(webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:)])
281         [wd webView:m_webView runJavaScriptAlertPanelWithMessage:message initiatedByFrame:kit(frame)];
282     else if ([wd respondsToSelector:@selector(webView:runJavaScriptAlertPanelWithMessage:)])
283         [wd webView:m_webView runJavaScriptAlertPanelWithMessage:message];
284     else
285         [[WebDefaultUIDelegate sharedUIDelegate] webView:m_webView runJavaScriptAlertPanelWithMessage:message initiatedByFrame:kit(frame)];    
286 }
287
288 bool WebChromeClient::runJavaScriptConfirm(Frame* frame, const String& message)
289 {
290     id wd = [m_webView UIDelegate];
291     // Check whether delegate implements new version, then whether delegate implements old version. If neither,
292     // fall back to shared delegate's implementation of new version.
293     if ([wd respondsToSelector:@selector(webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:)])
294         return [wd webView:m_webView runJavaScriptConfirmPanelWithMessage:message initiatedByFrame:kit(frame)];
295     if ([wd respondsToSelector:@selector(webView:runJavaScriptConfirmPanelWithMessage:)])
296         return [wd webView:m_webView runJavaScriptConfirmPanelWithMessage:message];    
297     return [[WebDefaultUIDelegate sharedUIDelegate] webView:m_webView runJavaScriptConfirmPanelWithMessage:message initiatedByFrame:kit(frame)];
298 }
299
300 bool WebChromeClient::runJavaScriptPrompt(Frame* frame, const String& prompt, const String& defaultText, String& result)
301 {
302     id wd = [m_webView UIDelegate];
303     // Check whether delegate implements new version, then whether delegate implements old version. If neither,
304     // fall back to shared delegate's implementation of new version.
305     if ([wd respondsToSelector:@selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:)])
306         result = [wd webView:m_webView runJavaScriptTextInputPanelWithPrompt:prompt defaultText:defaultText initiatedByFrame:kit(frame)];
307     else if ([wd respondsToSelector:@selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:)])
308         result = [wd webView:m_webView runJavaScriptTextInputPanelWithPrompt:prompt defaultText:defaultText];
309     else
310         result = [[WebDefaultUIDelegate sharedUIDelegate] webView:m_webView runJavaScriptTextInputPanelWithPrompt:prompt defaultText:defaultText initiatedByFrame:kit(frame)];
311     
312     return !result.isNull();
313 }
314
315 bool WebChromeClient::shouldInterruptJavaScript()
316 {
317     BEGIN_BLOCK_OBJC_EXCEPTIONS;
318     id wd = [m_webView UIDelegate];
319     if ([wd respondsToSelector:@selector(webViewShouldInterruptJavaScript:)])
320         return [wd webViewShouldInterruptJavaScript:m_webView];
321     return false;
322     END_BLOCK_OBJC_EXCEPTIONS;
323     
324     return false;
325 }
326
327 void WebChromeClient::setStatusbarText(const WebCore::String& status)
328 {
329     id wd = [m_webView UIDelegate];
330
331     if ([wd respondsToSelector:@selector(webView:setStatusText:)]) {
332         // We want the temporaries allocated here to be released even before returning to the 
333         // event loop; see <http://bugs.webkit.org/show_bug.cgi?id=9880>.
334         NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
335     
336         [wd webView:m_webView setStatusText:status];
337         
338         [localPool release];
339     }
340 }
341
342 bool WebChromeClient::tabsToLinks() const
343 {
344     return [[m_webView preferences] tabsToLinks];
345 }
346
347 IntRect WebChromeClient::windowResizerRect() const
348 {
349     return IntRect();
350 }
351
352 void WebChromeClient::addToDirtyRegion(const IntRect&)
353 {
354 }
355
356 void WebChromeClient::scrollBackingStore(int, int, const IntRect&, const IntRect&)
357 {
358 }
359
360 void WebChromeClient::updateBackingStore()
361 {
362 }