1/*
2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33#include "ChromeClientImpl.h"
34
35#include "ColorChooserPopupUIController.h"
36#include "ColorChooserUIController.h"
37#include "DateTimeChooserImpl.h"
38#include "ExternalDateTimeChooser.h"
39#include "ExternalPopupMenu.h"
40#include "HTMLNames.h"
41#include "PopupContainer.h"
42#include "PopupMenuChromium.h"
43#include "RuntimeEnabledFeatures.h"
44#include "WebAXObject.h"
45#include "WebAutofillClient.h"
46#include "WebColorChooser.h"
47#include "WebColorSuggestion.h"
48#include "WebConsoleMessage.h"
49#include "WebFileChooserCompletionImpl.h"
50#include "WebFrameClient.h"
51#include "WebFrameImpl.h"
52#include "WebInputElement.h"
53#include "WebInputEvent.h"
54#include "WebInputEventConversion.h"
55#include "WebKit.h"
56#include "WebNode.h"
57#include "WebPasswordGeneratorClient.h"
58#include "WebPlugin.h"
59#include "WebPluginContainerImpl.h"
60#include "WebPopupMenuImpl.h"
61#include "WebPopupMenuInfo.h"
62#include "WebPopupType.h"
63#include "WebSettings.h"
64#include "WebSettingsImpl.h"
65#include "WebTextDirection.h"
66#include "WebUserGestureIndicator.h"
67#include "WebUserGestureToken.h"
68#include "WebViewClient.h"
69#include "WebViewImpl.h"
70#include "WebWindowFeatures.h"
71#include "bindings/v8/ScriptController.h"
72#include "core/accessibility/AXObject.h"
73#include "core/accessibility/AXObjectCache.h"
74#include "core/dom/Document.h"
75#include "core/dom/DocumentFullscreen.h"
76#include "core/dom/Node.h"
77#include "core/html/HTMLInputElement.h"
78#include "core/loader/DocumentLoader.h"
79#include "core/loader/FrameLoadRequest.h"
80#include "core/frame/Console.h"
81#include "core/frame/FrameView.h"
82#include "core/page/Page.h"
83#include "core/page/PagePopupDriver.h"
84#include "core/frame/Settings.h"
85#include "core/page/WindowFeatures.h"
86#include "core/rendering/HitTestResult.h"
87#include "core/rendering/RenderWidget.h"
88#include "modules/geolocation/Geolocation.h"
89#include "platform/ColorChooser.h"
90#include "platform/ColorChooserClient.h"
91#include "platform/Cursor.h"
92#include "platform/DateTimeChooser.h"
93#include "platform/FileChooser.h"
94#include "platform/PlatformScreen.h"
95#include "platform/exported/WrappedResourceRequest.h"
96#include "platform/geometry/FloatRect.h"
97#include "platform/geometry/IntRect.h"
98#include "platform/graphics/GraphicsLayer.h"
99#include "platform/weborigin/SecurityOrigin.h"
100#include "public/platform/Platform.h"
101#include "public/platform/WebCursorInfo.h"
102#include "public/platform/WebRect.h"
103#include "public/platform/WebURLRequest.h"
104#include "public/web/WebTouchAction.h"
105#include "wtf/text/CString.h"
106#include "wtf/text/StringBuilder.h"
107#include "wtf/text/StringConcatenate.h"
108#include "wtf/unicode/CharacterNames.h"
109
110using namespace WebCore;
111
112namespace blink {
113
114// Converts a WebCore::PopupContainerType to a blink::WebPopupType.
115static WebPopupType convertPopupType(PopupContainer::PopupType type)
116{
117    switch (type) {
118    case PopupContainer::Select:
119        return WebPopupTypeSelect;
120    case PopupContainer::Suggestion:
121        return WebPopupTypeSuggestion;
122    default:
123        ASSERT_NOT_REACHED();
124        return WebPopupTypeNone;
125    }
126}
127
128// Converts a WebCore::AXObjectCache::AXNotification to a blink::WebAXEvent
129static WebAXEvent toWebAXEvent(AXObjectCache::AXNotification notification)
130{
131    // These enums have the same values; enforced in AssertMatchingEnums.cpp.
132    return static_cast<WebAXEvent>(notification);
133}
134
135ChromeClientImpl::ChromeClientImpl(WebViewImpl* webView)
136    : m_webView(webView)
137    , m_toolbarsVisible(true)
138    , m_statusbarVisible(true)
139    , m_scrollbarsVisible(true)
140    , m_menubarVisible(true)
141    , m_resizable(true)
142    , m_pagePopupDriver(webView)
143{
144}
145
146ChromeClientImpl::~ChromeClientImpl()
147{
148}
149
150void* ChromeClientImpl::webView() const
151{
152    return static_cast<void*>(m_webView);
153}
154
155void ChromeClientImpl::chromeDestroyed()
156{
157    // Our lifetime is bound to the WebViewImpl.
158}
159
160void ChromeClientImpl::setWindowRect(const FloatRect& r)
161{
162    if (m_webView->client())
163        m_webView->client()->setWindowRect(IntRect(r));
164}
165
166FloatRect ChromeClientImpl::windowRect()
167{
168    WebRect rect;
169    if (m_webView->client())
170        rect = m_webView->client()->rootWindowRect();
171    else {
172        // These numbers will be fairly wrong. The window's x/y coordinates will
173        // be the top left corner of the screen and the size will be the content
174        // size instead of the window size.
175        rect.width = m_webView->size().width;
176        rect.height = m_webView->size().height;
177    }
178    return FloatRect(rect);
179}
180
181FloatRect ChromeClientImpl::pageRect()
182{
183    // We hide the details of the window's border thickness from the web page by
184    // simple re-using the window position here.  So, from the point-of-view of
185    // the web page, the window has no border.
186    return windowRect();
187}
188
189void ChromeClientImpl::focus()
190{
191    if (m_webView->client())
192        m_webView->client()->didFocus();
193}
194
195void ChromeClientImpl::unfocus()
196{
197    if (m_webView->client())
198        m_webView->client()->didBlur();
199}
200
201bool ChromeClientImpl::canTakeFocus(FocusDirection)
202{
203    // For now the browser can always take focus if we're not running layout
204    // tests.
205    return !layoutTestMode();
206}
207
208void ChromeClientImpl::takeFocus(FocusDirection direction)
209{
210    if (!m_webView->client())
211        return;
212    if (direction == FocusDirectionBackward)
213        m_webView->client()->focusPrevious();
214    else
215        m_webView->client()->focusNext();
216}
217
218void ChromeClientImpl::focusedNodeChanged(Node* node)
219{
220    m_webView->client()->focusedNodeChanged(WebNode(node));
221
222    WebURL focusURL;
223    if (node && node->isLink()) {
224        // This HitTestResult hack is the easiest way to get a link URL out of a
225        // WebCore::Node.
226        HitTestResult hitTest(IntPoint(0, 0));
227        // This cast must be valid because of the isLink() check.
228        hitTest.setURLElement(toElement(node));
229        if (hitTest.isLiveLink())
230            focusURL = hitTest.absoluteLinkURL();
231    }
232    m_webView->client()->setKeyboardFocusURL(focusURL);
233}
234
235Page* ChromeClientImpl::createWindow(Frame* frame, const FrameLoadRequest& r, const WindowFeatures& features,
236    NavigationPolicy navigationPolicy, ShouldSendReferrer shouldSendReferrer)
237{
238    if (!m_webView->client())
239        return 0;
240
241    WebNavigationPolicy policy = static_cast<WebNavigationPolicy>(navigationPolicy);
242    if (policy == WebNavigationPolicyIgnore)
243        policy = getNavigationPolicy();
244
245    DocumentFullscreen::webkitCancelFullScreen(frame->document());
246
247    WebViewImpl* newView = toWebViewImpl(
248        m_webView->client()->createView(WebFrameImpl::fromFrame(frame), WrappedResourceRequest(r.resourceRequest()), features, r.frameName(), policy, shouldSendReferrer == NeverSendReferrer));
249    if (!newView)
250        return 0;
251    return newView->page();
252}
253
254static inline void updatePolicyForEvent(const WebInputEvent* inputEvent, NavigationPolicy* policy)
255{
256    if (!inputEvent || inputEvent->type != WebInputEvent::MouseUp)
257        return;
258
259    const WebMouseEvent* mouseEvent = static_cast<const WebMouseEvent*>(inputEvent);
260
261    unsigned short buttonNumber;
262    switch (mouseEvent->button) {
263    case WebMouseEvent::ButtonLeft:
264        buttonNumber = 0;
265        break;
266    case WebMouseEvent::ButtonMiddle:
267        buttonNumber = 1;
268        break;
269    case WebMouseEvent::ButtonRight:
270        buttonNumber = 2;
271        break;
272    default:
273        return;
274    }
275    bool ctrl = mouseEvent->modifiers & WebMouseEvent::ControlKey;
276    bool shift = mouseEvent->modifiers & WebMouseEvent::ShiftKey;
277    bool alt = mouseEvent->modifiers & WebMouseEvent::AltKey;
278    bool meta = mouseEvent->modifiers & WebMouseEvent::MetaKey;
279
280    NavigationPolicy userPolicy = *policy;
281    navigationPolicyFromMouseEvent(buttonNumber, ctrl, shift, alt, meta, &userPolicy);
282    // User and app agree that we want a new window; let the app override the decorations.
283    if (userPolicy == NavigationPolicyNewWindow && *policy == NavigationPolicyNewPopup)
284        return;
285    *policy = userPolicy;
286}
287
288WebNavigationPolicy ChromeClientImpl::getNavigationPolicy()
289{
290    // If our default configuration was modified by a script or wasn't
291    // created by a user gesture, then show as a popup. Else, let this
292    // new window be opened as a toplevel window.
293    bool asPopup = !m_toolbarsVisible
294        || !m_statusbarVisible
295        || !m_scrollbarsVisible
296        || !m_menubarVisible
297        || !m_resizable;
298
299    NavigationPolicy policy = NavigationPolicyNewForegroundTab;
300    if (asPopup)
301        policy = NavigationPolicyNewPopup;
302    updatePolicyForEvent(WebViewImpl::currentInputEvent(), &policy);
303
304    return static_cast<WebNavigationPolicy>(policy);
305}
306
307void ChromeClientImpl::show(NavigationPolicy navigationPolicy)
308{
309    if (!m_webView->client())
310        return;
311
312    WebNavigationPolicy policy = static_cast<WebNavigationPolicy>(navigationPolicy);
313    if (policy == WebNavigationPolicyIgnore)
314        policy = getNavigationPolicy();
315    m_webView->client()->show(policy);
316}
317
318bool ChromeClientImpl::canRunModal()
319{
320    return !!m_webView->client();
321}
322
323void ChromeClientImpl::runModal()
324{
325    if (m_webView->client())
326        m_webView->client()->runModal();
327}
328
329void ChromeClientImpl::setToolbarsVisible(bool value)
330{
331    m_toolbarsVisible = value;
332}
333
334bool ChromeClientImpl::toolbarsVisible()
335{
336    return m_toolbarsVisible;
337}
338
339void ChromeClientImpl::setStatusbarVisible(bool value)
340{
341    m_statusbarVisible = value;
342}
343
344bool ChromeClientImpl::statusbarVisible()
345{
346    return m_statusbarVisible;
347}
348
349void ChromeClientImpl::setScrollbarsVisible(bool value)
350{
351    m_scrollbarsVisible = value;
352    WebFrameImpl* webFrame = toWebFrameImpl(m_webView->mainFrame());
353    if (webFrame)
354        webFrame->setCanHaveScrollbars(value);
355}
356
357bool ChromeClientImpl::scrollbarsVisible()
358{
359    return m_scrollbarsVisible;
360}
361
362void ChromeClientImpl::setMenubarVisible(bool value)
363{
364    m_menubarVisible = value;
365}
366
367bool ChromeClientImpl::menubarVisible()
368{
369    return m_menubarVisible;
370}
371
372void ChromeClientImpl::setResizable(bool value)
373{
374    m_resizable = value;
375}
376
377bool ChromeClientImpl::shouldReportDetailedMessageForSource(const String& url)
378{
379    return m_webView->client() && m_webView->client()->shouldReportDetailedMessageForSource(url);
380}
381
382void ChromeClientImpl::addMessageToConsole(MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceID, const String& stackTrace)
383{
384    if (m_webView->client()) {
385        m_webView->client()->didAddMessageToConsole(
386            WebConsoleMessage(static_cast<WebConsoleMessage::Level>(level), message),
387            sourceID,
388            lineNumber,
389            stackTrace);
390    }
391}
392
393bool ChromeClientImpl::canRunBeforeUnloadConfirmPanel()
394{
395    return !!m_webView->client();
396}
397
398bool ChromeClientImpl::runBeforeUnloadConfirmPanel(const String& message, Frame* frame)
399{
400    if (m_webView->client()) {
401        return m_webView->client()->runModalBeforeUnloadDialog(
402            WebFrameImpl::fromFrame(frame), message);
403    }
404    return false;
405}
406
407void ChromeClientImpl::closeWindowSoon()
408{
409    // Make sure this Page can no longer be found by JS.
410    m_webView->page()->clearPageGroup();
411
412    // Make sure that all loading is stopped.  Ensures that JS stops executing!
413    m_webView->mainFrame()->stopLoading();
414
415    if (m_webView->client())
416        m_webView->client()->closeWidgetSoon();
417}
418
419// Although a Frame is passed in, we don't actually use it, since we
420// already know our own m_webView.
421void ChromeClientImpl::runJavaScriptAlert(Frame* frame, const String& message)
422{
423    if (m_webView->client()) {
424        if (WebUserGestureIndicator::isProcessingUserGesture())
425            WebUserGestureIndicator::currentUserGestureToken().setJavascriptPrompt();
426        m_webView->client()->runModalAlertDialog(
427            WebFrameImpl::fromFrame(frame), message);
428    }
429}
430
431// See comments for runJavaScriptAlert().
432bool ChromeClientImpl::runJavaScriptConfirm(Frame* frame, const String& message)
433{
434    if (m_webView->client()) {
435        if (WebUserGestureIndicator::isProcessingUserGesture())
436            WebUserGestureIndicator::currentUserGestureToken().setJavascriptPrompt();
437        return m_webView->client()->runModalConfirmDialog(
438            WebFrameImpl::fromFrame(frame), message);
439    }
440    return false;
441}
442
443// See comments for runJavaScriptAlert().
444bool ChromeClientImpl::runJavaScriptPrompt(Frame* frame,
445                                           const String& message,
446                                           const String& defaultValue,
447                                           String& result)
448{
449    if (m_webView->client()) {
450        if (WebUserGestureIndicator::isProcessingUserGesture())
451            WebUserGestureIndicator::currentUserGestureToken().setJavascriptPrompt();
452        WebString actualValue;
453        bool ok = m_webView->client()->runModalPromptDialog(
454            WebFrameImpl::fromFrame(frame),
455            message,
456            defaultValue,
457            &actualValue);
458        if (ok)
459            result = actualValue;
460        return ok;
461    }
462    return false;
463}
464
465void ChromeClientImpl::setStatusbarText(const String& message)
466{
467    if (m_webView->client())
468        m_webView->client()->setStatusText(message);
469}
470
471bool ChromeClientImpl::tabsToLinks()
472{
473    return m_webView->tabsToLinks();
474}
475
476IntRect ChromeClientImpl::windowResizerRect() const
477{
478    IntRect result;
479    if (m_webView->client())
480        result = m_webView->client()->windowResizerRect();
481    return result;
482}
483
484void ChromeClientImpl::invalidateContentsAndRootView(const IntRect& updateRect)
485{
486    if (updateRect.isEmpty())
487        return;
488    m_webView->invalidateRect(updateRect);
489}
490
491void ChromeClientImpl::invalidateContentsForSlowScroll(const IntRect& updateRect)
492{
493    invalidateContentsAndRootView(updateRect);
494}
495
496void ChromeClientImpl::scheduleAnimation()
497{
498    m_webView->scheduleAnimation();
499}
500
501bool ChromeClientImpl::isCompositorFramePending() const
502{
503    return m_webView->client()->isCompositorFramePending();
504}
505
506void ChromeClientImpl::scroll(
507    const IntSize& scrollDelta, const IntRect& scrollRect,
508    const IntRect& clipRect)
509{
510    if (!m_webView->isAcceleratedCompositingActive()) {
511        if (m_webView->client()) {
512            int dx = scrollDelta.width();
513            int dy = scrollDelta.height();
514            m_webView->client()->didScrollRect(dx, dy, intersection(scrollRect, clipRect));
515        }
516    } else
517        m_webView->scrollRootLayerRect(scrollDelta, clipRect);
518}
519
520IntPoint ChromeClientImpl::screenToRootView(const IntPoint& point) const
521{
522    IntPoint windowPoint(point);
523
524    if (m_webView->client()) {
525        WebRect windowRect = m_webView->client()->windowRect();
526        windowPoint.move(-windowRect.x, -windowRect.y);
527    }
528
529    return windowPoint;
530}
531
532IntRect ChromeClientImpl::rootViewToScreen(const IntRect& rect) const
533{
534    IntRect screenRect(rect);
535
536    if (m_webView->client()) {
537        WebRect windowRect = m_webView->client()->windowRect();
538        screenRect.move(windowRect.x, windowRect.y);
539    }
540
541    return screenRect;
542}
543
544WebScreenInfo ChromeClientImpl::screenInfo() const
545{
546    return m_webView->client() ? m_webView->client()->screenInfo() : WebScreenInfo();
547}
548
549void ChromeClientImpl::contentsSizeChanged(Frame* frame, const IntSize& size) const
550{
551    m_webView->didChangeContentsSize();
552
553    WebFrameImpl* webframe = WebFrameImpl::fromFrame(frame);
554    webframe->didChangeContentsSize(size);
555    if (webframe->client())
556        webframe->client()->didChangeContentsSize(webframe, size);
557}
558
559void ChromeClientImpl::deviceOrPageScaleFactorChanged() const
560{
561    m_webView->deviceOrPageScaleFactorChanged();
562}
563
564void ChromeClientImpl::layoutUpdated(Frame* frame) const
565{
566    m_webView->layoutUpdated(WebFrameImpl::fromFrame(frame));
567}
568
569void ChromeClientImpl::mouseDidMoveOverElement(
570    const HitTestResult& result, unsigned modifierFlags)
571{
572    if (!m_webView->client())
573        return;
574
575    WebURL url;
576    // Find out if the mouse is over a link, and if so, let our UI know...
577    if (result.isLiveLink() && !result.absoluteLinkURL().string().isEmpty())
578        url = result.absoluteLinkURL();
579    else if (result.innerNonSharedNode()
580             && (result.innerNonSharedNode()->hasTagName(HTMLNames::objectTag)
581                 || result.innerNonSharedNode()->hasTagName(HTMLNames::embedTag))) {
582        RenderObject* object = result.innerNonSharedNode()->renderer();
583        if (object && object->isWidget()) {
584            Widget* widget = toRenderWidget(object)->widget();
585            if (widget && widget->isPluginContainer()) {
586                WebPluginContainerImpl* plugin = toPluginContainerImpl(widget);
587                url = plugin->plugin()->linkAtPosition(result.roundedPointInInnerNodeFrame());
588            }
589        }
590    }
591
592    m_webView->client()->setMouseOverURL(url);
593}
594
595void ChromeClientImpl::setToolTip(const String& tooltipText, TextDirection dir)
596{
597    if (!m_webView->client())
598        return;
599    WebTextDirection textDirection = (dir == RTL) ?
600        WebTextDirectionRightToLeft :
601        WebTextDirectionLeftToRight;
602    m_webView->client()->setToolTipText(
603        tooltipText, textDirection);
604}
605
606void ChromeClientImpl::dispatchViewportPropertiesDidChange(const ViewportDescription& description) const
607{
608    m_webView->updatePageDefinedViewportConstraints(description);
609}
610
611void ChromeClientImpl::print(Frame* frame)
612{
613    if (m_webView->client())
614        m_webView->client()->printPage(WebFrameImpl::fromFrame(frame));
615}
616
617PassOwnPtr<ColorChooser> ChromeClientImpl::createColorChooser(ColorChooserClient* chooserClient, const Color&)
618{
619    OwnPtr<ColorChooserUIController> controller;
620    if (RuntimeEnabledFeatures::pagePopupEnabled())
621        controller = adoptPtr(new ColorChooserPopupUIController(this, chooserClient));
622    else
623        controller = adoptPtr(new ColorChooserUIController(this, chooserClient));
624    controller->openUI();
625    return controller.release();
626}
627
628PassRefPtr<DateTimeChooser> ChromeClientImpl::openDateTimeChooser(DateTimeChooserClient* pickerClient, const DateTimeChooserParameters& parameters)
629{
630#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
631    return DateTimeChooserImpl::create(this, pickerClient, parameters);
632#else
633    return ExternalDateTimeChooser::create(this, m_webView->client(), pickerClient, parameters);
634#endif
635}
636
637void ChromeClientImpl::runOpenPanel(Frame* frame, PassRefPtr<FileChooser> fileChooser)
638{
639    WebViewClient* client = m_webView->client();
640    if (!client)
641        return;
642
643    WebFileChooserParams params;
644    params.multiSelect = fileChooser->settings().allowsMultipleFiles;
645    params.directory = fileChooser->settings().allowsDirectoryUpload;
646    params.acceptTypes = fileChooser->settings().acceptTypes();
647    params.selectedFiles = fileChooser->settings().selectedFiles;
648    if (params.selectedFiles.size() > 0)
649        params.initialValue = params.selectedFiles[0];
650#if ENABLE(MEDIA_CAPTURE)
651    params.useMediaCapture = fileChooser->settings().useMediaCapture;
652#endif
653    WebFileChooserCompletionImpl* chooserCompletion =
654        new WebFileChooserCompletionImpl(fileChooser);
655
656    if (client->runFileChooser(params, chooserCompletion))
657        return;
658
659    // Choosing failed, so do callback with an empty list.
660    chooserCompletion->didChooseFile(WebVector<WebString>());
661}
662
663void ChromeClientImpl::enumerateChosenDirectory(FileChooser* fileChooser)
664{
665    WebViewClient* client = m_webView->client();
666    if (!client)
667        return;
668
669    WebFileChooserCompletionImpl* chooserCompletion =
670        new WebFileChooserCompletionImpl(fileChooser);
671
672    ASSERT(fileChooser && fileChooser->settings().selectedFiles.size());
673
674    // If the enumeration can't happen, call the callback with an empty list.
675    if (!client->enumerateChosenDirectory(fileChooser->settings().selectedFiles[0], chooserCompletion))
676        chooserCompletion->didChooseFile(WebVector<WebString>());
677}
678
679void ChromeClientImpl::popupOpened(PopupContainer* popupContainer,
680                                   const IntRect& bounds,
681                                   bool handleExternally)
682{
683    // For Autofill popups, if the popup will not be fully visible, we shouldn't
684    // show it at all. Among other things, this prevents users from being able
685    // to interact via the keyboard with an invisible popup.
686    if (popupContainer->popupType() == PopupContainer::Suggestion) {
687        FrameView* view = m_webView->page()->mainFrame()->view();
688        IntRect visibleRect = view->visibleContentRect(ScrollableArea::IncludeScrollbars);
689        // |bounds| is in screen coordinates, so make sure to convert it to
690        // content coordinates prior to comparing to |visibleRect|.
691        IntRect screenRect = bounds;
692        screenRect.setLocation(view->screenToContents(bounds.location()));
693        if (!visibleRect.contains(screenRect)) {
694            m_webView->hideAutofillPopup();
695            return;
696        }
697    }
698
699    if (!m_webView->client())
700        return;
701
702    WebWidget* webwidget;
703    if (handleExternally) {
704        WebPopupMenuInfo popupInfo;
705        getPopupMenuInfo(popupContainer, &popupInfo);
706        webwidget = m_webView->client()->createPopupMenu(popupInfo);
707    } else {
708        webwidget = m_webView->client()->createPopupMenu(
709            convertPopupType(popupContainer->popupType()));
710        // We only notify when the WebView has to handle the popup, as when
711        // the popup is handled externally, the fact that a popup is showing is
712        // transparent to the WebView.
713        m_webView->popupOpened(popupContainer);
714    }
715    toWebPopupMenuImpl(webwidget)->initialize(popupContainer, bounds);
716}
717
718void ChromeClientImpl::popupClosed(WebCore::PopupContainer* popupContainer)
719{
720    m_webView->popupClosed(popupContainer);
721}
722
723void ChromeClientImpl::setCursor(const WebCore::Cursor& cursor)
724{
725    setCursor(WebCursorInfo(cursor));
726}
727
728void ChromeClientImpl::setCursor(const WebCursorInfo& cursor)
729{
730#if OS(MACOSX)
731    // On Mac the mousemove event propagates to both the popup and main window.
732    // If a popup is open we don't want the main window to change the cursor.
733    if (m_webView->hasOpenedPopup())
734        return;
735#endif
736    if (m_webView->client())
737        m_webView->client()->didChangeCursor(cursor);
738}
739
740void ChromeClientImpl::setCursorForPlugin(const WebCursorInfo& cursor)
741{
742    setCursor(cursor);
743}
744
745void ChromeClientImpl::formStateDidChange(const Node* node)
746{
747    // The current history item is not updated yet.  That happens lazily when
748    // WebFrame::currentHistoryItem is requested.
749    WebFrameImpl* webframe = WebFrameImpl::fromFrame(node->document().frame());
750    if (webframe->client())
751        webframe->client()->didUpdateCurrentHistoryItem(webframe);
752}
753
754void ChromeClientImpl::getPopupMenuInfo(PopupContainer* popupContainer,
755                                        WebPopupMenuInfo* info)
756{
757    const Vector<PopupItem*>& inputItems = popupContainer->popupData();
758
759    WebVector<WebMenuItemInfo> outputItems(inputItems.size());
760
761    for (size_t i = 0; i < inputItems.size(); ++i) {
762        const PopupItem& inputItem = *inputItems[i];
763        WebMenuItemInfo& outputItem = outputItems[i];
764
765        outputItem.label = inputItem.label;
766        outputItem.enabled = inputItem.enabled;
767        if (inputItem.textDirection == WebCore::RTL)
768            outputItem.textDirection = WebTextDirectionRightToLeft;
769        else
770            outputItem.textDirection = WebTextDirectionLeftToRight;
771        outputItem.hasTextDirectionOverride = inputItem.hasTextDirectionOverride;
772
773        switch (inputItem.type) {
774        case PopupItem::TypeOption:
775            outputItem.type = WebMenuItemInfo::Option;
776            break;
777        case PopupItem::TypeGroup:
778            outputItem.type = WebMenuItemInfo::Group;
779            break;
780        case PopupItem::TypeSeparator:
781            outputItem.type = WebMenuItemInfo::Separator;
782            break;
783        default:
784            ASSERT_NOT_REACHED();
785        }
786    }
787
788    info->itemHeight = popupContainer->menuItemHeight();
789    info->itemFontSize = popupContainer->menuItemFontSize();
790    info->selectedIndex = popupContainer->selectedIndex();
791    info->items.swap(outputItems);
792    info->rightAligned = popupContainer->menuStyle().textDirection() == RTL;
793}
794
795void ChromeClientImpl::postAccessibilityNotification(AXObject* obj, AXObjectCache::AXNotification notification)
796{
797    // Alert assistive technology about the accessibility object notification.
798    if (!obj)
799        return;
800
801    m_webView->client()->postAccessibilityEvent(WebAXObject(obj), toWebAXEvent(notification));
802}
803
804String ChromeClientImpl::acceptLanguages()
805{
806    return m_webView->client()->acceptLanguages();
807}
808
809bool ChromeClientImpl::paintCustomOverhangArea(GraphicsContext* context, const IntRect& horizontalOverhangArea, const IntRect& verticalOverhangArea, const IntRect& dirtyRect)
810{
811    Frame* frame = m_webView->mainFrameImpl()->frame();
812    WebPluginContainerImpl* pluginContainer = WebFrameImpl::pluginContainerFromFrame(frame);
813    if (pluginContainer)
814        return pluginContainer->paintCustomOverhangArea(context, horizontalOverhangArea, verticalOverhangArea, dirtyRect);
815    return false;
816}
817
818GraphicsLayerFactory* ChromeClientImpl::graphicsLayerFactory() const
819{
820    return m_webView->graphicsLayerFactory();
821}
822
823void ChromeClientImpl::attachRootGraphicsLayer(Frame* frame, GraphicsLayer* graphicsLayer)
824{
825    m_webView->setRootGraphicsLayer(graphicsLayer);
826}
827
828void ChromeClientImpl::scheduleCompositingLayerFlush()
829{
830    m_webView->scheduleCompositingLayerSync();
831}
832
833ChromeClient::CompositingTriggerFlags ChromeClientImpl::allowedCompositingTriggers() const
834{
835    if (!m_webView->allowsAcceleratedCompositing())
836        return 0;
837
838    CompositingTriggerFlags flags = 0;
839    Settings& settings = m_webView->page()->settings();
840    if (settings.acceleratedCompositingFor3DTransformsEnabled())
841        flags |= ThreeDTransformTrigger;
842    if (settings.acceleratedCompositingForVideoEnabled())
843        flags |= VideoTrigger;
844    if (settings.acceleratedCompositingForPluginsEnabled())
845        flags |= PluginTrigger;
846    if (settings.acceleratedCompositingForAnimationEnabled())
847        flags |= AnimationTrigger;
848    if (settings.acceleratedCompositingForCanvasEnabled())
849        flags |= CanvasTrigger;
850    if (settings.acceleratedCompositingForScrollableFramesEnabled())
851        flags |= ScrollableInnerFrameTrigger;
852    if (settings.acceleratedCompositingForFiltersEnabled())
853        flags |= FilterTrigger;
854
855    return flags;
856}
857
858void ChromeClientImpl::enterFullScreenForElement(Element* element)
859{
860    m_webView->enterFullScreenForElement(element);
861}
862
863void ChromeClientImpl::exitFullScreenForElement(Element* element)
864{
865    m_webView->exitFullScreenForElement(element);
866}
867
868bool ChromeClientImpl::hasOpenedPopup() const
869{
870    return m_webView->hasOpenedPopup();
871}
872
873PassRefPtr<PopupMenu> ChromeClientImpl::createPopupMenu(Frame& frame, PopupMenuClient* client) const
874{
875    if (WebViewImpl::useExternalPopupMenus())
876        return adoptRef(new ExternalPopupMenu(frame, client, m_webView->client()));
877
878    return adoptRef(new PopupMenuChromium(frame, client));
879}
880
881PagePopup* ChromeClientImpl::openPagePopup(PagePopupClient* client, const IntRect& originBoundsInRootView)
882{
883    ASSERT(m_pagePopupDriver);
884    return m_pagePopupDriver->openPagePopup(client, originBoundsInRootView);
885}
886
887void ChromeClientImpl::closePagePopup(PagePopup* popup)
888{
889    ASSERT(m_pagePopupDriver);
890    m_pagePopupDriver->closePagePopup(popup);
891}
892
893void ChromeClientImpl::setPagePopupDriver(PagePopupDriver* driver)
894{
895    ASSERT(driver);
896    m_pagePopupDriver = driver;
897}
898
899void ChromeClientImpl::resetPagePopupDriver()
900{
901    m_pagePopupDriver = m_webView;
902}
903
904bool ChromeClientImpl::isPasswordGenerationEnabled() const
905{
906    return m_webView->passwordGeneratorClient();
907}
908
909void ChromeClientImpl::openPasswordGenerator(HTMLInputElement* input)
910{
911    ASSERT(isPasswordGenerationEnabled());
912    WebInputElement webInput(input);
913    m_webView->passwordGeneratorClient()->openPasswordGenerator(webInput);
914}
915
916bool ChromeClientImpl::shouldRunModalDialogDuringPageDismissal(const DialogType& dialogType, const String& dialogMessage, Document::PageDismissalType dismissalType) const
917{
918    const char* kDialogs[] = {"alert", "confirm", "prompt", "showModalDialog"};
919    int dialog = static_cast<int>(dialogType);
920    ASSERT_WITH_SECURITY_IMPLICATION(0 <= dialog && dialog < static_cast<int>(arraysize(kDialogs)));
921
922    const char* kDismissals[] = {"beforeunload", "pagehide", "unload"};
923    int dismissal = static_cast<int>(dismissalType) - 1; // Exclude NoDismissal.
924    ASSERT_WITH_SECURITY_IMPLICATION(0 <= dismissal && dismissal < static_cast<int>(arraysize(kDismissals)));
925
926    blink::Platform::current()->histogramEnumeration("Renderer.ModalDialogsDuringPageDismissal", dismissal * arraysize(kDialogs) + dialog, arraysize(kDialogs) * arraysize(kDismissals));
927
928    String message = String("Blocked ") + kDialogs[dialog] + "('" + dialogMessage + "') during " + kDismissals[dismissal] + ".";
929    m_webView->mainFrame()->addMessageToConsole(WebConsoleMessage(WebConsoleMessage::LevelError, message));
930
931    return false;
932}
933
934bool ChromeClientImpl::shouldRubberBandInDirection(WebCore::ScrollDirection direction) const
935{
936    ASSERT(direction != WebCore::ScrollUp && direction != WebCore::ScrollDown);
937
938    if (!m_webView->client())
939        return false;
940
941    if (direction == WebCore::ScrollLeft)
942        return !m_webView->client()->historyBackListCount();
943    if (direction == WebCore::ScrollRight)
944        return !m_webView->client()->historyForwardListCount();
945
946    ASSERT_NOT_REACHED();
947    return true;
948}
949
950void ChromeClientImpl::numWheelEventHandlersChanged(unsigned numberOfWheelHandlers)
951{
952    m_webView->numberOfWheelEventHandlersChanged(numberOfWheelHandlers);
953}
954
955void ChromeClientImpl::needTouchEvents(bool needsTouchEvents)
956{
957    m_webView->hasTouchEventHandlers(needsTouchEvents);
958}
959
960void ChromeClientImpl::setTouchAction(TouchAction touchAction)
961{
962    if (WebViewClient* client = m_webView->client()) {
963        WebTouchAction webTouchAction = static_cast<WebTouchAction>(touchAction);
964        client->setTouchAction(webTouchAction);
965    }
966}
967
968bool ChromeClientImpl::requestPointerLock()
969{
970    return m_webView->requestPointerLock();
971}
972
973void ChromeClientImpl::requestPointerUnlock()
974{
975    return m_webView->requestPointerUnlock();
976}
977
978bool ChromeClientImpl::isPointerLocked()
979{
980    return m_webView->isPointerLocked();
981}
982
983void ChromeClientImpl::annotatedRegionsChanged()
984{
985    WebViewClient* client = m_webView->client();
986    if (client)
987        client->draggableRegionsChanged();
988}
989
990void ChromeClientImpl::didAssociateFormControls(const Vector<RefPtr<Element> >& elements)
991{
992    if (!m_webView->autofillClient())
993        return;
994    WebVector<WebNode> elementVector(static_cast<size_t>(elements.size()));
995    size_t elementsCount = elements.size();
996    for (size_t i = 0; i < elementsCount; ++i)
997        elementVector[i] = elements[i];
998    m_webView->autofillClient()->didAssociateFormControls(elementVector);
999}
1000
1001void ChromeClientImpl::didCancelCompositionOnSelectionChange()
1002{
1003    if (m_webView->client())
1004        m_webView->client()->didCancelCompositionOnSelectionChange();
1005}
1006
1007void ChromeClientImpl::willSetInputMethodState()
1008{
1009    if (m_webView->client())
1010        m_webView->client()->resetInputMethod();
1011}
1012
1013void ChromeClientImpl::handleKeyboardEventOnTextField(HTMLInputElement& inputElement, KeyboardEvent& event)
1014{
1015    if (!m_webView->autofillClient())
1016        return;
1017    m_webView->autofillClient()->textFieldDidReceiveKeyDown(WebInputElement(&inputElement), WebKeyboardEventBuilder(event));
1018}
1019
1020void ChromeClientImpl::didChangeValueInTextField(HTMLInputElement& inputElement)
1021{
1022    if (!m_webView->autofillClient())
1023        return;
1024    m_webView->autofillClient()->textFieldDidChange(WebInputElement(&inputElement));
1025}
1026
1027void ChromeClientImpl::didEndEditingOnTextField(HTMLInputElement& inputElement)
1028{
1029    if (m_webView->autofillClient())
1030        m_webView->autofillClient()->textFieldDidEndEditing(WebInputElement(&inputElement));
1031
1032    // Notification that focus was lost. Be careful with this, it's also sent
1033    // when the page is being closed.
1034
1035    // Hide any showing popup.
1036    m_webView->hideAutofillPopup();
1037}
1038
1039void ChromeClientImpl::openTextDataListChooser(HTMLInputElement& input)
1040{
1041    if (m_webView->autofillClient())
1042        m_webView->autofillClient()->openTextDataListChooser(WebInputElement(&input));
1043}
1044
1045#if ENABLE(NAVIGATOR_CONTENT_UTILS)
1046PassOwnPtr<NavigatorContentUtilsClientImpl> NavigatorContentUtilsClientImpl::create(WebViewImpl* webView)
1047{
1048    return adoptPtr(new NavigatorContentUtilsClientImpl(webView));
1049}
1050
1051NavigatorContentUtilsClientImpl::NavigatorContentUtilsClientImpl(WebViewImpl* webView)
1052    : m_webView(webView)
1053{
1054}
1055
1056void NavigatorContentUtilsClientImpl::registerProtocolHandler(const String& scheme, const String& baseURL, const String& url, const String& title)
1057{
1058    m_webView->client()->registerProtocolHandler(scheme, baseURL, url, title);
1059}
1060#endif
1061
1062} // namespace blink
1063