ChromeClientQt.cpp   [plain text]


/*
 * Copyright (C) 2006 Zack Rusin <zack@kde.org>
 * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "ChromeClientQt.h"

#include "ApplicationCacheStorage.h"
#include "ColorChooser.h"
#include "ColorChooserClient.h"
#include "DatabaseManager.h"
#include "Document.h"
#include "FileChooser.h"
#include "FileIconLoader.h"
#include "Frame.h"
#include "FrameLoadRequest.h"
#include "FrameLoader.h"
#include "FrameLoaderClientQt.h"
#include "FrameView.h"
#include "Geolocation.h"
#if USE(ACCELERATED_COMPOSITING)
#include "GraphicsLayer.h"
#endif
#include "HTMLFormElement.h"
#include "HitTestResult.h"
#include "Icon.h"
#include "NavigationAction.h"
#include "NetworkingContext.h"
#include "NotImplemented.h"
#include "Page.h"
#include "PopupMenuQt.h"
#include "QWebFrameAdapter.h"
#include "QWebPageAdapter.h"
#include "QWebPageClient.h"
#include "ScrollbarTheme.h"
#include "SearchPopupMenuQt.h"
#include "SecurityOrigin.h"
#include "TextureMapperLayerClientQt.h"
#include "TiledBackingStore.h"
#include "ViewportArguments.h"
#include "WindowFeatures.h"
#include "qwebkitplatformplugin.h"
#include "qwebsecurityorigin.h"
#include "qwebsecurityorigin_p.h"
#include "qwebsettings.h"

#include <qabstractanimation.h>
#include <qdebug.h>
#include <qeventloop.h>
#include <qwindow.h>
#include <wtf/CurrentTime.h>
#include <wtf/OwnPtr.h>

#if ENABLE(VIDEO) && ((USE(GSTREAMER) && USE(NATIVE_FULLSCREEN_VIDEO)) || USE(QT_MULTIMEDIA))
#include "FullScreenVideoQt.h"
#include "HTMLMediaElement.h"
#include "HTMLNames.h"
#include "HTMLVideoElement.h"
#if USE(QT_MULTIMEDIA)
#include "MediaPlayerPrivateQt.h"
#endif
#endif

namespace WebCore {

#if ENABLE(REQUEST_ANIMATION_FRAME) && !USE(REQUEST_ANIMATION_FRAME_TIMER)
class RefreshAnimation : public QAbstractAnimation {
public:
    RefreshAnimation(ChromeClientQt* chromeClient)
        : QAbstractAnimation()
        , m_chromeClient(chromeClient)
        , m_animationScheduled(false)
    { }

    virtual int duration() const { return -1; }

    void scheduleAnimation()
    {
        m_animationScheduled = true;
        if (state() != QAbstractAnimation::Running)
            QMetaObject::invokeMethod(this, "start", Qt::QueuedConnection);
    }

protected:
    virtual void updateCurrentTime(int currentTime)
    {
        UNUSED_PARAM(currentTime);
        if (m_animationScheduled) {
            m_animationScheduled = false;
            m_chromeClient->serviceScriptedAnimations();
        } else
            stop();
    }
private:
    ChromeClientQt* m_chromeClient;
    bool m_animationScheduled;
};
#endif

bool ChromeClientQt::dumpVisitedLinksCallbacks = false;

ChromeClientQt::ChromeClientQt(QWebPageAdapter* webPageAdapter)
    : m_webPage(webPageAdapter)
    , m_eventLoop(0)
#if ENABLE(VIDEO) && ((USE(GSTREAMER) && USE(NATIVE_FULLSCREEN_VIDEO)) || USE(QT_MULTIMEDIA))
    , m_fullScreenVideo(0)
#endif
{
    toolBarsVisible = statusBarVisible = menuBarVisible = true;
}

ChromeClientQt::~ChromeClientQt()
{
    if (m_eventLoop)
        m_eventLoop->exit();

#if ENABLE(VIDEO) && ((USE(GSTREAMER) && USE(NATIVE_FULLSCREEN_VIDEO)) || USE(QT_MULTIMEDIA))
    delete m_fullScreenVideo;
#endif
}

void ChromeClientQt::setWindowRect(const FloatRect& rect)
{
    if (!m_webPage)
        return;
    m_webPage->setWindowRect(QRect(qRound(rect.x()), qRound(rect.y()), qRound(rect.width()), qRound(rect.height())));
}

/*!
    windowRect represents the rect of the Window, including all interface elements
    like toolbars/scrollbars etc. It is used by the viewport meta tag as well as
    by the DOM Window object: outerHeight(), outerWidth(), screenX(), screenY().
*/
FloatRect ChromeClientQt::windowRect()
{
    if (!platformPageClient())
        return FloatRect();
    return platformPageClient()->windowRect();
}

