content/base/src/nsDocumentEncoder.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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

mercurial