nsDocumentEncoder.cpp (78044B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 * Object that can be used to serialize selections, ranges, or nodes 9 * to strings in a gazillion different ways. 10 */ 11 12 #include <utility> 13 14 #include "mozilla/Encoding.h" 15 #include "mozilla/IntegerRange.h" 16 #include "mozilla/Maybe.h" 17 #include "mozilla/ScopeExit.h" 18 #include "mozilla/StringBuffer.h" 19 #include "mozilla/UniquePtr.h" 20 #include "mozilla/dom/AbstractRange.h" 21 #include "mozilla/dom/Comment.h" 22 #include "mozilla/dom/Document.h" 23 #include "mozilla/dom/DocumentType.h" 24 #include "mozilla/dom/Element.h" 25 #include "mozilla/dom/HTMLBRElement.h" 26 #include "mozilla/dom/ProcessingInstruction.h" 27 #include "mozilla/dom/Selection.h" 28 #include "mozilla/dom/ShadowRoot.h" 29 #include "mozilla/dom/Text.h" 30 #include "nsCOMPtr.h" 31 #include "nsCRT.h" 32 #include "nsComponentManagerUtils.h" 33 #include "nsContentUtils.h" 34 #include "nsElementTable.h" 35 #include "nsGkAtoms.h" 36 #include "nsHTMLDocument.h" 37 #include "nsIContent.h" 38 #include "nsIContentInlines.h" 39 #include "nsIContentSerializer.h" 40 #include "nsIDocumentEncoder.h" 41 #include "nsIFrame.h" 42 #include "nsINode.h" 43 #include "nsIOutputStream.h" 44 #include "nsIScriptContext.h" 45 #include "nsIScriptGlobalObject.h" 46 #include "nsISupports.h" 47 #include "nsITransferable.h" 48 #include "nsLayoutUtils.h" 49 #include "nsMimeTypes.h" 50 #include "nsRange.h" 51 #include "nsReadableUtils.h" 52 #include "nsTArray.h" 53 #include "nsUnicharUtils.h" 54 #include "nscore.h" 55 56 using namespace mozilla; 57 using namespace mozilla::dom; 58 59 enum nsRangeIterationDirection { kDirectionOut = -1, kDirectionIn = 1 }; 60 61 class TextStreamer { 62 public: 63 /** 64 * @param aStream Will be kept alive by the TextStreamer. 65 * @param aUnicodeEncoder Needs to be non-nullptr. 66 */ 67 TextStreamer(nsIOutputStream& aStream, UniquePtr<Encoder> aUnicodeEncoder, 68 bool aIsPlainText, nsAString& aOutputBuffer); 69 70 /** 71 * String will be truncated if it is written to stream. 72 */ 73 nsresult FlushIfStringLongEnough(); 74 75 /** 76 * String will be truncated. 77 */ 78 nsresult ForceFlush(); 79 80 private: 81 const static uint32_t kMaxLengthBeforeFlush = 1024; 82 83 const static uint32_t kEncoderBufferSizeInBytes = 4096; 84 85 nsresult EncodeAndWrite(); 86 87 nsresult EncodeAndWriteAndTruncate(); 88 89 const nsCOMPtr<nsIOutputStream> mStream; 90 const UniquePtr<Encoder> mUnicodeEncoder; 91 const bool mIsPlainText; 92 nsAString& mOutputBuffer; 93 }; 94 95 TextStreamer::TextStreamer(nsIOutputStream& aStream, 96 UniquePtr<Encoder> aUnicodeEncoder, 97 bool aIsPlainText, nsAString& aOutputBuffer) 98 : mStream{&aStream}, 99 mUnicodeEncoder(std::move(aUnicodeEncoder)), 100 mIsPlainText(aIsPlainText), 101 mOutputBuffer(aOutputBuffer) { 102 MOZ_ASSERT(mUnicodeEncoder); 103 } 104 105 nsresult TextStreamer::FlushIfStringLongEnough() { 106 nsresult rv = NS_OK; 107 108 if (mOutputBuffer.Length() > kMaxLengthBeforeFlush) { 109 rv = EncodeAndWriteAndTruncate(); 110 } 111 112 return rv; 113 } 114 115 nsresult TextStreamer::ForceFlush() { return EncodeAndWriteAndTruncate(); } 116 117 nsresult TextStreamer::EncodeAndWrite() { 118 if (mOutputBuffer.IsEmpty()) { 119 return NS_OK; 120 } 121 122 uint8_t buffer[kEncoderBufferSizeInBytes]; 123 auto src = Span(mOutputBuffer); 124 auto bufferSpan = Span(buffer); 125 // Reserve space for terminator 126 auto dst = bufferSpan.To(bufferSpan.Length() - 1); 127 for (;;) { 128 uint32_t result; 129 size_t read; 130 size_t written; 131 if (mIsPlainText) { 132 std::tie(result, read, written) = 133 mUnicodeEncoder->EncodeFromUTF16WithoutReplacement(src, dst, false); 134 if (result != kInputEmpty && result != kOutputFull) { 135 // There's always room for one byte in the case of 136 // an unmappable character, because otherwise 137 // we'd have gotten `kOutputFull`. 138 dst[written++] = '?'; 139 } 140 } else { 141 std::tie(result, read, written, std::ignore) = 142 mUnicodeEncoder->EncodeFromUTF16(src, dst, false); 143 } 144 src = src.From(read); 145 // Sadly, we still have test cases that implement nsIOutputStream in JS, so 146 // the buffer needs to be zero-terminated for XPConnect to do its thing. 147 // See bug 170416. 148 bufferSpan[written] = 0; 149 uint32_t streamWritten; 150 nsresult rv = mStream->Write(reinterpret_cast<char*>(dst.Elements()), 151 written, &streamWritten); 152 if (NS_FAILED(rv)) { 153 return rv; 154 } 155 if (result == kInputEmpty) { 156 return NS_OK; 157 } 158 } 159 } 160 161 nsresult TextStreamer::EncodeAndWriteAndTruncate() { 162 const nsresult rv = EncodeAndWrite(); 163 mOutputBuffer.Truncate(); 164 return rv; 165 } 166 167 /** 168 * The scope may be limited to either a selection, range, or node. 169 */ 170 class EncodingScope { 171 public: 172 /** 173 * @return true, iff the scope is limited to a selection, range or node. 174 */ 175 bool IsLimited() const; 176 177 RefPtr<Selection> mSelection; 178 RefPtr<nsRange> mRange; 179 nsCOMPtr<nsINode> mNode; 180 bool mNodeIsContainer = false; 181 }; 182 183 bool EncodingScope::IsLimited() const { return mSelection || mRange || mNode; } 184 185 struct RangeBoundariesInclusiveAncestorsAndOffsets { 186 /** 187 * https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor. 188 */ 189 using InclusiveAncestors = AutoTArray<nsIContent*, 8>; 190 191 /** 192 * https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor. 193 */ 194 using InclusiveAncestorsOffsets = AutoTArray<Maybe<uint32_t>, 8>; 195 196 // The first node is the range's boundary node, the following ones the 197 // ancestors. 198 InclusiveAncestors mInclusiveAncestorsOfStart; 199 // The first offset represents where at the boundary node the range starts. 200 // Each other offset is the index of the child relative to its parent. 201 InclusiveAncestorsOffsets mInclusiveAncestorsOffsetsOfStart; 202 203 // The first node is the range's boundary node, the following one the 204 // ancestors. 205 InclusiveAncestors mInclusiveAncestorsOfEnd; 206 // The first offset represents where at the boundary node the range ends. 207 // Each other offset is the index of the child relative to its parent. 208 InclusiveAncestorsOffsets mInclusiveAncestorsOffsetsOfEnd; 209 }; 210 211 struct ContextInfoDepth { 212 uint32_t mStart = 0; 213 uint32_t mEnd = 0; 214 }; 215 216 class nsDocumentEncoder : public nsIDocumentEncoder { 217 protected: 218 class RangeNodeContext { 219 public: 220 virtual ~RangeNodeContext() = default; 221 222 virtual bool IncludeInContext(nsINode& aNode) const { return false; } 223 224 virtual int32_t GetImmediateContextCount( 225 const nsTArray<nsINode*>& aAncestorArray) const { 226 return -1; 227 } 228 }; 229 230 public: 231 nsDocumentEncoder(); 232 233 protected: 234 /** 235 * @param aRangeNodeContext has to be non-null. 236 */ 237 explicit nsDocumentEncoder(UniquePtr<RangeNodeContext> aRangeNodeContext); 238 239 public: 240 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 241 NS_DECL_CYCLE_COLLECTION_CLASS(nsDocumentEncoder) 242 NS_DECL_NSIDOCUMENTENCODER 243 244 protected: 245 virtual ~nsDocumentEncoder(); 246 247 void Initialize(bool aClearCachedSerializer = true, 248 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = 249 AllowRangeCrossShadowBoundary::No); 250 251 /** 252 * @param aMaxLength As described at 253 * `nsIDocumentEncodder.encodeToStringWithMaxLength`. 254 */ 255 nsresult SerializeDependingOnScope(uint32_t aMaxLength); 256 257 nsresult SerializeSelection(); 258 259 nsresult SerializeNode(); 260 261 /** 262 * @param aMaxLength As described at 263 * `nsIDocumentEncodder.encodeToStringWithMaxLength`. 264 */ 265 nsresult SerializeWholeDocument(uint32_t aMaxLength); 266 267 /** 268 * @param aFlags multiple of the flags defined in nsIDocumentEncoder.idl.o 269 */ 270 static bool IsInvisibleNodeAndShouldBeSkipped(const nsINode& aNode, 271 const uint32_t aFlags) { 272 if (aFlags & SkipInvisibleContent) { 273 // Treat the visibility of the ShadowRoot as if it were 274 // the host content. 275 // 276 // FIXME(emilio): I suspect instead of this a bunch of the GetParent() 277 // calls here should be doing GetFlattenedTreeParent, then this condition 278 // should be unreachable... 279 const nsINode* node{&aNode}; 280 if (const ShadowRoot* shadowRoot = ShadowRoot::FromNode(node)) { 281 node = shadowRoot->GetHost(); 282 } 283 284 if (node->IsContent()) { 285 nsIFrame* frame = node->AsContent()->GetPrimaryFrame(); 286 if (!frame) { 287 if (node->IsElement() && node->AsElement()->IsDisplayContents()) { 288 return false; 289 } 290 if (node->IsText()) { 291 // We have already checked that our parent is visible. 292 // 293 // FIXME(emilio): Text not assigned to a <slot> in Shadow DOM should 294 // probably return false... 295 return false; 296 } 297 if (node->IsHTMLElement(nsGkAtoms::rp)) { 298 // Ruby parentheses are part of ruby structure, hence 299 // shouldn't be stripped out even if it is not displayed. 300 return false; 301 } 302 return true; 303 } 304 if (node->IsText() && 305 (!frame->StyleVisibility()->IsVisible() || 306 frame->IsHiddenByContentVisibilityOnAnyAncestor())) { 307 return true; 308 } 309 } 310 } 311 return false; 312 } 313 314 void ReleaseDocumentReferenceAndInitialize(bool aClearCachedSerializer); 315 316 class MOZ_STACK_CLASS AutoReleaseDocumentIfNeeded final { 317 public: 318 explicit AutoReleaseDocumentIfNeeded(nsDocumentEncoder* aEncoder) 319 : mEncoder(aEncoder) {} 320 321 ~AutoReleaseDocumentIfNeeded() { 322 if (mEncoder->mFlags & RequiresReinitAfterOutput) { 323 const bool clearCachedSerializer = false; 324 mEncoder->ReleaseDocumentReferenceAndInitialize(clearCachedSerializer); 325 } 326 } 327 328 private: 329 nsDocumentEncoder* mEncoder; 330 }; 331 332 nsCOMPtr<Document> mDocument; 333 EncodingScope mEncodingScope; 334 nsCOMPtr<nsIContentSerializer> mSerializer; 335 336 Maybe<TextStreamer> mTextStreamer; 337 nsCOMPtr<nsIDocumentEncoderNodeFixup> mNodeFixup; 338 339 nsString mMimeType; 340 const Encoding* mEncoding; 341 // Multiple of the flags defined in nsIDocumentEncoder.idl. 342 uint32_t mFlags; 343 uint32_t mWrapColumn; 344 // Whether the serializer cares about being notified to scan elements to 345 // keep track of whether they are preformatted. This stores the out 346 // argument of nsIContentSerializer::Init(). 347 bool mNeedsPreformatScanning; 348 bool mIsCopying; // Set to true only while copying 349 RefPtr<StringBuffer> mCachedBuffer; 350 351 class NodeSerializer { 352 public: 353 /** 354 * @param aFlags multiple of the flags defined in nsIDocumentEncoder.idl. 355 */ 356 NodeSerializer(const bool& aNeedsPreformatScanning, 357 const nsCOMPtr<nsIContentSerializer>& aSerializer, 358 const uint32_t& aFlags, 359 const nsCOMPtr<nsIDocumentEncoderNodeFixup>& aNodeFixup, 360 Maybe<TextStreamer>& aTextStreamer) 361 : mNeedsPreformatScanning{aNeedsPreformatScanning}, 362 mSerializer{aSerializer}, 363 mFlags{aFlags}, 364 mNodeFixup{aNodeFixup}, 365 mTextStreamer{aTextStreamer} {} 366 367 nsresult SerializeNodeStart(nsINode& aOriginalNode, int32_t aStartOffset, 368 int32_t aEndOffset, 369 nsINode* aFixupNode = nullptr) const; 370 371 enum class SerializeRoot { eYes, eNo }; 372 373 nsresult SerializeToStringRecursive(nsINode* aNode, 374 SerializeRoot aSerializeRoot, 375 uint32_t aMaxLength = 0) const; 376 377 nsresult SerializeNodeEnd(nsINode& aOriginalNode, 378 nsINode* aFixupNode = nullptr) const; 379 380 [[nodiscard]] nsresult SerializeTextNode(nsINode& aNode, 381 int32_t aStartOffset, 382 int32_t aEndOffset) const; 383 384 nsresult SerializeToStringIterative(nsINode* aNode) const; 385 386 private: 387 const bool& mNeedsPreformatScanning; 388 const nsCOMPtr<nsIContentSerializer>& mSerializer; 389 // Multiple of the flags defined in nsIDocumentEncoder.idl. 390 const uint32_t& mFlags; 391 const nsCOMPtr<nsIDocumentEncoderNodeFixup>& mNodeFixup; 392 Maybe<TextStreamer>& mTextStreamer; 393 }; 394 395 NodeSerializer mNodeSerializer; 396 397 const UniquePtr<RangeNodeContext> mRangeNodeContext; 398 399 struct RangeContextSerializer final { 400 RangeContextSerializer(const RangeNodeContext& aRangeNodeContext, 401 const NodeSerializer& aNodeSerializer) 402 : mDisableContextSerialize{false}, 403 mRangeNodeContext{aRangeNodeContext}, 404 mNodeSerializer{aNodeSerializer} {} 405 406 nsresult SerializeRangeContextStart( 407 const nsTArray<nsINode*>& aAncestorArray); 408 nsresult SerializeRangeContextEnd(); 409 410 // Used when context has already been serialized for 411 // table cell selections (where parent is <tr>) 412 bool mDisableContextSerialize; 413 AutoTArray<AutoTArray<nsINode*, 8>, 8> mRangeContexts; 414 415 const RangeNodeContext& mRangeNodeContext; 416 417 private: 418 const NodeSerializer& mNodeSerializer; 419 }; 420 421 RangeContextSerializer mRangeContextSerializer; 422 423 struct RangeSerializer { 424 // @param aFlags multiple of the flags defined in nsIDocumentEncoder.idl. 425 RangeSerializer(const uint32_t& aFlags, 426 const NodeSerializer& aNodeSerializer, 427 RangeContextSerializer& aRangeContextSerializer) 428 : mStartRootIndex{0}, 429 mEndRootIndex{0}, 430 mHaltRangeHint{false}, 431 mFlags{aFlags}, 432 mNodeSerializer{aNodeSerializer}, 433 mRangeContextSerializer{aRangeContextSerializer} {} 434 435 void Initialize(AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary); 436 437 /** 438 * @param aDepth the distance (number of `GetParent` calls) from aNode to 439 * aRange's closest common inclusive ancestor. 440 */ 441 nsresult SerializeRangeNodes(const nsRange* aRange, nsINode* aNode, 442 int32_t aDepth); 443 444 /** 445 * Serialize aContent's children from aStartOffset to aEndOffset. 446 * 447 * @param aDepth the distance (number of `GetParent` calls) from aContent to 448 * aRange's closest common inclusive ancestor. 449 */ 450 [[nodiscard]] nsresult SerializeChildrenOfContent(nsIContent& aContent, 451 uint32_t aStartOffset, 452 uint32_t aEndOffset, 453 const nsRange* aRange, 454 int32_t aDepth); 455 456 nsresult SerializeRangeToString(const nsRange* aRange); 457 458 /** 459 * https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor. 460 */ 461 nsCOMPtr<nsINode> mClosestCommonInclusiveAncestorOfRange; 462 463 /** 464 * https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor. 465 */ 466 AutoTArray<nsINode*, 8> mCommonInclusiveAncestors; 467 468 ContextInfoDepth mContextInfoDepth; 469 470 private: 471 struct StartAndEndContent { 472 nsCOMPtr<nsIContent> mStart; 473 nsCOMPtr<nsIContent> mEnd; 474 }; 475 476 StartAndEndContent GetStartAndEndContentForRecursionLevel( 477 int32_t aDepth) const; 478 479 bool HasInvisibleParentAndShouldBeSkipped(nsINode& aNode) const; 480 481 nsresult SerializeNodePartiallyContainedInRange( 482 nsIContent& aContent, const StartAndEndContent& aStartAndEndContent, 483 const nsRange& aRange, int32_t aDepth); 484 485 nsresult SerializeTextNode(nsIContent& aContent, 486 const StartAndEndContent& aStartAndEndContent, 487 const nsRange& aRange) const; 488 489 RangeBoundariesInclusiveAncestorsAndOffsets 490 mRangeBoundariesInclusiveAncestorsAndOffsets; 491 int32_t mStartRootIndex; 492 int32_t mEndRootIndex; 493 bool mHaltRangeHint; 494 495 // Multiple of the flags defined in nsIDocumentEncoder.idl. 496 const uint32_t& mFlags; 497 498 const NodeSerializer& mNodeSerializer; 499 RangeContextSerializer& mRangeContextSerializer; 500 501 AllowRangeCrossShadowBoundary mAllowCrossShadowBoundary = 502 AllowRangeCrossShadowBoundary::No; 503 }; 504 505 RangeSerializer mRangeSerializer; 506 }; 507 508 void nsDocumentEncoder::RangeSerializer::Initialize( 509 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) { 510 mContextInfoDepth = {}; 511 mStartRootIndex = 0; 512 mEndRootIndex = 0; 513 mHaltRangeHint = false; 514 mClosestCommonInclusiveAncestorOfRange = nullptr; 515 mRangeBoundariesInclusiveAncestorsAndOffsets = {}; 516 mAllowCrossShadowBoundary = aAllowCrossShadowBoundary; 517 } 518 519 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocumentEncoder) 520 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE( 521 nsDocumentEncoder, ReleaseDocumentReferenceAndInitialize(true)) 522 523 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocumentEncoder) 524 NS_INTERFACE_MAP_ENTRY(nsIDocumentEncoder) 525 NS_INTERFACE_MAP_ENTRY(nsISupports) 526 NS_INTERFACE_MAP_END 527 528 NS_IMPL_CYCLE_COLLECTION( 529 nsDocumentEncoder, mDocument, mEncodingScope.mSelection, 530 mEncodingScope.mRange, mEncodingScope.mNode, mSerializer, 531 mRangeSerializer.mClosestCommonInclusiveAncestorOfRange) 532 533 nsDocumentEncoder::nsDocumentEncoder( 534 UniquePtr<RangeNodeContext> aRangeNodeContext) 535 : mEncoding(nullptr), 536 mIsCopying(false), 537 mCachedBuffer(nullptr), 538 mNodeSerializer(mNeedsPreformatScanning, mSerializer, mFlags, mNodeFixup, 539 mTextStreamer), 540 mRangeNodeContext(std::move(aRangeNodeContext)), 541 mRangeContextSerializer(*mRangeNodeContext, mNodeSerializer), 542 mRangeSerializer(mFlags, mNodeSerializer, mRangeContextSerializer) { 543 MOZ_ASSERT(mRangeNodeContext); 544 545 Initialize(); 546 mMimeType.AssignLiteral("text/plain"); 547 } 548 549 nsDocumentEncoder::nsDocumentEncoder() 550 : nsDocumentEncoder(MakeUnique<RangeNodeContext>()) {} 551 552 void nsDocumentEncoder::Initialize( 553 bool aClearCachedSerializer, 554 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) { 555 mFlags = 0; 556 mWrapColumn = 72; 557 mRangeSerializer.Initialize(aAllowCrossShadowBoundary); 558 mNeedsPreformatScanning = false; 559 mRangeContextSerializer.mDisableContextSerialize = false; 560 mEncodingScope = {}; 561 mNodeFixup = nullptr; 562 if (aClearCachedSerializer) { 563 mSerializer = nullptr; 564 } 565 } 566 567 static bool ParentIsTR(nsIContent* aContent) { 568 mozilla::dom::Element* parent = aContent->GetParentElement(); 569 if (!parent) { 570 return false; 571 } 572 return parent->IsHTMLElement(nsGkAtoms::tr); 573 } 574 575 static AllowRangeCrossShadowBoundary GetAllowRangeCrossShadowBoundary( 576 const uint32_t aFlags) { 577 return (aFlags & nsIDocumentEncoder::AllowCrossShadowBoundary) 578 ? AllowRangeCrossShadowBoundary::Yes 579 : AllowRangeCrossShadowBoundary::No; 580 } 581 582 nsresult nsDocumentEncoder::SerializeDependingOnScope(uint32_t aMaxLength) { 583 nsresult rv = NS_OK; 584 if (mEncodingScope.mSelection) { 585 rv = SerializeSelection(); 586 } else if (nsRange* range = mEncodingScope.mRange) { 587 rv = mRangeSerializer.SerializeRangeToString(range); 588 } else if (mEncodingScope.mNode) { 589 rv = SerializeNode(); 590 } else { 591 rv = SerializeWholeDocument(aMaxLength); 592 } 593 594 mEncodingScope = {}; 595 596 return rv; 597 } 598 599 nsresult nsDocumentEncoder::SerializeSelection() { 600 NS_ENSURE_TRUE(mEncodingScope.mSelection, NS_ERROR_FAILURE); 601 602 nsresult rv = NS_OK; 603 const Selection* selection = mEncodingScope.mSelection; 604 nsCOMPtr<nsINode> node; 605 nsCOMPtr<nsINode> prevNode; 606 uint32_t firstRangeStartDepth = 0; 607 const uint32_t rangeCount = selection->RangeCount(); 608 for (const uint32_t i : IntegerRange(rangeCount)) { 609 MOZ_ASSERT(selection->RangeCount() == rangeCount); 610 RefPtr<const nsRange> range = selection->GetRangeAt(i); 611 612 // Bug 236546: newlines not added when copying table cells into clipboard 613 // Each selected cell shows up as a range containing a row with a single 614 // cell get the row, compare it to previous row and emit </tr><tr> as 615 // needed Bug 137450: Problem copying/pasting a table from a web page to 616 // Excel. Each separate block of <tr></tr> produced above will be wrapped 617 // by the immediate context. This assumes that you can't select cells that 618 // are multiple selections from two tables simultaneously. 619 node = ShadowDOMSelectionHelpers::GetStartContainer( 620 range, GetAllowRangeCrossShadowBoundary(mFlags)); 621 NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); 622 if (node != prevNode) { 623 if (prevNode) { 624 rv = mNodeSerializer.SerializeNodeEnd(*prevNode); 625 NS_ENSURE_SUCCESS(rv, rv); 626 } 627 nsCOMPtr<nsIContent> content = nsIContent::FromNodeOrNull(node); 628 if (content && content->IsHTMLElement(nsGkAtoms::tr) && 629 !ParentIsTR(content)) { 630 if (!prevNode) { 631 // Went from a non-<tr> to a <tr> 632 mRangeSerializer.mCommonInclusiveAncestors.Clear(); 633 nsContentUtils::GetInclusiveAncestors( 634 node->GetParentNode(), 635 mRangeSerializer.mCommonInclusiveAncestors); 636 rv = mRangeContextSerializer.SerializeRangeContextStart( 637 mRangeSerializer.mCommonInclusiveAncestors); 638 NS_ENSURE_SUCCESS(rv, rv); 639 // Don't let SerializeRangeToString serialize the context again 640 mRangeContextSerializer.mDisableContextSerialize = true; 641 } 642 643 rv = mNodeSerializer.SerializeNodeStart(*node, 0, -1); 644 NS_ENSURE_SUCCESS(rv, rv); 645 prevNode = node; 646 } else if (prevNode) { 647 // Went from a <tr> to a non-<tr> 648 mRangeContextSerializer.mDisableContextSerialize = false; 649 650 // `mCommonInclusiveAncestors` is used in `EncodeToStringWithContext` 651 // too. Update it here to mimic the old behavior. 652 mRangeSerializer.mCommonInclusiveAncestors.Clear(); 653 nsContentUtils::GetInclusiveAncestors( 654 prevNode->GetParentNode(), 655 mRangeSerializer.mCommonInclusiveAncestors); 656 657 rv = mRangeContextSerializer.SerializeRangeContextEnd(); 658 NS_ENSURE_SUCCESS(rv, rv); 659 prevNode = nullptr; 660 } 661 } 662 663 rv = mRangeSerializer.SerializeRangeToString(range); 664 NS_ENSURE_SUCCESS(rv, rv); 665 if (i == 0) { 666 firstRangeStartDepth = mRangeSerializer.mContextInfoDepth.mStart; 667 } 668 } 669 mRangeSerializer.mContextInfoDepth.mStart = firstRangeStartDepth; 670 671 if (prevNode) { 672 rv = mNodeSerializer.SerializeNodeEnd(*prevNode); 673 NS_ENSURE_SUCCESS(rv, rv); 674 mRangeContextSerializer.mDisableContextSerialize = false; 675 676 // `mCommonInclusiveAncestors` is used in `EncodeToStringWithContext` 677 // too. Update it here to mimic the old behavior. 678 mRangeSerializer.mCommonInclusiveAncestors.Clear(); 679 nsContentUtils::GetInclusiveAncestors( 680 prevNode->GetParentNode(), mRangeSerializer.mCommonInclusiveAncestors); 681 682 rv = mRangeContextSerializer.SerializeRangeContextEnd(); 683 NS_ENSURE_SUCCESS(rv, rv); 684 } 685 686 // Just to be safe 687 mRangeContextSerializer.mDisableContextSerialize = false; 688 689 return rv; 690 } 691 692 nsresult nsDocumentEncoder::SerializeNode() { 693 NS_ENSURE_TRUE(mEncodingScope.mNode, NS_ERROR_FAILURE); 694 695 nsresult rv = NS_OK; 696 nsINode* node = mEncodingScope.mNode; 697 const bool nodeIsContainer = mEncodingScope.mNodeIsContainer; 698 if (!mNodeFixup && !(mFlags & SkipInvisibleContent) && !mTextStreamer && 699 nodeIsContainer) { 700 rv = mNodeSerializer.SerializeToStringIterative(node); 701 } else { 702 rv = mNodeSerializer.SerializeToStringRecursive( 703 node, nodeIsContainer ? NodeSerializer::SerializeRoot::eNo 704 : NodeSerializer::SerializeRoot::eYes); 705 } 706 707 return rv; 708 } 709 710 nsresult nsDocumentEncoder::SerializeWholeDocument(uint32_t aMaxLength) { 711 NS_ENSURE_FALSE(mEncodingScope.mSelection, NS_ERROR_FAILURE); 712 NS_ENSURE_FALSE(mEncodingScope.mRange, NS_ERROR_FAILURE); 713 NS_ENSURE_FALSE(mEncodingScope.mNode, NS_ERROR_FAILURE); 714 715 nsresult rv = mSerializer->AppendDocumentStart(mDocument); 716 NS_ENSURE_SUCCESS(rv, rv); 717 718 rv = mNodeSerializer.SerializeToStringRecursive( 719 mDocument, NodeSerializer::SerializeRoot::eYes, aMaxLength); 720 return rv; 721 } 722 723 nsDocumentEncoder::~nsDocumentEncoder() = default; 724 725 NS_IMETHODIMP 726 nsDocumentEncoder::Init(Document* aDocument, const nsAString& aMimeType, 727 uint32_t aFlags) { 728 if (!aDocument) { 729 return NS_ERROR_INVALID_ARG; 730 } 731 732 Initialize(!mMimeType.Equals(aMimeType), 733 GetAllowRangeCrossShadowBoundary(aFlags)); 734 735 mDocument = aDocument; 736 737 mMimeType = aMimeType; 738 739 mFlags = aFlags; 740 mIsCopying = false; 741 742 return NS_OK; 743 } 744 745 NS_IMETHODIMP 746 nsDocumentEncoder::SetWrapColumn(uint32_t aWC) { 747 mWrapColumn = aWC; 748 return NS_OK; 749 } 750 751 NS_IMETHODIMP 752 nsDocumentEncoder::SetSelection(Selection* aSelection) { 753 mEncodingScope.mSelection = aSelection; 754 return NS_OK; 755 } 756 757 NS_IMETHODIMP 758 nsDocumentEncoder::SetRange(nsRange* aRange) { 759 mEncodingScope.mRange = aRange; 760 return NS_OK; 761 } 762 763 NS_IMETHODIMP 764 nsDocumentEncoder::SetNode(nsINode* aNode) { 765 mEncodingScope.mNodeIsContainer = false; 766 mEncodingScope.mNode = aNode; 767 return NS_OK; 768 } 769 770 NS_IMETHODIMP 771 nsDocumentEncoder::SetContainerNode(nsINode* aContainer) { 772 mEncodingScope.mNodeIsContainer = true; 773 mEncodingScope.mNode = aContainer; 774 return NS_OK; 775 } 776 777 NS_IMETHODIMP 778 nsDocumentEncoder::SetCharset(const nsACString& aCharset) { 779 const Encoding* encoding = Encoding::ForLabel(aCharset); 780 if (!encoding) { 781 return NS_ERROR_UCONV_NOCONV; 782 } 783 mEncoding = encoding->OutputEncoding(); 784 return NS_OK; 785 } 786 787 NS_IMETHODIMP 788 nsDocumentEncoder::GetMimeType(nsAString& aMimeType) { 789 aMimeType = mMimeType; 790 return NS_OK; 791 } 792 793 class FixupNodeDeterminer { 794 public: 795 FixupNodeDeterminer(nsIDocumentEncoderNodeFixup* aNodeFixup, 796 nsINode* aFixupNode, nsINode& aOriginalNode) 797 : mIsSerializationOfFixupChildrenNeeded{false}, 798 mNodeFixup(aNodeFixup), 799 mOriginalNode(aOriginalNode) { 800 if (mNodeFixup) { 801 if (aFixupNode) { 802 mFixupNode = aFixupNode; 803 } else { 804 mNodeFixup->FixupNode(&mOriginalNode, 805 &mIsSerializationOfFixupChildrenNeeded, 806 getter_AddRefs(mFixupNode)); 807 } 808 } 809 } 810 811 bool IsSerializationOfFixupChildrenNeeded() const { 812 return mIsSerializationOfFixupChildrenNeeded; 813 } 814 815 /** 816 * @return The fixup node, if available, otherwise the original node. The 817 * former is kept alive by this object. 818 */ 819 nsINode& GetFixupNodeFallBackToOriginalNode() const { 820 return mFixupNode ? *mFixupNode : mOriginalNode; 821 } 822 823 private: 824 bool mIsSerializationOfFixupChildrenNeeded; 825 nsIDocumentEncoderNodeFixup* mNodeFixup; 826 nsCOMPtr<nsINode> mFixupNode; 827 nsINode& mOriginalNode; 828 }; 829 830 nsresult nsDocumentEncoder::NodeSerializer::SerializeNodeStart( 831 nsINode& aOriginalNode, int32_t aStartOffset, int32_t aEndOffset, 832 nsINode* aFixupNode) const { 833 if (mNeedsPreformatScanning) { 834 if (aOriginalNode.IsElement()) { 835 mSerializer->ScanElementForPreformat(aOriginalNode.AsElement()); 836 } else if (aOriginalNode.IsText()) { 837 const nsCOMPtr<nsINode> parent = aOriginalNode.GetParent(); 838 if (parent && parent->IsElement()) { 839 mSerializer->ScanElementForPreformat(parent->AsElement()); 840 } 841 } 842 } 843 844 if (IsInvisibleNodeAndShouldBeSkipped(aOriginalNode, mFlags)) { 845 return NS_OK; 846 } 847 848 FixupNodeDeterminer fixupNodeDeterminer{mNodeFixup, aFixupNode, 849 aOriginalNode}; 850 nsINode* node = &fixupNodeDeterminer.GetFixupNodeFallBackToOriginalNode(); 851 852 nsresult rv = NS_OK; 853 854 if (node->IsElement()) { 855 if ((mFlags & (nsIDocumentEncoder::OutputPreformatted | 856 nsIDocumentEncoder::OutputDropInvisibleBreak)) && 857 nsLayoutUtils::IsInvisibleBreak(node)) { 858 return rv; 859 } 860 rv = mSerializer->AppendElementStart(node->AsElement(), 861 aOriginalNode.AsElement()); 862 return rv; 863 } 864 865 switch (node->NodeType()) { 866 case nsINode::TEXT_NODE: { 867 rv = mSerializer->AppendText(node->AsText(), aStartOffset, aEndOffset); 868 break; 869 } 870 case nsINode::CDATA_SECTION_NODE: { 871 rv = mSerializer->AppendCDATASection(node->AsText(), aStartOffset, 872 aEndOffset); 873 break; 874 } 875 case nsINode::PROCESSING_INSTRUCTION_NODE: { 876 rv = mSerializer->AppendProcessingInstruction( 877 static_cast<ProcessingInstruction*>(node), aStartOffset, aEndOffset); 878 break; 879 } 880 case nsINode::COMMENT_NODE: { 881 rv = mSerializer->AppendComment(static_cast<Comment*>(node), aStartOffset, 882 aEndOffset); 883 break; 884 } 885 case nsINode::DOCUMENT_TYPE_NODE: { 886 rv = mSerializer->AppendDoctype(static_cast<DocumentType*>(node)); 887 break; 888 } 889 } 890 891 return rv; 892 } 893 894 nsresult nsDocumentEncoder::NodeSerializer::SerializeNodeEnd( 895 nsINode& aOriginalNode, nsINode* aFixupNode) const { 896 if (mNeedsPreformatScanning) { 897 if (aOriginalNode.IsElement()) { 898 mSerializer->ForgetElementForPreformat(aOriginalNode.AsElement()); 899 } else if (aOriginalNode.IsText()) { 900 const nsCOMPtr<nsINode> parent = aOriginalNode.GetParent(); 901 if (parent && parent->IsElement()) { 902 mSerializer->ForgetElementForPreformat(parent->AsElement()); 903 } 904 } 905 } 906 907 if (IsInvisibleNodeAndShouldBeSkipped(aOriginalNode, mFlags)) { 908 return NS_OK; 909 } 910 911 nsresult rv = NS_OK; 912 913 FixupNodeDeterminer fixupNodeDeterminer{mNodeFixup, aFixupNode, 914 aOriginalNode}; 915 nsINode* node = &fixupNodeDeterminer.GetFixupNodeFallBackToOriginalNode(); 916 917 if (node->IsElement()) { 918 rv = mSerializer->AppendElementEnd(node->AsElement(), 919 aOriginalNode.AsElement()); 920 } 921 922 return rv; 923 } 924 925 nsresult nsDocumentEncoder::NodeSerializer::SerializeToStringRecursive( 926 nsINode* aNode, SerializeRoot aSerializeRoot, uint32_t aMaxLength) const { 927 uint32_t outputLength{0}; 928 nsresult rv = mSerializer->GetOutputLength(outputLength); 929 NS_ENSURE_SUCCESS(rv, rv); 930 931 if (aMaxLength > 0 && outputLength >= aMaxLength) { 932 return NS_OK; 933 } 934 935 NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); 936 937 if (IsInvisibleNodeAndShouldBeSkipped(*aNode, mFlags)) { 938 return NS_OK; 939 } 940 941 FixupNodeDeterminer fixupNodeDeterminer{mNodeFixup, nullptr, *aNode}; 942 nsINode* maybeFixedNode = 943 &fixupNodeDeterminer.GetFixupNodeFallBackToOriginalNode(); 944 945 if (mFlags & SkipInvisibleContent) { 946 if (aNode->IsContent()) { 947 if (nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame()) { 948 if (!frame->IsSelectable()) { 949 aSerializeRoot = SerializeRoot::eNo; 950 } 951 } 952 } 953 } 954 955 if (aSerializeRoot == SerializeRoot::eYes) { 956 int32_t endOffset = -1; 957 if (aMaxLength > 0) { 958 MOZ_ASSERT(aMaxLength >= outputLength); 959 endOffset = aMaxLength - outputLength; 960 } 961 rv = SerializeNodeStart(*aNode, 0, endOffset, maybeFixedNode); 962 NS_ENSURE_SUCCESS(rv, rv); 963 } 964 965 ShadowRoot* shadowRoot = ShadowDOMSelectionHelpers::GetShadowRoot( 966 aNode, GetAllowRangeCrossShadowBoundary(mFlags)); 967 968 if (shadowRoot) { 969 MOZ_ASSERT(StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()); 970 // Serialize the ShadowRoot first when the entire node needs to be 971 // serialized. 972 SerializeToStringRecursive(shadowRoot, aSerializeRoot, aMaxLength); 973 } 974 975 nsINode* node = fixupNodeDeterminer.IsSerializationOfFixupChildrenNeeded() 976 ? maybeFixedNode 977 : aNode; 978 979 int32_t counter = -1; 980 981 const bool allowCrossShadowBoundary = 982 GetAllowRangeCrossShadowBoundary(mFlags) == 983 AllowRangeCrossShadowBoundary::Yes; 984 auto GetNextNode = [&counter, node, allowCrossShadowBoundary]( 985 nsINode* aCurrentNode) -> nsINode* { 986 ++counter; 987 if (allowCrossShadowBoundary) { 988 if (const auto* slot = HTMLSlotElement::FromNode(node)) { 989 auto assigned = slot->AssignedNodes(); 990 if (size_t(counter) < assigned.Length()) { 991 return assigned[counter]; 992 } 993 return nullptr; 994 } 995 } 996 997 if (counter == 0) { 998 return node->GetFirstChildOfTemplateOrNode(); 999 } 1000 // counter isn't really used for non-slot cases. 1001 return aCurrentNode->GetNextSibling(); 1002 }; 1003 1004 if (!shadowRoot) { 1005 // We only iterate light DOM children of aNode if it isn't a shadow host 1006 // since it doesn't make sense to iterate them this way. Slotted contents 1007 // has been handled by serializing the <slot> element. 1008 for (nsINode* child = GetNextNode(nullptr); child; 1009 child = GetNextNode(child)) { 1010 rv = SerializeToStringRecursive(child, SerializeRoot::eYes, aMaxLength); 1011 NS_ENSURE_SUCCESS(rv, rv); 1012 } 1013 } 1014 1015 if (aSerializeRoot == SerializeRoot::eYes) { 1016 rv = SerializeNodeEnd(*aNode, maybeFixedNode); 1017 NS_ENSURE_SUCCESS(rv, rv); 1018 } 1019 1020 if (mTextStreamer) { 1021 rv = mTextStreamer->FlushIfStringLongEnough(); 1022 } 1023 1024 return rv; 1025 } 1026 1027 nsresult nsDocumentEncoder::NodeSerializer::SerializeToStringIterative( 1028 nsINode* aNode) const { 1029 nsresult rv; 1030 1031 nsINode* node = aNode->GetFirstChildOfTemplateOrNode(); 1032 while (node) { 1033 nsINode* current = node; 1034 rv = SerializeNodeStart(*current, 0, -1, current); 1035 NS_ENSURE_SUCCESS(rv, rv); 1036 node = current->GetFirstChildOfTemplateOrNode(); 1037 while (!node && current && current != aNode) { 1038 rv = SerializeNodeEnd(*current); 1039 NS_ENSURE_SUCCESS(rv, rv); 1040 // Check if we have siblings. 1041 node = current->GetNextSibling(); 1042 if (!node) { 1043 // Perhaps parent node has siblings. 1044 current = current->GetParentNode(); 1045 1046 // Handle template element. If the parent is a template's content, 1047 // then adjust the parent to be the template element. 1048 if (current && current != aNode && current->IsDocumentFragment()) { 1049 nsIContent* host = current->AsDocumentFragment()->GetHost(); 1050 if (host && host->IsHTMLElement(nsGkAtoms::_template)) { 1051 current = host; 1052 } 1053 } 1054 } 1055 } 1056 } 1057 1058 return NS_OK; 1059 } 1060 1061 static bool IsTextNode(nsINode* aNode) { return aNode && aNode->IsText(); } 1062 1063 nsresult nsDocumentEncoder::NodeSerializer::SerializeTextNode( 1064 nsINode& aNode, int32_t aStartOffset, int32_t aEndOffset) const { 1065 MOZ_ASSERT(IsTextNode(&aNode)); 1066 1067 nsresult rv = SerializeNodeStart(aNode, aStartOffset, aEndOffset); 1068 NS_ENSURE_SUCCESS(rv, rv); 1069 rv = SerializeNodeEnd(aNode); 1070 NS_ENSURE_SUCCESS(rv, rv); 1071 return rv; 1072 } 1073 1074 nsDocumentEncoder::RangeSerializer::StartAndEndContent 1075 nsDocumentEncoder::RangeSerializer::GetStartAndEndContentForRecursionLevel( 1076 const int32_t aDepth) const { 1077 StartAndEndContent result; 1078 1079 const auto& inclusiveAncestorsOfStart = 1080 mRangeBoundariesInclusiveAncestorsAndOffsets.mInclusiveAncestorsOfStart; 1081 const auto& inclusiveAncestorsOfEnd = 1082 mRangeBoundariesInclusiveAncestorsAndOffsets.mInclusiveAncestorsOfEnd; 1083 int32_t start = mStartRootIndex - aDepth; 1084 if (start >= 0 && (uint32_t)start <= inclusiveAncestorsOfStart.Length()) { 1085 result.mStart = inclusiveAncestorsOfStart[start]; 1086 } 1087 1088 int32_t end = mEndRootIndex - aDepth; 1089 if (end >= 0 && (uint32_t)end <= inclusiveAncestorsOfEnd.Length()) { 1090 result.mEnd = inclusiveAncestorsOfEnd[end]; 1091 } 1092 1093 return result; 1094 } 1095 1096 nsresult nsDocumentEncoder::RangeSerializer::SerializeTextNode( 1097 nsIContent& aContent, const StartAndEndContent& aStartAndEndContent, 1098 const nsRange& aRange) const { 1099 const int32_t startOffset = (aStartAndEndContent.mStart == &aContent) 1100 ? ShadowDOMSelectionHelpers::StartOffset( 1101 &aRange, mAllowCrossShadowBoundary) 1102 : 0; 1103 const int32_t endOffset = (aStartAndEndContent.mEnd == &aContent) 1104 ? ShadowDOMSelectionHelpers::EndOffset( 1105 &aRange, mAllowCrossShadowBoundary) 1106 : -1; 1107 return mNodeSerializer.SerializeTextNode(aContent, startOffset, endOffset); 1108 } 1109 1110 nsresult nsDocumentEncoder::RangeSerializer::SerializeRangeNodes( 1111 const nsRange* const aRange, nsINode* const aNode, const int32_t aDepth) { 1112 MOZ_ASSERT(aDepth >= 0); 1113 MOZ_ASSERT(aRange); 1114 1115 nsCOMPtr<nsIContent> content = nsIContent::FromNodeOrNull(aNode); 1116 NS_ENSURE_TRUE(content, NS_ERROR_FAILURE); 1117 1118 if (nsDocumentEncoder::IsInvisibleNodeAndShouldBeSkipped(*aNode, mFlags)) { 1119 return NS_OK; 1120 } 1121 1122 nsresult rv = NS_OK; 1123 1124 StartAndEndContent startAndEndContent = 1125 GetStartAndEndContentForRecursionLevel(aDepth); 1126 1127 if (startAndEndContent.mStart != content && 1128 startAndEndContent.mEnd != content) { 1129 // node is completely contained in range. Serialize the whole subtree 1130 // rooted by this node. 1131 rv = mNodeSerializer.SerializeToStringRecursive( 1132 aNode, NodeSerializer::SerializeRoot::eYes); 1133 NS_ENSURE_SUCCESS(rv, rv); 1134 } else { 1135 rv = SerializeNodePartiallyContainedInRange(*content, startAndEndContent, 1136 *aRange, aDepth); 1137 if (NS_WARN_IF(NS_FAILED(rv))) { 1138 return rv; 1139 } 1140 } 1141 return NS_OK; 1142 } 1143 1144 nsresult 1145 nsDocumentEncoder::RangeSerializer::SerializeNodePartiallyContainedInRange( 1146 nsIContent& aContent, const StartAndEndContent& aStartAndEndContent, 1147 const nsRange& aRange, const int32_t aDepth) { 1148 // due to implementation it is impossible for text node to be both start and 1149 // end of range. We would have handled that case without getting here. 1150 // XXXsmaug What does this all mean? 1151 if (IsTextNode(&aContent)) { 1152 nsresult rv = SerializeTextNode(aContent, aStartAndEndContent, aRange); 1153 NS_ENSURE_SUCCESS(rv, rv); 1154 } else { 1155 if (&aContent != mClosestCommonInclusiveAncestorOfRange) { 1156 if (mRangeContextSerializer.mRangeNodeContext.IncludeInContext( 1157 aContent)) { 1158 // halt the incrementing of mContextInfoDepth. This 1159 // is so paste client will include this node in paste. 1160 mHaltRangeHint = true; 1161 } 1162 if ((aStartAndEndContent.mStart == &aContent) && !mHaltRangeHint) { 1163 ++mContextInfoDepth.mStart; 1164 } 1165 if ((aStartAndEndContent.mEnd == &aContent) && !mHaltRangeHint) { 1166 ++mContextInfoDepth.mEnd; 1167 } 1168 1169 // serialize the start of this node 1170 nsresult rv = mNodeSerializer.SerializeNodeStart(aContent, 0, -1); 1171 NS_ENSURE_SUCCESS(rv, rv); 1172 } 1173 1174 const auto& inclusiveAncestorsOffsetsOfStart = 1175 mRangeBoundariesInclusiveAncestorsAndOffsets 1176 .mInclusiveAncestorsOffsetsOfStart; 1177 const auto& inclusiveAncestorsOffsetsOfEnd = 1178 mRangeBoundariesInclusiveAncestorsAndOffsets 1179 .mInclusiveAncestorsOffsetsOfEnd; 1180 // do some calculations that will tell us which children of this 1181 // node are in the range. 1182 Maybe<uint32_t> startOffset = Some(0); 1183 Maybe<uint32_t> endOffset; 1184 if (aStartAndEndContent.mStart == &aContent && mStartRootIndex >= aDepth) { 1185 startOffset = inclusiveAncestorsOffsetsOfStart[mStartRootIndex - aDepth]; 1186 } 1187 if (aStartAndEndContent.mEnd == &aContent && mEndRootIndex >= aDepth) { 1188 endOffset = inclusiveAncestorsOffsetsOfEnd[mEndRootIndex - aDepth]; 1189 } 1190 // generated aContent will cause offset values of Nothing to be returned. 1191 if (startOffset.isNothing()) { 1192 startOffset = Some(0); 1193 } 1194 if (endOffset.isNothing()) { 1195 endOffset = Some(aContent.GetChildCount()); 1196 1197 if (mAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) { 1198 if (const auto* slot = HTMLSlotElement::FromNode(aContent)) { 1199 const auto& assignedNodes = slot->AssignedNodes(); 1200 if (!assignedNodes.IsEmpty()) { 1201 endOffset = Some(assignedNodes.Length()); 1202 } 1203 } 1204 } 1205 } else { 1206 // if we are at the "tip" of the selection, endOffset is fine. 1207 // otherwise, we need to add one. This is because of the semantics 1208 // of the offset list created by GetInclusiveAncestorsAndOffsets(). The 1209 // intermediate points on the list use the endOffset of the 1210 // location of the ancestor, rather than just past it. So we need 1211 // to add one here in order to include it in the children we serialize. 1212 const nsINode* endContainer = ShadowDOMSelectionHelpers::GetEndContainer( 1213 &aRange, mAllowCrossShadowBoundary); 1214 if (&aContent != endContainer) { 1215 MOZ_ASSERT(*endOffset != UINT32_MAX); 1216 endOffset.ref()++; 1217 } 1218 } 1219 1220 MOZ_ASSERT(endOffset.isSome()); 1221 nsresult rv = SerializeChildrenOfContent(aContent, *startOffset, *endOffset, 1222 &aRange, aDepth); 1223 NS_ENSURE_SUCCESS(rv, rv); 1224 1225 // serialize the end of this node 1226 if (&aContent != mClosestCommonInclusiveAncestorOfRange) { 1227 nsresult rv = mNodeSerializer.SerializeNodeEnd(aContent); 1228 NS_ENSURE_SUCCESS(rv, rv); 1229 } 1230 } 1231 1232 return NS_OK; 1233 } 1234 1235 nsresult nsDocumentEncoder::RangeSerializer::SerializeChildrenOfContent( 1236 nsIContent& aContent, uint32_t aStartOffset, uint32_t aEndOffset, 1237 const nsRange* aRange, int32_t aDepth) { 1238 ShadowRoot* shadowRoot = ShadowDOMSelectionHelpers::GetShadowRoot( 1239 &aContent, mAllowCrossShadowBoundary); 1240 if (shadowRoot) { 1241 // Serialize the ShadowRoot when the entire node needs to be serialized. 1242 // Return early to skip light DOM children. 1243 SerializeRangeNodes(aRange, shadowRoot, aDepth + 1); 1244 return NS_OK; 1245 } 1246 1247 if (!aEndOffset) { 1248 return NS_OK; 1249 } 1250 1251 nsINode* childAsNode = 1252 mAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes 1253 ? aContent.GetChildAtInFlatTree(aStartOffset) 1254 : aContent.GetChildAt_Deprecated(aStartOffset); 1255 1256 MOZ_ASSERT_IF(childAsNode, childAsNode->IsContent()); 1257 1258 auto GetNextSibling = [this, &aContent]( 1259 nsINode* aCurrentNode, 1260 uint32_t aCurrentIndex) -> nsIContent* { 1261 if (mAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) { 1262 if (const auto* slot = HTMLSlotElement::FromNode(&aContent)) { 1263 auto assigned = slot->AssignedNodes(); 1264 if (++aCurrentIndex < assigned.Length()) { 1265 return nsIContent::FromNode(assigned[aCurrentIndex]); 1266 } 1267 return nullptr; 1268 } 1269 } 1270 1271 return aCurrentNode->GetNextSibling(); 1272 }; 1273 1274 for (size_t j = aStartOffset; childAsNode && j < aEndOffset; ++j) { 1275 nsresult rv{NS_OK}; 1276 const bool isFirstOrLastNodeToSerialize = 1277 j == aStartOffset || j == aEndOffset - 1; 1278 if (isFirstOrLastNodeToSerialize) { 1279 rv = SerializeRangeNodes(aRange, childAsNode, aDepth + 1); 1280 } else { 1281 rv = mNodeSerializer.SerializeToStringRecursive( 1282 childAsNode, NodeSerializer::SerializeRoot::eYes); 1283 } 1284 1285 if (NS_FAILED(rv)) { 1286 return rv; 1287 } 1288 1289 childAsNode = GetNextSibling(childAsNode, j); 1290 } 1291 1292 return NS_OK; 1293 } 1294 1295 nsresult nsDocumentEncoder::RangeContextSerializer::SerializeRangeContextStart( 1296 const nsTArray<nsINode*>& aAncestorArray) { 1297 if (mDisableContextSerialize) { 1298 return NS_OK; 1299 } 1300 1301 AutoTArray<nsINode*, 8>* serializedContext = mRangeContexts.AppendElement(); 1302 1303 int32_t i = aAncestorArray.Length(), j; 1304 nsresult rv = NS_OK; 1305 1306 // currently only for table-related elements; see Bug 137450 1307 j = mRangeNodeContext.GetImmediateContextCount(aAncestorArray); 1308 1309 while (i > 0) { 1310 nsINode* node = aAncestorArray.ElementAt(--i); 1311 if (!node) break; 1312 1313 // Either a general inclusion or as immediate context 1314 if (mRangeNodeContext.IncludeInContext(*node) || i < j) { 1315 rv = mNodeSerializer.SerializeNodeStart(*node, 0, -1); 1316 serializedContext->AppendElement(node); 1317 if (NS_FAILED(rv)) break; 1318 } 1319 } 1320 1321 return rv; 1322 } 1323 1324 nsresult nsDocumentEncoder::RangeContextSerializer::SerializeRangeContextEnd() { 1325 if (mDisableContextSerialize) { 1326 return NS_OK; 1327 } 1328 1329 MOZ_RELEASE_ASSERT(!mRangeContexts.IsEmpty(), 1330 "Tried to end context without starting one."); 1331 AutoTArray<nsINode*, 8>& serializedContext = mRangeContexts.LastElement(); 1332 1333 nsresult rv = NS_OK; 1334 for (nsINode* node : Reversed(serializedContext)) { 1335 rv = mNodeSerializer.SerializeNodeEnd(*node); 1336 1337 if (NS_FAILED(rv)) break; 1338 } 1339 1340 mRangeContexts.RemoveLastElement(); 1341 return rv; 1342 } 1343 1344 bool nsDocumentEncoder::RangeSerializer::HasInvisibleParentAndShouldBeSkipped( 1345 nsINode& aNode) const { 1346 if (!(mFlags & SkipInvisibleContent)) { 1347 return false; 1348 } 1349 1350 // Check that the parent is visible if we don't a frame. 1351 // IsInvisibleNodeAndShouldBeSkipped() will do it when there's a frame. 1352 nsCOMPtr<nsIContent> content = nsIContent::FromNode(aNode); 1353 if (content && !content->GetPrimaryFrame()) { 1354 nsIContent* parent = content->GetParent(); 1355 return !parent || IsInvisibleNodeAndShouldBeSkipped(*parent, mFlags); 1356 } 1357 1358 return false; 1359 } 1360 1361 nsresult nsDocumentEncoder::RangeSerializer::SerializeRangeToString( 1362 const nsRange* aRange) { 1363 if (!aRange || 1364 (aRange->Collapsed() && 1365 (mAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::No || 1366 !aRange->MayCrossShadowBoundary()))) { 1367 return NS_OK; 1368 } 1369 1370 // Consider a case where the boundary of the selection is ShadowRoot (ie, the 1371 // first child of ShadowRoot is selected, so ShadowRoot is the container hence 1372 // the boundary), allowing GetClosestCommonInclusiveAncestor to cross the 1373 // boundary can return the host element as the container. 1374 // SerializeRangeContextStart doesn't support this case. 1375 mClosestCommonInclusiveAncestorOfRange = 1376 aRange->GetClosestCommonInclusiveAncestor(mAllowCrossShadowBoundary); 1377 1378 if (!mClosestCommonInclusiveAncestorOfRange) { 1379 return NS_OK; 1380 } 1381 1382 nsINode* startContainer = ShadowDOMSelectionHelpers::GetStartContainer( 1383 aRange, mAllowCrossShadowBoundary); 1384 NS_ENSURE_TRUE(startContainer, NS_ERROR_FAILURE); 1385 const int32_t startOffset = 1386 ShadowDOMSelectionHelpers::StartOffset(aRange, mAllowCrossShadowBoundary); 1387 1388 nsINode* endContainer = ShadowDOMSelectionHelpers::GetEndContainer( 1389 aRange, mAllowCrossShadowBoundary); 1390 NS_ENSURE_TRUE(endContainer, NS_ERROR_FAILURE); 1391 const int32_t endOffset = 1392 ShadowDOMSelectionHelpers::EndOffset(aRange, mAllowCrossShadowBoundary); 1393 1394 mContextInfoDepth = {}; 1395 mCommonInclusiveAncestors.Clear(); 1396 1397 mRangeBoundariesInclusiveAncestorsAndOffsets = {}; 1398 auto& inclusiveAncestorsOfStart = 1399 mRangeBoundariesInclusiveAncestorsAndOffsets.mInclusiveAncestorsOfStart; 1400 auto& inclusiveAncestorsOffsetsOfStart = 1401 mRangeBoundariesInclusiveAncestorsAndOffsets 1402 .mInclusiveAncestorsOffsetsOfStart; 1403 auto& inclusiveAncestorsOfEnd = 1404 mRangeBoundariesInclusiveAncestorsAndOffsets.mInclusiveAncestorsOfEnd; 1405 auto& inclusiveAncestorsOffsetsOfEnd = 1406 mRangeBoundariesInclusiveAncestorsAndOffsets 1407 .mInclusiveAncestorsOffsetsOfEnd; 1408 1409 nsContentUtils::GetInclusiveAncestors(mClosestCommonInclusiveAncestorOfRange, 1410 mCommonInclusiveAncestors); 1411 if (mAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) { 1412 nsContentUtils::GetFlattenedTreeAncestorsAndOffsets( 1413 startContainer, startOffset, inclusiveAncestorsOfStart, 1414 inclusiveAncestorsOffsetsOfStart); 1415 nsContentUtils::GetFlattenedTreeAncestorsAndOffsets( 1416 endContainer, endOffset, inclusiveAncestorsOfEnd, 1417 inclusiveAncestorsOffsetsOfEnd); 1418 } else { 1419 nsContentUtils::GetInclusiveAncestorsAndOffsets( 1420 startContainer, startOffset, inclusiveAncestorsOfStart, 1421 inclusiveAncestorsOffsetsOfStart); 1422 nsContentUtils::GetInclusiveAncestorsAndOffsets( 1423 endContainer, endOffset, inclusiveAncestorsOfEnd, 1424 inclusiveAncestorsOffsetsOfEnd); 1425 } 1426 1427 nsCOMPtr<nsIContent> commonContent = 1428 nsIContent::FromNodeOrNull(mClosestCommonInclusiveAncestorOfRange); 1429 mStartRootIndex = inclusiveAncestorsOfStart.IndexOf(commonContent); 1430 mEndRootIndex = inclusiveAncestorsOfEnd.IndexOf(commonContent); 1431 1432 nsresult rv = NS_OK; 1433 1434 rv = mRangeContextSerializer.SerializeRangeContextStart( 1435 mCommonInclusiveAncestors); 1436 NS_ENSURE_SUCCESS(rv, rv); 1437 1438 if (startContainer == endContainer && IsTextNode(startContainer)) { 1439 if (HasInvisibleParentAndShouldBeSkipped(*startContainer)) { 1440 return NS_OK; 1441 } 1442 rv = mNodeSerializer.SerializeTextNode(*startContainer, startOffset, 1443 endOffset); 1444 NS_ENSURE_SUCCESS(rv, rv); 1445 } else { 1446 rv = SerializeRangeNodes(aRange, mClosestCommonInclusiveAncestorOfRange, 0); 1447 NS_ENSURE_SUCCESS(rv, rv); 1448 } 1449 rv = mRangeContextSerializer.SerializeRangeContextEnd(); 1450 NS_ENSURE_SUCCESS(rv, rv); 1451 1452 return rv; 1453 } 1454 1455 void nsDocumentEncoder::ReleaseDocumentReferenceAndInitialize( 1456 bool aClearCachedSerializer) { 1457 mDocument = nullptr; 1458 1459 Initialize(aClearCachedSerializer); 1460 } 1461 1462 NS_IMETHODIMP 1463 nsDocumentEncoder::EncodeToString(nsAString& aOutputString) { 1464 return EncodeToStringWithMaxLength(0, aOutputString); 1465 } 1466 1467 NS_IMETHODIMP 1468 nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength, 1469 nsAString& aOutputString) { 1470 MOZ_ASSERT(mRangeContextSerializer.mRangeContexts.IsEmpty(), 1471 "Re-entrant call to nsDocumentEncoder."); 1472 auto rangeContextGuard = 1473 MakeScopeExit([&] { mRangeContextSerializer.mRangeContexts.Clear(); }); 1474 1475 if (!mDocument) return NS_ERROR_NOT_INITIALIZED; 1476 1477 AutoReleaseDocumentIfNeeded autoReleaseDocument(this); 1478 1479 aOutputString.Truncate(); 1480 1481 nsString output; 1482 static const size_t kStringBufferSizeInBytes = 2048; 1483 if (!mCachedBuffer) { 1484 mCachedBuffer = StringBuffer::Alloc(kStringBufferSizeInBytes); 1485 if (NS_WARN_IF(!mCachedBuffer)) { 1486 return NS_ERROR_OUT_OF_MEMORY; 1487 } 1488 } 1489 NS_ASSERTION( 1490 !mCachedBuffer->IsReadonly(), 1491 "nsIDocumentEncoder shouldn't keep reference to non-readonly buffer!"); 1492 static_cast<char16_t*>(mCachedBuffer->Data())[0] = char16_t(0); 1493 output.Assign(mCachedBuffer.forget(), 0); 1494 1495 if (!mSerializer) { 1496 nsAutoCString progId(NS_CONTENTSERIALIZER_CONTRACTID_PREFIX); 1497 AppendUTF16toUTF8(mMimeType, progId); 1498 1499 mSerializer = do_CreateInstance(progId.get()); 1500 NS_ENSURE_TRUE(mSerializer, NS_ERROR_NOT_IMPLEMENTED); 1501 } 1502 1503 nsresult rv = NS_OK; 1504 1505 bool rewriteEncodingDeclaration = 1506 !mEncodingScope.IsLimited() && 1507 !(mFlags & OutputDontRewriteEncodingDeclaration); 1508 mSerializer->Init(mFlags, mWrapColumn, mEncoding, mIsCopying, 1509 rewriteEncodingDeclaration, &mNeedsPreformatScanning, 1510 output); 1511 1512 rv = SerializeDependingOnScope(aMaxLength); 1513 NS_ENSURE_SUCCESS(rv, rv); 1514 1515 rv = mSerializer->FlushAndFinish(); 1516 1517 // We have to be careful how we set aOutputString, because we don't 1518 // want it to end up sharing mCachedBuffer if we plan to reuse it. 1519 bool setOutput = false; 1520 MOZ_ASSERT(!mCachedBuffer); 1521 // Try to cache the buffer. 1522 if (StringBuffer* outputBuffer = output.GetStringBuffer()) { 1523 if (outputBuffer->StorageSize() == kStringBufferSizeInBytes && 1524 !outputBuffer->IsReadonly()) { 1525 mCachedBuffer = outputBuffer; 1526 } else if (NS_SUCCEEDED(rv)) { 1527 aOutputString.Assign(outputBuffer, output.Length()); 1528 setOutput = true; 1529 } 1530 } 1531 1532 if (!setOutput && NS_SUCCEEDED(rv)) { 1533 aOutputString.Append(output.get(), output.Length()); 1534 } 1535 1536 return rv; 1537 } 1538 1539 NS_IMETHODIMP 1540 nsDocumentEncoder::EncodeToStream(nsIOutputStream* aStream) { 1541 MOZ_ASSERT(mRangeContextSerializer.mRangeContexts.IsEmpty(), 1542 "Re-entrant call to nsDocumentEncoder."); 1543 auto rangeContextGuard = 1544 MakeScopeExit([&] { mRangeContextSerializer.mRangeContexts.Clear(); }); 1545 NS_ENSURE_ARG_POINTER(aStream); 1546 1547 nsresult rv = NS_OK; 1548 1549 if (!mDocument) return NS_ERROR_NOT_INITIALIZED; 1550 1551 if (!mEncoding) { 1552 return NS_ERROR_UCONV_NOCONV; 1553 } 1554 1555 nsAutoString buf; 1556 const bool isPlainText = mMimeType.LowerCaseEqualsLiteral(kTextMime); 1557 mTextStreamer.emplace(*aStream, mEncoding->NewEncoder(), isPlainText, buf); 1558 1559 rv = EncodeToString(buf); 1560 1561 // Force a flush of the last chunk of data. 1562 rv = mTextStreamer->ForceFlush(); 1563 NS_ENSURE_SUCCESS(rv, rv); 1564 1565 mTextStreamer.reset(); 1566 1567 return rv; 1568 } 1569 1570 NS_IMETHODIMP 1571 nsDocumentEncoder::EncodeToStringWithContext(nsAString& aContextString, 1572 nsAString& aInfoString, 1573 nsAString& aEncodedString) { 1574 return NS_ERROR_NOT_IMPLEMENTED; 1575 } 1576 1577 NS_IMETHODIMP 1578 nsDocumentEncoder::SetNodeFixup(nsIDocumentEncoderNodeFixup* aFixup) { 1579 mNodeFixup = aFixup; 1580 return NS_OK; 1581 } 1582 1583 bool do_getDocumentTypeSupportedForEncoding(const char* aContentType) { 1584 if (!nsCRT::strcmp(aContentType, TEXT_XML) || 1585 !nsCRT::strcmp(aContentType, APPLICATION_XML) || 1586 !nsCRT::strcmp(aContentType, APPLICATION_XHTML_XML) || 1587 !nsCRT::strcmp(aContentType, IMAGE_SVG_XML) || 1588 !nsCRT::strcmp(aContentType, TEXT_HTML) || 1589 !nsCRT::strcmp(aContentType, TEXT_PLAIN)) { 1590 return true; 1591 } 1592 return false; 1593 } 1594 1595 already_AddRefed<nsIDocumentEncoder> do_createDocumentEncoder( 1596 const char* aContentType) { 1597 if (do_getDocumentTypeSupportedForEncoding(aContentType)) { 1598 return do_AddRef(new nsDocumentEncoder); 1599 } 1600 return nullptr; 1601 } 1602 1603 class nsHTMLCopyEncoder final : public nsDocumentEncoder { 1604 private: 1605 class RangeNodeContext final : public nsDocumentEncoder::RangeNodeContext { 1606 bool IncludeInContext(nsINode& aNode) const final; 1607 1608 int32_t GetImmediateContextCount( 1609 const nsTArray<nsINode*>& aAncestorArray) const final; 1610 }; 1611 1612 public: 1613 nsHTMLCopyEncoder(); 1614 ~nsHTMLCopyEncoder(); 1615 1616 NS_IMETHOD Init(Document* aDocument, const nsAString& aMimeType, 1617 uint32_t aFlags) override; 1618 1619 // overridden methods from nsDocumentEncoder 1620 MOZ_CAN_RUN_SCRIPT_BOUNDARY 1621 NS_IMETHOD SetSelection(Selection* aSelection) override; 1622 NS_IMETHOD EncodeToStringWithContext(nsAString& aContextString, 1623 nsAString& aInfoString, 1624 nsAString& aEncodedString) override; 1625 NS_IMETHOD EncodeToString(nsAString& aOutputString) override; 1626 1627 protected: 1628 enum Endpoint { kStart, kEnd }; 1629 1630 nsresult PromoteRange(nsRange* inRange); 1631 nsresult PromoteAncestorChain(nsCOMPtr<nsINode>* ioNode, 1632 uint32_t* aIOStartOffset, 1633 uint32_t* aIOEndOffset); 1634 nsresult GetPromotedPoint(const Endpoint aWhere, nsINode* const aNode, 1635 const uint32_t aOffset, nsCOMPtr<nsINode>* aOutNode, 1636 uint32_t* aOutOffset, nsINode* const aCommon); 1637 static nsCOMPtr<nsINode> GetChildAt(nsINode* aParent, const uint32_t aOffset); 1638 static bool IsMozBR(Element* aNode); 1639 nsresult GetNodeLocation(nsINode* const aInChild, 1640 nsCOMPtr<nsINode>* aOutParent, 1641 Maybe<uint32_t>* aOutOffsetInParent); 1642 bool IsRoot(nsINode* aNode); 1643 static bool IsFirstNode(nsINode* aNode); 1644 static bool IsLastNode(nsINode* aNode); 1645 1646 bool mIsTextWidget{false}; 1647 }; 1648 1649 nsHTMLCopyEncoder::nsHTMLCopyEncoder() 1650 : nsDocumentEncoder{MakeUnique<nsHTMLCopyEncoder::RangeNodeContext>()} {} 1651 1652 nsHTMLCopyEncoder::~nsHTMLCopyEncoder() = default; 1653 1654 NS_IMETHODIMP 1655 nsHTMLCopyEncoder::Init(Document* aDocument, const nsAString& aMimeType, 1656 uint32_t aFlags) { 1657 if (!aDocument) return NS_ERROR_INVALID_ARG; 1658 1659 mIsTextWidget = false; 1660 Initialize(true, GetAllowRangeCrossShadowBoundary(aFlags)); 1661 1662 mIsCopying = true; 1663 mDocument = aDocument; 1664 1665 // nsHTMLCopyEncoder only accepts "text/plain" or "text/html" MIME types, and 1666 // the initial MIME type may change after setting the selection. 1667 MOZ_ASSERT(aMimeType.EqualsLiteral(kTextMime) || 1668 aMimeType.EqualsLiteral(kHTMLMime)); 1669 if (aMimeType.EqualsLiteral(kTextMime)) { 1670 mMimeType.AssignLiteral(kTextMime); 1671 } else { 1672 mMimeType.AssignLiteral(kHTMLMime); 1673 } 1674 1675 // Make all links absolute when copying 1676 // (see related bugs #57296, #41924, #58646, #32768) 1677 mFlags = aFlags | OutputAbsoluteLinks; 1678 1679 if (!mDocument->IsScriptEnabled()) mFlags |= OutputNoScriptContent; 1680 1681 return NS_OK; 1682 } 1683 1684 NS_IMETHODIMP 1685 nsHTMLCopyEncoder::SetSelection(Selection* aSelection) { 1686 // check for text widgets: we need to recognize these so that 1687 // we don't tweak the selection to be outside of the magic 1688 // div that ender-lite text widgets are embedded in. 1689 1690 if (!aSelection) return NS_ERROR_NULL_POINTER; 1691 1692 const uint32_t rangeCount = aSelection->RangeCount(); 1693 1694 // if selection is uninitialized return 1695 if (!rangeCount) { 1696 return NS_ERROR_FAILURE; 1697 } 1698 1699 // we'll just use the common parent of the first range. Implicit assumption 1700 // here that multi-range selections are table cell selections, in which case 1701 // the common parent is somewhere in the table and we don't really care where. 1702 // 1703 // FIXME(emilio, bug 1455894): This assumption is already wrong, and will 1704 // probably be more wrong in a Shadow DOM world... 1705 // 1706 // We should be able to write this as "Find the common ancestor of the 1707 // selection, then go through the flattened tree and serialize the selected 1708 // nodes", effectively serializing the composed tree. 1709 RefPtr<nsRange> range = aSelection->GetRangeAt(0); 1710 nsINode* commonParent = range->GetClosestCommonInclusiveAncestor(); 1711 1712 for (nsCOMPtr<nsIContent> selContent( 1713 nsIContent::FromNodeOrNull(commonParent)); 1714 selContent; selContent = selContent->GetParent()) { 1715 // checking for selection inside a plaintext form widget 1716 if (selContent->IsAnyOfHTMLElements(nsGkAtoms::input, 1717 nsGkAtoms::textarea)) { 1718 mIsTextWidget = true; 1719 break; 1720 } 1721 } 1722 1723 // normalize selection if we are not in a widget 1724 if (mIsTextWidget) { 1725 mEncodingScope.mSelection = aSelection; 1726 mMimeType.AssignLiteral("text/plain"); 1727 return NS_OK; 1728 } 1729 1730 // XXX We should try to get rid of the Selection object here. 1731 // XXX bug 1245883 1732 1733 // also consider ourselves in a text widget if we can't find an html document 1734 // XXX: nsCopySupport relies on the MIME type not being updated immediately 1735 // here, so it can apply different encoding for XHTML documents. 1736 if (!(mDocument && mDocument->IsHTMLDocument())) { 1737 mIsTextWidget = true; 1738 mEncodingScope.mSelection = aSelection; 1739 // mMimeType is set to text/plain when encoding starts. 1740 return NS_OK; 1741 } 1742 1743 // there's no Clone() for selection! fix... 1744 // nsresult rv = aSelection->Clone(getter_AddRefs(mSelection); 1745 // NS_ENSURE_SUCCESS(rv, rv); 1746 mEncodingScope.mSelection = new Selection(SelectionType::eNormal, nullptr); 1747 1748 // loop thru the ranges in the selection 1749 for (const uint32_t rangeIdx : IntegerRange(rangeCount)) { 1750 MOZ_ASSERT(aSelection->RangeCount() == rangeCount); 1751 range = aSelection->GetRangeAt(rangeIdx); 1752 NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); 1753 RefPtr<nsRange> myRange = range->CloneRange(); 1754 MOZ_ASSERT(myRange); 1755 1756 // adjust range to include any ancestors who's children are entirely 1757 // selected 1758 nsresult rv = PromoteRange(myRange); 1759 NS_ENSURE_SUCCESS(rv, rv); 1760 1761 ErrorResult result; 1762 RefPtr<Selection> selection(mEncodingScope.mSelection); 1763 RefPtr<Document> document(mDocument); 1764 selection->AddRangeAndSelectFramesAndNotifyListenersInternal( 1765 *myRange, document, result); 1766 rv = result.StealNSResult(); 1767 NS_ENSURE_SUCCESS(rv, rv); 1768 } 1769 1770 return NS_OK; 1771 } 1772 1773 NS_IMETHODIMP 1774 nsHTMLCopyEncoder::EncodeToString(nsAString& aOutputString) { 1775 if (mIsTextWidget) { 1776 mMimeType.AssignLiteral("text/plain"); 1777 } 1778 return nsDocumentEncoder::EncodeToString(aOutputString); 1779 } 1780 1781 NS_IMETHODIMP 1782 nsHTMLCopyEncoder::EncodeToStringWithContext(nsAString& aContextString, 1783 nsAString& aInfoString, 1784 nsAString& aEncodedString) { 1785 nsresult rv = EncodeToString(aEncodedString); 1786 NS_ENSURE_SUCCESS(rv, rv); 1787 1788 // do not encode any context info or range hints if we are in a text widget. 1789 if (mIsTextWidget) return NS_OK; 1790 1791 // now encode common ancestors into aContextString. Note that the common 1792 // ancestors will be for the last range in the selection in the case of 1793 // multirange selections. encoding ancestors every range in a multirange 1794 // selection in a way that could be understood by the paste code would be a 1795 // lot more work to do. As a practical matter, selections are single range, 1796 // and the ones that aren't are table cell selections where all the cells are 1797 // in the same table. 1798 1799 mSerializer->Init(mFlags, mWrapColumn, mEncoding, mIsCopying, false, 1800 &mNeedsPreformatScanning, aContextString); 1801 1802 // leaf of ancestors might be text node. If so discard it. 1803 int32_t count = mRangeSerializer.mCommonInclusiveAncestors.Length(); 1804 int32_t i; 1805 nsCOMPtr<nsINode> node; 1806 if (count > 0) { 1807 node = mRangeSerializer.mCommonInclusiveAncestors.ElementAt(0); 1808 } 1809 1810 if (node && IsTextNode(node)) { 1811 mRangeSerializer.mCommonInclusiveAncestors.RemoveElementAt(0); 1812 if (mRangeSerializer.mContextInfoDepth.mStart) { 1813 --mRangeSerializer.mContextInfoDepth.mStart; 1814 } 1815 if (mRangeSerializer.mContextInfoDepth.mEnd) { 1816 --mRangeSerializer.mContextInfoDepth.mEnd; 1817 } 1818 count--; 1819 } 1820 1821 i = count; 1822 while (i > 0) { 1823 node = mRangeSerializer.mCommonInclusiveAncestors.ElementAt(--i); 1824 rv = mNodeSerializer.SerializeNodeStart(*node, 0, -1); 1825 NS_ENSURE_SUCCESS(rv, rv); 1826 } 1827 // i = 0; guaranteed by above 1828 while (i < count) { 1829 node = mRangeSerializer.mCommonInclusiveAncestors.ElementAt(i++); 1830 rv = mNodeSerializer.SerializeNodeEnd(*node); 1831 NS_ENSURE_SUCCESS(rv, rv); 1832 } 1833 1834 mSerializer->Finish(); 1835 1836 // encode range info : the start and end depth of the selection, where the 1837 // depth is distance down in the parent hierarchy. Later we will need to add 1838 // leading/trailing whitespace info to this. 1839 nsAutoString infoString; 1840 infoString.AppendInt(mRangeSerializer.mContextInfoDepth.mStart); 1841 infoString.Append(char16_t(',')); 1842 infoString.AppendInt(mRangeSerializer.mContextInfoDepth.mEnd); 1843 aInfoString = infoString; 1844 1845 return rv; 1846 } 1847 1848 bool nsHTMLCopyEncoder::RangeNodeContext::IncludeInContext( 1849 nsINode& aNode) const { 1850 const nsIContent* const content = nsIContent::FromNodeOrNull(&aNode); 1851 if (!content) { 1852 return false; 1853 } 1854 1855 // If it's an inline editing host, we should not treat it gives a context to 1856 // avoid to duplicate its style. 1857 if (content->IsEditingHost()) { 1858 return false; 1859 } 1860 1861 return content->IsAnyOfHTMLElements( 1862 nsGkAtoms::b, nsGkAtoms::i, nsGkAtoms::u, nsGkAtoms::a, nsGkAtoms::tt, 1863 nsGkAtoms::s, nsGkAtoms::big, nsGkAtoms::small, nsGkAtoms::strike, 1864 nsGkAtoms::em, nsGkAtoms::strong, nsGkAtoms::dfn, nsGkAtoms::code, 1865 nsGkAtoms::cite, nsGkAtoms::var, nsGkAtoms::abbr, nsGkAtoms::font, 1866 nsGkAtoms::script, nsGkAtoms::span, nsGkAtoms::pre, nsGkAtoms::h1, 1867 nsGkAtoms::h2, nsGkAtoms::h3, nsGkAtoms::h4, nsGkAtoms::h5, 1868 nsGkAtoms::h6); 1869 } 1870 1871 nsresult nsHTMLCopyEncoder::PromoteRange(nsRange* inRange) { 1872 if (!inRange->IsPositioned()) { 1873 return NS_ERROR_UNEXPECTED; 1874 } 1875 nsCOMPtr<nsINode> startNode = 1876 inRange->GetMayCrossShadowBoundaryStartContainer(); 1877 const uint32_t startOffset = inRange->MayCrossShadowBoundaryStartOffset(); 1878 nsCOMPtr<nsINode> endNode = inRange->GetMayCrossShadowBoundaryEndContainer(); 1879 const uint32_t endOffset = inRange->MayCrossShadowBoundaryEndOffset(); 1880 nsCOMPtr<nsINode> common = inRange->GetClosestCommonInclusiveAncestor( 1881 AllowRangeCrossShadowBoundary::Yes); 1882 1883 nsCOMPtr<nsINode> opStartNode; 1884 nsCOMPtr<nsINode> opEndNode; 1885 uint32_t opStartOffset, opEndOffset; 1886 1887 // examine range endpoints. 1888 nsresult rv = 1889 GetPromotedPoint(kStart, startNode, startOffset, address_of(opStartNode), 1890 &opStartOffset, common); 1891 NS_ENSURE_SUCCESS(rv, rv); 1892 rv = GetPromotedPoint(kEnd, endNode, endOffset, address_of(opEndNode), 1893 &opEndOffset, common); 1894 NS_ENSURE_SUCCESS(rv, rv); 1895 1896 // if both range endpoints are at the common ancestor, check for possible 1897 // inclusion of ancestors 1898 if (StaticPrefs::dom_serializer_includeCommonAncestor_enabled() && 1899 opStartNode == common && opEndNode == common) { 1900 rv = PromoteAncestorChain(address_of(opStartNode), &opStartOffset, 1901 &opEndOffset); 1902 NS_ENSURE_SUCCESS(rv, rv); 1903 opEndNode = opStartNode; 1904 } 1905 1906 // set the range to the new values 1907 ErrorResult err; 1908 inRange->SetStart(*opStartNode, opStartOffset, err, 1909 GetAllowRangeCrossShadowBoundary(mFlags)); 1910 if (NS_WARN_IF(err.Failed())) { 1911 return err.StealNSResult(); 1912 } 1913 inRange->SetEnd(*opEndNode, opEndOffset, err, 1914 GetAllowRangeCrossShadowBoundary(mFlags)); 1915 if (NS_WARN_IF(err.Failed())) { 1916 return err.StealNSResult(); 1917 } 1918 return NS_OK; 1919 } 1920 1921 // PromoteAncestorChain will promote a range represented by 1922 // [{*ioNode,*aIOStartOffset} , {*ioNode,*aIOEndOffset}] The promotion is 1923 // different from that found in getPromotedPoint: it will only promote one 1924 // endpoint if it can promote the other. Thus, instead of having a 1925 // startnode/endNode, there is just the one ioNode. 1926 nsresult nsHTMLCopyEncoder::PromoteAncestorChain(nsCOMPtr<nsINode>* ioNode, 1927 uint32_t* aIOStartOffset, 1928 uint32_t* aIOEndOffset) { 1929 if (!ioNode || !aIOStartOffset || !aIOEndOffset) { 1930 return NS_ERROR_NULL_POINTER; 1931 } 1932 1933 nsresult rv = NS_OK; 1934 bool done = false; 1935 1936 nsCOMPtr<nsINode> frontNode, endNode, parent; 1937 uint32_t frontOffset, endOffset; 1938 1939 // save the editable state of the ioNode, so we don't promote an ancestor if 1940 // it has different editable state 1941 nsCOMPtr<nsINode> node = *ioNode; 1942 bool isEditable = node->IsEditable(); 1943 1944 // loop for as long as we can promote both endpoints 1945 while (!done) { 1946 node = *ioNode; 1947 parent = node->GetParentNode(); 1948 if (!parent) { 1949 done = true; 1950 } else { 1951 // passing parent as last param to GetPromotedPoint() allows it to promote 1952 // only one level up the hierarchy. 1953 rv = GetPromotedPoint(kStart, *ioNode, *aIOStartOffset, 1954 address_of(frontNode), &frontOffset, parent); 1955 NS_ENSURE_SUCCESS(rv, rv); 1956 // then we make the same attempt with the endpoint 1957 rv = GetPromotedPoint(kEnd, *ioNode, *aIOEndOffset, address_of(endNode), 1958 &endOffset, parent); 1959 NS_ENSURE_SUCCESS(rv, rv); 1960 1961 // if both endpoints were promoted one level and isEditable is the same as 1962 // the original node, keep looping - otherwise we are done. 1963 if ((frontNode != parent) || (endNode != parent) || 1964 (frontNode->IsEditable() != isEditable)) 1965 done = true; 1966 else { 1967 *ioNode = frontNode; 1968 *aIOStartOffset = frontOffset; 1969 *aIOEndOffset = endOffset; 1970 } 1971 } 1972 } 1973 return rv; 1974 } 1975 1976 nsresult nsHTMLCopyEncoder::GetPromotedPoint( 1977 const Endpoint aWhere, nsINode* const aNode, const uint32_t aOffset, 1978 nsCOMPtr<nsINode>* aOutNode, uint32_t* aOutOffset, nsINode* const aCommon) { 1979 MOZ_ASSERT(aOutNode); 1980 MOZ_ASSERT(aOutOffset); 1981 1982 // default values 1983 *aOutNode = aNode; 1984 *aOutOffset = aOffset; 1985 1986 if (aCommon == aNode) { 1987 return NS_OK; 1988 } 1989 1990 nsresult rv = NS_OK; 1991 // XXX: These don’t seem to need to be strong pointers. 1992 nsCOMPtr<nsINode> node; 1993 nsCOMPtr<nsINode> parent; 1994 Maybe<uint32_t> offsetInParent; 1995 bool bResetPromotion = false; 1996 1997 if (aWhere == kStart) { 1998 // some special casing for text nodes 1999 if (auto nodeAsText = aNode->GetAsText()) { 2000 // if not at beginning of text node, we are done 2001 if (aOffset > 0) { 2002 // unless everything before us in just whitespace. NOTE: we need a more 2003 // general solution that truly detects all cases of non-significant 2004 // whitesace with no false alarms. 2005 if (!nodeAsText->TextStartsWithOnlyWhitespace(aOffset)) { 2006 return NS_OK; 2007 } 2008 bResetPromotion = true; 2009 } 2010 // else 2011 rv = GetNodeLocation(aNode, address_of(parent), &offsetInParent); 2012 NS_ENSURE_SUCCESS(rv, rv); 2013 node = aNode; 2014 } else { 2015 node = GetChildAt(aNode, aOffset); 2016 if (node) { 2017 parent = aNode; 2018 offsetInParent = Some(aOffset); 2019 } else { 2020 // XXX: Should we only start from aNode when aOffset is 0 and aNode has 2021 // no children? Currently we start from aNode even when aOffset is an 2022 // invalid offset, which seems wrong. 2023 node = aNode; 2024 } 2025 } 2026 MOZ_ASSERT(node); 2027 2028 // finding the real start for this point. look up the tree for as long as 2029 // we are the first node in the container, and as long as we haven't hit the 2030 // body node. 2031 if (!IsRoot(node) && parent != aCommon) { 2032 // XXX: We need to do this again because parent and offsetInParent might 2033 // not be set up properly above, but this also means it will be performed 2034 // twice on text nodes. Perhaps we could move this above and only do it 2035 // when needed? 2036 rv = GetNodeLocation(node, address_of(parent), &offsetInParent); 2037 NS_ENSURE_SUCCESS(rv, rv); 2038 // we hit generated content; STOP 2039 if (!offsetInParent) { 2040 return NS_OK; 2041 } 2042 2043 while (IsFirstNode(node) && !IsRoot(parent) && parent != aCommon) { 2044 if (bResetPromotion) { 2045 nsCOMPtr<nsIContent> content = nsIContent::FromNodeOrNull(parent); 2046 if (content && content->IsHTMLElement()) { 2047 if (nsHTMLElement::IsBlock( 2048 nsHTMLTags::AtomTagToId(content->NodeInfo()->NameAtom()))) { 2049 bResetPromotion = false; 2050 } 2051 } 2052 } 2053 2054 node = parent; 2055 rv = GetNodeLocation(node, address_of(parent), &offsetInParent); 2056 NS_ENSURE_SUCCESS(rv, rv); 2057 // we hit generated content; STOP 2058 if (!offsetInParent) { 2059 // back up a bit 2060 parent = node; 2061 offsetInParent = Some(0); 2062 break; 2063 } 2064 } 2065 2066 if (bResetPromotion) { 2067 *aOutNode = aNode; 2068 *aOutOffset = aOffset; 2069 } else { 2070 *aOutNode = parent; 2071 *aOutOffset = *offsetInParent; 2072 } 2073 return rv; 2074 } 2075 } 2076 2077 if (aWhere == kEnd) { 2078 // some special casing for text nodes 2079 if (auto nodeAsText = aNode->GetAsText()) { 2080 // if not at end of text node, we are done 2081 uint32_t len = aNode->Length(); 2082 if (aOffset < len) { 2083 // unless everything after us in just whitespace. NOTE: we need a more 2084 // general solution that truly detects all cases of non-significant 2085 // whitespace with no false alarms. 2086 if (!nodeAsText->TextEndsWithOnlyWhitespace(aOffset)) { 2087 return NS_OK; 2088 } 2089 bResetPromotion = true; 2090 } 2091 rv = GetNodeLocation(aNode, address_of(parent), &offsetInParent); 2092 NS_ENSURE_SUCCESS(rv, rv); 2093 node = aNode; 2094 } else { 2095 // we want node _before_ offset 2096 node = GetChildAt(aNode, aOffset ? aOffset - 1 : aOffset); 2097 if (node) { 2098 parent = aNode; 2099 offsetInParent = Some(aOffset); 2100 } else { 2101 // XXX: Should we only start from aNode when aOffset is 0 and aNode has 2102 // no children? Currently we start from aNode even when aOffset is an 2103 // invalid offset, which seems wrong. 2104 node = aNode; 2105 } 2106 } 2107 MOZ_ASSERT(node); 2108 2109 // finding the real end for this point. look up the tree for as long as we 2110 // are the last node in the container, and as long as we haven't hit the 2111 // body node. 2112 if (!IsRoot(node) && parent != aCommon) { 2113 // XXX: We need to do this again because parent and offsetInParent might 2114 // not be set up properly above, but this also means it will be performed 2115 // twice on text nodes. Perhaps we could move this above and only do it 2116 // when needed? 2117 rv = GetNodeLocation(node, address_of(parent), &offsetInParent); 2118 NS_ENSURE_SUCCESS(rv, rv); 2119 // we hit generated content; STOP 2120 if (!offsetInParent) { 2121 return NS_OK; 2122 } 2123 2124 while (IsLastNode(node) && !IsRoot(parent) && parent != aCommon) { 2125 if (bResetPromotion) { 2126 nsCOMPtr<nsIContent> content = nsIContent::FromNodeOrNull(parent); 2127 if (content && content->IsHTMLElement()) { 2128 if (nsHTMLElement::IsBlock( 2129 nsHTMLTags::AtomTagToId(content->NodeInfo()->NameAtom()))) { 2130 bResetPromotion = false; 2131 } 2132 } 2133 } 2134 2135 node = parent; 2136 rv = GetNodeLocation(node, address_of(parent), &offsetInParent); 2137 NS_ENSURE_SUCCESS(rv, rv); 2138 2139 // When node is the shadow root and parent is the shadow host, 2140 // the offsetInParent would also be -1, and we'd like to keep going. 2141 const bool isGeneratedContent = 2142 !offsetInParent && 2143 ShadowDOMSelectionHelpers::GetShadowRoot( 2144 parent, GetAllowRangeCrossShadowBoundary(mFlags)) != node; 2145 // we hit generated content; STOP 2146 if (isGeneratedContent) { 2147 // back up a bit 2148 parent = node; 2149 offsetInParent = Some(0); 2150 break; 2151 } 2152 } 2153 2154 if (bResetPromotion) { 2155 *aOutNode = aNode; 2156 *aOutOffset = aOffset; 2157 } else { 2158 *aOutNode = parent; 2159 // add one since this in an endpoint - want to be AFTER node. 2160 *aOutOffset = *offsetInParent + 1; 2161 } 2162 return rv; 2163 } 2164 } 2165 2166 return rv; 2167 } 2168 2169 nsCOMPtr<nsINode> nsHTMLCopyEncoder::GetChildAt(nsINode* aParent, 2170 const uint32_t aOffset) { 2171 nsCOMPtr<nsINode> resultNode; 2172 2173 if (!aParent) { 2174 return resultNode; 2175 } 2176 2177 nsCOMPtr<nsIContent> content = nsIContent::FromNodeOrNull(aParent); 2178 MOZ_ASSERT(content, "null content in nsHTMLCopyEncoder::GetChildAt"); 2179 2180 resultNode = content->GetChildAt_Deprecated(aOffset); 2181 2182 return resultNode; 2183 } 2184 2185 bool nsHTMLCopyEncoder::IsMozBR(Element* aElement) { 2186 HTMLBRElement* brElement = HTMLBRElement::FromNodeOrNull(aElement); 2187 return brElement && brElement->IsPaddingForEmptyLastLine(); 2188 } 2189 2190 nsresult nsHTMLCopyEncoder::GetNodeLocation( 2191 nsINode* const aInChild, nsCOMPtr<nsINode>* aOutParent, 2192 Maybe<uint32_t>* aOutOffsetInParent) { 2193 NS_ASSERTION(aInChild && aOutParent && aOutOffsetInParent, "bad args"); 2194 2195 if (aInChild && aOutParent && aOutOffsetInParent) { 2196 nsCOMPtr<nsIContent> child = nsIContent::FromNodeOrNull(aInChild); 2197 if (!child) { 2198 return NS_ERROR_NULL_POINTER; 2199 } 2200 2201 nsINode* parent = mFlags & nsIDocumentEncoder::AllowCrossShadowBoundary 2202 ? child->GetFlattenedTreeParentNodeForSelection() 2203 : child->GetParent(); 2204 if (!parent) { 2205 return NS_ERROR_NULL_POINTER; 2206 } 2207 2208 *aOutParent = parent; 2209 *aOutOffsetInParent = mFlags & nsIDocumentEncoder::AllowCrossShadowBoundary 2210 ? parent->ComputeFlatTreeIndexOf(child) 2211 : parent->ComputeIndexOf(child); 2212 2213 return NS_OK; 2214 } 2215 2216 return NS_ERROR_NULL_POINTER; 2217 } 2218 2219 bool nsHTMLCopyEncoder::IsRoot(nsINode* aNode) { 2220 nsCOMPtr<nsIContent> content = nsIContent::FromNodeOrNull(aNode); 2221 if (!content) { 2222 return false; 2223 } 2224 2225 if (mIsTextWidget) { 2226 return content->IsHTMLElement(nsGkAtoms::div); 2227 } 2228 2229 // XXX(sefeng): This is some old code from 2006, so I can't 2230 // promise my comment is correct. However, I think these elements 2231 // are considered to be `Root` because if we keep going up 2232 // in nsHTMLCopyEncoder::GetPromotedPoint, we would lose the 2233 // correct representation of the point, so we have to stop at 2234 // these nodes. 2235 2236 // nsGkAtoms::slot is here because we'd lose the index 2237 // of the slotted element if we keep going up as 2238 // `nsHTMLCopyEncoder::GetNodeLocation` would promote the 2239 // offset to be index of the <slot> that is relative to 2240 // the <slot>'s parent. 2241 return content->IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::td, 2242 nsGkAtoms::th, nsGkAtoms::slot); 2243 } 2244 2245 bool nsHTMLCopyEncoder::IsFirstNode(nsINode* aNode) { 2246 // need to check if any nodes before us are really visible. 2247 // Mike wrote something for me along these lines in nsSelectionController, 2248 // but I don't think it's ready for use yet - revisit. 2249 // HACK: for now, simply consider all whitespace text nodes to be 2250 // invisible formatting nodes. 2251 for (nsIContent* sibling = aNode->GetPreviousSibling(); sibling; 2252 sibling = sibling->GetPreviousSibling()) { 2253 if (!sibling->TextIsOnlyWhitespace()) { 2254 return false; 2255 } 2256 } 2257 2258 return true; 2259 } 2260 2261 bool nsHTMLCopyEncoder::IsLastNode(nsINode* aNode) { 2262 // need to check if any nodes after us are really visible. 2263 // Mike wrote something for me along these lines in nsSelectionController, 2264 // but I don't think it's ready for use yet - revisit. 2265 // HACK: for now, simply consider all whitespace text nodes to be 2266 // invisible formatting nodes. 2267 for (nsIContent* sibling = aNode->GetNextSibling(); sibling; 2268 sibling = sibling->GetNextSibling()) { 2269 if (sibling->IsElement() && IsMozBR(sibling->AsElement())) { 2270 // we ignore trailing moz BRs. 2271 continue; 2272 } 2273 if (!sibling->TextIsOnlyWhitespace()) { 2274 return false; 2275 } 2276 } 2277 2278 return true; 2279 } 2280 2281 already_AddRefed<nsIDocumentEncoder> do_createHTMLCopyEncoder() { 2282 return do_AddRef(new nsHTMLCopyEncoder); 2283 } 2284 2285 int32_t nsHTMLCopyEncoder::RangeNodeContext::GetImmediateContextCount( 2286 const nsTArray<nsINode*>& aAncestorArray) const { 2287 int32_t i = aAncestorArray.Length(), j = 0; 2288 while (j < i) { 2289 nsINode* node = aAncestorArray.ElementAt(j); 2290 if (!node) { 2291 break; 2292 } 2293 nsCOMPtr<nsIContent> content(nsIContent::FromNodeOrNull(node)); 2294 if (!content || !content->IsAnyOfHTMLElements( 2295 nsGkAtoms::tr, nsGkAtoms::thead, nsGkAtoms::tbody, 2296 nsGkAtoms::tfoot, nsGkAtoms::table)) { 2297 break; 2298 } 2299 ++j; 2300 } 2301 return j; 2302 }