From 5d66b66df1bc205a9ce3d25cfaaa84c961fa7a29 Mon Sep 17 00:00:00 2001 From: Arvid Nilsson Date: Thu, 28 Nov 2013 15:29:40 +0100 Subject: Quick: Add experimental.viewport.devicePixelRatio This specifies a devicePixelRatio to be used by web content instead of the QScreen::devicePixelRatio(). This is necessary on non-iOS mobile devices to remain compatible with the mobile web which assumes devicePixelRatio is computed as the ratio of actual dpi to 160 dpi. Non-iOS mobile platforms may use different criteria to determine the QScreen::devicePixelRatio(), depending on the history of the platform, or simply leave it at 1.0. For QNX, this setting gets a reasonable default value so developers don't have to regularly use this experimental API. These changes were inspired by the Android Chromium port which uses a GetDpiScale() to accomplish the same in content/browser/android/content_view_core_impl.cc. Change-Id: I1bc8878a47dabcdb6986c4fe5c8c4ac230ae2514 Reviewed-by: Jocelyn Turcotte --- src/core/render_widget_host_view_qt.cpp | 23 +++++++--- src/core/render_widget_host_view_qt.h | 1 + src/core/type_conversion.h | 5 ++ src/core/web_contents_adapter.cpp | 11 +++++ src/core/web_contents_adapter.h | 2 + src/core/web_contents_adapter_client.h | 1 + src/core/web_event_factory.cpp | 18 ++++---- src/core/web_event_factory.h | 6 +-- src/webengine/api/qquickwebengineview.cpp | 69 +++++++++++++++++++++++++++- src/webengine/api/qquickwebengineview_p_p.h | 34 +++++++++++++- src/webengine/plugin/experimental/plugin.cpp | 2 + src/webenginewidgets/api/qwebenginepage.cpp | 5 ++ src/webenginewidgets/api/qwebenginepage_p.h | 1 + 13 files changed, 157 insertions(+), 21 deletions(-) diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index e2df54afe..20c1dc4fe 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -46,6 +46,7 @@ #include "delegated_frame_node.h" #include "render_widget_host_view_qt_delegate.h" #include "type_conversion.h" +#include "web_contents_adapter_client.h" #include "web_event_factory.h" #include "cc/output/compositor_frame_ack.h" @@ -225,7 +226,8 @@ gfx::Size RenderWidgetHostViewQt::GetPhysicalBackingSize() const return gfx::Size(); const QScreen* screen = m_delegate->window()->screen(); - return gfx::ToCeiledSize(gfx::ScaleSize(GetViewBounds().size(), screen->devicePixelRatio())); + gfx::SizeF size = toGfx(m_delegate->screenRect().size()); + return gfx::ToCeiledSize(gfx::ScaleSize(size, screen->devicePixelRatio())); } gfx::NativeView RenderWidgetHostViewQt::GetNativeView() const @@ -290,7 +292,10 @@ bool RenderWidgetHostViewQt::IsShowing() gfx::Rect RenderWidgetHostViewQt::GetViewBounds() const { QRectF p = m_delegate->screenRect(); - return gfx::Rect(p.x(), p.y(), p.width(), p.height()); + float s = dpiScale(); + gfx::Point p1(floor(p.x() / s), floor(p.y() / s)); + gfx::Point p2(ceil(p.right() /s), ceil(p.bottom() / s)); + return gfx::BoundingRect(p1, p2); } // Subclasses should override this method to do what is appropriate to set @@ -592,6 +597,7 @@ void RenderWidgetHostViewQt::GetScreenInfo(WebKit::WebScreenInfo* results) if (!window) return; GetScreenInfoFromNativeWindow(window, results); + results->deviceScaleFactor *= dpiScale(); } gfx::Rect RenderWidgetHostViewQt::GetBoundsInRootWindow() @@ -814,6 +820,11 @@ void RenderWidgetHostViewQt::RemoveExpiredMappings(QTouchEvent *ev) m_touchIdMapping.swap(newMap); } +float RenderWidgetHostViewQt::dpiScale() const +{ + return m_adapterClient->dpiScale(); +} + bool RenderWidgetHostViewQt::IsPopup() const { return popup_type_ != WebKit::WebPopupTypeNone; @@ -825,7 +836,7 @@ void RenderWidgetHostViewQt::handleMouseEvent(QMouseEvent* event) if (eventType == QEvent::MouseButtonDblClick) return; - WebKit::WebMouseEvent webEvent = WebEventFactory::toWebMouseEvent(event); + WebKit::WebMouseEvent webEvent = WebEventFactory::toWebMouseEvent(event, dpiScale()); if (eventType == QMouseEvent::MouseButtonPress) { if (event->button() != m_clickHelper.lastPressButton || (event->timestamp() - m_clickHelper.lastPressTimestamp > static_cast(qGuiApp->styleHints()->mouseDoubleClickInterval())) @@ -848,7 +859,7 @@ void RenderWidgetHostViewQt::handleKeyEvent(QKeyEvent *ev) void RenderWidgetHostViewQt::handleWheelEvent(QWheelEvent *ev) { - m_host->ForwardWheelEvent(WebEventFactory::toWebWheelEvent(ev)); + m_host->ForwardWheelEvent(WebEventFactory::toWebWheelEvent(ev, dpiScale())); } void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) @@ -866,7 +877,7 @@ void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) ui::TouchEvent uiEvent( toUIEventType(touchPoint.state()), - toGfxPoint(touchPoint.pos().toPoint()), + toGfxPoint((touchPoint.pos() / dpiScale()).toPoint()), 0, // flags GetMappedTouch(touchPoint.id()), timestamp, @@ -892,7 +903,7 @@ void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) void RenderWidgetHostViewQt::handleHoverEvent(QHoverEvent *ev) { - m_host->ForwardMouseEvent(WebEventFactory::toWebMouseEvent(ev)); + m_host->ForwardMouseEvent(WebEventFactory::toWebMouseEvent(ev, dpiScale())); } void RenderWidgetHostViewQt::handleFocusEvent(QFocusEvent *ev) diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h index c0905ffeb..e7fab3ff5 100644 --- a/src/core/render_widget_host_view_qt.h +++ b/src/core/render_widget_host_view_qt.h @@ -205,6 +205,7 @@ private: void ProcessGestures(ui::GestureRecognizer::Gestures *gestures); int GetMappedTouch(int qtTouchId); void RemoveExpiredMappings(QTouchEvent *ev); + float dpiScale() const; bool IsPopup() const; diff --git a/src/core/type_conversion.h b/src/core/type_conversion.h index 0f0a8ab60..94e30d72b 100644 --- a/src/core/type_conversion.h +++ b/src/core/type_conversion.h @@ -93,6 +93,11 @@ inline QSize toQt(const gfx::Size &size) return QSize(size.width(), size.height()); } +inline gfx::SizeF toGfx(const QSizeF& size) +{ + return gfx::SizeF(size.width(), size.height()); +} + inline QSizeF toQt(const gfx::SizeF &size) { return QSizeF(size.width(), size.height()); diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index c09fc362a..f3caa7478 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -49,6 +49,7 @@ #include "web_engine_context.h" #include "base/values.h" +#include "content/browser/renderer_host/render_view_host_impl.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" @@ -344,3 +345,13 @@ void WebContentsAdapter::runJavaScript(const QString &javaScript, const QString rvh->ExecuteJavascriptInWebFrameCallbackResult(toString16(xPath), toString16(javaScript), callback); } } + +void WebContentsAdapter::dpiScaleChanged() +{ + Q_D(WebContentsAdapter); + content::RenderWidgetHostImpl* impl = NULL; + if (d->webContents->GetRenderViewHost()) + impl = content::RenderWidgetHostImpl::From(d->webContents->GetRenderViewHost()); + if (impl) + impl->NotifyScreenInfoChanged(); +} diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h index f631b6d69..4bad8aa9b 100644 --- a/src/core/web_contents_adapter.h +++ b/src/core/web_contents_adapter.h @@ -89,6 +89,8 @@ public: void enableInspector(bool); void runJavaScript(const QString &javaScript, const QString &xPath = QString(), JSCallbackBase * = 0); + void dpiScaleChanged(); + private: Q_DISABLE_COPY(WebContentsAdapter); Q_DECLARE_PRIVATE(WebContentsAdapter); diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h index c7746ffb1..dad77cf57 100644 --- a/src/core/web_contents_adapter_client.h +++ b/src/core/web_contents_adapter_client.h @@ -108,6 +108,7 @@ public: virtual void loadingStateChanged() = 0; virtual void loadProgressChanged(int progress) = 0; virtual QRectF viewportRect() const = 0; + virtual qreal dpiScale() const = 0; virtual void loadFinished(bool success) = 0; virtual void focusContainer() = 0; virtual void adoptNewWindow(WebContentsAdapter *newWebContents, WindowOpenDisposition disposition, const QRect & initialGeometry) = 0; diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp index d750aa3e0..febad0a3a 100644 --- a/src/core/web_event_factory.cpp +++ b/src/core/web_event_factory.cpp @@ -527,15 +527,15 @@ static WebInputEvent::Type webEventTypeForEvent(const QEvent* event) } } -WebMouseEvent WebEventFactory::toWebMouseEvent(QMouseEvent *ev) +WebMouseEvent WebEventFactory::toWebMouseEvent(QMouseEvent *ev, double dpiScale) { WebMouseEvent webKitEvent; webKitEvent.timeStampSeconds = currentTimeForEvent(ev); webKitEvent.button = mouseButtonForEvent(ev); webKitEvent.modifiers = modifiersForEvent(ev); - webKitEvent.x = webKitEvent.windowX = ev->x(); - webKitEvent.y = webKitEvent.windowY = ev->y(); + webKitEvent.x = webKitEvent.windowX = ev->x() / dpiScale; + webKitEvent.y = webKitEvent.windowY = ev->y() / dpiScale; webKitEvent.globalX = ev->globalX(); webKitEvent.globalY = ev->globalY(); @@ -545,20 +545,20 @@ WebMouseEvent WebEventFactory::toWebMouseEvent(QMouseEvent *ev) return webKitEvent; } -WebMouseEvent WebEventFactory::toWebMouseEvent(QHoverEvent *ev) +WebMouseEvent WebEventFactory::toWebMouseEvent(QHoverEvent *ev, double dpiScale) { WebMouseEvent webKitEvent; webKitEvent.timeStampSeconds = currentTimeForEvent(ev); webKitEvent.modifiers = modifiersForEvent(ev); - webKitEvent.x = webKitEvent.windowX = ev->pos().x(); - webKitEvent.y = webKitEvent.windowY = ev->pos().y(); + webKitEvent.x = webKitEvent.windowX = ev->pos().x() / dpiScale; + webKitEvent.y = webKitEvent.windowY = ev->pos().y() / dpiScale; webKitEvent.type = webEventTypeForEvent(ev); return webKitEvent; } -WebKit::WebMouseWheelEvent WebEventFactory::toWebWheelEvent(QWheelEvent *ev) +WebKit::WebMouseWheelEvent WebEventFactory::toWebWheelEvent(QWheelEvent *ev, double dpiScale) { WebMouseWheelEvent webEvent; webEvent.type = webEventTypeForEvent(ev); @@ -582,8 +582,8 @@ WebKit::WebMouseWheelEvent WebEventFactory::toWebWheelEvent(QWheelEvent *ev) webEvent.deltaX = webEvent.wheelTicksX * wheelScrollLines * cDefaultQtScrollStep; webEvent.deltaY = webEvent.wheelTicksY * wheelScrollLines * cDefaultQtScrollStep; - webEvent.x = webEvent.windowX = ev->x(); - webEvent.y = webEvent.windowY = ev->y(); + webEvent.x = webEvent.windowX = ev->x() / dpiScale; + webEvent.y = webEvent.windowY = ev->y() / dpiScale; webEvent.globalX = ev->globalX(); webEvent.globalY = ev->globalY(); return webEvent; diff --git a/src/core/web_event_factory.h b/src/core/web_event_factory.h index e5ee91510..29a75a285 100644 --- a/src/core/web_event_factory.h +++ b/src/core/web_event_factory.h @@ -57,9 +57,9 @@ QT_END_NAMESPACE class WebEventFactory { public: - static WebKit::WebMouseEvent toWebMouseEvent(QMouseEvent*); - static WebKit::WebMouseEvent toWebMouseEvent(QHoverEvent*); - static WebKit::WebMouseWheelEvent toWebWheelEvent(QWheelEvent*); + static WebKit::WebMouseEvent toWebMouseEvent(QMouseEvent*, double dpiScale); + static WebKit::WebMouseEvent toWebMouseEvent(QHoverEvent*, double dpiScale); + static WebKit::WebMouseWheelEvent toWebWheelEvent(QWheelEvent*, double dpiScale); static content::NativeWebKeyboardEvent toWebKeyboardEvent(QKeyEvent*); }; diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp index 4cfff0102..b7d9a2d7c 100644 --- a/src/webengine/api/qquickwebengineview.cpp +++ b/src/webengine/api/qquickwebengineview.cpp @@ -45,6 +45,7 @@ #include "web_contents_adapter.h" #include "render_widget_host_view_qt_delegate_quick.h" +#include #include QT_BEGIN_NAMESPACE @@ -52,15 +53,40 @@ QT_BEGIN_NAMESPACE QQuickWebEngineViewPrivate::QQuickWebEngineViewPrivate() : adapter(new WebContentsAdapter(qApp->property("QQuickWebEngineView_DisableHardwareAcceleration").toBool() ? SoftwareRenderingMode : HardwareAccelerationMode)) , e(new QQuickWebEngineViewExperimental(this)) + , v(new QQuickWebEngineViewport(this)) , loadProgress(0) , inspectable(false) + , devicePixelRatio(QGuiApplication::primaryScreen()->devicePixelRatio()) + , m_dpiScale(1.0) { + // The gold standard for mobile web content is 160 dpi, and the devicePixelRatio expected + // is the (possibly quantized) ratio of device dpi to 160 dpi. + // However GUI toolkits on non-iOS platforms may be using different criteria than relative + // DPI (depending on the history of that platform), dictating the choice of + // QScreen::devicePixelRatio(). + // Where applicable (i.e. non-iOS mobile platforms), override QScreen::devicePixelRatio + // and instead use a reasonable default value for viewport.devicePixelRatio to avoid every + // app having to use this experimental API. + QString platform = qApp->platformName().toLower(); + if (platform == QStringLiteral("qnx")) { + qreal webPixelRatio = QGuiApplication::primaryScreen()->physicalDotsPerInch() / 160; + + // Quantize devicePixelRatio to increments of 1 to allow JS and media queries to select + // 1x, 2x, 3x etc assets that fit an integral number of pixels. + setDevicePixelRatio(qMax(1, qRound(webPixelRatio))); + } + adapter->initialize(this); } QQuickWebEngineViewExperimental *QQuickWebEngineViewPrivate::experimental() const { - return e; + return e.data(); +} + +QQuickWebEngineViewport *QQuickWebEngineViewPrivate::viewport() const +{ + return v.data(); } RenderWidgetHostViewQtDelegate *QQuickWebEngineViewPrivate::CreateRenderWidgetHostViewQtDelegate(RenderWidgetHostViewQtDelegateClient *client, RenderingMode mode) @@ -112,6 +138,11 @@ QRectF QQuickWebEngineViewPrivate::viewportRect() const return QRectF(q->x(), q->y(), q->width(), q->height()); } +qreal QQuickWebEngineViewPrivate::dpiScale() const +{ + return m_dpiScale; +} + void QQuickWebEngineViewPrivate::loadFinished(bool success) { Q_Q(QQuickWebEngineView); @@ -137,6 +168,13 @@ void QQuickWebEngineViewPrivate::close() Q_UNREACHABLE(); } +void QQuickWebEngineViewPrivate::setDevicePixelRatio(qreal devicePixelRatio) +{ + this->devicePixelRatio = devicePixelRatio; + QScreen *screen = window ? window->screen() : QGuiApplication::primaryScreen(); + m_dpiScale = devicePixelRatio / screen->devicePixelRatio(); +} + QQuickWebEngineView::QQuickWebEngineView(QQuickItem *parent) : QQuickItem(*(new QQuickWebEngineViewPrivate), parent) { @@ -253,4 +291,33 @@ QQuickWebEngineViewExperimental::QQuickWebEngineViewExperimental(QQuickWebEngine { } +QQuickWebEngineViewport *QQuickWebEngineViewExperimental::viewport() const +{ + Q_D(const QQuickWebEngineView); + return d->viewport(); +} + +QQuickWebEngineViewport::QQuickWebEngineViewport(QQuickWebEngineViewPrivate *viewPrivate) + : d_ptr(viewPrivate) +{ +} + +qreal QQuickWebEngineViewport::devicePixelRatio() const +{ + Q_D(const QQuickWebEngineView); + return d->devicePixelRatio; +} + +void QQuickWebEngineViewport::setDevicePixelRatio(qreal devicePixelRatio) +{ + Q_D(QQuickWebEngineView); + // Valid range is [1, inf) + devicePixelRatio = qMax(1.0, devicePixelRatio); + if (d->devicePixelRatio == devicePixelRatio) + return; + d->setDevicePixelRatio(devicePixelRatio); + d->adapter->dpiScaleChanged(); + Q_EMIT devicePixelRatioChanged(); +} + QT_END_NAMESPACE diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h index e7c87ce1d..b356b8881 100644 --- a/src/webengine/api/qquickwebengineview_p_p.h +++ b/src/webengine/api/qquickwebengineview_p_p.h @@ -45,7 +45,7 @@ #include "qquickwebengineview_p.h" #include "web_contents_adapter_client.h" -#include +#include #include class WebContentsAdapter; @@ -53,9 +53,29 @@ class WebContentsAdapter; QT_BEGIN_NAMESPACE class QQuickWebEngineView; +class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineViewport : public QObject { + Q_OBJECT + Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio WRITE setDevicePixelRatio NOTIFY devicePixelRatioChanged) +public: + QQuickWebEngineViewport(QQuickWebEngineViewPrivate *viewPrivate); + + qreal devicePixelRatio() const; + void setDevicePixelRatio(qreal); + +Q_SIGNALS: + void devicePixelRatioChanged(); + +private: + QQuickWebEngineViewPrivate *d_ptr; + + Q_DECLARE_PRIVATE(QQuickWebEngineView) +}; + class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineViewExperimental : public QObject { Q_OBJECT + Q_PROPERTY(QQuickWebEngineViewport *viewport READ viewport) public: + QQuickWebEngineViewport *viewport() const; private: QQuickWebEngineViewExperimental(QQuickWebEngineViewPrivate* viewPrivate); @@ -73,6 +93,7 @@ public: QQuickWebEngineViewPrivate(); QQuickWebEngineViewExperimental *experimental() const; + QQuickWebEngineViewport *viewport() const; virtual RenderWidgetHostViewQtDelegate* CreateRenderWidgetHostViewQtDelegate(RenderWidgetHostViewQtDelegateClient *client, RenderingMode) Q_DECL_OVERRIDE; virtual void titleChanged(const QString&) Q_DECL_OVERRIDE; @@ -81,6 +102,7 @@ public: virtual void loadingStateChanged() Q_DECL_OVERRIDE; virtual void loadProgressChanged(int progress) Q_DECL_OVERRIDE; virtual QRectF viewportRect() const Q_DECL_OVERRIDE; + virtual qreal dpiScale() const Q_DECL_OVERRIDE; virtual void loadFinished(bool success) Q_DECL_OVERRIDE; virtual void focusContainer() Q_DECL_OVERRIDE; virtual void adoptNewWindow(WebContentsAdapter *newWebContents, WindowOpenDisposition disposition, const QRect &) Q_DECL_OVERRIDE; @@ -88,15 +110,23 @@ public: virtual bool contextMenuRequested(const WebEngineContextMenuData &) Q_DECL_OVERRIDE { return false;} virtual bool javascriptDialog(JavascriptDialogType type, const QString &message, const QString &defaultValue = QString(), QString *result = 0) Q_DECL_OVERRIDE { return false; } + void setDevicePixelRatio(qreal); + QExplicitlySharedDataPointer adapter; - QQuickWebEngineViewExperimental *e; + QScopedPointer e; + QScopedPointer v; QUrl icon; int loadProgress; bool inspectable; + qreal devicePixelRatio; + +private: + qreal m_dpiScale; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickWebEngineViewExperimental) +QML_DECLARE_TYPE(QQuickWebEngineViewport) #endif // QQUICKWEBENGINEVIEW_P_P_H diff --git a/src/webengine/plugin/experimental/plugin.cpp b/src/webengine/plugin/experimental/plugin.cpp index 08d482508..6107147cf 100644 --- a/src/webengine/plugin/experimental/plugin.cpp +++ b/src/webengine/plugin/experimental/plugin.cpp @@ -69,6 +69,8 @@ public: qmlRegisterExtendedType(uri, 1, 0, "WebEngineView"); qmlRegisterUncreatableType(uri, 1, 0, "WebEngineViewExperimental", QObject::tr("Cannot create a separate instance of WebEngineViewExperimental")); + qmlRegisterUncreatableType(uri, 1, 0, "WebEngineViewport", + QObject::tr("Cannot create a separate instance of WebEngineViewport")); } }; diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index 106e46949..be1cdc9f7 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -104,6 +104,11 @@ QRectF QWebEnginePagePrivate::viewportRect() const return view ? view->geometry() : QRectF(); } +qreal QWebEnginePagePrivate::dpiScale() const +{ + return 1.0; +} + void QWebEnginePagePrivate::loadFinished(bool success) { Q_Q(QWebEnginePage); diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h index 0e654e6c9..43f24b813 100644 --- a/src/webenginewidgets/api/qwebenginepage_p.h +++ b/src/webenginewidgets/api/qwebenginepage_p.h @@ -71,6 +71,7 @@ public: virtual void loadingStateChanged() Q_DECL_OVERRIDE; virtual void loadProgressChanged(int progress) Q_DECL_OVERRIDE; virtual QRectF viewportRect() const Q_DECL_OVERRIDE; + virtual qreal dpiScale() const Q_DECL_OVERRIDE; virtual void loadFinished(bool success) Q_DECL_OVERRIDE; virtual void focusContainer() Q_DECL_OVERRIDE; virtual void adoptNewWindow(WebContentsAdapter *newWebContents, WindowOpenDisposition disposition, const QRect &initialGeometry) Q_DECL_OVERRIDE; -- cgit v1.2.3