0
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2024-12-22 08:21:52 +00:00

Forms implementation for %HTML.Trusted. Some backend changes:

* Added Charsets and Character attribute types
* Fix a heavily recursive form of ContentSets, this allows a content-set
  to include another content-set which includes another content-set, and
  so forth.

Signed-off-by: Edward Z. Yang <edwardzyang@thewritingpot.com>
This commit is contained in:
Edward Z. Yang 2008-08-15 18:57:44 -04:00
parent dc28346677
commit c9b6f125aa
17 changed files with 475 additions and 37 deletions

1
NEWS
View File

@ -30,6 +30,7 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
Users who are not performing inbound filtering, this may seem a little Users who are not performing inbound filtering, this may seem a little
useless, but as a bonus, the test suite and handling of edge cases is also useless, but as a bonus, the test suite and handling of edge cases is also
improved. improved.
! Experimental implementation of forms for %HTML.Trusted
- Fix two bugs in %URI.MakeAbsolute; one involving empty paths in base URLs, - Fix two bugs in %URI.MakeAbsolute; one involving empty paths in base URLs,
the other involving an undefined $is_folder error. the other involving an undefined $is_folder error.
- Throw error when %Core.Encoding is set to a spurious value. Previously, - Throw error when %Core.Encoding is set to a spurious value. Previously,

4
TODO
View File

@ -22,7 +22,6 @@ FUTURE VERSIONS
3.2 release [It's All About Trust] (floating) 3.2 release [It's All About Trust] (floating)
# Implement untrusted, dangerous elements/attributes # Implement untrusted, dangerous elements/attributes
- Forms are especially wanted
# Implement IDREF support (harder than it seems, since you cannot have # Implement IDREF support (harder than it seems, since you cannot have
IDREFs to non-existent IDs) IDREFs to non-existent IDs)
# Frameset XHTML 1.0 and HTML 4.01 doctypes # Frameset XHTML 1.0 and HTML 4.01 doctypes
@ -44,7 +43,6 @@ FUTURE VERSIONS
contents should be dropped or not (currently, there's code that could do contents should be dropped or not (currently, there's code that could do
something like this if it didn't drop the inner text too.) something like this if it didn't drop the inner text too.)
- Remove <span> tags that don't do anything (no attributes) - Remove <span> tags that don't do anything (no attributes)
- Remove empty inline tags<i></i>
- Append something to duplicate IDs so they're still usable (impl. note: the - Append something to duplicate IDs so they're still usable (impl. note: the
dupe detector would also need to detect the suffix as well) dupe detector would also need to detect the suffix as well)
- Externalize inline CSS to promote clean HTML, proposed by Sander Tekelenburg - Externalize inline CSS to promote clean HTML, proposed by Sander Tekelenburg
@ -60,8 +58,6 @@ FUTURE VERSIONS
Also, enable disabling of directionality Also, enable disabling of directionality
5.0 release [To XML and Beyond] 5.0 release [To XML and Beyond]
- AllowedAttributes and ForbiddenAttributes step on the toes of XML by
using periods; this needs to be changed.
- Extended HTML capabilities based on namespacing and tag transforms (COMPLEX) - Extended HTML capabilities based on namespacing and tag transforms (COMPLEX)
- Hooks for adding custom processors to custom namespaced tags and - Hooks for adding custom processors to custom namespaced tags and
attributes, offer default implementation attributes, offer default implementation

View File

