Bug 917322 part.1 Create mozilla::widget::TextEventDispatcher class r=smaug, sr=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 28 Jan 2015 15:27:30 +0900
changeset 239572 87c8054d9040f96a3babbd3decfb9c7ad2d6882a
parent 239571 de96fc93d9ec60b7e40e8881d422a4e641d47d0c
child 239573 d7c660bbe955ffd01892590b4e38da9106a2ebc4
push id500
push userjoshua.m.grant@gmail.com
push dateThu, 29 Jan 2015 01:48:36 +0000
reviewerssmaug, smaug
bugs917322
milestone38.0a1
Bug 917322 part.1 Create mozilla::widget::TextEventDispatcher class r=smaug, sr=smaug
widget/TextEventDispatcher.cpp
widget/TextEventDispatcher.h
widget/moz.build
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
widget/nsIWidget.h
copy from dom/base/CompositionStringSynthesizer.cpp
copy to widget/TextEventDispatcher.cpp
--- a/dom/base/CompositionStringSynthesizer.cpp
+++ b/widget/TextEventDispatcher.cpp
@@ -1,160 +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 "CompositionStringSynthesizer.h"
-#include "nsContentUtils.h"
+#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"
-#include "mozilla/TextEvents.h"
 
 namespace mozilla {
-namespace dom {
+namespace widget {
 
-NS_IMPL_ISUPPORTS(CompositionStringSynthesizer,
-                  nsICompositionStringSynthesizer)
+/******************************************************************************
+ * TextEventDispatcher
+ *****************************************************************************/
 
-CompositionStringSynthesizer::CompositionStringSynthesizer(
-                                nsPIDOMWindow* aWindow)
+TextEventDispatcher::TextEventDispatcher(nsIWidget* aWidget)
+  : mWidget(aWidget)
+  , mInitialized(false)
+  , mForTests(false)
 {
-  mWindow = do_GetWeakReference(aWindow);
-  mClauses = new TextRangeArray();
-  ClearInternal();
+  MOZ_RELEASE_ASSERT(mWidget, "aWidget must not be nullptr");
 }
 
-CompositionStringSynthesizer::~CompositionStringSynthesizer()
+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
-CompositionStringSynthesizer::ClearInternal()
+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;
 }
 
-nsIWidget*
-CompositionStringSynthesizer::GetWidget()
+nsresult
+TextEventDispatcher::PendingComposition::SetString(const nsAString& aString)
 {
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
-  if (!window) {
-    return nullptr;
-  }
-  nsIDocShell *docShell = window->GetDocShell();
-  if (!docShell) {
-    return nullptr;
-  }
-  nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
-  if (!presShell) {
-    return nullptr;
-  }
-  nsIFrame* frame = presShell->GetRootFrame();
-  if (!frame) {
-    return nullptr;
-  }
-  return frame->GetView()->GetNearestWidget(nullptr);
-}
-
-NS_IMETHODIMP
-CompositionStringSynthesizer::SetString(const nsAString& aString)
-{
-  nsCOMPtr<nsIWidget> widget = GetWidget();
-  NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE);
-
   mString = aString;
   return NS_OK;
 }
 
-NS_IMETHODIMP
-CompositionStringSynthesizer::AppendClause(uint32_t aLength,
-                                           uint32_t aAttribute)
+nsresult
+TextEventDispatcher::PendingComposition::AppendClause(uint32_t aLength,
+                                                      uint32_t aAttribute)
 {
-  nsCOMPtr<nsIWidget> widget = GetWidget();
-  NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE);
+  if (NS_WARN_IF(!aLength)) {
+    return NS_ERROR_INVALID_ARG;
+  }
 
   switch (aAttribute) {
-    case ATTR_RAWINPUT:
-    case ATTR_SELECTEDRAWTEXT:
-    case ATTR_CONVERTEDTEXT:
-    case ATTR_SELECTEDCONVERTEDTEXT: {
+    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;
   }
 }
 
-NS_IMETHODIMP
-CompositionStringSynthesizer::SetCaret(uint32_t aOffset, uint32_t aLength)
+nsresult
+TextEventDispatcher::PendingComposition::SetCaret(uint32_t aOffset,
+                                                  uint32_t aLength)
 {
-  nsCOMPtr<nsIWidget> widget = GetWidget();
-  NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE);
-
   mCaret.mStartOffset = aOffset;
   mCaret.mEndOffset = mCaret.mStartOffset + aLength;
   mCaret.mRangeType = NS_TEXTRANGE_CARETPOSITION;
   return NS_OK;
 }
 
-NS_IMETHODIMP
-CompositionStringSynthesizer::DispatchEvent(bool* aDefaultPrevented)
+nsresult
+TextEventDispatcher::PendingComposition::Flush(
+                       const TextEventDispatcher* aDispatcher,
+                       nsEventStatus& aStatus)
 {
-  NS_ENSURE_ARG_POINTER(aDefaultPrevented);
-  nsCOMPtr<nsIWidget> widget = GetWidget();
-  NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE);
+  aStatus = nsEventStatus_eIgnore;
 
