dom/events/IMEContentObserver.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 sw=2 et tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "ContentEventHandler.h"
michael@0 8 #include "IMEContentObserver.h"
michael@0 9 #include "mozilla/AsyncEventDispatcher.h"
michael@0 10 #include "mozilla/EventStateManager.h"
michael@0 11 #include "mozilla/IMEStateManager.h"
michael@0 12 #include "mozilla/TextComposition.h"
michael@0 13 #include "mozilla/dom/Element.h"
michael@0 14 #include "nsAutoPtr.h"
michael@0 15 #include "nsContentUtils.h"
michael@0 16 #include "nsGkAtoms.h"
michael@0 17 #include "nsIAtom.h"
michael@0 18 #include "nsIContent.h"
michael@0 19 #include "nsIDocument.h"
michael@0 20 #include "nsIDOMDocument.h"
michael@0 21 #include "nsIDOMRange.h"
michael@0 22 #include "nsIFrame.h"
michael@0 23 #include "nsINode.h"
michael@0 24 #include "nsIPresShell.h"
michael@0 25 #include "nsISelectionController.h"
michael@0 26 #include "nsISelectionPrivate.h"
michael@0 27 #include "nsISupports.h"
michael@0 28 #include "nsIWidget.h"
michael@0 29 #include "nsPresContext.h"
michael@0 30 #include "nsThreadUtils.h"
michael@0 31 #include "nsWeakReference.h"
michael@0 32
michael@0 33 namespace mozilla {
michael@0 34
michael@0 35 using namespace widget;
michael@0 36
michael@0 37 NS_IMPL_CYCLE_COLLECTION(IMEContentObserver,
michael@0 38 mWidget, mSelection,
michael@0 39 mRootContent, mEditableNode, mDocShell)
michael@0 40
michael@0 41 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver)
michael@0 42 NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
michael@0 43 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
michael@0 44 NS_INTERFACE_MAP_ENTRY(nsIReflowObserver)
michael@0 45 NS_INTERFACE_MAP_ENTRY(nsIScrollObserver)
michael@0 46 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
michael@0 47 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelectionListener)
michael@0 48 NS_INTERFACE_MAP_END
michael@0 49
michael@0 50 NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
michael@0 51 NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
michael@0 52
michael@0 53 IMEContentObserver::IMEContentObserver()
michael@0 54 : mESM(nullptr)
michael@0 55 {
michael@0 56 }
michael@0 57
michael@0 58 void
michael@0 59 IMEContentObserver::Init(nsIWidget* aWidget,
michael@0 60 nsPresContext* aPresContext,
michael@0 61 nsIContent* aContent)
michael@0 62 {
michael@0 63 mESM = aPresContext->EventStateManager();
michael@0 64 mESM->OnStartToObserveContent(this);
michael@0 65
michael@0 66 mWidget = aWidget;
michael@0 67 mEditableNode = IMEStateManager::GetRootEditableNode(aPresContext, aContent);
michael@0 68 if (!mEditableNode) {
michael@0 69 return;
michael@0 70 }
michael@0 71
michael@0 72 nsIPresShell* presShell = aPresContext->PresShell();
michael@0 73
michael@0 74 // get selection and root content
michael@0 75 nsCOMPtr<nsISelectionController> selCon;
michael@0 76 if (mEditableNode->IsNodeOfType(nsINode::eCONTENT)) {
michael@0 77 nsIFrame* frame =
michael@0 78 static_cast<nsIContent*>(mEditableNode.get())->GetPrimaryFrame();
michael@0 79 NS_ENSURE_TRUE_VOID(frame);
michael@0 80
michael@0 81 frame->GetSelectionController(aPresContext,
michael@0 82 getter_AddRefs(selCon));
michael@0 83 } else {
michael@0 84 // mEditableNode is a document
michael@0 85 selCon = do_QueryInterface(presShell);
michael@0 86 }
michael@0 87 NS_ENSURE_TRUE_VOID(selCon);
michael@0 88
michael@0 89 selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
michael@0 90 getter_AddRefs(mSelection));
michael@0 91 NS_ENSURE_TRUE_VOID(mSelection);
michael@0 92
michael@0 93 nsCOMPtr<nsIDOMRange> selDomRange;
michael@0 94 if (NS_SUCCEEDED(mSelection->GetRangeAt(0, getter_AddRefs(selDomRange)))) {
michael@0 95 nsRange* selRange = static_cast<nsRange*>(selDomRange.get());
michael@0 96 NS_ENSURE_TRUE_VOID(selRange && selRange->GetStartParent());
michael@0 97
michael@0 98 mRootContent = selRange->GetStartParent()->
michael@0 99 GetSelectionRootContent(presShell);
michael@0 100 } else {
michael@0 101 mRootContent = mEditableNode->GetSelectionRootContent(presShell);
michael@0 102 }
michael@0 103 if (!mRootContent && mEditableNode->IsNodeOfType(nsINode::eDOCUMENT)) {
michael@0 104 // The document node is editable, but there are no contents, this document
michael@0 105 // is not editable.
michael@0 106 return;
michael@0 107 }
michael@0 108 NS_ENSURE_TRUE_VOID(mRootContent);
michael@0 109
michael@0 110 if (IMEStateManager::IsTestingIME()) {
michael@0 111 nsIDocument* doc = aPresContext->Document();
michael@0 112 (new AsyncEventDispatcher(doc, NS_LITERAL_STRING("MozIMEFocusIn"),
michael@0 113 false, false))->RunDOMEventWhenSafe();
michael@0 114 }
michael@0 115
michael@0 116 aWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS));
michael@0 117
michael@0 118 // NOTIFY_IME_OF_FOCUS might cause recreating IMEContentObserver
michael@0 119 // instance via IMEStateManager::UpdateIMEState(). So, this
michael@0 120 // instance might already have been destroyed, check it.
michael@0 121 if (!mRootContent) {
michael@0 122 return;
michael@0 123 }
michael@0 124
michael@0 125 mDocShell = aPresContext->GetDocShell();
michael@0 126
michael@0 127 ObserveEditableNode();
michael@0 128 }
michael@0 129
michael@0 130 void
michael@0 131 IMEContentObserver::ObserveEditableNode()
michael@0 132 {
michael@0 133 MOZ_ASSERT(mSelection);
michael@0 134 MOZ_ASSERT(mRootContent);
michael@0 135
michael@0 136 mUpdatePreference = mWidget->GetIMEUpdatePreference();
michael@0 137 if (mUpdatePreference.WantSelectionChange()) {
michael@0 138 // add selection change listener
michael@0 139 nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
michael@0 140 NS_ENSURE_TRUE_VOID(selPrivate);
michael@0 141 nsresult rv = selPrivate->AddSelectionListener(this);
michael@0 142 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 143 }
michael@0 144
michael@0 145 if (mUpdatePreference.WantTextChange()) {
michael@0 146 // add text change observer
michael@0 147 mRootContent->AddMutationObserver(this);
michael@0 148 }
michael@0 149
michael@0 150 if (mUpdatePreference.WantPositionChanged() && mDocShell) {
michael@0 151 // Add scroll position listener and reflow observer to detect position and
michael@0 152 // size changes
michael@0 153 mDocShell->AddWeakScrollObserver(this);
michael@0 154 mDocShell->AddWeakReflowObserver(this);
michael@0 155 }
michael@0 156 }
michael@0 157
michael@0 158 void
michael@0 159 IMEContentObserver::Destroy()
michael@0 160 {
michael@0 161 // If CreateTextStateManager failed, mRootContent will be null,
michael@0 162 // and we should not call NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR))
michael@0 163 if (mRootContent) {
michael@0 164 if (IMEStateManager::IsTestingIME() && mEditableNode) {
michael@0 165 nsIDocument* doc = mEditableNode->OwnerDoc();
michael@0 166 (new AsyncEventDispatcher(doc, NS_LITERAL_STRING("MozIMEFocusOut"),
michael@0 167 false, false))->RunDOMEventWhenSafe();
michael@0 168 }
michael@0 169 mWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
michael@0 170 }
michael@0 171 // Even if there are some pending notification, it'll never notify the widget.
michael@0 172 mWidget = nullptr;
michael@0 173 if (mUpdatePreference.WantSelectionChange() && mSelection) {
michael@0 174 nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
michael@0 175 if (selPrivate) {
michael@0 176 selPrivate->RemoveSelectionListener(this);
michael@0 177 }
michael@0 178 }
michael@0 179 mSelection = nullptr;
michael@0 180 if (mUpdatePreference.WantTextChange() && mRootContent) {
michael@0 181 mRootContent->RemoveMutationObserver(this);
michael@0 182 }
michael@0 183 if (mUpdatePreference.WantPositionChanged() && mDocShell) {
michael@0 184 mDocShell->RemoveWeakScrollObserver(this);
michael@0 185 mDocShell->RemoveWeakReflowObserver(this);
michael@0 186 }
michael@0 187 mRootContent = nullptr;
michael@0 188 mEditableNode = nullptr;
michael@0 189 mDocShell = nullptr;
michael@0 190 mUpdatePreference.mWantUpdates = nsIMEUpdatePreference::NOTIFY_NOTHING;
michael@0 191
michael@0 192 if (mESM) {
michael@0 193 mESM->OnStopObservingContent(this);
michael@0 194 mESM = nullptr;
michael@0 195 }
michael@0 196 }
michael@0 197
michael@0 198 void
michael@0 199 IMEContentObserver::DisconnectFromEventStateManager()
michael@0 200 {
michael@0 201 mESM = nullptr;
michael@0 202 }
michael@0 203
michael@0 204 bool
michael@0 205 IMEContentObserver::IsManaging(nsPresContext* aPresContext,
michael@0 206 nsIContent* aContent)
michael@0 207 {
michael@0 208 if (!mSelection || !mRootContent || !mEditableNode) {
michael@0 209 return false; // failed to initialize.
michael@0 210 }
michael@0 211 if (!mRootContent->IsInDoc()) {
michael@0 212 return false; // the focused editor has already been reframed.
michael@0 213 }
michael@0 214 return mEditableNode == IMEStateManager::GetRootEditableNode(aPresContext,
michael@0 215 aContent);
michael@0 216 }
michael@0 217
michael@0 218 bool
michael@0 219 IMEContentObserver::IsEditorHandlingEventForComposition() const
michael@0 220 {
michael@0 221 if (!mWidget) {
michael@0 222 return false;
michael@0 223 }
michael@0 224 nsRefPtr<TextComposition> composition =
michael@0 225 IMEStateManager::GetTextCompositionFor(mWidget);
michael@0 226 if (!composition) {
michael@0 227 return false;
michael@0 228 }
michael@0 229 return composition->IsEditorHandlingEvent();
michael@0 230 }
michael@0 231
michael@0 232 nsresult
michael@0 233 IMEContentObserver::GetSelectionAndRoot(nsISelection** aSelection,
michael@0 234 nsIContent** aRootContent) const
michael@0 235 {
michael@0 236 if (!mEditableNode || !mSelection) {
michael@0 237 return NS_ERROR_NOT_AVAILABLE;
michael@0 238 }
michael@0 239
michael@0 240 NS_ASSERTION(mSelection && mRootContent, "uninitialized content observer");
michael@0 241 NS_ADDREF(*aSelection = mSelection);
michael@0 242 NS_ADDREF(*aRootContent = mRootContent);
michael@0 243 return NS_OK;
michael@0 244 }
michael@0 245
michael@0 246 // Helper class, used for selection change notification
michael@0 247 class SelectionChangeEvent : public nsRunnable
michael@0 248 {
michael@0 249 public:
michael@0 250 SelectionChangeEvent(IMEContentObserver* aDispatcher,
michael@0 251 bool aCausedByComposition)
michael@0 252 : mDispatcher(aDispatcher)
michael@0 253 , mCausedByComposition(aCausedByComposition)
michael@0 254 {
michael@0 255 MOZ_ASSERT(mDispatcher);
michael@0 256 }
michael@0 257
michael@0 258 NS_IMETHOD Run()
michael@0 259 {
michael@0 260 if (mDispatcher->GetWidget()) {
michael@0 261 IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
michael@0 262 notification.mSelectionChangeData.mCausedByComposition =
michael@0 263 mCausedByComposition;
michael@0 264 mDispatcher->GetWidget()->NotifyIME(notification);
michael@0 265 }
michael@0 266 return NS_OK;
michael@0 267 }
michael@0 268
michael@0 269 private:
michael@0 270 nsRefPtr<IMEContentObserver> mDispatcher;
michael@0 271 bool mCausedByComposition;
michael@0 272 };
michael@0 273
michael@0 274 nsresult
michael@0 275 IMEContentObserver::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
michael@0 276 nsISelection* aSelection,
michael@0 277 int16_t aReason)
michael@0 278 {
michael@0 279 bool causedByComposition = IsEditorHandlingEventForComposition();
michael@0 280 if (causedByComposition &&
michael@0 281 !mUpdatePreference.WantChangesCausedByComposition()) {
michael@0 282 return NS_OK;
michael@0 283 }
michael@0 284
michael@0 285 int32_t count = 0;
michael@0 286 nsresult rv = aSelection->GetRangeCount(&count);
michael@0 287 NS_ENSURE_SUCCESS(rv, rv);
michael@0 288 if (count > 0 && mWidget) {
michael@0 289 nsContentUtils::AddScriptRunner(
michael@0 290 new SelectionChangeEvent(this, causedByComposition));
michael@0 291 }
michael@0 292 return NS_OK;
michael@0 293 }
michael@0 294
michael@0 295 // Helper class, used for position change notification
michael@0 296 class PositionChangeEvent MOZ_FINAL : public nsRunnable
michael@0 297 {
michael@0 298 public:
michael@0 299 PositionChangeEvent(IMEContentObserver* aDispatcher)
michael@0 300 : mDispatcher(aDispatcher)
michael@0 301 {
michael@0 302 MOZ_ASSERT(mDispatcher);
michael@0 303 }
michael@0 304
michael@0 305 NS_IMETHOD Run()
michael@0 306 {
michael@0 307 if (mDispatcher->GetWidget()) {
michael@0 308 mDispatcher->GetWidget()->NotifyIME(
michael@0 309 IMENotification(NOTIFY_IME_OF_POSITION_CHANGE));
michael@0 310 }
michael@0 311 return NS_OK;
michael@0 312 }
michael@0 313
michael@0 314 private:
michael@0 315 nsRefPtr<IMEContentObserver> mDispatcher;
michael@0 316 };
michael@0 317
michael@0 318 void
michael@0 319 IMEContentObserver::ScrollPositionChanged()
michael@0 320 {
michael@0 321 if (mWidget) {
michael@0 322 nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
michael@0 323 }
michael@0 324 }
michael@0 325
michael@0 326 NS_IMETHODIMP
michael@0 327 IMEContentObserver::Reflow(DOMHighResTimeStamp aStart,
michael@0 328 DOMHighResTimeStamp aEnd)
michael@0 329 {
michael@0 330 if (mWidget) {
michael@0 331 nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
michael@0 332 }
michael@0 333 return NS_OK;
michael@0 334 }
michael@0 335
michael@0 336 NS_IMETHODIMP
michael@0 337 IMEContentObserver::ReflowInterruptible(DOMHighResTimeStamp aStart,
michael@0 338 DOMHighResTimeStamp aEnd)
michael@0 339 {
michael@0 340 if (mWidget) {
michael@0 341 nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
michael@0 342 }
michael@0 343 return NS_OK;
michael@0 344 }
michael@0 345
michael@0 346 // Helper class, used for text change notification
michael@0 347 class TextChangeEvent : public nsRunnable
michael@0 348 {
michael@0 349 public:
michael@0 350 TextChangeEvent(IMEContentObserver* aDispatcher,
michael@0 351 uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd,
michael@0 352 bool aCausedByComposition)
michael@0 353 : mDispatcher(aDispatcher)
michael@0 354 , mStart(aStart)
michael@0 355 , mOldEnd(aOldEnd)
michael@0 356 , mNewEnd(aNewEnd)
michael@0 357 , mCausedByComposition(aCausedByComposition)
michael@0 358 {
michael@0 359 MOZ_ASSERT(mDispatcher);
michael@0 360 }
michael@0 361
michael@0 362 NS_IMETHOD Run()
michael@0 363 {
michael@0 364 if (mDispatcher->GetWidget()) {
michael@0 365 IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
michael@0 366 notification.mTextChangeData.mStartOffset = mStart;
michael@0 367 notification.mTextChangeData.mOldEndOffset = mOldEnd;
michael@0 368 notification.mTextChangeData.mNewEndOffset = mNewEnd;
michael@0 369 notification.mTextChangeData.mCausedByComposition = mCausedByComposition;
michael@0 370 mDispatcher->GetWidget()->NotifyIME(notification);
michael@0 371 }
michael@0 372 return NS_OK;
michael@0 373 }
michael@0 374
michael@0 375 private:
michael@0 376 nsRefPtr<IMEContentObserver> mDispatcher;
michael@0 377 uint32_t mStart, mOldEnd, mNewEnd;
michael@0 378 bool mCausedByComposition;
michael@0 379 };
michael@0 380
michael@0 381 void
michael@0 382 IMEContentObserver::CharacterDataChanged(nsIDocument* aDocument,
michael@0 383 nsIContent* aContent,
michael@0 384 CharacterDataChangeInfo* aInfo)
michael@0 385 {
michael@0 386 NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
michael@0 387 "character data changed for non-text node");
michael@0 388
michael@0 389 bool causedByComposition = IsEditorHandlingEventForComposition();
michael@0 390 if (causedByComposition &&
michael@0 391 !mUpdatePreference.WantChangesCausedByComposition()) {
michael@0 392 return;
michael@0 393 }
michael@0 394
michael@0 395 uint32_t offset = 0;
michael@0 396 // get offsets of change and fire notification
michael@0 397 nsresult rv =
michael@0 398 ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, aContent,
michael@0 399 aInfo->mChangeStart,
michael@0 400 &offset,
michael@0 401 LINE_BREAK_TYPE_NATIVE);
michael@0 402 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 403
michael@0 404 uint32_t oldEnd = offset + aInfo->mChangeEnd - aInfo->mChangeStart;
michael@0 405 uint32_t newEnd = offset + aInfo->mReplaceLength;
michael@0 406
michael@0 407 nsContentUtils::AddScriptRunner(
michael@0 408 new TextChangeEvent(this, offset, oldEnd, newEnd, causedByComposition));
michael@0 409 }
michael@0 410
michael@0 411 void
michael@0 412 IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
michael@0 413 int32_t aStartIndex,
michael@0 414 int32_t aEndIndex)
michael@0 415 {
michael@0 416 bool causedByComposition = IsEditorHandlingEventForComposition();
michael@0 417 if (causedByComposition &&
michael@0 418 !mUpdatePreference.WantChangesCausedByComposition()) {
michael@0 419 return;
michael@0 420 }
michael@0 421
michael@0 422 uint32_t offset = 0;
michael@0 423 nsresult rv =
michael@0 424 ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, aContainer,
michael@0 425 aStartIndex, &offset,
michael@0 426 LINE_BREAK_TYPE_NATIVE);
michael@0 427 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 428
michael@0 429 // get offset at the end of the last added node
michael@0 430 nsIContent* childAtStart = aContainer->GetChildAt(aStartIndex);
michael@0 431 uint32_t addingLength = 0;
michael@0 432 rv = ContentEventHandler::GetFlatTextOffsetOfRange(childAtStart, aContainer,
michael@0 433 aEndIndex, &addingLength,
michael@0 434 LINE_BREAK_TYPE_NATIVE);
michael@0 435 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 436
michael@0 437 if (!addingLength) {
michael@0 438 return;
michael@0 439 }
michael@0 440
michael@0 441 nsContentUtils::AddScriptRunner(
michael@0 442 new TextChangeEvent(this, offset, offset, offset + addingLength,
michael@0 443 causedByComposition));
michael@0 444 }
michael@0 445
michael@0 446 void
michael@0 447 IMEContentObserver::ContentAppended(nsIDocument* aDocument,
michael@0 448 nsIContent* aContainer,
michael@0 449 nsIContent* aFirstNewContent,
michael@0 450 int32_t aNewIndexInContainer)
michael@0 451 {
michael@0 452 NotifyContentAdded(aContainer, aNewIndexInContainer,
michael@0 453 aContainer->GetChildCount());
michael@0 454 }
michael@0 455
michael@0 456 void
michael@0 457 IMEContentObserver::ContentInserted(nsIDocument* aDocument,
michael@0 458 nsIContent* aContainer,
michael@0 459 nsIContent* aChild,
michael@0 460 int32_t aIndexInContainer)
michael@0 461 {
michael@0 462 NotifyContentAdded(NODE_FROM(aContainer, aDocument),
michael@0 463 aIndexInContainer, aIndexInContainer + 1);
michael@0 464 }
michael@0 465
michael@0 466 void
michael@0 467 IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
michael@0 468 nsIContent* aContainer,
michael@0 469 nsIContent* aChild,
michael@0 470 int32_t aIndexInContainer,
michael@0 471 nsIContent* aPreviousSibling)
michael@0 472 {
michael@0 473 bool causedByComposition = IsEditorHandlingEventForComposition();
michael@0 474 if (causedByComposition &&
michael@0 475 !mUpdatePreference.WantChangesCausedByComposition()) {
michael@0 476 return;
michael@0 477 }
michael@0 478
michael@0 479 uint32_t offset = 0;
michael@0 480 nsresult rv =
michael@0 481 ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent,
michael@0 482 NODE_FROM(aContainer,
michael@0 483 aDocument),
michael@0 484 aIndexInContainer, &offset,
michael@0 485 LINE_BREAK_TYPE_NATIVE);
michael@0 486 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 487
michael@0 488 // get offset at the end of the deleted node
michael@0 489 int32_t nodeLength =
michael@0 490 aChild->IsNodeOfType(nsINode::eTEXT) ?
michael@0 491 static_cast<int32_t>(aChild->TextLength()) :
michael@0 492 std::max(static_cast<int32_t>(aChild->GetChildCount()), 1);
michael@0 493 MOZ_ASSERT(nodeLength >= 0, "The node length is out of range");
michael@0 494 uint32_t textLength = 0;
michael@0 495 rv = ContentEventHandler::GetFlatTextOffsetOfRange(aChild, aChild,
michael@0 496 nodeLength, &textLength,
michael@0 497 LINE_BREAK_TYPE_NATIVE);
michael@0 498 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 499
michael@0 500 if (!textLength) {
michael@0 501 return;
michael@0 502 }
michael@0 503
michael@0 504 nsContentUtils::AddScriptRunner(
michael@0 505 new TextChangeEvent(this, offset, offset + textLength, offset,
michael@0 506 causedByComposition));
michael@0 507 }
michael@0 508
michael@0 509 static nsIContent*
michael@0 510 GetContentBR(dom::Element* aElement)
michael@0 511 {
michael@0 512 if (!aElement->IsNodeOfType(nsINode::eCONTENT)) {
michael@0 513 return nullptr;
michael@0 514 }
michael@0 515 nsIContent* content = static_cast<nsIContent*>(aElement);
michael@0 516 return content->IsHTML(nsGkAtoms::br) ? content : nullptr;
michael@0 517 }
michael@0 518
michael@0 519 void
michael@0 520 IMEContentObserver::AttributeWillChange(nsIDocument* aDocument,
michael@0 521 dom::Element* aElement,
michael@0 522 int32_t aNameSpaceID,
michael@0 523 nsIAtom* aAttribute,
michael@0 524 int32_t aModType)
michael@0 525 {
michael@0 526 nsIContent *content = GetContentBR(aElement);
michael@0 527 mPreAttrChangeLength = content ?
michael@0 528 ContentEventHandler::GetNativeTextLength(content) : 0;
michael@0 529 }
michael@0 530
michael@0 531 void
michael@0 532 IMEContentObserver::AttributeChanged(nsIDocument* aDocument,
michael@0 533 dom::Element* aElement,
michael@0 534 int32_t aNameSpaceID,
michael@0 535 nsIAtom* aAttribute,
michael@0 536 int32_t aModType)
michael@0 537 {
michael@0 538 bool causedByComposition = IsEditorHandlingEventForComposition();
michael@0 539 if (causedByComposition &&
michael@0 540 !mUpdatePreference.WantChangesCausedByComposition()) {
michael@0 541 return;
michael@0 542 }
michael@0 543
michael@0 544 nsIContent *content = GetContentBR(aElement);
michael@0 545 if (!content) {
michael@0 546 return;
michael@0 547 }
michael@0 548
michael@0 549 uint32_t postAttrChangeLength =
michael@0 550 ContentEventHandler::GetNativeTextLength(content);
michael@0 551 if (postAttrChangeLength == mPreAttrChangeLength) {
michael@0 552 return;
michael@0 553 }
michael@0 554 uint32_t start;
michael@0 555 nsresult rv =
michael@0 556 ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, content,
michael@0 557 0, &start,
michael@0 558 LINE_BREAK_TYPE_NATIVE);
michael@0 559 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 560
michael@0 561 nsContentUtils::AddScriptRunner(
michael@0 562 new TextChangeEvent(this, start, start + mPreAttrChangeLength,
michael@0 563 start + postAttrChangeLength, causedByComposition));
michael@0 564 }
michael@0 565
michael@0 566 } // namespace mozilla

mercurial