summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPeter Varga <pvarga@inf.u-szeged.hu>2020-06-09 08:41:17 +0200
committerPeter Varga <pvarga@inf.u-szeged.hu>2020-06-17 16:42:09 +0200
commit18b86842bf5169cf89e5328c64e5ef67276ece6f (patch)
tree3b80b6cc63dbccd26b0014a1382b4916f3d74421 /src
parentf92a4b3028f112616dedac892e379a13e3c53bcd (diff)
Refactor RenderWidgetHostViewQt
Make RenderWidgetHostViewQtDelegateClient class instantiable and remove its implementation from RenderWidgetHostViewQt. Change-Id: Idcad3a805defe8b910f418b91f14808b6dbf083b Reviewed-by: Jüri Valdmann <juri.valdmann@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/core/core_chromium.pri2
-rw-r--r--src/core/render_widget_host_view_qt.cpp984
-rw-r--r--src/core/render_widget_host_view_qt.h164
-rw-r--r--src/core/render_widget_host_view_qt_delegate.h28
-rw-r--r--src/core/render_widget_host_view_qt_delegate_client.cpp973
-rw-r--r--src/core/render_widget_host_view_qt_delegate_client.h166
-rw-r--r--src/core/type_conversion.cpp36
-rw-r--r--src/core/type_conversion.h3
-rw-r--r--src/core/web_contents_adapter.cpp1
-rw-r--r--src/core/web_contents_view_qt.cpp6
-rw-r--r--src/webengine/render_widget_host_view_qt_delegate_quick.cpp2
-rw-r--r--src/webengine/render_widget_host_view_qt_delegate_quick.h2
-rw-r--r--src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp2
13 files changed, 1281 insertions, 1088 deletions
diff --git a/src/core/core_chromium.pri b/src/core/core_chromium.pri
index 4292f56d0..ad766ad77 100644
--- a/src/core/core_chromium.pri
+++ b/src/core/core_chromium.pri
@@ -108,6 +108,7 @@ SOURCES = \
register_protocol_handler_request_controller_impl.cpp \
render_view_context_menu_qt.cpp \
render_widget_host_view_qt.cpp \
+ render_widget_host_view_qt_delegate_client.cpp \
renderer/content_renderer_client_qt.cpp \
renderer/content_settings_observer_qt.cpp \
renderer/render_frame_observer_qt.cpp \
@@ -214,6 +215,7 @@ HEADERS = \
render_view_context_menu_qt.h \
render_widget_host_view_qt.h \
render_widget_host_view_qt_delegate.h \
+ render_widget_host_view_qt_delegate_client.h \
renderer/content_renderer_client_qt.h \
renderer/content_settings_observer_qt.h \
renderer/render_frame_observer_qt.h \
diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp
index 95ec20857..27229df81 100644
--- a/src/core/render_widget_host_view_qt.cpp
+++ b/src/core/render_widget_host_view_qt.cpp
@@ -44,9 +44,8 @@
#include "common/qt_messages.h"
#include "qtwebenginecoreglobal_p.h"
#include "render_widget_host_view_qt_delegate.h"
-#include "touch_handle_drawable_client.h"
+#include "render_widget_host_view_qt_delegate_client.h"
#include "touch_selection_controller_client_qt.h"
-#include "touch_selection_menu_controller.h"
#include "type_conversion.h"
#include "web_contents_adapter.h"
#include "web_contents_adapter_client.h"
@@ -57,7 +56,6 @@
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/common/surfaces/frame_sink_id_allocator.h"
#include "components/viz/host/host_frame_sink_manager.h"
-#include "content/browser/compositor/surface_utils.h"
#include "content/browser/frame_host/frame_tree.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/input/synthetic_gesture_target.h"
@@ -73,9 +71,7 @@
#include "ui/events/event.h"
#include "ui/events/gesture_detection/gesture_configuration.h"
#include "ui/events/gesture_detection/gesture_provider_config_helper.h"
-#include "ui/events/gesture_detection/motion_event.h"
#include "ui/gfx/image/image_skia.h"
-#include "ui/touch_selection/touch_selection_controller.h"
#if defined(USE_OZONE)
#include "ui/base/clipboard/scoped_clipboard_writer.h"
@@ -87,23 +83,10 @@
#include "ui/base/resource/resource_bundle.h"
#endif
-#include <private/qguiapplication_p.h>
-#include <qpa/qplatforminputcontext.h>
-#include <qpa/qplatformintegration.h>
-#include <QEvent>
-#include <QFocusEvent>
#include <QGuiApplication>
-#include <QInputMethodEvent>
-#include <QTextFormat>
-#include <QKeyEvent>
-#include <QMouseEvent>
#include <QPixmap>
#include <QScreen>
-#include <QStyleHints>
-#include <QVariant>
-#include <QWheelEvent>
#include <QWindow>
-#include <QtGui/private/qinputcontrol_p.h>
namespace QtWebEngineCore {
@@ -126,50 +109,6 @@ static inline ui::LatencyInfo CreateLatencyInfo(const blink::WebInputEvent& even
return latency_info;
}
-static inline Qt::InputMethodHints toQtInputMethodHints(ui::TextInputType inputType)
-{
- switch (inputType) {
- case ui::TEXT_INPUT_TYPE_TEXT:
- return Qt::ImhPreferLowercase;
- case ui::TEXT_INPUT_TYPE_SEARCH:
- return Qt::ImhPreferLowercase | Qt::ImhNoAutoUppercase;
- case ui::TEXT_INPUT_TYPE_PASSWORD:
- return Qt::ImhSensitiveData | Qt::ImhNoPredictiveText | Qt::ImhNoAutoUppercase | Qt::ImhHiddenText;
- case ui::TEXT_INPUT_TYPE_EMAIL:
- return Qt::ImhEmailCharactersOnly;
- case ui::TEXT_INPUT_TYPE_NUMBER:
- return Qt::ImhFormattedNumbersOnly;
- case ui::TEXT_INPUT_TYPE_TELEPHONE:
- return Qt::ImhDialableCharactersOnly;
- case ui::TEXT_INPUT_TYPE_URL:
- return Qt::ImhUrlCharactersOnly | Qt::ImhNoPredictiveText | Qt::ImhNoAutoUppercase;
- case ui::TEXT_INPUT_TYPE_DATE_TIME:
- case ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL:
- case ui::TEXT_INPUT_TYPE_DATE_TIME_FIELD:
- return Qt::ImhDate | Qt::ImhTime;
- case ui::TEXT_INPUT_TYPE_DATE:
- case ui::TEXT_INPUT_TYPE_MONTH:
- case ui::TEXT_INPUT_TYPE_WEEK:
- return Qt::ImhDate;
- case ui::TEXT_INPUT_TYPE_TIME:
- return Qt::ImhTime;
- case ui::TEXT_INPUT_TYPE_TEXT_AREA:
- case ui::TEXT_INPUT_TYPE_CONTENT_EDITABLE:
- return Qt::ImhMultiLine | Qt::ImhPreferLowercase;
- default:
- return Qt::ImhNone;
- }
-}
-
-static inline int firstAvailableId(const QMap<int, int> &map)
-{
- ui::BitSet32 usedIds;
- QMap<int, int>::const_iterator end = map.end();
- for (QMap<int, int>::const_iterator it = map.begin(); it != end; ++it)
- usedIds.mark_bit(it.value());
- return usedIds.first_unmarked_bit();
-}
-
static inline ui::GestureProvider::Config QtGestureProviderConfig() {
ui::GestureProvider::Config config = ui::GetGestureProviderConfig(ui::GestureProviderConfigType::CURRENT_PLATFORM);
// Causes an assert in CreateWebGestureEventFromGestureEventData and we don't need them in Qt.
@@ -179,126 +118,16 @@ static inline ui::GestureProvider::Config QtGestureProviderConfig() {
return config;
}
-static inline bool compareTouchPoints(const QTouchEvent::TouchPoint &lhs, const QTouchEvent::TouchPoint &rhs)
-{
- // TouchPointPressed < TouchPointMoved < TouchPointReleased
- return lhs.state() < rhs.state();
-}
-
-static inline bool isCommonTextEditShortcut(const QKeyEvent *ke)
-{
- return QInputControl::isCommonTextEditShortcut(ke);
-}
-
-static uint32_t s_eventId = 0;
-class MotionEventQt : public ui::MotionEvent {
-public:
- MotionEventQt(const QList<QTouchEvent::TouchPoint> &touchPoints, const base::TimeTicks &eventTime, Action action, const Qt::KeyboardModifiers modifiers, int index = -1)
- : touchPoints(touchPoints)
- , eventTime(eventTime)
- , action(action)
- , eventId(++s_eventId)
- , flags(flagsFromModifiers(modifiers))
- , index(index)
- {
- // ACTION_DOWN and ACTION_UP must be accesssed through pointer_index 0
- Q_ASSERT((action != Action::DOWN && action != Action::UP) || index == 0);
- }
-
- uint32_t GetUniqueEventId() const override { return eventId; }
- Action GetAction() const override { return action; }
- int GetActionIndex() const override { return index; }
- size_t GetPointerCount() const override { return touchPoints.size(); }
- int GetPointerId(size_t pointer_index) const override { return touchPoints.at(pointer_index).id(); }
- float GetX(size_t pointer_index) const override { return touchPoints.at(pointer_index).pos().x(); }
- float GetY(size_t pointer_index) const override { return touchPoints.at(pointer_index).pos().y(); }
- float GetRawX(size_t pointer_index) const override { return touchPoints.at(pointer_index).screenPos().x(); }
- float GetRawY(size_t pointer_index) const override { return touchPoints.at(pointer_index).screenPos().y(); }
- float GetTouchMajor(size_t pointer_index) const override
- {
- QSizeF diams = touchPoints.at(pointer_index).ellipseDiameters();
- return std::max(diams.height(), diams.width());
- }
- float GetTouchMinor(size_t pointer_index) const override
- {
- QSizeF diams = touchPoints.at(pointer_index).ellipseDiameters();
- return std::min(diams.height(), diams.width());
- }
- float GetOrientation(size_t pointer_index) const override
- {
- return 0;
- }
- int GetFlags() const override { return flags; }
- float GetPressure(size_t pointer_index) const override { return touchPoints.at(pointer_index).pressure(); }
- float GetTiltX(size_t pointer_index) const override { return 0; }
- float GetTiltY(size_t pointer_index) const override { return 0; }
- float GetTwist(size_t) const override { return 0; }
- float GetTangentialPressure(size_t) const override { return 0; }
- base::TimeTicks GetEventTime() const override { return eventTime; }
-
- size_t GetHistorySize() const override { return 0; }
- base::TimeTicks GetHistoricalEventTime(size_t historical_index) const override { return base::TimeTicks(); }
- float GetHistoricalTouchMajor(size_t pointer_index, size_t historical_index) const override { return 0; }
- float GetHistoricalX(size_t pointer_index, size_t historical_index) const override { return 0; }
- float GetHistoricalY(size_t pointer_index, size_t historical_index) const override { return 0; }
- ToolType GetToolType(size_t pointer_index) const override {
- return (touchPoints.at(pointer_index).flags() & QTouchEvent::TouchPoint::InfoFlag::Pen) ? ui::MotionEvent::ToolType::STYLUS
- : ui::MotionEvent::ToolType::FINGER;
- }
- int GetButtonState() const override { return 0; }
-
-private:
- QList<QTouchEvent::TouchPoint> touchPoints;
- base::TimeTicks eventTime;
- Action action;
- const uint32_t eventId;
- int flags;
- int index;
-};
-
-static content::ScreenInfo screenInfoFromQScreen(QScreen *screen)
-{
- content::ScreenInfo r;
- if (screen) {
- r.device_scale_factor = screen->devicePixelRatio();
- r.depth_per_component = 8;
- r.depth = screen->depth();
- r.is_monochrome = (r.depth == 1);
- r.rect = toGfx(screen->geometry());
- r.available_rect = toGfx(screen->availableGeometry());
- } else {
- r.device_scale_factor = qGuiApp->devicePixelRatio();
- }
- return r;
-}
-
RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget)
: content::RenderWidgetHostViewBase::RenderWidgetHostViewBase(widget)
, m_taskRunner(base::ThreadTaskRunnerHandle::Get())
, m_gestureProvider(QtGestureProviderConfig(), this)
- , m_sendMotionActionDown(false)
- , m_touchMotionStarted(false)
- , m_visible(false)
- , m_loadVisuallyCommittedState(NotCommitted)
- , m_adapterClient(0)
- , m_imeInProgress(false)
- , m_receivedEmptyImeEvent(false)
- , m_isMouseLocked(false)
- , m_imState(0)
- , m_anchorPositionWithinSelection(-1)
- , m_cursorPositionWithinSelection(-1)
- , m_cursorPosition(0)
- , m_emptyPreviousSelection(true)
- , m_wheelAckPending(false)
- , m_mouseWheelPhaseHandler(this)
, m_frameSinkId(host()->GetFrameSinkId())
+ , m_delegateClient(new RenderWidgetHostViewQtDelegateClient(this))
{
if (GetTextInputManager())
GetTextInputManager()->AddObserver(this);
- const QPlatformInputContext *context = QGuiApplicationPrivate::platformIntegration()->inputContext();
- m_imeHasHiddenTextCapability = context && context->hasCapability(QPlatformInputContext::HiddenTextCapability);
-
m_rootLayer.reset(new ui::Layer(ui::LAYER_SOLID_COLOR));
m_rootLayer->SetColor(SK_ColorTRANSPARENT);
@@ -359,7 +188,7 @@ void RenderWidgetHostViewQt::setDelegate(RenderWidgetHostViewQtDelegate* delegat
m_deferredShow = false;
Show();
}
- visualPropertiesChanged();
+ delegateClient()->visualPropertiesChanged();
}
void RenderWidgetHostViewQt::setAdapterClient(WebContentsAdapterClient *adapterClient)
@@ -393,7 +222,7 @@ void RenderWidgetHostViewQt::SetSize(const gfx::Size &sizeInDips)
void RenderWidgetHostViewQt::SetBounds(const gfx::Rect &windowRectInDips)
{
- DCHECK(IsPopup());
+ DCHECK(isPopup());
m_delegate->move(toQt(windowRectInDips.origin()));
m_delegate->resize(windowRectInDips.width(), windowRectInDips.height());
}
@@ -430,7 +259,7 @@ content::BrowserAccessibilityManager* RenderWidgetHostViewQt::CreateBrowserAcces
// Set focus to the associated View component.
void RenderWidgetHostViewQt::Focus()
{
- if (!IsPopup())
+ if (!isPopup())
m_delegate->setKeyboardFocus();
host()->Focus();
}
@@ -480,7 +309,7 @@ bool RenderWidgetHostViewQt::IsShowing()
// Retrieve the bounds of the View, in screen coordinates.
gfx::Rect RenderWidgetHostViewQt::GetViewBounds()
{
- return m_viewRectInDips;
+ return toGfx(delegateClient()->viewRectInDips());
}
void RenderWidgetHostViewQt::UpdateBackgroundColor()
@@ -502,7 +331,7 @@ void RenderWidgetHostViewQt::UpdateBackgroundColor()
// Return value indicates whether the mouse is locked successfully or not.
bool RenderWidgetHostViewQt::LockMouse(bool)
{
- m_previousMousePosition = QCursor::pos();
+ delegateClient()->resetPreviousMousePosition();
m_delegate->lockMouse();
m_isMouseLocked = true;
qApp->setOverrideCursor(Qt::BlankCursor);
@@ -736,7 +565,7 @@ void RenderWidgetHostViewQt::GetScreenInfo(content::ScreenInfo *results)
gfx::Rect RenderWidgetHostViewQt::GetBoundsInRootWindow()
{
- return m_windowRectInDips;
+ return toGfx(delegateClient()->windowRectInDips());
}
void RenderWidgetHostViewQt::OnUpdateTextInputStateCalled(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view, bool did_update_state)
@@ -758,16 +587,18 @@ void RenderWidgetHostViewQt::OnUpdateTextInputStateCalled(content::TextInputMana
#else
m_delegate->setInputMethodHints(toQtInputMethodHints(getTextInputType()) | Qt::ImhNoPredictiveText);
#endif
- m_surroundingText = toQt(state->value);
+ QString surroundingText = toQt(state->value);
// Remove IME composition text from the surrounding text
if (state->composition_start != -1 && state->composition_end != -1)
- m_surroundingText.remove(state->composition_start, state->composition_end - state->composition_start);
+ surroundingText.remove(state->composition_start,
+ state->composition_end - state->composition_start);
+ delegateClient()->setSurroundingText(surroundingText);
// In case of text selection, the update is expected in RenderWidgetHostViewQt::selectionChanged().
if (GetSelectedText().empty()) {
// At this point it is unknown whether the text input state has been updated due to a text selection.
// Keep the cursor position updated for cursor movements too.
- m_cursorPosition = state->selection_start;
+ delegateClient()->setCursorPosition(state->selection_start);
m_delegate->inputMethodStateChanged(type != ui::TEXT_INPUT_TYPE_NONE, type == ui::TEXT_INPUT_TYPE_PASSWORD);
}
@@ -777,14 +608,14 @@ void RenderWidgetHostViewQt::OnUpdateTextInputStateCalled(content::TextInputMana
}
// Ignore selection change triggered by ime composition unless it clears an actual text selection
- if (state->composition_start != -1 && m_emptyPreviousSelection) {
+ if (state->composition_start != -1 && delegateClient()->isPreviousSelectionEmpty()) {
m_imState = 0;
return;
}
m_imState |= ImStateFlags::TextInputStateUpdated;
if (m_imState == ImStateFlags::AllFlags)
- selectionChanged();
+ delegateClient()->selectionChanged();
}
void RenderWidgetHostViewQt::OnSelectionBoundsChanged(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view)
@@ -795,7 +626,7 @@ void RenderWidgetHostViewQt::OnSelectionBoundsChanged(content::TextInputManager
m_imState |= ImStateFlags::TextSelectionBoundsUpdated;
if (m_imState == ImStateFlags::AllFlags
|| (m_imState == ImStateFlags::TextSelectionFlags && getTextInputType() == ui::TEXT_INPUT_TYPE_NONE)) {
- selectionChanged();
+ delegateClient()->selectionChanged();
}
}
@@ -819,80 +650,8 @@ void RenderWidgetHostViewQt::OnTextSelectionChanged(content::TextInputManager *t
m_imState |= ImStateFlags::TextSelectionUpdated;
if (m_imState == ImStateFlags::AllFlags
|| (m_imState == ImStateFlags::TextSelectionFlags && getTextInputType() == ui::TEXT_INPUT_TYPE_NONE)) {
- selectionChanged();
- }
-}
-
-void RenderWidgetHostViewQt::selectionChanged()
-{
- // Reset input manager state
- m_imState = 0;
- ui::TextInputType type = getTextInputType();
-
- // Handle text selection out of an input field
- if (type == ui::TEXT_INPUT_TYPE_NONE) {
- if (GetSelectedText().empty() && m_emptyPreviousSelection)
- return;
-
- // Reset position values to emit selectionChanged signal when clearing text selection
- // by clicking into an input field. These values are intended to be used by inputMethodQuery
- // so they are not expected to be valid when selection is out of an input field.
- m_anchorPositionWithinSelection = -1;
- m_cursorPositionWithinSelection = -1;
-
- m_emptyPreviousSelection = GetSelectedText().empty();
- m_adapterClient->selectionChanged();
- return;
+ delegateClient()->selectionChanged();
}
-
- if (GetSelectedText().empty()) {
- // RenderWidgetHostViewQt::OnUpdateTextInputStateCalled() does not update the cursor position
- // if the selection is cleared because TextInputState changes before the TextSelection change.
- Q_ASSERT(text_input_manager_->GetTextInputState());
- m_cursorPosition = text_input_manager_->GetTextInputState()->selection_start;
- m_delegate->inputMethodStateChanged(true /*editorVisible*/, type == ui::TEXT_INPUT_TYPE_PASSWORD);
-
- m_anchorPositionWithinSelection = m_cursorPosition;
- m_cursorPositionWithinSelection = m_cursorPosition;
-
- if (!m_emptyPreviousSelection) {
- m_emptyPreviousSelection = true;
- m_adapterClient->selectionChanged();
- }
-
- return;
- }
-
- const content::TextInputManager::TextSelection *selection = text_input_manager_->GetTextSelection();
- if (!selection)
- return;
-
- if (!selection->range().IsValid())
- return;
-
- int newAnchorPositionWithinSelection = 0;
- int newCursorPositionWithinSelection = 0;
-
- if (text_input_manager_->GetSelectionRegion()->anchor.type() == gfx::SelectionBound::RIGHT) {
- newAnchorPositionWithinSelection = selection->range().GetMax() - selection->offset();
- newCursorPositionWithinSelection = selection->range().GetMin() - selection->offset();
- } else {
- newAnchorPositionWithinSelection = selection->range().GetMin() - selection->offset();
- newCursorPositionWithinSelection = selection->range().GetMax() - selection->offset();
- }
-
- if (m_anchorPositionWithinSelection == newAnchorPositionWithinSelection && m_cursorPositionWithinSelection == newCursorPositionWithinSelection)
- return;
-
- m_anchorPositionWithinSelection = newAnchorPositionWithinSelection;
- m_cursorPositionWithinSelection = newCursorPositionWithinSelection;
-
- if (!selection->selected_text().empty())
- m_cursorPosition = newCursorPositionWithinSelection;
-
- m_emptyPreviousSelection = selection->selected_text().empty();
- m_delegate->inputMethodStateChanged(true /*editorVisible*/, type == ui::TEXT_INPUT_TYPE_PASSWORD);
- m_adapterClient->selectionChanged();
}
void RenderWidgetHostViewQt::OnGestureEvent(const ui::GestureEventData& gesture)
@@ -993,8 +752,7 @@ void RenderWidgetHostViewQt::notifyShown()
m_delegatedFrameHost->AttachToCompositor(m_uiCompositor.get());
m_delegatedFrameHost->WasShown(GetLocalSurfaceIdAllocation().local_surface_id(),
- m_viewRectInDips.size(),
- base::nullopt);
+ toGfx(delegateClient()->viewRectInDips().size()), base::nullopt);
}
void RenderWidgetHostViewQt::notifyHidden()
@@ -1007,202 +765,6 @@ void RenderWidgetHostViewQt::notifyHidden()
m_delegatedFrameHost->DetachFromCompositor();
}
-void RenderWidgetHostViewQt::visualPropertiesChanged()
-{
- if (!m_delegate)
- return;
-
- gfx::Rect oldViewRect = m_viewRectInDips;
- m_viewRectInDips = toGfx(m_delegate->viewGeometry().toAlignedRect());
-
- gfx::Rect oldWindowRect = m_windowRectInDips;
- m_windowRectInDips = toGfx(m_delegate->windowGeometry());
-
- QWindow *window = m_delegate->window();
- content::ScreenInfo oldScreenInfo = m_screenInfo;
- m_screenInfo = screenInfoFromQScreen(window ? window->screen() : nullptr);
-
- if (m_viewRectInDips != oldViewRect || m_windowRectInDips != oldWindowRect)
- host()->SendScreenRects();
-
- if (m_viewRectInDips.size() != oldViewRect.size() || m_screenInfo != oldScreenInfo)
- synchronizeVisualProperties(base::nullopt);
-}
-
-bool RenderWidgetHostViewQt::forwardEvent(QEvent *event)
-{
- Q_ASSERT(host()->GetView());
-
- switch (event->type()) {
- case QEvent::ShortcutOverride: {
- QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
-
- auto acceptKeyOutOfInputField = [](QKeyEvent *keyEvent) -> bool {
-#ifdef Q_OS_MACOS
- // Check if a shortcut is registered for this key sequence.
- QKeySequence sequence = QKeySequence (
- (keyEvent->modifiers() | keyEvent->key()) &
- ~(Qt::KeypadModifier | Qt::GroupSwitchModifier));
- if (QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(sequence))
- return false;
-
- // The following shortcuts are handled out of input field too but
- // disabled on macOS to let the blinking menu handling to the
- // embedder application (see kKeyboardCodeKeyDownEntries in
- // third_party/WebKit/Source/core/editing/EditingBehavior.cpp).
- // Let them pass on macOS to generate the corresponding edit command.
- return keyEvent->matches(QKeySequence::Copy)
- || keyEvent->matches(QKeySequence::Paste)
- || keyEvent->matches(QKeySequence::Cut)
- || keyEvent->matches(QKeySequence::SelectAll);
-#else
- return false;
-#endif
- };
-
- if (!inputMethodQuery(Qt::ImEnabled).toBool() && !(inputMethodQuery(Qt::ImHints).toInt() & Qt::ImhHiddenText) && !acceptKeyOutOfInputField(keyEvent))
- return false;
-
- Q_ASSERT(m_editCommand.empty());
- if (WebEventFactory::getEditCommand(keyEvent, &m_editCommand)
- || isCommonTextEditShortcut(keyEvent)) {
- event->accept();
- return true;
- }
-
- return false;
- }
- case QEvent::MouseButtonPress:
- Focus();
- Q_FALLTHROUGH();
- case QEvent::MouseButtonRelease:
- case QEvent::MouseMove:
- // Skip second MouseMove event when a window is being adopted, so that Chromium
- // can properly handle further move events.
- // Also make sure the adapter client exists to prevent a null pointer dereference,
- // because it's possible for a QWebEnginePagePrivate (adapter) instance to be destroyed,
- // and then the OS (observed on Windows) might still send mouse move events to a still
- // existing popup RWHVQDW instance.
- if (m_adapterClient && m_adapterClient->isBeingAdopted())
- return false;
- handleMouseEvent(static_cast<QMouseEvent*>(event));
- break;
- case QEvent::KeyPress:
- case QEvent::KeyRelease:
- handleKeyEvent(static_cast<QKeyEvent*>(event));
- break;
- case QEvent::Wheel:
- handleWheelEvent(static_cast<QWheelEvent*>(event));
- break;
- case QEvent::TouchBegin:
- Focus();
- Q_FALLTHROUGH();
- case QEvent::TouchUpdate:
- case QEvent::TouchEnd:
- case QEvent::TouchCancel:
- handleTouchEvent(static_cast<QTouchEvent*>(event));
- break;
-#if QT_CONFIG(tabletevent)
- case QEvent::TabletPress:
- Focus();
- Q_FALLTHROUGH();
- case QEvent::TabletRelease:
- case QEvent::TabletMove:
- handleTabletEvent(static_cast<QTabletEvent*>(event));
- break;
-#endif
-#ifndef QT_NO_GESTURES
- case QEvent::NativeGesture:
- handleGestureEvent(static_cast<QNativeGestureEvent *>(event));
- break;
-#endif // QT_NO_GESTURES
- case QEvent::HoverMove:
- handleHoverEvent(static_cast<QHoverEvent*>(event));
- break;
- case QEvent::FocusIn:
- case QEvent::FocusOut:
- handleFocusEvent(static_cast<QFocusEvent*>(event));
- break;
- case QEvent::InputMethod:
- handleInputMethodEvent(static_cast<QInputMethodEvent*>(event));
- break;
- case QEvent::InputMethodQuery:
- handleInputMethodQueryEvent(static_cast<QInputMethodQueryEvent*>(event));
- break;
- case QEvent::Leave:
-#ifdef Q_OS_WIN
- if (m_mouseButtonPressed > 0)
- return false;
-#endif
- case QEvent::HoverLeave:
- host()->ForwardMouseEvent(WebEventFactory::toWebMouseEvent(event));
- break;
- default:
- return false;
- }
- return true;
-}
-
-QVariant RenderWidgetHostViewQt::inputMethodQuery(Qt::InputMethodQuery query)
-{
- switch (query) {
- case Qt::ImEnabled: {
- ui::TextInputType type = getTextInputType();
- bool editorVisible = type != ui::TEXT_INPUT_TYPE_NONE;
- // IME manager should disable composition on input fields with ImhHiddenText hint if supported
- if (m_imeHasHiddenTextCapability)
- return QVariant(editorVisible);
-
- bool passwordInput = type == ui::TEXT_INPUT_TYPE_PASSWORD;
- return QVariant(editorVisible && !passwordInput);
- }
- case Qt::ImFont:
- // TODO: Implement this
- return QVariant();
- case Qt::ImCursorRectangle: {
- if (text_input_manager_) {
- if (auto *region = text_input_manager_->GetSelectionRegion()) {
- if (region->focus.GetHeight() > 0) {
- gfx::Rect caretRect = gfx::RectBetweenSelectionBounds(region->anchor, region->focus);
- if (caretRect.width() == 0)
- caretRect.set_width(1); // IME API on Windows expects a width > 0
- return toQt(caretRect);
- }
- }
- }
- return QVariant();
- }
- case Qt::ImCursorPosition:
- return m_cursorPosition;
- case Qt::ImAnchorPosition:
- return GetSelectedText().empty() ? m_cursorPosition : m_anchorPositionWithinSelection;
- case Qt::ImSurroundingText:
- return m_surroundingText;
- case Qt::ImCurrentSelection:
- return toQt(GetSelectedText());
- case Qt::ImMaximumTextLength:
- // TODO: Implement this
- return QVariant(); // No limit.
- case Qt::ImHints:
-#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
- return int(toQtInputMethodHints(getTextInputType()) | Qt::ImhNoPredictiveText | Qt::ImhNoTextHandles | Qt::ImhNoEditMenu);
-#else
- return int(toQtInputMethodHints(getTextInputType()) | Qt::ImhNoPredictiveText);
-#endif
- default:
- return QVariant();
- }
-}
-
-void RenderWidgetHostViewQt::closePopup()
-{
- // We notify the popup to be closed by telling it that it lost focus. WebKit does the rest
- // (hiding the widget and automatic memory cleanup via
- // RenderWidget::CloseWidgetSoon() -> RenderWidgetHostImpl::ShutdownAndDestroyWidget(true).
- host()->SetActive(false);
- host()->LostFocus();
-}
-
void RenderWidgetHostViewQt::ProcessAckedTouchEvent(const content::TouchEventWithLatencyInfo &touch, content::InputEventAckState ack_result) {
Q_UNUSED(touch);
const bool eventConsumed = ack_result == content::INPUT_EVENT_ACK_STATE_CONSUMED;
@@ -1221,277 +783,46 @@ void RenderWidgetHostViewQt::processMotionEvent(const ui::MotionEvent &motionEve
host()->ForwardTouchEventWithLatencyInfo(touchEvent, CreateLatencyInfo(touchEvent));
}
-QList<QTouchEvent::TouchPoint> RenderWidgetHostViewQt::mapTouchPointIds(const QList<QTouchEvent::TouchPoint> &inputPoints)
-{
- QList<QTouchEvent::TouchPoint> outputPoints = inputPoints;
- for (int i = 0; i < outputPoints.size(); ++i) {
- QTouchEvent::TouchPoint &point = outputPoints[i];
-
- int qtId = point.id();
- QMap<int, int>::const_iterator it = m_touchIdMapping.find(qtId);
- if (it == m_touchIdMapping.end())
- it = m_touchIdMapping.insert(qtId, firstAvailableId(m_touchIdMapping));
- point.setId(it.value());
-
- if (point.state() == Qt::TouchPointReleased)
- m_touchIdMapping.remove(qtId);
- }
-
- return outputPoints;
-}
-
-bool RenderWidgetHostViewQt::IsPopup() const
+bool RenderWidgetHostViewQt::isPopup() const
{
return widget_type_ == content::WidgetType::kPopup;
}
-void RenderWidgetHostViewQt::handleMouseEvent(QMouseEvent* event)
-{
- if (event->type() == QEvent::MouseButtonPress)
- m_mouseButtonPressed++;
- if (event->type() == QEvent::MouseButtonRelease)
- m_mouseButtonPressed--;
-
- // Don't forward mouse events synthesized by the system, which are caused by genuine touch
- // events. Chromium would then process for e.g. a mouse click handler twice, once due to the
- // system synthesized mouse event, and another time due to a touch-to-gesture-to-mouse
- // transformation done by Chromium.
- if (event->source() == Qt::MouseEventSynthesizedBySystem)
- return;
- handlePointerEvent<QMouseEvent>(event);
-}
-
-void RenderWidgetHostViewQt::handleKeyEvent(QKeyEvent *ev)
-{
- if (IsMouseLocked() && ev->key() == Qt::Key_Escape && ev->type() == QEvent::KeyRelease)
- UnlockMouse();
-
- if (m_receivedEmptyImeEvent) {
- // IME composition was not finished with a valid commit string.
- // We're getting the composition result in a key event.
- if (ev->key() != 0) {
- // The key event is not a result of an IME composition. Cancel IME.
- host()->ImeCancelComposition();
- m_receivedEmptyImeEvent = false;
- } else {
- if (ev->type() == QEvent::KeyRelease) {
- host()->ImeCommitText(toString16(ev->text()),
- std::vector<ui::ImeTextSpan>(),
- gfx::Range::InvalidRange(),
- 0);
- m_receivedEmptyImeEvent = false;
- m_imeInProgress = false;
- }
- return;
- }
- }
-
- // Ignore autorepeating KeyRelease events so that the generated web events
- // conform to the spec, which requires autorepeat to result in a sequence of
- // keypress events and only one final keyup event:
- // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Auto-repeat_handling
- // https://w3c.github.io/uievents/#dom-keyboardevent-repeat
- if (ev->type() == QEvent::KeyRelease && ev->isAutoRepeat())
- return;
-
- content::NativeWebKeyboardEvent webEvent = WebEventFactory::toWebKeyboardEvent(ev);
- if (webEvent.GetType() == blink::WebInputEvent::kRawKeyDown && !m_editCommand.empty()) {
- ui::LatencyInfo latency;
- latency.set_source_event_type(ui::SourceEventType::KEY_PRESS);
- content::EditCommands commands;
- commands.emplace_back(m_editCommand, "");
- m_editCommand.clear();
- host()->ForwardKeyboardEventWithCommands(webEvent, latency, &commands, nullptr);
- return;
- }
-
- bool keyDownTextInsertion = webEvent.GetType() == blink::WebInputEvent::kRawKeyDown && webEvent.text[0];
- webEvent.skip_in_browser = keyDownTextInsertion;
- host()->ForwardKeyboardEvent(webEvent);
-
- if (keyDownTextInsertion) {
- // Blink won't consume the RawKeyDown, but rather the Char event in this case.
- // The RawKeyDown is skipped on the way back (see above).
- // The same os_event will be set on both NativeWebKeyboardEvents.
- webEvent.skip_in_browser = false;
- webEvent.SetType(blink::WebInputEvent::kChar);
- host()->ForwardKeyboardEvent(webEvent);
- }
-}
-
-void RenderWidgetHostViewQt::handleInputMethodEvent(QInputMethodEvent *ev)
+bool RenderWidgetHostViewQt::updateScreenInfo()
{
- // Reset input manager state
- m_imState = 0;
-
- if (!host())
- return;
-
- QString commitString = ev->commitString();
- QString preeditString = ev->preeditString();
-
- int cursorPositionInPreeditString = -1;
- gfx::Range selectionRange = gfx::Range::InvalidRange();
-
- const QList<QInputMethodEvent::Attribute> &attributes = ev->attributes();
- std::vector<ui::ImeTextSpan> underlines;
- bool hasSelection = false;
-
- for (const auto &attribute : attributes) {
- switch (attribute.type) {
- case QInputMethodEvent::TextFormat: {
- if (preeditString.isEmpty())
- break;
-
- int start = qMin(attribute.start, (attribute.start + attribute.length));
- int end = qMax(attribute.start, (attribute.start + attribute.length));
-
- // Blink does not support negative position values. Adjust start and end positions
- // to non-negative values.
- if (start < 0) {
- start = 0;
- end = qMax(0, start + end);
- }
-
- underlines.push_back(ui::ImeTextSpan(ui::ImeTextSpan::Type::kComposition, start, end, ui::ImeTextSpan::Thickness::kThin, SK_ColorTRANSPARENT));
-
- QTextCharFormat format = qvariant_cast<QTextFormat>(attribute.value).toCharFormat();
- if (format.underlineStyle() != QTextCharFormat::NoUnderline)
- underlines.back().underline_color = toSk(format.underlineColor());
-
- break;
- }
- case QInputMethodEvent::Cursor:
- // Always set the position of the cursor, even if it's marked invisible by Qt, otherwise
- // there is no way the user will know which part of the composition string will be
- // changed, when performing an IME-specific action (like selecting a different word
- // suggestion).
- cursorPositionInPreeditString = attribute.start;
- break;
- case QInputMethodEvent::Selection:
- hasSelection = true;
-
- // Cancel IME composition
- if (preeditString.isEmpty() && attribute.start + attribute.length == 0) {
- selectionRange.set_start(0);
- selectionRange.set_end(0);
- break;
- }
-
- selectionRange.set_start(qMin(attribute.start, (attribute.start + attribute.length)));
- selectionRange.set_end(qMax(attribute.start, (attribute.start + attribute.length)));
- break;
- default:
- break;
- }
- }
-
- if (!selectionRange.IsValid()) {
- // We did not receive a valid selection range, hence the range is going to mark the
- // cursor position.
- int newCursorPosition =
- (cursorPositionInPreeditString < 0) ? preeditString.length()
- : cursorPositionInPreeditString;
- selectionRange.set_start(newCursorPosition);
- selectionRange.set_end(newCursorPosition);
- }
-
- if (hasSelection) {
- content::mojom::FrameInputHandler *frameInputHandler = getFrameInputHandler();
- if (frameInputHandler)
- frameInputHandler->SetEditableSelectionOffsets(selectionRange.start(), selectionRange.end());
- }
-
- int replacementLength = ev->replacementLength();
- gfx::Range replacementRange = gfx::Range::InvalidRange();
-
- if (replacementLength > 0)
- {
- int replacementStart = ev->replacementStart() < 0 ? m_cursorPosition + ev->replacementStart() : ev->replacementStart();
- if (replacementStart >= 0 && replacementStart < m_surroundingText.length())
- replacementRange = gfx::Range(replacementStart, replacementStart + replacementLength);
- }
-
- // There are so-far two known cases, when an empty QInputMethodEvent is received.
- // First one happens when backspace is used to remove the last character in the pre-edit
- // string, thus signaling the end of the composition.
- // The second one happens (on Windows) when a Korean char gets composed, but instead of
- // the event having a commit string, both strings are empty, and the actual char is received
- // as a QKeyEvent after the QInputMethodEvent is processed.
- // In lieu of the second case, we can't simply cancel the composition on an empty event,
- // and then add the Korean char when QKeyEvent is received, because that leads to text
- // flickering in the textarea (or any other element).
- // Instead we postpone the processing of the empty QInputMethodEvent by posting it
- // to the same focused object, and cancelling the composition on the next event loop tick.
- if (commitString.isEmpty() && preeditString.isEmpty() && replacementLength == 0) {
- if (!m_receivedEmptyImeEvent && m_imeInProgress && !hasSelection) {
- m_receivedEmptyImeEvent = true;
- QInputMethodEvent *eventCopy = new QInputMethodEvent(*ev);
- QGuiApplication::postEvent(qApp->focusObject(), eventCopy);
- } else {
- m_receivedEmptyImeEvent = false;
- if (m_imeInProgress) {
- m_imeInProgress = false;
- host()->ImeCancelComposition();
- }
- }
-
- return;
- }
-
- m_receivedEmptyImeEvent = false;
-
- // Finish compostion: insert or erase text.
- if (!commitString.isEmpty() || replacementLength > 0) {
- host()->ImeCommitText(toString16(commitString),
- underlines,
- replacementRange,
- 0);
- m_imeInProgress = false;
- }
+ content::ScreenInfo oldScreenInfo = m_screenInfo;
+ QScreen *screen = m_delegate->window() ? m_delegate->window()->screen() : nullptr;
- // Update or start new composition.
- // Be aware of that, we might get a commit string and a pre-edit string in a single event and
- // this means a new composition.
- if (!preeditString.isEmpty()) {
- host()->ImeSetComposition(toString16(preeditString),
- underlines,
- replacementRange,
- selectionRange.start(),
- selectionRange.end());
- m_imeInProgress = true;
+ if (screen) {
+ m_screenInfo.device_scale_factor = screen->devicePixelRatio();
+ m_screenInfo.depth_per_component = 8;
+ m_screenInfo.depth = screen->depth();
+ m_screenInfo.is_monochrome = (m_screenInfo.depth == 1);
+ m_screenInfo.rect = toGfx(screen->geometry());
+ m_screenInfo.available_rect = toGfx(screen->availableGeometry());
+ } else {
+ m_screenInfo.device_scale_factor = qGuiApp->devicePixelRatio();
}
-}
-void RenderWidgetHostViewQt::handleInputMethodQueryEvent(QInputMethodQueryEvent *ev)
-{
- Qt::InputMethodQueries queries = ev->queries();
- for (uint i = 0; i < 32; ++i) {
- Qt::InputMethodQuery query = (Qt::InputMethodQuery)(int)(queries & (1<<i));
- if (query) {
- QVariant v = inputMethodQuery(query);
- ev->setValue(query, v);
- }
- }
- ev->accept();
+ return (m_screenInfo != oldScreenInfo);
}
-void RenderWidgetHostViewQt::handleWheelEvent(QWheelEvent *ev)
+void RenderWidgetHostViewQt::handleWheelEvent(QWheelEvent *event)
{
if (!m_wheelAckPending) {
Q_ASSERT(m_pendingWheelEvents.isEmpty());
- blink::WebMouseWheelEvent webEvent = WebEventFactory::toWebWheelEvent(ev);
+ blink::WebMouseWheelEvent webEvent = WebEventFactory::toWebWheelEvent(event);
m_wheelAckPending = (webEvent.phase != blink::WebMouseWheelEvent::kPhaseEnded);
- m_mouseWheelPhaseHandler.AddPhaseIfNeededAndScheduleEndEvent(webEvent, false);
+ GetMouseWheelPhaseHandler()->AddPhaseIfNeededAndScheduleEndEvent(webEvent, false);
host()->ForwardWheelEvent(webEvent);
return;
}
if (!m_pendingWheelEvents.isEmpty()) {
// Try to combine with this wheel event with the last pending one.
- if (WebEventFactory::coalesceWebWheelEvent(m_pendingWheelEvents.last(), ev))
+ if (WebEventFactory::coalesceWebWheelEvent(m_pendingWheelEvents.last(), event))
return;
}
- m_pendingWheelEvents.append(WebEventFactory::toWebWheelEvent(ev));
+ m_pendingWheelEvents.append(WebEventFactory::toWebWheelEvent(event));
}
void RenderWidgetHostViewQt::WheelEventAck(const blink::WebMouseWheelEvent &event, content::InputEventAckState /*ack_result*/)
@@ -1528,247 +859,6 @@ content::MouseWheelPhaseHandler *RenderWidgetHostViewQt::GetMouseWheelPhaseHandl
return &m_mouseWheelPhaseHandler;
}
-void RenderWidgetHostViewQt::clearPreviousTouchMotionState()
-{
- m_previousTouchPoints.clear();
- m_touchMotionStarted = false;
-}
-
-#ifndef QT_NO_GESTURES
-void RenderWidgetHostViewQt::handleGestureEvent(QNativeGestureEvent *ev)
-{
- const Qt::NativeGestureType type = ev->gestureType();
- // These are the only supported gestures by Chromium so far.
- if (type == Qt::ZoomNativeGesture || type == Qt::SmartZoomNativeGesture) {
- host()->ForwardGestureEvent(WebEventFactory::toWebGestureEvent(ev));
- }
-}
-#endif
-
-void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev)
-{
- // On macOS instead of handling touch events, we use the OS provided QNativeGestureEvents.
-#ifdef Q_OS_MACOS
- if (ev->spontaneous()) {
- return;
- } else {
- VLOG(1)
- << "Sending simulated touch events to Chromium does not work properly on macOS. "
- "Consider using QNativeGestureEvents or QMouseEvents.";
- }
-#endif
-
- // Chromium expects the touch event timestamps to be comparable to base::TimeTicks::Now().
- // Most importantly we also have to preserve the relative time distance between events.
- // Calculate a delta between event timestamps and Now() on the first received event, and
- // apply this delta to all successive events. This delta is most likely smaller than it
- // should by calculating it here but this will hopefully cause less than one frame of delay.
- base::TimeTicks eventTimestamp = base::TimeTicks() + base::TimeDelta::FromMilliseconds(ev->timestamp());
- if (m_eventsToNowDelta == base::TimeDelta())
- m_eventsToNowDelta = base::TimeTicks::Now() - eventTimestamp;
- eventTimestamp += m_eventsToNowDelta;
-
- QList<QTouchEvent::TouchPoint> touchPoints = mapTouchPointIds(ev->touchPoints());
- // Make sure that ACTION_POINTER_DOWN is delivered before ACTION_MOVE,
- // and ACTION_MOVE before ACTION_POINTER_UP.
- std::sort(touchPoints.begin(), touchPoints.end(), compareTouchPoints);
-
- // Check first if the touch event should be routed to the selectionController
- if (!touchPoints.isEmpty()) {
- ui::MotionEvent::Action action;
- switch (touchPoints[0].state()) {
- case Qt::TouchPointPressed:
- action = ui::MotionEvent::Action::DOWN;
- break;
- case Qt::TouchPointMoved:
- action = ui::MotionEvent::Action::MOVE;
- break;
- case Qt::TouchPointReleased:
- action = ui::MotionEvent::Action::UP;
- break;
- default:
- action = ui::MotionEvent::Action::NONE;
- break;
- }
-
- MotionEventQt motionEvent(touchPoints, eventTimestamp, action, ev->modifiers(), 0);
- if (m_touchSelectionController->WillHandleTouchEvent(motionEvent)) {
- m_previousTouchPoints = touchPoints;
- ev->accept();
- return;
- }
- } else {
- // An empty touchPoints always corresponds to a TouchCancel event.
- // We can't forward touch cancellations without a previously processed touch event,
- // as Chromium expects the previous touchPoints for Action::CANCEL.
- // If both are empty that means the TouchCancel was sent without an ongoing touch,
- // so there's nothing to cancel anyway.
- touchPoints = m_previousTouchPoints;
- if (touchPoints.isEmpty())
- return;
-
- MotionEventQt cancelEvent(touchPoints, eventTimestamp, ui::MotionEvent::Action::CANCEL, ev->modifiers());
- if (m_touchSelectionController->WillHandleTouchEvent(cancelEvent)) {
- m_previousTouchPoints.clear();
- ev->accept();
- return;
- }
- }
-
- switch (ev->type()) {
- case QEvent::TouchBegin:
- m_sendMotionActionDown = true;
- m_touchMotionStarted = true;
- m_touchSelectionControllerClient->onTouchDown();
- break;
- case QEvent::TouchUpdate:
- m_touchMotionStarted = true;
- break;
- case QEvent::TouchCancel:
- {
- // Only process TouchCancel events received following a TouchBegin or TouchUpdate event
- if (m_touchMotionStarted) {
- MotionEventQt cancelEvent(touchPoints, eventTimestamp, ui::MotionEvent::Action::CANCEL, ev->modifiers());
- processMotionEvent(cancelEvent);
- }
-
- clearPreviousTouchMotionState();
- return;
- }
- case QEvent::TouchEnd:
- clearPreviousTouchMotionState();
- m_touchSelectionControllerClient->onTouchUp();
- break;
- default:
- break;
- }
-
- if (m_imeInProgress && ev->type() == QEvent::TouchBegin) {
- m_imeInProgress = false;
- // Tell input method to commit the pre-edit string entered so far, and finish the
- // composition operation.
-#ifdef Q_OS_WIN
- // Yes the function name is counter-intuitive, but commit isn't actually implemented
- // by the Windows QPA, and reset does exactly what is necessary in this case.
- qApp->inputMethod()->reset();
-#else
- qApp->inputMethod()->commit();
-#endif
- }
-
- for (int i = 0; i < touchPoints.size(); ++i) {
- ui::MotionEvent::Action action;
- switch (touchPoints[i].state()) {
- case Qt::TouchPointPressed:
- if (m_sendMotionActionDown) {
- action = ui::MotionEvent::Action::DOWN;
- m_sendMotionActionDown = false;
- } else {
- action = ui::MotionEvent::Action::POINTER_DOWN;
- }
- break;
- case Qt::TouchPointMoved:
- action = ui::MotionEvent::Action::MOVE;
- break;
- case Qt::TouchPointReleased:
- action = touchPoints.size() > 1 ? ui::MotionEvent::Action::POINTER_UP :
- ui::MotionEvent::Action::UP;
- break;
- default:
- // Ignore Qt::TouchPointStationary
- continue;
- }
-
- MotionEventQt motionEvent(touchPoints, eventTimestamp, action, ev->modifiers(), i);
- processMotionEvent(motionEvent);
- }
-
- m_previousTouchPoints = touchPoints;
-}
-
-#if QT_CONFIG(tabletevent)
-void RenderWidgetHostViewQt::handleTabletEvent(QTabletEvent *event)
-{
- handlePointerEvent<QTabletEvent>(event);
-}
-#endif
-
-template<class T>
-void RenderWidgetHostViewQt::handlePointerEvent(T *event)
-{
- // Currently WebMouseEvent is a subclass of WebPointerProperties, so basically
- // tablet events are mouse events with extra properties.
- blink::WebMouseEvent webEvent = WebEventFactory::toWebMouseEvent(event);
- if ((webEvent.GetType() == blink::WebInputEvent::kMouseDown || webEvent.GetType() == blink::WebInputEvent::kMouseUp)
- && webEvent.button == blink::WebMouseEvent::Button::kNoButton) {
- // Blink can only handle the 3 main mouse-buttons and may assert when processing mouse-down for no button.
- LOG(INFO) << "Unhandled mouse button";
- return;
- }
-
- if (webEvent.GetType() == blink::WebInputEvent::kMouseDown) {
- if (event->button() != m_clickHelper.lastPressButton
- || (event->timestamp() - m_clickHelper.lastPressTimestamp > static_cast<ulong>(qGuiApp->styleHints()->mouseDoubleClickInterval()))
- || (event->pos() - m_clickHelper.lastPressPosition).manhattanLength() > qGuiApp->styleHints()->startDragDistance()
- || m_clickHelper.clickCounter >= 3)
- m_clickHelper.clickCounter = 0;
-
- m_clickHelper.lastPressTimestamp = event->timestamp();
- webEvent.click_count = ++m_clickHelper.clickCounter;
- m_clickHelper.lastPressButton = event->button();
- m_clickHelper.lastPressPosition = QPointF(event->pos()).toPoint();
- }
-
- webEvent.movement_x = event->globalX() - m_previousMousePosition.x();
- webEvent.movement_y = event->globalY() - m_previousMousePosition.y();
-
- if (IsMouseLocked())
- QCursor::setPos(m_previousMousePosition);
- else
- m_previousMousePosition = event->globalPos();
-
- if (m_imeInProgress && webEvent.GetType() == blink::WebInputEvent::kMouseDown) {
- m_imeInProgress = false;
- // Tell input method to commit the pre-edit string entered so far, and finish the
- // composition operation.
-#ifdef Q_OS_WIN
- // Yes the function name is counter-intuitive, but commit isn't actually implemented
- // by the Windows QPA, and reset does exactly what is necessary in this case.
- qApp->inputMethod()->reset();
-#else
- qApp->inputMethod()->commit();
-#endif
- }
-
- host()->ForwardMouseEvent(webEvent);
-}
-
-void RenderWidgetHostViewQt::handleHoverEvent(QHoverEvent *ev)
-{
- host()->ForwardMouseEvent(WebEventFactory::toWebMouseEvent(ev));
-}
-
-void RenderWidgetHostViewQt::handleFocusEvent(QFocusEvent *ev)
-{
- if (ev->gotFocus()) {
- host()->GotFocus();
- host()->SetActive(true);
- content::RenderViewHostImpl *viewHost = content::RenderViewHostImpl::From(host());
- Q_ASSERT(viewHost);
- if (ev->reason() == Qt::TabFocusReason)
- viewHost->SetInitialFocus(false);
- else if (ev->reason() == Qt::BacktabFocusReason)
- viewHost->SetInitialFocus(true);
- ev->accept();
-
- m_adapterClient->webContentsAdapter()->handlePendingMouseLockPermission();
- } else if (ev->lostFocus()) {
- host()->SetActive(false);
- host()->LostFocus();
- ev->accept();
- }
-}
-
void RenderWidgetHostViewQt::SetNeedsBeginFrames(bool needs_begin_frames)
{
// Not used with viz
diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h
index 139a4ebfe..42c44b007 100644
--- a/src/core/render_widget_host_view_qt.h
+++ b/src/core/render_widget_host_view_qt.h
@@ -52,15 +52,8 @@
#include "content/browser/renderer_host/input/mouse_wheel_phase_handler.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/browser/renderer_host/text_input_manager.h"
-#include "content/public/browser/render_process_host_observer.h"
-#include "gpu/ipc/common/gpu_messages.h"
#include "ui/events/gesture_detection/filtered_gesture_provider.h"
-#include <QMap>
-#include <QPoint>
-#include <QtGlobal>
-#include <QtGui/QTouchEvent>
-
QT_BEGIN_NAMESPACE
class QAccessibleInterface;
QT_END_NAMESPACE
@@ -79,30 +72,13 @@ class TouchSelectionController;
namespace QtWebEngineCore {
-class TouchHandleDrawableClient;
+class RenderWidgetHostViewQtDelegateClient;
class TouchSelectionControllerClientQt;
-class TouchSelectionMenuController;
-
-struct MultipleMouseClickHelper
-{
- QPoint lastPressPosition;
- Qt::MouseButton lastPressButton;
- int clickCounter;
- ulong lastPressTimestamp;
-
- MultipleMouseClickHelper()
- : lastPressPosition(QPoint())
- , lastPressButton(Qt::NoButton)
- , clickCounter(0)
- , lastPressTimestamp(0)
- {
- }
-};
+class WebContentsAdapterClient;
class RenderWidgetHostViewQt
: public content::RenderWidgetHostViewBase
, public ui::GestureProviderClient
- , public RenderWidgetHostViewQtDelegateClient
, public base::SupportsWeakPtr<RenderWidgetHostViewQt>
, public content::TextInputManager::Observer
, public DisplayConsumer
@@ -121,6 +97,7 @@ public:
void setDelegate(RenderWidgetHostViewQtDelegate *delegate);
WebContentsAdapterClient *adapterClient() { return m_adapterClient; }
void setAdapterClient(WebContentsAdapterClient *adapterClient);
+ RenderWidgetHostViewQtDelegateClient *delegateClient() const { return m_delegateClient.get(); }
void InitAsChild(gfx::NativeView) override;
void InitAsPopup(content::RenderWidgetHostView*, const gfx::Rect&) override;
@@ -175,146 +152,101 @@ public:
void DidStopFlinging() override;
std::unique_ptr<content::SyntheticGestureTarget> CreateSyntheticGestureTarget() override;
ui::Compositor *GetCompositor() override;
+#if defined(OS_MACOSX)
+ void SetActive(bool active) override { QT_NOT_YET_IMPLEMENTED }
+ void SpeakSelection() override { QT_NOT_YET_IMPLEMENTED }
+ void ShowDefinitionForSelection() override { QT_NOT_YET_IMPLEMENTED }
+#endif // defined(OS_MACOSX)
// Overridden from ui::GestureProviderClient.
void OnGestureEvent(const ui::GestureEventData& gesture) override;
- // Overridden from RenderWidgetHostViewQtDelegateClient.
- QSGNode *updatePaintNode(QSGNode *) override;
- void notifyShown() override;
- void notifyHidden() override;
- void visualPropertiesChanged() override;
- bool forwardEvent(QEvent *) override;
- QVariant inputMethodQuery(Qt::InputMethodQuery query) override;
- void closePopup() override;
-
// Overridden from content::TextInputManager::Observer
void OnUpdateTextInputStateCalled(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view, bool did_update_state) override;
void OnSelectionBoundsChanged(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view) override;
void OnTextSelectionChanged(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view) override;
- void handleMouseEvent(QMouseEvent*);
- void handleKeyEvent(QKeyEvent*);
- void handleWheelEvent(QWheelEvent*);
- void handleTouchEvent(QTouchEvent*);
-#if QT_CONFIG(tabletevent)
- void handleTabletEvent(QTabletEvent *ev);
-#endif
-#ifndef QT_NO_GESTURES
- void handleGestureEvent(QNativeGestureEvent *);
-#endif
- void handleHoverEvent(QHoverEvent*);
- void handleFocusEvent(QFocusEvent*);
- void handleInputMethodEvent(QInputMethodEvent*);
- void handleInputMethodQueryEvent(QInputMethodQueryEvent*);
-
- template<class T> void handlePointerEvent(T*);
-
-#if defined(OS_MACOSX)
- void SetActive(bool active) override { QT_NOT_YET_IMPLEMENTED }
- void SpeakSelection() override { QT_NOT_YET_IMPLEMENTED }
- void ShowDefinitionForSelection() override { QT_NOT_YET_IMPLEMENTED }
-#endif // defined(OS_MACOSX)
-
// Overridden from content::BrowserAccessibilityDelegate
content::BrowserAccessibilityManager* CreateBrowserAccessibilityManager(content::BrowserAccessibilityDelegate* delegate, bool for_root_frame) override;
- // Called from WebContentsDelegateQt
- void OnDidFirstVisuallyNonEmptyPaint();
-
// Overridden from content::RenderFrameMetadataProvider::Observer
void OnRenderFrameMetadataChangedAfterActivation() override;
// Overridden from DisplayConsumer
void scheduleUpdate() override;
+ // Called from RenderWidgetHostViewQtDelegateClient.
+ QSGNode *updatePaintNode(QSGNode *);
+ void notifyShown();
+ void notifyHidden();
+ bool updateScreenInfo();
+ void handleWheelEvent(QWheelEvent *);
+ void processMotionEvent(const ui::MotionEvent &motionEvent);
+ void resetInputManagerState() { m_imState = 0; }
+
+ // Called from WebContentsDelegateQt.
+ void OnDidFirstVisuallyNonEmptyPaint();
+
+ // Called from WebContentsAdapter.
gfx::SizeF lastContentsSize() const { return m_lastContentsSize; }
gfx::Vector2dF lastScrollOffset() const { return m_lastScrollOffset; }
+ ui::TextInputType getTextInputType() const;
+ content::mojom::FrameInputHandler *getFrameInputHandler();
+
ui::TouchSelectionController *getTouchSelectionController() const { return m_touchSelectionController.get(); }
TouchSelectionControllerClientQt *getTouchSelectionControllerClient() const { return m_touchSelectionControllerClient.get(); }
- content::mojom::FrameInputHandler *getFrameInputHandler();
- ui::TextInputType getTextInputType() const;
+
+ void synchronizeVisualProperties(
+ const base::Optional<viz::LocalSurfaceIdAllocation> &childSurfaceId);
private:
friend class DelegatedFrameHostClientQt;
- void processMotionEvent(const ui::MotionEvent &motionEvent);
- void clearPreviousTouchMotionState();
- QList<QTouchEvent::TouchPoint> mapTouchPointIds(const QList<QTouchEvent::TouchPoint> &inputPoints);
-
- bool IsPopup() const;
-
- void selectionChanged();
+ bool isPopup() const;
content::RenderFrameHost *getFocusedFrameHost();
-
- void synchronizeVisualProperties(const base::Optional<viz::LocalSurfaceIdAllocation> &childSurfaceId);
-
void callUpdate();
- // Geometry of the view in screen DIPs.
- gfx::Rect m_viewRectInDips;
- // Geometry of the window, including frame, in screen DIPs.
- gfx::Rect m_windowRectInDips;
- content::ScreenInfo m_screenInfo;
-
scoped_refptr<base::SingleThreadTaskRunner> m_taskRunner;
-
ui::FilteredGestureProvider m_gestureProvider;
- base::TimeDelta m_eventsToNowDelta;
- bool m_sendMotionActionDown;
- bool m_touchMotionStarted;
- QMap<int, int> m_touchIdMapping;
- QList<QTouchEvent::TouchPoint> m_previousTouchPoints;
+ viz::FrameSinkId m_frameSinkId;
+ std::unique_ptr<RenderWidgetHostViewQtDelegateClient> m_delegateClient;
std::unique_ptr<RenderWidgetHostViewQtDelegate> m_delegate;
+ QMetaObject::Connection m_adapterClientDestroyedConnection;
+ WebContentsAdapterClient *m_adapterClient = nullptr;
- bool m_visible;
+ bool m_isMouseLocked = false;
+ bool m_visible = false;
bool m_deferredShow = false;
- DelegatedFrameHostClientQt m_delegatedFrameHostClient{this};
+ gfx::Vector2dF m_lastScrollOffset;
+ gfx::SizeF m_lastContentsSize;
+ DelegatedFrameHostClientQt m_delegatedFrameHostClient { this };
+ LoadVisuallyCommittedState m_loadVisuallyCommittedState = NotCommitted;
+
+ // VIZ
+ content::ScreenInfo m_screenInfo;
std::unique_ptr<content::DelegatedFrameHost> m_delegatedFrameHost;
std::unique_ptr<ui::Layer> m_rootLayer;
std::unique_ptr<ui::Compositor> m_uiCompositor;
scoped_refptr<DisplayFrameSink> m_displayFrameSink;
- LoadVisuallyCommittedState m_loadVisuallyCommittedState;
-
- QMetaObject::Connection m_adapterClientDestroyedConnection;
- WebContentsAdapterClient *m_adapterClient;
- MultipleMouseClickHelper m_clickHelper;
-
- bool m_imeInProgress;
- bool m_receivedEmptyImeEvent;
- QPoint m_previousMousePosition;
- bool m_isMouseLocked;
-
- gfx::Vector2dF m_lastScrollOffset;
- gfx::SizeF m_lastContentsSize;
viz::ParentLocalSurfaceIdAllocator m_dfhLocalSurfaceIdAllocator;
viz::ParentLocalSurfaceIdAllocator m_uiCompositorLocalSurfaceIdAllocator;
- uint m_imState;
- int m_anchorPositionWithinSelection;
- int m_cursorPositionWithinSelection;
- uint m_cursorPosition;
- bool m_emptyPreviousSelection;
- QString m_surroundingText;
-
- bool m_imeHasHiddenTextCapability;
+ // IME
+ uint m_imState = 0;
- bool m_wheelAckPending;
+ // Wheel
+ bool m_wheelAckPending = false;
QList<blink::WebMouseWheelEvent> m_pendingWheelEvents;
- content::MouseWheelPhaseHandler m_mouseWheelPhaseHandler;
- viz::FrameSinkId m_frameSinkId;
-
- std::string m_editCommand;
+ content::MouseWheelPhaseHandler m_mouseWheelPhaseHandler { this };
+ // TouchSelection
std::unique_ptr<TouchSelectionControllerClientQt> m_touchSelectionControllerClient;
std::unique_ptr<ui::TouchSelectionController> m_touchSelectionController;
gfx::SelectionBound m_selectionStart;
gfx::SelectionBound m_selectionEnd;
- base::WeakPtrFactory<RenderWidgetHostViewQt> m_weakPtrFactory{this};
-
- uint m_mouseButtonPressed = 0;
+ base::WeakPtrFactory<RenderWidgetHostViewQt> m_weakPtrFactory { this };
};
} // namespace QtWebEngineCore
diff --git a/src/core/render_widget_host_view_qt_delegate.h b/src/core/render_widget_host_view_qt_delegate.h
index 46f1802a6..0afe258f1 100644
--- a/src/core/render_widget_host_view_qt_delegate.h
+++ b/src/core/render_widget_host_view_qt_delegate.h
@@ -53,39 +53,23 @@
#include "qtwebenginecoreglobal_p.h"
-#include <QRect>
-#include <QtGui/qwindowdefs.h>
+#include <QtCore/QRect>
+#include <QtGui/QColor>
+#include <QtGui/QCursor>
+#include <QtGui/QImage>
QT_BEGIN_NAMESPACE
-class QEvent;
-class QInputMethodEvent;
+class QSGImageNode;
class QSGLayer;
-class QSGNode;
class QSGRectangleNode;
class QSGTexture;
-class QVariant;
class QWheelEvent;
-
-class QSGImageNode;
+class QWindow;
QT_END_NAMESPACE
namespace QtWebEngineCore {
-class WebContentsAdapterClient;
-
-class Q_WEBENGINECORE_PRIVATE_EXPORT RenderWidgetHostViewQtDelegateClient {
-public:
- virtual ~RenderWidgetHostViewQtDelegateClient() { }
- virtual QSGNode *updatePaintNode(QSGNode *) = 0;
- virtual void notifyShown() = 0;
- virtual void notifyHidden() = 0;
- virtual void visualPropertiesChanged() = 0;
- virtual bool forwardEvent(QEvent *) = 0;
- virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) = 0;
- virtual void closePopup() = 0;
-};
-
class Q_WEBENGINECORE_PRIVATE_EXPORT RenderWidgetHostViewQtDelegate {
public:
virtual ~RenderWidgetHostViewQtDelegate() { }
diff --git a/src/core/render_widget_host_view_qt_delegate_client.cpp b/src/core/render_widget_host_view_qt_delegate_client.cpp
new file mode 100644
index 000000000..d7674fd13
--- /dev/null
+++ b/src/core/render_widget_host_view_qt_delegate_client.cpp
@@ -0,0 +1,973 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "render_widget_host_view_qt_delegate_client.h"
+
+#include "render_widget_host_view_qt.h"
+#include "touch_selection_controller_client_qt.h"
+#include "type_conversion.h"
+#include "web_contents_adapter.h"
+#include "web_contents_adapter_client.h"
+#include "web_event_factory.h"
+
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "ui/touch_selection/touch_selection_controller.h"
+
+#include <QEvent>
+#include <QInputMethodEvent>
+#include <QSGNode>
+#include <QStyleHints>
+#include <QTextFormat>
+#include <QVariant>
+
+#include <private/qguiapplication_p.h>
+#include <qpa/qplatforminputcontext.h>
+#include <qpa/qplatformintegration.h>
+#include <QtGui/private/qinputcontrol_p.h>
+
+namespace QtWebEngineCore {
+
+static inline int firstAvailableId(const QMap<int, int> &map)
+{
+ ui::BitSet32 usedIds;
+ QMap<int, int>::const_iterator end = map.end();
+ for (QMap<int, int>::const_iterator it = map.begin(); it != end; ++it)
+ usedIds.mark_bit(it.value());
+ return usedIds.first_unmarked_bit();
+}
+
+static QList<QTouchEvent::TouchPoint>
+mapTouchPointIds(const QList<QTouchEvent::TouchPoint> &inputPoints)
+{
+ static QMap<int, int> touchIdMapping;
+ QList<QTouchEvent::TouchPoint> outputPoints = inputPoints;
+ for (int i = 0; i < outputPoints.size(); ++i) {
+ QTouchEvent::TouchPoint &point = outputPoints[i];
+
+ int qtId = point.id();
+ QMap<int, int>::const_iterator it = touchIdMapping.find(qtId);
+ if (it == touchIdMapping.end())
+ it = touchIdMapping.insert(qtId, firstAvailableId(touchIdMapping));
+ point.setId(it.value());
+
+ if (point.state() == Qt::TouchPointReleased)
+ touchIdMapping.remove(qtId);
+ }
+
+ return outputPoints;
+}
+
+static inline bool compareTouchPoints(const QTouchEvent::TouchPoint &lhs,
+ const QTouchEvent::TouchPoint &rhs)
+{
+ // TouchPointPressed < TouchPointMoved < TouchPointReleased
+ return lhs.state() < rhs.state();
+}
+
+static uint32_t s_eventId = 0;
+class MotionEventQt : public ui::MotionEvent
+{
+public:
+ MotionEventQt(const QList<QTouchEvent::TouchPoint> &touchPoints,
+ const base::TimeTicks &eventTime, Action action,
+ const Qt::KeyboardModifiers modifiers, int index = -1)
+ : touchPoints(touchPoints)
+ , eventTime(eventTime)
+ , action(action)
+ , eventId(++s_eventId)
+ , flags(flagsFromModifiers(modifiers))
+ , index(index)
+ {
+ // ACTION_DOWN and ACTION_UP must be accesssed through pointer_index 0
+ Q_ASSERT((action != Action::DOWN && action != Action::UP) || index == 0);
+ }
+
+ uint32_t GetUniqueEventId() const override { return eventId; }
+ Action GetAction() const override { return action; }
+ int GetActionIndex() const override { return index; }
+ size_t GetPointerCount() const override { return touchPoints.size(); }
+ int GetPointerId(size_t pointer_index) const override
+ {
+ return touchPoints.at(pointer_index).id();
+ }
+ float GetX(size_t pointer_index) const override
+ {
+ return touchPoints.at(pointer_index).pos().x();
+ }
+ float GetY(size_t pointer_index) const override
+ {
+ return touchPoints.at(pointer_index).pos().y();
+ }
+ float GetRawX(size_t pointer_index) const override
+ {
+ return touchPoints.at(pointer_index).screenPos().x();
+ }
+ float GetRawY(size_t pointer_index) const override
+ {
+ return touchPoints.at(pointer_index).screenPos().y();
+ }
+ float GetTouchMajor(size_t pointer_index) const override
+ {
+ QSizeF diams = touchPoints.at(pointer_index).ellipseDiameters();
+ return std::max(diams.height(), diams.width());
+ }
+ float GetTouchMinor(size_t pointer_index) const override
+ {
+ QSizeF diams = touchPoints.at(pointer_index).ellipseDiameters();
+ return std::min(diams.height(), diams.width());
+ }
+ float GetOrientation(size_t pointer_index) const override { return 0; }
+ int GetFlags() const override { return flags; }
+ float GetPressure(size_t pointer_index) const override
+ {
+ return touchPoints.at(pointer_index).pressure();
+ }
+ float GetTiltX(size_t pointer_index) const override { return 0; }
+ float GetTiltY(size_t pointer_index) const override { return 0; }
+ float GetTwist(size_t) const override { return 0; }
+ float GetTangentialPressure(size_t) const override { return 0; }
+ base::TimeTicks GetEventTime() const override { return eventTime; }
+
+ size_t GetHistorySize() const override { return 0; }
+ base::TimeTicks GetHistoricalEventTime(size_t historical_index) const override
+ {
+ return base::TimeTicks();
+ }
+ float GetHistoricalTouchMajor(size_t pointer_index, size_t historical_index) const override
+ {
+ return 0;
+ }
+ float GetHistoricalX(size_t pointer_index, size_t historical_index) const override { return 0; }
+ float GetHistoricalY(size_t pointer_index, size_t historical_index) const override { return 0; }
+ ToolType GetToolType(size_t pointer_index) const override
+ {
+ return (touchPoints.at(pointer_index).flags() & QTouchEvent::TouchPoint::InfoFlag::Pen)
+ ? ui::MotionEvent::ToolType::STYLUS
+ : ui::MotionEvent::ToolType::FINGER;
+ }
+ int GetButtonState() const override { return 0; }
+
+private:
+ QList<QTouchEvent::TouchPoint> touchPoints;
+ base::TimeTicks eventTime;
+ Action action;
+ const uint32_t eventId;
+ int flags;
+ int index;
+};
+
+RenderWidgetHostViewQtDelegateClient::RenderWidgetHostViewQtDelegateClient(
+ RenderWidgetHostViewQt *rwhv)
+ : m_rwhv(rwhv)
+{
+ Q_ASSERT(rwhv);
+
+ const QPlatformInputContext *context =
+ QGuiApplicationPrivate::platformIntegration()->inputContext();
+ m_imeHasHiddenTextCapability =
+ context && context->hasCapability(QPlatformInputContext::HiddenTextCapability);
+}
+
+QSGNode *RenderWidgetHostViewQtDelegateClient::updatePaintNode(QSGNode *oldNode)
+{
+ return m_rwhv->updatePaintNode(oldNode);
+}
+
+void RenderWidgetHostViewQtDelegateClient::notifyShown()
+{
+ m_rwhv->notifyShown();
+}
+
+void RenderWidgetHostViewQtDelegateClient::notifyHidden()
+{
+ m_rwhv->notifyHidden();
+}
+
+void RenderWidgetHostViewQtDelegateClient::visualPropertiesChanged()
+{
+ RenderWidgetHostViewQtDelegate *delegate = m_rwhv->delegate();
+ if (!delegate)
+ return;
+
+ QRect oldViewRect = m_viewRectInDips;
+ m_viewRectInDips = delegate->viewGeometry().toAlignedRect();
+
+ QRect oldWindowRect = m_windowRectInDips;
+ m_windowRectInDips = delegate->windowGeometry();
+
+ bool screenInfoChanged = m_rwhv->updateScreenInfo();
+
+ if (m_viewRectInDips != oldViewRect || m_windowRectInDips != oldWindowRect)
+ m_rwhv->host()->SendScreenRects();
+
+ if (m_viewRectInDips.size() != oldViewRect.size() || screenInfoChanged)
+ m_rwhv->synchronizeVisualProperties(base::nullopt);
+}
+
+bool RenderWidgetHostViewQtDelegateClient::forwardEvent(QEvent *event)
+{
+ Q_ASSERT(m_rwhv->host()->GetView());
+
+ switch (event->type()) {
+ case QEvent::ShortcutOverride: {
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
+
+ auto acceptKeyOutOfInputField = [](QKeyEvent *keyEvent) -> bool {
+#ifdef Q_OS_MACOS
+ // Check if a shortcut is registered for this key sequence.
+ QKeySequence sequence = QKeySequence((keyEvent->modifiers() | keyEvent->key())
+ & ~(Qt::KeypadModifier | Qt::GroupSwitchModifier));
+ if (QGuiApplicationPrivate::instance()->shortcutMap.hasShortcutForKeySequence(sequence))
+ return false;
+
+ // The following shortcuts are handled out of input field too but
+ // disabled on macOS to let the blinking menu handling to the
+ // embedder application (see kKeyboardCodeKeyDownEntries in
+ // third_party/WebKit/Source/core/editing/EditingBehavior.cpp).
+ // Let them pass on macOS to generate the corresponding edit command.
+ return keyEvent->matches(QKeySequence::Copy) || keyEvent->matches(QKeySequence::Paste)
+ || keyEvent->matches(QKeySequence::Cut)
+ || keyEvent->matches(QKeySequence::SelectAll);
+#else
+ return false;
+#endif
+ };
+
+ if (!inputMethodQuery(Qt::ImEnabled).toBool()
+ && !(inputMethodQuery(Qt::ImHints).toInt() & Qt::ImhHiddenText)
+ && !acceptKeyOutOfInputField(keyEvent))
+ return false;
+
+ Q_ASSERT(m_editCommand.empty());
+ if (WebEventFactory::getEditCommand(keyEvent, &m_editCommand)
+ || QInputControl::isCommonTextEditShortcut(keyEvent)) {
+ event->accept();
+ return true;
+ }
+
+ return false;
+ }
+ case QEvent::MouseButtonPress:
+ m_rwhv->Focus();
+ Q_FALLTHROUGH();
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseMove:
+ // Skip second MouseMove event when a window is being adopted, so that Chromium
+ // can properly handle further move events.
+ // Also make sure the adapter client exists to prevent a null pointer dereference,
+ // because it's possible for a QWebEnginePagePrivate (adapter) instance to be destroyed,
+ // and then the OS (observed on Windows) might still send mouse move events to a still
+ // existing popup RWHVQDW instance.
+ if (m_rwhv->adapterClient() && m_rwhv->adapterClient()->isBeingAdopted())
+ return false;
+ handleMouseEvent(static_cast<QMouseEvent *>(event));
+ break;
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ handleKeyEvent(static_cast<QKeyEvent *>(event));
+ break;
+ case QEvent::Wheel:
+ m_rwhv->handleWheelEvent(static_cast<QWheelEvent *>(event));
+ break;
+ case QEvent::TouchBegin:
+ m_rwhv->Focus();
+ Q_FALLTHROUGH();
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ case QEvent::TouchCancel:
+ handleTouchEvent(static_cast<QTouchEvent *>(event));
+ break;
+#if QT_CONFIG(tabletevent)
+ case QEvent::TabletPress:
+ m_rwhv->Focus();
+ Q_FALLTHROUGH();
+ case QEvent::TabletRelease:
+ case QEvent::TabletMove:
+ handleTabletEvent(static_cast<QTabletEvent *>(event));
+ break;
+#endif
+#if QT_CONFIG(gestures)
+ case QEvent::NativeGesture:
+ handleGestureEvent(static_cast<QNativeGestureEvent *>(event));
+ break;
+#endif
+ case QEvent::HoverMove:
+ handleHoverEvent(static_cast<QHoverEvent *>(event));
+ break;
+ case QEvent::FocusIn:
+ case QEvent::FocusOut:
+ handleFocusEvent(static_cast<QFocusEvent *>(event));
+ break;
+ case QEvent::InputMethod:
+ handleInputMethodEvent(static_cast<QInputMethodEvent *>(event));
+ break;
+ case QEvent::InputMethodQuery:
+ handleInputMethodQueryEvent(static_cast<QInputMethodQueryEvent *>(event));
+ break;
+ case QEvent::Leave:
+#ifdef Q_OS_WIN
+ if (m_mouseButtonPressed > 0)
+ return false;
+#endif
+ case QEvent::HoverLeave:
+ m_rwhv->host()->ForwardMouseEvent(WebEventFactory::toWebMouseEvent(event));
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+QVariant RenderWidgetHostViewQtDelegateClient::inputMethodQuery(Qt::InputMethodQuery query)
+{
+ switch (query) {
+ case Qt::ImEnabled: {
+ ui::TextInputType type = m_rwhv->getTextInputType();
+ bool editorVisible = type != ui::TEXT_INPUT_TYPE_NONE;
+ // IME manager should disable composition on input fields with ImhHiddenText hint if
+ // supported
+ if (m_imeHasHiddenTextCapability)
+ return QVariant(editorVisible);
+
+ bool passwordInput = type == ui::TEXT_INPUT_TYPE_PASSWORD;
+ return QVariant(editorVisible && !passwordInput);
+ }
+ case Qt::ImFont:
+ // TODO: Implement this
+ return QVariant();
+ case Qt::ImCursorRectangle: {
+ if (m_rwhv->GetTextInputManager()) {
+ if (auto *region = m_rwhv->GetTextInputManager()->GetSelectionRegion()) {
+ if (region->focus.GetHeight() > 0) {
+ gfx::Rect caretRect =
+ gfx::RectBetweenSelectionBounds(region->anchor, region->focus);
+ if (caretRect.width() == 0)
+ caretRect.set_width(1); // IME API on Windows expects a width > 0
+ return toQt(caretRect);
+ }
+ }
+ }
+ return QVariant();
+ }
+ case Qt::ImCursorPosition:
+ return m_cursorPosition;
+ case Qt::ImAnchorPosition:
+ return m_rwhv->GetSelectedText().empty() ? m_cursorPosition
+ : m_anchorPositionWithinSelection;
+ case Qt::ImSurroundingText:
+ return m_surroundingText;
+ case Qt::ImCurrentSelection:
+ return toQt(m_rwhv->GetSelectedText());
+ case Qt::ImMaximumTextLength:
+ // TODO: Implement this
+ return QVariant(); // No limit.
+ case Qt::ImHints:
+#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
+ return int(toQtInputMethodHints(m_rwhv->getTextInputType()) | Qt::ImhNoPredictiveText
+ | Qt::ImhNoTextHandles | Qt::ImhNoEditMenu);
+#else
+ return int(toQtInputMethodHints(m_rwhv->getTextInputType()) | Qt::ImhNoPredictiveText);
+#endif
+ default:
+ return QVariant();
+ }
+}
+
+void RenderWidgetHostViewQtDelegateClient::closePopup()
+{
+ // We notify the popup to be closed by telling it that it lost focus. WebKit does the rest
+ // (hiding the widget and automatic memory cleanup via
+ // RenderWidget::CloseWidgetSoon() -> RenderWidgetHostImpl::ShutdownAndDestroyWidget(true).
+ m_rwhv->host()->SetActive(false);
+ m_rwhv->host()->LostFocus();
+}
+
+template<class T>
+void RenderWidgetHostViewQtDelegateClient::handlePointerEvent(T *event)
+{
+ // Currently WebMouseEvent is a subclass of WebPointerProperties, so basically
+ // tablet events are mouse events with extra properties.
+ blink::WebMouseEvent webEvent = WebEventFactory::toWebMouseEvent(event);
+ if ((webEvent.GetType() == blink::WebInputEvent::kMouseDown
+ || webEvent.GetType() == blink::WebInputEvent::kMouseUp)
+ && webEvent.button == blink::WebMouseEvent::Button::kNoButton) {
+ // Blink can only handle the 3 main mouse-buttons and may assert when processing mouse-down
+ // for no button.
+ LOG(INFO) << "Unhandled mouse button";
+ return;
+ }
+
+ if (webEvent.GetType() == blink::WebInputEvent::kMouseDown) {
+ if (event->button() != m_clickHelper.lastPressButton
+ || (event->timestamp() - m_clickHelper.lastPressTimestamp
+ > static_cast<ulong>(qGuiApp->styleHints()->mouseDoubleClickInterval()))
+ || (event->pos() - m_clickHelper.lastPressPosition).manhattanLength()
+ > qGuiApp->styleHints()->startDragDistance()
+ || m_clickHelper.clickCounter >= 3)
+ m_clickHelper.clickCounter = 0;
+
+ m_clickHelper.lastPressTimestamp = event->timestamp();
+ webEvent.click_count = ++m_clickHelper.clickCounter;
+ m_clickHelper.lastPressButton = event->button();
+ m_clickHelper.lastPressPosition = QPointF(event->pos()).toPoint();
+ }
+
+ webEvent.movement_x = event->globalX() - m_previousMousePosition.x();
+ webEvent.movement_y = event->globalY() - m_previousMousePosition.y();
+
+ if (m_rwhv->IsMouseLocked())
+ QCursor::setPos(m_previousMousePosition);
+ else
+ m_previousMousePosition = event->globalPos();
+
+ if (m_imeInProgress && webEvent.GetType() == blink::WebInputEvent::kMouseDown) {
+ m_imeInProgress = false;
+ // Tell input method to commit the pre-edit string entered so far, and finish the
+ // composition operation.
+#ifdef Q_OS_WIN
+ // Yes the function name is counter-intuitive, but commit isn't actually implemented
+ // by the Windows QPA, and reset does exactly what is necessary in this case.
+ qApp->inputMethod()->reset();
+#else
+ qApp->inputMethod()->commit();
+#endif
+ }
+
+ m_rwhv->host()->ForwardMouseEvent(webEvent);
+}
+
+void RenderWidgetHostViewQtDelegateClient::handleMouseEvent(QMouseEvent *event)
+{
+ if (event->type() == QEvent::MouseButtonPress)
+ m_mouseButtonPressed++;
+ if (event->type() == QEvent::MouseButtonRelease)
+ m_mouseButtonPressed--;
+
+ // Don't forward mouse events synthesized by the system, which are caused by genuine touch
+ // events. Chromium would then process for e.g. a mouse click handler twice, once due to the
+ // system synthesized mouse event, and another time due to a touch-to-gesture-to-mouse
+ // transformation done by Chromium.
+ if (event->source() == Qt::MouseEventSynthesizedBySystem)
+ return;
+ handlePointerEvent<QMouseEvent>(event);
+}
+
+void RenderWidgetHostViewQtDelegateClient::handleKeyEvent(QKeyEvent *event)
+{
+ if (m_rwhv->IsMouseLocked() && event->key() == Qt::Key_Escape
+ && event->type() == QEvent::KeyRelease)
+ m_rwhv->UnlockMouse();
+
+ if (m_receivedEmptyImeEvent) {
+ // IME composition was not finished with a valid commit string.
+ // We're getting the composition result in a key event.
+ if (event->key() != 0) {
+ // The key event is not a result of an IME composition. Cancel IME.
+ m_rwhv->host()->ImeCancelComposition();
+ m_receivedEmptyImeEvent = false;
+ } else {
+ if (event->type() == QEvent::KeyRelease) {
+ m_rwhv->host()->ImeCommitText(toString16(event->text()),
+ std::vector<ui::ImeTextSpan>(),
+ gfx::Range::InvalidRange(), 0);
+ m_receivedEmptyImeEvent = false;
+ m_imeInProgress = false;
+ }
+ return;
+ }
+ }
+
+ // Ignore autorepeating KeyRelease events so that the generated web events
+ // conform to the spec, which requires autorepeat to result in a sequence of
+ // keypress events and only one final keyup event:
+ // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Auto-repeat_handling
+ // https://w3c.github.io/uievents/#dom-keyboardevent-repeat
+ if (event->type() == QEvent::KeyRelease && event->isAutoRepeat())
+ return;
+
+ content::NativeWebKeyboardEvent webEvent = WebEventFactory::toWebKeyboardEvent(event);
+ if (webEvent.GetType() == blink::WebInputEvent::kRawKeyDown && !m_editCommand.empty()) {
+ ui::LatencyInfo latency;
+ latency.set_source_event_type(ui::SourceEventType::KEY_PRESS);
+ content::EditCommands commands;
+ commands.emplace_back(m_editCommand, "");
+ m_editCommand.clear();
+ m_rwhv->host()->ForwardKeyboardEventWithCommands(webEvent, latency, &commands, nullptr);
+ return;
+ }
+
+ bool keyDownTextInsertion =
+ webEvent.GetType() == blink::WebInputEvent::kRawKeyDown && webEvent.text[0];
+ webEvent.skip_in_browser = keyDownTextInsertion;
+ m_rwhv->host()->ForwardKeyboardEvent(webEvent);
+
+ if (keyDownTextInsertion) {
+ // Blink won't consume the RawKeyDown, but rather the Char event in this case.
+ // The RawKeyDown is skipped on the way back (see above).
+ // The same os_event will be set on both NativeWebKeyboardEvents.
+ webEvent.skip_in_browser = false;
+ webEvent.SetType(blink::WebInputEvent::kChar);
+ m_rwhv->host()->ForwardKeyboardEvent(webEvent);
+ }
+}
+
+void RenderWidgetHostViewQtDelegateClient::handleTouchEvent(QTouchEvent *event)
+{
+ // On macOS instead of handling touch events, we use the OS provided QNativeGestureEvents.
+#ifdef Q_OS_MACOS
+ if (event->spontaneous()) {
+ return;
+ } else {
+ VLOG(1) << "Sending simulated touch events to Chromium does not work properly on macOS. "
+ "Consider using QNativeGestureEvents or QMouseEvents.";
+ }
+#endif
+
+ // Chromium expects the touch event timestamps to be comparable to base::TimeTicks::Now().
+ // Most importantly we also have to preserve the relative time distance between events.
+ // Calculate a delta between event timestamps and Now() on the first received event, and
+ // apply this delta to all successive events. This delta is most likely smaller than it
+ // should by calculating it here but this will hopefully cause less than one frame of delay.
+ base::TimeTicks eventTimestamp =
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(event->timestamp());
+ static base::TimeDelta eventsToNowDelta = base::TimeTicks::Now() - eventTimestamp;
+ eventTimestamp += eventsToNowDelta;
+
+ QList<QTouchEvent::TouchPoint> touchPoints = mapTouchPointIds(event->touchPoints());
+ // Make sure that ACTION_POINTER_DOWN is delivered before ACTION_MOVE,
+ // and ACTION_MOVE before ACTION_POINTER_UP.
+ std::sort(touchPoints.begin(), touchPoints.end(), compareTouchPoints);
+
+ // Check first if the touch event should be routed to the selectionController
+ if (!touchPoints.isEmpty()) {
+ ui::MotionEvent::Action action;
+ switch (touchPoints[0].state()) {
+ case Qt::TouchPointPressed:
+ action = ui::MotionEvent::Action::DOWN;
+ break;
+ case Qt::TouchPointMoved:
+ action = ui::MotionEvent::Action::MOVE;
+ break;
+ case Qt::TouchPointReleased:
+ action = ui::MotionEvent::Action::UP;
+ break;
+ default:
+ action = ui::MotionEvent::Action::NONE;
+ break;
+ }
+
+ MotionEventQt motionEvent(touchPoints, eventTimestamp, action, event->modifiers(), 0);
+ if (m_rwhv->getTouchSelectionController()->WillHandleTouchEvent(motionEvent)) {
+ m_previousTouchPoints = touchPoints;
+ event->accept();
+ return;
+ }
+ } else {
+ // An empty touchPoints always corresponds to a TouchCancel event.
+ // We can't forward touch cancellations without a previously processed touch event,
+ // as Chromium expects the previous touchPoints for Action::CANCEL.
+ // If both are empty that means the TouchCancel was sent without an ongoing touch,
+ // so there's nothing to cancel anyway.
+ touchPoints = m_previousTouchPoints;
+ if (touchPoints.isEmpty())
+ return;
+
+ MotionEventQt cancelEvent(touchPoints, eventTimestamp, ui::MotionEvent::Action::CANCEL,
+ event->modifiers());
+ if (m_rwhv->getTouchSelectionController()->WillHandleTouchEvent(cancelEvent)) {
+ m_previousTouchPoints.clear();
+ event->accept();
+ return;
+ }
+ }
+
+ switch (event->type()) {
+ case QEvent::TouchBegin:
+ m_sendMotionActionDown = true;
+ m_touchMotionStarted = true;
+ m_rwhv->getTouchSelectionControllerClient()->onTouchDown();
+ break;
+ case QEvent::TouchUpdate:
+ m_touchMotionStarted = true;
+ break;
+ case QEvent::TouchCancel: {
+ // Only process TouchCancel events received following a TouchBegin or TouchUpdate event
+ if (m_touchMotionStarted) {
+ MotionEventQt cancelEvent(touchPoints, eventTimestamp, ui::MotionEvent::Action::CANCEL,
+ event->modifiers());
+ m_rwhv->processMotionEvent(cancelEvent);
+ }
+
+ clearPreviousTouchMotionState();
+ return;
+ }
+ case QEvent::TouchEnd:
+ clearPreviousTouchMotionState();
+ m_rwhv->getTouchSelectionControllerClient()->onTouchUp();
+ break;
+ default:
+ break;
+ }
+
+ if (m_imeInProgress && event->type() == QEvent::TouchBegin) {
+ m_imeInProgress = false;
+ // Tell input method to commit the pre-edit string entered so far, and finish the
+ // composition operation.
+#ifdef Q_OS_WIN
+ // Yes the function name is counter-intuitive, but commit isn't actually implemented
+ // by the Windows QPA, and reset does exactly what is necessary in this case.
+ qApp->inputMethod()->reset();
+#else
+ qApp->inputMethod()->commit();
+#endif
+ }
+
+ for (int i = 0; i < touchPoints.size(); ++i) {
+ ui::MotionEvent::Action action;
+ switch (touchPoints[i].state()) {
+ case Qt::TouchPointPressed:
+ if (m_sendMotionActionDown) {
+ action = ui::MotionEvent::Action::DOWN;
+ m_sendMotionActionDown = false;
+ } else {
+ action = ui::MotionEvent::Action::POINTER_DOWN;
+ }
+ break;
+ case Qt::TouchPointMoved:
+ action = ui::MotionEvent::Action::MOVE;
+ break;
+ case Qt::TouchPointReleased:
+ action = touchPoints.size() > 1 ? ui::MotionEvent::Action::POINTER_UP
+ : ui::MotionEvent::Action::UP;
+ break;
+ default:
+ // Ignore Qt::TouchPointStationary
+ continue;
+ }
+
+ MotionEventQt motionEvent(touchPoints, eventTimestamp, action, event->modifiers(), i);
+ m_rwhv->processMotionEvent(motionEvent);
+ }
+
+ m_previousTouchPoints = touchPoints;
+}
+
+#if QT_CONFIG(tabletevent)
+void RenderWidgetHostViewQtDelegateClient::handleTabletEvent(QTabletEvent *event)
+{
+ handlePointerEvent<QTabletEvent>(event);
+}
+#endif
+
+#if QT_CONFIG(gestures)
+void RenderWidgetHostViewQtDelegateClient::handleGestureEvent(QNativeGestureEvent *event)
+{
+ const Qt::NativeGestureType type = event->gestureType();
+ // These are the only supported gestures by Chromium so far.
+ if (type == Qt::ZoomNativeGesture || type == Qt::SmartZoomNativeGesture) {
+ m_rwhv->host()->ForwardGestureEvent(WebEventFactory::toWebGestureEvent(event));
+ }
+}
+#endif
+
+void RenderWidgetHostViewQtDelegateClient::handleHoverEvent(QHoverEvent *event)
+{
+ m_rwhv->host()->ForwardMouseEvent(WebEventFactory::toWebMouseEvent(event));
+}
+
+void RenderWidgetHostViewQtDelegateClient::handleFocusEvent(QFocusEvent *event)
+{
+ if (event->gotFocus()) {
+ m_rwhv->host()->GotFocus();
+ m_rwhv->host()->SetActive(true);
+ content::RenderViewHostImpl *rvh = content::RenderViewHostImpl::From(m_rwhv->host());
+ Q_ASSERT(rvh);
+ if (event->reason() == Qt::TabFocusReason)
+ rvh->SetInitialFocus(false);
+ else if (event->reason() == Qt::BacktabFocusReason)
+ rvh->SetInitialFocus(true);
+ event->accept();
+
+ m_rwhv->adapterClient()->webContentsAdapter()->handlePendingMouseLockPermission();
+ } else if (event->lostFocus()) {
+ m_rwhv->host()->SetActive(false);
+ m_rwhv->host()->LostFocus();
+ event->accept();
+ }
+}
+
+void RenderWidgetHostViewQtDelegateClient::handleInputMethodEvent(QInputMethodEvent *event)
+{
+ m_rwhv->resetInputManagerState();
+
+ if (!m_rwhv->host())
+ return;
+
+ QString commitString = event->commitString();
+ QString preeditString = event->preeditString();
+
+ int cursorPositionInPreeditString = -1;
+ gfx::Range selectionRange = gfx::Range::InvalidRange();
+
+ const QList<QInputMethodEvent::Attribute> &attributes = event->attributes();
+ std::vector<ui::ImeTextSpan> underlines;
+ bool hasSelection = false;
+
+ for (const auto &attribute : attributes) {
+ switch (attribute.type) {
+ case QInputMethodEvent::TextFormat: {
+ if (preeditString.isEmpty())
+ break;
+
+ int start = qMin(attribute.start, (attribute.start + attribute.length));
+ int end = qMax(attribute.start, (attribute.start + attribute.length));
+
+ // Blink does not support negative position values. Adjust start and end positions
+ // to non-negative values.
+ if (start < 0) {
+ start = 0;
+ end = qMax(0, start + end);
+ }
+
+ underlines.push_back(ui::ImeTextSpan(ui::ImeTextSpan::Type::kComposition, start, end,
+ ui::ImeTextSpan::Thickness::kThin,
+ SK_ColorTRANSPARENT));
+
+ QTextCharFormat format = qvariant_cast<QTextFormat>(attribute.value).toCharFormat();
+ if (format.underlineStyle() != QTextCharFormat::NoUnderline)
+ underlines.back().underline_color = toSk(format.underlineColor());
+
+ break;
+ }
+ case QInputMethodEvent::Cursor:
+ // Always set the position of the cursor, even if it's marked invisible by Qt, otherwise
+ // there is no way the user will know which part of the composition string will be
+ // changed, when performing an IME-specific action (like selecting a different word
+ // suggestion).
+ cursorPositionInPreeditString = attribute.start;
+ break;
+ case QInputMethodEvent::Selection:
+ hasSelection = true;
+
+ // Cancel IME composition
+ if (preeditString.isEmpty() && attribute.start + attribute.length == 0) {
+ selectionRange.set_start(0);
+ selectionRange.set_end(0);
+ break;
+ }
+
+ selectionRange.set_start(qMin(attribute.start, (attribute.start + attribute.length)));
+ selectionRange.set_end(qMax(attribute.start, (attribute.start + attribute.length)));
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!selectionRange.IsValid()) {
+ // We did not receive a valid selection range, hence the range is going to mark the
+ // cursor position.
+ int newCursorPosition = (cursorPositionInPreeditString < 0) ? preeditString.length()
+ : cursorPositionInPreeditString;
+ selectionRange.set_start(newCursorPosition);
+ selectionRange.set_end(newCursorPosition);
+ }
+
+ if (hasSelection) {
+ content::mojom::FrameInputHandler *frameInputHandler = m_rwhv->getFrameInputHandler();
+ if (frameInputHandler)
+ frameInputHandler->SetEditableSelectionOffsets(selectionRange.start(),
+ selectionRange.end());
+ }
+
+ int replacementLength = event->replacementLength();
+ gfx::Range replacementRange = gfx::Range::InvalidRange();
+
+ if (replacementLength > 0) {
+ int replacementStart = event->replacementStart() < 0
+ ? m_cursorPosition + event->replacementStart()
+ : event->replacementStart();
+ if (replacementStart >= 0 && replacementStart < m_surroundingText.length())
+ replacementRange = gfx::Range(replacementStart, replacementStart + replacementLength);
+ }
+
+ // There are so-far two known cases, when an empty QInputMethodEvent is received.
+ // First one happens when backspace is used to remove the last character in the pre-edit
+ // string, thus signaling the end of the composition.
+ // The second one happens (on Windows) when a Korean char gets composed, but instead of
+ // the event having a commit string, both strings are empty, and the actual char is received
+ // as a QKeyEvent after the QInputMethodEvent is processed.
+ // In lieu of the second case, we can't simply cancel the composition on an empty event,
+ // and then add the Korean char when QKeyEvent is received, because that leads to text
+ // flickering in the textarea (or any other element).
+ // Instead we postpone the processing of the empty QInputMethodEvent by posting it
+ // to the same focused object, and cancelling the composition on the next event loop tick.
+ if (commitString.isEmpty() && preeditString.isEmpty() && replacementLength == 0) {
+ if (!m_receivedEmptyImeEvent && m_imeInProgress && !hasSelection) {
+ m_receivedEmptyImeEvent = true;
+ QInputMethodEvent *eventCopy = new QInputMethodEvent(*event);
+ QGuiApplication::postEvent(qApp->focusObject(), eventCopy);
+ } else {
+ m_receivedEmptyImeEvent = false;
+ if (m_imeInProgress) {
+ m_imeInProgress = false;
+ m_rwhv->host()->ImeCancelComposition();
+ }
+ }
+
+ return;
+ }
+
+ m_receivedEmptyImeEvent = false;
+
+ // Finish compostion: insert or erase text.
+ if (!commitString.isEmpty() || replacementLength > 0) {
+ m_rwhv->host()->ImeCommitText(toString16(commitString), underlines, replacementRange, 0);
+ m_imeInProgress = false;
+ }
+
+ // Update or start new composition.
+ // Be aware of that, we might get a commit string and a pre-edit string in a single event and
+ // this means a new composition.
+ if (!preeditString.isEmpty()) {
+ m_rwhv->host()->ImeSetComposition(toString16(preeditString), underlines, replacementRange,
+ selectionRange.start(), selectionRange.end());
+ m_imeInProgress = true;
+ }
+}
+
+void RenderWidgetHostViewQtDelegateClient::handleInputMethodQueryEvent(
+ QInputMethodQueryEvent *event)
+{
+ Qt::InputMethodQueries queries = event->queries();
+ for (uint i = 0; i < 32; ++i) {
+ Qt::InputMethodQuery query = (Qt::InputMethodQuery)(int)(queries & (1 << i));
+ if (query) {
+ QVariant v = inputMethodQuery(query);
+ event->setValue(query, v);
+ }
+ }
+ event->accept();
+}
+
+void RenderWidgetHostViewQtDelegateClient::clearPreviousTouchMotionState()
+{
+ m_previousTouchPoints.clear();
+ m_touchMotionStarted = false;
+}
+
+void RenderWidgetHostViewQtDelegateClient::selectionChanged()
+{
+ m_rwhv->resetInputManagerState();
+ ui::TextInputType type = m_rwhv->getTextInputType();
+ content::TextInputManager *text_input_manager = m_rwhv->GetTextInputManager();
+
+ // Handle text selection out of an input field
+ if (type == ui::TEXT_INPUT_TYPE_NONE) {
+ if (m_rwhv->GetSelectedText().empty() && m_emptyPreviousSelection)
+ return;
+
+ // Reset position values to emit selectionChanged signal when clearing text selection
+ // by clicking into an input field. These values are intended to be used by inputMethodQuery
+ // so they are not expected to be valid when selection is out of an input field.
+ m_anchorPositionWithinSelection = -1;
+ m_cursorPositionWithinSelection = -1;
+
+ m_emptyPreviousSelection = m_rwhv->GetSelectedText().empty();
+ m_rwhv->adapterClient()->selectionChanged();
+ return;
+ }
+
+ if (m_rwhv->GetSelectedText().empty()) {
+ // RenderWidgetHostViewQt::OnUpdateTextInputStateCalled() does not update the cursor
+ // position if the selection is cleared because TextInputState changes before the
+ // TextSelection change.
+ Q_ASSERT(text_input_manager->GetTextInputState());
+ m_cursorPosition = text_input_manager->GetTextInputState()->selection_start;
+ m_rwhv->delegate()->inputMethodStateChanged(true /*editorVisible*/,
+ type == ui::TEXT_INPUT_TYPE_PASSWORD);
+
+ m_anchorPositionWithinSelection = m_cursorPosition;
+ m_cursorPositionWithinSelection = m_cursorPosition;
+
+ if (!m_emptyPreviousSelection) {
+ m_emptyPreviousSelection = true;
+ m_rwhv->adapterClient()->selectionChanged();
+ }
+
+ return;
+ }
+
+ const content::TextInputManager::TextSelection *selection =
+ text_input_manager->GetTextSelection();
+ if (!selection)
+ return;
+
+ if (!selection->range().IsValid())
+ return;
+
+ int newAnchorPositionWithinSelection = 0;
+ int newCursorPositionWithinSelection = 0;
+
+ if (text_input_manager->GetSelectionRegion()->anchor.type() == gfx::SelectionBound::RIGHT) {
+ newAnchorPositionWithinSelection = selection->range().GetMax() - selection->offset();
+ newCursorPositionWithinSelection = selection->range().GetMin() - selection->offset();
+ } else {
+ newAnchorPositionWithinSelection = selection->range().GetMin() - selection->offset();
+ newCursorPositionWithinSelection = selection->range().GetMax() - selection->offset();
+ }
+
+ if (m_anchorPositionWithinSelection == newAnchorPositionWithinSelection
+ && m_cursorPositionWithinSelection == newCursorPositionWithinSelection) {
+ return;
+ }
+
+ m_anchorPositionWithinSelection = newAnchorPositionWithinSelection;
+ m_cursorPositionWithinSelection = newCursorPositionWithinSelection;
+
+ if (!selection->selected_text().empty())
+ m_cursorPosition = newCursorPositionWithinSelection;
+
+ m_emptyPreviousSelection = selection->selected_text().empty();
+ m_rwhv->delegate()->inputMethodStateChanged(true /*editorVisible*/,
+ type == ui::TEXT_INPUT_TYPE_PASSWORD);
+ m_rwhv->adapterClient()->selectionChanged();
+}
+
+} // namespace QtWebEngineCore
diff --git a/src/core/render_widget_host_view_qt_delegate_client.h b/src/core/render_widget_host_view_qt_delegate_client.h
new file mode 100644
index 000000000..bb14fe951
--- /dev/null
+++ b/src/core/render_widget_host_view_qt_delegate_client.h
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef RENDER_WIDGET_HOST_VIEW_QT_DELEGATE_CLIENT_H
+#define RENDER_WIDGET_HOST_VIEW_QT_DELEGATE_CLIENT_H
+
+#include "qtwebenginecoreglobal_p.h"
+
+#include <QtGui/QCursor>
+#include <QtGui/QTouchEvent>
+
+QT_BEGIN_NAMESPACE
+class QEvent;
+class QSGNode;
+class QVariant;
+
+class QMouseEvent;
+class QKeyEvent;
+class QTabletEvent;
+class QNativeGestureEvent;
+class QHoverEvent;
+class QFocusEvent;
+class QInputMethodEvent;
+class QInputMethodQueryEvent;
+QT_END_NAMESPACE
+
+namespace QtWebEngineCore {
+
+class RenderWidgetHostViewQt;
+
+struct MultipleMouseClickHelper
+{
+ QPoint lastPressPosition;
+ Qt::MouseButton lastPressButton = Qt::NoButton;
+ int clickCounter = 0;
+ ulong lastPressTimestamp = 0;
+};
+
+class Q_WEBENGINECORE_PRIVATE_EXPORT RenderWidgetHostViewQtDelegateClient
+{
+public:
+ RenderWidgetHostViewQtDelegateClient(RenderWidgetHostViewQt *rwhv);
+
+ QSGNode *updatePaintNode(QSGNode *);
+ void notifyShown();
+ void notifyHidden();
+ void visualPropertiesChanged();
+ bool forwardEvent(QEvent *);
+ QVariant inputMethodQuery(Qt::InputMethodQuery query);
+ void closePopup();
+
+private:
+ friend class RenderWidgetHostViewQt;
+
+ template<class T>
+ void handlePointerEvent(T *);
+ void handleMouseEvent(QMouseEvent *);
+ void handleKeyEvent(QKeyEvent *);
+ void handleTouchEvent(QTouchEvent *);
+#if QT_CONFIG(tabletevent)
+ void handleTabletEvent(QTabletEvent *);
+#endif
+#if QT_CONFIG(gestures)
+ void handleGestureEvent(QNativeGestureEvent *);
+#endif
+ void handleHoverEvent(QHoverEvent *);
+ void handleFocusEvent(QFocusEvent *);
+ void handleInputMethodEvent(QInputMethodEvent *);
+ void handleInputMethodQueryEvent(QInputMethodQueryEvent *);
+
+ QRect viewRectInDips() const { return m_viewRectInDips; }
+ QRect windowRectInDips() const { return m_windowRectInDips; }
+
+ // Mouse
+ void resetPreviousMousePosition() { m_previousMousePosition = QCursor::pos(); }
+
+ // Touch
+ void clearPreviousTouchMotionState();
+
+ // IME
+ void selectionChanged();
+ void setCursorPosition(uint pos) { m_cursorPosition = pos; }
+ void setSurroundingText(const QString &text) { m_surroundingText = text; }
+ bool isPreviousSelectionEmpty() const { return m_emptyPreviousSelection; }
+
+ RenderWidgetHostViewQt *m_rwhv;
+
+ // Mouse
+ bool m_imeHasHiddenTextCapability;
+ uint m_mouseButtonPressed = 0;
+ QPoint m_previousMousePosition;
+ MultipleMouseClickHelper m_clickHelper;
+
+ // Key
+ std::string m_editCommand;
+
+ // Touch
+ QList<QTouchEvent::TouchPoint> m_previousTouchPoints;
+ bool m_touchMotionStarted = false;
+ bool m_sendMotionActionDown = false;
+
+ // IME
+ bool m_receivedEmptyImeEvent = false;
+ bool m_imeInProgress = false;
+ bool m_emptyPreviousSelection = true;
+ uint m_cursorPosition = 0;
+ int m_anchorPositionWithinSelection = -1;
+ int m_cursorPositionWithinSelection = -1;
+ QString m_surroundingText;
+
+ // Geometry of the view in screen DIPs.
+ QRect m_viewRectInDips;
+ // Geometry of the window, including frame, in screen DIPs.
+ QRect m_windowRectInDips;
+};
+
+} // namespace QtWebEngineCore
+
+#endif // RENDER_WIDGET_HOST_VIEW_QT_DELEGATE_CLIENT_H
diff --git a/src/core/type_conversion.cpp b/src/core/type_conversion.cpp
index 403125784..de507f836 100644
--- a/src/core/type_conversion.cpp
+++ b/src/core/type_conversion.cpp
@@ -296,4 +296,40 @@ QList<QSslCertificate> toCertificateChain(net::X509Certificate *certificate)
return chain;
}
+Qt::InputMethodHints toQtInputMethodHints(ui::TextInputType inputType)
+{
+ switch (inputType) {
+ case ui::TEXT_INPUT_TYPE_TEXT:
+ return Qt::ImhPreferLowercase;
+ case ui::TEXT_INPUT_TYPE_SEARCH:
+ return Qt::ImhPreferLowercase | Qt::ImhNoAutoUppercase;
+ case ui::TEXT_INPUT_TYPE_PASSWORD:
+ return Qt::ImhSensitiveData | Qt::ImhNoPredictiveText | Qt::ImhNoAutoUppercase
+ | Qt::ImhHiddenText;
+ case ui::TEXT_INPUT_TYPE_EMAIL:
+ return Qt::ImhEmailCharactersOnly;
+ case ui::TEXT_INPUT_TYPE_NUMBER:
+ return Qt::ImhFormattedNumbersOnly;
+ case ui::TEXT_INPUT_TYPE_TELEPHONE:
+ return Qt::ImhDialableCharactersOnly;
+ case ui::TEXT_INPUT_TYPE_URL:
+ return Qt::ImhUrlCharactersOnly | Qt::ImhNoPredictiveText | Qt::ImhNoAutoUppercase;
+ case ui::TEXT_INPUT_TYPE_DATE_TIME:
+ case ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL:
+ case ui::TEXT_INPUT_TYPE_DATE_TIME_FIELD:
+ return Qt::ImhDate | Qt::ImhTime;
+ case ui::TEXT_INPUT_TYPE_DATE:
+ case ui::TEXT_INPUT_TYPE_MONTH:
+ case ui::TEXT_INPUT_TYPE_WEEK:
+ return Qt::ImhDate;
+ case ui::TEXT_INPUT_TYPE_TIME:
+ return Qt::ImhTime;
+ case ui::TEXT_INPUT_TYPE_TEXT_AREA:
+ case ui::TEXT_INPUT_TYPE_CONTENT_EDITABLE:
+ return Qt::ImhMultiLine | Qt::ImhPreferLowercase;
+ default:
+ return Qt::ImhNone;
+ }
+}
+
} // namespace QtWebEngineCore
diff --git a/src/core/type_conversion.h b/src/core/type_conversion.h
index 2275ae82e..9c193e69e 100644
--- a/src/core/type_conversion.h
+++ b/src/core/type_conversion.h
@@ -59,6 +59,7 @@
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkPixelRef.h"
#include "third_party/skia/include/core/SkMatrix44.h"
+#include "ui/base/ime/text_input_type.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"
#include "url/gurl.h"
@@ -298,6 +299,8 @@ FaviconInfo toFaviconInfo(const content::FaviconURL &);
QList<QSslCertificate> toCertificateChain(net::X509Certificate *certificate);
+Qt::InputMethodHints toQtInputMethodHints(ui::TextInputType inputType);
+
} // namespace QtWebEngineCore
#endif // TYPE_CONVERSION_H
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index ef482ef08..e88c80bc8 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -115,6 +115,7 @@
#include <QtCore/qmimedata.h>
#include <QtCore/qtemporarydir.h>
#include <QtGui/qdrag.h>
+#include <QtGui/QDragEnterEvent>
#include <QtGui/qpixmap.h>
// Can't include headers as qaccessible.h conflicts with Chromium headers.
diff --git a/src/core/web_contents_view_qt.cpp b/src/core/web_contents_view_qt.cpp
index 7f6bfe34f..d1c5012eb 100644
--- a/src/core/web_contents_view_qt.cpp
+++ b/src/core/web_contents_view_qt.cpp
@@ -73,7 +73,7 @@ void WebContentsViewQt::setFactoryClient(WebContentsAdapterClient* client)
// Check if a RWHV was created before the pre-initialization.
if (auto view = static_cast<RenderWidgetHostViewQt *>(m_webContents->GetRenderWidgetHostView())) {
- view->setDelegate(m_factoryClient->CreateRenderWidgetHostViewQtDelegate(view));
+ view->setDelegate(m_factoryClient->CreateRenderWidgetHostViewQtDelegate(view->delegateClient()));
}
}
@@ -94,7 +94,7 @@ content::RenderWidgetHostViewBase* WebContentsViewQt::CreateViewForWidget(conten
RenderWidgetHostViewQt *view = new RenderWidgetHostViewQt(render_widget_host);
if (m_factoryClient) {
- view->setDelegate(m_factoryClient->CreateRenderWidgetHostViewQtDelegate(view));
+ view->setDelegate(m_factoryClient->CreateRenderWidgetHostViewQtDelegate(view->delegateClient()));
if (m_client)
view->setAdapterClient(m_client);
}
@@ -107,7 +107,7 @@ content::RenderWidgetHostViewBase* WebContentsViewQt::CreateViewForChildWidget(c
RenderWidgetHostViewQt *view = new RenderWidgetHostViewQt(render_widget_host);
Q_ASSERT(m_client);
- view->setDelegate(m_client->CreateRenderWidgetHostViewQtDelegateForPopup(view));
+ view->setDelegate(m_client->CreateRenderWidgetHostViewQtDelegateForPopup(view->delegateClient()));
view->setAdapterClient(m_client);
return view;
diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp
index 3bba84b39..f625ab115 100644
--- a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp
+++ b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp
@@ -39,6 +39,8 @@
#include "render_widget_host_view_qt_delegate_quick.h"
+#include "render_widget_host_view_qt_delegate_client.h"
+
#include "qquickwebengineview_p.h"
#include "qquickwebengineview_p_p.h"
#include <QGuiApplication>
diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.h b/src/webengine/render_widget_host_view_qt_delegate_quick.h
index 439616bf3..feea10017 100644
--- a/src/webengine/render_widget_host_view_qt_delegate_quick.h
+++ b/src/webengine/render_widget_host_view_qt_delegate_quick.h
@@ -53,6 +53,8 @@ QT_END_NAMESPACE
namespace QtWebEngineCore {
+class RenderWidgetHostViewQtDelegateClient;
+
class RenderWidgetHostViewQtDelegateQuick : public QQuickItem, public RenderWidgetHostViewQtDelegate
{
Q_OBJECT
diff --git a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp
index 08c471763..0695806a6 100644
--- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp
+++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp
@@ -39,6 +39,8 @@
#include "render_widget_host_view_qt_delegate_widget.h"
+#include "render_widget_host_view_qt_delegate_client.h"
+
#include "qwebenginepage_p.h"
#include "qwebengineview.h"
#include "qwebengineview_p.h"