Bug Summary

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