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 87 NSString *WebConsoleMessageHTMLMessageSource = @"HTMLMessageSource"; 88 NSString *WebConsoleMessageWMLMessageSource = @"WMLMessageSource"; 89 NSString *WebConsoleMessageXMLMessageSource = @"XMLMessageSource"; 90 NSString *WebConsoleMessageJSMessageSource = @"JSMessageSource"; 91 NSString *WebConsoleMessageCSSMessageSource = @"CSSMessageSource"; 92 NSString *WebConsoleMessageOtherMessageSource = @"OtherMessageSource"; 93 94 NSString *WebConsoleMessageLogMessageType = @"LogMessageType"; 95 NSString *WebConsoleMessageObjectMessageType = @"ObjectMessageType"; 96 NSString *WebConsoleMessageTraceMessageType = @"TraceMessageType"; 97 NSString *WebConsoleMessageStartGroupMessageType = @"StartGroupMessageType"; 98 NSString *WebConsoleMessageStartGroupCollapsedMessageType = @"StartGroupCollapsedMessageType"; 99 NSString *WebConsoleMessageEndGroupMessageType = @"EndGroupMessageType"; 100 NSString *WebConsoleMessageAssertMessageType = @"AssertMessageType"; 101 NSString *WebConsoleMessageUncaughtExceptionMessageType = @"UncaughtExceptionMessageType"; 102 NSString *WebConsoleMessageNetworkErrorMessageType = @"NetworkErrorMessageType"; 103 104 NSString *WebConsoleMessageTipMessageLevel = @"TipMessageLevel"; 105 NSString *WebConsoleMessageLogMessageLevel = @"LogMessageLevel"; 106 NSString *WebConsoleMessageWarningMessageLevel = @"WarningMessageLevel"; 107 NSString *WebConsoleMessageErrorMessageLevel = @"ErrorMessageLevel"; 108 NSString *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 127 using 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 148 WebChromeClient::WebChromeClient(WebView *webView) 149 : m_webView(webView) 150 { 151 } 152 153 void 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 161 void 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 167 FloatRect 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. 174 FloatRect WebChromeClient::pageRect() 175 { 176 return [m_webView frame]; 177 } 178 179 float 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 193 void WebChromeClient::focus() 194 { 195 [[m_webView _UIDelegateForwarder] webViewFocus:m_webView]; 196 } 197 198 void WebChromeClient::unfocus() 199 { 200 [[m_webView _UIDelegateForwarder] webViewUnfocus:m_webView]; 201 } 202 203 bool 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 210 void 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 230 void WebChromeClient::focusedNodeChanged(Node*) 231 { 232 } 233 234 void WebChromeClient::focusedFrameChanged(Frame*) 235 { 236 } 237 238 Page* 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 303 void WebChromeClient::show() 304 { 305 [[m_webView _UIDelegateForwarder] webViewShow:m_webView]; 306 } 307 308 bool WebChromeClient::canRunModal() 309 { 310 return [[m_webView UIDelegate] respondsToSelector:@selector(webViewRunModal:)]; 311 } 312 313 void WebChromeClient::runModal() 314 { 315 CallUIDelegate(m_webView, @selector(webViewRunModal:)); 316 } 317 318 void WebChromeClient::setToolbarsVisible(bool b) 319 { 320 [[m_webView _UIDelegateForwarder] webView:m_webView setToolbarsVisible:b]; 321 } 322 323 bool WebChromeClient::toolbarsVisible() 324 { 325 return CallUIDelegateReturningBoolean(NO, m_webView, @selector(webViewAreToolbarsVisible:)); 326 } 327 328 void WebChromeClient::setStatusbarVisible(bool b) 329 { 330 [[m_webView _UIDelegateForwarder] webView:m_webView setStatusBarVisible:b]; 331 } 332 333 bool WebChromeClient::statusbarVisible() 334 { 335 return CallUIDelegateReturningBoolean(NO, m_webView, @selector(webViewIsStatusBarVisible:)); 336 } 337 338 void WebChromeClient::setScrollbarsVisible(bool b) 339 { 340 [[[m_webView mainFrame] frameView] setAllowsScrolling:b]; 341 } 342 343 bool WebChromeClient::scrollbarsVisible() 344 { 345 return [[[m_webView mainFrame] frameView] allowsScrolling]; 346 } 347 348 void WebChromeClient::setMenubarVisible(bool) 349 { 350 // The menubar is always visible in Mac OS X. 351 return; 352 } 353 354 bool WebChromeClient::menubarVisible() 355 { 356 // The menubar is always visible in Mac OS X. 357 return true; 358 } 359 360 void WebChromeClient::setResizable(bool b) 361 { 362 [[m_webView _UIDelegateForwarder] webView:m_webView setResizable:b]; 363 } 364 365 inline 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 385 inline 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 411 inline 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 429 void 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 464 bool WebChromeClient::canRunBeforeUnloadConfirmPanel() 465 { 466 return [[m_webView UIDelegate] respondsToSelector:@selector(webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:)]; 467 } 468 469 bool WebChromeClient::runBeforeUnloadConfirmPanel(const String& message, Frame* frame) 470 { 471 return CallUIDelegateReturningBoolean(true, m_webView, @selector(webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:), message, kit(frame)); 472 } 473 474 void 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 494 void 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 511 bool 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 526 bool 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 546 bool WebChromeClient::shouldInterruptJavaScript() 547 { 548 return CallUIDelegate(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 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 568 void WebChromeClient::invalidateWindow(const IntRect&, bool immediate) 569 { 570 if (immediate) { 571 [[m_webView window] displayIfNeeded]; 572 [[m_webView window] flushWindowIfNeeded]; 573 } 574 } 575 576 void 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 589 void WebChromeClient::invalidateContentsForSlowScroll(const IntRect& rect, bool immediate) 590 { 591 invalidateContentsAndWindow(rect, immediate); 592 } 593 594 void WebChromeClient::scroll(const IntSize&, const IntRect&, const IntRect&) 595 { 596 } 597 598 IntPoint 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 606 IntRect 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 616 PlatformPageClient WebChromeClient::platformPageClient() const 617 { 618 if ([m_webView _usesDocumentViews]) 619 return 0; 620 return m_webView; 621 } 622 623 void WebChromeClient::contentsSizeChanged(Frame*, const IntSize&) const 624 { 625 } 626 627 void 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 645 bool WebChromeClient::shouldMissingPluginMessageBeButton() const 646 { 647 return [[m_webView UIDelegate] respondsToSelector:@selector(webView:didPressMissingPluginButton:)]; 648 } 649 650 void WebChromeClient::missingPluginButtonClicked(Element* element) const 651 { 652 CallUIDelegate(m_webView, @selector(webView:didPressMissingPluginButton:), kit(element)); 653 } 654 655 void 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 662 void WebChromeClient::setToolTip(const String& toolTip, TextDirection) 663 { 664 [m_webView _setToolTip:toolTip]; 665 } 666 667 void 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 678 void 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 698 void WebChromeClient::reachedMaxAppCacheSize(int64_t spaceNeeded) 699 { 700 // FIXME: Free some space. 701 } 702 703 void 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 716 void 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 734 void 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 743 FloatRect 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 760 void 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 776 void 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 790 void WebChromeClient::chooseIconForFiles(const Vector<String>& filenames, FileChooser* chooser) 791 { 792 chooser->iconLoaded(Icon::createIconForFiles(filenames)); 793 } 794 795 void 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 806 KeyboardUIMode WebChromeClient::keyboardUIMode() 807 { 808 BEGIN_BLOCK_OBJC_EXCEPTIONS; 809 return [m_webView _keyboardUIMode]; 810 END_BLOCK_OBJC_EXCEPTIONS; 811 return KeyboardAccessDefault; 812 } 813 814 NSResponder *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 822 void 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 831 void 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 838 bool 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 847 String WebChromeClient::generateReplacementFile(const String& path) 848 { 849 return [[m_webView _UIDelegateForwarder] webView:m_webView generateReplacementFile:path]; 850 } 851 852 void WebChromeClient::formDidFocus(const WebCore::Node* node) 853 { 854 CallUIDelegate(m_webView, @selector(webView:formDidFocusNode:), kit(const_cast<WebCore::Node*>(node))); 855 } 856 857 void WebChromeClient::formDidBlur(const WebCore::Node* node) 858 { 859 CallUIDelegate(m_webView, @selector(webView:formDidBlurNode:), kit(const_cast<WebCore::Node*>(node))); 860 } 861 862 bool 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 871 bool 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 880 PassRefPtr<WebCore::PopupMenu> WebChromeClient::createPopupMenu(WebCore::PopupMenuClient* client) const 881 { 882 return adoptRef(new PopupMenuMac(client)); 883 } 884 885 PassRefPtr<WebCore::SearchPopupMenu> WebChromeClient::createSearchPopupMenu(WebCore::PopupMenuClient* client) const 886 { 887 return adoptRef(new SearchPopupMenuMac(client)); 888 } 889 890 #if ENABLE(CONTEXT_MENUS) 891 void 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 921 void 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 940 void WebChromeClient::setNeedsOneShotDrawingSynchronization() 941 { 942 BEGIN_BLOCK_OBJC_EXCEPTIONS; 943 [m_webView _setNeedsOneShotDrawingSynchronization:YES]; 944 END_BLOCK_OBJC_EXCEPTIONS; 945 } 946 947 void 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 958 bool WebChromeClient::supportsFullscreenForNode(const Node* node) 959 { 960 return node->hasTagName(WebCore::HTMLNames::videoTag); 961 } 962 963 void WebChromeClient::enterFullscreenForNode(Node* node) 964 { 965 BEGIN_BLOCK_OBJC_EXCEPTIONS; 966 [m_webView _enterFullscreenForNode:node]; 967 END_BLOCK_OBJC_EXCEPTIONS; 968 } 969 970 void 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 981 bool 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 989 void 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 1000 void 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 1011 void 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