1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/events/TextComposition.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,326 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 sw=2 et tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "ContentEventHandler.h" 1.11 +#include "nsContentUtils.h" 1.12 +#include "nsIContent.h" 1.13 +#include "nsIEditor.h" 1.14 +#include "nsIPresShell.h" 1.15 +#include "nsPresContext.h" 1.16 +#include "mozilla/EventDispatcher.h" 1.17 +#include "mozilla/IMEStateManager.h" 1.18 +#include "mozilla/MiscEvents.h" 1.19 +#include "mozilla/TextComposition.h" 1.20 +#include "mozilla/TextEvents.h" 1.21 + 1.22 +using namespace mozilla::widget; 1.23 + 1.24 +namespace mozilla { 1.25 + 1.26 +/****************************************************************************** 1.27 + * TextComposition 1.28 + ******************************************************************************/ 1.29 + 1.30 +TextComposition::TextComposition(nsPresContext* aPresContext, 1.31 + nsINode* aNode, 1.32 + WidgetGUIEvent* aEvent) 1.33 + : mPresContext(aPresContext) 1.34 + , mNode(aNode) 1.35 + , mNativeContext(aEvent->widget->GetInputContext().mNativeIMEContext) 1.36 + , mCompositionStartOffset(0) 1.37 + , mCompositionTargetOffset(0) 1.38 + , mIsSynthesizedForTests(aEvent->mFlags.mIsSynthesizedForTests) 1.39 + , mIsComposing(false) 1.40 + , mIsEditorHandlingEvent(false) 1.41 +{ 1.42 +} 1.43 + 1.44 +void 1.45 +TextComposition::Destroy() 1.46 +{ 1.47 + mPresContext = nullptr; 1.48 + mNode = nullptr; 1.49 + // TODO: If the editor is still alive and this is held by it, we should tell 1.50 + // this being destroyed for cleaning up the stuff. 1.51 +} 1.52 + 1.53 +bool 1.54 +TextComposition::MatchesNativeContext(nsIWidget* aWidget) const 1.55 +{ 1.56 + return mNativeContext == aWidget->GetInputContext().mNativeIMEContext; 1.57 +} 1.58 + 1.59 +void 1.60 +TextComposition::DispatchEvent(WidgetGUIEvent* aEvent, 1.61 + nsEventStatus* aStatus, 1.62 + EventDispatchingCallback* aCallBack) 1.63 +{ 1.64 + if (aEvent->message == NS_COMPOSITION_UPDATE) { 1.65 + mLastData = aEvent->AsCompositionEvent()->data; 1.66 + } 1.67 + 1.68 + EventDispatcher::Dispatch(mNode, mPresContext, 1.69 + aEvent, nullptr, aStatus, aCallBack); 1.70 + 1.71 + if (!mPresContext) { 1.72 + return; 1.73 + } 1.74 + 1.75 + // Emulate editor behavior of text event handler if no editor handles 1.76 + // composition/text events. 1.77 + if (aEvent->message == NS_TEXT_TEXT && !HasEditor()) { 1.78 + EditorWillHandleTextEvent(aEvent->AsTextEvent()); 1.79 + EditorDidHandleTextEvent(); 1.80 + } 1.81 + 1.82 +#ifdef DEBUG 1.83 + else if (aEvent->message == NS_COMPOSITION_END) { 1.84 + MOZ_ASSERT(!mIsComposing, "Why is the editor still composing?"); 1.85 + MOZ_ASSERT(!HasEditor(), "Why does the editor still keep to hold this?"); 1.86 + } 1.87 +#endif // #ifdef DEBUG 1.88 + 1.89 + // Notify composition update to widget if possible 1.90 + NotityUpdateComposition(aEvent); 1.91 +} 1.92 + 1.93 +void 1.94 +TextComposition::NotityUpdateComposition(WidgetGUIEvent* aEvent) 1.95 +{ 1.96 + nsEventStatus status; 1.97 + 1.98 + // When compositon start, notify the rect of first offset character. 1.99 + // When not compositon start, notify the rect of selected composition 1.100 + // string if text event. 1.101 + if (aEvent->message == NS_COMPOSITION_START) { 1.102 + nsCOMPtr<nsIWidget> widget = mPresContext->GetRootWidget(); 1.103 + // Update composition start offset 1.104 + WidgetQueryContentEvent selectedTextEvent(true, 1.105 + NS_QUERY_SELECTED_TEXT, 1.106 + widget); 1.107 + widget->DispatchEvent(&selectedTextEvent, status); 1.108 + if (selectedTextEvent.mSucceeded) { 1.109 + mCompositionStartOffset = selectedTextEvent.mReply.mOffset; 1.110 + } else { 1.111 + // Unknown offset 1.112 + NS_WARNING("Cannot get start offset of IME composition"); 1.113 + mCompositionStartOffset = 0; 1.114 + } 1.115 + mCompositionTargetOffset = mCompositionStartOffset; 1.116 + } else if (aEvent->eventStructType != NS_TEXT_EVENT) { 1.117 + return; 1.118 + } else { 1.119 + mCompositionTargetOffset = 1.120 + mCompositionStartOffset + aEvent->AsTextEvent()->TargetClauseOffset(); 1.121 + } 1.122 + 1.123 + NotifyIME(NOTIFY_IME_OF_COMPOSITION_UPDATE); 1.124 +} 1.125 + 1.126 +void 1.127 +TextComposition::DispatchCompositionEventRunnable(uint32_t aEventMessage, 1.128 + const nsAString& aData) 1.129 +{ 1.130 + nsContentUtils::AddScriptRunner( 1.131 + new CompositionEventDispatcher(mPresContext, mNode, 1.132 + aEventMessage, aData)); 1.133 +} 1.134 + 1.135 +void 1.136 +TextComposition::SynthesizeCommit(bool aDiscard) 1.137 +{ 1.138 + nsRefPtr<TextComposition> kungFuDeathGrip(this); 1.139 + nsAutoString data(aDiscard ? EmptyString() : mLastData); 1.140 + if (mLastData != data) { 1.141 + DispatchCompositionEventRunnable(NS_COMPOSITION_UPDATE, data); 1.142 + DispatchCompositionEventRunnable(NS_TEXT_TEXT, data); 1.143 + } 1.144 + DispatchCompositionEventRunnable(NS_COMPOSITION_END, data); 1.145 +} 1.146 + 1.147 +nsresult 1.148 +TextComposition::NotifyIME(IMEMessage aMessage) 1.149 +{ 1.150 + NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE); 1.151 + return IMEStateManager::NotifyIME(aMessage, mPresContext); 1.152 +} 1.153 + 1.154 +void 1.155 +TextComposition::EditorWillHandleTextEvent(const WidgetTextEvent* aTextEvent) 1.156 +{ 1.157 + mIsComposing = aTextEvent->IsComposing(); 1.158 + mRanges = aTextEvent->mRanges; 1.159 + mIsEditorHandlingEvent = true; 1.160 + 1.161 + MOZ_ASSERT(mLastData == aTextEvent->theText, 1.162 + "The text of a text event must be same as previous data attribute value " 1.163 + "of the latest compositionupdate event"); 1.164 +} 1.165 + 1.166 +void 1.167 +TextComposition::EditorDidHandleTextEvent() 1.168 +{ 1.169 + mString = mLastData; 1.170 + mIsEditorHandlingEvent = false; 1.171 +} 1.172 + 1.173 +void 1.174 +TextComposition::StartHandlingComposition(nsIEditor* aEditor) 1.175 +{ 1.176 + MOZ_ASSERT(!HasEditor(), "There is a handling editor already"); 1.177 + mEditorWeak = do_GetWeakReference(aEditor); 1.178 +} 1.179 + 1.180 +void 1.181 +TextComposition::EndHandlingComposition(nsIEditor* aEditor) 1.182 +{ 1.183 +#ifdef DEBUG 1.184 + nsCOMPtr<nsIEditor> editor = GetEditor(); 1.185 + MOZ_ASSERT(editor == aEditor, "Another editor handled the composition?"); 1.186 +#endif // #ifdef DEBUG 1.187 + mEditorWeak = nullptr; 1.188 +} 1.189 + 1.190 +already_AddRefed<nsIEditor> 1.191 +TextComposition::GetEditor() const 1.192 +{ 1.193 + nsCOMPtr<nsIEditor> editor = do_QueryReferent(mEditorWeak); 1.194 + return editor.forget(); 1.195 +} 1.196 + 1.197 +bool 1.198 +TextComposition::HasEditor() const 1.199 +{ 1.200 + nsCOMPtr<nsIEditor> editor = GetEditor(); 1.201 + return !!editor; 1.202 +} 1.203 + 1.204 +/****************************************************************************** 1.205 + * TextComposition::CompositionEventDispatcher 1.206 + ******************************************************************************/ 1.207 + 1.208 +TextComposition::CompositionEventDispatcher::CompositionEventDispatcher( 1.209 + nsPresContext* aPresContext, 1.210 + nsINode* aEventTarget, 1.211 + uint32_t aEventMessage, 1.212 + const nsAString& aData) : 1.213 + mPresContext(aPresContext), mEventTarget(aEventTarget), 1.214 + mEventMessage(aEventMessage), mData(aData) 1.215 +{ 1.216 + mWidget = mPresContext->GetRootWidget(); 1.217 +} 1.218 + 1.219 +NS_IMETHODIMP 1.220 +TextComposition::CompositionEventDispatcher::Run() 1.221 +{ 1.222 + if (!mPresContext->GetPresShell() || 1.223 + mPresContext->GetPresShell()->IsDestroying()) { 1.224 + return NS_OK; // cannot dispatch any events anymore 1.225 + } 1.226 + 1.227 + nsEventStatus status = nsEventStatus_eIgnore; 1.228 + switch (mEventMessage) { 1.229 + case NS_COMPOSITION_START: { 1.230 + WidgetCompositionEvent compStart(true, NS_COMPOSITION_START, mWidget); 1.231 + WidgetQueryContentEvent selectedText(true, NS_QUERY_SELECTED_TEXT, 1.232 + mWidget); 1.233 + ContentEventHandler handler(mPresContext); 1.234 + handler.OnQuerySelectedText(&selectedText); 1.235 + NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text"); 1.236 + compStart.data = selectedText.mReply.mString; 1.237 + IMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext, 1.238 + &compStart, &status, nullptr); 1.239 + break; 1.240 + } 1.241 + case NS_COMPOSITION_UPDATE: 1.242 + case NS_COMPOSITION_END: { 1.243 + WidgetCompositionEvent compEvent(true, mEventMessage, mWidget); 1.244 + compEvent.data = mData; 1.245 + IMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext, 1.246 + &compEvent, &status, nullptr); 1.247 + break; 1.248 + } 1.249 + case NS_TEXT_TEXT: { 1.250 + WidgetTextEvent textEvent(true, NS_TEXT_TEXT, mWidget); 1.251 + textEvent.theText = mData; 1.252 + IMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext, 1.253 + &textEvent, &status, nullptr); 1.254 + break; 1.255 + } 1.256 + default: 1.257 + MOZ_CRASH("Unsupported event"); 1.258 + } 1.259 + return NS_OK; 1.260 +} 1.261 + 1.262 +/****************************************************************************** 1.263 + * TextCompositionArray 1.264 + ******************************************************************************/ 1.265 + 1.266 +TextCompositionArray::index_type 1.267 +TextCompositionArray::IndexOf(nsIWidget* aWidget) 1.268 +{ 1.269 + for (index_type i = Length(); i > 0; --i) { 1.270 + if (ElementAt(i - 1)->MatchesNativeContext(aWidget)) { 1.271 + return i - 1; 1.272 + } 1.273 + } 1.274 + return NoIndex; 1.275 +} 1.276 + 1.277 +TextCompositionArray::index_type 1.278 +TextCompositionArray::IndexOf(nsPresContext* aPresContext) 1.279 +{ 1.280 + for (index_type i = Length(); i > 0; --i) { 1.281 + if (ElementAt(i - 1)->GetPresContext() == aPresContext) { 1.282 + return i - 1; 1.283 + } 1.284 + } 1.285 + return NoIndex; 1.286 +} 1.287 + 1.288 +TextCompositionArray::index_type 1.289 +TextCompositionArray::IndexOf(nsPresContext* aPresContext, 1.290 + nsINode* aNode) 1.291 +{ 1.292 + index_type index = IndexOf(aPresContext); 1.293 + if (index == NoIndex) { 1.294 + return NoIndex; 1.295 + } 1.296 + nsINode* node = ElementAt(index)->GetEventTargetNode(); 1.297 + return node == aNode ? index : NoIndex; 1.298 +} 1.299 + 1.300 +TextComposition* 1.301 +TextCompositionArray::GetCompositionFor(nsIWidget* aWidget) 1.302 +{ 1.303 + index_type i = IndexOf(aWidget); 1.304 + return i != NoIndex ? ElementAt(i) : nullptr; 1.305 +} 1.306 + 1.307 +TextComposition* 1.308 +TextCompositionArray::GetCompositionFor(nsPresContext* aPresContext, 1.309 + nsINode* aNode) 1.310 +{ 1.311 + index_type i = IndexOf(aPresContext, aNode); 1.312 + return i != NoIndex ? ElementAt(i) : nullptr; 1.313 +} 1.314 + 1.315 +TextComposition* 1.316 +TextCompositionArray::GetCompositionInContent(nsPresContext* aPresContext, 1.317 + nsIContent* aContent) 1.318 +{ 1.319 + // There should be only one composition per content object. 1.320 + for (index_type i = Length(); i > 0; --i) { 1.321 + nsINode* node = ElementAt(i - 1)->GetEventTargetNode(); 1.322 + if (node && nsContentUtils::ContentIsDescendantOf(node, aContent)) { 1.323 + return ElementAt(i - 1); 1.324 + } 1.325 + } 1.326 + return nullptr; 1.327 +} 1.328 + 1.329 +} // namespace mozilla