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
102NSString *WebConsoleMessageXMLMessageSource = @"XMLMessageSource";
103NSString *WebConsoleMessageJSMessageSource = @"JSMessageSource";
104NSString *WebConsoleMessageNetworkMessageSource = @"NetworkMessageSource";
105NSString *WebConsoleMessageConsoleAPIMessageSource = @"ConsoleAPIMessageSource";
106NSString *WebConsoleMessageStorageMessageSource = @"StorageMessageSource";
107NSString *WebConsoleMessageAppCacheMessageSource = @"AppCacheMessageSource";
108NSString *WebConsoleMessageRenderingMessageSource = @"RenderingMessageSource";
109NSString *WebConsoleMessageCSSMessageSource = @"CSSMessageSource";
110NSString *WebConsoleMessageSecurityMessageSource = @"SecurityMessageSource";
111NSString *WebConsoleMessageOtherMessageSource = @"OtherMessageSource";
112
113NSString *WebConsoleMessageDebugMessageLevel = @"DebugMessageLevel";
114NSString *WebConsoleMessageLogMessageLevel = @"LogMessageLevel";
115NSString *WebConsoleMessageWarningMessageLevel = @"WarningMessageLevel";
116NSString *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
140using namespace WebCore;
141using namespace HTMLNames;
142
143WebChromeClient::WebChromeClient(WebView *webView)
144    : m_webView(webView)
145{
146}
147
148void 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
156void 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
164FloatRect 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.
175FloatRect WebChromeClient::pageRect()
176{
177    return [m_webView frame];
178}
179
180void WebChromeClient::focus()
181{
182    [[m_webView _UIDelegateForwarder] webViewFocus:m_webView];
183}
184
185void WebChromeClient::unfocus()
186{
187    [[m_webView _UIDelegateForwarder] webViewUnfocus:m_webView];
188}
189
190bool 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
197void 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
219void 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
233void WebChromeClient::focusedFrameChanged(Frame*)
234{
235}
236
237Page* 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
307void WebChromeClient::show()
308{
309    [[m_webView _UIDelegateForwarder] webViewShow:m_webView];
310}
311
312bool WebChromeClient::canRunModal()
313{
314    return [[m_webView UIDelegate] respondsToSelector:@selector(webViewRunModal:)];
315}
316
317void WebChromeClient::runModal()
318{
319    CallUIDelegate(m_webView, @selector(webViewRunModal:));
320}
321
322void WebChromeClient::setToolbarsVisible(bool b)
323{
324    [[m_webView _UIDelegateForwarder] webView:m_webView setToolbarsVisible:b];
325}
326
327bool WebChromeClient::toolbarsVisible()
328{
329    return CallUIDelegateReturningBoolean(NO, m_webView, @selector(webViewAreToolbarsVisible:));
330}
331
332void WebChromeClient::setStatusbarVisible(bool b)
333{
334    [[m_webView _UIDelegateForwarder] webView:m_webView setStatusBarVisible:b];
335}
336
337bool WebChromeClient::statusbarVisible()
338{
339    return CallUIDelegateReturningBoolean(NO, m_webView, @selector(webViewIsStatusBarVisible:));
340}
341
342void WebChromeClient::setScrollbarsVisible(bool b)
343{
344    [[[m_webView mainFrame] frameView] setAllowsScrolling:b];
345}
346
347bool WebChromeClient::scrollbarsVisible()
348{
349    return [[[m_webView mainFrame] frameView] allowsScrolling];
350}
351
352void WebChromeClient::setMenubarVisible(bool)
353{
354    // The menubar is always visible in Mac OS X.
355    return;
356}
357
358bool WebChromeClient::menubarVisible()
359{
360    // The menubar is always visible in Mac OS X.
361    return true;
362}
363
364void WebChromeClient::setResizable(bool b)
365{
366    [[m_webView _UIDelegateForwarder] webView:m_webView setResizable:b];
367}
368
369inline 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
397inline 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
413void 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
463bool WebChromeClient::canRunBeforeUnloadConfirmPanel()
464{
465    return [[m_webView UIDelegate] respondsToSelector:@selector(webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:)];
466}
467
468bool WebChromeClient::runBeforeUnloadConfirmPanel(const String& message, Frame* frame)
469{
470    return CallUIDelegateReturningBoolean(true, m_webView, @selector(webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:), message, kit(frame));
471}
472
473void 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
493void 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
510bool 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
525bool 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
546bool WebChromeClient::shouldInterruptJavaScript()
547{
548    return CallUIDelegateReturningBoolean(NO, m_webView, @selector(webViewShouldInterruptJavaScript:));
549}
550
551void 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
560IntRect WebChromeClient::windowResizerRect() const
561{
562#if !PLATFORM(IOS)
563    return enclosingIntRect([[m_webView window] _growBoxRect]);
564#else
565    return IntRect();
566#endif
567}
568
569bool WebChromeClient::supportsImmediateInvalidation()
570{
571    return true;
572}
573
574void WebChromeClient::invalidateRootView(const IntRect&)
575{
576}
577
578void WebChromeClient::invalidateContentsAndRootView(const IntRect& rect)
579{
580}
581
582void WebChromeClient::invalidateContentsForSlowScroll(const IntRect& rect)
583{
584    invalidateContentsAndRootView(rect);
585}
586
587void WebChromeClient::scroll(const IntSize&, const IntRect&, const IntRect&)
588{
589}
590
591IntPoint WebChromeClient::screenToRootView(const IntPoint& p) const
592{
593    // FIXME: Implement this.
594    return p;
595}
596
597IntRect WebChromeClient::rootViewToScreen(const IntRect& r) const
598{
599    // FIXME: Implement this.
600    return r;
601}
602
603#if PLATFORM(IOS)
604IntPoint WebChromeClient::accessibilityScreenToRootView(const IntPoint& p) const
605{
606    return p;
607}
608
609IntRect WebChromeClient::rootViewToAccessibilityScreen(const IntRect& r) const
610{
611    return r;
612}
613#endif
614
615PlatformPageClient WebChromeClient::platformPageClient() const
616{
617    return 0;
618}
619
620void WebChromeClient::contentsSizeChanged(Frame*, const IntSize&) const
621{
622}
623
624void WebChromeClient::scrollRectIntoView(const IntRect& r) const
625{
626    // FIXME: This scrolling behavior should be under the control of the embedding client,
627    // perhaps in a delegate method, rather than something WebKit does unconditionally.
628    NSView *coordinateView = [[[m_webView mainFrame] frameView] documentView];
629    NSRect rect = r;
630    for (NSView *view = m_webView; view; view = [view superview]) {
631        if ([view isKindOfClass:[NSClipView class]]) {
632            NSClipView *clipView = (NSClipView *)view;
633            NSView *documentView = [clipView documentView];
634            [documentView scrollRectToVisible:[documentView convertRect:rect fromView:coordinateView]];
635        }
636    }
637}
638
639// End host window methods.
640
641bool WebChromeClient::shouldUnavailablePluginMessageBeButton(RenderEmbeddedObject::PluginUnavailabilityReason pluginUnavailabilityReason) const
642{
643    if (pluginUnavailabilityReason == RenderEmbeddedObject::PluginMissing)
644        return [[m_webView UIDelegate] respondsToSelector:@selector(webView:didPressMissingPluginButton:)];
645
646    return false;
647}
648
649void WebChromeClient::unavailablePluginButtonClicked(Element* element, RenderEmbeddedObject::PluginUnavailabilityReason pluginUnavailabilityReason) const
650{
651    ASSERT(element->hasTagName(objectTag) || element->hasTagName(embedTag) || element->hasTagName(appletTag));
652
653    ASSERT(pluginUnavailabilityReason == RenderEmbeddedObject::PluginMissing);
654    CallUIDelegate(m_webView, @selector(webView:didPressMissingPluginButton:), kit(element));
655}
656
657void WebChromeClient::mouseDidMoveOverElement(const HitTestResult& result, unsigned modifierFlags)
658{
659    WebElementDictionary *element = [[WebElementDictionary alloc] initWithHitTestResult:result];
660    [m_webView _mouseDidMoveOverElement:element modifierFlags:modifierFlags];
661    [element release];
662}
663
664void WebChromeClient::setToolTip(const String& toolTip, TextDirection)
665{
666    NSView<WebDocumentView> *documentView = [[[m_webView _selectedOrMainFrame] frameView] documentView];
667    if ([documentView isKindOfClass:[WebHTMLView class]])
668        [(WebHTMLView *)documentView _setToolTip:toolTip];
669}
670
671void WebChromeClient::print(Frame* frame)
672{
673    WebFrame *webFrame = kit(frame);
674    if ([[m_webView UIDelegate] respondsToSelector:@selector(webView:printFrame:)])
675        CallUIDelegate(m_webView, @selector(webView:printFrame:), webFrame);
676    else
677        CallUIDelegate(m_webView, @selector(webView:printFrameView:), [webFrame frameView]);
678}
679
680#if ENABLE(SQL_DATABASE)
681
682void WebChromeClient::exceededDatabaseQuota(Frame* frame, const String& databaseName, DatabaseDetails)
683{
684    BEGIN_BLOCK_OBJC_EXCEPTIONS;
685
686    WebSecurityOrigin *webOrigin = [[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:frame->document()->securityOrigin()];
687    CallUIDelegate(m_webView, @selector(webView:frame:exceededDatabaseQuotaForSecurityOrigin:database:), kit(frame), webOrigin, (NSString *)databaseName);
688    [webOrigin release];
689
690    END_BLOCK_OBJC_EXCEPTIONS;
691}
692
693#endif
694
695void WebChromeClient::reachedMaxAppCacheSize(int64_t spaceNeeded)
696{
697    // FIXME: Free some space.
698}
699
700void WebChromeClient::reachedApplicationCacheOriginQuota(SecurityOrigin* origin, int64_t totalSpaceNeeded)
701{
702    BEGIN_BLOCK_OBJC_EXCEPTIONS;
703
704    WebSecurityOrigin *webOrigin = [[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:origin];
705    CallUIDelegate(m_webView, @selector(webView:exceededApplicationCacheOriginQuotaForSecurityOrigin:totalSpaceNeeded:), webOrigin, static_cast<NSUInteger>(totalSpaceNeeded));
706    [webOrigin release];
707
708    END_BLOCK_OBJC_EXCEPTIONS;
709}
710
711void WebChromeClient::populateVisitedLinks()
712{
713    if ([m_webView historyDelegate]) {
714        WebHistoryDelegateImplementationCache* implementations = WebViewGetHistoryDelegateImplementations(m_webView);
715
716        if (implementations->populateVisitedLinksFunc)
717            CallHistoryDelegate(implementations->populateVisitedLinksFunc, m_webView, @selector(populateVisitedLinksForWebView:));
718
719        return;
720    }
721
722    BEGIN_BLOCK_OBJC_EXCEPTIONS;
723    [[WebHistory optionalSharedHistory] _addVisitedLinksToPageGroup:[m_webView page]->group()];
724    END_BLOCK_OBJC_EXCEPTIONS;
725}
726
727#if ENABLE(DASHBOARD_SUPPORT)
728
729void WebChromeClient::annotatedRegionsChanged()
730{
731    BEGIN_BLOCK_OBJC_EXCEPTIONS;
732    CallUIDelegate(m_webView, @selector(webView:dashboardRegionsChanged:), [m_webView _dashboardRegions]);
733    END_BLOCK_OBJC_EXCEPTIONS;
734}
735
736#endif
737
738#if ENABLE(INPUT_TYPE_COLOR)
739PassOwnPtr<ColorChooser> WebChromeClient::createColorChooser(ColorChooserClient* client, const Color& initialColor)
740{
741    // FIXME: Implement <input type='color'> for WK1 (Bug 119094).
742    ASSERT_NOT_REACHED();
743    return nullptr;
744}
745#endif
746
747void WebChromeClient::runOpenPanel(Frame*, PassRefPtr<FileChooser> chooser)
748{
749    BEGIN_BLOCK_OBJC_EXCEPTIONS;
750    BOOL allowMultipleFiles = chooser->settings().allowsMultipleFiles;
751    WebOpenPanelResultListener *listener = [[WebOpenPanelResultListener alloc] initWithChooser:chooser];
752    id delegate = [m_webView UIDelegate];
753    if ([delegate respondsToSelector:@selector(webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:)])
754        CallUIDelegate(m_webView, @selector(webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:), listener, allowMultipleFiles);
755    else if ([delegate respondsToSelector:@selector(webView:runOpenPanelForFileButtonWithResultListener:)])
756        CallUIDelegate(m_webView, @selector(webView:runOpenPanelForFileButtonWithResultListener:), listener);
757    else
758        [listener cancel];
759    [listener release];
760    END_BLOCK_OBJC_EXCEPTIONS;
761}
762
763void WebChromeClient::loadIconForFiles(const Vector<String>& filenames, FileIconLoader* iconLoader)
764{
765    iconLoader->notifyFinished(Icon::createIconForFiles(filenames));
766}
767
768#if !PLATFORM(IOS)
769
770void WebChromeClient::setCursor(const WebCore::Cursor& cursor)
771{
772    // FIXME: Would be nice to share this code with WebKit2's PageClientImpl.
773
774    if ([NSApp _cursorRectCursor])
775        return;
776
777    if (!m_webView)
778        return;
779
780    NSWindow *window = [m_webView window];
781    if (!window)
782        return;
783
784    if ([window windowNumber] != [NSWindow windowNumberAtPoint:[NSEvent mouseLocation] belowWindowWithWindowNumber:0])
785        return;
786
787    NSCursor *platformCursor = cursor.platformCursor();
788    if ([NSCursor currentCursor] == platformCursor)
789        return;
790
791    [platformCursor set];
792}
793
794void WebChromeClient::setCursorHiddenUntilMouseMoves(bool hiddenUntilMouseMoves)
795{
796    [NSCursor setHiddenUntilMouseMoves:hiddenUntilMouseMoves];
797}
798
799#endif
800
801KeyboardUIMode WebChromeClient::keyboardUIMode()
802{
803    BEGIN_BLOCK_OBJC_EXCEPTIONS;
804    return [m_webView _keyboardUIMode];
805    END_BLOCK_OBJC_EXCEPTIONS;
806    return KeyboardAccessDefault;
807}
808
809NSResponder *WebChromeClient::firstResponder()
810{
811    BEGIN_BLOCK_OBJC_EXCEPTIONS;
812    return [[m_webView _UIDelegateForwarder] webViewFirstResponder:m_webView];
813    END_BLOCK_OBJC_EXCEPTIONS;
814    return nil;
815}
816
817void WebChromeClient::makeFirstResponder(NSResponder *responder)
818{
819    BEGIN_BLOCK_OBJC_EXCEPTIONS;
820    [m_webView _pushPerformingProgrammaticFocus];
821    [[m_webView _UIDelegateForwarder] webView:m_webView makeFirstResponder:responder];
822    [m_webView _popPerformingProgrammaticFocus];
823    END_BLOCK_OBJC_EXCEPTIONS;
824}
825
826void WebChromeClient::enableSuddenTermination()
827{
828#if !PLATFORM(IOS)
829    [[NSProcessInfo processInfo] enableSuddenTermination];
830#endif
831}
832
833void WebChromeClient::disableSuddenTermination()
834{
835#if !PLATFORM(IOS)
836    [[NSProcessInfo processInfo] disableSuddenTermination];
837#endif
838}
839
840bool WebChromeClient::shouldReplaceWithGeneratedFileForUpload(const String& path, String& generatedFilename)
841{
842    NSString* filename;
843    if (![[m_webView _UIDelegateForwarder] webView:m_webView shouldReplaceUploadFile:path usingGeneratedFilename:&filename])
844        return false;
845    generatedFilename = filename;
846    return true;
847}
848
849String WebChromeClient::generateReplacementFile(const String& path)
850{
851    return [[m_webView _UIDelegateForwarder] webView:m_webView generateReplacementFile:path];
852}
853
854void WebChromeClient::elementDidFocus(const WebCore::Node* node)
855{
856    CallUIDelegate(m_webView, @selector(webView:formDidFocusNode:), kit(const_cast<WebCore::Node*>(node)));
857}
858
859void WebChromeClient::elementDidBlur(const WebCore::Node* node)
860{
861    CallUIDelegate(m_webView, @selector(webView:formDidBlurNode:), kit(const_cast<WebCore::Node*>(node)));
862}
863
864bool WebChromeClient::selectItemWritingDirectionIsNatural()
865{
866    return false;
867}
868
869bool WebChromeClient::selectItemAlignmentFollowsMenuWritingDirection()
870{
871    return true;
872}
873
874bool WebChromeClient::hasOpenedPopup() const
875{
876    notImplemented();
877    return false;
878}
879
880PassRefPtr<WebCore::PopupMenu> WebChromeClient::createPopupMenu(WebCore::PopupMenuClient* client) const
881{
882#if !PLATFORM(IOS)
883    return adoptRef(new PopupMenuMac(client));
884#else
885    return nullptr;
886#endif
887}
888
889PassRefPtr<WebCore::SearchPopupMenu> WebChromeClient::createSearchPopupMenu(WebCore::PopupMenuClient* client) const
890{
891#if !PLATFORM(IOS)
892    return adoptRef(new SearchPopupMenuMac(client));
893#else
894    return nullptr;
895#endif
896}
897
898bool WebChromeClient::shouldPaintEntireContents() const
899{
900#if PLATFORM(IOS)
901    return false;
902#else
903    NSView *documentView = [[[m_webView mainFrame] frameView] documentView];
904    return [documentView layer];
905#endif
906}
907
908void WebChromeClient::attachRootGraphicsLayer(Frame* frame, GraphicsLayer* graphicsLayer)
909{
910    BEGIN_BLOCK_OBJC_EXCEPTIONS;
911
912    NSView *documentView = [[kit(frame) frameView] documentView];
913    if (![documentView isKindOfClass:[WebHTMLView class]]) {
914        // We should never be attaching when we don't have a WebHTMLView.
915        ASSERT(!graphicsLayer);
916        return;
917    }
918
919    WebHTMLView *webHTMLView = (WebHTMLView *)documentView;
920    if (graphicsLayer)
921        [webHTMLView attachRootLayer:graphicsLayer->platformLayer()];
922    else
923        [webHTMLView detachRootLayer];
924    END_BLOCK_OBJC_EXCEPTIONS;
925}
926
927void WebChromeClient::setNeedsOneShotDrawingSynchronization()
928{
929    BEGIN_BLOCK_OBJC_EXCEPTIONS;
930    [m_webView _setNeedsOneShotDrawingSynchronization:YES];
931    END_BLOCK_OBJC_EXCEPTIONS;
932}
933
934void WebChromeClient::scheduleCompositingLayerFlush()
935{
936    BEGIN_BLOCK_OBJC_EXCEPTIONS;
937    [m_webView _scheduleCompositingLayerFlush];
938    END_BLOCK_OBJC_EXCEPTIONS;
939}
940
941#if ENABLE(VIDEO)
942
943bool WebChromeClient::supportsFullscreenForNode(const Node* node)
944{
945#if PLATFORM(IOS)
946    if (!Settings::avKitEnabled())
947        return false;
948#endif
949    return isHTMLVideoElement(node);
950}
951
952void WebChromeClient::enterFullscreenForNode(Node* node)
953{
954    BEGIN_BLOCK_OBJC_EXCEPTIONS;
955    [m_webView _enterFullscreenForNode:node];
956    END_BLOCK_OBJC_EXCEPTIONS;
957}
958
959void WebChromeClient::exitFullscreenForNode(Node*)
960{
961    BEGIN_BLOCK_OBJC_EXCEPTIONS;
962    [m_webView _exitFullscreen];
963    END_BLOCK_OBJC_EXCEPTIONS;
964}
965
966#endif
967
968#if ENABLE(FULLSCREEN_API)
969
970bool WebChromeClient::supportsFullScreenForElement(const Element* element, bool withKeyboard)
971{
972    SEL selector = @selector(webView:supportsFullScreenForElement:withKeyboard:);
973    if ([[m_webView UIDelegate] respondsToSelector:selector])
974        return CallUIDelegateReturningBoolean(false, m_webView, selector, kit(const_cast<WebCore::Element*>(element)), withKeyboard);
975#if !PLATFORM(IOS)
976    return [m_webView _supportsFullScreenForElement:const_cast<WebCore::Element*>(element) withKeyboard:withKeyboard];
977#else
978    return NO;
979#endif
980}
981
982void WebChromeClient::enterFullScreenForElement(Element* element)
983{
984    SEL selector = @selector(webView:enterFullScreenForElement:listener:);
985    if ([[m_webView UIDelegate] respondsToSelector:selector]) {
986        WebKitFullScreenListener* listener = [[WebKitFullScreenListener alloc] initWithElement:element];
987        CallUIDelegate(m_webView, selector, kit(element), listener);
988        [listener release];
989    }
990#if !PLATFORM(IOS)
991    else
992        [m_webView _enterFullScreenForElement:element];
993#endif
994}
995
996void WebChromeClient::exitFullScreenForElement(Element* element)
997{
998    SEL selector = @selector(webView:exitFullScreenForElement:listener:);
999    if ([[m_webView UIDelegate] respondsToSelector:selector]) {
1000        WebKitFullScreenListener* listener = [[WebKitFullScreenListener alloc] initWithElement:element];
1001        CallUIDelegate(m_webView, selector, kit(element), listener);
1002        [listener release];
1003    }
1004#if !PLATFORM(IOS)
1005    else
1006        [m_webView _exitFullScreenForElement:element];
1007#endif
1008}
1009
1010#endif // ENABLE(FULLSCREEN_API)
1011
1012#if ENABLE(SUBTLE_CRYPTO)
1013bool WebChromeClient::wrapCryptoKey(const Vector<uint8_t>& key, Vector<uint8_t>& wrappedKey) const
1014{
1015    Vector<uint8_t> masterKey;
1016    SEL selector = @selector(webCryptoMasterKeyForWebView:);
1017    if ([[m_webView UIDelegate] respondsToSelector:selector]) {
1018        NSData *keyData = CallUIDelegate(m_webView, selector);
1019        masterKey.append((uint8_t*)[keyData bytes], [keyData length]);
1020    } else if (!getDefaultWebCryptoMasterKey(masterKey))
1021        return false;
1022
1023    return wrapSerializedCryptoKey(masterKey, key, wrappedKey);
1024}
1025
1026bool WebChromeClient::unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key) const
1027{
1028    Vector<uint8_t> masterKey;
1029    SEL selector = @selector(webCryptoMasterKeyForWebView:);
1030    if ([[m_webView UIDelegate] respondsToSelector:selector]) {
1031        NSData *keyData = CallUIDelegate(m_webView, selector);
1032        masterKey.append((uint8_t*)[keyData bytes], [keyData length]);
1033    } else if (!getDefaultWebCryptoMasterKey(masterKey))
1034        return false;
1035
1036    return unwrapSerializedCryptoKey(masterKey, wrappedKey, key);
1037}
1038#endif
1039