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