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

This commit is contained in:
Ehsan Akhgari 2010-09-27 13:21:34 -04:00
parent 7706293fb5
commit 37297d45dc
5 changed files with 152 additions and 55 deletions

View File

@ -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<nsIDOMNode> 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<nsIDOMNode> *outStartNode,
nsCOMPtr<nsIDOMNode> *outEndNode,
PRInt32 *outStartOffset,
PRInt32 *outEndOffset)
PRInt32 *outEndOffset,
PRBool aTrustedInput)
{
NS_ENSURE_TRUE(outFragNode && outStartNode && outEndNode, NS_ERROR_NULL_POINTER);
nsCOMPtr<nsIDOMDocumentFragment> docfrag;
@ -2571,7 +2593,8 @@ nsresult nsHTMLEditor::CreateDOMFragmentFromPaste(const nsAString &aInputString,
nsCOMPtr<nsIDOMNode> 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<nsString> &aTagStack,
nsIDocument* aTargetDocument,
nsCOMPtr<nsIDOMNode> *outNode)
nsCOMPtr<nsIDOMNode> *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<nsIContentSink> 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<nsIParanoidFragmentContentSink> 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<nsIFragmentContentSink> fragSink(do_QueryInterface(sink));
NS_ENSURE_TRUE(fragSink, NS_ERROR_FAILURE);
nsCOMPtr<nsIParanoidFragmentContentSink> 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

View File

@ -622,10 +622,12 @@ protected:
nsCOMPtr<nsIDOMNode> *outStartNode,
nsCOMPtr<nsIDOMNode> *outEndNode,
PRInt32 *outStartOffset,
PRInt32 *outEndOffset);
PRInt32 *outEndOffset,
PRBool aTrustedInput);
nsresult ParseFragment(const nsAString & aStr, nsTArray<nsString> &aTagStack,
nsIDocument* aTargetDoc,
nsCOMPtr<nsIDOMNode> *outNode);
nsCOMPtr<nsIDOMNode> *outNode,
PRBool aTrustedInput);
nsresult CreateListOfNodesToPaste(nsIDOMNode *aFragmentAsNode,
nsCOMArray<nsIDOMNode>& 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:

View File

@ -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 \

View File

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

View File

@ -0,0 +1,39 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=597784
-->
<head>
<title>Test for Bug 597784</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=597784">Mozilla Bug 597784</a>
<p id="display"></p>
<div id="content"></div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 597784 **/
SimpleTest.waitForExplicitFinish();
addLoadEvent(function() {
document.designMode = "on";
var content = document.getElementById("content");
getSelection().collapse(content, 0);
var html1 = "<test:tag>test:tag</test:tag>";
var html2 = "<a href=\"http://mozilla.org/\" test:attr=\"test:attr\" custom=\"value\">link</a>";
document.execCommand("insertHTML", false, html1);
document.execCommand("insertHTML", false, html2);
is(content.innerHTML, html1 + html2,
"The custom tags and attributes should be inserted into the document using the insertHTML command");
SimpleTest.finish();
});
</script>
</pre>
</body>
</html>