Bug Summary

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