2009-12-07 Zhe Su <suzhe@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)
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     bool suppress = m_suppressNextKeypressEvent;
533     m_suppressNextKeypressEvent = false;
534
535     Frame* frame = focusedWebCoreFrame();
536     if (!frame)
537         return suppress;
538
539     EventHandler* handler = frame->eventHandler();
540     if (!handler)
541         return suppress || keyEventDefault(event);
542
543     PlatformKeyboardEventBuilder evt(event);
544     if (!evt.isCharacterKey())
545         return true;
546
547     // Accesskeys are triggered by char events and can't be suppressed.
548     if (handler->handleAccessKey(evt))
549         return true;
550
551     // Safari 3.1 does not pass off windows system key messages (WM_SYSCHAR) to
552     // the eventHandler::keyEvent. We mimic this behavior on all platforms since
553     // for now we are converting other platform's key events to windows key
554     // events.
555     if (evt.isSystemKey())
556         return false;
557
558     if (!suppress && !handler->keyEvent(evt))
559         return keyEventDefault(event);
560
561     return true;
562 }
563
564 // The WebViewImpl::SendContextMenuEvent function is based on the Webkit
565 // function
566 // bool WebView::handleContextMenuEvent(WPARAM wParam, LPARAM lParam) in
567 // webkit\webkit\win\WebView.cpp. The only significant change in this
568 // function is the code to convert from a Keyboard event to the Right
569 // Mouse button up event.
570 //
571 // This function is an ugly copy/paste and should be cleaned up when the
572 // WebKitWin version is cleaned: https://bugs.webkit.org/show_bug.cgi?id=20438
573 #if PLATFORM(WIN_OS) || PLATFORM(LINUX)
574 // FIXME: implement on Mac
575 bool WebViewImpl::sendContextMenuEvent(const WebKeyboardEvent& event)
576 {
577     static const int kContextMenuMargin = 1;
578     Frame* mainFrameImpl = page()->mainFrame();
579     FrameView* view = mainFrameImpl->view();
580     if (!view)
581         return false;
582
583     IntPoint coords(-1, -1);
584 #if PLATFORM(WIN_OS)
585     int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT);
586 #else
587     int rightAligned = 0;
588 #endif
589     IntPoint location;
590
591     // The context menu event was generated from the keyboard, so show the
592     // context menu by the current selection.
593     Position start = mainFrameImpl->selection()->selection().start();
594     Position end = mainFrameImpl->selection()->selection().end();
595
596     Frame* focusedFrame = page()->focusController()->focusedOrMainFrame();
597     Node* focusedNode = focusedFrame->document()->focusedNode();
598
599     if (start.node() && end.node()) {
600         RenderObject* renderer = start.node()->renderer();
601         if (!renderer)
602             return false;
603
604         RefPtr<Range> selection = mainFrameImpl->selection()->toNormalizedRange();
605         IntRect firstRect = mainFrameImpl->firstRectForRange(selection.get());
606
607         int x = rightAligned ? firstRect.right() : firstRect.x();
608         location = IntPoint(x, firstRect.bottom());
609     } else if (focusedNode)
610         location = focusedNode->getRect().bottomLeft();
611     else {
612         location = IntPoint(
613             rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin,
614             kContextMenuMargin);
615     }
616
617     location = view->contentsToWindow(location);
618     // FIXME: The IntSize(0, -1) is a hack to get the hit-testing to result in
619     // the selected element. Ideally we'd have the position of a context menu
620     // event be separate from its target node.
621     coords = location + IntSize(0, -1);
622
623     // The contextMenuController() holds onto the last context menu that was
624     // popped up on the page until a new one is created. We need to clear
625     // this menu before propagating the event through the DOM so that we can
626     // detect if we create a new menu for this event, since we won't create
627     // a new menu if the DOM swallows the event and the defaultEventHandler does
628     // not run.
629     page()->contextMenuController()->clearContextMenu();
630
631     focusedFrame->view()->setCursor(pointerCursor());
632     WebMouseEvent mouseEvent;
633     mouseEvent.button = WebMouseEvent::ButtonRight;
634     mouseEvent.x = coords.x();
635     mouseEvent.y = coords.y();
636     mouseEvent.type = WebInputEvent::MouseUp;
637
638     PlatformMouseEventBuilder platformEvent(view, mouseEvent);
639
640     m_contextMenuAllowed = true;
641     bool handled = focusedFrame->eventHandler()->sendContextMenuEvent(platformEvent);
642     m_contextMenuAllowed = false;
643     return handled;
644 }
645 #endif
646
647 bool WebViewImpl::keyEventDefault(const WebKeyboardEvent& event)
648 {
649     Frame* frame = focusedWebCoreFrame();
650     if (!frame)
651         return false;
652
653     switch (event.type) {
654     case WebInputEvent::Char:
655         if (event.windowsKeyCode == VKEY_SPACE) {
656             int keyCode = ((event.modifiers & WebInputEvent::ShiftKey) ? VKEY_PRIOR : VKEY_NEXT);
657             return scrollViewWithKeyboard(keyCode, event.modifiers);
658         }
659         break;
660     case WebInputEvent::RawKeyDown:
661         if (event.modifiers == WebInputEvent::ControlKey) {
662             switch (event.windowsKeyCode) {
663             case 'A':
664                 focusedFrame()->executeCommand(WebString::fromUTF8("SelectAll"));
665                 return true;
666             case VKEY_INSERT:
667             case 'C':
668                 focusedFrame()->executeCommand(WebString::fromUTF8("Copy"));
669                 return true;
670             // Match FF behavior in the sense that Ctrl+home/end are the only Ctrl
671             // key combinations which affect scrolling. Safari is buggy in the
672             // sense that it scrolls the page for all Ctrl+scrolling key
673             // combinations. For e.g. Ctrl+pgup/pgdn/up/down, etc.
674             case VKEY_HOME:
675             case VKEY_END:
676                 break;
677             default:
678                 return false;
679             }
680         }
681         if (!event.isSystemKey && !(event.modifiers & WebInputEvent::ShiftKey))
682             return scrollViewWithKeyboard(event.windowsKeyCode, event.modifiers);
683         break;
684     default:
685         break;
686     }
687     return false;
688 }
689
690 bool WebViewImpl::scrollViewWithKeyboard(int keyCode, int modifiers)
691 {
692     ScrollDirection scrollDirection;
693     ScrollGranularity scrollGranularity;
694
695     switch (keyCode) {
696     case VKEY_LEFT:
697         scrollDirection = ScrollLeft;
698         scrollGranularity = ScrollByLine;
699         break;
700     case VKEY_RIGHT:
701         scrollDirection = ScrollRight;
702         scrollGranularity = ScrollByLine;
703         break;
704     case VKEY_UP:
705         scrollDirection = ScrollUp;
706         scrollGranularity = ScrollByLine;
707         break;
708     case VKEY_DOWN:
709         scrollDirection = ScrollDown;
710         scrollGranularity = ScrollByLine;
711         break;
712     case VKEY_HOME:
713         scrollDirection = ScrollUp;
714         scrollGranularity = ScrollByDocument;
715         break;
716     case VKEY_END:
717         scrollDirection = ScrollDown;
718         scrollGranularity = ScrollByDocument;
719         break;
720     case VKEY_PRIOR:  // page up
721         scrollDirection = ScrollUp;
722         scrollGranularity = ScrollByPage;
723         break;
724     case VKEY_NEXT:  // page down
725         scrollDirection = ScrollDown;
726         scrollGranularity = ScrollByPage;
727         break;
728     default:
729         return false;
730     }
731
732     return propagateScroll(scrollDirection, scrollGranularity);
733 }
734
735 bool WebViewImpl::propagateScroll(ScrollDirection scrollDirection,
736                                   ScrollGranularity scrollGranularity)
737 {
738     Frame* frame = focusedWebCoreFrame();
739     if (!frame)
740         return false;
741
742     bool scrollHandled =
743         frame->eventHandler()->scrollOverflow(scrollDirection,
744                                               scrollGranularity);
745     Frame* currentFrame = frame;
746     while (!scrollHandled && currentFrame) {
747         scrollHandled = currentFrame->view()->scroll(scrollDirection,
748                                                      scrollGranularity);
749         currentFrame = currentFrame->tree()->parent();
750     }
751     return scrollHandled;
752 }
753
754 Frame* WebViewImpl::focusedWebCoreFrame()
755 {
756     return m_page.get() ? m_page->focusController()->focusedOrMainFrame() : 0;
757 }
758
759 WebViewImpl* WebViewImpl::fromPage(Page* page)
760 {
761     if (!page)
762         return 0;
763
764     return static_cast<ChromeClientImpl*>(page->chrome()->client())->webView();
765 }
766
767 // WebWidget ------------------------------------------------------------------
768
769 void WebViewImpl::close()
770 {
771     RefPtr<WebFrameImpl> mainFrameImpl;
772
773     if (m_page.get()) {
774         // Initiate shutdown for the entire frameset.  This will cause a lot of
775         // notifications to be sent.
776         if (m_page->mainFrame()) {
777             mainFrameImpl = WebFrameImpl::fromFrame(m_page->mainFrame());
778             m_page->mainFrame()->loader()->frameDetached();
779         }
780         m_page.clear();
781     }
782
783     // Should happen after m_page.clear().
784     if (m_devToolsAgent.get())
785         m_devToolsAgent.clear();
786
787     // We drop the client after the page has been destroyed to support the
788     // WebFrameClient::didDestroyScriptContext method.
789     if (mainFrameImpl)
790         mainFrameImpl->dropClient();
791
792     // Reset the delegate to prevent notifications being sent as we're being
793     // deleted.
794     m_client = 0;
795
796     deref();  // Balances ref() acquired in WebView::create
797 }
798
799 void WebViewImpl::resize(const WebSize& newSize)
800 {
801     if (m_size == newSize)
802         return;
803     m_size = newSize;
804
805     if (mainFrameImpl()->frameView()) {
806         mainFrameImpl()->frameView()->resize(m_size.width, m_size.height);
807         mainFrameImpl()->frame()->eventHandler()->sendResizeEvent();
808     }
809
810     if (m_client) {
811         WebRect damagedRect(0, 0, m_size.width, m_size.height);
812         m_client->didInvalidateRect(damagedRect);
813     }
814 }
815
816 void WebViewImpl::layout()
817 {
818     WebFrameImpl* webframe = mainFrameImpl();
819     if (webframe) {
820         // In order for our child HWNDs (NativeWindowWidgets) to update properly,
821         // they need to be told that we are updating the screen.  The problem is
822         // that the native widgets need to recalculate their clip region and not
823         // overlap any of our non-native widgets.  To force the resizing, call
824         // setFrameRect().  This will be a quick operation for most frames, but
825         // the NativeWindowWidgets will update a proper clipping region.
826         FrameView* view = webframe->frameView();
827         if (view)
828             view->setFrameRect(view->frameRect());
829
830         // setFrameRect may have the side-effect of causing existing page
831         // layout to be invalidated, so layout needs to be called last.
832
833         webframe->layout();
834     }
835 }
836
837 void WebViewImpl::paint(WebCanvas* canvas, const WebRect& rect)
838 {
839     WebFrameImpl* webframe = mainFrameImpl();
840     if (webframe)
841         webframe->paint(canvas, rect);
842 }
843
844 // FIXME: m_currentInputEvent should be removed once ChromeClient::show() can
845 // get the current-event information from WebCore.
846 const WebInputEvent* WebViewImpl::m_currentInputEvent = 0;
847
848 bool WebViewImpl::handleInputEvent(const WebInputEvent& inputEvent)
849 {
850     // If we've started a drag and drop operation, ignore input events until
851     // we're done.
852     if (m_doingDragAndDrop)
853         return true;
854
855     if (m_ignoreInputEvents)
856         return true;
857
858     // FIXME: Remove m_currentInputEvent.
859     // This only exists to allow ChromeClient::show() to know which mouse button
860     // triggered a window.open event.
861     // Safari must perform a similar hack, ours is in our WebKit glue layer
862     // theirs is in the application.  This should go when WebCore can be fixed
863     // to pass more event information to ChromeClient::show()
864     m_currentInputEvent = &inputEvent;
865
866     bool handled = true;
867
868     // FIXME: WebKit seems to always return false on mouse events processing
869     // methods. For now we'll assume it has processed them (as we are only
870     // interested in whether keyboard events are processed).
871     switch (inputEvent.type) {
872     case WebInputEvent::MouseMove:
873         mouseMove(*static_cast<const WebMouseEvent*>(&inputEvent));
874         break;
875
876     case WebInputEvent::MouseLeave:
877         mouseLeave(*static_cast<const WebMouseEvent*>(&inputEvent));
878         break;
879
880     case WebInputEvent::MouseWheel:
881         mouseWheel(*static_cast<const WebMouseWheelEvent*>(&inputEvent));
882         break;
883
884     case WebInputEvent::MouseDown:
885         mouseDown(*static_cast<const WebMouseEvent*>(&inputEvent));
886         break;
887
888     case WebInputEvent::MouseUp:
889         mouseUp(*static_cast<const WebMouseEvent*>(&inputEvent));
890         break;
891
892     case WebInputEvent::RawKeyDown:
893     case WebInputEvent::KeyDown:
894     case WebInputEvent::KeyUp:
895         handled = keyEvent(*static_cast<const WebKeyboardEvent*>(&inputEvent));
896         break;
897
898     case WebInputEvent::Char:
899         handled = charEvent(*static_cast<const WebKeyboardEvent*>(&inputEvent));
900         break;
901
902     default:
903         handled = false;
904     }
905
906     m_currentInputEvent = 0;
907
908     return handled;
909 }
910
911 void WebViewImpl::mouseCaptureLost()
912 {
913 }
914
915 void WebViewImpl::setFocus(bool enable)
916 {
917     m_page->focusController()->setFocused(enable);
918     if (enable) {
919         // Note that we don't call setActive() when disabled as this cause extra
920         // focus/blur events to be dispatched.
921         m_page->focusController()->setActive(true);
922         RefPtr<Frame> focusedFrame = m_page->focusController()->focusedFrame();
923         if (focusedFrame) {
924             Node* focusedNode = focusedFrame->document()->focusedNode();
925             if (focusedNode && focusedNode->isElementNode()
926                 && focusedFrame->selection()->selection().isNone()) {
927                 // If the selection was cleared while the WebView was not
928                 // focused, then the focus element shows with a focus ring but
929                 // no caret and does respond to keyboard inputs.
930                 Element* element = static_cast<Element*>(focusedNode);
931                 if (element->isTextFormControl())
932                     element->updateFocusAppearance(true);
933                 else if (focusedNode->isContentEditable()) {
934                     // updateFocusAppearance() selects all the text of
935                     // contentseditable DIVs. So we set the selection explicitly
936                     // instead. Note that this has the side effect of moving the
937                     // caret back to the beginning of the text.
938                     Position position(focusedNode, 0,
939                                       Position::PositionIsOffsetInAnchor);
940                     focusedFrame->selection()->setSelection(
941                         VisibleSelection(position, SEL_DEFAULT_AFFINITY));
942                 }
943             }
944         }
945         m_imeAcceptEvents = true;
946     } else {
947         hideAutoCompletePopup();
948
949         // Clear focus on the currently focused frame if any.
950         if (!m_page.get())
951             return;
952
953         Frame* frame = m_page->mainFrame();
954         if (!frame)
955             return;
956
957         RefPtr<Frame> focusedFrame = m_page->focusController()->focusedFrame();
958         if (focusedFrame.get()) {
959             // Finish an ongoing composition to delete the composition node.
960             Editor* editor = focusedFrame->editor();
961             if (editor && editor->hasComposition())
962                 editor->confirmComposition();
963             m_imeAcceptEvents = false;
964         }
965     }
966 }
967
968 bool WebViewImpl::handleCompositionEvent(WebCompositionCommand command,
969                                          int cursorPosition,
970                                          int targetStart,
971                                          int targetEnd,
972                                          const WebString& imeString)
973 {
974     Frame* focused = focusedWebCoreFrame();
975     if (!focused || !m_imeAcceptEvents)
976         return false;
977     Editor* editor = focused->editor();
978     if (!editor)
979         return false;
980     if (!editor->canEdit()) {
981         // The input focus has been moved to another WebWidget object.
982         // We should use this |editor| object only to complete the ongoing
983         // composition.
984         if (!editor->hasComposition())
985             return false;
986     }
987
988     // We should verify the parent node of this IME composition node are
989     // editable because JavaScript may delete a parent node of the composition
990     // node. In this case, WebKit crashes while deleting texts from the parent
991     // node, which doesn't exist any longer.
992     PassRefPtr<Range> range = editor->compositionRange();
993     if (range) {
994         const Node* node = range->startPosition().node();
995         if (!node || !node->isContentEditable())
996             return false;
997     }
998
999     if (command == WebCompositionCommandDiscard) {
1000         // A browser process sent an IPC message which does not contain a valid
1001         // string, which means an ongoing composition has been canceled.
1002         // If the ongoing composition has been canceled, replace the ongoing
1003         // composition string with an empty string and complete it.
1004         String emptyString;
1005         Vector<CompositionUnderline> emptyUnderlines;
1006         editor->setComposition(emptyString, emptyUnderlines, 0, 0);
1007     } else {
1008         // A browser process sent an IPC message which contains a string to be
1009         // displayed in this Editor object.
1010         // To display the given string, set the given string to the
1011         // m_compositionNode member of this Editor object and display it.
1012         if (targetStart < 0)
1013             targetStart = 0;
1014         if (targetEnd < 0)
1015             targetEnd = static_cast<int>(imeString.length());
1016         String compositionString(imeString);
1017         // Create custom underlines.
1018         // To emphasize the selection, the selected region uses a solid black
1019         // for its underline while other regions uses a pale gray for theirs.
1020         Vector<CompositionUnderline> underlines(3);
1021         underlines[0].startOffset = 0;
1022         underlines[0].endOffset = targetStart;
1023         underlines[0].thick = true;
1024         underlines[0].color.setRGB(0xd3, 0xd3, 0xd3);
1025         underlines[1].startOffset = targetStart;
1026         underlines[1].endOffset = targetEnd;
1027         underlines[1].thick = true;
1028         underlines[1].color.setRGB(0x00, 0x00, 0x00);
1029         underlines[2].startOffset = targetEnd;
1030         underlines[2].endOffset = static_cast<int>(imeString.length());
1031         underlines[2].thick = true;
1032         underlines[2].color.setRGB(0xd3, 0xd3, 0xd3);
1033         // When we use custom underlines, WebKit ("InlineTextBox.cpp" Line 282)
1034         // prevents from writing a text in between 'selectionStart' and
1035         // 'selectionEnd' somehow.
1036         // Therefore, we use the 'cursorPosition' for these arguments so that
1037         // there are not any characters in the above region.
1038         editor->setComposition(compositionString, underlines,
1039                                cursorPosition, cursorPosition);
1040         // The given string is a result string, which means the ongoing
1041         // composition has been completed. I have to call the
1042         // Editor::confirmCompletion() and complete this composition.
1043         if (command == WebCompositionCommandConfirm)
1044             editor->confirmComposition();
1045     }
1046
1047     return editor->hasComposition();
1048 }
1049
1050 bool WebViewImpl::queryCompositionStatus(bool* enableIME, WebRect* caretRect)
1051 {
1052     // Store whether the selected node needs IME and the caret rectangle.
1053     // This process consists of the following four steps:
1054     //  1. Retrieve the selection controller of the focused frame;
1055     //  2. Retrieve the caret rectangle from the controller;
1056     //  3. Convert the rectangle, which is relative to the parent view, to the
1057     //     one relative to the client window, and;
1058     //  4. Store the converted rectangle.
1059     const Frame* focused = focusedWebCoreFrame();
1060     if (!focused)
1061         return false;
1062
1063     const Editor* editor = focused->editor();
1064     if (!editor || !editor->canEdit())
1065         return false;
1066
1067     SelectionController* controller = focused->selection();
1068     if (!controller)
1069         return false;
1070
1071     const Node* node = controller->start().node();
1072     if (!node)
1073         return false;
1074
1075     *enableIME = node->shouldUseInputMethod() && !controller->isInPasswordField();
1076     const FrameView* view = node->document()->view();
1077     if (!view)
1078         return false;
1079
1080     *caretRect = view->contentsToWindow(controller->absoluteCaretBounds());
1081     return true;
1082 }
1083
1084 void WebViewImpl::setTextDirection(WebTextDirection direction)
1085 {
1086     // The Editor::setBaseWritingDirection() function checks if we can change
1087     // the text direction of the selected node and updates its DOM "dir"
1088     // attribute and its CSS "direction" property.
1089     // So, we just call the function as Safari does.
1090     const Frame* focused = focusedWebCoreFrame();
1091     if (!focused)
1092         return;
1093
1094     Editor* editor = focused->editor();
1095     if (!editor || !editor->canEdit())
1096         return;
1097
1098     switch (direction) {
1099     case WebTextDirectionDefault:
1100         editor->setBaseWritingDirection(NaturalWritingDirection);
1101         break;
1102
1103     case WebTextDirectionLeftToRight:
1104         editor->setBaseWritingDirection(LeftToRightWritingDirection);
1105         break;
1106
1107     case WebTextDirectionRightToLeft:
1108         editor->setBaseWritingDirection(RightToLeftWritingDirection);
1109         break;
1110
1111     default:
1112         notImplemented();
1113         break;
1114     }
1115 }
1116
1117 // WebView --------------------------------------------------------------------
1118
1119 WebSettings* WebViewImpl::settings()
1120 {
1121     if (!m_webSettings.get())
1122         m_webSettings.set(new WebSettingsImpl(m_page->settings()));
1123     ASSERT(m_webSettings.get());
1124     return m_webSettings.get();
1125 }
1126
1127 WebString WebViewImpl::pageEncoding() const
1128 {
1129     if (!m_page.get())
1130         return WebString();
1131
1132     return m_page->mainFrame()->loader()->encoding();
1133 }
1134
1135 void WebViewImpl::setPageEncoding(const WebString& encodingName)
1136 {
1137     if (!m_page.get())
1138         return;
1139
1140     // Only change override encoding, don't change default encoding.
1141     // Note that the new encoding must be 0 if it isn't supposed to be set.
1142     String newEncodingName;
1143     if (!encodingName.isEmpty())
1144         newEncodingName = encodingName;
1145     m_page->mainFrame()->loader()->reloadWithOverrideEncoding(newEncodingName);
1146 }
1147
1148 bool WebViewImpl::dispatchBeforeUnloadEvent()
1149 {
1150     // FIXME: This should really cause a recursive depth-first walk of all
1151     // frames in the tree, calling each frame's onbeforeunload.  At the moment,
1152     // we're consistent with Safari 3.1, not IE/FF.
1153     Frame* frame = m_page->focusController()->focusedOrMainFrame();
1154     if (!frame)
1155         return true;
1156
1157     return frame->shouldClose();
1158 }
1159
1160 void WebViewImpl::dispatchUnloadEvent()
1161 {
1162     // Run unload handlers.
1163     m_page->mainFrame()->loader()->closeURL();
1164 }
1165
1166 WebFrame* WebViewImpl::mainFrame()
1167 {
1168     return mainFrameImpl();
1169 }
1170
1171 WebFrame* WebViewImpl::findFrameByName(
1172     const WebString& name, WebFrame* relativeToFrame)
1173 {
1174     if (!relativeToFrame)
1175         relativeToFrame = mainFrame();
1176     Frame* frame = static_cast<WebFrameImpl*>(relativeToFrame)->frame();
1177     frame = frame->tree()->find(name);
1178     return WebFrameImpl::fromFrame(frame);
1179 }
1180
1181 WebFrame* WebViewImpl::focusedFrame()
1182 {
1183     return WebFrameImpl::fromFrame(focusedWebCoreFrame());
1184 }
1185
1186 void WebViewImpl::setFocusedFrame(WebFrame* frame)
1187 {
1188     if (!frame) {
1189         // Clears the focused frame if any.
1190         Frame* frame = focusedWebCoreFrame();
1191         if (frame)
1192             frame->selection()->setFocused(false);
1193         return;
1194     }
1195     WebFrameImpl* frameImpl = static_cast<WebFrameImpl*>(frame);
1196     Frame* webcoreFrame = frameImpl->frame();
1197     webcoreFrame->page()->focusController()->setFocusedFrame(webcoreFrame);
1198 }
1199
1200 void WebViewImpl::setInitialFocus(bool reverse)
1201 {
1202     if (!m_page.get())
1203         return;
1204
1205     // Since we don't have a keyboard event, we'll create one.
1206     WebKeyboardEvent keyboardEvent;
1207     keyboardEvent.type = WebInputEvent::RawKeyDown;
1208     if (reverse)
1209         keyboardEvent.modifiers = WebInputEvent::ShiftKey;
1210
1211     // VK_TAB which is only defined on Windows.
1212     keyboardEvent.windowsKeyCode = 0x09;
1213     PlatformKeyboardEventBuilder platformEvent(keyboardEvent);
1214     RefPtr<KeyboardEvent> webkitEvent = KeyboardEvent::create(platformEvent, 0);
1215     page()->focusController()->setInitialFocus(
1216         reverse ? FocusDirectionBackward : FocusDirectionForward,
1217         webkitEvent.get());
1218 }
1219
1220 void WebViewImpl::clearFocusedNode()
1221 {
1222     if (!m_page.get())
1223         return;
1224
1225     RefPtr<Frame> frame = m_page->mainFrame();
1226     if (!frame.get())
1227         return;
1228
1229     RefPtr<Document> document = frame->document();
1230     if (!document.get())
1231         return;
1232
1233     RefPtr<Node> oldFocusedNode = document->focusedNode();
1234
1235     // Clear the focused node.
1236     document->setFocusedNode(0);
1237
1238     if (!oldFocusedNode.get())
1239         return;
1240
1241     // If a text field has focus, we need to make sure the selection controller
1242     // knows to remove selection from it. Otherwise, the text field is still
1243     // processing keyboard events even though focus has been moved to the page and
1244     // keystrokes get eaten as a result.
1245     if (oldFocusedNode->hasTagName(HTMLNames::textareaTag)
1246         || (oldFocusedNode->hasTagName(HTMLNames::inputTag)
1247             && static_cast<HTMLInputElement*>(oldFocusedNode.get())->isTextField())) {
1248         // Clear the selection.
1249         SelectionController* selection = frame->selection();
1250         selection->clear();
1251     }
1252 }
1253
1254 int WebViewImpl::zoomLevel()
1255 {
1256     return m_zoomLevel;
1257 }
1258
1259 int WebViewImpl::setZoomLevel(bool textOnly, int zoomLevel)
1260 {
1261     float zoomFactor = static_cast<float>(
1262         std::max(std::min(std::pow(textSizeMultiplierRatio, zoomLevel),
1263                           maxTextSizeMultiplier),
1264                  minTextSizeMultiplier));
1265     Frame* frame = mainFrameImpl()->frame();
1266     if (zoomFactor != frame->zoomFactor()) {
1267         m_zoomLevel = zoomLevel;
1268         frame->setZoomFactor(zoomFactor, textOnly);
1269     }
1270     return m_zoomLevel;
1271 }
1272
1273 void WebViewImpl::performMediaPlayerAction(const WebMediaPlayerAction& action,
1274                                            const WebPoint& location)
1275 {
1276     HitTestResult result =
1277         hitTestResultForWindowPos(location);
1278     RefPtr<Node> node = result.innerNonSharedNode();
1279     if (!node->hasTagName(HTMLNames::videoTag) && !node->hasTagName(HTMLNames::audioTag))
1280       return;
1281
1282     RefPtr<HTMLMediaElement> mediaElement =
1283         static_pointer_cast<HTMLMediaElement>(node);
1284     switch (action.type) {
1285     case WebMediaPlayerAction::Play:
1286         if (action.enable)
1287             mediaElement->play();
1288         else
1289             mediaElement->pause();
1290         break;
1291     case WebMediaPlayerAction::Mute:
1292         mediaElement->setMuted(action.enable);
1293         break;
1294     case WebMediaPlayerAction::Loop:
1295         mediaElement->setLoop(action.enable);
1296         break;
1297     default:
1298         ASSERT_NOT_REACHED();
1299     }
1300 }
1301
1302 void WebViewImpl::copyImageAt(const WebPoint& point)
1303 {
1304     if (!m_page.get())
1305         return;
1306
1307     HitTestResult result = hitTestResultForWindowPos(point);
1308
1309     if (result.absoluteImageURL().isEmpty()) {
1310         // There isn't actually an image at these coordinates.  Might be because
1311         // the window scrolled while the context menu was open or because the page
1312         // changed itself between when we thought there was an image here and when
1313         // we actually tried to retreive the image.
1314         //
1315         // FIXME: implement a cache of the most recent HitTestResult to avoid having
1316         //        to do two hit tests.
1317         return;
1318     }
1319
1320     m_page->mainFrame()->editor()->copyImage(result);
1321 }
1322
1323 void WebViewImpl::dragSourceEndedAt(
1324     const WebPoint& clientPoint,
1325     const WebPoint& screenPoint,
1326     WebDragOperation operation)
1327 {
1328     PlatformMouseEvent pme(clientPoint,
1329                            screenPoint,
1330                            LeftButton, MouseEventMoved, 0, false, false, false,
1331                            false, 0);
1332     m_page->mainFrame()->eventHandler()->dragSourceEndedAt(pme,
1333         static_cast<DragOperation>(operation));
1334 }
1335
1336 void WebViewImpl::dragSourceSystemDragEnded()
1337 {
1338     // It's possible for us to get this callback while not doing a drag if
1339     // it's from a previous page that got unloaded.
1340     if (m_doingDragAndDrop) {
1341         m_page->dragController()->dragEnded();
1342         m_doingDragAndDrop = false;
1343     }
1344 }
1345
1346 WebDragOperation WebViewImpl::dragTargetDragEnter(
1347     const WebDragData& webDragData, int identity,
1348     const WebPoint& clientPoint,
1349     const WebPoint& screenPoint,
1350     WebDragOperationsMask operationsAllowed)
1351 {
1352     ASSERT(!m_currentDragData.get());
1353
1354     m_currentDragData = webDragData;
1355     m_dragIdentity = identity;
1356     m_operationsAllowed = operationsAllowed;
1357
1358     DragData dragData(
1359         m_currentDragData.get(),
1360         clientPoint,
1361         screenPoint,
1362         static_cast<DragOperation>(operationsAllowed));
1363
1364     m_dropEffect = DropEffectDefault;
1365     m_dragTargetDispatch = true;
1366     DragOperation effect = m_page->dragController()->dragEntered(&dragData);
1367     // Mask the operation against the drag source's allowed operations.
1368     if ((effect & dragData.draggingSourceOperationMask()) != effect)
1369         effect = DragOperationNone;
1370     m_dragTargetDispatch = false;
1371
1372     if (m_dropEffect != DropEffectDefault) {
1373         m_dragOperation = (m_dropEffect != DropEffectNone) ? WebDragOperationCopy
1374                                                            : WebDragOperationNone;
1375     } else
1376         m_dragOperation = static_cast<WebDragOperation>(effect);
1377     return m_dragOperation;
1378 }
1379
1380 WebDragOperation WebViewImpl::dragTargetDragOver(
1381     const WebPoint& clientPoint,
1382     const WebPoint& screenPoint,
1383     WebDragOperationsMask operationsAllowed)
1384 {
1385     ASSERT(m_currentDragData.get());
1386
1387     m_operationsAllowed = operationsAllowed;
1388     DragData dragData(
1389         m_currentDragData.get(),
1390         clientPoint,
1391         screenPoint,
1392         static_cast<DragOperation>(operationsAllowed));
1393
1394     m_dropEffect = DropEffectDefault;
1395     m_dragTargetDispatch = true;
1396     DragOperation effect = m_page->dragController()->dragUpdated(&dragData);
1397     // Mask the operation against the drag source's allowed operations.
1398     if ((effect & dragData.draggingSourceOperationMask()) != effect)
1399         effect = DragOperationNone;
1400     m_dragTargetDispatch = false;
1401
1402     if (m_dropEffect != DropEffectDefault) {
1403         m_dragOperation = (m_dropEffect != DropEffectNone) ? WebDragOperationCopy
1404                                                            : WebDragOperationNone;
1405     } else
1406         m_dragOperation = static_cast<WebDragOperation>(effect);
1407     return m_dragOperation;
1408 }
1409
1410 void WebViewImpl::dragTargetDragLeave()
1411 {
1412     ASSERT(m_currentDragData.get());
1413
1414     DragData dragData(
1415         m_currentDragData.get(),
1416         IntPoint(),
1417         IntPoint(),
1418         static_cast<DragOperation>(m_operationsAllowed));
1419
1420     m_dragTargetDispatch = true;
1421     m_page->dragController()->dragExited(&dragData);
1422     m_dragTargetDispatch = false;
1423
1424     m_currentDragData = 0;
1425     m_dropEffect = DropEffectDefault;
1426     m_dragOperation = WebDragOperationNone;
1427     m_dragIdentity = 0;
1428 }
1429
1430 void WebViewImpl::dragTargetDrop(const WebPoint& clientPoint,
1431                                  const WebPoint& screenPoint)
1432 {
1433     ASSERT(m_currentDragData.get());
1434
1435     // If this webview transitions from the "drop accepting" state to the "not
1436     // accepting" state, then our IPC message reply indicating that may be in-
1437     // flight, or else delayed by javascript processing in this webview.  If a
1438     // drop happens before our IPC reply has reached the browser process, then
1439     // the browser forwards the drop to this webview.  So only allow a drop to
1440     // proceed if our webview m_dragOperation state is not DragOperationNone.
1441
1442     if (m_dragOperation == WebDragOperationNone) { // IPC RACE CONDITION: do not allow this drop.
1443         dragTargetDragLeave();
1444         return;
1445     }
1446
1447     DragData dragData(
1448         m_currentDragData.get(),
1449         clientPoint,
1450         screenPoint,
1451         static_cast<DragOperation>(m_operationsAllowed));
1452
1453     m_dragTargetDispatch = true;
1454     m_page->dragController()->performDrag(&dragData);
1455     m_dragTargetDispatch = false;
1456
1457     m_currentDragData = 0;
1458     m_dropEffect = DropEffectDefault;
1459     m_dragOperation = WebDragOperationNone;
1460     m_dragIdentity = 0;
1461 }
1462
1463 int WebViewImpl::dragIdentity()
1464 {
1465     if (m_dragTargetDispatch)
1466         return m_dragIdentity;
1467     return 0;
1468 }
1469
1470 unsigned long WebViewImpl::createUniqueIdentifierForRequest() {
1471     if (m_page)
1472         return m_page->progress()->createUniqueIdentifier();
1473     return 0;
1474 }
1475
1476 void WebViewImpl::inspectElementAt(const WebPoint& point)
1477 {
1478     if (!m_page.get())
1479         return;
1480
1481     if (point.x == -1 || point.y == -1)
1482         m_page->inspectorController()->inspect(0);
1483     else {
1484         HitTestResult result = hitTestResultForWindowPos(point);
1485
1486         if (!result.innerNonSharedNode())
1487             return;
1488
1489         m_page->inspectorController()->inspect(result.innerNonSharedNode());
1490     }
1491 }
1492
1493 WebString WebViewImpl::inspectorSettings() const
1494 {
1495     return m_inspectorSettings;
1496 }
1497
1498 void WebViewImpl::setInspectorSettings(const WebString& settings)
1499 {
1500     m_inspectorSettings = settings;
1501 }
1502
1503 WebDevToolsAgent* WebViewImpl::devToolsAgent()
1504 {
1505     return m_devToolsAgent.get();
1506 }
1507
1508 void WebViewImpl::setDevToolsAgent(WebDevToolsAgent* devToolsAgent)
1509 {
1510     ASSERT(!m_devToolsAgent.get()); // May only set once!
1511     m_devToolsAgent.set(static_cast<WebDevToolsAgentPrivate*>(devToolsAgent));
1512 }
1513
1514 WebAccessibilityObject WebViewImpl::accessibilityObject()
1515 {
1516     if (!mainFrameImpl())
1517         return WebAccessibilityObject();
1518
1519     Document* document = mainFrameImpl()->frame()->document();
1520     return WebAccessibilityObject(
1521         document->axObjectCache()->getOrCreate(document->renderer()));
1522 }
1523
1524 void WebViewImpl::applyAutofillSuggestions(
1525     const WebNode& node,
1526     const WebVector<WebString>& suggestions,
1527     int defaultSuggestionIndex)
1528 {
1529     if (!m_page.get() || suggestions.isEmpty()) {
1530         hideAutoCompletePopup();
1531         return;
1532     }
1533
1534     ASSERT(defaultSuggestionIndex < static_cast<int>(suggestions.size()));
1535
1536     if (RefPtr<Frame> focused = m_page->focusController()->focusedFrame()) {
1537         RefPtr<Document> document = focused->document();
1538         if (!document.get()) {
1539             hideAutoCompletePopup();
1540             return;
1541         }
1542
1543         RefPtr<Node> focusedNode = document->focusedNode();
1544         // If the node for which we queried the autofill suggestions is not the
1545         // focused node, then we have nothing to do.  FIXME: also check the
1546         // carret is at the end and that the text has not changed.
1547         if (!focusedNode.get() || focusedNode != PassRefPtr<Node>(node)) {
1548             hideAutoCompletePopup();
1549             return;
1550         }
1551
1552         if (!focusedNode->hasTagName(HTMLNames::inputTag)) {
1553             ASSERT_NOT_REACHED();
1554             return;
1555         }
1556
1557         HTMLInputElement* inputElem =
1558             static_cast<HTMLInputElement*>(focusedNode.get());
1559
1560         // The first time the autocomplete is shown we'll create the client and the
1561         // popup.
1562         if (!m_autocompletePopupClient.get())
1563             m_autocompletePopupClient.set(new AutocompletePopupMenuClient(this));
1564         m_autocompletePopupClient->initialize(inputElem,
1565                                               suggestions,
1566                                               defaultSuggestionIndex);
1567         if (!m_autocompletePopup.get()) {
1568             m_autocompletePopup =
1569                 PopupContainer::create(m_autocompletePopupClient.get(),
1570                                        autocompletePopupSettings);
1571         }
1572
1573         if (m_autocompletePopupShowing) {
1574             m_autocompletePopupClient->setSuggestions(suggestions);
1575             refreshAutofillPopup();
1576         } else {
1577             m_autocompletePopup->show(focusedNode->getRect(),
1578                                       focusedNode->ownerDocument()->view(), 0);
1579             m_autocompletePopupShowing = true;
1580         }
1581     }
1582 }
1583
1584 void WebViewImpl::hideAutofillPopup()
1585 {
1586     hideAutoCompletePopup();
1587 }
1588
1589 // WebView --------------------------------------------------------------------
1590
1591 bool WebViewImpl::setDropEffect(bool accept)
1592 {
1593     if (m_dragTargetDispatch) {
1594         m_dropEffect = accept ? DropEffectCopy : DropEffectNone;
1595         return true;
1596     }
1597     return false;
1598 }
1599
1600 void WebViewImpl::setIsTransparent(bool isTransparent)
1601 {
1602     // Set any existing frames to be transparent.
1603     Frame* frame = m_page->mainFrame();
1604     while (frame) {
1605         frame->view()->setTransparent(isTransparent);
1606         frame = frame->tree()->traverseNext();
1607     }
1608
1609     // Future frames check this to know whether to be transparent.
1610     m_isTransparent = isTransparent;
1611 }
1612
1613 bool WebViewImpl::isTransparent() const
1614 {
1615     return m_isTransparent;
1616 }
1617
1618 void WebViewImpl::setIsActive(bool active)
1619 {
1620     if (page() && page()->focusController())
1621         page()->focusController()->setActive(active);
1622 }
1623
1624 bool WebViewImpl::isActive() const
1625 {
1626     return (page() && page()->focusController()) ? page()->focusController()->isActive() : false;
1627 }
1628
1629 void WebViewImpl::setScrollbarColors(unsigned inactiveColor,
1630                                      unsigned activeColor,
1631                                      unsigned trackColor) {
1632 #if PLATFORM(LINUX)
1633     RenderThemeChromiumLinux::setScrollbarColors(inactiveColor,
1634                                                  activeColor,
1635                                                  trackColor);
1636 #endif
1637 }
1638
1639 void WebViewImpl::didCommitLoad(bool* isNewNavigation)
1640 {
1641     if (isNewNavigation)
1642         *isNewNavigation = m_observedNewNavigation;
1643
1644 #ifndef NDEBUG
1645     ASSERT(!m_observedNewNavigation
1646         || m_page->mainFrame()->loader()->documentLoader() == m_newNavigationLoader);
1647     m_newNavigationLoader = 0;
1648 #endif
1649     m_observedNewNavigation = false;
1650 }
1651
1652 bool WebViewImpl::navigationPolicyFromMouseEvent(unsigned short button,
1653                                                  bool ctrl, bool shift,
1654                                                  bool alt, bool meta,
1655                                                  WebNavigationPolicy* policy)
1656 {
1657 #if PLATFORM(WIN_OS) || PLATFORM(LINUX) || PLATFORM(FREEBSD)
1658     const bool newTabModifier = (button == 1) || ctrl;
1659 #elif PLATFORM(DARWIN)
1660     const bool newTabModifier = (button == 1) || meta;
1661 #endif
1662     if (!newTabModifier && !shift && !alt)
1663       return false;
1664
1665     ASSERT(policy);
1666     if (newTabModifier) {
1667         if (shift)
1668           *policy = WebNavigationPolicyNewForegroundTab;
1669         else
1670           *policy = WebNavigationPolicyNewBackgroundTab;
1671     } else {
1672         if (shift)
1673           *policy = WebNavigationPolicyNewWindow;
1674         else
1675           *policy = WebNavigationPolicyDownload;
1676     }
1677     return true;
1678 }
1679
1680 void WebViewImpl::startDragging(const WebPoint& eventPos,
1681                                 const WebDragData& dragData,
1682                                 WebDragOperationsMask mask)
1683 {
1684     if (!m_client)
1685         return;
1686     ASSERT(!m_doingDragAndDrop);
1687     m_doingDragAndDrop = true;
1688     m_client->startDragging(eventPos, dragData, mask);
1689 }
1690
1691 void WebViewImpl::setCurrentHistoryItem(HistoryItem* item)
1692 {
1693     m_backForwardListClientImpl.setCurrentHistoryItem(item);
1694 }
1695
1696 HistoryItem* WebViewImpl::previousHistoryItem()
1697 {
1698     return m_backForwardListClientImpl.previousHistoryItem();
1699 }
1700
1701 void WebViewImpl::observeNewNavigation()
1702 {
1703     m_observedNewNavigation = true;
1704 #ifndef NDEBUG
1705     m_newNavigationLoader = m_page->mainFrame()->loader()->documentLoader();
1706 #endif
1707 }
1708
1709 void WebViewImpl::hideAutoCompletePopup()
1710 {
1711     if (m_autocompletePopupShowing) {
1712         m_autocompletePopup->hidePopup();
1713         autoCompletePopupDidHide();
1714     }
1715 }
1716
1717 void WebViewImpl::autoCompletePopupDidHide()
1718 {
1719     m_autocompletePopupShowing = false;
1720 }
1721
1722 void WebViewImpl::setIgnoreInputEvents(bool newValue)
1723 {
1724     ASSERT(m_ignoreInputEvents != newValue);
1725     m_ignoreInputEvents = newValue;
1726 }
1727
1728 #if ENABLE(NOTIFICATIONS)
1729 NotificationPresenterImpl* WebViewImpl::notificationPresenterImpl()
1730 {
1731     if (!m_notificationPresenter.isInitialized() && m_client)
1732         m_notificationPresenter.initialize(m_client->notificationPresenter());
1733     return &m_notificationPresenter;
1734 }
1735 #endif
1736
1737 void WebViewImpl::refreshAutofillPopup()
1738 {
1739     ASSERT(m_autocompletePopupShowing);
1740
1741     // Hide the popup if it has become empty.
1742     if (!m_autocompletePopupClient->listSize()) {
1743         hideAutoCompletePopup();
1744         return;
1745     }
1746
1747     IntRect oldBounds = m_autocompletePopup->boundsRect();
1748     m_autocompletePopup->refresh();
1749     IntRect newBounds = m_autocompletePopup->boundsRect();
1750     // Let's resize the backing window if necessary.
1751     if (oldBounds != newBounds) {
1752         WebPopupMenuImpl* popupMenu =
1753             static_cast<WebPopupMenuImpl*>(m_autocompletePopup->client());
1754         popupMenu->client()->setWindowRect(newBounds);
1755     }
1756 }
1757
1758 Node* WebViewImpl::focusedWebCoreNode()
1759 {
1760     Frame* frame = m_page->focusController()->focusedFrame();
1761     if (!frame)
1762         return 0;
1763
1764     Document* document = frame->document();
1765     if (!document)
1766         return 0;
1767
1768     return document->focusedNode();
1769 }
1770
1771 HitTestResult WebViewImpl::hitTestResultForWindowPos(const IntPoint& pos)
1772 {
1773     IntPoint docPoint(m_page->mainFrame()->view()->windowToContents(pos));
1774     return m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(docPoint, false);
1775 }
1776
1777 void WebViewImpl::setTabsToLinks(bool enable)
1778 {
1779     m_tabsToLinks = enable;
1780 }
1781
1782 bool WebViewImpl::tabsToLinks() const
1783 {
1784     return m_tabsToLinks;
1785 }
1786
1787 } // namespace WebKit