author | Masayuki Nakano <masayuki@d-toybox.com> |
Sat, 08 Mar 2014 10:20:07 +0900 | |
changeset 172622 | ba18273bd056f540f29caef41b27c75d649a81d6 |
parent 172621 | 9cf34c82f975bdcaff30c22c299dfcd1a66df99a |
child 172623 | 21ed4b9b06153df0d9ba04e0acc6e7a40491bf21 |
push id | 40782 |
push user | masayuki@d-toybox.com |
push date | Sat, 08 Mar 2014 01:20:16 +0000 |
treeherder | mozilla-inbound@062114c06fee [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | smaug |
bugs | 978023 |
milestone | 30.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
|
copy from dom/events/nsIMEStateManager.cpp copy to dom/events/IMEContentObserver.cpp --- a/dom/events/nsIMEStateManager.cpp +++ b/dom/events/IMEContentObserver.cpp @@ -1,711 +1,56 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 sw=2 et tw=80: */ /* 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 "nsIMEStateManager.h" - -#include "HTMLInputElement.h" -#include "nsCOMPtr.h" -#include "nsIPresShell.h" -#include "nsISupports.h" +#include "IMEContentObserver.h" +#include "mozilla/dom/Element.h" +#include "nsAutoPtr.h" +#include "nsAsyncDOMEvent.h" +#include "nsContentEventHandler.h" +#include "nsContentUtils.h" +#include "nsGkAtoms.h" +#include "nsIAtom.h" #include "nsIContent.h" #include "nsIDocument.h" -#include "nsPresContext.h" -#include "nsIDOMMouseEvent.h" -#include "nsContentUtils.h" -#include "nsINode.h" +#include "nsIDOMDocument.h" +#include "nsIDOMRange.h" #include "nsIFrame.h" -#include "nsRange.h" -#include "nsIDOMRange.h" -#include "nsISelection.h" -#include "nsISelectionPrivate.h" -#include "nsISelectionListener.h" +#include "nsIMEStateManager.h" +#include "nsINode.h" +#include "nsIPresShell.h" #include "nsISelectionController.h" -#include "nsIMutationObserver.h" -#include "nsContentEventHandler.h" -#include "nsIObserverService.h" -#include "mozilla/Services.h" -#include "nsIFormControl.h" -#include "nsIForm.h" -#include "mozilla/dom/HTMLFormElement.h" -#include "mozilla/Attributes.h" -#include "mozilla/TextEvents.h" -#include "TextComposition.h" -#include "mozilla/Preferences.h" -#include "nsAsyncDOMEvent.h" -#include "nsIDocShell.h" -#include "nsIReflowObserver.h" -#include "nsIScrollObserver.h" +#include "nsISelectionPrivate.h" +#include "nsISupports.h" +#include "nsIWidget.h" +#include "nsPresContext.h" +#include "nsThreadUtils.h" #include "nsWeakReference.h" - -using namespace mozilla; -using namespace mozilla::dom; -using namespace mozilla::widget; - -// nsTextStateManager notifies widget of any text and selection changes -// in the currently focused editor -// sTextStateObserver points to the currently active nsTextStateManager -// sTextStateObserver is null if there is no focused editor - -class nsTextStateManager MOZ_FINAL : public nsISelectionListener, - public nsStubMutationObserver, - public nsIReflowObserver, - public nsIScrollObserver, - public nsSupportsWeakReference -{ -public: - nsTextStateManager() - { - } - - NS_DECL_ISUPPORTS - NS_DECL_NSISELECTIONLISTENER - NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED - NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED - NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED - NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED - NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE - NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED - NS_DECL_NSIREFLOWOBSERVER - - // nsIScrollObserver - virtual void ScrollPositionChanged() MOZ_OVERRIDE; - - void Init(nsIWidget* aWidget, - nsPresContext* aPresContext, - nsIContent* aContent); - void Destroy(void); - bool IsManaging(nsPresContext* aPresContext, nsIContent* aContent); - bool IsEditorHandlingEventForComposition() const; - bool KeepAliveDuringDeactive() const - { - return mUpdatePreference.WantDuringDeactive(); - } - - nsCOMPtr<nsIWidget> mWidget; - nsCOMPtr<nsISelection> mSel; - nsCOMPtr<nsIContent> mRootContent; - nsCOMPtr<nsINode> mEditableNode; - -private: - void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd); - void ObserveEditableNode(); - - nsCOMPtr<nsIDocShell> mDocShell; - nsIMEUpdatePreference mUpdatePreference; - uint32_t mPreAttrChangeLength; -}; - -/******************************************************************/ -/* nsIMEStateManager */ -/******************************************************************/ - -nsIContent* nsIMEStateManager::sContent = nullptr; -nsPresContext* nsIMEStateManager::sPresContext = nullptr; -bool nsIMEStateManager::sInstalledMenuKeyboardListener = false; -bool nsIMEStateManager::sIsTestingIME = false; - -nsTextStateManager* nsIMEStateManager::sTextStateObserver = nullptr; -TextCompositionArray* nsIMEStateManager::sTextCompositions = nullptr; - -void -nsIMEStateManager::Shutdown() -{ - MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length()); - delete sTextCompositions; - sTextCompositions = nullptr; -} - -nsresult -nsIMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext) -{ - NS_ENSURE_ARG_POINTER(aPresContext); - - // First, if there is a composition in the aPresContext, clean up it. - if (sTextCompositions) { - TextCompositionArray::index_type i = - sTextCompositions->IndexOf(aPresContext); - if (i != TextCompositionArray::NoIndex) { - // there should be only one composition per presContext object. - sTextCompositions->ElementAt(i)->Destroy(); - sTextCompositions->RemoveElementAt(i); - MOZ_ASSERT(sTextCompositions->IndexOf(aPresContext) == - TextCompositionArray::NoIndex); - } - } - - if (aPresContext != sPresContext) - return NS_OK; - - DestroyTextStateManager(); - - nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget(); - if (widget) { - IMEState newState = GetNewIMEState(sPresContext, nullptr); - InputContextAction action(InputContextAction::CAUSE_UNKNOWN, - InputContextAction::LOST_FOCUS); - SetIMEState(newState, nullptr, widget, action); - } - NS_IF_RELEASE(sContent); - sPresContext = nullptr; - return NS_OK; -} - -nsresult -nsIMEStateManager::OnRemoveContent(nsPresContext* aPresContext, - nsIContent* aContent) -{ - NS_ENSURE_ARG_POINTER(aPresContext); - - // First, if there is a composition in the aContent, clean up it. - if (sTextCompositions) { - nsRefPtr<TextComposition> compositionInContent = - sTextCompositions->GetCompositionInContent(aPresContext, aContent); +#include "TextComposition.h" - if (compositionInContent) { - // Try resetting the native IME state. Be aware, typically, this method - // is called during the content being removed. Then, the native - // composition events which are caused by following APIs are ignored due - // to unsafe to run script (in PresShell::HandleEvent()). - nsCOMPtr<nsIWidget> widget = aPresContext->GetRootWidget(); - if (widget) { - nsresult rv = - compositionInContent->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION); - if (NS_FAILED(rv)) { - compositionInContent->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION); - } - // By calling the APIs, the composition may have been finished normally. - compositionInContent = - sTextCompositions->GetCompositionFor( - compositionInContent->GetPresContext(), - compositionInContent->GetEventTargetNode()); - } - } - - // If the compositionInContent is still available, we should finish the - // composition just on the content forcibly. - if (compositionInContent) { - compositionInContent->SynthesizeCommit(true); - } - } - - if (!sPresContext || !sContent || - !nsContentUtils::ContentIsDescendantOf(sContent, aContent)) { - return NS_OK; - } - - DestroyTextStateManager(); - - // Current IME transaction should commit - nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget(); - if (widget) { - IMEState newState = GetNewIMEState(sPresContext, nullptr); - InputContextAction action(InputContextAction::CAUSE_UNKNOWN, - InputContextAction::LOST_FOCUS); - SetIMEState(newState, nullptr, widget, action); - } +namespace mozilla { - NS_IF_RELEASE(sContent); - sPresContext = nullptr; - - return NS_OK; -} - -nsresult -nsIMEStateManager::OnChangeFocus(nsPresContext* aPresContext, - nsIContent* aContent, - InputContextAction::Cause aCause) -{ - InputContextAction action(aCause); - return OnChangeFocusInternal(aPresContext, aContent, action); -} - -nsresult -nsIMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext, - nsIContent* aContent, - InputContextAction aAction) -{ - bool focusActuallyChanging = - (sContent != aContent || sPresContext != aPresContext); - - nsCOMPtr<nsIWidget> oldWidget = - sPresContext ? sPresContext->GetRootWidget() : nullptr; - if (oldWidget && focusActuallyChanging) { - // If we're deactivating, we shouldn't commit composition forcibly because - // the user may want to continue the composition. - if (aPresContext) { - NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget); - } - } - - if (sTextStateObserver && - (aPresContext || !sTextStateObserver->KeepAliveDuringDeactive()) && - !sTextStateObserver->IsManaging(aPresContext, aContent)) { - DestroyTextStateManager(); - } - - if (!aPresContext) { - return NS_OK; - } +using namespace widget; - nsCOMPtr<nsIWidget> widget = - (sPresContext == aPresContext) ? oldWidget.get() : - aPresContext->GetRootWidget(); - if (!widget) { - return NS_OK; - } - - IMEState newState = GetNewIMEState(aPresContext, aContent); - if (!focusActuallyChanging) { - // actual focus isn't changing, but if IME enabled state is changing, - // we should do it. - InputContext context = widget->GetInputContext(); - if (context.mIMEState.mEnabled == newState.mEnabled) { - // the enabled state isn't changing. - return NS_OK; - } - aAction.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED; - - // Even if focus isn't changing actually, we should commit current - // composition here since the IME state is changing. - if (sPresContext && oldWidget && !focusActuallyChanging) { - NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget); - } - } else if (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED) { - // If aContent isn't null or aContent is null but editable, somebody gets - // focus. - bool gotFocus = aContent || (newState.mEnabled == IMEState::ENABLED); - aAction.mFocusChange = - gotFocus ? InputContextAction::GOT_FOCUS : InputContextAction::LOST_FOCUS; - } - - // Update IME state for new focus widget - SetIMEState(newState, aContent, widget, aAction); - - sPresContext = aPresContext; - if (sContent != aContent) { - NS_IF_RELEASE(sContent); - NS_IF_ADDREF(sContent = aContent); - } - - // Don't call CreateTextStateManager() here, it should be called from - // focus event handler of editor. - - return NS_OK; -} +NS_IMPL_ISUPPORTS5(IMEContentObserver, + nsIMutationObserver, + nsISelectionListener, + nsIReflowObserver, + nsIScrollObserver, + nsISupportsWeakReference) -void -nsIMEStateManager::OnInstalledMenuKeyboardListener(bool aInstalling) -{ - sInstalledMenuKeyboardListener = aInstalling; - - InputContextAction action(InputContextAction::CAUSE_UNKNOWN, - aInstalling ? InputContextAction::MENU_GOT_PSEUDO_FOCUS : - InputContextAction::MENU_LOST_PSEUDO_FOCUS); - OnChangeFocusInternal(sPresContext, sContent, action); -} - -void -nsIMEStateManager::OnClickInEditor(nsPresContext* aPresContext, - nsIContent* aContent, - nsIDOMMouseEvent* aMouseEvent) +IMEContentObserver::IMEContentObserver() { - if (sPresContext != aPresContext || sContent != aContent) { - return; - } - - nsCOMPtr<nsIWidget> widget = aPresContext->GetRootWidget(); - NS_ENSURE_TRUE_VOID(widget); - - bool isTrusted; - nsresult rv = aMouseEvent->GetIsTrusted(&isTrusted); - NS_ENSURE_SUCCESS_VOID(rv); - if (!isTrusted) { - return; // ignore untrusted event. - } - - int16_t button; - rv = aMouseEvent->GetButton(&button); - NS_ENSURE_SUCCESS_VOID(rv); - if (button != 0) { - return; // not a left click event. - } - - int32_t clickCount; - rv = aMouseEvent->GetDetail(&clickCount); - NS_ENSURE_SUCCESS_VOID(rv); - if (clickCount != 1) { - return; // should notify only first click event. - } - - InputContextAction action(InputContextAction::CAUSE_MOUSE, - InputContextAction::FOCUS_NOT_CHANGED); - IMEState newState = GetNewIMEState(aPresContext, aContent); - SetIMEState(newState, aContent, widget, action); } void -nsIMEStateManager::OnFocusInEditor(nsPresContext* aPresContext, - nsIContent* aContent) -{ - if (sPresContext != aPresContext || sContent != aContent) { - return; - } - - // If the nsTextStateManager instance isn't managing the editor actually, - // we need to recreate the instance. - if (sTextStateObserver) { - if (sTextStateObserver->IsManaging(aPresContext, aContent)) { - return; - } - DestroyTextStateManager(); - } - - CreateTextStateManager(); -} - -void -nsIMEStateManager::UpdateIMEState(const IMEState &aNewIMEState, - nsIContent* aContent) -{ - if (!sPresContext) { - NS_WARNING("ISM doesn't know which editor has focus"); - return; - } - nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget(); - if (!widget) { - NS_WARNING("focused widget is not found"); - return; - } - - // If the nsTextStateManager instance isn't managing the editor's current - // editable root content, the editor frame might be reframed. We should - // recreate the instance at that time. - bool createTextStateManager = - (!sTextStateObserver || - !sTextStateObserver->IsManaging(sPresContext, aContent)); - - bool updateIMEState = - (widget->GetInputContext().mIMEState.mEnabled != aNewIMEState.mEnabled); - - if (updateIMEState) { - // commit current composition before modifying IME state. - NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, widget); - } - - if (createTextStateManager) { - DestroyTextStateManager(); - } - - if (updateIMEState) { - InputContextAction action(InputContextAction::CAUSE_UNKNOWN, - InputContextAction::FOCUS_NOT_CHANGED); - SetIMEState(aNewIMEState, aContent, widget, action); - } - - if (createTextStateManager) { - CreateTextStateManager(); - } -} - -IMEState -nsIMEStateManager::GetNewIMEState(nsPresContext* aPresContext, - nsIContent* aContent) -{ - // On Printing or Print Preview, we don't need IME. - if (aPresContext->Type() == nsPresContext::eContext_PrintPreview || - aPresContext->Type() == nsPresContext::eContext_Print) { - return IMEState(IMEState::DISABLED); - } - - if (sInstalledMenuKeyboardListener) { - return IMEState(IMEState::DISABLED); - } - - if (!aContent) { - // Even if there are no focused content, the focused document might be - // editable, such case is design mode. - nsIDocument* doc = aPresContext->Document(); - if (doc && doc->HasFlag(NODE_IS_EDITABLE)) { - return IMEState(IMEState::ENABLED); - } - return IMEState(IMEState::DISABLED); - } - - return aContent->GetDesiredIMEState(); -} - -// Helper class, used for IME enabled state change notification -class IMEEnabledStateChangedEvent : public nsRunnable { -public: - IMEEnabledStateChangedEvent(uint32_t aState) - : mState(aState) - { - } - - NS_IMETHOD Run() { - nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); - if (observerService) { - nsAutoString state; - state.AppendInt(mState); - observerService->NotifyObservers(nullptr, "ime-enabled-state-changed", state.get()); - } - return NS_OK; - } - -private: - uint32_t mState; -}; - -void -nsIMEStateManager::SetIMEState(const IMEState &aState, - nsIContent* aContent, - nsIWidget* aWidget, - InputContextAction aAction) -{ - NS_ENSURE_TRUE_VOID(aWidget); - - InputContext oldContext = aWidget->GetInputContext(); - - InputContext context; - context.mIMEState = aState; - - if (aContent && aContent->GetNameSpaceID() == kNameSpaceID_XHTML && - (aContent->Tag() == nsGkAtoms::input || - aContent->Tag() == nsGkAtoms::textarea)) { - if (aContent->Tag() != nsGkAtoms::textarea) { - // <input type=number> has an anonymous <input type=text> descendant - // that gets focus whenever anyone tries to focus the number control. We - // need to check if aContent is one of those anonymous text controls and, - // if so, use the number control instead: - nsIContent* content = aContent; - HTMLInputElement* inputElement = - HTMLInputElement::FromContentOrNull(aContent); - if (inputElement) { - HTMLInputElement* ownerNumberControl = - inputElement->GetOwnerNumberControl(); - if (ownerNumberControl) { - content = ownerNumberControl; // an <input type=number> - } - } - content->GetAttr(kNameSpaceID_None, nsGkAtoms::type, - context.mHTMLInputType); - } else { - context.mHTMLInputType.Assign(nsGkAtoms::textarea->GetUTF16String()); - } - - if (Preferences::GetBool("dom.forms.inputmode", false)) { - aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::inputmode, - context.mHTMLInputInputmode); - } - - aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::moz_action_hint, - context.mActionHint); - - // if we don't have an action hint and return won't submit the form use "next" - if (context.mActionHint.IsEmpty() && aContent->Tag() == nsGkAtoms::input) { - bool willSubmit = false; - nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent)); - mozilla::dom::Element* formElement = control->GetFormElement(); - nsCOMPtr<nsIForm> form; - if (control) { - // is this a form and does it have a default submit element? - if ((form = do_QueryInterface(formElement)) && form->GetDefaultSubmitElement()) { - willSubmit = true; - // is this an html form and does it only have a single text input element? - } else if (formElement && formElement->Tag() == nsGkAtoms::form && formElement->IsHTML() && - !static_cast<dom::HTMLFormElement*>(formElement)->ImplicitSubmissionIsDisabled()) { - willSubmit = true; - } - } - context.mActionHint.Assign(willSubmit ? control->GetType() == NS_FORM_INPUT_SEARCH - ? NS_LITERAL_STRING("search") - : NS_LITERAL_STRING("go") - : formElement - ? NS_LITERAL_STRING("next") - : EmptyString()); - } - } - - // XXX I think that we should use nsContentUtils::IsCallerChrome() instead - // of the process type. - if (aAction.mCause == InputContextAction::CAUSE_UNKNOWN && - XRE_GetProcessType() != GeckoProcessType_Content) { - aAction.mCause = InputContextAction::CAUSE_UNKNOWN_CHROME; - } - - aWidget->SetInputContext(context, aAction); - if (oldContext.mIMEState.mEnabled != context.mIMEState.mEnabled) { - nsContentUtils::AddScriptRunner( - new IMEEnabledStateChangedEvent(context.mIMEState.mEnabled)); - } -} - -void -nsIMEStateManager::EnsureTextCompositionArray() -{ - if (sTextCompositions) { - return; - } - sTextCompositions = new TextCompositionArray(); -} - -void -nsIMEStateManager::DispatchCompositionEvent(nsINode* aEventTargetNode, - nsPresContext* aPresContext, - WidgetEvent* aEvent, - nsEventStatus* aStatus, - nsDispatchingCallback* aCallBack) -{ - MOZ_ASSERT(aEvent->eventStructType == NS_COMPOSITION_EVENT || - aEvent->eventStructType == NS_TEXT_EVENT); - if (!aEvent->mFlags.mIsTrusted || aEvent->mFlags.mPropagationStopped) { - return; - } - - EnsureTextCompositionArray(); - - WidgetGUIEvent* GUIEvent = aEvent->AsGUIEvent(); - - nsRefPtr<TextComposition> composition = - sTextCompositions->GetCompositionFor(GUIEvent->widget); - if (!composition) { - MOZ_ASSERT(GUIEvent->message == NS_COMPOSITION_START); - composition = new TextComposition(aPresContext, aEventTargetNode, GUIEvent); - sTextCompositions->AppendElement(composition); - } -#ifdef DEBUG - else { - MOZ_ASSERT(GUIEvent->message != NS_COMPOSITION_START); - } -#endif // #ifdef DEBUG - - // Dispatch the event on composing target. - composition->DispatchEvent(GUIEvent, aStatus, aCallBack); - - // WARNING: the |composition| might have been destroyed already. - - // Remove the ended composition from the array. - if (aEvent->message == NS_COMPOSITION_END) { - TextCompositionArray::index_type i = - sTextCompositions->IndexOf(GUIEvent->widget); - if (i != TextCompositionArray::NoIndex) { - sTextCompositions->ElementAt(i)->Destroy(); - sTextCompositions->RemoveElementAt(i); - } - } -} - -// static -nsresult -nsIMEStateManager::NotifyIME(IMEMessage aMessage, - nsIWidget* aWidget) -{ - NS_ENSURE_TRUE(aWidget, NS_ERROR_INVALID_ARG); - - nsRefPtr<TextComposition> composition; - if (sTextCompositions) { - composition = sTextCompositions->GetCompositionFor(aWidget); - } - if (!composition || !composition->IsSynthesizedForTests()) { - switch (aMessage) { - case NOTIFY_IME_OF_CURSOR_POS_CHANGED: - return aWidget->NotifyIME(IMENotification(aMessage)); - case REQUEST_TO_COMMIT_COMPOSITION: - case REQUEST_TO_CANCEL_COMPOSITION: - case NOTIFY_IME_OF_COMPOSITION_UPDATE: - return composition ? - aWidget->NotifyIME(IMENotification(aMessage)) : NS_OK; - default: - MOZ_CRASH("Unsupported notification"); - } - MOZ_CRASH( - "Failed to handle the notification for non-synthesized composition"); - } - - // If the composition is synthesized events for automated tests, we should - // dispatch composition events for emulating the native composition behavior. - // NOTE: The dispatched events are discarded if it's not safe to run script. - switch (aMessage) { - case REQUEST_TO_COMMIT_COMPOSITION: { - nsCOMPtr<nsIWidget> widget(aWidget); - nsEventStatus status = nsEventStatus_eIgnore; - if (!composition->LastData().IsEmpty()) { - WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget); - textEvent.theText = composition->LastData(); - textEvent.mFlags.mIsSynthesizedForTests = true; - widget->DispatchEvent(&textEvent, status); - if (widget->Destroyed()) { - return NS_OK; - } - } - - status = nsEventStatus_eIgnore; - WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget); - endEvent.data = composition->LastData(); - endEvent.mFlags.mIsSynthesizedForTests = true; - widget->DispatchEvent(&endEvent, status); - - return NS_OK; - } - case REQUEST_TO_CANCEL_COMPOSITION: { - nsCOMPtr<nsIWidget> widget(aWidget); - nsEventStatus status = nsEventStatus_eIgnore; - if (!composition->LastData().IsEmpty()) { - WidgetCompositionEvent updateEvent(true, NS_COMPOSITION_UPDATE, widget); - updateEvent.data = composition->LastData(); - updateEvent.mFlags.mIsSynthesizedForTests = true; - widget->DispatchEvent(&updateEvent, status); - if (widget->Destroyed()) { - return NS_OK; - } - - status = nsEventStatus_eIgnore; - WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget); - textEvent.theText = composition->LastData(); - textEvent.mFlags.mIsSynthesizedForTests = true; - widget->DispatchEvent(&textEvent, status); - if (widget->Destroyed()) { - return NS_OK; - } - } - - status = nsEventStatus_eIgnore; - WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget); - endEvent.data = composition->LastData(); - endEvent.mFlags.mIsSynthesizedForTests = true; - widget->DispatchEvent(&endEvent, status); - - return NS_OK; - } - default: - return NS_OK; - } -} - -// static -nsresult -nsIMEStateManager::NotifyIME(IMEMessage aMessage, - nsPresContext* aPresContext) -{ - NS_ENSURE_TRUE(aPresContext, NS_ERROR_INVALID_ARG); - - nsIWidget* widget = aPresContext->GetRootWidget(); - if (!widget) { - return NS_ERROR_NOT_AVAILABLE; - } - return NotifyIME(aMessage, widget); -} - -void -nsTextStateManager::Init(nsIWidget* aWidget, +IMEContentObserver::Init(nsIWidget* aWidget, nsPresContext* aPresContext, nsIContent* aContent) { mWidget = aWidget; mEditableNode = nsIMEStateManager::GetRootEditableNode(aPresContext, aContent); if (!mEditableNode) { return; @@ -744,38 +89,38 @@ nsTextStateManager::Init(nsIWidget* aWid } if (!mRootContent && mEditableNode->IsNodeOfType(nsINode::eDOCUMENT)) { // The document node is editable, but there are no contents, this document // is not editable. return; } NS_ENSURE_TRUE_VOID(mRootContent); - if (nsIMEStateManager::sIsTestingIME) { + if (nsIMEStateManager::IsTestingIME()) { nsIDocument* doc = aPresContext->Document(); (new nsAsyncDOMEvent(doc, NS_LITERAL_STRING("MozIMEFocusIn"), false, false))->RunDOMEventWhenSafe(); } aWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS)); - // NOTIFY_IME_OF_FOCUS might cause recreating nsTextStateManager + // NOTIFY_IME_OF_FOCUS might cause recreating IMEContentObserver // instance via nsIMEStateManager::UpdateIMEState(). So, this // instance might already have been destroyed, check it. if (!mRootContent) { return; } mDocShell = aPresContext->GetDocShell(); ObserveEditableNode(); } void -nsTextStateManager::ObserveEditableNode() +IMEContentObserver::ObserveEditableNode() { MOZ_ASSERT(mSel); MOZ_ASSERT(mRootContent); mUpdatePreference = mWidget->GetIMEUpdatePreference(); if (mUpdatePreference.WantSelectionChange()) { // add selection change listener nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel)); @@ -793,22 +138,22 @@ nsTextStateManager::ObserveEditableNode( // Add scroll position listener and reflow observer to detect position and // size changes mDocShell->AddWeakScrollObserver(this); mDocShell->AddWeakReflowObserver(this); } } void -nsTextStateManager::Destroy(void) +IMEContentObserver::Destroy(void) { // If CreateTextStateManager failed, mRootContent will be null, // and we should not call NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR)) if (mRootContent) { - if (nsIMEStateManager::sIsTestingIME && mEditableNode) { + if (nsIMEStateManager::IsTestingIME() && mEditableNode) { nsIDocument* doc = mEditableNode->OwnerDoc(); (new nsAsyncDOMEvent(doc, NS_LITERAL_STRING("MozIMEFocusOut"), false, false))->RunDOMEventWhenSafe(); } mWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR)); } // Even if there are some pending notification, it'll never notify the widget. mWidget = nullptr; @@ -827,78 +172,73 @@ nsTextStateManager::Destroy(void) } mRootContent = nullptr; mEditableNode = nullptr; mDocShell = nullptr; mUpdatePreference.mWantUpdates = nsIMEUpdatePreference::NOTIFY_NOTHING; } bool -nsTextStateManager::IsManaging(nsPresContext* aPresContext, +IMEContentObserver::IsManaging(nsPresContext* aPresContext, nsIContent* aContent) { if (!mSel || !mRootContent || !mEditableNode) { return false; // failed to initialize. } if (!mRootContent->IsInDoc()) { return false; // the focused editor has already been reframed. } return mEditableNode == nsIMEStateManager::GetRootEditableNode(aPresContext, aContent); } bool -nsTextStateManager::IsEditorHandlingEventForComposition() const +IMEContentObserver::IsEditorHandlingEventForComposition() const { if (!mWidget) { return false; } nsRefPtr<TextComposition> composition = nsIMEStateManager::GetTextCompositionFor(mWidget); if (!composition) { return false; } return composition->IsEditorHandlingEvent(); } -NS_IMPL_ISUPPORTS5(nsTextStateManager, - nsIMutationObserver, - nsISelectionListener, - nsIReflowObserver, - nsIScrollObserver, - nsISupportsWeakReference) - // Helper class, used for selection change notification -class SelectionChangeEvent : public nsRunnable { +class SelectionChangeEvent : public nsRunnable +{ public: - SelectionChangeEvent(nsTextStateManager *aDispatcher, + SelectionChangeEvent(IMEContentObserver* aDispatcher, bool aCausedByComposition) : mDispatcher(aDispatcher) , mCausedByComposition(aCausedByComposition) { MOZ_ASSERT(mDispatcher); } - NS_IMETHOD Run() { + NS_IMETHOD Run() + { if (mDispatcher->mWidget) { IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE); notification.mSelectionChangeData.mCausedByComposition = mCausedByComposition; mDispatcher->mWidget->NotifyIME(notification); } return NS_OK; } private: - nsRefPtr<nsTextStateManager> mDispatcher; + nsRefPtr<IMEContentObserver> mDispatcher; bool mCausedByComposition; }; nsresult -nsTextStateManager::NotifySelectionChanged(nsIDOMDocument* aDoc, +IMEContentObserver::NotifySelectionChanged(nsIDOMDocument* aDoc, nsISelection* aSel, int16_t aReason) { bool causedByComposition = IsEditorHandlingEventForComposition(); if (causedByComposition && !mUpdatePreference.WantChangesCausedByComposition()) { return NS_OK; } @@ -908,99 +248,104 @@ nsTextStateManager::NotifySelectionChang NS_ENSURE_SUCCESS(rv, rv); if (count > 0 && mWidget) { nsContentUtils::AddScriptRunner( new SelectionChangeEvent(this, causedByComposition)); } return NS_OK; } +// Helper class, used for position change notification +class PositionChangeEvent MOZ_FINAL : public nsRunnable +{ +public: + PositionChangeEvent(IMEContentObserver* aDispatcher) + : mDispatcher(aDispatcher) + { + MOZ_ASSERT(mDispatcher); + } + + NS_IMETHOD Run() + { + if (mDispatcher->mWidget) { + mDispatcher->mWidget->NotifyIME( + IMENotification(NOTIFY_IME_OF_POSITION_CHANGE)); + } + return NS_OK; + } + +private: + nsRefPtr<IMEContentObserver> mDispatcher; +}; + +void +IMEContentObserver::ScrollPositionChanged() +{ + if (mWidget) { + nsContentUtils::AddScriptRunner(new PositionChangeEvent(this)); + } +} + +NS_IMETHODIMP +IMEContentObserver::Reflow(DOMHighResTimeStamp aStart, + DOMHighResTimeStamp aEnd) +{ + if (mWidget) { + nsContentUtils::AddScriptRunner(new PositionChangeEvent(this)); + } + return NS_OK; +} + +NS_IMETHODIMP +IMEContentObserver::ReflowInterruptible(DOMHighResTimeStamp aStart, + DOMHighResTimeStamp aEnd) +{ + if (mWidget) { + nsContentUtils::AddScriptRunner(new PositionChangeEvent(this)); + } + return NS_OK; +} + // Helper class, used for text change notification -class TextChangeEvent : public nsRunnable { +class TextChangeEvent : public nsRunnable +{ public: - TextChangeEvent(nsTextStateManager* aDispatcher, + TextChangeEvent(IMEContentObserver* aDispatcher, uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd, bool aCausedByComposition) : mDispatcher(aDispatcher) , mStart(aStart) , mOldEnd(aOldEnd) , mNewEnd(aNewEnd) , mCausedByComposition(aCausedByComposition) { MOZ_ASSERT(mDispatcher); } - NS_IMETHOD Run() { + NS_IMETHOD Run() + { if (mDispatcher->mWidget) { IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE); notification.mTextChangeData.mStartOffset = mStart; notification.mTextChangeData.mOldEndOffset = mOldEnd; notification.mTextChangeData.mNewEndOffset = mNewEnd; notification.mTextChangeData.mCausedByComposition = mCausedByComposition; mDispatcher->mWidget->NotifyIME(notification); } return NS_OK; } private: - nsRefPtr<nsTextStateManager> mDispatcher; + nsRefPtr<IMEContentObserver> mDispatcher; uint32_t mStart, mOldEnd, mNewEnd; bool mCausedByComposition; }; -class PositionChangeEvent MOZ_FINAL : public nsRunnable -{ -public: - PositionChangeEvent(nsTextStateManager* aDispatcher) - : mDispatcher(aDispatcher) { - MOZ_ASSERT(mDispatcher); - } - - NS_IMETHOD Run() { - if (mDispatcher->mWidget) { - mDispatcher->mWidget->NotifyIME( - IMENotification(NOTIFY_IME_OF_POSITION_CHANGE)); - } - return NS_OK; - } - -private: - nsRefPtr<nsTextStateManager> mDispatcher; -}; - void -nsTextStateManager::ScrollPositionChanged() -{ - if (mWidget) { - nsContentUtils::AddScriptRunner(new PositionChangeEvent(this)); - } -} - -NS_IMETHODIMP -nsTextStateManager::Reflow(DOMHighResTimeStamp aStart, - DOMHighResTimeStamp aEnd) -{ - if (mWidget) { - nsContentUtils::AddScriptRunner(new PositionChangeEvent(this)); - } - return NS_OK; -} - -NS_IMETHODIMP -nsTextStateManager::ReflowInterruptible(DOMHighResTimeStamp aStart, - DOMHighResTimeStamp aEnd) -{ - if (mWidget) { - nsContentUtils::AddScriptRunner(new PositionChangeEvent(this)); - } - return NS_OK; -} - -void -nsTextStateManager::CharacterDataChanged(nsIDocument* aDocument, +IMEContentObserver::CharacterDataChanged(nsIDocument* aDocument, nsIContent* aContent, CharacterDataChangeInfo* aInfo) { NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), "character data changed for non-text node"); bool causedByComposition = IsEditorHandlingEventForComposition(); if (causedByComposition && @@ -1017,17 +362,17 @@ nsTextStateManager::CharacterDataChanged uint32_t oldEnd = offset + aInfo->mChangeEnd - aInfo->mChangeStart; uint32_t newEnd = offset + aInfo->mReplaceLength; nsContentUtils::AddScriptRunner( new TextChangeEvent(this, offset, oldEnd, newEnd, causedByComposition)); } void -nsTextStateManager::NotifyContentAdded(nsINode* aContainer, +IMEContentObserver::NotifyContentAdded(nsINode* aContainer, int32_t aStartIndex, int32_t aEndIndex) { bool causedByComposition = IsEditorHandlingEventForComposition(); if (causedByComposition && !mUpdatePreference.WantChangesCausedByComposition()) { return; } @@ -1047,37 +392,37 @@ nsTextStateManager::NotifyContentAdded(n if (newOffset) { nsContentUtils::AddScriptRunner( new TextChangeEvent(this, offset, offset, offset + newOffset, causedByComposition)); } } void -nsTextStateManager::ContentAppended(nsIDocument* aDocument, +IMEContentObserver::ContentAppended(nsIDocument* aDocument, nsIContent* aContainer, nsIContent* aFirstNewContent, int32_t aNewIndexInContainer) { NotifyContentAdded(aContainer, aNewIndexInContainer, aContainer->GetChildCount()); } void -nsTextStateManager::ContentInserted(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aChild, - int32_t aIndexInContainer) +IMEContentObserver::ContentInserted(nsIDocument* aDocument, + nsIContent* aContainer, + nsIContent* aChild, + int32_t aIndexInContainer) { NotifyContentAdded(NODE_FROM(aContainer, aDocument), aIndexInContainer, aIndexInContainer + 1); } void -nsTextStateManager::ContentRemoved(nsIDocument* aDocument, +IMEContentObserver::ContentRemoved(nsIDocument* aDocument, nsIContent* aContainer, nsIContent* aChild, int32_t aIndexInContainer, nsIContent* aPreviousSibling) { bool causedByComposition = IsEditorHandlingEventForComposition(); if (causedByComposition && !mUpdatePreference.WantChangesCausedByComposition()) { @@ -1104,39 +449,40 @@ nsTextStateManager::ContentRemoved(nsIDo if (childOffset) { nsContentUtils::AddScriptRunner( new TextChangeEvent(this, offset, offset + childOffset, offset, causedByComposition)); } } static nsIContent* -GetContentBR(mozilla::dom::Element *aElement) { +GetContentBR(dom::Element* aElement) +{ if (!aElement->IsNodeOfType(nsINode::eCONTENT)) { return nullptr; } nsIContent *content = static_cast<nsIContent*>(aElement); return content->IsHTML(nsGkAtoms::br) ? content : nullptr; } void -nsTextStateManager::AttributeWillChange(nsIDocument* aDocument, - mozilla::dom::Element* aElement, - int32_t aNameSpaceID, - nsIAtom* aAttribute, - int32_t aModType) +IMEContentObserver::AttributeWillChange(nsIDocument* aDocument, + dom::Element* aElement, + int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType) { nsIContent *content = GetContentBR(aElement); mPreAttrChangeLength = content ? nsContentEventHandler::GetNativeTextLength(content) : 0; } void -nsTextStateManager::AttributeChanged(nsIDocument* aDocument, - mozilla::dom::Element* aElement, +IMEContentObserver::AttributeChanged(nsIDocument* aDocument, + dom::Element* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType) { bool causedByComposition = IsEditorHandlingEventForComposition(); if (causedByComposition && !mUpdatePreference.WantChangesCausedByComposition()) { return; @@ -1154,139 +500,9 @@ nsTextStateManager::AttributeChanged(nsI mRootContent, content, 0, &start))) { nsContentUtils::AddScriptRunner(new TextChangeEvent(this, start, start + mPreAttrChangeLength, start + postAttrChangeLength, causedByComposition)); } } } -bool -nsIMEStateManager::IsEditable(nsINode* node) -{ - if (node->IsEditable()) { - return true; - } - // |node| might be readwrite (for example, a text control) - if (node->IsElement() && node->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) { - return true; - } - return false; -} - -nsINode* -nsIMEStateManager::GetRootEditableNode(nsPresContext* aPresContext, - nsIContent* aContent) -{ - if (aContent) { - nsINode* root = nullptr; - nsINode* node = aContent; - while (node && IsEditable(node)) { - root = node; - node = node->GetParentNode(); - } - return root; - } - if (aPresContext) { - nsIDocument* document = aPresContext->Document(); - if (document && document->IsEditable()) - return document; - } - return nullptr; -} - -bool -nsIMEStateManager::IsEditableIMEState(nsIWidget* aWidget) -{ - switch (aWidget->GetInputContext().mIMEState.mEnabled) { - case widget::IMEState::ENABLED: - case widget::IMEState::PASSWORD: - return true; - case widget::IMEState::PLUGIN: - case widget::IMEState::DISABLED: - return false; - default: - MOZ_CRASH("Unknown IME enable state"); - } -} - -void -nsIMEStateManager::DestroyTextStateManager() -{ - if (!sTextStateObserver) { - return; - } - - nsRefPtr<nsTextStateManager> tsm; - tsm.swap(sTextStateObserver); - tsm->Destroy(); -} - -void -nsIMEStateManager::CreateTextStateManager() -{ - if (sTextStateObserver) { - NS_WARNING("text state observer has been there already"); - MOZ_ASSERT(sTextStateObserver->IsManaging(sPresContext, sContent)); - return; - } - - nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget(); - if (!widget) { - return; // Sometimes, there are no widgets. - } - - // If it's not text ediable, we don't need to create nsTextStateManager. - if (!IsEditableIMEState(widget)) { - return; - } - - static bool sInitializeIsTestingIME = true; - if (sInitializeIsTestingIME) { - Preferences::AddBoolVarCache(&sIsTestingIME, "test.IME", false); - sInitializeIsTestingIME = false; - } - - sTextStateObserver = new nsTextStateManager(); - NS_ADDREF(sTextStateObserver); - - // nsTextStateManager::Init() might create another nsTextStateManager - // instance. So, sTextStateObserver would be replaced with new one. - // We should hold the current instance here. - nsRefPtr<nsTextStateManager> kungFuDeathGrip(sTextStateObserver); - sTextStateObserver->Init(widget, sPresContext, sContent); -} - -nsresult -nsIMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSel, - nsIContent** aRoot) -{ - if (!sTextStateObserver || !sTextStateObserver->mEditableNode || - !sTextStateObserver->mSel) - return NS_ERROR_NOT_AVAILABLE; - - NS_ASSERTION(sTextStateObserver->mSel && sTextStateObserver->mRootContent, - "uninitialized text state observer"); - NS_ADDREF(*aSel = sTextStateObserver->mSel); - NS_ADDREF(*aRoot = sTextStateObserver->mRootContent); - return NS_OK; -} - -// static -already_AddRefed<TextComposition> -nsIMEStateManager::GetTextCompositionFor(nsIWidget* aWidget) -{ - if (!sTextCompositions) { - return nullptr; - } - nsRefPtr<TextComposition> textComposition = - sTextCompositions->GetCompositionFor(aWidget); - return textComposition.forget(); -} - -// static -already_AddRefed<TextComposition> -nsIMEStateManager::GetTextCompositionFor(WidgetGUIEvent* aEvent) -{ - MOZ_ASSERT(aEvent->AsCompositionEvent() || aEvent->AsTextEvent(), - "aEvent has to be WidgetCompositionEvent or WidgetTextEvent"); - return GetTextCompositionFor(aEvent->widget); -} +} // namespace mozilla
copy from dom/events/nsIMEStateManager.cpp copy to dom/events/IMEContentObserver.h --- a/dom/events/nsIMEStateManager.cpp +++ b/dom/events/IMEContentObserver.h @@ -1,69 +1,44 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 sw=2 et tw=80: */ /* 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 "nsIMEStateManager.h" +#ifndef mozilla_IMEContentObserver_h_ +#define mozilla_IMEContentObserver_h_ -#include "HTMLInputElement.h" +#include "mozilla/Attributes.h" #include "nsCOMPtr.h" -#include "nsIPresShell.h" -#include "nsISupports.h" -#include "nsIContent.h" -#include "nsIDocument.h" -#include "nsPresContext.h" -#include "nsIDOMMouseEvent.h" -#include "nsContentUtils.h" -#include "nsINode.h" -#include "nsIFrame.h" -#include "nsRange.h" -#include "nsIDOMRange.h" -#include "nsISelection.h" -#include "nsISelectionPrivate.h" +#include "nsIDocShell.h" // XXX Why does only this need to be included here? +#include "nsIReflowObserver.h" #include "nsISelectionListener.h" -#include "nsISelectionController.h" -#include "nsIMutationObserver.h" -#include "nsContentEventHandler.h" -#include "nsIObserverService.h" -#include "mozilla/Services.h" -#include "nsIFormControl.h" -#include "nsIForm.h" -#include "mozilla/dom/HTMLFormElement.h" -#include "mozilla/Attributes.h" -#include "mozilla/TextEvents.h" -#include "TextComposition.h" -#include "mozilla/Preferences.h" -#include "nsAsyncDOMEvent.h" -#include "nsIDocShell.h" -#include "nsIReflowObserver.h" #include "nsIScrollObserver.h" +#include "nsIWidget.h" // for nsIMEUpdatePreference +#include "nsStubMutationObserver.h" #include "nsWeakReference.h" -using namespace mozilla; -using namespace mozilla::dom; -using namespace mozilla::widget; +class nsIContent; +class nsINode; +class nsISelection; +class nsPresContext; -// nsTextStateManager notifies widget of any text and selection changes -// in the currently focused editor -// sTextStateObserver points to the currently active nsTextStateManager -// sTextStateObserver is null if there is no focused editor +namespace mozilla { -class nsTextStateManager MOZ_FINAL : public nsISelectionListener, +// IMEContentObserver notifies widget of any text and selection changes +// in the currently focused editor +class IMEContentObserver MOZ_FINAL : public nsISelectionListener, public nsStubMutationObserver, public nsIReflowObserver, public nsIScrollObserver, public nsSupportsWeakReference { public: - nsTextStateManager() - { - } + IMEContentObserver(); NS_DECL_ISUPPORTS NS_DECL_NSISELECTIONLISTENER NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE @@ -93,1200 +68,11 @@ private: void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd); void ObserveEditableNode(); nsCOMPtr<nsIDocShell> mDocShell; nsIMEUpdatePreference mUpdatePreference; uint32_t mPreAttrChangeLength; }; -/******************************************************************/ -/* nsIMEStateManager */ -/******************************************************************/ - -nsIContent* nsIMEStateManager::sContent = nullptr; -nsPresContext* nsIMEStateManager::sPresContext = nullptr; -bool nsIMEStateManager::sInstalledMenuKeyboardListener = false; -bool nsIMEStateManager::sIsTestingIME = false; - -nsTextStateManager* nsIMEStateManager::sTextStateObserver = nullptr; -TextCompositionArray* nsIMEStateManager::sTextCompositions = nullptr; - -void -nsIMEStateManager::Shutdown() -{ - MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length()); - delete sTextCompositions; - sTextCompositions = nullptr; -} - -nsresult -nsIMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext) -{ - NS_ENSURE_ARG_POINTER(aPresContext); - - // First, if there is a composition in the aPresContext, clean up it. - if (sTextCompositions) { - TextCompositionArray::index_type i = - sTextCompositions->IndexOf(aPresContext); - if (i != TextCompositionArray::NoIndex) { - // there should be only one composition per presContext object. - sTextCompositions->ElementAt(i)->Destroy(); - sTextCompositions->RemoveElementAt(i); - MOZ_ASSERT(sTextCompositions->IndexOf(aPresContext) == - TextCompositionArray::NoIndex); - } - } - - if (aPresContext != sPresContext) - return NS_OK; - - DestroyTextStateManager(); - - nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget(); - if (widget) { - IMEState newState = GetNewIMEState(sPresContext, nullptr); - InputContextAction action(InputContextAction::CAUSE_UNKNOWN, - InputContextAction::LOST_FOCUS); - SetIMEState(newState, nullptr, widget, action); - } - NS_IF_RELEASE(sContent); - sPresContext = nullptr; - return NS_OK; -} - -nsresult -nsIMEStateManager::OnRemoveContent(nsPresContext* aPresContext, - nsIContent* aContent) -{ - NS_ENSURE_ARG_POINTER(aPresContext); - - // First, if there is a composition in the aContent, clean up it. - if (sTextCompositions) { - nsRefPtr<TextComposition> compositionInContent = - sTextCompositions->GetCompositionInContent(aPresContext, aContent); - - if (compositionInContent) { - // Try resetting the native IME state. Be aware, typically, this method - // is called during the content being removed. Then, the native - // composition events which are caused by following APIs are ignored due - // to unsafe to run script (in PresShell::HandleEvent()). - nsCOMPtr<nsIWidget> widget = aPresContext->GetRootWidget(); - if (widget) { - nsresult rv = - compositionInContent->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION); - if (NS_FAILED(rv)) { - compositionInContent->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION); - } - // By calling the APIs, the composition may have been finished normally. - compositionInContent = - sTextCompositions->GetCompositionFor( - compositionInContent->GetPresContext(), - compositionInContent->GetEventTargetNode()); - } - } - - // If the compositionInContent is still available, we should finish the - // composition just on the content forcibly. - if (compositionInContent) { - compositionInContent->SynthesizeCommit(true); - } - } - - if (!sPresContext || !sContent || - !nsContentUtils::ContentIsDescendantOf(sContent, aContent)) { - return NS_OK; - } - - DestroyTextStateManager(); - - // Current IME transaction should commit - nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget(); - if (widget) { - IMEState newState = GetNewIMEState(sPresContext, nullptr); - InputContextAction action(InputContextAction::CAUSE_UNKNOWN, - InputContextAction::LOST_FOCUS); - SetIMEState(newState, nullptr, widget, action); - } - - NS_IF_RELEASE(sContent); - sPresContext = nullptr; - - return NS_OK; -} - -nsresult -nsIMEStateManager::OnChangeFocus(nsPresContext* aPresContext, - nsIContent* aContent, - InputContextAction::Cause aCause) -{ - InputContextAction action(aCause); - return OnChangeFocusInternal(aPresContext, aContent, action); -} - -nsresult -nsIMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext, - nsIContent* aContent, - InputContextAction aAction) -{ - bool focusActuallyChanging = - (sContent != aContent || sPresContext != aPresContext); - - nsCOMPtr<nsIWidget> oldWidget = - sPresContext ? sPresContext->GetRootWidget() : nullptr; - if (oldWidget && focusActuallyChanging) { - // If we're deactivating, we shouldn't commit composition forcibly because - // the user may want to continue the composition. - if (aPresContext) { - NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget); - } - } - - if (sTextStateObserver && - (aPresContext || !sTextStateObserver->KeepAliveDuringDeactive()) && - !sTextStateObserver->IsManaging(aPresContext, aContent)) { - DestroyTextStateManager(); - } - - if (!aPresContext) { - return NS_OK; - } - - nsCOMPtr<nsIWidget> widget = - (sPresContext == aPresContext) ? oldWidget.get() : - aPresContext->GetRootWidget(); - if (!widget) { - return NS_OK; - } - - IMEState newState = GetNewIMEState(aPresContext, aContent); - if (!focusActuallyChanging) { - // actual focus isn't changing, but if IME enabled state is changing, - // we should do it. - InputContext context = widget->GetInputContext(); - if (context.mIMEState.mEnabled == newState.mEnabled) { - // the enabled state isn't changing. - return NS_OK; - } - aAction.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED; - - // Even if focus isn't changing actually, we should commit current - // composition here since the IME state is changing. - if (sPresContext && oldWidget && !focusActuallyChanging) { - NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget); - } - } else if (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED) { - // If aContent isn't null or aContent is null but editable, somebody gets - // focus. - bool gotFocus = aContent || (newState.mEnabled == IMEState::ENABLED); - aAction.mFocusChange = - gotFocus ? InputContextAction::GOT_FOCUS : InputContextAction::LOST_FOCUS; - } - - // Update IME state for new focus widget - SetIMEState(newState, aContent, widget, aAction); - - sPresContext = aPresContext; - if (sContent != aContent) { - NS_IF_RELEASE(sContent); - NS_IF_ADDREF(sContent = aContent); - } - - // Don't call CreateTextStateManager() here, it should be called from - // focus event handler of editor. - - return NS_OK; -} - -void -nsIMEStateManager::OnInstalledMenuKeyboardListener(bool aInstalling) -{ - sInstalledMenuKeyboardListener = aInstalling; - - InputContextAction action(InputContextAction::CAUSE_UNKNOWN, - aInstalling ? InputContextAction::MENU_GOT_PSEUDO_FOCUS : - InputContextAction::MENU_LOST_PSEUDO_FOCUS); - OnChangeFocusInternal(sPresContext, sContent, action); -} - -void -nsIMEStateManager::OnClickInEditor(nsPresContext* aPresContext, - nsIContent* aContent, - nsIDOMMouseEvent* aMouseEvent) -{ - if (sPresContext != aPresContext || sContent != aContent) { - return; - } - - nsCOMPtr<nsIWidget> widget = aPresContext->GetRootWidget(); - NS_ENSURE_TRUE_VOID(widget); - - bool isTrusted; - nsresult rv = aMouseEvent->GetIsTrusted(&isTrusted); - NS_ENSURE_SUCCESS_VOID(rv); - if (!isTrusted) { - return; // ignore untrusted event. - } - - int16_t button; - rv = aMouseEvent->GetButton(&button); - NS_ENSURE_SUCCESS_VOID(rv); - if (button != 0) { - return; // not a left click event. - } - - int32_t clickCount; - rv = aMouseEvent->GetDetail(&clickCount); - NS_ENSURE_SUCCESS_VOID(rv); - if (clickCount != 1) { - return; // should notify only first click event. - } - - InputContextAction action(InputContextAction::CAUSE_MOUSE, - InputContextAction::FOCUS_NOT_CHANGED); - IMEState newState = GetNewIMEState(aPresContext, aContent); - SetIMEState(newState, aContent, widget, action); -} - -void -nsIMEStateManager::OnFocusInEditor(nsPresContext* aPresContext, - nsIContent* aContent) -{ - if (sPresContext != aPresContext || sContent != aContent) { - return; - } - - // If the nsTextStateManager instance isn't managing the editor actually, - // we need to recreate the instance. - if (sTextStateObserver) { - if (sTextStateObserver->IsManaging(aPresContext, aContent)) { - return; - } - DestroyTextStateManager(); - } - - CreateTextStateManager(); -} - -void -nsIMEStateManager::UpdateIMEState(const IMEState &aNewIMEState, - nsIContent* aContent) -{ - if (!sPresContext) { - NS_WARNING("ISM doesn't know which editor has focus"); - return; - } - nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget(); - if (!widget) { - NS_WARNING("focused widget is not found"); - return; - } - - // If the nsTextStateManager instance isn't managing the editor's current - // editable root content, the editor frame might be reframed. We should - // recreate the instance at that time. - bool createTextStateManager = - (!sTextStateObserver || - !sTextStateObserver->IsManaging(sPresContext, aContent)); - - bool updateIMEState = - (widget->GetInputContext().mIMEState.mEnabled != aNewIMEState.mEnabled); - - if (updateIMEState) { - // commit current composition before modifying IME state. - NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, widget); - } - - if (createTextStateManager) { - DestroyTextStateManager(); - } - - if (updateIMEState) { - InputContextAction action(InputContextAction::CAUSE_UNKNOWN, - InputContextAction::FOCUS_NOT_CHANGED); - SetIMEState(aNewIMEState, aContent, widget, action); - } - - if (createTextStateManager) { - CreateTextStateManager(); - } -} - -IMEState -nsIMEStateManager::GetNewIMEState(nsPresContext* aPresContext, - nsIContent* aContent) -{ - // On Printing or Print Preview, we don't need IME. - if (aPresContext->Type() == nsPresContext::eContext_PrintPreview || - aPresContext->Type() == nsPresContext::eContext_Print) { - return IMEState(IMEState::DISABLED); - } - - if (sInstalledMenuKeyboardListener) { - return IMEState(IMEState::DISABLED); - } - - if (!aContent) { - // Even if there are no focused content, the focused document might be - // editable, such case is design mode. - nsIDocument* doc = aPresContext->Document(); - if (doc && doc->HasFlag(NODE_IS_EDITABLE)) { - return IMEState(IMEState::ENABLED); - } - return IMEState(IMEState::DISABLED); - } - - return aContent->GetDesiredIMEState(); -} - -// Helper class, used for IME enabled state change notification -class IMEEnabledStateChangedEvent : public nsRunnable { -public: - IMEEnabledStateChangedEvent(uint32_t aState) - : mState(aState) - { - } - - NS_IMETHOD Run() { - nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); - if (observerService) { - nsAutoString state; - state.AppendInt(mState); - observerService->NotifyObservers(nullptr, "ime-enabled-state-changed", state.get()); - } - return NS_OK; - } - -private: - uint32_t mState; -}; - -void -nsIMEStateManager::SetIMEState(const IMEState &aState, - nsIContent* aContent, - nsIWidget* aWidget, - InputContextAction aAction) -{ - NS_ENSURE_TRUE_VOID(aWidget); - - InputContext oldContext = aWidget->GetInputContext(); - - InputContext context; - context.mIMEState = aState; - - if (aContent && aContent->GetNameSpaceID() == kNameSpaceID_XHTML && - (aContent->Tag() == nsGkAtoms::input || - aContent->Tag() == nsGkAtoms::textarea)) { - if (aContent->Tag() != nsGkAtoms::textarea) { - // <input type=number> has an anonymous <input type=text> descendant - // that gets focus whenever anyone tries to focus the number control. We - // need to check if aContent is one of those anonymous text controls and, - // if so, use the number control instead: - nsIContent* content = aContent; - HTMLInputElement* inputElement = - HTMLInputElement::FromContentOrNull(aContent); - if (inputElement) { - HTMLInputElement* ownerNumberControl = - inputElement->GetOwnerNumberControl(); - if (ownerNumberControl) { - content = ownerNumberControl; // an <input type=number> - } - } - content->GetAttr(kNameSpaceID_None, nsGkAtoms::type, - context.mHTMLInputType); - } else { - context.mHTMLInputType.Assign(nsGkAtoms::textarea->GetUTF16String()); - } - - if (Preferences::GetBool("dom.forms.inputmode", false)) { - aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::inputmode, - context.mHTMLInputInputmode); - } - - aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::moz_action_hint, - context.mActionHint); - - // if we don't have an action hint and return won't submit the form use "next" - if (context.mActionHint.IsEmpty() && aContent->Tag() == nsGkAtoms::input) { - bool willSubmit = false; - nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent)); - mozilla::dom::Element* formElement = control->GetFormElement(); - nsCOMPtr<nsIForm> form; - if (control) { - // is this a form and does it have a default submit element? - if ((form = do_QueryInterface(formElement)) && form->GetDefaultSubmitElement()) { - willSubmit = true; - // is this an html form and does it only have a single text input element? - } else if (formElement && formElement->Tag() == nsGkAtoms::form && formElement->IsHTML() && - !static_cast<dom::HTMLFormElement*>(formElement)->ImplicitSubmissionIsDisabled()) { - willSubmit = true; - } - } - context.mActionHint.Assign(willSubmit ? control->GetType() == NS_FORM_INPUT_SEARCH - ? NS_LITERAL_STRING("search") - : NS_LITERAL_STRING("go") - : formElement - ? NS_LITERAL_STRING("next") - : EmptyString()); - } - } - - // XXX I think that we should use nsContentUtils::IsCallerChrome() instead - // of the process type. - if (aAction.mCause == InputContextAction::CAUSE_UNKNOWN && - XRE_GetProcessType() != GeckoProcessType_Content) { - aAction.mCause = InputContextAction::CAUSE_UNKNOWN_CHROME; - } - - aWidget->SetInputContext(context, aAction); - if (oldContext.mIMEState.mEnabled != context.mIMEState.mEnabled) { - nsContentUtils::AddScriptRunner( - new IMEEnabledStateChangedEvent(context.mIMEState.mEnabled)); - } -} - -void -nsIMEStateManager::EnsureTextCompositionArray() -{ - if (sTextCompositions) { - return; - } - sTextCompositions = new TextCompositionArray(); -} - -void -nsIMEStateManager::DispatchCompositionEvent(nsINode* aEventTargetNode, - nsPresContext* aPresContext, - WidgetEvent* aEvent, - nsEventStatus* aStatus, - nsDispatchingCallback* aCallBack) -{ - MOZ_ASSERT(aEvent->eventStructType == NS_COMPOSITION_EVENT || - aEvent->eventStructType == NS_TEXT_EVENT); - if (!aEvent->mFlags.mIsTrusted || aEvent->mFlags.mPropagationStopped) { - return; - } - - EnsureTextCompositionArray(); - - WidgetGUIEvent* GUIEvent = aEvent->AsGUIEvent(); - - nsRefPtr<TextComposition> composition = - sTextCompositions->GetCompositionFor(GUIEvent->widget); - if (!composition) { - MOZ_ASSERT(GUIEvent->message == NS_COMPOSITION_START); - composition = new TextComposition(aPresContext, aEventTargetNode, GUIEvent); - sTextCompositions->AppendElement(composition); - } -#ifdef DEBUG - else { - MOZ_ASSERT(GUIEvent->message != NS_COMPOSITION_START); - } -#endif // #ifdef DEBUG - - // Dispatch the event on composing target. - composition->DispatchEvent(GUIEvent, aStatus, aCallBack); - - // WARNING: the |composition| might have been destroyed already. - - // Remove the ended composition from the array. - if (aEvent->message == NS_COMPOSITION_END) { - TextCompositionArray::index_type i = - sTextCompositions->IndexOf(GUIEvent->widget); - if (i != TextCompositionArray::NoIndex) { - sTextCompositions->ElementAt(i)->Destroy(); - sTextCompositions->RemoveElementAt(i); - } - } -} - -// static -nsresult -nsIMEStateManager::NotifyIME(IMEMessage aMessage, - nsIWidget* aWidget) -{ - NS_ENSURE_TRUE(aWidget, NS_ERROR_INVALID_ARG); - - nsRefPtr<TextComposition> composition; - if (sTextCompositions) { - composition = sTextCompositions->GetCompositionFor(aWidget); - } - if (!composition || !composition->IsSynthesizedForTests()) { - switch (aMessage) { - case NOTIFY_IME_OF_CURSOR_POS_CHANGED: - return aWidget->NotifyIME(IMENotification(aMessage)); - case REQUEST_TO_COMMIT_COMPOSITION: - case REQUEST_TO_CANCEL_COMPOSITION: - case NOTIFY_IME_OF_COMPOSITION_UPDATE: - return composition ? - aWidget->NotifyIME(IMENotification(aMessage)) : NS_OK; - default: - MOZ_CRASH("Unsupported notification"); - } - MOZ_CRASH( - "Failed to handle the notification for non-synthesized composition"); - } - - // If the composition is synthesized events for automated tests, we should - // dispatch composition events for emulating the native composition behavior. - // NOTE: The dispatched events are discarded if it's not safe to run script. - switch (aMessage) { - case REQUEST_TO_COMMIT_COMPOSITION: { - nsCOMPtr<nsIWidget> widget(aWidget); - nsEventStatus status = nsEventStatus_eIgnore; - if (!composition->LastData().IsEmpty()) { - WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget); - textEvent.theText = composition->LastData(); - textEvent.mFlags.mIsSynthesizedForTests = true; - widget->DispatchEvent(&textEvent, status); - if (widget->Destroyed()) { - return NS_OK; - } - } - - status = nsEventStatus_eIgnore; - WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget); - endEvent.data = composition->LastData(); - endEvent.mFlags.mIsSynthesizedForTests = true; - widget->DispatchEvent(&endEvent, status); - - return NS_OK; - } - case REQUEST_TO_CANCEL_COMPOSITION: { - nsCOMPtr<nsIWidget> widget(aWidget); - nsEventStatus status = nsEventStatus_eIgnore; - if (!composition->LastData().IsEmpty()) { - WidgetCompositionEvent updateEvent(true, NS_COMPOSITION_UPDATE, widget); - updateEvent.data = composition->LastData(); - updateEvent.mFlags.mIsSynthesizedForTests = true; - widget->DispatchEvent(&updateEvent, status); - if (widget->Destroyed()) { - return NS_OK; - } - - status = nsEventStatus_eIgnore; - WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget); - textEvent.theText = composition->LastData(); - textEvent.mFlags.mIsSynthesizedForTests = true; - widget->DispatchEvent(&textEvent, status); - if (widget->Destroyed()) { - return NS_OK; - } - } - - status = nsEventStatus_eIgnore; - WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget); - endEvent.data = composition->LastData(); - endEvent.mFlags.mIsSynthesizedForTests = true; - widget->DispatchEvent(&endEvent, status); - - return NS_OK; - } - default: - return NS_OK; - } -} - -// static -nsresult -nsIMEStateManager::NotifyIME(IMEMessage aMessage, - nsPresContext* aPresContext) -{ - NS_ENSURE_TRUE(aPresContext, NS_ERROR_INVALID_ARG); +} // namespace mozilla - nsIWidget* widget = aPresContext->GetRootWidget(); - if (!widget) { - return NS_ERROR_NOT_AVAILABLE; - } - return NotifyIME(aMessage, widget); -} - -void -nsTextStateManager::Init(nsIWidget* aWidget, - nsPresContext* aPresContext, - nsIContent* aContent) -{ - mWidget = aWidget; - mEditableNode = - nsIMEStateManager::GetRootEditableNode(aPresContext, aContent); - if (!mEditableNode) { - return; - } - - nsIPresShell* presShell = aPresContext->PresShell(); - - // get selection and root content - nsCOMPtr<nsISelectionController> selCon; - if (mEditableNode->IsNodeOfType(nsINode::eCONTENT)) { - nsIFrame* frame = - static_cast<nsIContent*>(mEditableNode.get())->GetPrimaryFrame(); - NS_ENSURE_TRUE_VOID(frame); - - frame->GetSelectionController(aPresContext, - getter_AddRefs(selCon)); - } else { - // mEditableNode is a document - selCon = do_QueryInterface(presShell); - } - NS_ENSURE_TRUE_VOID(selCon); - - selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, - getter_AddRefs(mSel)); - NS_ENSURE_TRUE_VOID(mSel); - - nsCOMPtr<nsIDOMRange> selDomRange; - if (NS_SUCCEEDED(mSel->GetRangeAt(0, getter_AddRefs(selDomRange)))) { - nsRange* selRange = static_cast<nsRange*>(selDomRange.get()); - NS_ENSURE_TRUE_VOID(selRange && selRange->GetStartParent()); - - mRootContent = selRange->GetStartParent()-> - GetSelectionRootContent(presShell); - } else { - mRootContent = mEditableNode->GetSelectionRootContent(presShell); - } - if (!mRootContent && mEditableNode->IsNodeOfType(nsINode::eDOCUMENT)) { - // The document node is editable, but there are no contents, this document - // is not editable. - return; - } - NS_ENSURE_TRUE_VOID(mRootContent); - - if (nsIMEStateManager::sIsTestingIME) { - nsIDocument* doc = aPresContext->Document(); - (new nsAsyncDOMEvent(doc, NS_LITERAL_STRING("MozIMEFocusIn"), - false, false))->RunDOMEventWhenSafe(); - } - - aWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS)); - - // NOTIFY_IME_OF_FOCUS might cause recreating nsTextStateManager - // instance via nsIMEStateManager::UpdateIMEState(). So, this - // instance might already have been destroyed, check it. - if (!mRootContent) { - return; - } - - mDocShell = aPresContext->GetDocShell(); - - ObserveEditableNode(); -} - -void -nsTextStateManager::ObserveEditableNode() -{ - MOZ_ASSERT(mSel); - MOZ_ASSERT(mRootContent); - - mUpdatePreference = mWidget->GetIMEUpdatePreference(); - if (mUpdatePreference.WantSelectionChange()) { - // add selection change listener - nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel)); - NS_ENSURE_TRUE_VOID(selPrivate); - nsresult rv = selPrivate->AddSelectionListener(this); - NS_ENSURE_SUCCESS_VOID(rv); - } - - if (mUpdatePreference.WantTextChange()) { - // add text change observer - mRootContent->AddMutationObserver(this); - } - - if (mUpdatePreference.WantPositionChanged() && mDocShell) { - // Add scroll position listener and reflow observer to detect position and - // size changes - mDocShell->AddWeakScrollObserver(this); - mDocShell->AddWeakReflowObserver(this); - } -} - -void -nsTextStateManager::Destroy(void) -{ - // If CreateTextStateManager failed, mRootContent will be null, - // and we should not call NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR)) - if (mRootContent) { - if (nsIMEStateManager::sIsTestingIME && mEditableNode) { - nsIDocument* doc = mEditableNode->OwnerDoc(); - (new nsAsyncDOMEvent(doc, NS_LITERAL_STRING("MozIMEFocusOut"), - false, false))->RunDOMEventWhenSafe(); - } - mWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR)); - } - // Even if there are some pending notification, it'll never notify the widget. - mWidget = nullptr; - if (mUpdatePreference.WantSelectionChange() && mSel) { - nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel)); - if (selPrivate) - selPrivate->RemoveSelectionListener(this); - } - mSel = nullptr; - if (mUpdatePreference.WantTextChange() && mRootContent) { - mRootContent->RemoveMutationObserver(this); - } - if (mUpdatePreference.WantPositionChanged() && mDocShell) { - mDocShell->RemoveWeakScrollObserver(this); - mDocShell->RemoveWeakReflowObserver(this); - } - mRootContent = nullptr; - mEditableNode = nullptr; - mDocShell = nullptr; - mUpdatePreference.mWantUpdates = nsIMEUpdatePreference::NOTIFY_NOTHING; -} - -bool -nsTextStateManager::IsManaging(nsPresContext* aPresContext, - nsIContent* aContent) -{ - if (!mSel || !mRootContent || !mEditableNode) { - return false; // failed to initialize. - } - if (!mRootContent->IsInDoc()) { - return false; // the focused editor has already been reframed. - } - return mEditableNode == nsIMEStateManager::GetRootEditableNode(aPresContext, - aContent); -} - -bool -nsTextStateManager::IsEditorHandlingEventForComposition() const -{ - if (!mWidget) { - return false; - } - nsRefPtr<TextComposition> composition = - nsIMEStateManager::GetTextCompositionFor(mWidget); - if (!composition) { - return false; - } - return composition->IsEditorHandlingEvent(); -} - -NS_IMPL_ISUPPORTS5(nsTextStateManager, - nsIMutationObserver, - nsISelectionListener, - nsIReflowObserver, - nsIScrollObserver, - nsISupportsWeakReference) - -// Helper class, used for selection change notification -class SelectionChangeEvent : public nsRunnable { -public: - SelectionChangeEvent(nsTextStateManager *aDispatcher, - bool aCausedByComposition) - : mDispatcher(aDispatcher) - , mCausedByComposition(aCausedByComposition) - { - MOZ_ASSERT(mDispatcher); - } - - NS_IMETHOD Run() { - if (mDispatcher->mWidget) { - IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE); - notification.mSelectionChangeData.mCausedByComposition = - mCausedByComposition; - mDispatcher->mWidget->NotifyIME(notification); - } - return NS_OK; - } - -private: - nsRefPtr<nsTextStateManager> mDispatcher; - bool mCausedByComposition; -}; - -nsresult -nsTextStateManager::NotifySelectionChanged(nsIDOMDocument* aDoc, - nsISelection* aSel, - int16_t aReason) -{ - bool causedByComposition = IsEditorHandlingEventForComposition(); - if (causedByComposition && - !mUpdatePreference.WantChangesCausedByComposition()) { - return NS_OK; - } - - int32_t count = 0; - nsresult rv = aSel->GetRangeCount(&count); - NS_ENSURE_SUCCESS(rv, rv); - if (count > 0 && mWidget) { - nsContentUtils::AddScriptRunner( - new SelectionChangeEvent(this, causedByComposition)); - } - return NS_OK; -} - -// Helper class, used for text change notification -class TextChangeEvent : public nsRunnable { -public: - TextChangeEvent(nsTextStateManager* aDispatcher, - uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd, - bool aCausedByComposition) - : mDispatcher(aDispatcher) - , mStart(aStart) - , mOldEnd(aOldEnd) - , mNewEnd(aNewEnd) - , mCausedByComposition(aCausedByComposition) - { - MOZ_ASSERT(mDispatcher); - } - - NS_IMETHOD Run() { - if (mDispatcher->mWidget) { - IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE); - notification.mTextChangeData.mStartOffset = mStart; - notification.mTextChangeData.mOldEndOffset = mOldEnd; - notification.mTextChangeData.mNewEndOffset = mNewEnd; - notification.mTextChangeData.mCausedByComposition = mCausedByComposition; - mDispatcher->mWidget->NotifyIME(notification); - } - return NS_OK; - } - -private: - nsRefPtr<nsTextStateManager> mDispatcher; - uint32_t mStart, mOldEnd, mNewEnd; - bool mCausedByComposition; -}; - -class PositionChangeEvent MOZ_FINAL : public nsRunnable -{ -public: - PositionChangeEvent(nsTextStateManager* aDispatcher) - : mDispatcher(aDispatcher) { - MOZ_ASSERT(mDispatcher); - } - - NS_IMETHOD Run() { - if (mDispatcher->mWidget) { - mDispatcher->mWidget->NotifyIME( - IMENotification(NOTIFY_IME_OF_POSITION_CHANGE)); - } - return NS_OK; - } - -private: - nsRefPtr<nsTextStateManager> mDispatcher; -}; - -void -nsTextStateManager::ScrollPositionChanged() -{ - if (mWidget) { - nsContentUtils::AddScriptRunner(new PositionChangeEvent(this)); - } -} - -NS_IMETHODIMP -nsTextStateManager::Reflow(DOMHighResTimeStamp aStart, - DOMHighResTimeStamp aEnd) -{ - if (mWidget) { - nsContentUtils::AddScriptRunner(new PositionChangeEvent(this)); - } - return NS_OK; -} - -NS_IMETHODIMP -nsTextStateManager::ReflowInterruptible(DOMHighResTimeStamp aStart, - DOMHighResTimeStamp aEnd) -{ - if (mWidget) { - nsContentUtils::AddScriptRunner(new PositionChangeEvent(this)); - } - return NS_OK; -} - -void -nsTextStateManager::CharacterDataChanged(nsIDocument* aDocument, - nsIContent* aContent, - CharacterDataChangeInfo* aInfo) -{ - NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), - "character data changed for non-text node"); - - bool causedByComposition = IsEditorHandlingEventForComposition(); - if (causedByComposition && - !mUpdatePreference.WantChangesCausedByComposition()) { - return; - } - - uint32_t offset = 0; - // get offsets of change and fire notification - if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange( - mRootContent, aContent, aInfo->mChangeStart, &offset))) - return; - - uint32_t oldEnd = offset + aInfo->mChangeEnd - aInfo->mChangeStart; - uint32_t newEnd = offset + aInfo->mReplaceLength; - - nsContentUtils::AddScriptRunner( - new TextChangeEvent(this, offset, oldEnd, newEnd, causedByComposition)); -} - -void -nsTextStateManager::NotifyContentAdded(nsINode* aContainer, - int32_t aStartIndex, - int32_t aEndIndex) -{ - bool causedByComposition = IsEditorHandlingEventForComposition(); - if (causedByComposition && - !mUpdatePreference.WantChangesCausedByComposition()) { - return; - } - - uint32_t offset = 0, newOffset = 0; - if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange( - mRootContent, aContainer, aStartIndex, &offset))) - return; - - // get offset at the end of the last added node - if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange( - aContainer->GetChildAt(aStartIndex), - aContainer, aEndIndex, &newOffset))) - return; - - // fire notification - if (newOffset) { - nsContentUtils::AddScriptRunner( - new TextChangeEvent(this, offset, offset, offset + newOffset, - causedByComposition)); - } -} - -void -nsTextStateManager::ContentAppended(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aFirstNewContent, - int32_t aNewIndexInContainer) -{ - NotifyContentAdded(aContainer, aNewIndexInContainer, - aContainer->GetChildCount()); -} - -void -nsTextStateManager::ContentInserted(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aChild, - int32_t aIndexInContainer) -{ - NotifyContentAdded(NODE_FROM(aContainer, aDocument), - aIndexInContainer, aIndexInContainer + 1); -} - -void -nsTextStateManager::ContentRemoved(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aChild, - int32_t aIndexInContainer, - nsIContent* aPreviousSibling) -{ - bool causedByComposition = IsEditorHandlingEventForComposition(); - if (causedByComposition && - !mUpdatePreference.WantChangesCausedByComposition()) { - return; - } - - uint32_t offset = 0, childOffset = 1; - if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange( - mRootContent, NODE_FROM(aContainer, aDocument), - aIndexInContainer, &offset))) - return; - - // get offset at the end of the deleted node - if (aChild->IsNodeOfType(nsINode::eTEXT)) - childOffset = aChild->TextLength(); - else if (0 < aChild->GetChildCount()) - childOffset = aChild->GetChildCount(); - - if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange( - aChild, aChild, childOffset, &childOffset))) - return; - - // fire notification - if (childOffset) { - nsContentUtils::AddScriptRunner( - new TextChangeEvent(this, offset, offset + childOffset, offset, - causedByComposition)); - } -} - -static nsIContent* -GetContentBR(mozilla::dom::Element *aElement) { - if (!aElement->IsNodeOfType(nsINode::eCONTENT)) { - return nullptr; - } - nsIContent *content = static_cast<nsIContent*>(aElement); - return content->IsHTML(nsGkAtoms::br) ? content : nullptr; -} - -void -nsTextStateManager::AttributeWillChange(nsIDocument* aDocument, - mozilla::dom::Element* aElement, - int32_t aNameSpaceID, - nsIAtom* aAttribute, - int32_t aModType) -{ - nsIContent *content = GetContentBR(aElement); - mPreAttrChangeLength = content ? - nsContentEventHandler::GetNativeTextLength(content) : 0; -} - -void -nsTextStateManager::AttributeChanged(nsIDocument* aDocument, - mozilla::dom::Element* aElement, - int32_t aNameSpaceID, - nsIAtom* aAttribute, - int32_t aModType) -{ - bool causedByComposition = IsEditorHandlingEventForComposition(); - if (causedByComposition && - !mUpdatePreference.WantChangesCausedByComposition()) { - return; - } - - nsIContent *content = GetContentBR(aElement); - if (!content) { - return; - } - uint32_t postAttrChangeLength = - nsContentEventHandler::GetNativeTextLength(content); - if (postAttrChangeLength != mPreAttrChangeLength) { - uint32_t start; - if (NS_SUCCEEDED(nsContentEventHandler::GetFlatTextOffsetOfRange( - mRootContent, content, 0, &start))) { - nsContentUtils::AddScriptRunner(new TextChangeEvent(this, start, - start + mPreAttrChangeLength, start + postAttrChangeLength, - causedByComposition)); - } - } -} - -bool -nsIMEStateManager::IsEditable(nsINode* node) -{ - if (node->IsEditable()) { - return true; - } - // |node| might be readwrite (for example, a text control) - if (node->IsElement() && node->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) { - return true; - } - return false; -} - -nsINode* -nsIMEStateManager::GetRootEditableNode(nsPresContext* aPresContext, - nsIContent* aContent) -{ - if (aContent) { - nsINode* root = nullptr; - nsINode* node = aContent; - while (node && IsEditable(node)) { - root = node; - node = node->GetParentNode(); - } - return root; - } - if (aPresContext) { - nsIDocument* document = aPresContext->Document(); - if (document && document->IsEditable()) - return document; - } - return nullptr; -} - -bool -nsIMEStateManager::IsEditableIMEState(nsIWidget* aWidget) -{ - switch (aWidget->GetInputContext().mIMEState.mEnabled) { - case widget::IMEState::ENABLED: - case widget::IMEState::PASSWORD: - return true; - case widget::IMEState::PLUGIN: - case widget::IMEState::DISABLED: - return false; - default: - MOZ_CRASH("Unknown IME enable state"); - } -} - -void -nsIMEStateManager::DestroyTextStateManager() -{ - if (!sTextStateObserver) { - return; - } - - nsRefPtr<nsTextStateManager> tsm; - tsm.swap(sTextStateObserver); - tsm->Destroy(); -} - -void -nsIMEStateManager::CreateTextStateManager() -{ - if (sTextStateObserver) { - NS_WARNING("text state observer has been there already"); - MOZ_ASSERT(sTextStateObserver->IsManaging(sPresContext, sContent)); - return; - } - - nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget(); - if (!widget) { - return; // Sometimes, there are no widgets. - } - - // If it's not text ediable, we don't need to create nsTextStateManager. - if (!IsEditableIMEState(widget)) { - return; - } - - static bool sInitializeIsTestingIME = true; - if (sInitializeIsTestingIME) { - Preferences::AddBoolVarCache(&sIsTestingIME, "test.IME", false); - sInitializeIsTestingIME = false; - } - - sTextStateObserver = new nsTextStateManager(); - NS_ADDREF(sTextStateObserver); - - // nsTextStateManager::Init() might create another nsTextStateManager - // instance. So, sTextStateObserver would be replaced with new one. - // We should hold the current instance here. - nsRefPtr<nsTextStateManager> kungFuDeathGrip(sTextStateObserver); - sTextStateObserver->Init(widget, sPresContext, sContent); -} - -nsresult -nsIMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSel, - nsIContent** aRoot) -{ - if (!sTextStateObserver || !sTextStateObserver->mEditableNode || - !sTextStateObserver->mSel) - return NS_ERROR_NOT_AVAILABLE; - - NS_ASSERTION(sTextStateObserver->mSel && sTextStateObserver->mRootContent, - "uninitialized text state observer"); - NS_ADDREF(*aSel = sTextStateObserver->mSel); - NS_ADDREF(*aRoot = sTextStateObserver->mRootContent); - return NS_OK; -} - -// static -already_AddRefed<TextComposition> -nsIMEStateManager::GetTextCompositionFor(nsIWidget* aWidget) -{ - if (!sTextCompositions) { - return nullptr; - } - nsRefPtr<TextComposition> textComposition = - sTextCompositions->GetCompositionFor(aWidget); - return textComposition.forget(); -} - -// static -already_AddRefed<TextComposition> -nsIMEStateManager::GetTextCompositionFor(WidgetGUIEvent* aEvent) -{ - MOZ_ASSERT(aEvent->AsCompositionEvent() || aEvent->AsTextEvent(), - "aEvent has to be WidgetCompositionEvent or WidgetTextEvent"); - return GetTextCompositionFor(aEvent->widget); -} +#endif // mozilla_IMEContentObserver_h_ \ No newline at end of file
--- a/dom/events/moz.build +++ b/dom/events/moz.build @@ -72,16 +72,17 @@ UNIFIED_SOURCES += [ 'CompositionEvent.cpp', 'DataContainerEvent.cpp', 'DataTransfer.cpp', 'DeviceMotionEvent.cpp', 'DragEvent.cpp', 'Event.cpp', 'EventTarget.cpp', 'FocusEvent.cpp', + 'IMEContentObserver.cpp', 'KeyboardEvent.cpp', 'MessageEvent.cpp', 'MouseEvent.cpp', 'MouseScrollEvent.cpp', 'MutationEvent.cpp', 'NotifyAudioAvailableEvent.cpp', 'NotifyPaintEvent.cpp', 'nsAsyncDOMEvent.cpp',
--- a/dom/events/nsIMEStateManager.cpp +++ b/dom/events/nsIMEStateManager.cpp @@ -1,118 +1,50 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 sw=2 et tw=80: */ /* 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 "nsIMEStateManager.h" +#include "IMEContentObserver.h" #include "HTMLInputElement.h" #include "nsCOMPtr.h" #include "nsIPresShell.h" #include "nsISupports.h" #include "nsIContent.h" #include "nsIDocument.h" #include "nsPresContext.h" #include "nsIDOMMouseEvent.h" #include "nsContentUtils.h" #include "nsINode.h" -#include "nsIFrame.h" -#include "nsRange.h" -#include "nsIDOMRange.h" #include "nsISelection.h" -#include "nsISelectionPrivate.h" -#include "nsISelectionListener.h" -#include "nsISelectionController.h" -#include "nsIMutationObserver.h" -#include "nsContentEventHandler.h" #include "nsIObserverService.h" #include "mozilla/Services.h" #include "nsIFormControl.h" #include "nsIForm.h" #include "mozilla/dom/HTMLFormElement.h" #include "mozilla/Attributes.h" #include "mozilla/TextEvents.h" #include "TextComposition.h" #include "mozilla/Preferences.h" -#include "nsAsyncDOMEvent.h" -#include "nsIDocShell.h" -#include "nsIReflowObserver.h" -#include "nsIScrollObserver.h" -#include "nsWeakReference.h" using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::widget; -// nsTextStateManager notifies widget of any text and selection changes -// in the currently focused editor -// sTextStateObserver points to the currently active nsTextStateManager -// sTextStateObserver is null if there is no focused editor - -class nsTextStateManager MOZ_FINAL : public nsISelectionListener, - public nsStubMutationObserver, - public nsIReflowObserver, - public nsIScrollObserver, - public nsSupportsWeakReference -{ -public: - nsTextStateManager() - { - } - - NS_DECL_ISUPPORTS - NS_DECL_NSISELECTIONLISTENER - NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED - NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED - NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED - NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED - NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE - NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED - NS_DECL_NSIREFLOWOBSERVER - - // nsIScrollObserver - virtual void ScrollPositionChanged() MOZ_OVERRIDE; - - void Init(nsIWidget* aWidget, - nsPresContext* aPresContext, - nsIContent* aContent); - void Destroy(void); - bool IsManaging(nsPresContext* aPresContext, nsIContent* aContent); - bool IsEditorHandlingEventForComposition() const; - bool KeepAliveDuringDeactive() const - { - return mUpdatePreference.WantDuringDeactive(); - } - - nsCOMPtr<nsIWidget> mWidget; - nsCOMPtr<nsISelection> mSel; - nsCOMPtr<nsIContent> mRootContent; - nsCOMPtr<nsINode> mEditableNode; - -private: - void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd); - void ObserveEditableNode(); - - nsCOMPtr<nsIDocShell> mDocShell; - nsIMEUpdatePreference mUpdatePreference; - uint32_t mPreAttrChangeLength; -}; - -/******************************************************************/ -/* nsIMEStateManager */ -/******************************************************************/ - nsIContent* nsIMEStateManager::sContent = nullptr; nsPresContext* nsIMEStateManager::sPresContext = nullptr; bool nsIMEStateManager::sInstalledMenuKeyboardListener = false; bool nsIMEStateManager::sIsTestingIME = false; -nsTextStateManager* nsIMEStateManager::sTextStateObserver = nullptr; +// sActiveIMEContentObserver points to the currently active IMEContentObserver. +// sActiveIMEContentObserver is null if there is no focused editor. +IMEContentObserver* nsIMEStateManager::sActiveIMEContentObserver = nullptr; TextCompositionArray* nsIMEStateManager::sTextCompositions = nullptr; void nsIMEStateManager::Shutdown() { MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length()); delete sTextCompositions; sTextCompositions = nullptr; @@ -235,19 +167,19 @@ nsIMEStateManager::OnChangeFocusInternal if (oldWidget && focusActuallyChanging) { // If we're deactivating, we shouldn't commit composition forcibly because // the user may want to continue the composition. if (aPresContext) { NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget); } } - if (sTextStateObserver && - (aPresContext || !sTextStateObserver->KeepAliveDuringDeactive()) && - !sTextStateObserver->IsManaging(aPresContext, aContent)) { + if (sActiveIMEContentObserver && + (aPresContext || !sActiveIMEContentObserver->KeepAliveDuringDeactive()) && + !sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) { DestroyTextStateManager(); } if (!aPresContext) { return NS_OK; } nsCOMPtr<nsIWidget> widget = @@ -285,17 +217,17 @@ nsIMEStateManager::OnChangeFocusInternal SetIMEState(newState, aContent, widget, aAction); sPresContext = aPresContext; if (sContent != aContent) { NS_IF_RELEASE(sContent); NS_IF_ADDREF(sContent = aContent); } - // Don't call CreateTextStateManager() here, it should be called from + // Don't call CreateIMEContentObserver() here, it should be called from // focus event handler of editor. return NS_OK; } void nsIMEStateManager::OnInstalledMenuKeyboardListener(bool aInstalling) { @@ -349,48 +281,48 @@ nsIMEStateManager::OnClickInEditor(nsPre void nsIMEStateManager::OnFocusInEditor(nsPresContext* aPresContext, nsIContent* aContent) { if (sPresContext != aPresContext || sContent != aContent) { return; } - // If the nsTextStateManager instance isn't managing the editor actually, + // If the IMEContentObserver instance isn't managing the editor actually, // we need to recreate the instance. - if (sTextStateObserver) { - if (sTextStateObserver->IsManaging(aPresContext, aContent)) { + if (sActiveIMEContentObserver) { + if (sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) { return; } DestroyTextStateManager(); } - CreateTextStateManager(); + CreateIMEContentObserver(); } void nsIMEStateManager::UpdateIMEState(const IMEState &aNewIMEState, nsIContent* aContent) { if (!sPresContext) { NS_WARNING("ISM doesn't know which editor has focus"); return; } nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget(); if (!widget) { NS_WARNING("focused widget is not found"); return; } - // If the nsTextStateManager instance isn't managing the editor's current + // If the IMEContentObserver instance isn't managing the editor's current // editable root content, the editor frame might be reframed. We should // recreate the instance at that time. bool createTextStateManager = - (!sTextStateObserver || - !sTextStateObserver->IsManaging(sPresContext, aContent)); + (!sActiveIMEContentObserver || + !sActiveIMEContentObserver->IsManaging(sPresContext, aContent)); bool updateIMEState = (widget->GetInputContext().mIMEState.mEnabled != aNewIMEState.mEnabled); if (updateIMEState) { // commit current composition before modifying IME state. NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, widget); } @@ -401,17 +333,17 @@ nsIMEStateManager::UpdateIMEState(const if (updateIMEState) { InputContextAction action(InputContextAction::CAUSE_UNKNOWN, InputContextAction::FOCUS_NOT_CHANGED); SetIMEState(aNewIMEState, aContent, widget, action); } if (createTextStateManager) { - CreateTextStateManager(); + CreateIMEContentObserver(); } } IMEState nsIMEStateManager::GetNewIMEState(nsPresContext* aPresContext, nsIContent* aContent) { // On Printing or Print Preview, we don't need IME. @@ -694,476 +626,16 @@ nsIMEStateManager::NotifyIME(IMEMessage nsIWidget* widget = aPresContext->GetRootWidget(); if (!widget) { return NS_ERROR_NOT_AVAILABLE; } return NotifyIME(aMessage, widget); } -void -nsTextStateManager::Init(nsIWidget* aWidget, - nsPresContext* aPresContext, - nsIContent* aContent) -{ - mWidget = aWidget; - mEditableNode = - nsIMEStateManager::GetRootEditableNode(aPresContext, aContent); - if (!mEditableNode) { - return; - } - - nsIPresShell* presShell = aPresContext->PresShell(); - - // get selection and root content - nsCOMPtr<nsISelectionController> selCon; - if (mEditableNode->IsNodeOfType(nsINode::eCONTENT)) { - nsIFrame* frame = - static_cast<nsIContent*>(mEditableNode.get())->GetPrimaryFrame(); - NS_ENSURE_TRUE_VOID(frame); - - frame->GetSelectionController(aPresContext, - getter_AddRefs(selCon)); - } else { - // mEditableNode is a document - selCon = do_QueryInterface(presShell); - } - NS_ENSURE_TRUE_VOID(selCon); - - selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, - getter_AddRefs(mSel)); - NS_ENSURE_TRUE_VOID(mSel); - - nsCOMPtr<nsIDOMRange> selDomRange; - if (NS_SUCCEEDED(mSel->GetRangeAt(0, getter_AddRefs(selDomRange)))) { - nsRange* selRange = static_cast<nsRange*>(selDomRange.get()); - NS_ENSURE_TRUE_VOID(selRange && selRange->GetStartParent()); - - mRootContent = selRange->GetStartParent()-> - GetSelectionRootContent(presShell); - } else { - mRootContent = mEditableNode->GetSelectionRootContent(presShell); - } - if (!mRootContent && mEditableNode->IsNodeOfType(nsINode::eDOCUMENT)) { - // The document node is editable, but there are no contents, this document - // is not editable. - return; - } - NS_ENSURE_TRUE_VOID(mRootContent); - - if (nsIMEStateManager::sIsTestingIME) { - nsIDocument* doc = aPresContext->Document(); - (new nsAsyncDOMEvent(doc, NS_LITERAL_STRING("MozIMEFocusIn"), - false, false))->RunDOMEventWhenSafe(); - } - - aWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS)); - - // NOTIFY_IME_OF_FOCUS might cause recreating nsTextStateManager - // instance via nsIMEStateManager::UpdateIMEState(). So, this - // instance might already have been destroyed, check it. - if (!mRootContent) { - return; - } - - mDocShell = aPresContext->GetDocShell(); - - ObserveEditableNode(); -} - -void -nsTextStateManager::ObserveEditableNode() -{ - MOZ_ASSERT(mSel); - MOZ_ASSERT(mRootContent); - - mUpdatePreference = mWidget->GetIMEUpdatePreference(); - if (mUpdatePreference.WantSelectionChange()) { - // add selection change listener - nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel)); - NS_ENSURE_TRUE_VOID(selPrivate); - nsresult rv = selPrivate->AddSelectionListener(this); - NS_ENSURE_SUCCESS_VOID(rv); - } - - if (mUpdatePreference.WantTextChange()) { - // add text change observer - mRootContent->AddMutationObserver(this); - } - - if (mUpdatePreference.WantPositionChanged() && mDocShell) { - // Add scroll position listener and reflow observer to detect position and - // size changes - mDocShell->AddWeakScrollObserver(this); - mDocShell->AddWeakReflowObserver(this); - } -} - -void -nsTextStateManager::Destroy(void) -{ - // If CreateTextStateManager failed, mRootContent will be null, - // and we should not call NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR)) - if (mRootContent) { - if (nsIMEStateManager::sIsTestingIME && mEditableNode) { - nsIDocument* doc = mEditableNode->OwnerDoc(); - (new nsAsyncDOMEvent(doc, NS_LITERAL_STRING("MozIMEFocusOut"), - false, false))->RunDOMEventWhenSafe(); - } - mWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR)); - } - // Even if there are some pending notification, it'll never notify the widget. - mWidget = nullptr; - if (mUpdatePreference.WantSelectionChange() && mSel) { - nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel)); - if (selPrivate) - selPrivate->RemoveSelectionListener(this); - } - mSel = nullptr; - if (mUpdatePreference.WantTextChange() && mRootContent) { - mRootContent->RemoveMutationObserver(this); - } - if (mUpdatePreference.WantPositionChanged() && mDocShell) { - mDocShell->RemoveWeakScrollObserver(this); - mDocShell->RemoveWeakReflowObserver(this); - } - mRootContent = nullptr; - mEditableNode = nullptr; - mDocShell = nullptr; - mUpdatePreference.mWantUpdates = nsIMEUpdatePreference::NOTIFY_NOTHING; -} - -bool -nsTextStateManager::IsManaging(nsPresContext* aPresContext, - nsIContent* aContent) -{ - if (!mSel || !mRootContent || !mEditableNode) { - return false; // failed to initialize. - } - if (!mRootContent->IsInDoc()) { - return false; // the focused editor has already been reframed. - } - return mEditableNode == nsIMEStateManager::GetRootEditableNode(aPresContext, - aContent); -} - -bool -nsTextStateManager::IsEditorHandlingEventForComposition() const -{ - if (!mWidget) { - return false; - } - nsRefPtr<TextComposition> composition = - nsIMEStateManager::GetTextCompositionFor(mWidget); - if (!composition) { - return false; - } - return composition->IsEditorHandlingEvent(); -} - -NS_IMPL_ISUPPORTS5(nsTextStateManager, - nsIMutationObserver, - nsISelectionListener, - nsIReflowObserver, - nsIScrollObserver, - nsISupportsWeakReference) - -// Helper class, used for selection change notification -class SelectionChangeEvent : public nsRunnable { -public: - SelectionChangeEvent(nsTextStateManager *aDispatcher, - bool aCausedByComposition) - : mDispatcher(aDispatcher) - , mCausedByComposition(aCausedByComposition) - { - MOZ_ASSERT(mDispatcher); - } - - NS_IMETHOD Run() { - if (mDispatcher->mWidget) { - IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE); - notification.mSelectionChangeData.mCausedByComposition = - mCausedByComposition; - mDispatcher->mWidget->NotifyIME(notification); - } - return NS_OK; - } - -private: - nsRefPtr<nsTextStateManager> mDispatcher; - bool mCausedByComposition; -}; - -nsresult -nsTextStateManager::NotifySelectionChanged(nsIDOMDocument* aDoc, - nsISelection* aSel, - int16_t aReason) -{ - bool causedByComposition = IsEditorHandlingEventForComposition(); - if (causedByComposition && - !mUpdatePreference.WantChangesCausedByComposition()) { - return NS_OK; - } - - int32_t count = 0; - nsresult rv = aSel->GetRangeCount(&count); - NS_ENSURE_SUCCESS(rv, rv); - if (count > 0 && mWidget) { - nsContentUtils::AddScriptRunner( - new SelectionChangeEvent(this, causedByComposition)); - } - return NS_OK; -} - -// Helper class, used for text change notification -class TextChangeEvent : public nsRunnable { -public: - TextChangeEvent(nsTextStateManager* aDispatcher, - uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd, - bool aCausedByComposition) - : mDispatcher(aDispatcher) - , mStart(aStart) - , mOldEnd(aOldEnd) - , mNewEnd(aNewEnd) - , mCausedByComposition(aCausedByComposition) - { - MOZ_ASSERT(mDispatcher); - } - - NS_IMETHOD Run() { - if (mDispatcher->mWidget) { - IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE); - notification.mTextChangeData.mStartOffset = mStart; - notification.mTextChangeData.mOldEndOffset = mOldEnd; - notification.mTextChangeData.mNewEndOffset = mNewEnd; - notification.mTextChangeData.mCausedByComposition = mCausedByComposition; - mDispatcher->mWidget->NotifyIME(notification); - } - return NS_OK; - } - -private: - nsRefPtr<nsTextStateManager> mDispatcher; - uint32_t mStart, mOldEnd, mNewEnd; - bool mCausedByComposition; -}; - -class PositionChangeEvent MOZ_FINAL : public nsRunnable -{ -public: - PositionChangeEvent(nsTextStateManager* aDispatcher) - : mDispatcher(aDispatcher) { - MOZ_ASSERT(mDispatcher); - } - - NS_IMETHOD Run() { - if (mDispatcher->mWidget) { - mDispatcher->mWidget->NotifyIME( - IMENotification(NOTIFY_IME_OF_POSITION_CHANGE)); - } - return NS_OK; - } - -private: - nsRefPtr<nsTextStateManager> mDispatcher; -}; - -void -nsTextStateManager::ScrollPositionChanged() -{ - if (mWidget) { - nsContentUtils::AddScriptRunner(new PositionChangeEvent(this)); - } -} - -NS_IMETHODIMP -nsTextStateManager::Reflow(DOMHighResTimeStamp aStart, - DOMHighResTimeStamp aEnd) -{ - if (mWidget) { - nsContentUtils::AddScriptRunner(new PositionChangeEvent(this)); - } - return NS_OK; -} - -NS_IMETHODIMP -nsTextStateManager::ReflowInterruptible(DOMHighResTimeStamp aStart, - DOMHighResTimeStamp aEnd) -{ - if (mWidget) { - nsContentUtils::AddScriptRunner(new PositionChangeEvent(this)); - } - return NS_OK; -} - -void -nsTextStateManager::CharacterDataChanged(nsIDocument* aDocument, - nsIContent* aContent, - CharacterDataChangeInfo* aInfo) -{ - NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), - "character data changed for non-text node"); - - bool causedByComposition = IsEditorHandlingEventForComposition(); - if (causedByComposition && - !mUpdatePreference.WantChangesCausedByComposition()) { - return; - } - - uint32_t offset = 0; - // get offsets of change and fire notification - if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange( - mRootContent, aContent, aInfo->mChangeStart, &offset))) - return; - - uint32_t oldEnd = offset + aInfo->mChangeEnd - aInfo->mChangeStart; - uint32_t newEnd = offset + aInfo->mReplaceLength; - - nsContentUtils::AddScriptRunner( - new TextChangeEvent(this, offset, oldEnd, newEnd, causedByComposition)); -} - -void -nsTextStateManager::NotifyContentAdded(nsINode* aContainer, - int32_t aStartIndex, - int32_t aEndIndex) -{ - bool causedByComposition = IsEditorHandlingEventForComposition(); - if (causedByComposition && - !mUpdatePreference.WantChangesCausedByComposition()) { - return; - } - - uint32_t offset = 0, newOffset = 0; - if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange( - mRootContent, aContainer, aStartIndex, &offset))) - return; - - // get offset at the end of the last added node - if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange( - aContainer->GetChildAt(aStartIndex), - aContainer, aEndIndex, &newOffset))) - return; - - // fire notification - if (newOffset) { - nsContentUtils::AddScriptRunner( - new TextChangeEvent(this, offset, offset, offset + newOffset, - causedByComposition)); - } -} - -void -nsTextStateManager::ContentAppended(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aFirstNewContent, - int32_t aNewIndexInContainer) -{ - NotifyContentAdded(aContainer, aNewIndexInContainer, - aContainer->GetChildCount()); -} - -void -nsTextStateManager::ContentInserted(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aChild, - int32_t aIndexInContainer) -{ - NotifyContentAdded(NODE_FROM(aContainer, aDocument), - aIndexInContainer, aIndexInContainer + 1); -} - -void -nsTextStateManager::ContentRemoved(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aChild, - int32_t aIndexInContainer, - nsIContent* aPreviousSibling) -{ - bool causedByComposition = IsEditorHandlingEventForComposition(); - if (causedByComposition && - !mUpdatePreference.WantChangesCausedByComposition()) { - return; - } - - uint32_t offset = 0, childOffset = 1; - if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange( - mRootContent, NODE_FROM(aContainer, aDocument), - aIndexInContainer, &offset))) - return; - - // get offset at the end of the deleted node - if (aChild->IsNodeOfType(nsINode::eTEXT)) - childOffset = aChild->TextLength(); - else if (0 < aChild->GetChildCount()) - childOffset = aChild->GetChildCount(); - - if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange( - aChild, aChild, childOffset, &childOffset))) - return; - - // fire notification - if (childOffset) { - nsContentUtils::AddScriptRunner( - new TextChangeEvent(this, offset, offset + childOffset, offset, - causedByComposition)); - } -} - -static nsIContent* -GetContentBR(mozilla::dom::Element *aElement) { - if (!aElement->IsNodeOfType(nsINode::eCONTENT)) { - return nullptr; - } - nsIContent *content = static_cast<nsIContent*>(aElement); - return content->IsHTML(nsGkAtoms::br) ? content : nullptr; -} - -void -nsTextStateManager::AttributeWillChange(nsIDocument* aDocument, - mozilla::dom::Element* aElement, - int32_t aNameSpaceID, - nsIAtom* aAttribute, - int32_t aModType) -{ - nsIContent *content = GetContentBR(aElement); - mPreAttrChangeLength = content ? - nsContentEventHandler::GetNativeTextLength(content) : 0; -} - -void -nsTextStateManager::AttributeChanged(nsIDocument* aDocument, - mozilla::dom::Element* aElement, - int32_t aNameSpaceID, - nsIAtom* aAttribute, - int32_t aModType) -{ - bool causedByComposition = IsEditorHandlingEventForComposition(); - if (causedByComposition && - !mUpdatePreference.WantChangesCausedByComposition()) { - return; - } - - nsIContent *content = GetContentBR(aElement); - if (!content) { - return; - } - uint32_t postAttrChangeLength = - nsContentEventHandler::GetNativeTextLength(content); - if (postAttrChangeLength != mPreAttrChangeLength) { - uint32_t start; - if (NS_SUCCEEDED(nsContentEventHandler::GetFlatTextOffsetOfRange( - mRootContent, content, 0, &start))) { - nsContentUtils::AddScriptRunner(new TextChangeEvent(this, start, - start + mPreAttrChangeLength, start + postAttrChangeLength, - causedByComposition)); - } - } -} - bool nsIMEStateManager::IsEditable(nsINode* node) { if (node->IsEditable()) { return true; } // |node| might be readwrite (for example, a text control) if (node->IsElement() && node->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) { @@ -1206,72 +678,74 @@ nsIMEStateManager::IsEditableIMEState(ns default: MOZ_CRASH("Unknown IME enable state"); } } void nsIMEStateManager::DestroyTextStateManager() { - if (!sTextStateObserver) { + if (!sActiveIMEContentObserver) { return; } - nsRefPtr<nsTextStateManager> tsm; - tsm.swap(sTextStateObserver); + nsRefPtr<IMEContentObserver> tsm; + tsm.swap(sActiveIMEContentObserver); tsm->Destroy(); } void -nsIMEStateManager::CreateTextStateManager() +nsIMEStateManager::CreateIMEContentObserver() { - if (sTextStateObserver) { + if (sActiveIMEContentObserver) { NS_WARNING("text state observer has been there already"); - MOZ_ASSERT(sTextStateObserver->IsManaging(sPresContext, sContent)); + MOZ_ASSERT(sActiveIMEContentObserver->IsManaging(sPresContext, sContent)); return; } nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget(); if (!widget) { return; // Sometimes, there are no widgets. } - // If it's not text ediable, we don't need to create nsTextStateManager. + // If it's not text ediable, we don't need to create IMEContentObserver. if (!IsEditableIMEState(widget)) { return; } static bool sInitializeIsTestingIME = true; if (sInitializeIsTestingIME) { Preferences::AddBoolVarCache(&sIsTestingIME, "test.IME", false); sInitializeIsTestingIME = false; } - sTextStateObserver = new nsTextStateManager(); - NS_ADDREF(sTextStateObserver); + sActiveIMEContentObserver = new IMEContentObserver(); + NS_ADDREF(sActiveIMEContentObserver); - // nsTextStateManager::Init() might create another nsTextStateManager - // instance. So, sTextStateObserver would be replaced with new one. + // IMEContentObserver::Init() might create another IMEContentObserver + // instance. So, sActiveIMEContentObserver would be replaced with new one. // We should hold the current instance here. - nsRefPtr<nsTextStateManager> kungFuDeathGrip(sTextStateObserver); - sTextStateObserver->Init(widget, sPresContext, sContent); + nsRefPtr<IMEContentObserver> kungFuDeathGrip(sActiveIMEContentObserver); + sActiveIMEContentObserver->Init(widget, sPresContext, sContent); } nsresult -nsIMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSel, - nsIContent** aRoot) +nsIMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSelection, + nsIContent** aRootContent) { - if (!sTextStateObserver || !sTextStateObserver->mEditableNode || - !sTextStateObserver->mSel) + if (!sActiveIMEContentObserver || !sActiveIMEContentObserver->mEditableNode || + !sActiveIMEContentObserver->mSel) { return NS_ERROR_NOT_AVAILABLE; + } - NS_ASSERTION(sTextStateObserver->mSel && sTextStateObserver->mRootContent, - "uninitialized text state observer"); - NS_ADDREF(*aSel = sTextStateObserver->mSel); - NS_ADDREF(*aRoot = sTextStateObserver->mRootContent); + NS_ASSERTION(sActiveIMEContentObserver->mSel && + sActiveIMEContentObserver->mRootContent, + "uninitialized content observer"); + NS_ADDREF(*aSelection = sActiveIMEContentObserver->mSel); + NS_ADDREF(*aRootContent = sActiveIMEContentObserver->mRootContent); return NS_OK; } // static already_AddRefed<TextComposition> nsIMEStateManager::GetTextCompositionFor(nsIWidget* aWidget) { if (!sTextCompositions) {
--- a/dom/events/nsIMEStateManager.h +++ b/dom/events/nsIMEStateManager.h @@ -10,20 +10,20 @@ #include "nsIWidget.h" class nsDispatchingCallback; class nsIContent; class nsIDOMMouseEvent; class nsINode; class nsPIDOMWindow; class nsPresContext; -class nsTextStateManager; class nsISelection; namespace mozilla { +class IMEContentObserver; class TextCompositionArray; class TextComposition; } // namespace mozilla /* * IME state manager */ @@ -113,43 +113,45 @@ public: /** * Send a notification to IME. It depends on the IME or platform spec what * will occur (or not occur). */ static nsresult NotifyIME(IMEMessage aMessage, nsIWidget* aWidget); static nsresult NotifyIME(IMEMessage aMessage, nsPresContext* aPresContext); + static nsINode* GetRootEditableNode(nsPresContext* aPresContext, + nsIContent* aContent); + static bool IsTestingIME() { return sIsTestingIME; } + protected: static nsresult OnChangeFocusInternal(nsPresContext* aPresContext, nsIContent* aContent, InputContextAction aAction); static void SetIMEState(const IMEState &aState, nsIContent* aContent, nsIWidget* aWidget, InputContextAction aAction); static IMEState GetNewIMEState(nsPresContext* aPresContext, nsIContent* aContent); static void EnsureTextCompositionArray(); - static void CreateTextStateManager(); + static void CreateIMEContentObserver(); static void DestroyTextStateManager(); static bool IsEditable(nsINode* node); - static nsINode* GetRootEditableNode(nsPresContext* aPresContext, - nsIContent* aContent); static bool IsEditableIMEState(nsIWidget* aWidget); static nsIContent* sContent; static nsPresContext* sPresContext; static bool sInstalledMenuKeyboardListener; static bool sIsTestingIME; - static nsTextStateManager* sTextStateObserver; + static mozilla::IMEContentObserver* sActiveIMEContentObserver; // All active compositions in the process are stored by this array. // When you get an item of this array and use it, please be careful. // The instances in this array can be destroyed automatically if you do // something to cause committing or canceling the composition. static mozilla::TextCompositionArray* sTextCompositions; };