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

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

mercurial