1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/events/IMEStateManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,778 @@ 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 "mozilla/IMEStateManager.h" 1.11 + 1.12 +#include "mozilla/Attributes.h" 1.13 +#include "mozilla/EventStates.h" 1.14 +#include "mozilla/Preferences.h" 1.15 +#include "mozilla/Services.h" 1.16 +#include "mozilla/TextComposition.h" 1.17 +#include "mozilla/TextEvents.h" 1.18 +#include "mozilla/dom/HTMLFormElement.h" 1.19 + 1.20 +#include "HTMLInputElement.h" 1.21 +#include "IMEContentObserver.h" 1.22 + 1.23 +#include "nsCOMPtr.h" 1.24 +#include "nsContentUtils.h" 1.25 +#include "nsIContent.h" 1.26 +#include "nsIDocument.h" 1.27 +#include "nsIDOMMouseEvent.h" 1.28 +#include "nsIForm.h" 1.29 +#include "nsIFormControl.h" 1.30 +#include "nsINode.h" 1.31 +#include "nsIObserverService.h" 1.32 +#include "nsIPresShell.h" 1.33 +#include "nsISelection.h" 1.34 +#include "nsISupports.h" 1.35 +#include "nsPresContext.h" 1.36 + 1.37 +namespace mozilla { 1.38 + 1.39 +using namespace dom; 1.40 +using namespace widget; 1.41 + 1.42 +nsIContent* IMEStateManager::sContent = nullptr; 1.43 +nsPresContext* IMEStateManager::sPresContext = nullptr; 1.44 +bool IMEStateManager::sInstalledMenuKeyboardListener = false; 1.45 +bool IMEStateManager::sIsTestingIME = false; 1.46 + 1.47 +// sActiveIMEContentObserver points to the currently active IMEContentObserver. 1.48 +// sActiveIMEContentObserver is null if there is no focused editor. 1.49 +IMEContentObserver* IMEStateManager::sActiveIMEContentObserver = nullptr; 1.50 +TextCompositionArray* IMEStateManager::sTextCompositions = nullptr; 1.51 + 1.52 +void 1.53 +IMEStateManager::Shutdown() 1.54 +{ 1.55 + MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length()); 1.56 + delete sTextCompositions; 1.57 + sTextCompositions = nullptr; 1.58 +} 1.59 + 1.60 +nsresult 1.61 +IMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext) 1.62 +{ 1.63 + NS_ENSURE_ARG_POINTER(aPresContext); 1.64 + 1.65 + // First, if there is a composition in the aPresContext, clean up it. 1.66 + if (sTextCompositions) { 1.67 + TextCompositionArray::index_type i = 1.68 + sTextCompositions->IndexOf(aPresContext); 1.69 + if (i != TextCompositionArray::NoIndex) { 1.70 + // there should be only one composition per presContext object. 1.71 + sTextCompositions->ElementAt(i)->Destroy(); 1.72 + sTextCompositions->RemoveElementAt(i); 1.73 + MOZ_ASSERT(sTextCompositions->IndexOf(aPresContext) == 1.74 + TextCompositionArray::NoIndex); 1.75 + } 1.76 + } 1.77 + 1.78 + if (aPresContext != sPresContext) { 1.79 + return NS_OK; 1.80 + } 1.81 + 1.82 + DestroyTextStateManager(); 1.83 + 1.84 + nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget(); 1.85 + if (widget) { 1.86 + IMEState newState = GetNewIMEState(sPresContext, nullptr); 1.87 + InputContextAction action(InputContextAction::CAUSE_UNKNOWN, 1.88 + InputContextAction::LOST_FOCUS); 1.89 + SetIMEState(newState, nullptr, widget, action); 1.90 + } 1.91 + NS_IF_RELEASE(sContent); 1.92 + sPresContext = nullptr; 1.93 + return NS_OK; 1.94 +} 1.95 + 1.96 +nsresult 1.97 +IMEStateManager::OnRemoveContent(nsPresContext* aPresContext, 1.98 + nsIContent* aContent) 1.99 +{ 1.100 + NS_ENSURE_ARG_POINTER(aPresContext); 1.101 + 1.102 + // First, if there is a composition in the aContent, clean up it. 1.103 + if (sTextCompositions) { 1.104 + nsRefPtr<TextComposition> compositionInContent = 1.105 + sTextCompositions->GetCompositionInContent(aPresContext, aContent); 1.106 + 1.107 + if (compositionInContent) { 1.108 + // Try resetting the native IME state. Be aware, typically, this method 1.109 + // is called during the content being removed. Then, the native 1.110 + // composition events which are caused by following APIs are ignored due 1.111 + // to unsafe to run script (in PresShell::HandleEvent()). 1.112 + nsCOMPtr<nsIWidget> widget = aPresContext->GetRootWidget(); 1.113 + if (widget) { 1.114 + nsresult rv = 1.115 + compositionInContent->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION); 1.116 + if (NS_FAILED(rv)) { 1.117 + compositionInContent->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION); 1.118 + } 1.119 + // By calling the APIs, the composition may have been finished normally. 1.120 + compositionInContent = 1.121 + sTextCompositions->GetCompositionFor( 1.122 + compositionInContent->GetPresContext(), 1.123 + compositionInContent->GetEventTargetNode()); 1.124 + } 1.125 + } 1.126 + 1.127 + // If the compositionInContent is still available, we should finish the 1.128 + // composition just on the content forcibly. 1.129 + if (compositionInContent) { 1.130 + compositionInContent->SynthesizeCommit(true); 1.131 + } 1.132 + } 1.133 + 1.134 + if (!sPresContext || !sContent || 1.135 + !nsContentUtils::ContentIsDescendantOf(sContent, aContent)) { 1.136 + return NS_OK; 1.137 + } 1.138 + 1.139 + DestroyTextStateManager(); 1.140 + 1.141 + // Current IME transaction should commit 1.142 + nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget(); 1.143 + if (widget) { 1.144 + IMEState newState = GetNewIMEState(sPresContext, nullptr); 1.145 + InputContextAction action(InputContextAction::CAUSE_UNKNOWN, 1.146 + InputContextAction::LOST_FOCUS); 1.147 + SetIMEState(newState, nullptr, widget, action); 1.148 + } 1.149 + 1.150 + NS_IF_RELEASE(sContent); 1.151 + sPresContext = nullptr; 1.152 + 1.153 + return NS_OK; 1.154 +} 1.155 + 1.156 +nsresult 1.157 +IMEStateManager::OnChangeFocus(nsPresContext* aPresContext, 1.158 + nsIContent* aContent, 1.159 + InputContextAction::Cause aCause) 1.160 +{ 1.161 + InputContextAction action(aCause); 1.162 + return OnChangeFocusInternal(aPresContext, aContent, action); 1.163 +} 1.164 + 1.165 +nsresult 1.166 +IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext, 1.167 + nsIContent* aContent, 1.168 + InputContextAction aAction) 1.169 +{ 1.170 + bool focusActuallyChanging = 1.171 + (sContent != aContent || sPresContext != aPresContext); 1.172 + 1.173 + nsCOMPtr<nsIWidget> oldWidget = 1.174 + sPresContext ? sPresContext->GetRootWidget() : nullptr; 1.175 + if (oldWidget && focusActuallyChanging) { 1.176 + // If we're deactivating, we shouldn't commit composition forcibly because 1.177 + // the user may want to continue the composition. 1.178 + if (aPresContext) { 1.179 + NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget); 1.180 + } 1.181 + } 1.182 + 1.183 + if (sActiveIMEContentObserver && 1.184 + (aPresContext || !sActiveIMEContentObserver->KeepAliveDuringDeactive()) && 1.185 + !sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) { 1.186 + DestroyTextStateManager(); 1.187 + } 1.188 + 1.189 + if (!aPresContext) { 1.190 + return NS_OK; 1.191 + } 1.192 + 1.193 + nsCOMPtr<nsIWidget> widget = 1.194 + (sPresContext == aPresContext) ? oldWidget.get() : 1.195 + aPresContext->GetRootWidget(); 1.196 + if (!widget) { 1.197 + return NS_OK; 1.198 + } 1.199 + 1.200 + IMEState newState = GetNewIMEState(aPresContext, aContent); 1.201 + if (!focusActuallyChanging) { 1.202 + // actual focus isn't changing, but if IME enabled state is changing, 1.203 + // we should do it. 1.204 + InputContext context = widget->GetInputContext(); 1.205 + if (context.mIMEState.mEnabled == newState.mEnabled) { 1.206 + // the enabled state isn't changing. 1.207 + return NS_OK; 1.208 + } 1.209 + aAction.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED; 1.210 + 1.211 + // Even if focus isn't changing actually, we should commit current 1.212 + // composition here since the IME state is changing. 1.213 + if (sPresContext && oldWidget && !focusActuallyChanging) { 1.214 + NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget); 1.215 + } 1.216 + } else if (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED) { 1.217 + // If aContent isn't null or aContent is null but editable, somebody gets 1.218 + // focus. 1.219 + bool gotFocus = aContent || (newState.mEnabled == IMEState::ENABLED); 1.220 + aAction.mFocusChange = 1.221 + gotFocus ? InputContextAction::GOT_FOCUS : InputContextAction::LOST_FOCUS; 1.222 + } 1.223 + 1.224 + // Update IME state for new focus widget 1.225 + SetIMEState(newState, aContent, widget, aAction); 1.226 + 1.227 + sPresContext = aPresContext; 1.228 + if (sContent != aContent) { 1.229 + NS_IF_RELEASE(sContent); 1.230 + NS_IF_ADDREF(sContent = aContent); 1.231 + } 1.232 + 1.233 + // Don't call CreateIMEContentObserver() here, it should be called from 1.234 + // focus event handler of editor. 1.235 + 1.236 + return NS_OK; 1.237 +} 1.238 + 1.239 +void 1.240 +IMEStateManager::OnInstalledMenuKeyboardListener(bool aInstalling) 1.241 +{ 1.242 + sInstalledMenuKeyboardListener = aInstalling; 1.243 + 1.244 + InputContextAction action(InputContextAction::CAUSE_UNKNOWN, 1.245 + aInstalling ? InputContextAction::MENU_GOT_PSEUDO_FOCUS : 1.246 + InputContextAction::MENU_LOST_PSEUDO_FOCUS); 1.247 + OnChangeFocusInternal(sPresContext, sContent, action); 1.248 +} 1.249 + 1.250 +void 1.251 +IMEStateManager::OnClickInEditor(nsPresContext* aPresContext, 1.252 + nsIContent* aContent, 1.253 + nsIDOMMouseEvent* aMouseEvent) 1.254 +{ 1.255 + if (sPresContext != aPresContext || sContent != aContent) { 1.256 + return; 1.257 + } 1.258 + 1.259 + nsCOMPtr<nsIWidget> widget = aPresContext->GetRootWidget(); 1.260 + NS_ENSURE_TRUE_VOID(widget); 1.261 + 1.262 + bool isTrusted; 1.263 + nsresult rv = aMouseEvent->GetIsTrusted(&isTrusted); 1.264 + NS_ENSURE_SUCCESS_VOID(rv); 1.265 + if (!isTrusted) { 1.266 + return; // ignore untrusted event. 1.267 + } 1.268 + 1.269 + int16_t button; 1.270 + rv = aMouseEvent->GetButton(&button); 1.271 + NS_ENSURE_SUCCESS_VOID(rv); 1.272 + if (button != 0) { 1.273 + return; // not a left click event. 1.274 + } 1.275 + 1.276 + int32_t clickCount; 1.277 + rv = aMouseEvent->GetDetail(&clickCount); 1.278 + NS_ENSURE_SUCCESS_VOID(rv); 1.279 + if (clickCount != 1) { 1.280 + return; // should notify only first click event. 1.281 + } 1.282 + 1.283 + InputContextAction action(InputContextAction::CAUSE_MOUSE, 1.284 + InputContextAction::FOCUS_NOT_CHANGED); 1.285 + IMEState newState = GetNewIMEState(aPresContext, aContent); 1.286 + SetIMEState(newState, aContent, widget, action); 1.287 +} 1.288 + 1.289 +void 1.290 +IMEStateManager::OnFocusInEditor(nsPresContext* aPresContext, 1.291 + nsIContent* aContent) 1.292 +{ 1.293 + if (sPresContext != aPresContext || sContent != aContent) { 1.294 + return; 1.295 + } 1.296 + 1.297 + // If the IMEContentObserver instance isn't managing the editor actually, 1.298 + // we need to recreate the instance. 1.299 + if (sActiveIMEContentObserver) { 1.300 + if (sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) { 1.301 + return; 1.302 + } 1.303 + DestroyTextStateManager(); 1.304 + } 1.305 + 1.306 + CreateIMEContentObserver(); 1.307 +} 1.308 + 1.309 +void 1.310 +IMEStateManager::UpdateIMEState(const IMEState& aNewIMEState, 1.311 + nsIContent* aContent) 1.312 +{ 1.313 + if (!sPresContext) { 1.314 + NS_WARNING("ISM doesn't know which editor has focus"); 1.315 + return; 1.316 + } 1.317 + nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget(); 1.318 + if (!widget) { 1.319 + NS_WARNING("focused widget is not found"); 1.320 + return; 1.321 + } 1.322 + 1.323 + // If the IMEContentObserver instance isn't managing the editor's current 1.324 + // editable root content, the editor frame might be reframed. We should 1.325 + // recreate the instance at that time. 1.326 + bool createTextStateManager = 1.327 + (!sActiveIMEContentObserver || 1.328 + !sActiveIMEContentObserver->IsManaging(sPresContext, aContent)); 1.329 + 1.330 + bool updateIMEState = 1.331 + (widget->GetInputContext().mIMEState.mEnabled != aNewIMEState.mEnabled); 1.332 + 1.333 + if (updateIMEState) { 1.334 + // commit current composition before modifying IME state. 1.335 + NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, widget); 1.336 + } 1.337 + 1.338 + if (createTextStateManager) { 1.339 + DestroyTextStateManager(); 1.340 + } 1.341 + 1.342 + if (updateIMEState) { 1.343 + InputContextAction action(InputContextAction::CAUSE_UNKNOWN, 1.344 + InputContextAction::FOCUS_NOT_CHANGED); 1.345 + SetIMEState(aNewIMEState, aContent, widget, action); 1.346 + } 1.347 + 1.348 + if (createTextStateManager) { 1.349 + CreateIMEContentObserver(); 1.350 + } 1.351 +} 1.352 + 1.353 +IMEState 1.354 +IMEStateManager::GetNewIMEState(nsPresContext* aPresContext, 1.355 + nsIContent* aContent) 1.356 +{ 1.357 + // On Printing or Print Preview, we don't need IME. 1.358 + if (aPresContext->Type() == nsPresContext::eContext_PrintPreview || 1.359 + aPresContext->Type() == nsPresContext::eContext_Print) { 1.360 + return IMEState(IMEState::DISABLED); 1.361 + } 1.362 + 1.363 + if (sInstalledMenuKeyboardListener) { 1.364 + return IMEState(IMEState::DISABLED); 1.365 + } 1.366 + 1.367 + if (!aContent) { 1.368 + // Even if there are no focused content, the focused document might be 1.369 + // editable, such case is design mode. 1.370 + nsIDocument* doc = aPresContext->Document(); 1.371 + if (doc && doc->HasFlag(NODE_IS_EDITABLE)) { 1.372 + return IMEState(IMEState::ENABLED); 1.373 + } 1.374 + return IMEState(IMEState::DISABLED); 1.375 + } 1.376 + 1.377 + return aContent->GetDesiredIMEState(); 1.378 +} 1.379 + 1.380 +// Helper class, used for IME enabled state change notification 1.381 +class IMEEnabledStateChangedEvent : public nsRunnable { 1.382 +public: 1.383 + IMEEnabledStateChangedEvent(uint32_t aState) 1.384 + : mState(aState) 1.385 + { 1.386 + } 1.387 + 1.388 + NS_IMETHOD Run() 1.389 + { 1.390 + nsCOMPtr<nsIObserverService> observerService = 1.391 + services::GetObserverService(); 1.392 + if (observerService) { 1.393 + nsAutoString state; 1.394 + state.AppendInt(mState); 1.395 + observerService->NotifyObservers(nullptr, "ime-enabled-state-changed", 1.396 + state.get()); 1.397 + } 1.398 + return NS_OK; 1.399 + } 1.400 + 1.401 +private: 1.402 + uint32_t mState; 1.403 +}; 1.404 + 1.405 +void 1.406 +IMEStateManager::SetIMEState(const IMEState& aState, 1.407 + nsIContent* aContent, 1.408 + nsIWidget* aWidget, 1.409 + InputContextAction aAction) 1.410 +{ 1.411 + NS_ENSURE_TRUE_VOID(aWidget); 1.412 + 1.413 + InputContext oldContext = aWidget->GetInputContext(); 1.414 + 1.415 + InputContext context; 1.416 + context.mIMEState = aState; 1.417 + 1.418 + if (aContent && aContent->GetNameSpaceID() == kNameSpaceID_XHTML && 1.419 + (aContent->Tag() == nsGkAtoms::input || 1.420 + aContent->Tag() == nsGkAtoms::textarea)) { 1.421 + if (aContent->Tag() != nsGkAtoms::textarea) { 1.422 + // <input type=number> has an anonymous <input type=text> descendant 1.423 + // that gets focus whenever anyone tries to focus the number control. We 1.424 + // need to check if aContent is one of those anonymous text controls and, 1.425 + // if so, use the number control instead: 1.426 + nsIContent* content = aContent; 1.427 + HTMLInputElement* inputElement = 1.428 + HTMLInputElement::FromContentOrNull(aContent); 1.429 + if (inputElement) { 1.430 + HTMLInputElement* ownerNumberControl = 1.431 + inputElement->GetOwnerNumberControl(); 1.432 + if (ownerNumberControl) { 1.433 + content = ownerNumberControl; // an <input type=number> 1.434 + } 1.435 + } 1.436 + content->GetAttr(kNameSpaceID_None, nsGkAtoms::type, 1.437 + context.mHTMLInputType); 1.438 + } else { 1.439 + context.mHTMLInputType.Assign(nsGkAtoms::textarea->GetUTF16String()); 1.440 + } 1.441 + 1.442 + if (Preferences::GetBool("dom.forms.inputmode", false)) { 1.443 + aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::inputmode, 1.444 + context.mHTMLInputInputmode); 1.445 + } 1.446 + 1.447 + aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::moz_action_hint, 1.448 + context.mActionHint); 1.449 + 1.450 + // if we don't have an action hint and return won't submit the form use "next" 1.451 + if (context.mActionHint.IsEmpty() && aContent->Tag() == nsGkAtoms::input) { 1.452 + bool willSubmit = false; 1.453 + nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent)); 1.454 + mozilla::dom::Element* formElement = control->GetFormElement(); 1.455 + nsCOMPtr<nsIForm> form; 1.456 + if (control) { 1.457 + // is this a form and does it have a default submit element? 1.458 + if ((form = do_QueryInterface(formElement)) && 1.459 + form->GetDefaultSubmitElement()) { 1.460 + willSubmit = true; 1.461 + // is this an html form and does it only have a single text input element? 1.462 + } else if (formElement && formElement->Tag() == nsGkAtoms::form && 1.463 + formElement->IsHTML() && 1.464 + !static_cast<dom::HTMLFormElement*>(formElement)-> 1.465 + ImplicitSubmissionIsDisabled()) { 1.466 + willSubmit = true; 1.467 + } 1.468 + } 1.469 + context.mActionHint.Assign( 1.470 + willSubmit ? (control->GetType() == NS_FORM_INPUT_SEARCH ? 1.471 + NS_LITERAL_STRING("search") : NS_LITERAL_STRING("go")) : 1.472 + (formElement ? 1.473 + NS_LITERAL_STRING("next") : EmptyString())); 1.474 + } 1.475 + } 1.476 + 1.477 + // XXX I think that we should use nsContentUtils::IsCallerChrome() instead 1.478 + // of the process type. 1.479 + if (aAction.mCause == InputContextAction::CAUSE_UNKNOWN && 1.480 + XRE_GetProcessType() != GeckoProcessType_Content) { 1.481 + aAction.mCause = InputContextAction::CAUSE_UNKNOWN_CHROME; 1.482 + } 1.483 + 1.484 + aWidget->SetInputContext(context, aAction); 1.485 + if (oldContext.mIMEState.mEnabled == context.mIMEState.mEnabled) { 1.486 + return; 1.487 + } 1.488 + 1.489 + nsContentUtils::AddScriptRunner( 1.490 + new IMEEnabledStateChangedEvent(context.mIMEState.mEnabled)); 1.491 +} 1.492 + 1.493 +void 1.494 +IMEStateManager::EnsureTextCompositionArray() 1.495 +{ 1.496 + if (sTextCompositions) { 1.497 + return; 1.498 + } 1.499 + sTextCompositions = new TextCompositionArray(); 1.500 +} 1.501 + 1.502 +void 1.503 +IMEStateManager::DispatchCompositionEvent(nsINode* aEventTargetNode, 1.504 + nsPresContext* aPresContext, 1.505 + WidgetEvent* aEvent, 1.506 + nsEventStatus* aStatus, 1.507 + EventDispatchingCallback* aCallBack) 1.508 +{ 1.509 + MOZ_ASSERT(aEvent->eventStructType == NS_COMPOSITION_EVENT || 1.510 + aEvent->eventStructType == NS_TEXT_EVENT); 1.511 + if (!aEvent->mFlags.mIsTrusted || aEvent->mFlags.mPropagationStopped) { 1.512 + return; 1.513 + } 1.514 + 1.515 + EnsureTextCompositionArray(); 1.516 + 1.517 + WidgetGUIEvent* GUIEvent = aEvent->AsGUIEvent(); 1.518 + 1.519 + nsRefPtr<TextComposition> composition = 1.520 + sTextCompositions->GetCompositionFor(GUIEvent->widget); 1.521 + if (!composition) { 1.522 + MOZ_ASSERT(GUIEvent->message == NS_COMPOSITION_START); 1.523 + composition = new TextComposition(aPresContext, aEventTargetNode, GUIEvent); 1.524 + sTextCompositions->AppendElement(composition); 1.525 + } 1.526 +#ifdef DEBUG 1.527 + else { 1.528 + MOZ_ASSERT(GUIEvent->message != NS_COMPOSITION_START); 1.529 + } 1.530 +#endif // #ifdef DEBUG 1.531 + 1.532 + // Dispatch the event on composing target. 1.533 + composition->DispatchEvent(GUIEvent, aStatus, aCallBack); 1.534 + 1.535 + // WARNING: the |composition| might have been destroyed already. 1.536 + 1.537 + // Remove the ended composition from the array. 1.538 + if (aEvent->message == NS_COMPOSITION_END) { 1.539 + TextCompositionArray::index_type i = 1.540 + sTextCompositions->IndexOf(GUIEvent->widget); 1.541 + if (i != TextCompositionArray::NoIndex) { 1.542 + sTextCompositions->ElementAt(i)->Destroy(); 1.543 + sTextCompositions->RemoveElementAt(i); 1.544 + } 1.545 + } 1.546 +} 1.547 + 1.548 +// static 1.549 +nsresult 1.550 +IMEStateManager::NotifyIME(IMEMessage aMessage, 1.551 + nsIWidget* aWidget) 1.552 +{ 1.553 + NS_ENSURE_TRUE(aWidget, NS_ERROR_INVALID_ARG); 1.554 + 1.555 + nsRefPtr<TextComposition> composition; 1.556 + if (sTextCompositions) { 1.557 + composition = sTextCompositions->GetCompositionFor(aWidget); 1.558 + } 1.559 + if (!composition || !composition->IsSynthesizedForTests()) { 1.560 + switch (aMessage) { 1.561 + case NOTIFY_IME_OF_CURSOR_POS_CHANGED: 1.562 + return aWidget->NotifyIME(IMENotification(aMessage)); 1.563 + case REQUEST_TO_COMMIT_COMPOSITION: 1.564 + case REQUEST_TO_CANCEL_COMPOSITION: 1.565 + case NOTIFY_IME_OF_COMPOSITION_UPDATE: 1.566 + return composition ? 1.567 + aWidget->NotifyIME(IMENotification(aMessage)) : NS_OK; 1.568 + default: 1.569 + MOZ_CRASH("Unsupported notification"); 1.570 + } 1.571 + MOZ_CRASH( 1.572 + "Failed to handle the notification for non-synthesized composition"); 1.573 + } 1.574 + 1.575 + // If the composition is synthesized events for automated tests, we should 1.576 + // dispatch composition events for emulating the native composition behavior. 1.577 + // NOTE: The dispatched events are discarded if it's not safe to run script. 1.578 + switch (aMessage) { 1.579 + case REQUEST_TO_COMMIT_COMPOSITION: { 1.580 + nsCOMPtr<nsIWidget> widget(aWidget); 1.581 + nsEventStatus status = nsEventStatus_eIgnore; 1.582 + if (!composition->LastData().IsEmpty()) { 1.583 + WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget); 1.584 + textEvent.theText = composition->LastData(); 1.585 + textEvent.mFlags.mIsSynthesizedForTests = true; 1.586 + widget->DispatchEvent(&textEvent, status); 1.587 + if (widget->Destroyed()) { 1.588 + return NS_OK; 1.589 + } 1.590 + } 1.591 + 1.592 + status = nsEventStatus_eIgnore; 1.593 + WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget); 1.594 + endEvent.data = composition->LastData(); 1.595 + endEvent.mFlags.mIsSynthesizedForTests = true; 1.596 + widget->DispatchEvent(&endEvent, status); 1.597 + 1.598 + return NS_OK; 1.599 + } 1.600 + case REQUEST_TO_CANCEL_COMPOSITION: { 1.601 + nsCOMPtr<nsIWidget> widget(aWidget); 1.602 + nsEventStatus status = nsEventStatus_eIgnore; 1.603 + if (!composition->LastData().IsEmpty()) { 1.604 + WidgetCompositionEvent updateEvent(true, NS_COMPOSITION_UPDATE, widget); 1.605 + updateEvent.data = composition->LastData(); 1.606 + updateEvent.mFlags.mIsSynthesizedForTests = true; 1.607 + widget->DispatchEvent(&updateEvent, status); 1.608 + if (widget->Destroyed()) { 1.609 + return NS_OK; 1.610 + } 1.611 + 1.612 + status = nsEventStatus_eIgnore; 1.613 + WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget); 1.614 + textEvent.theText = composition->LastData(); 1.615 + textEvent.mFlags.mIsSynthesizedForTests = true; 1.616 + widget->DispatchEvent(&textEvent, status); 1.617 + if (widget->Destroyed()) { 1.618 + return NS_OK; 1.619 + } 1.620 + } 1.621 + 1.622 + status = nsEventStatus_eIgnore; 1.623 + WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget); 1.624 + endEvent.data = composition->LastData(); 1.625 + endEvent.mFlags.mIsSynthesizedForTests = true; 1.626 + widget->DispatchEvent(&endEvent, status); 1.627 + 1.628 + return NS_OK; 1.629 + } 1.630 + default: 1.631 + return NS_OK; 1.632 + } 1.633 +} 1.634 + 1.635 +// static 1.636 +nsresult 1.637 +IMEStateManager::NotifyIME(IMEMessage aMessage, 1.638 + nsPresContext* aPresContext) 1.639 +{ 1.640 + NS_ENSURE_TRUE(aPresContext, NS_ERROR_INVALID_ARG); 1.641 + 1.642 + nsIWidget* widget = aPresContext->GetRootWidget(); 1.643 + if (!widget) { 1.644 + return NS_ERROR_NOT_AVAILABLE; 1.645 + } 1.646 + return NotifyIME(aMessage, widget); 1.647 +} 1.648 + 1.649 +bool 1.650 +IMEStateManager::IsEditable(nsINode* node) 1.651 +{ 1.652 + if (node->IsEditable()) { 1.653 + return true; 1.654 + } 1.655 + // |node| might be readwrite (for example, a text control) 1.656 + if (node->IsElement() && 1.657 + node->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) { 1.658 + return true; 1.659 + } 1.660 + return false; 1.661 +} 1.662 + 1.663 +nsINode* 1.664 +IMEStateManager::GetRootEditableNode(nsPresContext* aPresContext, 1.665 + nsIContent* aContent) 1.666 +{ 1.667 + if (aContent) { 1.668 + nsINode* root = nullptr; 1.669 + nsINode* node = aContent; 1.670 + while (node && IsEditable(node)) { 1.671 + root = node; 1.672 + node = node->GetParentNode(); 1.673 + } 1.674 + return root; 1.675 + } 1.676 + if (aPresContext) { 1.677 + nsIDocument* document = aPresContext->Document(); 1.678 + if (document && document->IsEditable()) { 1.679 + return document; 1.680 + } 1.681 + } 1.682 + return nullptr; 1.683 +} 1.684 + 1.685 +bool 1.686 +IMEStateManager::IsEditableIMEState(nsIWidget* aWidget) 1.687 +{ 1.688 + switch (aWidget->GetInputContext().mIMEState.mEnabled) { 1.689 + case IMEState::ENABLED: 1.690 + case IMEState::PASSWORD: 1.691 + return true; 1.692 + case IMEState::PLUGIN: 1.693 + case IMEState::DISABLED: 1.694 + return false; 1.695 + default: 1.696 + MOZ_CRASH("Unknown IME enable state"); 1.697 + } 1.698 +} 1.699 + 1.700 +void 1.701 +IMEStateManager::DestroyTextStateManager() 1.702 +{ 1.703 + if (!sActiveIMEContentObserver) { 1.704 + return; 1.705 + } 1.706 + 1.707 + nsRefPtr<IMEContentObserver> tsm; 1.708 + tsm.swap(sActiveIMEContentObserver); 1.709 + tsm->Destroy(); 1.710 +} 1.711 + 1.712 +void 1.713 +IMEStateManager::CreateIMEContentObserver() 1.714 +{ 1.715 + if (sActiveIMEContentObserver) { 1.716 + NS_WARNING("text state observer has been there already"); 1.717 + MOZ_ASSERT(sActiveIMEContentObserver->IsManaging(sPresContext, sContent)); 1.718 + return; 1.719 + } 1.720 + 1.721 + nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget(); 1.722 + if (!widget) { 1.723 + return; // Sometimes, there are no widgets. 1.724 + } 1.725 + 1.726 + // If it's not text ediable, we don't need to create IMEContentObserver. 1.727 + if (!IsEditableIMEState(widget)) { 1.728 + return; 1.729 + } 1.730 + 1.731 + static bool sInitializeIsTestingIME = true; 1.732 + if (sInitializeIsTestingIME) { 1.733 + Preferences::AddBoolVarCache(&sIsTestingIME, "test.IME", false); 1.734 + sInitializeIsTestingIME = false; 1.735 + } 1.736 + 1.737 + sActiveIMEContentObserver = new IMEContentObserver(); 1.738 + NS_ADDREF(sActiveIMEContentObserver); 1.739 + 1.740 + // IMEContentObserver::Init() might create another IMEContentObserver 1.741 + // instance. So, sActiveIMEContentObserver would be replaced with new one. 1.742 + // We should hold the current instance here. 1.743 + nsRefPtr<IMEContentObserver> kungFuDeathGrip(sActiveIMEContentObserver); 1.744 + sActiveIMEContentObserver->Init(widget, sPresContext, sContent); 1.745 +} 1.746 + 1.747 +nsresult 1.748 +IMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSelection, 1.749 + nsIContent** aRootContent) 1.750 +{ 1.751 + if (!sActiveIMEContentObserver) { 1.752 + return NS_ERROR_NOT_AVAILABLE; 1.753 + } 1.754 + return sActiveIMEContentObserver->GetSelectionAndRoot(aSelection, 1.755 + aRootContent); 1.756 +} 1.757 + 1.758 +// static 1.759 +already_AddRefed<TextComposition> 1.760 +IMEStateManager::GetTextCompositionFor(nsIWidget* aWidget) 1.761 +{ 1.762 + if (!sTextCompositions) { 1.763 + return nullptr; 1.764 + } 1.765 + nsRefPtr<TextComposition> textComposition = 1.766 + sTextCompositions->GetCompositionFor(aWidget); 1.767 + return textComposition.forget(); 1.768 +} 1.769 + 1.770 +// static 1.771 +already_AddRefed<TextComposition> 1.772 +IMEStateManager::GetTextCompositionFor(WidgetGUIEvent* aEvent) 1.773 +{ 1.774 + MOZ_ASSERT(aEvent->AsCompositionEvent() || aEvent->AsTextEvent() || 1.775 + aEvent->AsKeyboardEvent(), 1.776 + "aEvent has to be WidgetCompositionEvent, WidgetTextEvent or " 1.777 + "WidgetKeyboardEvent"); 1.778 + return GetTextCompositionFor(aEvent->widget); 1.779 +} 1.780 + 1.781 +} // namespace mozilla