Bug 917322 part.1 Create mozilla::widget::TextEventDispatcher class r=smaug, sr=smaug

This commit is contained in:
Masayuki Nakano 2015-01-28 15:27:30 +09:00
parent f0c4dad7b8
commit 2e23da33fe
6 changed files with 351 additions and 2 deletions

View File

@ -0,0 +1,171 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/TextEvents.h"
#include "mozilla/TextEventDispatcher.h"
#include "nsIDocShell.h"
#include "nsIFrame.h"
#include "nsIPresShell.h"
#include "nsIWidget.h"
#include "nsPIDOMWindow.h"
#include "nsView.h"
namespace mozilla {
namespace widget {
/******************************************************************************
* TextEventDispatcher
*****************************************************************************/
TextEventDispatcher::TextEventDispatcher(nsIWidget* aWidget)
: mWidget(aWidget)
, mInitialized(false)
, mForTests(false)
{
MOZ_RELEASE_ASSERT(mWidget, "aWidget must not be nullptr");
}
nsresult
TextEventDispatcher::Init()
{
if (mInitialized) {
return NS_ERROR_ALREADY_INITIALIZED;
}
mInitialized = true;
mForTests = false;
return NS_OK;
}
nsresult
TextEventDispatcher::InitForTests()
{
if (mInitialized) {
return NS_ERROR_ALREADY_INITIALIZED;
}
mInitialized = true;
mForTests = true;
return NS_OK;
}
void
TextEventDispatcher::OnDestroyWidget()
{
mWidget = nullptr;
mPendingComposition.Clear();
}
/******************************************************************************
* TextEventDispatcher::PendingComposition
*****************************************************************************/
TextEventDispatcher::PendingComposition::PendingComposition()
{
mClauses = new TextRangeArray();
Clear();
}
void
TextEventDispatcher::PendingComposition::Clear()
{
mString.Truncate();
mClauses->Clear();
mCaret.mRangeType = 0;
}
nsresult
TextEventDispatcher::PendingComposition::SetString(const nsAString& aString)
{
mString = aString;
return NS_OK;
}
nsresult
TextEventDispatcher::PendingComposition::AppendClause(uint32_t aLength,
uint32_t aAttribute)
{
if (NS_WARN_IF(!aLength)) {
return NS_ERROR_INVALID_ARG;
}
switch (aAttribute) {
case NS_TEXTRANGE_RAWINPUT:
case NS_TEXTRANGE_SELECTEDRAWTEXT:
case NS_TEXTRANGE_CONVERTEDTEXT:
case NS_TEXTRANGE_SELECTEDCONVERTEDTEXT: {
TextRange textRange;
textRange.mStartOffset =
mClauses->IsEmpty() ? 0 : mClauses->LastElement().mEndOffset;
textRange.mEndOffset = textRange.mStartOffset + aLength;
textRange.mRangeType = aAttribute;
mClauses->AppendElement(textRange);
return NS_OK;
}
default:
return NS_ERROR_INVALID_ARG;
}
}
nsresult
TextEventDispatcher::PendingComposition::SetCaret(uint32_t aOffset,
uint32_t aLength)
{
mCaret.mStartOffset = aOffset;
mCaret.mEndOffset = mCaret.mStartOffset + aLength;
mCaret.mRangeType = NS_TEXTRANGE_CARETPOSITION;
return NS_OK;
}
nsresult
TextEventDispatcher::PendingComposition::Flush(
const TextEventDispatcher* aDispatcher,
nsEventStatus& aStatus)
{
aStatus = nsEventStatus_eIgnore;
if (NS_WARN_IF(!aDispatcher->mInitialized)) {
return NS_ERROR_NOT_INITIALIZED;
}
nsCOMPtr<nsIWidget> widget(aDispatcher->mWidget);
if (NS_WARN_IF(!widget || widget->Destroyed())) {
return NS_ERROR_NOT_AVAILABLE;
}
if (!mClauses->IsEmpty() &&
mClauses->LastElement().mEndOffset != mString.Length()) {
NS_WARNING("Sum of length of the all clauses must be same as the string "
"length");
Clear();
return NS_ERROR_ILLEGAL_VALUE;
}
if (mCaret.mRangeType == NS_TEXTRANGE_CARETPOSITION) {
if (mCaret.mEndOffset > mString.Length()) {
NS_WARNING("Caret position is out of the composition string");
Clear();
return NS_ERROR_ILLEGAL_VALUE;
}
mClauses->AppendElement(mCaret);
}
WidgetCompositionEvent compChangeEvent(true, NS_COMPOSITION_CHANGE, widget);
compChangeEvent.time = PR_IntervalNow();
compChangeEvent.mData = mString;
if (!mClauses->IsEmpty()) {
compChangeEvent.mRanges = mClauses;
}
compChangeEvent.mFlags.mIsSynthesizedForTests = aDispatcher->mForTests;
nsresult rv = widget->DispatchEvent(&compChangeEvent, aStatus);
Clear();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
} // namespace widget
} // namespace mozilla

View File

@ -0,0 +1,148 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_textcompositionsynthesizer_h_
#define mozilla_textcompositionsynthesizer_h_
#include "nsAutoPtr.h"
#include "nsString.h"
#include "mozilla/Attributes.h"
#include "mozilla/EventForwards.h"
#include "mozilla/TextRange.h"
class nsIWidget;
namespace mozilla {
namespace widget {
/**
* TextEventDispatcher is a helper class for dispatching widget events defined
* in TextEvents.h. Currently, this is a helper for dispatching
* WidgetCompositionEvent. However, WidgetKeyboardEvent and/or
* WidgetQueryContentEvent may be supported by this class in the future.
* An instance of this class is created by nsIWidget instance and owned by it.
* This is typically created only by the top level widgets because only they
* handle IME.
*/
class TextEventDispatcher MOZ_FINAL
{
~TextEventDispatcher()
{
}
NS_INLINE_DECL_REFCOUNTING(TextEventDispatcher)
public:
explicit TextEventDispatcher(nsIWidget* aWidget);
/**
* 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.
*/
nsresult Init();
nsresult InitForTests();
/**
* OnDestroyWidget() is called when mWidget is being destroyed.
*/
void OnDestroyWidget();
/**
* SetPendingCompositionString() sets new composition string which will be
* dispatched with NS_COMPOSITION_CHANGE event by calling Flush().
*
* @param aString New composition string.
*/
nsresult SetPendingCompositionString(const nsAString& aString)
{
return mPendingComposition.SetString(aString);
}
/**
* AppendClauseToPendingComposition() appends a clause information to
* the pending composition string.
*
* @param aLength Length of the clause.
* @param aAttribute One of NS_TEXTRANGE_RAWINPUT,
* NS_TEXTRANGE_SELECTEDRAWTEXT,
* NS_TEXTRANGE_CONVERTEDTEXT or
* NS_TEXTRANGE_SELECTEDCONVERTEDTEXT.
*/
nsresult AppendClauseToPendingComposition(uint32_t aLength,
uint32_t aAttribute)
{
return mPendingComposition.AppendClause(aLength, aAttribute);
}
/**
* SetCaretInPendingComposition() sets caret position in the pending
* composition string and its length. This is optional. If IME doesn't
* want to show caret, it shouldn't need to call this.
*
* @param aOffset Offset of the caret in the pending composition
* string. This should not be larger than the length
* of the pending composition string.
* @param aLength Caret width. If this is 0, caret will be collapsed.
* Note that Gecko doesn't supported wide caret yet,
* therefore, this is ignored for now.
*/
nsresult SetCaretInPendingComposition(uint32_t aOffset,
uint32_t aLength)
{
return mPendingComposition.SetCaret(aOffset, aLength);
}
/**
* FlushPendingComposition() sends the pending composition string
* to the widget of the store DOM window. Before calling this, IME needs to
* set pending composition string with SetPendingCompositionString(),
* AppendClauseToPendingComposition() and/or
* SetCaretInPendingComposition().
*/
nsresult FlushPendingComposition(nsEventStatus& aStatus)
{
return mPendingComposition.Flush(this, aStatus);
}
private:
// mWidget is owner of the instance. When this is created, this is set.
// And when mWidget is released, this is cleared by OnDestroyWidget().
// Note that mWidget may be destroyed already (i.e., mWidget->Destroyed() may
// return true).
nsIWidget* mWidget;
// mPendingComposition stores new composition string temporarily.
// These values will be used for dispatching NS_COMPOSITION_CHANGE event
// in Flush(). When Flush() is called, the members will be cleared
// automatically.
class PendingComposition
{
public:
PendingComposition();
nsresult SetString(const nsAString& aString);
nsresult AppendClause(uint32_t aLength, uint32_t aAttribute);
nsresult SetCaret(uint32_t aOffset, uint32_t aLength);
nsresult Flush(const TextEventDispatcher* aDispatcher,
nsEventStatus& aStatus);
void Clear();
private:
nsAutoString mString;
nsRefPtr<TextRangeArray> mClauses;
TextRange mCaret;
};
PendingComposition mPendingComposition;
bool mInitialized;
bool mForTests;
};
} // namespace widget
} // namespace mozilla
#endif // #ifndef mozilla_widget_textcompositionsynthesizer_h_

View File

@ -120,6 +120,7 @@ EXPORTS.mozilla += [
'LookAndFeel.h',
'MiscEvents.h',
'MouseEvents.h',
'TextEventDispatcher.h',
'TextEvents.h',
'TextRange.h',
'TouchEvents.h',
@ -156,6 +157,7 @@ UNIFIED_SOURCES += [
'PuppetWidget.cpp',
'ScreenProxy.cpp',
'SharedWidgetUtils.cpp',
'TextEventDispatcher.cpp',
'VsyncDispatcher.cpp',
'WidgetEventImpl.cpp',
'WidgetUtils.cpp',

View File

@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ArrayUtils.h"
#include "mozilla/TextEventDispatcher.h"
#include "mozilla/layers/CompositorChild.h"
#include "mozilla/layers/CompositorParent.h"
@ -1139,6 +1140,12 @@ void nsBaseWidget::OnDestroy()
{
// release references to device context and app shell
NS_IF_RELEASE(mContext);
if (mTextEventDispatcher) {
mTextEventDispatcher->OnDestroyWidget();
// Don't release it until this widget actually released because after this
// is called, TextEventDispatcher() may create it again.
}
}
NS_METHOD nsBaseWidget::SetWindowClass(const nsAString& xulWinType)
@ -1578,6 +1585,15 @@ nsBaseWidget::NotifyUIStateChanged(UIStateChangeType aShowAccelerators,
}
}
NS_IMETHODIMP_(nsIWidget::TextEventDispatcher*)
nsBaseWidget::GetTextEventDispatcher()
{
if (!mTextEventDispatcher) {
mTextEventDispatcher = new TextEventDispatcher(this);
}
return mTextEventDispatcher;
}
#ifdef ACCESSIBILITY
a11y::Accessible*

View File

@ -221,6 +221,7 @@ public:
virtual void SetAttachedWidgetListener(nsIWidgetListener* aListener) MOZ_OVERRIDE;
NS_IMETHOD RegisterTouchWindow() MOZ_OVERRIDE;
NS_IMETHOD UnregisterTouchWindow() MOZ_OVERRIDE;
NS_IMETHOD_(TextEventDispatcher*) GetTextEventDispatcher() MOZ_OVERRIDE MOZ_FINAL;
void NotifyWindowDestroyed();
void NotifySizeMoveDone();
@ -428,6 +429,7 @@ protected:
nsRefPtr<mozilla::CompositorVsyncDispatcher> mCompositorVsyncDispatcher;
nsRefPtr<APZCTreeManager> mAPZC;
nsRefPtr<WidgetShutdownObserver> mShutdownObserver;
nsRefPtr<TextEventDispatcher> mTextEventDispatcher;
nsCursor mCursor;
bool mUpdateCursor;
nsBorderStyle mBorderStyle;

View File

@ -53,6 +53,9 @@ class PLayerTransactionChild;
namespace gfx {
class DrawTarget;
}
namespace widget {
class TextEventDispatcher;
}
}
/**
@ -104,8 +107,8 @@ typedef void* nsNativeWidget;
#endif
#define NS_IWIDGET_IID \
{ 0x13239ca, 0xaf3f, 0x4f27, \
{ 0xaf, 0x83, 0x47, 0xa9, 0x82, 0x3d, 0x99, 0xee } };
{ 0x029a269f, 0x8b1a, 0x466b, \
{ 0x89, 0x61, 0xc9, 0xcd, 0x23, 0x4e, 0x21, 0x27 } };
/*
* Window shadow styles
@ -727,6 +730,7 @@ class nsIWidget : public nsISupports {
typedef mozilla::widget::InputContext InputContext;
typedef mozilla::widget::InputContextAction InputContextAction;
typedef mozilla::widget::SizeConstraints SizeConstraints;
typedef mozilla::widget::TextEventDispatcher TextEventDispatcher;
typedef mozilla::CompositorVsyncDispatcher CompositorVsyncDispatcher;
// Used in UpdateThemeGeometries.
@ -2200,6 +2204,12 @@ public:
*/
virtual int32_t RoundsWidgetCoordinatesTo() { return 1; }
/**
* GetTextEventDispatcher() returns TextEventDispatcher belonging to the
* widget. Note that this never returns nullptr.
*/
NS_IMETHOD_(TextEventDispatcher*) GetTextEventDispatcher() = 0;
protected:
/**
* Like GetDefaultScale, but taking into account only the system settings