Bug Summary

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