diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index f1cb7d027f74..0d6e72303303 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -582,6 +582,11 @@ value: @IS_NOT_NIGHTLY_BUILD@ mirror: always +- name: apz.windows.use_direct_manipulation + type: RelaxedAtomicBool + value: false + mirror: always + - name: apz.x_skate_highmem_adjust type: AtomicFloat value: 0.0f diff --git a/widget/windows/DirectManipulationOwner.cpp b/widget/windows/DirectManipulationOwner.cpp new file mode 100644 index 000000000000..4f9746283c44 --- /dev/null +++ b/widget/windows/DirectManipulationOwner.cpp @@ -0,0 +1,292 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DirectManipulationOwner.h" +#include "nsWindow.h" + +// Direct Manipulation is only defined for Win8 and newer. +#if defined(_WIN32_WINNT) +# undef _WIN32_WINNT +# define _WIN32_WINNT _WIN32_WINNT_WIN8 +#endif // defined(_WIN32_WINNT) +#if defined(NTDDI_VERSION) +# undef NTDDI_VERSION +# define NTDDI_VERSION NTDDI_WIN8 +#endif // defined(NTDDI_VERSION) + +#include "directmanipulation.h" + +namespace mozilla { +namespace widget { + +class DManipEventHandler : public IDirectManipulationViewportEventHandler, + public IDirectManipulationInteractionEventHandler { + public: + friend class DirectManipulationOwner; + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DManipEventHandler) + + STDMETHODIMP QueryInterface(REFIID, void**) override; + + explicit DManipEventHandler(nsWindow* aWindow, + DirectManipulationOwner* aOwner) + : mWindow(aWindow), mOwner(aOwner) {} + + HRESULT STDMETHODCALLTYPE OnViewportStatusChanged( + IDirectManipulationViewport* viewport, DIRECTMANIPULATION_STATUS current, + DIRECTMANIPULATION_STATUS previous) override; + + HRESULT STDMETHODCALLTYPE + OnViewportUpdated(IDirectManipulationViewport* viewport) override; + + HRESULT STDMETHODCALLTYPE + OnContentUpdated(IDirectManipulationViewport* viewport, + IDirectManipulationContent* content) override; + + HRESULT STDMETHODCALLTYPE + OnInteraction(IDirectManipulationViewport2* viewport, + DIRECTMANIPULATION_INTERACTION_TYPE interaction) override; + + private: + virtual ~DManipEventHandler() = default; + + nsWindow* mWindow; + DirectManipulationOwner* mOwner; +}; + +STDMETHODIMP +DManipEventHandler::QueryInterface(REFIID iid, void** ppv) { + const IID IID_IDirectManipulationViewportEventHandler = + __uuidof(IDirectManipulationViewportEventHandler); + const IID IID_IDirectManipulationInteractionEventHandler = + __uuidof(IDirectManipulationInteractionEventHandler); + + if ((IID_IUnknown == iid) || + (IID_IDirectManipulationViewportEventHandler == iid)) { + *ppv = static_cast(this); + AddRef(); + return S_OK; + } + if (IID_IDirectManipulationInteractionEventHandler == iid) { + *ppv = static_cast(this); + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +HRESULT +DManipEventHandler::OnViewportStatusChanged( + IDirectManipulationViewport* viewport, DIRECTMANIPULATION_STATUS current, + DIRECTMANIPULATION_STATUS previous) { + return S_OK; +} + +HRESULT +DManipEventHandler::OnViewportUpdated(IDirectManipulationViewport* viewport) { + return S_OK; +} + +HRESULT +DManipEventHandler::OnContentUpdated(IDirectManipulationViewport* viewport, + IDirectManipulationContent* content) { + return S_OK; +} + +HRESULT +DManipEventHandler::OnInteraction( + IDirectManipulationViewport2* viewport, + DIRECTMANIPULATION_INTERACTION_TYPE interaction) { + return S_OK; +} + +DirectManipulationOwner::DirectManipulationOwner(nsWindow* aWindow) + : mWindow(aWindow) {} + +DirectManipulationOwner::~DirectManipulationOwner() { Destroy(); } + +void DirectManipulationOwner::Init(const LayoutDeviceIntRect& aBounds) { + HRESULT hr = CoCreateInstance( + CLSID_DirectManipulationManager, nullptr, CLSCTX_INPROC_SERVER, + IID_IDirectManipulationManager, getter_AddRefs(mDmManager)); + if (!SUCCEEDED(hr)) { + NS_WARNING("CoCreateInstance(CLSID_DirectManipulationManager failed"); + mDmManager = nullptr; + return; + } + + hr = mDmManager->GetUpdateManager(IID_IDirectManipulationUpdateManager, + getter_AddRefs(mDmUpdateManager)); + if (!SUCCEEDED(hr)) { + NS_WARNING("GetUpdateManager failed"); + mDmManager = nullptr; + mDmUpdateManager = nullptr; + return; + } + + HWND wnd = static_cast(mWindow->GetNativeData(NS_NATIVE_WINDOW)); + + hr = mDmManager->CreateViewport(nullptr, wnd, IID_IDirectManipulationViewport, + getter_AddRefs(mDmViewport)); + if (!SUCCEEDED(hr)) { + NS_WARNING("CreateViewport failed"); + mDmManager = nullptr; + mDmUpdateManager = nullptr; + mDmViewport = nullptr; + return; + } + + 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; + if (StaticPrefs::apz_allow_zooming()) { + configuration |= DIRECTMANIPULATION_CONFIGURATION_SCALING; + } + + hr = mDmViewport->ActivateConfiguration(configuration); + if (!SUCCEEDED(hr)) { + NS_WARNING("ActivateConfiguration failed"); + mDmManager = nullptr; + mDmUpdateManager = nullptr; + mDmViewport = nullptr; + return; + } + + hr = mDmViewport->SetViewportOptions( + DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE); + if (!SUCCEEDED(hr)) { + NS_WARNING("SetViewportOptions failed"); + mDmManager = nullptr; + mDmUpdateManager = nullptr; + mDmViewport = nullptr; + return; + } + + mDmHandler = new DManipEventHandler(mWindow, this); + + hr = mDmViewport->AddEventHandler(wnd, mDmHandler.get(), + &mDmViewportHandlerCookie); + if (!SUCCEEDED(hr)) { + NS_WARNING("AddEventHandler failed"); + mDmManager = nullptr; + mDmUpdateManager = nullptr; + mDmViewport = nullptr; + mDmHandler = nullptr; + return; + } + + RECT rect = {0, 0, aBounds.Width(), aBounds.Height()}; + hr = mDmViewport->SetViewportRect(&rect); + if (!SUCCEEDED(hr)) { + NS_WARNING("SetViewportRect failed"); + mDmManager = nullptr; + mDmUpdateManager = nullptr; + mDmViewport = nullptr; + mDmHandler = nullptr; + return; + } + + hr = mDmManager->Activate(wnd); + if (!SUCCEEDED(hr)) { + NS_WARNING("manager Activate failed"); + mDmManager = nullptr; + mDmUpdateManager = nullptr; + mDmViewport = nullptr; + mDmHandler = nullptr; + return; + } + + hr = mDmViewport->Enable(); + if (!SUCCEEDED(hr)) { + NS_WARNING("mDmViewport->Enable failed"); + mDmManager = nullptr; + mDmUpdateManager = nullptr; + mDmViewport = nullptr; + mDmHandler = nullptr; + return; + } + + hr = mDmUpdateManager->Update(nullptr); + if (!SUCCEEDED(hr)) { + NS_WARNING("mDmUpdateManager->Update failed"); + mDmManager = nullptr; + mDmUpdateManager = nullptr; + mDmViewport = nullptr; + mDmHandler = nullptr; + return; + } +} + +void DirectManipulationOwner::ResizeViewport( + const LayoutDeviceIntRect& aBounds) { + if (mDmViewport) { + RECT rect = {0, 0, aBounds.Width(), aBounds.Height()}; + HRESULT hr = mDmViewport->SetViewportRect(&rect); + if (!SUCCEEDED(hr)) { + NS_WARNING("SetViewportRect failed"); + } + } +} + +void DirectManipulationOwner::Destroy() { + if (mDmHandler) { + mDmHandler->mWindow = nullptr; + mDmHandler->mOwner = nullptr; + } + + HRESULT hr; + if (mDmViewport) { + hr = mDmViewport->Stop(); + if (!SUCCEEDED(hr)) { + NS_WARNING("mDmViewport->Stop() failed"); + } + + hr = mDmViewport->Disable(); + if (!SUCCEEDED(hr)) { + NS_WARNING("mDmViewport->Disable() failed"); + } + + hr = mDmViewport->RemoveEventHandler(mDmViewportHandlerCookie); + if (!SUCCEEDED(hr)) { + NS_WARNING("mDmViewport->RemoveEventHandler() failed"); + } + + hr = mDmViewport->Abandon(); + if (!SUCCEEDED(hr)) { + NS_WARNING("mDmViewport->Abandon() failed"); + } + } + + if (mWindow) { + HWND wnd = static_cast(mWindow->GetNativeData(NS_NATIVE_WINDOW)); + + if (mDmManager) { + hr = mDmManager->Deactivate(wnd); + if (!SUCCEEDED(hr)) { + NS_WARNING("mDmManager->Deactivate() failed"); + } + } + } + + mDmHandler = nullptr; + mDmViewport = nullptr; + mDmUpdateManager = nullptr; + mDmManager = nullptr; + mWindow = nullptr; +} + +void DirectManipulationOwner::SetContact(UINT aContactId) { + if (mDmViewport) { + mDmViewport->SetContact(aContactId); + } +} + +} // namespace widget +} // namespace mozilla diff --git a/widget/windows/DirectManipulationOwner.h b/widget/windows/DirectManipulationOwner.h new file mode 100644 index 000000000000..df928cb79f4f --- /dev/null +++ b/widget/windows/DirectManipulationOwner.h @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DirectManipulationOwner_h__ +#define DirectManipulationOwner_h__ + +#include "Units.h" +#include "WinDef.h" + +class nsWindow; +class IDirectManipulationManager; +class IDirectManipulationUpdateManager; +class IDirectManipulationViewport; + +namespace mozilla { +namespace widget { + +class DManipEventHandler; + +class DirectManipulationOwner { + public: + typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect; + + explicit DirectManipulationOwner(nsWindow* aWindow); + ~DirectManipulationOwner(); + void Init(const LayoutDeviceIntRect& aBounds); + void ResizeViewport(const LayoutDeviceIntRect& aBounds); + void Destroy(); + + void SetContact(UINT aContactId); + + private: + nsWindow* mWindow; + DWORD mDmViewportHandlerCookie; + RefPtr mDmManager; + RefPtr mDmUpdateManager; + RefPtr mDmViewport; + RefPtr mDmHandler; +}; + +} // namespace widget +} // namespace mozilla + +#endif // #ifndef DirectManipulationOwner_h__ diff --git a/widget/windows/moz.build b/widget/windows/moz.build index 53f180f27387..ed6a941f882a 100644 --- a/widget/windows/moz.build +++ b/widget/windows/moz.build @@ -106,6 +106,11 @@ SOURCES += [ 'WinMouseScrollHandler.cpp', ] +# These files redefine the winsdk api version macro and we don't want it to leak to other files. +SOURCES += [ + 'DirectManipulationOwner.cpp', +] + # Needs INITGUID and we don't allow INITGUID in unified sources since bug 970429. SOURCES += [ 'InputDeviceUtils.cpp', diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index c49ca226be9b..7297af9d0098 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -65,6 +65,7 @@ #include "mozilla/MiscEvents.h" #include "mozilla/MouseEvents.h" #include "mozilla/TouchEvents.h" +#include "mozilla/TimeStamp.h" #include "mozilla/ipc/MessageChannel.h" #include @@ -212,6 +213,9 @@ #include "mozilla/Telemetry.h" #include "mozilla/plugins/PluginProcessParent.h" #include "mozilla/webrender/WebRenderAPI.h" +#include "mozilla/layers/IAPZCTreeManager.h" + +#include "DirectManipulationOwner.h" using namespace mozilla; using namespace mozilla::dom; @@ -712,6 +716,47 @@ static bool ShouldCacheTitleBarInfo(nsWindowType aWindowType, !nsUXThemeData::sTitlebarInfoPopulatedAero); } +void nsWindow::RecreateDirectManipulationIfNeeded() { + DestroyDirectManipulation(); + + if (mWindowType != eWindowType_toplevel && mWindowType != eWindowType_popup) { + return; + } + + if (!StaticPrefs::apz_windows_use_direct_manipulation() || + StaticPrefs::apz_windows_force_disable_direct_manipulation()) { + return; + } + + if (!IsWin10OrLater()) { + // Chrome source said the Windows Direct Manipulation implementation had + // important bugs until Windows 10 (although IE on Windows 8.1 seems to use + // Direct Manipulation). + return; + } + + mDmOwner = MakeUnique(this); + + LayoutDeviceIntRect bounds(mBounds.X(), mBounds.Y(), mBounds.Width(), + GetHeight(mBounds.Height())); + mDmOwner->Init(bounds); +} + +void nsWindow::ResizeDirectManipulationViewport() { + if (mDmOwner) { + LayoutDeviceIntRect bounds(mBounds.X(), mBounds.Y(), mBounds.Width(), + GetHeight(mBounds.Height())); + mDmOwner->ResizeViewport(bounds); + } +} + +void nsWindow::DestroyDirectManipulation() { + if (mDmOwner) { + mDmOwner->Destroy(); + mDmOwner.reset(); + } +} + // Create the proper widget nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, const LayoutDeviceIntRect& aRect, @@ -915,6 +960,9 @@ nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, ::PostMessage(mWnd, MOZ_WM_STARTA11Y, 0, 0); } } + + RecreateDirectManipulationIfNeeded(); + return NS_OK; } @@ -932,6 +980,8 @@ void nsWindow::Destroy() { // deleted. nsCOMPtr kungFuDeathGrip(this); + DestroyDirectManipulation(); + /** * On windows the LayerManagerOGL destructor wants the widget to be around for * cleanup. It also would like to have the HWND intact, so we nullptr it here. @@ -1227,6 +1277,7 @@ void nsWindow::SetParent(nsIWidget* aNewParent) { if (mWnd) { // If we have no parent, SetParent should return the desktop. VERIFY(::SetParent(mWnd, nullptr)); + RecreateDirectManipulationIfNeeded(); } } @@ -1241,6 +1292,7 @@ void nsWindow::ReparentNativeWidget(nsIWidget* aNewParent) { NS_ASSERTION(newParent, "Parent widget has a null native window handle"); if (newParent && mWnd) { ::SetParent(mWnd, newParent); + RecreateDirectManipulationIfNeeded(); } } @@ -1818,6 +1870,8 @@ void nsWindow::Move(double aX, double aY) { } SetThemeRegion(); + + ResizeDirectManipulationViewport(); } NotifyRollupGeometryChange(); } @@ -1864,6 +1918,8 @@ void nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) { ChangedDPI(); } SetThemeRegion(); + + ResizeDirectManipulationViewport(); } if (aRepaint) Invalidate(); @@ -1922,6 +1978,8 @@ void nsWindow::Resize(double aX, double aY, double aWidth, double aHeight, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } SetThemeRegion(); + + ResizeDirectManipulationViewport(); } if (aRepaint) Invalidate(); @@ -3502,6 +3560,7 @@ void nsWindow::SetNativeData(uint32_t aDataType, uintptr_t aVal) { ? mWnd : WinUtils::GetTopLevelHWND(mWnd); SetChildStyleAndParent(childHwnd, parentHwnd); + RecreateDirectManipulationIfNeeded(); break; } default: @@ -5611,6 +5670,17 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam, } break; + case DM_POINTERHITTEST: + if (mDmOwner) { + UINT contactId = GET_POINTERID_WPARAM(wParam); + POINTER_INPUT_TYPE pointerType; + if (mPointerEvents.GetPointerType(contactId, &pointerType) && + pointerType == PT_TOUCHPAD) { + mDmOwner->SetContact(contactId); + } + } + break; + case WM_LBUTTONDBLCLK: result = DispatchMouseEvent(eMouseDoubleClick, wParam, lParam, false, MouseButton::eLeft, MOUSE_INPUT_SOURCE()); diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index c7fe11b828a4..eea73ba2074a 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -30,6 +30,7 @@ #include "mozilla/TimeStamp.h" #include "mozilla/webrender/WebRenderTypes.h" #include "mozilla/dom/MouseEventBinding.h" +#include "mozilla/UniquePtr.h" #include "nsMargin.h" #include "nsRegionFwd.h" @@ -62,6 +63,7 @@ namespace widget { class NativeKey; class InProcessWinCompositorWidget; struct MSGResult; +class DirectManipulationOwner; } // namespace widget } // namespace mozilla @@ -564,6 +566,10 @@ class nsWindow final : public nsWindowBase { void CreateCompositor() override; void RequestFxrOutput(); + void RecreateDirectManipulationIfNeeded(); + void ResizeDirectManipulationViewport(); + void DestroyDirectManipulation(); + protected: nsCOMPtr mParent; nsIntSize mLastSize; @@ -728,6 +734,8 @@ class nsWindow final : public nsWindowBase { // When true, used to indicate an async call to RequestFxrOutput to the GPU // process after the Compositor is created bool mRequestFxrOutputPending; + + mozilla::UniquePtr mDmOwner; }; #endif // Window_h__