Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/serializers/./../../../dom/serializers/nsDocumentEncoder.cpp
Warning:line 1569, column 3
Value stored to 'rv' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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