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 "WebDefaultUIDelegate.h" 35#import "WebDelegateImplementationCaching.h" 36#import "WebElementDictionary.h" 37#import "WebFrameInternal.h" 38#import "WebFrameView.h" 39#import "WebHTMLViewInternal.h" 40#import "WebHistoryInternal.h" 41#import "WebKitPrefix.h" 42#import "WebKitSystemInterface.h" 43#import "WebNSURLRequestExtras.h" 44#import "WebPlugin.h" 45#import "WebQuotaManager.h" 46#import "WebSecurityOriginInternal.h" 47#import "WebUIDelegatePrivate.h" 48#import "WebView.h" 49#import "WebViewInternal.h" 50#import <Foundation/Foundation.h> 51#import <WebCore/BlockExceptions.h> 52#import <WebCore/Console.h> 53#import <WebCore/Cursor.h> 54#import <WebCore/ContextMenu.h> 55#import <WebCore/ContextMenuController.h> 56#import <WebCore/Element.h> 57#import <WebCore/FileChooser.h> 58#import <WebCore/FloatRect.h> 59#import <WebCore/Frame.h> 60#import <WebCore/FrameLoadRequest.h> 61#import <WebCore/FrameView.h> 62#import <WebCore/HTMLNames.h> 63#import <WebCore/HitTestResult.h> 64#import <WebCore/Icon.h> 65#import <WebCore/IntPoint.h> 66#import <WebCore/IntRect.h> 67#import <WebCore/NavigationAction.h> 68#import <WebCore/Page.h> 69#import <WebCore/PlatformScreen.h> 70#import <WebCore/PlatformString.h> 71#import <WebCore/PopupMenuMac.h> 72#import <WebCore/ResourceRequest.h> 73#import <WebCore/SearchPopupMenuMac.h> 74#import <WebCore/Widget.h> 75#import <WebCore/WindowFeatures.h> 76#import <wtf/PassRefPtr.h> 77#import <wtf/Vector.h> 78 79#if USE(ACCELERATED_COMPOSITING) 80#import <WebCore/GraphicsLayer.h> 81#endif 82 83#if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) 84#import "NetscapePluginHostManager.h" 85#endif 86 87NSString *WebConsoleMessageHTMLMessageSource = @"HTMLMessageSource"; 88NSString *WebConsoleMessageWMLMessageSource = @"WMLMessageSource"; 89NSString *WebConsoleMessageXMLMessageSource = @"XMLMessageSource"; 90NSString *WebConsoleMessageJSMessageSource = @"JSMessageSource"; 91NSString *WebConsoleMessageCSSMessageSource = @"CSSMessageSource"; 92NSString *WebConsoleMessageOtherMessageSource = @"OtherMessageSource"; 93 94NSString *WebConsoleMessageLogMessageType = @"LogMessageType"; 95NSString *WebConsoleMessageObjectMessageType = @"ObjectMessageType"; 96NSString *WebConsoleMessageTraceMessageType = @"TraceMessageType"; 97NSString *WebConsoleMessageStartGroupMessageType = @"StartGroupMessageType"; 98NSString *WebConsoleMessageStartGroupCollapsedMessageType = @"StartGroupCollapsedMessageType"; 99NSString *WebConsoleMessageEndGroupMessageType = @"EndGroupMessageType"; 100NSString *WebConsoleMessageAssertMessageType = @"AssertMessageType"; 101NSString *WebConsoleMessageUncaughtExceptionMessageType = @"UncaughtExceptionMessageType"; 102NSString *WebConsoleMessageNetworkErrorMessageType = @"NetworkErrorMessageType"; 103 104NSString *WebConsoleMessageTipMessageLevel = @"TipMessageLevel"; 105NSString *WebConsoleMessageLogMessageLevel = @"LogMessageLevel"; 106NSString *WebConsoleMessageWarningMessageLevel = @"WarningMessageLevel"; 107NSString *WebConsoleMessageErrorMessageLevel = @"ErrorMessageLevel"; 108NSString *WebConsoleMessageDebugMessageLevel = @"DebugMessageLevel"; 109 110@interface NSApplication (WebNSApplicationDetails) 111- (NSCursor *)_cursorRectCursor; 112@end 113 114@interface NSView (WebNSViewDetails) 115- (NSView *)_findLastViewInKeyViewLoop; 116@end 117 118// For compatibility with old SPI. 119@interface NSView (WebOldWebKitPlugInDetails) 120- (void)setIsSelected:(BOOL)isSelected; 121@end 122 123@interface NSWindow (AppKitSecretsIKnowAbout) 124- (NSRect)_growBoxRect; 125@end 126 127using namespace WebCore; 128 129@interface WebOpenPanelResultListener : NSObject <WebOpenPanelResultListener> 130{ 131 FileChooser* _chooser; 132} 133- (id)initWithChooser:(PassRefPtr<FileChooser>)chooser; 134@end 135 136#if ENABLE(FULLSCREEN_API) 137 138@interface WebKitFullScreenListener : NSObject <WebKitFullScreenListener> 139{ 140 RefPtr<Element> _element; 141} 142 143- (id)initWithElement:(Element*)element; 144@end 145 146#endif 147 148WebChromeClient::WebChromeClient(WebView *webView) 149 : m_webView(webView) 150{ 151} 152 153void WebChromeClient::chromeDestroyed() 154{ 155 delete this; 156} 157 158// These functions scale between window and WebView coordinates because JavaScript/DOM operations 159// assume that the WebView and the window share the same coordinate system. 160 161void WebChromeClient::setWindowRect(const FloatRect& rect) 162{ 163 NSRect windowRect = toDeviceSpace(rect, [m_webView window]); 164 [[m_webView _UIDelegateForwarder] webView:m_webView setFrame:windowRect]; 165} 166 167FloatRect WebChromeClient::windowRect() 168{ 169 NSRect windowRect = [[m_webView _UIDelegateForwarder] webViewFrame:m_webView]; 170 return toUserSpace(windowRect, [m_webView window]); 171} 172 173// FIXME: We need to add API for setting and getting this. 174FloatRect WebChromeClient::pageRect() 175{ 176 return [m_webView frame]; 177} 178 179float WebChromeClient::scaleFactor() 180{ 181 NSWindow *window = [m_webView window]; 182#if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) 183 if (window) 184 return [window backingScaleFactor]; 185 return [[NSScreen mainScreen] backingScaleFactor]; 186#else 187 if (window) 188 return [window userSpaceScaleFactor]; 189 return [[NSScreen mainScreen] userSpaceScaleFactor]; 190#endif 191} 192 193void WebChromeClient::focus() 194{ 195 [[m_webView _UIDelegateForwarder] webViewFocus:m_webView]; 196} 197 198void WebChromeClient::unfocus() 199{ 200 [[m_webView _UIDelegateForwarder] webViewUnfocus:m_webView]; 201} 202 203bool WebChromeClient::canTakeFocus(FocusDirection) 204{ 205 // There's unfortunately no way to determine if we will become first responder again 206 // once we give it up, so we just have to guess that we won't. 207 return true; 208} 209 210void WebChromeClient::takeFocus(FocusDirection direction) 211{ 212 if (direction == FocusDirectionForward) { 213 // Since we're trying to move focus out of m_webView, and because 214 // m_webView may contain subviews within it, we ask it for the next key 215 // view of the last view in its key view loop. This makes m_webView 216 // behave as if it had no subviews, which is the behavior we want. 217 NSView *lastView = [m_webView _findLastViewInKeyViewLoop]; 218 // avoid triggering assertions if the WebView is the only thing in the key loop 219 if ([m_webView _becomingFirstResponderFromOutside] && m_webView == [lastView nextValidKeyView]) 220 return; 221 [[m_webView window] selectKeyViewFollowingView:lastView]; 222 } else { 223 // avoid triggering assertions if the WebView is the only thing in the key loop 224 if ([m_webView _becomingFirstResponderFromOutside] && m_webView == [m_webView previousValidKeyView]) 225 return; 226 [[m_webView window] selectKeyViewPrecedingView:m_webView]; 227 } 228} 229 230void WebChromeClient::focusedNodeChanged(Node*) 231{ 232} 233 234void WebChromeClient::focusedFrameChanged(Frame*) 235{ 236} 237 238Page* WebChromeClient::createWindow(Frame* frame, const FrameLoadRequest&, const WindowFeatures& features, const NavigationAction&) 239{ 240 id delegate = [m_webView UIDelegate]; 241 WebView *newWebView; 242 243 if ([delegate respondsToSelector:@selector(webView:createWebViewWithRequest:windowFeatures:)]) { 244 NSNumber *x = features.xSet ? [[NSNumber alloc] initWithFloat:features.x] : nil; 245 NSNumber *y = features.ySet ? [[NSNumber alloc] initWithFloat:features.y] : nil; 246 NSNumber *width = features.widthSet ? [[NSNumber alloc] initWithFloat:features.width] : nil; 247 NSNumber *height = features.heightSet ? [[NSNumber alloc] initWithFloat:features.height] : nil; 248 NSNumber *menuBarVisible = [[NSNumber alloc] initWithBool:features.menuBarVisible]; 249 NSNumber *statusBarVisible = [[NSNumber alloc] initWithBool:features.statusBarVisible]; 250 NSNumber *toolBarVisible = [[NSNumber alloc] initWithBool:features.toolBarVisible]; 251 NSNumber *scrollbarsVisible = [[NSNumber alloc] initWithBool:features.scrollbarsVisible]; 252 NSNumber *resizable = [[NSNumber alloc] initWithBool:features.resizable]; 253 NSNumber *fullscreen = [[NSNumber alloc] initWithBool:features.fullscreen]; 254 NSNumber *dialog = [[NSNumber alloc] initWithBool:features.dialog]; 255 256 NSMutableDictionary *dictFeatures = [[NSMutableDictionary alloc] initWithObjectsAndKeys: 257 menuBarVisible, @"menuBarVisible", 258 statusBarVisible, @"statusBarVisible", 259 toolBarVisible, @"toolBarVisible", 260 scrollbarsVisible, @"scrollbarsVisible", 261 resizable, @"resizable", 262 fullscreen, @"fullscreen", 263 dialog, @"dialog", 264 nil]; 265 266 if (x) 267 [dictFeatures setObject:x forKey:@"x"]; 268 if (y) 269 [dictFeatures setObject:y forKey:@"y"]; 270 if (width) 271 [dictFeatures setObject:width forKey:@"width"]; 272 if (height) 273 [dictFeatures setObject:height forKey:@"height"]; 274 275 newWebView = CallUIDelegate(m_webView, @selector(webView:createWebViewWithRequest:windowFeatures:), nil, dictFeatures); 276 277 [dictFeatures release]; 278 [x release]; 279 [y release]; 280 [width release]; 281 [height release]; 282 [menuBarVisible release]; 283 [statusBarVisible release]; 284 [toolBarVisible release]; 285 [scrollbarsVisible release]; 286 [resizable release]; 287 [fullscreen release]; 288 [dialog release]; 289 } else if (features.dialog && [delegate respondsToSelector:@selector(webView:createWebViewModalDialogWithRequest:)]) { 290 newWebView = CallUIDelegate(m_webView, @selector(webView:createWebViewModalDialogWithRequest:), nil); 291 } else { 292 newWebView = CallUIDelegate(m_webView, @selector(webView:createWebViewWithRequest:), nil); 293 } 294 295#if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) 296 if (newWebView) 297 WebKit::NetscapePluginHostManager::shared().didCreateWindow(); 298#endif 299 300 return core(newWebView); 301} 302 303void WebChromeClient::show() 304{ 305 [[m_webView _UIDelegateForwarder] webViewShow:m_webView]; 306} 307 308bool WebChromeClient::canRunModal() 309{ 310 return [[m_webView UIDelegate] respondsToSelector:@selector(webViewRunModal:)]; 311} 312 313void WebChromeClient::runModal() 314{ 315 CallUIDelegate(m_webView, @selector(webViewRunModal:)); 316} 317 318void WebChromeClient::setToolbarsVisible(bool b) 319{ 320 [[m_webView _UIDelegateForwarder] webView:m_webView setToolbarsVisible:b]; 321} 322 323bool WebChromeClient::toolbarsVisible() 324{ 325 return CallUIDelegateReturningBoolean(NO, m_webView, @selector(webViewAreToolbarsVisible:)); 326} 327 328void WebChromeClient::setStatusbarVisible(bool b) 329{ 330 [[m_webView _UIDelegateForwarder] webView:m_webView setStatusBarVisible:b]; 331} 332 333bool WebChromeClient::statusbarVisible() 334{ 335 return CallUIDelegateReturningBoolean(NO, m_webView, @selector(webViewIsStatusBarVisible:)); 336} 337 338void WebChromeClient::setScrollbarsVisible(bool b) 339{ 340 [[[m_webView mainFrame] frameView] setAllowsScrolling:b]; 341} 342 343bool WebChromeClient::scrollbarsVisible() 344{ 345 return [[[m_webView mainFrame] frameView] allowsScrolling]; 346} 347 348void WebChromeClient::setMenubarVisible(bool) 349{ 350 // The menubar is always visible in Mac OS X. 351 return; 352} 353 354bool WebChromeClient::menubarVisible() 355{ 356 // The menubar is always visible in Mac OS X. 357 return true; 358} 359 360void WebChromeClient::setResizable(bool b) 361{ 362 [[m_webView _UIDelegateForwarder] webView:m_webView setResizable:b]; 363} 364 365inline static NSString *stringForMessageSource(MessageSource source) 366{ 367 switch (source) { 368 case HTMLMessageSource: 369 return WebConsoleMessageHTMLMessageSource; 370 case WMLMessageSource: 371 return WebConsoleMessageWMLMessageSource; 372 case XMLMessageSource: 373 return WebConsoleMessageXMLMessageSource; 374 case JSMessageSource: 375 return WebConsoleMessageJSMessageSource; 376 case CSSMessageSource: 377 return WebConsoleMessageCSSMessageSource; 378 case OtherMessageSource: 379 return WebConsoleMessageOtherMessageSource; 380 } 381 ASSERT_NOT_REACHED(); 382 return @""; 383} 384 385inline static NSString *stringForMessageType(MessageType type) 386{ 387 switch (type) { 388 case LogMessageType: 389 return WebConsoleMessageLogMessageType; 390 case ObjectMessageType: 391 return WebConsoleMessageObjectMessageType; 392 case TraceMessageType: 393 return WebConsoleMessageTraceMessageType; 394 case StartGroupMessageType: 395 return WebConsoleMessageStartGroupMessageType; 396 case StartGroupCollapsedMessageType: 397 return WebConsoleMessageStartGroupCollapsedMessageType; 398 case EndGroupMessageType: 399 return WebConsoleMessageEndGroupMessageType; 400 case AssertMessageType: 401 return WebConsoleMessageAssertMessageType; 402 case UncaughtExceptionMessageType: 403 return WebConsoleMessageUncaughtExceptionMessageType; 404 case NetworkErrorMessageType: 405 return WebConsoleMessageNetworkErrorMessageType; 406 } 407 ASSERT_NOT_REACHED(); 408 return @""; 409} 410 411inline static NSString *stringForMessageLevel(MessageLevel level) 412{ 413 switch (level) { 414 case TipMessageLevel: 415 return WebConsoleMessageTipMessageLevel; 416 case LogMessageLevel: 417 return WebConsoleMessageLogMessageLevel; 418 case WarningMessageLevel: 419 return WebConsoleMessageWarningMessageLevel; 420 case ErrorMessageLevel: 421 return WebConsoleMessageErrorMessageLevel; 422 case DebugMessageLevel: 423 return WebConsoleMessageDebugMessageLevel; 424 } 425 ASSERT_NOT_REACHED(); 426 return @""; 427} 428 429void WebChromeClient::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned int lineNumber, const String& sourceURL) 430{ 431 id delegate = [m_webView UIDelegate]; 432 BOOL respondsToNewSelector = NO; 433 434 SEL selector = @selector(webView:addMessageToConsole:withSource:); 435 if ([delegate respondsToSelector:selector]) 436 respondsToNewSelector = YES; 437 else { 438 // The old selector only takes JSMessageSource messages. 439 if (source != JSMessageSource) 440 return; 441 selector = @selector(webView:addMessageToConsole:); 442 if (![delegate respondsToSelector:selector]) 443 return; 444 } 445 446 NSString *messageSource = stringForMessageSource(source); 447 NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys: 448 (NSString *)message, @"message", 449 [NSNumber numberWithUnsignedInt:lineNumber], @"lineNumber", 450 (NSString *)sourceURL, @"sourceURL", 451 messageSource, @"MessageSource", 452 stringForMessageType(type), @"MessageType", 453 stringForMessageLevel(level), @"MessageLevel", 454 NULL]; 455 456 if (respondsToNewSelector) 457 CallUIDelegate(m_webView, selector, dictionary, messageSource); 458 else 459 CallUIDelegate(m_webView, selector, dictionary); 460 461 [dictionary release]; 462} 463 464bool WebChromeClient::canRunBeforeUnloadConfirmPanel() 465{ 466 return [[m_webView UIDelegate] respondsToSelector:@selector(webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:)]; 467} 468 469bool WebChromeClient::runBeforeUnloadConfirmPanel(const String& message, Frame* frame) 470{ 471 return CallUIDelegateReturningBoolean(true, m_webView, @selector(webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:), message, kit(frame)); 472} 473 474void WebChromeClient::closeWindowSoon() 475{ 476 // We need to remove the parent WebView from WebViewSets here, before it actually 477 // closes, to make sure that JavaScript code that executes before it closes 478 // can't find it. Otherwise, window.open will select a closed WebView instead of 479 // opening a new one <rdar://problem/3572585>. 480 481 // We also need to stop the load to prevent further parsing or JavaScript execution 482 // after the window has torn down <rdar://problem/4161660>. 483 484 // FIXME: This code assumes that the UI delegate will respond to a webViewClose 485 // message by actually closing the WebView. Safari guarantees this behavior, but other apps might not. 486 // This approach is an inherent limitation of not making a close execute immediately 487 // after a call to window.close. 488 489 [m_webView setGroupName:nil]; 490 [m_webView stopLoading:nil]; 491 [m_webView performSelector:@selector(_closeWindow) withObject:nil afterDelay:0.0]; 492} 493 494void WebChromeClient::runJavaScriptAlert(Frame* frame, const String& message) 495{ 496 id delegate = [m_webView UIDelegate]; 497 SEL selector = @selector(webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:); 498 if ([delegate respondsToSelector:selector]) { 499 CallUIDelegate(m_webView, selector, message, kit(frame)); 500 return; 501 } 502 503 // Call the old version of the delegate method if it is implemented. 504 selector = @selector(webView:runJavaScriptAlertPanelWithMessage:); 505 if ([delegate respondsToSelector:selector]) { 506 CallUIDelegate(m_webView, selector, message); 507 return; 508 } 509} 510 511bool WebChromeClient::runJavaScriptConfirm(Frame* frame, const String& message) 512{ 513 id delegate = [m_webView UIDelegate]; 514 SEL selector = @selector(webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:); 515 if ([delegate respondsToSelector:selector]) 516 return CallUIDelegateReturningBoolean(NO, m_webView, selector, message, kit(frame)); 517 518 // Call the old version of the delegate method if it is implemented. 519 selector = @selector(webView:runJavaScriptConfirmPanelWithMessage:); 520 if ([delegate respondsToSelector:selector]) 521 return CallUIDelegateReturningBoolean(NO, m_webView, selector, message); 522 523 return NO; 524} 525 526bool WebChromeClient::runJavaScriptPrompt(Frame* frame, const String& prompt, const String& defaultText, String& result) 527{ 528 id delegate = [m_webView UIDelegate]; 529 SEL selector = @selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:); 530 if ([delegate respondsToSelector:selector]) { 531 result = (NSString *)CallUIDelegate(m_webView, selector, prompt, defaultText, 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, defaultText); 539 return !result.isNull(); 540 } 541 542 result = [[WebDefaultUIDelegate sharedUIDelegate] webView:m_webView runJavaScriptTextInputPanelWithPrompt:prompt defaultText:defaultText initiatedByFrame:kit(frame)]; 543 return !result.isNull(); 544} 545 546bool WebChromeClient::shouldInterruptJavaScript() 547{ 548 return CallUIDelegate(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 NSRect rect = [[m_webView window] _growBoxRect]; 563 if ([m_webView _usesDocumentViews]) 564 return enclosingIntRect(rect); 565 return enclosingIntRect([m_webView convertRect:rect fromView:nil]); 566} 567 568void WebChromeClient::invalidateWindow(const IntRect&, bool immediate) 569{ 570 if (immediate) { 571 [[m_webView window] displayIfNeeded]; 572 [[m_webView window] flushWindowIfNeeded]; 573 } 574} 575 576void WebChromeClient::invalidateContentsAndWindow(const IntRect& rect, bool immediate) 577{ 578 if ([m_webView _usesDocumentViews]) 579 return; 580 581 [m_webView setNeedsDisplayInRect:rect]; 582 583 if (immediate) { 584 [[m_webView window] displayIfNeeded]; 585 [[m_webView window] flushWindowIfNeeded]; 586 } 587} 588 589void WebChromeClient::invalidateContentsForSlowScroll(const IntRect& rect, bool immediate) 590{ 591 invalidateContentsAndWindow(rect, immediate); 592} 593 594void WebChromeClient::scroll(const IntSize&, const IntRect&, const IntRect&) 595{ 596} 597 598IntPoint WebChromeClient::screenToWindow(const IntPoint& p) const 599{ 600 if ([m_webView _usesDocumentViews]) 601 return p; 602 NSPoint windowCoord = [[m_webView window] convertScreenToBase:p]; 603 return IntPoint([m_webView convertPoint:windowCoord fromView:nil]); 604} 605 606IntRect WebChromeClient::windowToScreen(const IntRect& r) const 607{ 608 if ([m_webView _usesDocumentViews]) 609 return r; 610 NSRect tempRect = r; 611 tempRect = [m_webView convertRect:tempRect toView:nil]; 612 tempRect.origin = [[m_webView window] convertBaseToScreen:tempRect.origin]; 613 return enclosingIntRect(tempRect); 614} 615 616PlatformPageClient WebChromeClient::platformPageClient() const 617{ 618 if ([m_webView _usesDocumentViews]) 619 return 0; 620 return m_webView; 621} 622 623void WebChromeClient::contentsSizeChanged(Frame*, const IntSize&) const 624{ 625} 626 627void WebChromeClient::scrollRectIntoView(const IntRect& r, const ScrollView*) const 628{ 629 // FIXME: This scrolling behavior should be under the control of the embedding client, 630 // perhaps in a delegate method, rather than something WebKit does unconditionally. 631 NSView *coordinateView = [m_webView _usesDocumentViews] 632 ? [[[m_webView mainFrame] frameView] documentView] : m_webView; 633 NSRect rect = r; 634 for (NSView *view = m_webView; view; view = [view superview]) { 635 if ([view isKindOfClass:[NSClipView class]]) { 636 NSClipView *clipView = (NSClipView *)view; 637 NSView *documentView = [clipView documentView]; 638 [documentView scrollRectToVisible:[documentView convertRect:rect fromView:coordinateView]]; 639 } 640 } 641} 642 643// End host window methods. 644 645bool WebChromeClient::shouldMissingPluginMessageBeButton() const 646{ 647 return [[m_webView UIDelegate] respondsToSelector:@selector(webView:didPressMissingPluginButton:)]; 648} 649 650void WebChromeClient::missingPluginButtonClicked(Element* element) const 651{ 652 CallUIDelegate(m_webView, @selector(webView:didPressMissingPluginButton:), kit(element)); 653} 654 655void WebChromeClient::mouseDidMoveOverElement(const HitTestResult& result, unsigned modifierFlags) 656{ 657 WebElementDictionary *element = [[WebElementDictionary alloc] initWithHitTestResult:result]; 658 [m_webView _mouseDidMoveOverElement:element modifierFlags:modifierFlags]; 659 [element release]; 660} 661 662void WebChromeClient::setToolTip(const String& toolTip, TextDirection) 663{ 664 [m_webView _setToolTip:toolTip]; 665} 666 667void WebChromeClient::print(Frame* frame) 668{ 669 WebFrame *webFrame = kit(frame); 670 if ([[m_webView UIDelegate] respondsToSelector:@selector(webView:printFrame:)]) 671 CallUIDelegate(m_webView, @selector(webView:printFrame:), webFrame); 672 else if ([m_webView _usesDocumentViews]) 673 CallUIDelegate(m_webView, @selector(webView:printFrameView:), [webFrame frameView]); 674} 675 676#if ENABLE(DATABASE) 677 678void WebChromeClient::exceededDatabaseQuota(Frame* frame, const String& databaseName) 679{ 680 BEGIN_BLOCK_OBJC_EXCEPTIONS; 681 682 WebSecurityOrigin *webOrigin = [[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:frame->document()->securityOrigin()]; 683 // FIXME: remove this workaround once shipping Safari has the necessary delegate implemented. 684 if (WKAppVersionCheckLessThan(@"com.apple.Safari", -1, 3.1)) { 685 const unsigned long long defaultQuota = 5 * 1024 * 1024; // 5 megabytes should hopefully be enough to test storage support. 686 [[webOrigin databaseQuotaManager] setQuota:defaultQuota]; 687 } else 688 CallUIDelegate(m_webView, @selector(webView:frame:exceededDatabaseQuotaForSecurityOrigin:database:), kit(frame), webOrigin, (NSString *)databaseName); 689 [webOrigin release]; 690 691 END_BLOCK_OBJC_EXCEPTIONS; 692} 693 694#endif 695 696#if ENABLE(OFFLINE_WEB_APPLICATIONS) 697 698void WebChromeClient::reachedMaxAppCacheSize(int64_t spaceNeeded) 699{ 700 // FIXME: Free some space. 701} 702 703void WebChromeClient::reachedApplicationCacheOriginQuota(SecurityOrigin* origin) 704{ 705 BEGIN_BLOCK_OBJC_EXCEPTIONS; 706 707 WebSecurityOrigin *webOrigin = [[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:origin]; 708 CallUIDelegate(m_webView, @selector(webView:exceededApplicationCacheOriginQuotaForSecurityOrigin:), webOrigin); 709 [webOrigin release]; 710 711 END_BLOCK_OBJC_EXCEPTIONS; 712} 713 714#endif 715 716void WebChromeClient::populateVisitedLinks() 717{ 718 if ([m_webView historyDelegate]) { 719 WebHistoryDelegateImplementationCache* implementations = WebViewGetHistoryDelegateImplementations(m_webView); 720 721 if (implementations->populateVisitedLinksFunc) 722 CallHistoryDelegate(implementations->populateVisitedLinksFunc, m_webView, @selector(populateVisitedLinksForWebView:)); 723 724 return; 725 } 726 727 BEGIN_BLOCK_OBJC_EXCEPTIONS; 728 [[WebHistory optionalSharedHistory] _addVisitedLinksToPageGroup:[m_webView page]->group()]; 729 END_BLOCK_OBJC_EXCEPTIONS; 730} 731 732#if ENABLE(DASHBOARD_SUPPORT) 733 734void WebChromeClient::dashboardRegionsChanged() 735{ 736 BEGIN_BLOCK_OBJC_EXCEPTIONS; 737 CallUIDelegate(m_webView, @selector(webView:dashboardRegionsChanged:), [m_webView _dashboardRegions]); 738 END_BLOCK_OBJC_EXCEPTIONS; 739} 740 741#endif 742 743FloatRect WebChromeClient::customHighlightRect(Node* node, const AtomicString& type, const FloatRect& lineRect) 744{ 745 BEGIN_BLOCK_OBJC_EXCEPTIONS; 746 747 NSView *documentView = [[kit(node->document()->frame()) frameView] documentView]; 748 if (![documentView isKindOfClass:[WebHTMLView class]]) 749 return NSZeroRect; 750 751 WebHTMLView *webHTMLView = (WebHTMLView *)documentView; 752 id<WebHTMLHighlighter> highlighter = [webHTMLView _highlighterForType:type]; 753 return [highlighter highlightRectForLine:lineRect representedNode:kit(node)]; 754 755 END_BLOCK_OBJC_EXCEPTIONS; 756 757 return NSZeroRect; 758} 759 760void WebChromeClient::paintCustomHighlight(Node* node, const AtomicString& type, const FloatRect& boxRect, const FloatRect& lineRect, 761 bool behindText, bool entireLine) 762{ 763 BEGIN_BLOCK_OBJC_EXCEPTIONS; 764 765 NSView *documentView = [[kit(node->document()->frame()) frameView] documentView]; 766 if (![documentView isKindOfClass:[WebHTMLView class]]) 767 return; 768 769 WebHTMLView *webHTMLView = (WebHTMLView *)documentView; 770 id<WebHTMLHighlighter> highlighter = [webHTMLView _highlighterForType:type]; 771 [highlighter paintHighlightForBox:boxRect onLine:lineRect behindText:behindText entireLine:entireLine representedNode:kit(node)]; 772 773 END_BLOCK_OBJC_EXCEPTIONS; 774} 775 776void WebChromeClient::runOpenPanel(Frame*, PassRefPtr<FileChooser> chooser) 777{ 778 BEGIN_BLOCK_OBJC_EXCEPTIONS; 779 BOOL allowMultipleFiles = chooser->allowsMultipleFiles(); 780 WebOpenPanelResultListener *listener = [[WebOpenPanelResultListener alloc] initWithChooser:chooser]; 781 id delegate = [m_webView UIDelegate]; 782 if ([delegate respondsToSelector:@selector(webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:)]) 783 CallUIDelegate(m_webView, @selector(webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:), listener, allowMultipleFiles); 784 else 785 CallUIDelegate(m_webView, @selector(webView:runOpenPanelForFileButtonWithResultListener:), listener); 786 [listener release]; 787 END_BLOCK_OBJC_EXCEPTIONS; 788} 789 790void WebChromeClient::chooseIconForFiles(const Vector<String>& filenames, FileChooser* chooser) 791{ 792 chooser->iconLoaded(Icon::createIconForFiles(filenames)); 793} 794 795void WebChromeClient::setCursor(const WebCore::Cursor& cursor) 796{ 797 if ([NSApp _cursorRectCursor]) 798 return; 799 800 NSCursor *platformCursor = cursor.platformCursor(); 801 if ([NSCursor currentCursor] == platformCursor) 802 return; 803 [platformCursor set]; 804} 805 806KeyboardUIMode WebChromeClient::keyboardUIMode() 807{ 808 BEGIN_BLOCK_OBJC_EXCEPTIONS; 809 return [m_webView _keyboardUIMode]; 810 END_BLOCK_OBJC_EXCEPTIONS; 811 return KeyboardAccessDefault; 812} 813 814NSResponder *WebChromeClient::firstResponder() 815{ 816 BEGIN_BLOCK_OBJC_EXCEPTIONS; 817 return [[m_webView _UIDelegateForwarder] webViewFirstResponder:m_webView]; 818 END_BLOCK_OBJC_EXCEPTIONS; 819 return nil; 820} 821 822void WebChromeClient::makeFirstResponder(NSResponder *responder) 823{ 824 BEGIN_BLOCK_OBJC_EXCEPTIONS; 825 [m_webView _pushPerformingProgrammaticFocus]; 826 [[m_webView _UIDelegateForwarder] webView:m_webView makeFirstResponder:responder]; 827 [m_webView _popPerformingProgrammaticFocus]; 828 END_BLOCK_OBJC_EXCEPTIONS; 829} 830 831void WebChromeClient::willPopUpMenu(NSMenu *menu) 832{ 833 BEGIN_BLOCK_OBJC_EXCEPTIONS; 834 CallUIDelegate(m_webView, @selector(webView:willPopupMenu:), menu); 835 END_BLOCK_OBJC_EXCEPTIONS; 836} 837 838bool WebChromeClient::shouldReplaceWithGeneratedFileForUpload(const String& path, String& generatedFilename) 839{ 840 NSString* filename; 841 if (![[m_webView _UIDelegateForwarder] webView:m_webView shouldReplaceUploadFile:path usingGeneratedFilename:&filename]) 842 return false; 843 generatedFilename = filename; 844 return true; 845} 846 847String WebChromeClient::generateReplacementFile(const String& path) 848{ 849 return [[m_webView _UIDelegateForwarder] webView:m_webView generateReplacementFile:path]; 850} 851 852void WebChromeClient::formDidFocus(const WebCore::Node* node) 853{ 854 CallUIDelegate(m_webView, @selector(webView:formDidFocusNode:), kit(const_cast<WebCore::Node*>(node))); 855} 856 857void WebChromeClient::formDidBlur(const WebCore::Node* node) 858{ 859 CallUIDelegate(m_webView, @selector(webView:formDidBlurNode:), kit(const_cast<WebCore::Node*>(node))); 860} 861 862bool WebChromeClient::selectItemWritingDirectionIsNatural() 863{ 864#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) 865 return false; 866#else 867 return true; 868#endif 869} 870 871bool WebChromeClient::selectItemAlignmentFollowsMenuWritingDirection() 872{ 873#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) 874 return true; 875#else 876 return false; 877#endif 878} 879 880PassRefPtr<WebCore::PopupMenu> WebChromeClient::createPopupMenu(WebCore::PopupMenuClient* client) const 881{ 882 return adoptRef(new PopupMenuMac(client)); 883} 884 885PassRefPtr<WebCore::SearchPopupMenu> WebChromeClient::createSearchPopupMenu(WebCore::PopupMenuClient* client) const 886{ 887 return adoptRef(new SearchPopupMenuMac(client)); 888} 889 890#if ENABLE(CONTEXT_MENUS) 891void WebChromeClient::showContextMenu() 892{ 893 Page* page = [m_webView page]; 894 if (!page) 895 return; 896 897 ContextMenuController* controller = page->contextMenuController(); 898 Node* node = controller->hitTestResult().innerNonSharedNode(); 899 if (!node) 900 return; 901 Frame* frame = node->document()->frame(); 902 if (!frame) 903 return; 904 FrameView* frameView = frame->view(); 905 if (!frameView) 906 return; 907 NSView* view = frameView->documentView(); 908 909 IntPoint point = frameView->contentsToWindow(controller->hitTestResult().point()); 910 NSPoint nsScreenPoint = [view convertPoint:point toView:nil]; 911 // Show the contextual menu for this event. 912 NSEvent* event = [NSEvent mouseEventWithType:NSRightMouseDown location:nsScreenPoint modifierFlags:0 timestamp:0 windowNumber:[[view window] windowNumber] context:0 eventNumber:0 clickCount:1 pressure:1]; 913 NSMenu* nsMenu = [view menuForEvent:event]; 914 if (nsMenu) 915 [NSMenu popUpContextMenu:nsMenu withEvent:event forView:view]; 916} 917#endif 918 919#if USE(ACCELERATED_COMPOSITING) 920 921void WebChromeClient::attachRootGraphicsLayer(Frame* frame, GraphicsLayer* graphicsLayer) 922{ 923 BEGIN_BLOCK_OBJC_EXCEPTIONS; 924 925 NSView *documentView = [[kit(frame) frameView] documentView]; 926 if (![documentView isKindOfClass:[WebHTMLView class]]) { 927 // We should never be attaching when we don't have a WebHTMLView. 928 ASSERT(!graphicsLayer); 929 return; 930 } 931 932 WebHTMLView *webHTMLView = (WebHTMLView *)documentView; 933 if (graphicsLayer) 934 [webHTMLView attachRootLayer:graphicsLayer->platformLayer()]; 935 else 936 [webHTMLView detachRootLayer]; 937 END_BLOCK_OBJC_EXCEPTIONS; 938} 939 940void WebChromeClient::setNeedsOneShotDrawingSynchronization() 941{ 942 BEGIN_BLOCK_OBJC_EXCEPTIONS; 943 [m_webView _setNeedsOneShotDrawingSynchronization:YES]; 944 END_BLOCK_OBJC_EXCEPTIONS; 945} 946 947void WebChromeClient::scheduleCompositingLayerSync() 948{ 949 BEGIN_BLOCK_OBJC_EXCEPTIONS; 950 [m_webView _scheduleCompositingLayerSync]; 951 END_BLOCK_OBJC_EXCEPTIONS; 952} 953 954#endif 955 956#if ENABLE(VIDEO) 957 958bool WebChromeClient::supportsFullscreenForNode(const Node* node) 959{ 960 return node->hasTagName(WebCore::HTMLNames::videoTag); 961} 962 963void WebChromeClient::enterFullscreenForNode(Node* node) 964{ 965 BEGIN_BLOCK_OBJC_EXCEPTIONS; 966 [m_webView _enterFullscreenForNode:node]; 967 END_BLOCK_OBJC_EXCEPTIONS; 968} 969 970void WebChromeClient::exitFullscreenForNode(Node*) 971{ 972 BEGIN_BLOCK_OBJC_EXCEPTIONS; 973 [m_webView _exitFullscreen]; 974 END_BLOCK_OBJC_EXCEPTIONS; 975} 976 977#endif 978 979#if ENABLE(FULLSCREEN_API) 980 981bool WebChromeClient::supportsFullScreenForElement(const Element* element, bool withKeyboard) 982{ 983 SEL selector = @selector(webView:supportsFullScreenForElement:withKeyboard:); 984 if ([[m_webView UIDelegate] respondsToSelector:selector]) 985 return CallUIDelegateReturningBoolean(false, m_webView, selector, kit(const_cast<WebCore::Element*>(element)), withKeyboard); 986 return [m_webView _supportsFullScreenForElement:const_cast<WebCore::Element*>(element) withKeyboard:withKeyboard]; 987} 988 989void WebChromeClient::enterFullScreenForElement(Element* element) 990{ 991 SEL selector = @selector(webView:enterFullScreenForElement:listener:); 992 if ([[m_webView UIDelegate] respondsToSelector:selector]) { 993 WebKitFullScreenListener* listener = [[WebKitFullScreenListener alloc] initWithElement:element]; 994 CallUIDelegate(m_webView, selector, kit(element), listener); 995 [listener release]; 996 } else 997 [m_webView _enterFullScreenForElement:element]; 998} 999 1000void WebChromeClient::exitFullScreenForElement(Element* element) 1001{ 1002 SEL selector = @selector(webView:exitFullScreenForElement:listener:); 1003 if ([[m_webView UIDelegate] respondsToSelector:selector]) { 1004 WebKitFullScreenListener* listener = [[WebKitFullScreenListener alloc] initWithElement:element]; 1005 CallUIDelegate(m_webView, selector, kit(element), listener); 1006 [listener release]; 1007 } else 1008 [m_webView _exitFullScreenForElement:element]; 1009} 1010 1011void WebChromeClient::fullScreenRendererChanged(RenderBox* renderer) 1012{ 1013 SEL selector = @selector(webView:fullScreenRendererChanged:); 1014 if ([[m_webView UIDelegate] respondsToSelector:selector]) 1015 CallUIDelegate(m_webView, selector, (id)renderer); 1016 else 1017 [m_webView _fullScreenRendererChanged:renderer]; 1018} 1019 1020#endif 1021 1022@implementation WebOpenPanelResultListener 1023 1024- (id)initWithChooser:(PassRefPtr<FileChooser>)chooser 1025{ 1026 self = [super init]; 1027 if (!self) 1028 return nil; 1029 _chooser = chooser.releaseRef(); 1030 return self; 1031} 1032 1033#ifndef NDEBUG 1034 1035- (void)dealloc 1036{ 1037 ASSERT(!_chooser); 1038 [super dealloc]; 1039} 1040 1041- (void)finalize 1042{ 1043 ASSERT(!_chooser); 1044 [super finalize]; 1045} 1046 1047#endif 1048 1049- (void)cancel 1050{ 1051 ASSERT(_chooser); 1052 if (!_chooser) 1053 return; 1054 _chooser->deref(); 1055 _chooser = 0; 1056} 1057 1058- (void)chooseFilename:(NSString *)filename 1059{ 1060 ASSERT(_chooser); 1061 if (!_chooser) 1062 return; 1063 _chooser->chooseFile(filename); 1064 _chooser->deref(); 1065 _chooser = 0; 1066} 1067 1068- (void)chooseFilenames:(NSArray *)filenames 1069{ 1070 ASSERT(_chooser); 1071 if (!_chooser) 1072 return; 1073 int count = [filenames count]; 1074 Vector<String> names(count); 1075 for (int i = 0; i < count; i++) 1076 names[i] = [filenames objectAtIndex:i]; 1077 _chooser->chooseFiles(names); 1078 _chooser->deref(); 1079 _chooser = 0; 1080} 1081 1082@end 1083 1084#if ENABLE(FULLSCREEN_API) 1085 1086@implementation WebKitFullScreenListener 1087 1088- (id)initWithElement:(Element*)element 1089{ 1090 if (!(self = [super init])) 1091 return nil; 1092 1093 _element = element; 1094 return self; 1095} 1096 1097- (void)webkitWillEnterFullScreen 1098{ 1099 if (_element) 1100 _element->document()->webkitWillEnterFullScreenForElement(_element.get()); 1101} 1102 1103- (void)webkitDidEnterFullScreen 1104{ 1105 if (_element) 1106 _element->document()->webkitDidEnterFullScreenForElement(_element.get()); 1107} 1108 1109- (void)webkitWillExitFullScreen 1110{ 1111 if (_element) 1112 _element->document()->webkitWillExitFullScreenForElement(_element.get()); 1113} 1114 1115- (void)webkitDidExitFullScreen 1116{ 1117 if (_element) 1118 _element->document()->webkitDidExitFullScreenForElement(_element.get()); 1119} 1120 1121@end 1122 1123#endif 1124