0e996ad44befb8187c3fcdbbd6eeace2df7ef5b6
[WebKit-https.git] / Source / WebKit / mac / WebCoreSupport / WebChromeClient.mm
1 /*
2  * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
3  * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies)
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 "DOMElementInternal.h"
33 #import "DOMNodeInternal.h"
34 #import "PopupMenuMac.h"
35 #import "SearchPopupMenuMac.h"
36 #import "WebBasePluginPackage.h"
37 #import "WebDefaultUIDelegate.h"
38 #import "WebDelegateImplementationCaching.h"
39 #import "WebElementDictionary.h"
40 #import "WebFrameInternal.h"
41 #import "WebFrameView.h"
42 #import "WebHTMLViewInternal.h"
43 #import "WebHistoryInternal.h"
44 #import "WebKitFullScreenListener.h"
45 #import "WebKitPrefix.h"
46 #import "WebKitSystemInterface.h"
47 #import "WebNSURLRequestExtras.h"
48 #import "WebOpenPanelResultListener.h"
49 #import "WebPlugin.h"
50 #import "WebQuotaManager.h"
51 #import "WebSecurityOriginInternal.h"
52 #import "WebUIDelegatePrivate.h"
53 #import "WebView.h"
54 #import "WebViewInternal.h"
55 #import <Foundation/Foundation.h>
56 #import <WebCore/BlockExceptions.h>
57 #import <WebCore/ColorChooser.h>
58 #import <WebCore/Console.h>
59 #import <WebCore/ContextMenu.h>
60 #import <WebCore/ContextMenuController.h>
61 #import <WebCore/Cursor.h>
62 #import <WebCore/Element.h>
63 #import <WebCore/FileChooser.h>
64 #import <WebCore/FileIconLoader.h>
65 #import <WebCore/FloatRect.h>
66 #import <WebCore/Frame.h>
67 #import <WebCore/FrameLoadRequest.h>
68 #import <WebCore/FrameView.h>
69 #import <WebCore/HTMLInputElement.h>
70 #import <WebCore/HTMLNames.h>
71 #import <WebCore/HTMLPlugInImageElement.h>
72 #import <WebCore/HitTestResult.h>
73 #import <WebCore/Icon.h>
74 #import <WebCore/IntPoint.h>
75 #import <WebCore/IntRect.h>
76 #import <WebCore/NavigationAction.h>
77 #import <WebCore/NotImplemented.h>
78 #import <WebCore/Page.h>
79 #import <WebCore/PlatformScreen.h>
80 #import <WebCore/ResourceRequest.h>
81 #import <WebCore/Widget.h>
82 #import <WebCore/WindowFeatures.h>
83 #import <wtf/PassRefPtr.h>
84 #import <wtf/Vector.h>
85 #import <wtf/text/WTFString.h>
86
87 #if USE(ACCELERATED_COMPOSITING)
88 #import <WebCore/GraphicsLayer.h>
89 #endif
90
91 #if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
92 #import "NetscapePluginHostManager.h"
93 #endif
94
95 NSString *WebConsoleMessageXMLMessageSource = @"XMLMessageSource";
96 NSString *WebConsoleMessageJSMessageSource = @"JSMessageSource";
97 NSString *WebConsoleMessageNetworkMessageSource = @"NetworkMessageSource";
98 NSString *WebConsoleMessageConsoleAPIMessageSource = @"ConsoleAPIMessageSource";
99 NSString *WebConsoleMessageStorageMessageSource = @"StorageMessageSource";
100 NSString *WebConsoleMessageAppCacheMessageSource = @"AppCacheMessageSource";
101 NSString *WebConsoleMessageRenderingMessageSource = @"RenderingMessageSource";
102 NSString *WebConsoleMessageCSSMessageSource = @"CSSMessageSource";
103 NSString *WebConsoleMessageSecurityMessageSource = @"SecurityMessageSource";
104 NSString *WebConsoleMessageOtherMessageSource = @"OtherMessageSource";
105
106 NSString *WebConsoleMessageDebugMessageLevel = @"DebugMessageLevel";
107 NSString *WebConsoleMessageLogMessageLevel = @"LogMessageLevel";
108 NSString *WebConsoleMessageWarningMessageLevel = @"WarningMessageLevel";
109 NSString *WebConsoleMessageErrorMessageLevel = @"ErrorMessageLevel";
110
111
112 @interface NSApplication (WebNSApplicationDetails)
113 - (NSCursor *)_cursorRectCursor;
114 @end
115
116 @interface NSView (WebNSViewDetails)
117 - (NSView *)_findLastViewInKeyViewLoop;
118 @end
119
120 // For compatibility with old SPI.
121 @interface NSView (WebOldWebKitPlugInDetails)
122 - (void)setIsSelected:(BOOL)isSelected;
123 @end
124
125 @interface NSWindow (AppKitSecretsIKnowAbout)
126 - (NSRect)_growBoxRect;
127 @end
128
129 using namespace WebCore;
130 using namespace HTMLNames;
131
132 WebChromeClient::WebChromeClient(WebView *webView) 
133     : m_webView(webView)
134 {
135 }
136
137 void WebChromeClient::chromeDestroyed()
138 {
139     delete this;
140 }
141
142 // These functions scale between window and WebView coordinates because JavaScript/DOM operations 
143 // assume that the WebView and the window share the same coordinate system.
144
145 void WebChromeClient::setWindowRect(const FloatRect& rect)
146 {
147     NSRect windowRect = toDeviceSpace(rect, [m_webView window]);
148     [[m_webView _UIDelegateForwarder] webView:m_webView setFrame:windowRect];
149 }
150
151 FloatRect WebChromeClient::windowRect()
152 {
153     NSRect windowRect = [[m_webView _UIDelegateForwarder] webViewFrame:m_webView];
154     return toUserSpace(windowRect, [m_webView window]);
155 }
156
157 // FIXME: We need to add API for setting and getting this.
158 FloatRect WebChromeClient::pageRect()
159 {
160     return [m_webView frame];
161 }
162
163 void WebChromeClient::focus()
164 {
165     [[m_webView _UIDelegateForwarder] webViewFocus:m_webView];
166 }
167
168 void WebChromeClient::unfocus()
169 {
170     [[m_webView _UIDelegateForwarder] webViewUnfocus:m_webView];
171 }
172
173 bool WebChromeClient::canTakeFocus(FocusDirection)
174 {
175     // There's unfortunately no way to determine if we will become first responder again
176     // once we give it up, so we just have to guess that we won't.
177     return true;
178 }
179
180 void WebChromeClient::takeFocus(FocusDirection direction)
181 {
182     if (direction == FocusDirectionForward) {
183         // Since we're trying to move focus out of m_webView, and because
184         // m_webView may contain subviews within it, we ask it for the next key
185         // view of the last view in its key view loop. This makes m_webView
186         // behave as if it had no subviews, which is the behavior we want.
187         NSView *lastView = [m_webView _findLastViewInKeyViewLoop];
188         // avoid triggering assertions if the WebView is the only thing in the key loop
189         if ([m_webView _becomingFirstResponderFromOutside] && m_webView == [lastView nextValidKeyView])
190             return;
191         [[m_webView window] selectKeyViewFollowingView:lastView];
192     } else {
193         // avoid triggering assertions if the WebView is the only thing in the key loop
194         if ([m_webView _becomingFirstResponderFromOutside] && m_webView == [m_webView previousValidKeyView])
195             return;
196         [[m_webView window] selectKeyViewPrecedingView:m_webView];
197     }
198 }
199
200 void WebChromeClient::focusedElementChanged(Element* element)
201 {
202     if (!element)
203         return;
204     if (!isHTMLInputElement(element))
205         return;
206
207     HTMLInputElement* inputElement = toHTMLInputElement(element);
208     if (!inputElement->isText())
209         return;
210
211     CallFormDelegate(m_webView, @selector(didFocusTextField:inFrame:), kit(inputElement), kit(inputElement->document().frame()));
212 }
213
214 void WebChromeClient::focusedFrameChanged(Frame*)
215 {
216 }
217
218 Page* WebChromeClient::createWindow(Frame* frame, const FrameLoadRequest&, const WindowFeatures& features, const NavigationAction&)
219 {
220     id delegate = [m_webView UIDelegate];
221     WebView *newWebView;
222
223 #if ENABLE(FULLSCREEN_API)
224     if (frame->document() && frame->document()->webkitCurrentFullScreenElement())
225         frame->document()->webkitCancelFullScreen();
226 #endif
227     
228     if ([delegate respondsToSelector:@selector(webView:createWebViewWithRequest:windowFeatures:)]) {
229         NSNumber *x = features.xSet ? [[NSNumber alloc] initWithFloat:features.x] : nil;
230         NSNumber *y = features.ySet ? [[NSNumber alloc] initWithFloat:features.y] : nil;
231         NSNumber *width = features.widthSet ? [[NSNumber alloc] initWithFloat:features.width] : nil;
232         NSNumber *height = features.heightSet ? [[NSNumber alloc] initWithFloat:features.height] : nil;
233         NSNumber *menuBarVisible = [[NSNumber alloc] initWithBool:features.menuBarVisible];
234         NSNumber *statusBarVisible = [[NSNumber alloc] initWithBool:features.statusBarVisible];
235         NSNumber *toolBarVisible = [[NSNumber alloc] initWithBool:features.toolBarVisible];
236         NSNumber *scrollbarsVisible = [[NSNumber alloc] initWithBool:features.scrollbarsVisible];
237         NSNumber *resizable = [[NSNumber alloc] initWithBool:features.resizable];
238         NSNumber *fullscreen = [[NSNumber alloc] initWithBool:features.fullscreen];
239         NSNumber *dialog = [[NSNumber alloc] initWithBool:features.dialog];
240         
241         NSMutableDictionary *dictFeatures = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
242                                              menuBarVisible, @"menuBarVisible", 
243                                              statusBarVisible, @"statusBarVisible",
244                                              toolBarVisible, @"toolBarVisible",
245                                              scrollbarsVisible, @"scrollbarsVisible",
246                                              resizable, @"resizable",
247                                              fullscreen, @"fullscreen",
248                                              dialog, @"dialog",
249                                              nil];
250         
251         if (x)
252             [dictFeatures setObject:x forKey:@"x"];
253         if (y)
254             [dictFeatures setObject:y forKey:@"y"];
255         if (width)
256             [dictFeatures setObject:width forKey:@"width"];
257         if (height)
258             [dictFeatures setObject:height forKey:@"height"];
259         
260         newWebView = CallUIDelegate(m_webView, @selector(webView:createWebViewWithRequest:windowFeatures:), nil, dictFeatures);
261         
262         [dictFeatures release];
263         [x release];
264         [y release];
265         [width release];
266         [height release];
267         [menuBarVisible release];
268         [statusBarVisible release];
269         [toolBarVisible release];
270         [scrollbarsVisible release];
271         [resizable release];
272         [fullscreen release];
273         [dialog release];
274     } else if (features.dialog && [delegate respondsToSelector:@selector(webView:createWebViewModalDialogWithRequest:)]) {
275         newWebView = CallUIDelegate(m_webView, @selector(webView:createWebViewModalDialogWithRequest:), nil);
276     } else {
277         newWebView = CallUIDelegate(m_webView, @selector(webView:createWebViewWithRequest:), nil);
278     }
279
280 #if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
281     if (newWebView)
282         WebKit::NetscapePluginHostManager::shared().didCreateWindow();
283 #endif
284     
285     return core(newWebView);
286 }
287
288 void WebChromeClient::show()
289 {
290     [[m_webView _UIDelegateForwarder] webViewShow:m_webView];
291 }
292
293 bool WebChromeClient::canRunModal()
294 {
295     return [[m_webView UIDelegate] respondsToSelector:@selector(webViewRunModal:)];
296 }
297
298 void WebChromeClient::runModal()
299 {
300     CallUIDelegate(m_webView, @selector(webViewRunModal:));
301 }
302
303 void WebChromeClient::setToolbarsVisible(bool b)
304 {
305     [[m_webView _UIDelegateForwarder] webView:m_webView setToolbarsVisible:b];
306 }
307
308 bool WebChromeClient::toolbarsVisible()
309 {
310     return CallUIDelegateReturningBoolean(NO, m_webView, @selector(webViewAreToolbarsVisible:));
311 }
312
313 void WebChromeClient::setStatusbarVisible(bool b)
314 {
315     [[m_webView _UIDelegateForwarder] webView:m_webView setStatusBarVisible:b];
316 }
317
318 bool WebChromeClient::statusbarVisible()
319 {
320     return CallUIDelegateReturningBoolean(NO, m_webView, @selector(webViewIsStatusBarVisible:));
321 }
322
323 void WebChromeClient::setScrollbarsVisible(bool b)
324 {
325     [[[m_webView mainFrame] frameView] setAllowsScrolling:b];
326 }
327
328 bool WebChromeClient::scrollbarsVisible()
329 {
330     return [[[m_webView mainFrame] frameView] allowsScrolling];
331 }
332
333 void WebChromeClient::setMenubarVisible(bool)
334 {
335     // The menubar is always visible in Mac OS X.
336     return;
337 }
338
339 bool WebChromeClient::menubarVisible()
340 {
341     // The menubar is always visible in Mac OS X.
342     return true;
343 }
344
345 void WebChromeClient::setResizable(bool b)
346 {
347     [[m_webView _UIDelegateForwarder] webView:m_webView setResizable:b];
348 }
349
350 inline static NSString *stringForMessageSource(MessageSource source)
351 {
352     switch (source) {
353     case XMLMessageSource:
354         return WebConsoleMessageXMLMessageSource;
355     case JSMessageSource:
356         return WebConsoleMessageJSMessageSource;
357     case NetworkMessageSource:
358         return WebConsoleMessageNetworkMessageSource;
359     case ConsoleAPIMessageSource:
360         return WebConsoleMessageConsoleAPIMessageSource;
361     case StorageMessageSource:
362         return WebConsoleMessageStorageMessageSource;
363     case AppCacheMessageSource:
364         return WebConsoleMessageAppCacheMessageSource;
365     case RenderingMessageSource:
366         return WebConsoleMessageRenderingMessageSource;
367     case CSSMessageSource:
368         return WebConsoleMessageCSSMessageSource;
369     case SecurityMessageSource:
370         return WebConsoleMessageSecurityMessageSource;
371     case OtherMessageSource:
372         return WebConsoleMessageOtherMessageSource;
373     }
374     ASSERT_NOT_REACHED();
375     return @"";
376 }
377
378 inline static NSString *stringForMessageLevel(MessageLevel level)
379 {
380     switch (level) {
381     case DebugMessageLevel:
382         return WebConsoleMessageDebugMessageLevel;
383     case LogMessageLevel:
384         return WebConsoleMessageLogMessageLevel;
385     case WarningMessageLevel:
386         return WebConsoleMessageWarningMessageLevel;
387     case ErrorMessageLevel:
388         return WebConsoleMessageErrorMessageLevel;
389     }
390     ASSERT_NOT_REACHED();
391     return @"";
392 }
393
394 void WebChromeClient::addMessageToConsole(MessageSource source, MessageLevel level, const String& message, unsigned int lineNumber, unsigned columnNumber, const String& sourceURL)
395 {
396     id delegate = [m_webView UIDelegate];
397     BOOL respondsToNewSelector = NO;
398
399     SEL selector = @selector(webView:addMessageToConsole:withSource:);
400     if ([delegate respondsToSelector:selector])
401         respondsToNewSelector = YES;
402     else {
403         // The old selector only takes JSMessageSource messages.
404         if (source != JSMessageSource)
405             return;
406         selector = @selector(webView:addMessageToConsole:);
407         if (![delegate respondsToSelector:selector])
408             return;
409     }
410
411     NSString *messageSource = stringForMessageSource(source);
412     NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys:
413         (NSString *)message, @"message",
414         [NSNumber numberWithUnsignedInt:lineNumber], @"lineNumber",
415         [NSNumber numberWithUnsignedInt:columnNumber], @"columnNumber",
416         (NSString *)sourceURL, @"sourceURL",
417         messageSource, @"MessageSource",
418         stringForMessageLevel(level), @"MessageLevel",
419         NULL];
420
421     if (respondsToNewSelector)
422         CallUIDelegate(m_webView, selector, dictionary, messageSource);
423     else
424         CallUIDelegate(m_webView, selector, dictionary);
425
426     [dictionary release];
427 }
428
429 bool WebChromeClient::canRunBeforeUnloadConfirmPanel()
430 {
431     return [[m_webView UIDelegate] respondsToSelector:@selector(webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:)];
432 }
433
434 bool WebChromeClient::runBeforeUnloadConfirmPanel(const String& message, Frame* frame)
435 {
436     return CallUIDelegateReturningBoolean(true, m_webView, @selector(webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:), message, kit(frame));
437 }
438
439 void WebChromeClient::closeWindowSoon()
440 {
441     // We need to remove the parent WebView from WebViewSets here, before it actually
442     // closes, to make sure that JavaScript code that executes before it closes
443     // can't find it. Otherwise, window.open will select a closed WebView instead of 
444     // opening a new one <rdar://problem/3572585>.
445
446     // We also need to stop the load to prevent further parsing or JavaScript execution
447     // after the window has torn down <rdar://problem/4161660>.
448   
449     // FIXME: This code assumes that the UI delegate will respond to a webViewClose
450     // message by actually closing the WebView. Safari guarantees this behavior, but other apps might not.
451     // This approach is an inherent limitation of not making a close execute immediately
452     // after a call to window.close.
453
454     [m_webView setGroupName:nil];
455     [m_webView stopLoading:nil];
456     [m_webView performSelector:@selector(_closeWindow) withObject:nil afterDelay:0.0];
457 }
458
459 void WebChromeClient::runJavaScriptAlert(Frame* frame, const String& message)
460 {
461     id delegate = [m_webView UIDelegate];
462     SEL selector = @selector(webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:);
463     if ([delegate respondsToSelector:selector]) {
464         CallUIDelegate(m_webView, selector, message, kit(frame));
465         return;
466     }
467
468     // Call the old version of the delegate method if it is implemented.
469     selector = @selector(webView:runJavaScriptAlertPanelWithMessage:);
470     if ([delegate respondsToSelector:selector]) {
471         CallUIDelegate(m_webView, selector, message);
472         return;
473     }
474 }
475
476 bool WebChromeClient::runJavaScriptConfirm(Frame* frame, const String& message)
477 {
478     id delegate = [m_webView UIDelegate];
479     SEL selector = @selector(webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:);
480     if ([delegate respondsToSelector:selector])
481         return CallUIDelegateReturningBoolean(NO, m_webView, selector, message, kit(frame));
482
483     // Call the old version of the delegate method if it is implemented.
484     selector = @selector(webView:runJavaScriptConfirmPanelWithMessage:);
485     if ([delegate respondsToSelector:selector])
486         return CallUIDelegateReturningBoolean(NO, m_webView, selector, message);
487
488     return NO;
489 }
490
491 bool WebChromeClient::runJavaScriptPrompt(Frame* frame, const String& prompt, const String& defaultText, String& result)
492 {
493     id delegate = [m_webView UIDelegate];
494     SEL selector = @selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:);
495     NSString *defaultString = defaultText;
496     if ([delegate respondsToSelector:selector]) {
497         result = (NSString *)CallUIDelegate(m_webView, selector, prompt, defaultString, kit(frame));
498         return !result.isNull();
499     }
500
501     // Call the old version of the delegate method if it is implemented.
502     selector = @selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:);
503     if ([delegate respondsToSelector:selector]) {
504         result = (NSString *)CallUIDelegate(m_webView, selector, prompt, defaultString);
505         return !result.isNull();
506     }
507
508     result = [[WebDefaultUIDelegate sharedUIDelegate] webView:m_webView runJavaScriptTextInputPanelWithPrompt:prompt defaultText:defaultString initiatedByFrame:kit(frame)];
509     return !result.isNull();
510 }
511
512 bool WebChromeClient::shouldInterruptJavaScript()
513 {
514     return CallUIDelegateReturningBoolean(NO, m_webView, @selector(webViewShouldInterruptJavaScript:));
515 }
516
517 void WebChromeClient::setStatusbarText(const String& status)
518 {
519     // We want the temporaries allocated here to be released even before returning to the 
520     // event loop; see <http://bugs.webkit.org/show_bug.cgi?id=9880>.
521     NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
522     CallUIDelegate(m_webView, @selector(webView:setStatusText:), (NSString *)status);
523     [localPool drain];
524 }
525
526 IntRect WebChromeClient::windowResizerRect() const
527 {
528     return enclosingIntRect([[m_webView window] _growBoxRect]);
529 }
530
531 bool WebChromeClient::supportsImmediateInvalidation()
532 {
533     return true;
534 }
535
536 void WebChromeClient::invalidateRootView(const IntRect&, bool immediate)
537 {
538     if (immediate) {
539         [[m_webView window] displayIfNeeded];
540         [[m_webView window] flushWindowIfNeeded];
541     }
542 }
543
544 void WebChromeClient::invalidateContentsAndRootView(const IntRect& rect, bool immediate)
545 {
546 }
547
548 void WebChromeClient::invalidateContentsForSlowScroll(const IntRect& rect, bool immediate)
549 {
550     invalidateContentsAndRootView(rect, immediate);
551 }
552
553 void WebChromeClient::scroll(const IntSize&, const IntRect&, const IntRect&)
554 {
555 }
556
557 IntPoint WebChromeClient::screenToRootView(const IntPoint& p) const
558 {
559     // FIXME: Implement this.
560     return p;
561 }
562
563 IntRect WebChromeClient::rootViewToScreen(const IntRect& r) const
564 {
565     // FIXME: Implement this.
566     return r;
567 }
568
569 PlatformPageClient WebChromeClient::platformPageClient() const
570 {
571     return 0;
572 }
573
574 void WebChromeClient::contentsSizeChanged(Frame*, const IntSize&) const
575 {
576 }
577
578 void WebChromeClient::scrollRectIntoView(const IntRect& r) const
579 {
580     // FIXME: This scrolling behavior should be under the control of the embedding client,
581     // perhaps in a delegate method, rather than something WebKit does unconditionally.
582     NSView *coordinateView = [[[m_webView mainFrame] frameView] documentView];
583     NSRect rect = r;
584     for (NSView *view = m_webView; view; view = [view superview]) {
585         if ([view isKindOfClass:[NSClipView class]]) {
586             NSClipView *clipView = (NSClipView *)view;
587             NSView *documentView = [clipView documentView];
588             [documentView scrollRectToVisible:[documentView convertRect:rect fromView:coordinateView]];
589         }
590     }
591 }
592
593 // End host window methods.
594
595 bool WebChromeClient::shouldUnavailablePluginMessageBeButton(RenderEmbeddedObject::PluginUnavailabilityReason pluginUnavailabilityReason) const
596 {
597     if (pluginUnavailabilityReason == RenderEmbeddedObject::PluginMissing)
598         return [[m_webView UIDelegate] respondsToSelector:@selector(webView:didPressMissingPluginButton:)];
599
600     return false;
601 }
602
603 void WebChromeClient::unavailablePluginButtonClicked(Element* element, RenderEmbeddedObject::PluginUnavailabilityReason pluginUnavailabilityReason) const
604 {
605     ASSERT(element->hasTagName(objectTag) || element->hasTagName(embedTag) || element->hasTagName(appletTag));
606
607     ASSERT(pluginUnavailabilityReason == RenderEmbeddedObject::PluginMissing);
608     CallUIDelegate(m_webView, @selector(webView:didPressMissingPluginButton:), kit(element));
609 }
610
611 void WebChromeClient::mouseDidMoveOverElement(const HitTestResult& result, unsigned modifierFlags)
612 {
613     WebElementDictionary *element = [[WebElementDictionary alloc] initWithHitTestResult:result];
614     [m_webView _mouseDidMoveOverElement:element modifierFlags:modifierFlags];
615     [element release];
616 }
617
618 void WebChromeClient::setToolTip(const String& toolTip, TextDirection)
619 {
620     NSView<WebDocumentView> *documentView = [[[m_webView _selectedOrMainFrame] frameView] documentView];
621     if ([documentView isKindOfClass:[WebHTMLView class]])
622         [(WebHTMLView *)documentView _setToolTip:toolTip];
623 }
624
625 void WebChromeClient::print(Frame* frame)
626 {
627     WebFrame *webFrame = kit(frame);
628     if ([[m_webView UIDelegate] respondsToSelector:@selector(webView:printFrame:)])
629         CallUIDelegate(m_webView, @selector(webView:printFrame:), webFrame);
630     else
631         CallUIDelegate(m_webView, @selector(webView:printFrameView:), [webFrame frameView]);
632 }
633
634 #if ENABLE(SQL_DATABASE)
635
636 void WebChromeClient::exceededDatabaseQuota(Frame* frame, const String& databaseName, DatabaseDetails)
637 {
638     BEGIN_BLOCK_OBJC_EXCEPTIONS;
639
640     WebSecurityOrigin *webOrigin = [[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:frame->document()->securityOrigin()];
641     // FIXME: remove this workaround once shipping Safari has the necessary delegate implemented.
642     if (WKAppVersionCheckLessThan(@"com.apple.Safari", -1, 3.1)) {
643         const unsigned long long defaultQuota = 5 * 1024 * 1024; // 5 megabytes should hopefully be enough to test storage support.
644         [[webOrigin databaseQuotaManager] setQuota:defaultQuota];
645     } else
646         CallUIDelegate(m_webView, @selector(webView:frame:exceededDatabaseQuotaForSecurityOrigin:database:), kit(frame), webOrigin, (NSString *)databaseName);
647     [webOrigin release];
648
649     END_BLOCK_OBJC_EXCEPTIONS;
650 }
651
652 #endif
653
654 void WebChromeClient::reachedMaxAppCacheSize(int64_t spaceNeeded)
655 {
656     // FIXME: Free some space.
657 }
658
659 void WebChromeClient::reachedApplicationCacheOriginQuota(SecurityOrigin* origin, int64_t totalSpaceNeeded)
660 {
661     BEGIN_BLOCK_OBJC_EXCEPTIONS;
662
663     WebSecurityOrigin *webOrigin = [[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:origin];
664     CallUIDelegate(m_webView, @selector(webView:exceededApplicationCacheOriginQuotaForSecurityOrigin:totalSpaceNeeded:), webOrigin, static_cast<NSUInteger>(totalSpaceNeeded));
665     [webOrigin release];
666
667     END_BLOCK_OBJC_EXCEPTIONS;
668 }
669
670 void WebChromeClient::populateVisitedLinks()
671 {
672     if ([m_webView historyDelegate]) {
673         WebHistoryDelegateImplementationCache* implementations = WebViewGetHistoryDelegateImplementations(m_webView);
674         
675         if (implementations->populateVisitedLinksFunc)
676             CallHistoryDelegate(implementations->populateVisitedLinksFunc, m_webView, @selector(populateVisitedLinksForWebView:));
677
678         return;
679     }
680
681     BEGIN_BLOCK_OBJC_EXCEPTIONS;
682     [[WebHistory optionalSharedHistory] _addVisitedLinksToPageGroup:[m_webView page]->group()];
683     END_BLOCK_OBJC_EXCEPTIONS;
684 }
685
686 #if ENABLE(DASHBOARD_SUPPORT)
687
688 void WebChromeClient::annotatedRegionsChanged()
689 {
690     BEGIN_BLOCK_OBJC_EXCEPTIONS;
691     CallUIDelegate(m_webView, @selector(webView:dashboardRegionsChanged:), [m_webView _dashboardRegions]);
692     END_BLOCK_OBJC_EXCEPTIONS;
693 }
694
695 #endif
696
697 FloatRect WebChromeClient::customHighlightRect(Node* node, const AtomicString& type, const FloatRect& lineRect)
698 {
699     BEGIN_BLOCK_OBJC_EXCEPTIONS;
700
701     NSView *documentView = [[kit(node->document().frame()) frameView] documentView];
702     if (![documentView isKindOfClass:[WebHTMLView class]])
703         return NSZeroRect;
704
705     WebHTMLView *webHTMLView = (WebHTMLView *)documentView;
706     id<WebHTMLHighlighter> highlighter = [webHTMLView _highlighterForType:type];
707     return [highlighter highlightRectForLine:lineRect representedNode:kit(node)];
708
709     END_BLOCK_OBJC_EXCEPTIONS;
710
711     return NSZeroRect;
712 }
713
714 void WebChromeClient::paintCustomHighlight(Node* node, const AtomicString& type, const FloatRect& boxRect, const FloatRect& lineRect,
715     bool behindText, bool entireLine)
716 {
717     BEGIN_BLOCK_OBJC_EXCEPTIONS;
718
719     NSView *documentView = [[kit(node->document().frame()) frameView] documentView];
720     if (![documentView isKindOfClass:[WebHTMLView class]])
721         return;
722
723     WebHTMLView *webHTMLView = (WebHTMLView *)documentView;
724     id<WebHTMLHighlighter> highlighter = [webHTMLView _highlighterForType:type];
725     [highlighter paintHighlightForBox:boxRect onLine:lineRect behindText:behindText entireLine:entireLine representedNode:kit(node)];
726
727     END_BLOCK_OBJC_EXCEPTIONS;
728 }
729
730 #if ENABLE(INPUT_TYPE_COLOR)
731 PassOwnPtr<ColorChooser> WebChromeClient::createColorChooser(ColorChooserClient* client, const Color& initialColor)
732 {
733     // FIXME: Implement <input type='color'> for WK1 (Bug 119094).
734     ASSERT_NOT_REACHED();
735     return nullptr;
736 }
737 #endif
738
739 void WebChromeClient::runOpenPanel(Frame*, PassRefPtr<FileChooser> chooser)
740 {
741     BEGIN_BLOCK_OBJC_EXCEPTIONS;
742     BOOL allowMultipleFiles = chooser->settings().allowsMultipleFiles;
743     WebOpenPanelResultListener *listener = [[WebOpenPanelResultListener alloc] initWithChooser:chooser];
744     id delegate = [m_webView UIDelegate];
745     if ([delegate respondsToSelector:@selector(webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:)])
746         CallUIDelegate(m_webView, @selector(webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:), listener, allowMultipleFiles);
747     else if ([delegate respondsToSelector:@selector(webView:runOpenPanelForFileButtonWithResultListener:)])
748         CallUIDelegate(m_webView, @selector(webView:runOpenPanelForFileButtonWithResultListener:), listener);
749     else
750         [listener cancel];
751     [listener release];
752     END_BLOCK_OBJC_EXCEPTIONS;
753 }
754
755 void WebChromeClient::loadIconForFiles(const Vector<String>& filenames, FileIconLoader* iconLoader)
756 {
757     iconLoader->notifyFinished(Icon::createIconForFiles(filenames));
758 }
759
760 void WebChromeClient::setCursor(const WebCore::Cursor& cursor)
761 {
762     if ([NSApp _cursorRectCursor])
763         return;
764
765     NSCursor *platformCursor = cursor.platformCursor();
766     if ([NSCursor currentCursor] == platformCursor)
767         return;
768     [platformCursor set];
769 }
770
771 void WebChromeClient::setCursorHiddenUntilMouseMoves(bool hiddenUntilMouseMoves)
772 {
773     [NSCursor setHiddenUntilMouseMoves:hiddenUntilMouseMoves];
774 }
775
776 KeyboardUIMode WebChromeClient::keyboardUIMode()
777 {
778     BEGIN_BLOCK_OBJC_EXCEPTIONS;
779     return [m_webView _keyboardUIMode];
780     END_BLOCK_OBJC_EXCEPTIONS;
781     return KeyboardAccessDefault;
782 }
783
784 NSResponder *WebChromeClient::firstResponder()
785 {
786     BEGIN_BLOCK_OBJC_EXCEPTIONS;
787     return [[m_webView _UIDelegateForwarder] webViewFirstResponder:m_webView];
788     END_BLOCK_OBJC_EXCEPTIONS;
789     return nil;
790 }
791
792 void WebChromeClient::makeFirstResponder(NSResponder *responder)
793 {
794     BEGIN_BLOCK_OBJC_EXCEPTIONS;
795     [m_webView _pushPerformingProgrammaticFocus];
796     [[m_webView _UIDelegateForwarder] webView:m_webView makeFirstResponder:responder];
797     [m_webView _popPerformingProgrammaticFocus];
798     END_BLOCK_OBJC_EXCEPTIONS;
799 }
800
801 void WebChromeClient::enableSuddenTermination()
802 {
803     [[NSProcessInfo processInfo] enableSuddenTermination];
804 }
805
806 void WebChromeClient::disableSuddenTermination()
807 {
808     [[NSProcessInfo processInfo] disableSuddenTermination];
809 }
810
811 bool WebChromeClient::shouldReplaceWithGeneratedFileForUpload(const String& path, String& generatedFilename)
812 {
813     NSString* filename;
814     if (![[m_webView _UIDelegateForwarder] webView:m_webView shouldReplaceUploadFile:path usingGeneratedFilename:&filename])
815         return false;
816     generatedFilename = filename;
817     return true;
818 }
819
820 String WebChromeClient::generateReplacementFile(const String& path)
821 {
822     return [[m_webView _UIDelegateForwarder] webView:m_webView generateReplacementFile:path];
823 }
824
825 void WebChromeClient::elementDidFocus(const WebCore::Node* node)
826 {
827     CallUIDelegate(m_webView, @selector(webView:formDidFocusNode:), kit(const_cast<WebCore::Node*>(node)));
828 }
829
830 void WebChromeClient::elementDidBlur(const WebCore::Node* node)
831 {
832     CallUIDelegate(m_webView, @selector(webView:formDidBlurNode:), kit(const_cast<WebCore::Node*>(node)));
833 }
834
835 bool WebChromeClient::selectItemWritingDirectionIsNatural()
836 {
837     return false;
838 }
839
840 bool WebChromeClient::selectItemAlignmentFollowsMenuWritingDirection()
841 {
842     return true;
843 }
844
845 bool WebChromeClient::hasOpenedPopup() const
846 {
847     notImplemented();
848     return false;
849 }
850
851 PassRefPtr<WebCore::PopupMenu> WebChromeClient::createPopupMenu(WebCore::PopupMenuClient* client) const
852 {
853     return adoptRef(new PopupMenuMac(client));
854 }
855
856 PassRefPtr<WebCore::SearchPopupMenu> WebChromeClient::createSearchPopupMenu(WebCore::PopupMenuClient* client) const
857 {
858     return adoptRef(new SearchPopupMenuMac(client));
859 }
860
861 bool WebChromeClient::shouldPaintEntireContents() const
862 {
863     NSView *documentView = [[[m_webView mainFrame] frameView] documentView];
864     return [documentView layer];
865 }
866
867 #if USE(ACCELERATED_COMPOSITING)
868
869 void WebChromeClient::attachRootGraphicsLayer(Frame* frame, GraphicsLayer* graphicsLayer)
870 {
871     BEGIN_BLOCK_OBJC_EXCEPTIONS;
872
873     NSView *documentView = [[kit(frame) frameView] documentView];
874     if (![documentView isKindOfClass:[WebHTMLView class]]) {
875         // We should never be attaching when we don't have a WebHTMLView.
876         ASSERT(!graphicsLayer);
877         return;
878     }
879
880     WebHTMLView *webHTMLView = (WebHTMLView *)documentView;
881     if (graphicsLayer)
882         [webHTMLView attachRootLayer:graphicsLayer->platformLayer()];
883     else
884         [webHTMLView detachRootLayer];
885     END_BLOCK_OBJC_EXCEPTIONS;
886 }
887
888 void WebChromeClient::setNeedsOneShotDrawingSynchronization()
889 {
890     BEGIN_BLOCK_OBJC_EXCEPTIONS;
891     [m_webView _setNeedsOneShotDrawingSynchronization:YES];
892     END_BLOCK_OBJC_EXCEPTIONS;
893 }
894
895 void WebChromeClient::scheduleCompositingLayerFlush()
896 {
897     BEGIN_BLOCK_OBJC_EXCEPTIONS;
898     [m_webView _scheduleCompositingLayerFlush];
899     END_BLOCK_OBJC_EXCEPTIONS;
900 }
901
902 #endif
903
904 #if ENABLE(VIDEO)
905
906 bool WebChromeClient::supportsFullscreenForNode(const Node* node)
907 {
908     return isHTMLVideoElement(node);
909 }
910
911 void WebChromeClient::enterFullscreenForNode(Node* node)
912 {
913     BEGIN_BLOCK_OBJC_EXCEPTIONS;
914     [m_webView _enterFullscreenForNode:node];
915     END_BLOCK_OBJC_EXCEPTIONS;
916 }
917
918 void WebChromeClient::exitFullscreenForNode(Node*)
919 {
920     BEGIN_BLOCK_OBJC_EXCEPTIONS;
921     [m_webView _exitFullscreen];
922     END_BLOCK_OBJC_EXCEPTIONS;    
923 }
924
925 #endif
926
927 #if ENABLE(FULLSCREEN_API)
928
929 bool WebChromeClient::supportsFullScreenForElement(const Element* element, bool withKeyboard)
930 {
931     SEL selector = @selector(webView:supportsFullScreenForElement:withKeyboard:);
932     if ([[m_webView UIDelegate] respondsToSelector:selector])
933         return CallUIDelegateReturningBoolean(false, m_webView, selector, kit(const_cast<WebCore::Element*>(element)), withKeyboard);
934     return [m_webView _supportsFullScreenForElement:const_cast<WebCore::Element*>(element) withKeyboard:withKeyboard];
935 }
936
937 void WebChromeClient::enterFullScreenForElement(Element* element)
938 {
939     SEL selector = @selector(webView:enterFullScreenForElement:listener:);
940     if ([[m_webView UIDelegate] respondsToSelector:selector]) {
941         WebKitFullScreenListener* listener = [[WebKitFullScreenListener alloc] initWithElement:element];
942         CallUIDelegate(m_webView, selector, kit(element), listener);
943         [listener release];
944     } else
945         [m_webView _enterFullScreenForElement:element];
946 }
947
948 void WebChromeClient::exitFullScreenForElement(Element* element)
949 {
950     SEL selector = @selector(webView:exitFullScreenForElement:listener:);
951     if ([[m_webView UIDelegate] respondsToSelector:selector]) {
952         WebKitFullScreenListener* listener = [[WebKitFullScreenListener alloc] initWithElement:element];
953         CallUIDelegate(m_webView, selector, kit(element), listener);
954         [listener release];
955     } else
956         [m_webView _exitFullScreenForElement:element];
957 }
958
959 void WebChromeClient::fullScreenRendererChanged(RenderBox* renderer)
960 {
961     SEL selector = @selector(webView:fullScreenRendererChanged:);
962     if ([[m_webView UIDelegate] respondsToSelector:selector])
963         CallUIDelegate(m_webView, selector, (id)renderer);
964 }
965
966 #endif