Merge mozilla-central and fx-team

This commit is contained in:
Ed Morley 2013-09-06 16:25:48 +01:00
commit 80a1b74dd0
27 changed files with 779 additions and 551 deletions

View File

@ -4,6 +4,7 @@
"use strict";
function test() {
requestLongerTimeout(2);
waitForExplicitFinish();
try {

View File

@ -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"));
}
}

View File

@ -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.

View File

@ -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());

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}
};

View File

@ -261,6 +261,7 @@ struct ParamTraits<nsKeyEvent>
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<nsKeyEvent>
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<mozilla::widget::KeyNameIndex>(keyNameIndex);
aResult->mNativeKeyEvent = NULL;

View File

@ -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<void*>(&pluginEvent);
}
return (mWidget->DispatchWindowEvent(&aKeyEvent) || mWidget->Destroyed());
return (mWidget->DispatchKeyboardEvent(&aKeyEvent) || mWidget->Destroyed());
}
bool

View File

@ -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,

View File

@ -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,

View File

@ -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:

View File

@ -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;

View File

@ -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; }

View File

@ -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;

View File

@ -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<nsString>();
LogFunction();
@ -84,27 +77,6 @@ FrameworkView::Initialize(ICoreApplicationView* aAppView)
HRESULT
FrameworkView::Uninitialize()
{
LogFunction();
mShuttingDown = true;
if (mAutomationProvider) {
ComPtr<IUIABridge> 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<MetroInput>(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<IUIABridge> 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;
}

View File

@ -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<IInspectable> mAutomationProvider;
@ -190,7 +189,6 @@ private:
//Microsoft::WRL::ComPtr<IWICImagingFactory2> mWicFactory;
Microsoft::WRL::ComPtr<MetroApp> mMetroApp;
Microsoft::WRL::ComPtr<ICoreWindow> mWindow;
Microsoft::WRL::ComPtr<ICoreDispatcher> mDispatcher;
Microsoft::WRL::ComPtr<MetroWidget> mWidget;
Microsoft::WRL::ComPtr<MetroInput> mMetroInput;
static bool sKeyboardIsVisible;

View File

@ -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 <windows.ui.xaml.media.dxinterop.h>
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<bool> painting(mPainting);
mPainting = true;
gfxWindowsPlatform::GetPlatform()->UpdateRenderMode();
mWidget->Paint(aInvalidRegion);
return true;
}
} } }

View File

@ -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);

View File

@ -42,7 +42,7 @@ public:
// nsIWinMetroUtils tile related async callbacks
HRESULT OnAsyncTileCreated(ABI::Windows::Foundation::IAsyncOperation<bool>* aOperation, AsyncStatus aStatus);
void Initialize();
void Run();
void CoreExit();
void ShutdownXPCOM();

View File

@ -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<MetroApp> sMetroApp;
extern ComPtr<FrameworkView> sFrameworkView;
} } }
namespace mozilla {
@ -32,6 +39,7 @@ extern UINT sAppShellGeckoMsgId;
} }
static ComPtr<ICoreWindowStatic> 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<bool> 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

View File

@ -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;
};

View File

@ -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<void*>(&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> 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<UI::Input::IPointerPoint> currentPoint;
WRL::ComPtr<Devices::Input::IPointerDevice> 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> 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<Foundation::Collections::IVector<UI::Input::PointerPoint*>>
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<Foundation::Collections::IVector<UI::Input::PointerPoint*>>
pointerPoints;
aArgs->GetIntermediatePoints(pointerPoints.GetAddressOf());
mGestureRecognizer->ProcessMoveEvents(pointerPoints.Get());
return S_OK;
}
void
MetroInput::OnFirstPointerMoveCallback()
{
nsTouchEvent* event = static_cast<nsTouchEvent*>(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<UI::Input::IPointerPoint> currentPoint;
WRL::ComPtr<Devices::Input::IPointerDevice> 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> 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<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &MetroInput::DeliverNextQueuedEventIgnoreStatus);
NS_DispatchToCurrentThread(runnable);
}
void
MetroInput::DeliverNextQueuedEventIgnoreStatus()
{
nsGUIEvent* event = static_cast<nsGUIEvent*>(mInputEventQueue.PopFront());
MOZ_ASSERT(event);
DispatchEventIgnoreStatus(event);
delete event;
}
nsEventStatus
MetroInput::DeliverNextQueuedEvent()
{
nsGUIEvent* event = static_cast<nsGUIEvent*>(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<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &MetroInput::DeliverNextQueuedTouchEvent);
NS_DispatchToCurrentThread(runnable);
}
nsEventStatus
MetroInput::DeliverNextQueuedTouchEvent()
{
nsTouchEvent* event = static_cast<nsTouchEvent*>(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<nsIRunnable> 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<void*>(&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<UI::Input::IEdgeGestureStatics> edgeStatics;
Foundation::GetActivationFactory(

View File

@ -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 <EventToken.h> // 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<ICoreWindow> mWindow;
Microsoft::WRL::ComPtr<MetroWidget> mWidget;
Microsoft::WRL::ComPtr<ICoreDispatcher> mDispatcher;
Microsoft::WRL::ComPtr<IGestureRecognizer> 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<nsUint32HashKey,
nsRefPtr<mozilla::dom::Touch>,
nsRefPtr<mozilla::dom::Touch> > 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;
};
} } }

View File

@ -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<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &MetroWidget::DeliverNextScrollEvent);
NS_DispatchToCurrentThread(runnable);
}
void
MetroWidget::DeliverNextScrollEvent()
{
DispatchMsg* msg = static_cast<DispatchMsg*>(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<nsKeyEvent*>(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<nsIRunnable> 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<nsKeyEvent*>(aObject);
if (event->mUniqueId == mId) {
event->mFlags.mPropagationStopped = true;
}
return nullptr;
}
protected:
uint32_t mId;
};
void
MetroWidget::DeliverNextKeyboardEvent()
{
nsKeyEvent* event = static_cast<nsKeyEvent*>(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;

View File

@ -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<mozilla::layers::APZCTreeManager> sAPZC;
};

View File

@ -8,7 +8,6 @@ MODULE = 'widget'
CPP_SOURCES += [
'FrameworkView.cpp',
'FrameworkViewGfx.cpp',
'MetroApp.cpp',
'MetroAppShell.cpp',
'MetroContracts.cpp',