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 226207 87c8054d9040f96a3babbd3decfb9c7ad2d6882a
parent 226206 de96fc93d9ec60b7e40e8881d422a4e641d47d0c
child 226208 d7c660bbe955ffd01892590b4e38da9106a2ebc4
push id28187
push usercbook@mozilla.com
push dateWed, 28 Jan 2015 13:20:48 +0000
treeherdermozilla-central@fc21937ca612 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, smaug
bugs917322
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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.