From 37297d45dc50be4651f505fd7d08db257b02a6b4 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 27 Sep 2010 13:21:34 -0400 Subject: [PATCH] Bug 597784 - The inserthtml command should not use a sanitizing fragment content sink; r=bzbarsky a=blocking-betaN,1.9.2.11,1.9.1.14 --- editor/libeditor/html/nsHTMLDataTransfer.cpp | 109 +++++++++++------- editor/libeditor/html/nsHTMLEditor.h | 23 +++- editor/libeditor/html/tests/Makefile.in | 1 + .../libeditor/html/tests/test_bug520189.html | 35 +++--- .../libeditor/html/tests/test_bug597784.html | 39 +++++++ 5 files changed, 152 insertions(+), 55 deletions(-) create mode 100644 editor/libeditor/html/tests/test_bug597784.html diff --git a/editor/libeditor/html/nsHTMLDataTransfer.cpp b/editor/libeditor/html/nsHTMLDataTransfer.cpp index eab2b1c47f3..eafc50e0621 100644 --- a/editor/libeditor/html/nsHTMLDataTransfer.cpp +++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp @@ -262,6 +262,22 @@ nsHTMLEditor::InsertHTMLWithContext(const nsAString & aInputString, nsIDOMNode *aDestNode, PRInt32 aDestOffset, PRBool aDeleteSelection) +{ + return DoInsertHTMLWithContext(aInputString, aContextStr, aInfoStr, + aFlavor, aSourceDoc, aDestNode, aDestOffset, aDeleteSelection, + PR_TRUE); +} + +nsresult +nsHTMLEditor::DoInsertHTMLWithContext(const nsAString & aInputString, + const nsAString & aContextStr, + const nsAString & aInfoStr, + const nsAString & aFlavor, + nsIDOMDocument *aSourceDoc, + nsIDOMNode *aDestNode, + PRInt32 aDestOffset, + PRBool aDeleteSelection, + PRBool aTrustedInput) { NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED); @@ -285,7 +301,8 @@ nsHTMLEditor::InsertHTMLWithContext(const nsAString & aInputString, address_of(streamStartParent), address_of(streamEndParent), &streamStartOffset, - &streamEndOffset); + &streamEndOffset, + aTrustedInput); NS_ENSURE_SUCCESS(res, res); nsCOMPtr targetNode, tempNode; @@ -1301,11 +1318,12 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable if (NS_SUCCEEDED(rv) && !cffragment.IsEmpty()) { nsAutoEditBatch beginBatching(this); - rv = InsertHTMLWithContext(cffragment, - cfcontext, cfselection, flavor, - aSourceDoc, - aDestinationNode, aDestOffset, - aDoDeleteSelection); + rv = DoInsertHTMLWithContext(cffragment, + cfcontext, cfselection, flavor, + aSourceDoc, + aDestinationNode, aDestOffset, + aDoDeleteSelection, + PR_FALSE); } } } @@ -1319,11 +1337,12 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable NS_ASSERTION(text.Length() <= (len/2), "Invalid length!"); stuffToPaste.Assign(text.get(), len / 2); nsAutoEditBatch beginBatching(this); - rv = InsertHTMLWithContext(stuffToPaste, - aContextStr, aInfoStr, flavor, - aSourceDoc, - aDestinationNode, aDestOffset, - aDoDeleteSelection); + rv = DoInsertHTMLWithContext(stuffToPaste, + aContextStr, aInfoStr, flavor, + aSourceDoc, + aDestinationNode, aDestOffset, + aDoDeleteSelection, + PR_FALSE); } } else if (0 == nsCRT::strcmp(bestFlavor, kUnicodeMime) || @@ -1389,11 +1408,12 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable nsAutoEditBatch beginBatching(this); const nsAFlatString& empty = EmptyString(); - rv = InsertHTMLWithContext(stuffToPaste, - empty, empty, flavor, - aSourceDoc, - aDestinationNode, aDestOffset, - aDoDeleteSelection); + rv = DoInsertHTMLWithContext(stuffToPaste, + empty, empty, flavor, + aSourceDoc, + aDestinationNode, aDestOffset, + aDoDeleteSelection, + PR_FALSE); } } } @@ -1418,11 +1438,12 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable AppendUTF8toUTF16(base64, stuffToPaste); stuffToPaste.AppendLiteral("\" alt=\"\" >"); nsAutoEditBatch beginBatching(this); - rv = InsertHTMLWithContext(stuffToPaste, EmptyString(), EmptyString(), - NS_LITERAL_STRING(kFileMime), - aSourceDoc, - aDestinationNode, aDestOffset, - aDoDeleteSelection); + rv = DoInsertHTMLWithContext(stuffToPaste, EmptyString(), EmptyString(), + NS_LITERAL_STRING(kFileMime), + aSourceDoc, + aDestinationNode, aDestOffset, + aDoDeleteSelection, + PR_FALSE); PR_Free(base64); } } @@ -2552,7 +2573,8 @@ nsresult nsHTMLEditor::CreateDOMFragmentFromPaste(const nsAString &aInputString, nsCOMPtr *outStartNode, nsCOMPtr *outEndNode, PRInt32 *outStartOffset, - PRInt32 *outEndOffset) + PRInt32 *outEndOffset, + PRBool aTrustedInput) { NS_ENSURE_TRUE(outFragNode && outStartNode && outEndNode, NS_ERROR_NULL_POINTER); nsCOMPtr docfrag; @@ -2571,7 +2593,8 @@ nsresult nsHTMLEditor::CreateDOMFragmentFromPaste(const nsAString &aInputString, nsCOMPtr contextLeaf, junk; if (!aContextStr.IsEmpty()) { - res = ParseFragment(aContextStr, tagStack, doc, address_of(contextAsNode)); + res = ParseFragment(aContextStr, tagStack, doc, address_of(contextAsNode), + aTrustedInput); NS_ENSURE_SUCCESS(res, res); NS_ENSURE_TRUE(contextAsNode, NS_ERROR_FAILURE); @@ -2591,7 +2614,7 @@ nsresult nsHTMLEditor::CreateDOMFragmentFromPaste(const nsAString &aInputString, NS_ENSURE_SUCCESS(res, res); // create fragment for pasted html - res = ParseFragment(aInputString, tagStack, doc, outFragNode); + res = ParseFragment(aInputString, tagStack, doc, outFragNode, aTrustedInput); NS_ENSURE_SUCCESS(res, res); NS_ENSURE_TRUE(*outFragNode, NS_ERROR_FAILURE); @@ -2651,7 +2674,8 @@ nsresult nsHTMLEditor::CreateDOMFragmentFromPaste(const nsAString &aInputString, nsresult nsHTMLEditor::ParseFragment(const nsAString & aFragStr, nsTArray &aTagStack, nsIDocument* aTargetDocument, - nsCOMPtr *outNode) + nsCOMPtr *outNode, + PRBool aTrustedInput) { // figure out if we are parsing full context or not PRBool bContext = aTagStack.IsEmpty(); @@ -2664,25 +2688,32 @@ nsresult nsHTMLEditor::ParseFragment(const nsAString & aFragStr, // create the html fragment sink nsCOMPtr sink; - if (bContext) - sink = do_CreateInstance(NS_HTMLPARANOIDFRAGMENTSINK2_CONTRACTID); - else - sink = do_CreateInstance(NS_HTMLPARANOIDFRAGMENTSINK_CONTRACTID); + if (aTrustedInput) { + if (bContext) + sink = do_CreateInstance(NS_HTMLFRAGMENTSINK2_CONTRACTID); + else + sink = do_CreateInstance(NS_HTMLFRAGMENTSINK_CONTRACTID); + } else { + if (bContext) + sink = do_CreateInstance(NS_HTMLPARANOIDFRAGMENTSINK2_CONTRACTID); + else + sink = do_CreateInstance(NS_HTMLPARANOIDFRAGMENTSINK_CONTRACTID); + + nsCOMPtr paranoidSink(do_QueryInterface(sink)); + NS_ASSERTION(paranoidSink, "Our content sink is paranoid"); + if (bContext) { + // Allow comments for the context to catch our placeholder cookie + paranoidSink->AllowComments(); + } else { + // Allow style elements and attributes for the actual content + paranoidSink->AllowStyles(); + } + } NS_ENSURE_TRUE(sink, NS_ERROR_FAILURE); nsCOMPtr fragSink(do_QueryInterface(sink)); NS_ENSURE_TRUE(fragSink, NS_ERROR_FAILURE); - nsCOMPtr paranoidSink(do_QueryInterface(sink)); - NS_ASSERTION(paranoidSink, "Our content sink is paranoid"); - if (bContext) { - // Allow comemnts for the context to catch our placeholder cookie - paranoidSink->AllowComments(); - } else { - // Allow style elements and attributes for the actual content - paranoidSink->AllowStyles(); - } - fragSink->SetTargetDocument(aTargetDocument); // parse the fragment diff --git a/editor/libeditor/html/nsHTMLEditor.h b/editor/libeditor/html/nsHTMLEditor.h index b440e321ac9..e352168c7f3 100644 --- a/editor/libeditor/html/nsHTMLEditor.h +++ b/editor/libeditor/html/nsHTMLEditor.h @@ -622,10 +622,12 @@ protected: nsCOMPtr *outStartNode, nsCOMPtr *outEndNode, PRInt32 *outStartOffset, - PRInt32 *outEndOffset); + PRInt32 *outEndOffset, + PRBool aTrustedInput); nsresult ParseFragment(const nsAString & aStr, nsTArray &aTagStack, nsIDocument* aTargetDoc, - nsCOMPtr *outNode); + nsCOMPtr *outNode, + PRBool aTrustedInput); nsresult CreateListOfNodesToPaste(nsIDOMNode *aFragmentAsNode, nsCOMArray& outNodeList, nsIDOMNode *aStartNode, @@ -751,6 +753,23 @@ protected: // Whether the outer window of the DOM event target has focus or not. PRBool OurWindowHasFocus(); + // This function is used to insert a string of HTML input optionally with some + // context information into the editable field. The HTML input either comes + // from a transferable object created as part of a drop/paste operation, or from + // the InsertHTML method. We may want the HTML input to be sanitized (for example, + // if it's coming from a transferable object), in which case aTrustedInput should + // be set to false, otherwise, the caller should set it to true, which means that + // the HTML will be inserted in the DOM verbatim. + nsresult DoInsertHTMLWithContext(const nsAString& aInputString, + const nsAString& aContextStr, + const nsAString& aInfoStr, + const nsAString& aFlavor, + nsIDOMDocument* aSourceDoc, + nsIDOMNode* aDestNode, + PRInt32 aDestOffset, + PRBool aDeleteSelection, + PRBool aTrustedInput); + // Data members protected: diff --git a/editor/libeditor/html/tests/Makefile.in b/editor/libeditor/html/tests/Makefile.in index 4ff4a5a1463..d9fe9cfd771 100644 --- a/editor/libeditor/html/tests/Makefile.in +++ b/editor/libeditor/html/tests/Makefile.in @@ -60,6 +60,7 @@ _TEST_FILES = \ test_bug537046.html \ test_bug550434.html \ test_bug592592.html \ + test_bug597784.html \ test_CF_HTML_clipboard.html \ test_contenteditable_focus.html \ test_contenteditable_text_input_handling.html \ diff --git a/editor/libeditor/html/tests/test_bug520189.html b/editor/libeditor/html/tests/test_bug520189.html index ff7ec733cbe..be6fb7da78d 100644 --- a/editor/libeditor/html/tests/test_bug520189.html +++ b/editor/libeditor/html/tests/test_bug520189.html @@ -343,7 +343,7 @@ var tests = [ { id: "mm", isIFrame: true, - insertHTML: true, + indirectPaste: true, payload: invalidStyle7Payload, rootElement: function() document.getElementById("mm").contentDocument.documentElement, checkResult: function(html) { @@ -353,7 +353,7 @@ var tests = [ }, { id: "nn", - insertHTML: true, + indirectPaste: true, payload: invalidStyle7Payload, rootElement: function() document.getElementById("nn"), checkResult: function(html) { @@ -397,26 +397,33 @@ function runTest(test) { } else elem.focus(); - if ("insertHTML" in test) { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + + var trans = Components.classes["@mozilla.org/widget/transferable;1"] + .createInstance(Components.interfaces.nsITransferable); + var data = Components.classes["@mozilla.org/supports-string;1"] + .createInstance(Components.interfaces.nsISupportsString); + data.data = test.payload; + trans.addDataFlavor("text/html"); + trans.setTransferData("text/html", data, data.data.length * 2); + + if ("indirectPaste" in test) { + var editor, win; if ("isIFrame" in test) { - elem.contentDocument.execCommand("inserthtml", false, test.payload); + win = elem.contentDocument.defaultView; } else { getSelection().collapse(elem, 0); - document.execCommand("inserthtml", false, test.payload); + win = window; } + editor = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIWebNavigation) + .QueryInterface(Components.interfaces.nsIEditorDocShell) + .editor; + editor.pasteTransferable(trans); } else { - netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"] .getService(Components.interfaces.nsIClipboard); - var trans = Components.classes["@mozilla.org/widget/transferable;1"] - .createInstance(Components.interfaces.nsITransferable); - var data = Components.classes["@mozilla.org/supports-string;1"] - .createInstance(Components.interfaces.nsISupportsString); - data.data = test.payload; - trans.addDataFlavor("text/html"); - trans.setTransferData("text/html", data, data.data.length * 2); clipboard.setData(trans, null, Components.interfaces.nsIClipboard.kGlobalClipboard); var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) diff --git a/editor/libeditor/html/tests/test_bug597784.html b/editor/libeditor/html/tests/test_bug597784.html new file mode 100644 index 00000000000..9dcfd8c1fb2 --- /dev/null +++ b/editor/libeditor/html/tests/test_bug597784.html @@ -0,0 +1,39 @@ + + + + + Test for Bug 597784 + + + + + + +Mozilla Bug 597784 +

+
+
+
+
+ +