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