From 32d78b6bbb6ec2d67636e59243c80bf761d38d96 Mon Sep 17 00:00:00 2001 From: lwz Date: Wed, 16 May 2012 12:39:54 -0400 Subject: [PATCH] Bug 137450 - Problem copying and pasting a table from a web page to excel. r=bz --- content/base/src/nsDocumentEncoder.cpp | 96 +++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 8 deletions(-) diff --git a/content/base/src/nsDocumentEncoder.cpp b/content/base/src/nsDocumentEncoder.cpp index bfe9d357ca5..6a44e8255c9 100644 --- a/content/base/src/nsDocumentEncoder.cpp +++ b/content/base/src/nsDocumentEncoder.cpp @@ -130,6 +130,11 @@ protected: nsAString& aString); nsresult SerializeRangeContextEnd(const nsTArray& aAncestorArray, nsAString& aString); + virtual PRInt32 + GetImmediateContextCount(const nsTArray& aAncestorArray) + { + return -1; + } nsresult FlushText(nsAString& aString, bool aForce); @@ -185,6 +190,9 @@ protected: nsAutoTArray mEndNodes; nsAutoTArray mEndOffsets; bool mHaltRangeHint; + // HTML context has already been serialized for + // table cell selections (where parent is ) + bool mContextAlreadySerialized; bool mIsCopying; // Set to true only while copying bool mNodeIsContainer; nsStringBuffer* mCachedBuffer; @@ -232,6 +240,7 @@ void nsDocumentEncoder::Initialize(bool aClearCachedSerializer) mStartRootIndex = 0; mEndRootIndex = 0; mHaltRangeHint = false; + mContextAlreadySerialized = false; mNodeIsContainer = false; if (aClearCachedSerializer) { mSerializer = nsnull; @@ -913,16 +922,23 @@ nsresult nsDocumentEncoder::SerializeRangeContextStart(const nsTArray& aAncestorArray, nsAString& aString) { - PRInt32 i = aAncestorArray.Length(); + if (mContextAlreadySerialized) { + return NS_OK; + } + PRInt32 i = aAncestorArray.Length(), j; nsresult rv = NS_OK; + // currently only for table-related elements; see Bug 137450 + j = GetImmediateContextCount(aAncestorArray); + while (i > 0) { nsINode *node = aAncestorArray.ElementAt(--i); if (!node) break; - if (IncludeInContext(node)) { + // Either a general inclusion or as immediate context + if (IncludeInContext(node) || i < j) { rv = SerializeNodeStart(node, 0, -1, aString); if (NS_FAILED(rv)) @@ -937,17 +953,24 @@ nsresult nsDocumentEncoder::SerializeRangeContextEnd(const nsTArray& aAncestorArray, nsAString& aString) { - PRInt32 i = 0; + if (mContextAlreadySerialized) { + return NS_OK; + } + PRInt32 i = 0, j; PRInt32 count = aAncestorArray.Length(); nsresult rv = NS_OK; + // currently only for table-related elements + j = GetImmediateContextCount(aAncestorArray); + while (i < count) { nsINode *node = aAncestorArray.ElementAt(i++); if (!node) break; - if (IncludeInContext(node)) { + // Either a general inclusion or as immediate context + if (IncludeInContext(node) || i - 1 < j) { rv = SerializeNodeEnd(node, aString); if (NS_FAILED(rv)) @@ -1083,21 +1106,44 @@ nsDocumentEncoder::EncodeToString(nsAString& aOutputString) // Bug 236546: newlines not added when copying table cells into clipboard // Each selected cell shows up as a range containing a row with a single cell // get the row, compare it to previous row and emit as needed + // Bug 137450: Problem copying/pasting a table from a web page to Excel. + // Each separate block of produced above will be wrapped by the + // immediate context. This assumes that you can't select cells that are + // multiple selections from two tables simultaneously. range->GetStartContainer(getter_AddRefs(node)); NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); if (node != prevNode) { + nsCOMPtr p; if (prevNode) { - nsCOMPtr p = do_QueryInterface(prevNode); + p = do_QueryInterface(prevNode); rv = SerializeNodeEnd(p, output); NS_ENSURE_SUCCESS(rv, rv); - prevNode = nsnull; } nsCOMPtr content = do_QueryInterface(node); - if (content && content->Tag() == nsGkAtoms::tr) { - nsCOMPtr n = do_QueryInterface(node); + if (content && content->IsHTML(nsGkAtoms::tr)) { + nsINode* n = content; + if (!prevNode) { + // Went from a non- to a + mCommonAncestors.Clear(); + nsContentUtils::GetAncestors(n->GetNodeParent(), mCommonAncestors); + rv = SerializeRangeContextStart(mCommonAncestors, output); + NS_ENSURE_SUCCESS(rv, rv); + // Don't let SerializeRangeToString serialize the context again + mContextAlreadySerialized = true; + } + rv = SerializeNodeStart(n, 0, -1, output); NS_ENSURE_SUCCESS(rv, rv); prevNode = node; + } else if (prevNode) { + // Went from a to a non- + mCommonAncestors.Clear(); + nsContentUtils::GetAncestors(p->GetNodeParent(), mCommonAncestors); + rv = SerializeRangeContextEnd(mCommonAncestors, output); + NS_ENSURE_SUCCESS(rv, rv); + // Allow output of context again + mContextAlreadySerialized = false; + prevNode = nsnull; } } @@ -1105,12 +1151,21 @@ nsDocumentEncoder::EncodeToString(nsAString& aOutputString) rv = SerializeRangeToString(r, output); NS_ENSURE_SUCCESS(rv, rv); } + if (prevNode) { nsCOMPtr p = do_QueryInterface(prevNode); rv = SerializeNodeEnd(p, output); NS_ENSURE_SUCCESS(rv, rv); + mCommonAncestors.Clear(); + nsContentUtils::GetAncestors(p->GetNodeParent(), mCommonAncestors); + rv = SerializeRangeContextEnd(mCommonAncestors, output); + NS_ENSURE_SUCCESS(rv, rv); } + // could have been placed into the above if-loop + // but put outside to be safe + mContextAlreadySerialized = false; + mSelection = nsnull; } else if (mRange) { rv = SerializeRangeToString(mRange, output); @@ -1262,6 +1317,8 @@ protected: bool IsLastNode(nsIDOMNode *aNode); bool IsEmptyTextContent(nsIDOMNode* aNode); virtual bool IncludeInContext(nsINode *aNode); + virtual PRInt32 + GetImmediateContextCount(const nsTArray& aAncestorArray); bool mIsTextWidget; }; @@ -1975,3 +2032,26 @@ NS_NewHTMLCopyTextEncoder(nsIDocumentEncoder** aResult) NS_ADDREF(*aResult); return NS_OK; } + +PRInt32 +nsHTMLCopyEncoder::GetImmediateContextCount(const nsTArray& aAncestorArray) +{ + PRInt32 i = aAncestorArray.Length(), j = 0; + while (j < i) { + nsINode *node = aAncestorArray.ElementAt(j); + if (!node) { + break; + } + nsCOMPtr content(do_QueryInterface(node)); + if (!content || !content->IsHTML() || content->Tag() != nsGkAtoms::tr && + content->Tag() != nsGkAtoms::thead && + content->Tag() != nsGkAtoms::tbody && + content->Tag() != nsGkAtoms::tfoot && + content->Tag() != nsGkAtoms::table) { + break; + } + ++j; + } + return j; +} +