2015-05-03 22:32:37 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2015-01-28 09:27:32 +03:00
|
|
|
/* 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/. */
|
|
|
|
|
2015-08-04 12:52:00 +03:00
|
|
|
#include "gfxPrefs.h"
|
2016-06-09 13:42:21 +03:00
|
|
|
#include "mozilla/dom/Event.h"
|
2015-01-28 09:27:32 +03:00
|
|
|
#include "mozilla/EventForwards.h"
|
2015-01-28 09:27:32 +03:00
|
|
|
#include "mozilla/TextEventDispatcher.h"
|
2015-02-19 09:50:19 +03:00
|
|
|
#include "mozilla/TextEvents.h"
|
2015-01-28 09:27:32 +03:00
|
|
|
#include "mozilla/TextInputProcessor.h"
|
2017-04-20 14:17:03 +03:00
|
|
|
#include "mozilla/widget/IMEData.h"
|
2018-02-09 19:17:10 +03:00
|
|
|
#include "mozilla/dom/KeyboardEvent.h"
|
2016-06-09 13:42:21 +03:00
|
|
|
#include "nsContentUtils.h"
|
2015-01-28 09:27:32 +03:00
|
|
|
#include "nsIDocShell.h"
|
|
|
|
#include "nsIWidget.h"
|
|
|
|
#include "nsPIDOMWindow.h"
|
|
|
|
#include "nsPresContext.h"
|
|
|
|
|
2017-06-14 17:44:17 +03:00
|
|
|
using mozilla::dom::KeyboardEvent;
|
2015-01-28 09:27:32 +03:00
|
|
|
using namespace mozilla::widget;
|
|
|
|
|
2015-01-28 09:27:32 +03:00
|
|
|
namespace mozilla {
|
|
|
|
|
2015-01-28 09:27:33 +03:00
|
|
|
/******************************************************************************
|
|
|
|
* TextInputProcessorNotification
|
|
|
|
******************************************************************************/
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class TextInputProcessorNotification final :
|
2015-01-28 09:27:33 +03:00
|
|
|
public nsITextInputProcessorNotification
|
|
|
|
{
|
2017-04-20 14:17:03 +03:00
|
|
|
typedef IMENotification::SelectionChangeData SelectionChangeData;
|
|
|
|
typedef IMENotification::SelectionChangeDataBase SelectionChangeDataBase;
|
|
|
|
typedef IMENotification::TextChangeData TextChangeData;
|
|
|
|
typedef IMENotification::TextChangeDataBase TextChangeDataBase;
|
|
|
|
|
2015-01-28 09:27:33 +03:00
|
|
|
public:
|
|
|
|
explicit TextInputProcessorNotification(const char* aType)
|
|
|
|
: mType(aType)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-04-20 14:17:03 +03:00
|
|
|
explicit TextInputProcessorNotification(
|
|
|
|
const TextChangeDataBase& aTextChangeData)
|
|
|
|
: mType("notify-text-change")
|
|
|
|
, mTextChangeData(aTextChangeData)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
explicit TextInputProcessorNotification(
|
|
|
|
const SelectionChangeDataBase& aSelectionChangeData)
|
|
|
|
: mType("notify-selection-change")
|
|
|
|
, mSelectionChangeData(aSelectionChangeData)
|
|
|
|
{
|
|
|
|
// SelectionChangeDataBase::mString still refers nsString instance owned
|
|
|
|
// by aSelectionChangeData. So, this needs to copy the instance.
|
|
|
|
nsString* string = new nsString(aSelectionChangeData.String());
|
|
|
|
mSelectionChangeData.mString = string;
|
|
|
|
}
|
|
|
|
|
2015-01-28 09:27:33 +03:00
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
|
2018-02-06 09:50:00 +03:00
|
|
|
NS_IMETHOD GetType(nsACString& aType) final
|
2015-01-28 09:27:33 +03:00
|
|
|
{
|
|
|
|
aType = mType;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2017-04-20 14:17:03 +03:00
|
|
|
// "notify-text-change" and "notify-selection-change"
|
2018-02-06 09:50:00 +03:00
|
|
|
NS_IMETHOD GetOffset(uint32_t* aOffset) final
|
2017-04-20 14:17:03 +03:00
|
|
|
{
|
|
|
|
if (NS_WARN_IF(!aOffset)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
if (IsSelectionChange()) {
|
|
|
|
*aOffset = mSelectionChangeData.mOffset;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
if (IsTextChange()) {
|
|
|
|
*aOffset = mTextChangeData.mStartOffset;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// "notify-selection-change"
|
2018-02-06 09:50:00 +03:00
|
|
|
NS_IMETHOD GetText(nsAString& aText) final
|
2017-04-20 14:17:03 +03:00
|
|
|
{
|
|
|
|
if (IsSelectionChange()) {
|
|
|
|
aText = mSelectionChangeData.String();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
2018-02-06 09:50:00 +03:00
|
|
|
NS_IMETHOD GetCollapsed(bool* aCollapsed) final
|
2017-04-20 14:17:03 +03:00
|
|
|
{
|
|
|
|
if (NS_WARN_IF(!aCollapsed)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
if (IsSelectionChange()) {
|
|
|
|
*aCollapsed = mSelectionChangeData.IsCollapsed();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
2018-02-06 09:50:00 +03:00
|
|
|
NS_IMETHOD GetLength(uint32_t* aLength) final
|
2017-04-20 14:17:03 +03:00
|
|
|
{
|
|
|
|
if (NS_WARN_IF(!aLength)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
if (IsSelectionChange()) {
|
|
|
|
*aLength = mSelectionChangeData.Length();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
2018-02-06 09:50:00 +03:00
|
|
|
NS_IMETHOD GetReversed(bool* aReversed) final
|
2017-04-20 14:17:03 +03:00
|
|
|
{
|
|
|
|
if (NS_WARN_IF(!aReversed)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
if (IsSelectionChange()) {
|
|
|
|
*aReversed = mSelectionChangeData.mReversed;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
2018-02-06 09:50:00 +03:00
|
|
|
NS_IMETHOD GetWritingMode(nsACString& aWritingMode) final
|
2017-04-20 14:17:03 +03:00
|
|
|
{
|
|
|
|
if (IsSelectionChange()) {
|
|
|
|
WritingMode writingMode = mSelectionChangeData.GetWritingMode();
|
|
|
|
if (!writingMode.IsVertical()) {
|
|
|
|
aWritingMode.AssignLiteral("horizontal-tb");
|
|
|
|
} else if (writingMode.IsVerticalLR()) {
|
|
|
|
aWritingMode.AssignLiteral("vertical-lr");
|
|
|
|
} else {
|
|
|
|
aWritingMode.AssignLiteral("vertical-rl");
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
2018-02-06 09:50:00 +03:00
|
|
|
NS_IMETHOD GetCausedByComposition(bool* aCausedByComposition) final
|
2017-04-20 14:17:03 +03:00
|
|
|
{
|
|
|
|
if (NS_WARN_IF(!aCausedByComposition)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
if (IsSelectionChange()) {
|
|
|
|
*aCausedByComposition = mSelectionChangeData.mCausedByComposition;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD GetCausedBySelectionEvent(
|
2018-02-06 09:50:00 +03:00
|
|
|
bool* aCausedBySelectionEvent) final
|
2017-04-20 14:17:03 +03:00
|
|
|
{
|
|
|
|
if (NS_WARN_IF(!aCausedBySelectionEvent)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
if (IsSelectionChange()) {
|
|
|
|
*aCausedBySelectionEvent = mSelectionChangeData.mCausedBySelectionEvent;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD GetOccurredDuringComposition(
|
2018-02-06 09:50:00 +03:00
|
|
|
bool* aOccurredDuringComposition) final
|
2017-04-20 14:17:03 +03:00
|
|
|
{
|
|
|
|
if (NS_WARN_IF(!aOccurredDuringComposition)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
if (IsSelectionChange()) {
|
|
|
|
*aOccurredDuringComposition =
|
|
|
|
mSelectionChangeData.mOccurredDuringComposition;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// "notify-text-change"
|
2018-02-06 09:50:00 +03:00
|
|
|
NS_IMETHOD GetRemovedLength(uint32_t* aLength) final
|
2017-04-20 14:17:03 +03:00
|
|
|
{
|
|
|
|
if (NS_WARN_IF(!aLength)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
if (IsTextChange()) {
|
|
|
|
*aLength = mTextChangeData.OldLength();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
2018-02-06 09:50:00 +03:00
|
|
|
NS_IMETHOD GetAddedLength(uint32_t* aLength) final
|
2017-04-20 14:17:03 +03:00
|
|
|
{
|
|
|
|
if (NS_WARN_IF(!aLength)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
if (IsTextChange()) {
|
|
|
|
*aLength = mTextChangeData.NewLength();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD GetCausedOnlyByComposition(
|
2018-02-06 09:50:00 +03:00
|
|
|
bool* aCausedOnlyByComposition) final
|
2017-04-20 14:17:03 +03:00
|
|
|
{
|
|
|
|
if (NS_WARN_IF(!aCausedOnlyByComposition)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
if (IsTextChange()) {
|
|
|
|
*aCausedOnlyByComposition = mTextChangeData.mCausedOnlyByComposition;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD GetIncludingChangesDuringComposition(
|
2018-02-06 09:50:00 +03:00
|
|
|
bool* aIncludingChangesDuringComposition) final
|
2017-04-20 14:17:03 +03:00
|
|
|
{
|
|
|
|
if (NS_WARN_IF(!aIncludingChangesDuringComposition)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
if (IsTextChange()) {
|
|
|
|
*aIncludingChangesDuringComposition =
|
|
|
|
mTextChangeData.mIncludingChangesDuringComposition;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD GetIncludingChangesWithoutComposition(
|
2018-02-06 09:50:00 +03:00
|
|
|
bool* aIncludingChangesWithoutComposition) final
|
2017-04-20 14:17:03 +03:00
|
|
|
{
|
|
|
|
if (NS_WARN_IF(!aIncludingChangesWithoutComposition)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
if (IsTextChange()) {
|
|
|
|
*aIncludingChangesWithoutComposition =
|
|
|
|
mTextChangeData.mIncludingChangesWithoutComposition;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
2015-01-28 09:27:33 +03:00
|
|
|
protected:
|
2017-04-20 14:17:03 +03:00
|
|
|
virtual ~TextInputProcessorNotification()
|
|
|
|
{
|
|
|
|
if (IsSelectionChange()) {
|
|
|
|
delete mSelectionChangeData.mString;
|
|
|
|
mSelectionChangeData.mString = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsTextChange() const
|
|
|
|
{
|
|
|
|
return mType.EqualsLiteral("notify-text-change");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsSelectionChange() const
|
|
|
|
{
|
|
|
|
return mType.EqualsLiteral("notify-selection-change");
|
|
|
|
}
|
2015-01-28 09:27:33 +03:00
|
|
|
|
|
|
|
private:
|
|
|
|
nsAutoCString mType;
|
2017-04-20 14:17:03 +03:00
|
|
|
union
|
|
|
|
{
|
|
|
|
TextChangeDataBase mTextChangeData;
|
|
|
|
SelectionChangeDataBase mSelectionChangeData;
|
|
|
|
};
|
2015-01-28 09:27:33 +03:00
|
|
|
|
|
|
|
TextInputProcessorNotification() { }
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS(TextInputProcessorNotification,
|
|
|
|
nsITextInputProcessorNotification)
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* TextInputProcessor
|
|
|
|
******************************************************************************/
|
|
|
|
|
2015-01-28 09:27:32 +03:00
|
|
|
NS_IMPL_ISUPPORTS(TextInputProcessor,
|
2015-01-28 09:27:32 +03:00
|
|
|
nsITextInputProcessor,
|
|
|
|
TextEventDispatcherListener,
|
|
|
|
nsISupportsWeakReference)
|
2015-01-28 09:27:32 +03:00
|
|
|
|
|
|
|
TextInputProcessor::TextInputProcessor()
|
|
|
|
: mDispatcher(nullptr)
|
|
|
|
, mForTests(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
TextInputProcessor::~TextInputProcessor()
|
|
|
|
{
|
2015-01-28 09:27:33 +03:00
|
|
|
if (mDispatcher && mDispatcher->IsComposing()) {
|
|
|
|
// If this is composing and not canceling the composition, nobody can steal
|
|
|
|
// the rights of TextEventDispatcher from this instance. Therefore, this
|
|
|
|
// needs to cancel the composition here.
|
|
|
|
if (NS_SUCCEEDED(IsValidStateForComposition())) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
|
2015-01-28 09:27:33 +03:00
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2016-08-24 21:12:09 +03:00
|
|
|
kungFuDeathGrip->CommitComposition(status, &EmptyString());
|
2015-01-28 09:27:33 +03:00
|
|
|
}
|
|
|
|
}
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
|
|
|
|
2015-02-19 09:50:20 +03:00
|
|
|
bool
|
|
|
|
TextInputProcessor::IsComposing() const
|
|
|
|
{
|
|
|
|
return mDispatcher && mDispatcher->IsComposing();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TextInputProcessor::GetHasComposition(bool* aHasComposition)
|
|
|
|
{
|
|
|
|
MOZ_RELEASE_ASSERT(aHasComposition, "aHasComposition must not be nullptr");
|
|
|
|
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
|
|
|
*aHasComposition = IsComposing();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2015-01-28 09:27:32 +03:00
|
|
|
NS_IMETHODIMP
|
2015-02-10 11:09:29 +03:00
|
|
|
TextInputProcessor::BeginInputTransaction(
|
2016-01-30 20:05:36 +03:00
|
|
|
mozIDOMWindow* aWindow,
|
2015-02-10 11:09:29 +03:00
|
|
|
nsITextInputProcessorCallback* aCallback,
|
|
|
|
bool* aSucceeded)
|
2015-01-28 09:27:32 +03:00
|
|
|
{
|
|
|
|
MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
|
|
|
|
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
2015-01-28 09:27:33 +03:00
|
|
|
if (NS_WARN_IF(!aCallback)) {
|
|
|
|
*aSucceeded = false;
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
2015-02-10 11:09:29 +03:00
|
|
|
return BeginInputTransactionInternal(aWindow, aCallback, false, *aSucceeded);
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2015-02-10 11:09:29 +03:00
|
|
|
TextInputProcessor::BeginInputTransactionForTests(
|
2016-01-30 20:05:36 +03:00
|
|
|
mozIDOMWindow* aWindow,
|
2015-02-10 11:09:29 +03:00
|
|
|
nsITextInputProcessorCallback* aCallback,
|
|
|
|
uint8_t aOptionalArgc,
|
|
|
|
bool* aSucceeded)
|
2015-01-28 09:27:32 +03:00
|
|
|
{
|
|
|
|
MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
|
|
|
|
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
2015-01-28 09:27:33 +03:00
|
|
|
nsITextInputProcessorCallback* callback =
|
|
|
|
aOptionalArgc >= 1 ? aCallback : nullptr;
|
2015-02-10 11:09:29 +03:00
|
|
|
return BeginInputTransactionInternal(aWindow, callback, true, *aSucceeded);
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
2015-02-10 11:09:29 +03:00
|
|
|
TextInputProcessor::BeginInputTransactionInternal(
|
2016-01-30 20:05:36 +03:00
|
|
|
mozIDOMWindow* aWindow,
|
2015-02-10 11:09:29 +03:00
|
|
|
nsITextInputProcessorCallback* aCallback,
|
|
|
|
bool aForTests,
|
|
|
|
bool& aSucceeded)
|
2015-01-28 09:27:32 +03:00
|
|
|
{
|
|
|
|
aSucceeded = false;
|
|
|
|
if (NS_WARN_IF(!aWindow)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
2016-01-30 20:05:36 +03:00
|
|
|
nsCOMPtr<nsPIDOMWindowInner> pWindow = nsPIDOMWindowInner::From(aWindow);
|
2015-01-28 09:27:32 +03:00
|
|
|
if (NS_WARN_IF(!pWindow)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
nsCOMPtr<nsIDocShell> docShell(pWindow->GetDocShell());
|
|
|
|
if (NS_WARN_IF(!docShell)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<nsPresContext> presContext;
|
2015-01-28 09:27:32 +03:00
|
|
|
nsresult rv = docShell->GetPresContext(getter_AddRefs(presContext));
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
if (NS_WARN_IF(!presContext)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
nsCOMPtr<nsIWidget> widget = presContext->GetRootWidget();
|
|
|
|
if (NS_WARN_IF(!widget)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<TextEventDispatcher> dispatcher = widget->GetTextEventDispatcher();
|
2015-01-28 09:27:32 +03:00
|
|
|
MOZ_RELEASE_ASSERT(dispatcher, "TextEventDispatcher must not be null");
|
|
|
|
|
|
|
|
// If the instance was initialized and is being initialized for same
|
|
|
|
// dispatcher and same purpose, we don't need to initialize the dispatcher
|
|
|
|
// again.
|
2015-01-28 09:27:33 +03:00
|
|
|
if (mDispatcher && dispatcher == mDispatcher && aCallback == mCallback &&
|
|
|
|
aForTests == mForTests) {
|
2015-01-28 09:27:32 +03:00
|
|
|
aSucceeded = true;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2015-02-19 09:50:21 +03:00
|
|
|
// If this instance is composing or dispatching an event, don't allow to
|
|
|
|
// initialize again. Especially, if we allow to begin input transaction with
|
|
|
|
// another TextEventDispatcher during dispatching an event, it may cause that
|
|
|
|
// nobody cannot begin input transaction with it if the last event causes
|
|
|
|
// opening modal dialog.
|
|
|
|
if (mDispatcher &&
|
|
|
|
(mDispatcher->IsComposing() || mDispatcher->IsDispatchingEvent())) {
|
2015-01-28 09:27:32 +03:00
|
|
|
return NS_ERROR_ALREADY_INITIALIZED;
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
|
|
|
|
2015-02-19 09:50:20 +03:00
|
|
|
// And also if another instance is composing with the new dispatcher or
|
|
|
|
// dispatching an event, it'll fail to steal its ownership. Then, we should
|
|
|
|
// not throw an exception, just return false.
|
|
|
|
if (dispatcher->IsComposing() || dispatcher->IsDispatchingEvent()) {
|
2015-01-28 09:27:32 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This instance has finished preparing to link to the dispatcher. Therefore,
|
|
|
|
// let's forget the old dispatcher and purpose.
|
2015-06-11 06:53:42 +03:00
|
|
|
if (mDispatcher) {
|
|
|
|
mDispatcher->EndInputTransaction(this);
|
|
|
|
if (NS_WARN_IF(mDispatcher)) {
|
|
|
|
// Forcibly initialize the members if we failed to end the input
|
|
|
|
// transaction.
|
|
|
|
UnlinkFromTextEventDispatcher();
|
|
|
|
}
|
|
|
|
}
|
2015-01-28 09:27:32 +03:00
|
|
|
|
|
|
|
if (aForTests) {
|
2016-03-16 07:47:48 +03:00
|
|
|
bool isAPZAware = gfxPrefs::TestEventsAsyncEnabled();
|
|
|
|
rv = dispatcher->BeginTestInputTransaction(this, isAPZAware);
|
2015-01-28 09:27:32 +03:00
|
|
|
} else {
|
2015-02-10 11:09:29 +03:00
|
|
|
rv = dispatcher->BeginInputTransaction(this);
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
mDispatcher = dispatcher;
|
2015-01-28 09:27:33 +03:00
|
|
|
mCallback = aCallback;
|
2015-01-28 09:27:32 +03:00
|
|
|
mForTests = aForTests;
|
2015-01-28 09:27:32 +03:00
|
|
|
aSucceeded = true;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2015-01-28 09:27:32 +03:00
|
|
|
void
|
|
|
|
TextInputProcessor::UnlinkFromTextEventDispatcher()
|
|
|
|
{
|
|
|
|
mDispatcher = nullptr;
|
|
|
|
mForTests = false;
|
2015-01-28 09:27:33 +03:00
|
|
|
if (mCallback) {
|
|
|
|
nsCOMPtr<nsITextInputProcessorCallback> callback(mCallback);
|
|
|
|
mCallback = nullptr;
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<TextInputProcessorNotification> notification =
|
2015-02-10 11:09:29 +03:00
|
|
|
new TextInputProcessorNotification("notify-end-input-transaction");
|
2015-01-28 09:27:33 +03:00
|
|
|
bool result = false;
|
|
|
|
callback->OnNotify(this, notification, &result);
|
|
|
|
}
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
|
|
|
|
2015-01-28 09:27:32 +03:00
|
|
|
nsresult
|
2015-01-28 09:27:32 +03:00
|
|
|
TextInputProcessor::IsValidStateForComposition()
|
2015-01-28 09:27:32 +03:00
|
|
|
{
|
2015-01-28 09:27:32 +03:00
|
|
|
if (NS_WARN_IF(!mDispatcher)) {
|
2015-01-28 09:27:32 +03:00
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult rv = mDispatcher->GetState();
|
2015-01-28 09:27:32 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
2015-01-28 09:27:32 +03:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2015-01-28 09:27:32 +03:00
|
|
|
return NS_OK;
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
|
|
|
|
2015-02-19 09:50:20 +03:00
|
|
|
bool
|
|
|
|
TextInputProcessor::IsValidEventTypeForComposition(
|
|
|
|
const WidgetKeyboardEvent& aKeyboardEvent) const
|
|
|
|
{
|
Bug 1446253 - Make EventUtils.synthesizeComposition() dispatch keydown and keyup event in default r=smaug
We'll start to dispatch keydown event and keyup event even during composition.
So, for testing those events won't break our UI, we should make
EventUtils.synhtesizeComposition() and EventUtils.synthesizeCompositionChange()
dispatch keydown event and keyup event even if callers don't specify keyboard
event explicitly.
Typically, "keydown" event is marked as "processed by IME", i.e., keyCode
value is set to DOM_VK_PROCESSKEY and key is set to "Process", with our
widget which handles native IME and key input. On the other hand, "keyup"
is NOT marked as so.
Therefore, this patch makes TextInputProcessor emulates this behavior without
any new special flags. And for making possible to emulate special cases,
this patch adds two flags to nsITextInputProcessor. One is
KEY_DONT_MARK_KEYDOWN_AS_PROCESSED. The other is KEY_MARK_KEYUP_AS_PROCESSED.
Unfortunately, those flags have opposite meaning but this must be better than
making necessary to one flag for emulating usual keydown/keyup events.
Finally, this makes some tests specify better keyboard information to
synthesizeComposition() and synthesizeCompositionChange() to emulate
actual keyboard events during composition.
MozReview-Commit-ID: ItYaXILkNQE
--HG--
extra : rebase_source : e50cc77c1efbc12686d7ea334d41926c7392b30d
2018-03-16 16:35:07 +03:00
|
|
|
// The key event type of composition methods must be "", "keydown" or "keyup".
|
|
|
|
if (aKeyboardEvent.mMessage == eKeyDown ||
|
|
|
|
aKeyboardEvent.mMessage == eKeyUp) {
|
2015-02-19 09:50:20 +03:00
|
|
|
return true;
|
|
|
|
}
|
2015-09-10 04:40:04 +03:00
|
|
|
if (aKeyboardEvent.mMessage == eUnidentifiedEvent &&
|
2016-04-18 17:53:03 +03:00
|
|
|
aKeyboardEvent.mSpecifiedEventType &&
|
|
|
|
nsDependentAtomString(
|
|
|
|
aKeyboardEvent.mSpecifiedEventType).EqualsLiteral("on")) {
|
2015-02-19 09:50:20 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TextInputProcessor::EventDispatcherResult
|
|
|
|
TextInputProcessor::MaybeDispatchKeydownForComposition(
|
|
|
|
const WidgetKeyboardEvent* aKeyboardEvent,
|
|
|
|
uint32_t aKeyFlags)
|
|
|
|
{
|
|
|
|
EventDispatcherResult result;
|
|
|
|
|
|
|
|
result.mResult = IsValidStateForComposition();
|
|
|
|
if (NS_WARN_IF(NS_FAILED(result.mResult))) {
|
|
|
|
result.mCanContinue = false;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aKeyboardEvent) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
Bug 1446253 - Make EventUtils.synthesizeComposition() dispatch keydown and keyup event in default r=smaug
We'll start to dispatch keydown event and keyup event even during composition.
So, for testing those events won't break our UI, we should make
EventUtils.synhtesizeComposition() and EventUtils.synthesizeCompositionChange()
dispatch keydown event and keyup event even if callers don't specify keyboard
event explicitly.
Typically, "keydown" event is marked as "processed by IME", i.e., keyCode
value is set to DOM_VK_PROCESSKEY and key is set to "Process", with our
widget which handles native IME and key input. On the other hand, "keyup"
is NOT marked as so.
Therefore, this patch makes TextInputProcessor emulates this behavior without
any new special flags. And for making possible to emulate special cases,
this patch adds two flags to nsITextInputProcessor. One is
KEY_DONT_MARK_KEYDOWN_AS_PROCESSED. The other is KEY_MARK_KEYUP_AS_PROCESSED.
Unfortunately, those flags have opposite meaning but this must be better than
making necessary to one flag for emulating usual keydown/keyup events.
Finally, this makes some tests specify better keyboard information to
synthesizeComposition() and synthesizeCompositionChange() to emulate
actual keyboard events during composition.
MozReview-Commit-ID: ItYaXILkNQE
--HG--
extra : rebase_source : e50cc77c1efbc12686d7ea334d41926c7392b30d
2018-03-16 16:35:07 +03:00
|
|
|
// If the mMessage is eKeyUp, the caller doesn't want TIP to dispatch
|
|
|
|
// eKeyDown event.
|
|
|
|
if (aKeyboardEvent->mMessage == eKeyUp) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-02-19 09:50:20 +03:00
|
|
|
// Modifier keys are not allowed because managing modifier state in this
|
|
|
|
// method makes this messy.
|
|
|
|
if (NS_WARN_IF(aKeyboardEvent->IsModifierKeyEvent())) {
|
|
|
|
result.mResult = NS_ERROR_INVALID_ARG;
|
|
|
|
result.mCanContinue = false;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-08-05 08:58:00 +03:00
|
|
|
uint32_t consumedFlags = 0;
|
|
|
|
|
2015-02-19 09:50:20 +03:00
|
|
|
result.mResult = KeydownInternal(*aKeyboardEvent, aKeyFlags, false,
|
2015-08-05 08:58:00 +03:00
|
|
|
consumedFlags);
|
|
|
|
result.mDoDefault = !consumedFlags;
|
2015-02-19 09:50:20 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(result.mResult))) {
|
|
|
|
result.mCanContinue = false;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
result.mCanContinue = NS_SUCCEEDED(IsValidStateForComposition());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
TextInputProcessor::EventDispatcherResult
|
|
|
|
TextInputProcessor::MaybeDispatchKeyupForComposition(
|
|
|
|
const WidgetKeyboardEvent* aKeyboardEvent,
|
|
|
|
uint32_t aKeyFlags)
|
|
|
|
{
|
|
|
|
EventDispatcherResult result;
|
|
|
|
|
|
|
|
if (!aKeyboardEvent) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-08-29 02:58:27 +03:00
|
|
|
// If the mMessage is eKeyDown, the caller doesn't want TIP to dispatch
|
Bug 1446253 - Make EventUtils.synthesizeComposition() dispatch keydown and keyup event in default r=smaug
We'll start to dispatch keydown event and keyup event even during composition.
So, for testing those events won't break our UI, we should make
EventUtils.synhtesizeComposition() and EventUtils.synthesizeCompositionChange()
dispatch keydown event and keyup event even if callers don't specify keyboard
event explicitly.
Typically, "keydown" event is marked as "processed by IME", i.e., keyCode
value is set to DOM_VK_PROCESSKEY and key is set to "Process", with our
widget which handles native IME and key input. On the other hand, "keyup"
is NOT marked as so.
Therefore, this patch makes TextInputProcessor emulates this behavior without
any new special flags. And for making possible to emulate special cases,
this patch adds two flags to nsITextInputProcessor. One is
KEY_DONT_MARK_KEYDOWN_AS_PROCESSED. The other is KEY_MARK_KEYUP_AS_PROCESSED.
Unfortunately, those flags have opposite meaning but this must be better than
making necessary to one flag for emulating usual keydown/keyup events.
Finally, this makes some tests specify better keyboard information to
synthesizeComposition() and synthesizeCompositionChange() to emulate
actual keyboard events during composition.
MozReview-Commit-ID: ItYaXILkNQE
--HG--
extra : rebase_source : e50cc77c1efbc12686d7ea334d41926c7392b30d
2018-03-16 16:35:07 +03:00
|
|
|
// eKeyUp event.
|
2015-08-29 02:58:27 +03:00
|
|
|
if (aKeyboardEvent->mMessage == eKeyDown) {
|
2015-02-19 09:50:20 +03:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the widget has been destroyed, we can do nothing here.
|
|
|
|
result.mResult = IsValidStateForComposition();
|
|
|
|
if (NS_FAILED(result.mResult)) {
|
|
|
|
result.mCanContinue = false;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
result.mResult = KeyupInternal(*aKeyboardEvent, aKeyFlags, result.mDoDefault);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(result.mResult))) {
|
|
|
|
result.mCanContinue = false;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
result.mCanContinue = NS_SUCCEEDED(IsValidStateForComposition());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
TextInputProcessor::PrepareKeyboardEventForComposition(
|
2018-02-09 19:17:10 +03:00
|
|
|
KeyboardEvent* aDOMKeyEvent,
|
2015-02-19 09:50:20 +03:00
|
|
|
uint32_t& aKeyFlags,
|
|
|
|
uint8_t aOptionalArgc,
|
|
|
|
WidgetKeyboardEvent*& aKeyboardEvent)
|
|
|
|
{
|
|
|
|
aKeyboardEvent = nullptr;
|
|
|
|
|
|
|
|
aKeyboardEvent =
|
|
|
|
aOptionalArgc && aDOMKeyEvent ?
|
2018-02-09 19:17:10 +03:00
|
|
|
aDOMKeyEvent->WidgetEventPtr()->AsKeyboardEvent() : nullptr;
|
2015-02-19 09:50:20 +03:00
|
|
|
if (!aKeyboardEvent || aOptionalArgc < 2) {
|
|
|
|
aKeyFlags = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aKeyboardEvent) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NS_WARN_IF(!IsValidEventTypeForComposition(*aKeyboardEvent))) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2015-01-28 09:27:32 +03:00
|
|
|
NS_IMETHODIMP
|
2018-02-09 19:17:10 +03:00
|
|
|
TextInputProcessor::StartComposition(nsIDOMEvent* aDOMKeyEvent,
|
2015-02-19 09:50:20 +03:00
|
|
|
uint32_t aKeyFlags,
|
|
|
|
uint8_t aOptionalArgc,
|
|
|
|
bool* aSucceeded)
|
2015-01-28 09:27:32 +03:00
|
|
|
{
|
|
|
|
MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
|
|
|
|
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
|
|
|
*aSucceeded = false;
|
2015-02-19 09:50:20 +03:00
|
|
|
|
2018-02-09 19:17:10 +03:00
|
|
|
RefPtr<KeyboardEvent> keyEvent;
|
|
|
|
if (aDOMKeyEvent) {
|
|
|
|
keyEvent = aDOMKeyEvent->InternalDOMEvent()->AsKeyboardEvent();
|
|
|
|
if (NS_WARN_IF(!keyEvent)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-24 21:12:09 +03:00
|
|
|
RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
|
2015-02-19 09:50:20 +03:00
|
|
|
|
|
|
|
WidgetKeyboardEvent* keyboardEvent;
|
|
|
|
nsresult rv =
|
2018-02-09 19:17:10 +03:00
|
|
|
PrepareKeyboardEventForComposition(keyEvent, aKeyFlags, aOptionalArgc,
|
2015-02-19 09:50:20 +03:00
|
|
|
keyboardEvent);
|
2015-01-28 09:27:32 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2015-02-19 09:50:20 +03:00
|
|
|
|
|
|
|
EventDispatcherResult dispatcherResult =
|
|
|
|
MaybeDispatchKeydownForComposition(keyboardEvent, aKeyFlags);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) ||
|
|
|
|
!dispatcherResult.mCanContinue) {
|
|
|
|
return dispatcherResult.mResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dispatcherResult.mDoDefault) {
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2016-08-24 21:12:09 +03:00
|
|
|
rv = kungFuDeathGrip->StartComposition(status);
|
2015-02-19 09:50:20 +03:00
|
|
|
*aSucceeded = status != nsEventStatus_eConsumeNoDefault &&
|
2016-08-24 21:12:09 +03:00
|
|
|
kungFuDeathGrip && kungFuDeathGrip->IsComposing();
|
2015-02-19 09:50:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
MaybeDispatchKeyupForComposition(keyboardEvent, aKeyFlags);
|
|
|
|
|
2015-01-28 09:27:32 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TextInputProcessor::SetPendingCompositionString(const nsAString& aString)
|
|
|
|
{
|
|
|
|
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
2016-08-24 21:12:09 +03:00
|
|
|
RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
|
2015-01-28 09:27:32 +03:00
|
|
|
nsresult rv = IsValidStateForComposition();
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2016-08-24 21:12:09 +03:00
|
|
|
return kungFuDeathGrip->SetPendingCompositionString(aString);
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TextInputProcessor::AppendClauseToPendingComposition(uint32_t aLength,
|
|
|
|
uint32_t aAttribute)
|
|
|
|
{
|
|
|
|
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
2016-08-24 21:12:09 +03:00
|
|
|
RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
|
2016-06-04 03:49:21 +03:00
|
|
|
TextRangeType textRangeType;
|
2015-01-28 09:27:32 +03:00
|
|
|
switch (aAttribute) {
|
|
|
|
case ATTR_RAW_CLAUSE:
|
|
|
|
case ATTR_SELECTED_RAW_CLAUSE:
|
|
|
|
case ATTR_CONVERTED_CLAUSE:
|
|
|
|
case ATTR_SELECTED_CLAUSE:
|
2016-06-04 03:49:21 +03:00
|
|
|
textRangeType = ToTextRangeType(aAttribute);
|
2015-01-28 09:27:32 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
nsresult rv = IsValidStateForComposition();
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2016-08-24 21:12:09 +03:00
|
|
|
return kungFuDeathGrip->AppendClauseToPendingComposition(aLength, textRangeType);
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TextInputProcessor::SetCaretInPendingComposition(uint32_t aOffset)
|
|
|
|
{
|
|
|
|
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
2016-08-24 21:12:09 +03:00
|
|
|
RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
|
2015-01-28 09:27:32 +03:00
|
|
|
nsresult rv = IsValidStateForComposition();
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2016-08-24 21:12:09 +03:00
|
|
|
return kungFuDeathGrip->SetCaretInPendingComposition(aOffset, 0);
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2018-02-09 19:17:10 +03:00
|
|
|
TextInputProcessor::FlushPendingComposition(nsIDOMEvent* aDOMKeyEvent,
|
2015-02-19 09:50:20 +03:00
|
|
|
uint32_t aKeyFlags,
|
|
|
|
uint8_t aOptionalArgc,
|
|
|
|
bool* aSucceeded)
|
2015-01-28 09:27:32 +03:00
|
|
|
{
|
|
|
|
MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
|
|
|
|
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
2015-02-19 09:50:20 +03:00
|
|
|
|
|
|
|
// Even if this doesn't flush pending composition actually, we need to reset
|
|
|
|
// pending composition for starting next composition with new user input.
|
|
|
|
AutoPendingCompositionResetter resetter(this);
|
|
|
|
|
2015-01-28 09:27:32 +03:00
|
|
|
*aSucceeded = false;
|
2016-08-24 21:12:09 +03:00
|
|
|
RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
|
2015-02-19 09:50:20 +03:00
|
|
|
bool wasComposing = IsComposing();
|
|
|
|
|
2018-02-09 19:17:10 +03:00
|
|
|
RefPtr<KeyboardEvent> keyEvent;
|
|
|
|
if (aDOMKeyEvent) {
|
|
|
|
keyEvent = aDOMKeyEvent->InternalDOMEvent()->AsKeyboardEvent();
|
|
|
|
if (NS_WARN_IF(!keyEvent)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-19 09:50:20 +03:00
|
|
|
WidgetKeyboardEvent* keyboardEvent;
|
|
|
|
nsresult rv =
|
2018-02-09 19:17:10 +03:00
|
|
|
PrepareKeyboardEventForComposition(keyEvent, aKeyFlags, aOptionalArgc,
|
2015-02-19 09:50:20 +03:00
|
|
|
keyboardEvent);
|
2015-01-28 09:27:32 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2015-02-19 09:50:20 +03:00
|
|
|
|
|
|
|
EventDispatcherResult dispatcherResult =
|
|
|
|
MaybeDispatchKeydownForComposition(keyboardEvent, aKeyFlags);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) ||
|
|
|
|
!dispatcherResult.mCanContinue) {
|
|
|
|
return dispatcherResult.mResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Even if the preceding keydown event was consumed, if the composition
|
|
|
|
// was already started, we shouldn't prevent the change of composition.
|
|
|
|
if (dispatcherResult.mDoDefault || wasComposing) {
|
|
|
|
// Preceding keydown event may cause destroying the widget.
|
|
|
|
if (NS_FAILED(IsValidStateForComposition())) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2016-08-24 21:12:09 +03:00
|
|
|
rv = kungFuDeathGrip->FlushPendingComposition(status);
|
2015-02-19 09:50:20 +03:00
|
|
|
*aSucceeded = status != nsEventStatus_eConsumeNoDefault;
|
|
|
|
}
|
|
|
|
|
|
|
|
MaybeDispatchKeyupForComposition(keyboardEvent, aKeyFlags);
|
|
|
|
|
2015-01-28 09:27:32 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2015-02-19 09:50:20 +03:00
|
|
|
return NS_OK;
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2018-02-09 19:17:10 +03:00
|
|
|
TextInputProcessor::CommitComposition(nsIDOMEvent* aDOMKeyEvent,
|
2015-02-19 09:50:20 +03:00
|
|
|
uint32_t aKeyFlags,
|
2015-02-19 09:50:21 +03:00
|
|
|
uint8_t aOptionalArgc)
|
2015-01-28 09:27:32 +03:00
|
|
|
{
|
|
|
|
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
2015-02-19 09:50:20 +03:00
|
|
|
|
2018-02-09 19:17:10 +03:00
|
|
|
RefPtr<KeyboardEvent> keyEvent;
|
|
|
|
if (aDOMKeyEvent) {
|
|
|
|
keyEvent = aDOMKeyEvent->InternalDOMEvent()->AsKeyboardEvent();
|
|
|
|
if (NS_WARN_IF(!keyEvent)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-19 09:50:20 +03:00
|
|
|
WidgetKeyboardEvent* keyboardEvent;
|
|
|
|
nsresult rv =
|
2018-02-09 19:17:10 +03:00
|
|
|
PrepareKeyboardEventForComposition(keyEvent, aKeyFlags, aOptionalArgc,
|
2015-02-19 09:50:20 +03:00
|
|
|
keyboardEvent);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2015-02-19 09:50:21 +03:00
|
|
|
return CommitCompositionInternal(keyboardEvent, aKeyFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TextInputProcessor::CommitCompositionWith(const nsAString& aCommitString,
|
2018-02-09 19:17:10 +03:00
|
|
|
nsIDOMEvent* aDOMKeyEvent,
|
2015-02-19 09:50:21 +03:00
|
|
|
uint32_t aKeyFlags,
|
|
|
|
uint8_t aOptionalArgc,
|
|
|
|
bool* aSucceeded)
|
|
|
|
{
|
|
|
|
MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
|
|
|
|
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
|
|
|
|
2018-02-09 19:17:10 +03:00
|
|
|
RefPtr<KeyboardEvent> keyEvent;
|
|
|
|
if (aDOMKeyEvent) {
|
|
|
|
keyEvent = aDOMKeyEvent->InternalDOMEvent()->AsKeyboardEvent();
|
|
|
|
if (NS_WARN_IF(!keyEvent)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-19 09:50:21 +03:00
|
|
|
WidgetKeyboardEvent* keyboardEvent;
|
|
|
|
nsresult rv =
|
2018-02-09 19:17:10 +03:00
|
|
|
PrepareKeyboardEventForComposition(keyEvent, aKeyFlags, aOptionalArgc,
|
2015-02-19 09:50:21 +03:00
|
|
|
keyboardEvent);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2015-02-19 09:50:20 +03:00
|
|
|
|
|
|
|
return CommitCompositionInternal(keyboardEvent, aKeyFlags,
|
2015-02-19 09:50:21 +03:00
|
|
|
&aCommitString, aSucceeded);
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
2015-02-19 09:50:20 +03:00
|
|
|
TextInputProcessor::CommitCompositionInternal(
|
|
|
|
const WidgetKeyboardEvent* aKeyboardEvent,
|
|
|
|
uint32_t aKeyFlags,
|
|
|
|
const nsAString* aCommitString,
|
|
|
|
bool* aSucceeded)
|
2015-01-28 09:27:32 +03:00
|
|
|
{
|
|
|
|
if (aSucceeded) {
|
|
|
|
*aSucceeded = false;
|
|
|
|
}
|
2016-08-24 21:12:09 +03:00
|
|
|
RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
|
2015-02-19 09:50:20 +03:00
|
|
|
bool wasComposing = IsComposing();
|
|
|
|
|
|
|
|
EventDispatcherResult dispatcherResult =
|
|
|
|
MaybeDispatchKeydownForComposition(aKeyboardEvent, aKeyFlags);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) ||
|
|
|
|
!dispatcherResult.mCanContinue) {
|
|
|
|
return dispatcherResult.mResult;
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
2015-02-19 09:50:20 +03:00
|
|
|
|
|
|
|
// Even if the preceding keydown event was consumed, if the composition
|
|
|
|
// was already started, we shouldn't prevent the commit of composition.
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
if (dispatcherResult.mDoDefault || wasComposing) {
|
|
|
|
// Preceding keydown event may cause destroying the widget.
|
|
|
|
if (NS_FAILED(IsValidStateForComposition())) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2016-08-24 21:12:09 +03:00
|
|
|
rv = kungFuDeathGrip->CommitComposition(status, aCommitString);
|
2015-02-19 09:50:20 +03:00
|
|
|
if (aSucceeded) {
|
|
|
|
*aSucceeded = status != nsEventStatus_eConsumeNoDefault;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MaybeDispatchKeyupForComposition(aKeyboardEvent, aKeyFlags);
|
|
|
|
|
2015-01-28 09:27:32 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2015-02-19 09:50:20 +03:00
|
|
|
return NS_OK;
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2018-02-09 19:17:10 +03:00
|
|
|
TextInputProcessor::CancelComposition(nsIDOMEvent* aDOMKeyEvent,
|
2015-02-19 09:50:20 +03:00
|
|
|
uint32_t aKeyFlags,
|
|
|
|
uint8_t aOptionalArgc)
|
2015-01-28 09:27:32 +03:00
|
|
|
{
|
|
|
|
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
2015-02-19 09:50:20 +03:00
|
|
|
|
2018-02-09 19:17:10 +03:00
|
|
|
RefPtr<KeyboardEvent> keyEvent;
|
|
|
|
if (aDOMKeyEvent) {
|
|
|
|
keyEvent = aDOMKeyEvent->InternalDOMEvent()->AsKeyboardEvent();
|
|
|
|
if (NS_WARN_IF(!keyEvent)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-19 09:50:20 +03:00
|
|
|
WidgetKeyboardEvent* keyboardEvent;
|
|
|
|
nsresult rv =
|
2018-02-09 19:17:10 +03:00
|
|
|
PrepareKeyboardEventForComposition(keyEvent, aKeyFlags, aOptionalArgc,
|
2015-02-19 09:50:20 +03:00
|
|
|
keyboardEvent);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CancelCompositionInternal(keyboardEvent, aKeyFlags);
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
2015-02-19 09:50:20 +03:00
|
|
|
TextInputProcessor::CancelCompositionInternal(
|
|
|
|
const WidgetKeyboardEvent* aKeyboardEvent,
|
|
|
|
uint32_t aKeyFlags)
|
2015-01-28 09:27:32 +03:00
|
|
|
{
|
2016-08-24 21:12:09 +03:00
|
|
|
RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
|
2015-02-19 09:50:20 +03:00
|
|
|
|
|
|
|
EventDispatcherResult dispatcherResult =
|
|
|
|
MaybeDispatchKeydownForComposition(aKeyboardEvent, aKeyFlags);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) ||
|
|
|
|
!dispatcherResult.mCanContinue) {
|
|
|
|
return dispatcherResult.mResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2016-08-24 21:12:09 +03:00
|
|
|
nsresult rv = kungFuDeathGrip->CommitComposition(status, &EmptyString());
|
2015-02-19 09:50:20 +03:00
|
|
|
|
|
|
|
MaybeDispatchKeyupForComposition(aKeyboardEvent, aKeyFlags);
|
|
|
|
|
2015-01-28 09:27:32 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2015-02-19 09:50:20 +03:00
|
|
|
return NS_OK;
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
|
|
|
|
2015-01-28 09:27:32 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
TextInputProcessor::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
|
|
|
|
const IMENotification& aNotification)
|
|
|
|
{
|
|
|
|
// If This is called while this is being initialized, ignore the call.
|
2016-03-16 07:47:47 +03:00
|
|
|
// In such case, this method should return NS_ERROR_NOT_IMPLEMENTED because
|
|
|
|
// we can say, TextInputProcessor doesn't implement any handlers of the
|
|
|
|
// requests and notifications.
|
2015-01-28 09:27:32 +03:00
|
|
|
if (!mDispatcher) {
|
2016-03-16 07:47:47 +03:00
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
2015-01-28 09:27:32 +03:00
|
|
|
}
|
|
|
|
MOZ_ASSERT(aTextEventDispatcher == mDispatcher,
|
|
|
|
"Wrong TextEventDispatcher notifies this");
|
2015-01-28 09:27:33 +03:00
|
|
|
NS_ASSERTION(mForTests || mCallback,
|
|
|
|
"mCallback can be null only when IME is initialized for tests");
|
|
|
|
if (mCallback) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<TextInputProcessorNotification> notification;
|
2015-01-28 09:27:33 +03:00
|
|
|
switch (aNotification.mMessage) {
|
|
|
|
case REQUEST_TO_COMMIT_COMPOSITION: {
|
|
|
|
NS_ASSERTION(aTextEventDispatcher->IsComposing(),
|
|
|
|
"Why is this requested without composition?");
|
|
|
|
notification = new TextInputProcessorNotification("request-to-commit");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case REQUEST_TO_CANCEL_COMPOSITION: {
|
|
|
|
NS_ASSERTION(aTextEventDispatcher->IsComposing(),
|
|
|
|
"Why is this requested without composition?");
|
|
|
|
notification = new TextInputProcessorNotification("request-to-cancel");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NOTIFY_IME_OF_FOCUS:
|
|
|
|
notification = new TextInputProcessorNotification("notify-focus");
|
|
|
|
break;
|
|
|
|
case NOTIFY_IME_OF_BLUR:
|
|
|
|
notification = new TextInputProcessorNotification("notify-blur");
|
|
|
|
break;
|
2017-04-20 14:17:03 +03:00
|
|
|
case NOTIFY_IME_OF_TEXT_CHANGE:
|
|
|
|
notification = new TextInputProcessorNotification(
|
|
|
|
aNotification.mTextChangeData);
|
|
|
|
break;
|
|
|
|
case NOTIFY_IME_OF_SELECTION_CHANGE:
|
|
|
|
notification = new TextInputProcessorNotification(
|
|
|
|
aNotification.mSelectionChangeData);
|
|
|
|
break;
|
|
|
|
case NOTIFY_IME_OF_POSITION_CHANGE:
|
|
|
|
notification = new TextInputProcessorNotification(
|
|
|
|
"notify-position-change");
|
|
|
|
break;
|
2015-01-28 09:27:33 +03:00
|
|
|
default:
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
MOZ_RELEASE_ASSERT(notification);
|
|
|
|
bool result = false;
|
|
|
|
nsresult rv = mCallback->OnNotify(this, notification, &result);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
return result ? NS_OK : NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
2015-01-28 09:27:32 +03:00
|
|
|
switch (aNotification.mMessage) {
|
|
|
|
case REQUEST_TO_COMMIT_COMPOSITION: {
|
|
|
|
NS_ASSERTION(aTextEventDispatcher->IsComposing(),
|
|
|
|
"Why is this requested without composition?");
|
|
|
|
CommitCompositionInternal();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
case REQUEST_TO_CANCEL_COMPOSITION: {
|
|
|
|
NS_ASSERTION(aTextEventDispatcher->IsComposing(),
|
|
|
|
"Why is this requested without composition?");
|
|
|
|
CancelCompositionInternal();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-11 15:24:55 +03:00
|
|
|
NS_IMETHODIMP_(IMENotificationRequests)
|
|
|
|
TextInputProcessor::GetIMENotificationRequests()
|
Bug 1343075 - Use GeckoEditableSupport from PuppetWidget; r=masayuki r=rbarker r=snorp r=esawin
Bug 1343075 - 1a. Add TextEventDispatcherListener::GetIMEUpdatePreference; r=masayuki
Add a GetIMEUpdatePreference method to TextEventDispatcherListener to
optionally control which IME notifications are received by NotifyIME.
This patch also makes nsBaseWidget forward its GetIMEUpdatePreference
call to the widget's native TextEventDispatcherListener.
Bug 1343075 - 1b. Implement GetIMEUpdatePreference for all TextEventDispatcherListener; r=masayuki
This patch implements GetIMEUpdatePreference for all
TextEventDispatcherListener implementations, by moving previous
implementations of nsIWidget::GetIMEUpdatePreference.
Bug 1343075 - 2. Allow setting a PuppetWidget's native TextEventDispatcherListener; r=masayuki
In PuppetWidget, add getter and setter for the widget's native
TextEventDispatcherListener. This allows overriding of PuppetWidget's
default IME handling. For example, on Android, the PuppetWidget's native
TextEventDispatcherListener will communicate directly with Java IME code
in the main process.
Bug 1343075 - 3. Add AIDL interface for main process; r=rbarker
Add AIDL definition and implementation for an interface for the main
process that child processes can access.
Bug 1343075 - 4. Set Gecko thread JNIEnv for child process; r=snorp
Add a JNIEnv* parameter to XRE_SetAndroidChildFds, which is used to set
the Gecko thread JNIEnv for child processes. XRE_SetAndroidChildFds is
the only Android-specific entry point for child processes, so I think
it's the most logical place to initialize JNI.
Bug 1343075 - 5. Support multiple remote GeckoEditableChild; r=esawin
Support remote GeckoEditableChild instances that are created in the
content processes and connect to the parent process GeckoEditableParent
through binders.
Support having multiple GeckoEditableChild instances in GeckoEditable by
keeping track of which child is currently focused, and only allow
calls to/from the focused child by using access tokens.
Bug 1343075 - 6. Add method to get GeckoEditableParent instance; r=esawin
Add IProcessManager.getEditableParent, which a content process can call
to get the GeckoEditableParent instance that corresponds to a given
content process tab, from the main process.
Bug 1343075 - 7. Support GeckoEditableSupport in content processes; r=esawin
Support creating and running GeckoEditableSupport attached to a
PuppetWidget in content processes.
Because we don't know PuppetWidget's lifetime as well as nsWindow's,
when attached to PuppetWidget, we need to attach/detach our native
object on focus/blur, respectively.
Bug 1343075 - 8. Connect GeckoEditableSupport on PuppetWidget creation; r=esawin
Listen to the "tab-child-created" notification and attach our content
process GeckoEditableSupport to the new PuppetWidget.
Bug 1343075 - 9. Update auto-generated bindings; r=me
2017-03-08 06:34:39 +03:00
|
|
|
{
|
2017-04-20 14:17:03 +03:00
|
|
|
// TextInputProcessor should support all change notifications.
|
|
|
|
return IMENotificationRequests(
|
|
|
|
IMENotificationRequests::NOTIFY_TEXT_CHANGE |
|
|
|
|
IMENotificationRequests::NOTIFY_POSITION_CHANGE);
|
Bug 1343075 - Use GeckoEditableSupport from PuppetWidget; r=masayuki r=rbarker r=snorp r=esawin
Bug 1343075 - 1a. Add TextEventDispatcherListener::GetIMEUpdatePreference; r=masayuki
Add a GetIMEUpdatePreference method to TextEventDispatcherListener to
optionally control which IME notifications are received by NotifyIME.
This patch also makes nsBaseWidget forward its GetIMEUpdatePreference
call to the widget's native TextEventDispatcherListener.
Bug 1343075 - 1b. Implement GetIMEUpdatePreference for all TextEventDispatcherListener; r=masayuki
This patch implements GetIMEUpdatePreference for all
TextEventDispatcherListener implementations, by moving previous
implementations of nsIWidget::GetIMEUpdatePreference.
Bug 1343075 - 2. Allow setting a PuppetWidget's native TextEventDispatcherListener; r=masayuki
In PuppetWidget, add getter and setter for the widget's native
TextEventDispatcherListener. This allows overriding of PuppetWidget's
default IME handling. For example, on Android, the PuppetWidget's native
TextEventDispatcherListener will communicate directly with Java IME code
in the main process.
Bug 1343075 - 3. Add AIDL interface for main process; r=rbarker
Add AIDL definition and implementation for an interface for the main
process that child processes can access.
Bug 1343075 - 4. Set Gecko thread JNIEnv for child process; r=snorp
Add a JNIEnv* parameter to XRE_SetAndroidChildFds, which is used to set
the Gecko thread JNIEnv for child processes. XRE_SetAndroidChildFds is
the only Android-specific entry point for child processes, so I think
it's the most logical place to initialize JNI.
Bug 1343075 - 5. Support multiple remote GeckoEditableChild; r=esawin
Support remote GeckoEditableChild instances that are created in the
content processes and connect to the parent process GeckoEditableParent
through binders.
Support having multiple GeckoEditableChild instances in GeckoEditable by
keeping track of which child is currently focused, and only allow
calls to/from the focused child by using access tokens.
Bug 1343075 - 6. Add method to get GeckoEditableParent instance; r=esawin
Add IProcessManager.getEditableParent, which a content process can call
to get the GeckoEditableParent instance that corresponds to a given
content process tab, from the main process.
Bug 1343075 - 7. Support GeckoEditableSupport in content processes; r=esawin
Support creating and running GeckoEditableSupport attached to a
PuppetWidget in content processes.
Because we don't know PuppetWidget's lifetime as well as nsWindow's,
when attached to PuppetWidget, we need to attach/detach our native
object on focus/blur, respectively.
Bug 1343075 - 8. Connect GeckoEditableSupport on PuppetWidget creation; r=esawin
Listen to the "tab-child-created" notification and attach our content
process GeckoEditableSupport to the new PuppetWidget.
Bug 1343075 - 9. Update auto-generated bindings; r=me
2017-03-08 06:34:39 +03:00
|
|
|
}
|
|
|
|
|
2015-01-28 09:27:32 +03:00
|
|
|
NS_IMETHODIMP_(void)
|
|
|
|
TextInputProcessor::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher)
|
|
|
|
{
|
|
|
|
// If This is called while this is being initialized, ignore the call.
|
|
|
|
if (!mDispatcher) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
MOZ_ASSERT(aTextEventDispatcher == mDispatcher,
|
|
|
|
"Wrong TextEventDispatcher notifies this");
|
|
|
|
UnlinkFromTextEventDispatcher();
|
|
|
|
}
|
|
|
|
|
2016-03-16 07:47:48 +03:00
|
|
|
NS_IMETHODIMP_(void)
|
|
|
|
TextInputProcessor::WillDispatchKeyboardEvent(
|
|
|
|
TextEventDispatcher* aTextEventDispatcher,
|
|
|
|
WidgetKeyboardEvent& aKeyboardEvent,
|
|
|
|
uint32_t aIndexOfKeypress,
|
|
|
|
void* aData)
|
|
|
|
{
|
2016-03-19 14:57:11 +03:00
|
|
|
// TextInputProcessor doesn't set alternative char code nor modify charCode
|
|
|
|
// even when Ctrl key is pressed.
|
2016-03-16 07:47:48 +03:00
|
|
|
}
|
|
|
|
|
2015-02-19 09:50:19 +03:00
|
|
|
nsresult
|
|
|
|
TextInputProcessor::PrepareKeyboardEventToDispatch(
|
|
|
|
WidgetKeyboardEvent& aKeyboardEvent,
|
|
|
|
uint32_t aKeyFlags)
|
|
|
|
{
|
|
|
|
if (NS_WARN_IF(aKeyboardEvent.mCodeNameIndex == CODE_NAME_INDEX_USE_STRING)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
if ((aKeyFlags & KEY_NON_PRINTABLE_KEY) &&
|
|
|
|
NS_WARN_IF(aKeyboardEvent.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
if ((aKeyFlags & KEY_FORCE_PRINTABLE_KEY) &&
|
|
|
|
aKeyboardEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING) {
|
|
|
|
aKeyboardEvent.GetDOMKeyName(aKeyboardEvent.mKeyValue);
|
|
|
|
aKeyboardEvent.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
|
|
|
|
}
|
2015-02-19 09:50:19 +03:00
|
|
|
if (aKeyFlags & KEY_KEEP_KEY_LOCATION_STANDARD) {
|
|
|
|
// If .location is initialized with specific value, using
|
|
|
|
// KEY_KEEP_KEY_LOCATION_STANDARD must be a bug of the caller.
|
|
|
|
// Let's throw an exception for notifying the developer of this bug.
|
2016-05-12 12:17:22 +03:00
|
|
|
if (NS_WARN_IF(aKeyboardEvent.mLocation)) {
|
2015-02-19 09:50:19 +03:00
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
2016-05-12 12:17:22 +03:00
|
|
|
} else if (!aKeyboardEvent.mLocation) {
|
|
|
|
// If KeyboardEvent.mLocation is 0, it may be uninitialized. If so, we
|
|
|
|
// should compute proper mLocation value from its .code value.
|
|
|
|
aKeyboardEvent.mLocation =
|
2015-02-19 09:50:19 +03:00
|
|
|
WidgetKeyboardEvent::ComputeLocationFromCodeValue(
|
|
|
|
aKeyboardEvent.mCodeNameIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aKeyFlags & KEY_KEEP_KEYCODE_ZERO) {
|
|
|
|
// If .keyCode is initialized with specific value, using
|
|
|
|
// KEY_KEEP_KEYCODE_ZERO must be a bug of the caller. Let's throw an
|
|
|
|
// exception for notifying the developer of such bug.
|
2016-05-12 11:13:49 +03:00
|
|
|
if (NS_WARN_IF(aKeyboardEvent.mKeyCode)) {
|
2015-02-19 09:50:19 +03:00
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
2016-05-12 11:13:49 +03:00
|
|
|
} else if (!aKeyboardEvent.mKeyCode &&
|
2015-02-19 09:50:19 +03:00
|
|
|
aKeyboardEvent.mKeyNameIndex > KEY_NAME_INDEX_Unidentified &&
|
|
|
|
aKeyboardEvent.mKeyNameIndex < KEY_NAME_INDEX_USE_STRING) {
|
|
|
|
// If KeyboardEvent.keyCode is 0, it may be uninitialized. If so, we may
|
|
|
|
// be able to decide a good .keyCode value if the .key value is a
|
|
|
|
// non-printable key.
|
2016-05-12 11:13:49 +03:00
|
|
|
aKeyboardEvent.mKeyCode =
|
2015-02-19 09:50:19 +03:00
|
|
|
WidgetKeyboardEvent::ComputeKeyCodeFromKeyNameIndex(
|
|
|
|
aKeyboardEvent.mKeyNameIndex);
|
|
|
|
}
|
2015-02-19 09:50:19 +03:00
|
|
|
|
2016-03-15 08:50:16 +03:00
|
|
|
aKeyboardEvent.mIsSynthesizedByTIP = (mForTests)? false : true;
|
|
|
|
|
2015-02-19 09:50:19 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2018-02-09 19:17:10 +03:00
|
|
|
TextInputProcessor::Keydown(nsIDOMEvent* aDOMKeyEvent,
|
2015-02-19 09:50:19 +03:00
|
|
|
uint32_t aKeyFlags,
|
|
|
|
uint8_t aOptionalArgc,
|
2015-08-05 08:58:00 +03:00
|
|
|
uint32_t* aConsumedFlags)
|
2015-02-19 09:50:19 +03:00
|
|
|
{
|
2015-08-05 08:58:00 +03:00
|
|
|
MOZ_RELEASE_ASSERT(aConsumedFlags, "aConsumedFlags must not be nullptr");
|
2015-02-19 09:50:19 +03:00
|
|
|
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
|
|
|
if (!aOptionalArgc) {
|
|
|
|
aKeyFlags = 0;
|
|
|
|
}
|
|
|
|
if (NS_WARN_IF(!aDOMKeyEvent)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
WidgetKeyboardEvent* originalKeyEvent =
|
2018-02-09 19:17:10 +03:00
|
|
|
aDOMKeyEvent->InternalDOMEvent()->WidgetEventPtr()->AsKeyboardEvent();
|
2015-02-19 09:50:19 +03:00
|
|
|
if (NS_WARN_IF(!originalKeyEvent)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
2015-08-05 08:58:00 +03:00
|
|
|
return KeydownInternal(*originalKeyEvent, aKeyFlags, true, *aConsumedFlags);
|
2015-08-04 12:52:00 +03:00
|
|
|
}
|
|
|
|
|
2015-02-19 09:50:20 +03:00
|
|
|
nsresult
|
|
|
|
TextInputProcessor::KeydownInternal(const WidgetKeyboardEvent& aKeyboardEvent,
|
|
|
|
uint32_t aKeyFlags,
|
|
|
|
bool aAllowToDispatchKeypress,
|
2015-08-05 08:58:00 +03:00
|
|
|
uint32_t& aConsumedFlags)
|
2015-02-19 09:50:20 +03:00
|
|
|
{
|
2015-08-05 08:58:00 +03:00
|
|
|
aConsumedFlags = KEYEVENT_NOT_CONSUMED;
|
2015-02-19 09:50:20 +03:00
|
|
|
|
2015-02-19 09:50:19 +03:00
|
|
|
// We shouldn't modify the internal WidgetKeyboardEvent.
|
2015-02-19 09:50:20 +03:00
|
|
|
WidgetKeyboardEvent keyEvent(aKeyboardEvent);
|
2015-02-19 09:50:19 +03:00
|
|
|
nsresult rv = PrepareKeyboardEventToDispatch(keyEvent, aKeyFlags);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2015-02-19 09:50:20 +03:00
|
|
|
|
2015-08-05 08:58:00 +03:00
|
|
|
aConsumedFlags = (aKeyFlags & KEY_DEFAULT_PREVENTED) ? KEYDOWN_IS_CONSUMED :
|
|
|
|
KEYEVENT_NOT_CONSUMED;
|
2015-02-19 09:50:19 +03:00
|
|
|
|
|
|
|
if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent.mKeyNameIndex)) {
|
|
|
|
ModifierKeyData modifierKeyData(keyEvent);
|
|
|
|
if (WidgetKeyboardEvent::IsLockableModifier(keyEvent.mKeyNameIndex)) {
|
|
|
|
// If the modifier key is lockable modifier key such as CapsLock,
|
|
|
|
// let's toggle modifier key state at keydown.
|
|
|
|
ToggleModifierKey(modifierKeyData);
|
|
|
|
} else {
|
|
|
|
// Activate modifier flag before dispatching keydown event (i.e., keydown
|
|
|
|
// event should indicate the releasing modifier is active.
|
|
|
|
ActivateModifierKey(modifierKeyData);
|
|
|
|
}
|
2015-02-19 09:50:19 +03:00
|
|
|
if (aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
} else if (NS_WARN_IF(aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
2015-02-19 09:50:19 +03:00
|
|
|
}
|
2016-03-31 11:03:00 +03:00
|
|
|
keyEvent.mModifiers = GetActiveModifiers();
|
2015-02-19 09:50:19 +03:00
|
|
|
|
Bug 1446253 - Make EventUtils.synthesizeComposition() dispatch keydown and keyup event in default r=smaug
We'll start to dispatch keydown event and keyup event even during composition.
So, for testing those events won't break our UI, we should make
EventUtils.synhtesizeComposition() and EventUtils.synthesizeCompositionChange()
dispatch keydown event and keyup event even if callers don't specify keyboard
event explicitly.
Typically, "keydown" event is marked as "processed by IME", i.e., keyCode
value is set to DOM_VK_PROCESSKEY and key is set to "Process", with our
widget which handles native IME and key input. On the other hand, "keyup"
is NOT marked as so.
Therefore, this patch makes TextInputProcessor emulates this behavior without
any new special flags. And for making possible to emulate special cases,
this patch adds two flags to nsITextInputProcessor. One is
KEY_DONT_MARK_KEYDOWN_AS_PROCESSED. The other is KEY_MARK_KEYUP_AS_PROCESSED.
Unfortunately, those flags have opposite meaning but this must be better than
making necessary to one flag for emulating usual keydown/keyup events.
Finally, this makes some tests specify better keyboard information to
synthesizeComposition() and synthesizeCompositionChange() to emulate
actual keyboard events during composition.
MozReview-Commit-ID: ItYaXILkNQE
--HG--
extra : rebase_source : e50cc77c1efbc12686d7ea334d41926c7392b30d
2018-03-16 16:35:07 +03:00
|
|
|
if (!aAllowToDispatchKeypress &&
|
|
|
|
!(aKeyFlags & KEY_DONT_MARK_KEYDOWN_AS_PROCESSED)) {
|
|
|
|
keyEvent.mKeyCode = NS_VK_PROCESSKEY;
|
|
|
|
keyEvent.mKeyNameIndex = KEY_NAME_INDEX_Process;
|
|
|
|
}
|
|
|
|
|
2016-08-24 21:12:09 +03:00
|
|
|
RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
|
2015-02-19 09:50:19 +03:00
|
|
|
rv = IsValidStateForComposition();
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2015-08-05 08:58:00 +03:00
|
|
|
nsEventStatus status = aConsumedFlags ? nsEventStatus_eConsumeNoDefault :
|
|
|
|
nsEventStatus_eIgnore;
|
2016-08-24 21:12:09 +03:00
|
|
|
if (!kungFuDeathGrip->DispatchKeyboardEvent(eKeyDown, keyEvent, status)) {
|
2015-02-19 09:50:19 +03:00
|
|
|
// If keydown event isn't dispatched, we don't need to dispatch keypress
|
|
|
|
// events.
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2015-08-05 08:58:00 +03:00
|
|
|
aConsumedFlags |=
|
|
|
|
(status == nsEventStatus_eConsumeNoDefault) ? KEYDOWN_IS_CONSUMED :
|
|
|
|
KEYEVENT_NOT_CONSUMED;
|
|
|
|
|
|
|
|
if (aAllowToDispatchKeypress &&
|
2016-08-24 21:12:09 +03:00
|
|
|
kungFuDeathGrip->MaybeDispatchKeypressEvents(keyEvent, status)) {
|
2015-08-05 08:58:00 +03:00
|
|
|
aConsumedFlags |=
|
|
|
|
(status == nsEventStatus_eConsumeNoDefault) ? KEYPRESS_IS_CONSUMED :
|
|
|
|
KEYEVENT_NOT_CONSUMED;
|
2015-02-19 09:50:20 +03:00
|
|
|
}
|
2015-02-19 09:50:19 +03:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2018-02-09 19:17:10 +03:00
|
|
|
TextInputProcessor::Keyup(nsIDOMEvent* aDOMKeyEvent,
|
2015-02-19 09:50:19 +03:00
|
|
|
uint32_t aKeyFlags,
|
|
|
|
uint8_t aOptionalArgc,
|
|
|
|
bool* aDoDefault)
|
|
|
|
{
|
|
|
|
MOZ_RELEASE_ASSERT(aDoDefault, "aDoDefault must not be nullptr");
|
|
|
|
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
|
|
|
if (!aOptionalArgc) {
|
|
|
|
aKeyFlags = 0;
|
|
|
|
}
|
|
|
|
if (NS_WARN_IF(!aDOMKeyEvent)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
WidgetKeyboardEvent* originalKeyEvent =
|
2018-02-09 19:17:10 +03:00
|
|
|
aDOMKeyEvent->InternalDOMEvent()->WidgetEventPtr()->AsKeyboardEvent();
|
2015-02-19 09:50:19 +03:00
|
|
|
if (NS_WARN_IF(!originalKeyEvent)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
2015-02-19 09:50:20 +03:00
|
|
|
return KeyupInternal(*originalKeyEvent, aKeyFlags, *aDoDefault);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
TextInputProcessor::KeyupInternal(const WidgetKeyboardEvent& aKeyboardEvent,
|
|
|
|
uint32_t aKeyFlags,
|
|
|
|
bool& aDoDefault)
|
|
|
|
{
|
|
|
|
aDoDefault = false;
|
|
|
|
|
2015-02-19 09:50:19 +03:00
|
|
|
// We shouldn't modify the internal WidgetKeyboardEvent.
|
2015-02-19 09:50:20 +03:00
|
|
|
WidgetKeyboardEvent keyEvent(aKeyboardEvent);
|
2015-02-19 09:50:19 +03:00
|
|
|
nsresult rv = PrepareKeyboardEventToDispatch(keyEvent, aKeyFlags);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2015-02-19 09:50:20 +03:00
|
|
|
|
|
|
|
aDoDefault = !(aKeyFlags & KEY_DEFAULT_PREVENTED);
|
2015-02-19 09:50:19 +03:00
|
|
|
|
2015-02-19 09:50:19 +03:00
|
|
|
if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent.mKeyNameIndex)) {
|
|
|
|
if (!WidgetKeyboardEvent::IsLockableModifier(keyEvent.mKeyNameIndex)) {
|
|
|
|
// Inactivate modifier flag before dispatching keyup event (i.e., keyup
|
|
|
|
// event shouldn't indicate the releasing modifier is active.
|
|
|
|
InactivateModifierKey(ModifierKeyData(keyEvent));
|
|
|
|
}
|
|
|
|
if (aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
} else if (NS_WARN_IF(aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
2015-02-19 09:50:19 +03:00
|
|
|
}
|
2016-03-31 11:03:00 +03:00
|
|
|
keyEvent.mModifiers = GetActiveModifiers();
|
2015-02-19 09:50:19 +03:00
|
|
|
|
Bug 1446253 - Make EventUtils.synthesizeComposition() dispatch keydown and keyup event in default r=smaug
We'll start to dispatch keydown event and keyup event even during composition.
So, for testing those events won't break our UI, we should make
EventUtils.synhtesizeComposition() and EventUtils.synthesizeCompositionChange()
dispatch keydown event and keyup event even if callers don't specify keyboard
event explicitly.
Typically, "keydown" event is marked as "processed by IME", i.e., keyCode
value is set to DOM_VK_PROCESSKEY and key is set to "Process", with our
widget which handles native IME and key input. On the other hand, "keyup"
is NOT marked as so.
Therefore, this patch makes TextInputProcessor emulates this behavior without
any new special flags. And for making possible to emulate special cases,
this patch adds two flags to nsITextInputProcessor. One is
KEY_DONT_MARK_KEYDOWN_AS_PROCESSED. The other is KEY_MARK_KEYUP_AS_PROCESSED.
Unfortunately, those flags have opposite meaning but this must be better than
making necessary to one flag for emulating usual keydown/keyup events.
Finally, this makes some tests specify better keyboard information to
synthesizeComposition() and synthesizeCompositionChange() to emulate
actual keyboard events during composition.
MozReview-Commit-ID: ItYaXILkNQE
--HG--
extra : rebase_source : e50cc77c1efbc12686d7ea334d41926c7392b30d
2018-03-16 16:35:07 +03:00
|
|
|
if (aKeyFlags & KEY_MARK_KEYUP_AS_PROCESSED) {
|
|
|
|
keyEvent.mKeyCode = NS_VK_PROCESSKEY;
|
|
|
|
keyEvent.mKeyNameIndex = KEY_NAME_INDEX_Process;
|
|
|
|
}
|
|
|
|
|
2016-08-24 21:12:09 +03:00
|
|
|
RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
|
2015-02-19 09:50:19 +03:00
|
|
|
rv = IsValidStateForComposition();
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2015-02-19 09:50:20 +03:00
|
|
|
nsEventStatus status = aDoDefault ? nsEventStatus_eIgnore :
|
|
|
|
nsEventStatus_eConsumeNoDefault;
|
2016-08-24 21:12:09 +03:00
|
|
|
kungFuDeathGrip->DispatchKeyboardEvent(eKeyUp, keyEvent, status);
|
2015-02-19 09:50:20 +03:00
|
|
|
aDoDefault = (status != nsEventStatus_eConsumeNoDefault);
|
2015-02-19 09:50:19 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2015-02-19 09:50:19 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
TextInputProcessor::GetModifierState(const nsAString& aModifierKeyName,
|
|
|
|
bool* aActive)
|
|
|
|
{
|
|
|
|
MOZ_RELEASE_ASSERT(aActive, "aActive must not be null");
|
|
|
|
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
|
|
|
if (!mModifierKeyDataArray) {
|
|
|
|
*aActive = false;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
Modifiers activeModifiers = mModifierKeyDataArray->GetActiveModifiers();
|
|
|
|
Modifiers modifier = WidgetInputEvent::GetModifier(aModifierKeyName);
|
|
|
|
*aActive = ((activeModifiers & modifier) != 0);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TextInputProcessor::ShareModifierStateOf(nsITextInputProcessor* aOther)
|
|
|
|
{
|
|
|
|
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
|
|
|
if (!aOther) {
|
|
|
|
mModifierKeyDataArray = nullptr;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
TextInputProcessor* other = static_cast<TextInputProcessor*>(aOther);
|
|
|
|
if (!other->mModifierKeyDataArray) {
|
|
|
|
other->mModifierKeyDataArray = new ModifierKeyDataArray();
|
|
|
|
}
|
|
|
|
mModifierKeyDataArray = other->mModifierKeyDataArray;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2015-02-19 09:50:20 +03:00
|
|
|
/******************************************************************************
|
|
|
|
* TextInputProcessor::AutoPendingCompositionResetter
|
|
|
|
******************************************************************************/
|
|
|
|
TextInputProcessor::AutoPendingCompositionResetter::
|
|
|
|
AutoPendingCompositionResetter(TextInputProcessor* aTIP)
|
|
|
|
: mTIP(aTIP)
|
|
|
|
{
|
|
|
|
MOZ_RELEASE_ASSERT(mTIP.get(), "mTIP must not be null");
|
|
|
|
}
|
|
|
|
|
|
|
|
TextInputProcessor::AutoPendingCompositionResetter::
|
|
|
|
~AutoPendingCompositionResetter()
|
|
|
|
{
|
|
|
|
if (mTIP->mDispatcher) {
|
|
|
|
mTIP->mDispatcher->ClearPendingComposition();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-19 09:50:19 +03:00
|
|
|
/******************************************************************************
|
|
|
|
* TextInputProcessor::ModifierKeyData
|
|
|
|
******************************************************************************/
|
|
|
|
TextInputProcessor::ModifierKeyData::ModifierKeyData(
|
|
|
|
const WidgetKeyboardEvent& aKeyboardEvent)
|
|
|
|
: mKeyNameIndex(aKeyboardEvent.mKeyNameIndex)
|
|
|
|
, mCodeNameIndex(aKeyboardEvent.mCodeNameIndex)
|
|
|
|
{
|
|
|
|
mModifier = WidgetKeyboardEvent::GetModifierForKeyName(mKeyNameIndex);
|
|
|
|
MOZ_ASSERT(mModifier, "mKeyNameIndex must be a modifier key name");
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* TextInputProcessor::ModifierKeyDataArray
|
|
|
|
******************************************************************************/
|
|
|
|
Modifiers
|
|
|
|
TextInputProcessor::ModifierKeyDataArray::GetActiveModifiers() const
|
|
|
|
{
|
|
|
|
Modifiers result = MODIFIER_NONE;
|
|
|
|
for (uint32_t i = 0; i < Length(); i++) {
|
|
|
|
result |= ElementAt(i).mModifier;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TextInputProcessor::ModifierKeyDataArray::ActivateModifierKey(
|
|
|
|
const TextInputProcessor::ModifierKeyData& aModifierKeyData)
|
|
|
|
{
|
|
|
|
if (Contains(aModifierKeyData)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
AppendElement(aModifierKeyData);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TextInputProcessor::ModifierKeyDataArray::InactivateModifierKey(
|
|
|
|
const TextInputProcessor::ModifierKeyData& aModifierKeyData)
|
|
|
|
{
|
|
|
|
RemoveElement(aModifierKeyData);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TextInputProcessor::ModifierKeyDataArray::ToggleModifierKey(
|
|
|
|
const TextInputProcessor::ModifierKeyData& aModifierKeyData)
|
|
|
|
{
|
|
|
|
auto index = IndexOf(aModifierKeyData);
|
|
|
|
if (index == NoIndex) {
|
|
|
|
AppendElement(aModifierKeyData);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
RemoveElementAt(index);
|
|
|
|
}
|
|
|
|
|
2015-01-28 09:27:32 +03:00
|
|
|
} // namespace mozilla
|