@ -151,7 +151,7 @@
</directive> </directive>
<directive id="HTML.Trusted"> <directive id="HTML.Trusted">
<file name="HTMLPurifier/HTMLModuleManager.php"> <file name="HTMLPurifier/HTMLModuleManager.php">
<line>199</line> <line>202</line>
</file> </file>
<file name="HTMLPurifier/Lexer.php"> <file name="HTMLPurifier/Lexer.php">
<line>233</line> <line>233</line>
@ -168,27 +168,27 @@
</directive> </directive>
<directive id="HTML.AllowedModules"> <directive id="HTML.AllowedModules">
<file name="HTMLPurifier/HTMLModuleManager.php"> <file name="HTMLPurifier/HTMLModuleManager.php">
<line>206</line> <line>209</line>
</file> </file>
</directive> </directive>
<directive id="HTML.CoreModules"> <directive id="HTML.CoreModules">
<file name="HTMLPurifier/HTMLModuleManager.php"> <file name="HTMLPurifier/HTMLModuleManager.php">
<line>207</line> <line>210</line>
</file> </file>
</directive> </directive>
<directive id="HTML.Proprietary"> <directive id="HTML.Proprietary">
<file name="HTMLPurifier/HTMLModuleManager.php"> <file name="HTMLPurifier/HTMLModuleManager.php">
<line>221</line> <line>224</line>
</file> </file>
</directive> </directive>
<directive id="HTML.SafeObject"> <directive id="HTML.SafeObject">
<file name="HTMLPurifier/HTMLModuleManager.php"> <file name="HTMLPurifier/HTMLModuleManager.php">
<line>226</line> <line>229</line>
</file> </file>
</directive> </directive>
<directive id="HTML.SafeEmbed"> <directive id="HTML.SafeEmbed">
<file name="HTMLPurifier/HTMLModuleManager.php"> <file name="HTMLPurifier/HTMLModuleManager.php">
<line>229</line> <line>232</line>
</file> </file>
</directive> </directive>
<directive id="Attr.IDBlacklist"> <directive id="Attr.IDBlacklist">

View File

@ -115,6 +115,7 @@ require 'HTMLPurifier/AttrTransform/Border.php';
require 'HTMLPurifier/AttrTransform/EnumToCSS.php'; require 'HTMLPurifier/AttrTransform/EnumToCSS.php';
require 'HTMLPurifier/AttrTransform/ImgRequired.php'; require 'HTMLPurifier/AttrTransform/ImgRequired.php';
require 'HTMLPurifier/AttrTransform/ImgSpace.php'; require 'HTMLPurifier/AttrTransform/ImgSpace.php';
require 'HTMLPurifier/AttrTransform/Input.php';
require 'HTMLPurifier/AttrTransform/Lang.php'; require 'HTMLPurifier/AttrTransform/Lang.php';
require 'HTMLPurifier/AttrTransform/Length.php'; require 'HTMLPurifier/AttrTransform/Length.php';
require 'HTMLPurifier/AttrTransform/Name.php'; require 'HTMLPurifier/AttrTransform/Name.php';
@ -122,6 +123,7 @@ require 'HTMLPurifier/AttrTransform/SafeEmbed.php';
require 'HTMLPurifier/AttrTransform/SafeObject.php'; require 'HTMLPurifier/AttrTransform/SafeObject.php';
require 'HTMLPurifier/AttrTransform/SafeParam.php'; require 'HTMLPurifier/AttrTransform/SafeParam.php';
require 'HTMLPurifier/AttrTransform/ScriptRequired.php'; require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
require 'HTMLPurifier/AttrTransform/Textarea.php';
require 'HTMLPurifier/ChildDef/Chameleon.php'; require 'HTMLPurifier/ChildDef/Chameleon.php';
require 'HTMLPurifier/ChildDef/Custom.php'; require 'HTMLPurifier/ChildDef/Custom.php';
require 'HTMLPurifier/ChildDef/Empty.php'; require 'HTMLPurifier/ChildDef/Empty.php';
@ -137,6 +139,7 @@ require 'HTMLPurifier/DefinitionCache/Decorator/Memory.php';
require 'HTMLPurifier/HTMLModule/Bdo.php'; require 'HTMLPurifier/HTMLModule/Bdo.php';
require 'HTMLPurifier/HTMLModule/CommonAttributes.php'; require 'HTMLPurifier/HTMLModule/CommonAttributes.php';
require 'HTMLPurifier/HTMLModule/Edit.php'; require 'HTMLPurifier/HTMLModule/Edit.php';
require 'HTMLPurifier/HTMLModule/Forms.php';
require 'HTMLPurifier/HTMLModule/Hypertext.php'; require 'HTMLPurifier/HTMLModule/Hypertext.php';
require 'HTMLPurifier/HTMLModule/Image.php'; require 'HTMLPurifier/HTMLModule/Image.php';
require 'HTMLPurifier/HTMLModule/Legacy.php'; require 'HTMLPurifier/HTMLModule/Legacy.php';

