dom/events/IMEContentObserver.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/events/IMEContentObserver.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,566 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 sw=2 et tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "ContentEventHandler.h"
    1.11 +#include "IMEContentObserver.h"
    1.12 +#include "mozilla/AsyncEventDispatcher.h"
    1.13 +#include "mozilla/EventStateManager.h"
    1.14 +#include "mozilla/IMEStateManager.h"
    1.15 +#include "mozilla/TextComposition.h"
    1.16 +#include "mozilla/dom/Element.h"
    1.17 +#include "nsAutoPtr.h"
    1.18 +#include "nsContentUtils.h"
    1.19 +#include "nsGkAtoms.h"
    1.20 +#include "nsIAtom.h"
    1.21 +#include "nsIContent.h"
    1.22 +#include "nsIDocument.h"
    1.23 +#include "nsIDOMDocument.h"
    1.24 +#include "nsIDOMRange.h"
    1.25 +#include "nsIFrame.h"
    1.26 +#include "nsINode.h"
    1.27 +#include "nsIPresShell.h"
    1.28 +#include "nsISelectionController.h"
    1.29 +#include "nsISelectionPrivate.h"
    1.30 +#include "nsISupports.h"
    1.31 +#include "nsIWidget.h"
    1.32 +#include "nsPresContext.h"
    1.33 +#include "nsThreadUtils.h"
    1.34 +#include "nsWeakReference.h"
    1.35 +
    1.36 +namespace mozilla {
    1.37 +
    1.38 +using namespace widget;
    1.39 +
    1.40 +NS_IMPL_CYCLE_COLLECTION(IMEContentObserver,
    1.41 +                         mWidget, mSelection,
    1.42 +                         mRootContent, mEditableNode, mDocShell)
    1.43 +
    1.44 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver)
    1.45 + NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
    1.46 + NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
    1.47 + NS_INTERFACE_MAP_ENTRY(nsIReflowObserver)
    1.48 + NS_INTERFACE_MAP_ENTRY(nsIScrollObserver)
    1.49 + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    1.50 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelectionListener)
    1.51 +NS_INTERFACE_MAP_END
    1.52 +
    1.53 +NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
    1.54 +NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
    1.55 +
    1.56 +IMEContentObserver::IMEContentObserver()
    1.57 +  : mESM(nullptr)
    1.58 +{
    1.59 +}
    1.60 +
    1.61 +void
    1.62 +IMEContentObserver::Init(nsIWidget* aWidget,
    1.63 +                         nsPresContext* aPresContext,
    1.64 +                         nsIContent* aContent)
    1.65 +{
    1.66 +  mESM = aPresContext->EventStateManager();
    1.67 +  mESM->OnStartToObserveContent(this);
    1.68 +
    1.69 +  mWidget = aWidget;
    1.70 +  mEditableNode = IMEStateManager::GetRootEditableNode(aPresContext, aContent);
    1.71 +  if (!mEditableNode) {
    1.72 +    return;
    1.73 +  }
    1.74 +
    1.75 +  nsIPresShell* presShell = aPresContext->PresShell();
    1.76 +
    1.77 +  // get selection and root content
    1.78 +  nsCOMPtr<nsISelectionController> selCon;
    1.79 +  if (mEditableNode->IsNodeOfType(nsINode::eCONTENT)) {
    1.80 +    nsIFrame* frame =
    1.81 +      static_cast<nsIContent*>(mEditableNode.get())->GetPrimaryFrame();
    1.82 +    NS_ENSURE_TRUE_VOID(frame);
    1.83 +
    1.84 +    frame->GetSelectionController(aPresContext,
    1.85 +                                  getter_AddRefs(selCon));
    1.86 +  } else {
    1.87 +    // mEditableNode is a document
    1.88 +    selCon = do_QueryInterface(presShell);
    1.89 +  }
    1.90 +  NS_ENSURE_TRUE_VOID(selCon);
    1.91 +
    1.92 +  selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
    1.93 +                       getter_AddRefs(mSelection));
    1.94 +  NS_ENSURE_TRUE_VOID(mSelection);
    1.95 +
    1.96 +  nsCOMPtr<nsIDOMRange> selDomRange;
    1.97 +  if (NS_SUCCEEDED(mSelection->GetRangeAt(0, getter_AddRefs(selDomRange)))) {
    1.98 +    nsRange* selRange = static_cast<nsRange*>(selDomRange.get());
    1.99 +    NS_ENSURE_TRUE_VOID(selRange && selRange->GetStartParent());
   1.100 +
   1.101 +    mRootContent = selRange->GetStartParent()->
   1.102 +                     GetSelectionRootContent(presShell);
   1.103 +  } else {
   1.104 +    mRootContent = mEditableNode->GetSelectionRootContent(presShell);
   1.105 +  }
   1.106 +  if (!mRootContent && mEditableNode->IsNodeOfType(nsINode::eDOCUMENT)) {
   1.107 +    // The document node is editable, but there are no contents, this document
   1.108 +    // is not editable.
   1.109 +    return;
   1.110 +  }
   1.111 +  NS_ENSURE_TRUE_VOID(mRootContent);
   1.112 +
   1.113 +  if (IMEStateManager::IsTestingIME()) {
   1.114 +    nsIDocument* doc = aPresContext->Document();
   1.115 +    (new AsyncEventDispatcher(doc, NS_LITERAL_STRING("MozIMEFocusIn"),
   1.116 +                              false, false))->RunDOMEventWhenSafe();
   1.117 +  }
   1.118 +
   1.119 +  aWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS));
   1.120 +
   1.121 +  // NOTIFY_IME_OF_FOCUS might cause recreating IMEContentObserver
   1.122 +  // instance via IMEStateManager::UpdateIMEState().  So, this
   1.123 +  // instance might already have been destroyed, check it.
   1.124 +  if (!mRootContent) {
   1.125 +    return;
   1.126 +  }
   1.127 +
   1.128 +  mDocShell = aPresContext->GetDocShell();
   1.129 +
   1.130 +  ObserveEditableNode();
   1.131 +}
   1.132 +
   1.133 +void
   1.134 +IMEContentObserver::ObserveEditableNode()
   1.135 +{
   1.136 +  MOZ_ASSERT(mSelection);
   1.137 +  MOZ_ASSERT(mRootContent);
   1.138 +
   1.139 +  mUpdatePreference = mWidget->GetIMEUpdatePreference();
   1.140 +  if (mUpdatePreference.WantSelectionChange()) {
   1.141 +    // add selection change listener
   1.142 +    nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
   1.143 +    NS_ENSURE_TRUE_VOID(selPrivate);
   1.144 +    nsresult rv = selPrivate->AddSelectionListener(this);
   1.145 +    NS_ENSURE_SUCCESS_VOID(rv);
   1.146 +  }
   1.147 +
   1.148 +  if (mUpdatePreference.WantTextChange()) {
   1.149 +    // add text change observer
   1.150 +    mRootContent->AddMutationObserver(this);
   1.151 +  }
   1.152 +
   1.153 +  if (mUpdatePreference.WantPositionChanged() && mDocShell) {
   1.154 +    // Add scroll position listener and reflow observer to detect position and
   1.155 +    // size changes
   1.156 +    mDocShell->AddWeakScrollObserver(this);
   1.157 +    mDocShell->AddWeakReflowObserver(this);
   1.158 +  }
   1.159 +}
   1.160 +
   1.161 +void
   1.162 +IMEContentObserver::Destroy()
   1.163 +{
   1.164 +  // If CreateTextStateManager failed, mRootContent will be null,
   1.165 +  // and we should not call NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR))
   1.166 +  if (mRootContent) {
   1.167 +    if (IMEStateManager::IsTestingIME() && mEditableNode) {
   1.168 +      nsIDocument* doc = mEditableNode->OwnerDoc();
   1.169 +      (new AsyncEventDispatcher(doc, NS_LITERAL_STRING("MozIMEFocusOut"),
   1.170 +                                false, false))->RunDOMEventWhenSafe();
   1.171 +    }
   1.172 +    mWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
   1.173 +  }
   1.174 +  // Even if there are some pending notification, it'll never notify the widget.
   1.175 +  mWidget = nullptr;
   1.176 +  if (mUpdatePreference.WantSelectionChange() && mSelection) {
   1.177 +    nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
   1.178 +    if (selPrivate) {
   1.179 +      selPrivate->RemoveSelectionListener(this);
   1.180 +    }
   1.181 +  }
   1.182 +  mSelection = nullptr;
   1.183 +  if (mUpdatePreference.WantTextChange() && mRootContent) {
   1.184 +    mRootContent->RemoveMutationObserver(this);
   1.185 +  }
   1.186 +  if (mUpdatePreference.WantPositionChanged() && mDocShell) {
   1.187 +    mDocShell->RemoveWeakScrollObserver(this);
   1.188 +    mDocShell->RemoveWeakReflowObserver(this);
   1.189 +  }
   1.190 +  mRootContent = nullptr;
   1.191 +  mEditableNode = nullptr;
   1.192 +  mDocShell = nullptr;
   1.193 +  mUpdatePreference.mWantUpdates = nsIMEUpdatePreference::NOTIFY_NOTHING;
   1.194 +
   1.195 +  if (mESM) {
   1.196 +    mESM->OnStopObservingContent(this);
   1.197 +    mESM = nullptr;
   1.198 +  }
   1.199 +}
   1.200 +
   1.201 +void
   1.202 +IMEContentObserver::DisconnectFromEventStateManager()
   1.203 +{
   1.204 +  mESM = nullptr;
   1.205 +}
   1.206 +
   1.207 +bool
   1.208 +IMEContentObserver::IsManaging(nsPresContext* aPresContext,
   1.209 +                               nsIContent* aContent)
   1.210 +{
   1.211 +  if (!mSelection || !mRootContent || !mEditableNode) {
   1.212 +    return false; // failed to initialize.
   1.213 +  }
   1.214 +  if (!mRootContent->IsInDoc()) {
   1.215 +    return false; // the focused editor has already been reframed.
   1.216 +  }
   1.217 +  return mEditableNode == IMEStateManager::GetRootEditableNode(aPresContext,
   1.218 +                                                               aContent);
   1.219 +}
   1.220 +
   1.221 +bool
   1.222 +IMEContentObserver::IsEditorHandlingEventForComposition() const
   1.223 +{
   1.224 +  if (!mWidget) {
   1.225 +    return false;
   1.226 +  }
   1.227 +  nsRefPtr<TextComposition> composition =
   1.228 +    IMEStateManager::GetTextCompositionFor(mWidget);
   1.229 +  if (!composition) {
   1.230 +    return false;
   1.231 +  }
   1.232 +  return composition->IsEditorHandlingEvent();
   1.233 +}
   1.234 +
   1.235 +nsresult
   1.236 +IMEContentObserver::GetSelectionAndRoot(nsISelection** aSelection,
   1.237 +                                        nsIContent** aRootContent) const
   1.238 +{
   1.239 +  if (!mEditableNode || !mSelection) {
   1.240 +    return NS_ERROR_NOT_AVAILABLE;
   1.241 +  }
   1.242 +
   1.243 +  NS_ASSERTION(mSelection && mRootContent, "uninitialized content observer");
   1.244 +  NS_ADDREF(*aSelection = mSelection);
   1.245 +  NS_ADDREF(*aRootContent = mRootContent);
   1.246 +  return NS_OK;
   1.247 +}
   1.248 +
   1.249 +// Helper class, used for selection change notification
   1.250 +class SelectionChangeEvent : public nsRunnable
   1.251 +{
   1.252 +public:
   1.253 +  SelectionChangeEvent(IMEContentObserver* aDispatcher,
   1.254 +                       bool aCausedByComposition)
   1.255 +    : mDispatcher(aDispatcher)
   1.256 +    , mCausedByComposition(aCausedByComposition)
   1.257 +  {
   1.258 +    MOZ_ASSERT(mDispatcher);
   1.259 +  }
   1.260 +
   1.261 +  NS_IMETHOD Run()
   1.262 +  {
   1.263 +    if (mDispatcher->GetWidget()) {
   1.264 +      IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
   1.265 +      notification.mSelectionChangeData.mCausedByComposition =
   1.266 +         mCausedByComposition;
   1.267 +      mDispatcher->GetWidget()->NotifyIME(notification);
   1.268 +    }
   1.269 +    return NS_OK;
   1.270 +  }
   1.271 +
   1.272 +private:
   1.273 +  nsRefPtr<IMEContentObserver> mDispatcher;
   1.274 +  bool mCausedByComposition;
   1.275 +};
   1.276 +
   1.277 +nsresult
   1.278 +IMEContentObserver::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
   1.279 +                                           nsISelection* aSelection,
   1.280 +                                           int16_t aReason)
   1.281 +{
   1.282 +  bool causedByComposition = IsEditorHandlingEventForComposition();
   1.283 +  if (causedByComposition &&
   1.284 +      !mUpdatePreference.WantChangesCausedByComposition()) {
   1.285 +    return NS_OK;
   1.286 +  }
   1.287 +
   1.288 +  int32_t count = 0;
   1.289 +  nsresult rv = aSelection->GetRangeCount(&count);
   1.290 +  NS_ENSURE_SUCCESS(rv, rv);
   1.291 +  if (count > 0 && mWidget) {
   1.292 +    nsContentUtils::AddScriptRunner(
   1.293 +      new SelectionChangeEvent(this, causedByComposition));
   1.294 +  }
   1.295 +  return NS_OK;
   1.296 +}
   1.297 +
   1.298 +// Helper class, used for position change notification
   1.299 +class PositionChangeEvent MOZ_FINAL : public nsRunnable
   1.300 +{
   1.301 +public:
   1.302 +  PositionChangeEvent(IMEContentObserver* aDispatcher)
   1.303 +    : mDispatcher(aDispatcher)
   1.304 +  {
   1.305 +    MOZ_ASSERT(mDispatcher);
   1.306 +  }
   1.307 +
   1.308 +  NS_IMETHOD Run()
   1.309 +  {
   1.310 +    if (mDispatcher->GetWidget()) {
   1.311 +      mDispatcher->GetWidget()->NotifyIME(
   1.312 +        IMENotification(NOTIFY_IME_OF_POSITION_CHANGE));
   1.313 +    }
   1.314 +    return NS_OK;
   1.315 +  }
   1.316 +
   1.317 +private:
   1.318 +  nsRefPtr<IMEContentObserver> mDispatcher;
   1.319 +};
   1.320 +
   1.321 +void
   1.322 +IMEContentObserver::ScrollPositionChanged()
   1.323 +{
   1.324 +  if (mWidget) {
   1.325 +    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
   1.326 +  }
   1.327 +}
   1.328 +
   1.329 +NS_IMETHODIMP
   1.330 +IMEContentObserver::Reflow(DOMHighResTimeStamp aStart,
   1.331 +                           DOMHighResTimeStamp aEnd)
   1.332 +{
   1.333 +  if (mWidget) {
   1.334 +    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
   1.335 +  }
   1.336 +  return NS_OK;
   1.337 +}
   1.338 +
   1.339 +NS_IMETHODIMP
   1.340 +IMEContentObserver::ReflowInterruptible(DOMHighResTimeStamp aStart,
   1.341 +                                        DOMHighResTimeStamp aEnd)
   1.342 +{
   1.343 +  if (mWidget) {
   1.344 +    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
   1.345 +  }
   1.346 +  return NS_OK;
   1.347 +}
   1.348 +
   1.349 +// Helper class, used for text change notification
   1.350 +class TextChangeEvent : public nsRunnable
   1.351 +{
   1.352 +public:
   1.353 +  TextChangeEvent(IMEContentObserver* aDispatcher,
   1.354 +                  uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd,
   1.355 +                  bool aCausedByComposition)
   1.356 +    : mDispatcher(aDispatcher)
   1.357 +    , mStart(aStart)
   1.358 +    , mOldEnd(aOldEnd)
   1.359 +    , mNewEnd(aNewEnd)
   1.360 +    , mCausedByComposition(aCausedByComposition)
   1.361 +  {
   1.362 +    MOZ_ASSERT(mDispatcher);
   1.363 +  }
   1.364 +
   1.365 +  NS_IMETHOD Run()
   1.366 +  {
   1.367 +    if (mDispatcher->GetWidget()) {
   1.368 +      IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
   1.369 +      notification.mTextChangeData.mStartOffset = mStart;
   1.370 +      notification.mTextChangeData.mOldEndOffset = mOldEnd;
   1.371 +      notification.mTextChangeData.mNewEndOffset = mNewEnd;
   1.372 +      notification.mTextChangeData.mCausedByComposition = mCausedByComposition;
   1.373 +      mDispatcher->GetWidget()->NotifyIME(notification);
   1.374 +    }
   1.375 +    return NS_OK;
   1.376 +  }
   1.377 +
   1.378 +private:
   1.379 +  nsRefPtr<IMEContentObserver> mDispatcher;
   1.380 +  uint32_t mStart, mOldEnd, mNewEnd;
   1.381 +  bool mCausedByComposition;
   1.382 +};
   1.383 +
   1.384 +void
   1.385 +IMEContentObserver::CharacterDataChanged(nsIDocument* aDocument,
   1.386 +                                         nsIContent* aContent,
   1.387 +                                         CharacterDataChangeInfo* aInfo)
   1.388 +{
   1.389 +  NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
   1.390 +               "character data changed for non-text node");
   1.391 +
   1.392 +  bool causedByComposition = IsEditorHandlingEventForComposition();
   1.393 +  if (causedByComposition &&
   1.394 +      !mUpdatePreference.WantChangesCausedByComposition()) {
   1.395 +    return;
   1.396 +  }
   1.397 +
   1.398 +  uint32_t offset = 0;
   1.399 +  // get offsets of change and fire notification
   1.400 +  nsresult rv =
   1.401 +    ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, aContent,
   1.402 +                                                  aInfo->mChangeStart,
   1.403 +                                                  &offset,
   1.404 +                                                  LINE_BREAK_TYPE_NATIVE);
   1.405 +  NS_ENSURE_SUCCESS_VOID(rv);
   1.406 +
   1.407 +  uint32_t oldEnd = offset + aInfo->mChangeEnd - aInfo->mChangeStart;
   1.408 +  uint32_t newEnd = offset + aInfo->mReplaceLength;
   1.409 +
   1.410 +  nsContentUtils::AddScriptRunner(
   1.411 +    new TextChangeEvent(this, offset, oldEnd, newEnd, causedByComposition));
   1.412 +}
   1.413 +
   1.414 +void
   1.415 +IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
   1.416 +                                       int32_t aStartIndex,
   1.417 +                                       int32_t aEndIndex)
   1.418 +{
   1.419 +  bool causedByComposition = IsEditorHandlingEventForComposition();
   1.420 +  if (causedByComposition &&
   1.421 +      !mUpdatePreference.WantChangesCausedByComposition()) {
   1.422 +    return;
   1.423 +  }
   1.424 +
   1.425 +  uint32_t offset = 0;
   1.426 +  nsresult rv =
   1.427 +    ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, aContainer,
   1.428 +                                                  aStartIndex, &offset,
   1.429 +                                                  LINE_BREAK_TYPE_NATIVE);
   1.430 +  NS_ENSURE_SUCCESS_VOID(rv);
   1.431 +
   1.432 +  // get offset at the end of the last added node
   1.433 +  nsIContent* childAtStart = aContainer->GetChildAt(aStartIndex);
   1.434 +  uint32_t addingLength = 0;
   1.435 +  rv = ContentEventHandler::GetFlatTextOffsetOfRange(childAtStart, aContainer,
   1.436 +                                                     aEndIndex, &addingLength,
   1.437 +                                                     LINE_BREAK_TYPE_NATIVE);
   1.438 +  NS_ENSURE_SUCCESS_VOID(rv);
   1.439 +
   1.440 +  if (!addingLength) {
   1.441 +    return;
   1.442 +  }
   1.443 +
   1.444 +  nsContentUtils::AddScriptRunner(
   1.445 +    new TextChangeEvent(this, offset, offset, offset + addingLength,
   1.446 +                        causedByComposition));
   1.447 +}
   1.448 +
   1.449 +void
   1.450 +IMEContentObserver::ContentAppended(nsIDocument* aDocument,
   1.451 +                                    nsIContent* aContainer,
   1.452 +                                    nsIContent* aFirstNewContent,
   1.453 +                                    int32_t aNewIndexInContainer)
   1.454 +{
   1.455 +  NotifyContentAdded(aContainer, aNewIndexInContainer,
   1.456 +                     aContainer->GetChildCount());
   1.457 +}
   1.458 +
   1.459 +void
   1.460 +IMEContentObserver::ContentInserted(nsIDocument* aDocument,
   1.461 +                                    nsIContent* aContainer,
   1.462 +                                    nsIContent* aChild,
   1.463 +                                    int32_t aIndexInContainer)
   1.464 +{
   1.465 +  NotifyContentAdded(NODE_FROM(aContainer, aDocument),
   1.466 +                     aIndexInContainer, aIndexInContainer + 1);
   1.467 +}
   1.468 +
   1.469 +void
   1.470 +IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
   1.471 +                                   nsIContent* aContainer,
   1.472 +                                   nsIContent* aChild,
   1.473 +                                   int32_t aIndexInContainer,
   1.474 +                                   nsIContent* aPreviousSibling)
   1.475 +{
   1.476 +  bool causedByComposition = IsEditorHandlingEventForComposition();
   1.477 +  if (causedByComposition &&
   1.478 +      !mUpdatePreference.WantChangesCausedByComposition()) {
   1.479 +    return;
   1.480 +  }
   1.481 +
   1.482 +  uint32_t offset = 0;
   1.483 +  nsresult rv =
   1.484 +    ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent,
   1.485 +                                                  NODE_FROM(aContainer,
   1.486 +                                                            aDocument),
   1.487 +                                                  aIndexInContainer, &offset,
   1.488 +                                                  LINE_BREAK_TYPE_NATIVE);
   1.489 +  NS_ENSURE_SUCCESS_VOID(rv);
   1.490 +
   1.491 +  // get offset at the end of the deleted node
   1.492 +  int32_t nodeLength =
   1.493 +    aChild->IsNodeOfType(nsINode::eTEXT) ?
   1.494 +      static_cast<int32_t>(aChild->TextLength()) :
   1.495 +      std::max(static_cast<int32_t>(aChild->GetChildCount()), 1);
   1.496 +  MOZ_ASSERT(nodeLength >= 0, "The node length is out of range");
   1.497 +  uint32_t textLength = 0;
   1.498 +  rv = ContentEventHandler::GetFlatTextOffsetOfRange(aChild, aChild,
   1.499 +                                                     nodeLength, &textLength,
   1.500 +                                                     LINE_BREAK_TYPE_NATIVE);
   1.501 +  NS_ENSURE_SUCCESS_VOID(rv);
   1.502 +
   1.503 +  if (!textLength) {
   1.504 +    return;
   1.505 +  }
   1.506 +
   1.507 +  nsContentUtils::AddScriptRunner(
   1.508 +    new TextChangeEvent(this, offset, offset + textLength, offset,
   1.509 +                        causedByComposition));
   1.510 +}
   1.511 +
   1.512 +static nsIContent*
   1.513 +GetContentBR(dom::Element* aElement)
   1.514 +{
   1.515 +  if (!aElement->IsNodeOfType(nsINode::eCONTENT)) {
   1.516 +    return nullptr;
   1.517 +  }
   1.518 +  nsIContent* content = static_cast<nsIContent*>(aElement);
   1.519 +  return content->IsHTML(nsGkAtoms::br) ? content : nullptr;
   1.520 +}
   1.521 +
   1.522 +void
   1.523 +IMEContentObserver::AttributeWillChange(nsIDocument* aDocument,
   1.524 +                                        dom::Element* aElement,
   1.525 +                                        int32_t aNameSpaceID,
   1.526 +                                        nsIAtom* aAttribute,
   1.527 +                                        int32_t aModType)
   1.528 +{
   1.529 +  nsIContent *content = GetContentBR(aElement);
   1.530 +  mPreAttrChangeLength = content ?
   1.531 +    ContentEventHandler::GetNativeTextLength(content) : 0;
   1.532 +}
   1.533 +
   1.534 +void
   1.535 +IMEContentObserver::AttributeChanged(nsIDocument* aDocument,
   1.536 +                                     dom::Element* aElement,
   1.537 +                                     int32_t aNameSpaceID,
   1.538 +                                     nsIAtom* aAttribute,
   1.539 +                                     int32_t aModType)
   1.540 +{
   1.541 +  bool causedByComposition = IsEditorHandlingEventForComposition();
   1.542 +  if (causedByComposition &&
   1.543 +      !mUpdatePreference.WantChangesCausedByComposition()) {
   1.544 +    return;
   1.545 +  }
   1.546 +
   1.547 +  nsIContent *content = GetContentBR(aElement);
   1.548 +  if (!content) {
   1.549 +    return;
   1.550 +  }
   1.551 +
   1.552 +  uint32_t postAttrChangeLength =
   1.553 +    ContentEventHandler::GetNativeTextLength(content);
   1.554 +  if (postAttrChangeLength == mPreAttrChangeLength) {
   1.555 +    return;
   1.556 +  }
   1.557 +  uint32_t start;
   1.558 +  nsresult rv =
   1.559 +    ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, content,
   1.560 +                                                  0, &start,
   1.561 +                                                  LINE_BREAK_TYPE_NATIVE);
   1.562 +  NS_ENSURE_SUCCESS_VOID(rv);
   1.563 +
   1.564 +  nsContentUtils::AddScriptRunner(
   1.565 +    new TextChangeEvent(this, start, start + mPreAttrChangeLength,
   1.566 +                        start + postAttrChangeLength, causedByComposition));
   1.567 +}
   1.568 +
   1.569 +} // namespace mozilla

mercurial