bool ChromeClientQt::allowsAcceleratedCompositing() const
{
    if (!platformPageClient())
        return false;
#if USE(ACCELERATED_COMPOSITING)
    return true;
#else
    return false;
#endif
}

FloatRect ChromeClientQt::pageRect()
{
    if (!m_webPage)
        return FloatRect();
    return FloatRect(QRectF(QPointF(0, 0), m_webPage->viewportSize()));
}

void ChromeClientQt::focus()
{
    if (!m_webPage)
        return;
    m_webPage->setFocus();
}


void ChromeClientQt::unfocus()
{
    if (!m_webPage)
        return;
    m_webPage->unfocus();
}

bool ChromeClientQt::canTakeFocus(FocusDirection)
{
    // This is called when cycling through links/focusable objects and we
    // reach the last focusable object. Then we want to claim that we can
    // take the focus to avoid wrapping.
    return true;
}

void ChromeClientQt::takeFocus(FocusDirection)
{
    // don't do anything. This is only called when cycling to links/focusable objects,
    // which in turn is called from focusNextPrevChild. We let focusNextPrevChild
    // call QWidget::focusNextPrevChild accordingly, so there is no need to do anything
    // here.
}


void ChromeClientQt::focusedNodeChanged(Node*)
{
}

void ChromeClientQt::focusedFrameChanged(Frame*)
{
}

Page* ChromeClientQt::createWindow(Frame*, const FrameLoadRequest& request, const WindowFeatures& features, const NavigationAction&)
{
    QWebPageAdapter* newPage = m_webPage->createWindow(features.dialog);
    if (!newPage)
        return 0;

    return newPage->page;
}

void ChromeClientQt::show()
{
    if (!m_webPage)
        return;
    m_webPage->show();
}


bool ChromeClientQt::canRunModal()
{
    return true;
}


void ChromeClientQt::runModal()
{
    m_eventLoop = new QEventLoop();
    QEventLoop* eventLoop = m_eventLoop;
    m_eventLoop->exec();
    delete eventLoop;
}


void ChromeClientQt::setToolbarsVisible(bool visible)
{
    toolBarsVisible = visible;
    QMetaObject::invokeMethod(m_webPage->handle(), "toolBarVisibilityChangeRequested", Q_ARG(bool, visible));
}


bool ChromeClientQt::toolbarsVisible()
{
    return toolBarsVisible;
}


void ChromeClientQt::setStatusbarVisible(bool visible)
{
    QMetaObject::invokeMethod(m_webPage->handle(), "statusBarVisibilityChangeRequested", Q_ARG(bool, visible));
    statusBarVisible = visible;
}


bool ChromeClientQt::statusbarVisible()
{
    return statusBarVisible;
}


void ChromeClientQt::setScrollbarsVisible(bool)
{
    notImplemented();
}


bool ChromeClientQt::scrollbarsVisible()
{
    notImplemented();
    return true;
}


void ChromeClientQt::setMenubarVisible(bool visible)
{
    menuBarVisible = visible;
    QMetaObject::invokeMethod(m_webPage->handle(), "menuBarVisibilityChangeRequested", Q_ARG(bool, visible));
}

bool ChromeClientQt::menubarVisible()
{
    return menuBarVisible;
}

void ChromeClientQt::setResizable(bool)
{
    notImplemented();
}

void ChromeClientQt::addMessageToConsole(MessageSource, MessageLevel, const String& message, unsigned lineNumber, unsigned columnNumber, const String& sourceID)
{
    QString x = message;
    QString y = sourceID;
    UNUSED_PARAM(columnNumber);
    m_webPage->javaScriptConsoleMessage(x, lineNumber, y);
}

void ChromeClientQt::chromeDestroyed()
{
    delete this;
}

