Index: content/browser/renderer_host/legacy_render_widget_host_win.cc |
diff --git a/content/browser/renderer_host/legacy_render_widget_host_win.cc b/content/browser/renderer_host/legacy_render_widget_host_win.cc |
index 5126ad2bc2da97d1c829ad9f6913ecaeb5de0424..1b814bbd908f94ad3a5b994f1c17eef6f564513a 100644 |
--- a/content/browser/renderer_host/legacy_render_widget_host_win.cc |
+++ b/content/browser/renderer_host/legacy_render_widget_host_win.cc |
@@ -4,8 +4,11 @@ |
#include "content/browser/renderer_host/legacy_render_widget_host_win.h" |
+#include <directmanipulation.h> |
+ |
#include "base/command_line.h" |
#include "base/memory/scoped_ptr.h" |
+#include "base/win/scoped_comptr.h" |
#include "base/win/windows_version.h" |
#include "content/browser/accessibility/browser_accessibility_manager_win.h" |
#include "content/browser/accessibility/browser_accessibility_win.h" |
@@ -22,6 +25,128 @@ |
namespace content { |
+// Windows 10 provides a new API called Direct Manipulation which generates |
+// smooth scroll events via WM_MOUSEWHEEL messages with predictable deltas |
+// on high precision touch pads. This basically requires the application window |
+// to register as a Direct Manipulation consumer. The way mouse wheel messages |
+// are dispatched is |
+// 1. The foreground window is checked to see if it is a Direct Manipulation |
+// consumer. |
+// 2. If it is then Direct Manipulation takes over and sends the following |
+// messages. WM_POINTERACTIVATE, WM_POINTERDOWN and DM_POINTERHITTEST. |
+// 3. It then posts WM_MOUSEWHEEL messages with precision deltas which vary |
+// based on the amount of the scroll. |
+// 4. If the foreground window is not a Direct Manipulation consumer, it |
+// then takes a fallback route where it posts WM_MOUSEWHEEL messages |
+// with precision but varying deltas to the window. There is a also |
+// a slight delay in receiving the first set of mouse wheel messages. |
+// This causes scrolling in Chrome to appear janky and jumpy. |
+// Our approach for addressing this is to do the absolute minimum to |
+// register our window as a Direct Manipulation consumer. This class |
+// provides the necessary functionality to register the legacy HWND as a |
+// Direct Manipulation consumer. We don't rely on Direct manipulation |
+// to do the smooth scrolling in the background thread as documented on |
+// msdn. |
+class DirectManipulationHelper { |
sky
2015/08/11 15:43:12
Would it make more sense to do this in views so th
ananta
2015/08/11 20:52:08
I guess so. I moved the DirectManipulationHelper c
|
+ public: |
+ DirectManipulationHelper() {} |
+ |
+ // This function instantiates Direct Manipulation and creates a viewport for |
+ // the passed in |window|. |
+ // consumer. Most of the code is boiler plate and is based on the sample. |
+ bool Initialize(HWND window) { |
+ DCHECK(::IsWindow(window)); |
+ |
+ HRESULT hr = manager_.CreateInstance(CLSID_DirectManipulationManager, |
+ nullptr, CLSCTX_INPROC_SERVER); |
+ CHECK(SUCCEEDED(hr)); |
sky
2015/08/11 15:43:12
Do these all need to be CHECKs, if any of these fa
ananta
2015/08/11 20:52:08
Added a TODO to remove the CHECKs once this stabli
|
+ |
+ hr = compositor_.CreateInstance(CLSID_DCompManipulationCompositor, |
+ nullptr, CLSCTX_INPROC_SERVER); |
+ CHECK(SUCCEEDED(hr)); |
+ |
+ hr = manager_->GetUpdateManager(IID_PPV_ARGS(update_manager_.Receive())); |
+ CHECK(SUCCEEDED(hr)); |
+ |
+ hr = compositor_->SetUpdateManager(update_manager_.get()); |
+ CHECK(SUCCEEDED(hr)); |
+ |
+ hr = frame_info_.QueryFrom(compositor_.get()); |
+ CHECK(SUCCEEDED(hr)); |
+ |
+ hr = manager_->CreateViewport(frame_info_.get(), window, |
+ IID_PPV_ARGS(view_port_outer_.Receive())); |
+ CHECK(SUCCEEDED(hr)); |
+ |
+ // |
+ // Enable the desired configuration for each viewport. |
+ // |
+ DIRECTMANIPULATION_CONFIGURATION configuration = |
+ DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
+ | DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X |
+ | DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y |
+ | DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA |
+ | DIRECTMANIPULATION_CONFIGURATION_RAILS_X |
+ | DIRECTMANIPULATION_CONFIGURATION_RAILS_Y |
+ | DIRECTMANIPULATION_CONFIGURATION_SCALING |
+ | DIRECTMANIPULATION_CONFIGURATION_SCALING_INERTIA; |
+ |
+ hr = view_port_outer_->ActivateConfiguration(configuration); |
+ CHECK(SUCCEEDED(hr)); |
+ return true; |
sky
2015/08/11 15:43:12
Seems like you always return true.
ananta
2015/08/11 20:52:08
Changed the type to void
|
+ } |
+ |
+ // Sets the bounds of the fake Direct manipulation viewport to match those |
+ // of the legacy window. |
+ void SetBounds(const gfx::Rect& bounds) { |
+ base::win::ScopedComPtr<IDirectManipulationPrimaryContent> |
+ primary_content_outer; |
+ HRESULT hr = view_port_outer_->GetPrimaryContent( |
+ IID_PPV_ARGS(primary_content_outer.Receive())); |
+ CHECK(SUCCEEDED(hr)); |
+ |
+ base::win::ScopedComPtr<IDirectManipulationContent> content_outer; |
+ hr = content_outer.QueryFrom(primary_content_outer.get()); |
+ CHECK(SUCCEEDED(hr)); |
+ |
+ RECT rect = bounds.ToRECT(); |
+ |
+ hr = view_port_outer_->SetViewportRect(&rect); |
+ CHECK(SUCCEEDED(hr)); |
+ |
+ hr = content_outer->SetContentRect(&rect); |
+ CHECK(SUCCEEDED(hr)); |
+ } |
+ |
+ // Registers the passed in |window| as a Direct Manipulation consumer. |
+ void Activate(HWND window) { |
+ DCHECK(::IsWindow(window)); |
+ HRESULT hr = manager_->Activate(window); |
+ CHECK(SUCCEEDED(hr)); |
+ } |
+ |
+ // Passes the WM_MOUSEWHEEL messages to Direct Manipulation. This is for |
+ // logistics purposes. |
+ void HandleMouseWheel(HWND window, UINT message, WPARAM w_param, |
+ LPARAM l_param) { |
+ MSG msg = { window, message, w_param, l_param}; |
+ |
+ HRESULT hr = view_port_outer_->SetContact(DIRECTMANIPULATION_MOUSEFOCUS); |
+ if (SUCCEEDED(hr)) { |
+ BOOL handled = FALSE; |
+ manager_->ProcessInput(&msg, &handled); |
+ view_port_outer_->ReleaseContact(DIRECTMANIPULATION_MOUSEFOCUS); |
+ } |
+ } |
+ |
+ private: |
+ base::win::ScopedComPtr<IDirectManipulationManager2> manager_; |
+ base::win::ScopedComPtr<IDirectManipulationCompositor> compositor_; |
+ base::win::ScopedComPtr<IDirectManipulationUpdateManager> update_manager_; |
+ base::win::ScopedComPtr<IDirectManipulationFrameInfoProvider> frame_info_; |
+ base::win::ScopedComPtr<IDirectManipulationViewport2> view_port_outer_; |
+}; |
sky
2015/08/11 15:43:12
DISALLOW...
ananta
2015/08/11 20:52:08
Done.
|
+ |
// A custom MSAA object id used to determine if a screen reader or some |
// other client is listening on MSAA events - if so, we enable full web |
// accessibility support. |
@@ -75,6 +200,8 @@ HWND LegacyRenderWidgetHostHWND::GetParent() { |
void LegacyRenderWidgetHostHWND::Show() { |
::ShowWindow(hwnd(), SW_SHOW); |
+ if (direct_manipulation_helper_) |
+ direct_manipulation_helper_->Activate(hwnd()); |
} |
void LegacyRenderWidgetHostHWND::Hide() { |
@@ -86,6 +213,8 @@ void LegacyRenderWidgetHostHWND::SetBounds(const gfx::Rect& bounds) { |
::SetWindowPos(hwnd(), NULL, bounds_in_pixel.x(), bounds_in_pixel.y(), |
bounds_in_pixel.width(), bounds_in_pixel.height(), |
SWP_NOREDRAW); |
+ if (direct_manipulation_helper_) |
+ direct_manipulation_helper_->SetBounds(bounds_in_pixel); |
} |
void LegacyRenderWidgetHostHWND::OnFinalMessage(HWND hwnd) { |
@@ -126,6 +255,11 @@ bool LegacyRenderWidgetHostHWND::Init() { |
CHILDID_SELF); |
} |
+ if (base::win::GetVersion() >= base::win::VERSION_WIN10) { |
+ direct_manipulation_helper_.reset(new DirectManipulationHelper); |
+ direct_manipulation_helper_->Initialize(hwnd()); |
+ } |
+ |
return !!SUCCEEDED(hr); |
} |
@@ -242,6 +376,12 @@ LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message, |
handled = TRUE; |
} |
} |
+ |
+ if (direct_manipulation_helper_ && |
+ (message == WM_MOUSEWHEEL || message == WM_MOUSEHWHEEL)) { |
+ direct_manipulation_helper_->HandleMouseWheel(hwnd(), message, w_param, |
+ l_param); |
+ } |
return ret; |
} |