Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/serializers/./../../../dom/serializers/nsDocumentEncoder.cpp
Warning:line 1570, 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-30-101350-841527-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/Range.h"
30#include "mozilla/dom/Selection.h"
31#include "mozilla/dom/ShadowRoot.h"
32#include "mozilla/dom/Text.h"
33#include "nsCOMPtr.h"
34#include "nsCRT.h"
35#include "nsComponentManagerUtils.h"
36#include "nsContentUtils.h"
37#include "nsElementTable.h"
38#include "nsGkAtoms.h"
39#include "nsHTMLDocument.h"
40#include "nsIContent.h"
41#include "nsIContentInlines.h"
42#include "nsIContentSerializer.h"
43#include "nsIDocumentEncoder.h"
44#include "nsIFrame.h"
45#include "nsINode.h"
46#include "nsIOutputStream.h"
47#include "nsIScriptContext.h"
48#include "nsIScriptGlobalObject.h"
49#include "nsISupports.h"
50#include "nsITransferable.h"
51#include "nsLayoutUtils.h"
52#include "nsMimeTypes.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<dom::Range> 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(mozilla::dom::Range *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 dom::Range* 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 dom::Range* aRange,
456 int32_t aDepth);
457
458 nsresult SerializeRangeToString(const dom::Range* 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 dom::Range& aRange, int32_t aDepth);
486
487 nsresult SerializeTextNode(nsIContent& aContent,
488 const StartAndEndContent& aStartAndEndContent,
489 const dom::Range& 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 (dom::Range* 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 dom::Range> 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(dom::Range* 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 dom::Range& 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 dom::Range* const aRange, nsINode* const aNode,
1113 const int32_t aDepth) {
1114 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", 1114); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aDepth >= 0" ")"); do { MOZ_CrashSequence
(__null, 1114); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1115 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"
, 1115); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aRange" ")"
); do { MOZ_CrashSequence(__null, 1115); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1116
1117 nsCOMPtr<nsIContent> content = nsIContent::FromNodeOrNull(aNode);
1118 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", 1118); return
NS_ERROR_FAILURE; } } while (false)
;
1119
1120 if (nsDocumentEncoder::IsInvisibleNodeAndShouldBeSkipped(*aNode, mFlags)) {
1121 return NS_OK;
1122 }
1123
1124 nsresult rv = NS_OK;
1125
1126 StartAndEndContent startAndEndContent =
1127 GetStartAndEndContentForRecursionLevel(aDepth);
1128
1129 if (startAndEndContent.mStart != content &&
1130 startAndEndContent.mEnd != content) {
1131 // node is completely contained in range. Serialize the whole subtree
1132 // rooted by this node.
1133 rv = mNodeSerializer.SerializeToStringRecursive(
1134 aNode, NodeSerializer::SerializeRoot::eYes);
1135 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"
, 1135); return rv; } } while (false)
;
1136 } else {
1137 rv = SerializeNodePartiallyContainedInRange(*content, startAndEndContent,
1138 *aRange, aDepth);
1139 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"
, 1139)
) {
1140 return rv;
1141 }
1142 }
1143 return NS_OK;
1144}
1145
1146nsresult
1147nsDocumentEncoder::RangeSerializer::SerializeNodePartiallyContainedInRange(
1148 nsIContent& aContent, const StartAndEndContent& aStartAndEndContent,
1149 const dom::Range& aRange, const int32_t aDepth) {
1150 // due to implementation it is impossible for text node to be both start and
1151 // end of range. We would have handled that case without getting here.
1152 // XXXsmaug What does this all mean?
1153 if (IsTextNode(&aContent)) {
1154 nsresult rv = SerializeTextNode(aContent, aStartAndEndContent, aRange);
1155 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"
, 1155); return rv; } } while (false)
;
1156 } else {
1157 if (&aContent != mClosestCommonInclusiveAncestorOfRange) {
1158 if (mRangeContextSerializer.mRangeNodeContext.IncludeInContext(
1159 aContent)) {
1160 // halt the incrementing of mContextInfoDepth. This
1161 // is so paste client will include this node in paste.
1162 mHaltRangeHint = true;
1163 }
1164 if ((aStartAndEndContent.mStart == &aContent) && !mHaltRangeHint) {
1165 ++mContextInfoDepth.mStart;
1166 }
1167 if ((aStartAndEndContent.mEnd == &aContent) && !mHaltRangeHint) {
1168 ++mContextInfoDepth.mEnd;
1169 }
1170
1171 // serialize the start of this node
1172 nsresult rv = mNodeSerializer.SerializeNodeStart(aContent, 0, -1);
1173 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"
, 1173); return rv; } } while (false)
;
1174 }
1175
1176 const auto& inclusiveAncestorsOffsetsOfStart =
1177 mRangeBoundariesInclusiveAncestorsAndOffsets
1178 .mInclusiveAncestorsOffsetsOfStart;
1179 const auto& inclusiveAncestorsOffsetsOfEnd =
1180 mRangeBoundariesInclusiveAncestorsAndOffsets
1181 .mInclusiveAncestorsOffsetsOfEnd;
1182 // do some calculations that will tell us which children of this
1183 // node are in the range.
1184 Maybe<uint32_t> startOffset = Some(0);
1185 Maybe<uint32_t> endOffset;
1186 if (aStartAndEndContent.mStart == &aContent && mStartRootIndex >= aDepth) {
1187 startOffset = inclusiveAncestorsOffsetsOfStart[mStartRootIndex - aDepth];
1188 }
1189 if (aStartAndEndContent.mEnd == &aContent && mEndRootIndex >= aDepth) {
1190 endOffset = inclusiveAncestorsOffsetsOfEnd[mEndRootIndex - aDepth];
1191 }
1192 // generated aContent will cause offset values of Nothing to be returned.
1193 if (startOffset.isNothing()) {
1194 startOffset = Some(0);
1195 }
1196 if (endOffset.isNothing()) {
1197 endOffset = Some(aContent.GetChildCount());
1198
1199 if (mAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) {
1200 if (const auto* slot = HTMLSlotElement::FromNode(aContent)) {
1201 const auto& assignedNodes = slot->AssignedNodes();
1202 if (!assignedNodes.IsEmpty()) {
1203 endOffset = Some(assignedNodes.Length());
1204 }
1205 }
1206 }
1207 } else {
1208 // if we are at the "tip" of the selection, endOffset is fine.
1209 // otherwise, we need to add one. This is because of the semantics
1210 // of the offset list created by GetInclusiveAncestorsAndOffsets(). The
1211 // intermediate points on the list use the endOffset of the
1212 // location of the ancestor, rather than just past it. So we need
1213 // to add one here in order to include it in the children we serialize.
1214 const nsINode* endContainer = ShadowDOMSelectionHelpers::GetEndContainer(
1215 &aRange, mAllowCrossShadowBoundary);
1216 if (&aContent != endContainer) {
1217 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", 1217); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "*endOffset != (4294967295U)" ")"); do { MOZ_CrashSequence
(__null, 1217); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1218 endOffset.ref()++;
1219 }
1220 }
1221
1222 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", 1222); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "endOffset.isSome()" ")"); do { MOZ_CrashSequence
(__null, 1222); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1223 nsresult rv = SerializeChildrenOfContent(aContent, *startOffset, *endOffset,
1224 &aRange, aDepth);
1225 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"
, 1225); return rv; } } while (false)
;
1226
1227 // serialize the end of this node
1228 if (&aContent != mClosestCommonInclusiveAncestorOfRange) {
1229 nsresult rv = mNodeSerializer.SerializeNodeEnd(aContent);
1230 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"
, 1230); return rv; } } while (false)
;
1231 }
1232 }
1233
1234 return NS_OK;
1235}
1236
1237nsresult nsDocumentEncoder::RangeSerializer::SerializeChildrenOfContent(
1238 nsIContent& aContent, uint32_t aStartOffset, uint32_t aEndOffset,
1239 const dom::Range* aRange, int32_t aDepth) {
1240 ShadowRoot* shadowRoot = ShadowDOMSelectionHelpers::GetShadowRoot(
1241 &aContent, mAllowCrossShadowBoundary);
1242 if (shadowRoot) {
1243 // Serialize the ShadowRoot when the entire node needs to be serialized.
1244 // Return early to skip light DOM children.
1245 SerializeRangeNodes(aRange, shadowRoot, aDepth + 1);
1246 return NS_OK;
1247 }
1248
1249 if (!aEndOffset) {
1250 return NS_OK;
1251 }
1252
1253 nsIContent* child =
1254 mAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes
1255 ? aContent.GetChildAtInFlatTreeForSelection(aStartOffset)
1256 : aContent.GetChildAt_Deprecated(aStartOffset);
1257
1258 auto GetNextSibling = [this, &aContent](
1259 nsINode* aCurrentNode,
1260 uint32_t aCurrentIndex) -> nsIContent* {
1261 if (mAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) {
1262 if (const auto* slot = HTMLSlotElement::FromNode(&aContent)) {
1263 auto assigned = slot->AssignedNodes();
1264 if (++aCurrentIndex < assigned.Length()) {
1265 return nsIContent::FromNode(assigned[aCurrentIndex]);
1266 }
1267 return nullptr;
1268 }
1269 }
1270
1271 return aCurrentNode->GetNextSibling();
1272 };
1273
1274 for (size_t j = aStartOffset; child && j < aEndOffset; ++j) {
1275 nsresult rv{NS_OK};
1276 const bool isFirstOrLastNodeToSerialize =
1277 j == aStartOffset || j == aEndOffset - 1;
1278 if (isFirstOrLastNodeToSerialize) {
1279 rv = SerializeRangeNodes(aRange, child, aDepth + 1);
1280 } else {
1281 rv = mNodeSerializer.SerializeToStringRecursive(
1282 child, NodeSerializer::SerializeRoot::eYes);
1283 }
1284
1285 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
1286 return rv;
1287 }
1288
1289 child = GetNextSibling(child, j);
1290 }
1291
1292 return NS_OK;
1293}
1294
1295bool nsDocumentEncoder::RangeNodeContext::IncludeInContext(
1296 nsINode& aNode) const {
1297 // Thunderbird wraps quoted replies in <span _moz_quote="true">, styled
1298 // pre-wrap so the plaintext serializer leaves the "> " lines unwrapped.
1299 // Re-emit it as context; otherwise selecting only its contents drops the
1300 // span's start tag and the quote is re-wrapped without its "> " markers.
1301 const nsIContent* const content = nsIContent::FromNodeOrNull(&aNode);
1302 return content && content->IsHTMLElement(nsGkAtoms::span) &&
1303 content->AsElement()->HasAttr(nsGkAtoms::mozquote);
1304}
1305
1306nsresult nsDocumentEncoder::RangeContextSerializer::SerializeRangeContextStart(
1307 const nsTArray<nsINode*>& aAncestorArray) {
1308 if (mDisableContextSerialize) {
1309 return NS_OK;
1310 }
1311
1312 AutoTArray<nsINode*, 8>* serializedContext = mRangeContexts.AppendElement();
1313
1314 int32_t i = aAncestorArray.Length(), j;
1315 nsresult rv = NS_OK;
1316
1317 // currently only for table-related elements; see Bug 137450
1318 j = mRangeNodeContext.GetImmediateContextCount(aAncestorArray);
1319
1320 while (i > 0) {
1321 nsINode* node = aAncestorArray.ElementAt(--i);
1322 if (!node) break;
1323
1324 // Either a general inclusion or as immediate context
1325 if (mRangeNodeContext.IncludeInContext(*node) || i < j) {
1326 rv = mNodeSerializer.SerializeNodeStart(*node, 0, -1);
1327 serializedContext->AppendElement(node);
1328 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) break;
1329 }
1330 }
1331
1332 return rv;
1333}
1334
1335nsresult nsDocumentEncoder::RangeContextSerializer::SerializeRangeContextEnd() {
1336 if (mDisableContextSerialize) {
1337 return NS_OK;
1338 }
1339
1340 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"
, 1341); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!mRangeContexts.IsEmpty()"
") (" "Tried to end context without starting one." ")"); do {
MOZ_CrashSequence(__null, 1341); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
1341 "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"
, 1341); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!mRangeContexts.IsEmpty()"
") (" "Tried to end context without starting one." ")"); do {
MOZ_CrashSequence(__null, 1341); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
1342 AutoTArray<nsINode*, 8>& serializedContext = mRangeContexts.LastElement();
1343
1344 nsresult rv = NS_OK;
1345 for (nsINode* node : Reversed(serializedContext)) {
1346 rv = mNodeSerializer.SerializeNodeEnd(*node);
1347
1348 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) break;
1349 }
1350
1351 mRangeContexts.RemoveLastElement();
1352 return rv;
1353}
1354
1355bool nsDocumentEncoder::RangeSerializer::HasInvisibleParentAndShouldBeSkipped(
1356 nsINode& aNode) const {
1357 if (!(mFlags & SkipInvisibleContent)) {
1358 return false;
1359 }
1360
1361 // Check that the parent is visible if we don't a frame.
1362 // IsInvisibleNodeAndShouldBeSkipped() will do it when there's a frame.
1363 nsCOMPtr<nsIContent> content = nsIContent::FromNode(aNode);
1364 if (content && !content->GetPrimaryFrame()) {
1365 nsIContent* parent = content->GetParent();
1366 return !parent || IsInvisibleNodeAndShouldBeSkipped(*parent, mFlags);
1367 }
1368
1369 return false;
1370}
1371
1372nsresult nsDocumentEncoder::RangeSerializer::SerializeRangeToString(
1373 const dom::Range* aRange) {
1374 if (!aRange ||
1375 (aRange->Collapsed() &&
1376 (mAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::No ||
1377 !aRange->MayCrossShadowBoundary()))) {
1378 return NS_OK;
1379 }
1380
1381 // Consider a case where the boundary of the selection is ShadowRoot (ie, the
1382 // first child of ShadowRoot is selected, so ShadowRoot is the container hence
1383 // the boundary), allowing GetClosestCommonInclusiveAncestor to cross the
1384 // boundary can return the host element as the container.
1385 // SerializeRangeContextStart doesn't support this case.
1386 mClosestCommonInclusiveAncestorOfRange =
1387 aRange->GetClosestCommonInclusiveAncestor(mAllowCrossShadowBoundary);
1388
1389 if (!mClosestCommonInclusiveAncestorOfRange) {
1390 return NS_OK;
1391 }
1392
1393 nsINode* startContainer = ShadowDOMSelectionHelpers::GetStartContainer(
1394 aRange, mAllowCrossShadowBoundary);
1395 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"
, 1395); return NS_ERROR_FAILURE; } } while (false)
;
1396 const int32_t startOffset =
1397 ShadowDOMSelectionHelpers::StartOffset(aRange, mAllowCrossShadowBoundary);
1398
1399 nsINode* endContainer = ShadowDOMSelectionHelpers::GetEndContainer(
1400 aRange, mAllowCrossShadowBoundary);
1401 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"
, 1401); return NS_ERROR_FAILURE; } } while (false)
;
1402 const int32_t endOffset =
1403 ShadowDOMSelectionHelpers::EndOffset(aRange, mAllowCrossShadowBoundary);
1404
1405 mContextInfoDepth = {};
1406 mCommonInclusiveAncestors.Clear();
1407
1408 mRangeBoundariesInclusiveAncestorsAndOffsets = {};
1409 auto& inclusiveAncestorsOfStart =
1410 mRangeBoundariesInclusiveAncestorsAndOffsets.mInclusiveAncestorsOfStart;
1411 auto& inclusiveAncestorsOffsetsOfStart =
1412 mRangeBoundariesInclusiveAncestorsAndOffsets
1413 .mInclusiveAncestorsOffsetsOfStart;
1414 auto& inclusiveAncestorsOfEnd =
1415 mRangeBoundariesInclusiveAncestorsAndOffsets.mInclusiveAncestorsOfEnd;
1416 auto& inclusiveAncestorsOffsetsOfEnd =
1417 mRangeBoundariesInclusiveAncestorsAndOffsets
1418 .mInclusiveAncestorsOffsetsOfEnd;
1419
1420 nsContentUtils::GetInclusiveAncestors(mClosestCommonInclusiveAncestorOfRange,
1421 mCommonInclusiveAncestors);
1422 if (mAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) {
1423 nsContentUtils::GetFlattenedTreeAncestorsAndOffsetsForSelection(
1424 startContainer, startOffset, inclusiveAncestorsOfStart,
1425 inclusiveAncestorsOffsetsOfStart);
1426 nsContentUtils::GetFlattenedTreeAncestorsAndOffsetsForSelection(
1427 endContainer, endOffset, inclusiveAncestorsOfEnd,
1428 inclusiveAncestorsOffsetsOfEnd);
1429 } else {
1430 nsContentUtils::GetInclusiveAncestorsAndOffsets(
1431 startContainer, startOffset, inclusiveAncestorsOfStart,
1432 inclusiveAncestorsOffsetsOfStart);
1433 nsContentUtils::GetInclusiveAncestorsAndOffsets(
1434 endContainer, endOffset, inclusiveAncestorsOfEnd,
1435 inclusiveAncestorsOffsetsOfEnd);
1436 }
1437
1438 nsCOMPtr<nsIContent> commonContent =
1439 nsIContent::FromNodeOrNull(mClosestCommonInclusiveAncestorOfRange);
1440 mStartRootIndex = inclusiveAncestorsOfStart.IndexOf(commonContent);
1441 mEndRootIndex = inclusiveAncestorsOfEnd.IndexOf(commonContent);
1442
1443 nsresult rv = NS_OK;
1444
1445 rv = mRangeContextSerializer.SerializeRangeContextStart(
1446 mCommonInclusiveAncestors);
1447 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"
, 1447); return rv; } } while (false)
;
1448
1449 if (startContainer == endContainer && IsTextNode(startContainer)) {
1450 if (HasInvisibleParentAndShouldBeSkipped(*startContainer)) {
1451 return NS_OK;
1452 }
1453 rv = mNodeSerializer.SerializeTextNode(*startContainer, startOffset,
1454 endOffset);
1455 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"
, 1455); return rv; } } while (false)
;
1456 } else {
1457 rv = SerializeRangeNodes(aRange, mClosestCommonInclusiveAncestorOfRange, 0);
1458 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"
, 1458); return rv; } } while (false)
;
1459 }
1460 rv = mRangeContextSerializer.SerializeRangeContextEnd();
1461 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"
, 1461); return rv; } } while (false)
;
1462
1463 return rv;
1464}
1465
1466void nsDocumentEncoder::ReleaseDocumentReferenceAndInitialize(
1467 bool aClearCachedSerializer) {
1468 mDocument = nullptr;
1469
1470 Initialize(aClearCachedSerializer);
1471}
1472
1473NS_IMETHODIMPnsresult
1474nsDocumentEncoder::EncodeToString(nsAString& aOutputString) {
1475 return EncodeToStringWithMaxLength(0, aOutputString);
1476}
1477
1478NS_IMETHODIMPnsresult
1479nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
1480 nsAString& aOutputString) {
1481 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"
, 1482); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mRangeContextSerializer.mRangeContexts.IsEmpty()"
") (" "Re-entrant call to nsDocumentEncoder." ")"); do { MOZ_CrashSequence
(__null, 1482); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
1482 "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"
, 1482); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mRangeContextSerializer.mRangeContexts.IsEmpty()"
") (" "Re-entrant call to nsDocumentEncoder." ")"); do { MOZ_CrashSequence
(__null, 1482); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1483 auto rangeContextGuard =
1484 MakeScopeExit([&] { mRangeContextSerializer.mRangeContexts.Clear(); });
1485
1486 if (!mDocument) return NS_ERROR_NOT_INITIALIZED;
1487
1488 AutoReleaseDocumentIfNeeded autoReleaseDocument(this);
1489
1490 aOutputString.Truncate();
1491
1492 nsString output;
1493 static const size_t kStringBufferSizeInBytes = 2048;
1494 if (!mCachedBuffer) {
1495 mCachedBuffer = StringBuffer::Alloc(kStringBufferSizeInBytes);
1496 if (NS_WARN_IF(!mCachedBuffer)NS_warn_if_impl(!mCachedBuffer, "!mCachedBuffer", "./../../../dom/serializers/nsDocumentEncoder.cpp"
, 1496)
) {
1497 return NS_ERROR_OUT_OF_MEMORY;
1498 }
1499 }
1500 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"
, 1502); MOZ_PretendNoReturn(); } } while (0)
1501 !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"
, 1502); MOZ_PretendNoReturn(); } } while (0)
1502 "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"
, 1502); MOZ_PretendNoReturn(); } } while (0)
;
1503 static_cast<char16_t*>(mCachedBuffer->Data())[0] = char16_t(0);
1504 output.Assign(mCachedBuffer.forget(), 0);
1505
1506 if (!mSerializer) {
1507 nsAutoCString progId(NS_CONTENTSERIALIZER_CONTRACTID_PREFIX"@mozilla.org/layout/contentserializer;1?mimetype=");
1508 AppendUTF16toUTF8(mMimeType, progId);
1509
1510 mSerializer = do_CreateInstance(progId.get());
1511 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"
, 1511); return NS_ERROR_NOT_IMPLEMENTED; } } while (false)
;
1512 }
1513
1514 nsresult rv = NS_OK;
1515
1516 bool rewriteEncodingDeclaration =
1517 !mEncodingScope.IsLimited() &&
1518 !(mFlags & OutputDontRewriteEncodingDeclaration);
1519 mSerializer->Init(mFlags, mWrapColumn, mEncoding, mIsCopying,
1520 rewriteEncodingDeclaration, &mNeedsPreformatScanning,
1521 output);
1522
1523 rv = SerializeDependingOnScope(aMaxLength);
1524 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"
, 1524); return rv; } } while (false)
;
1525
1526 rv = mSerializer->FlushAndFinish();
1527
1528 // We have to be careful how we set aOutputString, because we don't
1529 // want it to end up sharing mCachedBuffer if we plan to reuse it.
1530 bool setOutput = false;
1531 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", 1531); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!mCachedBuffer" ")"); do { MOZ_CrashSequence
(__null, 1531); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1532 // Try to cache the buffer.
1533 if (StringBuffer* outputBuffer = output.GetOwnedStringBuffer()) {
1534 if (outputBuffer->StorageSize() == kStringBufferSizeInBytes &&
1535 !outputBuffer->IsReadonly()) {
1536 mCachedBuffer = outputBuffer;
1537 } else if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) {
1538 aOutputString.Assign(outputBuffer, output.Length());
1539 setOutput = true;
1540 }
1541 }
1542
1543 if (!setOutput && NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) {
1544 aOutputString.Append(output.get(), output.Length());
1545 }
1546
1547 return rv;
1548}
1549
1550NS_IMETHODIMPnsresult
1551nsDocumentEncoder::EncodeToStream(nsIOutputStream* aStream) {
1552 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"
, 1553); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mRangeContextSerializer.mRangeContexts.IsEmpty()"
") (" "Re-entrant call to nsDocumentEncoder." ")"); do { MOZ_CrashSequence
(__null, 1553); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
1553 "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"
, 1553); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mRangeContextSerializer.mRangeContexts.IsEmpty()"
") (" "Re-entrant call to nsDocumentEncoder." ")"); do { MOZ_CrashSequence
(__null, 1553); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1554 auto rangeContextGuard =
1555 MakeScopeExit([&] { mRangeContextSerializer.mRangeContexts.Clear(); });
1556 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", 1556); return
NS_ERROR_INVALID_POINTER; } } while (false)
;
1557
1558 nsresult rv = NS_OK;
1559
1560 if (!mDocument) return NS_ERROR_NOT_INITIALIZED;
1561
1562 if (!mEncoding) {
1563 return NS_ERROR_UCONV_NOCONV;
1564 }
1565
1566 nsAutoString buf;
1567 const bool isPlainText = mMimeType.LowerCaseEqualsLiteral(kTextMime"text/plain");
1568 mTextStreamer.emplace(*aStream, mEncoding->NewEncoder(), isPlainText, buf);
1569
1570 rv = EncodeToString(buf);
Value stored to 'rv' is never read
1571
1572 // Force a flush of the last chunk of data.
1573 rv = mTextStreamer->ForceFlush();
1574 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"
, 1574); return rv; } } while (false)
;
1575
1576 mTextStreamer.reset();
1577
1578 return rv;
1579}
1580
1581NS_IMETHODIMPnsresult
1582nsDocumentEncoder::EncodeToStringWithContext(nsAString& aContextString,
1583 nsAString& aInfoString,
1584 nsAString& aEncodedString) {
1585 return NS_ERROR_NOT_IMPLEMENTED;
1586}
1587
1588NS_IMETHODIMPnsresult
1589nsDocumentEncoder::SetNodeFixup(nsIDocumentEncoderNodeFixup* aFixup) {
1590 mNodeFixup = aFixup;
1591 return NS_OK;
1592}
1593
1594bool do_getDocumentTypeSupportedForEncoding(const char* aContentType) {
1595 if (!nsCRT::strcmp(aContentType, TEXT_XML"text/xml") ||
1596 !nsCRT::strcmp(aContentType, APPLICATION_XML"application/xml") ||
1597 !nsCRT::strcmp(aContentType, APPLICATION_XHTML_XML"application/xhtml+xml") ||
1598 !nsCRT::strcmp(aContentType, IMAGE_SVG_XML"image/svg+xml") ||
1599 !nsCRT::strcmp(aContentType, TEXT_HTML"text/html") ||
1600 !nsCRT::strcmp(aContentType, TEXT_PLAIN"text/plain")) {
1601 return true;
1602 }
1603 return false;
1604}
1605
1606already_AddRefed<nsIDocumentEncoder> do_createDocumentEncoder(
1607 const char* aContentType) {
1608 if (do_getDocumentTypeSupportedForEncoding(aContentType)) {
1609 return do_AddRef(new nsDocumentEncoder);
1610 }
1611 return nullptr;
1612}
1613
1614class nsHTMLCopyEncoder final : public nsDocumentEncoder {
1615 private:
1616 class RangeNodeContext final : public nsDocumentEncoder::RangeNodeContext {
1617 bool IncludeInContext(nsINode& aNode) const final;
1618
1619 int32_t GetImmediateContextCount(
1620 const nsTArray<nsINode*>& aAncestorArray) const final;
1621 };
1622
1623 public:
1624 nsHTMLCopyEncoder();
1625 ~nsHTMLCopyEncoder();
1626
1627 NS_IMETHODvirtual nsresult Init(Document* aDocument, const nsAString& aMimeType,
1628 uint32_t aFlags) override;
1629
1630 // overridden methods from nsDocumentEncoder
1631 MOZ_CAN_RUN_SCRIPT_BOUNDARY
1632 NS_IMETHODvirtual nsresult SetSelection(Selection* aSelection) override;
1633 NS_IMETHODvirtual nsresult EncodeToStringWithContext(nsAString& aContextString,
1634 nsAString& aInfoString,
1635 nsAString& aEncodedString) override;
1636 NS_IMETHODvirtual nsresult EncodeToString(nsAString& aOutputString) override;
1637
1638 protected:
1639 [[nodiscard]] TreeKind GetTreeKind() const {
1640 return mFlags & nsIDocumentEncoder::AllowCrossShadowBoundary
1641 ? TreeKind::FlatForSelection
1642 : TreeKind::DOM;
1643 }
1644 nsresult PromoteRange(dom::Range* inRange);
1645
1646 /**
1647 * Return a promoted start point which may be extended to a point at an
1648 * ancestor element or error. This climbs up the flattened tree if
1649 * aPoint.GetTreeKind() is TreeKind::FlatForSelection.
1650 *
1651 * @param aPoint Must be set to a valid point.
1652 * @param aCommon This is used as an ancestor limiter when climbing up the
1653 * tree.
1654 * @return If it's not an error, the boundary is always set.
1655 */
1656 Result<RawRangeBoundary, nsresult> GetPromotedStartPoint(
1657 const RawRangeBoundary& aPoint, const nsINode* const aCommon) const;
1658
1659 /**
1660 * Return a promoted end point which may be extended to a point after an
1661 * ancestor element or error. This climbs up the flattened tree if
1662 * aPoint.GetTreeKind() is TreeKind::FlatForSelection.
1663 *
1664 * @param aPoint Must be set to a valid point.
1665 * @param aCommon This is used as an ancestor limiter when climbing up the
1666 * tree.
1667 * @return If it's not an error, the boundary is always set.
1668 */
1669 Result<RawRangeBoundary, nsresult> GetPromotedEndPoint(
1670 const RawRangeBoundary& aPoint, const nsINode* const aCommon) const;
1671
1672 /**
1673 * Return a parent point of aPoint, i.e., a point referring the container node
1674 * of aPoint. If the container is a root of a generated content, this returns
1675 * unset boundary instead of an error.
1676 *
1677 * @param aPoint Must be set to a valid point.
1678 * @return Even if it's not an error, the boundary may be unset if
1679 * aPoint's container is a root node of generated content.
1680 */
1681 static Result<RawRangeBoundary, nsresult> GetParentPoint(
1682 const RawRangeBoundary& aPoint);
1683
1684 /**
1685 * Return the point after the container node of aPoint. If the container is a
1686 * root of a generated content, this returns unset boundary instead of an
1687 * error.
1688 *
1689 * @param aPoint Must be set to a valid point.
1690 * @return Even if it's not an error, the boundary may be unset if
1691 * aPoint's container is a root node of generated content.
1692 */
1693 static Result<RawRangeBoundary, nsresult> GetPointAfterContainer(
1694 const RawRangeBoundary& aPoint);
1695
1696 [[nodiscard]] static Maybe<uint32_t> ComputeIndexOfContent(
1697 const nsINode* aParent, const nsIContent* aChild, TreeKind aTreeKind);
1698 static bool IsMozBR(Element* aNode);
1699 bool IsRoot(nsINode* aNode, TreeKind aKind) const;
1700
1701 /**
1702 * Return true if the child node at the offset of aPoint does not follow a
1703 * meaningful child in the container. This checks the flattened tree siblings
1704 * if aPoint.GetTreeKind() is TreeKind::FlatForSelection.
1705 *
1706 * @param aPoint Must refers a child node, i.e., must not point the end
1707 * of the container.
1708 */
1709 static bool ChildIsFirstNode(const RawRangeBoundary& aPoint);
1710
1711 /**
1712 * Return true if the child node at the offset of aPoint is not followed by a
1713 * meaningful child in the container. This checks the flattened tree siblings
1714 * if aPoint.GetTreeKind() is TreeKind::FlatForSelection.
1715 *
1716 * @param aPoint Must refers a child node if not pointing to the end of
1717 * the container.
1718 */
1719 static bool ChildIsLastNode(const RawRangeBoundary& aPoint);
1720
1721 bool mIsTextWidget{false};
1722};
1723
1724nsHTMLCopyEncoder::nsHTMLCopyEncoder()
1725 : nsDocumentEncoder{MakeUnique<nsHTMLCopyEncoder::RangeNodeContext>()} {}
1726
1727nsHTMLCopyEncoder::~nsHTMLCopyEncoder() = default;
1728
1729NS_IMETHODIMPnsresult
1730nsHTMLCopyEncoder::Init(Document* aDocument, const nsAString& aMimeType,
1731 uint32_t aFlags) {
1732 if (!aDocument) return NS_ERROR_INVALID_ARG;
1733
1734 mIsTextWidget = false;
1735 Initialize(true, GetAllowRangeCrossShadowBoundary(aFlags));
1736
1737 mIsCopying = true;
1738 mDocument = aDocument;
1739
1740 // nsHTMLCopyEncoder only accepts "text/plain" or "text/html" MIME types, and
1741 // the initial MIME type may change after setting the selection.
1742 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", 1743); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aMimeType.EqualsLiteral(\"text/plain\") || aMimeType.EqualsLiteral(\"text/html\")"
")"); do { MOZ_CrashSequence(__null, 1743); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1743 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", 1743); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aMimeType.EqualsLiteral(\"text/plain\") || aMimeType.EqualsLiteral(\"text/html\")"
")"); do { MOZ_CrashSequence(__null, 1743); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1744 if (aMimeType.EqualsLiteral(kTextMime"text/plain")) {
1745 mMimeType.AssignLiteral(kTextMime"text/plain");
1746 } else {
1747 mMimeType.AssignLiteral(kHTMLMime"text/html");
1748 }
1749
1750 // Make all links absolute when copying
1751 // (see related bugs #57296, #41924, #58646, #32768)
1752 mFlags = aFlags | OutputAbsoluteLinks;
1753
1754 if (!mDocument->IsScriptEnabled()) mFlags |= OutputNoScriptContent;
1755
1756 return NS_OK;
1757}
1758
1759NS_IMETHODIMPnsresult
1760nsHTMLCopyEncoder::SetSelection(Selection* aSelection) {
1761 // check for text widgets: we need to recognize these so that
1762 // we don't tweak the selection to be outside of the magic
1763 // div that ender-lite text widgets are embedded in.
1764
1765 if (!aSelection) return NS_ERROR_NULL_POINTER;
1766
1767 const uint32_t rangeCount = aSelection->RangeCount();
1768
1769 // if selection is uninitialized return
1770 if (!rangeCount) {
1771 return NS_ERROR_FAILURE;
1772 }
1773
1774 // we'll just use the common parent of the first range. Implicit assumption
1775 // here that multi-range selections are table cell selections, in which case
1776 // the common parent is somewhere in the table and we don't really care where.
1777 //
1778 // FIXME(emilio, bug 1455894): This assumption is already wrong, and will
1779 // probably be more wrong in a Shadow DOM world...
1780 //
1781 // We should be able to write this as "Find the common ancestor of the
1782 // selection, then go through the flattened tree and serialize the selected
1783 // nodes", effectively serializing the composed tree.
1784 RefPtr<dom::Range> range = aSelection->GetRangeAt(0);
1785 nsINode* commonParent = range->GetClosestCommonInclusiveAncestor();
1786
1787 mIsTextWidget =
1788 commonParent &&
1789 TextControlElement::FromNodeOrNull(
1790 commonParent->GetClosestNativeAnonymousSubtreeRootParentOrHost());
1791
1792 // normalize selection if we are not in a widget
1793 if (mIsTextWidget) {
1794 mEncodingScope.mSelection = aSelection;
1795 mMimeType.AssignLiteral("text/plain");
1796 return NS_OK;
1797 }
1798
1799 // XXX We should try to get rid of the Selection object here.
1800 // XXX bug 1245883
1801
1802 // also consider ourselves in a text widget if we can't find an html document
1803 // XXX: nsCopySupport relies on the MIME type not being updated immediately
1804 // here, so it can apply different encoding for XHTML documents.
1805 if (!(mDocument && mDocument->IsHTMLDocument())) {
1806 mIsTextWidget = true;
1807 mEncodingScope.mSelection = aSelection;
1808 // mMimeType is set to text/plain when encoding starts.
1809 return NS_OK;
1810 }
1811
1812 // there's no Clone() for selection! fix...
1813 // nsresult rv = aSelection->Clone(getter_AddRefs(mSelection);
1814 // NS_ENSURE_SUCCESS(rv, rv);
1815 mEncodingScope.mSelection = new Selection(SelectionType::eNormal, nullptr);
1816
1817 // loop thru the ranges in the selection
1818 for (const uint32_t rangeIdx : IntegerRange(rangeCount)) {
1819 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", 1819); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aSelection->RangeCount() == rangeCount"
")"); do { MOZ_CrashSequence(__null, 1819); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1820 range = aSelection->GetRangeAt(rangeIdx);
1821 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", 1821); return
NS_ERROR_FAILURE; } } while (false)
;
1822 RefPtr<dom::Range> myRange = range->CloneRange();
1823 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"
, 1823); AnnotateMozCrashReason("MOZ_ASSERT" "(" "myRange" ")"
); do { MOZ_CrashSequence(__null, 1823); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1824
1825 // adjust range to include any ancestors who's children are entirely
1826 // selected
1827 nsresult rv = PromoteRange(myRange);
1828 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"
, 1828); return rv; } } while (false)
;
1829
1830 ErrorResult result;
1831 RefPtr<Selection> selection(mEncodingScope.mSelection);
1832 RefPtr<Document> document(mDocument);
1833 selection->AddRangeAndSelectFramesAndNotifyListenersInternal(
1834 *myRange, document, result);
1835 rv = result.StealNSResult();
1836 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"
, 1836); return rv; } } while (false)
;
1837 }
1838
1839 return NS_OK;
1840}
1841
1842NS_IMETHODIMPnsresult
1843nsHTMLCopyEncoder::EncodeToString(nsAString& aOutputString) {
1844 if (mIsTextWidget) {
1845 mMimeType.AssignLiteral("text/plain");
1846 }
1847 return nsDocumentEncoder::EncodeToString(aOutputString);
1848}
1849
1850NS_IMETHODIMPnsresult
1851nsHTMLCopyEncoder::EncodeToStringWithContext(nsAString& aContextString,
1852 nsAString& aInfoString,
1853 nsAString& aEncodedString) {
1854 nsresult rv = EncodeToString(aEncodedString);
1855 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"
, 1855); return rv; } } while (false)
;
1856
1857 // do not encode any context info or range hints if we are in a text widget.
1858 if (mIsTextWidget) return NS_OK;
1859
1860 // now encode common ancestors into aContextString. Note that the common
1861 // ancestors will be for the last range in the selection in the case of
1862 // multirange selections. encoding ancestors every range in a multirange
1863 // selection in a way that could be understood by the paste code would be a
1864 // lot more work to do. As a practical matter, selections are single range,
1865 // and the ones that aren't are table cell selections where all the cells are
1866 // in the same table.
1867
1868 mSerializer->Init(mFlags, mWrapColumn, mEncoding, mIsCopying, false,
1869 &mNeedsPreformatScanning, aContextString);
1870
1871 // leaf of ancestors might be text node. If so discard it.
1872 int32_t count = mRangeSerializer.mCommonInclusiveAncestors.Length();
1873 int32_t i;
1874 nsCOMPtr<nsINode> node;
1875 if (count > 0) {
1876 node = mRangeSerializer.mCommonInclusiveAncestors.ElementAt(0);
1877 }
1878
1879 if (node && IsTextNode(node)) {
1880 mRangeSerializer.mCommonInclusiveAncestors.RemoveElementAt(0);
1881 if (mRangeSerializer.mContextInfoDepth.mStart) {
1882 --mRangeSerializer.mContextInfoDepth.mStart;
1883 }
1884 if (mRangeSerializer.mContextInfoDepth.mEnd) {
1885 --mRangeSerializer.mContextInfoDepth.mEnd;
1886 }
1887 count--;
1888 }
1889
1890 i = count;
1891 while (i > 0) {
1892 node = mRangeSerializer.mCommonInclusiveAncestors.ElementAt(--i);
1893 rv = mNodeSerializer.SerializeNodeStart(*node, 0, -1);
1894 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"
, 1894); return rv; } } while (false)
;
1895 }
1896 // i = 0; guaranteed by above
1897 while (i < count) {
1898 node = mRangeSerializer.mCommonInclusiveAncestors.ElementAt(i++);
1899 rv = mNodeSerializer.SerializeNodeEnd(*node);
1900 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"
, 1900); return rv; } } while (false)
;
1901 }
1902
1903 mSerializer->Finish();
1904
1905 // encode range info : the start and end depth of the selection, where the
1906 // depth is distance down in the parent hierarchy. Later we will need to add
1907 // leading/trailing whitespace info to this.
1908 nsAutoString infoString;
1909 infoString.AppendInt(mRangeSerializer.mContextInfoDepth.mStart);
1910 infoString.Append(char16_t(','));
1911 infoString.AppendInt(mRangeSerializer.mContextInfoDepth.mEnd);
1912 aInfoString = std::move(infoString);
1913
1914 return rv;
1915}
1916
1917bool nsHTMLCopyEncoder::RangeNodeContext::IncludeInContext(
1918 nsINode& aNode) const {
1919 const nsIContent* const content = nsIContent::FromNodeOrNull(&aNode);
1920 if (!content) {
1921 return false;
1922 }
1923
1924 // If it's an inline editing host, we should not treat it gives a context to
1925 // avoid to duplicate its style.
1926 if (content->IsEditingHost()) {
1927 return false;
1928 }
1929
1930 return content->IsAnyOfHTMLElements(
1931 nsGkAtoms::b, nsGkAtoms::i, nsGkAtoms::u, nsGkAtoms::a, nsGkAtoms::tt,
1932 nsGkAtoms::s, nsGkAtoms::big, nsGkAtoms::small, nsGkAtoms::strike,
1933 nsGkAtoms::em, nsGkAtoms::strong, nsGkAtoms::dfn, nsGkAtoms::code,
1934 nsGkAtoms::cite, nsGkAtoms::var, nsGkAtoms::abbr, nsGkAtoms::font,
1935 nsGkAtoms::script, nsGkAtoms::span, nsGkAtoms::pre, nsGkAtoms::h1,
1936 nsGkAtoms::h2, nsGkAtoms::h3, nsGkAtoms::h4, nsGkAtoms::h5,
1937 nsGkAtoms::h6);
1938}
1939
1940nsresult nsHTMLCopyEncoder::PromoteRange(dom::Range* inRange) {
1941 if (!inRange->IsPositioned()) {
1942 return NS_ERROR_UNEXPECTED;
1943 }
1944 const RawRangeBoundary startRef = [&]() -> RawRangeBoundary {
1945 if (GetTreeKind() == TreeKind::DOM) {
1946 // XXX If GetTreeKind() returns TreeKind::DOM but
1947 // inRange->MayCrossShadowBoundaryStartRef().GetTreeKind() returns
1948 // TreeKind::FlatForSelection, what should we do? The result may cross
1949 // the shadow DOM boundaries even though the our user do not want that.
1950 return inRange->MayCrossShadowBoundaryStartRef().AsRaw();
1951 }
1952 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", 1952); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GetTreeKind() == TreeKind::FlatForSelection"
")"); do { MOZ_CrashSequence(__null, 1952); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1953 const RangeBoundaryFor startBoundaryIsFor =
1954 inRange->Collapsed() ? RangeBoundaryFor::Collapsed
1955 : RangeBoundaryFor::Start;
1956 // Ensure the range boundary is in a flattened node.
1957 return inRange->MayCrossShadowBoundaryStartRef()
1958 .AsRaw()
1959 .GetRangeBoundaryInFlatTree(startBoundaryIsFor);
1960 }();
1961 const RawRangeBoundary endRef = [&]() -> RawRangeBoundary {
1962 if (GetTreeKind() == TreeKind::DOM) {
1963 // XXX If GetTreeKind() returns TreeKind::DOM but
1964 // inRange->MayCrossShadowBoundaryEndRef().GetTreeKind() returns
1965 // TreeKind::FlatForSelection, what should we do? The result may cross
1966 // the shadow DOM boundaries even though the our user do not want that.
1967 return inRange->MayCrossShadowBoundaryEndRef().AsRaw();
1968 }
1969 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", 1969); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GetTreeKind() == TreeKind::FlatForSelection"
")"); do { MOZ_CrashSequence(__null, 1969); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1970 const RangeBoundaryFor endBoundaryIsFor = inRange->Collapsed()
1971 ? RangeBoundaryFor::Collapsed
1972 : RangeBoundaryFor::End;
1973 // Ensure the range boundary is in a flattened node.
1974 return inRange->MayCrossShadowBoundaryEndRef()
1975 .AsRaw()
1976 .GetRangeBoundaryInFlatTree(endBoundaryIsFor);
1977 }();
1978 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", 1978); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "startRef.GetTreeKind() == endRef.GetTreeKind()"
")"); do { MOZ_CrashSequence(__null, 1978); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1979 const nsINode* const commonAncestor =
1980 inRange->GetClosestCommonInclusiveAncestor(
1981 AllowRangeCrossShadowBoundary::Yes);
1982 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", 1982); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "commonAncestor" ")"); do { MOZ_CrashSequence
(__null, 1982); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1983
1984 // examine range endpoints.
1985 Result<RawRangeBoundary, nsresult> promotedStartPointOrError =
1986 GetPromotedStartPoint(startRef, commonAncestor);
1987 if (NS_WARN_IF(promotedStartPointOrError.isErr())NS_warn_if_impl(promotedStartPointOrError.isErr(), "promotedStartPointOrError.isErr()"
, "./../../../dom/serializers/nsDocumentEncoder.cpp", 1987)
) {
1988 return NS_ERROR_FAILURE;
1989 }
1990 Result<RawRangeBoundary, nsresult> promotedEndPointOrError =
1991 GetPromotedEndPoint(endRef, commonAncestor);
1992 if (NS_WARN_IF(promotedEndPointOrError.isErr())NS_warn_if_impl(promotedEndPointOrError.isErr(), "promotedEndPointOrError.isErr()"
, "./../../../dom/serializers/nsDocumentEncoder.cpp", 1992)
) {
1993 return NS_ERROR_FAILURE;
1994 }
1995
1996 RawRangeBoundary promotedStartPoint = promotedStartPointOrError.unwrap();
1997 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", 1997); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "promotedStartPoint.IsSet()" ")"); do { MOZ_CrashSequence
(__null, 1997); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1998 RawRangeBoundary promotedEndPoint = promotedEndPointOrError.unwrap();
1999 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", 1999); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "promotedEndPoint.IsSet()" ")"); do { MOZ_CrashSequence
(__null, 1999); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2000
2001 // set the range to the new values
2002 ErrorResult err;
2003 inRange->SetStart(promotedStartPoint.AsRangeBoundaryInDOMTree(), err,
2004 GetAllowRangeCrossShadowBoundary(mFlags));
2005 if (NS_WARN_IF(err.Failed())NS_warn_if_impl(err.Failed(), "err.Failed()", "./../../../dom/serializers/nsDocumentEncoder.cpp"
, 2005)
) {
2006 return err.StealNSResult();
2007 }
2008 inRange->SetEnd(RawRangeBoundary(promotedEndPoint.AsRangeBoundaryInDOMTree()),
2009 err, GetAllowRangeCrossShadowBoundary(mFlags));
2010 if (NS_WARN_IF(err.Failed())NS_warn_if_impl(err.Failed(), "err.Failed()", "./../../../dom/serializers/nsDocumentEncoder.cpp"
, 2010)
) {
2011 return err.StealNSResult();
2012 }
2013 return NS_OK;
2014}
2015
2016Result<RawRangeBoundary, nsresult> nsHTMLCopyEncoder::GetPromotedStartPoint(
2017 const RawRangeBoundary& aPoint, const nsINode* const aCommon) const {
2018 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", 2018); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aPoint.IsSet()" ")"); do { MOZ_CrashSequence
(__null, 2018); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2019
2020 using OffsetFilter = RawRangeBoundary::OffsetFilter;
2021
2022 // default values
2023 if (aCommon == aPoint.GetContainer() ||
2024 IsRoot(aPoint.GetContainer(), aPoint.GetTreeKind())) {
2025 return aPoint;
2026 }
2027
2028 RawRangeBoundary point(aPoint.GetTreeKind());
2029 bool resetPromotion = false;
2030
2031 // some special casing for text nodes
2032 if (auto* const nodeAsText = Text::FromNode(aPoint.GetContainer())) {
2033 // if not at beginning of text node, we are done
2034 if (!aPoint.IsStartOfContainer()) {
2035 // unless everything before us in just whitespace. NOTE: we need a more
2036 // general solution that truly detects all cases of non-significant
2037 // whitesace with no false alarms.
2038 if (!nodeAsText->TextStartsWithOnlyWhitespace(
2039 *aPoint.Offset(OffsetFilter::kValidOrInvalidOffsets))) {
2040 return aPoint;
2041 }
2042 resetPromotion = true;
2043 }
2044 // If it points the start of a `Text`, we want to extend the start boundary
2045 // to the parent element.
2046 Result<RawRangeBoundary, nsresult> parentPointOrError =
2047 GetParentPoint(aPoint);
2048 if (NS_WARN_IF(parentPointOrError.isErr())NS_warn_if_impl(parentPointOrError.isErr(), "parentPointOrError.isErr()"
, "./../../../dom/serializers/nsDocumentEncoder.cpp", 2048)
) {
2049 return parentPointOrError.propagateErr();
2050 }
2051 point = parentPointOrError.unwrap();
2052 if (MOZ_UNLIKELY(!point.IsSet())(__builtin_expect(!!(!point.IsSet()), 0))) {
2053 NS_WARNING(fmt::format("aPoint={}", aPoint).c_str())NS_DebugBreak(NS_DEBUG_WARNING, fmt::format("aPoint={}", aPoint
).c_str(), nullptr, "./../../../dom/serializers/nsDocumentEncoder.cpp"
, 2053)
;
2054 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"
, 2056); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Selection shouldn't start/end in generated content nor content "
"being removed" ")"); do { MOZ_CrashSequence(__null, 2056); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
2055 "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"
, 2056); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Selection shouldn't start/end in generated content nor content "
"being removed" ")"); do { MOZ_CrashSequence(__null, 2056); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
2056 "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"
, 2056); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Selection shouldn't start/end in generated content nor content "
"being removed" ")"); do { MOZ_CrashSequence(__null, 2056); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
2057 return aPoint;
2058 }
2059 if (point.GetContainer() == aCommon) {
2060 return aPoint;
2061 }
2062 } else {
2063 // If aPoint points a child node, try to climbing up the tree from the
2064 // point.
2065 // XXX: Should we only start from the container of aPoint when it points to
2066 // start of the container and the container has no children? Currently we
2067 // start from the container even when aPoint is invalid, which seems wrong.
2068 if (aPoint.GetContainer()->HasChildNodes() && !aPoint.IsEndOfContainer()) {
2069 if (aPoint.GetContainer() == aCommon) {
2070 return aPoint;
2071 }
2072 point = aPoint;
2073 }
2074 // Otherwise, aPoint points the end of the container (including when the
2075 // container has no child), we can climbing up the tree from its parent.
2076 else {
2077 Result<RawRangeBoundary, nsresult> parentPointOrError =
2078 GetParentPoint(aPoint);
2079 if (NS_WARN_IF(parentPointOrError.isErr())NS_warn_if_impl(parentPointOrError.isErr(), "parentPointOrError.isErr()"
, "./../../../dom/serializers/nsDocumentEncoder.cpp", 2079)
) {
2080 return parentPointOrError.propagateErr();
2081 }
2082 point = parentPointOrError.unwrap();
2083 if (MOZ_UNLIKELY(!point.IsSet())(__builtin_expect(!!(!point.IsSet()), 0))) {
2084 NS_WARNING(fmt::format("aPoint={}", aPoint).c_str())NS_DebugBreak(NS_DEBUG_WARNING, fmt::format("aPoint={}", aPoint
).c_str(), nullptr, "./../../../dom/serializers/nsDocumentEncoder.cpp"
, 2084)
;
2085 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"
, 2087); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Selection shouldn't start/end in generated content nor content "
"being removed" ")"); do { MOZ_CrashSequence(__null, 2087); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
2086 "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"
, 2087); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Selection shouldn't start/end in generated content nor content "
"being removed" ")"); do { MOZ_CrashSequence(__null, 2087); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
2087 "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"
, 2087); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Selection shouldn't start/end in generated content nor content "
"being removed" ")"); do { MOZ_CrashSequence(__null, 2087); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
2088 return aPoint;
2089 }
2090 }
2091 }
2092 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"
, 2097); } } while (false)
2093 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"
, 2097); } } while (false)
2094 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"
, 2097); } } while (false)
2095 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"
, 2097); } } while (false)
2096 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"
, 2097); } } while (false)
2097 .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"
, 2097); } } while (false)
;
2098 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", 2098); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "point.GetChildAtOffset()" ")"); do { MOZ_CrashSequence
(__null, 2098); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2099
2100 // finding the real start for this point. look up the tree for as long as
2101 // we are the first node in the container, and as long as we haven't hit the
2102 // body node.
2103 if (aPoint.GetContainer() != point.GetChildAtOffset() &&
2104 IsRoot(point.GetChildAtOffset(), point.GetTreeKind())) {
2105 return aPoint;
2106 }
2107
2108 while (point.GetContainer() != aCommon &&
2109 !IsRoot(point.GetContainer(), point.GetTreeKind()) &&
2110 ChildIsFirstNode(point)) {
2111 if (resetPromotion) {
2112 nsIContent* const parentContent =
2113 nsIContent::FromNodeOrNull(point.GetContainer());
2114 if (parentContent && parentContent->IsHTMLElement() &&
2115 nsHTMLElement::IsBlock(
2116 nsHTMLTags::AtomTagToId(parentContent->NodeInfo()->NameAtom()))) {
2117 resetPromotion = false;
2118 }
2119 }
2120 Result<RawRangeBoundary, nsresult> parentPointOrError =
2121 GetParentPoint(point);
2122 if (MOZ_UNLIKELY(parentPointOrError.isErr())(__builtin_expect(!!(parentPointOrError.isErr()), 0))) {
2123 return parentPointOrError.propagateErr();
2124 }
2125 if (MOZ_UNLIKELY(!parentPointOrError.inspect().IsSet())(__builtin_expect(!!(!parentPointOrError.inspect().IsSet()), 0
))
) {
2126 NS_WARNING(fmt::format("aPoint={}", aPoint).c_str())NS_DebugBreak(NS_DEBUG_WARNING, fmt::format("aPoint={}", aPoint
).c_str(), nullptr, "./../../../dom/serializers/nsDocumentEncoder.cpp"
, 2126)
;
2127 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"
, 2129); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Selection shouldn't start/end in generated content nor content "
"being removed" ")"); do { MOZ_CrashSequence(__null, 2129); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
2128 "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"
, 2129); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Selection shouldn't start/end in generated content nor content "
"being removed" ")"); do { MOZ_CrashSequence(__null, 2129); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
2129 "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"
, 2129); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Selection shouldn't start/end in generated content nor content "
"being removed" ")"); do { MOZ_CrashSequence(__null, 2129); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
2130 return Err(NS_ERROR_FAILURE);
2131 }
2132 point = parentPointOrError.unwrap();
2133 }
2134
2135 return resetPromotion ? aPoint : point;
2136}
2137
2138Result<RawRangeBoundary, nsresult> nsHTMLCopyEncoder::GetPromotedEndPoint(
2139 const RawRangeBoundary& aPoint, const nsINode* const aCommon) const {
2140 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", 2140); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aPoint.IsSet()" ")"); do { MOZ_CrashSequence
(__null, 2140); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2141
2142 using OffsetFilter = RawRangeBoundary::OffsetFilter;
2143
2144 // default values
2145 if (aCommon == aPoint.GetContainer() ||
2146 IsRoot(aPoint.GetContainer(), aPoint.GetTreeKind())) {
2147 return aPoint;
2148 }
2149
2150 RawRangeBoundary point(aPoint.GetTreeKind());
2151 bool resetPromotion = false;
2152
2153 // Some special casing for CharacterData nodes.
2154 if (aPoint.GetContainer()->IsCharacterData()) {
2155 if (auto* const nodeAsText = Text::FromNode(aPoint.GetContainer())) {
2156 // if not at end of text node, we are done
2157 if (!aPoint.IsEndOfContainer()) {
2158 // unless everything after us is just whitespace. NOTE: we need a more
2159 // general solution that truly detects all cases of non-significant
2160 // whitespace with no false alarms.
2161 if (!nodeAsText->TextEndsWithOnlyWhitespace(
2162 *aPoint.Offset(OffsetFilter::kValidOrInvalidOffsets))) {
2163 return aPoint;
2164 }
2165 resetPromotion = true;
2166 }
2167 // If it points the end of a `Text`, we want to extend the end boundary
2168 // to the parent element.
2169 }
2170 // For other CharacterData nodes, we always extend the end boundary to the
2171 // parent element.
2172 Result<RawRangeBoundary, nsresult> parentPointOrError =
2173 GetPointAfterContainer(aPoint);
2174 if (NS_WARN_IF(parentPointOrError.isErr())NS_warn_if_impl(parentPointOrError.isErr(), "parentPointOrError.isErr()"
, "./../../../dom/serializers/nsDocumentEncoder.cpp", 2174)
) {
2175 return parentPointOrError.propagateErr();
2176 }
2177 point = parentPointOrError.unwrap();
2178 if (MOZ_UNLIKELY(!point.IsSet())(__builtin_expect(!!(!point.IsSet()), 0))) {
2179 NS_WARNING(fmt::format("aPoint={}", aPoint).c_str())NS_DebugBreak(NS_DEBUG_WARNING, fmt::format("aPoint={}", aPoint
).c_str(), nullptr, "./../../../dom/serializers/nsDocumentEncoder.cpp"
, 2179)
;
2180 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"
, 2182); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Selection shouldn't start/end in generated content nor content "
"being removed" ")"); do { MOZ_CrashSequence(__null, 2182); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
2181 "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"
, 2182); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Selection shouldn't start/end in generated content nor content "
"being removed" ")"); do { MOZ_CrashSequence(__null, 2182); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
2182 "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"
, 2182); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Selection shouldn't start/end in generated content nor content "
"being removed" ")"); do { MOZ_CrashSequence(__null, 2182); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
2183 return aPoint;
2184 }
2185 if (point.GetContainer() == aCommon ||
2186 IsRoot(point.GetContainer(), point.GetTreeKind())) {
2187 return aPoint;
2188 }
2189 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", 2194); }
} while (false)
2190 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", 2194); }
} while (false)
2191 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", 2194); }
} while (false)
2192 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", 2194); }
} while (false)
2193 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", 2194); }
} while (false)
2194 .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", 2194); }
} while (false)
;
2195 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", 2195); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "point.GetPreviousSiblingOfChildAtOffset()"
")"); do { MOZ_CrashSequence(__null, 2195); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2196 } else {
2197 point = aPoint;
2198 }
2199 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", 2199); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "point.IsSet()" ")"); do { MOZ_CrashSequence
(__null, 2199); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2200 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", 2200); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsRoot(point.GetContainer(), point.GetTreeKind())"
")"); do { MOZ_CrashSequence(__null, 2200); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2201
2202 // finding the real end for this point. look up the tree for as long as we
2203 // are the last node in the container, and as long as we haven't hit the
2204 // body node.
2205 while (point.GetContainer() != aCommon &&
2206 !IsRoot(point.GetContainer(), point.GetTreeKind()) &&
2207 ChildIsLastNode(point)) {
2208 if (resetPromotion) {
2209 nsIContent* const parentContent =
2210 nsIContent::FromNodeOrNull(point.GetContainer());
2211 if (parentContent && parentContent->IsHTMLElement() &&
2212 nsHTMLElement::IsBlock(
2213 nsHTMLTags::AtomTagToId(parentContent->NodeInfo()->NameAtom()))) {
2214 resetPromotion = false;
2215 }
2216 }
2217
2218 Result<RawRangeBoundary, nsresult> parentPointOrError =
2219 GetPointAfterContainer(point);
2220 if (MOZ_UNLIKELY(parentPointOrError.isErr())(__builtin_expect(!!(parentPointOrError.isErr()), 0))) {
2221 NS_WARNING(fmt::format("point={}", point).c_str())NS_DebugBreak(NS_DEBUG_WARNING, fmt::format("point={}", point
).c_str(), nullptr, "./../../../dom/serializers/nsDocumentEncoder.cpp"
, 2221)
;
2222 return parentPointOrError.propagateErr();
2223 }
2224
2225 if (MOZ_UNLIKELY(!parentPointOrError.inspect().IsSet())(__builtin_expect(!!(!parentPointOrError.inspect().IsSet()), 0
))
) {
2226 NS_WARNING(fmt::format("point={}", point).c_str())NS_DebugBreak(NS_DEBUG_WARNING, fmt::format("point={}", point
).c_str(), nullptr, "./../../../dom/serializers/nsDocumentEncoder.cpp"
, 2226)
;
2227 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"
, 2229); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Selection shouldn't start/end in generated content nor content "
"being removed" ")"); do { MOZ_CrashSequence(__null, 2229); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
2228 "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"
, 2229); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Selection shouldn't start/end in generated content nor content "
"being removed" ")"); do { MOZ_CrashSequence(__null, 2229); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
2229 "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"
, 2229); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Selection shouldn't start/end in generated content nor content "
"being removed" ")"); do { MOZ_CrashSequence(__null, 2229); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
2230 return Err(NS_ERROR_FAILURE);
2231 }
2232 point = parentPointOrError.unwrap();
2233 }
2234
2235 return resetPromotion ? aPoint : point;
2236}
2237
2238bool nsHTMLCopyEncoder::IsMozBR(Element* aElement) {
2239 HTMLBRElement* brElement = HTMLBRElement::FromNodeOrNull(aElement);
2240 return brElement && brElement->IsPaddingForEmptyLastLine();
2241}
2242
2243// static
2244Maybe<uint32_t> nsHTMLCopyEncoder::ComputeIndexOfContent(
2245 const nsINode* aParent, const nsIContent* aChild, TreeKind aTreeKind) {
2246 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"
, 2246); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aParent" ")"
); do { MOZ_CrashSequence(__null, 2246); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2247 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"
, 2247); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aChild" ")"
); do { MOZ_CrashSequence(__null, 2247); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2248
2249 return aTreeKind == TreeKind::DOM
2250 ? aParent->ComputeIndexOf(aChild)
2251 : aParent->ComputeFlatTreeForSelectionIndexOf(aChild);
2252}
2253
2254Result<RawRangeBoundary, nsresult> nsHTMLCopyEncoder::GetParentPoint(
2255 const RawRangeBoundary& aPoint) {
2256 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", 2256); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aPoint.IsSet()" ")"); do { MOZ_CrashSequence
(__null, 2256); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2257
2258 nsIContent* const containerContent =
2259 nsIContent::FromNodeOrNull(aPoint.GetContainer());
2260 if (MOZ_UNLIKELY(!containerContent)(__builtin_expect(!!(!containerContent), 0))) {
2261 return Err(NS_ERROR_NULL_POINTER);
2262 }
2263
2264 // If the container is a ShadowRoot, GetFlattenedTreeParentNodeForSelection()
2265 // returns nullptr. However, we want to keep handling in the host.
2266 if (aPoint.GetTreeKind() == TreeKind::FlatForSelection) {
2267 if (ShadowRoot* const shadowRoot = ShadowRoot::FromNode(containerContent)) {
2268 Element* const host = shadowRoot->GetHost();
2269 if (MOZ_UNLIKELY(!host)(__builtin_expect(!!(!host), 0))) {
2270 return Err(NS_ERROR_NULL_POINTER);
2271 }
2272 // Return the point of the host element. Then, the caller can check
2273 // whether the host element is the first/last meaningful node in its
2274 // parent.
2275 RawRangeBoundary atHost =
2276 RawRangeBoundary::FromChild(*host, aPoint.GetTreeKind());
2277 if (MOZ_UNLIKELY(!atHost.IsSet())(__builtin_expect(!!(!atHost.IsSet()), 0))) {
2278 // The host element may not be a part of the flattened tree, i.e., its
2279 // parent node is another shadow host and not assigned to any <slot>.
2280 return Err(NS_ERROR_NULL_POINTER);
2281 }
2282 return std::move(atHost);
2283 }
2284 }
2285
2286 nsINode* const containerParentNode =
2287 aPoint.GetTreeKind() == TreeKind::FlatForSelection
2288 ? containerContent->GetFlattenedTreeParentNodeForSelection()
2289 : containerContent->GetParentNode();
2290 if (MOZ_UNLIKELY(!containerParentNode)(__builtin_expect(!!(!containerParentNode), 0))) {
2291 return Err(NS_ERROR_NULL_POINTER);
2292 }
2293
2294 const Maybe<uint32_t> indexOfContainer = ComputeIndexOfContent(
2295 containerParentNode, containerContent, aPoint.GetTreeKind());
2296 if (MOZ_UNLIKELY(indexOfContainer.isNothing())(__builtin_expect(!!(indexOfContainer.isNothing()), 0))) {
2297 return RawRangeBoundary(aPoint.GetTreeKind());
2298 }
2299 return RawRangeBoundary(
2300 containerParentNode, *indexOfContainer,
2301 // Do not compute the previous sibling of the child immediately because it
2302 // may not be cheap if we're handling in the flat tree.
2303 RangeBoundarySetBy::Offset, aPoint.GetTreeKind());
2304}
2305
2306Result<RawRangeBoundary, nsresult> nsHTMLCopyEncoder::GetPointAfterContainer(
2307 const RawRangeBoundary& aPoint) {
2308 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", 2308); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aPoint.IsSet()" ")"); do { MOZ_CrashSequence
(__null, 2308); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2309
2310 nsIContent* const containerContent =
2311 nsIContent::FromNodeOrNull(aPoint.GetContainer());
2312 if (MOZ_UNLIKELY(!containerContent)(__builtin_expect(!!(!containerContent), 0))) {
2313 return Err(NS_ERROR_NULL_POINTER);
2314 }
2315
2316 // If the container is a ShadowRoot, RawRangeBoundary::After() returns an
2317 // unset point. However, we want to keep handling in the host.
2318 if (aPoint.GetTreeKind() == TreeKind::FlatForSelection) {
2319 if (ShadowRoot* const shadowRoot = ShadowRoot::FromNode(containerContent)) {
2320 Element* const host = shadowRoot->GetHost();
2321 if (MOZ_UNLIKELY(!host)(__builtin_expect(!!(!host), 0))) {
2322 return Err(NS_ERROR_NULL_POINTER);
2323 }
2324
2325 // Return the point after the host element.
2326 RawRangeBoundary afterHost =
2327 RawRangeBoundary::After(*host, aPoint.GetTreeKind());
2328 if (MOZ_UNLIKELY(!afterHost.IsSet())(__builtin_expect(!!(!afterHost.IsSet()), 0))) {
2329 // The host element may not be a part of the flattened tree, i.e., its
2330 // parent node is another shadow host and not assigned to any <slot>.
2331 return Err(NS_ERROR_NULL_POINTER);
2332 }
2333 return std::move(afterHost);
2334 }
2335 }
2336
2337 return RawRangeBoundary::After(*containerContent, aPoint.GetTreeKind());
2338}
2339
2340bool nsHTMLCopyEncoder::IsRoot(nsINode* aNode, TreeKind aKind) const {
2341 nsCOMPtr<nsIContent> content = nsIContent::FromNodeOrNull(aNode);
2342 if (!content) {
2343 return false;
2344 }
2345
2346 if (mIsTextWidget) {
2347 return content->IsHTMLElement(nsGkAtoms::div);
2348 }
2349
2350 if (aKind == TreeKind::FlatForSelection) {
2351 // If we're handling the flattened tree and aNode is a ShadowRoot,
2352 // GetParentPoint() for a point whose container is aNode will return the
2353 // point at the host. However, if the host is not a part of the flattened
2354 // tree, it will return an error instead. In this case, if we didn't reach
2355 // the ShadowRoot, we succeeded promoting the range. Therefore, we should
2356 // treat the ShadowRoot as a root.
2357 if (const ShadowRoot* const shadowRoot = ShadowRoot::FromNode(*content)) {
2358 if (MOZ_UNLIKELY(shadowRoot->IsUAWidget())(__builtin_expect(!!(shadowRoot->IsUAWidget()), 0))) {
2359 // Special case for the fallback content of <slot> in the non-content
2360 // shadow. E.g., the default summary of <details>.
2361 return true;
2362 }
2363 const Element* const host = shadowRoot->GetHost();
2364 if (NS_WARN_IF(!host)NS_warn_if_impl(!host, "!host", "./../../../dom/serializers/nsDocumentEncoder.cpp"
, 2364)
) {
2365 return true;
2366 }
2367 const nsINode* const flattenedTreeParentNode =
2368 host->GetFlattenedTreeParentNodeForSelection();
2369 if (MOZ_UNLIKELY(!flattenedTreeParentNode)(__builtin_expect(!!(!flattenedTreeParentNode), 0))) {
2370 return true;
2371 }
2372 }
2373 }
2374
2375 // XXX(sefeng): This is some old code from 2006, so I can't
2376 // promise my comment is correct. However, I think these elements
2377 // are considered to be `Root` because if we keep going up
2378 // in nsHTMLCopyEncoder::GetPromoted(Start|End)Point, we would lose the
2379 // correct representation of the point, so we have to stop at
2380 // these nodes.
2381
2382 // nsGkAtoms::slot is here because we'd lose the index
2383 // of the slotted element if we keep going up as
2384 // `nsHTMLCopyEncoder::GetNodeLocation` would promote the
2385 // offset to be index of the <slot> that is relative to
2386 // the <slot>'s parent.
2387 return content->IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::td,
2388 nsGkAtoms::th, nsGkAtoms::slot);
2389}
2390
2391bool nsHTMLCopyEncoder::ChildIsFirstNode(const RawRangeBoundary& aPoint) {
2392 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", 2392); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aPoint.GetChildAtOffset()" ")"); do { MOZ_CrashSequence
(__null, 2392); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2393
2394 // need to check if any nodes before us are really visible.
2395 // Mike wrote something for me along these lines in nsSelectionController,
2396 // but I don't think it's ready for use yet - revisit.
2397 // HACK: for now, simply consider all whitespace text nodes to be
2398 // invisible formatting nodes.
2399
2400 const auto ChildIsSignificant = [](nsIContent& aContent) {
2401 return !aContent.TextIsOnlyWhitespace();
2402 };
2403 if (aPoint.GetTreeKind() == TreeKind::FlatForSelection) {
2404 FlattenedChildIteratorForSelection iter(aPoint.GetContainer());
2405 if (!iter.Seek(aPoint.GetChildAtOffset())) {
2406 return false;
2407 }
2408 for (nsIContent* sibling = iter.GetPreviousChild(); sibling;
2409 sibling = iter.GetPreviousChild()) {
2410 if (ChildIsSignificant(*sibling)) {
2411 return false;
2412 }
2413 }
2414 return true;
2415 }
2416
2417 ChildIterator iter(aPoint.GetContainer());
2418 if (!iter.Seek(aPoint.GetChildAtOffset())) {
2419 return false;
2420 }
2421 for (nsIContent* sibling = iter.GetPreviousChild(); sibling;
2422 sibling = iter.GetPreviousChild()) {
2423 if (ChildIsSignificant(*sibling)) {
2424 return false;
2425 }
2426 }
2427 return true;
2428}
2429
2430bool nsHTMLCopyEncoder::ChildIsLastNode(const RawRangeBoundary& aPoint) {
2431 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", 2431); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aPoint.IsSet()" ")"); do { MOZ_CrashSequence
(__null, 2431); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2432 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", 2432); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!aPoint.GetContainer()->IsCharacterData()"
")"); do { MOZ_CrashSequence(__null, 2432); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2433
2434 if (aPoint.IsEndOfContainer()) {
2435 return true;
2436 }
2437
2438 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", 2438); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aPoint.GetChildAtOffset()" ")"); do { MOZ_CrashSequence
(__null, 2438); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2439
2440 // need to check if any nodes after us are really visible.
2441 // Mike wrote something for me along these lines in nsSelectionController,
2442 // but I don't think it's ready for use yet - revisit.
2443 // HACK: for now, simply consider all whitespace text nodes to be
2444 // invisible formatting nodes.
2445
2446 const auto ChildIsSignificant = [](nsIContent& aContent) {
2447 if (aContent.IsElement() && IsMozBR(aContent.AsElement())) {
2448 // we ignore trailing moz BRs.
2449 return false;
2450 }
2451 return !aContent.TextIsOnlyWhitespace();
2452 };
2453 if (aPoint.GetTreeKind() == TreeKind::FlatForSelection) {
2454 FlattenedChildIteratorForSelection iter(aPoint.GetContainer());
2455 if (!iter.Seek(aPoint.GetChildAtOffset())) {
2456 return false;
2457 }
2458 for (nsIContent* sibling = iter.Get(); sibling;
2459 sibling = iter.GetNextChild()) {
2460 if (ChildIsSignificant(*sibling)) {
2461 return false;
2462 }
2463 }
2464 return true;
2465 }
2466 ChildIterator iter(aPoint.GetContainer());
2467 if (!iter.Seek(aPoint.GetChildAtOffset())) {
2468 return false;
2469 }
2470 for (nsIContent* sibling = iter.Get(); sibling;
2471 sibling = iter.GetNextChild()) {
2472 if (ChildIsSignificant(*sibling)) {
2473 return false;
2474 }
2475 }
2476 return true;
2477}
2478
2479already_AddRefed<nsIDocumentEncoder> do_createHTMLCopyEncoder() {
2480 return do_AddRef(new nsHTMLCopyEncoder);
2481}
2482
2483int32_t nsHTMLCopyEncoder::RangeNodeContext::GetImmediateContextCount(
2484 const nsTArray<nsINode*>& aAncestorArray) const {
2485 int32_t i = aAncestorArray.Length(), j = 0;
2486 while (j < i) {
2487 nsINode* node = aAncestorArray.ElementAt(j);
2488 if (!node) {
2489 break;
2490 }
2491 nsCOMPtr<nsIContent> content(nsIContent::FromNodeOrNull(node));
2492 if (!content || !content->IsAnyOfHTMLElements(
2493 nsGkAtoms::tr, nsGkAtoms::thead, nsGkAtoms::tbody,
2494 nsGkAtoms::tfoot, nsGkAtoms::table)) {
2495 break;
2496 }
2497 ++j;
2498 }
2499 return j;
2500}