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