dom/events/TextComposition.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 sw=2 et tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "ContentEventHandler.h"
     8 #include "nsContentUtils.h"
     9 #include "nsIContent.h"
    10 #include "nsIEditor.h"
    11 #include "nsIPresShell.h"
    12 #include "nsPresContext.h"
    13 #include "mozilla/EventDispatcher.h"
    14 #include "mozilla/IMEStateManager.h"
    15 #include "mozilla/MiscEvents.h"
    16 #include "mozilla/TextComposition.h"
    17 #include "mozilla/TextEvents.h"
    19 using namespace mozilla::widget;
    21 namespace mozilla {
    23 /******************************************************************************
    24  * TextComposition
    25  ******************************************************************************/
    27 TextComposition::TextComposition(nsPresContext* aPresContext,
    28                                  nsINode* aNode,
    29                                  WidgetGUIEvent* aEvent)
    30   : mPresContext(aPresContext)
    31   , mNode(aNode)
    32   , mNativeContext(aEvent->widget->GetInputContext().mNativeIMEContext)
    33   , mCompositionStartOffset(0)
    34   , mCompositionTargetOffset(0)
    35   , mIsSynthesizedForTests(aEvent->mFlags.mIsSynthesizedForTests)
    36   , mIsComposing(false)
    37   , mIsEditorHandlingEvent(false)
    38 {
    39 }
    41 void
    42 TextComposition::Destroy()
    43 {
    44   mPresContext = nullptr;
    45   mNode = nullptr;
    46   // TODO: If the editor is still alive and this is held by it, we should tell
    47   //       this being destroyed for cleaning up the stuff.
    48 }
    50 bool
    51 TextComposition::MatchesNativeContext(nsIWidget* aWidget) const
    52 {
    53   return mNativeContext == aWidget->GetInputContext().mNativeIMEContext;
    54 }
    56 void
    57 TextComposition::DispatchEvent(WidgetGUIEvent* aEvent,
    58                                nsEventStatus* aStatus,
    59                                EventDispatchingCallback* aCallBack)
    60 {
    61   if (aEvent->message == NS_COMPOSITION_UPDATE) {
    62     mLastData = aEvent->AsCompositionEvent()->data;
    63   }
    65   EventDispatcher::Dispatch(mNode, mPresContext,
    66                             aEvent, nullptr, aStatus, aCallBack);
    68   if (!mPresContext) {
    69     return;
    70   }
    72   // Emulate editor behavior of text event handler if no editor handles
    73   // composition/text events.
    74   if (aEvent->message == NS_TEXT_TEXT && !HasEditor()) {
    75     EditorWillHandleTextEvent(aEvent->AsTextEvent());
    76     EditorDidHandleTextEvent();
    77   }
    79 #ifdef DEBUG
    80   else if (aEvent->message == NS_COMPOSITION_END) {
    81     MOZ_ASSERT(!mIsComposing, "Why is the editor still composing?");
    82     MOZ_ASSERT(!HasEditor(), "Why does the editor still keep to hold this?");
    83   }
    84 #endif // #ifdef DEBUG
    86   // Notify composition update to widget if possible
    87   NotityUpdateComposition(aEvent);
    88 }
    90 void
    91 TextComposition::NotityUpdateComposition(WidgetGUIEvent* aEvent)
    92 {
    93   nsEventStatus status;
    95   // When compositon start, notify the rect of first offset character.
    96   // When not compositon start, notify the rect of selected composition
    97   // string if text event.
    98   if (aEvent->message == NS_COMPOSITION_START) {
    99     nsCOMPtr<nsIWidget> widget = mPresContext->GetRootWidget();
   100     // Update composition start offset
   101     WidgetQueryContentEvent selectedTextEvent(true,
   102                                               NS_QUERY_SELECTED_TEXT,
   103                                               widget);
   104     widget->DispatchEvent(&selectedTextEvent, status);
   105     if (selectedTextEvent.mSucceeded) {
   106       mCompositionStartOffset = selectedTextEvent.mReply.mOffset;
   107     } else {
   108       // Unknown offset
   109       NS_WARNING("Cannot get start offset of IME composition");
   110       mCompositionStartOffset = 0;
   111     }
   112     mCompositionTargetOffset = mCompositionStartOffset;
   113   } else if (aEvent->eventStructType != NS_TEXT_EVENT) {
   114     return;
   115   } else {
   116     mCompositionTargetOffset =
   117       mCompositionStartOffset + aEvent->AsTextEvent()->TargetClauseOffset();
   118   }
   120   NotifyIME(NOTIFY_IME_OF_COMPOSITION_UPDATE);
   121 }
   123 void
   124 TextComposition::DispatchCompositionEventRunnable(uint32_t aEventMessage,
   125                                                   const nsAString& aData)
   126 {
   127   nsContentUtils::AddScriptRunner(
   128     new CompositionEventDispatcher(mPresContext, mNode,
   129                                    aEventMessage, aData));
   130 }
   132 void
   133 TextComposition::SynthesizeCommit(bool aDiscard)
   134 {
   135   nsRefPtr<TextComposition> kungFuDeathGrip(this);
   136   nsAutoString data(aDiscard ? EmptyString() : mLastData);
   137   if (mLastData != data) {
   138     DispatchCompositionEventRunnable(NS_COMPOSITION_UPDATE, data);
   139     DispatchCompositionEventRunnable(NS_TEXT_TEXT, data);
   140   }
   141   DispatchCompositionEventRunnable(NS_COMPOSITION_END, data);
   142 }
   144 nsresult
   145 TextComposition::NotifyIME(IMEMessage aMessage)
   146 {
   147   NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
   148   return IMEStateManager::NotifyIME(aMessage, mPresContext);
   149 }
   151 void
   152 TextComposition::EditorWillHandleTextEvent(const WidgetTextEvent* aTextEvent)
   153 {
   154   mIsComposing = aTextEvent->IsComposing();
   155   mRanges = aTextEvent->mRanges;
   156   mIsEditorHandlingEvent = true;
   158   MOZ_ASSERT(mLastData == aTextEvent->theText,
   159     "The text of a text event must be same as previous data attribute value "
   160     "of the latest compositionupdate event");
   161 }
   163 void
   164 TextComposition::EditorDidHandleTextEvent()
   165 {
   166   mString = mLastData;
   167   mIsEditorHandlingEvent = false;
   168 }
   170 void
   171 TextComposition::StartHandlingComposition(nsIEditor* aEditor)
   172 {
   173   MOZ_ASSERT(!HasEditor(), "There is a handling editor already");
   174   mEditorWeak = do_GetWeakReference(aEditor);
   175 }
   177 void
   178 TextComposition::EndHandlingComposition(nsIEditor* aEditor)
   179 {
   180 #ifdef DEBUG
   181   nsCOMPtr<nsIEditor> editor = GetEditor();
   182   MOZ_ASSERT(editor == aEditor, "Another editor handled the composition?");
   183 #endif // #ifdef DEBUG
   184   mEditorWeak = nullptr;
   185 }
   187 already_AddRefed<nsIEditor>
   188 TextComposition::GetEditor() const
   189 {
   190   nsCOMPtr<nsIEditor> editor = do_QueryReferent(mEditorWeak);
   191   return editor.forget();
   192 }
   194 bool
   195 TextComposition::HasEditor() const
   196 {
   197   nsCOMPtr<nsIEditor> editor = GetEditor();
   198   return !!editor;
   199 }
   201 /******************************************************************************
   202  * TextComposition::CompositionEventDispatcher
   203  ******************************************************************************/
   205 TextComposition::CompositionEventDispatcher::CompositionEventDispatcher(
   206                                                nsPresContext* aPresContext,
   207                                                nsINode* aEventTarget,
   208                                                uint32_t aEventMessage,
   209                                                const nsAString& aData) :
   210   mPresContext(aPresContext), mEventTarget(aEventTarget),
   211   mEventMessage(aEventMessage), mData(aData)
   212 {
   213   mWidget = mPresContext->GetRootWidget();
   214 }
   216 NS_IMETHODIMP
   217 TextComposition::CompositionEventDispatcher::Run()
   218 {
   219   if (!mPresContext->GetPresShell() ||
   220       mPresContext->GetPresShell()->IsDestroying()) {
   221     return NS_OK; // cannot dispatch any events anymore
   222   }
   224   nsEventStatus status = nsEventStatus_eIgnore;
   225   switch (mEventMessage) {
   226     case NS_COMPOSITION_START: {
   227       WidgetCompositionEvent compStart(true, NS_COMPOSITION_START, mWidget);
   228       WidgetQueryContentEvent selectedText(true, NS_QUERY_SELECTED_TEXT,
   229                                            mWidget);
   230       ContentEventHandler handler(mPresContext);
   231       handler.OnQuerySelectedText(&selectedText);
   232       NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text");
   233       compStart.data = selectedText.mReply.mString;
   234       IMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext,
   235                                                 &compStart, &status, nullptr);
   236       break;
   237     }
   238     case NS_COMPOSITION_UPDATE:
   239     case NS_COMPOSITION_END: {
   240       WidgetCompositionEvent compEvent(true, mEventMessage, mWidget);
   241       compEvent.data = mData;
   242       IMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext,
   243                                                 &compEvent, &status, nullptr);
   244       break;
   245     }
   246     case NS_TEXT_TEXT: {
   247       WidgetTextEvent textEvent(true, NS_TEXT_TEXT, mWidget);
   248       textEvent.theText = mData;
   249       IMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext,
   250                                                 &textEvent, &status, nullptr);
   251       break;
   252     }
   253     default:
   254       MOZ_CRASH("Unsupported event");
   255   }
   256   return NS_OK;
   257 }
   259 /******************************************************************************
   260  * TextCompositionArray
   261  ******************************************************************************/
   263 TextCompositionArray::index_type
   264 TextCompositionArray::IndexOf(nsIWidget* aWidget)
   265 {
   266   for (index_type i = Length(); i > 0; --i) {
   267     if (ElementAt(i - 1)->MatchesNativeContext(aWidget)) {
   268       return i - 1;
   269     }
   270   }
   271   return NoIndex;
   272 }
   274 TextCompositionArray::index_type
   275 TextCompositionArray::IndexOf(nsPresContext* aPresContext)
   276 {
   277   for (index_type i = Length(); i > 0; --i) {
   278     if (ElementAt(i - 1)->GetPresContext() == aPresContext) {
   279       return i - 1;
   280     }
   281   }
   282   return NoIndex;
   283 }
   285 TextCompositionArray::index_type
   286 TextCompositionArray::IndexOf(nsPresContext* aPresContext,
   287                               nsINode* aNode)
   288 {
   289   index_type index = IndexOf(aPresContext);
   290   if (index == NoIndex) {
   291     return NoIndex;
   292   }
   293   nsINode* node = ElementAt(index)->GetEventTargetNode();
   294   return node == aNode ? index : NoIndex;
   295 }
   297 TextComposition*
   298 TextCompositionArray::GetCompositionFor(nsIWidget* aWidget)
   299 {
   300   index_type i = IndexOf(aWidget);
   301   return i != NoIndex ? ElementAt(i) : nullptr;
   302 }
   304 TextComposition*
   305 TextCompositionArray::GetCompositionFor(nsPresContext* aPresContext,
   306                                            nsINode* aNode)
   307 {
   308   index_type i = IndexOf(aPresContext, aNode);
   309   return i != NoIndex ? ElementAt(i) : nullptr;
   310 }
   312 TextComposition*
   313 TextCompositionArray::GetCompositionInContent(nsPresContext* aPresContext,
   314                                               nsIContent* aContent)
   315 {
   316   // There should be only one composition per content object.
   317   for (index_type i = Length(); i > 0; --i) {
   318     nsINode* node = ElementAt(i - 1)->GetEventTargetNode();
   319     if (node && nsContentUtils::ContentIsDescendantOf(node, aContent)) {
   320       return ElementAt(i - 1);
   321     }
   322   }
   323   return nullptr;
   324 }
   326 } // namespace mozilla

mercurial