Bug Summary

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