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