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.)

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

mercurial