diff --git a/browser/components/search/test/browser_healthreport.js b/browser/components/search/test/browser_healthreport.js index 58dc9d881ab..0cbe2e88c21 100644 --- a/browser/components/search/test/browser_healthreport.js +++ b/browser/components/search/test/browser_healthreport.js @@ -4,6 +4,7 @@ "use strict"; function test() { + requestLongerTimeout(2); waitForExplicitFinish(); try { diff --git a/browser/devtools/app-manager/app-validator.js b/browser/devtools/app-manager/app-validator.js index 823ecef9ad4..77003382e86 100644 --- a/browser/devtools/app-manager/app-validator.js +++ b/browser/devtools/app-manager/app-validator.js @@ -60,7 +60,7 @@ AppValidator.prototype._fetchManifest = function (manifestURL) { try { manifest = JSON.parse(req.responseText); } catch(e) { - this.error(strings.formatStringFromName("validator.invalidManifestJSON", [manifestURL, e], 2)); + this.error(strings.formatStringFromName("validator.invalidManifestJSON", [e, manifestURL], 2)); } deferred.resolve(manifest); }).bind(this); @@ -109,7 +109,7 @@ AppValidator.prototype.validateManifest = function (manifest) { if (!manifest.icons || Object.keys(manifest.icons).length == 0) { this.warning(strings.GetStringFromName("validator.missIconsManifestProperty")); } else if (!manifest.icons["128"]) { - this.warning(strings.GetStringFromName("validator.missIconForMarketplace")); + this.warning(strings.GetStringFromName("validator.missIconMarketplace")); } } diff --git a/browser/locales/en-US/chrome/browser/devtools/app-manager.properties b/browser/locales/en-US/chrome/browser/devtools/app-manager.properties index 206f08d2079..7c0eced48f8 100644 --- a/browser/locales/en-US/chrome/browser/devtools/app-manager.properties +++ b/browser/locales/en-US/chrome/browser/devtools/app-manager.properties @@ -2,22 +2,32 @@ # 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/. +# LOCALIZATION NOTE (device.deviceSize): %1$S is the device's width, %2$S is +# the device's height, %3$S is the device's pixel density. +# Example: 800x480 (86 DPI). device.deviceSize=Device size: %1$Sx%2$S (%3$S DPI) +# LOCALIZATION NOTE (connection.connectedToDevice, connection.connectTo): +# %1$S is the host name, %2$S is the port number. connection.connectedToDevice=Connected to %1$S connection.connectTo=Connect to %1$S:%2$S project.filePickerTitle=Select a webapp folder - validator.nonExistingFolder=The project folder doesn't exists validator.expectProjectFolder=The project folder ends up being a file validator.wrongManifestFileName=Packaged apps require a manifest file that can only be named 'manifest.webapp' at project root folder validator.invalidManifestURL=Invalid manifest URL '%S' +# LOCALIZATION NOTE (validator.invalidManifestJSON, validator.noAccessManifestURL): +# %1$S is the error message, %2$S is the URI of the manifest. validator.invalidManifestJSON=The webapp manifest isn't a valid JSON file: %1$S at: %2$S validator.noAccessManifestURL=Unable to read manifest file: %1$S at: %2$S +# LOCALIZATION NOTE (validator.invalidHostedManifestURL): %1$S is the URI of +# the manifest, %2$S is the error message. validator.invalidHostedManifestURL=Invalid hosted manifest URL '%1$S': %2$S validator.invalidProjectType=Unknown project type '%S' +# LOCALIZATION NOTE (validator.missNameManifestProperty, validator.missIconsManifestProperty): +# don't translate 'icons' and 'name'. validator.missNameManifestProperty=Missing mandatory 'name' in Manifest. validator.missIconsManifestProperty=Missing 'icons' in Manifest. -validator.missIconForMarketplace=app submission to the Marketplace needs at least an 128 icon +validator.missIconMarketplace=app submission to the Marketplace needs at least a 128px icon validator.invalidAppType=Unknown app type: '%S'. validator.invalidHostedPriviledges=Hosted App can't be type '%S'. validator.noCertifiedSupport='certified' apps are not fully supported on the App manager. diff --git a/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js b/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js index a240072647c..c89f16fac25 100644 --- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js +++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js @@ -649,37 +649,41 @@ function test_info() { let stop = new Date(); - // We round down/up by 1s as file system precision is lower than Date precision - // (no clear specifications about that, but it seems that this can be a little - // over 1 second under ext3 and 2 seconds under FAT) - let startMs = start.getTime() - 2500; - let stopMs = stop.getTime() + 2500; + // We round down/up by 1s as file system precision is lower than + // Date precision (no clear specifications about that, but it seems + // that this can be a little over 1 second under ext3 and 2 seconds + // under FAT). + let SLOPPY_FILE_SYSTEM_ADJUSTMENT = 3000; + let startMs = start.getTime() - SLOPPY_FILE_SYSTEM_ADJUSTMENT; + let stopMs = stop.getTime() + SLOPPY_FILE_SYSTEM_ADJUSTMENT; + info("Testing stat with bounds [ " + startMs + ", " + stopMs +" ]"); (function() { let birth; - if ("winBirthDate" in info) { - birth = info.winBirthDate; - } else if ("macBirthDate" in info) { - birth = info.macBirthDate; + if ("winBirthDate" in stat) { + birth = stat.winBirthDate; + } else if ("macBirthDate" in stat) { + birth = stat.macBirthDate; } else { ok(true, "Skipping birthdate test"); return; } ok(birth.getTime() <= stopMs, - "test_info: file was created before now - " + stop + ", " + birth); - // Note: Previous versions of this test checked whether the file has - // been created after the start of the test. Unfortunately, this sometimes - // failed under Windows, in specific circumstances: if the file has been - // removed at the start of the test and recreated immediately, the Windows - // file system detects this and decides that the file was actually truncated - // rather than recreated, hence that it should keep its previous creation date. - // Debugging hilarity ensues. + "test_info: platformBirthDate is consistent"); + // Note: Previous versions of this test checked whether the file + // has been created after the start of the test. Unfortunately, + // this sometimes failed under Windows, in specific circumstances: + // if the file has been removed at the start of the test and + // recreated immediately, the Windows file system detects this and + // decides that the file was actually truncated rather than + // recreated, hence that it should keep its previous creation + // date. Debugging hilarity ensues. }); let change = stat.lastModificationDate; - ok(change.getTime() >= startMs - && change.getTime() <= stopMs, - "test_info: file has changed between the start of the test and now - " + start + ", " + stop + ", " + change); + info("Testing lastModificationDate: " + change); + ok(change.getTime() >= startMs && change.getTime() <= stopMs, + "test_info: lastModificationDate is consistent"); // Test OS.File.prototype.stat on new file file = OS.File.open(filename); @@ -696,23 +700,20 @@ function test_info() { stop = new Date(); - // We round down/up by 1s as file system precision is lower than Date precision - startMs = start.getTime() - 1000; - stopMs = stop.getTime() + 1000; - - let birth = stat.creationDate; - ok(birth.getTime() <= stopMs, - "test_info: file 2 was created between the start of the test and now - " + start + ", " + stop + ", " + birth); + // Round up/down as above + startMs = start.getTime() - SLOPPY_FILE_SYSTEM_ADJUSTMENT; + stopMs = stop.getTime() + SLOPPY_FILE_SYSTEM_ADJUSTMENT; + info("Testing stat 2 with bounds [ " + startMs + ", " + stopMs +" ]"); let access = stat.lastAccessDate; - ok(access.getTime() >= startMs - && access.getTime() <= stopMs, - "test_info: file 2 was accessed between the start of the test and now - " + start + ", " + stop + ", " + access); + info("Testing lastAccessDate: " + access); + ok(access.getTime() >= startMs && access.getTime() <= stopMs, + "test_info: lastAccessDate is consistent"); change = stat.lastModificationDate; - ok(change.getTime() >= startMs - && change.getTime() <= stopMs, - "test_info: file 2 has changed between the start of the test and now - " + start + ", " + stop + ", " + change); + info("Testing lastModificationDate 2: " + change); + ok(change.getTime() >= startMs && change.getTime() <= stopMs, + "test_info: lastModificationDate 2 is consistent"); // Test OS.File.stat on directory stat = OS.File.stat(OS.File.getCurrentDirectory()); diff --git a/toolkit/modules/Promise.jsm b/toolkit/modules/Promise.jsm index a3bbe144970..c8e9f1940e0 100755 --- a/toolkit/modules/Promise.jsm +++ b/toolkit/modules/Promise.jsm @@ -114,6 +114,10 @@ const N_STATUS = Name("status"); const N_VALUE = Name("value"); const N_HANDLERS = Name("handlers"); +// The following error types are considered programmer errors, which should be +// reported (possibly redundantly) so as to let programmers fix their code. +const ERRORS_TO_REPORT = ["EvalError", "RangeError", "ReferenceError", "TypeError"]; + //////////////////////////////////////////////////////////////////////////////// //// Promise @@ -572,7 +576,27 @@ Handler.prototype = { nextStatus = STATUS_RESOLVED; } } catch (ex) { - // If an exception occurred in the handler, reject the next promise. + + // An exception has occurred in the handler. + + if (ex && typeof ex == "object" && "name" in ex && + ERRORS_TO_REPORT.indexOf(ex.name) != -1) { + + // We suspect that the exception is a programmer error, so we now + // display it using dump(). Note that we do not use Cu.reportError as + // we assume that this is a programming error, so we do not want end + // users to see it. Also, if the programmer handles errors correctly, + // they will either treat the error or log them somewhere. + + dump("A coding exception was thrown in a Promise " + + ((nextStatus == STATUS_RESOLVED) ? "resolution":"rejection") + + " callback.\n"); + dump("Full message: " + ex + "\n"); + dump("See https://developer.mozilla.org/Mozilla/JavaScript_code_modules/Promise.jsm/Promise\n"); + dump("Full stack: " + (("stack" in ex)?ex.stack:"not available") + "\n"); + } + + // Additionally, reject the next promise. nextStatus = STATUS_REJECTED; nextValue = ex; } diff --git a/toolkit/modules/Task.jsm b/toolkit/modules/Task.jsm index 8c4b5bdff48..fa7262a2917 100644 --- a/toolkit/modules/Task.jsm +++ b/toolkit/modules/Task.jsm @@ -97,6 +97,10 @@ const Cr = Components.results; Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js"); +// The following error types are considered programmer errors, which should be +// reported (possibly redundantly) so as to let programmers fix their code. +const ERRORS_TO_REPORT = ["EvalError", "RangeError", "ReferenceError", "TypeError"]; + //////////////////////////////////////////////////////////////////////////////// //// Task @@ -226,6 +230,21 @@ TaskImpl.prototype = { this.deferred.resolve(); } catch (ex) { // The generator function failed with an uncaught exception. + + if (ex && typeof ex == "object" && "name" in ex && + ERRORS_TO_REPORT.indexOf(ex.name) != -1) { + + // We suspect that the exception is a programmer error, so we now + // display it using dump(). Note that we do not use Cu.reportError as + // we assume that this is a programming error, so we do not want end + // users to see it. Also, if the programmer handles errors correctly, + // they will either treat the error or log them somewhere. + + dump("A coding exception was thrown and uncaught in a Task.\n"); + dump("Full message: " + ex + "\n"); + dump("Full stack: " + (("stack" in ex)?ex.stack:"not available") + "\n"); + } + this.deferred.reject(ex); } } diff --git a/widget/nsGUIEvent.h b/widget/nsGUIEvent.h index 591f04aed6e..3147dd95582 100644 --- a/widget/nsGUIEvent.h +++ b/widget/nsGUIEvent.h @@ -1152,7 +1152,8 @@ public: keyCode(0), charCode(0), location(nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD), isChar(0), mKeyNameIndex(mozilla::widget::KEY_NAME_INDEX_Unidentified), - mNativeKeyEvent(nullptr) + mNativeKeyEvent(nullptr), + mUniqueId(0) { } @@ -1171,6 +1172,11 @@ public: mozilla::widget::KeyNameIndex mKeyNameIndex; // OS-specific native event can optionally be preserved void* mNativeKeyEvent; + // Unique id associated with a keydown / keypress event. Used in identifing + // keypress events for removal from async event dispatch queue in metrofx + // after preventDefault is called on keydown events. It's ok if this wraps + // over long periods. + uint32_t mUniqueId; void GetDOMKeyName(nsAString& aKeyName) { @@ -1205,6 +1211,7 @@ public: // Don't copy mNativeKeyEvent because it may be referred after its instance // is destroyed. mNativeKeyEvent = nullptr; + mUniqueId = aEvent.mUniqueId; } }; diff --git a/widget/nsGUIEventIPC.h b/widget/nsGUIEventIPC.h index a27a895c7ae..21eb7742bbe 100644 --- a/widget/nsGUIEventIPC.h +++ b/widget/nsGUIEventIPC.h @@ -261,6 +261,7 @@ struct ParamTraits WriteParam(aMsg, aParam.charCode); WriteParam(aMsg, aParam.isChar); WriteParam(aMsg, aParam.location); + WriteParam(aMsg, aParam.mUniqueId); // An OS-specific native event might be attached in |mNativeKeyEvent|, but // that cannot be copied across process boundaries. } @@ -273,7 +274,8 @@ struct ParamTraits ReadParam(aMsg, aIter, &aResult->keyCode) && ReadParam(aMsg, aIter, &aResult->charCode) && ReadParam(aMsg, aIter, &aResult->isChar) && - ReadParam(aMsg, aIter, &aResult->location)) + ReadParam(aMsg, aIter, &aResult->location) && + ReadParam(aMsg, aIter, &aResult->mUniqueId)) { aResult->mKeyNameIndex = static_cast(keyNameIndex); aResult->mNativeKeyEvent = NULL; diff --git a/widget/windows/KeyboardLayout.cpp b/widget/windows/KeyboardLayout.cpp index 1f3aad16d63..77dd14168df 100644 --- a/widget/windows/KeyboardLayout.cpp +++ b/widget/windows/KeyboardLayout.cpp @@ -41,6 +41,11 @@ namespace mozilla { namespace widget { +// Unique id counter associated with a keydown / keypress events. Used in +// identifing keypress events for removal from async event dispatch queue +// in metrofx after preventDefault is called on keydown events. +static uint32_t sUniqueKeyEventId = 0; + struct DeadKeyEntry { PRUnichar BaseChar; @@ -756,6 +761,9 @@ NativeKey::InitKeyEvent(nsKeyEvent& aKeyEvent, switch (aKeyEvent.message) { case NS_KEY_DOWN: aKeyEvent.keyCode = mDOMKeyCode; + // Unique id for this keydown event and its associated keypress. + sUniqueKeyEventId++; + aKeyEvent.mUniqueId = sUniqueKeyEventId; break; case NS_KEY_UP: aKeyEvent.keyCode = mDOMKeyCode; @@ -768,6 +776,7 @@ NativeKey::InitKeyEvent(nsKeyEvent& aKeyEvent, (mOriginalVirtualKeyCode == VK_MENU && mMsg.message != WM_SYSKEYUP); break; case NS_KEY_PRESS: + aKeyEvent.mUniqueId = sUniqueKeyEventId; break; default: MOZ_CRASH("Invalid event message"); @@ -797,7 +806,7 @@ NativeKey::DispatchKeyEvent(nsKeyEvent& aKeyEvent, aKeyEvent.pluginEvent = static_cast(&pluginEvent); } - return (mWidget->DispatchWindowEvent(&aKeyEvent) || mWidget->Destroyed()); + return (mWidget->DispatchKeyboardEvent(&aKeyEvent) || mWidget->Destroyed()); } bool diff --git a/widget/windows/WinMouseScrollHandler.cpp b/widget/windows/WinMouseScrollHandler.cpp index ee75ffe87f3..b3794fa6362 100644 --- a/widget/windows/WinMouseScrollHandler.cpp +++ b/widget/windows/WinMouseScrollHandler.cpp @@ -147,6 +147,27 @@ MouseScrollHandler::~MouseScrollHandler() delete mSynthesizingEvent; } +/* static */ +bool +MouseScrollHandler::NeedsMessage(UINT aMsg) +{ + switch (aMsg) { + case WM_SETTINGCHANGE: + case WM_MOUSEWHEEL: + case WM_MOUSEHWHEEL: + case WM_HSCROLL: + case WM_VSCROLL: + case MOZ_WM_MOUSEVWHEEL: + case MOZ_WM_MOUSEHWHEEL: + case MOZ_WM_HSCROLL: + case MOZ_WM_VSCROLL: + case WM_KEYDOWN: + case WM_KEYUP: + return true; + } + return false; +} + /* static */ bool MouseScrollHandler::ProcessMessage(nsWindowBase* aWidget, UINT msg, diff --git a/widget/windows/WinMouseScrollHandler.h b/widget/windows/WinMouseScrollHandler.h index 457cceff9a3..28a233cfaed 100644 --- a/widget/windows/WinMouseScrollHandler.h +++ b/widget/windows/WinMouseScrollHandler.h @@ -32,6 +32,7 @@ public: static void Initialize(); static void Shutdown(); + static bool NeedsMessage(UINT aMsg); static bool ProcessMessage(nsWindowBase* aWidget, UINT msg, WPARAM wParam, diff --git a/widget/windows/WinUtils.h b/widget/windows/WinUtils.h index e00f37900c2..1907e8cef77 100644 --- a/widget/windows/WinUtils.h +++ b/widget/windows/WinUtils.h @@ -34,6 +34,24 @@ struct KeyPair; namespace mozilla { namespace widget { +// More complete QS definitions for MsgWaitForMultipleObjects() and +// GetQueueStatus() that include newer win8 specific defines. + +#ifndef QS_RAWINPUT +#define QS_RAWINPUT 0x0400 +#endif + +#ifndef QS_TOUCH +#define QS_TOUCH 0x0800 +#define QS_POINTER 0x1000 +#endif + +#define MOZ_QS_ALLEVENT (QS_KEY | QS_MOUSEMOVE | QS_MOUSEBUTTON | \ + QS_POSTMESSAGE | QS_TIMER | QS_PAINT | \ + QS_SENDMESSAGE | QS_HOTKEY | \ + QS_ALLPOSTMESSAGE | QS_RAWINPUT | \ + QS_TOUCH | QS_POINTER) + class myDownloadObserver MOZ_FINAL : public nsIDownloadObserver { public: diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 8c1bbbbfb2e..ebdcff1aea8 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -3662,6 +3662,13 @@ bool nsWindow::DispatchStandardEvent(uint32_t aMsg) return result; } +bool nsWindow::DispatchKeyboardEvent(nsGUIEvent* event) +{ + nsEventStatus status; + DispatchEvent(event, status); + return ConvertStatus(status); +} + bool nsWindow::DispatchWindowEvent(nsGUIEvent* event) { nsEventStatus status; diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index 594dd0a0e20..4accf35dd3b 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -87,6 +87,7 @@ public: // nsWindowBase virtual void InitEvent(nsGUIEvent& aEvent, nsIntPoint* aPoint = nullptr) MOZ_OVERRIDE; virtual bool DispatchWindowEvent(nsGUIEvent* aEvent) MOZ_OVERRIDE; + virtual bool DispatchKeyboardEvent(nsGUIEvent* aEvent) MOZ_OVERRIDE; virtual nsWindowBase* GetParentWindowBase(bool aIncludeOwner) MOZ_OVERRIDE; virtual bool IsTopLevelWidget() MOZ_OVERRIDE { return mIsTopWidgetWindow; } diff --git a/widget/windows/nsWindowBase.h b/widget/windows/nsWindowBase.h index bbf9344b399..8dc7badfa34 100644 --- a/widget/windows/nsWindowBase.h +++ b/widget/windows/nsWindowBase.h @@ -49,9 +49,16 @@ public: virtual bool DispatchWindowEvent(nsGUIEvent* aEvent) = 0; /* - * Dispatch a plugin event with the message. + * Dispatch a gecko keyboard event for this widget. This + * is called by KeyboardLayout to dispatch gecko events. + * Returns true if it's consumed. Otherwise, false. */ - virtual bool DispatchPluginEvent(const MSG &aMsg) MOZ_FINAL + virtual bool DispatchKeyboardEvent(nsGUIEvent* aEvent) = 0; + + /* + * Default dispatch of a plugin event. + */ + virtual bool DispatchPluginEvent(const MSG &aMsg) { if (!PluginHasFocus()) { return false; diff --git a/widget/windows/winrt/FrameworkView.cpp b/widget/windows/winrt/FrameworkView.cpp index f4878363f27..76e3638f5bb 100644 --- a/widget/windows/winrt/FrameworkView.cpp +++ b/widget/windows/winrt/FrameworkView.cpp @@ -32,12 +32,6 @@ using namespace ABI::Windows::Foundation; using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; -/* - * Due to issues on older platforms with linking the winrt runtime lib we - * can't have ref new winrt variables in the global scope. Everything should - * be encapsulated in a class. See toolkit/library/nsDllMain for the details. - */ - namespace mozilla { namespace widget { namespace winrt { @@ -57,7 +51,6 @@ FrameworkView::FrameworkView(MetroApp* aMetroApp) : mWinVisible(false), mWinActiveState(false) { - mPainting = false; memset(&sKeyboardRect, 0, sizeof(Rect)); sSettingsArray = new nsTArray(); LogFunction(); @@ -84,27 +77,6 @@ FrameworkView::Initialize(ICoreApplicationView* aAppView) HRESULT FrameworkView::Uninitialize() { - LogFunction(); - mShuttingDown = true; - - if (mAutomationProvider) { - ComPtr provider; - mAutomationProvider.As(&provider); - if (provider) { - provider->Disconnect(); - } - } - mAutomationProvider = nullptr; - - mMetroInput = nullptr; - mD2DWindowSurface = nullptr; - delete sSettingsArray; - sSettingsArray = nullptr; - mWidget = nullptr; - mMetroApp = nullptr; - mDispatcher = nullptr; - mWindow = nullptr; - return S_OK; } @@ -114,34 +86,41 @@ FrameworkView::Load(HSTRING aEntryPoint) return S_OK; } +// called by winrt on startup HRESULT FrameworkView::Run() { LogFunction(); - // XPCOM is initialized here. mWidget is also created. - mMetroApp->Initialize(); + // Initialize XPCOM, create mWidget and go! We get a + // callback in MetroAppShell::Run, in which we kick + // off normal browser execution / event dispatching. + mMetroApp->Run(); - ProcessLaunchArguments(); + // Gecko is completely shut down at this point. + Log("Exiting FrameworkView::Run()"); - // Activate the window - mWindow->Activate(); + return S_OK; +} + +HRESULT +FrameworkView::ActivateView() +{ + LogFunction(); UpdateWidgetSizeAndPosition(); - MetroUtils::GetViewState(mViewState); - // Get the metro event dispatcher - HRESULT hr = mWindow->get_Dispatcher(&mDispatcher); - AssertRetHRESULT(hr, hr); + nsIntRegion region(nsIntRect(0, 0, mWindowBounds.width, mWindowBounds.height)); + mWidget->Paint(region); - // Needs mDispatcher + // Activate the window, this kills the splash screen + mWindow->Activate(); + + ProcessLaunchArguments(); AddEventHandlers(); + SetupContracts(); - // Drop into the main metro event loop - mDispatcher->ProcessEvents(ABI::Windows::UI::Core::CoreProcessEventsOption::CoreProcessEventsOption_ProcessUntilQuit); - - Log("Exiting FrameworkView::Run()"); return S_OK; } @@ -165,12 +144,9 @@ void FrameworkView::AddEventHandlers() { NS_ASSERTION(mWindow, "SetWindow must be called before AddEventHandlers!"); NS_ASSERTION(mWidget, "SetWidget must be called before AddEventHAndlers!"); - NS_ASSERTION(mDispatcher, "Must have a valid CoreDispatcher before " - "calling AddEventHAndlers!"); mMetroInput = Make(mWidget.Get(), - mWindow.Get(), - mDispatcher.Get()); + mWindow.Get()); mWindow->add_VisibilityChanged(Callback<__FITypedEventHandler_2_Windows__CUI__CCore__CCoreWindow_Windows__CUI__CCore__CVisibilityChangedEventArgs>( this, &FrameworkView::OnWindowVisibilityChanged).Get(), &mWindowVisibilityChanged); @@ -208,7 +184,25 @@ FrameworkView::AddEventHandlers() { void FrameworkView::ShutdownXPCOM() { - Uninitialize(); + LogFunction(); + mShuttingDown = true; + + if (mAutomationProvider) { + ComPtr provider; + mAutomationProvider.As(&provider); + if (provider) { + provider->Disconnect(); + } + } + mAutomationProvider = nullptr; + + mMetroInput = nullptr; + mD2DWindowSurface = nullptr; + delete sSettingsArray; + sSettingsArray = nullptr; + mWidget = nullptr; + mMetroApp = nullptr; + mWindow = nullptr; } void @@ -289,6 +283,8 @@ FrameworkView::IsVisible() const void FrameworkView::SetDpi(float aDpi) { if (aDpi != mDPI) { + LogFunction(); + mDPI = aDpi; // Often a DPI change implies a window size change. NS_ASSERTION(mWindow, "SetWindow must be called before SetDpi!"); @@ -346,8 +342,6 @@ FrameworkView::OnActivated(ICoreApplicationView* aApplicationView, IActivatedEventArgs* aArgs) { LogFunction(); - // If we're on startup, we want to wait for FrameworkView::Run to run because - // XPCOM is not initialized yet and and we can't use nsICommandLineRunner ApplicationExecutionState state; aArgs->get_PreviousExecutionState(&state); @@ -452,11 +446,6 @@ FrameworkView::OnWindowActivated(ICoreWindow* aSender, IWindowActivatedEventArgs aArgs->get_WindowActivationState(&state); mWinActiveState = !(state == CoreWindowActivationState::CoreWindowActivationState_Deactivated); SendActivationEvent(); - - // Flush out all remaining events so base widget doesn't process other stuff - // earlier which would lead to a white flash of a second at startup. - MetroAppShell::ProcessAllNativeEventsPresent(); - return S_OK; } @@ -465,7 +454,9 @@ FrameworkView::OnLogicalDpiChanged(IInspectable* aSender) { LogFunction(); UpdateLogicalDPI(); - Render(); + if (mWidget) { + mWidget->Invalidate(); + } return S_OK; } diff --git a/widget/windows/winrt/FrameworkView.h b/widget/windows/winrt/FrameworkView.h index 36e3dfbb33e..b3f56cf6f08 100644 --- a/widget/windows/winrt/FrameworkView.h +++ b/widget/windows/winrt/FrameworkView.h @@ -78,10 +78,10 @@ public: STDMETHODIMP Run(); STDMETHODIMP Uninitialize(); + HRESULT ActivateView(); + // Public apis for MetroWidget void ShutdownXPCOM(); - bool Render(); - bool Render(const nsIntRegion& aInvalidRegion); float GetDPI() { return mDPI; } ICoreWindow* GetCoreWindow() { return mWindow.Get(); } void SetWidget(MetroWidget* aWidget); @@ -180,7 +180,6 @@ private: nsIntRect mWindowBounds; // in device-pixel coordinates float mDPI; bool mShuttingDown; - bool mPainting; nsAutoString mActivationURI; nsAutoString mActivationCommandLine; Microsoft::WRL::ComPtr mAutomationProvider; @@ -190,7 +189,6 @@ private: //Microsoft::WRL::ComPtr mWicFactory; Microsoft::WRL::ComPtr mMetroApp; Microsoft::WRL::ComPtr mWindow; - Microsoft::WRL::ComPtr mDispatcher; Microsoft::WRL::ComPtr mWidget; Microsoft::WRL::ComPtr mMetroInput; static bool sKeyboardIsVisible; diff --git a/widget/windows/winrt/FrameworkViewGfx.cpp b/widget/windows/winrt/FrameworkViewGfx.cpp deleted file mode 100644 index 69caa1b1e8c..00000000000 --- a/widget/windows/winrt/FrameworkViewGfx.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; 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 "base/basictypes.h" -#include "FrameworkView.h" -#include "MetroWidget.h" -#include "mozilla/AutoRestore.h" -#include "MetroUtils.h" -#include "nsIWidgetListener.h" - -#include - -using namespace mozilla::gfx; - -namespace mozilla { -namespace widget { -namespace winrt { - -static bool -IsRenderMode(gfxWindowsPlatform::RenderMode aRMode) -{ - return gfxWindowsPlatform::GetPlatform()->GetRenderMode() == aRMode; -} - -bool -FrameworkView::Render() -{ - Rect msrect; - mWindow->get_Bounds(&msrect); - nsIntRegion region(nsIntRect(0, 0, (uint32_t)ceil(msrect.Width), - (uint32_t)ceil(msrect.Height))); - return Render(region); -} - -bool -FrameworkView::Render(const nsIntRegion& aInvalidRegion) -{ - NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread"); - - if (mShuttingDown || mPainting || !mWidget) { - return false; - } - - // If we haven't created the layer manager, then create it now. - // The swap buffer will be resized automatically by the layer manager. - if (!mWidget->mLayerManager) { - (void)mWidget->GetLayerManager(); - if (!mWidget->mLayerManager) { - NS_WARNING("mWidget->GetLayerManager() failed!"); - return false; - } - } - - // If OMTC is not in use, then we only support D2D rendering - if (!mWidget->ShouldUseOffMainThreadCompositing()) { - if (IsRenderMode(gfxWindowsPlatform::RENDER_GDI) || - IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH32) || - IsRenderMode(gfxWindowsPlatform::RENDER_IMAGE_STRETCH24)) { - NS_WARNING("Unsupported render mode, can't draw. Needs to be D2D."); - return false; - } - } - - if (mWidget->GetTransparencyMode() != eTransparencyOpaque) { - NS_WARNING("transparency modes other than eTransparencyOpaque unsupported, can't draw."); - return false; - } - - AutoRestore painting(mPainting); - mPainting = true; - gfxWindowsPlatform::GetPlatform()->UpdateRenderMode(); - mWidget->Paint(aInvalidRegion); - return true; -} - -} } } diff --git a/widget/windows/winrt/MetroApp.cpp b/widget/windows/winrt/MetroApp.cpp index d048fd6f77f..776e72e7045 100644 --- a/widget/windows/winrt/MetroApp.cpp +++ b/widget/windows/winrt/MetroApp.cpp @@ -60,28 +60,12 @@ MetroApp::CreateView(ABI::Windows::ApplicationModel::Core::IFrameworkView **aVie //////////////////////////////////////////////////// // MetroApp impl. -// called after FrameworkView::Run() drops into the event dispatch loop void -MetroApp::Initialize() +MetroApp::Run() { - HRESULT hr; LogThread(); - static bool xpcomInit; - if (!xpcomInit) { - xpcomInit = true; - Log("XPCOM startup initialization began"); - nsresult rv = XRE_metroStartup(true); - Log("XPCOM startup initialization complete"); - if (NS_FAILED(rv)) { - Log("XPCOM startup initialization failed, bailing. rv=%X", rv); - CoreExit(); - return; - } - } - - sFrameworkView->SetupContracts(); - + HRESULT hr; hr = sCoreApp->add_Suspending(Callback<__FIEventHandler_1_Windows__CApplicationModel__CSuspendingEventArgs_t>( this, &MetroApp::OnSuspending).Get(), &mSuspendEvent); AssertHRESULT(hr); @@ -90,7 +74,13 @@ MetroApp::Initialize() this, &MetroApp::OnResuming).Get(), &mResumeEvent); AssertHRESULT(hr); - mozilla::widget::StartAudioSession(); + Log("XPCOM startup initialization began"); + nsresult rv = XRE_metroStartup(true); + Log("XPCOM startup initialization complete"); + if (NS_FAILED(rv)) { + Log("XPCOM startup initialization failed, bailing. rv=%X", rv); + CoreExit(); + } } // Free all xpcom related resources before calling the xre shutdown call. @@ -100,8 +90,6 @@ MetroApp::ShutdownXPCOM() { LogThread(); - mozilla::widget::StopAudioSession(); - if (sCoreApp) { sCoreApp->remove_Suspending(mSuspendEvent); sCoreApp->remove_Resuming(mResumeEvent); diff --git a/widget/windows/winrt/MetroApp.h b/widget/windows/winrt/MetroApp.h index b6fa1dbbc8a..d7f1b1c4960 100644 --- a/widget/windows/winrt/MetroApp.h +++ b/widget/windows/winrt/MetroApp.h @@ -42,7 +42,7 @@ public: // nsIWinMetroUtils tile related async callbacks HRESULT OnAsyncTileCreated(ABI::Windows::Foundation::IAsyncOperation* aOperation, AsyncStatus aStatus); - void Initialize(); + void Run(); void CoreExit(); void ShutdownXPCOM(); diff --git a/widget/windows/winrt/MetroAppShell.cpp b/widget/windows/winrt/MetroAppShell.cpp index d14408ec7ac..81c1d445ba8 100644 --- a/widget/windows/winrt/MetroAppShell.cpp +++ b/widget/windows/winrt/MetroAppShell.cpp @@ -8,10 +8,13 @@ #include "mozilla/widget/AudioSession.h" #include "MetroUtils.h" #include "MetroApp.h" +#include "FrameworkView.h" #include "nsIObserverService.h" #include "nsServiceManagerUtils.h" +#include "mozilla/AutoRestore.h" #include "WinUtils.h" +using namespace mozilla; using namespace mozilla::widget; using namespace mozilla::widget::winrt; using namespace Microsoft::WRL; @@ -19,10 +22,14 @@ using namespace Microsoft::WRL::Wrappers; using namespace ABI::Windows::UI::Core; using namespace ABI::Windows::Foundation; +// ProcessNextNativeEvent message wait timeout, see bug 907410. +#define MSG_WAIT_TIMEOUT 250 + namespace mozilla { namespace widget { namespace winrt { extern ComPtr sMetroApp; +extern ComPtr sFrameworkView; } } } namespace mozilla { @@ -32,6 +39,7 @@ extern UINT sAppShellGeckoMsgId; } } static ComPtr sCoreStatic; +static bool sIsDispatching = false; MetroAppShell::~MetroAppShell() { @@ -101,7 +109,16 @@ MetroAppShell::Run(void) rv = NS_ERROR_NOT_IMPLEMENTED; break; case GeckoProcessType_Default: - // Nothing to do, just return. + mozilla::widget::StartAudioSession(); + sFrameworkView->ActivateView(); + rv = nsBaseAppShell::Run(); + mozilla::widget::StopAudioSession(); + // This calls XRE_metroShutdown() in xre. This will also destroy + // MessagePump. + sMetroApp->ShutdownXPCOM(); + // This will free the real main thread in CoreApplication::Run() + // once winrt cleans up this thread. + sMetroApp->CoreExit(); break; } @@ -128,50 +145,29 @@ ProcessNativeEvents(CoreProcessEventsOption eventOption) } // static -void +bool MetroAppShell::ProcessOneNativeEventIfPresent() { + if (sIsDispatching) { + NS_RUNTIMEABORT("Reentrant call into process events, this is not allowed in Winrt land. Goodbye!"); + } + AutoRestore dispatching(sIsDispatching); ProcessNativeEvents(CoreProcessEventsOption::CoreProcessEventsOption_ProcessOneIfPresent); -} - -// static -void -MetroAppShell::ProcessAllNativeEventsPresent() -{ - ProcessNativeEvents(CoreProcessEventsOption::CoreProcessEventsOption_ProcessAllIfPresent); + return !!HIWORD(::GetQueueStatus(MOZ_QS_ALLEVENT)); } bool MetroAppShell::ProcessNextNativeEvent(bool mayWait) { - MSG msg; - + if (ProcessOneNativeEventIfPresent()) { + return true; + } if (mayWait) { - if (!WinUtils::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { - WinUtils::WaitForMessage(); - } - ProcessOneNativeEventIfPresent(); - return true; + DWORD result = ::MsgWaitForMultipleObjectsEx(0, NULL, MSG_WAIT_TIMEOUT, MOZ_QS_ALLEVENT, + MWMO_INPUTAVAILABLE|MWMO_ALERTABLE); + NS_WARN_IF_FALSE(result != WAIT_FAILED, "Wait failed"); } - - if (WinUtils::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { - ProcessOneNativeEventIfPresent(); - return true; - } - - return false; -} - -// Results from a call to appstartup->quit, which fires a final nsAppExitEvent -// event which calls us here. This is on the metro main thread. We want to -// call xpcom shutdown here, but we need to wait until the runnable that fires -// this is off the stack. See NativeEventCallback below. -NS_IMETHODIMP -MetroAppShell::Exit(void) -{ - LogFunction(); - mExiting = true; - return NS_OK; + return ProcessOneNativeEventIfPresent(); } void @@ -179,22 +175,6 @@ MetroAppShell::NativeCallback() { NS_ASSERTION(NS_IsMainThread(), "Native callbacks must be on the metro main thread"); NativeEventCallback(); - - // Handle shutdown after Exit() is called and unwinds. - if (mExiting) { - // shutdown fires events, don't recurse - static bool sShutdown = false; - if (sShutdown) - return; - sShutdown = true; - if (sMetroApp) { - // This calls XRE_metroShutdown() in xre - sMetroApp->ShutdownXPCOM(); - // This will free the real main thread in CoreApplication::Run() - // once winrt cleans up this thread. - sMetroApp->CoreExit(); - } - } } // static diff --git a/widget/windows/winrt/MetroAppShell.h b/widget/windows/winrt/MetroAppShell.h index 79b0b5239c6..93d5468ead3 100644 --- a/widget/windows/winrt/MetroAppShell.h +++ b/widget/windows/winrt/MetroAppShell.h @@ -15,26 +15,27 @@ class MetroAppShell : public nsBaseAppShell public: NS_DECL_NSIOBSERVER - MetroAppShell() : mEventWnd(NULL), mExiting(false), mPowerRequestCount(0) - {} + MetroAppShell() : + mEventWnd(NULL), + mPowerRequestCount(0) + { + } nsresult Init(); void DoProcessMoreGeckoEvents(); void NativeCallback(); static LRESULT CALLBACK EventWindowProc(HWND, UINT, WPARAM, LPARAM); - static void ProcessAllNativeEventsPresent(); - static void ProcessOneNativeEventIfPresent(); + static bool ProcessOneNativeEventIfPresent(); protected: - HWND mEventWnd; - bool mExiting; - nsAutoHandle mPowerRequest; - ULONG mPowerRequestCount; - NS_IMETHOD Run(); - NS_IMETHOD Exit(); + virtual void ScheduleNativeEventCallback(); virtual bool ProcessNextNativeEvent(bool mayWait); virtual ~MetroAppShell(); + + HWND mEventWnd; + nsAutoHandle mPowerRequest; + ULONG mPowerRequestCount; }; diff --git a/widget/windows/winrt/MetroInput.cpp b/widget/windows/winrt/MetroInput.cpp index 588ffa0790f..69a3fb7bf87 100644 --- a/widget/windows/winrt/MetroInput.cpp +++ b/widget/windows/winrt/MetroInput.cpp @@ -153,12 +153,9 @@ namespace widget { namespace winrt { MetroInput::MetroInput(MetroWidget* aWidget, - UI::Core::ICoreWindow* aWindow, - UI::Core::ICoreDispatcher* aDispatcher) + UI::Core::ICoreWindow* aWindow) : mWidget(aWidget), - mWindow(aWindow), - mDispatcher(aDispatcher), - mTouchEvent(true, NS_TOUCH_MOVE, aWidget) + mWindow(aWindow) { LogFunction(); NS_ASSERTION(aWidget, "Attempted to create MetroInput for null widget!"); @@ -220,6 +217,7 @@ MetroInput::OnEdgeGestureStarted(UI::Input::IEdgeGesture* sender, geckoEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; + // Safe DispatchEventIgnoreStatus(&geckoEvent); return S_OK; } @@ -251,6 +249,7 @@ MetroInput::OnEdgeGestureCanceled(UI::Input::IEdgeGesture* sender, geckoEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; + // Safe DispatchEventIgnoreStatus(&geckoEvent); return S_OK; } @@ -288,6 +287,7 @@ MetroInput::OnEdgeGestureCompleted(UI::Input::IEdgeGesture* sender, geckoEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; } + // Safe DispatchEventIgnoreStatus(&geckoEvent); return S_OK; } @@ -309,43 +309,51 @@ MetroInput::OnPointerNonTouch(UI::Input::IPointerPoint* aPoint) { aPoint->get_Properties(props.GetAddressOf()); props->get_PointerUpdateKind(&pointerUpdateKind); - nsMouseEvent mouseEvent(true, - NS_MOUSE_MOVE, - mWidget.Get(), - nsMouseEvent::eReal, - nsMouseEvent::eNormal); + nsMouseEvent* event = + new nsMouseEvent(true, + NS_MOUSE_MOVE, + mWidget.Get(), + nsMouseEvent::eReal, + nsMouseEvent::eNormal); switch (pointerUpdateKind) { case UI::Input::PointerUpdateKind::PointerUpdateKind_LeftButtonPressed: // We don't bother setting mouseEvent.button because it is already // set to nsMouseEvent::buttonType::eLeftButton whose value is 0. - mouseEvent.message = NS_MOUSE_BUTTON_DOWN; + event->message = NS_MOUSE_BUTTON_DOWN; break; case UI::Input::PointerUpdateKind::PointerUpdateKind_MiddleButtonPressed: - mouseEvent.button = nsMouseEvent::buttonType::eMiddleButton; - mouseEvent.message = NS_MOUSE_BUTTON_DOWN; + event->button = nsMouseEvent::buttonType::eMiddleButton; + event->message = NS_MOUSE_BUTTON_DOWN; break; case UI::Input::PointerUpdateKind::PointerUpdateKind_RightButtonPressed: - mouseEvent.button = nsMouseEvent::buttonType::eRightButton; - mouseEvent.message = NS_MOUSE_BUTTON_DOWN; + event->button = nsMouseEvent::buttonType::eRightButton; + event->message = NS_MOUSE_BUTTON_DOWN; break; case UI::Input::PointerUpdateKind::PointerUpdateKind_LeftButtonReleased: // We don't bother setting mouseEvent.button because it is already // set to nsMouseEvent::buttonType::eLeftButton whose value is 0. - mouseEvent.message = NS_MOUSE_BUTTON_UP; + event->message = NS_MOUSE_BUTTON_UP; break; case UI::Input::PointerUpdateKind::PointerUpdateKind_MiddleButtonReleased: - mouseEvent.button = nsMouseEvent::buttonType::eMiddleButton; - mouseEvent.message = NS_MOUSE_BUTTON_UP; + event->button = nsMouseEvent::buttonType::eMiddleButton; + event->message = NS_MOUSE_BUTTON_UP; break; case UI::Input::PointerUpdateKind::PointerUpdateKind_RightButtonReleased: - mouseEvent.button = nsMouseEvent::buttonType::eRightButton; - mouseEvent.message = NS_MOUSE_BUTTON_UP; + event->button = nsMouseEvent::buttonType::eRightButton; + event->message = NS_MOUSE_BUTTON_UP; break; } - InitGeckoMouseEventFromPointerPoint(mouseEvent, aPoint); - DispatchEventIgnoreStatus(&mouseEvent); - return; + InitGeckoMouseEventFromPointerPoint(event, aPoint); + DispatchAsyncEventIgnoreStatus(event); +} + +void +MetroInput::InitTouchEventTouchList(nsTouchEvent* aEvent) +{ + MOZ_ASSERT(aEvent); + mTouches.Enumerate(&AppendToTouchList, + static_cast(&aEvent->touches)); } // This event is raised when the user pushes the left mouse button, presses a @@ -381,104 +389,40 @@ MetroInput::OnPointerPressed(UI::Core::ICoreWindow* aSender, nsRefPtr touch = CreateDOMTouch(currentPoint.Get()); touch->mChanged = true; mTouches.Put(pointerId, touch); - mTouchEvent.message = NS_TOUCH_START; - // If this is the first touchstart of a touch session, - // dispatch it now so we can see if preventDefault gets called on it. + nsTouchEvent* touchEvent = + new nsTouchEvent(true, NS_TOUCH_START, mWidget.Get()); + if (mTouches.Count() == 1) { - nsEventStatus status; - DispatchPendingTouchEvent(status, true); - mTouchStartDefaultPrevented = (nsEventStatus_eConsumeNoDefault == status); - // If the first touchstart event has preventDefault called on it, then - // we will not perform any default actions associated with any touch - // events for this session, including touchmove events. - // Thus, mTouchStartDefaultPrevented implies mTouchMoveDefaultPrevented. - mTouchMoveDefaultPrevented = mTouchStartDefaultPrevented; - mIsFirstTouchMove = !mTouchStartDefaultPrevented; + // If this is the first touchstart of a touch session reset some + // tracking flags and dispatch the event with a custom callback + // so we can check preventDefault result. + mTouchStartDefaultPrevented = false; + mTouchMoveDefaultPrevented = false; + mIsFirstTouchMove = true; + InitTouchEventTouchList(touchEvent); + DispatchAsyncTouchEventWithCallback(touchEvent, &MetroInput::OnPointerPressedCallback); + } else { + InitTouchEventTouchList(touchEvent); + DispatchAsyncTouchEventIgnoreStatus(touchEvent); } - // If the first touchstart of this touch session had its preventDefault - // called on it, we will not perform any default actions for any of the - // touches in this touch session. if (!mTouchStartDefaultPrevented) { mGestureRecognizer->ProcessDownEvent(currentPoint.Get()); } - return S_OK; } -// This event is raised when the user lifts the left mouse button, lifts a -// pen from the surface, or lifts her/his finger from a touch screen. -HRESULT -MetroInput::OnPointerReleased(UI::Core::ICoreWindow* aSender, - UI::Core::IPointerEventArgs* aArgs) +void +MetroInput::OnPointerPressedCallback() { -#ifdef DEBUG_INPUT - LogFunction(); -#endif - - WRL::ComPtr currentPoint; - WRL::ComPtr device; - Devices::Input::PointerDeviceType deviceType; - - aArgs->get_CurrentPoint(currentPoint.GetAddressOf()); - currentPoint->get_PointerDevice(device.GetAddressOf()); - device->get_PointerDeviceType(&deviceType); - - // For mouse and pen input, simply call our helper function - if (deviceType != - Devices::Input::PointerDeviceType::PointerDeviceType_Touch) { - OnPointerNonTouch(currentPoint.Get()); - mGestureRecognizer->ProcessUpEvent(currentPoint.Get()); - return S_OK; + nsEventStatus status = DeliverNextQueuedTouchEvent(); + mTouchStartDefaultPrevented = (nsEventStatus_eConsumeNoDefault == status); + // If content cancelled the first touchstart don't generate any gesture based + // input - clear the recognizer state without sending any events. + if (mTouchStartDefaultPrevented) { + mGestureRecognizer->CompleteGesture(); } - - // This is touch input. - // Get the touch associated with this touch point. - uint32_t pointerId; - currentPoint->get_PointerId(&pointerId); - nsRefPtr touch = mTouches.Get(pointerId); - - // We are about to dispatch a touchend. Before we do that, we should make - // sure that we don't have a touchmove or touchstart sitting around for this - // point. - if (touch->mChanged) { - DispatchPendingTouchEvent(true); - } - mTouches.Remove(pointerId); - - // touchend events only have a single touch; the touch that has been removed - mTouchEvent.message = NS_TOUCH_END; - mTouchEvent.touches.Clear(); - mTouchEvent.touches.AppendElement(CreateDOMTouch(currentPoint.Get())); - mTouchEvent.time = ::GetMessageTime(); - mModifierKeyState.Update(); - mModifierKeyState.InitInputEvent(mTouchEvent); - - nsEventStatus status; - mWidget->DispatchEvent(&mTouchEvent, status); - if (status != nsEventStatus_eConsumeNoDefault) { - MultiTouchInput inputData(mTouchEvent); - if (MetroWidget::sAPZC) { - status = MetroWidget::sAPZC->ReceiveInputEvent(inputData); - } - } - - // mTouchEvent.message should always be set to NS_TOUCH_MOVE - mTouchEvent.message = NS_TOUCH_MOVE; - - // If the first touchstart of this touch session had its preventDefault - // called on it, we will not perform any default actions for any of the - // touches in this touch session. Note that we don't check - // mTouchMoveDefaultPrevented here. The reason is that, even if - // preventDefault was called on the first touchmove event, we might still - // want to dispatch a click (mousemove, mousedown, mouseup) in response to - // this touch. - if (!mTouchStartDefaultPrevented) { - mGestureRecognizer->ProcessUpEvent(currentPoint.Get()); - } - - return S_OK; } // This event is raised when the user moves the mouse, moves a pen that is @@ -525,42 +469,119 @@ MetroInput::OnPointerMoved(UI::Core::ICoreWindow* aSender, return S_OK; } - // If we're modifying a touch entry that has a pending update, go through - // with the update. + // If we've accumulated a batch of pointer moves and we're now on a new batch + // at a new position send the previous batch. (perf opt) if (touch->mChanged) { - DispatchPendingTouchEvent(true); + nsTouchEvent* touchEvent = + new nsTouchEvent(true, NS_TOUCH_MOVE, mWidget.Get()); + InitTouchEventTouchList(touchEvent); + DispatchAsyncTouchEventIgnoreStatus(touchEvent); } touch = CreateDOMTouch(currentPoint.Get()); touch->mChanged = true; + // replacing old touch point in mTouches map mTouches.Put(pointerId, touch); - // If this is the first touch move of our session, we should dispatch it - // and store our mTouchMoveDefaultPrevented value + nsTouchEvent* touchEvent = + new nsTouchEvent(true, NS_TOUCH_MOVE, mWidget.Get()); + + // If this is the first touch move of our session, we should check the result. + // Note we may lose some touch move data here for the recognizer since we want + // to wait until we have the result of the first touchmove dispatch. For gesture + // based events this shouldn't break anything. if (mIsFirstTouchMove) { - nsEventStatus status; - DispatchPendingTouchEvent(status, true); - mTouchMoveDefaultPrevented = (nsEventStatus_eConsumeNoDefault == status); + InitTouchEventTouchList(touchEvent); + DispatchAsyncTouchEventWithCallback(touchEvent, &MetroInput::OnFirstPointerMoveCallback); mIsFirstTouchMove = false; + } else { + // Only feed move input to the recognizer if the first touchstart and + // subsequent touchmove return results were not eConsumeNoDefault. + if (!mTouchStartDefaultPrevented && !mTouchMoveDefaultPrevented) { + WRL::ComPtr> + pointerPoints; + aArgs->GetIntermediatePoints(pointerPoints.GetAddressOf()); + mGestureRecognizer->ProcessMoveEvents(pointerPoints.Get()); + } } - // We will perform default actions for touchmove events only if - // preventDefault was not called on the first touchmove event and - // preventDefault was not called on the first touchstart event. Checking - // mTouchMoveDefaultPrevented is enough here because it will be set if - // mTouchStartDefaultPrevented is true. - if (!mTouchMoveDefaultPrevented) { - WRL::ComPtr> - pointerPoints; - aArgs->GetIntermediatePoints(pointerPoints.GetAddressOf()); - mGestureRecognizer->ProcessMoveEvents(pointerPoints.Get()); + return S_OK; +} + +void +MetroInput::OnFirstPointerMoveCallback() +{ + nsTouchEvent* event = static_cast(mInputEventQueue.PopFront()); + MOZ_ASSERT(event); + nsEventStatus status; + mWidget->DispatchEvent(event, status); + mTouchMoveDefaultPrevented = (nsEventStatus_eConsumeNoDefault == status); + delete event; +} + +// This event is raised when the user lifts the left mouse button, lifts a +// pen from the surface, or lifts her/his finger from a touch screen. +HRESULT +MetroInput::OnPointerReleased(UI::Core::ICoreWindow* aSender, + UI::Core::IPointerEventArgs* aArgs) +{ +#ifdef DEBUG_INPUT + LogFunction(); +#endif + + WRL::ComPtr currentPoint; + WRL::ComPtr device; + Devices::Input::PointerDeviceType deviceType; + + aArgs->get_CurrentPoint(currentPoint.GetAddressOf()); + currentPoint->get_PointerDevice(device.GetAddressOf()); + device->get_PointerDeviceType(&deviceType); + + // For mouse and pen input, simply call our helper function + if (deviceType != + Devices::Input::PointerDeviceType::PointerDeviceType_Touch) { + OnPointerNonTouch(currentPoint.Get()); + mGestureRecognizer->ProcessUpEvent(currentPoint.Get()); + return S_OK; } + + // This is touch input. + // Get the touch associated with this touch point. + uint32_t pointerId; + currentPoint->get_PointerId(&pointerId); + nsRefPtr touch = mTouches.Get(pointerId); + + // Purge any pending moves for this pointer + if (touch->mChanged) { + nsTouchEvent* touchEvent = + new nsTouchEvent(true, NS_TOUCH_MOVE, mWidget.Get()); + InitTouchEventTouchList(touchEvent); + DispatchAsyncTouchEventIgnoreStatus(touchEvent); + } + + // Remove this touch point from our map. Eventually all touch points are + // removed for this session since we receive released events for every + // point. + mTouches.Remove(pointerId); + + // touchend events only have a single touch; the touch that has been removed + nsTouchEvent* touchEvent = + new nsTouchEvent(true, NS_TOUCH_END, mWidget.Get()); + touchEvent->touches.AppendElement(CreateDOMTouch(currentPoint.Get())); + DispatchAsyncTouchEventIgnoreStatus(touchEvent); + + // If content didn't cancel the first touchstart feed touchend data to the + // recognizer. + if (!mTouchStartDefaultPrevented) { + mGestureRecognizer->ProcessUpEvent(currentPoint.Get()); + } + return S_OK; } void MetroInput::InitGeckoMouseEventFromPointerPoint( - nsMouseEvent& aEvent, + nsMouseEvent* aEvent, UI::Input::IPointerPoint* aPointerPoint) { NS_ASSERTION(aPointerPoint, "InitGeckoMouseEventFromPointerPoint " "called with null PointerPoint!"); @@ -581,19 +602,16 @@ MetroInput::InitGeckoMouseEventFromPointerPoint( props->get_Pressure(&pressure); mGestureRecognizer->CanBeDoubleTap(aPointerPoint, &canBeDoubleTap); - mModifierKeyState.Update(); - mModifierKeyState.InitInputEvent(aEvent); - aEvent.refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position)); - aEvent.time = timestamp; + aEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position)); if (!canBeDoubleTap) { - aEvent.clickCount = 1; + aEvent->clickCount = 1; } else { - aEvent.clickCount = 2; + aEvent->clickCount = 2; } - aEvent.pressure = pressure; + aEvent->pressure = pressure; - MozInputSourceFromDeviceType(deviceType, aEvent.inputSource); + MozInputSourceFromDeviceType(deviceType, aEvent->inputSource); } // This event is raised when a precise pointer moves into the bounding box of @@ -618,13 +636,13 @@ MetroInput::OnPointerEntered(UI::Core::ICoreWindow* aSender, // We only dispatch mouseenter and mouseexit events for mouse and pen input. if (deviceType != Devices::Input::PointerDeviceType::PointerDeviceType_Touch) { - nsMouseEvent mouseEvent(true, - NS_MOUSE_ENTER, - mWidget.Get(), - nsMouseEvent::eReal, - nsMouseEvent::eNormal); - InitGeckoMouseEventFromPointerPoint(mouseEvent, currentPoint.Get()); - DispatchEventIgnoreStatus(&mouseEvent); + nsMouseEvent* event = new nsMouseEvent(true, + NS_MOUSE_ENTER, + mWidget.Get(), + nsMouseEvent::eReal, + nsMouseEvent::eNormal); + InitGeckoMouseEventFromPointerPoint(event, currentPoint.Get()); + DispatchAsyncEventIgnoreStatus(event); } return S_OK; } @@ -651,13 +669,13 @@ MetroInput::OnPointerExited(UI::Core::ICoreWindow* aSender, // We only dispatch mouseenter and mouseexit events for mouse and pen input. if (deviceType != Devices::Input::PointerDeviceType::PointerDeviceType_Touch) { - nsMouseEvent mouseEvent(true, - NS_MOUSE_EXIT, - mWidget.Get(), - nsMouseEvent::eReal, - nsMouseEvent::eNormal); - InitGeckoMouseEventFromPointerPoint(mouseEvent, currentPoint.Get()); - DispatchEventIgnoreStatus(&mouseEvent); + nsMouseEvent* event = new nsMouseEvent(true, + NS_MOUSE_EXIT, + mWidget.Get(), + nsMouseEvent::eReal, + nsMouseEvent::eNormal); + InitGeckoMouseEventFromPointerPoint(event, currentPoint.Get()); + DispatchAsyncEventIgnoreStatus(event); } return S_OK; } @@ -691,33 +709,27 @@ MetroInput::ProcessManipulationDelta( } // Send a gecko event indicating the magnification since the last update. - nsSimpleGestureEvent magEvent(true, - aMagEventType, - mWidget.Get(), 0, 0.0); - magEvent.delta = aDelta.Expansion; - mModifierKeyState.Update(); - mModifierKeyState.InitInputEvent(magEvent); - magEvent.time = ::GetMessageTime(); - magEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; - magEvent.refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(aPosition)); - DispatchEventIgnoreStatus(&magEvent); + nsSimpleGestureEvent* magEvent = + new nsSimpleGestureEvent(true, aMagEventType, mWidget.Get(), 0, 0.0); + + magEvent->delta = aDelta.Expansion; + magEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; + magEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(aPosition)); + DispatchAsyncEventIgnoreStatus(magEvent); // Send a gecko event indicating the rotation since the last update. - nsSimpleGestureEvent rotEvent(true, - aRotEventType, - mWidget.Get(), 0, 0.0); - rotEvent.delta = aDelta.Rotation; - mModifierKeyState.Update(); - mModifierKeyState.InitInputEvent(rotEvent); - rotEvent.time = ::GetMessageTime(); - rotEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; - rotEvent.refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(aPosition)); - if (rotEvent.delta >= 0) { - rotEvent.direction = nsIDOMSimpleGestureEvent::ROTATION_COUNTERCLOCKWISE; + nsSimpleGestureEvent* rotEvent = + new nsSimpleGestureEvent(true, aRotEventType, mWidget.Get(), 0, 0.0); + + rotEvent->delta = aDelta.Rotation; + rotEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; + rotEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(aPosition)); + if (rotEvent->delta >= 0) { + rotEvent->direction = nsIDOMSimpleGestureEvent::ROTATION_COUNTERCLOCKWISE; } else { - rotEvent.direction = nsIDOMSimpleGestureEvent::ROTATION_CLOCKWISE; + rotEvent->direction = nsIDOMSimpleGestureEvent::ROTATION_CLOCKWISE; } - DispatchEventIgnoreStatus(&rotEvent); + DispatchAsyncEventIgnoreStatus(rotEvent); } // This event is raised when a gesture is detected to have started. The @@ -837,33 +849,29 @@ MetroInput::OnManipulationCompleted( } if (isHorizontalSwipe) { - nsSimpleGestureEvent swipeEvent(true, NS_SIMPLE_GESTURE_SWIPE, - mWidget.Get(), 0, 0.0); - swipeEvent.direction = delta.Translation.X > 0 + nsSimpleGestureEvent* swipeEvent = + new nsSimpleGestureEvent(true, NS_SIMPLE_GESTURE_SWIPE, + mWidget.Get(), 0, 0.0); + swipeEvent->direction = delta.Translation.X > 0 ? nsIDOMSimpleGestureEvent::DIRECTION_RIGHT : nsIDOMSimpleGestureEvent::DIRECTION_LEFT; - swipeEvent.delta = delta.Translation.X; - mModifierKeyState.Update(); - mModifierKeyState.InitInputEvent(swipeEvent); - swipeEvent.time = ::GetMessageTime(); - swipeEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; - swipeEvent.refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position)); - DispatchEventIgnoreStatus(&swipeEvent); + swipeEvent->delta = delta.Translation.X; + swipeEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; + swipeEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position)); + DispatchAsyncEventIgnoreStatus(swipeEvent); } if (isVerticalSwipe) { - nsSimpleGestureEvent swipeEvent(true, NS_SIMPLE_GESTURE_SWIPE, - mWidget.Get(), 0, 0.0); - swipeEvent.direction = delta.Translation.Y > 0 + nsSimpleGestureEvent* swipeEvent = + new nsSimpleGestureEvent(true, NS_SIMPLE_GESTURE_SWIPE, + mWidget.Get(), 0, 0.0); + swipeEvent->direction = delta.Translation.Y > 0 ? nsIDOMSimpleGestureEvent::DIRECTION_DOWN : nsIDOMSimpleGestureEvent::DIRECTION_UP; - swipeEvent.delta = delta.Translation.Y; - mModifierKeyState.Update(); - mModifierKeyState.InitInputEvent(swipeEvent); - swipeEvent.time = ::GetMessageTime(); - swipeEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; - swipeEvent.refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position)); - DispatchEventIgnoreStatus(&swipeEvent); + swipeEvent->delta = delta.Translation.Y; + swipeEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; + swipeEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position)); + DispatchAsyncEventIgnoreStatus(swipeEvent); } return S_OK; @@ -927,15 +935,18 @@ MetroInput::HandleDoubleTap(const LayoutDeviceIntPoint& aPoint) #ifdef DEBUG_INPUT LogFunction(); #endif - nsSimpleGestureEvent geckoEvent(true, NS_SIMPLE_GESTURE_TAP, mWidget.Get(), 0, 0.0); - mModifierKeyState.Update(); - mModifierKeyState.InitInputEvent(geckoEvent); - geckoEvent.time = ::GetMessageTime(); - geckoEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; - geckoEvent.refPoint = aPoint; - geckoEvent.clickCount = 2; - geckoEvent.pressure = 1; - DispatchEventIgnoreStatus(&geckoEvent); + nsSimpleGestureEvent* tapEvent = + new nsSimpleGestureEvent(true, + NS_SIMPLE_GESTURE_TAP, + mWidget.Get(), + 0, + 0.0); + + tapEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; + tapEvent->refPoint = aPoint; + tapEvent->clickCount = 2; + tapEvent->pressure = 1; + DispatchAsyncEventIgnoreStatus(tapEvent); } void @@ -945,31 +956,39 @@ MetroInput::HandleSingleTap(const LayoutDeviceIntPoint& aPoint) LogFunction(); #endif - // Set up the mouse event that we'll reuse for mousemove, mousedown, and - // mouseup - nsMouseEvent mouseEvent(true, - NS_MOUSE_MOVE, - mWidget.Get(), - nsMouseEvent::eReal, - nsMouseEvent::eNormal); - mModifierKeyState.Update(); - mModifierKeyState.InitInputEvent(mouseEvent); - mouseEvent.refPoint = aPoint; - mouseEvent.time = ::GetMessageTime(); - mouseEvent.clickCount = 1; - mouseEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; - - // Send the mousemove - DispatchEventIgnoreStatus(&mouseEvent); + // send mousemove + nsMouseEvent* mouseEvent = new nsMouseEvent(true, + NS_MOUSE_MOVE, + mWidget.Get(), + nsMouseEvent::eReal, + nsMouseEvent::eNormal); + mouseEvent->refPoint = aPoint; + mouseEvent->clickCount = 1; + mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; + DispatchAsyncEventIgnoreStatus(mouseEvent); // Send the mousedown - mouseEvent.message = NS_MOUSE_BUTTON_DOWN; - mouseEvent.button = nsMouseEvent::buttonType::eLeftButton; - DispatchEventIgnoreStatus(&mouseEvent); + mouseEvent = new nsMouseEvent(true, + NS_MOUSE_BUTTON_DOWN, + mWidget.Get(), + nsMouseEvent::eReal, + nsMouseEvent::eNormal); + mouseEvent->refPoint = aPoint; + mouseEvent->clickCount = 1; + mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; + mouseEvent->button = nsMouseEvent::buttonType::eLeftButton; + DispatchAsyncEventIgnoreStatus(mouseEvent); - // Send the mouseup - mouseEvent.message = NS_MOUSE_BUTTON_UP; - DispatchEventIgnoreStatus(&mouseEvent); + mouseEvent = new nsMouseEvent(true, + NS_MOUSE_BUTTON_UP, + mWidget.Get(), + nsMouseEvent::eReal, + nsMouseEvent::eNormal); + mouseEvent->refPoint = aPoint; + mouseEvent->clickCount = 1; + mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; + mouseEvent->button = nsMouseEvent::buttonType::eLeftButton; + DispatchAsyncEventIgnoreStatus(mouseEvent); // Send one more mousemove to avoid getting a hover state. // In the Metro environment for any application, a tap does not imply a @@ -978,10 +997,15 @@ MetroInput::HandleSingleTap(const LayoutDeviceIntPoint& aPoint) POINT point; if (GetCursorPos(&point)) { ScreenToClient((HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW), &point); - mouseEvent.refPoint = LayoutDeviceIntPoint(point.x, point.y); - mouseEvent.message = NS_MOUSE_MOVE; - mouseEvent.button = 0; - DispatchEventIgnoreStatus(&mouseEvent); + mouseEvent = new nsMouseEvent(true, + NS_MOUSE_MOVE, + mWidget.Get(), + nsMouseEvent::eReal, + nsMouseEvent::eNormal); + mouseEvent->refPoint = LayoutDeviceIntPoint(point.x, point.y); + mouseEvent->clickCount = 1; + mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; + DispatchAsyncEventIgnoreStatus(mouseEvent); } } @@ -993,17 +1017,14 @@ MetroInput::HandleLongTap(const LayoutDeviceIntPoint& aPoint) LogFunction(); #endif - nsMouseEvent contextMenu(true, - NS_CONTEXTMENU, - mWidget.Get(), - nsMouseEvent::eReal, - nsMouseEvent::eNormal); - mModifierKeyState.Update(); - mModifierKeyState.InitInputEvent(contextMenu); - contextMenu.refPoint = aPoint; - contextMenu.time = ::GetMessageTime(); - contextMenu.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; - DispatchEventIgnoreStatus(&contextMenu); + nsMouseEvent* contextEvent = new nsMouseEvent(true, + NS_CONTEXTMENU, + mWidget.Get(), + nsMouseEvent::eReal, + nsMouseEvent::eNormal); + contextEvent->refPoint = aPoint; + contextEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; + DispatchAsyncEventIgnoreStatus(contextEvent); } /** @@ -1011,39 +1032,82 @@ MetroInput::HandleLongTap(const LayoutDeviceIntPoint& aPoint) */ nsEventStatus MetroInput::sThrowawayStatus; -// This function allows us to call MetroWidget's DispatchEvent function -// without passing in a status. It uses a static nsEventStatus whose value -// is never read. This allows us to avoid the (admittedly small) overhead -// of creating a new nsEventStatus every time we dispatch an event. +void +MetroInput::DispatchAsyncEventIgnoreStatus(nsInputEvent* aEvent) +{ + aEvent->time = ::GetMessageTime(); + mModifierKeyState.Update(); + mModifierKeyState.InitInputEvent(*aEvent); + mInputEventQueue.Push(aEvent); + nsCOMPtr runnable = + NS_NewRunnableMethod(this, &MetroInput::DeliverNextQueuedEventIgnoreStatus); + NS_DispatchToCurrentThread(runnable); +} + +void +MetroInput::DeliverNextQueuedEventIgnoreStatus() +{ + nsGUIEvent* event = static_cast(mInputEventQueue.PopFront()); + MOZ_ASSERT(event); + DispatchEventIgnoreStatus(event); + delete event; +} + +nsEventStatus +MetroInput::DeliverNextQueuedEvent() +{ + nsGUIEvent* event = static_cast(mInputEventQueue.PopFront()); + MOZ_ASSERT(event); + nsEventStatus status; + mWidget->DispatchEvent(event, status); + delete event; + return status; +} + +void +MetroInput::DispatchAsyncTouchEventIgnoreStatus(nsTouchEvent* aEvent) +{ + aEvent->time = ::GetMessageTime(); + mModifierKeyState.Update(); + mModifierKeyState.InitInputEvent(*aEvent); + mInputEventQueue.Push(aEvent); + nsCOMPtr runnable = + NS_NewRunnableMethod(this, &MetroInput::DeliverNextQueuedTouchEvent); + NS_DispatchToCurrentThread(runnable); +} + +nsEventStatus +MetroInput::DeliverNextQueuedTouchEvent() +{ + nsTouchEvent* event = static_cast(mInputEventQueue.PopFront()); + MOZ_ASSERT(event); + nsEventStatus status; + mWidget->DispatchEvent(event, status); + if (status != nsEventStatus_eConsumeNoDefault && MetroWidget::sAPZC) { + MultiTouchInput inputData(*event); + MetroWidget::sAPZC->ReceiveInputEvent(inputData); + } + delete event; + return status; +} + +void +MetroInput::DispatchAsyncTouchEventWithCallback(nsTouchEvent* aEvent, void (MetroInput::*Callback)()) +{ + aEvent->time = ::GetMessageTime(); + mModifierKeyState.Update(); + mModifierKeyState.InitInputEvent(*aEvent); + mInputEventQueue.Push(aEvent); + nsCOMPtr runnable = + NS_NewRunnableMethod(this, Callback); + NS_DispatchToCurrentThread(runnable); +} + void MetroInput::DispatchEventIgnoreStatus(nsGUIEvent *aEvent) { mWidget->DispatchEvent(aEvent, sThrowawayStatus); } -void -MetroInput::DispatchPendingTouchEvent(nsEventStatus& aStatus, bool aDispatchToAPZC) { - mTouchEvent.touches.Clear(); - mTouches.Enumerate(&AppendToTouchList, - static_cast(&mTouchEvent.touches)); - mTouchEvent.time = ::GetMessageTime(); - mModifierKeyState.Update(); - mModifierKeyState.InitInputEvent(mTouchEvent); - - mWidget->DispatchEvent(&mTouchEvent, aStatus); - if (aStatus != nsEventStatus_eConsumeNoDefault && aDispatchToAPZC && MetroWidget::sAPZC) { - MultiTouchInput inputData(mTouchEvent); - aStatus = MetroWidget::sAPZC->ReceiveInputEvent(inputData); - } - - // mTouchEvent.message should always be set to NS_TOUCH_MOVE - mTouchEvent.message = NS_TOUCH_MOVE; -} - -void -MetroInput::DispatchPendingTouchEvent(bool aDispatchToAPZC) { - DispatchPendingTouchEvent(sThrowawayStatus, aDispatchToAPZC); -} - void MetroInput::UnregisterInputEvents() { // Unregister ourselves for the edge swipe event @@ -1085,8 +1149,6 @@ MetroInput::RegisterInputEvents() NS_ASSERTION(mWindow, "Must have a window to register for input events!"); NS_ASSERTION(mGestureRecognizer, "Must have a GestureRecognizer for input events!"); - NS_ASSERTION(mDispatcher, - "Must have a CoreDispatcher to register for input events!"); // Register for edge swipe WRL::ComPtr edgeStatics; Foundation::GetActivationFactory( diff --git a/widget/windows/winrt/MetroInput.h b/widget/windows/winrt/MetroInput.h index 735dfa5ee98..826fc64e679 100644 --- a/widget/windows/winrt/MetroInput.h +++ b/widget/windows/winrt/MetroInput.h @@ -11,6 +11,7 @@ #include "nsGUIEvent.h" // mTouchEvent (nsTouchEvent) #include "nsHashKeys.h" // type of key for mTouches #include "mozwrlbase.h" +#include "nsDeque.h" // System headers (alphabetical) #include // EventRegistrationToken @@ -44,7 +45,6 @@ namespace ABI { namespace UI { namespace Core { struct ICoreWindow; - struct ICoreDispatcher; struct IAcceleratorKeyEventArgs; struct IKeyEventArgs; struct IPointerEventArgs; @@ -84,7 +84,6 @@ private: typedef ABI::Windows::UI::Core::ICoreWindow ICoreWindow; typedef ABI::Windows::UI::Core::IAcceleratorKeyEventArgs \ IAcceleratorKeyEventArgs; - typedef ABI::Windows::UI::Core::ICoreDispatcher ICoreDispatcher; typedef ABI::Windows::UI::Core::IKeyEventArgs IKeyEventArgs; typedef ABI::Windows::UI::Core::IPointerEventArgs IPointerEventArgs; @@ -105,8 +104,7 @@ private: public: MetroInput(MetroWidget* aWidget, - ICoreWindow* aWindow, - ICoreDispatcher* aDispatcher); + ICoreWindow* aWindow); virtual ~MetroInput(); // These input events are received from our window. These are basic @@ -157,7 +155,6 @@ public: private: Microsoft::WRL::ComPtr mWindow; Microsoft::WRL::ComPtr mWidget; - Microsoft::WRL::ComPtr mDispatcher; Microsoft::WRL::ComPtr mGestureRecognizer; ModifierKeyState mModifierKeyState; @@ -168,16 +165,13 @@ private: // Event processing helpers. See function definitions for more info. void OnPointerNonTouch(IPointerPoint* aPoint); - void InitGeckoMouseEventFromPointerPoint(nsMouseEvent& aEvent, + void InitGeckoMouseEventFromPointerPoint(nsMouseEvent* aEvent, IPointerPoint* aPoint); void ProcessManipulationDelta(ManipulationDelta const& aDelta, Point const& aPosition, uint32_t aMagEventType, uint32_t aRotEventType); - void DispatchEventIgnoreStatus(nsGUIEvent *aEvent); - static nsEventStatus sThrowawayStatus; - // The W3C spec states that "whether preventDefault has been called" should // be tracked on a per-touchpoint basis, but it also states that touchstart // and touchmove events can contain multiple changed points. At the time of @@ -224,10 +218,7 @@ private: // the updated touchpoint info and record the fact that the touchpoint // has changed. If ever we try to update a touchpoint has already // changed, we dispatch a touch event containing all the changed touches. - nsEventStatus mTouchEventStatus; - nsTouchEvent mTouchEvent; - void DispatchPendingTouchEvent(bool aDispatchToAPZC); - void DispatchPendingTouchEvent(nsEventStatus& status, bool aDispatchToAPZC); + void InitTouchEventTouchList(nsTouchEvent* aEvent); nsBaseHashtable, nsRefPtr > mTouches; @@ -257,6 +248,34 @@ private: EventRegistrationToken mTokenManipulationCompleted; EventRegistrationToken mTokenTapped; EventRegistrationToken mTokenRightTapped; + + // Due to a limitation added in 8.1 the ui thread can't re-enter the main + // native event dispatcher in MetroAppShell. So all events delivered to us + // on the ui thread via a native event dispatch call get bounced through + // the gecko thread event queue using runnables. Most events can be sent + // async without the need to see the status result. Those that do have + // specialty callbacks. Note any event that arrives to us on the ui thread + // that originates from another thread is safe to send sync. + + // Async event dispatching + void DispatchAsyncEventIgnoreStatus(nsInputEvent* aEvent); + void DispatchAsyncTouchEventIgnoreStatus(nsTouchEvent* aEvent); + void DispatchAsyncTouchEventWithCallback(nsTouchEvent* aEvent, void (MetroInput::*Callback)()); + + // Async event callbacks + void DeliverNextQueuedEventIgnoreStatus(); + nsEventStatus DeliverNextQueuedEvent(); + nsEventStatus DeliverNextQueuedTouchEvent(); + + // Misc. specialty async callbacks + void OnPointerPressedCallback(); + void OnFirstPointerMoveCallback(); + + // Sync event dispatching + void DispatchEventIgnoreStatus(nsGUIEvent *aEvent); + + nsDeque mInputEventQueue; + static nsEventStatus sThrowawayStatus; }; } } } diff --git a/widget/windows/winrt/MetroWidget.cpp b/widget/windows/winrt/MetroWidget.cpp index b6c04edb218..28b330a3bc5 100644 --- a/widget/windows/winrt/MetroWidget.cpp +++ b/widget/windows/winrt/MetroWidget.cpp @@ -562,6 +562,124 @@ CloseGesture() } } +// Async event sending for mouse and keyboard input. + +// Simple Windows message wrapper for dispatching async events. +class DispatchMsg +{ +public: + DispatchMsg(UINT aMsg, WPARAM aWParam, LPARAM aLParam) : + mMsg(aMsg), + mWParam(aWParam), + mLParam(aLParam) + { + } + ~DispatchMsg() + { + } + + UINT mMsg; + WPARAM mWParam; + LPARAM mLParam; +}; + +DispatchMsg* +MetroWidget::CreateDispatchMsg(UINT aMsg, WPARAM aWParam, LPARAM aLParam) +{ + switch (aMsg) { + case WM_SETTINGCHANGE: + case WM_MOUSEWHEEL: + case WM_MOUSEHWHEEL: + case WM_HSCROLL: + case WM_VSCROLL: + case MOZ_WM_HSCROLL: + case MOZ_WM_VSCROLL: + case WM_KEYDOWN: + case WM_KEYUP: + // MOZ_WM events are plugin specific, we keep them for completness + case MOZ_WM_MOUSEVWHEEL: + case MOZ_WM_MOUSEHWHEEL: + return new DispatchMsg(aMsg, aWParam, aLParam); + default: + MOZ_CRASH("Unknown event being passed to CreateDispatchMsg."); + return nullptr; + } +} + +void +MetroWidget::DispatchAsyncScrollEvent(DispatchMsg* aEvent) +{ + mMsgEventQueue.Push(aEvent); + nsCOMPtr runnable = + NS_NewRunnableMethod(this, &MetroWidget::DeliverNextScrollEvent); + NS_DispatchToCurrentThread(runnable); +} + +void +MetroWidget::DeliverNextScrollEvent() +{ + DispatchMsg* msg = static_cast(mMsgEventQueue.PopFront()); + MOZ_ASSERT(msg); + MSGResult msgResult; + MouseScrollHandler::ProcessMessage(this, msg->mMsg, msg->mWParam, msg->mLParam, msgResult); + delete msg; +} + +// defined in nsWiondowBase, called from shared module KeyboardLayout. +bool +MetroWidget::DispatchKeyboardEvent(nsGUIEvent* aEvent) +{ + MOZ_ASSERT(aEvent); + nsKeyEvent* oldKeyEvent = static_cast(aEvent); + nsKeyEvent* keyEvent = + new nsKeyEvent(oldKeyEvent->mFlags.mIsTrusted, oldKeyEvent->message, oldKeyEvent->widget); + // XXX note this leaves pluginEvent null, which is fine for now. + keyEvent->AssignKeyEventData(*oldKeyEvent, true); + mKeyEventQueue.Push(keyEvent); + nsCOMPtr runnable = + NS_NewRunnableMethod(this, &MetroWidget::DeliverNextKeyboardEvent); + NS_DispatchToCurrentThread(runnable); + return false; +} + +// Used in conjunction with mKeyEventQueue to find a keypress event +// that should not be delivered due to the return result of the +// preceeding keydown. +class KeyQueryIdAndCancel : public nsDequeFunctor { +public: + KeyQueryIdAndCancel(uint32_t aIdToCancel) : + mId(aIdToCancel) { + } + virtual void* operator() (void* aObject) { + nsKeyEvent* event = static_cast(aObject); + if (event->mUniqueId == mId) { + event->mFlags.mPropagationStopped = true; + } + return nullptr; + } +protected: + uint32_t mId; +}; + +void +MetroWidget::DeliverNextKeyboardEvent() +{ + nsKeyEvent* event = static_cast(mKeyEventQueue.PopFront()); + if (event->mFlags.mPropagationStopped) { + // This can happen if a keypress was previously cancelled. + delete event; + return; + } + + if (DispatchWindowEvent(event) && event->message == NS_KEY_DOWN) { + // keydown events may be followed by multiple keypress events which + // shouldn't be sent if preventDefault is called on keydown. + KeyQueryIdAndCancel query(event->mUniqueId); + mKeyEventQueue.ForEach(query); + } + delete event; +} + // static LRESULT CALLBACK MetroWidget::StaticWindowProcedure(HWND aWnd, UINT aMsg, WPARAM aWParam, LPARAM aLParam) @@ -589,10 +707,14 @@ MetroWidget::WindowProcedure(HWND aWnd, UINT aMsg, WPARAM aWParam, LPARAM aLPara // The result returned if we do not do default processing. LRESULT processResult = 0; - MSGResult msgResult(&processResult); - MouseScrollHandler::ProcessMessage(this, aMsg, aWParam, aLParam, msgResult); - if (msgResult.mConsumed) { - return processResult; + // We ignore return results from the scroll module and pass everything + // to mMetroWndProc. These fall through to winrt handlers that generate + // input events in MetroInput. Since we have no listeners for scroll + // events no processing should occur. For now processDefault must be left + // true since the mouse module consumes non-mouse wheel related events. + if (MouseScrollHandler::NeedsMessage(aMsg)) { + DispatchMsg* msg = CreateDispatchMsg(aMsg, aWParam, aLParam); + DispatchAsyncScrollEvent(msg); } switch (aMsg) { @@ -604,7 +726,7 @@ MetroWidget::WindowProcedure(HWND aWnd, UINT aMsg, WPARAM aWParam, LPARAM aLPara DeleteObject(rgn); if (region.IsEmpty()) break; - mView->Render(region); + Paint(region); break; } @@ -624,6 +746,9 @@ MetroWidget::WindowProcedure(HWND aWnd, UINT aMsg, WPARAM aWParam, LPARAM aLPara break; } + // Keyboard handling is passed to KeyboardLayout, which delivers gecko events + // via DispatchKeyboardEvent. + case WM_KEYDOWN: case WM_SYSKEYDOWN: { @@ -983,6 +1108,8 @@ MetroWidget::GetPaintListener() void MetroWidget::Paint(const nsIntRegion& aInvalidRegion) { + gfxWindowsPlatform::GetPlatform()->UpdateRenderMode(); + nsIWidgetListener* listener = GetPaintListener(); if (!listener) return; diff --git a/widget/windows/winrt/MetroWidget.h b/widget/windows/winrt/MetroWidget.h index 5d9aef8c6cd..c83b99b4612 100644 --- a/widget/windows/winrt/MetroWidget.h +++ b/widget/windows/winrt/MetroWidget.h @@ -45,6 +45,8 @@ class FrameworkView; } } } +class DispatchMsg; + class MetroWidget : public nsWindowBase, public mozilla::layers::GeckoContentController, public nsIObserver @@ -73,6 +75,8 @@ public: // nsWindowBase virtual bool DispatchWindowEvent(nsGUIEvent* aEvent) MOZ_OVERRIDE; + virtual bool DispatchKeyboardEvent(nsGUIEvent* aEvent) MOZ_OVERRIDE; + virtual bool DispatchPluginEvent(const MSG &aMsg) MOZ_OVERRIDE { return false; } virtual bool IsTopLevelWidget() MOZ_OVERRIDE { return true; } virtual nsWindowBase* GetParentWindowBase(bool aIncludeOwner) MOZ_OVERRIDE { return nullptr; } // InitEvent assumes physical coordinates and is used by shared win32 code. Do @@ -248,6 +252,15 @@ protected: mozilla::layers::FrameMetrics mFrameMetrics; uint64_t mRootLayerTreeId; + // Async event dispatching + void DispatchAsyncScrollEvent(DispatchMsg* aEvent); + void DeliverNextScrollEvent(); + void DeliverNextKeyboardEvent(); + DispatchMsg* CreateDispatchMsg(UINT aMsg, WPARAM aWParam, LPARAM aLParam); + + nsDeque mMsgEventQueue; + nsDeque mKeyEventQueue; + public: static nsRefPtr sAPZC; }; diff --git a/widget/windows/winrt/moz.build b/widget/windows/winrt/moz.build index 1f5b2313339..e899b282fad 100644 --- a/widget/windows/winrt/moz.build +++ b/widget/windows/winrt/moz.build @@ -8,7 +8,6 @@ MODULE = 'widget' CPP_SOURCES += [ 'FrameworkView.cpp', - 'FrameworkViewGfx.cpp', 'MetroApp.cpp', 'MetroAppShell.cpp', 'MetroContracts.cpp',