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