зеркало из https://github.com/mozilla/gecko-dev.git
Bug 917322 part.15 Create TextEventDispatcherListener abstract class for listening notifications to IME r=smaug, sr=smaug
This commit is contained in:
Родитель
b1225d076e
Коммит
66d42c3189
|
@ -3,6 +3,7 @@
|
|||
* 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 "mozilla/EventForwards.h"
|
||||
#include "mozilla/TextEventDispatcher.h"
|
||||
#include "mozilla/TextInputProcessor.h"
|
||||
#include "nsIDocShell.h"
|
||||
|
@ -10,14 +11,17 @@
|
|||
#include "nsPIDOMWindow.h"
|
||||
#include "nsPresContext.h"
|
||||
|
||||
using namespace mozilla::widget;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
NS_IMPL_ISUPPORTS(TextInputProcessor,
|
||||
nsITextInputProcessor)
|
||||
nsITextInputProcessor,
|
||||
TextEventDispatcherListener,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
TextInputProcessor::TextInputProcessor()
|
||||
: mDispatcher(nullptr)
|
||||
, mIsInitialized(false)
|
||||
, mForTests(false)
|
||||
{
|
||||
}
|
||||
|
@ -50,8 +54,6 @@ TextInputProcessor::InitInternal(nsIDOMWindow* aWindow,
|
|||
bool& aSucceeded)
|
||||
{
|
||||
aSucceeded = false;
|
||||
bool wasInitialized = mIsInitialized;
|
||||
mIsInitialized = false;
|
||||
if (NS_WARN_IF(!aWindow)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
@ -82,51 +84,63 @@ TextInputProcessor::InitInternal(nsIDOMWindow* aWindow,
|
|||
// If the instance was initialized and is being initialized for same
|
||||
// dispatcher and same purpose, we don't need to initialize the dispatcher
|
||||
// again.
|
||||
if (wasInitialized && dispatcher == mDispatcher && aForTests == mForTests) {
|
||||
mIsInitialized = true;
|
||||
if (mDispatcher && dispatcher == mDispatcher && aForTests == mForTests) {
|
||||
aSucceeded = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (aForTests) {
|
||||
rv = dispatcher->InitForTests();
|
||||
} else {
|
||||
rv = dispatcher->Init();
|
||||
// If this instance is composing, don't allow to initialize again.
|
||||
if (mDispatcher && mDispatcher->IsComposing()) {
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
// Another IME framework is using it now.
|
||||
if (rv == NS_ERROR_ALREADY_INITIALIZED) {
|
||||
return NS_OK; // Don't throw exception.
|
||||
// And also if another instance is composing with the new dispatcher, it'll
|
||||
// fail to steal its ownership. Then, we should not throw an exception,
|
||||
// just return false.
|
||||
if (dispatcher->IsComposing()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// This instance has finished preparing to link to the dispatcher. Therefore,
|
||||
// let's forget the old dispatcher and purpose.
|
||||
UnlinkFromTextEventDispatcher();
|
||||
|
||||
if (aForTests) {
|
||||
rv = dispatcher->InitForTests(this);
|
||||
} else {
|
||||
rv = dispatcher->Init(this);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mIsInitialized = true;
|
||||
mForTests = aForTests;
|
||||
mDispatcher = dispatcher;
|
||||
mForTests = aForTests;
|
||||
aSucceeded = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TextInputProcessor::IsValidStateForComposition() const
|
||||
void
|
||||
TextInputProcessor::UnlinkFromTextEventDispatcher()
|
||||
{
|
||||
if (NS_WARN_IF(!mIsInitialized)) {
|
||||
mDispatcher = nullptr;
|
||||
mForTests = false;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TextInputProcessor::IsValidStateForComposition()
|
||||
{
|
||||
if (NS_WARN_IF(!mDispatcher)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!mDispatcher) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsresult rv = mDispatcher->GetState();
|
||||
if (rv != NS_ERROR_NOT_INITIALIZED) {
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return mForTests ? mDispatcher->InitForTests() : mDispatcher->Init();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -223,19 +237,31 @@ TextInputProcessor::CommitComposition(const nsAString& aCommitString,
|
|||
{
|
||||
MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
|
||||
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
const nsAString* commitString =
|
||||
aOptionalArgc >= 1 ? &aCommitString : nullptr;
|
||||
return CommitCompositionInternal(commitString, aSucceeded);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TextInputProcessor::CommitCompositionInternal(const nsAString* aCommitString,
|
||||
bool* aSucceeded)
|
||||
{
|
||||
if (aSucceeded) {
|
||||
*aSucceeded = false;
|
||||
}
|
||||
nsRefPtr<TextEventDispatcher> kungfuDeathGrip(mDispatcher);
|
||||
nsresult rv = IsValidStateForComposition();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
const nsAString* commitString =
|
||||
aOptionalArgc >= 1 ? &aCommitString : nullptr;
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
rv = mDispatcher->CommitComposition(status, commitString);
|
||||
rv = mDispatcher->CommitComposition(status, aCommitString);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
*aSucceeded = status != nsEventStatus_eConsumeNoDefault;
|
||||
if (aSucceeded) {
|
||||
*aSucceeded = status != nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -243,6 +269,12 @@ NS_IMETHODIMP
|
|||
TextInputProcessor::CancelComposition()
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
return CancelCompositionInternal();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TextInputProcessor::CancelCompositionInternal()
|
||||
{
|
||||
nsRefPtr<TextEventDispatcher> kungfuDeathGrip(mDispatcher);
|
||||
nsresult rv = IsValidStateForComposition();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
|
@ -252,4 +284,44 @@ TextInputProcessor::CancelComposition()
|
|||
return mDispatcher->CommitComposition(status, &EmptyString());
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TextInputProcessor::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
|
||||
const IMENotification& aNotification)
|
||||
{
|
||||
// If This is called while this is being initialized, ignore the call.
|
||||
if (!mDispatcher) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
MOZ_ASSERT(aTextEventDispatcher == mDispatcher,
|
||||
"Wrong TextEventDispatcher notifies this");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#ifndef mozilla_dom_textinputprocessor_h_
|
||||
#define mozilla_dom_textinputprocessor_h_
|
||||
|
||||
#include "mozilla/TextEventDispatcherListener.h"
|
||||
#include "nsITextInputProcessor.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -15,7 +16,9 @@ class TextEventDispatcher;
|
|||
} // namespace widget
|
||||
|
||||
class TextInputProcessor MOZ_FINAL : public nsITextInputProcessor
|
||||
, public widget::TextEventDispatcherListener
|
||||
{
|
||||
typedef mozilla::widget::IMENotification IMENotification;
|
||||
typedef mozilla::widget::TextEventDispatcher TextEventDispatcher;
|
||||
|
||||
public:
|
||||
|
@ -24,16 +27,25 @@ public:
|
|||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSITEXTINPUTPROCESSOR
|
||||
|
||||
// TextEventDispatcherListener
|
||||
NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
|
||||
const IMENotification& aNotification) MOZ_OVERRIDE;
|
||||
NS_IMETHOD_(void)
|
||||
OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
~TextInputProcessor();
|
||||
|
||||
nsresult InitInternal(nsIDOMWindow* aWindow,
|
||||
bool aForTests,
|
||||
bool& aSucceeded);
|
||||
nsresult IsValidStateForComposition() const;
|
||||
nsresult CommitCompositionInternal(const nsAString* aCommitString = nullptr,
|
||||
bool* aSucceeded = nullptr);
|
||||
nsresult CancelCompositionInternal();
|
||||
nsresult IsValidStateForComposition();
|
||||
void UnlinkFromTextEventDispatcher();
|
||||
|
||||
TextEventDispatcher* mDispatcher; // [Weak]
|
||||
bool mIsInitialized;
|
||||
bool mForTests;
|
||||
};
|
||||
|
||||
|
|
|
@ -881,9 +881,9 @@ function _getTIP(aWindow)
|
|||
aWindow._EU_TIP =
|
||||
_EU_Cc["@mozilla.org/text-input-processor;1"].
|
||||
createInstance(_EU_Ci.nsITextInputProcessor);
|
||||
if (!aWindow._EU_TIP.initForTests(aWindow)) {
|
||||
aWindow._EU_TIP = null;
|
||||
}
|
||||
}
|
||||
if (!aWindow._EU_TIP.initForTests(aWindow)) {
|
||||
aWindow._EU_TIP = null;
|
||||
}
|
||||
return aWindow._EU_TIP;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ namespace widget {
|
|||
|
||||
TextEventDispatcher::TextEventDispatcher(nsIWidget* aWidget)
|
||||
: mWidget(aWidget)
|
||||
, mInitialized(false)
|
||||
, mForTests(false)
|
||||
, mIsComposing(false)
|
||||
{
|
||||
|
@ -29,26 +28,39 @@ TextEventDispatcher::TextEventDispatcher(nsIWidget* aWidget)
|
|||
}
|
||||
|
||||
nsresult
|
||||
TextEventDispatcher::Init()
|
||||
TextEventDispatcher::Init(TextEventDispatcherListener* aListener)
|
||||
{
|
||||
if (mInitialized) {
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
MOZ_ASSERT(!mIsComposing, "There should not be active composition");
|
||||
mInitialized = true;
|
||||
mForTests = false;
|
||||
return NS_OK;
|
||||
return InitInternal(aListener, false);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TextEventDispatcher::InitForTests()
|
||||
TextEventDispatcher::InitForTests(TextEventDispatcherListener* aListener)
|
||||
{
|
||||
if (mInitialized) {
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
return InitInternal(aListener, true);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TextEventDispatcher::InitInternal(TextEventDispatcherListener* aListener,
|
||||
bool aForTests)
|
||||
{
|
||||
if (NS_WARN_IF(!aListener)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
|
||||
if (listener) {
|
||||
if (listener == aListener && mForTests == aForTests) {
|
||||
return NS_OK;
|
||||
}
|
||||
// If this has composition, any other listener can steal ownership.
|
||||
if (IsComposing()) {
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
}
|
||||
mListener = do_GetWeakReference(aListener);
|
||||
mForTests = aForTests;
|
||||
if (listener && listener != aListener) {
|
||||
listener->OnRemovedFrom(this);
|
||||
}
|
||||
MOZ_ASSERT(!mIsComposing, "There should not be active composition");
|
||||
mInitialized = true;
|
||||
mForTests = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -57,12 +69,18 @@ TextEventDispatcher::OnDestroyWidget()
|
|||
{
|
||||
mWidget = nullptr;
|
||||
mPendingComposition.Clear();
|
||||
nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
|
||||
mListener = nullptr;
|
||||
if (listener) {
|
||||
listener->OnRemovedFrom(this);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
TextEventDispatcher::GetState() const
|
||||
{
|
||||
if (!mInitialized) {
|
||||
nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
|
||||
if (!listener) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (!mWidget || mWidget->Destroyed()) {
|
||||
|
@ -170,7 +188,6 @@ TextEventDispatcher::CommitComposition(nsEventStatus& aStatus,
|
|||
|
||||
// End current composition and make this free for other IMEs.
|
||||
mIsComposing = false;
|
||||
mInitialized = false;
|
||||
|
||||
uint32_t message = aCommitString ? NS_COMPOSITION_COMMIT :
|
||||
NS_COMPOSITION_COMMIT_AS_IS;
|
||||
|
@ -190,22 +207,18 @@ TextEventDispatcher::CommitComposition(nsEventStatus& aStatus,
|
|||
nsresult
|
||||
TextEventDispatcher::NotifyIME(const IMENotification& aIMENotification)
|
||||
{
|
||||
switch (aIMENotification.mMessage) {
|
||||
case REQUEST_TO_COMMIT_COMPOSITION: {
|
||||
NS_ASSERTION(mIsComposing, "Why is this requested without composition?");
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
CommitComposition(status);
|
||||
return NS_OK;
|
||||
}
|
||||
case REQUEST_TO_CANCEL_COMPOSITION: {
|
||||
NS_ASSERTION(mIsComposing, "Why is this requested without composition?");
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
CommitComposition(status, &EmptyString());
|
||||
return NS_OK;
|
||||
}
|
||||
default:
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
|
||||
if (!listener) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
nsresult rv = listener->NotifyIME(this, aIMENotification);
|
||||
// If the listener isn't available, it means that it cannot handle the
|
||||
// notification or request for now. In this case, we should return
|
||||
// NS_ERROR_NOT_IMPLEMENTED because it's not implemented at such moment.
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "nsString.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/TextEventDispatcherListener.h"
|
||||
#include "mozilla/TextRange.h"
|
||||
|
||||
class nsIWidget;
|
||||
|
@ -42,12 +43,20 @@ public:
|
|||
|
||||
/**
|
||||
* Initializes the instance for IME or automated test. Either IME or tests
|
||||
* need to call one of them before starting composition every time. If they
|
||||
* return NS_ERROR_ALREADY_INITIALIZED, it means that another IME composes
|
||||
* with the instance. Then, the caller shouldn't start composition.
|
||||
* need to call one of them before starting composition. If they return
|
||||
* NS_ERROR_ALREADY_INITIALIZED, it means that the listener already listens
|
||||
* notifications from TextEventDispatcher for same purpose (for IME or tests).
|
||||
* If this returns another error, the caller shouldn't keep starting
|
||||
* composition.
|
||||
*
|
||||
* @param aListener Specify the listener to listen notifications and
|
||||
* requests. This must not be null.
|
||||
* NOTE: aListener is stored as weak reference in
|
||||
* TextEventDispatcher. See mListener
|
||||
* definition below.
|
||||
*/
|
||||
nsresult Init();
|
||||
nsresult InitForTests();
|
||||
nsresult Init(TextEventDispatcherListener* aListener);
|
||||
nsresult InitForTests(TextEventDispatcherListener* aListener);
|
||||
|
||||
/**
|
||||
* OnDestroyWidget() is called when mWidget is being destroyed.
|
||||
|
@ -154,6 +163,12 @@ private:
|
|||
// Note that mWidget may be destroyed already (i.e., mWidget->Destroyed() may
|
||||
// return true).
|
||||
nsIWidget* mWidget;
|
||||
// mListener is a weak reference to TextEventDispatcherListener. That might
|
||||
// be referred by JS. Therefore, the listener might be difficult to release
|
||||
// itself if this is a strong reference. Additionally, it's difficult to
|
||||
// check if a method to uninstall the listener is called by valid instance.
|
||||
// So, using weak reference is the best way in this case.
|
||||
nsWeakPtr mListener;
|
||||
|
||||
// mPendingComposition stores new composition string temporarily.
|
||||
// These values will be used for dispatching NS_COMPOSITION_CHANGE event
|
||||
|
@ -178,11 +193,12 @@ private:
|
|||
};
|
||||
PendingComposition mPendingComposition;
|
||||
|
||||
bool mInitialized;
|
||||
bool mForTests;
|
||||
// See IsComposing().
|
||||
bool mIsComposing;
|
||||
|
||||
nsresult InitInternal(TextEventDispatcherListener* aListener, bool aForTests);
|
||||
|
||||
/**
|
||||
* InitEvent() initializes aEvent. This must be called before dispatching
|
||||
* the event.
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_textinputdispatcherlistener_h_
|
||||
#define mozilla_textinputdispatcherlistener_h_
|
||||
|
||||
#include "nsWeakReference.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace widget {
|
||||
|
||||
class TextEventDispatcher;
|
||||
struct IMENotification;
|
||||
|
||||
#define NS_TEXT_INPUT_PROXY_LISTENER_IID \
|
||||
{ 0xf2226f55, 0x6ddb, 0x40d5, \
|
||||
{ 0x8a, 0x24, 0xce, 0x4d, 0x5b, 0x38, 0x15, 0xf0 } };
|
||||
|
||||
class TextEventDispatcherListener : public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEXT_INPUT_PROXY_LISTENER_IID)
|
||||
|
||||
/**
|
||||
* NotifyIME() is called by TextEventDispatcher::NotifyIME(). This is a
|
||||
* notification or request to IME. See document of nsIWidget::NotifyIME()
|
||||
* for the detail.
|
||||
*/
|
||||
NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
|
||||
const IMENotification& aNotification) = 0;
|
||||
|
||||
/**
|
||||
* OnRemovedFrom() is called when the TextEventDispatcher stops working and
|
||||
* is releasing the listener.
|
||||
*/
|
||||
NS_IMETHOD_(void) OnRemovedFrom(
|
||||
TextEventDispatcher* aTextEventDispatcher) = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(TextEventDispatcherListener,
|
||||
NS_TEXT_INPUT_PROXY_LISTENER_IID)
|
||||
|
||||
} // namespace widget
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // #ifndef mozilla_textinputdispatcherlistener_h_
|
|
@ -121,6 +121,7 @@ EXPORTS.mozilla += [
|
|||
'MiscEvents.h',
|
||||
'MouseEvents.h',
|
||||
'TextEventDispatcher.h',
|
||||
'TextEventDispatcherListener.h',
|
||||
'TextEvents.h',
|
||||
'TextRange.h',
|
||||
'TouchEvents.h',
|
||||
|
|
Загрузка…
Ссылка в новой задаче