HTMLEditorMutationObserver.cpp (21588B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "HTMLEditor.h" 7 8 #include "EditAction.h" 9 #include "EditorDOMAPIWrapper.h" 10 #include "EditorUtils.h" 11 #include "HTMLEditorNestedClasses.h" 12 13 #include "mozilla/Assertions.h" 14 #include "mozilla/Attributes.h" 15 #include "mozilla/DebugOnly.h" 16 #include "mozilla/IMEStateManager.h" 17 #include "mozilla/Logging.h" 18 #include "mozilla/Maybe.h" 19 #include "mozilla/RefPtr.h" 20 #include "mozilla/dom/AncestorIterator.h" 21 #include "mozilla/dom/Element.h" 22 #include "mozInlineSpellChecker.h" 23 #include "nsContentUtils.h" 24 #include "nsIContent.h" 25 #include "nsIContentInlines.h" 26 #include "nsIMutationObserver.h" 27 #include "nsINode.h" 28 #include "nsRange.h" 29 #include "nsThreadUtils.h" 30 31 namespace mozilla { 32 33 using namespace dom; 34 35 /****************************************************************************** 36 * DOM mutation logger 37 ******************************************************************************/ 38 39 // - HTMLEditorMutation:3: Logging only mutations in editable containers which 40 // is not expected. 41 // - HTMLEditorMutation:4: Logging only mutations in editable containers which 42 // is either expected or not expected. 43 // - HTMLEditorMutation:5: Logging any mutations including in 44 // non-editable containers. 45 LazyLogModule gHTMLEditorMutationLog("HTMLEditorMutation"); 46 47 LogLevel HTMLEditor::MutationLogLevelOf( 48 nsIContent* aContent, 49 const CharacterDataChangeInfo* aCharacterDataChangeInfo, 50 DOMMutationType aDOMMutationType) const { 51 // Should be called only when the "info" level is enabled at least since we 52 // shouldn't add any new unnecessary calls in the hot paths when the logging 53 // is disabled. 54 MOZ_ASSERT(MOZ_LOG_TEST(gHTMLEditorMutationLog, LogLevel::Info)); 55 56 if (MOZ_UNLIKELY(!aContent->IsInComposedDoc())) { 57 return LogLevel::Disabled; 58 } 59 Element* const containerElement = aContent->GetAsElementOrParentElement(); 60 if (!containerElement || !containerElement->IsEditable()) { 61 return MOZ_LOG_TEST(gHTMLEditorMutationLog, LogLevel::Verbose) 62 ? LogLevel::Verbose 63 : LogLevel::Disabled; 64 } 65 if (!mRunningDOMAPIWrapper) { 66 return LogLevel::Info; 67 } 68 switch (aDOMMutationType) { 69 case DOMMutationType::ContentAppended: 70 return mRunningDOMAPIWrapper->IsExpectedContentAppended(aContent) 71 ? LogLevel::Debug 72 : LogLevel::Info; 73 case DOMMutationType::ContentInserted: 74 return mRunningDOMAPIWrapper->IsExpectedContentInserted(aContent) 75 ? LogLevel::Debug 76 : LogLevel::Info; 77 case DOMMutationType::ContentWillBeRemoved: 78 return mRunningDOMAPIWrapper->IsExpectedContentWillBeRemoved(aContent) 79 ? LogLevel::Debug 80 : LogLevel::Info; 81 case DOMMutationType::CharacterDataChanged: 82 MOZ_ASSERT(aCharacterDataChangeInfo); 83 return mRunningDOMAPIWrapper->IsExpectedCharacterDataChanged( 84 aContent, *aCharacterDataChangeInfo) 85 ? LogLevel::Debug 86 : LogLevel::Info; 87 default: 88 MOZ_ASSERT_UNREACHABLE("Invalid DOMMutationType value"); 89 return LogLevel::Disabled; 90 } 91 } 92 93 // - HTMLEditorAttrMutation:3: Logging only mutations of editable element which 94 // is not expected. 95 // - HTMLEditorAttrMutation:4: Logging only mutations of editable element which 96 // is either expected or not expected. 97 // - HTMLEditorAttrMutation:5: Logging any mutations including non-editable 98 // elements' attributes. 99 LazyLogModule gHTMLEditorAttrMutationLog("HTMLEditorAttrMutation"); 100 101 LogLevel HTMLEditor::AttrMutationLogLevelOf( 102 Element* aElement, int32_t aNameSpaceID, nsAtom* aAttribute, 103 AttrModType aModType, const nsAttrValue* aOldValue) const { 104 // Should be called only when the "info" level is enabled at least since we 105 // shouldn't add any new unnecessary calls in the hot paths when the logging 106 // is disabled. 107 MOZ_ASSERT(MOZ_LOG_TEST(gHTMLEditorAttrMutationLog, LogLevel::Info)); 108 if (MOZ_UNLIKELY(!aElement->IsInComposedDoc())) { 109 return LogLevel::Disabled; 110 } 111 if (!aElement->IsEditable()) { 112 return MOZ_LOG_TEST(gHTMLEditorAttrMutationLog, LogLevel::Verbose) 113 ? LogLevel::Verbose 114 : LogLevel::Disabled; 115 } 116 if (!mRunningDOMAPIWrapper) { 117 return LogLevel::Info; 118 } 119 return mRunningDOMAPIWrapper->IsExpectedAttributeChanged( 120 aElement, aNameSpaceID, aAttribute, aModType, aOldValue) 121 ? LogLevel::Debug 122 : LogLevel::Info; 123 } 124 125 void HTMLEditor::MaybeLogContentAppended(nsIContent* aFirstNewContent) const { 126 const LogLevel logLevel = MutationLogLevelOf( 127 aFirstNewContent, nullptr, DOMMutationType::ContentAppended); 128 if (logLevel == LogLevel::Disabled) { 129 return; 130 } 131 MOZ_LOG( 132 gHTMLEditorMutationLog, logLevel, 133 ("%p %s ContentAppended: %s (previousSibling=%s, nextSibling=%s)", this, 134 logLevel == LogLevel::Debug ? "HTMLEditor " : "SomebodyElse", 135 NodeToString(aFirstNewContent).get(), 136 NodeToString(aFirstNewContent ? aFirstNewContent->GetPreviousSibling() 137 : nullptr) 138 .get(), 139 NodeToString(aFirstNewContent ? aFirstNewContent->GetNextSibling() 140 : nullptr) 141 .get())); 142 } 143 144 void HTMLEditor::MaybeLogContentInserted(nsIContent* aChild) const { 145 const LogLevel logLevel = 146 MutationLogLevelOf(aChild, nullptr, DOMMutationType::ContentInserted); 147 if (logLevel == LogLevel::Disabled) { 148 return; 149 } 150 MOZ_LOG(gHTMLEditorMutationLog, logLevel, 151 ("%p %s ContentInserted: %s (previousSibling=%s, nextSibling=%s)", 152 this, logLevel == LogLevel::Debug ? "HTMLEditor " : "SomebodyElse", 153 NodeToString(aChild).get(), 154 NodeToString(aChild ? aChild->GetPreviousSibling() : nullptr).get(), 155 NodeToString(aChild ? aChild->GetNextSibling() : nullptr).get())); 156 } 157 158 void HTMLEditor::MaybeLogContentWillBeRemoved(nsIContent* aChild) const { 159 const LogLevel logLevel = MutationLogLevelOf( 160 aChild, nullptr, DOMMutationType::ContentWillBeRemoved); 161 if (logLevel == LogLevel::Disabled) { 162 return; 163 } 164 MOZ_LOG( 165 gHTMLEditorMutationLog, logLevel, 166 ("%p %s ContentWillBeRemoved: %s (previousSibling=%s, nextSibling=%s)", 167 this, logLevel == LogLevel::Debug ? "HTMLEditor " : "SomebodyElse", 168 NodeToString(aChild).get(), 169 NodeToString(aChild ? aChild->GetPreviousSibling() : nullptr).get(), 170 NodeToString(aChild ? aChild->GetNextSibling() : nullptr).get())); 171 } 172 173 void HTMLEditor::MaybeLogCharacterDataChanged( 174 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) const { 175 const LogLevel logLevel = MutationLogLevelOf( 176 aContent, &aInfo, DOMMutationType::CharacterDataChanged); 177 if (logLevel == LogLevel::Disabled) { 178 return; 179 } 180 nsAutoString data; 181 aContent->GetCharacterDataBuffer()->AppendTo(data); 182 MarkSelectionAndShrinkLongString shrunkenData( 183 data, aInfo.mChangeStart, aInfo.mChangeStart + aInfo.mReplaceLength); 184 MakeHumanFriendly(shrunkenData); 185 MOZ_LOG( 186 gHTMLEditorMutationLog, logLevel, 187 ("%p %s CharacterDataChanged: %s, data=\"%s\", (length=%u), aInfo=%s", 188 this, logLevel == LogLevel::Debug ? "HTMLEditor " : "SomebodyElse", 189 ToString(*aContent).c_str(), NS_ConvertUTF16toUTF8(shrunkenData).get(), 190 aContent->Length(), ToString(aInfo).c_str())); 191 } 192 193 void HTMLEditor::MaybeLogAttributeChanged(Element* aElement, 194 int32_t aNameSpaceID, 195 nsAtom* aAttribute, 196 AttrModType aModType, 197 const nsAttrValue* aOldValue) const { 198 const LogLevel logLevel = AttrMutationLogLevelOf( 199 aElement, aNameSpaceID, aAttribute, aModType, aOldValue); 200 if (logLevel == LogLevel::Disabled) { 201 return; 202 } 203 nsAutoString value; 204 aElement->GetAttr(aAttribute, value); 205 MOZ_LOG( 206 gHTMLEditorAttrMutationLog, logLevel, 207 ("%p %s AttributeChanged: %s of %s %s", this, 208 logLevel == LogLevel::Debug ? "HTMLEditor " : "SomebodyElse", 209 nsAutoAtomCString(aAttribute).get(), NodeToString(aElement).get(), 210 aModType == AttrModType::Removal 211 ? "Removed" 212 : nsPrintfCString("to \"%s\"", NS_ConvertUTF16toUTF8(value).get()) 213 .get())); 214 } 215 216 /****************************************************************************** 217 * mozilla::HTMLEditor - Start/end of a DOM API call to modify the DOM 218 ******************************************************************************/ 219 220 const AutoDOMAPIWrapperBase* HTMLEditor::OnDOMAPICallStart( 221 const AutoDOMAPIWrapperBase& aWrapperBase) { 222 const AutoDOMAPIWrapperBase* const prevRunner = mRunningDOMAPIWrapper; 223 mRunningDOMAPIWrapper = &aWrapperBase; 224 MOZ_LOG(gHTMLEditorMutationLog, LogLevel::Warning, 225 (">>>> %p Calling DOM API: %s", this, 226 ToString(*mRunningDOMAPIWrapper).c_str())); 227 return prevRunner; 228 } 229 230 void HTMLEditor::OnDOMAPICallEnd(const AutoDOMAPIWrapperBase* aPrevWrapper) { 231 MOZ_LOG(gHTMLEditorMutationLog, LogLevel::Warning, 232 ("<<<< %p Called DOM API: %s", this, 233 ToString(mRunningDOMAPIWrapper->Type()).c_str())); 234 mRunningDOMAPIWrapper = aPrevWrapper; 235 } 236 237 /****************************************************************************** 238 * mozilla::HTMLEditor - mutation observers/handlers 239 ******************************************************************************/ 240 241 void HTMLEditor::NotifyRootChanged() { 242 MOZ_ASSERT(mPendingRootElementUpdatedRunner, 243 "HTMLEditor::NotifyRootChanged() should be called via a runner"); 244 mPendingRootElementUpdatedRunner = nullptr; 245 246 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); 247 248 AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing); 249 if (NS_WARN_IF(!editActionData.CanHandle())) { 250 return; 251 } 252 253 RemoveEventListeners(); 254 nsresult rv = InstallEventListeners(); 255 if (NS_FAILED(rv)) { 256 NS_WARNING("HTMLEditor::InstallEventListeners() failed, but ignored"); 257 return; 258 } 259 260 UpdateRootElement(); 261 262 if (MOZ_LIKELY(mRootElement)) { 263 rv = MaybeCollapseSelectionAtFirstEditableNode(false); 264 if (NS_FAILED(rv)) { 265 NS_WARNING( 266 "HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(false) " 267 "failed, " 268 "but ignored"); 269 return; 270 } 271 272 // When this editor has focus, we need to reset the selection limiter to 273 // new root. Otherwise, that is going to be done when this gets focus. 274 nsCOMPtr<nsINode> node = GetFocusedNode(); 275 if (node) { 276 DebugOnly<nsresult> rvIgnored = InitializeSelection(*node); 277 NS_WARNING_ASSERTION( 278 NS_SUCCEEDED(rvIgnored), 279 "EditorBase::InitializeSelection() failed, but ignored"); 280 } 281 282 SyncRealTimeSpell(); 283 } 284 285 RefPtr<Element> newRootElement(mRootElement); 286 IMEStateManager::OnUpdateHTMLEditorRootElement(*this, newRootElement); 287 } 288 289 // nsStubMutationObserver::ContentAppended override 290 MOZ_CAN_RUN_SCRIPT_BOUNDARY void HTMLEditor::ContentAppended( 291 nsIContent* aFirstNewContent, const ContentAppendInfo&) { 292 DoContentInserted(aFirstNewContent, ContentNodeIs::Appended); 293 } 294 295 // nsStubMutationObserver::ContentInserted override 296 MOZ_CAN_RUN_SCRIPT_BOUNDARY void HTMLEditor::ContentInserted( 297 nsIContent* aChild, const ContentInsertInfo&) { 298 DoContentInserted(aChild, ContentNodeIs::Inserted); 299 } 300 301 bool HTMLEditor::IsInObservedSubtree(nsIContent* aChild) { 302 if (!aChild) { 303 return false; 304 } 305 306 // FIXME(emilio, bug 1596856): This should probably work if the root is in the 307 // same shadow tree as the child, probably? I don't know what the 308 // contenteditable-in-shadow-dom situation is. 309 if (Element* root = GetRoot()) { 310 // To be super safe here, check both ChromeOnlyAccess and NAC / Shadow DOM. 311 // That catches (also unbound) native anonymous content and ShadowDOM. 312 if (root->ChromeOnlyAccess() != aChild->ChromeOnlyAccess() || 313 root->IsInNativeAnonymousSubtree() != 314 aChild->IsInNativeAnonymousSubtree() || 315 root->IsInShadowTree() != aChild->IsInShadowTree()) { 316 return false; 317 } 318 } 319 320 return !aChild->ChromeOnlyAccess() && !aChild->IsInShadowTree() && 321 !aChild->IsInNativeAnonymousSubtree(); 322 } 323 324 bool HTMLEditor::ShouldReplaceRootElement() const { 325 if (!mRootElement) { 326 // If we don't know what is our root element, we should find our root. 327 return true; 328 } 329 330 // If we temporary set document root element to mRootElement, but there is 331 // body element now, we should replace the root element by the body element. 332 return mRootElement != GetBodyElement(); 333 } 334 335 void HTMLEditor::DoContentInserted(nsIContent* aChild, 336 ContentNodeIs aContentNodeIs) { 337 MOZ_ASSERT(aChild); 338 nsINode* container = aChild->GetParentNode(); 339 MOZ_ASSERT(container); 340 341 if (!IsInObservedSubtree(aChild)) { 342 return; 343 } 344 345 if (MOZ_LOG_TEST(gHTMLEditorMutationLog, LogLevel::Info)) { 346 if (aContentNodeIs == ContentNodeIs::Appended) { 347 MaybeLogContentAppended(aChild); 348 } else { 349 MOZ_ASSERT(aContentNodeIs == ContentNodeIs::Inserted); 350 MaybeLogContentInserted(aChild); 351 } 352 } 353 354 // XXX Why do we need this? This method is a helper of mutation observer. 355 // So, the callers of mutation observer should guarantee that this won't 356 // be deleted at least during the call. 357 RefPtr<HTMLEditor> kungFuDeathGrip(this); 358 359 // Do not create AutoEditActionDataSetter here because it grabs `Selection`, 360 // but that appear in the profile. If you need to create to it in some cases, 361 // you should do it in the minimum scope. 362 363 if (ShouldReplaceRootElement()) { 364 // Forget maybe disconnected root element right now because nobody should 365 // work with it. 366 mRootElement = nullptr; 367 if (mPendingRootElementUpdatedRunner) { 368 return; 369 } 370 mPendingRootElementUpdatedRunner = NewRunnableMethod( 371 "HTMLEditor::NotifyRootChanged", this, &HTMLEditor::NotifyRootChanged); 372 nsContentUtils::AddScriptRunner( 373 do_AddRef(mPendingRootElementUpdatedRunner)); 374 return; 375 } 376 377 // We don't need to handle our own modifications 378 if (!GetTopLevelEditSubAction() && container->IsEditable()) { 379 if (EditorUtils::IsPaddingBRElementForEmptyEditor(*aChild)) { 380 // Ignore insertion of the padding <br> element. 381 return; 382 } 383 nsresult rv = RunOrScheduleOnModifyDocument(); 384 if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { 385 return; 386 } 387 NS_WARNING_ASSERTION( 388 NS_SUCCEEDED(rv), 389 "HTMLEditor::RunOrScheduleOnModifyDocument() failed, but ignored"); 390 391 // Update spellcheck for only the newly-inserted node (bug 743819) 392 if (mInlineSpellChecker) { 393 nsIContent* endContent = aChild; 394 if (aContentNodeIs == ContentNodeIs::Appended) { 395 nsIContent* child = nullptr; 396 for (child = aChild; child; child = child->GetNextSibling()) { 397 if (child->InclusiveDescendantMayNeedSpellchecking(this)) { 398 break; 399 } 400 } 401 if (!child) { 402 // No child needed spellchecking, return. 403 return; 404 } 405 406 // Maybe more than 1 child was appended. 407 endContent = container->GetLastChild(); 408 } else if (!aChild->InclusiveDescendantMayNeedSpellchecking(this)) { 409 return; 410 } 411 412 RefPtr<nsRange> range = nsRange::Create(aChild); 413 range->SelectNodesInContainer(container, aChild, endContent); 414 DebugOnly<nsresult> rvIgnored = 415 mInlineSpellChecker->SpellCheckRange(range); 416 NS_WARNING_ASSERTION( 417 rvIgnored == NS_ERROR_NOT_INITIALIZED || NS_SUCCEEDED(rvIgnored), 418 "mozInlineSpellChecker::SpellCheckRange() failed, but ignored"); 419 } 420 } 421 } 422 423 // nsStubMutationObserver::ContentWillBeRemoved override 424 MOZ_CAN_RUN_SCRIPT_BOUNDARY void HTMLEditor::ContentWillBeRemoved( 425 nsIContent* aChild, const ContentRemoveInfo&) { 426 if (mLastCollapsibleWhiteSpaceAppendedTextNode == aChild) { 427 mLastCollapsibleWhiteSpaceAppendedTextNode = nullptr; 428 } 429 430 if (!IsInObservedSubtree(aChild)) { 431 return; 432 } 433 434 if (MOZ_LOG_TEST(gHTMLEditorMutationLog, LogLevel::Info)) { 435 MaybeLogContentWillBeRemoved(aChild); 436 } 437 438 // XXX Why do we need to do this? This method is a mutation observer's 439 // method. Therefore, the caller should guarantee that this won't be 440 // deleted during the call. 441 RefPtr<HTMLEditor> kungFuDeathGrip(this); 442 443 // Do not create AutoEditActionDataSetter here because it grabs `Selection`, 444 // but that appear in the profile. If you need to create to it in some cases, 445 // you should do it in the minimum scope. 446 447 // FYI: mRootElement may be the <body> of the document or the root element. 448 // Therefore, we don't need to check it across shadow DOM boundaries. 449 if (mRootElement && mRootElement->IsInclusiveDescendantOf(aChild)) { 450 // Forget the disconnected root element right now because nobody should work 451 // with it. 452 mRootElement = nullptr; 453 if (mPendingRootElementUpdatedRunner) { 454 return; 455 } 456 mPendingRootElementUpdatedRunner = NewRunnableMethod( 457 "HTMLEditor::NotifyRootChanged", this, &HTMLEditor::NotifyRootChanged); 458 nsContentUtils::AddScriptRunner( 459 do_AddRef(mPendingRootElementUpdatedRunner)); 460 return; 461 } 462 463 // We don't need to handle our own modifications 464 if (!GetTopLevelEditSubAction() && aChild->GetParentNode()->IsEditable()) { 465 if (aChild && EditorUtils::IsPaddingBRElementForEmptyEditor(*aChild)) { 466 // Ignore removal of the padding <br> element for empty editor. 467 return; 468 } 469 470 nsresult rv = RunOrScheduleOnModifyDocument(aChild); 471 if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { 472 return; 473 } 474 NS_WARNING_ASSERTION( 475 NS_SUCCEEDED(rv), 476 "HTMLEditor::RunOrScheduleOnModifyDocument() failed, but ignored"); 477 } 478 } 479 480 // nsStubMutationObserver::CharacterDataChanged override 481 MOZ_CAN_RUN_SCRIPT_BOUNDARY void HTMLEditor::CharacterDataChanged( 482 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) { 483 if (!IsInObservedSubtree(aContent)) { 484 return; 485 } 486 if (MOZ_LOG_TEST(gHTMLEditorMutationLog, LogLevel::Info)) { 487 MaybeLogCharacterDataChanged(aContent, aInfo); 488 } 489 if (!mInlineSpellChecker || !aContent->IsEditable() || 490 GetTopLevelEditSubAction() != EditSubAction::eNone) { 491 return; 492 } 493 494 nsIContent* parent = aContent->GetParent(); 495 if (!parent || !parent->InclusiveDescendantMayNeedSpellchecking(this)) { 496 return; 497 } 498 499 RefPtr<nsRange> range = nsRange::Create(aContent); 500 range->SelectNodesInContainer(parent, aContent, aContent); 501 DebugOnly<nsresult> rvIgnored = mInlineSpellChecker->SpellCheckRange(range); 502 } 503 504 nsresult HTMLEditor::RunOrScheduleOnModifyDocument( 505 const nsIContent* aContentWillBeRemoved /* = nullptr */) { 506 if (mPendingDocumentModifiedRunner) { 507 return NS_OK; // We've already posted same runnable into the queue. 508 } 509 mPendingDocumentModifiedRunner = new DocumentModifiedEvent(*this); 510 nsContentUtils::AddScriptRunner(do_AddRef(mPendingDocumentModifiedRunner)); 511 // Be aware, if OnModifyDocument() may be called synchronously, the 512 // editor might have been destroyed here. 513 return NS_WARN_IF(Destroyed()) ? NS_ERROR_EDITOR_DESTROYED : NS_OK; 514 } 515 516 // nsStubMutationObserver::AttributeChanged override 517 MOZ_CAN_RUN_SCRIPT_BOUNDARY void HTMLEditor::AttributeChanged( 518 Element* aElement, int32_t aNameSpaceID, nsAtom* aAttribute, 519 AttrModType aModType, const nsAttrValue* aOldValue) { 520 if (MOZ_LOG_TEST(gHTMLEditorAttrMutationLog, LogLevel::Info) && 521 IsInObservedSubtree(aElement)) { 522 MaybeLogAttributeChanged(aElement, aNameSpaceID, aAttribute, aModType, 523 aOldValue); 524 } 525 } 526 527 nsresult HTMLEditor::OnModifyDocument(const DocumentModifiedEvent& aRunner) { 528 MOZ_ASSERT(mPendingDocumentModifiedRunner, 529 "HTMLEditor::OnModifyDocument() should be called via a runner"); 530 MOZ_ASSERT(&aRunner == mPendingDocumentModifiedRunner); 531 mPendingDocumentModifiedRunner = nullptr; 532 533 Maybe<AutoEditActionDataSetter> editActionData; 534 if (!IsEditActionDataAvailable()) { 535 editActionData.emplace(*this, 536 EditAction::eCreatePaddingBRElementForEmptyEditor); 537 if (NS_WARN_IF(!editActionData->CanHandle())) { 538 return NS_ERROR_NOT_AVAILABLE; 539 } 540 } 541 542 // EnsureNoPaddingBRElementForEmptyEditor() below may cause a flush, which 543 // could destroy the editor 544 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker; 545 546 // Delete our padding <br> element for empty editor, if we have one, since 547 // the document might not be empty any more. 548 nsresult rv = EnsureNoPaddingBRElementForEmptyEditor(); 549 if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { 550 return rv; 551 } 552 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 553 "EditorBase::EnsureNoPaddingBRElementForEmptyEditor() " 554 "failed, but ignored"); 555 556 // Try to recreate the padding <br> element for empty editor if needed. 557 rv = MaybeCreatePaddingBRElementForEmptyEditor(); 558 if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { 559 return NS_ERROR_EDITOR_DESTROYED; 560 } 561 NS_WARNING_ASSERTION( 562 NS_SUCCEEDED(rv), 563 "EditorBase::MaybeCreatePaddingBRElementForEmptyEditor() failed"); 564 565 return rv; 566 } 567 568 } // namespace mozilla