bool ChromeClientQt::canRunBeforeUnloadConfirmPanel()
{
    return true;
}

bool ChromeClientQt::runBeforeUnloadConfirmPanel(const String& message, Frame* frame)
{
    return runJavaScriptConfirm(frame, message);
}

void ChromeClientQt::closeWindowSoon()
{
    m_webPage->page->setGroupName(String());
    m_webPage->page->mainFrame()->loader()->stopAllLoaders();
    QMetaObject::invokeMethod(m_webPage->handle(), "windowCloseRequested");
}

void ChromeClientQt::runJavaScriptAlert(Frame* f, const String& msg)
{
    m_webPage->javaScriptAlert(QWebFrameAdapter::kit(f), msg);
}

bool ChromeClientQt::runJavaScriptConfirm(Frame* f, const String& msg)
{
    return m_webPage->javaScriptConfirm(QWebFrameAdapter::kit(f), msg);
}

bool ChromeClientQt::runJavaScriptPrompt(Frame* f, const String& message, const String& defaultValue, String& result)
{
    QString x = result;
    QWebFrameAdapter* webFrame = QWebFrameAdapter::kit(f);
    bool rc = m_webPage->javaScriptPrompt(webFrame, message, defaultValue, &x);

    // Fix up a quirk in the QInputDialog class. If no input happened the string should be empty
    // but it is null. See https://bugs.webkit.org/show_bug.cgi?id=30914.
    if (rc && x.isNull())
        result = String("");
    else
        result = x;

    return rc;
}

void ChromeClientQt::setStatusbarText(const String& msg)
{
    QString x = msg;
    QMetaObject::invokeMethod(m_webPage->handle(), "statusBarMessage", Q_ARG(QString, x));
}

bool ChromeClientQt::shouldInterruptJavaScript()
{
    return m_webPage->shouldInterruptJavaScript();
}

KeyboardUIMode ChromeClientQt::keyboardUIMode()
{
    return m_webPage->settings->testAttribute(QWebSettings::LinksIncludedInFocusChain)
        ? KeyboardAccessTabsToLinks : KeyboardAccessDefault;
}

IntRect ChromeClientQt::windowResizerRect() const
{
#if defined(Q_WS_MAC)
    if (!m_webPage)
        return IntRect();

    QWebPageClient* pageClient = platformPageClient();
    if (!pageClient)
        return IntRect();

    QWindow* topLevelWidget = pageClient->ownerWindow();
    if (!topLevelWidget)
        return IntRect();

    QRect topLevelGeometry(topLevelWidget->geometry());

    // There's no API in Qt to query for the size of the resizer, so we assume
    // it has the same width and height as the scrollbar thickness.
    int scollbarThickness = ScrollbarTheme::theme()->scrollbarThickness();

    // There's no API in Qt to query for the position of the resizer. Sometimes
    // it's drawn by the system, and sometimes it's a QSizeGrip. For RTL locales
    // it might even be on the lower left side of the window, but in WebKit we
    // always draw scrollbars on the right hand side, so we assume this to be the
    // location when computing the resize rect to reserve for WebKit.
    QPoint resizeCornerTopLeft = QPoint(topLevelGeometry.width(), topLevelGeometry.height())
        - QPoint(scollbarThickness, scollbarThickness))
        - m_webPage->viewRectRelativeToWindow().topLeft();

    QRect resizeCornerRect = QRect(resizeCornerTopLeft, QSize(scollbarThickness, scollbarThickness));
    return resizeCornerRect.intersected(pageClient->geometryRelativeToOwnerWidget());

#else
    return IntRect();
#endif
}

void ChromeClientQt::invalidateRootView(const IntRect& windowRect, bool)
{
#if USE(TILED_BACKING_STORE)
    if (platformPageClient()) {
        WebCore::TiledBackingStore* backingStore = m_webPage->mainFrameAdapter()->frame->tiledBackingStore();
        if (!backingStore)
            return;
        backingStore->invalidate(windowRect);
    }
#else
    Q_UNUSED(windowRect);
#endif
}