View File

@ -109,6 +109,7 @@ require_once $__dir . '/HTMLPurifier/AttrTransform/Border.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/EnumToCSS.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/EnumToCSS.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgRequired.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/ImgRequired.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgSpace.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/ImgSpace.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Input.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php';
@ -116,6 +117,7 @@ require_once $__dir . '/HTMLPurifier/AttrTransform/SafeEmbed.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Textarea.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php'; require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php'; require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Empty.php'; require_once $__dir . '/HTMLPurifier/ChildDef/Empty.php';
@ -131,6 +133,7 @@ require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Memory.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Bdo.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/Bdo.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/CommonAttributes.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/CommonAttributes.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Edit.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/Edit.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Forms.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Hypertext.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/Hypertext.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Image.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/Image.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Legacy.php'; require_once $__dir . '/HTMLPurifier/HTMLModule/Legacy.php';

View File

@ -0,0 +1,39 @@
<?php
/**
* Performs miscellaneous cross attribute validation and filtering for
* input elements. This is meant to be a post-transform.
*/
class HTMLPurifier_AttrTransform_Input extends HTMLPurifier_AttrTransform {
protected $pixels;
public function __construct() {
$this->pixels = new HTMLPurifier_AttrDef_HTML_Pixels();
}
public function transform($attr, $config, $context) {
if (!isset($attr['type'])) $t = 'text';
else $t = strtolower($attr['type']);
if (isset($attr['checked']) && $t !== 'radio' && $t !== 'checkbox') {
unset($attr['checked']);
}
if (isset($attr['maxlength']) && $t !== 'text' && $t !== 'password') {
unset($attr['maxlength']);
}
if (isset($attr['size']) && $t !== 'text' && $t !== 'password') {
$result = $this->pixels->validate($attr['size'], $config, $context);
if ($result === false) unset($attr['size']);
else $attr['size'] = $result;
}
if (isset($attr['src']) && $t !== 'image') {
unset($attr['src']);
}
if (!isset($attr['value']) && ($t === 'radio' || $t === 'checkbox')) {
$attr['value'] = '';
}
return $attr;
}
}

View File

@ -0,0 +1,16 @@
<?php
/**
* Sets height/width defaults for <textarea>
*/
class HTMLPurifier_AttrTransform_Textarea extends HTMLPurifier_AttrTransform
{
public function transform($attr, $config, $context) {
// Calculated from Firefox
if (!isset($attr['cols'])) $attr['cols'] = '22';
if (!isset($attr['rows'])) $attr['rows'] = '3';
return $attr;
}
}

View File

@ -32,6 +32,9 @@ class HTMLPurifier_AttrTypes
// unimplemented aliases // unimplemented aliases
$this->info['ContentType'] = new HTMLPurifier_AttrDef_Text(); $this->info['ContentType'] = new HTMLPurifier_AttrDef_Text();
$this->info['ContentTypes'] = new HTMLPurifier_AttrDef_Text();
$this->info['Charsets'] = new HTMLPurifier_AttrDef_Text();
$this->info['Character'] = new HTMLPurifier_AttrDef_Text();
// number is really a positive integer (one or more digits) // number is really a positive integer (one or more digits)
// FIXME: ^^ not always, see start and value of list items // FIXME: ^^ not always, see start and value of list items

View File

