1 /* 2 * Copyright (C) 2010 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 "WebViewImpl.h" 33 34 #include "AutoFillPopupMenuClient.h" 35 #include "AutocompletePopupMenuClient.h" 36 #include "AXObjectCache.h" 37 #include "Chrome.h" 38 #include "ContextMenu.h" 39 #include "ContextMenuController.h" 40 #include "ContextMenuItem.h" 41 #include "CSSStyleSelector.h" 42 #include "CSSValueKeywords.h" 43 #include "Cursor.h" 44 #include "Document.h" 45 #include "DocumentLoader.h" 46 #include "DOMUtilitiesPrivate.h" 47 #include "DragController.h" 48 #include "DragData.h" 49 #include "Editor.h" 50 #include "EventHandler.h" 51 #include "FocusController.h" 52 #include "FontDescription.h" 53 #include "FrameLoader.h" 54 #include "FrameTree.h" 55 #include "FrameView.h" 56 #include "GraphicsContext.h" 57 #include "HitTestResult.h" 58 #include "HTMLInputElement.h" 59 #include "HTMLMediaElement.h" 60 #include "HTMLNames.h" 61 #include "Image.h" 62 #include "InspectorController.h" 63 #include "IntRect.h" 64 #include "KeyboardCodes.h" 65 #include "KeyboardEvent.h" 66 #include "MIMETypeRegistry.h" 67 #include "NodeRenderStyle.h" 68 #include "Page.h" 69 #include "PageGroup.h" 70 #include "PageGroupLoadDeferrer.h" 71 #include "Pasteboard.h" 72 #include "PlatformContextSkia.h" 73 #include "PlatformKeyboardEvent.h" 74 #include "PlatformMouseEvent.h" 75 #include "PlatformWheelEvent.h" 76 #include "PluginInfoStore.h" 77 #include "PopupMenuChromium.h" 78 #include "PopupMenuClient.h" 79 #include "ProgressTracker.h" 80 #include "RenderView.h" 81 #include "ResourceHandle.h" 82 #include "SecurityOrigin.h" 83 #include "SelectionController.h" 84 #include "Settings.h" 85 #include "TypingCommand.h" 86 #include "WebAccessibilityObject.h" 87 #include "WebDevToolsAgentPrivate.h" 88 #include "WebDragData.h" 89 #include "WebFrameImpl.h" 90 #include "WebInputEvent.h" 91 #include "WebInputEventConversion.h" 92 #include "WebMediaPlayerAction.h" 93 #include "WebNode.h" 94 #include "WebPoint.h" 95 #include "WebPopupMenuImpl.h" 96 #include "WebRect.h" 97 #include "WebSettingsImpl.h" 98 #include "WebString.h" 99 #include "WebVector.h" 100 #include "WebViewClient.h" 101 102 #if OS(WINDOWS) 103 #include "KeyboardCodesWin.h" 104 #include "RenderThemeChromiumWin.h" 105 #else 106 #if OS(LINUX) 107 #include "RenderThemeChromiumLinux.h" 108 #endif 109 #include "KeyboardCodesPosix.h" 110 #include "RenderTheme.h" 111 #endif 112 113 // Get rid of WTF's pow define so we can use std::pow. 114 #undef pow 115 #include <cmath> // for std::pow 116 117 using namespace WebCore; 118 119 namespace WebKit { 120 121 // Change the text zoom level by kTextSizeMultiplierRatio each time the user 122 // zooms text in or out (ie., change by 20%). The min and max values limit 123 // text zoom to half and 3x the original text size. These three values match 124 // those in Apple's port in WebKit/WebKit/WebView/WebView.mm 125 static const double textSizeMultiplierRatio = 1.2; 126 static const double minTextSizeMultiplier = 0.5; 127 static const double maxTextSizeMultiplier = 3.0; 128 129 // The group name identifies a namespace of pages. Page group is used on OSX 130 // for some programs that use HTML views to display things that don't seem like 131 // web pages to the user (so shouldn't have visited link coloring). We only use 132 // one page group. 133 const char* pageGroupName = "default"; 134 135 // Used to defer all page activity in cases where the embedder wishes to run 136 // a nested event loop. 137 static PageGroupLoadDeferrer* pageGroupLoadDeferrer; 138 139 // Ensure that the WebDragOperation enum values stay in sync with the original 140 // DragOperation constants. 141 #define COMPILE_ASSERT_MATCHING_ENUM(coreName) \ 142 COMPILE_ASSERT(int(coreName) == int(Web##coreName), dummy##coreName) 143 COMPILE_ASSERT_MATCHING_ENUM(DragOperationNone); 144 COMPILE_ASSERT_MATCHING_ENUM(DragOperationCopy); 145 COMPILE_ASSERT_MATCHING_ENUM(DragOperationLink); 146 COMPILE_ASSERT_MATCHING_ENUM(DragOperationGeneric); 147 COMPILE_ASSERT_MATCHING_ENUM(DragOperationPrivate); 148 COMPILE_ASSERT_MATCHING_ENUM(DragOperationMove); 149 COMPILE_ASSERT_MATCHING_ENUM(DragOperationDelete); 150 COMPILE_ASSERT_MATCHING_ENUM(DragOperationEvery); 151 152 // Note that focusOnShow is false so that the suggestions popup is shown not 153 // activated. We need the page to still have focus so the user can keep typing 154 // while the popup is showing. 155 static const PopupContainerSettings suggestionsPopupSettings = { 156 false, // focusOnShow 157 false, // setTextOnIndexChange 158 false, // acceptOnAbandon 159 true, // loopSelectionNavigation 160 true, // restrictWidthOfListBox. Same as other browser (Fx, IE, and safari) 161 // For suggestions, we use the direction of the input field as the direction 162 // of the popup items. The main reason is to keep the display of items in 163 // drop-down the same as the items in the input field. 164 PopupContainerSettings::DOMElementDirection, 165 }; 166 167 // WebView ---------------------------------------------------------------- 168 169 WebView* WebView::create(WebViewClient* client) 170 { 171 return new WebViewImpl(client); 172 } 173 174 void WebView::updateVisitedLinkState(unsigned long long linkHash) 175 { 176 Page::visitedStateChanged(PageGroup::pageGroup(pageGroupName), linkHash); 177 } 178 179 void WebView::resetVisitedLinkState() 180 { 181 Page::allVisitedStateChanged(PageGroup::pageGroup(pageGroupName)); 182 } 183 184 void WebView::willEnterModalLoop() 185 { 186 // It is not valid to nest more than once. 187 ASSERT(!pageGroupLoadDeferrer); 188 189 PageGroup* pageGroup = PageGroup::pageGroup(pageGroupName); 190 ASSERT(pageGroup); 191 ASSERT(!pageGroup->pages().isEmpty()); 192 193 // Pick any page in the page group since we are deferring all pages. 194 pageGroupLoadDeferrer = new PageGroupLoadDeferrer(*pageGroup->pages().begin(), true); 195 } 196 197 void WebView::didExitModalLoop() 198 { 199 // The embedder must have called willEnterNestedEventLoop. 200 ASSERT(pageGroupLoadDeferrer); 201 202 delete pageGroupLoadDeferrer; 203 pageGroupLoadDeferrer = 0; 204 } 205 206 void WebViewImpl::initializeMainFrame(WebFrameClient* frameClient) 207 { 208 // NOTE: The WebFrameImpl takes a reference to itself within InitMainFrame 209 // and releases that reference once the corresponding Frame is destroyed. 210 RefPtr<WebFrameImpl> frame = WebFrameImpl::create(frameClient); 211 212 frame->initializeAsMainFrame(this); 213 214 // Restrict the access to the local file system 215 // (see WebView.mm WebView::_commonInitializationWithFrameName). 216 SecurityOrigin::setLocalLoadPolicy(SecurityOrigin::AllowLocalLoadsForLocalOnly); 217 } 218 219 WebViewImpl::WebViewImpl(WebViewClient* client) 220 : m_client(client) 221 , m_backForwardListClientImpl(this) 222 , m_chromeClientImpl(this) 223 , m_contextMenuClientImpl(this) 224 , m_dragClientImpl(this) 225 , m_editorClientImpl(this) 226 , m_inspectorClientImpl(this) 227 , m_observedNewNavigation(false) 228 #ifndef NDEBUG 229 , m_newNavigationLoader(0) 230 #endif 231 , m_zoomLevel(0) 232 , m_contextMenuAllowed(false) 233 , m_doingDragAndDrop(false) 234 , m_ignoreInputEvents(false) 235 , m_suppressNextKeypressEvent(false) 236 , m_initialNavigationPolicy(WebNavigationPolicyIgnore) 237 , m_imeAcceptEvents(true) 238 , m_dragTargetDispatch(false) 239 , m_dragIdentity(0) 240 , m_dropEffect(DropEffectDefault) 241 , m_operationsAllowed(WebDragOperationNone) 242 , m_dragOperation(WebDragOperationNone) 243 , m_suggestionsPopupShowing(false) 244 , m_suggestionsPopupClient(0) 245 , m_suggestionsPopup(0) 246 , m_isTransparent(false) 247 , m_tabsToLinks(false) 248 { 249 // WebKit/win/WebView.cpp does the same thing, except they call the 250 // KJS specific wrapper around this method. We need to have threading 251 // initialized because CollatorICU requires it. 252 WTF::initializeThreading(); 253 254 // set to impossible point so we always get the first mouse pos 255 m_lastMousePosition = WebPoint(-1, -1); 256 257 // the page will take ownership of the various clients 258 m_page.set(new Page(&m_chromeClientImpl, 259 &m_contextMenuClientImpl, 260 &m_editorClientImpl, 261 &m_dragClientImpl, 262 &m_inspectorClientImpl, 263 0, 264 0)); 265 266 m_page->backForwardList()->setClient(&m_backForwardListClientImpl); 267 m_page->setGroupName(pageGroupName); 268 } 269 270 WebViewImpl::~WebViewImpl() 271 { 272 ASSERT(!m_page); 273 } 274 275 RenderTheme* WebViewImpl::theme() const 276 { 277 return m_page.get() ? m_page->theme() : RenderTheme::defaultTheme().get(); 278 } 279 280 WebFrameImpl* WebViewImpl::mainFrameImpl() 281 { 282 return m_page.get() ? WebFrameImpl::fromFrame(m_page->mainFrame()) : 0; 283 } 284 285 bool WebViewImpl::tabKeyCyclesThroughElements() const 286 { 287 ASSERT(m_page.get()); 288 return m_page->tabKeyCyclesThroughElements(); 289 } 290 291 void WebViewImpl::setTabKeyCyclesThroughElements(bool value) 292 { 293 if (m_page) 294 m_page->setTabKeyCyclesThroughElements(value); 295 } 296 297 void WebViewImpl::mouseMove(const WebMouseEvent& event) 298 { 299 if (!mainFrameImpl() || !mainFrameImpl()->frameView()) 300 return; 301 302 m_lastMousePosition = WebPoint(event.x, event.y); 303 304 // We call mouseMoved here instead of handleMouseMovedEvent because we need 305 // our ChromeClientImpl to receive changes to the mouse position and 306 // tooltip text, and mouseMoved handles all of that. 307 mainFrameImpl()->frame()->eventHandler()->mouseMoved( 308 PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event)); 309 } 310 311 void WebViewImpl::mouseLeave(const WebMouseEvent& event) 312 { 313 // This event gets sent as the main frame is closing. In that case, just 314 // ignore it. 315 if (!mainFrameImpl() || !mainFrameImpl()->frameView()) 316 return; 317 318 m_client->setMouseOverURL(WebURL()); 319 320 mainFrameImpl()->frame()->eventHandler()->handleMouseMoveEvent( 321 PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event)); 322 } 323 324 void WebViewImpl::mouseDown(const WebMouseEvent& event) 325 { 326 if (!mainFrameImpl() || !mainFrameImpl()->frameView()) 327 return; 328 329 m_lastMouseDownPoint = WebPoint(event.x, event.y); 330 331 // If a text field that has focus is clicked again, we should display the 332 // suggestions popup. 333 RefPtr<Node> clickedNode; 334 if (event.button == WebMouseEvent::ButtonLeft) { 335 RefPtr<Node> focusedNode = focusedWebCoreNode(); 336 if (focusedNode.get() && toHTMLInputElement(focusedNode.get())) { 337 IntPoint point(event.x, event.y); 338 point = m_page->mainFrame()->view()->windowToContents(point); 339 HitTestResult result(point); 340 result = m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(point, false); 341 if (result.innerNonSharedNode() == focusedNode) { 342 // Already focused text field was clicked, let's remember this. If 343 // focus has not changed after the mouse event is processed, we'll 344 // trigger the autocomplete. 345 clickedNode = focusedNode; 346 } 347 } 348 } 349 350 mainFrameImpl()->frame()->eventHandler()->handleMousePressEvent( 351 PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event)); 352 353 if (clickedNode.get() && clickedNode == focusedWebCoreNode()) { 354 // Focus has not changed, show the suggestions popup. 355 static_cast<EditorClientImpl*>(m_page->editorClient())-> 356 showFormAutofillForNode(clickedNode.get()); 357 } 358 359 // Dispatch the contextmenu event regardless of if the click was swallowed. 360 // On Windows, we handle it on mouse up, not down. 361 #if OS(DARWIN) 362 if (event.button == WebMouseEvent::ButtonRight 363 || (event.button == WebMouseEvent::ButtonLeft 364 && event.modifiers & WebMouseEvent::ControlKey)) 365 mouseContextMenu(event); 366 #elif OS(LINUX) 367 if (event.button == WebMouseEvent::ButtonRight) 368 mouseContextMenu(event); 369 #endif 370 } 371 372 void WebViewImpl::mouseContextMenu(const WebMouseEvent& event) 373 { 374 if (!mainFrameImpl() || !mainFrameImpl()->frameView()) 375 return; 376 377 m_page->contextMenuController()->clearContextMenu(); 378 379 PlatformMouseEventBuilder pme(mainFrameImpl()->frameView(), event); 380 381 // Find the right target frame. See issue 1186900. 382 HitTestResult result = hitTestResultForWindowPos(pme.pos()); 383 Frame* targetFrame; 384 if (result.innerNonSharedNode()) 385 targetFrame = result.innerNonSharedNode()->document()->frame(); 386 else 387 targetFrame = m_page->focusController()->focusedOrMainFrame(); 388 389 #if OS(WINDOWS) 390 targetFrame->view()->setCursor(pointerCursor()); 391 #endif 392 393 m_contextMenuAllowed = true; 394 targetFrame->eventHandler()->sendContextMenuEvent(pme); 395 m_contextMenuAllowed = false; 396 // Actually showing the context menu is handled by the ContextMenuClient 397 // implementation... 398 } 399 400 void WebViewImpl::mouseUp(const WebMouseEvent& event) 401 { 402 if (!mainFrameImpl() || !mainFrameImpl()->frameView()) 403 return; 404 405 #if OS(LINUX) 406 // If the event was a middle click, attempt to copy text into the focused 407 // frame. We execute this before we let the page have a go at the event 408 // because the page may change what is focused during in its event handler. 409 // 410 // This code is in the mouse up handler. There is some debate about putting 411 // this here, as opposed to the mouse down handler. 412 // xterm: pastes on up. 413 // GTK: pastes on down. 414 // Firefox: pastes on up. 415 // Midori: couldn't paste at all with 0.1.2 416 // 417 // There is something of a webcompat angle to this well, as highlighted by 418 // crbug.com/14608. Pages can clear text boxes 'onclick' and, if we paste on 419 // down then the text is pasted just before the onclick handler runs and 420 // clears the text box. So it's important this happens after the 421 // handleMouseReleaseEvent() earlier in this function 422 if (event.button == WebMouseEvent::ButtonMiddle) { 423 Frame* focused = focusedWebCoreFrame(); 424 FrameView* view = m_page->mainFrame()->view(); 425 IntPoint clickPoint(m_lastMouseDownPoint.x, m_lastMouseDownPoint.y); 426 IntPoint contentPoint = view->windowToContents(clickPoint); 427 HitTestResult hitTestResult = focused->eventHandler()->hitTestResultAtPoint(contentPoint, false, false, ShouldHitTestScrollbars); 428 // We don't want to send a paste when middle clicking a scroll bar or a 429 // link (which will navigate later in the code). The main scrollbars 430 // have to be handled separately. 431 if (!hitTestResult.scrollbar() && !hitTestResult.isLiveLink() && focused && !view->scrollbarAtPoint(clickPoint)) { 432 Editor* editor = focused->editor(); 433 Pasteboard* pasteboard = Pasteboard::generalPasteboard(); 434 bool oldSelectionMode = pasteboard->isSelectionMode(); 435 pasteboard->setSelectionMode(true); 436 editor->command(AtomicString("Paste")).execute(); 437 pasteboard->setSelectionMode(oldSelectionMode); 438 } 439 } 440 #endif 441 442 mouseCaptureLost(); 443 mainFrameImpl()->frame()->eventHandler()->handleMouseReleaseEvent( 444 PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event)); 445 446 #if OS(WINDOWS) 447 // Dispatch the contextmenu event regardless of if the click was swallowed. 448 // On Mac/Linux, we handle it on mouse down, not up. 449 if (event.button == WebMouseEvent::ButtonRight) 450 mouseContextMenu(event); 451 #endif 452 } 453 454 void WebViewImpl::mouseWheel(const WebMouseWheelEvent& event) 455 { 456 PlatformWheelEventBuilder platformEvent(mainFrameImpl()->frameView(), event); 457 mainFrameImpl()->frame()->eventHandler()->handleWheelEvent(platformEvent); 458 } 459 460 bool WebViewImpl::keyEvent(const WebKeyboardEvent& event) 461 { 462 ASSERT((event.type == WebInputEvent::RawKeyDown) 463 || (event.type == WebInputEvent::KeyDown) 464 || (event.type == WebInputEvent::KeyUp)); 465 466 // Please refer to the comments explaining the m_suppressNextKeypressEvent 467 // member. 468 // The m_suppressNextKeypressEvent is set if the KeyDown is handled by 469 // Webkit. A keyDown event is typically associated with a keyPress(char) 470 // event and a keyUp event. We reset this flag here as this is a new keyDown 471 // event. 472 m_suppressNextKeypressEvent = false; 473 474 // Give Autocomplete a chance to consume the key events it is interested in. 475 if (autocompleteHandleKeyEvent(event)) 476 return true; 477 478 Frame* frame = focusedWebCoreFrame(); 479 if (!frame) 480 return false; 481 482 EventHandler* handler = frame->eventHandler(); 483 if (!handler) 484 return keyEventDefault(event); 485 486 #if OS(WINDOWS) || OS(LINUX) 487 const WebInputEvent::Type contextMenuTriggeringEventType = 488 #if OS(WINDOWS) 489 WebInputEvent::KeyUp; 490 #elif OS(LINUX) 491 WebInputEvent::RawKeyDown; 492 #endif 493 494 if (((!event.modifiers && (event.windowsKeyCode == VKEY_APPS)) 495 || ((event.modifiers == WebInputEvent::ShiftKey) && (event.windowsKeyCode == VKEY_F10))) 496 && event.type == contextMenuTriggeringEventType) { 497 sendContextMenuEvent(event); 498 return true; 499 } 500 #endif 501 502 // It's not clear if we should continue after detecting a capslock keypress. 503 // I'll err on the side of continuing, which is the pre-existing behaviour. 504 if (event.windowsKeyCode == VKEY_CAPITAL) 505 handler->capsLockStateMayHaveChanged(); 506 507 PlatformKeyboardEventBuilder evt(event); 508 509 if (handler->keyEvent(evt)) { 510 if (WebInputEvent::RawKeyDown == event.type) 511 m_suppressNextKeypressEvent = true; 512 return true; 513 } 514 515 return keyEventDefault(event); 516 } 517 518 bool WebViewImpl::autocompleteHandleKeyEvent(const WebKeyboardEvent& event) 519 { 520 if (!m_suggestionsPopupShowing 521 // Home and End should be left to the text field to process. 522 || event.windowsKeyCode == VKEY_HOME 523 || event.windowsKeyCode == VKEY_END) 524 return false; 525 526 // Pressing delete triggers the removal of the selected suggestion from the DB. 527 if (event.windowsKeyCode == VKEY_DELETE 528 && m_suggestionsPopup->selectedIndex() != -1) { 529 Node* node = focusedWebCoreNode(); 530 if (!node || (node->nodeType() != Node::ELEMENT_NODE)) { 531 ASSERT_NOT_REACHED(); 532 return false; 533 } 534 Element* element = static_cast<Element*>(node); 535 if (!element->hasLocalName(HTMLNames::inputTag)) { 536 ASSERT_NOT_REACHED(); 537 return false; 538 } 539 540 int selectedIndex = m_suggestionsPopup->selectedIndex(); 541 HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(element); 542 WebString name = inputElement->name(); 543 WebString value = m_autocompletePopupClient->itemText(selectedIndex); 544 m_client->removeAutofillSuggestions(name, value); 545 // Update the entries in the currently showing popup to reflect the 546 // deletion. 547 m_autocompletePopupClient->removeSuggestionAtIndex(selectedIndex); 548 refreshSuggestionsPopup(); 549 return false; 550 } 551 552 if (!m_suggestionsPopup->isInterestedInEventForKey(event.windowsKeyCode)) 553 return false; 554 555 if (m_suggestionsPopup->handleKeyEvent(PlatformKeyboardEventBuilder(event))) { 556 // We need to ignore the next Char event after this otherwise pressing 557 // enter when selecting an item in the menu will go to the page. 558 if (WebInputEvent::RawKeyDown == event.type) 559 m_suppressNextKeypressEvent = true; 560 return true; 561 } 562 563 return false; 564 } 565 566 bool WebViewImpl::charEvent(const WebKeyboardEvent& event) 567 { 568 ASSERT(event.type == WebInputEvent::Char); 569 570 // Please refer to the comments explaining the m_suppressNextKeypressEvent 571 // member. The m_suppressNextKeypressEvent is set if the KeyDown is 572 // handled by Webkit. A keyDown event is typically associated with a 573 // keyPress(char) event and a keyUp event. We reset this flag here as it 574 // only applies to the current keyPress event. 575 bool suppress = m_suppressNextKeypressEvent; 576 m_suppressNextKeypressEvent = false; 577 578 Frame* frame = focusedWebCoreFrame(); 579 if (!frame) 580 return suppress; 581 582 EventHandler* handler = frame->eventHandler(); 583 if (!handler) 584 return suppress || keyEventDefault(event); 585 586 PlatformKeyboardEventBuilder evt(event); 587 if (!evt.isCharacterKey()) 588 return true; 589 590 // Accesskeys are triggered by char events and can't be suppressed. 591 if (handler->handleAccessKey(evt)) 592 return true; 593 594 // Safari 3.1 does not pass off windows system key messages (WM_SYSCHAR) to 595 // the eventHandler::keyEvent. We mimic this behavior on all platforms since 596 // for now we are converting other platform's key events to windows key 597 // events. 598 if (evt.isSystemKey()) 599 return false; 600 601 if (!suppress && !handler->keyEvent(evt)) 602 return keyEventDefault(event); 603 604 return true; 605 } 606 607 // The WebViewImpl::SendContextMenuEvent function is based on the Webkit 608 // function 609 // bool WebView::handleContextMenuEvent(WPARAM wParam, LPARAM lParam) in 610 // webkit\webkit\win\WebView.cpp. The only significant change in this 611 // function is the code to convert from a Keyboard event to the Right 612 // Mouse button up event. 613 // 614 // This function is an ugly copy/paste and should be cleaned up when the 615 // WebKitWin version is cleaned: https://bugs.webkit.org/show_bug.cgi?id=20438 616 #if OS(WINDOWS) || OS(LINUX) 617 // FIXME: implement on Mac 618 bool WebViewImpl::sendContextMenuEvent(const WebKeyboardEvent& event) 619 { 620 static const int kContextMenuMargin = 1; 621 Frame* mainFrameImpl = page()->mainFrame(); 622 FrameView* view = mainFrameImpl->view(); 623 if (!view) 624 return false; 625 626 IntPoint coords(-1, -1); 627 #if OS(WINDOWS) 628 int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT); 629 #else 630 int rightAligned = 0; 631 #endif 632 IntPoint location; 633 634 635 Frame* focusedFrame = page()->focusController()->focusedOrMainFrame(); 636 Node* focusedNode = focusedFrame->document()->focusedNode(); 637 Position start = mainFrameImpl->selection()->selection().start(); 638 639 if (focusedFrame->editor() && focusedFrame->editor()->canEdit() && start.node()) { 640 RenderObject* renderer = start.node()->renderer(); 641 if (!renderer) 642 return false; 643 644 RefPtr<Range> selection = mainFrameImpl->selection()->toNormalizedRange(); 645 IntRect firstRect = mainFrameImpl->firstRectForRange(selection.get()); 646 647 int x = rightAligned ? firstRect.right() : firstRect.x(); 648 location = IntPoint(x, firstRect.bottom()); 649 } else if (focusedNode) 650 location = focusedNode->getRect().bottomLeft(); 651 else { 652 location = IntPoint( 653 rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin, 654 kContextMenuMargin); 655 } 656 657 location = view->contentsToWindow(location); 658 // FIXME: The IntSize(0, -1) is a hack to get the hit-testing to result in 659 // the selected element. Ideally we'd have the position of a context menu 660 // event be separate from its target node. 661 coords = location + IntSize(0, -1); 662 663 // The contextMenuController() holds onto the last context menu that was 664 // popped up on the page until a new one is created. We need to clear 665 // this menu before propagating the event through the DOM so that we can 666 // detect if we create a new menu for this event, since we won't create 667 // a new menu if the DOM swallows the event and the defaultEventHandler does 668 // not run. 669 page()->contextMenuController()->clearContextMenu(); 670 671 focusedFrame->view()->setCursor(pointerCursor()); 672 WebMouseEvent mouseEvent; 673 mouseEvent.button = WebMouseEvent::ButtonRight; 674 mouseEvent.x = coords.x(); 675 mouseEvent.y = coords.y(); 676 mouseEvent.type = WebInputEvent::MouseUp; 677 678 PlatformMouseEventBuilder platformEvent(view, mouseEvent); 679 680 m_contextMenuAllowed = true; 681 bool handled = focusedFrame->eventHandler()->sendContextMenuEvent(platformEvent); 682 m_contextMenuAllowed = false; 683 return handled; 684 } 685 #endif 686 687 bool WebViewImpl::keyEventDefault(const WebKeyboardEvent& event) 688 { 689 Frame* frame = focusedWebCoreFrame(); 690 if (!frame) 691 return false; 692 693 switch (event.type) { 694 case WebInputEvent::Char: 695 if (event.windowsKeyCode == VKEY_SPACE) { 696 int keyCode = ((event.modifiers & WebInputEvent::ShiftKey) ? VKEY_PRIOR : VKEY_NEXT); 697 return scrollViewWithKeyboard(keyCode, event.modifiers); 698 } 699 break; 700 case WebInputEvent::RawKeyDown: 701 if (event.modifiers == WebInputEvent::ControlKey) { 702 switch (event.windowsKeyCode) { 703 #if !OS(DARWIN) 704 case 'A': 705 focusedFrame()->executeCommand(WebString::fromUTF8("SelectAll")); 706 return true; 707 case VKEY_INSERT: 708 case 'C': 709 focusedFrame()->executeCommand(WebString::fromUTF8("Copy")); 710 return true; 711 #endif 712 // Match FF behavior in the sense that Ctrl+home/end are the only Ctrl 713 // key combinations which affect scrolling. Safari is buggy in the 714 // sense that it scrolls the page for all Ctrl+scrolling key 715 // combinations. For e.g. Ctrl+pgup/pgdn/up/down, etc. 716 case VKEY_HOME: 717 case VKEY_END: 718 break; 719 default: 720 return false; 721 } 722 } 723 if (!event.isSystemKey && !(event.modifiers & WebInputEvent::ShiftKey)) 724 return scrollViewWithKeyboard(event.windowsKeyCode, event.modifiers); 725 break; 726 default: 727 break; 728 } 729 return false; 730 } 731 732 bool WebViewImpl::scrollViewWithKeyboard(int keyCode, int modifiers) 733 { 734 ScrollDirection scrollDirection; 735 ScrollGranularity scrollGranularity; 736 737 switch (keyCode) { 738 case VKEY_LEFT: 739 scrollDirection = ScrollLeft; 740 scrollGranularity = ScrollByLine; 741 break; 742 case VKEY_RIGHT: 743 scrollDirection = ScrollRight; 744 scrollGranularity = ScrollByLine; 745 break; 746 case VKEY_UP: 747 scrollDirection = ScrollUp; 748 scrollGranularity = ScrollByLine; 749 break; 750 case VKEY_DOWN: 751 scrollDirection = ScrollDown; 752 scrollGranularity = ScrollByLine; 753 break; 754 case VKEY_HOME: 755 scrollDirection = ScrollUp; 756 scrollGranularity = ScrollByDocument; 757 break; 758 case VKEY_END: 759 scrollDirection = ScrollDown; 760 scrollGranularity = ScrollByDocument; 761 break; 762 case VKEY_PRIOR: // page up 763 scrollDirection = ScrollUp; 764 scrollGranularity = ScrollByPage; 765 break; 766 case VKEY_NEXT: // page down 767 scrollDirection = ScrollDown; 768 scrollGranularity = ScrollByPage; 769 break; 770 default: 771 return false; 772 } 773 774 return propagateScroll(scrollDirection, scrollGranularity); 775 } 776 777 bool WebViewImpl::propagateScroll(ScrollDirection scrollDirection, 778 ScrollGranularity scrollGranularity) 779 { 780 Frame* frame = focusedWebCoreFrame(); 781 if (!frame) 782 return false; 783 784 bool scrollHandled = 785 frame->eventHandler()->scrollOverflow(scrollDirection, 786 scrollGranularity); 787 Frame* currentFrame = frame; 788 while (!scrollHandled && currentFrame) { 789 scrollHandled = currentFrame->view()->scroll(scrollDirection, 790 scrollGranularity); 791 currentFrame = currentFrame->tree()->parent(); 792 } 793 return scrollHandled; 794 } 795 796 Frame* WebViewImpl::focusedWebCoreFrame() 797 { 798 return m_page.get() ? m_page->focusController()->focusedOrMainFrame() : 0; 799 } 800 801 WebViewImpl* WebViewImpl::fromPage(Page* page) 802 { 803 if (!page) 804 return 0; 805 806 return static_cast<ChromeClientImpl*>(page->chrome()->client())->webView(); 807 } 808 809 // WebWidget ------------------------------------------------------------------ 810 811 void WebViewImpl::close() 812 { 813 RefPtr<WebFrameImpl> mainFrameImpl; 814 815 if (m_page.get()) { 816 // Initiate shutdown for the entire frameset. This will cause a lot of 817 // notifications to be sent. 818 if (m_page->mainFrame()) { 819 mainFrameImpl = WebFrameImpl::fromFrame(m_page->mainFrame()); 820 m_page->mainFrame()->loader()->frameDetached(); 821 } 822 m_page.clear(); 823 } 824 825 // Should happen after m_page.clear(). 826 if (m_devToolsAgent.get()) 827 m_devToolsAgent.clear(); 828 829 // Reset the delegate to prevent notifications being sent as we're being 830 // deleted. 831 m_client = 0; 832 833 deref(); // Balances ref() acquired in WebView::create 834 } 835 836 void WebViewImpl::resize(const WebSize& newSize) 837 { 838 if (m_size == newSize) 839 return; 840 m_size = newSize; 841 842 if (mainFrameImpl()->frameView()) { 843 mainFrameImpl()->frameView()->resize(m_size.width, m_size.height); 844 mainFrameImpl()->frame()->eventHandler()->sendResizeEvent(); 845 } 846 847 if (m_client) { 848 WebRect damagedRect(0, 0, m_size.width, m_size.height); 849 m_client->didInvalidateRect(damagedRect); 850 } 851 } 852 853 void WebViewImpl::layout() 854 { 855 WebFrameImpl* webframe = mainFrameImpl(); 856 if (webframe) { 857 // In order for our child HWNDs (NativeWindowWidgets) to update properly, 858 // they need to be told that we are updating the screen. The problem is 859 // that the native widgets need to recalculate their clip region and not 860 // overlap any of our non-native widgets. To force the resizing, call 861 // setFrameRect(). This will be a quick operation for most frames, but 862 // the NativeWindowWidgets will update a proper clipping region. 863 FrameView* view = webframe->frameView(); 864 if (view) 865 view->setFrameRect(view->frameRect()); 866 867 // setFrameRect may have the side-effect of causing existing page 868 // layout to be invalidated, so layout needs to be called last. 869 870 webframe->layout(); 871 } 872 } 873 874 void WebViewImpl::paint(WebCanvas* canvas, const WebRect& rect) 875 { 876 WebFrameImpl* webframe = mainFrameImpl(); 877 if (webframe) 878 webframe->paint(canvas, rect); 879 } 880 881 // FIXME: m_currentInputEvent should be removed once ChromeClient::show() can 882 // get the current-event information from WebCore. 883 const WebInputEvent* WebViewImpl::m_currentInputEvent = 0; 884 885 bool WebViewImpl::handleInputEvent(const WebInputEvent& inputEvent) 886 { 887 // If we've started a drag and drop operation, ignore input events until 888 // we're done. 889 if (m_doingDragAndDrop) 890 return true; 891 892 if (m_ignoreInputEvents) 893 return true; 894 895 // FIXME: Remove m_currentInputEvent. 896 // This only exists to allow ChromeClient::show() to know which mouse button 897 // triggered a window.open event. 898 // Safari must perform a similar hack, ours is in our WebKit glue layer 899 // theirs is in the application. This should go when WebCore can be fixed 900 // to pass more event information to ChromeClient::show() 901 m_currentInputEvent = &inputEvent; 902 903 bool handled = true; 904 905 // FIXME: WebKit seems to always return false on mouse events processing 906 // methods. For now we'll assume it has processed them (as we are only 907 // interested in whether keyboard events are processed). 908 switch (inputEvent.type) { 909 case WebInputEvent::MouseMove: 910 mouseMove(*static_cast<const WebMouseEvent*>(&inputEvent)); 911 break; 912 913 case WebInputEvent::MouseLeave: 914 mouseLeave(*static_cast<const WebMouseEvent*>(&inputEvent)); 915 break; 916 917 case WebInputEvent::MouseWheel: 918 mouseWheel(*static_cast<const WebMouseWheelEvent*>(&inputEvent)); 919 break; 920 921 case WebInputEvent::MouseDown: 922 mouseDown(*static_cast<const WebMouseEvent*>(&inputEvent)); 923 break; 924 925 case WebInputEvent::MouseUp: 926 mouseUp(*static_cast<const WebMouseEvent*>(&inputEvent)); 927 break; 928 929 case WebInputEvent::RawKeyDown: 930 case WebInputEvent::KeyDown: 931 case WebInputEvent::KeyUp: 932 handled = keyEvent(*static_cast<const WebKeyboardEvent*>(&inputEvent)); 933 break; 934 935 case WebInputEvent::Char: 936 handled = charEvent(*static_cast<const WebKeyboardEvent*>(&inputEvent)); 937 break; 938 939 default: 940 handled = false; 941 } 942 943 m_currentInputEvent = 0; 944 945 return handled; 946 } 947 948 void WebViewImpl::mouseCaptureLost() 949 { 950 } 951 952 void WebViewImpl::setFocus(bool enable) 953 { 954 m_page->focusController()->setFocused(enable); 955 if (enable) { 956 // Note that we don't call setActive() when disabled as this cause extra 957 // focus/blur events to be dispatched. 958 m_page->focusController()->setActive(true); 959 RefPtr<Frame> focusedFrame = m_page->focusController()->focusedFrame(); 960 if (focusedFrame) { 961 Node* focusedNode = focusedFrame->document()->focusedNode(); 962 if (focusedNode && focusedNode->isElementNode() 963 && focusedFrame->selection()->selection().isNone()) { 964 // If the selection was cleared while the WebView was not 965 // focused, then the focus element shows with a focus ring but 966 // no caret and does respond to keyboard inputs. 967 Element* element = static_cast<Element*>(focusedNode); 968 if (element->isTextFormControl()) 969 element->updateFocusAppearance(true); 970 else if (focusedNode->isContentEditable()) { 971 // updateFocusAppearance() selects all the text of 972 // contentseditable DIVs. So we set the selection explicitly 973 // instead. Note that this has the side effect of moving the 974 // caret back to the beginning of the text. 975 Position position(focusedNode, 0, 976 Position::PositionIsOffsetInAnchor); 977 focusedFrame->selection()->setSelection( 978 VisibleSelection(position, SEL_DEFAULT_AFFINITY)); 979 } 980 } 981 } 982 m_imeAcceptEvents = true; 983 } else { 984 hideSuggestionsPopup(); 985 986 // Clear focus on the currently focused frame if any. 987 if (!m_page.get()) 988 return; 989 990 Frame* frame = m_page->mainFrame(); 991 if (!frame) 992 return; 993 994 RefPtr<Frame> focusedFrame = m_page->focusController()->focusedFrame(); 995 if (focusedFrame.get()) { 996 // Finish an ongoing composition to delete the composition node. 997 Editor* editor = focusedFrame->editor(); 998 if (editor && editor->hasComposition()) 999 editor->confirmComposition(); 1000 m_imeAcceptEvents = false; 1001 } 1002 } 1003 } 1004 1005 bool WebViewImpl::handleCompositionEvent(WebCompositionCommand command, 1006 int cursorPosition, 1007 int targetStart, 1008 int targetEnd, 1009 const WebString& imeString) 1010 { 1011 Frame* focused = focusedWebCoreFrame(); 1012 if (!focused || !m_imeAcceptEvents) 1013 return false; 1014 Editor* editor = focused->editor(); 1015 if (!editor) 1016 return false; 1017 if (!editor->canEdit()) { 1018 // The input focus has been moved to another WebWidget object. 1019 // We should use this |editor| object only to complete the ongoing 1020 // composition. 1021 if (!editor->hasComposition()) 1022 return false; 1023 } 1024 1025 // We should verify the parent node of this IME composition node are 1026 // editable because JavaScript may delete a parent node of the composition 1027 // node. In this case, WebKit crashes while deleting texts from the parent 1028 // node, which doesn't exist any longer. 1029 PassRefPtr<Range> range = editor->compositionRange(); 1030 if (range) { 1031 const Node* node = range->startPosition().node(); 1032 if (!node || !node->isContentEditable()) 1033 return false; 1034 } 1035 1036 if (command == WebCompositionCommandDiscard) { 1037 // A browser process sent an IPC message which does not contain a valid 1038 // string, which means an ongoing composition has been canceled. 1039 // If the ongoing composition has been canceled, replace the ongoing 1040 // composition string with an empty string and complete it. 1041 String emptyString; 1042 Vector<CompositionUnderline> emptyUnderlines; 1043 editor->setComposition(emptyString, emptyUnderlines, 0, 0); 1044 } else { 1045 // A browser process sent an IPC message which contains a string to be 1046 // displayed in this Editor object. 1047 // To display the given string, set the given string to the 1048 // m_compositionNode member of this Editor object and display it. 1049 if (targetStart < 0) 1050 targetStart = 0; 1051 if (targetEnd < 0) 1052 targetEnd = static_cast<int>(imeString.length()); 1053 String compositionString(imeString); 1054 // Create custom underlines. 1055 // To emphasize the selection, the selected region uses a solid black 1056 // for its underline while other regions uses a pale gray for theirs. 1057 Vector<CompositionUnderline> underlines(3); 1058 underlines[0].startOffset = 0; 1059 underlines[0].endOffset = targetStart; 1060 underlines[0].thick = true; 1061 underlines[0].color.setRGB(0xd3, 0xd3, 0xd3); 1062 underlines[1].startOffset = targetStart; 1063 underlines[1].endOffset = targetEnd; 1064 underlines[1].thick = true; 1065 underlines[1].color.setRGB(0x00, 0x00, 0x00); 1066 underlines[2].startOffset = targetEnd; 1067 underlines[2].endOffset = static_cast<int>(imeString.length()); 1068 underlines[2].thick = true; 1069 underlines[2].color.setRGB(0xd3, 0xd3, 0xd3); 1070 // When we use custom underlines, WebKit ("InlineTextBox.cpp" Line 282) 1071 // prevents from writing a text in between 'selectionStart' and 1072 // 'selectionEnd' somehow. 1073 // Therefore, we use the 'cursorPosition' for these arguments so that 1074 // there are not any characters in the above region. 1075 editor->setComposition(compositionString, underlines, 1076 cursorPosition, cursorPosition); 1077 // The given string is a result string, which means the ongoing 1078 // composition has been completed. I have to call the 1079 // Editor::confirmCompletion() and complete this composition. 1080 if (command == WebCompositionCommandConfirm) 1081 editor->confirmComposition(); 1082 } 1083 1084 return editor->hasComposition(); 1085 } 1086 1087 bool WebViewImpl::queryCompositionStatus(bool* enableIME, WebRect* caretRect) 1088 { 1089 // Store whether the selected node needs IME and the caret rectangle. 1090 // This process consists of the following four steps: 1091 // 1. Retrieve the selection controller of the focused frame; 1092 // 2. Retrieve the caret rectangle from the controller; 1093 // 3. Convert the rectangle, which is relative to the parent view, to the 1094 // one relative to the client window, and; 1095 // 4. Store the converted rectangle. 1096 const Frame* focused = focusedWebCoreFrame(); 1097 if (!focused) 1098 return false; 1099 1100 const Editor* editor = focused->editor(); 1101 if (!editor || !editor->canEdit()) 1102 return false; 1103 1104 SelectionController* controller = focused->selection(); 1105 if (!controller) 1106 return false; 1107 1108 const Node* node = controller->start().node(); 1109 if (!node) 1110 return false; 1111 1112 *enableIME = node->shouldUseInputMethod() && !controller->isInPasswordField(); 1113 const FrameView* view = node->document()->view(); 1114 if (!view) 1115 return false; 1116 1117 *caretRect = view->contentsToWindow(controller->absoluteCaretBounds()); 1118 return true; 1119 } 1120 1121 void WebViewImpl::setTextDirection(WebTextDirection direction) 1122 { 1123 // The Editor::setBaseWritingDirection() function checks if we can change 1124 // the text direction of the selected node and updates its DOM "dir" 1125 // attribute and its CSS "direction" property. 1126 // So, we just call the function as Safari does. 1127 const Frame* focused = focusedWebCoreFrame(); 1128 if (!focused) 1129 return; 1130 1131 Editor* editor = focused->editor(); 1132 if (!editor || !editor->canEdit()) 1133 return; 1134 1135 switch (direction) { 1136 case WebTextDirectionDefault: 1137 editor->setBaseWritingDirection(NaturalWritingDirection); 1138 break; 1139 1140 case WebTextDirectionLeftToRight: 1141 editor->setBaseWritingDirection(LeftToRightWritingDirection); 1142 break; 1143 1144 case WebTextDirectionRightToLeft: 1145 editor->setBaseWritingDirection(RightToLeftWritingDirection); 1146 break; 1147 1148 default: 1149 notImplemented(); 1150 break; 1151 } 1152 } 1153 1154 // WebView -------------------------------------------------------------------- 1155 1156 WebSettings* WebViewImpl::settings() 1157 { 1158 if (!m_webSettings.get()) 1159 m_webSettings.set(new WebSettingsImpl(m_page->settings())); 1160 ASSERT(m_webSettings.get()); 1161 return m_webSettings.get(); 1162 } 1163 1164 WebString WebViewImpl::pageEncoding() const 1165 { 1166 if (!m_page.get()) 1167 return WebString(); 1168 1169 return m_page->mainFrame()->loader()->encoding(); 1170 } 1171 1172 void WebViewImpl::setPageEncoding(const WebString& encodingName) 1173 { 1174 if (!m_page.get()) 1175 return; 1176 1177 // Only change override encoding, don't change default encoding. 1178 // Note that the new encoding must be 0 if it isn't supposed to be set. 1179 String newEncodingName; 1180 if (!encodingName.isEmpty()) 1181 newEncodingName = encodingName; 1182 m_page->mainFrame()->loader()->reloadWithOverrideEncoding(newEncodingName); 1183 } 1184 1185 bool WebViewImpl::dispatchBeforeUnloadEvent() 1186 { 1187 // FIXME: This should really cause a recursive depth-first walk of all 1188 // frames in the tree, calling each frame's onbeforeunload. At the moment, 1189 // we're consistent with Safari 3.1, not IE/FF. 1190 Frame* frame = m_page->mainFrame(); 1191 if (!frame) 1192 return true; 1193 1194 return frame->shouldClose(); 1195 } 1196 1197 void WebViewImpl::dispatchUnloadEvent() 1198 { 1199 // Run unload handlers. 1200 m_page->mainFrame()->loader()->closeURL(); 1201 } 1202 1203 WebFrame* WebViewImpl::mainFrame() 1204 { 1205 return mainFrameImpl(); 1206 } 1207 1208 WebFrame* WebViewImpl::findFrameByName( 1209 const WebString& name, WebFrame* relativeToFrame) 1210 { 1211 if (!relativeToFrame) 1212 relativeToFrame = mainFrame(); 1213 Frame* frame = static_cast<WebFrameImpl*>(relativeToFrame)->frame(); 1214 frame = frame->tree()->find(name); 1215 return WebFrameImpl::fromFrame(frame); 1216 } 1217 1218 WebFrame* WebViewImpl::focusedFrame() 1219 { 1220 return WebFrameImpl::fromFrame(focusedWebCoreFrame()); 1221 } 1222 1223 void WebViewImpl::setFocusedFrame(WebFrame* frame) 1224 { 1225 if (!frame) { 1226 // Clears the focused frame if any. 1227 Frame* frame = focusedWebCoreFrame(); 1228 if (frame) 1229 frame->selection()->setFocused(false); 1230 return; 1231 } 1232 WebFrameImpl* frameImpl = static_cast<WebFrameImpl*>(frame); 1233 Frame* webcoreFrame = frameImpl->frame(); 1234 webcoreFrame->page()->focusController()->setFocusedFrame(webcoreFrame); 1235 } 1236 1237 void WebViewImpl::setInitialFocus(bool reverse) 1238 { 1239 if (!m_page.get()) 1240 return; 1241 1242 // Since we don't have a keyboard event, we'll create one. 1243 WebKeyboardEvent keyboardEvent; 1244 keyboardEvent.type = WebInputEvent::RawKeyDown; 1245 if (reverse) 1246 keyboardEvent.modifiers = WebInputEvent::ShiftKey; 1247 1248 // VK_TAB which is only defined on Windows. 1249 keyboardEvent.windowsKeyCode = 0x09; 1250 PlatformKeyboardEventBuilder platformEvent(keyboardEvent); 1251 RefPtr<KeyboardEvent> webkitEvent = KeyboardEvent::create(platformEvent, 0); 1252 page()->focusController()->setInitialFocus( 1253 reverse ? FocusDirectionBackward : FocusDirectionForward, 1254 webkitEvent.get()); 1255 } 1256 1257 void WebViewImpl::clearFocusedNode() 1258 { 1259 if (!m_page.get()) 1260 return; 1261 1262 RefPtr<Frame> frame = m_page->mainFrame(); 1263 if (!frame.get()) 1264 return; 1265 1266 RefPtr<Document> document = frame->document(); 1267 if (!document.get()) 1268 return; 1269 1270 RefPtr<Node> oldFocusedNode = document->focusedNode(); 1271 1272 // Clear the focused node. 1273 document->setFocusedNode(0); 1274 1275 if (!oldFocusedNode.get()) 1276 return; 1277 1278 // If a text field has focus, we need to make sure the selection controller 1279 // knows to remove selection from it. Otherwise, the text field is still 1280 // processing keyboard events even though focus has been moved to the page and 1281 // keystrokes get eaten as a result. 1282 if (oldFocusedNode->hasTagName(HTMLNames::textareaTag) 1283 || (oldFocusedNode->hasTagName(HTMLNames::inputTag) 1284 && static_cast<HTMLInputElement*>(oldFocusedNode.get())->isTextField())) { 1285 // Clear the selection. 1286 SelectionController* selection = frame->selection(); 1287 selection->clear(); 1288 } 1289 } 1290 1291 int WebViewImpl::zoomLevel() 1292 { 1293 return m_zoomLevel; 1294 } 1295 1296 int WebViewImpl::setZoomLevel(bool textOnly, int zoomLevel) 1297 { 1298 float zoomFactor = static_cast<float>( 1299 std::max(std::min(std::pow(textSizeMultiplierRatio, zoomLevel), 1300 maxTextSizeMultiplier), 1301 minTextSizeMultiplier)); 1302 Frame* frame = mainFrameImpl()->frame(); 1303 if (zoomFactor != frame->zoomFactor()) { 1304 m_zoomLevel = zoomLevel; 1305 frame->setZoomFactor(zoomFactor, textOnly); 1306 } 1307 return m_zoomLevel; 1308 } 1309 1310 void WebViewImpl::performMediaPlayerAction(const WebMediaPlayerAction& action, 1311 const WebPoint& location) 1312 { 1313 HitTestResult result = 1314 hitTestResultForWindowPos(location); 1315 RefPtr<Node> node = result.innerNonSharedNode(); 1316 if (!node->hasTagName(HTMLNames::videoTag) && !node->hasTagName(HTMLNames::audioTag)) 1317 return; 1318 1319 RefPtr<HTMLMediaElement> mediaElement = 1320 static_pointer_cast<HTMLMediaElement>(node); 1321 switch (action.type) { 1322 case WebMediaPlayerAction::Play: 1323 if (action.enable) 1324 mediaElement->play(mediaElement->processingUserGesture()); 1325 else 1326 mediaElement->pause(mediaElement->processingUserGesture()); 1327 break; 1328 case WebMediaPlayerAction::Mute: 1329 mediaElement->setMuted(action.enable); 1330 break; 1331 case WebMediaPlayerAction::Loop: 1332 mediaElement->setLoop(action.enable); 1333 break; 1334 default: 1335 ASSERT_NOT_REACHED(); 1336 } 1337 } 1338 1339 void WebViewImpl::copyImageAt(const WebPoint& point) 1340 { 1341 if (!m_page.get()) 1342 return; 1343 1344 HitTestResult result = hitTestResultForWindowPos(point); 1345 1346 if (result.absoluteImageURL().isEmpty()) { 1347 // There isn't actually an image at these coordinates. Might be because 1348 // the window scrolled while the context menu was open or because the page 1349 // changed itself between when we thought there was an image here and when 1350 // we actually tried to retreive the image. 1351 // 1352 // FIXME: implement a cache of the most recent HitTestResult to avoid having 1353 // to do two hit tests. 1354 return; 1355 } 1356 1357 m_page->mainFrame()->editor()->copyImage(result); 1358 } 1359 1360 void WebViewImpl::dragSourceEndedAt( 1361 const WebPoint& clientPoint, 1362 const WebPoint& screenPoint, 1363 WebDragOperation operation) 1364 { 1365 PlatformMouseEvent pme(clientPoint, 1366 screenPoint, 1367 LeftButton, MouseEventMoved, 0, false, false, false, 1368 false, 0); 1369 m_page->mainFrame()->eventHandler()->dragSourceEndedAt(pme, 1370 static_cast<DragOperation>(operation)); 1371 } 1372 1373 void WebViewImpl::dragSourceSystemDragEnded() 1374 { 1375 // It's possible for us to get this callback while not doing a drag if 1376 // it's from a previous page that got unloaded. 1377 if (m_doingDragAndDrop) { 1378 m_page->dragController()->dragEnded(); 1379 m_doingDragAndDrop = false; 1380 } 1381 } 1382 1383 WebDragOperation WebViewImpl::dragTargetDragEnter( 1384 const WebDragData& webDragData, int identity, 1385 const WebPoint& clientPoint, 1386 const WebPoint& screenPoint, 1387 WebDragOperationsMask operationsAllowed) 1388 { 1389 ASSERT(!m_currentDragData.get()); 1390 1391 m_currentDragData = webDragData; 1392 m_dragIdentity = identity; 1393 m_operationsAllowed = operationsAllowed; 1394 1395 DragData dragData( 1396 m_currentDragData.get(), 1397 clientPoint, 1398 screenPoint, 1399 static_cast<DragOperation>(operationsAllowed)); 1400 1401 m_dropEffect = DropEffectDefault; 1402 m_dragTargetDispatch = true; 1403 DragOperation effect = m_page->dragController()->dragEntered(&dragData); 1404 // Mask the operation against the drag source's allowed operations. 1405 if ((effect & dragData.draggingSourceOperationMask()) != effect) 1406 effect = DragOperationNone; 1407 m_dragTargetDispatch = false; 1408 1409 if (m_dropEffect != DropEffectDefault) { 1410 m_dragOperation = (m_dropEffect != DropEffectNone) ? WebDragOperationCopy 1411 : WebDragOperationNone; 1412 } else 1413 m_dragOperation = static_cast<WebDragOperation>(effect); 1414 return m_dragOperation; 1415 } 1416 1417 WebDragOperation WebViewImpl::dragTargetDragOver( 1418 const WebPoint& clientPoint, 1419 const WebPoint& screenPoint, 1420 WebDragOperationsMask operationsAllowed) 1421 { 1422 ASSERT(m_currentDragData.get()); 1423 1424 m_operationsAllowed = operationsAllowed; 1425 DragData dragData( 1426 m_currentDragData.get(), 1427 clientPoint, 1428 screenPoint, 1429 static_cast<DragOperation>(operationsAllowed)); 1430 1431 m_dropEffect = DropEffectDefault; 1432 m_dragTargetDispatch = true; 1433 DragOperation effect = m_page->dragController()->dragUpdated(&dragData); 1434 // Mask the operation against the drag source's allowed operations. 1435 if ((effect & dragData.draggingSourceOperationMask()) != effect) 1436 effect = DragOperationNone; 1437 m_dragTargetDispatch = false; 1438 1439 if (m_dropEffect != DropEffectDefault) { 1440 m_dragOperation = (m_dropEffect != DropEffectNone) ? WebDragOperationCopy 1441 : WebDragOperationNone; 1442 } else 1443 m_dragOperation = static_cast<WebDragOperation>(effect); 1444 return m_dragOperation; 1445 } 1446 1447 void WebViewImpl::dragTargetDragLeave() 1448 { 1449 ASSERT(m_currentDragData.get()); 1450 1451 DragData dragData( 1452 m_currentDragData.get(), 1453 IntPoint(), 1454 IntPoint(), 1455 static_cast<DragOperation>(m_operationsAllowed)); 1456 1457 m_dragTargetDispatch = true; 1458 m_page->dragController()->dragExited(&dragData); 1459 m_dragTargetDispatch = false; 1460 1461 m_currentDragData = 0; 1462 m_dropEffect = DropEffectDefault; 1463 m_dragOperation = WebDragOperationNone; 1464 m_dragIdentity = 0; 1465 } 1466 1467 void WebViewImpl::dragTargetDrop(const WebPoint& clientPoint, 1468 const WebPoint& screenPoint) 1469 { 1470 ASSERT(m_currentDragData.get()); 1471 1472 // If this webview transitions from the "drop accepting" state to the "not 1473 // accepting" state, then our IPC message reply indicating that may be in- 1474 // flight, or else delayed by javascript processing in this webview. If a 1475 // drop happens before our IPC reply has reached the browser process, then 1476 // the browser forwards the drop to this webview. So only allow a drop to 1477 // proceed if our webview m_dragOperation state is not DragOperationNone. 1478 1479 if (m_dragOperation == WebDragOperationNone) { // IPC RACE CONDITION: do not allow this drop. 1480 dragTargetDragLeave(); 1481 return; 1482 } 1483 1484 DragData dragData( 1485 m_currentDragData.get(), 1486 clientPoint, 1487 screenPoint, 1488 static_cast<DragOperation>(m_operationsAllowed)); 1489 1490 m_dragTargetDispatch = true; 1491 m_page->dragController()->performDrag(&dragData); 1492 m_dragTargetDispatch = false; 1493 1494 m_currentDragData = 0; 1495 m_dropEffect = DropEffectDefault; 1496 m_dragOperation = WebDragOperationNone; 1497 m_dragIdentity = 0; 1498 } 1499 1500 int WebViewImpl::dragIdentity() 1501 { 1502 if (m_dragTargetDispatch) 1503 return m_dragIdentity; 1504 return 0; 1505 } 1506 1507 unsigned long WebViewImpl::createUniqueIdentifierForRequest() { 1508 if (m_page) 1509 return m_page->progress()->createUniqueIdentifier(); 1510 return 0; 1511 } 1512 1513 void WebViewImpl::inspectElementAt(const WebPoint& point) 1514 { 1515 if (!m_page.get()) 1516 return; 1517 1518 if (point.x == -1 || point.y == -1) 1519 m_page->inspectorController()->inspect(0); 1520 else { 1521 HitTestResult result = hitTestResultForWindowPos(point); 1522 1523 if (!result.innerNonSharedNode()) 1524 return; 1525 1526 m_page->inspectorController()->inspect(result.innerNonSharedNode()); 1527 } 1528 } 1529 1530 WebString WebViewImpl::inspectorSettings() const 1531 { 1532 return m_inspectorSettings; 1533 } 1534 1535 void WebViewImpl::setInspectorSettings(const WebString& settings) 1536 { 1537 m_inspectorSettings = settings; 1538 } 1539 1540 WebDevToolsAgent* WebViewImpl::devToolsAgent() 1541 { 1542 return m_devToolsAgent.get(); 1543 } 1544 1545 void WebViewImpl::setDevToolsAgent(WebDevToolsAgent* devToolsAgent) 1546 { 1547 ASSERT(!m_devToolsAgent.get()); // May only set once! 1548 m_devToolsAgent.set(static_cast<WebDevToolsAgentPrivate*>(devToolsAgent)); 1549 } 1550 1551 WebAccessibilityObject WebViewImpl::accessibilityObject() 1552 { 1553 if (!mainFrameImpl()) 1554 return WebAccessibilityObject(); 1555 1556 Document* document = mainFrameImpl()->frame()->document(); 1557 return WebAccessibilityObject( 1558 document->axObjectCache()->getOrCreate(document->renderer())); 1559 } 1560 1561 void WebViewImpl::applyAutofillSuggestions( 1562 const WebNode& node, 1563 const WebVector<WebString>& suggestions, 1564 int defaultSuggestionIndex) 1565 { 1566 applyAutocompleteSuggestions(node, suggestions, defaultSuggestionIndex); 1567 } 1568 1569 void WebViewImpl::applyAutoFillSuggestions( 1570 const WebNode& node, 1571 const WebVector<WebString>& names, 1572 const WebVector<WebString>& labels, 1573 int defaultSuggestionIndex) 1574 { 1575 ASSERT(names.size() == labels.size()); 1576 ASSERT(defaultSuggestionIndex < static_cast<int>(names.size())); 1577 1578 if (names.isEmpty()) { 1579 hideSuggestionsPopup(); 1580 return; 1581 } 1582 1583 RefPtr<Node> focusedNode = focusedWebCoreNode(); 1584 // If the node for which we queried the AutoFill suggestions is not the 1585 // focused node, then we have nothing to do. FIXME: also check the 1586 // caret is at the end and that the text has not changed. 1587 if (!focusedNode || focusedNode != PassRefPtr<Node>(node)) { 1588 hideSuggestionsPopup(); 1589 return; 1590 } 1591 1592 HTMLInputElement* inputElem = 1593 static_cast<HTMLInputElement*>(focusedNode.get()); 1594 1595 // The first time the AutoFill popup is shown we'll create the client and 1596 // the popup. 1597 if (!m_autoFillPopupClient.get()) 1598 m_autoFillPopupClient.set(new AutoFillPopupMenuClient); 1599 1600 m_autoFillPopupClient->initialize(inputElem, names, labels, 1601 defaultSuggestionIndex); 1602 1603 if (m_suggestionsPopupClient != m_autoFillPopupClient.get()) { 1604 hideSuggestionsPopup(); 1605 m_suggestionsPopupClient = m_autoFillPopupClient.get(); 1606 } 1607 1608 if (!m_autoFillPopup.get()) { 1609 m_autoFillPopup = PopupContainer::create(m_suggestionsPopupClient, 1610 suggestionsPopupSettings); 1611 } 1612 1613 if (m_suggestionsPopup != m_autoFillPopup.get()) 1614 m_suggestionsPopup = m_autoFillPopup.get(); 1615 1616 if (m_suggestionsPopupShowing) { 1617 m_autoFillPopupClient->setSuggestions(names, labels); 1618 refreshSuggestionsPopup(); 1619 } else { 1620 m_suggestionsPopup->show(focusedNode->getRect(), 1621 focusedNode->ownerDocument()->view(), 0); 1622 m_suggestionsPopupShowing = true; 1623 } 1624 } 1625 1626 void WebViewImpl::applyAutocompleteSuggestions( 1627 const WebNode& node, 1628 const WebVector<WebString>& suggestions, 1629 int defaultSuggestionIndex) 1630 { 1631 ASSERT(defaultSuggestionIndex < static_cast<int>(suggestions.size())); 1632 1633 if (!m_page.get() || suggestions.isEmpty()) { 1634 hideSuggestionsPopup(); 1635 return; 1636 } 1637 1638 RefPtr<Node> focusedNode = focusedWebCoreNode(); 1639 // If the node for which we queried the Autocomplete suggestions is not the 1640 // focused node, then we have nothing to do. FIXME: also check the 1641 // caret is at the end and that the text has not changed. 1642 if (!focusedNode || focusedNode != PassRefPtr<Node>(node)) { 1643 hideSuggestionsPopup(); 1644 return; 1645 } 1646 1647 HTMLInputElement* inputElem = 1648 static_cast<HTMLInputElement*>(focusedNode.get()); 1649 1650 // The first time the Autocomplete is shown we'll create the client and the 1651 // popup. 1652 if (!m_autocompletePopupClient.get()) 1653 m_autocompletePopupClient.set(new AutocompletePopupMenuClient); 1654 1655 m_autocompletePopupClient->initialize(inputElem, suggestions, 1656 defaultSuggestionIndex); 1657 1658 if (m_suggestionsPopupClient != m_autocompletePopupClient.get()) { 1659 hideSuggestionsPopup(); 1660 m_suggestionsPopupClient = m_autocompletePopupClient.get(); 1661 } 1662 1663 if (!m_autocompletePopup.get()) { 1664 m_autocompletePopup = PopupContainer::create(m_suggestionsPopupClient, 1665 suggestionsPopupSettings); 1666 } 1667 1668 if (m_suggestionsPopup != m_autocompletePopup.get()) 1669 m_suggestionsPopup = m_autocompletePopup.get(); 1670 1671 if (m_suggestionsPopupShowing) { 1672 m_autocompletePopupClient->setSuggestions(suggestions); 1673 refreshSuggestionsPopup(); 1674 } else { 1675 m_suggestionsPopup->show(focusedNode->getRect(), 1676 focusedNode->ownerDocument()->view(), 0); 1677 m_suggestionsPopupShowing = true; 1678 } 1679 } 1680 1681 void WebViewImpl::hideAutofillPopup() 1682 { 1683 hideSuggestionsPopup(); 1684 } 1685 1686 void WebViewImpl::hideSuggestionsPopup() 1687 { 1688 if (m_suggestionsPopupShowing) { 1689 m_suggestionsPopup->hidePopup(); 1690 m_suggestionsPopupShowing = false; 1691 } 1692 } 1693 1694 void WebViewImpl::performCustomContextMenuAction(unsigned action) 1695 { 1696 if (!m_page) 1697 return; 1698 ContextMenu* menu = m_page->contextMenuController()->contextMenu(); 1699 if (!menu) 1700 return; 1701 ContextMenuItem* item = menu->itemWithAction(static_cast<ContextMenuAction>(ContextMenuItemBaseCustomTag + action)); 1702 if (item) 1703 m_page->contextMenuController()->contextMenuItemSelected(item); 1704 m_page->contextMenuController()->clearContextMenu(); 1705 } 1706 1707 // WebView -------------------------------------------------------------------- 1708 1709 bool WebViewImpl::setDropEffect(bool accept) 1710 { 1711 if (m_dragTargetDispatch) { 1712 m_dropEffect = accept ? DropEffectCopy : DropEffectNone; 1713 return true; 1714 } 1715 return false; 1716 } 1717 1718 void WebViewImpl::setIsTransparent(bool isTransparent) 1719 { 1720 // Set any existing frames to be transparent. 1721 Frame* frame = m_page->mainFrame(); 1722 while (frame) { 1723 frame->view()->setTransparent(isTransparent); 1724 frame = frame->tree()->traverseNext(); 1725 } 1726 1727 // Future frames check this to know whether to be transparent. 1728 m_isTransparent = isTransparent; 1729 } 1730 1731 bool WebViewImpl::isTransparent() const 1732 { 1733 return m_isTransparent; 1734 } 1735 1736 void WebViewImpl::setIsActive(bool active) 1737 { 1738 if (page() && page()->focusController()) 1739 page()->focusController()->setActive(active); 1740 } 1741 1742 bool WebViewImpl::isActive() const 1743 { 1744 return (page() && page()->focusController()) ? page()->focusController()->isActive() : false; 1745 } 1746 1747 void WebViewImpl::setScrollbarColors(unsigned inactiveColor, 1748 unsigned activeColor, 1749 unsigned trackColor) { 1750 #if OS(LINUX) 1751 RenderThemeChromiumLinux::setScrollbarColors(inactiveColor, 1752 activeColor, 1753 trackColor); 1754 #endif 1755 } 1756 1757 void WebViewImpl::setSelectionColors(unsigned activeBackgroundColor, 1758 unsigned activeForegroundColor, 1759 unsigned inactiveBackgroundColor, 1760 unsigned inactiveForegroundColor) { 1761 #if OS(LINUX) 1762 RenderThemeChromiumLinux::setSelectionColors(activeBackgroundColor, 1763 activeForegroundColor, 1764 inactiveBackgroundColor, 1765 inactiveForegroundColor); 1766 theme()->platformColorsDidChange(); 1767 #endif 1768 } 1769 1770 void WebViewImpl::addUserScript(const WebString& sourceCode, bool runAtStart) 1771 { 1772 PageGroup* pageGroup = PageGroup::pageGroup(pageGroupName); 1773 RefPtr<DOMWrapperWorld> world(DOMWrapperWorld::create()); 1774 pageGroup->addUserScriptToWorld(world.get(), sourceCode, WebURL(), 0, 0, 1775 runAtStart ? InjectAtDocumentStart : InjectAtDocumentEnd); 1776 } 1777 1778 void WebViewImpl::removeAllUserContent() 1779 { 1780 PageGroup* pageGroup = PageGroup::pageGroup(pageGroupName); 1781 pageGroup->removeAllUserContent(); 1782 } 1783 1784 void WebViewImpl::didCommitLoad(bool* isNewNavigation) 1785 { 1786 if (isNewNavigation) 1787 *isNewNavigation = m_observedNewNavigation; 1788 1789 #ifndef NDEBUG 1790 ASSERT(!m_observedNewNavigation 1791 || m_page->mainFrame()->loader()->documentLoader() == m_newNavigationLoader); 1792 m_newNavigationLoader = 0; 1793 #endif 1794 m_observedNewNavigation = false; 1795 } 1796 1797 bool WebViewImpl::navigationPolicyFromMouseEvent(unsigned short button, 1798 bool ctrl, bool shift, 1799 bool alt, bool meta, 1800 WebNavigationPolicy* policy) 1801 { 1802 #if OS(WINDOWS) || OS(LINUX) || OS(FREEBSD) 1803 const bool newTabModifier = (button == 1) || ctrl; 1804 #elif OS(DARWIN) 1805 const bool newTabModifier = (button == 1) || meta; 1806 #endif 1807 if (!newTabModifier && !shift && !alt) 1808 return false; 1809 1810 ASSERT(policy); 1811 if (newTabModifier) { 1812 if (shift) 1813 *policy = WebNavigationPolicyNewForegroundTab; 1814 else 1815 *policy = WebNavigationPolicyNewBackgroundTab; 1816 } else { 1817 if (shift) 1818 *policy = WebNavigationPolicyNewWindow; 1819 else 1820 *policy = WebNavigationPolicyDownload; 1821 } 1822 return true; 1823 } 1824 1825 void WebViewImpl::startDragging(const WebPoint& eventPos, 1826 const WebDragData& dragData, 1827 WebDragOperationsMask mask) 1828 { 1829 if (!m_client) 1830 return; 1831 ASSERT(!m_doingDragAndDrop); 1832 m_doingDragAndDrop = true; 1833 m_client->startDragging(eventPos, dragData, mask); 1834 } 1835 1836 void WebViewImpl::setCurrentHistoryItem(HistoryItem* item) 1837 { 1838 m_backForwardListClientImpl.setCurrentHistoryItem(item); 1839 } 1840 1841 HistoryItem* WebViewImpl::previousHistoryItem() 1842 { 1843 return m_backForwardListClientImpl.previousHistoryItem(); 1844 } 1845 1846 void WebViewImpl::observeNewNavigation() 1847 { 1848 m_observedNewNavigation = true; 1849 #ifndef NDEBUG 1850 m_newNavigationLoader = m_page->mainFrame()->loader()->documentLoader(); 1851 #endif 1852 } 1853 1854 void WebViewImpl::setIgnoreInputEvents(bool newValue) 1855 { 1856 ASSERT(m_ignoreInputEvents != newValue); 1857 m_ignoreInputEvents = newValue; 1858 } 1859 1860 #if ENABLE(NOTIFICATIONS) 1861 NotificationPresenterImpl* WebViewImpl::notificationPresenterImpl() 1862 { 1863 if (!m_notificationPresenter.isInitialized() && m_client) 1864 m_notificationPresenter.initialize(m_client->notificationPresenter()); 1865 return &m_notificationPresenter; 1866 } 1867 #endif 1868 1869 void WebViewImpl::refreshSuggestionsPopup() 1870 { 1871 ASSERT(m_suggestionsPopupShowing); 1872 1873 // Hide the popup if it has become empty. 1874 if (!m_autocompletePopupClient->listSize()) { 1875 hideSuggestionsPopup(); 1876 return; 1877 } 1878 1879 IntRect oldBounds = m_suggestionsPopup->boundsRect(); 1880 m_suggestionsPopup->refresh(); 1881 IntRect newBounds = m_suggestionsPopup->boundsRect(); 1882 // Let's resize the backing window if necessary. 1883 if (oldBounds != newBounds) { 1884 WebPopupMenuImpl* popupMenu = 1885 static_cast<WebPopupMenuImpl*>(m_suggestionsPopup->client()); 1886 popupMenu->client()->setWindowRect(newBounds); 1887 } 1888 } 1889 1890 Node* WebViewImpl::focusedWebCoreNode() 1891 { 1892 Frame* frame = m_page->focusController()->focusedFrame(); 1893 if (!frame) 1894 return 0; 1895 1896 Document* document = frame->document(); 1897 if (!document) 1898 return 0; 1899 1900 return document->focusedNode(); 1901 } 1902 1903 HitTestResult WebViewImpl::hitTestResultForWindowPos(const IntPoint& pos) 1904 { 1905 IntPoint docPoint(m_page->mainFrame()->view()->windowToContents(pos)); 1906 return m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(docPoint, false); 1907 } 1908 1909 void WebViewImpl::setTabsToLinks(bool enable) 1910 { 1911 m_tabsToLinks = enable; 1912 } 1913 1914 bool WebViewImpl::tabsToLinks() const 1915 { 1916 return m_tabsToLinks; 1917 } 1918 1919 } // namespace WebKit 1920