Bug 520189 - Fix copy and test for the HTML editor; f=bzbarsky,dbaron r=sayrer,peterv,bzbarsky sr=roc

This commit is contained in:
Ehsan Akhgari 2010-04-09 17:06:09 -04:00
parent 8003226855
commit 1d27c39d4a
10 changed files with 633 additions and 21 deletions

View File

@ -215,6 +215,10 @@
#define NS_HTMLPARANOIDFRAGMENTSINK_CID \
{ 0xa47e9526, 0x6e48, 0x4574, { 0x9d, 0x6c, 0x31, 0x64, 0xe2, 0x71, 0xf7, 0x4e } }
// {A47EF526-6E48-4574-9D60-3164E271F75E}
#define NS_HTMLPARANOIDFRAGMENTSINK2_CID \
{ 0xa47ef526, 0x6e48, 0x4574, { 0x9d, 0x60, 0x31, 0x64, 0xe2, 0x71, 0xf7, 0x5e } }
// {4B664E54-72A2-4bbf-A5C2-66D4DC3066A0}
#define NS_XMLFRAGMENTSINK_CID \
{ 0x4b664e54, 0x72a2, 0x4bbf, { 0xa5, 0xc2, 0x66, 0xd4, 0xdc, 0x30, 0x66, 0xa0 } }
@ -227,6 +231,10 @@
#define NS_XHTMLPARANOIDFRAGMENTSINK_CID \
{ 0x2d78bbf0, 0xe26c, 0x482b, { 0x92, 0xb3, 0x78, 0xa7, 0xb2, 0xaf, 0xc8, 0xf7} }
// {AD78BBF0-E261-482B-32B3-78A7B2AFC8F7}
#define NS_XHTMLPARANOIDFRAGMENTSINK2_CID \
{ 0xad78bbf0, 0xe261, 0x482b, { 0x32, 0xb3, 0x78, 0xa7, 0xb2, 0xaf, 0xc8, 0xf7} }
// {3986B301-097C-11d3-BF87-00105A1B0627}
#define NS_XULPOPUPLISTENER_CID \
{ 0x3986b301, 0x97c, 0x11d3, { 0xbf, 0x87, 0x0, 0x10, 0x5a, 0x1b, 0x6, 0x27 } }

View File

@ -77,6 +77,7 @@ INCLUDES += \
-I$(srcdir)/../../content/src \
-I$(srcdir)/../../../../layout/style \
-I$(srcdir)/../../../../dom/base \
-I$(srcdir)/../../../../xpcom/io \
$(NULL)
DEFINES += -D_IMPL_NS_LAYOUT

View File