@ -5,8 +5,6 @@
* *
* @warning Currently this class is an all or nothing proposition, that is, * @warning Currently this class is an all or nothing proposition, that is,
* it will only give a bool return value. * it will only give a bool return value.
* @note This class is currently not used by any code, although it is unit
* tested.
*/ */
class HTMLPurifier_ChildDef_Custom extends HTMLPurifier_ChildDef class HTMLPurifier_ChildDef_Custom extends HTMLPurifier_ChildDef
{ {

View File

@ -37,33 +37,35 @@ class HTMLPurifier_ContentSets
// sorry, no way of overloading // sorry, no way of overloading
foreach ($modules as $module_i => $module) { foreach ($modules as $module_i => $module) {
foreach ($module->content_sets as $key => $value) { foreach ($module->content_sets as $key => $value) {
if (isset($this->info[$key])) { $temp = $this->convertToLookup($value);
if (isset($this->lookup[$key])) {
// add it into the existing content set // add it into the existing content set
$this->info[$key] = $this->info[$key] . ' | ' . $value; $this->lookup[$key] = array_merge($this->lookup[$key], $temp);
} else { } else {
$this->info[$key] = $value; $this->lookup[$key] = $temp;
} }
} }
} }
// perform content_set expansions $old_lookup = false;
$this->keys = array_keys($this->info); while ($old_lookup !== $this->lookup) {
foreach ($this->info as $i => $set) { $old_lookup = $this->lookup;
// only performed once, so infinite recursion is not foreach ($this->lookup as $i => $set) {
// a problem $add = array();
$this->info[$i] = foreach ($set as $element => $x) {
str_replace( if (isset($this->lookup[$element])) {
$this->keys, $add += $this->lookup[$element];
// must be recalculated each time due to unset($this->lookup[$i][$element]);
// changing substitutions }
array_values($this->info), }
$set); $this->lookup[$i] += $add;
}
} }
$this->values = array_values($this->info);
// generate lookup tables foreach ($this->lookup as $key => $lookup) {
foreach ($this->info as $name => $set) { $this->info[$key] = implode(' | ', array_keys($lookup));
$this->lookup[$name] = $this->convertToLookup($set);
} }
$this->keys = array_keys($this->info);
$this->values = array_values($this->info);
} }
/** /**
@ -75,12 +77,22 @@ class HTMLPurifier_ContentSets
if (!empty($def->child)) return; // already done! if (!empty($def->child)) return; // already done!
$content_model = $def->content_model; $content_model = $def->content_model;
if (is_string($content_model)) { if (is_string($content_model)) {
$def->content_model = str_replace( // Assume that $this->keys is alphanumeric
$this->keys, $this->values, $content_model); $def->content_model = preg_replace_callback(
'/\b(' . implode('|', $this->keys) . ')\b/',
array($this, 'generateChildDefCallback'),
$content_model
);
//$def->content_model = str_replace(
// $this->keys, $this->values, $content_model);
} }
$def->child = $this->getChildDef($def, $module); $def->child = $this->getChildDef($def, $module);
} }
public function generateChildDefCallback($matches) {
return $this->info[$matches[0]];
}
/** /**
* Instantiates a ChildDef based on content_model and content_model_type * Instantiates a ChildDef based on content_model and content_model_type
* member variables in HTMLPurifier_ElementDef * member variables in HTMLPurifier_ElementDef

View File

@ -0,0 +1,117 @@
<?php
/**
* XHTML 1.1 Forms module, defines all form-related elements found in HTML 4.
*/
class HTMLPurifier_HTMLModule_Forms extends HTMLPurifier_HTMLModule
{
public $name = 'Forms';
public $safe = false;
public $content_sets = array(
'Block' => 'Form',
'Inline' => 'Formctrl',
);
public function setup($config) {
$form = $this->addElement('form', 'Form',
'Required: Heading | List | Block | fieldset', 'Common', array(
'accept' => 'ContentTypes',
'accept-charset' => 'Charsets',
'action*' => 'URI',
'method' => 'Enum#get,post',
// really ContentType, but these two are the only ones used today
'enctype' => 'Enum#application/x-www-form-urlencoded,multipart/form-data',
));
$form->excludes = array('form' => true);
$input = $this->addElement('input', 'Formctrl', 'Empty', 'Common', array(
'accept' => 'ContentTypes',
'accesskey' => 'Character',
'alt' => 'Text',
'checked' => 'Bool#checked',
'disabled' => 'Bool#disabled',
'maxlength' => 'Number',
'name' => 'CDATA',
'readonly' => 'Bool#readonly',
'size' => 'Number',
'src' => 'URI#embeds',
'tabindex' => 'Number',
'type' => 'Enum#text,password,checkbox,button,radio,submit,reset,file,hidden,image',
'value' => 'CDATA',
));
$input->attr_transform_post[] = new HTMLPurifier_AttrTransform_Input();
$this->addElement('select', 'Formctrl', 'Required: optgroup | option', 'Common', array(
'disabled' => 'Bool#disabled',
'multiple' => 'Bool#multiple',
'name' => 'CDATA',
'size' => 'Number',
'tabindex' => 'Number',
));
$this->addElement('option', false, 'Optional: #PCDATA', 'Common', array(
'disabled' => 'Bool#disabled',
'label' => 'Text',
'selected' => 'Bool#selected',
'value' => 'CDATA',
));
// It's illegal for there to be more than one selected, but not
// be multiple. Also, no selected means undefined behavior. This might
// be difficult to implement; perhaps an injector, or a context variable.
$textarea = $this->addElement('textarea', 'Formctrl', 'Optional: #PCDATA', 'Common', array(
'accesskey' => 'Character',
'cols*' => 'Number',
'disabled' => 'Bool#disabled',
'name' => 'CDATA',
'readonly' => 'Bool#readonly',
'rows*' => 'Number',
'tabindex' => 'Number',
));
$textarea->attr_transform_pre[] = new HTMLPurifier_AttrTransform_Textarea();
$button = $this->addElement('button', 'Formctrl', 'Optional: #PCDATA | Heading | List | Block | Inline', 'Common', array(
'accesskey' => 'Character',
'disabled' => 'Bool#disabled',
'name' => 'CDATA',
'tabindex' => 'Number',
'type' => 'Enum#button,submit,reset',
'value' => 'CDATA',
));
// For exclusions, ideally we'd specify content sets, not literal elements
$button->excludes = $this->makeLookup(
'form', 'fieldset', // Form
'input', 'select', 'textarea', 'label', 'button', // Formctrl
'a' // as per HTML 4.01 spec, this is omitted by modularization
);
// Extra exclusion: img usemap="" is not permitted within this element.
// We'll omit this for now, since we don't have any good way of
// indicating it yet.
// This is HIGHLY user-unfriendly; we need a custom child-def for this
$this->addElement('fieldset', 'Form', 'Custom: (#WS?,legend,(Flow|#PCDATA)*)', 'Common');
$label = $this->addElement('label', 'Formctrl', 'Optional: #PCDATA | Inline', 'Common', array(
'accesskey' => 'Character',
// 'for' => 'IDREF', // IDREF not implemented, cannot allow
));
$label->excludes = array('label' => true);
$this->addElement('legend', false, 'Optional: #PCDATA | Inline', 'Common', array(
'accesskey' => 'Character',
));
$this->addElement('optgroup', false, 'Required: option', 'Common', array(
'disabled' => 'Bool#disabled',
'label*' => 'Text',
));
// Don't forget an injector for <isindex>. This one's a little complex
// because it maps to multiple elements.
}
}

