LCOV - code coverage report
Current view: directory - content/base/src - nsDocumentEncoder.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 876 211 24.1 %
Date: 2012-06-02 Functions: 67 25 37.3 %

       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 : }

Generated by: LCOV version 1.7