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 #ifndef mozilla_EditorUtils_h 7 #define mozilla_EditorUtils_h 8 9 #include "mozilla/EditAction.h" 10 #include "mozilla/EditorBase.h" 11 #include "mozilla/EditorDOMPoint.h" 12 #include "mozilla/IntegerRange.h" 13 #include "mozilla/RangeBoundary.h" 14 #include "mozilla/Result.h" 15 #include "mozilla/dom/Element.h" 16 #include "mozilla/dom/HTMLBRElement.h" 17 #include "mozilla/dom/Selection.h" 18 #include "mozilla/dom/Text.h" 19 #include "nsAtom.h" 20 #include "nsCOMPtr.h" 21 #include "nsContentUtils.h" 22 #include "nsDebug.h" 23 #include "nsDirection.h" 24 #include "nsError.h" 25 #include "nsRange.h" 26 #include "nsString.h" 27 28 class nsITransferable; 29 30 namespace mozilla { 31 class MoveNodeResult; 32 template <class T> 33 class OwningNonNull; 34 35 /*************************************************************************** 36 * EditActionResult is useful to return multiple results of an editor 37 * action handler without out params. 38 * Note that when you return an anonymous instance from a method, you should 39 * use EditActionIgnored(), EditActionHandled() or EditActionCanceled() for 40 * easier to read. In other words, EditActionResult should be used when 41 * declaring return type of a method, being an argument or defined as a local 42 * variable. 43 */ 44 class MOZ_STACK_CLASS EditActionResult final { 45 public: Succeeded()46 bool Succeeded() const { return NS_SUCCEEDED(mRv); } Failed()47 bool Failed() const { return NS_FAILED(mRv); } Rv()48 nsresult Rv() const { return mRv; } Canceled()49 bool Canceled() const { return mCanceled; } Handled()50 bool Handled() const { return mHandled; } Ignored()51 bool Ignored() const { return !mCanceled && !mHandled; } EditorDestroyed()52 bool EditorDestroyed() const { return mRv == NS_ERROR_EDITOR_DESTROYED; } 53 SetResult(nsresult aRv)54 EditActionResult SetResult(nsresult aRv) { 55 mRv = aRv; 56 return *this; 57 } MarkAsCanceled()58 EditActionResult MarkAsCanceled() { 59 mCanceled = true; 60 return *this; 61 } MarkAsHandled()62 EditActionResult MarkAsHandled() { 63 mHandled = true; 64 return *this; 65 } 66 EditActionResult(nsresult aRv)67 explicit EditActionResult(nsresult aRv) 68 : mRv(aRv), mCanceled(false), mHandled(false) {} 69 70 EditActionResult& operator|=(const EditActionResult& aOther) { 71 mCanceled |= aOther.mCanceled; 72 mHandled |= aOther.mHandled; 73 // When both result are same, keep the result. 74 if (mRv == aOther.mRv) { 75 return *this; 76 } 77 // If one of the result is NS_ERROR_EDITOR_DESTROYED, use it since it's 78 // the most important error code for editor. 79 if (EditorDestroyed() || aOther.EditorDestroyed()) { 80 mRv = NS_ERROR_EDITOR_DESTROYED; 81 } 82 // If one of the results is error, use NS_ERROR_FAILURE. 83 else if (Failed() || aOther.Failed()) { 84 mRv = NS_ERROR_FAILURE; 85 } else { 86 // Otherwise, use generic success code, NS_OK. 87 mRv = NS_OK; 88 } 89 return *this; 90 } 91 92 EditActionResult& operator|=(const MoveNodeResult& aMoveNodeResult); 93 94 private: 95 nsresult mRv; 96 bool mCanceled; 97 bool mHandled; 98 EditActionResult(nsresult aRv,bool aCanceled,bool aHandled)99 EditActionResult(nsresult aRv, bool aCanceled, bool aHandled) 100 : mRv(aRv), mCanceled(aCanceled), mHandled(aHandled) {} 101 EditActionResult()102 EditActionResult() 103 : mRv(NS_ERROR_NOT_INITIALIZED), mCanceled(false), mHandled(false) {} 104 105 friend EditActionResult EditActionIgnored(nsresult aRv); 106 friend EditActionResult EditActionHandled(nsresult aRv); 107 friend EditActionResult EditActionCanceled(nsresult aRv); 108 }; 109 110 /*************************************************************************** 111 * When an edit action handler (or its helper) does nothing, 112 * EditActionIgnored should be returned. 113 */ 114 inline EditActionResult EditActionIgnored(nsresult aRv = NS_OK) { 115 return EditActionResult(aRv, false, false); 116 } 117 118 /*************************************************************************** 119 * When an edit action handler (or its helper) handled and not canceled, 120 * EditActionHandled should be returned. 121 */ 122 inline EditActionResult EditActionHandled(nsresult aRv = NS_OK) { 123 return EditActionResult(aRv, false, true); 124 } 125 126 /*************************************************************************** 127 * When an edit action handler (or its helper) handled and canceled, 128 * EditActionHandled should be returned. 129 */ 130 inline EditActionResult EditActionCanceled(nsresult aRv = NS_OK) { 131 return EditActionResult(aRv, true, true); 132 } 133 134 /*************************************************************************** 135 * CreateNodeResultBase is a simple class for CreateSomething() methods 136 * which want to return new node. 137 */ 138 template <typename NodeType> 139 class CreateNodeResultBase; 140 141 typedef CreateNodeResultBase<dom::Element> CreateElementResult; 142 143 template <typename NodeType> 144 class MOZ_STACK_CLASS CreateNodeResultBase final { 145 typedef CreateNodeResultBase<NodeType> SelfType; 146 147 public: Succeeded()148 bool Succeeded() const { return NS_SUCCEEDED(mRv); } Failed()149 bool Failed() const { return NS_FAILED(mRv); } Rv()150 nsresult Rv() const { return mRv; } EditorDestroyed()151 bool EditorDestroyed() const { return mRv == NS_ERROR_EDITOR_DESTROYED; } GetNewNode()152 NodeType* GetNewNode() const { return mNode; } 153 154 CreateNodeResultBase() = delete; 155 CreateNodeResultBase(nsresult aRv)156 explicit CreateNodeResultBase(nsresult aRv) : mRv(aRv) { 157 MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(mRv)); 158 } 159 CreateNodeResultBase(NodeType * aNode)160 explicit CreateNodeResultBase(NodeType* aNode) 161 : mNode(aNode), mRv(aNode ? NS_OK : NS_ERROR_FAILURE) {} 162 CreateNodeResultBase(RefPtr<NodeType> && aNode)163 explicit CreateNodeResultBase(RefPtr<NodeType>&& aNode) 164 : mNode(std::move(aNode)), mRv(mNode.get() ? NS_OK : NS_ERROR_FAILURE) {} 165 166 CreateNodeResultBase(const SelfType& aOther) = delete; 167 SelfType& operator=(const SelfType& aOther) = delete; 168 CreateNodeResultBase(SelfType&& aOther) = default; 169 SelfType& operator=(SelfType&& aOther) = default; 170 forget()171 already_AddRefed<NodeType> forget() { 172 mRv = NS_ERROR_NOT_INITIALIZED; 173 return mNode.forget(); 174 } 175 176 private: 177 RefPtr<NodeType> mNode; 178 nsresult mRv; 179 }; 180 181 /*************************************************************************** 182 * stack based helper class for calling EditorBase::EndTransaction() after 183 * EditorBase::BeginTransaction(). This shouldn't be used in editor classes 184 * or helper classes while an edit action is being handled. Use 185 * AutoTransactionBatch in such cases since it uses non-virtual internal 186 * methods. 187 ***************************************************************************/ 188 class MOZ_RAII AutoTransactionBatchExternal final { 189 public: AutoTransactionBatchExternal(EditorBase & aEditorBase)190 MOZ_CAN_RUN_SCRIPT explicit AutoTransactionBatchExternal( 191 EditorBase& aEditorBase) 192 : mEditorBase(aEditorBase) { 193 MOZ_KnownLive(mEditorBase).BeginTransaction(); 194 } 195 ~AutoTransactionBatchExternal()196 MOZ_CAN_RUN_SCRIPT ~AutoTransactionBatchExternal() { 197 MOZ_KnownLive(mEditorBase).EndTransaction(); 198 } 199 200 private: 201 EditorBase& mEditorBase; 202 }; 203 204 /****************************************************************************** 205 * AutoSelectionRangeArray stores all ranges in `aSelection`. 206 * Note that modifying the ranges means modifing the selection ranges. 207 *****************************************************************************/ 208 class MOZ_STACK_CLASS AutoSelectionRangeArray final { 209 public: AutoSelectionRangeArray(dom::Selection & aSelection)210 explicit AutoSelectionRangeArray(dom::Selection& aSelection) { 211 for (const uint32_t i : IntegerRange(aSelection.RangeCount())) { 212 MOZ_ASSERT(aSelection.GetRangeAt(i)); 213 mRanges.AppendElement(*aSelection.GetRangeAt(i)); 214 } 215 } 216 217 AutoTArray<mozilla::OwningNonNull<nsRange>, 8> mRanges; 218 }; 219 220 /****************************************************************************** 221 * AutoRangeArray stores ranges which do no belong any `Selection`. 222 * So, different from `AutoSelectionRangeArray`, this can be used for 223 * ranges which may need to be modified before touching the DOM tree, 224 * but does not want to modify `Selection` for the performance. 225 *****************************************************************************/ 226 class MOZ_STACK_CLASS AutoRangeArray final { 227 public: AutoRangeArray(const dom::Selection & aSelection)228 explicit AutoRangeArray(const dom::Selection& aSelection) { 229 Initialize(aSelection); 230 } 231 Initialize(const dom::Selection & aSelection)232 void Initialize(const dom::Selection& aSelection) { 233 mDirection = aSelection.GetDirection(); 234 mRanges.Clear(); 235 for (const uint32_t i : IntegerRange(aSelection.RangeCount())) { 236 MOZ_ASSERT(aSelection.GetRangeAt(i)); 237 mRanges.AppendElement(aSelection.GetRangeAt(i)->CloneRange()); 238 if (aSelection.GetRangeAt(i) == aSelection.GetAnchorFocusRange()) { 239 mAnchorFocusRange = mRanges.LastElement(); 240 } 241 } 242 } 243 244 /** 245 * EnsureOnlyEditableRanges() removes ranges which cannot modify. 246 * Note that this is designed only for `HTMLEditor` because this must not 247 * be required by `TextEditor`. 248 */ 249 void EnsureOnlyEditableRanges(const dom::Element& aEditingHost); 250 251 /** 252 * EnsureRangesInTextNode() is designed for TextEditor to guarantee that 253 * all ranges are in its text node which is first child of the anonymous <div> 254 * element and is first child. 255 */ 256 void EnsureRangesInTextNode(const dom::Text& aTextNode); 257 258 static bool IsEditableRange(const dom::AbstractRange& aRange, 259 const dom::Element& aEditingHost); 260 261 /** 262 * IsAtLeastOneContainerOfRangeBoundariesInclusiveDescendantOf() returns true 263 * if at least one of the containers of the range boundaries is an inclusive 264 * descendant of aContent. 265 */ IsAtLeastOneContainerOfRangeBoundariesInclusiveDescendantOf(const nsIContent & aContent)266 bool IsAtLeastOneContainerOfRangeBoundariesInclusiveDescendantOf( 267 const nsIContent& aContent) const { 268 for (const OwningNonNull<nsRange>& range : mRanges) { 269 nsINode* startContainer = range->GetStartContainer(); 270 if (startContainer && 271 startContainer->IsInclusiveDescendantOf(&aContent)) { 272 return true; 273 } 274 nsINode* endContainer = range->GetEndContainer(); 275 if (startContainer == endContainer) { 276 continue; 277 } 278 if (endContainer && endContainer->IsInclusiveDescendantOf(&aContent)) { 279 return true; 280 } 281 } 282 return false; 283 } 284 Ranges()285 auto& Ranges() { return mRanges; } Ranges()286 const auto& Ranges() const { return mRanges; } FirstRangeRef()287 auto& FirstRangeRef() { return mRanges[0]; } FirstRangeRef()288 const auto& FirstRangeRef() const { return mRanges[0]; } 289 290 template <template <typename> typename StrongPtrType> CloneRanges()291 AutoTArray<StrongPtrType<nsRange>, 8> CloneRanges() const { 292 AutoTArray<StrongPtrType<nsRange>, 8> ranges; 293 for (const auto& range : mRanges) { 294 ranges.AppendElement(range->CloneRange()); 295 } 296 return ranges; 297 } 298 GetStartPointOfFirstRange()299 EditorDOMPoint GetStartPointOfFirstRange() const { 300 if (mRanges.IsEmpty() || !mRanges[0]->IsPositioned()) { 301 return EditorDOMPoint(); 302 } 303 return EditorDOMPoint(mRanges[0]->StartRef()); 304 } GetEndPointOfFirstRange()305 EditorDOMPoint GetEndPointOfFirstRange() const { 306 if (mRanges.IsEmpty() || !mRanges[0]->IsPositioned()) { 307 return EditorDOMPoint(); 308 } 309 return EditorDOMPoint(mRanges[0]->EndRef()); 310 } 311 SelectNode(nsINode & aNode)312 nsresult SelectNode(nsINode& aNode) { 313 mRanges.Clear(); 314 if (!mAnchorFocusRange) { 315 mAnchorFocusRange = nsRange::Create(&aNode); 316 if (!mAnchorFocusRange) { 317 return NS_ERROR_FAILURE; 318 } 319 } 320 ErrorResult error; 321 mAnchorFocusRange->SelectNode(aNode, error); 322 if (error.Failed()) { 323 mAnchorFocusRange = nullptr; 324 return error.StealNSResult(); 325 } 326 mRanges.AppendElement(*mAnchorFocusRange); 327 return NS_OK; 328 } 329 330 /** 331 * ExtendAnchorFocusRangeFor() extends the anchor-focus range for deleting 332 * content for aDirectionAndAmount. The range won't be extended to outer of 333 * selection limiter. Note that if a range is extened, the range is 334 * recreated. Therefore, caller cannot cache pointer of any ranges before 335 * calling this. 336 */ 337 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<nsIEditor::EDirection, nsresult> 338 ExtendAnchorFocusRangeFor(const EditorBase& aEditorBase, 339 nsIEditor::EDirection aDirectionAndAmount); 340 341 /** 342 * For compatiblity with the other browsers, we should shrink ranges to 343 * start from an atomic content and/or end after one instead of start 344 * from end of a preceding text node and end by start of a follwing text 345 * node. Returns true if this modifies a range. 346 */ 347 enum class IfSelectingOnlyOneAtomicContent { 348 Collapse, // Collapse to the range selecting only one atomic content to 349 // start or after of it. Whether to collapse start or after 350 // it depends on aDirectionAndAmount. This is ignored if 351 // there are multiple ranges. 352 KeepSelecting, // Won't collapse the range. 353 }; 354 Result<bool, nsresult> ShrinkRangesIfStartFromOrEndAfterAtomicContent( 355 const HTMLEditor& aHTMLEditor, nsIEditor::EDirection aDirectionAndAmount, 356 IfSelectingOnlyOneAtomicContent aIfSelectingOnlyOneAtomicContent, 357 const dom::Element* aEditingHost); 358 359 /** 360 * The following methods are same as `Selection`'s methods. 361 */ IsCollapsed()362 bool IsCollapsed() const { 363 return mRanges.IsEmpty() || 364 (mRanges.Length() == 1 && mRanges[0]->Collapsed()); 365 } 366 template <typename PT, typename CT> Collapse(const EditorDOMPointBase<PT,CT> & aPoint)367 nsresult Collapse(const EditorDOMPointBase<PT, CT>& aPoint) { 368 mRanges.Clear(); 369 if (!mAnchorFocusRange) { 370 ErrorResult error; 371 mAnchorFocusRange = nsRange::Create(aPoint.ToRawRangeBoundary(), 372 aPoint.ToRawRangeBoundary(), error); 373 if (error.Failed()) { 374 mAnchorFocusRange = nullptr; 375 return error.StealNSResult(); 376 } 377 } else { 378 nsresult rv = mAnchorFocusRange->CollapseTo(aPoint.ToRawRangeBoundary()); 379 if (NS_FAILED(rv)) { 380 mAnchorFocusRange = nullptr; 381 return rv; 382 } 383 } 384 mRanges.AppendElement(*mAnchorFocusRange); 385 return NS_OK; 386 } 387 template <typename SPT, typename SCT, typename EPT, typename ECT> SetStartAndEnd(const EditorDOMPointBase<SPT,SCT> & aStart,const EditorDOMPointBase<EPT,ECT> & aEnd)388 nsresult SetStartAndEnd(const EditorDOMPointBase<SPT, SCT>& aStart, 389 const EditorDOMPointBase<EPT, ECT>& aEnd) { 390 mRanges.Clear(); 391 if (!mAnchorFocusRange) { 392 ErrorResult error; 393 mAnchorFocusRange = nsRange::Create(aStart.ToRawRangeBoundary(), 394 aEnd.ToRawRangeBoundary(), error); 395 if (error.Failed()) { 396 mAnchorFocusRange = nullptr; 397 return error.StealNSResult(); 398 } 399 } else { 400 nsresult rv = mAnchorFocusRange->SetStartAndEnd( 401 aStart.ToRawRangeBoundary(), aEnd.ToRawRangeBoundary()); 402 if (NS_FAILED(rv)) { 403 mAnchorFocusRange = nullptr; 404 return rv; 405 } 406 } 407 mRanges.AppendElement(*mAnchorFocusRange); 408 return NS_OK; 409 } GetAnchorFocusRange()410 const nsRange* GetAnchorFocusRange() const { return mAnchorFocusRange; } GetDirection()411 nsDirection GetDirection() const { return mDirection; } 412 AnchorRef()413 const RangeBoundary& AnchorRef() const { 414 if (!mAnchorFocusRange) { 415 static RangeBoundary sEmptyRangeBoundary; 416 return sEmptyRangeBoundary; 417 } 418 return mDirection == nsDirection::eDirNext ? mAnchorFocusRange->StartRef() 419 : mAnchorFocusRange->EndRef(); 420 } GetAnchorNode()421 nsINode* GetAnchorNode() const { 422 return AnchorRef().IsSet() ? AnchorRef().Container() : nullptr; 423 } GetAnchorOffset()424 uint32_t GetAnchorOffset() const { 425 return AnchorRef().IsSet() 426 ? AnchorRef() 427 .Offset(RangeBoundary::OffsetFilter::kValidOffsets) 428 .valueOr(0) 429 : 0; 430 } GetChildAtAnchorOffset()431 nsIContent* GetChildAtAnchorOffset() const { 432 return AnchorRef().IsSet() ? AnchorRef().GetChildAtOffset() : nullptr; 433 } 434 FocusRef()435 const RangeBoundary& FocusRef() const { 436 if (!mAnchorFocusRange) { 437 static RangeBoundary sEmptyRangeBoundary; 438 return sEmptyRangeBoundary; 439 } 440 return mDirection == nsDirection::eDirNext ? mAnchorFocusRange->EndRef() 441 : mAnchorFocusRange->StartRef(); 442 } GetFocusNode()443 nsINode* GetFocusNode() const { 444 return FocusRef().IsSet() ? FocusRef().Container() : nullptr; 445 } FocusOffset()446 uint32_t FocusOffset() const { 447 return FocusRef().IsSet() 448 ? FocusRef() 449 .Offset(RangeBoundary::OffsetFilter::kValidOffsets) 450 .valueOr(0) 451 : 0; 452 } GetChildAtFocusOffset()453 nsIContent* GetChildAtFocusOffset() const { 454 return FocusRef().IsSet() ? FocusRef().GetChildAtOffset() : nullptr; 455 } 456 RemoveAllRanges()457 void RemoveAllRanges() { 458 mRanges.Clear(); 459 mAnchorFocusRange = nullptr; 460 mDirection = nsDirection::eDirNext; 461 } 462 463 private: 464 AutoTArray<mozilla::OwningNonNull<nsRange>, 8> mRanges; 465 RefPtr<nsRange> mAnchorFocusRange; 466 nsDirection mDirection = nsDirection::eDirNext; 467 }; 468 469 class EditorUtils final { 470 public: 471 using EditorType = EditorBase::EditorType; 472 using Selection = dom::Selection; 473 474 /** 475 * IsDescendantOf() checks if aNode is a child or a descendant of aParent. 476 * aOutPoint is set to the child of aParent. 477 * 478 * @return true if aNode is a child or a descendant of aParent. 479 */ 480 static bool IsDescendantOf(const nsINode& aNode, const nsINode& aParent, 481 EditorRawDOMPoint* aOutPoint = nullptr); 482 static bool IsDescendantOf(const nsINode& aNode, const nsINode& aParent, 483 EditorDOMPoint* aOutPoint); 484 485 /** 486 * Returns true if aContent is a <br> element and it's marked as padding for 487 * empty editor. 488 */ IsPaddingBRElementForEmptyEditor(const nsIContent & aContent)489 static bool IsPaddingBRElementForEmptyEditor(const nsIContent& aContent) { 490 const dom::HTMLBRElement* brElement = 491 dom::HTMLBRElement::FromNode(&aContent); 492 return brElement && brElement->IsPaddingForEmptyEditor(); 493 } 494 495 /** 496 * Returns true if aContent is a <br> element and it's marked as padding for 497 * empty last line. 498 */ IsPaddingBRElementForEmptyLastLine(const nsIContent & aContent)499 static bool IsPaddingBRElementForEmptyLastLine(const nsIContent& aContent) { 500 const dom::HTMLBRElement* brElement = 501 dom::HTMLBRElement::FromNode(&aContent); 502 return brElement && brElement->IsPaddingForEmptyLastLine(); 503 } 504 505 /** 506 * IsEditableContent() returns true if aContent's data or children is ediable 507 * for the given editor type. Be aware, returning true does NOT mean the 508 * node can be removed from its parent node, and returning false does NOT 509 * mean the node cannot be removed from the parent node. 510 * XXX May be the anonymous nodes in TextEditor not editable? If it's not 511 * so, we can get rid of aEditorType. 512 */ IsEditableContent(const nsIContent & aContent,EditorType aEditorType)513 static bool IsEditableContent(const nsIContent& aContent, 514 EditorType aEditorType) { 515 if (aEditorType == EditorType::HTML && 516 (!aContent.IsEditable() || !aContent.IsInComposedDoc())) { 517 // FIXME(emilio): Why only for HTML editors? All content from the root 518 // content in text editors is also editable, so afaict we can remove the 519 // special-case. 520 return false; 521 } 522 return IsElementOrText(aContent); 523 } 524 525 /** 526 * Returns true if aContent is a usual element node (not padding <br> element 527 * for empty editor) or a text node. In other words, returns true if 528 * aContent is a usual element node or visible data node. 529 */ IsElementOrText(const nsIContent & aContent)530 static bool IsElementOrText(const nsIContent& aContent) { 531 if (aContent.IsText()) { 532 return true; 533 } 534 return aContent.IsElement() && !IsPaddingBRElementForEmptyEditor(aContent); 535 } 536 537 /** 538 * IsWhiteSpacePreformatted() checks the style info for the node for the 539 * preformatted text style. This does NOT flush layout. 540 */ 541 static bool IsWhiteSpacePreformatted(const nsIContent& aContent); 542 543 /** 544 * IsNewLinePreformatted() checks whether the linefeed characters are 545 * preformatted or collapsible white-spaces. This does NOT flush layout. 546 */ 547 static bool IsNewLinePreformatted(const nsIContent& aContent); 548 549 /** 550 * IsOnlyNewLinePreformatted() checks whether the linefeed characters are 551 * preformated but white-spaces are collapsed, or otherwise. I.e., this 552 * returns true only when `white-space:pre-line`. 553 */ 554 static bool IsOnlyNewLinePreformatted(const nsIContent& aContent); 555 556 /** 557 * Helper method for `AppendString()` and `AppendSubString()`. This should 558 * be called only when `aText` is in a password field. This method masks 559 * A part of or all of `aText` (`aStartOffsetInText` and later) should've 560 * been copied (apppended) to `aString`. `aStartOffsetInString` is where 561 * the password was appended into `aString`. 562 */ 563 static void MaskString(nsString& aString, const dom::Text& aTextNode, 564 uint32_t aStartOffsetInString, 565 uint32_t aStartOffsetInText); 566 GetTagNameAtom(const nsAString & aTagName)567 static nsStaticAtom* GetTagNameAtom(const nsAString& aTagName) { 568 if (aTagName.IsEmpty()) { 569 return nullptr; 570 } 571 nsAutoString lowerTagName; 572 nsContentUtils::ASCIIToLower(aTagName, lowerTagName); 573 return NS_GetStaticAtom(lowerTagName); 574 } 575 GetAttributeAtom(const nsAString & aAttribute)576 static nsStaticAtom* GetAttributeAtom(const nsAString& aAttribute) { 577 if (aAttribute.IsEmpty()) { 578 return nullptr; // Don't use nsGkAtoms::_empty for attribute. 579 } 580 return NS_GetStaticAtom(aAttribute); 581 } 582 583 /** 584 * Helper method for deletion. When this returns true, Selection will be 585 * computed with nsFrameSelection that also requires flushed layout 586 * information. 587 */ 588 template <typename SelectionOrAutoRangeArray> IsFrameSelectionRequiredToExtendSelection(nsIEditor::EDirection aDirectionAndAmount,SelectionOrAutoRangeArray & aSelectionOrAutoRangeArray)589 static bool IsFrameSelectionRequiredToExtendSelection( 590 nsIEditor::EDirection aDirectionAndAmount, 591 SelectionOrAutoRangeArray& aSelectionOrAutoRangeArray) { 592 switch (aDirectionAndAmount) { 593 case nsIEditor::eNextWord: 594 case nsIEditor::ePreviousWord: 595 case nsIEditor::eToBeginningOfLine: 596 case nsIEditor::eToEndOfLine: 597 return true; 598 case nsIEditor::ePrevious: 599 case nsIEditor::eNext: 600 return aSelectionOrAutoRangeArray.IsCollapsed(); 601 default: 602 return false; 603 } 604 } 605 606 /** 607 * Returns true if aSelection includes the point in aParentContent. 608 */ 609 static bool IsPointInSelection(const Selection& aSelection, 610 const nsINode& aParentNode, uint32_t aOffset); 611 612 /** 613 * Create an nsITransferable instance which has kUnicodeMime and 614 * kMozTextInternal flavors. 615 */ 616 static Result<nsCOMPtr<nsITransferable>, nsresult> 617 CreateTransferableForPlainText(const dom::Document& aDocument); 618 }; 619 620 } // namespace mozilla 621 622 #endif // #ifndef mozilla_EditorUtils_h 623