2009-12-14 Evan Stade <estade@chromium.org>
[webbrowser.git] / WebKit / chromium / src / WebViewImpl.cpp
blob87a150a7490fa94d7c63b4ef5376be1a3a2940c9
1 /*
2 * Copyright (C) 2009 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
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.
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.
31 #include "config.h"
32 #include "WebViewImpl.h"
34 #include "AutocompletePopupMenuClient.h"
35 #include "AXObjectCache.h"
36 #include "ContextMenu.h"
37 #include "ContextMenuController.h"
38 #include "ContextMenuItem.h"
39 #include "CSSStyleSelector.h"
40 #include "CSSValueKeywords.h"
41 #include "Cursor.h"
42 #include "Document.h"
43 #include "DocumentLoader.h"
44 #include "DOMUtilitiesPrivate.h"
45 #include "DragController.h"
46 #include "DragData.h"
47 #include "Editor.h"
48 #include "EventHandler.h"
49 #include "FocusController.h"
50 #include "FontDescription.h"
51 #include "FrameLoader.h"
52 #include "FrameTree.h"
53 #include "FrameView.h"
54 #include "GraphicsContext.h"
55 #include "HitTestResult.h"
56 #include "HTMLInputElement.h"
57 #include "HTMLMediaElement.h"
58 #include "HTMLNames.h"
59 #include "Image.h"
60 #include "InspectorController.h"
61 #include "IntRect.h"
62 #include "KeyboardCodes.h"
63 #include "KeyboardEvent.h"
64 #include "MIMETypeRegistry.h"
65 #include "NodeRenderStyle.h"
66 #include "Page.h"
67 #include "PageGroup.h"
68 #include "Pasteboard.h"
69 #include "PlatformContextSkia.h"
70 #include "PlatformKeyboardEvent.h"
71 #include "PlatformMouseEvent.h"
72 #include "PlatformWheelEvent.h"
73 #include "PluginInfoStore.h"
74 #include "PopupMenuChromium.h"
75 #include "PopupMenuClient.h"
76 #include "ProgressTracker.h"
77 #include "RenderView.h"
78 #include "ResourceHandle.h"
79 #include "SecurityOrigin.h"
80 #include "SelectionController.h"
81 #include "Settings.h"
82 #include "TypingCommand.h"
83 #include "WebAccessibilityObject.h"
84 #include "WebDevToolsAgentPrivate.h"
85 #include "WebDragData.h"
86 #include "WebFrameImpl.h"
87 #include "WebInputEvent.h"
88 #include "WebInputEventConversion.h"
89 #include "WebMediaPlayerAction.h"
90 #include "WebNode.h"
91 #include "WebPoint.h"
92 #include "WebPopupMenuImpl.h"
93 #include "WebRect.h"
94 #include "WebSettingsImpl.h"
95 #include "WebString.h"
96 #include "WebVector.h"
97 #include "WebViewClient.h"
99 #if PLATFORM(WIN_OS)
100 #include "KeyboardCodesWin.h"
101 #include "RenderThemeChromiumWin.h"
102 #else
103 #if PLATFORM(LINUX)
104 #include "RenderThemeChromiumLinux.h"
105 #endif
106 #include "KeyboardCodesPosix.h"
107 #include "RenderTheme.h"
108 #endif
110 // Get rid of WTF's pow define so we can use std::pow.
111 #undef pow
112 #include <cmath> // for std::pow
114 using namespace WebCore;
116 namespace WebKit {
118 // Change the text zoom level by kTextSizeMultiplierRatio each time the user
119 // zooms text in or out (ie., change by 20%). The min and max values limit
120 // text zoom to half and 3x the original text size. These three values match
121 // those in Apple's port in WebKit/WebKit/WebView/WebView.mm
122 static const double textSizeMultiplierRatio = 1.2;
123 static const double minTextSizeMultiplier = 0.5;
124 static const double maxTextSizeMultiplier = 3.0;
126 // The group name identifies a namespace of pages. Page group is used on OSX
127 // for some programs that use HTML views to display things that don't seem like
128 // web pages to the user (so shouldn't have visited link coloring). We only use
129 // one page group.
130 const char* pageGroupName = "default";
132 // Ensure that the WebDragOperation enum values stay in sync with the original
133 // DragOperation constants.
134 #define COMPILE_ASSERT_MATCHING_ENUM(coreName) \
135 COMPILE_ASSERT(int(coreName) == int(Web##coreName), dummy##coreName)
136 COMPILE_ASSERT_MATCHING_ENUM(DragOperationNone);
137 COMPILE_ASSERT_MATCHING_ENUM(DragOperationCopy);
138 COMPILE_ASSERT_MATCHING_ENUM(DragOperationLink);
139 COMPILE_ASSERT_MATCHING_ENUM(DragOperationGeneric);
140 COMPILE_ASSERT_MATCHING_ENUM(DragOperationPrivate);
141 COMPILE_ASSERT_MATCHING_ENUM(DragOperationMove);
142 COMPILE_ASSERT_MATCHING_ENUM(DragOperationDelete);
143 COMPILE_ASSERT_MATCHING_ENUM(DragOperationEvery);
145 // Note that focusOnShow is false so that the autocomplete popup is shown not
146 // activated. We need the page to still have focus so the user can keep typing
147 // while the popup is showing.
148 static const PopupContainerSettings autocompletePopupSettings = {
149 false, // focusOnShow
150 false, // setTextOnIndexChange
151 false, // acceptOnAbandon
152 true, // loopSelectionNavigation
153 true, // restrictWidthOfListBox. Same as other browser (Fx, IE, and safari)
154 // For autocomplete, we use the direction of the input field as the direction
155 // of the popup items. The main reason is to keep the display of items in
156 // drop-down the same as the items in the input field.
157 PopupContainerSettings::DOMElementDirection,
160 // WebView ----------------------------------------------------------------
162 WebView* WebView::create(WebViewClient* client)
164 return new WebViewImpl(client);
167 void WebView::updateVisitedLinkState(unsigned long long linkHash)
169 Page::visitedStateChanged(PageGroup::pageGroup(pageGroupName), linkHash);
172 void WebView::resetVisitedLinkState()
174 Page::allVisitedStateChanged(PageGroup::pageGroup(pageGroupName));
177 void WebViewImpl::initializeMainFrame(WebFrameClient* frameClient)
179 // NOTE: The WebFrameImpl takes a reference to itself within InitMainFrame
180 // and releases that reference once the corresponding Frame is destroyed.
181 RefPtr<WebFrameImpl> frame = WebFrameImpl::create(frameClient);
183 frame->initializeAsMainFrame(this);
185 // Restrict the access to the local file system
186 // (see WebView.mm WebView::_commonInitializationWithFrameName).
187 SecurityOrigin::setLocalLoadPolicy(SecurityOrigin::AllowLocalLoadsForLocalOnly);
190 WebViewImpl::WebViewImpl(WebViewClient* client)
191 : m_client(client)
192 , m_backForwardListClientImpl(this)
193 , m_chromeClientImpl(this)
194 , m_contextMenuClientImpl(this)
195 , m_dragClientImpl(this)
196 , m_editorClientImpl(this)
197 , m_inspectorClientImpl(this)
198 , m_observedNewNavigation(false)
199 #ifndef NDEBUG
200 , m_newNavigationLoader(0)
201 #endif
202 , m_zoomLevel(0)
203 , m_contextMenuAllowed(false)
204 , m_doingDragAndDrop(false)
205 , m_ignoreInputEvents(false)
206 , m_suppressNextKeypressEvent(false)
207 , m_initialNavigationPolicy(WebNavigationPolicyIgnore)
208 , m_imeAcceptEvents(true)
209 , m_dragTargetDispatch(false)
210 , m_dragIdentity(0)
211 , m_dropEffect(DropEffectDefault)
212 , m_operationsAllowed(WebDragOperationNone)
213 , m_dragOperation(WebDragOperationNone)
214 , m_autocompletePopupShowing(false)
215 , m_isTransparent(false)
216 , m_tabsToLinks(false)
218 // WebKit/win/WebView.cpp does the same thing, except they call the
219 // KJS specific wrapper around this method. We need to have threading
220 // initialized because CollatorICU requires it.
221 WTF::initializeThreading();
223 // set to impossible point so we always get the first mouse pos
224 m_lastMousePosition = WebPoint(-1, -1);
226 // the page will take ownership of the various clients
227 m_page.set(new Page(&m_chromeClientImpl,
228 &m_contextMenuClientImpl,
229 &m_editorClientImpl,
230 &m_dragClientImpl,
231 &m_inspectorClientImpl,
233 0));
235 m_page->backForwardList()->setClient(&m_backForwardListClientImpl);
236 m_page->setGroupName(pageGroupName);
239 WebViewImpl::~WebViewImpl()
241 ASSERT(!m_page);
244 RenderTheme* WebViewImpl::theme() const
246 return m_page.get() ? m_page->theme() : RenderTheme::defaultTheme().get();
249 WebFrameImpl* WebViewImpl::mainFrameImpl()
251 return m_page.get() ? WebFrameImpl::fromFrame(m_page->mainFrame()) : 0;
254 bool WebViewImpl::tabKeyCyclesThroughElements() const
256 ASSERT(m_page.get());
257 return m_page->tabKeyCyclesThroughElements();
260 void WebViewImpl::setTabKeyCyclesThroughElements(bool value)
262 if (m_page)
263 m_page->setTabKeyCyclesThroughElements(value);
266 void WebViewImpl::mouseMove(const WebMouseEvent& event)
268 if (!mainFrameImpl() || !mainFrameImpl()->frameView())
269 return;
271 m_lastMousePosition = WebPoint(event.x, event.y);
273 // We call mouseMoved here instead of handleMouseMovedEvent because we need
274 // our ChromeClientImpl to receive changes to the mouse position and
275 // tooltip text, and mouseMoved handles all of that.
276 mainFrameImpl()->frame()->eventHandler()->mouseMoved(
277 PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event));
280 void WebViewImpl::mouseLeave(const WebMouseEvent& event)
282 // This event gets sent as the main frame is closing. In that case, just
283 // ignore it.
284 if (!mainFrameImpl() || !mainFrameImpl()->frameView())
285 return;
287 m_client->setMouseOverURL(WebURL());
289 mainFrameImpl()->frame()->eventHandler()->handleMouseMoveEvent(
290 PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event));
293 void WebViewImpl::mouseDown(const WebMouseEvent& event)
295 if (!mainFrameImpl() || !mainFrameImpl()->frameView())
296 return;
298 m_lastMouseDownPoint = WebPoint(event.x, event.y);
300 // If a text field that has focus is clicked again, we should display the
301 // autocomplete popup.
302 RefPtr<Node> clickedNode;
303 if (event.button == WebMouseEvent::ButtonLeft) {
304 RefPtr<Node> focusedNode = focusedWebCoreNode();
305 if (focusedNode.get() && toHTMLInputElement(focusedNode.get())) {
306 IntPoint point(event.x, event.y);
307 point = m_page->mainFrame()->view()->windowToContents(point);
308 HitTestResult result(point);
309 result = m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(point, false);
310 if (result.innerNonSharedNode() == focusedNode) {
311 // Already focused text field was clicked, let's remember this. If
312 // focus has not changed after the mouse event is processed, we'll
313 // trigger the autocomplete.
314 clickedNode = focusedNode;
319 mainFrameImpl()->frame()->eventHandler()->handleMousePressEvent(
320 PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event));
322 if (clickedNode.get() && clickedNode == focusedWebCoreNode()) {
323 // Focus has not changed, show the autocomplete popup.
324 static_cast<EditorClientImpl*>(m_page->editorClient())->
325 showFormAutofillForNode(clickedNode.get());
328 // Dispatch the contextmenu event regardless of if the click was swallowed.
329 // On Windows, we handle it on mouse up, not down.
330 #if PLATFORM(DARWIN)
331 if (event.button == WebMouseEvent::ButtonRight
332 || (event.button == WebMouseEvent::ButtonLeft
333 && event.modifiers & WebMouseEvent::ControlKey))
334 mouseContextMenu(event);
335 #elif PLATFORM(LINUX)
336 if (event.button == WebMouseEvent::ButtonRight)
337 mouseContextMenu(event);
338 #endif
341 void WebViewImpl::mouseContextMenu(const WebMouseEvent& event)
343 if (!mainFrameImpl() || !mainFrameImpl()->frameView())
344 return;
346 m_page->contextMenuController()->clearContextMenu();
348 PlatformMouseEventBuilder pme(mainFrameImpl()->frameView(), event);
350 // Find the right target frame. See issue 1186900.
351 HitTestResult result = hitTestResultForWindowPos(pme.pos());
352 Frame* targetFrame;
353 if (result.innerNonSharedNode())
354 targetFrame = result.innerNonSharedNode()->document()->frame();
355 else
356 targetFrame = m_page->focusController()->focusedOrMainFrame();
358 #if PLATFORM(WIN_OS)
359 targetFrame->view()->setCursor(pointerCursor());
360 #endif
362 m_contextMenuAllowed = true;
363 targetFrame->eventHandler()->sendContextMenuEvent(pme);
364 m_contextMenuAllowed = false;
365 // Actually showing the context menu is handled by the ContextMenuClient
366 // implementation...
369 void WebViewImpl::mouseUp(const WebMouseEvent& event)
371 if (!mainFrameImpl() || !mainFrameImpl()->frameView())
372 return;
374 #if PLATFORM(LINUX)
375 // If the event was a middle click, attempt to copy text into the focused
376 // frame. We execute this before we let the page have a go at the event
377 // because the page may change what is focused during in its event handler.
379 // This code is in the mouse up handler. There is some debate about putting
380 // this here, as opposed to the mouse down handler.
381 // xterm: pastes on up.
382 // GTK: pastes on down.
383 // Firefox: pastes on up.
384 // Midori: couldn't paste at all with 0.1.2
386 // There is something of a webcompat angle to this well, as highlighted by
387 // crbug.com/14608. Pages can clear text boxes 'onclick' and, if we paste on
388 // down then the text is pasted just before the onclick handler runs and
389 // clears the text box. So it's important this happens after the
390 // handleMouseReleaseEvent() earlier in this function
391 if (event.button == WebMouseEvent::ButtonMiddle) {
392 Frame* focused = focusedWebCoreFrame();
393 IntPoint clickPoint(m_lastMouseDownPoint.x, m_lastMouseDownPoint.y);
394 clickPoint = m_page->mainFrame()->view()->windowToContents(clickPoint);
395 HitTestResult hitTestResult =
396 focused->eventHandler()->hitTestResultAtPoint(clickPoint, false, false,
397 ShouldHitTestScrollbars);
398 // We don't want to send a paste when middle clicking a scroll bar or a
399 // link (which will navigate later in the code).
400 if (!hitTestResult.scrollbar() && !hitTestResult.isLiveLink() && focused) {
401 Editor* editor = focused->editor();
402 Pasteboard* pasteboard = Pasteboard::generalPasteboard();
403 bool oldSelectionMode = pasteboard->isSelectionMode();
404 pasteboard->setSelectionMode(true);
405 editor->command(AtomicString("Paste")).execute();
406 pasteboard->setSelectionMode(oldSelectionMode);
409 #endif
411 mouseCaptureLost();
412 mainFrameImpl()->frame()->eventHandler()->handleMouseReleaseEvent(
413 PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event));
415 #if PLATFORM(WIN_OS)
416 // Dispatch the contextmenu event regardless of if the click was swallowed.
417 // On Mac/Linux, we handle it on mouse down, not up.
418 if (event.button == WebMouseEvent::ButtonRight)
419 mouseContextMenu(event);
420 #endif
423 void WebViewImpl::mouseWheel(const WebMouseWheelEvent& event)
425 PlatformWheelEventBuilder platformEvent(mainFrameImpl()->frameView(), event);
426 mainFrameImpl()->frame()->eventHandler()->handleWheelEvent(platformEvent);
429 bool WebViewImpl::keyEvent(const WebKeyboardEvent& event)
431 ASSERT((event.type == WebInputEvent::RawKeyDown)
432 || (event.type == WebInputEvent::KeyDown)
433 || (event.type == WebInputEvent::KeyUp));
435 // Please refer to the comments explaining the m_suppressNextKeypressEvent
436 // member.
437 // The m_suppressNextKeypressEvent is set if the KeyDown is handled by
438 // Webkit. A keyDown event is typically associated with a keyPress(char)
439 // event and a keyUp event. We reset this flag here as this is a new keyDown
440 // event.
441 m_suppressNextKeypressEvent = false;
443 // Give autocomplete a chance to consume the key events it is interested in.
444 if (autocompleteHandleKeyEvent(event))
445 return true;
447 Frame* frame = focusedWebCoreFrame();
448 if (!frame)
449 return false;
451 EventHandler* handler = frame->eventHandler();
452 if (!handler)
453 return keyEventDefault(event);
455 #if PLATFORM(WIN_OS) || PLATFORM(LINUX)
456 const WebInputEvent::Type contextMenuTriggeringEventType =
457 #if PLATFORM(WIN_OS)
458 WebInputEvent::KeyUp;
459 #elif PLATFORM(LINUX)
460 WebInputEvent::RawKeyDown;
461 #endif
463 if (((!event.modifiers && (event.windowsKeyCode == VKEY_APPS))
464 || ((event.modifiers == WebInputEvent::ShiftKey) && (event.windowsKeyCode == VKEY_F10)))
465 && event.type == contextMenuTriggeringEventType) {
466 sendContextMenuEvent(event);
467 return true;
469 #endif
471 // It's not clear if we should continue after detecting a capslock keypress.
472 // I'll err on the side of continuing, which is the pre-existing behaviour.
473 if (event.windowsKeyCode == VKEY_CAPITAL)
474 handler->capsLockStateMayHaveChanged();
476 PlatformKeyboardEventBuilder evt(event);
478 if (handler->keyEvent(evt)) {
479 if (WebInputEvent::RawKeyDown == event.type)
480 m_suppressNextKeypressEvent = true;
481 return true;
484 return keyEventDefault(event);
487 bool WebViewImpl::autocompleteHandleKeyEvent(const WebKeyboardEvent& event)
489 if (!m_autocompletePopupShowing
490 // Home and End should be left to the text field to process.
491 || event.windowsKeyCode == VKEY_HOME
492 || event.windowsKeyCode == VKEY_END)
493 return false;
495 // Pressing delete triggers the removal of the selected suggestion from the DB.
496 if (event.windowsKeyCode == VKEY_DELETE
497 && m_autocompletePopup->selectedIndex() != -1) {
498 Node* node = focusedWebCoreNode();
499 if (!node || (node->nodeType() != Node::ELEMENT_NODE)) {
500 ASSERT_NOT_REACHED();
501 return false;
503 Element* element = static_cast<Element*>(node);
504 if (!element->hasLocalName(HTMLNames::inputTag)) {
505 ASSERT_NOT_REACHED();
506 return false;
509 int selectedIndex = m_autocompletePopup->selectedIndex();
510 HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(element);
511 WebString name = inputElement->name();
512 WebString value = m_autocompletePopupClient->itemText(selectedIndex);
513 m_client->removeAutofillSuggestions(name, value);
514 // Update the entries in the currently showing popup to reflect the
515 // deletion.
516 m_autocompletePopupClient->removeItemAtIndex(selectedIndex);
517 refreshAutofillPopup();
518 return false;
521 if (!m_autocompletePopup->isInterestedInEventForKey(event.windowsKeyCode))
522 return false;
524 if (m_autocompletePopup->handleKeyEvent(PlatformKeyboardEventBuilder(event))) {
525 // We need to ignore the next Char event after this otherwise pressing
526 // enter when selecting an item in the menu will go to the page.
527 if (WebInputEvent::RawKeyDown == event.type)
528 m_suppressNextKeypressEvent = true;
529 return true;
532 return false;
535 bool WebViewImpl::charEvent(const WebKeyboardEvent& event)
537 ASSERT(event.type == WebInputEvent::Char);
539 // Please refer to the comments explaining the m_suppressNextKeypressEvent
540 // member. The m_suppressNextKeypressEvent is set if the KeyDown is
541 // handled by Webkit. A keyDown event is typically associated with a
542 // keyPress(char) event and a keyUp event. We reset this flag here as it
543 // only applies to the current keyPress event.
544 bool suppress = m_suppressNextKeypressEvent;
545 m_suppressNextKeypressEvent = false;
547 Frame* frame = focusedWebCoreFrame();
548 if (!frame)
549 return suppress;
551 EventHandler* handler = frame->eventHandler();
552 if (!handler)
553 return suppress || keyEventDefault(event);
555 PlatformKeyboardEventBuilder evt(event);
556 if (!evt.isCharacterKey())
557 return true;
559 // Accesskeys are triggered by char events and can't be suppressed.
560 if (handler->handleAccessKey(evt))
561 return true;
563 // Safari 3.1 does not pass off windows system key messages (WM_SYSCHAR) to
564 // the eventHandler::keyEvent. We mimic this behavior on all platforms since
565 // for now we are converting other platform's key events to windows key
566 // events.
567 if (evt.isSystemKey())
568 return false;
570 if (!suppress && !handler->keyEvent(evt))
571 return keyEventDefault(event);
573 return true;
576 // The WebViewImpl::SendContextMenuEvent function is based on the Webkit
577 // function
578 // bool WebView::handleContextMenuEvent(WPARAM wParam, LPARAM lParam) in
579 // webkit\webkit\win\WebView.cpp. The only significant change in this
580 // function is the code to convert from a Keyboard event to the Right
581 // Mouse button up event.
583 // This function is an ugly copy/paste and should be cleaned up when the
584 // WebKitWin version is cleaned: https://bugs.webkit.org/show_bug.cgi?id=20438
585 #if PLATFORM(WIN_OS) || PLATFORM(LINUX)
586 // FIXME: implement on Mac
587 bool WebViewImpl::sendContextMenuEvent(const WebKeyboardEvent& event)
589 static const int kContextMenuMargin = 1;
590 Frame* mainFrameImpl = page()->mainFrame();
591 FrameView* view = mainFrameImpl->view();
592 if (!view)
593 return false;
595 IntPoint coords(-1, -1);
596 #if PLATFORM(WIN_OS)
597 int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT);
598 #else
599 int rightAligned = 0;
600 #endif
601 IntPoint location;
604 Frame* focusedFrame = page()->focusController()->focusedOrMainFrame();
605 Node* focusedNode = focusedFrame->document()->focusedNode();
606 Position start = mainFrameImpl->selection()->selection().start();
608 if (focusedFrame->editor() && focusedFrame->editor()->canEdit() && start.node()) {
609 RenderObject* renderer = start.node()->renderer();
610 if (!renderer)
611 return false;
613 RefPtr<Range> selection = mainFrameImpl->selection()->toNormalizedRange();
614 IntRect firstRect = mainFrameImpl->firstRectForRange(selection.get());
616 int x = rightAligned ? firstRect.right() : firstRect.x();
617 location = IntPoint(x, firstRect.bottom());
618 } else if (focusedNode)
619 location = focusedNode->getRect().bottomLeft();
620 else {
621 location = IntPoint(
622 rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin,
623 kContextMenuMargin);
626 location = view->contentsToWindow(location);
627 // FIXME: The IntSize(0, -1) is a hack to get the hit-testing to result in
628 // the selected element. Ideally we'd have the position of a context menu
629 // event be separate from its target node.
630 coords = location + IntSize(0, -1);
632 // The contextMenuController() holds onto the last context menu that was
633 // popped up on the page until a new one is created. We need to clear
634 // this menu before propagating the event through the DOM so that we can
635 // detect if we create a new menu for this event, since we won't create
636 // a new menu if the DOM swallows the event and the defaultEventHandler does
637 // not run.
638 page()->contextMenuController()->clearContextMenu();
640 focusedFrame->view()->setCursor(pointerCursor());
641 WebMouseEvent mouseEvent;
642 mouseEvent.button = WebMouseEvent::ButtonRight;
643 mouseEvent.x = coords.x();
644 mouseEvent.y = coords.y();
645 mouseEvent.type = WebInputEvent::MouseUp;
647 PlatformMouseEventBuilder platformEvent(view, mouseEvent);
649 m_contextMenuAllowed = true;
650 bool handled = focusedFrame->eventHandler()->sendContextMenuEvent(platformEvent);
651 m_contextMenuAllowed = false;
652 return handled;
654 #endif
656 bool WebViewImpl::keyEventDefault(const WebKeyboardEvent& event)
658 Frame* frame = focusedWebCoreFrame();
659 if (!frame)
660 return false;
662 switch (event.type) {
663 case WebInputEvent::Char:
664 if (event.windowsKeyCode == VKEY_SPACE) {
665 int keyCode = ((event.modifiers & WebInputEvent::ShiftKey) ? VKEY_PRIOR : VKEY_NEXT);
666 return scrollViewWithKeyboard(keyCode, event.modifiers);
668 break;
669 case WebInputEvent::RawKeyDown:
670 if (event.modifiers == WebInputEvent::ControlKey) {
671 switch (event.windowsKeyCode) {
672 case 'A':
673 focusedFrame()->executeCommand(WebString::fromUTF8("SelectAll"));
674 return true;
675 case VKEY_INSERT:
676 case 'C':
677 focusedFrame()->executeCommand(WebString::fromUTF8("Copy"));
678 return true;
679 // Match FF behavior in the sense that Ctrl+home/end are the only Ctrl
680 // key combinations which affect scrolling. Safari is buggy in the
681 // sense that it scrolls the page for all Ctrl+scrolling key
682 // combinations. For e.g. Ctrl+pgup/pgdn/up/down, etc.
683 case VKEY_HOME:
684 case VKEY_END:
685 break;
686 default:
687 return false;
690 if (!event.isSystemKey && !(event.modifiers & WebInputEvent::ShiftKey))
691 return scrollViewWithKeyboard(event.windowsKeyCode, event.modifiers);
692 break;
693 default:
694 break;
696 return false;
699 bool WebViewImpl::scrollViewWithKeyboard(int keyCode, int modifiers)
701 ScrollDirection scrollDirection;
702 ScrollGranularity scrollGranularity;
704 switch (keyCode) {
705 case VKEY_LEFT:
706 scrollDirection = ScrollLeft;
707 scrollGranularity = ScrollByLine;
708 break;
709 case VKEY_RIGHT:
710 scrollDirection = ScrollRight;
711 scrollGranularity = ScrollByLine;
712 break;
713 case VKEY_UP:
714 scrollDirection = ScrollUp;
715 scrollGranularity = ScrollByLine;
716 break;
717 case VKEY_DOWN:
718 scrollDirection = ScrollDown;
719 scrollGranularity = ScrollByLine;
720 break;
721 case VKEY_HOME:
722 scrollDirection = ScrollUp;
723 scrollGranularity = ScrollByDocument;
724 break;
725 case VKEY_END:
726 scrollDirection = ScrollDown;
727 scrollGranularity = ScrollByDocument;
728 break;
729 case VKEY_PRIOR: // page up
730 scrollDirection = ScrollUp;
731 scrollGranularity = ScrollByPage;
732 break;
733 case VKEY_NEXT: // page down
734 scrollDirection = ScrollDown;
735 scrollGranularity = ScrollByPage;
736 break;
737 default:
738 return false;
741 return propagateScroll(scrollDirection, scrollGranularity);
744 bool WebViewImpl::propagateScroll(ScrollDirection scrollDirection,
745 ScrollGranularity scrollGranularity)
747 Frame* frame = focusedWebCoreFrame();
748 if (!frame)
749 return false;
751 bool scrollHandled =
752 frame->eventHandler()->scrollOverflow(scrollDirection,
753 scrollGranularity);
754 Frame* currentFrame = frame;
755 while (!scrollHandled && currentFrame) {
756 scrollHandled = currentFrame->view()->scroll(scrollDirection,
757 scrollGranularity);
758 currentFrame = currentFrame->tree()->parent();
760 return scrollHandled;
763 Frame* WebViewImpl::focusedWebCoreFrame()
765 return m_page.get() ? m_page->focusController()->focusedOrMainFrame() : 0;
768 WebViewImpl* WebViewImpl::fromPage(Page* page)
770 if (!page)
771 return 0;
773 return static_cast<ChromeClientImpl*>(page->chrome()->client())->webView();
776 // WebWidget ------------------------------------------------------------------
778 void WebViewImpl::close()
780 RefPtr<WebFrameImpl> mainFrameImpl;
782 if (m_page.get()) {
783 // Initiate shutdown for the entire frameset. This will cause a lot of
784 // notifications to be sent.
785 if (m_page->mainFrame()) {
786 mainFrameImpl = WebFrameImpl::fromFrame(m_page->mainFrame());
787 m_page->mainFrame()->loader()->frameDetached();
789 m_page.clear();
792 // Should happen after m_page.clear().
793 if (m_devToolsAgent.get())
794 m_devToolsAgent.clear();
796 // Reset the delegate to prevent notifications being sent as we're being
797 // deleted.
798 m_client = 0;
800 deref(); // Balances ref() acquired in WebView::create
803 void WebViewImpl::resize(const WebSize& newSize)
805 if (m_size == newSize)
806 return;
807 m_size = newSize;
809 if (mainFrameImpl()->frameView()) {
810 mainFrameImpl()->frameView()->resize(m_size.width, m_size.height);
811 mainFrameImpl()->frame()->eventHandler()->sendResizeEvent();
814 if (m_client) {
815 WebRect damagedRect(0, 0, m_size.width, m_size.height);
816 m_client->didInvalidateRect(damagedRect);
820 void WebViewImpl::layout()
822 WebFrameImpl* webframe = mainFrameImpl();
823 if (webframe) {
824 // In order for our child HWNDs (NativeWindowWidgets) to update properly,
825 // they need to be told that we are updating the screen. The problem is
826 // that the native widgets need to recalculate their clip region and not
827 // overlap any of our non-native widgets. To force the resizing, call
828 // setFrameRect(). This will be a quick operation for most frames, but
829 // the NativeWindowWidgets will update a proper clipping region.
830 FrameView* view = webframe->frameView();
831 if (view)
832 view->setFrameRect(view->frameRect());
834 // setFrameRect may have the side-effect of causing existing page
835 // layout to be invalidated, so layout needs to be called last.
837 webframe->layout();
841 void WebViewImpl::paint(WebCanvas* canvas, const WebRect& rect)
843 WebFrameImpl* webframe = mainFrameImpl();
844 if (webframe)
845 webframe->paint(canvas, rect);
848 // FIXME: m_currentInputEvent should be removed once ChromeClient::show() can
849 // get the current-event information from WebCore.
850 const WebInputEvent* WebViewImpl::m_currentInputEvent = 0;
852 bool WebViewImpl::handleInputEvent(const WebInputEvent& inputEvent)
854 // If we've started a drag and drop operation, ignore input events until
855 // we're done.
856 if (m_doingDragAndDrop)
857 return true;
859 if (m_ignoreInputEvents)
860 return true;
862 // FIXME: Remove m_currentInputEvent.
863 // This only exists to allow ChromeClient::show() to know which mouse button
864 // triggered a window.open event.
865 // Safari must perform a similar hack, ours is in our WebKit glue layer
866 // theirs is in the application. This should go when WebCore can be fixed
867 // to pass more event information to ChromeClient::show()
868 m_currentInputEvent = &inputEvent;
870 bool handled = true;
872 // FIXME: WebKit seems to always return false on mouse events processing
873 // methods. For now we'll assume it has processed them (as we are only
874 // interested in whether keyboard events are processed).
875 switch (inputEvent.type) {
876 case WebInputEvent::MouseMove:
877 mouseMove(*static_cast<const WebMouseEvent*>(&inputEvent));
878 break;
880 case WebInputEvent::MouseLeave:
881 mouseLeave(*static_cast<const WebMouseEvent*>(&inputEvent));
882 break;
884 case WebInputEvent::MouseWheel:
885 mouseWheel(*static_cast<const WebMouseWheelEvent*>(&inputEvent));
886 break;
888 case WebInputEvent::MouseDown:
889 mouseDown(*static_cast<const WebMouseEvent*>(&inputEvent));
890 break;
892 case WebInputEvent::MouseUp:
893 mouseUp(*static_cast<const WebMouseEvent*>(&inputEvent));
894 break;
896 case WebInputEvent::RawKeyDown:
897 case WebInputEvent::KeyDown:
898 case WebInputEvent::KeyUp:
899 handled = keyEvent(*static_cast<const WebKeyboardEvent*>(&inputEvent));
900 break;
902 case WebInputEvent::Char:
903 handled = charEvent(*static_cast<const WebKeyboardEvent*>(&inputEvent));
904 break;
906 default:
907 handled = false;
910 m_currentInputEvent = 0;
912 return handled;
915 void WebViewImpl::mouseCaptureLost()
919 void WebViewImpl::setFocus(bool enable)
921 m_page->focusController()->setFocused(enable);
922 if (enable) {
923 // Note that we don't call setActive() when disabled as this cause extra
924 // focus/blur events to be dispatched.
925 m_page->focusController()->setActive(true);
926 RefPtr<Frame> focusedFrame = m_page->focusController()->focusedFrame();
927 if (focusedFrame) {
928 Node* focusedNode = focusedFrame->document()->focusedNode();
929 if (focusedNode && focusedNode->isElementNode()
930 && focusedFrame->selection()->selection().isNone()) {
931 // If the selection was cleared while the WebView was not
932 // focused, then the focus element shows with a focus ring but
933 // no caret and does respond to keyboard inputs.
934 Element* element = static_cast<Element*>(focusedNode);
935 if (element->isTextFormControl())
936 element->updateFocusAppearance(true);
937 else if (focusedNode->isContentEditable()) {
938 // updateFocusAppearance() selects all the text of
939 // contentseditable DIVs. So we set the selection explicitly
940 // instead. Note that this has the side effect of moving the
941 // caret back to the beginning of the text.
942 Position position(focusedNode, 0,
943 Position::PositionIsOffsetInAnchor);
944 focusedFrame->selection()->setSelection(
945 VisibleSelection(position, SEL_DEFAULT_AFFINITY));
949 m_imeAcceptEvents = true;
950 } else {
951 hideAutoCompletePopup();
953 // Clear focus on the currently focused frame if any.
954 if (!m_page.get())
955 return;
957 Frame* frame = m_page->mainFrame();
958 if (!frame)
959 return;
961 RefPtr<Frame> focusedFrame = m_page->focusController()->focusedFrame();
962 if (focusedFrame.get()) {
963 // Finish an ongoing composition to delete the composition node.
964 Editor* editor = focusedFrame->editor();
965 if (editor && editor->hasComposition())
966 editor->confirmComposition();
967 m_imeAcceptEvents = false;
972 bool WebViewImpl::handleCompositionEvent(WebCompositionCommand command,
973 int cursorPosition,
974 int targetStart,
975 int targetEnd,
976 const WebString& imeString)
978 Frame* focused = focusedWebCoreFrame();
979 if (!focused || !m_imeAcceptEvents)
980 return false;
981 Editor* editor = focused->editor();
982 if (!editor)
983 return false;
984 if (!editor->canEdit()) {
985 // The input focus has been moved to another WebWidget object.
986 // We should use this |editor| object only to complete the ongoing
987 // composition.
988 if (!editor->hasComposition())
989 return false;
992 // We should verify the parent node of this IME composition node are
993 // editable because JavaScript may delete a parent node of the composition
994 // node. In this case, WebKit crashes while deleting texts from the parent
995 // node, which doesn't exist any longer.
996 PassRefPtr<Range> range = editor->compositionRange();
997 if (range) {
998 const Node* node = range->startPosition().node();
999 if (!node || !node->isContentEditable())
1000 return false;
1003 if (command == WebCompositionCommandDiscard) {
1004 // A browser process sent an IPC message which does not contain a valid
1005 // string, which means an ongoing composition has been canceled.
1006 // If the ongoing composition has been canceled, replace the ongoing
1007 // composition string with an empty string and complete it.
1008 String emptyString;
1009 Vector<CompositionUnderline> emptyUnderlines;
1010 editor->setComposition(emptyString, emptyUnderlines, 0, 0);
1011 } else {
1012 // A browser process sent an IPC message which contains a string to be
1013 // displayed in this Editor object.
1014 // To display the given string, set the given string to the
1015 // m_compositionNode member of this Editor object and display it.
1016 if (targetStart < 0)
1017 targetStart = 0;
1018 if (targetEnd < 0)
1019 targetEnd = static_cast<int>(imeString.length());
1020 String compositionString(imeString);
1021 // Create custom underlines.
1022 // To emphasize the selection, the selected region uses a solid black
1023 // for its underline while other regions uses a pale gray for theirs.
1024 Vector<CompositionUnderline> underlines(3);
1025 underlines[0].startOffset = 0;
1026 underlines[0].endOffset = targetStart;
1027 underlines[0].thick = true;
1028 underlines[0].color.setRGB(0xd3, 0xd3, 0xd3);
1029 underlines[1].startOffset = targetStart;
1030 underlines[1].endOffset = targetEnd;
1031 underlines[1].thick = true;
1032 underlines[1].color.setRGB(0x00, 0x00, 0x00);
1033 underlines[2].startOffset = targetEnd;
1034 underlines[2].endOffset = static_cast<int>(imeString.length());
1035 underlines[2].thick = true;
1036 underlines[2].color.setRGB(0xd3, 0xd3, 0xd3);
1037 // When we use custom underlines, WebKit ("InlineTextBox.cpp" Line 282)
1038 // prevents from writing a text in between 'selectionStart' and
1039 // 'selectionEnd' somehow.
1040 // Therefore, we use the 'cursorPosition' for these arguments so that
1041 // there are not any characters in the above region.
1042 editor->setComposition(compositionString, underlines,
1043 cursorPosition, cursorPosition);
1044 // The given string is a result string, which means the ongoing
1045 // composition has been completed. I have to call the
1046 // Editor::confirmCompletion() and complete this composition.
1047 if (command == WebCompositionCommandConfirm)
1048 editor->confirmComposition();
1051 return editor->hasComposition();
1054 bool WebViewImpl::queryCompositionStatus(bool* enableIME, WebRect* caretRect)
1056 // Store whether the selected node needs IME and the caret rectangle.
1057 // This process consists of the following four steps:
1058 // 1. Retrieve the selection controller of the focused frame;
1059 // 2. Retrieve the caret rectangle from the controller;
1060 // 3. Convert the rectangle, which is relative to the parent view, to the
1061 // one relative to the client window, and;
1062 // 4. Store the converted rectangle.
1063 const Frame* focused = focusedWebCoreFrame();
1064 if (!focused)
1065 return false;
1067 const Editor* editor = focused->editor();
1068 if (!editor || !editor->canEdit())
1069 return false;
1071 SelectionController* controller = focused->selection();
1072 if (!controller)
1073 return false;
1075 const Node* node = controller->start().node();
1076 if (!node)
1077 return false;
1079 *enableIME = node->shouldUseInputMethod() && !controller->isInPasswordField();
1080 const FrameView* view = node->document()->view();
1081 if (!view)
1082 return false;
1084 *caretRect = view->contentsToWindow(controller->absoluteCaretBounds());
1085 return true;
1088 void WebViewImpl::setTextDirection(WebTextDirection direction)
1090 // The Editor::setBaseWritingDirection() function checks if we can change
1091 // the text direction of the selected node and updates its DOM "dir"
1092 // attribute and its CSS "direction" property.
1093 // So, we just call the function as Safari does.
1094 const Frame* focused = focusedWebCoreFrame();
1095 if (!focused)
1096 return;
1098 Editor* editor = focused->editor();
1099 if (!editor || !editor->canEdit())
1100 return;
1102 switch (direction) {
1103 case WebTextDirectionDefault:
1104 editor->setBaseWritingDirection(NaturalWritingDirection);
1105 break;
1107 case WebTextDirectionLeftToRight:
1108 editor->setBaseWritingDirection(LeftToRightWritingDirection);
1109 break;
1111 case WebTextDirectionRightToLeft:
1112 editor->setBaseWritingDirection(RightToLeftWritingDirection);
1113 break;
1115 default:
1116 notImplemented();
1117 break;
1121 // WebView --------------------------------------------------------------------
1123 WebSettings* WebViewImpl::settings()
1125 if (!m_webSettings.get())
1126 m_webSettings.set(new WebSettingsImpl(m_page->settings()));
1127 ASSERT(m_webSettings.get());
1128 return m_webSettings.get();
1131 WebString WebViewImpl::pageEncoding() const
1133 if (!m_page.get())
1134 return WebString();
1136 return m_page->mainFrame()->loader()->encoding();
1139 void WebViewImpl::setPageEncoding(const WebString& encodingName)
1141 if (!m_page.get())
1142 return;
1144 // Only change override encoding, don't change default encoding.
1145 // Note that the new encoding must be 0 if it isn't supposed to be set.
1146 String newEncodingName;
1147 if (!encodingName.isEmpty())
1148 newEncodingName = encodingName;
1149 m_page->mainFrame()->loader()->reloadWithOverrideEncoding(newEncodingName);
1152 bool WebViewImpl::dispatchBeforeUnloadEvent()
1154 // FIXME: This should really cause a recursive depth-first walk of all
1155 // frames in the tree, calling each frame's onbeforeunload. At the moment,
1156 // we're consistent with Safari 3.1, not IE/FF.
1157 Frame* frame = m_page->focusController()->focusedOrMainFrame();
1158 if (!frame)
1159 return true;
1161 return frame->shouldClose();
1164 void WebViewImpl::dispatchUnloadEvent()
1166 // Run unload handlers.
1167 m_page->mainFrame()->loader()->closeURL();
1170 WebFrame* WebViewImpl::mainFrame()
1172 return mainFrameImpl();
1175 WebFrame* WebViewImpl::findFrameByName(
1176 const WebString& name, WebFrame* relativeToFrame)
1178 if (!relativeToFrame)
1179 relativeToFrame = mainFrame();
1180 Frame* frame = static_cast<WebFrameImpl*>(relativeToFrame)->frame();
1181 frame = frame->tree()->find(name);
1182 return WebFrameImpl::fromFrame(frame);
1185 WebFrame* WebViewImpl::focusedFrame()
1187 return WebFrameImpl::fromFrame(focusedWebCoreFrame());
1190 void WebViewImpl::setFocusedFrame(WebFrame* frame)
1192 if (!frame) {
1193 // Clears the focused frame if any.
1194 Frame* frame = focusedWebCoreFrame();
1195 if (frame)
1196 frame->selection()->setFocused(false);
1197 return;
1199 WebFrameImpl* frameImpl = static_cast<WebFrameImpl*>(frame);
1200 Frame* webcoreFrame = frameImpl->frame();
1201 webcoreFrame->page()->focusController()->setFocusedFrame(webcoreFrame);
1204 void WebViewImpl::setInitialFocus(bool reverse)
1206 if (!m_page.get())
1207 return;
1209 // Since we don't have a keyboard event, we'll create one.
1210 WebKeyboardEvent keyboardEvent;
1211 keyboardEvent.type = WebInputEvent::RawKeyDown;
1212 if (reverse)
1213 keyboardEvent.modifiers = WebInputEvent::ShiftKey;
1215 // VK_TAB which is only defined on Windows.
1216 keyboardEvent.windowsKeyCode = 0x09;
1217 PlatformKeyboardEventBuilder platformEvent(keyboardEvent);
1218 RefPtr<KeyboardEvent> webkitEvent = KeyboardEvent::create(platformEvent, 0);
1219 page()->focusController()->setInitialFocus(
1220 reverse ? FocusDirectionBackward : FocusDirectionForward,
1221 webkitEvent.get());
1224 void WebViewImpl::clearFocusedNode()
1226 if (!m_page.get())
1227 return;
1229 RefPtr<Frame> frame = m_page->mainFrame();
1230 if (!frame.get())
1231 return;
1233 RefPtr<Document> document = frame->document();
1234 if (!document.get())
1235 return;
1237 RefPtr<Node> oldFocusedNode = document->focusedNode();
1239 // Clear the focused node.
1240 document->setFocusedNode(0);
1242 if (!oldFocusedNode.get())
1243 return;
1245 // If a text field has focus, we need to make sure the selection controller
1246 // knows to remove selection from it. Otherwise, the text field is still
1247 // processing keyboard events even though focus has been moved to the page and
1248 // keystrokes get eaten as a result.
1249 if (oldFocusedNode->hasTagName(HTMLNames::textareaTag)
1250 || (oldFocusedNode->hasTagName(HTMLNames::inputTag)
1251 && static_cast<HTMLInputElement*>(oldFocusedNode.get())->isTextField())) {
1252 // Clear the selection.
1253 SelectionController* selection = frame->selection();
1254 selection->clear();
1258 int WebViewImpl::zoomLevel()
1260 return m_zoomLevel;
1263 int WebViewImpl::setZoomLevel(bool textOnly, int zoomLevel)
1265 float zoomFactor = static_cast<float>(
1266 std::max(std::min(std::pow(textSizeMultiplierRatio, zoomLevel),
1267 maxTextSizeMultiplier),
1268 minTextSizeMultiplier));
1269 Frame* frame = mainFrameImpl()->frame();
1270 if (zoomFactor != frame->zoomFactor()) {
1271 m_zoomLevel = zoomLevel;
1272 frame->setZoomFactor(zoomFactor, textOnly);
1274 return m_zoomLevel;
1277 void WebViewImpl::performMediaPlayerAction(const WebMediaPlayerAction& action,
1278 const WebPoint& location)
1280 HitTestResult result =
1281 hitTestResultForWindowPos(location);
1282 RefPtr<Node> node = result.innerNonSharedNode();
1283 if (!node->hasTagName(HTMLNames::videoTag) && !node->hasTagName(HTMLNames::audioTag))
1284 return;
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();
1306 void WebViewImpl::copyImageAt(const WebPoint& point)
1308 if (!m_page.get())
1309 return;
1311 HitTestResult result = hitTestResultForWindowPos(point);
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.
1319 // FIXME: implement a cache of the most recent HitTestResult to avoid having
1320 // to do two hit tests.
1321 return;
1324 m_page->mainFrame()->editor()->copyImage(result);
1327 void WebViewImpl::dragSourceEndedAt(
1328 const WebPoint& clientPoint,
1329 const WebPoint& screenPoint,
1330 WebDragOperation operation)
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));
1340 void WebViewImpl::dragSourceSystemDragEnded()
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;
1350 WebDragOperation WebViewImpl::dragTargetDragEnter(
1351 const WebDragData& webDragData, int identity,
1352 const WebPoint& clientPoint,
1353 const WebPoint& screenPoint,
1354 WebDragOperationsMask operationsAllowed)
1356 ASSERT(!m_currentDragData.get());
1358 m_currentDragData = webDragData;
1359 m_dragIdentity = identity;
1360 m_operationsAllowed = operationsAllowed;
1362 DragData dragData(
1363 m_currentDragData.get(),
1364 clientPoint,
1365 screenPoint,
1366 static_cast<DragOperation>(operationsAllowed));
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;
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;
1384 WebDragOperation WebViewImpl::dragTargetDragOver(
1385 const WebPoint& clientPoint,
1386 const WebPoint& screenPoint,
1387 WebDragOperationsMask operationsAllowed)
1389 ASSERT(m_currentDragData.get());
1391 m_operationsAllowed = operationsAllowed;
1392 DragData dragData(
1393 m_currentDragData.get(),
1394 clientPoint,
1395 screenPoint,
1396 static_cast<DragOperation>(operationsAllowed));
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;
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;
1414 void WebViewImpl::dragTargetDragLeave()
1416 ASSERT(m_currentDragData.get());
1418 DragData dragData(
1419 m_currentDragData.get(),
1420 IntPoint(),
1421 IntPoint(),
1422 static_cast<DragOperation>(m_operationsAllowed));
1424 m_dragTargetDispatch = true;
1425 m_page->dragController()->dragExited(&dragData);
1426 m_dragTargetDispatch = false;
1428 m_currentDragData = 0;
1429 m_dropEffect = DropEffectDefault;
1430 m_dragOperation = WebDragOperationNone;
1431 m_dragIdentity = 0;
1434 void WebViewImpl::dragTargetDrop(const WebPoint& clientPoint,
1435 const WebPoint& screenPoint)
1437 ASSERT(m_currentDragData.get());
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.
1446 if (m_dragOperation == WebDragOperationNone) { // IPC RACE CONDITION: do not allow this drop.
1447 dragTargetDragLeave();
1448 return;
1451 DragData dragData(
1452 m_currentDragData.get(),
1453 clientPoint,
1454 screenPoint,
1455 static_cast<DragOperation>(m_operationsAllowed));
1457 m_dragTargetDispatch = true;
1458 m_page->dragController()->performDrag(&dragData);
1459 m_dragTargetDispatch = false;
1461 m_currentDragData = 0;
1462 m_dropEffect = DropEffectDefault;
1463 m_dragOperation = WebDragOperationNone;
1464 m_dragIdentity = 0;
1467 int WebViewImpl::dragIdentity()
1469 if (m_dragTargetDispatch)
1470 return m_dragIdentity;
1471 return 0;
1474 unsigned long WebViewImpl::createUniqueIdentifierForRequest() {
1475 if (m_page)
1476 return m_page->progress()->createUniqueIdentifier();
1477 return 0;
1480 void WebViewImpl::inspectElementAt(const WebPoint& point)
1482 if (!m_page.get())
1483 return;
1485 if (point.x == -1 || point.y == -1)
1486 m_page->inspectorController()->inspect(0);
1487 else {
1488 HitTestResult result = hitTestResultForWindowPos(point);
1490 if (!result.innerNonSharedNode())
1491 return;
1493 m_page->inspectorController()->inspect(result.innerNonSharedNode());
1497 WebString WebViewImpl::inspectorSettings() const
1499 return m_inspectorSettings;
1502 void WebViewImpl::setInspectorSettings(const WebString& settings)
1504 m_inspectorSettings = settings;
1507 WebDevToolsAgent* WebViewImpl::devToolsAgent()
1509 return m_devToolsAgent.get();
1512 void WebViewImpl::setDevToolsAgent(WebDevToolsAgent* devToolsAgent)
1514 ASSERT(!m_devToolsAgent.get()); // May only set once!
1515 m_devToolsAgent.set(static_cast<WebDevToolsAgentPrivate*>(devToolsAgent));
1518 WebAccessibilityObject WebViewImpl::accessibilityObject()
1520 if (!mainFrameImpl())
1521 return WebAccessibilityObject();
1523 Document* document = mainFrameImpl()->frame()->document();
1524 return WebAccessibilityObject(
1525 document->axObjectCache()->getOrCreate(document->renderer()));
1528 void WebViewImpl::applyAutofillSuggestions(
1529 const WebNode& node,
1530 const WebVector<WebString>& suggestions,
1531 int defaultSuggestionIndex)
1533 if (!m_page.get() || suggestions.isEmpty()) {
1534 hideAutoCompletePopup();
1535 return;
1538 ASSERT(defaultSuggestionIndex < static_cast<int>(suggestions.size()));
1540 if (RefPtr<Frame> focused = m_page->focusController()->focusedFrame()) {
1541 RefPtr<Document> document = focused->document();
1542 if (!document.get()) {
1543 hideAutoCompletePopup();
1544 return;
1547 RefPtr<Node> focusedNode = document->focusedNode();
1548 // If the node for which we queried the autofill suggestions is not the
1549 // focused node, then we have nothing to do. FIXME: also check the
1550 // carret is at the end and that the text has not changed.
1551 if (!focusedNode.get() || focusedNode != PassRefPtr<Node>(node)) {
1552 hideAutoCompletePopup();
1553 return;
1556 if (!focusedNode->hasTagName(HTMLNames::inputTag)) {
1557 ASSERT_NOT_REACHED();
1558 return;
1561 HTMLInputElement* inputElem =
1562 static_cast<HTMLInputElement*>(focusedNode.get());
1564 // The first time the autocomplete is shown we'll create the client and the
1565 // popup.
1566 if (!m_autocompletePopupClient.get())
1567 m_autocompletePopupClient.set(new AutocompletePopupMenuClient(this));
1568 m_autocompletePopupClient->initialize(inputElem,
1569 suggestions,
1570 defaultSuggestionIndex);
1571 if (!m_autocompletePopup.get()) {
1572 m_autocompletePopup =
1573 PopupContainer::create(m_autocompletePopupClient.get(),
1574 autocompletePopupSettings);
1577 if (m_autocompletePopupShowing) {
1578 m_autocompletePopupClient->setSuggestions(suggestions);
1579 refreshAutofillPopup();
1580 } else {
1581 m_autocompletePopup->show(focusedNode->getRect(),
1582 focusedNode->ownerDocument()->view(), 0);
1583 m_autocompletePopupShowing = true;
1588 void WebViewImpl::hideAutofillPopup()
1590 hideAutoCompletePopup();
1593 void WebViewImpl::performCustomContextMenuAction(unsigned action)
1595 if (!m_page)
1596 return;
1597 ContextMenu* menu = m_page->contextMenuController()->contextMenu();
1598 if (!menu)
1599 return;
1600 ContextMenuItem* item = menu->itemWithAction(static_cast<ContextMenuAction>(ContextMenuItemBaseCustomTag + action));
1601 if (item)
1602 m_page->contextMenuController()->contextMenuItemSelected(item);
1603 m_page->contextMenuController()->clearContextMenu();
1606 // WebView --------------------------------------------------------------------
1608 bool WebViewImpl::setDropEffect(bool accept)
1610 if (m_dragTargetDispatch) {
1611 m_dropEffect = accept ? DropEffectCopy : DropEffectNone;
1612 return true;
1614 return false;
1617 void WebViewImpl::setIsTransparent(bool isTransparent)
1619 // Set any existing frames to be transparent.
1620 Frame* frame = m_page->mainFrame();
1621 while (frame) {
1622 frame->view()->setTransparent(isTransparent);
1623 frame = frame->tree()->traverseNext();
1626 // Future frames check this to know whether to be transparent.
1627 m_isTransparent = isTransparent;
1630 bool WebViewImpl::isTransparent() const
1632 return m_isTransparent;
1635 void WebViewImpl::setIsActive(bool active)
1637 if (page() && page()->focusController())
1638 page()->focusController()->setActive(active);
1641 bool WebViewImpl::isActive() const
1643 return (page() && page()->focusController()) ? page()->focusController()->isActive() : false;
1646 void WebViewImpl::setScrollbarColors(unsigned inactiveColor,
1647 unsigned activeColor,
1648 unsigned trackColor) {
1649 #if PLATFORM(LINUX)
1650 RenderThemeChromiumLinux::setScrollbarColors(inactiveColor,
1651 activeColor,
1652 trackColor);
1653 #endif
1656 void WebViewImpl::didCommitLoad(bool* isNewNavigation)
1658 if (isNewNavigation)
1659 *isNewNavigation = m_observedNewNavigation;
1661 #ifndef NDEBUG
1662 ASSERT(!m_observedNewNavigation
1663 || m_page->mainFrame()->loader()->documentLoader() == m_newNavigationLoader);
1664 m_newNavigationLoader = 0;
1665 #endif
1666 m_observedNewNavigation = false;
1669 bool WebViewImpl::navigationPolicyFromMouseEvent(unsigned short button,
1670 bool ctrl, bool shift,
1671 bool alt, bool meta,
1672 WebNavigationPolicy* policy)
1674 #if PLATFORM(WIN_OS) || PLATFORM(LINUX) || PLATFORM(FREEBSD)
1675 const bool newTabModifier = (button == 1) || ctrl;
1676 #elif PLATFORM(DARWIN)
1677 const bool newTabModifier = (button == 1) || meta;
1678 #endif
1679 if (!newTabModifier && !shift && !alt)
1680 return false;
1682 ASSERT(policy);
1683 if (newTabModifier) {
1684 if (shift)
1685 *policy = WebNavigationPolicyNewForegroundTab;
1686 else
1687 *policy = WebNavigationPolicyNewBackgroundTab;
1688 } else {
1689 if (shift)
1690 *policy = WebNavigationPolicyNewWindow;
1691 else
1692 *policy = WebNavigationPolicyDownload;
1694 return true;
1697 void WebViewImpl::startDragging(const WebPoint& eventPos,
1698 const WebDragData& dragData,
1699 WebDragOperationsMask mask)
1701 if (!m_client)
1702 return;
1703 ASSERT(!m_doingDragAndDrop);
1704 m_doingDragAndDrop = true;
1705 m_client->startDragging(eventPos, dragData, mask);
1708 void WebViewImpl::setCurrentHistoryItem(HistoryItem* item)
1710 m_backForwardListClientImpl.setCurrentHistoryItem(item);
1713 HistoryItem* WebViewImpl::previousHistoryItem()
1715 return m_backForwardListClientImpl.previousHistoryItem();
1718 void WebViewImpl::observeNewNavigation()
1720 m_observedNewNavigation = true;
1721 #ifndef NDEBUG
1722 m_newNavigationLoader = m_page->mainFrame()->loader()->documentLoader();
1723 #endif
1726 void WebViewImpl::hideAutoCompletePopup()
1728 if (m_autocompletePopupShowing) {
1729 m_autocompletePopup->hidePopup();
1730 autoCompletePopupDidHide();
1734 void WebViewImpl::autoCompletePopupDidHide()
1736 m_autocompletePopupShowing = false;
1739 void WebViewImpl::setIgnoreInputEvents(bool newValue)
1741 ASSERT(m_ignoreInputEvents != newValue);
1742 m_ignoreInputEvents = newValue;
1745 #if ENABLE(NOTIFICATIONS)
1746 NotificationPresenterImpl* WebViewImpl::notificationPresenterImpl()
1748 if (!m_notificationPresenter.isInitialized() && m_client)
1749 m_notificationPresenter.initialize(m_client->notificationPresenter());
1750 return &m_notificationPresenter;
1752 #endif
1754 void WebViewImpl::refreshAutofillPopup()
1756 ASSERT(m_autocompletePopupShowing);
1758 // Hide the popup if it has become empty.
1759 if (!m_autocompletePopupClient->listSize()) {
1760 hideAutoCompletePopup();
1761 return;
1764 IntRect oldBounds = m_autocompletePopup->boundsRect();
1765 m_autocompletePopup->refresh();
1766 IntRect newBounds = m_autocompletePopup->boundsRect();
1767 // Let's resize the backing window if necessary.
1768 if (oldBounds != newBounds) {
1769 WebPopupMenuImpl* popupMenu =
1770 static_cast<WebPopupMenuImpl*>(m_autocompletePopup->client());
1771 popupMenu->client()->setWindowRect(newBounds);
1775 Node* WebViewImpl::focusedWebCoreNode()
1777 Frame* frame = m_page->focusController()->focusedFrame();
1778 if (!frame)
1779 return 0;
1781 Document* document = frame->document();
1782 if (!document)
1783 return 0;
1785 return document->focusedNode();
1788 HitTestResult WebViewImpl::hitTestResultForWindowPos(const IntPoint& pos)
1790 IntPoint docPoint(m_page->mainFrame()->view()->windowToContents(pos));
1791 return m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(docPoint, false);
1794 void WebViewImpl::setTabsToLinks(bool enable)
1796 m_tabsToLinks = enable;
1799 bool WebViewImpl::tabsToLinks() const
1801 return m_tabsToLinks;
1804 } // namespace WebKit