1 /* 2 * Copyright (C) 2009 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "ChromeClientImpl.h" 33 34 #include "AccessibilityObject.h" 35 #include "AXObjectCache.h" 36 #include "CharacterNames.h" 37 #include "Console.h" 38 #include "Cursor.h" 39 #include "DatabaseTracker.h" 40 #include "Document.h" 41 #include "DocumentLoader.h" 42 #include "FileChooser.h" 43 #include "FloatRect.h" 44 #include "FrameLoadRequest.h" 45 #include "FrameView.h" 46 #include "HitTestResult.h" 47 #include "IntRect.h" 48 #include "Node.h" 49 #include "NotificationPresenterImpl.h" 50 #include "Page.h" 51 #include "PopupMenuChromium.h" 52 #include "ScriptController.h" 53 #if USE(V8) 54 #include "V8Proxy.h" 55 #endif 56 #include "WebAccessibilityObject.h" 57 #include "WebConsoleMessage.h" 58 #include "WebCursorInfo.h" 59 #include "WebFileChooserCompletionImpl.h" 60 #include "WebFrameClient.h" 61 #include "WebFrameImpl.h" 62 #include "WebInputEvent.h" 63 #include "WebKit.h" 64 #include "WebPopupMenuImpl.h" 65 #include "WebPopupMenuInfo.h" 66 #include "WebRect.h" 67 #include "WebTextDirection.h" 68 #include "WebURLRequest.h" 69 #include "WebViewClient.h" 70 #include "WebViewImpl.h" 71 #include "WindowFeatures.h" 72 #include "WrappedResourceRequest.h" 73 74 using namespace WebCore; 75 76 namespace WebKit { 77 78 ChromeClientImpl::ChromeClientImpl(WebViewImpl* webView) 79 : m_webView(webView) 80 , m_toolbarsVisible(true) 81 , m_statusbarVisible(true) 82 , m_scrollbarsVisible(true) 83 , m_menubarVisible(true) 84 , m_resizable(true) 85 , m_ignoreNextSetCursor(false) 86 { 87 } 88 89 ChromeClientImpl::~ChromeClientImpl() 90 { 91 } 92 93 void ChromeClientImpl::chromeDestroyed() 94 { 95 // Our lifetime is bound to the WebViewImpl. 96 } 97 98 void ChromeClientImpl::setWindowRect(const FloatRect& r) 99 { 100 if (m_webView->client()) 101 m_webView->client()->setWindowRect(IntRect(r)); 102 } 103 104 FloatRect ChromeClientImpl::windowRect() 105 { 106 WebRect rect; 107 if (m_webView->client()) 108 rect = m_webView->client()->rootWindowRect(); 109 else { 110 // These numbers will be fairly wrong. The window's x/y coordinates will 111 // be the top left corner of the screen and the size will be the content 112 // size instead of the window size. 113 rect.width = m_webView->size().width; 114 rect.height = m_webView->size().height; 115 } 116 return FloatRect(rect); 117 } 118 119 FloatRect ChromeClientImpl::pageRect() 120 { 121 // We hide the details of the window's border thickness from the web page by 122 // simple re-using the window position here. So, from the point-of-view of 123 // the web page, the window has no border. 124 return windowRect(); 125 } 126 127 float ChromeClientImpl::scaleFactor() 128 { 129 // This is supposed to return the scale factor of the web page. It looks like 130 // the implementor of the graphics layer is responsible for doing most of the 131 // operations associated with scaling. However, this value is used ins some 132 // cases by WebCore. For example, this is used as a scaling factor in canvas 133 // so that things drawn in it are scaled just like the web page is. 134 // 135 // We don't currently implement scaling, so just return 1.0 (no scaling). 136 return 1.0; 137 } 138 139 void ChromeClientImpl::focus() 140 { 141 if (!m_webView->client()) 142 return; 143 144 m_webView->client()->didFocus(); 145 146 // If accessibility is enabled, we should notify assistive technology that 147 // the active AccessibilityObject changed. 148 const Frame* frame = m_webView->focusedWebCoreFrame(); 149 if (!frame) 150 return; 151 152 Document* doc = frame->document(); 153 154 if (doc && doc->axObjectCache()->accessibilityEnabled()) { 155 Node* focusedNode = m_webView->focusedWebCoreNode(); 156 157 if (!focusedNode) { 158 // Could not retrieve focused Node. 159 return; 160 } 161 162 // Retrieve the focused AccessibilityObject. 163 AccessibilityObject* focusedAccObj = 164 doc->axObjectCache()->getOrCreate(focusedNode->renderer()); 165 166 // Alert assistive technology that focus changed. 167 if (focusedAccObj) 168 m_webView->client()->focusAccessibilityObject(WebAccessibilityObject(focusedAccObj)); 169 } 170 } 171 172 void ChromeClientImpl::unfocus() 173 { 174 if (m_webView->client()) 175 m_webView->client()->didBlur(); 176 } 177 178 bool ChromeClientImpl::canTakeFocus(FocusDirection) 179 { 180 // For now the browser can always take focus if we're not running layout 181 // tests. 182 return !layoutTestMode(); 183 } 184 185 void ChromeClientImpl::takeFocus(FocusDirection direction) 186 { 187 if (!m_webView->client()) 188 return; 189 if (direction == FocusDirectionBackward) 190 m_webView->client()->focusPrevious(); 191 else 192 m_webView->client()->focusNext(); 193 } 194 195 void ChromeClientImpl::focusedNodeChanged(Node* node) 196 { 197 WebURL focus_url; 198 if (node && node->isLink()) { 199 // This HitTestResult hack is the easiest way to get a link URL out of a 200 // WebCore::Node. 201 HitTestResult hit_test(IntPoint(0, 0)); 202 // This cast must be valid because of the isLink() check. 203 hit_test.setURLElement(reinterpret_cast<Element*>(node)); 204 if (hit_test.isLiveLink()) 205 focus_url = hit_test.absoluteLinkURL(); 206 } 207 m_webView->client()->setKeyboardFocusURL(focus_url); 208 } 209 210 Page* ChromeClientImpl::createWindow( 211 Frame* frame, const FrameLoadRequest& r, const WindowFeatures& features) 212 { 213 if (!m_webView->client()) 214 return 0; 215 216 WebViewImpl* newView = static_cast<WebViewImpl*>( 217 m_webView->client()->createView(WebFrameImpl::fromFrame(frame))); 218 if (!newView) 219 return 0; 220 221 // The request is empty when we are just being asked to open a blank window. 222 // This corresponds to window.open(""), for example. 223 if (!r.resourceRequest().isEmpty()) { 224 WrappedResourceRequest request(r.resourceRequest()); 225 newView->mainFrame()->loadRequest(request); 226 } 227 228 return newView->page(); 229 } 230 231 static inline bool currentEventShouldCauseBackgroundTab(const WebInputEvent* inputEvent) 232 { 233 if (!inputEvent) 234 return false; 235 236 if (inputEvent->type != WebInputEvent::MouseUp) 237 return false; 238 239 const WebMouseEvent* mouseEvent = static_cast<const WebMouseEvent*>(inputEvent); 240 241 WebNavigationPolicy policy; 242 unsigned short buttonNumber; 243 switch (mouseEvent->button) { 244 case WebMouseEvent::ButtonLeft: 245 buttonNumber = 0; 246 break; 247 case WebMouseEvent::ButtonMiddle: 248 buttonNumber = 1; 249 break; 250 case WebMouseEvent::ButtonRight: 251 buttonNumber = 2; 252 break; 253 default: 254 return false; 255 } 256 bool ctrl = mouseEvent->modifiers & WebMouseEvent::ControlKey; 257 bool shift = mouseEvent->modifiers & WebMouseEvent::ShiftKey; 258 bool alt = mouseEvent->modifiers & WebMouseEvent::AltKey; 259 bool meta = mouseEvent->modifiers & WebMouseEvent::MetaKey; 260 261 if (!WebViewImpl::navigationPolicyFromMouseEvent(buttonNumber, ctrl, shift, alt, meta, &policy)) 262 return false; 263 264 return policy == WebNavigationPolicyNewBackgroundTab; 265 } 266 267 void ChromeClientImpl::show() 268 { 269 if (!m_webView->client()) 270 return; 271 272 // If our default configuration was modified by a script or wasn't 273 // created by a user gesture, then show as a popup. Else, let this 274 // new window be opened as a toplevel window. 275 bool asPopup = !m_toolbarsVisible 276 || !m_statusbarVisible 277 || !m_scrollbarsVisible 278 || !m_menubarVisible 279 || !m_resizable; 280 281 WebNavigationPolicy policy = WebNavigationPolicyNewForegroundTab; 282 if (asPopup) 283 policy = WebNavigationPolicyNewPopup; 284 if (currentEventShouldCauseBackgroundTab(WebViewImpl::currentInputEvent())) 285 policy = WebNavigationPolicyNewBackgroundTab; 286 287 m_webView->client()->show(policy); 288 } 289 290 bool ChromeClientImpl::canRunModal() 291 { 292 return !!m_webView->client(); 293 } 294 295 void ChromeClientImpl::runModal() 296 { 297 if (m_webView->client()) 298 m_webView->client()->runModal(); 299 } 300 301 void ChromeClientImpl::setToolbarsVisible(bool value) 302 { 303 m_toolbarsVisible = value; 304 } 305 306 bool ChromeClientImpl::toolbarsVisible() 307 { 308 return m_toolbarsVisible; 309 } 310 311 void ChromeClientImpl::setStatusbarVisible(bool value) 312 { 313 m_statusbarVisible = value; 314 } 315 316 bool ChromeClientImpl::statusbarVisible() 317 { 318 return m_statusbarVisible; 319 } 320 321 void ChromeClientImpl::setScrollbarsVisible(bool value) 322 { 323 m_scrollbarsVisible = value; 324 WebFrameImpl* web_frame = static_cast<WebFrameImpl*>(m_webView->mainFrame()); 325 if (web_frame) 326 web_frame->setAllowsScrolling(value); 327 } 328 329 bool ChromeClientImpl::scrollbarsVisible() 330 { 331 return m_scrollbarsVisible; 332 } 333 334 void ChromeClientImpl::setMenubarVisible(bool value) 335 { 336 m_menubarVisible = value; 337 } 338 339 bool ChromeClientImpl::menubarVisible() 340 { 341 return m_menubarVisible; 342 } 343 344 void ChromeClientImpl::setResizable(bool value) 345 { 346 m_resizable = value; 347 } 348 349 void ChromeClientImpl::addMessageToConsole(MessageSource source, 350 MessageType type, 351 MessageLevel level, 352 const String& message, 353 unsigned lineNumber, 354 const String& sourceID) 355 { 356 if (m_webView->client()) { 357 m_webView->client()->didAddMessageToConsole( 358 WebConsoleMessage(static_cast<WebConsoleMessage::Level>(level), message), 359 sourceID, 360 lineNumber); 361 } 362 } 363 364 bool ChromeClientImpl::canRunBeforeUnloadConfirmPanel() 365 { 366 return !!m_webView->client(); 367 } 368 369 bool ChromeClientImpl::runBeforeUnloadConfirmPanel(const String& message, Frame* frame) 370 { 371 if (m_webView->client()) { 372 return m_webView->client()->runModalBeforeUnloadDialog( 373 WebFrameImpl::fromFrame(frame), message); 374 } 375 return false; 376 } 377 378 void ChromeClientImpl::closeWindowSoon() 379 { 380 // Make sure this Page can no longer be found by JS. 381 m_webView->page()->setGroupName(String()); 382 383 // Make sure that all loading is stopped. Ensures that JS stops executing! 384 m_webView->mainFrame()->stopLoading(); 385 386 if (m_webView->client()) 387 m_webView->client()->closeWidgetSoon(); 388 } 389 390 // Although a Frame is passed in, we don't actually use it, since we 391 // already know our own m_webView. 392 void ChromeClientImpl::runJavaScriptAlert(Frame* frame, const String& message) 393 { 394 if (m_webView->client()) { 395 #if USE(V8) 396 // Before showing the JavaScript dialog, we give the proxy implementation 397 // a chance to process any pending console messages. 398 V8Proxy::processConsoleMessages(); 399 #endif 400 m_webView->client()->runModalAlertDialog( 401 WebFrameImpl::fromFrame(frame), message); 402 } 403 } 404 405 // See comments for runJavaScriptAlert(). 406 bool ChromeClientImpl::runJavaScriptConfirm(Frame* frame, const String& message) 407 { 408 if (m_webView->client()) { 409 return m_webView->client()->runModalConfirmDialog( 410 WebFrameImpl::fromFrame(frame), message); 411 } 412 return false; 413 } 414 415 // See comments for runJavaScriptAlert(). 416 bool ChromeClientImpl::runJavaScriptPrompt(Frame* frame, 417 const String& message, 418 const String& defaultValue, 419 String& result) 420 { 421 if (m_webView->client()) { 422 WebString actualValue; 423 bool ok = m_webView->client()->runModalPromptDialog( 424 WebFrameImpl::fromFrame(frame), 425 message, 426 defaultValue, 427 &actualValue); 428 if (ok) 429 result = actualValue; 430 return ok; 431 } 432 return false; 433 } 434 435 void ChromeClientImpl::setStatusbarText(const String& message) 436 { 437 if (m_webView->client()) 438 m_webView->client()->setStatusText(message); 439 } 440 441 bool ChromeClientImpl::shouldInterruptJavaScript() 442 { 443 // FIXME: implement me 444 return false; 445 } 446 447 bool ChromeClientImpl::tabsToLinks() const 448 { 449 // Returns true if anchors should accept keyboard focus with the tab key. 450 // This method is used in a convoluted fashion by EventHandler::tabsToLinks. 451 // It's a twisted path (self-evident, but more complicated than seems 452 // necessary), but the net result is that returning true from here, on a 453 // platform other than MAC or QT, lets anchors get keyboard focus. 454 return m_webView->tabsToLinks(); 455 } 456 457 IntRect ChromeClientImpl::windowResizerRect() const 458 { 459 IntRect result; 460 if (m_webView->client()) 461 result = m_webView->client()->windowResizerRect(); 462 return result; 463 } 464 465 void ChromeClientImpl::repaint( 466 const IntRect& paintRect, bool contentChanged, bool immediate, 467 bool repaintContentOnly) 468 { 469 // Ignore spurious calls. 470 if (!contentChanged || paintRect.isEmpty()) 471 return; 472 if (m_webView->client()) 473 m_webView->client()->didInvalidateRect(paintRect); 474 } 475 476 void ChromeClientImpl::scroll( 477 const IntSize& scrollDelta, const IntRect& scrollRect, 478 const IntRect& clipRect) 479 { 480 if (m_webView->client()) { 481 int dx = scrollDelta.width(); 482 int dy = scrollDelta.height(); 483 m_webView->client()->didScrollRect(dx, dy, clipRect); 484 } 485 } 486 487 IntPoint ChromeClientImpl::screenToWindow(const IntPoint&) const 488 { 489 notImplemented(); 490 return IntPoint(); 491 } 492 493 IntRect ChromeClientImpl::windowToScreen(const IntRect& rect) const 494 { 495 IntRect screenRect(rect); 496 497 if (m_webView->client()) { 498 WebRect windowRect = m_webView->client()->windowRect(); 499 screenRect.move(windowRect.x, windowRect.y); 500 } 501 502 return screenRect; 503 } 504 505 void ChromeClientImpl::contentsSizeChanged(Frame* frame, const IntSize& size) const 506 { 507 WebFrameImpl* webframe = WebFrameImpl::fromFrame(frame); 508 if (webframe->client()) 509 webframe->client()->didChangeContentsSize(webframe, size); 510 } 511 512 void ChromeClientImpl::scrollbarsModeDidChange() const 513 { 514 } 515 516 void ChromeClientImpl::mouseDidMoveOverElement( 517 const HitTestResult& result, unsigned modifierFlags) 518 { 519 if (!m_webView->client()) 520 return; 521 // Find out if the mouse is over a link, and if so, let our UI know... 522 if (result.isLiveLink() && !result.absoluteLinkURL().string().isEmpty()) 523 m_webView->client()->setMouseOverURL(result.absoluteLinkURL()); 524 else 525 m_webView->client()->setMouseOverURL(WebURL()); 526 } 527 528 void ChromeClientImpl::setToolTip(const String& tooltipText, TextDirection dir) 529 { 530 if (!m_webView->client()) 531 return; 532 WebTextDirection textDirection = (dir == RTL) ? 533 WebTextDirectionRightToLeft : 534 WebTextDirectionLeftToRight; 535 m_webView->client()->setToolTipText( 536 tooltipText, textDirection); 537 } 538 539 void ChromeClientImpl::print(Frame* frame) 540 { 541 if (m_webView->client()) 542 m_webView->client()->printPage(WebFrameImpl::fromFrame(frame)); 543 } 544 545 void ChromeClientImpl::exceededDatabaseQuota(Frame* frame, const String& databaseName) 546 { 547 // Chromium users cannot currently change the default quota 548 } 549 550 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 551 void ChromeClientImpl::reachedMaxAppCacheSize(int64_t spaceNeeded) 552 { 553 ASSERT_NOT_REACHED(); 554 } 555 #endif 556 557 void ChromeClientImpl::runOpenPanel(Frame* frame, PassRefPtr<FileChooser> fileChooser) 558 { 559 WebViewClient* client = m_webView->client(); 560 if (!client) 561 return; 562 563 WebFileChooserParams params; 564 params.multiSelect = fileChooser->allowsMultipleFiles(); 565 params.acceptTypes = fileChooser->acceptTypes(); 566 params.selectedFiles = fileChooser->filenames(); 567 if (params.selectedFiles.size() > 0) 568 params.initialValue = params.selectedFiles[0]; 569 WebFileChooserCompletionImpl* chooserCompletion = 570 new WebFileChooserCompletionImpl(fileChooser); 571 572 if (client->runFileChooser(params, chooserCompletion)) 573 return; 574 575 // Choosing failed, so do callback with an empty list. 576 chooserCompletion->didChooseFile(WebVector<WebString>()); 577 } 578 579 void ChromeClientImpl::popupOpened(PopupContainer* popupContainer, 580 const IntRect& bounds, 581 bool activatable, 582 bool handleExternally) 583 { 584 if (!m_webView->client()) 585 return; 586 587 WebWidget* webwidget; 588 if (handleExternally) { 589 WebPopupMenuInfo popupInfo; 590 getPopupMenuInfo(popupContainer, &popupInfo); 591 webwidget = m_webView->client()->createPopupMenu(popupInfo); 592 } else 593 webwidget = m_webView->client()->createPopupMenu(activatable); 594 595 static_cast<WebPopupMenuImpl*>(webwidget)->Init(popupContainer, bounds); 596 } 597 598 void ChromeClientImpl::setCursor(const WebCursorInfo& cursor) 599 { 600 if (m_ignoreNextSetCursor) { 601 m_ignoreNextSetCursor = false; 602 return; 603 } 604 605 if (m_webView->client()) 606 m_webView->client()->didChangeCursor(cursor); 607 } 608 609 void ChromeClientImpl::setCursorForPlugin(const WebCursorInfo& cursor) 610 { 611 setCursor(cursor); 612 613 // Currently, Widget::setCursor is always called after this function in 614 // EventHandler.cpp and since we don't want that we set a flag indicating 615 // that the next SetCursor call is to be ignored. 616 m_ignoreNextSetCursor = true; 617 } 618 619 void ChromeClientImpl::formStateDidChange(const Node* node) 620 { 621 // The current history item is not updated yet. That happens lazily when 622 // WebFrame::currentHistoryItem is requested. 623 WebFrameImpl* webframe = WebFrameImpl::fromFrame(node->document()->frame()); 624 if (webframe->client()) 625 webframe->client()->didUpdateCurrentHistoryItem(webframe); 626 } 627 628 void ChromeClientImpl::getPopupMenuInfo(PopupContainer* popupContainer, 629 WebPopupMenuInfo* info) 630 { 631 const Vector<PopupItem*>& inputItems = popupContainer->popupData(); 632 633 WebVector<WebPopupMenuInfo::Item> outputItems(inputItems.size()); 634 635 for (size_t i = 0; i < inputItems.size(); ++i) { 636 const PopupItem& inputItem = *inputItems[i]; 637 WebPopupMenuInfo::Item& outputItem = outputItems[i]; 638 639 outputItem.label = inputItem.label; 640 outputItem.enabled = inputItem.enabled; 641 642 switch (inputItem.type) { 643 case PopupItem::TypeOption: 644 outputItem.type = WebPopupMenuInfo::Item::Option; 645 break; 646 case PopupItem::TypeGroup: 647 outputItem.type = WebPopupMenuInfo::Item::Group; 648 break; 649 case PopupItem::TypeSeparator: 650 outputItem.type = WebPopupMenuInfo::Item::Separator; 651 break; 652 default: 653 ASSERT_NOT_REACHED(); 654 } 655 } 656 657 info->itemHeight = popupContainer->menuItemHeight(); 658 info->selectedIndex = popupContainer->selectedIndex(); 659 info->items.swap(outputItems); 660 } 661 662 void ChromeClientImpl::didChangeAccessibilityObjectState(AccessibilityObject* obj) 663 { 664 // Alert assistive technology about the accessibility object state change 665 if (obj) 666 m_webView->client()->didChangeAccessibilityObjectState(WebAccessibilityObject(obj)); 667 } 668 669 670 #if ENABLE(NOTIFICATIONS) 671 NotificationPresenter* ChromeClientImpl::notificationPresenter() const 672 { 673 return m_webView->notificationPresenterImpl(); 674 } 675 #endif 676 677 } // namespace WebKit 678