-  if (!nsContentUtils::IsCallerChrome()) {
-    return NS_ERROR_DOM_SECURITY_ERR;
+  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");
-    ClearInternal();
+    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");
-      ClearInternal();
+      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;
   }
 
-  // XXX How should we set false for this on b2g?
-  compChangeEvent.mFlags.mIsSynthesizedForTests = true;
+  compChangeEvent.mFlags.mIsSynthesizedForTests = aDispatcher->mForTests;
 
-  nsEventStatus status = nsEventStatus_eIgnore;
-  nsresult rv = widget->DispatchEvent(&compChangeEvent, status);
-  *aDefaultPrevented = (status == nsEventStatus_eConsumeNoDefault);
-
-  ClearInternal();
-
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv = widget->DispatchEvent(&compChangeEvent, aStatus);
+  Clear();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   return NS_OK;
 }
 
-} // namespace dom
+} // namespace widget
 } // namespace mozilla
copy from dom/base/CompositionStringSynthesizer.h
copy to widget/TextEventDispatcher.h
--- a/dom/base/CompositionStringSynthesizer.h
+++ b/widget/TextEventDispatcher.h
@@ -1,45 +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_dom_compositionstringsynthesizer_h__
-#define mozilla_dom_compositionstringsynthesizer_h__
+#ifndef mozilla_textcompositionsynthesizer_h_
+#define mozilla_textcompositionsynthesizer_h_
 
-#include "nsICompositionStringSynthesizer.h"
+#include "nsAutoPtr.h"
 #include "nsString.h"
-#include "nsWeakReference.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/EventForwards.h"
 #include "mozilla/TextRange.h"
 
 class nsIWidget;