View File

@ -63,8 +63,11 @@ class HTMLPurifier_HTMLModuleManager
$common = array( $common = array(
'CommonAttributes', 'Text', 'Hypertext', 'List', 'CommonAttributes', 'Text', 'Hypertext', 'List',
'Presentation', 'Edit', 'Bdo', 'Tables', 'Image', 'Presentation', 'Edit', 'Bdo', 'Tables', 'Image',
'StyleAttribute', 'Scripting', 'Object', 'StyleAttribute',
'Name' // technically legacy, but present in all the specs // Unsafe:
'Scripting', 'Object', 'Forms',
// Sorta legacy, but present in strict:
'Name',
); );
$transitional = array('Legacy', 'Target'); $transitional = array('Legacy', 'Target');
$xml = array('XMLCommonAttributes'); $xml = array('XMLCommonAttributes');

View File

@ -0,0 +1,93 @@
<?php
class HTMLPurifier_AttrTransform_InputTest extends HTMLPurifier_AttrTransformHarness
{
function setUp() {
parent::setUp();
$this->obj = new HTMLPurifier_AttrTransform_Input();
}
function testEmptyInput() {
$this->assertResult(array());
}
function testInvalidCheckedWithEmpty() {
$this->assertResult(array('checked' => 'checked'), array());
}
function testInvalidCheckedWithPassword() {
$this->assertResult(array(
'checked' => 'checked',
'type' => 'password'
), array(
'type' => 'password'
));
}
function testValidCheckedWithUcCheckbox() {
$this->assertResult(array(
'checked' => 'checked',
'type' => 'CHECKBOX',
'value' => 'bar',
));
}
function testInvalidMaxlength() {
$this->assertResult(array(
'maxlength' => '10',
'type' => 'checkbox',
'value' => 'foo',
), array(
'type' => 'checkbox',
'value' => 'foo',
));
}
function testValidMaxLength() {
$this->assertResult(array(
'maxlength' => '10',
));
}
// these two are really bad test-cases
function testSizeWithCheckbox() {
$this->assertResult(array(
'type' => 'checkbox',
'value' => 'foo',
'size' => '100px',
), array(
'type' => 'checkbox',
'value' => 'foo',
'size' => '100',
));
}
function testSizeWithText() {
$this->assertResult(array(
'type' => 'password',
'size' => '100px', // spurious value, to indicate no validation takes place
), array(
'type' => 'password',
'size' => '100px',
));
}
function testInvalidSrc() {
$this->assertResult(array(
'src' => 'img.png',
), array());
}
function testMissingValue() {
$this->assertResult(array(
'type' => 'checkbox',
), array(
'type' => 'checkbox',
'value' => '',
));
}
}

