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