-class nsPIDOMWindow;
 
 namespace mozilla {
-namespace dom {
+namespace widget {
 
-class CompositionStringSynthesizer MOZ_FINAL :
-  public nsICompositionStringSynthesizer
+/**
+ * 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 CompositionStringSynthesizer(nsPIDOMWindow* aWindow);
+  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);
+  }
 
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSICOMPOSITIONSTRINGSYNTHESIZER
+  /**
+   * 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:
-  ~CompositionStringSynthesizer();
+  // 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;
 
-  nsWeakPtr mWindow; // refers an instance of nsPIDOMWindow
-  nsString mString;
-  nsRefPtr<TextRangeArray> mClauses;
-  TextRange mCaret;
+  // 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();
 
-  nsIWidget* GetWidget();
-  void ClearInternal();
+  private:
+    nsAutoString mString;
+    nsRefPtr<TextRangeArray> mClauses;
+    TextRange mCaret;
+  };
+  PendingComposition mPendingComposition;
+
+  bool mInitialized;
+  bool mForTests;
 };
 
-} // namespace dom
+} // namespace widget
 } // namespace mozilla
 
-#endif // #ifndef mozilla_dom_compositionstringsynthesizer_h__
+#endif // #ifndef mozilla_widget_textcompositionsynthesizer_h_
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -115,16 +115,17 @@ EXPORTS.mozilla += [
     'BasicEvents.h',
     'CommandList.h',
     'ContentEvents.h',
     'EventClassList.h',
     'EventForwards.h',
     'LookAndFeel.h',
     'MiscEvents.h',
     'MouseEvents.h',
+    'TextEventDispatcher.h',
     'TextEvents.h',
     'TextRange.h',
     'TouchEvents.h',
     'VsyncDispatcher.h',
     'WidgetUtils.h',
 ]
 
 UNIFIED_SOURCES += [
@@ -151,16 +152,17 @@ UNIFIED_SOURCES += [
     'nsScreenManagerProxy.cpp',
     'nsShmImage.cpp',
     'nsTransferable.cpp',
     'nsXPLookAndFeel.cpp',
     'PluginWidgetProxy.cpp',
     'PuppetWidget.cpp',
     'ScreenProxy.cpp',
     'SharedWidgetUtils.cpp',
+    'TextEventDispatcher.cpp',
     'VsyncDispatcher.cpp',
     'WidgetEventImpl.cpp',
     'WidgetUtils.cpp',
 ]
 
 if CONFIG['MOZ_XUL'] and CONFIG['NS_PRINTING']:
     EXPORTS += [
         'nsPrintOptionsImpl.h',
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -1,14 +1,15 @@
 /* -*- 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/ArrayUtils.h"
+#include "mozilla/TextEventDispatcher.h"
 
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "nsBaseWidget.h"
 #include "nsDeviceContext.h"
 #include "nsCOMPtr.h"
 #include "nsGfxCIID.h"
@@ -1134,16 +1135,22 @@ nsDeviceContext* nsBaseWidget::GetDevice
 //
 // Destroy the window
 //
 //-------------------------------------------------------------------------
 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)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_METHOD nsBaseWidget::MoveClient(double aX, double aY)
@@ -1573,16 +1580,25 @@ nsBaseWidget::NotifyUIStateChanged(UISta
   if (doc) {
     nsPIDOMWindow* win = doc->GetWindow();
     if (win) {
       win->SetKeyboardIndicators(aShowAccelerators, aShowFocusRings);
     }
   }
 }
 
+NS_IMETHODIMP_(nsIWidget::TextEventDispatcher*)
+nsBaseWidget::GetTextEventDispatcher()
+{
+  if (!mTextEventDispatcher) {
+    mTextEventDispatcher = new TextEventDispatcher(this);
+  }
+  return mTextEventDispatcher;
+}
+
 #ifdef ACCESSIBILITY
 
 a11y::Accessible*
 nsBaseWidget::GetRootAccessible()
 {
   NS_ENSURE_TRUE(mWidgetListener, nullptr);
 
   nsIPresShell* presShell = mWidgetListener->GetPresShell();
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -216,16 +216,17 @@ public:
               nsDeviceContext *aContext,
               nsWidgetInitData *aInitData = nullptr,
               bool             aForceUseIWidgetParent = false) MOZ_OVERRIDE;
   NS_IMETHOD              AttachViewToTopLevel(bool aUseAttachedEvents, nsDeviceContext *aContext) MOZ_OVERRIDE;
   virtual nsIWidgetListener* GetAttachedWidgetListener() MOZ_OVERRIDE;
   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();
   void NotifyWindowMoved(int32_t aX, int32_t aY);
 
   // Should be called by derived implementations to notify on system color and
   // theme changes.
   void NotifySysColorChanged();
@@ -423,16 +424,17 @@ protected:
   nsDeviceContext* mContext;
   nsRefPtr<LayerManager> mLayerManager;
   nsRefPtr<LayerManager> mBasicLayerManager;
   nsRefPtr<CompositorChild> mCompositorChild;
   nsRefPtr<CompositorParent> mCompositorParent;
   nsRefPtr<mozilla::CompositorVsyncDispatcher> mCompositorVsyncDispatcher;
   nsRefPtr<APZCTreeManager> mAPZC;
   nsRefPtr<WidgetShutdownObserver> mShutdownObserver;
+  nsRefPtr<TextEventDispatcher> mTextEventDispatcher;
   nsCursor          mCursor;
   bool              mUpdateCursor;
   nsBorderStyle     mBorderStyle;
   bool              mUseLayersAcceleration;
   bool              mForceLayersAcceleration;
   bool              mTemporarilyUseBasicLayerManager;
   // Windows with out-of-process tabs always require OMTC. This flag designates
   // such windows.
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -48,16 +48,19 @@ class Composer2D;
 class CompositorChild;
 class LayerManager;
 class LayerManagerComposite;
 class PLayerTransactionChild;
 }
 namespace gfx {
 class DrawTarget;
 }
+namespace widget {
+class TextEventDispatcher;
+}
 }
 
 /**
  * Callback function that processes events.
  *
  * The argument is actually a subtype (subclass) of WidgetEvent which carries
  * platform specific information about the event. Platform specific code
  * knows how to deal with it.
@@ -99,18 +102,18 @@ typedef void* nsNativeWidget;
 #ifdef XP_WIN
 #define NS_NATIVE_TSF_THREAD_MGR       100
 #define NS_NATIVE_TSF_CATEGORY_MGR     101
 #define NS_NATIVE_TSF_DISPLAY_ATTR_MGR 102
 #define NS_NATIVE_ICOREWINDOW          103 // winrt specific
 #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
  * Also used for the -moz-window-shadow CSS property
  */
 
 #define NS_STYLE_WINDOW_SHADOW_NONE             0
 #define NS_STYLE_WINDOW_SHADOW_DEFAULT          1
@@ -722,16 +725,17 @@ class nsIWidget : public nsISupports {
     typedef mozilla::layers::LayersBackend LayersBackend;
     typedef mozilla::layers::PLayerTransactionChild PLayerTransactionChild;
     typedef mozilla::widget::IMEMessage IMEMessage;
     typedef mozilla::widget::IMENotification IMENotification;
     typedef mozilla::widget::IMEState IMEState;
     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.
     struct ThemeGeometry {
       // The -moz-appearance value for the themed widget
       uint8_t mWidgetType;
       // The device-pixel rect within the window for the themed widget
       nsIntRect mRect;
@@ -2195,16 +2199,22 @@ public:
 
     /**
      * Some platforms (only cocoa right now) round widget coordinates to the
      * nearest even pixels (see bug 892994), this function allows us to
      * determine how widget coordinates will be rounded.
      */
     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
      * and ignoring Gecko preferences.
      */
     virtual double GetDefaultScaleInternal() { return 1.0; }
 
     // keep the list of children.  We also keep track of our siblings.