View File

@ -69,5 +69,20 @@ class HTMLPurifier_ChildDef_CustomTest extends HTMLPurifier_ChildDefHarness
} }
function testPcdata() {
$this->obj = new HTMLPurifier_ChildDef_Custom('#PCDATA,a');
$this->assertEqual($this->obj->elements, array('#PCDATA' => true, 'a' => true));
$this->assertResult('foo<a />');
$this->assertResult('<a />', false);
}
function testWhitespace() {
$this->obj = new HTMLPurifier_ChildDef_Custom('a');
$this->assertEqual($this->obj->elements, array('a' => true));
$this->assertResult('foo<a />', false);
$this->assertResult('<a />');
$this->assertResult(' <a />');
}
} }

View File

@ -67,8 +67,6 @@ class HTMLPurifier_ChildDef_RequiredTest extends HTMLPurifier_ChildDefHarness
'Out <b>Bold text</b><img />', 'Out <b>Bold text</b><img />',
'Out <b>Bold text</b>&lt;img /&gt;' 'Out <b>Bold text</b>&lt;img /&gt;'
); );
} }
} }

View File

@ -80,6 +80,10 @@ class HTMLPurifier_ComplexHarness extends HTMLPurifier_Harness
} }
$this->assertIdentical($expect, $result); $this->assertIdentical($expect, $result);
if ($expect !== $result) {
echo '<pre>' . htmlspecialchars($result) . '</pre>';
}
} }
/** /**

View File

@ -0,0 +1,137 @@
<?php
class HTMLPurifier_HTMLModule_FormsTest extends HTMLPurifier_HTMLModuleHarness
{
function setUp() {
parent::setUp();
$this->config->set('HTML', 'Trusted', true);
$this->config->set('Attr', 'EnableID', true);
$this->config->set('Cache', 'DefinitionImpl', null);
}
function testBasicUse() {
$this->assertResult( // need support for label for later
'
<form action="http://somesite.com/prog/adduser" method="post">
<p>
<label>First name: </label>
<input type="text" id="firstname" /><br />
<label>Last name: </label>
<input type="text" id="lastname" /><br />
<label>email: </label>
<input type="text" id="email" /><br />
<input type="radio" name="sex" value="Male" /> Male<br />
<input type="radio" name="sex" value="Female" /> Female<br />
<input type="submit" value="Send" /> <input type="reset" />
</p>
</form>'
);
}
function testSelectOption() {
$this->assertResult('
<form action="http://somesite.com/prog/component-select" method="post">
<p>
<select multiple="multiple" size="4" name="component-select">
<option selected="selected" value="Component_1_a">Component_1</option>
<option selected="selected" value="Component_1_b">Component_2</option>
<option>Component_3</option>
<option>Component_4</option>
<option>Component_5</option>
<option>Component_6</option>
<option>Component_7</option>
</select>
<input type="submit" value="Send" /><input type="reset" />
</p>
</form>
');
}
function testSelectOptgroup() {
$this->assertResult('
<form action="http://somesite.com/prog/someprog" method="post">
<p>
<select name="ComOS">
<option selected="selected" label="none" value="none">None</option>
<optgroup label="PortMaster 3">
<option label="3.7.1" value="pm3_3.7.1">PortMaster 3 with ComOS 3.7.1</option>
<option label="3.7" value="pm3_3.7">PortMaster 3 with ComOS 3.7</option>
<option label="3.5" value="pm3_3.5">PortMaster 3 with ComOS 3.5</option>
</optgroup>
<optgroup label="PortMaster 2">
<option label="3.7" value="pm2_3.7">PortMaster 2 with ComOS 3.7</option>
<option label="3.5" value="pm2_3.5">PortMaster 2 with ComOS 3.5</option>
</optgroup>
<optgroup label="IRX">
<option label="3.7R" value="IRX_3.7R">IRX with ComOS 3.7R</option>
<option label="3.5R" value="IRX_3.5R">IRX with ComOS 3.5R</option>
</optgroup>
</select>
</p>
</form>
');
}
function testTextarea() {
$this->assertResult('
<form action="http://somesite.com/prog/text-read" method="post">
<p>
<textarea name="thetext" rows="20" cols="80">
First line of initial text.
Second line of initial text.
</textarea>
<input type="submit" value="Send" /><input type="reset" />
</p>
</form>
');
}
// label tests omitted
function testFieldset() {
$this->assertResult('
<form action="..." method="post">
<fieldset>
<legend>Personal Information</legend>
Last Name: <input name="personal_lastname" type="text" tabindex="1" />
First Name: <input name="personal_firstname" type="text" tabindex="2" />
Address: <input name="personal_address" type="text" tabindex="3" />
...more personal information...
</fieldset>
<fieldset>
<legend>Medical History</legend>
<input name="history_illness" type="checkbox" value="Smallpox" tabindex="20" />Smallpox
<input name="history_illness" type="checkbox" value="Mumps" tabindex="21" /> Mumps
<input name="history_illness" type="checkbox" value="Dizziness" tabindex="22" /> Dizziness
<input name="history_illness" type="checkbox" value="Sneezing" tabindex="23" /> Sneezing
...more medical history...
</fieldset>
<fieldset>
<legend>Current Medication</legend>
Are you currently taking any medication?
<input name="medication_now" type="radio" value="Yes" tabindex="35" />Yes
<input name="medication_now" type="radio" value="No" tabindex="35" />No
If you are currently taking medication, please indicate
it in the space below:
<textarea name="current_medication" rows="20" cols="50" tabindex="40"></textarea>
</fieldset>
</form>
');
}
function testInputTransform() {
$this->assertResult('<input type="checkbox" />', '<input type="checkbox" value="" />');
}
function testTextareaTransform() {
$this->assertResult('<textarea></textarea>', '<textarea cols="22" rows="3"></textarea>');
}
function testTextInFieldset() {
$this->assertResult('<fieldset> <legend></legend>foo</fieldset>');
}
}