void ChromeClientQt::invalidateContentsAndRootView(const IntRect& windowRect, bool)
{
    // No double buffer, so only update the QWidget if content changed.
    if (platformPageClient()) {
        QRect rect(windowRect);
        rect = rect.intersected(QRect(QPoint(0, 0), m_webPage->viewportSize()));
        if (!rect.isEmpty())
            platformPageClient()->update(rect);
    }
    QMetaObject::invokeMethod(m_webPage->handle(), "repaintRequested", Qt::QueuedConnection, Q_ARG(QRect, windowRect));

    // FIXME: There is no "immediate" support for window painting. This should be done always whenever the flag
    // is set.
}

void ChromeClientQt::invalidateContentsForSlowScroll(const IntRect& windowRect, bool immediate)
{
    invalidateContentsAndRootView(windowRect, immediate);
}

void ChromeClientQt::scroll(const IntSize& delta, const IntRect& scrollViewRect, const IntRect&)
{
    if (platformPageClient())
        platformPageClient()->scroll(delta.width(), delta.height(), scrollViewRect);
    QMetaObject::invokeMethod(m_webPage->handle(), "scrollRequested", Q_ARG(int, delta.width()), Q_ARG(int, delta.height()), Q_ARG(QRect, scrollViewRect));
}

#if USE(TILED_BACKING_STORE)
void ChromeClientQt::delegatedScrollRequested(const IntPoint& point)
{

    const QPoint ofs = m_webPage->mainFrameAdapter()->scrollPosition();
    IntSize currentPosition(ofs.x(), ofs.y());
    int x = point.x() - currentPosition.width();
    int y = point.y() - currentPosition.height();
    const QRect rect(QPoint(0, 0), m_webPage->viewportSize());
    QMetaObject::invokeMethod(m_webPage->handle(), "scrollRequested", Q_ARG(int, x), Q_ARG(int, y), Q_ARG(QRect, rect));
}
#endif

IntRect ChromeClientQt::rootViewToScreen(const IntRect& rect) const
{
    QWebPageClient* pageClient = platformPageClient();
    if (!pageClient)
        return rect;

    QWindow* ownerWindow = pageClient->ownerWindow();
    if (!ownerWindow)
        return rect;

    QRect screenRect(rect);
    screenRect.translate(ownerWindow->mapToGlobal(m_webPage->viewRectRelativeToWindow().topLeft()));

    return screenRect;
}

IntPoint ChromeClientQt::screenToRootView(const IntPoint& point) const
{
    QWebPageClient* pageClient = platformPageClient();
    if (!pageClient)
        return point;

    QWindow* ownerWindow = pageClient->ownerWindow();
    if (!ownerWindow)
        return point;

    return ownerWindow->mapFromGlobal(point) - m_webPage->viewRectRelativeToWindow().topLeft();
}

PlatformPageClient ChromeClientQt::platformPageClient() const
{
    return m_webPage->client.data();
}

void ChromeClientQt::contentsSizeChanged(Frame* frame, const IntSize& size) const
{
    if (frame->loader()->networkingContext())
        QWebFrameAdapter::kit(frame)->contentsSizeDidChange(size);
}

void ChromeClientQt::mouseDidMoveOverElement(const HitTestResult& result, unsigned)
{
    TextDirection dir;
    if (result.absoluteLinkURL() != lastHoverURL
        || result.title(dir) != lastHoverTitle
        || result.textContent() != lastHoverContent) {
        lastHoverURL = result.absoluteLinkURL();
        lastHoverTitle = result.title(dir);
        lastHoverContent = result.textContent();
        QMetaObject::invokeMethod(m_webPage->handle(), "linkHovered", Q_ARG(QString, lastHoverURL.string()),
            Q_ARG(QString, lastHoverTitle), Q_ARG(QString, lastHoverContent));
    }
}

void ChromeClientQt::setToolTip(const String &tip, TextDirection)
{
    m_webPage->setToolTip(tip);
}

void ChromeClientQt::print(Frame* frame)
{
    emit m_webPage->printRequested(QWebFrameAdapter::kit(frame));
}

#if ENABLE(SQL_DATABASE)
void ChromeClientQt::exceededDatabaseQuota(Frame* frame, const String& databaseName, DatabaseDetails)
{
    quint64 quota = QWebSettings::offlineStorageDefaultQuota();

    if (!DatabaseManager::manager().hasEntryForOrigin(frame->document()->securityOrigin()))
        DatabaseManager::manager().setQuota(frame->document()->securityOrigin(), quota);

    m_webPage->databaseQuotaExceeded(QWebFrameAdapter::kit(frame), databaseName);
}
#endif

