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 "EditorUtils.h"
7
8 #include "gfxFontUtils.h"
9 #include "WSRunObject.h"
10 #include "mozilla/ComputedStyle.h"
11 #include "mozilla/ContentIterator.h"
12 #include "mozilla/EditorDOMPoint.h"
13 #include "mozilla/HTMLEditor.h"
14 #include "mozilla/OwningNonNull.h"
15 #include "mozilla/TextEditor.h"
16 #include "mozilla/dom/Document.h"
17 #include "mozilla/dom/HTMLBRElement.h"
18 #include "mozilla/dom/Selection.h"
19 #include "mozilla/dom/Text.h"
20 #include "nsContentUtils.h"
21 #include "nsComponentManagerUtils.h"
22 #include "nsComputedDOMStyle.h"
23 #include "nsError.h"
24 #include "nsFrameSelection.h"
25 #include "nsIContent.h"
26 #include "nsIInterfaceRequestorUtils.h"
27 #include "nsILoadContext.h"
28 #include "nsINode.h"
29 #include "nsITransferable.h"
30 #include "nsRange.h"
31 #include "nsStyleStruct.h"
32 #include "nsTextFragment.h"
33
34 class nsISupports;
35
36 namespace mozilla {
37
38 using namespace dom;
39
40 template void DOMIterator::AppendAllNodesToArray(
41 nsTArray<OwningNonNull<nsIContent>>& aArrayOfNodes) const;
42 template void DOMIterator::AppendAllNodesToArray(
43 nsTArray<OwningNonNull<HTMLBRElement>>& aArrayOfNodes) const;
44 template void DOMIterator::AppendNodesToArray(
45 BoolFunctor aFunctor, nsTArray<OwningNonNull<nsIContent>>& aArrayOfNodes,
46 void* aClosure) const;
47 template void DOMIterator::AppendNodesToArray(
48 BoolFunctor aFunctor, nsTArray<OwningNonNull<Element>>& aArrayOfNodes,
49 void* aClosure) const;
50 template void DOMIterator::AppendNodesToArray(
51 BoolFunctor aFunctor, nsTArray<OwningNonNull<Text>>& aArrayOfNodes,
52 void* aClosure) const;
53
54 /******************************************************************************
55 * mozilla::EditActionResult
56 *****************************************************************************/
57
operator |=(const MoveNodeResult & aMoveNodeResult)58 EditActionResult& EditActionResult::operator|=(
59 const MoveNodeResult& aMoveNodeResult) {
60 mHandled |= aMoveNodeResult.Handled();
61 // When both result are same, keep the result.
62 if (mRv == aMoveNodeResult.Rv()) {
63 return *this;
64 }
65 // If one of the result is NS_ERROR_EDITOR_DESTROYED, use it since it's
66 // the most important error code for editor.
67 if (EditorDestroyed() || aMoveNodeResult.EditorDestroyed()) {
68 mRv = NS_ERROR_EDITOR_DESTROYED;
69 return *this;
70 }
71 // If aMoveNodeResult hasn't been set explicit nsresult value, keep current
72 // result.
73 if (aMoveNodeResult.Rv() == NS_ERROR_NOT_INITIALIZED) {
74 return *this;
75 }
76 // If one of the results is error, use NS_ERROR_FAILURE.
77 if (Failed() || aMoveNodeResult.Failed()) {
78 mRv = NS_ERROR_FAILURE;
79 return *this;
80 }
81 // Otherwise, use generic success code, NS_OK.
82 mRv = NS_OK;
83 return *this;
84 }
85
86 /******************************************************************************
87 * mozilla::AutoRangeArray
88 *****************************************************************************/
89
90 // static
IsEditableRange(const dom::AbstractRange & aRange,const Element & aEditingHost)91 bool AutoRangeArray::IsEditableRange(const dom::AbstractRange& aRange,
92 const Element& aEditingHost) {
93 // TODO: Perhaps, we should check whether the start/end boundaries are
94 // first/last point of non-editable element.
95 // See https://github.com/w3c/editing/issues/283#issuecomment-788654850
96 EditorRawDOMPoint atStart(aRange.StartRef());
97 const bool isStartEditable =
98 atStart.IsInContentNode() &&
99 EditorUtils::IsEditableContent(*atStart.ContainerAsContent(),
100 EditorUtils::EditorType::HTML) &&
101 !HTMLEditUtils::IsNonEditableReplacedContent(
102 *atStart.ContainerAsContent());
103 if (!isStartEditable) {
104 return false;
105 }
106
107 if (!aRange.Collapsed()) {
108 EditorRawDOMPoint atEnd(aRange.EndRef());
109 const bool isEndEditable =
110 atEnd.IsInContentNode() &&
111 EditorUtils::IsEditableContent(*atEnd.ContainerAsContent(),
112 EditorUtils::EditorType::HTML) &&
113 !HTMLEditUtils::IsNonEditableReplacedContent(
114 *atEnd.ContainerAsContent());
115 if (!isEndEditable) {
116 return false;
117 }
118
119 // Now, both start and end points are editable, but if they are in
120 // different editing host, we cannot edit the range.
121 if (atStart.ContainerAsContent() != atEnd.ContainerAsContent() &&
122 atStart.ContainerAsContent()->GetEditingHost() !=
123 atEnd.ContainerAsContent()->GetEditingHost()) {
124 return false;
125 }
126 }
127
128 // HTMLEditor does not support modifying outside `<body>` element for now.
129 nsINode* commonAncestor = aRange.GetClosestCommonInclusiveAncestor();
130 return commonAncestor && commonAncestor->IsContent() &&
131 commonAncestor->IsInclusiveDescendantOf(&aEditingHost);
132 }
133
EnsureOnlyEditableRanges(const Element & aEditingHost)134 void AutoRangeArray::EnsureOnlyEditableRanges(const Element& aEditingHost) {
135 for (size_t i = mRanges.Length(); i > 0; i--) {
136 const OwningNonNull<nsRange>& range = mRanges[i - 1];
137 if (!AutoRangeArray::IsEditableRange(range, aEditingHost)) {
138 mRanges.RemoveElementAt(i - 1);
139 }
140 }
141 mAnchorFocusRange = mRanges.IsEmpty() ? nullptr : mRanges.LastElement().get();
142 }
143
144 Result<nsIEditor::EDirection, nsresult>
ExtendAnchorFocusRangeFor(const EditorBase & aEditorBase,nsIEditor::EDirection aDirectionAndAmount)145 AutoRangeArray::ExtendAnchorFocusRangeFor(
146 const EditorBase& aEditorBase, nsIEditor::EDirection aDirectionAndAmount) {
147 MOZ_ASSERT(aEditorBase.IsEditActionDataAvailable());
148 MOZ_ASSERT(mAnchorFocusRange);
149 MOZ_ASSERT(mAnchorFocusRange->IsPositioned());
150 MOZ_ASSERT(mAnchorFocusRange->StartRef().IsSet());
151 MOZ_ASSERT(mAnchorFocusRange->EndRef().IsSet());
152
153 if (!EditorUtils::IsFrameSelectionRequiredToExtendSelection(
154 aDirectionAndAmount, *this)) {
155 return aDirectionAndAmount;
156 }
157
158 if (NS_WARN_IF(!aEditorBase.SelectionRef().RangeCount())) {
159 return Err(NS_ERROR_FAILURE);
160 }
161
162 // At this point, the anchor-focus ranges must match for bidi information.
163 // See `EditorBase::AutoCaretBidiLevelManager`.
164 MOZ_ASSERT(aEditorBase.SelectionRef().GetAnchorFocusRange()->StartRef() ==
165 mAnchorFocusRange->StartRef());
166 MOZ_ASSERT(aEditorBase.SelectionRef().GetAnchorFocusRange()->EndRef() ==
167 mAnchorFocusRange->EndRef());
168
169 RefPtr<nsFrameSelection> frameSelection =
170 aEditorBase.SelectionRef().GetFrameSelection();
171 if (NS_WARN_IF(!frameSelection)) {
172 return Err(NS_ERROR_NOT_INITIALIZED);
173 }
174
175 RefPtr<Element> editingHost;
176 if (aEditorBase.IsHTMLEditor()) {
177 editingHost = aEditorBase.AsHTMLEditor()->GetActiveEditingHost();
178 if (!editingHost) {
179 return Err(NS_ERROR_FAILURE);
180 }
181 }
182
183 Result<RefPtr<nsRange>, nsresult> result(NS_ERROR_UNEXPECTED);
184 nsIEditor::EDirection directionAndAmountResult = aDirectionAndAmount;
185 switch (aDirectionAndAmount) {
186 case nsIEditor::eNextWord:
187 result = frameSelection->CreateRangeExtendedToNextWordBoundary<nsRange>();
188 if (NS_WARN_IF(aEditorBase.Destroyed())) {
189 return Err(NS_ERROR_EDITOR_DESTROYED);
190 }
191 NS_WARNING_ASSERTION(
192 result.isOk(),
193 "nsFrameSelection::CreateRangeExtendedToNextWordBoundary() failed");
194 // DeleteSelectionWithTransaction() doesn't handle these actions
195 // because it's inside batching, so don't confuse it:
196 directionAndAmountResult = nsIEditor::eNone;
197 break;
198 case nsIEditor::ePreviousWord:
199 result =
200 frameSelection->CreateRangeExtendedToPreviousWordBoundary<nsRange>();
201 if (NS_WARN_IF(aEditorBase.Destroyed())) {
202 return Err(NS_ERROR_EDITOR_DESTROYED);
203 }
204 NS_WARNING_ASSERTION(
205 result.isOk(),
206 "nsFrameSelection::CreateRangeExtendedToPreviousWordBoundary() "
207 "failed");
208 // DeleteSelectionWithTransaction() doesn't handle these actions
209 // because it's inside batching, so don't confuse it:
210 directionAndAmountResult = nsIEditor::eNone;
211 break;
212 case nsIEditor::eNext:
213 result =
214 frameSelection
215 ->CreateRangeExtendedToNextGraphemeClusterBoundary<nsRange>();
216 if (NS_WARN_IF(aEditorBase.Destroyed())) {
217 return Err(NS_ERROR_EDITOR_DESTROYED);
218 }
219 NS_WARNING_ASSERTION(result.isOk(),
220 "nsFrameSelection::"
221 "CreateRangeExtendedToNextGraphemeClusterBoundary() "
222 "failed");
223 // Don't set directionAndAmount to eNone (see Bug 502259)
224 break;
225 case nsIEditor::ePrevious: {
226 // Only extend the selection where the selection is after a UTF-16
227 // surrogate pair or a variation selector.
228 // For other cases we don't want to do that, in order
229 // to make sure that pressing backspace will only delete the last
230 // typed character.
231 // XXX This is odd if the previous one is a sequence for a grapheme
232 // cluster.
233 EditorDOMPoint atStartOfSelection(GetStartPointOfFirstRange());
234 if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
235 return Err(NS_ERROR_FAILURE);
236 }
237
238 // node might be anonymous DIV, so we find better text node
239 EditorRawDOMPoint insertionPoint =
240 aEditorBase.FindBetterInsertionPoint(atStartOfSelection);
241 if (!insertionPoint.IsSet()) {
242 NS_WARNING(
243 "EditorBase::FindBetterInsertionPoint() failed, but ignored");
244 return aDirectionAndAmount;
245 }
246
247 if (!insertionPoint.IsInTextNode()) {
248 return aDirectionAndAmount;
249 }
250
251 const nsTextFragment* data =
252 &insertionPoint.GetContainerAsText()->TextFragment();
253 uint32_t offset = insertionPoint.Offset();
254 if (!(offset > 1 &&
255 data->IsLowSurrogateFollowingHighSurrogateAt(offset - 1)) &&
256 !(offset > 0 &&
257 gfxFontUtils::IsVarSelector(data->CharAt(offset - 1)))) {
258 return aDirectionAndAmount;
259 }
260 // Different from the `eNext` case, we look for character boundary.
261 // I'm not sure whether this inconsistency between "Delete" and
262 // "Backspace" is intentional or not.
263 result = frameSelection
264 ->CreateRangeExtendedToPreviousCharacterBoundary<nsRange>();
265 if (NS_WARN_IF(aEditorBase.Destroyed())) {
266 return Err(NS_ERROR_EDITOR_DESTROYED);
267 }
268 NS_WARNING_ASSERTION(
269 result.isOk(),
270 "nsFrameSelection::"
271 "CreateRangeExtendedToPreviousGraphemeClusterBoundary() failed");
272 break;
273 }
274 case nsIEditor::eToBeginningOfLine:
275 result =
276 frameSelection->CreateRangeExtendedToPreviousHardLineBreak<nsRange>();
277 if (NS_WARN_IF(aEditorBase.Destroyed())) {
278 return Err(NS_ERROR_EDITOR_DESTROYED);
279 }
280 NS_WARNING_ASSERTION(
281 result.isOk(),
282 "nsFrameSelection::CreateRangeExtendedToPreviousHardLineBreak() "
283 "failed");
284 directionAndAmountResult = nsIEditor::eNone;
285 break;
286 case nsIEditor::eToEndOfLine:
287 result =
288 frameSelection->CreateRangeExtendedToNextHardLineBreak<nsRange>();
289 if (NS_WARN_IF(aEditorBase.Destroyed())) {
290 return Err(NS_ERROR_EDITOR_DESTROYED);
291 }
292 NS_WARNING_ASSERTION(
293 result.isOk(),
294 "nsFrameSelection::CreateRangeExtendedToNextHardLineBreak() failed");
295 directionAndAmountResult = nsIEditor::eNext;
296 break;
297 default:
298 return aDirectionAndAmount;
299 }
300
301 if (result.isErr()) {
302 return Err(result.inspectErr());
303 }
304 RefPtr<nsRange> extendedRange(result.unwrap().forget());
305 if (!extendedRange || NS_WARN_IF(!extendedRange->IsPositioned())) {
306 NS_WARNING("Failed to extend the range, but ignored");
307 return directionAndAmountResult;
308 }
309
310 // If the new range isn't editable, keep using the original range.
311 if (aEditorBase.IsHTMLEditor() &&
312 !AutoRangeArray::IsEditableRange(*extendedRange, *editingHost)) {
313 return aDirectionAndAmount;
314 }
315
316 if (NS_WARN_IF(!frameSelection->IsValidSelectionPoint(
317 extendedRange->GetStartContainer())) ||
318 NS_WARN_IF(!frameSelection->IsValidSelectionPoint(
319 extendedRange->GetEndContainer()))) {
320 NS_WARNING("A range was extended to outer of selection limiter");
321 return Err(NS_ERROR_FAILURE);
322 }
323
324 // Swap focus/anchor range with the extended range.
325 DebugOnly<bool> found = false;
326 for (OwningNonNull<nsRange>& range : mRanges) {
327 if (range == mAnchorFocusRange) {
328 range = *extendedRange;
329 found = true;
330 break;
331 }
332 }
333 MOZ_ASSERT(found);
334 mAnchorFocusRange.swap(extendedRange);
335 return directionAndAmountResult;
336 }
337
338 Result<bool, nsresult>
ShrinkRangesIfStartFromOrEndAfterAtomicContent(const HTMLEditor & aHTMLEditor,nsIEditor::EDirection aDirectionAndAmount,IfSelectingOnlyOneAtomicContent aIfSelectingOnlyOneAtomicContent,const Element * aEditingHost)339 AutoRangeArray::ShrinkRangesIfStartFromOrEndAfterAtomicContent(
340 const HTMLEditor& aHTMLEditor, nsIEditor::EDirection aDirectionAndAmount,
341 IfSelectingOnlyOneAtomicContent aIfSelectingOnlyOneAtomicContent,
342 const Element* aEditingHost) {
343 if (IsCollapsed()) {
344 return false;
345 }
346
347 switch (aDirectionAndAmount) {
348 case nsIEditor::eNext:
349 case nsIEditor::eNextWord:
350 case nsIEditor::ePrevious:
351 case nsIEditor::ePreviousWord:
352 break;
353 default:
354 return false;
355 }
356
357 bool changed = false;
358 for (auto& range : mRanges) {
359 MOZ_ASSERT(!range->IsInSelection(),
360 "Changing range in selection may cause running script");
361 Result<bool, nsresult> result =
362 WSRunScanner::ShrinkRangeIfStartsFromOrEndsAfterAtomicContent(
363 aHTMLEditor, range, aEditingHost);
364 if (result.isErr()) {
365 NS_WARNING(
366 "WSRunScanner::ShrinkRangeIfStartsFromOrEndsAfterAtomicContent() "
367 "failed");
368 return Err(result.inspectErr());
369 }
370 changed |= result.inspect();
371 }
372
373 if (mRanges.Length() == 1 && aIfSelectingOnlyOneAtomicContent ==
374 IfSelectingOnlyOneAtomicContent::Collapse) {
375 MOZ_ASSERT(mRanges[0].get() == mAnchorFocusRange.get());
376 if (mAnchorFocusRange->GetStartContainer() ==
377 mAnchorFocusRange->GetEndContainer() &&
378 mAnchorFocusRange->GetChildAtStartOffset() &&
379 mAnchorFocusRange->StartRef().GetNextSiblingOfChildAtOffset() ==
380 mAnchorFocusRange->GetChildAtEndOffset()) {
381 mAnchorFocusRange->Collapse(aDirectionAndAmount == nsIEditor::eNext ||
382 aDirectionAndAmount == nsIEditor::eNextWord);
383 changed = true;
384 }
385 }
386
387 return changed;
388 }
389
390 /******************************************************************************
391 * some helper classes for iterating the dom tree
392 *****************************************************************************/
393
DOMIterator(nsINode & aNode)394 DOMIterator::DOMIterator(nsINode& aNode) : mIter(&mPostOrderIter) {
395 DebugOnly<nsresult> rv = mIter->Init(&aNode);
396 MOZ_ASSERT(NS_SUCCEEDED(rv));
397 }
398
Init(nsRange & aRange)399 nsresult DOMIterator::Init(nsRange& aRange) { return mIter->Init(&aRange); }
400
Init(const RawRangeBoundary & aStartRef,const RawRangeBoundary & aEndRef)401 nsresult DOMIterator::Init(const RawRangeBoundary& aStartRef,
402 const RawRangeBoundary& aEndRef) {
403 return mIter->Init(aStartRef, aEndRef);
404 }
405
DOMIterator()406 DOMIterator::DOMIterator() : mIter(&mPostOrderIter) {}
407
408 template <class NodeClass>
AppendAllNodesToArray(nsTArray<OwningNonNull<NodeClass>> & aArrayOfNodes) const409 void DOMIterator::AppendAllNodesToArray(
410 nsTArray<OwningNonNull<NodeClass>>& aArrayOfNodes) const {
411 for (; !mIter->IsDone(); mIter->Next()) {
412 if (NodeClass* node = NodeClass::FromNode(mIter->GetCurrentNode())) {
413 aArrayOfNodes.AppendElement(*node);
414 }
415 }
416 }
417
418 template <class NodeClass>
AppendNodesToArray(BoolFunctor aFunctor,nsTArray<OwningNonNull<NodeClass>> & aArrayOfNodes,void * aClosure) const419 void DOMIterator::AppendNodesToArray(
420 BoolFunctor aFunctor, nsTArray<OwningNonNull<NodeClass>>& aArrayOfNodes,
421 void* aClosure /* = nullptr */) const {
422 for (; !mIter->IsDone(); mIter->Next()) {
423 NodeClass* node = NodeClass::FromNode(mIter->GetCurrentNode());
424 if (node && aFunctor(*node, aClosure)) {
425 aArrayOfNodes.AppendElement(*node);
426 }
427 }
428 }
429
DOMSubtreeIterator()430 DOMSubtreeIterator::DOMSubtreeIterator() : DOMIterator() {
431 mIter = &mSubtreeIter;
432 }
433
Init(nsRange & aRange)434 nsresult DOMSubtreeIterator::Init(nsRange& aRange) {
435 return mIter->Init(&aRange);
436 }
437
438 /******************************************************************************
439 * some general purpose editor utils
440 *****************************************************************************/
441
IsDescendantOf(const nsINode & aNode,const nsINode & aParent,EditorRawDOMPoint * aOutPoint)442 bool EditorUtils::IsDescendantOf(const nsINode& aNode, const nsINode& aParent,
443 EditorRawDOMPoint* aOutPoint /* = nullptr */) {
444 if (aOutPoint) {
445 aOutPoint->Clear();
446 }
447
448 if (&aNode == &aParent) {
449 return false;
450 }
451
452 for (const nsINode* node = &aNode; node; node = node->GetParentNode()) {
453 if (node->GetParentNode() == &aParent) {
454 if (aOutPoint) {
455 MOZ_ASSERT(node->IsContent());
456 aOutPoint->Set(node->AsContent());
457 }
458 return true;
459 }
460 }
461
462 return false;
463 }
464
IsDescendantOf(const nsINode & aNode,const nsINode & aParent,EditorDOMPoint * aOutPoint)465 bool EditorUtils::IsDescendantOf(const nsINode& aNode, const nsINode& aParent,
466 EditorDOMPoint* aOutPoint) {
467 MOZ_ASSERT(aOutPoint);
468 aOutPoint->Clear();
469 if (&aNode == &aParent) {
470 return false;
471 }
472
473 for (const nsINode* node = &aNode; node; node = node->GetParentNode()) {
474 if (node->GetParentNode() == &aParent) {
475 MOZ_ASSERT(node->IsContent());
476 aOutPoint->Set(node->AsContent());
477 return true;
478 }
479 }
480
481 return false;
482 }
483
484 // static
MaskString(nsString & aString,Text * aText,uint32_t aStartOffsetInString,uint32_t aStartOffsetInText)485 void EditorUtils::MaskString(nsString& aString, Text* aText,
486 uint32_t aStartOffsetInString,
487 uint32_t aStartOffsetInText) {
488 MOZ_ASSERT(aText->HasFlag(NS_MAYBE_MASKED));
489 MOZ_ASSERT(aStartOffsetInString == 0 || aStartOffsetInText == 0);
490
491 uint32_t unmaskStart = UINT32_MAX, unmaskLength = 0;
492 TextEditor* textEditor =
493 nsContentUtils::GetTextEditorFromAnonymousNodeWithoutCreation(aText);
494 if (textEditor && textEditor->UnmaskedLength() > 0) {
495 unmaskStart = textEditor->UnmaskedStart();
496 unmaskLength = textEditor->UnmaskedLength();
497 // If text is copied from after unmasked range, we can treat this case
498 // as mask all.
499 if (aStartOffsetInText >= unmaskStart + unmaskLength) {
500 unmaskLength = 0;
501 unmaskStart = UINT32_MAX;
502 } else {
503 // If text is copied from middle of unmasked range, reduce the length
504 // and adjust start offset.
505 if (aStartOffsetInText > unmaskStart) {
506 unmaskLength = unmaskStart + unmaskLength - aStartOffsetInText;
507 unmaskStart = 0;
508 }
509 // If text is copied from before start of unmasked range, just adjust
510 // the start offset.
511 else {
512 unmaskStart -= aStartOffsetInText;
513 }
514 // Make the range is in the string.
515 unmaskStart += aStartOffsetInString;
516 }
517 }
518
519 const char16_t kPasswordMask = TextEditor::PasswordMask();
520 for (uint32_t i = aStartOffsetInString; i < aString.Length(); ++i) {
521 bool isSurrogatePair = NS_IS_HIGH_SURROGATE(aString.CharAt(i)) &&
522 i < aString.Length() - 1 &&
523 NS_IS_LOW_SURROGATE(aString.CharAt(i + 1));
524 if (i < unmaskStart || i >= unmaskStart + unmaskLength) {
525 if (isSurrogatePair) {
526 aString.SetCharAt(kPasswordMask, i);
527 aString.SetCharAt(kPasswordMask, i + 1);
528 } else {
529 aString.SetCharAt(kPasswordMask, i);
530 }
531 }
532
533 // Skip the following low surrogate.
534 if (isSurrogatePair) {
535 ++i;
536 }
537 }
538 }
539
540 // static
IsContentPreformatted(nsIContent & aContent)541 bool EditorUtils::IsContentPreformatted(nsIContent& aContent) {
542 // Look at the node (and its parent if it's not an element), and grab its
543 // ComputedStyle.
544 Element* element = aContent.GetAsElementOrParentElement();
545 if (!element) {
546 return false;
547 }
548
549 RefPtr<ComputedStyle> elementStyle =
550 nsComputedDOMStyle::GetComputedStyleNoFlush(element, nullptr);
551 if (!elementStyle) {
552 // Consider nodes without a ComputedStyle to be NOT preformatted:
553 // For instance, this is true of JS tags inside the body (which show
554 // up as #text nodes but have no ComputedStyle).
555 return false;
556 }
557
558 return elementStyle->StyleText()->WhiteSpaceIsSignificant();
559 }
560
IsPointInSelection(const Selection & aSelection,const nsINode & aParentNode,uint32_t aOffset)561 bool EditorUtils::IsPointInSelection(const Selection& aSelection,
562 const nsINode& aParentNode,
563 uint32_t aOffset) {
564 if (aSelection.IsCollapsed()) {
565 return false;
566 }
567
568 uint32_t rangeCount = aSelection.RangeCount();
569 for (uint32_t i = 0; i < rangeCount; i++) {
570 RefPtr<const nsRange> range = aSelection.GetRangeAt(i);
571 if (!range) {
572 // Don't bail yet, iterate through them all
573 continue;
574 }
575
576 IgnoredErrorResult ignoredError;
577 bool nodeIsInSelection =
578 range->IsPointInRange(aParentNode, aOffset, ignoredError) &&
579 !ignoredError.Failed();
580 NS_WARNING_ASSERTION(!ignoredError.Failed(),
581 "nsRange::IsPointInRange() failed");
582
583 // Done when we find a range that we are in
584 if (nodeIsInSelection) {
585 return true;
586 }
587 }
588
589 return false;
590 }
591
592 // static
593 Result<nsCOMPtr<nsITransferable>, nsresult>
CreateTransferableForPlainText(const Document & aDocument)594 EditorUtils::CreateTransferableForPlainText(const Document& aDocument) {
595 // Create generic Transferable for getting the data
596 nsresult rv;
597 nsCOMPtr<nsITransferable> transferable =
598 do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
599 if (NS_FAILED(rv)) {
600 NS_WARNING("do_CreateInstance() failed to create nsITransferable instance");
601 return Err(rv);
602 }
603
604 if (!transferable) {
605 NS_WARNING("do_CreateInstance() returned nullptr, but ignored");
606 return nsCOMPtr<nsITransferable>();
607 }
608
609 DebugOnly<nsresult> rvIgnored =
610 transferable->Init(aDocument.GetLoadContext());
611 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
612 "nsITransferable::Init() failed, but ignored");
613
614 rvIgnored = transferable->AddDataFlavor(kUnicodeMime);
615 NS_WARNING_ASSERTION(
616 NS_SUCCEEDED(rvIgnored),
617 "nsITransferable::AddDataFlavor(kUnicodeMime) failed, but ignored");
618 rvIgnored = transferable->AddDataFlavor(kMozTextInternal);
619 NS_WARNING_ASSERTION(
620 NS_SUCCEEDED(rvIgnored),
621 "nsITransferable::AddDataFlavor(kMozTextInternal) failed, but ignored");
622 return transferable;
623 }
624
625 } // namespace mozilla
626