content/base/src/nsDocumentEncoder.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/base/src/nsDocumentEncoder.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2033 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +/*
    1.10 + * Object that can be used to serialize selections, ranges, or nodes
    1.11 + * to strings in a gazillion different ways.
    1.12 + */
    1.13 + 
    1.14 +#include "nsIDocumentEncoder.h"
    1.15 +
    1.16 +#include "nscore.h"
    1.17 +#include "nsIFactory.h"
    1.18 +#include "nsISupports.h"
    1.19 +#include "nsIComponentManager.h" 
    1.20 +#include "nsIServiceManager.h"
    1.21 +#include "nsIDocument.h"
    1.22 +#include "nsIHTMLDocument.h"
    1.23 +#include "nsCOMPtr.h"
    1.24 +#include "nsIContentSerializer.h"
    1.25 +#include "nsIUnicodeEncoder.h"
    1.26 +#include "nsIOutputStream.h"
    1.27 +#include "nsIDOMElement.h"
    1.28 +#include "nsIDOMText.h"
    1.29 +#include "nsIDOMCDATASection.h"
    1.30 +#include "nsIDOMComment.h"
    1.31 +#include "nsIDOMProcessingInstruction.h"
    1.32 +#include "nsIDOMDocumentType.h"
    1.33 +#include "nsIDOMNodeList.h"
    1.34 +#include "nsRange.h"
    1.35 +#include "nsIDOMRange.h"
    1.36 +#include "nsIDOMDocument.h"
    1.37 +#include "nsICharsetConverterManager.h"
    1.38 +#include "nsGkAtoms.h"
    1.39 +#include "nsIContent.h"
    1.40 +#include "nsIParserService.h"
    1.41 +#include "nsIScriptContext.h"
    1.42 +#include "nsIScriptGlobalObject.h"
    1.43 +#include "nsIScriptSecurityManager.h"
    1.44 +#include "mozilla/dom/Selection.h"
    1.45 +#include "nsISelectionPrivate.h"
    1.46 +#include "nsITransferable.h" // for kUnicodeMime
    1.47 +#include "nsContentUtils.h"
    1.48 +#include "nsNodeUtils.h"
    1.49 +#include "nsUnicharUtils.h"
    1.50 +#include "nsReadableUtils.h"
    1.51 +#include "nsTArray.h"
    1.52 +#include "nsIFrame.h"
    1.53 +#include "nsStringBuffer.h"
    1.54 +#include "mozilla/dom/Element.h"
    1.55 +#include "mozilla/dom/ShadowRoot.h"
    1.56 +#include "nsIEditor.h"
    1.57 +#include "nsIHTMLEditor.h"
    1.58 +#include "nsIDocShell.h"
    1.59 +
    1.60 +using namespace mozilla;
    1.61 +using namespace mozilla::dom;
    1.62 +
    1.63 +nsresult NS_NewDomSelection(nsISelection **aDomSelection);
    1.64 +
    1.65 +enum nsRangeIterationDirection {
    1.66 +  kDirectionOut = -1,
    1.67 +  kDirectionIn = 1
    1.68 +};
    1.69 +
    1.70 +class nsDocumentEncoder : public nsIDocumentEncoder
    1.71 +{
    1.72 +public:
    1.73 +  nsDocumentEncoder();
    1.74 +  virtual ~nsDocumentEncoder();
    1.75 +
    1.76 +  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    1.77 +  NS_DECL_CYCLE_COLLECTION_CLASS(nsDocumentEncoder)
    1.78 +  NS_DECL_NSIDOCUMENTENCODER
    1.79 +
    1.80 +protected:
    1.81 +  void Initialize(bool aClearCachedSerializer = true);
    1.82 +  nsresult SerializeNodeStart(nsINode* aNode, int32_t aStartOffset,
    1.83 +                              int32_t aEndOffset, nsAString& aStr,
    1.84 +                              nsINode* aOriginalNode = nullptr);
    1.85 +  nsresult SerializeToStringRecursive(nsINode* aNode,
    1.86 +                                      nsAString& aStr,
    1.87 +                                      bool aDontSerializeRoot,
    1.88 +                                      uint32_t aMaxLength = 0);
    1.89 +  nsresult SerializeNodeEnd(nsINode* aNode, nsAString& aStr);
    1.90 +  // This serializes the content of aNode.
    1.91 +  nsresult SerializeToStringIterative(nsINode* aNode,
    1.92 +                                      nsAString& aStr);
    1.93 +  nsresult SerializeRangeToString(nsRange *aRange,
    1.94 +                                  nsAString& aOutputString);
    1.95 +  nsresult SerializeRangeNodes(nsRange* aRange, 
    1.96 +                               nsINode* aNode, 
    1.97 +                               nsAString& aString,
    1.98 +                               int32_t aDepth);
    1.99 +  nsresult SerializeRangeContextStart(const nsTArray<nsINode*>& aAncestorArray,
   1.100 +                                      nsAString& aString);
   1.101 +  nsresult SerializeRangeContextEnd(const nsTArray<nsINode*>& aAncestorArray,
   1.102 +                                    nsAString& aString);
   1.103 +  virtual int32_t
   1.104 +  GetImmediateContextCount(const nsTArray<nsINode*>& aAncestorArray)
   1.105 +  {
   1.106 +    return -1;
   1.107 +  }
   1.108 +
   1.109 +  nsresult FlushText(nsAString& aString, bool aForce);
   1.110 +
   1.111 +  bool IsVisibleNode(nsINode* aNode)
   1.112 +  {
   1.113 +    NS_PRECONDITION(aNode, "");
   1.114 +
   1.115 +    if (mFlags & SkipInvisibleContent) {
   1.116 +      // Treat the visibility of the ShadowRoot as if it were
   1.117 +      // the host content.
   1.118 +      nsCOMPtr<nsIContent> content;
   1.119 +      ShadowRoot* shadowRoot = ShadowRoot::FromNode(aNode);
   1.120 +      if (shadowRoot) {
   1.121 +        content = shadowRoot->GetHost();
   1.122 +      } else {
   1.123 +        content = do_QueryInterface(aNode);
   1.124 +      }
   1.125 +
   1.126 +      if (content) {
   1.127 +        nsIFrame* frame = content->GetPrimaryFrame();
   1.128 +        if (!frame) {
   1.129 +          if (aNode->IsNodeOfType(nsINode::eTEXT)) {
   1.130 +            // We have already checked that our parent is visible.
   1.131 +            return true;
   1.132 +          }
   1.133 +          return false;
   1.134 +        }
   1.135 +        bool isVisible = frame->StyleVisibility()->IsVisible();
   1.136 +        if (!isVisible && aNode->IsNodeOfType(nsINode::eTEXT))
   1.137 +          return false;
   1.138 +      }
   1.139 +    }
   1.140 +    return true;
   1.141 +  }
   1.142 +
   1.143 +  static bool IsTag(nsIContent* aContent, nsIAtom* aAtom);
   1.144 +  
   1.145 +  virtual bool IncludeInContext(nsINode *aNode);
   1.146 +
   1.147 +  nsCOMPtr<nsIDocument>          mDocument;
   1.148 +  nsCOMPtr<nsISelection>         mSelection;
   1.149 +  nsRefPtr<nsRange>              mRange;
   1.150 +  nsCOMPtr<nsINode>              mNode;
   1.151 +  nsCOMPtr<nsIOutputStream>      mStream;
   1.152 +  nsCOMPtr<nsIContentSerializer> mSerializer;
   1.153 +  nsCOMPtr<nsIUnicodeEncoder>    mUnicodeEncoder;
   1.154 +  nsCOMPtr<nsINode>              mCommonParent;
   1.155 +  nsCOMPtr<nsIDocumentEncoderNodeFixup> mNodeFixup;
   1.156 +  nsCOMPtr<nsICharsetConverterManager> mCharsetConverterManager;
   1.157 +
   1.158 +  nsString          mMimeType;
   1.159 +  nsCString         mCharset;
   1.160 +  uint32_t          mFlags;
   1.161 +  uint32_t          mWrapColumn;
   1.162 +  uint32_t          mStartDepth;
   1.163 +  uint32_t          mEndDepth;
   1.164 +  int32_t           mStartRootIndex;
   1.165 +  int32_t           mEndRootIndex;
   1.166 +  nsAutoTArray<nsINode*, 8>    mCommonAncestors;
   1.167 +  nsAutoTArray<nsIContent*, 8> mStartNodes;
   1.168 +  nsAutoTArray<int32_t, 8>     mStartOffsets;
   1.169 +  nsAutoTArray<nsIContent*, 8> mEndNodes;
   1.170 +  nsAutoTArray<int32_t, 8>     mEndOffsets;
   1.171 +  bool              mHaltRangeHint;  
   1.172 +  // Used when context has already been serialized for
   1.173 +  // table cell selections (where parent is <tr>)
   1.174 +  bool              mDisableContextSerialize;
   1.175 +  bool              mIsCopying;  // Set to true only while copying
   1.176 +  bool              mNodeIsContainer;
   1.177 +  nsStringBuffer*   mCachedBuffer;
   1.178 +};
   1.179 +
   1.180 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocumentEncoder)
   1.181 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDocumentEncoder)
   1.182 +
   1.183 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocumentEncoder)
   1.184 +   NS_INTERFACE_MAP_ENTRY(nsIDocumentEncoder)
   1.185 +   NS_INTERFACE_MAP_ENTRY(nsISupports)
   1.186 +NS_INTERFACE_MAP_END
   1.187 +
   1.188 +NS_IMPL_CYCLE_COLLECTION(nsDocumentEncoder,
   1.189 +                         mDocument, mSelection, mRange, mNode, mCommonParent)
   1.190 +
   1.191 +nsDocumentEncoder::nsDocumentEncoder() : mCachedBuffer(nullptr)
   1.192 +{
   1.193 +  Initialize();
   1.194 +  mMimeType.AssignLiteral("text/plain");
   1.195 +}
   1.196 +
   1.197 +void nsDocumentEncoder::Initialize(bool aClearCachedSerializer)
   1.198 +{
   1.199 +  mFlags = 0;
   1.200 +  mWrapColumn = 72;
   1.201 +  mStartDepth = 0;
   1.202 +  mEndDepth = 0;
   1.203 +  mStartRootIndex = 0;
   1.204 +  mEndRootIndex = 0;
   1.205 +  mHaltRangeHint = false;
   1.206 +  mDisableContextSerialize = false;
   1.207 +  mNodeIsContainer = false;
   1.208 +  if (aClearCachedSerializer) {
   1.209 +    mSerializer = nullptr;
   1.210 +  }
   1.211 +}
   1.212 +
   1.213 +nsDocumentEncoder::~nsDocumentEncoder()
   1.214 +{
   1.215 +  if (mCachedBuffer) {
   1.216 +    mCachedBuffer->Release();
   1.217 +  }
   1.218 +}
   1.219 +
   1.220 +NS_IMETHODIMP
   1.221 +nsDocumentEncoder::Init(nsIDOMDocument* aDocument,
   1.222 +                        const nsAString& aMimeType,
   1.223 +                        uint32_t aFlags)
   1.224 +{
   1.225 +  if (!aDocument)
   1.226 +    return NS_ERROR_INVALID_ARG;
   1.227 +
   1.228 +  nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
   1.229 +  NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
   1.230 +
   1.231 +  return NativeInit(doc, aMimeType, aFlags);
   1.232 +}
   1.233 +
   1.234 +NS_IMETHODIMP
   1.235 +nsDocumentEncoder::NativeInit(nsIDocument* aDocument,
   1.236 +                              const nsAString& aMimeType,
   1.237 +                              uint32_t aFlags)
   1.238 +{
   1.239 +  if (!aDocument)
   1.240 +    return NS_ERROR_INVALID_ARG;
   1.241 +
   1.242 +  Initialize(!mMimeType.Equals(aMimeType));
   1.243 +
   1.244 +  mDocument = aDocument;
   1.245 +
   1.246 +  mMimeType = aMimeType;
   1.247 +
   1.248 +  mFlags = aFlags;
   1.249 +  mIsCopying = false;
   1.250 +
   1.251 +  return NS_OK;
   1.252 +}
   1.253 +
   1.254 +NS_IMETHODIMP
   1.255 +nsDocumentEncoder::SetWrapColumn(uint32_t aWC)
   1.256 +{
   1.257 +  mWrapColumn = aWC;
   1.258 +  return NS_OK;
   1.259 +}
   1.260 +
   1.261 +NS_IMETHODIMP
   1.262 +nsDocumentEncoder::SetSelection(nsISelection* aSelection)
   1.263 +{
   1.264 +  mSelection = aSelection;
   1.265 +  return NS_OK;
   1.266 +}
   1.267 +
   1.268 +NS_IMETHODIMP
   1.269 +nsDocumentEncoder::SetRange(nsIDOMRange* aRange)
   1.270 +{
   1.271 +  mRange = static_cast<nsRange*>(aRange);
   1.272 +  return NS_OK;
   1.273 +}
   1.274 +
   1.275 +NS_IMETHODIMP
   1.276 +nsDocumentEncoder::SetNode(nsIDOMNode* aNode)
   1.277 +{
   1.278 +  mNodeIsContainer = false;
   1.279 +  mNode = do_QueryInterface(aNode);
   1.280 +  return NS_OK;
   1.281 +}
   1.282 +
   1.283 +NS_IMETHODIMP
   1.284 +nsDocumentEncoder::SetNativeNode(nsINode* aNode)
   1.285 +{
   1.286 +  mNodeIsContainer = false;
   1.287 +  mNode = aNode;
   1.288 +  return NS_OK;
   1.289 +}
   1.290 +
   1.291 +NS_IMETHODIMP
   1.292 +nsDocumentEncoder::SetContainerNode(nsIDOMNode *aContainer)
   1.293 +{
   1.294 +  mNodeIsContainer = true;
   1.295 +  mNode = do_QueryInterface(aContainer);
   1.296 +  return NS_OK;
   1.297 +}
   1.298 +
   1.299 +NS_IMETHODIMP
   1.300 +nsDocumentEncoder::SetNativeContainerNode(nsINode* aContainer)
   1.301 +{
   1.302 +  mNodeIsContainer = true;
   1.303 +  mNode = aContainer;
   1.304 +  return NS_OK;
   1.305 +}
   1.306 +
   1.307 +NS_IMETHODIMP
   1.308 +nsDocumentEncoder::SetCharset(const nsACString& aCharset)
   1.309 +{
   1.310 +  mCharset = aCharset;
   1.311 +  return NS_OK;
   1.312 +}
   1.313 +
   1.314 +NS_IMETHODIMP
   1.315 +nsDocumentEncoder::GetMimeType(nsAString& aMimeType)
   1.316 +{
   1.317 +  aMimeType = mMimeType;
   1.318 +  return NS_OK;
   1.319 +}
   1.320 +
   1.321 +
   1.322 +bool
   1.323 +nsDocumentEncoder::IncludeInContext(nsINode *aNode)
   1.324 +{
   1.325 +  return false;
   1.326 +}
   1.327 +
   1.328 +static
   1.329 +bool
   1.330 +IsInvisibleBreak(nsINode *aNode) {
   1.331 +  // xxxehsan: we should probably figure out a way to determine
   1.332 +  // if a BR node is visible without using the editor.
   1.333 +  Element* elt = aNode->AsElement();
   1.334 +  if (!elt->IsHTML(nsGkAtoms::br) ||
   1.335 +      !aNode->IsEditable()) {
   1.336 +    return false;
   1.337 +  }
   1.338 +
   1.339 +  // Grab the editor associated with the document
   1.340 +  nsIDocument *doc = aNode->GetCurrentDoc();
   1.341 +  if (doc) {
   1.342 +    nsPIDOMWindow *window = doc->GetWindow();
   1.343 +    if (window) {
   1.344 +      nsIDocShell *docShell = window->GetDocShell();
   1.345 +      if (docShell) {
   1.346 +        nsCOMPtr<nsIEditor> editor;
   1.347 +        docShell->GetEditor(getter_AddRefs(editor));
   1.348 +        nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(editor);
   1.349 +        if (htmlEditor) {
   1.350 +          bool isVisible = false;
   1.351 +          nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aNode);
   1.352 +          htmlEditor->BreakIsVisible(domNode, &isVisible);
   1.353 +          return !isVisible;
   1.354 +        }
   1.355 +      }
   1.356 +    }
   1.357 +  }
   1.358 +  return false;
   1.359 +}
   1.360 +
   1.361 +nsresult
   1.362 +nsDocumentEncoder::SerializeNodeStart(nsINode* aNode,
   1.363 +                                      int32_t aStartOffset,
   1.364 +                                      int32_t aEndOffset,
   1.365 +                                      nsAString& aStr,
   1.366 +                                      nsINode* aOriginalNode)
   1.367 +{
   1.368 +  if (!IsVisibleNode(aNode))
   1.369 +    return NS_OK;
   1.370 +  
   1.371 +  nsINode* node = nullptr;
   1.372 +  nsCOMPtr<nsINode> fixedNodeKungfuDeathGrip;
   1.373 +
   1.374 +  // Caller didn't do fixup, so we'll do it ourselves
   1.375 +  if (!aOriginalNode) {
   1.376 +    aOriginalNode = aNode;
   1.377 +    if (mNodeFixup) { 
   1.378 +      bool dummy;
   1.379 +      nsCOMPtr<nsIDOMNode> domNodeIn = do_QueryInterface(aNode);
   1.380 +      nsCOMPtr<nsIDOMNode> domNodeOut;
   1.381 +      mNodeFixup->FixupNode(domNodeIn, &dummy, getter_AddRefs(domNodeOut));
   1.382 +      fixedNodeKungfuDeathGrip = do_QueryInterface(domNodeOut);
   1.383 +      node = fixedNodeKungfuDeathGrip;
   1.384 +    }
   1.385 +  }
   1.386 +
   1.387 +  // Either there was no fixed-up node,
   1.388 +  // or the caller did fixup themselves and aNode is already fixed
   1.389 +  if (!node)
   1.390 +    node = aNode;
   1.391 +  
   1.392 +  if (node->IsElement()) {
   1.393 +    if ((mFlags & (nsIDocumentEncoder::OutputPreformatted |
   1.394 +                   nsIDocumentEncoder::OutputDropInvisibleBreak)) &&
   1.395 +        IsInvisibleBreak(node)) {
   1.396 +      return NS_OK;
   1.397 +    }
   1.398 +    Element* originalElement =
   1.399 +      aOriginalNode && aOriginalNode->IsElement() ?
   1.400 +        aOriginalNode->AsElement() : nullptr;
   1.401 +    mSerializer->AppendElementStart(node->AsElement(),
   1.402 +                                    originalElement, aStr);
   1.403 +    return NS_OK;
   1.404 +  }
   1.405 +
   1.406 +  switch (node->NodeType()) {
   1.407 +    case nsIDOMNode::TEXT_NODE:
   1.408 +    {
   1.409 +      mSerializer->AppendText(static_cast<nsIContent*>(node),
   1.410 +                              aStartOffset, aEndOffset, aStr);
   1.411 +      break;
   1.412 +    }
   1.413 +    case nsIDOMNode::CDATA_SECTION_NODE:
   1.414 +    {
   1.415 +      mSerializer->AppendCDATASection(static_cast<nsIContent*>(node),
   1.416 +                                      aStartOffset, aEndOffset, aStr);
   1.417 +      break;
   1.418 +    }
   1.419 +    case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
   1.420 +    {
   1.421 +      mSerializer->AppendProcessingInstruction(static_cast<nsIContent*>(node),
   1.422 +                                               aStartOffset, aEndOffset, aStr);
   1.423 +      break;
   1.424 +    }
   1.425 +    case nsIDOMNode::COMMENT_NODE:
   1.426 +    {
   1.427 +      mSerializer->AppendComment(static_cast<nsIContent*>(node),
   1.428 +                                 aStartOffset, aEndOffset, aStr);
   1.429 +      break;
   1.430 +    }
   1.431 +    case nsIDOMNode::DOCUMENT_TYPE_NODE:
   1.432 +    {
   1.433 +      mSerializer->AppendDoctype(static_cast<nsIContent*>(node), aStr);
   1.434 +      break;
   1.435 +    }
   1.436 +  }
   1.437 +
   1.438 +  return NS_OK;
   1.439 +}
   1.440 +
   1.441 +nsresult
   1.442 +nsDocumentEncoder::SerializeNodeEnd(nsINode* aNode,
   1.443 +                                    nsAString& aStr)
   1.444 +{
   1.445 +  if (!IsVisibleNode(aNode))
   1.446 +    return NS_OK;
   1.447 +
   1.448 +  if (aNode->IsElement()) {
   1.449 +    mSerializer->AppendElementEnd(aNode->AsElement(), aStr);
   1.450 +  }
   1.451 +  return NS_OK;
   1.452 +}
   1.453 +
   1.454 +nsresult
   1.455 +nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode,
   1.456 +                                              nsAString& aStr,
   1.457 +                                              bool aDontSerializeRoot,
   1.458 +                                              uint32_t aMaxLength)
   1.459 +{
   1.460 +  if (aMaxLength > 0 && aStr.Length() >= aMaxLength) {
   1.461 +    return NS_OK;
   1.462 +  }
   1.463 +
   1.464 +  if (!IsVisibleNode(aNode))
   1.465 +    return NS_OK;
   1.466 +
   1.467 +  nsresult rv = NS_OK;
   1.468 +  bool serializeClonedChildren = false;
   1.469 +  nsINode* maybeFixedNode = nullptr;
   1.470 +
   1.471 +  // Keep the node from FixupNode alive.
   1.472 +  nsCOMPtr<nsINode> fixedNodeKungfuDeathGrip;
   1.473 +  if (mNodeFixup) {
   1.474 +    nsCOMPtr<nsIDOMNode> domNodeIn = do_QueryInterface(aNode);
   1.475 +    nsCOMPtr<nsIDOMNode> domNodeOut;
   1.476 +    mNodeFixup->FixupNode(domNodeIn, &serializeClonedChildren, getter_AddRefs(domNodeOut));
   1.477 +    fixedNodeKungfuDeathGrip = do_QueryInterface(domNodeOut);
   1.478 +    maybeFixedNode = fixedNodeKungfuDeathGrip;
   1.479 +  }
   1.480 +
   1.481 +  if (!maybeFixedNode)
   1.482 +    maybeFixedNode = aNode;
   1.483 +
   1.484 +  if ((mFlags & SkipInvisibleContent) &&
   1.485 +      !(mFlags & OutputNonTextContentAsPlaceholder)) {
   1.486 +    if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
   1.487 +      nsIFrame* frame = static_cast<nsIContent*>(aNode)->GetPrimaryFrame();
   1.488 +      if (frame) {
   1.489 +        bool isSelectable;
   1.490 +        frame->IsSelectable(&isSelectable, nullptr);
   1.491 +        if (!isSelectable){
   1.492 +          aDontSerializeRoot = true;
   1.493 +        }
   1.494 +      }
   1.495 +    }
   1.496 +  }
   1.497 +
   1.498 +  if (!aDontSerializeRoot) {
   1.499 +    int32_t endOffset = -1;
   1.500 +    if (aMaxLength > 0) {
   1.501 +      MOZ_ASSERT(aMaxLength >= aStr.Length());
   1.502 +      endOffset = aMaxLength - aStr.Length();
   1.503 +    }
   1.504 +    rv = SerializeNodeStart(maybeFixedNode, 0, endOffset, aStr, aNode);
   1.505 +    NS_ENSURE_SUCCESS(rv, rv);
   1.506 +  }
   1.507 +
   1.508 +  nsINode* node = serializeClonedChildren ? maybeFixedNode : aNode;
   1.509 +
   1.510 +  for (nsINode* child = nsNodeUtils::GetFirstChildOfTemplateOrNode(node);
   1.511 +       child;
   1.512 +       child = child->GetNextSibling()) {
   1.513 +    rv = SerializeToStringRecursive(child, aStr, false, aMaxLength);
   1.514 +    NS_ENSURE_SUCCESS(rv, rv);
   1.515 +  }
   1.516 +
   1.517 +  if (!aDontSerializeRoot) {
   1.518 +    rv = SerializeNodeEnd(node, aStr);
   1.519 +    NS_ENSURE_SUCCESS(rv, rv);
   1.520 +  }
   1.521 +
   1.522 +  return FlushText(aStr, false);
   1.523 +}
   1.524 +
   1.525 +nsresult
   1.526 +nsDocumentEncoder::SerializeToStringIterative(nsINode* aNode,
   1.527 +                                              nsAString& aStr)
   1.528 +{
   1.529 +  nsresult rv;
   1.530 +
   1.531 +  nsINode* node = aNode->GetFirstChild();
   1.532 +  while (node) {
   1.533 +    nsINode* current = node;
   1.534 +    rv = SerializeNodeStart(current, 0, -1, aStr, current);
   1.535 +    NS_ENSURE_SUCCESS(rv, rv);
   1.536 +    node = current->GetFirstChild();
   1.537 +    while (!node && current && current != aNode) {
   1.538 +      rv = SerializeNodeEnd(current, aStr);
   1.539 +      NS_ENSURE_SUCCESS(rv, rv);
   1.540 +      // Check if we have siblings.
   1.541 +      node = current->GetNextSibling();
   1.542 +      if (!node) {
   1.543 +        // Perhaps parent node has siblings.
   1.544 +        current = current->GetParentNode();
   1.545 +      }
   1.546 +    }
   1.547 +  }
   1.548 +
   1.549 +  return NS_OK;
   1.550 +}
   1.551 +
   1.552 +bool 
   1.553 +nsDocumentEncoder::IsTag(nsIContent* aContent, nsIAtom* aAtom)
   1.554 +{
   1.555 +  return aContent && aContent->Tag() == aAtom;
   1.556 +}
   1.557 +
   1.558 +static nsresult
   1.559 +ConvertAndWrite(const nsAString& aString,
   1.560 +                nsIOutputStream* aStream,
   1.561 +                nsIUnicodeEncoder* aEncoder)
   1.562 +{
   1.563 +  NS_ENSURE_ARG_POINTER(aStream);
   1.564 +  NS_ENSURE_ARG_POINTER(aEncoder);
   1.565 +  nsresult rv;
   1.566 +  int32_t charLength, startCharLength;
   1.567 +  const nsPromiseFlatString& flat = PromiseFlatString(aString);
   1.568 +  const char16_t* unicodeBuf = flat.get();
   1.569 +  int32_t unicodeLength = aString.Length();
   1.570 +  int32_t startLength = unicodeLength;
   1.571 +
   1.572 +  rv = aEncoder->GetMaxLength(unicodeBuf, unicodeLength, &charLength);
   1.573 +  startCharLength = charLength;
   1.574 +  NS_ENSURE_SUCCESS(rv, rv);
   1.575 +
   1.576 +  if (!charLength) {
   1.577 +    // Nothing to write.  Besides, a length 0 string has an immutable buffer, so
   1.578 +    // attempts to null-terminate it will crash.
   1.579 +    return NS_OK;
   1.580 +  }
   1.581 +
   1.582 +  nsAutoCString charXferString;
   1.583 +  if (!charXferString.SetLength(charLength, fallible_t()))
   1.584 +    return NS_ERROR_OUT_OF_MEMORY;
   1.585 +
   1.586 +  char* charXferBuf = charXferString.BeginWriting();
   1.587 +  nsresult convert_rv = NS_OK;
   1.588 +
   1.589 +  do {
   1.590 +    unicodeLength = startLength;
   1.591 +    charLength = startCharLength;
   1.592 +
   1.593 +    convert_rv = aEncoder->Convert(unicodeBuf, &unicodeLength, charXferBuf, &charLength);
   1.594 +    NS_ENSURE_SUCCESS(convert_rv, convert_rv);
   1.595 +
   1.596 +    // Make sure charXferBuf is null-terminated before we call
   1.597 +    // Write().
   1.598 +
   1.599 +    charXferBuf[charLength] = '\0';
   1.600 +
   1.601 +    uint32_t written;
   1.602 +    rv = aStream->Write(charXferBuf, charLength, &written);
   1.603 +    NS_ENSURE_SUCCESS(rv, rv);
   1.604 +
   1.605 +    // If the converter couldn't convert a chraacer we replace the
   1.606 +    // character with a characre entity.
   1.607 +    if (convert_rv == NS_ERROR_UENC_NOMAPPING) {
   1.608 +      // Finishes the conversion. 
   1.609 +      // The converter has the possibility to write some extra data and flush its final state.
   1.610 +      char finish_buf[33];
   1.611 +      charLength = sizeof(finish_buf) - 1;
   1.612 +      rv = aEncoder->Finish(finish_buf, &charLength);
   1.613 +      NS_ENSURE_SUCCESS(rv, rv);
   1.614 +
   1.615 +      // Make sure finish_buf is null-terminated before we call
   1.616 +      // Write().
   1.617 +
   1.618 +      finish_buf[charLength] = '\0';
   1.619 +
   1.620 +      rv = aStream->Write(finish_buf, charLength, &written);
   1.621 +      NS_ENSURE_SUCCESS(rv, rv);
   1.622 +
   1.623 +      nsAutoCString entString("&#");
   1.624 +      if (NS_IS_HIGH_SURROGATE(unicodeBuf[unicodeLength - 1]) && 
   1.625 +          unicodeLength < startLength && NS_IS_LOW_SURROGATE(unicodeBuf[unicodeLength]))  {
   1.626 +        entString.AppendInt(SURROGATE_TO_UCS4(unicodeBuf[unicodeLength - 1],
   1.627 +                                              unicodeBuf[unicodeLength]));
   1.628 +        unicodeLength += 1;
   1.629 +      }
   1.630 +      else
   1.631 +        entString.AppendInt(unicodeBuf[unicodeLength - 1]);
   1.632 +      entString.Append(';');
   1.633 +
   1.634 +      // Since entString is an nsAutoCString we know entString.get()
   1.635 +      // returns a null-terminated string, so no need for extra
   1.636 +      // null-termination before calling Write() here.
   1.637 +
   1.638 +      rv = aStream->Write(entString.get(), entString.Length(), &written);
   1.639 +      NS_ENSURE_SUCCESS(rv, rv);
   1.640 +
   1.641 +      unicodeBuf += unicodeLength;
   1.642 +      startLength -= unicodeLength;
   1.643 +    }
   1.644 +  } while (convert_rv == NS_ERROR_UENC_NOMAPPING);
   1.645 +
   1.646 +  return rv;
   1.647 +}
   1.648 +
   1.649 +nsresult
   1.650 +nsDocumentEncoder::FlushText(nsAString& aString, bool aForce)
   1.651 +{
   1.652 +  if (!mStream)
   1.653 +    return NS_OK;
   1.654 +
   1.655 +  nsresult rv = NS_OK;
   1.656 +
   1.657 +  if (aString.Length() > 1024 || aForce) {
   1.658 +    rv = ConvertAndWrite(aString, mStream, mUnicodeEncoder);
   1.659 +
   1.660 +    aString.Truncate();
   1.661 +  }
   1.662 +
   1.663 +  return rv;
   1.664 +}
   1.665 +
   1.666 +#if 0 // This code is really fast at serializing a range, but unfortunately
   1.667 +      // there are problems with it so we don't use it now, maybe later...
   1.668 +static nsresult ChildAt(nsIDOMNode* aNode, int32_t aIndex, nsIDOMNode*& aChild)
   1.669 +{
   1.670 +  nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
   1.671 +
   1.672 +  aChild = nullptr;
   1.673 +
   1.674 +  NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
   1.675 +
   1.676 +  nsIContent *child = content->GetChildAt(aIndex);
   1.677 +
   1.678 +  if (child)
   1.679 +    return CallQueryInterface(child, &aChild);
   1.680 +
   1.681 +  return NS_OK;
   1.682 +}
   1.683 +
   1.684 +static int32_t IndexOf(nsIDOMNode* aParent, nsIDOMNode* aChild)
   1.685 +{
   1.686 +  nsCOMPtr<nsIContent> parent(do_QueryInterface(aParent));
   1.687 +  nsCOMPtr<nsIContent> child(do_QueryInterface(aChild));
   1.688 +
   1.689 +  if (!parent)
   1.690 +    return -1;
   1.691 +
   1.692 +  return parent->IndexOf(child);
   1.693 +}
   1.694 +
   1.695 +static inline int32_t GetIndex(nsTArray<int32_t>& aIndexArray)
   1.696 +{
   1.697 +  int32_t count = aIndexArray.Length();
   1.698 +
   1.699 +  if (count) {
   1.700 +    return aIndexArray.ElementAt(count - 1);
   1.701 +  }
   1.702 +
   1.703 +  return 0;
   1.704 +}
   1.705 +
   1.706 +static nsresult GetNextNode(nsIDOMNode* aNode, nsTArray<int32_t>& aIndexArray,
   1.707 +                            nsIDOMNode*& aNextNode,
   1.708 +                            nsRangeIterationDirection& aDirection)
   1.709 +{
   1.710 +  bool hasChildren;
   1.711 +
   1.712 +  aNextNode = nullptr;
   1.713 +
   1.714 +  aNode->HasChildNodes(&hasChildren);
   1.715 +
   1.716 +  if (hasChildren && aDirection == kDirectionIn) {
   1.717 +    ChildAt(aNode, 0, aNextNode);
   1.718 +    NS_ENSURE_TRUE(aNextNode, NS_ERROR_FAILURE);
   1.719 +
   1.720 +    aIndexArray.AppendElement(0);
   1.721 +
   1.722 +    aDirection = kDirectionIn;
   1.723 +  } else if (aDirection == kDirectionIn) {
   1.724 +    aNextNode = aNode;
   1.725 +
   1.726 +    NS_ADDREF(aNextNode);
   1.727 +
   1.728 +    aDirection = kDirectionOut;
   1.729 +  } else {
   1.730 +    nsCOMPtr<nsIDOMNode> parent;
   1.731 +
   1.732 +    aNode->GetParentNode(getter_AddRefs(parent));
   1.733 +    NS_ENSURE_TRUE(parent, NS_ERROR_FAILURE);
   1.734 +
   1.735 +    int32_t count = aIndexArray.Length();
   1.736 +
   1.737 +    if (count) {
   1.738 +      int32_t indx = aIndexArray.ElementAt(count - 1);
   1.739 +
   1.740 +      ChildAt(parent, indx + 1, aNextNode);
   1.741 +
   1.742 +      if (aNextNode)
   1.743 +        aIndexArray.ElementAt(count - 1) = indx + 1;
   1.744 +      else
   1.745 +        aIndexArray.RemoveElementAt(count - 1);
   1.746 +    } else {
   1.747 +      int32_t indx = IndexOf(parent, aNode);
   1.748 +
   1.749 +      if (indx >= 0) {
   1.750 +        ChildAt(parent, indx + 1, aNextNode);
   1.751 +
   1.752 +        if (aNextNode)
   1.753 +          aIndexArray.AppendElement(indx + 1);
   1.754 +      }
   1.755 +    }
   1.756 +
   1.757 +    if (aNextNode) {
   1.758 +      aDirection = kDirectionIn;
   1.759 +    } else {
   1.760 +      aDirection = kDirectionOut;
   1.761 +
   1.762 +      aNextNode = parent;
   1.763 +
   1.764 +      NS_ADDREF(aNextNode);
   1.765 +    }
   1.766 +  }
   1.767 +
   1.768 +  return NS_OK;
   1.769 +}
   1.770 +#endif
   1.771 +
   1.772 +static bool IsTextNode(nsINode *aNode)
   1.773 +{
   1.774 +  return aNode && aNode->IsNodeOfType(nsINode::eTEXT);
   1.775 +}
   1.776 +
   1.777 +nsresult
   1.778 +nsDocumentEncoder::SerializeRangeNodes(nsRange* aRange,
   1.779 +                                       nsINode* aNode,
   1.780 +                                       nsAString& aString,
   1.781 +                                       int32_t aDepth)
   1.782 +{
   1.783 +  nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
   1.784 +  NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
   1.785 +
   1.786 +  if (!IsVisibleNode(aNode))
   1.787 +    return NS_OK;
   1.788 +
   1.789 +  nsresult rv = NS_OK;
   1.790 +
   1.791 +  // get start and end nodes for this recursion level
   1.792 +  nsCOMPtr<nsIContent> startNode, endNode;
   1.793 +  {
   1.794 +    int32_t start = mStartRootIndex - aDepth;
   1.795 +    if (start >= 0 && (uint32_t)start <= mStartNodes.Length())
   1.796 +      startNode = mStartNodes[start];
   1.797 +
   1.798 +    int32_t end = mEndRootIndex - aDepth;
   1.799 +    if (end >= 0 && (uint32_t)end <= mEndNodes.Length())
   1.800 +      endNode = mEndNodes[end];
   1.801 +  }
   1.802 +
   1.803 +  if (startNode != content && endNode != content)
   1.804 +  {
   1.805 +    // node is completely contained in range.  Serialize the whole subtree
   1.806 +    // rooted by this node.
   1.807 +    rv = SerializeToStringRecursive(aNode, aString, false);
   1.808 +    NS_ENSURE_SUCCESS(rv, rv);
   1.809 +  }
   1.810 +  else
   1.811 +  {
   1.812 +    // due to implementation it is impossible for text node to be both start and end of 
   1.813 +    // range.  We would have handled that case without getting here.
   1.814 +    //XXXsmaug What does this all mean?
   1.815 +    if (IsTextNode(aNode))
   1.816 +    {
   1.817 +      if (startNode == content)
   1.818 +      {
   1.819 +        int32_t startOffset = aRange->StartOffset();
   1.820 +        rv = SerializeNodeStart(aNode, startOffset, -1, aString);
   1.821 +        NS_ENSURE_SUCCESS(rv, rv);
   1.822 +      }
   1.823 +      else
   1.824 +      {
   1.825 +        int32_t endOffset = aRange->EndOffset();
   1.826 +        rv = SerializeNodeStart(aNode, 0, endOffset, aString);
   1.827 +        NS_ENSURE_SUCCESS(rv, rv);
   1.828 +      }
   1.829 +    }
   1.830 +    else
   1.831 +    {
   1.832 +      if (aNode != mCommonParent)
   1.833 +      {
   1.834 +        if (IncludeInContext(aNode))
   1.835 +        {
   1.836 +          // halt the incrementing of mStartDepth/mEndDepth.  This is
   1.837 +          // so paste client will include this node in paste.
   1.838 +          mHaltRangeHint = true;
   1.839 +        }
   1.840 +        if ((startNode == content) && !mHaltRangeHint) mStartDepth++;
   1.841 +        if ((endNode == content) && !mHaltRangeHint) mEndDepth++;
   1.842 +      
   1.843 +        // serialize the start of this node
   1.844 +        rv = SerializeNodeStart(aNode, 0, -1, aString);
   1.845 +        NS_ENSURE_SUCCESS(rv, rv);
   1.846 +      }
   1.847 +      
   1.848 +      // do some calculations that will tell us which children of this
   1.849 +      // node are in the range.
   1.850 +      nsIContent* childAsNode = nullptr;
   1.851 +      int32_t startOffset = 0, endOffset = -1;
   1.852 +      if (startNode == content && mStartRootIndex >= aDepth)
   1.853 +        startOffset = mStartOffsets[mStartRootIndex - aDepth];
   1.854 +      if (endNode == content && mEndRootIndex >= aDepth)
   1.855 +        endOffset = mEndOffsets[mEndRootIndex - aDepth];
   1.856 +      // generated content will cause offset values of -1 to be returned.  
   1.857 +      int32_t j;
   1.858 +      uint32_t childCount = content->GetChildCount();
   1.859 +
   1.860 +      if (startOffset == -1) startOffset = 0;
   1.861 +      if (endOffset == -1) endOffset = childCount;
   1.862 +      else
   1.863 +      {
   1.864 +        // if we are at the "tip" of the selection, endOffset is fine.
   1.865 +        // otherwise, we need to add one.  This is because of the semantics
   1.866 +        // of the offset list created by GetAncestorsAndOffsets().  The
   1.867 +        // intermediate points on the list use the endOffset of the 
   1.868 +        // location of the ancestor, rather than just past it.  So we need
   1.869 +        // to add one here in order to include it in the children we serialize.
   1.870 +        if (aNode != aRange->GetEndParent())
   1.871 +        {
   1.872 +          endOffset++;
   1.873 +        }
   1.874 +      }
   1.875 +      // serialize the children of this node that are in the range
   1.876 +      for (j=startOffset; j<endOffset; j++)
   1.877 +      {
   1.878 +        childAsNode = content->GetChildAt(j);
   1.879 +
   1.880 +        if ((j==startOffset) || (j==endOffset-1))
   1.881 +          rv = SerializeRangeNodes(aRange, childAsNode, aString, aDepth+1);
   1.882 +        else
   1.883 +          rv = SerializeToStringRecursive(childAsNode, aString, false);
   1.884 +
   1.885 +        NS_ENSURE_SUCCESS(rv, rv);
   1.886 +      }
   1.887 +
   1.888 +      // serialize the end of this node
   1.889 +      if (aNode != mCommonParent)
   1.890 +      {
   1.891 +        rv = SerializeNodeEnd(aNode, aString);
   1.892 +        NS_ENSURE_SUCCESS(rv, rv); 
   1.893 +      }
   1.894 +    }
   1.895 +  }
   1.896 +  return NS_OK;
   1.897 +}
   1.898 +
   1.899 +nsresult
   1.900 +nsDocumentEncoder::SerializeRangeContextStart(const nsTArray<nsINode*>& aAncestorArray,
   1.901 +                                              nsAString& aString)
   1.902 +{
   1.903 +  if (mDisableContextSerialize) {
   1.904 +    return NS_OK;
   1.905 +  }
   1.906 +  int32_t i = aAncestorArray.Length(), j;
   1.907 +  nsresult rv = NS_OK;
   1.908 +
   1.909 +  // currently only for table-related elements; see Bug 137450
   1.910 +  j = GetImmediateContextCount(aAncestorArray);
   1.911 +
   1.912 +  while (i > 0) {
   1.913 +    nsINode *node = aAncestorArray.ElementAt(--i);
   1.914 +
   1.915 +    if (!node)
   1.916 +      break;
   1.917 +
   1.918 +    // Either a general inclusion or as immediate context
   1.919 +    if (IncludeInContext(node) || i < j) {
   1.920 +      rv = SerializeNodeStart(node, 0, -1, aString);
   1.921 +
   1.922 +      if (NS_FAILED(rv))
   1.923 +        break;
   1.924 +    }
   1.925 +  }
   1.926 +
   1.927 +  return rv;
   1.928 +}
   1.929 +
   1.930 +nsresult
   1.931 +nsDocumentEncoder::SerializeRangeContextEnd(const nsTArray<nsINode*>& aAncestorArray,
   1.932 +                                            nsAString& aString)
   1.933 +{
   1.934 +  if (mDisableContextSerialize) {
   1.935 +    return NS_OK;
   1.936 +  }
   1.937 +  int32_t i = 0, j;
   1.938 +  int32_t count = aAncestorArray.Length();
   1.939 +  nsresult rv = NS_OK;
   1.940 +
   1.941 +  // currently only for table-related elements
   1.942 +  j = GetImmediateContextCount(aAncestorArray);
   1.943 +
   1.944 +  while (i < count) {
   1.945 +    nsINode *node = aAncestorArray.ElementAt(i++);
   1.946 +
   1.947 +    if (!node)
   1.948 +      break;
   1.949 +
   1.950 +    // Either a general inclusion or as immediate context
   1.951 +    if (IncludeInContext(node) || i - 1 < j) {
   1.952 +      rv = SerializeNodeEnd(node, aString);
   1.953 +
   1.954 +      if (NS_FAILED(rv))
   1.955 +        break;
   1.956 +    }
   1.957 +  }
   1.958 +
   1.959 +  return rv;
   1.960 +}
   1.961 +
   1.962 +nsresult
   1.963 +nsDocumentEncoder::SerializeRangeToString(nsRange *aRange,
   1.964 +                                          nsAString& aOutputString)
   1.965 +{
   1.966 +  if (!aRange || aRange->Collapsed())
   1.967 +    return NS_OK;
   1.968 +
   1.969 +  mCommonParent = aRange->GetCommonAncestor();
   1.970 +
   1.971 +  if (!mCommonParent)
   1.972 +    return NS_OK;
   1.973 +  
   1.974 +  nsINode* startParent = aRange->GetStartParent();
   1.975 +  NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE);
   1.976 +  int32_t startOffset = aRange->StartOffset();
   1.977 +
   1.978 +  nsINode* endParent = aRange->GetEndParent();
   1.979 +  NS_ENSURE_TRUE(endParent, NS_ERROR_FAILURE);
   1.980 +  int32_t endOffset = aRange->EndOffset();
   1.981 +
   1.982 +  mCommonAncestors.Clear();
   1.983 +  mStartNodes.Clear();
   1.984 +  mStartOffsets.Clear();
   1.985 +  mEndNodes.Clear();
   1.986 +  mEndOffsets.Clear();
   1.987 +
   1.988 +  nsContentUtils::GetAncestors(mCommonParent, mCommonAncestors);
   1.989 +  nsCOMPtr<nsIDOMNode> sp = do_QueryInterface(startParent);
   1.990 +  nsContentUtils::GetAncestorsAndOffsets(sp, startOffset,
   1.991 +                                         &mStartNodes, &mStartOffsets);
   1.992 +  nsCOMPtr<nsIDOMNode> ep = do_QueryInterface(endParent);
   1.993 +  nsContentUtils::GetAncestorsAndOffsets(ep, endOffset,
   1.994 +                                         &mEndNodes, &mEndOffsets);
   1.995 +
   1.996 +  nsCOMPtr<nsIContent> commonContent = do_QueryInterface(mCommonParent);
   1.997 +  mStartRootIndex = mStartNodes.IndexOf(commonContent);
   1.998 +  mEndRootIndex = mEndNodes.IndexOf(commonContent);
   1.999 +  
  1.1000 +  nsresult rv = NS_OK;
  1.1001 +
  1.1002 +  rv = SerializeRangeContextStart(mCommonAncestors, aOutputString);
  1.1003 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1004 +
  1.1005 +  if ((startParent == endParent) && IsTextNode(startParent))
  1.1006 +  {
  1.1007 +    if (mFlags & SkipInvisibleContent) {
  1.1008 +      // Check that the parent is visible if we don't a frame.
  1.1009 +      // IsVisibleNode() will do it when there's a frame.
  1.1010 +      nsCOMPtr<nsIContent> content = do_QueryInterface(startParent);
  1.1011 +      if (content && !content->GetPrimaryFrame()) {
  1.1012 +        nsIContent* parent = content->GetParent();
  1.1013 +        if (!parent || !IsVisibleNode(parent))
  1.1014 +          return NS_OK;
  1.1015 +      }
  1.1016 +    }
  1.1017 +    rv = SerializeNodeStart(startParent, startOffset, endOffset, aOutputString);
  1.1018 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1019 +  }
  1.1020 +  else
  1.1021 +  {
  1.1022 +    rv = SerializeRangeNodes(aRange, mCommonParent, aOutputString, 0);
  1.1023 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1024 +  }
  1.1025 +  rv = SerializeRangeContextEnd(mCommonAncestors, aOutputString);
  1.1026 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1027 +
  1.1028 +  return rv;
  1.1029 +}
  1.1030 +
  1.1031 +NS_IMETHODIMP
  1.1032 +nsDocumentEncoder::EncodeToString(nsAString& aOutputString)
  1.1033 +{
  1.1034 +  return EncodeToStringWithMaxLength(0, aOutputString);
  1.1035 +}
  1.1036 +
  1.1037 +NS_IMETHODIMP
  1.1038 +nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
  1.1039 +                                               nsAString& aOutputString)
  1.1040 +{
  1.1041 +  if (!mDocument)
  1.1042 +    return NS_ERROR_NOT_INITIALIZED;
  1.1043 +
  1.1044 +  aOutputString.Truncate();
  1.1045 +
  1.1046 +  nsString output;
  1.1047 +  static const size_t bufferSize = 2048;
  1.1048 +  if (!mCachedBuffer) {
  1.1049 +    mCachedBuffer = nsStringBuffer::Alloc(bufferSize).take();
  1.1050 +  }
  1.1051 +  NS_ASSERTION(!mCachedBuffer->IsReadonly(),
  1.1052 +               "DocumentEncoder shouldn't keep reference to non-readonly buffer!");
  1.1053 +  static_cast<char16_t*>(mCachedBuffer->Data())[0] = char16_t(0);
  1.1054 +  mCachedBuffer->ToString(0, output, true);
  1.1055 +  // output owns the buffer now!
  1.1056 +  mCachedBuffer = nullptr;
  1.1057 +  
  1.1058 +
  1.1059 +  if (!mSerializer) {
  1.1060 +    nsAutoCString progId(NS_CONTENTSERIALIZER_CONTRACTID_PREFIX);
  1.1061 +    AppendUTF16toUTF8(mMimeType, progId);
  1.1062 +
  1.1063 +    mSerializer = do_CreateInstance(progId.get());
  1.1064 +    NS_ENSURE_TRUE(mSerializer, NS_ERROR_NOT_IMPLEMENTED);
  1.1065 +  }
  1.1066 +
  1.1067 +  nsresult rv = NS_OK;
  1.1068 +
  1.1069 +  nsCOMPtr<nsIAtom> charsetAtom;
  1.1070 +  if (!mCharset.IsEmpty()) {
  1.1071 +    if (!mCharsetConverterManager) {
  1.1072 +      mCharsetConverterManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
  1.1073 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1074 +    }
  1.1075 +  }
  1.1076 +  
  1.1077 +  bool rewriteEncodingDeclaration = !(mSelection || mRange || mNode) && !(mFlags & OutputDontRewriteEncodingDeclaration);
  1.1078 +  mSerializer->Init(mFlags, mWrapColumn, mCharset.get(), mIsCopying, rewriteEncodingDeclaration);
  1.1079 +
  1.1080 +  if (mSelection) {
  1.1081 +    nsCOMPtr<nsIDOMRange> range;
  1.1082 +    int32_t i, count = 0;
  1.1083 +
  1.1084 +    rv = mSelection->GetRangeCount(&count);
  1.1085 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1086 +
  1.1087 +    nsCOMPtr<nsIDOMNode> node, prevNode;
  1.1088 +    for (i = 0; i < count; i++) {
  1.1089 +      mSelection->GetRangeAt(i, getter_AddRefs(range));
  1.1090 +
  1.1091 +      // Bug 236546: newlines not added when copying table cells into clipboard
  1.1092 +      // Each selected cell shows up as a range containing a row with a single cell
  1.1093 +      // get the row, compare it to previous row and emit </tr><tr> as needed
  1.1094 +      // Bug 137450: Problem copying/pasting a table from a web page to Excel.
  1.1095 +      // Each separate block of <tr></tr> produced above will be wrapped by the
  1.1096 +      // immediate context. This assumes that you can't select cells that are
  1.1097 +      // multiple selections from two tables simultaneously.
  1.1098 +      range->GetStartContainer(getter_AddRefs(node));
  1.1099 +      NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
  1.1100 +      if (node != prevNode) {
  1.1101 +        nsCOMPtr<nsINode> p;
  1.1102 +        if (prevNode) {
  1.1103 +          p = do_QueryInterface(prevNode);
  1.1104 +          rv = SerializeNodeEnd(p, output);
  1.1105 +          NS_ENSURE_SUCCESS(rv, rv);
  1.1106 +        }
  1.1107 +        nsCOMPtr<nsIContent> content = do_QueryInterface(node);
  1.1108 +        if (content && content->IsHTML(nsGkAtoms::tr)) {
  1.1109 +          nsINode* n = content;
  1.1110 +          if (!prevNode) {
  1.1111 +            // Went from a non-<tr> to a <tr>
  1.1112 +            mCommonAncestors.Clear();
  1.1113 +            nsContentUtils::GetAncestors(n->GetParentNode(), mCommonAncestors);
  1.1114 +            rv = SerializeRangeContextStart(mCommonAncestors, output);
  1.1115 +            NS_ENSURE_SUCCESS(rv, rv);
  1.1116 +            // Don't let SerializeRangeToString serialize the context again
  1.1117 +            mDisableContextSerialize = true;
  1.1118 +          }
  1.1119 +
  1.1120 +          rv = SerializeNodeStart(n, 0, -1, output);
  1.1121 +          NS_ENSURE_SUCCESS(rv, rv);
  1.1122 +          prevNode = node;
  1.1123 +        } else if (prevNode) {
  1.1124 +          // Went from a <tr> to a non-<tr>
  1.1125 +          mCommonAncestors.Clear();
  1.1126 +          nsContentUtils::GetAncestors(p->GetParentNode(), mCommonAncestors);
  1.1127 +          mDisableContextSerialize = false;
  1.1128 +          rv = SerializeRangeContextEnd(mCommonAncestors, output);
  1.1129 +          NS_ENSURE_SUCCESS(rv, rv);
  1.1130 +          prevNode = nullptr;
  1.1131 +        }
  1.1132 +      }
  1.1133 +
  1.1134 +      nsRange* r = static_cast<nsRange*>(range.get());
  1.1135 +      rv = SerializeRangeToString(r, output);
  1.1136 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1137 +    }
  1.1138 +
  1.1139 +    if (prevNode) {
  1.1140 +      nsCOMPtr<nsINode> p = do_QueryInterface(prevNode);
  1.1141 +      rv = SerializeNodeEnd(p, output);
  1.1142 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1143 +      mCommonAncestors.Clear();
  1.1144 +      nsContentUtils::GetAncestors(p->GetParentNode(), mCommonAncestors);
  1.1145 +      mDisableContextSerialize = false; 
  1.1146 +      rv = SerializeRangeContextEnd(mCommonAncestors, output);
  1.1147 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1148 +    }
  1.1149 +
  1.1150 +    // Just to be safe
  1.1151 +    mDisableContextSerialize = false; 
  1.1152 +
  1.1153 +    mSelection = nullptr;
  1.1154 +  } else if (mRange) {
  1.1155 +      rv = SerializeRangeToString(mRange, output);
  1.1156 +
  1.1157 +      mRange = nullptr;
  1.1158 +  } else if (mNode) {
  1.1159 +    if (!mNodeFixup && !(mFlags & SkipInvisibleContent) && !mStream &&
  1.1160 +        mNodeIsContainer) {
  1.1161 +      rv = SerializeToStringIterative(mNode, output);
  1.1162 +    } else {
  1.1163 +      rv = SerializeToStringRecursive(mNode, output, mNodeIsContainer);
  1.1164 +    }
  1.1165 +    mNode = nullptr;
  1.1166 +  } else {
  1.1167 +    rv = mSerializer->AppendDocumentStart(mDocument, output);
  1.1168 +
  1.1169 +    if (NS_SUCCEEDED(rv)) {
  1.1170 +      rv = SerializeToStringRecursive(mDocument, output, false, aMaxLength);
  1.1171 +    }
  1.1172 +  }
  1.1173 +
  1.1174 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1175 +  rv = mSerializer->Flush(output);
  1.1176 + 
  1.1177 +  mCachedBuffer = nsStringBuffer::FromString(output);
  1.1178 +  // We have to be careful how we set aOutputString, because we don't
  1.1179 +  // want it to end up sharing mCachedBuffer if we plan to reuse it.
  1.1180 +  bool setOutput = false;
  1.1181 +  // Try to cache the buffer.
  1.1182 +  if (mCachedBuffer) {
  1.1183 +    if (mCachedBuffer->StorageSize() == bufferSize &&
  1.1184 +        !mCachedBuffer->IsReadonly()) {
  1.1185 +      mCachedBuffer->AddRef();
  1.1186 +    } else {
  1.1187 +      if (NS_SUCCEEDED(rv)) {
  1.1188 +        mCachedBuffer->ToString(output.Length(), aOutputString);
  1.1189 +        setOutput = true;
  1.1190 +      }
  1.1191 +      mCachedBuffer = nullptr;
  1.1192 +    }
  1.1193 +  }
  1.1194 +
  1.1195 +  if (!setOutput && NS_SUCCEEDED(rv)) {
  1.1196 +    aOutputString.Append(output.get(), output.Length());
  1.1197 +  }
  1.1198 +
  1.1199 +  return rv;
  1.1200 +}
  1.1201 +
  1.1202 +NS_IMETHODIMP
  1.1203 +nsDocumentEncoder::EncodeToStream(nsIOutputStream* aStream)
  1.1204 +{
  1.1205 +  nsresult rv = NS_OK;
  1.1206 +
  1.1207 +  if (!mDocument)
  1.1208 +    return NS_ERROR_NOT_INITIALIZED;
  1.1209 +
  1.1210 +  if (!mCharsetConverterManager) {
  1.1211 +    mCharsetConverterManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
  1.1212 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1213 +  }
  1.1214 +
  1.1215 +  rv = mCharsetConverterManager->GetUnicodeEncoder(mCharset.get(),
  1.1216 +                                                   getter_AddRefs(mUnicodeEncoder));
  1.1217 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1218 +
  1.1219 +  if (mMimeType.LowerCaseEqualsLiteral("text/plain")) {
  1.1220 +    rv = mUnicodeEncoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace, nullptr, '?');
  1.1221 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1222 +  }
  1.1223 +
  1.1224 +  mStream = aStream;
  1.1225 +
  1.1226 +  nsAutoString buf;
  1.1227 +
  1.1228 +  rv = EncodeToString(buf);
  1.1229 +
  1.1230 +  // Force a flush of the last chunk of data.
  1.1231 +  FlushText(buf, true);
  1.1232 +
  1.1233 +  mStream = nullptr;
  1.1234 +  mUnicodeEncoder = nullptr;
  1.1235 +
  1.1236 +  return rv;
  1.1237 +}
  1.1238 +
  1.1239 +NS_IMETHODIMP
  1.1240 +nsDocumentEncoder::EncodeToStringWithContext(nsAString& aContextString,
  1.1241 +                                             nsAString& aInfoString,
  1.1242 +                                             nsAString& aEncodedString)
  1.1243 +{
  1.1244 +  return NS_ERROR_NOT_IMPLEMENTED;
  1.1245 +}
  1.1246 +
  1.1247 +NS_IMETHODIMP
  1.1248 +nsDocumentEncoder::SetNodeFixup(nsIDocumentEncoderNodeFixup *aFixup)
  1.1249 +{
  1.1250 +  mNodeFixup = aFixup;
  1.1251 +  return NS_OK;
  1.1252 +}
  1.1253 +
  1.1254 +
  1.1255 +nsresult NS_NewTextEncoder(nsIDocumentEncoder** aResult); // make mac compiler happy
  1.1256 +
  1.1257 +nsresult
  1.1258 +NS_NewTextEncoder(nsIDocumentEncoder** aResult)
  1.1259 +{
  1.1260 +  *aResult = new nsDocumentEncoder;
  1.1261 +  if (!*aResult)
  1.1262 +    return NS_ERROR_OUT_OF_MEMORY;
  1.1263 + NS_ADDREF(*aResult);
  1.1264 + return NS_OK;
  1.1265 +}
  1.1266 +
  1.1267 +class nsHTMLCopyEncoder : public nsDocumentEncoder
  1.1268 +{
  1.1269 +public:
  1.1270 +
  1.1271 +  nsHTMLCopyEncoder();
  1.1272 +  virtual ~nsHTMLCopyEncoder();
  1.1273 +
  1.1274 +  NS_IMETHOD Init(nsIDOMDocument* aDocument, const nsAString& aMimeType, uint32_t aFlags);
  1.1275 +
  1.1276 +  // overridden methods from nsDocumentEncoder
  1.1277 +  NS_IMETHOD SetSelection(nsISelection* aSelection);
  1.1278 +  NS_IMETHOD EncodeToStringWithContext(nsAString& aContextString,
  1.1279 +                                       nsAString& aInfoString,
  1.1280 +                                       nsAString& aEncodedString);
  1.1281 +  NS_IMETHOD EncodeToString(nsAString& aOutputString);
  1.1282 +
  1.1283 +protected:
  1.1284 +
  1.1285 +  enum Endpoint
  1.1286 +  {
  1.1287 +    kStart,
  1.1288 +    kEnd
  1.1289 +  };
  1.1290 +  
  1.1291 +  nsresult PromoteRange(nsIDOMRange *inRange);
  1.1292 +  nsresult PromoteAncestorChain(nsCOMPtr<nsIDOMNode> *ioNode, 
  1.1293 +                                int32_t *ioStartOffset, 
  1.1294 +                                int32_t *ioEndOffset);
  1.1295 +  nsresult GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t aOffset, 
  1.1296 +                            nsCOMPtr<nsIDOMNode> *outNode, int32_t *outOffset, nsIDOMNode *aCommon);
  1.1297 +  nsCOMPtr<nsIDOMNode> GetChildAt(nsIDOMNode *aParent, int32_t aOffset);
  1.1298 +  bool IsMozBR(nsIDOMNode* aNode);
  1.1299 +  nsresult GetNodeLocation(nsIDOMNode *inChild, nsCOMPtr<nsIDOMNode> *outParent, int32_t *outOffset);
  1.1300 +  bool IsRoot(nsIDOMNode* aNode);
  1.1301 +  bool IsFirstNode(nsIDOMNode *aNode);
  1.1302 +  bool IsLastNode(nsIDOMNode *aNode);
  1.1303 +  bool IsEmptyTextContent(nsIDOMNode* aNode);
  1.1304 +  virtual bool IncludeInContext(nsINode *aNode);
  1.1305 +  virtual int32_t
  1.1306 +  GetImmediateContextCount(const nsTArray<nsINode*>& aAncestorArray);
  1.1307 +
  1.1308 +  bool mIsTextWidget;
  1.1309 +};
  1.1310 +
  1.1311 +nsHTMLCopyEncoder::nsHTMLCopyEncoder()
  1.1312 +{
  1.1313 +  mIsTextWidget = false;
  1.1314 +}
  1.1315 +
  1.1316 +nsHTMLCopyEncoder::~nsHTMLCopyEncoder()
  1.1317 +{
  1.1318 +}
  1.1319 +
  1.1320 +NS_IMETHODIMP
  1.1321 +nsHTMLCopyEncoder::Init(nsIDOMDocument* aDocument,
  1.1322 +                        const nsAString& aMimeType,
  1.1323 +                        uint32_t aFlags)
  1.1324 +{
  1.1325 +  if (!aDocument)
  1.1326 +    return NS_ERROR_INVALID_ARG;
  1.1327 +
  1.1328 +  mIsTextWidget = false;
  1.1329 +  Initialize();
  1.1330 +
  1.1331 +  mIsCopying = true;
  1.1332 +  mDocument = do_QueryInterface(aDocument);
  1.1333 +  NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
  1.1334 +
  1.1335 +  // Hack, hack! Traditionally, the caller passes text/unicode, which is
  1.1336 +  // treated as "guess text/html or text/plain" in this context. (It has a
  1.1337 +  // different meaning in other contexts. Sigh.) From now on, "text/plain"
  1.1338 +  // means forcing text/plain instead of guessing.
  1.1339 +  if (aMimeType.EqualsLiteral("text/plain")) {
  1.1340 +    mMimeType.AssignLiteral("text/plain");
  1.1341 +  } else {
  1.1342 +    mMimeType.AssignLiteral("text/html");
  1.1343 +  }
  1.1344 +
  1.1345 +  // Make all links absolute when copying
  1.1346 +  // (see related bugs #57296, #41924, #58646, #32768)
  1.1347 +  mFlags = aFlags | OutputAbsoluteLinks;
  1.1348 +
  1.1349 +  if (!mDocument->IsScriptEnabled())
  1.1350 +    mFlags |= OutputNoScriptContent;
  1.1351 +
  1.1352 +  return NS_OK;
  1.1353 +}
  1.1354 +
  1.1355 +NS_IMETHODIMP
  1.1356 +nsHTMLCopyEncoder::SetSelection(nsISelection* aSelection)
  1.1357 +{
  1.1358 +  // check for text widgets: we need to recognize these so that
  1.1359 +  // we don't tweak the selection to be outside of the magic
  1.1360 +  // div that ender-lite text widgets are embedded in.
  1.1361 +  
  1.1362 +  if (!aSelection) 
  1.1363 +    return NS_ERROR_NULL_POINTER;
  1.1364 +  
  1.1365 +  nsCOMPtr<nsIDOMRange> range;
  1.1366 +  nsCOMPtr<nsIDOMNode> commonParent;
  1.1367 +  Selection* selection = static_cast<Selection*>(aSelection);
  1.1368 +  uint32_t rangeCount = selection->GetRangeCount();
  1.1369 +
  1.1370 +  // if selection is uninitialized return
  1.1371 +  if (!rangeCount)
  1.1372 +    return NS_ERROR_FAILURE;
  1.1373 +  
  1.1374 +  // we'll just use the common parent of the first range.  Implicit assumption
  1.1375 +  // here that multi-range selections are table cell selections, in which case
  1.1376 +  // the common parent is somewhere in the table and we don't really care where.
  1.1377 +  nsresult rv = aSelection->GetRangeAt(0, getter_AddRefs(range));
  1.1378 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1379 +  if (!range)
  1.1380 +    return NS_ERROR_NULL_POINTER;
  1.1381 +  range->GetCommonAncestorContainer(getter_AddRefs(commonParent));
  1.1382 +
  1.1383 +  for (nsCOMPtr<nsIContent> selContent(do_QueryInterface(commonParent));
  1.1384 +       selContent;
  1.1385 +       selContent = selContent->GetParent())
  1.1386 +  {
  1.1387 +    // checking for selection inside a plaintext form widget
  1.1388 +    nsIAtom *atom = selContent->Tag();
  1.1389 +    if (atom == nsGkAtoms::input ||
  1.1390 +        atom == nsGkAtoms::textarea)
  1.1391 +    {
  1.1392 +      mIsTextWidget = true;
  1.1393 +      break;
  1.1394 +    }
  1.1395 +    else if (atom == nsGkAtoms::body)
  1.1396 +    {
  1.1397 +      // check for moz prewrap style on body.  If it's there we are 
  1.1398 +      // in a plaintext editor.  This is pretty cheezy but I haven't 
  1.1399 +      // found a good way to tell if we are in a plaintext editor.
  1.1400 +      nsCOMPtr<nsIDOMElement> bodyElem = do_QueryInterface(selContent);
  1.1401 +      nsAutoString wsVal;
  1.1402 +      rv = bodyElem->GetAttribute(NS_LITERAL_STRING("style"), wsVal);
  1.1403 +      if (NS_SUCCEEDED(rv) && (kNotFound != wsVal.Find(NS_LITERAL_STRING("pre-wrap"))))
  1.1404 +      {
  1.1405 +        mIsTextWidget = true;
  1.1406 +        break;
  1.1407 +      }
  1.1408 +    }
  1.1409 +  }
  1.1410 +  
  1.1411 +  // normalize selection if we are not in a widget
  1.1412 +  if (mIsTextWidget) 
  1.1413 +  {
  1.1414 +    mSelection = aSelection;
  1.1415 +    mMimeType.AssignLiteral("text/plain");
  1.1416 +    return NS_OK;
  1.1417 +  }
  1.1418 +
  1.1419 +  // also consider ourselves in a text widget if we can't find an html document
  1.1420 +  nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
  1.1421 +  if (!(htmlDoc && mDocument->IsHTML())) {
  1.1422 +    mIsTextWidget = true;
  1.1423 +    mSelection = aSelection;
  1.1424 +    // mMimeType is set to text/plain when encoding starts.
  1.1425 +    return NS_OK;
  1.1426 +  }
  1.1427 +  
  1.1428 +  // there's no Clone() for selection! fix...
  1.1429 +  //nsresult rv = aSelection->Clone(getter_AddRefs(mSelection);
  1.1430 +  //NS_ENSURE_SUCCESS(rv, rv);
  1.1431 +  NS_NewDomSelection(getter_AddRefs(mSelection));
  1.1432 +  NS_ENSURE_TRUE(mSelection, NS_ERROR_FAILURE);
  1.1433 +  
  1.1434 +  // loop thru the ranges in the selection
  1.1435 +  for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
  1.1436 +    range = selection->GetRangeAt(rangeIdx);
  1.1437 +    NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
  1.1438 +    nsCOMPtr<nsIDOMRange> myRange;
  1.1439 +    range->CloneRange(getter_AddRefs(myRange));
  1.1440 +    NS_ENSURE_TRUE(myRange, NS_ERROR_FAILURE);
  1.1441 +
  1.1442 +    // adjust range to include any ancestors who's children are entirely selected
  1.1443 +    rv = PromoteRange(myRange);
  1.1444 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1445 +    
  1.1446 +    rv = mSelection->AddRange(myRange);
  1.1447 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1448 +  }
  1.1449 +
  1.1450 +  return NS_OK;
  1.1451 +}
  1.1452 +
  1.1453 +NS_IMETHODIMP
  1.1454 +nsHTMLCopyEncoder::EncodeToString(nsAString& aOutputString)
  1.1455 +{
  1.1456 +  if (mIsTextWidget) {
  1.1457 +    mMimeType.AssignLiteral("text/plain");
  1.1458 +  }
  1.1459 +  return nsDocumentEncoder::EncodeToString(aOutputString);
  1.1460 +}
  1.1461 +
  1.1462 +NS_IMETHODIMP
  1.1463 +nsHTMLCopyEncoder::EncodeToStringWithContext(nsAString& aContextString,
  1.1464 +                                             nsAString& aInfoString,
  1.1465 +                                             nsAString& aEncodedString)
  1.1466 +{
  1.1467 +  nsresult rv = EncodeToString(aEncodedString);
  1.1468 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1469 +
  1.1470 +  // do not encode any context info or range hints if we are in a text widget.
  1.1471 +  if (mIsTextWidget) return NS_OK;
  1.1472 +
  1.1473 +  // now encode common ancestors into aContextString.  Note that the common ancestors
  1.1474 +  // will be for the last range in the selection in the case of multirange selections.
  1.1475 +  // encoding ancestors every range in a multirange selection in a way that could be 
  1.1476 +  // understood by the paste code would be a lot more work to do.  As a practical matter,
  1.1477 +  // selections are single range, and the ones that aren't are table cell selections
  1.1478 +  // where all the cells are in the same table.
  1.1479 +
  1.1480 +  // leaf of ancestors might be text node.  If so discard it.
  1.1481 +  int32_t count = mCommonAncestors.Length();
  1.1482 +  int32_t i;
  1.1483 +  nsCOMPtr<nsINode> node;
  1.1484 +  if (count > 0)
  1.1485 +    node = mCommonAncestors.ElementAt(0);
  1.1486 +
  1.1487 +  if (node && IsTextNode(node)) 
  1.1488 +  {
  1.1489 +    mCommonAncestors.RemoveElementAt(0);
  1.1490 +    // don't forget to adjust range depth info
  1.1491 +    if (mStartDepth) mStartDepth--;
  1.1492 +    if (mEndDepth) mEndDepth--;
  1.1493 +    // and the count
  1.1494 +    count--;
  1.1495 +  }
  1.1496 +  
  1.1497 +  i = count;
  1.1498 +  while (i > 0)
  1.1499 +  {
  1.1500 +    node = mCommonAncestors.ElementAt(--i);
  1.1501 +    SerializeNodeStart(node, 0, -1, aContextString);
  1.1502 +  }
  1.1503 +  //i = 0; guaranteed by above
  1.1504 +  while (i < count)
  1.1505 +  {
  1.1506 +    node = mCommonAncestors.ElementAt(i++);
  1.1507 +    SerializeNodeEnd(node, aContextString);
  1.1508 +  }
  1.1509 +
  1.1510 +  // encode range info : the start and end depth of the selection, where the depth is 
  1.1511 +  // distance down in the parent hierarchy.  Later we will need to add leading/trailing
  1.1512 +  // whitespace info to this.
  1.1513 +  nsAutoString infoString;
  1.1514 +  infoString.AppendInt(mStartDepth);
  1.1515 +  infoString.Append(char16_t(','));
  1.1516 +  infoString.AppendInt(mEndDepth);
  1.1517 +  aInfoString = infoString;
  1.1518 +  
  1.1519 +  return NS_OK;
  1.1520 +}
  1.1521 +
  1.1522 +
  1.1523 +bool
  1.1524 +nsHTMLCopyEncoder::IncludeInContext(nsINode *aNode)
  1.1525 +{
  1.1526 +  nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
  1.1527 +
  1.1528 +  if (!content)
  1.1529 +    return false;
  1.1530 +
  1.1531 +  nsIAtom *tag = content->Tag();
  1.1532 +
  1.1533 +  return (tag == nsGkAtoms::b        ||
  1.1534 +          tag == nsGkAtoms::i        ||
  1.1535 +          tag == nsGkAtoms::u        ||
  1.1536 +          tag == nsGkAtoms::a        ||
  1.1537 +          tag == nsGkAtoms::tt       ||
  1.1538 +          tag == nsGkAtoms::s        ||
  1.1539 +          tag == nsGkAtoms::big      ||
  1.1540 +          tag == nsGkAtoms::small    ||
  1.1541 +          tag == nsGkAtoms::strike   ||
  1.1542 +          tag == nsGkAtoms::em       ||
  1.1543 +          tag == nsGkAtoms::strong   ||
  1.1544 +          tag == nsGkAtoms::dfn      ||
  1.1545 +          tag == nsGkAtoms::code     ||
  1.1546 +          tag == nsGkAtoms::cite     ||
  1.1547 +          tag == nsGkAtoms::var      ||
  1.1548 +          tag == nsGkAtoms::abbr     ||
  1.1549 +          tag == nsGkAtoms::font     ||
  1.1550 +          tag == nsGkAtoms::script   ||
  1.1551 +          tag == nsGkAtoms::span     ||
  1.1552 +          tag == nsGkAtoms::pre      ||
  1.1553 +          tag == nsGkAtoms::h1       ||
  1.1554 +          tag == nsGkAtoms::h2       ||
  1.1555 +          tag == nsGkAtoms::h3       ||
  1.1556 +          tag == nsGkAtoms::h4       ||
  1.1557 +          tag == nsGkAtoms::h5       ||
  1.1558 +          tag == nsGkAtoms::h6);
  1.1559 +}
  1.1560 +
  1.1561 +
  1.1562 +nsresult 
  1.1563 +nsHTMLCopyEncoder::PromoteRange(nsIDOMRange *inRange)
  1.1564 +{
  1.1565 +  if (!inRange) return NS_ERROR_NULL_POINTER;
  1.1566 +  nsresult rv;
  1.1567 +  nsCOMPtr<nsIDOMNode> startNode, endNode, common;
  1.1568 +  int32_t startOffset, endOffset;
  1.1569 +  
  1.1570 +  rv = inRange->GetCommonAncestorContainer(getter_AddRefs(common));
  1.1571 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1572 +  rv = inRange->GetStartContainer(getter_AddRefs(startNode));
  1.1573 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1574 +  rv = inRange->GetStartOffset(&startOffset);
  1.1575 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1576 +  rv = inRange->GetEndContainer(getter_AddRefs(endNode));
  1.1577 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1578 +  rv = inRange->GetEndOffset(&endOffset);
  1.1579 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1580 +  
  1.1581 +  nsCOMPtr<nsIDOMNode> opStartNode;
  1.1582 +  nsCOMPtr<nsIDOMNode> opEndNode;
  1.1583 +  int32_t opStartOffset, opEndOffset;
  1.1584 +  nsCOMPtr<nsIDOMRange> opRange;
  1.1585 +  
  1.1586 +  // examine range endpoints.  
  1.1587 +  rv = GetPromotedPoint( kStart, startNode, startOffset, address_of(opStartNode), &opStartOffset, common);
  1.1588 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1589 +  rv = GetPromotedPoint( kEnd, endNode, endOffset, address_of(opEndNode), &opEndOffset, common);
  1.1590 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1591 +  
  1.1592 +  // if both range endpoints are at the common ancestor, check for possible inclusion of ancestors
  1.1593 +  if ( (opStartNode == common) && (opEndNode == common) )
  1.1594 +  {
  1.1595 +    rv = PromoteAncestorChain(address_of(opStartNode), &opStartOffset, &opEndOffset);
  1.1596 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1597 +    opEndNode = opStartNode;
  1.1598 +  }
  1.1599 +  
  1.1600 +  // set the range to the new values
  1.1601 +  rv = inRange->SetStart(opStartNode, opStartOffset);
  1.1602 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1603 +  rv = inRange->SetEnd(opEndNode, opEndOffset);
  1.1604 +  return rv;
  1.1605 +} 
  1.1606 +
  1.1607 +
  1.1608 +// PromoteAncestorChain will promote a range represented by [{*ioNode,*ioStartOffset} , {*ioNode,*ioEndOffset}]
  1.1609 +// The promotion is different from that found in getPromotedPoint: it will only promote one endpoint if it can
  1.1610 +// promote the other.  Thus, instead of having a startnode/endNode, there is just the one ioNode.
  1.1611 +nsresult
  1.1612 +nsHTMLCopyEncoder::PromoteAncestorChain(nsCOMPtr<nsIDOMNode> *ioNode, 
  1.1613 +                                        int32_t *ioStartOffset, 
  1.1614 +                                        int32_t *ioEndOffset) 
  1.1615 +{
  1.1616 +  if (!ioNode || !ioStartOffset || !ioEndOffset) return NS_ERROR_NULL_POINTER;
  1.1617 +
  1.1618 +  nsresult rv = NS_OK;
  1.1619 +  bool done = false;
  1.1620 +
  1.1621 +  nsCOMPtr<nsIDOMNode> frontNode, endNode, parent;
  1.1622 +  int32_t frontOffset, endOffset;
  1.1623 +
  1.1624 +  //save the editable state of the ioNode, so we don't promote an ancestor if it has different editable state
  1.1625 +  nsCOMPtr<nsINode> node = do_QueryInterface(*ioNode);
  1.1626 +  bool isEditable = node->IsEditable();
  1.1627 +  
  1.1628 +  // loop for as long as we can promote both endpoints
  1.1629 +  while (!done)
  1.1630 +  {
  1.1631 +    rv = (*ioNode)->GetParentNode(getter_AddRefs(parent));
  1.1632 +    if ((NS_FAILED(rv)) || !parent)
  1.1633 +      done = true;
  1.1634 +    else
  1.1635 +    {
  1.1636 +      // passing parent as last param to GetPromotedPoint() allows it to promote only one level
  1.1637 +      // up the hierarchy.
  1.1638 +      rv = GetPromotedPoint( kStart, *ioNode, *ioStartOffset, address_of(frontNode), &frontOffset, parent);
  1.1639 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1640 +      // then we make the same attempt with the endpoint
  1.1641 +      rv = GetPromotedPoint( kEnd, *ioNode, *ioEndOffset, address_of(endNode), &endOffset, parent);
  1.1642 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1643 +
  1.1644 +      nsCOMPtr<nsINode> frontINode = do_QueryInterface(frontNode);
  1.1645 +      // if both endpoints were promoted one level and isEditable is the same as the original node, 
  1.1646 +      // keep looping - otherwise we are done.
  1.1647 +      if ( (frontNode != parent) || (endNode != parent) || (frontINode->IsEditable() != isEditable) )
  1.1648 +        done = true;
  1.1649 +      else
  1.1650 +      {
  1.1651 +        *ioNode = frontNode;  
  1.1652 +        *ioStartOffset = frontOffset;
  1.1653 +        *ioEndOffset = endOffset;
  1.1654 +      }
  1.1655 +    }
  1.1656 +  }
  1.1657 +  return rv;
  1.1658 +}
  1.1659 +
  1.1660 +nsresult
  1.1661 +nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t aOffset, 
  1.1662 +                                  nsCOMPtr<nsIDOMNode> *outNode, int32_t *outOffset, nsIDOMNode *common)
  1.1663 +{
  1.1664 +  nsresult rv = NS_OK;
  1.1665 +  nsCOMPtr<nsIDOMNode> node = aNode;
  1.1666 +  nsCOMPtr<nsIDOMNode> parent = aNode;
  1.1667 +  int32_t offset = aOffset;
  1.1668 +  bool    bResetPromotion = false;
  1.1669 +  
  1.1670 +  // default values
  1.1671 +  *outNode = node;
  1.1672 +  *outOffset = offset;
  1.1673 +
  1.1674 +  if (common == node) 
  1.1675 +    return NS_OK;
  1.1676 +    
  1.1677 +  if (aWhere == kStart)
  1.1678 +  {
  1.1679 +    // some special casing for text nodes
  1.1680 +    nsCOMPtr<nsINode> t = do_QueryInterface(aNode);
  1.1681 +    if (IsTextNode(t))
  1.1682 +    {
  1.1683 +      // if not at beginning of text node, we are done
  1.1684 +      if (offset >  0) 
  1.1685 +      {
  1.1686 +        // unless everything before us in just whitespace.  NOTE: we need a more
  1.1687 +        // general solution that truly detects all cases of non-significant
  1.1688 +        // whitesace with no false alarms.
  1.1689 +        nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(aNode);
  1.1690 +        nsAutoString text;
  1.1691 +        nodeAsText->SubstringData(0, offset, text);
  1.1692 +        text.CompressWhitespace();
  1.1693 +        if (!text.IsEmpty())
  1.1694 +          return NS_OK;
  1.1695 +        bResetPromotion = true;
  1.1696 +      }
  1.1697 +      // else
  1.1698 +      rv = GetNodeLocation(aNode, address_of(parent), &offset);
  1.1699 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1700 +    }
  1.1701 +    else
  1.1702 +    {
  1.1703 +      node = GetChildAt(parent,offset);
  1.1704 +    }
  1.1705 +    if (!node) node = parent;
  1.1706 +
  1.1707 +    // finding the real start for this point.  look up the tree for as long as we are the 
  1.1708 +    // first node in the container, and as long as we haven't hit the body node.
  1.1709 +    if (!IsRoot(node) && (parent != common))
  1.1710 +    {
  1.1711 +      rv = GetNodeLocation(node, address_of(parent), &offset);
  1.1712 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1713 +      if (offset == -1) return NS_OK; // we hit generated content; STOP
  1.1714 +      nsIParserService *parserService = nsContentUtils::GetParserService();
  1.1715 +      if (!parserService)
  1.1716 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1717 +      while ((IsFirstNode(node)) && (!IsRoot(parent)) && (parent != common))
  1.1718 +      {
  1.1719 +        if (bResetPromotion)
  1.1720 +        {
  1.1721 +          nsCOMPtr<nsIContent> content = do_QueryInterface(parent);
  1.1722 +          if (content)
  1.1723 +          {
  1.1724 +            bool isBlock = false;
  1.1725 +            parserService->IsBlock(parserService->HTMLAtomTagToId(content->Tag()), isBlock);
  1.1726 +            if (isBlock)
  1.1727 +            {
  1.1728 +              bResetPromotion = false;
  1.1729 +            }
  1.1730 +          }   
  1.1731 +        }
  1.1732 +         
  1.1733 +        node = parent;
  1.1734 +        rv = GetNodeLocation(node, address_of(parent), &offset);
  1.1735 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1736 +        if (offset == -1)  // we hit generated content; STOP
  1.1737 +        {
  1.1738 +          // back up a bit
  1.1739 +          parent = node;
  1.1740 +          offset = 0;
  1.1741 +          break;
  1.1742 +        }
  1.1743 +      } 
  1.1744 +      if (bResetPromotion)
  1.1745 +      {
  1.1746 +        *outNode = aNode;
  1.1747 +        *outOffset = aOffset;
  1.1748 +      }
  1.1749 +      else
  1.1750 +      {
  1.1751 +        *outNode = parent;
  1.1752 +        *outOffset = offset;
  1.1753 +      }
  1.1754 +      return rv;
  1.1755 +    }
  1.1756 +  }
  1.1757 +  
  1.1758 +  if (aWhere == kEnd)
  1.1759 +  {
  1.1760 +    // some special casing for text nodes
  1.1761 +    nsCOMPtr<nsINode> n = do_QueryInterface(aNode);
  1.1762 +    if (IsTextNode(n))
  1.1763 +    {
  1.1764 +      // if not at end of text node, we are done
  1.1765 +      uint32_t len = n->Length();
  1.1766 +      if (offset < (int32_t)len)
  1.1767 +      {
  1.1768 +        // unless everything after us in just whitespace.  NOTE: we need a more
  1.1769 +        // general solution that truly detects all cases of non-significant
  1.1770 +        // whitespace with no false alarms.
  1.1771 +        nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(aNode);
  1.1772 +        nsAutoString text;
  1.1773 +        nodeAsText->SubstringData(offset, len-offset, text);
  1.1774 +        text.CompressWhitespace();
  1.1775 +        if (!text.IsEmpty())
  1.1776 +          return NS_OK;
  1.1777 +        bResetPromotion = true;
  1.1778 +      }
  1.1779 +      rv = GetNodeLocation(aNode, address_of(parent), &offset);
  1.1780 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1781 +    }
  1.1782 +    else
  1.1783 +    {
  1.1784 +      if (offset) offset--; // we want node _before_ offset
  1.1785 +      node = GetChildAt(parent,offset);
  1.1786 +    }
  1.1787 +    if (!node) node = parent;
  1.1788 +    
  1.1789 +    // finding the real end for this point.  look up the tree for as long as we are the 
  1.1790 +    // last node in the container, and as long as we haven't hit the body node.
  1.1791 +    if (!IsRoot(node) && (parent != common))
  1.1792 +    {
  1.1793 +      rv = GetNodeLocation(node, address_of(parent), &offset);
  1.1794 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1795 +      if (offset == -1) return NS_OK; // we hit generated content; STOP
  1.1796 +      nsIParserService *parserService = nsContentUtils::GetParserService();
  1.1797 +      if (!parserService)
  1.1798 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1799 +      while ((IsLastNode(node)) && (!IsRoot(parent)) && (parent != common))
  1.1800 +      {
  1.1801 +        if (bResetPromotion)
  1.1802 +        {
  1.1803 +          nsCOMPtr<nsIContent> content = do_QueryInterface(parent);
  1.1804 +          if (content)
  1.1805 +          {
  1.1806 +            bool isBlock = false;
  1.1807 +            parserService->IsBlock(parserService->HTMLAtomTagToId(content->Tag()), isBlock);
  1.1808 +            if (isBlock)
  1.1809 +            {
  1.1810 +              bResetPromotion = false;
  1.1811 +            }
  1.1812 +          }   
  1.1813 +        }
  1.1814 +          
  1.1815 +        node = parent;
  1.1816 +        rv = GetNodeLocation(node, address_of(parent), &offset);
  1.1817 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1818 +        if (offset == -1)  // we hit generated content; STOP
  1.1819 +        {
  1.1820 +          // back up a bit
  1.1821 +          parent = node;
  1.1822 +          offset = 0;
  1.1823 +          break;
  1.1824 +        }
  1.1825 +      } 
  1.1826 +      if (bResetPromotion)
  1.1827 +      {
  1.1828 +        *outNode = aNode;
  1.1829 +        *outOffset = aOffset;
  1.1830 +      }
  1.1831 +      else
  1.1832 +      {
  1.1833 +        *outNode = parent;
  1.1834 +        offset++;  // add one since this in an endpoint - want to be AFTER node.
  1.1835 +        *outOffset = offset;
  1.1836 +      }
  1.1837 +      return rv;
  1.1838 +    }
  1.1839 +  }
  1.1840 +  
  1.1841 +  return rv;
  1.1842 +}
  1.1843 +
  1.1844 +nsCOMPtr<nsIDOMNode> 
  1.1845 +nsHTMLCopyEncoder::GetChildAt(nsIDOMNode *aParent, int32_t aOffset)
  1.1846 +{
  1.1847 +  nsCOMPtr<nsIDOMNode> resultNode;
  1.1848 +  
  1.1849 +  if (!aParent) 
  1.1850 +    return resultNode;
  1.1851 +  
  1.1852 +  nsCOMPtr<nsIContent> content = do_QueryInterface(aParent);
  1.1853 +  NS_PRECONDITION(content, "null content in nsHTMLCopyEncoder::GetChildAt");
  1.1854 +
  1.1855 +  resultNode = do_QueryInterface(content->GetChildAt(aOffset));
  1.1856 +
  1.1857 +  return resultNode;
  1.1858 +}
  1.1859 +
  1.1860 +bool 
  1.1861 +nsHTMLCopyEncoder::IsMozBR(nsIDOMNode* aNode)
  1.1862 +{
  1.1863 +  MOZ_ASSERT(aNode);
  1.1864 +  nsCOMPtr<Element> element = do_QueryInterface(aNode);
  1.1865 +  return element &&
  1.1866 +         element->IsHTML(nsGkAtoms::br) &&
  1.1867 +         element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
  1.1868 +                              NS_LITERAL_STRING("_moz"), eIgnoreCase);
  1.1869 +}
  1.1870 +
  1.1871 +nsresult 
  1.1872 +nsHTMLCopyEncoder::GetNodeLocation(nsIDOMNode *inChild,
  1.1873 +                                   nsCOMPtr<nsIDOMNode> *outParent,
  1.1874 +                                   int32_t *outOffset)
  1.1875 +{
  1.1876 +  NS_ASSERTION((inChild && outParent && outOffset), "bad args");
  1.1877 +  nsresult result = NS_ERROR_NULL_POINTER;
  1.1878 +  if (inChild && outParent && outOffset)
  1.1879 +  {
  1.1880 +    result = inChild->GetParentNode(getter_AddRefs(*outParent));
  1.1881 +    if ((NS_SUCCEEDED(result)) && (*outParent))
  1.1882 +    {
  1.1883 +      nsCOMPtr<nsIContent> content = do_QueryInterface(*outParent);
  1.1884 +      nsCOMPtr<nsIContent> cChild = do_QueryInterface(inChild);
  1.1885 +      if (!cChild || !content)
  1.1886 +        return NS_ERROR_NULL_POINTER;
  1.1887 +
  1.1888 +      *outOffset = content->IndexOf(cChild);
  1.1889 +    }
  1.1890 +  }
  1.1891 +  return result;
  1.1892 +}
  1.1893 +
  1.1894 +bool
  1.1895 +nsHTMLCopyEncoder::IsRoot(nsIDOMNode* aNode)
  1.1896 +{
  1.1897 +  nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
  1.1898 +  if (content)
  1.1899 +  {
  1.1900 +    if (mIsTextWidget) 
  1.1901 +      return (IsTag(content, nsGkAtoms::div));
  1.1902 +
  1.1903 +    return (IsTag(content, nsGkAtoms::body) ||
  1.1904 +            IsTag(content, nsGkAtoms::td)   ||
  1.1905 +            IsTag(content, nsGkAtoms::th));
  1.1906 +  }
  1.1907 +  return false;
  1.1908 +}
  1.1909 +
  1.1910 +bool
  1.1911 +nsHTMLCopyEncoder::IsFirstNode(nsIDOMNode *aNode)
  1.1912 +{
  1.1913 +  nsCOMPtr<nsIDOMNode> parent;
  1.1914 +  int32_t offset, j=0;
  1.1915 +  nsresult rv = GetNodeLocation(aNode, address_of(parent), &offset);
  1.1916 +  if (NS_FAILED(rv)) 
  1.1917 +  {
  1.1918 +    NS_NOTREACHED("failure in IsFirstNode");
  1.1919 +    return false;
  1.1920 +  }
  1.1921 +  if (offset == 0)  // easy case, we are first dom child
  1.1922 +    return true;
  1.1923 +  if (!parent)  
  1.1924 +    return true;
  1.1925 +  
  1.1926 +  // need to check if any nodes before us are really visible.
  1.1927 +  // Mike wrote something for me along these lines in nsSelectionController,
  1.1928 +  // but I don't think it's ready for use yet - revisit.
  1.1929 +  // HACK: for now, simply consider all whitespace text nodes to be 
  1.1930 +  // invisible formatting nodes.
  1.1931 +  nsCOMPtr<nsIDOMNodeList> childList;
  1.1932 +  nsCOMPtr<nsIDOMNode> child;
  1.1933 +
  1.1934 +  rv = parent->GetChildNodes(getter_AddRefs(childList));
  1.1935 +  if (NS_FAILED(rv) || !childList) 
  1.1936 +  {
  1.1937 +    NS_NOTREACHED("failure in IsFirstNode");
  1.1938 +    return true;
  1.1939 +  }
  1.1940 +  while (j < offset)
  1.1941 +  {
  1.1942 +    childList->Item(j, getter_AddRefs(child));
  1.1943 +    if (!IsEmptyTextContent(child)) 
  1.1944 +      return false;
  1.1945 +    j++;
  1.1946 +  }
  1.1947 +  return true;
  1.1948 +}
  1.1949 +
  1.1950 +
  1.1951 +bool
  1.1952 +nsHTMLCopyEncoder::IsLastNode(nsIDOMNode *aNode)
  1.1953 +{
  1.1954 +  nsCOMPtr<nsIDOMNode> parent;
  1.1955 +  int32_t offset,j;
  1.1956 +  nsresult rv = GetNodeLocation(aNode, address_of(parent), &offset);
  1.1957 +  if (NS_FAILED(rv)) 
  1.1958 +  {
  1.1959 +    NS_NOTREACHED("failure in IsLastNode");
  1.1960 +    return false;
  1.1961 +  }
  1.1962 +  nsCOMPtr<nsINode> parentNode = do_QueryInterface(parent);
  1.1963 +  if (!parentNode) {
  1.1964 +    return true;
  1.1965 +  }
  1.1966 +
  1.1967 +  uint32_t numChildren = parentNode->Length();
  1.1968 +  if (offset+1 == (int32_t)numChildren) // easy case, we are last dom child
  1.1969 +    return true;
  1.1970 +  // need to check if any nodes after us are really visible.
  1.1971 +  // Mike wrote something for me along these lines in nsSelectionController,
  1.1972 +  // but I don't think it's ready for use yet - revisit.
  1.1973 +  // HACK: for now, simply consider all whitespace text nodes to be 
  1.1974 +  // invisible formatting nodes.
  1.1975 +  j = (int32_t)numChildren-1;
  1.1976 +  nsCOMPtr<nsIDOMNodeList>childList;
  1.1977 +  nsCOMPtr<nsIDOMNode> child;
  1.1978 +  rv = parent->GetChildNodes(getter_AddRefs(childList));
  1.1979 +  if (NS_FAILED(rv) || !childList) 
  1.1980 +  {
  1.1981 +    NS_NOTREACHED("failure in IsLastNode");
  1.1982 +    return true;
  1.1983 +  }
  1.1984 +  while (j > offset)
  1.1985 +  {
  1.1986 +    childList->Item(j, getter_AddRefs(child));
  1.1987 +    j--;
  1.1988 +    if (IsMozBR(child))  // we ignore trailing moz BRs.  
  1.1989 +      continue;
  1.1990 +    if (!IsEmptyTextContent(child)) 
  1.1991 +      return false;
  1.1992 +  }
  1.1993 +  return true;
  1.1994 +}
  1.1995 +
  1.1996 +bool
  1.1997 +nsHTMLCopyEncoder::IsEmptyTextContent(nsIDOMNode* aNode)
  1.1998 +{
  1.1999 +  nsCOMPtr<nsIContent> cont = do_QueryInterface(aNode);
  1.2000 +  return cont && cont->TextIsOnlyWhitespace();
  1.2001 +}
  1.2002 +
  1.2003 +nsresult NS_NewHTMLCopyTextEncoder(nsIDocumentEncoder** aResult); // make mac compiler happy
  1.2004 +
  1.2005 +nsresult
  1.2006 +NS_NewHTMLCopyTextEncoder(nsIDocumentEncoder** aResult)
  1.2007 +{
  1.2008 +  *aResult = new nsHTMLCopyEncoder;
  1.2009 +  if (!*aResult)
  1.2010 +    return NS_ERROR_OUT_OF_MEMORY;
  1.2011 + NS_ADDREF(*aResult);
  1.2012 + return NS_OK;
  1.2013 +}
  1.2014 +
  1.2015 +int32_t
  1.2016 +nsHTMLCopyEncoder::GetImmediateContextCount(const nsTArray<nsINode*>& aAncestorArray)
  1.2017 +{
  1.2018 +  int32_t i = aAncestorArray.Length(), j = 0;
  1.2019 +  while (j < i) {
  1.2020 +    nsINode *node = aAncestorArray.ElementAt(j);
  1.2021 +    if (!node) {
  1.2022 +      break;
  1.2023 +    }
  1.2024 +    nsCOMPtr<nsIContent> content(do_QueryInterface(node));
  1.2025 +    if (!content || !content->IsHTML() || (content->Tag() != nsGkAtoms::tr    &&
  1.2026 +                                           content->Tag() != nsGkAtoms::thead &&
  1.2027 +                                           content->Tag() != nsGkAtoms::tbody &&
  1.2028 +                                           content->Tag() != nsGkAtoms::tfoot &&
  1.2029 +                                           content->Tag() != nsGkAtoms::table)) {
  1.2030 +      break;
  1.2031 +    }
  1.2032 +    ++j;
  1.2033 +  }
  1.2034 +  return j;
  1.2035 +}
  1.2036 +

mercurial