void ChromeClientQt::reachedMaxAppCacheSize(int64_t)
{
    // FIXME: Free some space.
    notImplemented();
}

void ChromeClientQt::reachedApplicationCacheOriginQuota(SecurityOrigin* origin, int64_t totalSpaceNeeded)
{
    int64_t quota;
    quint64 defaultOriginQuota = WebCore::cacheStorage().defaultOriginQuota();

    QWebSecurityOriginPrivate* priv = new QWebSecurityOriginPrivate(origin);
    QWebSecurityOrigin* securityOrigin = new QWebSecurityOrigin(priv);

    if (!WebCore::cacheStorage().calculateQuotaForOrigin(origin, quota))
        WebCore::cacheStorage().storeUpdatedQuotaForOrigin(origin, defaultOriginQuota);

    m_webPage->applicationCacheQuotaExceeded(securityOrigin, defaultOriginQuota, static_cast<quint64>(totalSpaceNeeded));
}

#if ENABLE(INPUT_TYPE_COLOR)
PassOwnPtr<ColorChooser> ChromeClientQt::createColorChooser(ColorChooserClient* client, const Color& color)
{
    const QColor selectedColor = m_webPage->colorSelectionRequested(QColor(color));
    client->didChooseColor(selectedColor);
    client->didEndChooser();
    return nullptr;
}
#endif

void ChromeClientQt::runOpenPanel(Frame* frame, PassRefPtr<FileChooser> prpFileChooser)
{
    RefPtr<FileChooser> fileChooser = prpFileChooser;

    QStringList suggestedFileNames;
    for (unsigned i = 0; i < fileChooser->settings().selectedFiles.size(); ++i)
        suggestedFileNames += fileChooser->settings().selectedFiles[i];

    const bool allowMultiple = fileChooser->settings().allowsMultipleFiles;

    QStringList result = m_webPage->chooseFiles(QWebFrameAdapter::kit(frame), allowMultiple, suggestedFileNames);
    if (!result.isEmpty()) {
        if (allowMultiple) {
            Vector<String> names;
            for (int i = 0; i < result.count(); ++i)
                names.append(result.at(i));
            fileChooser->chooseFiles(names);
        } else
            fileChooser->chooseFile(result.first());
    }
}

void ChromeClientQt::loadIconForFiles(const Vector<String>& filenames, FileIconLoader* loader)
{
    loader->notifyFinished(Icon::createIconForFiles(filenames));
}

void ChromeClientQt::setCursor(const Cursor& cursor)
{
#ifndef QT_NO_CURSOR
    QWebPageClient* pageClient = platformPageClient();
    if (!pageClient)
        return;
    pageClient->setCursor(*cursor.platformCursor());
#else
    UNUSED_PARAM(cursor);
#endif
}

#if ENABLE(REQUEST_ANIMATION_FRAME) && !USE(REQUEST_ANIMATION_FRAME_TIMER)
void ChromeClientQt::scheduleAnimation()
{
    if (!m_refreshAnimation)
        m_refreshAnimation = adoptPtr(new RefreshAnimation(this));
    m_refreshAnimation->scheduleAnimation();
}

void ChromeClientQt::serviceScriptedAnimations()
{
    m_webPage->mainFrameAdapter()->frame->view()->serviceScriptedAnimations(currentTime());
}
#endif

#if USE(ACCELERATED_COMPOSITING)
void ChromeClientQt::attachRootGraphicsLayer(Frame* frame, GraphicsLayer* graphicsLayer)
{
    if (!m_textureMapperLayerClient)
        m_textureMapperLayerClient = adoptPtr(new TextureMapperLayerClientQt(m_webPage->mainFrameAdapter()));
    m_textureMapperLayerClient->setRootGraphicsLayer(graphicsLayer);
}

void ChromeClientQt::setNeedsOneShotDrawingSynchronization()
{
    // we want the layers to synchronize next time we update the screen anyway
    if (m_textureMapperLayerClient)
        m_textureMapperLayerClient->markForSync(false);
}

void ChromeClientQt::scheduleCompositingLayerFlush()
{
    // we want the layers to synchronize ASAP
    if (m_textureMapperLayerClient)
        m_textureMapperLayerClient->markForSync(true);
}