@ -66,6 +66,16 @@
#include "nsContentSink.h"
#include "nsTHashtable.h"
#include "nsCycleCollectionParticipant.h"
#include "nsCSSParser.h"
#include "nsCSSProperty.h"
#include "nsCSSDeclaration.h"
#include "nsICSSStyleRule.h"
#include "nsUnicharInputStream.h"
#include "nsCSSStyleSheet.h"
#include "nsICSSRuleList.h"
#include "nsCSSDeclaration.h"
#include "nsCSSProperty.h"
#include "nsIDOMCSSRule.h"
//
// XXX THIS IS TEMPORARY CODE
@ -77,6 +87,9 @@
class nsHTMLFragmentContentSink : public nsIFragmentContentSink,
public nsIHTMLContentSink {
public:
/**
* @param aAllContent Whether there is context information available for the fragment.
*/
nsHTMLFragmentContentSink(PRBool aAllContent = PR_FALSE);
virtual ~nsHTMLFragmentContentSink();
@ -136,8 +149,6 @@ public:
nsresult AddText(const nsAString& aString);
nsresult FlushText();
nsresult Init();
PRPackedBool mAllContent;
PRPackedBool mProcessing;
PRPackedBool mSeenBody;
@ -767,10 +778,11 @@ nsHTMLFragmentContentSink::AddAttributes(const nsIParserNode& aNode,
// Find the whitelist of allowed elements and attributes in
// nsContentSink.h We share it with nsHTMLParanoidFragmentSink
class nsHTMLParanoidFragmentSink : public nsHTMLFragmentContentSink
class nsHTMLParanoidFragmentSink : public nsHTMLFragmentContentSink,
public nsIParanoidFragmentContentSink
{
public:
nsHTMLParanoidFragmentSink();
nsHTMLParanoidFragmentSink(PRBool aAllContent = PR_FALSE);
static nsresult Init();
static void Cleanup();
@ -786,14 +798,22 @@ public:
nsresult AddAttributes(const nsIParserNode& aNode,
nsIContent* aContent);
// nsIParanoidFragmentContentSink
virtual void AllowStyles();
protected:
nsresult NameFromType(const nsHTMLTag aTag,
nsIAtom **aResult);
nsresult NameFromNode(const nsIParserNode& aNode,
nsIAtom **aResult);
void SanitizeStyleRule(nsICSSStyleRule *aRule, nsAutoString &aRuleText);
PRBool mSkip; // used when we descend into <style> or <script>
PRPackedBool mSkip; // used when we descend into <style> or <script>
PRPackedBool mProcessStyle; // used when style is explicitly white-listed
PRPackedBool mInStyle; // whether we're inside a style element
// Use nsTHashTable as a hash set for our whitelists
static nsTHashtable<nsISupportsHashKey>* sAllowedTags;
@ -803,8 +823,9 @@ protected:
nsTHashtable<nsISupportsHashKey>* nsHTMLParanoidFragmentSink::sAllowedTags;
nsTHashtable<nsISupportsHashKey>* nsHTMLParanoidFragmentSink::sAllowedAttributes;
nsHTMLParanoidFragmentSink::nsHTMLParanoidFragmentSink():
nsHTMLFragmentContentSink(PR_FALSE), mSkip(PR_FALSE)
nsHTMLParanoidFragmentSink::nsHTMLParanoidFragmentSink(PRBool aAllContent):
nsHTMLFragmentContentSink(aAllContent), mSkip(PR_FALSE),
mProcessStyle(PR_FALSE), mInStyle(PR_FALSE)
{
}
@ -875,14 +896,27 @@ NS_NewHTMLParanoidFragmentSink(nsIFragmentContentSink** aResult)
return NS_OK;
}
nsresult
NS_NewHTMLParanoidFragmentSink2(nsIFragmentContentSink** aResult)
{
nsHTMLParanoidFragmentSink* it = new nsHTMLParanoidFragmentSink(PR_TRUE);
if (!it) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult rv = nsHTMLParanoidFragmentSink::Init();
NS_ENSURE_SUCCESS(rv, rv);
NS_ADDREF(*aResult = it);
return NS_OK;
}
void
NS_HTMLParanoidFragmentSinkShutdown()
{
nsHTMLParanoidFragmentSink::Cleanup();
}
NS_IMPL_ISUPPORTS_INHERITED0(nsHTMLParanoidFragmentSink,
nsHTMLFragmentContentSink)
NS_IMPL_ISUPPORTS_INHERITED1(nsHTMLParanoidFragmentSink, nsHTMLFragmentContentSink, nsIParanoidFragmentContentSink)
nsresult
nsHTMLParanoidFragmentSink::NameFromType(const nsHTMLTag aTag,
@ -919,6 +953,12 @@ nsHTMLParanoidFragmentSink::NameFromNode(const nsIParserNode& aNode,
return rv;
}
void
nsHTMLParanoidFragmentSink::AllowStyles()
{
mProcessStyle = PR_TRUE;
}
// nsHTMLFragmentContentSink
nsresult
@ -946,7 +986,10 @@ nsHTMLParanoidFragmentSink::AddAttributes(const nsIParserNode& aNode,
// not an allowed attribute
if (!sAllowedAttributes || !sAllowedAttributes->GetEntry(keyAtom)) {
continue;
// unless it's style, and we're allowing it
if (!mProcessStyle || keyAtom != nsGkAtoms::style) {
continue;
}
}
// Get value and remove mandatory quotes
@ -975,7 +1018,27 @@ nsHTMLParanoidFragmentSink::AddAttributes(const nsIParserNode& aNode,
continue;
}
if (nodeType == eHTMLTag_a && keyAtom == nsGkAtoms::name) {
// Filter unsafe stuff from style attributes if they're allowed
if (mProcessStyle && keyAtom == nsGkAtoms::style) {
if (!baseURI) {
baseURI = aContent->GetBaseURI();
}
nsCSSParser parser;
nsCOMPtr<nsICSSStyleRule> rule;
rv = parser.ParseStyleAttribute(aNode.GetValueAt(i),
mTargetDocument->GetDocumentURI(),
baseURI,
mTargetDocument->NodePrincipal(),
getter_AddRefs(rule));
if (NS_SUCCEEDED(rv)) {
nsAutoString cleanValue;
SanitizeStyleRule(rule, cleanValue);
aContent->SetAttr(kNameSpaceID_None, keyAtom, cleanValue, PR_FALSE);
} else {
// we couldn't sanitize the style attribute, ignore it
continue;
}
} else if (nodeType == eHTMLTag_a && keyAtom == nsGkAtoms::name) {
NS_ConvertUTF16toUTF8 cname(v);
NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting()));
// Add attribute to content
@ -994,9 +1057,10 @@ nsHTMLParanoidFragmentSink::OpenContainer(const nsIParserNode& aNode)
{
nsresult rv = NS_OK;
// bail if it's a script or style, or we're already inside one of those
// bail if it's a script or style (when we don't allow processing of stylesheets),
// or we're already inside one of those
eHTMLTags type = (eHTMLTags)aNode.GetNodeType();
if (type == eHTMLTag_script || type == eHTMLTag_style) {
if (type == eHTMLTag_script || (!mProcessStyle && type == eHTMLTag_style)) {
mSkip = PR_TRUE;
return rv;
}
@ -1007,7 +1071,14 @@ nsHTMLParanoidFragmentSink::OpenContainer(const nsIParserNode& aNode)
// not on whitelist
if (!sAllowedTags || !sAllowedTags->GetEntry(name)) {
return NS_OK;
// unless it's style, and we're allowing it
if (!mProcessStyle || name != nsGkAtoms::style) {
return NS_OK;
}
}
if (type == eHTMLTag_style) {
mInStyle = PR_TRUE;
}
return nsHTMLFragmentContentSink::OpenContainer(aNode);
@ -1029,12 +1100,123 @@ nsHTMLParanoidFragmentSink::CloseContainer(const nsHTMLTag aTag)
// not on whitelist
if (!sAllowedTags || !sAllowedTags->GetEntry(name)) {
return NS_OK;
// unless it's style, and we're allowing it
if (!mProcessStyle || name != nsGkAtoms::style) {
return NS_OK;
}
}
if (mInStyle && name == nsGkAtoms::style) {
mInStyle = PR_FALSE;
// Flush the text to make sure that the style text is complete.
FlushText();
// sanitizedStyleText will hold the permitted CSS text.
// We use a white-listing approach, so we explicitly allow
// the CSS style and font-face rule types. We also clear
// -moz-binding CSS properties.
nsAutoString sanitizedStyleText;
nsIContent* style = GetCurrentContent();
if (style) {
// styleText will hold the text inside the style element.
nsAutoString styleText;
nsContentUtils::GetNodeTextContent(style, PR_FALSE, styleText);
// Create a unichar input stream for the CSS parser.
nsCOMPtr<nsIUnicharInputStream> uin;
rv = nsSimpleUnicharStreamFactory::GetInstance()->
CreateInstanceFromString(styleText, getter_AddRefs(uin));
if (NS_SUCCEEDED(rv)) {
// Create a sheet to hold the parsed CSS
nsRefPtr<nsCSSStyleSheet> sheet;
rv = NS_NewCSSStyleSheet(getter_AddRefs(sheet));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIURI> baseURI = style->GetBaseURI();
sheet->SetURIs(mTargetDocument->GetDocumentURI(), nsnull, baseURI);
sheet->SetPrincipal(mTargetDocument->NodePrincipal());
// Create the CSS parser, and parse the CSS text.
nsCSSParser parser(nsnull, sheet);
rv = parser.Parse(uin, mTargetDocument->GetDocumentURI(),
baseURI, mTargetDocument->NodePrincipal(),
0, PR_FALSE);
// Mark the sheet as complete.
if (NS_SUCCEEDED(rv)) {
sheet->SetModified(PR_FALSE);
sheet->SetComplete();
}
if (NS_SUCCEEDED(rv)) {
// Loop through all the rules found in the CSS text
PRInt32 ruleCount = sheet->StyleRuleCount();
for (PRInt32 i = 0; i < ruleCount; ++i) {
nsRefPtr<nsICSSRule> rule;
rv = sheet->GetStyleRuleAt(i, *getter_AddRefs(rule));
if (NS_FAILED(rv))
continue;
NS_ASSERTION(rule, "We should have a rule by now");
PRInt32 type;
rv = rule->GetType(type);
if (NS_FAILED(rv))
continue;
switch (type) {
case nsICSSRule::UNKNOWN_RULE:
case nsICSSRule::CHARSET_RULE:
case nsICSSRule::IMPORT_RULE:
case nsICSSRule::MEDIA_RULE:
case nsICSSRule::PAGE_RULE:
// Ignore these rule types.
break;
case nsICSSRule::NAMESPACE_RULE:
case nsICSSRule::FONT_FACE_RULE: {
// Append @namespace and @font-face rules verbatim.
nsAutoString cssText;
nsCOMPtr<nsIDOMCSSRule> styleRule = do_QueryInterface(rule);
if (styleRule) {
rv = styleRule->GetCssText(cssText);
if (NS_SUCCEEDED(rv)) {
sanitizedStyleText.Append(cssText);
}
}
break;
}
case nsICSSRule::STYLE_RULE: {
// For style rules, we will just look for and remove the
// -moz-binding properties.
nsCOMPtr<nsICSSStyleRule> styleRule = do_QueryInterface(rule);
NS_ASSERTION(styleRule, "Must be a style rule");
nsAutoString decl;
SanitizeStyleRule(styleRule, decl);
rv = styleRule->GetCssText(decl);
// Only add the rule when sanitized.
if (NS_SUCCEEDED(rv)) {
sanitizedStyleText.Append(decl);
}
}
}
}
}
}
}
// Replace the style element content with its sanitized style text
nsContentUtils::SetNodeTextContent(style, sanitizedStyleText, PR_TRUE);
}
}
return nsHTMLFragmentContentSink::CloseContainer(aTag);
}
void
nsHTMLParanoidFragmentSink::SanitizeStyleRule(nsICSSStyleRule *aRule, nsAutoString &aRuleText)
{
aRuleText.Truncate();
nsCSSDeclaration *style = aRule->GetDeclaration();
if (style) {
nsresult rv = style->RemoveProperty(eCSSProperty_binding);
if (NS_SUCCEEDED(rv)) {
style->ToString(aRuleText);
}
}
}
NS_IMETHODIMP
nsHTMLParanoidFragmentSink::AddLeaf(const nsIParserNode& aNode)
{
@ -1057,7 +1239,9 @@ nsHTMLParanoidFragmentSink::AddLeaf(const nsIParserNode& aNode)
}
if (!sAllowedTags || !sAllowedTags->GetEntry(name)) {
return NS_OK;
if (!mProcessStyle || name != nsGkAtoms::style) {
return NS_OK;
}
}
}

View File

@ -486,7 +486,7 @@ nsXMLFragmentContentSink::IgnoreFirstContainer()
class nsXHTMLParanoidFragmentSink : public nsXMLFragmentContentSink
{
public:
nsXHTMLParanoidFragmentSink();
nsXHTMLParanoidFragmentSink(PRBool aAllContent = PR_FALSE);
static nsresult Init();
static void Cleanup();
@ -525,8 +525,8 @@ protected:
nsTHashtable<nsISupportsHashKey>* nsXHTMLParanoidFragmentSink::sAllowedTags;
nsTHashtable<nsISupportsHashKey>* nsXHTMLParanoidFragmentSink::sAllowedAttributes;
nsXHTMLParanoidFragmentSink::nsXHTMLParanoidFragmentSink():
nsXMLFragmentContentSink(PR_FALSE), mSkipLevel(0)
nsXHTMLParanoidFragmentSink::nsXHTMLParanoidFragmentSink(PRBool aAllContent):
nsXMLFragmentContentSink(aAllContent), mSkipLevel(0)
{
}
@ -596,6 +596,20 @@ NS_NewXHTMLParanoidFragmentSink(nsIFragmentContentSink** aResult)
return NS_OK;
}
nsresult
NS_NewXHTMLParanoidFragmentSink2(nsIFragmentContentSink** aResult)
{
nsXHTMLParanoidFragmentSink* it = new nsXHTMLParanoidFragmentSink(PR_TRUE);
if (!it) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult rv = nsXHTMLParanoidFragmentSink::Init();
NS_ENSURE_SUCCESS(rv, rv);
NS_ADDREF(*aResult = it);
return NS_OK;
}
void
NS_XHTMLParanoidFragmentSinkShutdown()
{

View File

@ -2708,14 +2708,19 @@ nsresult nsHTMLEditor::ParseFragment(const nsAString & aFragStr,
// create the html fragment sink
nsCOMPtr<nsIContentSink> sink;
if (bContext)
sink = do_CreateInstance(NS_HTMLFRAGMENTSINK2_CONTRACTID);
sink = do_CreateInstance(NS_HTMLPARANOIDFRAGMENTSINK2_CONTRACTID);
else
sink = do_CreateInstance(NS_HTMLFRAGMENTSINK_CONTRACTID);
sink = do_CreateInstance(NS_HTMLPARANOIDFRAGMENTSINK_CONTRACTID);
NS_ENSURE_TRUE(sink, NS_ERROR_FAILURE);
nsCOMPtr<nsIFragmentContentSink> fragSink(do_QueryInterface(sink));
NS_ENSURE_TRUE(fragSink, NS_ERROR_FAILURE);
// Allow style elements and attributes
nsCOMPtr<nsIParanoidFragmentContentSink> paranoidSink(do_QueryInterface(sink));
NS_ASSERTION(paranoidSink, "Our content sink is paranoid");
paranoidSink->AllowStyles();
fragSink->SetTargetDocument(aTargetDocument);
// parse the fragment

View File

@ -55,6 +55,7 @@ _TEST_FILES = \
test_bug480972.html \
test_bug484181.html \
test_bug487524.html \
test_bug520189.html \
test_bug525389.html \
test_bug537046.html \
test_bug550434.html \

View File

@ -0,0 +1,358 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=520182
-->
<head>
<title>Test for Bug 520182</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=520182">Mozilla Bug 520182</a>
<p id="display"></p>
<div id="content">
<iframe id="a" src="about:blank"></iframe>
<iframe id="b" src="about:blank"></iframe>
<iframe id="c" src="about:blank"></iframe>
<div id="d" contenteditable="true"></div>
<div id="e" contenteditable="true"></div>
<div id="f" contenteditable="true"></div>
<iframe id="g" src="about:blank"></iframe>
<iframe id="h" src="about:blank"></iframe>
<div id="i" contenteditable="true"></div>
<div id="j" contenteditable="true"></div>
<iframe id="k" src="about:blank"></iframe>
<div id="l" contenteditable="true"></div>
<iframe id="m" src="about:blank"></iframe>
<div id="n" contenteditable="true"></div>
<iframe id="o" src="about:blank"></iframe>
<div id="p" contenteditable="true"></div>
<iframe id="q" src="about:blank"></iframe>
<div id="r" contenteditable="true"></div>
<iframe id="s" src="about:blank"></iframe>
<div id="t" contenteditable="true"></div>
<iframe id="u" src="about:blank"></iframe>
<div id="v" contenteditable="true"></div>
<iframe id="w" src="about:blank"></iframe>
<div id="x" contenteditable="true"></div>
<iframe id="y" src="about:blank"></iframe>
<div id="z" contenteditable="true"></div>
<iframe id="aa" src="about:blank"></iframe>
<div id="bb" contenteditable="true"></div>
<iframe id="cc" src="about:blank"></iframe>
<div id="dd" contenteditable="true"></div>
<iframe id="ee" src="about:blank"></iframe>
<div id="ff" contenteditable="true"></div>
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 520182 **/
const dataPayload = "foo<iframe src=\"data:text/html,bar\"></iframe>baz";
const jsPayload = "foo<iframe src=\"javascript:void('bar');\"></iframe>baz";
const httpPayload = "foo<iframe src=\"http://mochi.test:8888/\"></iframe>baz";
const scriptPayload ="foo<script>document.write(\"<iframe></iframe>\");</sc" + "ript>baz";
const scriptExternalPayload = "foo<script src=\"data:text/javascript,document.write('<iframe></iframe>');\"></sc" + "ript>baz";
const validStyle1Payload = "foo<style>#bar{color:red;}</style>baz";
const validStyle2Payload = "foo<span style=\"color:red\">bar</span>baz";
const validStyle3Payload = "foo<style>@font-face{font-family:xxx;src:'xxx.ttf';}</style>baz";
const validStyle4Payload = "foo<style>@namespace xxx url(http://example.com/);</style>baz";
const invalidStyle1Payload = "foo<style>#bar{-moz-binding:url('data:text/xml,<?xml version=\"1.0\"><binding xmlns=\"http://www.mozilla.org/xbl\"/>');}</style>baz";
const invalidStyle2Payload = "foo<span style=\"-moz-binding:url('data:text/xml,<?xml version=&quot;1.0&quot;><binding xmlns=&quot;http://www.mozilla.org/xbl&quot;/>');\">bar</span>baz";
const invalidStyle3Payload = "foo<style>@import 'xxx.css';</style>baz";
const invalidStyle4Payload = "foo<span style=\"@import 'xxx.css';\">bar</span>baz";
const invalidStyle5Payload = "foo<span style=\"@font-face{font-family:xxx;src:'xxx.ttf';}\">bar</span>baz";
const invalidStyle6Payload = "foo<span style=\"@namespace xxx url(http://example.com/);\">bar</span>baz";
const nestedStylePayload = "foo<style>#bar1{-moz-binding:url('data:text/xml,<?xml version=&quot;1.0&quot;><binding xmlns=&quot;http://www.mozilla.org/xbl&quot; id=&quot;binding-1&quot;/>');<style></style>#bar2{-moz-binding:url('data:text/xml,<?xml version=&quot;1.0&quot;><binding xmlns=&quot;http://www.mozilla.org/xbl&quot; id=&quot;binding-2&quot;/>');</style>baz";
var tests = [
{
id: "a",
isIFrame: true,
payload: dataPayload,
iframeCount: 0,
rootElement: function() document.getElementById("a").contentDocument.documentElement
},
{
id: "b",
isIFrame: true,
payload: jsPayload,
iframeCount: 0,
rootElement: function() document.getElementById("b").contentDocument.documentElement
},
{
id: "c",
isIFrame: true,
payload: httpPayload,
iframeCount: 0,
rootElement: function() document.getElementById("c").contentDocument.documentElement
},
{
id: "g",
isIFrame: true,
payload: scriptPayload,
rootElement: function() document.getElementById("g").contentDocument.documentElement,
iframeCount: 0
},
{
id: "h",
isIFrame: true,
payload: scriptExternalPayload,
rootElement: function() document.getElementById("h").contentDocument.documentElement,
iframeCount: 0
},
{
id: "d",
payload: dataPayload,
iframeCount: 0,
rootElement: function() document.getElementById("d")
},
{
id: "e",
payload: jsPayload,
iframeCount: 0,
rootElement: function() document.getElementById("e")
},
{
id: "f",
payload: httpPayload,
iframeCount: 0,
rootElement: function() document.getElementById("f")
},
{
id: "i",
payload: scriptPayload,
rootElement: function() document.getElementById("i"),
iframeCount: 0
},
{
id: "j",
payload: scriptExternalPayload,
rootElement: function() document.getElementById("j"),
iframeCount: 0
},
{
id: "k",
isIFrame: true,
payload: validStyle1Payload,
rootElement: function() document.getElementById("k").contentDocument.documentElement,
checkResult: function(html) isnot(html.indexOf("style"), -1, "Should have retained style")
},
{
id: "l",
payload: validStyle1Payload,
rootElement: function() document.getElementById("l"),
checkResult: function(html) isnot(html.indexOf("style"), -1, "Should have retained style")
},
{
id: "m",
isIFrame: true,
payload: validStyle2Payload,
rootElement: function() document.getElementById("m").contentDocument.documentElement,
checkResult: function(html) isnot(html.indexOf("style"), -1, "Should have retained style")
},
{
id: "n",
payload: validStyle2Payload,
rootElement: function() document.getElementById("n"),
checkResult: function(html) isnot(html.indexOf("style"), -1, "Should have retained style")
},
{
id: "o",
isIFrame: true,
payload: invalidStyle1Payload,
rootElement: function() document.getElementById("o").contentDocument.documentElement,
checkResult: function(html) is(html.indexOf("binding"), -1, "Should not have retained the binding style")
},
{
id: "p",
payload: invalidStyle1Payload,
rootElement: function() document.getElementById("p"),
checkResult: function(html) is(html.indexOf("binding"), -1, "Should not have retained the binding style")
},
{
id: "q",
isIFrame: true,
payload: invalidStyle2Payload,
rootElement: function() document.getElementById("q").contentDocument.documentElement,
checkResult: function(html) is(html.indexOf("binding"), -1, "Should not have retained the binding style")
},
{
id: "r",
payload: invalidStyle2Payload,
rootElement: function() document.getElementById("r"),
checkResult: function(html) is(html.indexOf("binding"), -1, "Should not have retained the binding style")
},
{
id: "s",
isIFrame: true,
payload: invalidStyle1Payload,
rootElement: function() document.getElementById("s").contentDocument.documentElement,
checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style")
},
{
id: "t",
payload: invalidStyle1Payload,
rootElement: function() document.getElementById("t"),
checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style")
},
{
id: "u",
isIFrame: true,
payload: invalidStyle2Payload,
rootElement: function() document.getElementById("u").contentDocument.documentElement,
checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style")
},
{
id: "v",
payload: invalidStyle2Payload,
rootElement: function() document.getElementById("v"),
checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style")
},
{
id: "w",
isIFrame: true,
payload: validStyle3Payload,
rootElement: function() document.getElementById("w").contentDocument.documentElement,
checkResult: function(html) isnot(html.indexOf("xxx"), -1, "Should have retained the font-face style")
},
{
id: "x",
payload: validStyle3Payload,
rootElement: function() document.getElementById("x"),
checkResult: function(html) isnot(html.indexOf("xxx"), -1, "Should have retained the font-face style")
},
{
id: "y",
isIFrame: true,
payload: invalidStyle5Payload,
rootElement: function() document.getElementById("y").contentDocument.documentElement,
checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the font-face style")
},
{
id: "z",
payload: invalidStyle5Payload,
rootElement: function() document.getElementById("z"),
checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the font-face style")
},
{
id: "aa",
isIFrame: true,
payload: nestedStylePayload,
rootElement: function() document.getElementById("aa").contentDocument.documentElement,
checkResult: function(html, text) {
is(html.indexOf("binding-1"), -1, "Should not have retained the binding-1 style");
isnot(text.indexOf("#bar2"), -1, "Should have retained binding-2 as text content");
is(text.indexOf("binding-2"), -1, "Should not have retained binding-2 as a tag");
}
},
{
id: "bb",
payload: nestedStylePayload,
rootElement: function() document.getElementById("bb"),
checkResult: function(html, text) {
is(html.indexOf("binding-1"), -1, "Should not have retained the binding-1 style");
isnot(text.indexOf("#bar2"), -1, "Should have retained binding-2 as text content");
is(text.indexOf("binding-2"), -1, "Should not have retained binding-2 as a tag");
}
},
{
id: "cc",
isIFrame: true,
payload: validStyle4Payload,
rootElement: function() document.getElementById("cc").contentDocument.documentElement,
checkResult: function(html) isnot(html.indexOf("xxx"), -1, "Should have retained the namespace style")
},
{
id: "dd",
payload: validStyle4Payload,
rootElement: function() document.getElementById("dd"),
checkResult: function(html) isnot(html.indexOf("xxx"), -1, "Should have retained the namespace style")
},
{
id: "ee",
isIFrame: true,
payload: invalidStyle6Payload,
rootElement: function() document.getElementById("ee").contentDocument.documentElement,
checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the namespace style")
},
{
id: "ff",
payload: invalidStyle6Payload,
rootElement: function() document.getElementById("ff"),
checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the namespace style")
}
];
function doNextTest() {
if (typeof testCounter == "undefined")
testCounter = 0;
else if (++testCounter == tests.length) {
SimpleTest.finish();
return;
}
runTest(tests[testCounter]);
doNextTest();
}
function runTest(test) {
var elem = document.getElementById(test.id);
if ("isIFrame" in test) {
elem.contentDocument.designMode = "on";
elem.contentWindow.focus();
} else
elem.focus();
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)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindow);
mainWindow.goDoCommand("cmd_paste");
if ("checkResult" in test) {
if ("isIFrame" in test) {
test.checkResult(elem.contentDocument.documentElement.innerHTML,
elem.contentDocument.documentElement.textContent);
} else {
test.checkResult(elem.innerHTML, elem.textContent);
}
} else {
var iframes = test.rootElement().querySelectorAll("iframe");
var expectedIFrameCount = ("iframeCount" in test) ? test.iframeCount : 1;
is(iframes.length, expectedIFrameCount, "Only " + expectedIFrameCount + " iframe should be pasted");
if (expectedIFrameCount > 0) {
ok(!iframes[0].hasAttribute("src"), "iframe should not have a src attrib");
}
}
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doNextTest);
</script>
</pre>
</body>
</html>

View File

@ -515,9 +515,11 @@ MAKE_CTOR(CreatePlainTextSerializer, nsIContentSerializer, NS_NewPla
MAKE_CTOR(CreateHTMLFragmentSink, nsIFragmentContentSink, NS_NewHTMLFragmentContentSink)
MAKE_CTOR(CreateHTMLFragmentSink2, nsIFragmentContentSink, NS_NewHTMLFragmentContentSink2)
MAKE_CTOR(CreateHTMLParanoidFragmentSink, nsIFragmentContentSink, NS_NewHTMLParanoidFragmentSink)
MAKE_CTOR(CreateHTMLParanoidFragmentSink2,nsIFragmentContentSink, NS_NewHTMLParanoidFragmentSink2)
MAKE_CTOR(CreateXMLFragmentSink, nsIFragmentContentSink, NS_NewXMLFragmentContentSink)
MAKE_CTOR(CreateXMLFragmentSink2, nsIFragmentContentSink, NS_NewXMLFragmentContentSink2)
MAKE_CTOR(CreateXHTMLParanoidFragmentSink,nsIFragmentContentSink, NS_NewXHTMLParanoidFragmentSink)
MAKE_CTOR(CreateXHTMLParanoidFragmentSink2,nsIFragmentContentSink, NS_NewXHTMLParanoidFragmentSink2)
MAKE_CTOR(CreateSanitizingHTMLSerializer, nsIContentSerializer, NS_NewSanitizingHTMLSerializer)
MAKE_CTOR(CreateXBLService, nsIXBLService, NS_NewXBLService)
MAKE_CTOR(CreateContentPolicy, nsIContentPolicy, NS_NewContentPolicy)
@ -1301,6 +1303,11 @@ static const nsModuleComponentInfo gLayoutComponents[] = {
NS_HTMLPARANOIDFRAGMENTSINK_CONTRACTID,
CreateHTMLParanoidFragmentSink },
{ "html paranoid fragment sink 2",
NS_HTMLPARANOIDFRAGMENTSINK2_CID,
NS_HTMLPARANOIDFRAGMENTSINK2_CONTRACTID,
CreateHTMLParanoidFragmentSink2 },
{ "HTML sanitizing content serializer",
MOZ_SANITIZINGHTMLSERIALIZER_CID,
MOZ_SANITIZINGHTMLSERIALIZER_CONTRACTID,
@ -1321,6 +1328,11 @@ static const nsModuleComponentInfo gLayoutComponents[] = {
NS_XHTMLPARANOIDFRAGMENTSINK_CONTRACTID,
CreateXHTMLParanoidFragmentSink },
{ "xhtml paranoid fragment sink 2",
NS_XHTMLPARANOIDFRAGMENTSINK2_CID,
NS_XHTMLPARANOIDFRAGMENTSINK2_CONTRACTID,
CreateXHTMLParanoidFragmentSink2 },
{ "XBL Service",
NS_XBLSERVICE_CID,
"@mozilla.org/xbl;1",

View File

@ -235,6 +235,8 @@ NS_NewHTMLFragmentContentSink2(nsIFragmentContentSink** aInstancePtrResult);
// in nsContentSink.h
nsresult
NS_NewHTMLParanoidFragmentSink(nsIFragmentContentSink** aInstancePtrResult);
nsresult
NS_NewHTMLParanoidFragmentSink2(nsIFragmentContentSink** aInstancePtrResult);
void
NS_HTMLParanoidFragmentSinkShutdown();
#endif /* nsHTMLParts_h___ */

View File

@ -46,6 +46,10 @@ class nsIDocument;
{ 0x1ecdb30d, 0x1f10, 0x45d2, \
{ 0xa4, 0xf4, 0xec, 0xbc, 0x03, 0x52, 0x9a, 0x7e } }
#define NS_I_PARANOID_FRAGMENT_CONTENT_SINK_IID \
{ 0x59ec77f5, 0x9e9b, 0x4040, \
{ 0xbd, 0xe7, 0x4e, 0xd0, 0x13, 0xa6, 0x21, 0x74 } }
/**
* The fragment sink allows a client to parse a fragment of sink, possibly
* surrounded in context. Also see nsIParser::ParseFragment().
@ -101,6 +105,23 @@ public:
NS_DEFINE_STATIC_IID_ACCESSOR(nsIFragmentContentSink,
NS_I_FRAGMENT_CONTENT_SINK_IID)
/**
* This interface is implemented by paranoid content sinks, and allows consumers
* to add tags and attributes to the default white-list set.
*/
class nsIParanoidFragmentContentSink : public nsISupports {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_I_PARANOID_FRAGMENT_CONTENT_SINK_IID)
/**
* Allow the content sink to accept style elements and attributes.
*/
virtual void AllowStyles() = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIParanoidFragmentContentSink,
NS_I_PARANOID_FRAGMENT_CONTENT_SINK_IID)
/**
* Base version takes string nested in context, content surrounded by
* WillBuildContent()/DidBuildContent() calls. The 2nd version just loads
@ -111,11 +132,15 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIFragmentContentSink,
#define NS_HTMLFRAGMENTSINK2_CONTRACTID "@mozilla.org/layout/htmlfragmentsink;2"
#define NS_HTMLPARANOIDFRAGMENTSINK_CONTRACTID \
"@mozilla.org/htmlparanoidfragmentsink;1"
#define NS_HTMLPARANOIDFRAGMENTSINK2_CONTRACTID \
"@mozilla.org/htmlparanoidfragmentsink;2"
#define NS_XMLFRAGMENTSINK_CONTRACTID "@mozilla.org/layout/xmlfragmentsink;1"
#define NS_XMLFRAGMENTSINK2_CONTRACTID "@mozilla.org/layout/xmlfragmentsink;2"
#define NS_XHTMLPARANOIDFRAGMENTSINK_CONTRACTID \
"@mozilla.org/xhtmlparanoidfragmentsink;1"
#define NS_XHTMLPARANOIDFRAGMENTSINK2_CONTRACTID \
"@mozilla.org/xhtmlparanoidfragmentsink;2"
// the HTML versions are in nsHTMLParts.h
@ -128,6 +153,8 @@ NS_NewXMLFragmentContentSink2(nsIFragmentContentSink** aInstancePtrResult);
// in nsContentSink.h
nsresult
NS_NewXHTMLParanoidFragmentSink(nsIFragmentContentSink** aInstancePtrResult);
nsresult
NS_NewXHTMLParanoidFragmentSink2(nsIFragmentContentSink** aInstancePtrResult);
void
NS_XHTMLParanoidFragmentSinkShutdown();
#endif