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