ChromeClient::CompositingTriggerFlags ChromeClientQt::allowedCompositingTriggers() const
{
    if (allowsAcceleratedCompositing())
        return ThreeDTransformTrigger | CanvasTrigger | AnimationTrigger | AnimatedOpacityTrigger;

    return 0;
}

#endif

#if USE(TILED_BACKING_STORE)
IntRect ChromeClientQt::visibleRectForTiledBackingStore() const
{
    if (!platformPageClient() || !m_webPage)
        return IntRect();

    if (!platformPageClient()->viewResizesToContentsEnabled()) {
        const QPoint ofs = m_webPage->mainFrameAdapter()->scrollPosition();
        IntSize offset(ofs.x(), ofs.y());
        return QRect(QPoint(offset.width(), offset.height()), m_webPage->mainFrameAdapter()->frameRect().size());
    }

    return enclosingIntRect(FloatRect(platformPageClient()->graphicsItemVisibleRect()));
}
#endif

#if ENABLE(VIDEO) && ((USE(GSTREAMER) && USE(NATIVE_FULLSCREEN_VIDEO)) || USE(QT_MULTIMEDIA))
FullScreenVideoQt* ChromeClientQt::fullScreenVideo()
{
    if (!m_fullScreenVideo)
        m_fullScreenVideo = new FullScreenVideoQt(this);
    return m_fullScreenVideo;
}

bool ChromeClientQt::supportsFullscreenForNode(const Node* node)
{
    ASSERT(node);
    return node->hasTagName(HTMLNames::videoTag) && fullScreenVideo()->isValid();
}

bool ChromeClientQt::requiresFullscreenForVideoPlayback()
{
    return fullScreenVideo()->requiresFullScreenForVideoPlayback();
}

void ChromeClientQt::enterFullscreenForNode(Node* node)
{
    ASSERT(node && node->hasTagName(HTMLNames::videoTag));

    fullScreenVideo()->enterFullScreenForNode(node);
}

void ChromeClientQt::exitFullscreenForNode(Node* node)
{
    ASSERT(node && node->hasTagName(HTMLNames::videoTag));

    fullScreenVideo()->exitFullScreenForNode(node);
} 
#endif

PassOwnPtr<QWebSelectMethod> ChromeClientQt::createSelectPopup() const
{
    OwnPtr<QWebSelectMethod> result = m_platformPlugin.createSelectInputMethod();
    if (result)
        return result.release();

#if !defined(QT_NO_COMBOBOX)
    return adoptPtr(m_webPage->createSelectPopup());
#else
    return nullptr;
#endif
}

void ChromeClientQt::dispatchViewportPropertiesDidChange(const ViewportArguments&) const
{
    m_webPage->emitViewportChangeRequested();
}

#if USE(QT_MULTIMEDIA)
QWebFullScreenVideoHandler* ChromeClientQt::createFullScreenVideoHandler()
{
    QWebFullScreenVideoHandler* handler = m_platformPlugin.createFullScreenVideoHandler().leakPtr();
    if (!handler)
        handler = m_webPage->createFullScreenVideoHandler();
    return handler;
}
#endif

bool ChromeClientQt::selectItemWritingDirectionIsNatural()
{
    return false;
}

bool ChromeClientQt::selectItemAlignmentFollowsMenuWritingDirection()
{
    return false;
}

bool ChromeClientQt::hasOpenedPopup() const
{
    notImplemented();
    return false;
}

PassRefPtr<PopupMenu> ChromeClientQt::createPopupMenu(PopupMenuClient* client) const
{
    return adoptRef(new PopupMenuQt(client, this));
}

PassRefPtr<SearchPopupMenu> ChromeClientQt::createSearchPopupMenu(PopupMenuClient* client) const
{
    return adoptRef(new SearchPopupMenuQt(createPopupMenu(client)));
}

void ChromeClientQt::populateVisitedLinks()
{
    // We don't need to do anything here because history is tied to QWebPage rather than stored
    // in a separate database
    if (dumpVisitedLinksCallbacks) {
        printf("Asked to populate visited links for WebView \"%s\"\n",
            qPrintable(QUrl(m_webPage->mainFrameAdapter()->url).toString()));
    }
}

} // namespace WebCore