diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-05-07 11:21:11 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-05-07 11:21:11 +0200 |
commit | 2cf6c8816a73e0132bd8fa3b509d62d7c51b6e47 (patch) | |
tree | 988e8c5b116dd0466244ae2fe5af8ee9be926d76 /Source/WebKit2/UIProcess/API/qt/qquickwebview.cpp | |
parent | dd91e772430dc294e3bf478c119ef8d43c0a3358 (diff) | |
download | qtwebkit-2cf6c8816a73e0132bd8fa3b509d62d7c51b6e47.tar.gz |
Imported WebKit commit 7e538425aa020340619e927792f3d895061fb54b (http://svn.webkit.org/repository/webkit/trunk@116286)
Diffstat (limited to 'Source/WebKit2/UIProcess/API/qt/qquickwebview.cpp')
-rw-r--r-- | Source/WebKit2/UIProcess/API/qt/qquickwebview.cpp | 828 |
1 files changed, 547 insertions, 281 deletions
diff --git a/Source/WebKit2/UIProcess/API/qt/qquickwebview.cpp b/Source/WebKit2/UIProcess/API/qt/qquickwebview.cpp index 6e518ff12..b1b465526 100644 --- a/Source/WebKit2/UIProcess/API/qt/qquickwebview.cpp +++ b/Source/WebKit2/UIProcess/API/qt/qquickwebview.cpp @@ -23,12 +23,19 @@ #include "DownloadProxy.h" #include "DrawingAreaProxyImpl.h" +#include "QtDialogRunner.h" #include "QtDownloadManager.h" +#include "QtViewportInteractionEngine.h" #include "QtWebContext.h" #include "QtWebIconDatabaseClient.h" #include "QtWebPageEventHandler.h" +#include "QtWebPageLoadClient.h" +#include "QtWebPagePolicyClient.h" #include "UtilsQt.h" #include "WebBackForwardList.h" +#if ENABLE(FULLSCREEN_API) +#include "WebFullScreenManagerProxy.h" +#endif #include "WebPageGroup.h" #include "WebPreferences.h" @@ -45,18 +52,21 @@ #include "qwebviewportinfo_p.h" #include <JavaScriptCore/InitializeThreading.h> -#include <QDeclarativeEngine> -#include <QFileDialog> -#include <QtQuick/QQuickCanvas> +#include <QDateTime> #include <WebCore/IntPoint.h> #include <WebCore/IntRect.h> #include <WKOpenPanelResultListener.h> #include <wtf/Assertions.h> +#include <wtf/MainThread.h> #include <wtf/text/WTFString.h> using namespace WebCore; +using namespace WebKit; static bool s_flickableViewportEnabled = true; +static const int kAxisLockSampleCount = 5; +static const qreal kAxisLockVelocityThreshold = 300; +static const qreal kAxisLockVelocityDirectionThreshold = 50; static QQuickWebViewPrivate* createPrivateObject(QQuickWebView* publicObject) { @@ -65,9 +75,80 @@ static QQuickWebViewPrivate* createPrivateObject(QQuickWebView* publicObject) return new QQuickWebViewLegacyPrivate(publicObject); } +QQuickWebViewPrivate::FlickableAxisLocker::FlickableAxisLocker() + : m_allowedDirection(QQuickFlickable::AutoFlickDirection) + , m_sampleCount(0) +{ +} + +QVector2D QQuickWebViewPrivate::FlickableAxisLocker::touchVelocity(const QTouchEvent* event) +{ + static bool touchVelocityAvailable = event->device()->capabilities().testFlag(QTouchDevice::Velocity); + const QTouchEvent::TouchPoint& touchPoint = event->touchPoints().first(); + + if (touchVelocityAvailable) + return touchPoint.velocity(); + + const QLineF movementLine(touchPoint.screenPos(), m_initialScreenPosition); + const qint64 elapsed = m_time.elapsed(); + + if (!elapsed) + return QVector2D(0, 0); + + // Calculate an approximate velocity vector in the unit of pixel / second. + return QVector2D(1000 * movementLine.dx() / elapsed, 1000 * movementLine.dy() / elapsed); +} + +void QQuickWebViewPrivate::FlickableAxisLocker::update(const QTouchEvent* event) +{ + ASSERT(event->touchPoints().size() == 1); + const QTouchEvent::TouchPoint& touchPoint = event->touchPoints().first(); + + ++m_sampleCount; + + if (m_sampleCount == 1) { + m_initialScreenPosition = touchPoint.screenPos(); + m_time.restart(); + return; + } + + if (m_sampleCount > kAxisLockSampleCount + || m_allowedDirection == QQuickFlickable::HorizontalFlick + || m_allowedDirection == QQuickFlickable::VerticalFlick) + return; + + QVector2D velocity = touchVelocity(event); + + qreal directionIndicator = qAbs(velocity.x()) - qAbs(velocity.y()); + + if (velocity.length() > kAxisLockVelocityThreshold && qAbs(directionIndicator) > kAxisLockVelocityDirectionThreshold) + m_allowedDirection = (directionIndicator > 0) ? QQuickFlickable::HorizontalFlick : QQuickFlickable::VerticalFlick; +} + +void QQuickWebViewPrivate::FlickableAxisLocker::setReferencePosition(const QPointF& position) +{ + m_lockReferencePosition = position; +} + +void QQuickWebViewPrivate::FlickableAxisLocker::reset() +{ + m_allowedDirection = QQuickFlickable::AutoFlickDirection; + m_sampleCount = 0; +} + +QPointF QQuickWebViewPrivate::FlickableAxisLocker::adjust(const QPointF& position) +{ + if (m_allowedDirection == QQuickFlickable::HorizontalFlick) + return QPointF(position.x(), m_lockReferencePosition.y()); + + if (m_allowedDirection == QQuickFlickable::VerticalFlick) + return QPointF(m_lockReferencePosition.x(), position.y()); + + return position; +} + QQuickWebViewPrivate::QQuickWebViewPrivate(QQuickWebView* viewport) : q_ptr(viewport) - , flickProvider(0) , alertDialog(0) , confirmDialog(0) , promptDialog(0) @@ -75,14 +156,15 @@ QQuickWebViewPrivate::QQuickWebViewPrivate(QQuickWebView* viewport) , certificateVerificationDialog(0) , itemSelector(0) , proxyAuthenticationDialog(0) - , userDidOverrideContentWidth(false) - , userDidOverrideContentHeight(false) + , filePicker(0) + , databaseQuotaDialog(0) + , m_useDefaultContentItemSize(true) , m_navigatorQtObjectEnabled(false) , m_renderToOffscreenBuffer(false) - , m_loadStartedSignalSent(false) - , m_dialogRunnerActive(false) + , m_dialogActive(false) { - viewport->setFlags(QQuickItem::ItemClipsChildrenToShape); + viewport->setClip(true); + viewport->setPixelAligned(true); QObject::connect(viewport, SIGNAL(visibleChanged()), viewport, SLOT(_q_onVisibleChanged())); QObject::connect(viewport, SIGNAL(urlChanged()), viewport, SLOT(_q_onUrlChanged())); pageView.reset(new QQuickWebPage(viewport)); @@ -104,6 +186,9 @@ void QQuickWebViewPrivate::initialize(WKContextRef contextRef, WKPageGroupRef pa context = contextRef ? QtWebContext::create(toImpl(contextRef)) : QtWebContext::defaultContext(); webPageProxy = context->createWebPage(&pageClient, pageGroup.get()); +#if ENABLE(FULLSCREEN_API) + webPageProxy->fullScreenManager()->setWebView(q_ptr); +#endif QQuickWebPagePrivate* const pageViewPrivate = pageView.data()->d; pageViewPrivate->initialize(webPageProxy.get()); @@ -135,26 +220,16 @@ bool QQuickWebViewPrivate::transparentBackground() const return webPageProxy->drawsTransparentBackground(); } -void QQuickWebViewPrivate::enableMouseEvents() -{ - Q_Q(QQuickWebView); - q->setAcceptedMouseButtons(Qt::MouseButtonMask); - q->setAcceptHoverEvents(true); -} - -void QQuickWebViewPrivate::disableMouseEvents() -{ - Q_Q(QQuickWebView); - q->setAcceptedMouseButtons(Qt::NoButton); - q->setAcceptHoverEvents(false); -} - QPointF QQuickWebViewPrivate::pageItemPos() { ASSERT(pageView); return pageView->pos(); } +/*! + \qmlsignal WebView::loadingChanged(WebLoadRequest request) +*/ + void QQuickWebViewPrivate::loadDidSucceed() { Q_Q(QQuickWebView); @@ -194,14 +269,6 @@ void QQuickWebViewPrivate::_q_onIconChangedForPageURL(const QUrl& pageURL, const setIcon(iconURL); } -void QQuickWebViewPrivate::didChangeLoadingState(QWebLoadRequest* loadRequest) -{ - Q_Q(QQuickWebView); - ASSERT(q->loading() == (loadRequest->status() == QQuickWebView::LoadStartedStatus)); - emit q->loadingChanged(loadRequest); - m_loadStartedSignalSent = loadRequest->status() == QQuickWebView::LoadStartedStatus; -} - void QQuickWebViewPrivate::didChangeBackForwardList() { navigationHistory->d->reset(); @@ -210,18 +277,18 @@ void QQuickWebViewPrivate::didChangeBackForwardList() void QQuickWebViewPrivate::processDidCrash() { pageView->eventHandler()->resetGestureRecognizers(); + pageLoadClient->completeLoadWhenProcessDidCrashIfNeeded(); + QUrl url(KURL(WebCore::ParsedURLString, webPageProxy->urlAtProcessExit())); - if (m_loadStartedSignalSent) { - QWebLoadRequest loadRequest(url, QQuickWebView::LoadFailedStatus, QLatin1String("The web process crashed."), QQuickWebView::InternalErrorDomain, 0); - didChangeLoadingState(&loadRequest); - } qWarning("WARNING: The web process experienced a crash on '%s'.", qPrintable(url.toString(QUrl::RemoveUserInfo))); } void QQuickWebViewPrivate::didRelaunchProcess() { qWarning("WARNING: The web process has been successfully restarted."); - pageView->d->setDrawingAreaSize(viewSize()); + + webPageProxy->drawingArea()->setSize(viewSize(), IntSize()); + updateViewportSize(); } PassOwnPtr<DrawingAreaProxy> QQuickWebViewPrivate::createDrawingAreaProxy() @@ -365,59 +432,52 @@ void QQuickWebViewPrivate::execDialogRunner(QtDialogRunner& dialogRunner) setViewInAttachedProperties(dialogRunner.dialog()); disableMouseEvents(); - m_dialogRunnerActive = true; + m_dialogActive = true; dialogRunner.exec(); - m_dialogRunnerActive = false; + m_dialogActive = false; enableMouseEvents(); } void QQuickWebViewPrivate::chooseFiles(WKOpenPanelResultListenerRef listenerRef, const QStringList& selectedFileNames, QtWebPageUIClient::FileChooserType type) { -#ifndef QT_NO_FILEDIALOG Q_Q(QQuickWebView); - openPanelResultListener = listenerRef; - - // Qt does not support multiple files suggestion, so we get just the first suggestion. - QString selectedFileName; - if (!selectedFileNames.isEmpty()) - selectedFileName = selectedFileNames.at(0); - Q_ASSERT(!fileDialog); + if (!filePicker) + return; - QWindow* window = q->canvas(); - if (!window) + QtDialogRunner dialogRunner; + if (!dialogRunner.initForFilePicker(filePicker, q, selectedFileNames, (type == QtWebPageUIClient::MultipleFilesSelection))) return; - fileDialog = new QFileDialog(0, QString(), selectedFileName); - fileDialog->window()->winId(); // Ensure that the dialog has a window - Q_ASSERT(fileDialog->window()->windowHandle()); - fileDialog->window()->windowHandle()->setTransientParent(window); + execDialogRunner(dialogRunner); - fileDialog->open(q, SLOT(_q_onOpenPanelFilesSelected())); + if (dialogRunner.wasAccepted()) { + QStringList selectedPaths = dialogRunner.filePaths(); - q->connect(fileDialog, SIGNAL(finished(int)), SLOT(_q_onOpenPanelFinished(int))); -#endif -} - -void QQuickWebViewPrivate::_q_onOpenPanelFilesSelected() -{ - const QStringList fileList = fileDialog->selectedFiles(); - Vector<RefPtr<APIObject> > wkFiles(fileList.size()); + Vector<RefPtr<APIObject> > wkFiles(selectedPaths.size()); + for (unsigned i = 0; i < selectedPaths.size(); ++i) + wkFiles[i] = WebURL::create(QUrl::fromLocalFile(selectedPaths.at(i)).toString()); - for (unsigned i = 0; i < fileList.size(); ++i) - wkFiles[i] = WebURL::create(QUrl::fromLocalFile(fileList.at(i)).toString()); + WKOpenPanelResultListenerChooseFiles(listenerRef, toAPI(ImmutableArray::adopt(wkFiles).leakRef())); + } else + WKOpenPanelResultListenerCancel(listenerRef); - WKOpenPanelResultListenerChooseFiles(openPanelResultListener, toAPI(ImmutableArray::adopt(wkFiles).leakRef())); } -void QQuickWebViewPrivate::_q_onOpenPanelFinished(int result) +quint64 QQuickWebViewPrivate::exceededDatabaseQuota(const QString& databaseName, const QString& displayName, WKSecurityOriginRef securityOrigin, quint64 currentQuota, quint64 currentOriginUsage, quint64 currentDatabaseUsage, quint64 expectedUsage) { - if (result == QDialog::Rejected) - WKOpenPanelResultListenerCancel(openPanelResultListener); + if (!databaseQuotaDialog) + return 0; + + Q_Q(QQuickWebView); + QtDialogRunner dialogRunner; + if (!dialogRunner.initForDatabaseQuotaDialog(databaseQuotaDialog, q, databaseName, displayName, securityOrigin, currentQuota, currentOriginUsage, currentDatabaseUsage, expectedUsage)) + return 0; + + execDialogRunner(dialogRunner); - fileDialog->deleteLater(); - fileDialog = 0; + return dialogRunner.wasAccepted() ? dialogRunner.databaseQuota() : 0; } void QQuickWebViewPrivate::setViewInAttachedProperties(QObject* object) @@ -462,12 +522,27 @@ void QQuickWebViewPrivate::setNavigatorQtObjectEnabled(bool enabled) context->setNavigatorQtObjectEnabled(webPageProxy.get(), enabled); } +QPointF QQuickWebViewPrivate::contentPos() const +{ + Q_Q(const QQuickWebView); + return QPointF(q->contentX(), q->contentY()); +} + +void QQuickWebViewPrivate::setContentPos(const QPointF& pos) +{ + Q_Q(QQuickWebView); + q->setContentX(pos.x()); + q->setContentY(pos.y()); +} + QRect QQuickWebViewPrivate::visibleContentsRect() const { Q_Q(const QQuickWebView); const QRectF visibleRect(q->boundingRect().intersected(pageView->boundingRect())); - return q->mapRectToWebContent(visibleRect).toAlignedRect(); + // We avoid using toAlignedRect() because it produces inconsistent width and height. + QRectF mappedRect(q->mapRectToWebContent(visibleRect)); + return QRect(floor(mappedRect.x()), floor(mappedRect.y()), floor(mappedRect.width()), floor(mappedRect.height())); } WebCore::IntSize QQuickWebViewPrivate::viewSize() const @@ -475,6 +550,15 @@ WebCore::IntSize QQuickWebViewPrivate::viewSize() const return WebCore::IntSize(pageView->width(), pageView->height()); } +/*! + \internal + + \qmlsignal WebViewExperimental::onMessageReceived(var message) + + \brief Emitted when JavaScript code executing on the web page calls navigator.qt.postMessage(). + + \sa postMessage +*/ void QQuickWebViewPrivate::didReceiveMessageFromNavigatorQtObject(const String& message) { QVariantMap variantMap; @@ -486,6 +570,12 @@ void QQuickWebViewPrivate::didReceiveMessageFromNavigatorQtObject(const String& QQuickWebViewLegacyPrivate::QQuickWebViewLegacyPrivate(QQuickWebView* viewport) : QQuickWebViewPrivate(viewport) { + // Default values for the Legacy view. + attributes.devicePixelRatio = 1; + attributes.initialScale = 1; + attributes.minimumScale = 1; + attributes.maximumScale = 1; + attributes.userScalable = 0; } void QQuickWebViewLegacyPrivate::initialize(WKContextRef contextRef, WKPageGroupRef pageGroupRef) @@ -505,16 +595,46 @@ void QQuickWebViewLegacyPrivate::updateViewportSize() // The fixed layout is handled by the FrameView and the drawing area doesn't behave differently // whether its fixed or not. We still need to tell the drawing area which part of it // has to be rendered on tiles, and in desktop mode it's all of it. - webPageProxy->drawingArea()->setVisibleContentsRectForScaling(IntRect(IntPoint(), viewportSize), 1); + webPageProxy->drawingArea()->setSize(viewportSize, IntSize()); + webPageProxy->drawingArea()->setVisibleContentsRect(IntRect(IntPoint(), viewportSize), 1, FloatPoint()); +} + +void QQuickWebViewLegacyPrivate::enableMouseEvents() +{ + Q_Q(QQuickWebView); + q->setAcceptedMouseButtons(Qt::MouseButtonMask); + q->setAcceptHoverEvents(true); +} + +void QQuickWebViewLegacyPrivate::disableMouseEvents() +{ + Q_Q(QQuickWebView); + q->setAcceptedMouseButtons(Qt::NoButton); + q->setAcceptHoverEvents(false); +} + +qreal QQuickWebViewLegacyPrivate::zoomFactor() const +{ + return webPageProxy->pageZoomFactor(); +} + +void QQuickWebViewLegacyPrivate::setZoomFactor(qreal factor) +{ + webPageProxy->setPageZoomFactor(factor); } QQuickWebViewFlickablePrivate::QQuickWebViewFlickablePrivate(QQuickWebView* viewport) : QQuickWebViewPrivate(viewport) - , postTransitionState(adoptPtr(new PostTransitionState(this))) - , isTransitioningToNewPage(false) , pageIsSuspended(true) , loadSuccessDispatchIsPending(false) { + // Disable mouse events on the flickable web view so we do not + // select text during pan gestures on platforms which send both + // touch and mouse events simultaneously. + // FIXME: Temporary workaround code which should be removed when + // bug http://codereview.qt-project.org/21896 is fixed. + viewport->setAcceptedMouseButtons(Qt::NoButton); + viewport->setAcceptHoverEvents(false); } QQuickWebViewFlickablePrivate::~QQuickWebViewFlickablePrivate() @@ -530,50 +650,42 @@ void QQuickWebViewFlickablePrivate::initialize(WKContextRef contextRef, WKPageGr QPointF QQuickWebViewFlickablePrivate::pageItemPos() { + Q_Q(QQuickWebView); // Flickable moves its contentItem so we need to take that position into account, // as well as the potential displacement of the page on the contentItem because // of additional QML items. - qreal xPos = flickProvider->contentItem()->x() + pageView->x(); - qreal yPos = flickProvider->contentItem()->y() + pageView->y(); + qreal xPos = q->contentItem()->x() + pageView->x(); + qreal yPos = q->contentItem()->y() + pageView->y(); return QPointF(xPos, yPos); } void QQuickWebViewFlickablePrivate::updateContentsSize(const QSizeF& size) { - ASSERT(flickProvider); + Q_Q(QQuickWebView); // Make sure that the contentItem is sized to the page // if the user did not add other flickable items in QML. // If the user adds items in QML he has to make sure to - // also bind the contentWidth and contentHeight accordingly. + // disable the default content item size property on the WebView + // and bind the contentWidth and contentHeight accordingly. // This is in accordance with normal QML Flickable behaviour. - if (!userDidOverrideContentWidth) - flickProvider->setContentWidth(size.width()); - if (!userDidOverrideContentHeight) - flickProvider->setContentHeight(size.height()); + if (!m_useDefaultContentItemSize) + return; + + q->setContentWidth(size.width()); + q->setContentHeight(size.height()); } void QQuickWebViewFlickablePrivate::onComponentComplete() { Q_Q(QQuickWebView); - ASSERT(!flickProvider); - flickProvider = new QtFlickProvider(q, pageView.data()); - - // Propagate flickable signals. - const QQuickWebViewExperimental* experimental = q->experimental(); - QObject::connect(flickProvider, SIGNAL(contentWidthChanged()), experimental, SIGNAL(contentWidthChanged())); - QObject::connect(flickProvider, SIGNAL(contentHeightChanged()), experimental, SIGNAL(contentHeightChanged())); - QObject::connect(flickProvider, SIGNAL(contentXChanged()), experimental, SIGNAL(contentXChanged())); - QObject::connect(flickProvider, SIGNAL(contentYChanged()), experimental, SIGNAL(contentYChanged())); - - interactionEngine.reset(new QtViewportInteractionEngine(q, pageView.data(), flickProvider)); + interactionEngine.reset(new QtViewportInteractionEngine(q, pageView.data())); pageView->eventHandler()->setViewportInteractionEngine(interactionEngine.data()); QObject::connect(interactionEngine.data(), SIGNAL(contentSuspendRequested()), q, SLOT(_q_suspend())); QObject::connect(interactionEngine.data(), SIGNAL(contentResumeRequested()), q, SLOT(_q_resume())); - QObject::connect(interactionEngine.data(), SIGNAL(contentWasMoved(const QPointF&)), q, SLOT(_q_commitPositionChange(const QPointF&))); - QObject::connect(interactionEngine.data(), SIGNAL(contentWasScaled()), q, SLOT(_q_commitScaleChange())); + QObject::connect(interactionEngine.data(), SIGNAL(contentViewportChanged(QPointF)), q, SLOT(_q_contentViewportChanged(QPointF))); _q_resume(); @@ -594,33 +706,47 @@ void QQuickWebViewFlickablePrivate::loadDidSucceed() QQuickWebViewPrivate::loadDidSucceed(); else loadSuccessDispatchIsPending = true; - } void QQuickWebViewFlickablePrivate::loadDidCommit() { // Due to entering provisional load before committing, we // might actually be suspended here. - - isTransitioningToNewPage = true; } void QQuickWebViewFlickablePrivate::didFinishFirstNonEmptyLayout() { - if (!pageIsSuspended) { - isTransitioningToNewPage = false; - postTransitionState->apply(); - } } -void QQuickWebViewFlickablePrivate::didChangeViewportProperties(const WebCore::ViewportArguments& args) +void QQuickWebViewFlickablePrivate::didChangeViewportProperties(const WebCore::ViewportAttributes& newAttributes) { - viewportArguments = args; + Q_Q(QQuickWebView); - if (isTransitioningToNewPage) - return; + QSize viewportSize = q->boundingRect().size().toSize(); + + // FIXME: Revise these when implementing fit-to-width. + WebCore::ViewportAttributes attr = newAttributes; + WebCore::restrictMinimumScaleFactorToViewportSize(attr, viewportSize); + WebCore::restrictScaleFactorToInitialScaleIfNotUserScalable(attr); + + // FIXME: Resetting here can reset more than needed. For instance it will end deferrers. + // This needs to be revised at some point. + interactionEngine->reset(); + + interactionEngine->setContentToDevicePixelRatio(attr.devicePixelRatio); - interactionEngine->applyConstraints(computeViewportConstraints()); + interactionEngine->setAllowsUserScaling(!!attr.userScalable); + interactionEngine->setCSSScaleBounds(attr.minimumScale, attr.maximumScale); + + if (!interactionEngine->hadUserInteraction() && !pageIsSuspended) + interactionEngine->setCSSScale(attr.initialScale); + + this->attributes = attr; + q->experimental()->viewportInfo()->didUpdateViewportConstraints(); + + // If the web app successively changes the viewport on purpose + // it wants to be in control and we should disable animations. + interactionEngine->ensureContentWithinViewportBoundary(/*immediate*/ true); } void QQuickWebViewFlickablePrivate::updateViewportSize() @@ -631,46 +757,36 @@ void QQuickWebViewFlickablePrivate::updateViewportSize() if (viewportSize.isEmpty() || !interactionEngine) return; - flickProvider->setViewportSize(viewportSize); + WebPreferences* wkPrefs = webPageProxy->pageGroup()->preferences(); + wkPrefs->setDeviceWidth(viewportSize.width()); + wkPrefs->setDeviceHeight(viewportSize.height()); // Let the WebProcess know about the new viewport size, so that // it can resize the content accordingly. webPageProxy->setViewportSize(viewportSize); - interactionEngine->applyConstraints(computeViewportConstraints()); - _q_commitScaleChange(); + _q_contentViewportChanged(QPointF()); } -void QQuickWebViewFlickablePrivate::_q_commitScaleChange() +void QQuickWebViewFlickablePrivate::_q_contentViewportChanged(const QPointF& trajectoryVector) { - DrawingAreaProxy* drawingArea = webPageProxy->drawingArea(); - if (!drawingArea) - return; - Q_Q(QQuickWebView); // This is only for our QML ViewportInfo debugging API. q->experimental()->viewportInfo()->didUpdateCurrentScale(); - const QRect visibleRect(visibleContentsRect()); - float scale = pageView->contentsScale(); - - drawingArea->setVisibleContentsRectForScaling(visibleRect, scale); - webPageProxy->setFixedVisibleContentRect(visibleRect); -} - -void QQuickWebViewPrivate::_q_commitPositionChange(const QPointF& trajectoryVector) -{ DrawingAreaProxy* drawingArea = webPageProxy->drawingArea(); if (!drawingArea) return; const QRect visibleRect(visibleContentsRect()); - drawingArea->setVisibleContentsRectForPanning(visibleRect, trajectoryVector); + float scale = pageView->contentsScale(); - if (!trajectoryVector.isNull()) - return; + QRectF accurateVisibleRect(q->boundingRect()); + accurateVisibleRect.translate(contentPos()); + drawingArea->setVisibleContentsRect(visibleRect, scale, trajectoryVector, FloatPoint(accurateVisibleRect.x(), accurateVisibleRect.y())); - webPageProxy->setFixedVisibleContentRect(visibleRect); + // Ensure that updatePaintNode is always called before painting. + pageView->update(); } void QQuickWebViewFlickablePrivate::_q_suspend() @@ -687,99 +803,35 @@ void QQuickWebViewFlickablePrivate::_q_resume() pageIsSuspended = false; webPageProxy->resumeActiveDOMObjectsAndAnimations(); - if (isTransitioningToNewPage) { - isTransitioningToNewPage = false; - postTransitionState->apply(); - } - - // FIXME: Revise this. - _q_commitScaleChange(); + _q_contentViewportChanged(QPointF()); } void QQuickWebViewFlickablePrivate::pageDidRequestScroll(const QPoint& pos) { - if (isTransitioningToNewPage) { - postTransitionState->position = pos; - return; - } - interactionEngine->pagePositionRequest(pos); } void QQuickWebViewFlickablePrivate::didChangeContentsSize(const QSize& newSize) { Q_Q(QQuickWebView); - // FIXME: We probably want to handle suspend here as well - if (isTransitioningToNewPage) { - postTransitionState->contentsSize = newSize; - return; - } - pageView->setContentsSize(newSize); q->experimental()->viewportInfo()->didUpdateContentsSize(); } -QtViewportInteractionEngine::Constraints QQuickWebViewFlickablePrivate::computeViewportConstraints() -{ - Q_Q(QQuickWebView); - - QtViewportInteractionEngine::Constraints newConstraints; - QSize availableSize = q->boundingRect().size().toSize(); - - // Return default values for zero sized viewport. - if (availableSize.isEmpty()) - return newConstraints; - - WebPreferences* wkPrefs = webPageProxy->pageGroup()->preferences(); - - // FIXME: Remove later; Hardcode some values for now to make sure the DPI adjustment is being tested. - wkPrefs->setDeviceDPI(240); - wkPrefs->setDeviceWidth(480); - wkPrefs->setDeviceHeight(720); - - int minimumLayoutFallbackWidth = qMax<int>(wkPrefs->layoutFallbackWidth(), availableSize.width()); - - WebCore::ViewportAttributes attr = WebCore::computeViewportAttributes(viewportArguments, minimumLayoutFallbackWidth, wkPrefs->deviceWidth(), wkPrefs->deviceHeight(), wkPrefs->deviceDPI(), availableSize); - WebCore::restrictMinimumScaleFactorToViewportSize(attr, availableSize); - WebCore::restrictScaleFactorToInitialScaleIfNotUserScalable(attr); - - newConstraints.initialScale = attr.initialScale; - newConstraints.minimumScale = attr.minimumScale; - newConstraints.maximumScale = attr.maximumScale; - newConstraints.devicePixelRatio = attr.devicePixelRatio; - newConstraints.isUserScalable = !!attr.userScalable; - newConstraints.layoutSize = attr.layoutSize; - - q->experimental()->viewportInfo()->didUpdateViewportConstraints(); - - return newConstraints; -} - -void QQuickWebViewFlickablePrivate::PostTransitionState::apply() -{ - p->interactionEngine->reset(); - p->interactionEngine->applyConstraints(p->computeViewportConstraints()); - p->interactionEngine->pagePositionRequest(position); - - if (contentsSize.isValid()) { - p->pageView->setContentsSize(contentsSize); - p->q_ptr->experimental()->viewportInfo()->didUpdateContentsSize(); - } - - position = QPoint(); - contentsSize = QSize(); -} - /*! - \qmlsignal WebView::onNavigationRequested(request) + \qmlsignal WebView::onNavigationRequested(WebNavigationRequest request) - This signal is emitted for every navigation request. The request object contains url, button and modifiers properties - describing the navigation action, e.g. "a middle click with shift key pressed to 'http://qt-project.org'". + This signal is emitted for every navigation request. The request object contains url, + button and modifiers properties describing the navigation action, e.g. "a middle click + with shift key pressed to 'http://qt-project.org'". - The navigation will be accepted by default. To change that, one can set the action property to WebView.IgnoreRequest to reject - the request or WebView.DownloadRequest to trigger a download instead of navigating to the url. + The navigation will be accepted by default. To change that, one can set the action + property to WebView.IgnoreRequest to reject the request or WebView.DownloadRequest to + trigger a download instead of navigating to the url. The request object cannot be used after the signal handler function ends. + + \sa WebNavigationRequest */ QQuickWebViewAttached::QQuickWebViewAttached(QObject* object) @@ -833,6 +885,49 @@ void QQuickWebViewExperimental::setTransparentBackground(bool enable) d->setTransparentBackground(enable); } +bool QQuickWebViewExperimental::useDefaultContentItemSize() const +{ + Q_D(const QQuickWebView); + return d->m_useDefaultContentItemSize; +} + +void QQuickWebViewExperimental::setUseDefaultContentItemSize(bool enable) +{ + Q_D(QQuickWebView); + d->m_useDefaultContentItemSize = enable; +} + +/*! + \internal + + \qmlproperty int WebViewExperimental::preferredMinimumContentsWidth + \brief Minimum contents width when not overriden by the page itself. + + Unless the page defines how contents should be laid out, using e.g. + the viewport meta tag, it is laid out given the width of the viewport + (in CSS units). + + This setting can be used to enforce a minimum width when the page + does not define a width itself. This is useful for laying out pages + designed for big screens, commonly knows as desktop pages, on small + devices. + + The default value is 0, but the value of 980 is recommented for small + screens as it provides a good trade off between legitable pages and + non-broken content. + */ +int QQuickWebViewExperimental::preferredMinimumContentsWidth() const +{ + Q_D(const QQuickWebView); + return d->webPageProxy->pageGroup()->preferences()->layoutFallbackWidth(); +} + +void QQuickWebViewExperimental::setPreferredMinimumContentsWidth(int width) +{ + Q_D(QQuickWebView); + d->webPageProxy->pageGroup()->preferences()->setLayoutFallbackWidth(width); +} + void QQuickWebViewExperimental::setFlickableViewportEnabled(bool enable) { s_flickableViewportEnabled = enable; @@ -843,6 +938,17 @@ bool QQuickWebViewExperimental::flickableViewportEnabled() return s_flickableViewportEnabled; } +/*! + \internal + + \qmlmethod void WebViewExperimental::postMessage(string message) + + \brief Post a message to an onmessage function registered with the navigator.qt object + by JavaScript code executing on the page. + + \sa onMessageReceived +*/ + void QQuickWebViewExperimental::postMessage(const QString& message) { Q_D(QQuickWebView); @@ -966,6 +1072,105 @@ void QQuickWebViewExperimental::setItemSelector(QDeclarativeComponent* itemSelec emit itemSelectorChanged(); } +QDeclarativeComponent* QQuickWebViewExperimental::filePicker() const +{ + Q_D(const QQuickWebView); + return d->filePicker; +} + +void QQuickWebViewExperimental::setFilePicker(QDeclarativeComponent* filePicker) +{ + Q_D(QQuickWebView); + if (d->filePicker == filePicker) + return; + d->filePicker = filePicker; + emit filePickerChanged(); +} + +QDeclarativeComponent* QQuickWebViewExperimental::databaseQuotaDialog() const +{ + Q_D(const QQuickWebView); + return d->databaseQuotaDialog; +} + +void QQuickWebViewExperimental::setDatabaseQuotaDialog(QDeclarativeComponent* databaseQuotaDialog) +{ + Q_D(QQuickWebView); + if (d->databaseQuotaDialog == databaseQuotaDialog) + return; + d->databaseQuotaDialog = databaseQuotaDialog; + emit databaseQuotaDialogChanged(); +} + +QString QQuickWebViewExperimental::userAgent() const +{ + Q_D(const QQuickWebView); + return d->webPageProxy->userAgent(); +} + +void QQuickWebViewExperimental::setUserAgent(const QString& userAgent) +{ + Q_D(QQuickWebView); + if (userAgent == QString(d->webPageProxy->userAgent())) + return; + + d->webPageProxy->setUserAgent(userAgent); + emit userAgentChanged(); +} + +/*! + \internal + + \qmlproperty real WebViewExperimental::devicePixelRatio + \brief The ratio between the CSS units and device pixels when the content is unscaled. + + When designing touch-friendly contents, knowing the approximated target size on a device + is important for contents providers in order to get the intented layout and element + sizes. + + As most first generation touch devices had a PPI of approximately 160, this became a + de-facto value, when used in conjunction with the viewport meta tag. + + Devices with a higher PPI learning towards 240 or 320, applies a pre-scaling on all + content, of either 1.5 or 2.0, not affecting the CSS scale or pinch zooming. + + This value can be set using this property and it is exposed to CSS media queries using + the -webkit-device-pixel-ratio query. + + For instance, if you want to load an image without having it upscaled on a web view + using a device pixel ratio of 2.0 it can be done by loading an image of say 100x100 + pixels but showing it at half the size. + + FIXME: Move documentation example out in separate files + + @media (-webkit-min-device-pixel-ratio: 1.5) { + .icon { + width: 50px; + height: 50px; + url: "/images/icon@2x.png"; // This is actually a 100x100 image + } + } + + If the above is used on a device with device pixel ratio of 1.5, it will be scaled + down but still provide a better looking image. + */ + +double QQuickWebViewExperimental::devicePixelRatio() const +{ + Q_D(const QQuickWebView); + return d->webPageProxy->pageGroup()->preferences()->devicePixelRatio(); +} + +void QQuickWebViewExperimental::setDevicePixelRatio(double devicePixelRatio) +{ + Q_D(QQuickWebView); + if (devicePixelRatio == this->devicePixelRatio()) + return; + + d->webPageProxy->pageGroup()->preferences()->setDevicePixelRatio(devicePixelRatio); + emit devicePixelRatioChanged(); +} + QQuickUrlSchemeDelegate* QQuickWebViewExperimental::schemeDelegates_At(QDeclarativeListProperty<QQuickUrlSchemeDelegate>* property, int index) { const QObjectList children = property->object->children(); @@ -1052,80 +1257,18 @@ QQuickWebPage* QQuickWebViewExperimental::page() return q_ptr->page(); } -QDeclarativeListProperty<QObject> QQuickWebViewExperimental::flickableData() -{ - Q_D(const QQuickWebView); - ASSERT(d->flickProvider); - return d->flickProvider->flickableData(); -} - -QQuickItem* QQuickWebViewExperimental::contentItem() -{ - Q_D(QQuickWebView); - ASSERT(d->flickProvider); - return d->flickProvider->contentItem(); -} - -qreal QQuickWebViewExperimental::contentWidth() const -{ - Q_D(const QQuickWebView); - ASSERT(d->flickProvider); - return d->flickProvider->contentWidth(); -} - -void QQuickWebViewExperimental::setContentWidth(qreal width) -{ - Q_D(QQuickWebView); - ASSERT(d->flickProvider); - d->userDidOverrideContentWidth = true; - d->flickProvider->setContentWidth(width); -} - -qreal QQuickWebViewExperimental::contentHeight() const -{ - Q_D(const QQuickWebView); - ASSERT(d->flickProvider); - return d->flickProvider->contentHeight(); -} - -void QQuickWebViewExperimental::setContentHeight(qreal height) -{ - Q_D(QQuickWebView); - ASSERT(d->flickProvider); - d->userDidOverrideContentHeight = true; - d->flickProvider->setContentHeight(height); -} - -qreal QQuickWebViewExperimental::contentX() const -{ - Q_D(const QQuickWebView); - ASSERT(d->flickProvider); - return d->flickProvider->contentX(); -} - -void QQuickWebViewExperimental::setContentX(qreal x) -{ - Q_D(QQuickWebView); - ASSERT(d->flickProvider); - d->flickProvider->setContentX(x); -} - -qreal QQuickWebViewExperimental::contentY() const -{ - Q_D(const QQuickWebView); - ASSERT(d->flickProvider); - return d->flickProvider->contentY(); -} +/*! + \qmlclass WebView QWebView + \inqmlmodule QtWebKit 3.0 +*/ -void QQuickWebViewExperimental::setContentY(qreal y) -{ - Q_D(QQuickWebView); - ASSERT(d->flickProvider); - d->flickProvider->setContentY(y); -} +/*! + \qmlmethod WebView(Item parent) + \brief Constructs a WebView with a parent. +*/ QQuickWebView::QQuickWebView(QQuickItem* parent) - : QQuickItem(parent) + : QQuickFlickable(parent) , d_ptr(createPrivateObject(this)) , m_experimental(new QQuickWebViewExperimental(this)) { @@ -1134,13 +1277,12 @@ QQuickWebView::QQuickWebView(QQuickItem* parent) } QQuickWebView::QQuickWebView(WKContextRef contextRef, WKPageGroupRef pageGroupRef, QQuickItem* parent) - : QQuickItem(parent) + : QQuickFlickable(parent) , d_ptr(createPrivateObject(this)) , m_experimental(new QQuickWebViewExperimental(this)) { Q_D(QQuickWebView); d->initialize(contextRef, pageGroupRef); - setClip(true); } QQuickWebView::~QQuickWebView() @@ -1208,6 +1350,13 @@ QUrl QQuickWebView::icon() const return d->m_iconURL; } +/*! + \qmlproperty int WebView::loadProgress + \brief The progress of loading the current web page. + + The range is from 0 to 100. +*/ + int QQuickWebView::loadProgress() const { Q_D(const QQuickWebView); @@ -1226,6 +1375,11 @@ bool QQuickWebView::canGoForward() const return d->webPageProxy->canGoForward(); } +/*! + \qmlproperty bool WebView::loading + \brief True if the web view is currently loading a web page, false otherwise. +*/ + bool QQuickWebView::loading() const { Q_D(const QQuickWebView); @@ -1233,30 +1387,50 @@ bool QQuickWebView::loading() const return mainFrame && !(WebFrameProxy::LoadStateFinished == mainFrame->loadState()); } +/*! + \internal + */ + QPointF QQuickWebView::mapToWebContent(const QPointF& pointInViewCoordinates) const { Q_D(const QQuickWebView); return d->pageView->transformFromItem().map(pointInViewCoordinates); } +/*! + \internal + */ + QRectF QQuickWebView::mapRectToWebContent(const QRectF& rectInViewCoordinates) const { Q_D(const QQuickWebView); return d->pageView->transformFromItem().mapRect(rectInViewCoordinates); } +/*! + \internal + */ + QPointF QQuickWebView::mapFromWebContent(const QPointF& pointInCSSCoordinates) const { Q_D(const QQuickWebView); return d->pageView->transformToItem().map(pointInCSSCoordinates); } +/*! + \internal + */ QRectF QQuickWebView::mapRectFromWebContent(const QRectF& rectInCSSCoordinates) const { Q_D(const QQuickWebView); return d->pageView->transformToItem().mapRect(rectInCSSCoordinates); } +/*! + \qmlproperty string WebView::title + \brief The title of the loaded page. +*/ + QString QQuickWebView::title() const { Q_D(const QQuickWebView); @@ -1287,10 +1461,16 @@ QVariant QQuickWebView::inputMethodQuery(Qt::InputMethodQuery property) const return int(Qt::InputMethodHints(state.inputMethodHints)); default: // Rely on the base implementation for ImEnabled, ImHints and ImPreferredLanguage. - return QQuickItem::inputMethodQuery(property); + return QQuickFlickable::inputMethodQuery(property); } } +/*! + \preliminary + + The experimental module consisting on experimental API which will break + from version to version. +*/ QQuickWebViewExperimental* QQuickWebView::experimental() const { return m_experimental; @@ -1301,6 +1481,9 @@ QQuickWebViewAttached* QQuickWebView::qmlAttachedProperties(QObject* object) return new QQuickWebViewAttached(object); } +/*! + \internal +*/ void QQuickWebView::platformInitialize() { JSC::initializeThreading(); @@ -1310,7 +1493,7 @@ void QQuickWebView::platformInitialize() void QQuickWebView::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) { Q_D(QQuickWebView); - QQuickItem::geometryChanged(newGeometry, oldGeometry); + QQuickFlickable::geometryChanged(newGeometry, oldGeometry); if (newGeometry.size() != oldGeometry.size()) d->updateViewportSize(); } @@ -1318,7 +1501,7 @@ void QQuickWebView::geometryChanged(const QRectF& newGeometry, const QRectF& old void QQuickWebView::componentComplete() { Q_D(QQuickWebView); - QQuickItem::componentComplete(); + QQuickFlickable::componentComplete(); d->onComponentComplete(); d->updateViewportSize(); @@ -1357,11 +1540,21 @@ void QQuickWebView::focusOutEvent(QFocusEvent* event) void QQuickWebView::touchEvent(QTouchEvent* event) { Q_D(QQuickWebView); - if (d->m_dialogRunnerActive) { + if (d->m_dialogActive) { event->ignore(); return; } + bool lockingDisabled = flickableDirection() != AutoFlickDirection + || event->touchPoints().size() != 1 + || width() >= contentWidth() + || height() >= contentHeight(); + + if (!lockingDisabled) + d->axisLocker.update(event); + else + d->axisLocker.reset(); + forceActiveFocus(); d->pageView->eventHandler()->handleTouchEvent(event); } @@ -1445,7 +1638,7 @@ void QQuickWebView::dropEvent(QDropEvent* event) bool QQuickWebView::event(QEvent* ev) { // Re-implemented for possible future use without breaking binary compatibility. - return QQuickItem::event(ev); + return QQuickFlickable::event(ev); } WKPageRef QQuickWebView::pageRef() const @@ -1454,13 +1647,52 @@ WKPageRef QQuickWebView::pageRef() const return toAPI(d->webPageProxy.get()); } +QPointF QQuickWebView::contentPos() const +{ + Q_D(const QQuickWebView); + return d->contentPos(); +} + +void QQuickWebView::setContentPos(const QPointF& pos) +{ + Q_D(QQuickWebView); + d->setContentPos(pos); +} + +void QQuickWebView::handleFlickableMousePress(const QPointF& position, qint64 eventTimestampMillis) +{ + Q_D(QQuickWebView); + d->axisLocker.setReferencePosition(position); + QMouseEvent mouseEvent(QEvent::MouseButtonPress, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier); + mouseEvent.setTimestamp(eventTimestampMillis); + QQuickFlickable::mousePressEvent(&mouseEvent); +} + +void QQuickWebView::handleFlickableMouseMove(const QPointF& position, qint64 eventTimestampMillis) +{ + Q_D(QQuickWebView); + QMouseEvent mouseEvent(QEvent::MouseMove, d->axisLocker.adjust(position), Qt::LeftButton, Qt::NoButton, Qt::NoModifier); + mouseEvent.setTimestamp(eventTimestampMillis); + QQuickFlickable::mouseMoveEvent(&mouseEvent); +} + +void QQuickWebView::handleFlickableMouseRelease(const QPointF& position, qint64 eventTimestampMillis) +{ + Q_D(QQuickWebView); + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, d->axisLocker.adjust(position), Qt::LeftButton, Qt::NoButton, Qt::NoModifier); + d->axisLocker.reset(); + mouseEvent.setTimestamp(eventTimestampMillis); + QQuickFlickable::mouseReleaseEvent(&mouseEvent); +} + /*! - Loads the specified \a html as the content of the web view. + \qmlmethod void WebView::loadHtml(string html, url baseUrl, url unreachableUrl) + \brief Loads the specified \a html as the content of the web view. External objects such as stylesheets or images referenced in the HTML document are located relative to \a baseUrl. - \sa load() + \sa WebView::url */ void QQuickWebView::loadHtml(const QString& html, const QUrl& baseUrl) { @@ -1480,4 +1712,38 @@ void QQuickWebView::updateContentsSize(const QSizeF& size) d->updateContentsSize(size); } +qreal QQuickWebView::zoomFactor() const +{ + Q_D(const QQuickWebView); + return d->zoomFactor(); +} + +void QQuickWebView::setZoomFactor(qreal factor) +{ + + Q_D(QQuickWebView); + d->setZoomFactor(factor); +} + +struct JSCallbackClosure { + QPointer<QObject> receiver; + QByteArray method; +}; + +static void javaScriptCallback(WKSerializedScriptValueRef, WKErrorRef, void* context) +{ + JSCallbackClosure* closure = reinterpret_cast<JSCallbackClosure*>(context); + QMetaObject::invokeMethod(closure->receiver, closure->method); + delete closure; +} + +void QQuickWebView::runJavaScriptInMainFrame(const QString &script, QObject *receiver, const char *method) +{ + Q_D(QQuickWebView); + JSCallbackClosure* closure = new JSCallbackClosure; + closure->receiver = receiver; + closure->method = method; + d->webPageProxy.get()->runJavaScriptInMainFrame(script, ScriptValueCallback::create(closure, javaScriptCallback)); +} + #include "moc_qquickwebview_p.cpp" |