-
Notifications
You must be signed in to change notification settings - Fork 0
Html api/add html processor #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from 1 commit
805fb55
84e35ef
84e25cf
c18a81a
af8d49d
72e5de0
095a110
9b3f90d
09e16b2
396285b
b2e6856
65c9f5b
f5e4d5e
247966b
f1ccd9b
fb31ea1
2af46d0
e945f16
96053c9
da7880a
264ae35
04a37b5
0975e34
d30f766
cafee03
67bef48
be6e901
9980a36
2e874e4
d0e3f4a
ece3a68
a5284d6
74c9e6b
a4569cd
98180bc
0e3ada5
739b4aa
2b263b5
e1ec10c
bc61ea8
6f58b70
d82b146
a6e5323
8a2215e
d96c05a
c93d420
2e0e4f9
f5c7651
3956ad9
37ce95e
613ae1a
46e3777
a1a095c
c2dcc37
c29570e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| <?php | ||
|
|
||
| class WP_HTML_Element_Stack_Item { | ||
| const IS_CLOSER = 1 << 0; | ||
| const HAS_SELF_CLOSING_FLAG = 1 << 1; | ||
|
|
||
| /** | ||
| * Stores the name of the bookmark pointing to the element at the position of the item. | ||
| * | ||
| * @var string|null | ||
| */ | ||
| public $bookmark_name = null; | ||
|
|
||
| /** | ||
| * Stores the element class name for the element at the position of the item. | ||
| * | ||
| * This is the name of the PHP class representing the element. | ||
| * For example, `WP_HTMLDivElement` from calling `WP_HTMLDivElement::class`. | ||
| * | ||
| * @var string|null | ||
| */ | ||
| public $element = null; | ||
|
|
||
| /** | ||
| * Properties about this item in the stack that are relevant for relating opening and closing tags. | ||
| * | ||
| * @var int | ||
| */ | ||
| public $flags = 0; | ||
|
|
||
| /** | ||
| * Pointer to related item on the stack, if one exists. | ||
| * For example, a tag opener that opens the current tag closer. | ||
| * | ||
| * @var WP_HTML_Element_Stack_Item|null | ||
| */ | ||
| public $related_item = null; | ||
|
|
||
| public function __construct( $bookmark_name, $element, $flags, $related_item = null ) { | ||
| $this->bookmark_name = $bookmark_name; | ||
| $this->element = $element; | ||
| $this->flags = $flags; | ||
| $this->related_item = $related_item; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,266 @@ | ||
| <?php | ||
|
|
||
| class WP_HTML_Element_Stack { | ||
| /** | ||
| * Stack holding HTML tokens, tag openers, tag closers, or plain bookmarks. | ||
| * | ||
| * @var WP_HTML_Element_Stack_Item[] | ||
| */ | ||
| public $stack = array(); | ||
|
|
||
| public function __construct( $bookmark_name, $element, $flags ) { | ||
| $this->bookmark_name = $bookmark_name; | ||
| $this->element = $element; | ||
| $this->flags = $flags; | ||
| } | ||
|
|
||
| /** | ||
| * Add an item to the top of the stack. | ||
| * | ||
| * @TODO: Do we need to insertion-sort these? | ||
| * | ||
| * @param $stack_item | ||
| * @return void | ||
| */ | ||
| public function push( $stack_item ) { | ||
| $this->stack[] = $stack_item; | ||
| } | ||
|
|
||
| public function count() { | ||
| return count( $this->stack ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the bottom-most node on the stack. | ||
| * | ||
| * @return WP_HTML_Element_Stack_Item|null | ||
| */ | ||
| public function current_node() { | ||
| $count = $this->count(); | ||
|
|
||
| return $this->count() > 0 | ||
| ? $this->stack[ $count - 1 ] | ||
| : null; | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether the given element is on the stack. | ||
| * | ||
| * @param string $element the ::class name of the element to check for. | ||
| * @return boolean whether the given element is on the stack. | ||
| */ | ||
| public function has_element( $element ) { | ||
| for ( $i = count( $this->stack ) - 1; $i > 0; $i++ ) { | ||
| if ( $this->stack[ $i ]->element === $element ) { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether an element is in a specific scope. | ||
| * | ||
| * @see https://html.spec.whatwg.org/#has-an-element-in-the-specific-scope | ||
| * | ||
| * @param string $element The target node. | ||
| * @param string[] $termination_list List of elements that terminate the search. | ||
| * @return bool | ||
| */ | ||
| public function has_element_in_specific_scope( $element, $termination_list ) { | ||
| $i = $this->count(); | ||
| if ( $i === 0 ) { | ||
| return false; | ||
| } | ||
|
|
||
| $node = $this->stack[ --$i ]; | ||
|
|
||
| if ( $node->element === $element ) { | ||
| return true; | ||
| } | ||
|
|
||
| if ( in_array( $element, $termination_list, true ) ) { | ||
| return false; | ||
| } | ||
|
|
||
| while ( $i > 0 && null !== ( $node = $this->stack[ --$i ] ) ) { | ||
| if ( $node->element === $element ) { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether a given element is in a particular scope. | ||
| * | ||
| * @see https://html.spec.whatwg.org/#has-an-element-in-scope | ||
| * | ||
| * @param string $element | ||
| * @return bool | ||
| */ | ||
| public function has_element_in_particular_scope( $element ) { | ||
| return $this->has_element_in_specific_scope( $element, array( | ||
| WP_HTMLAppletElement::class, | ||
| WP_HTMLCaptionElement::class, | ||
| WP_HTMLHtmlElement::class, | ||
| WP_HTMLTableElement::class, | ||
| WP_HTMLTdElement::class, | ||
| WP_HTMLThElement::class, | ||
| WP_HTMLMarqueeElement::class, | ||
| WP_HTMLObjectElement::class, | ||
| WP_HTMLTemplateElement::class, | ||
| WP_MathML_Mi_Element::class, | ||
| WP_MathML_Mo_Element::class, | ||
| WP_MathML_Mn_Element::class, | ||
| WP_MathML_Ms_Element::class, | ||
| WP_MathML_Mtext_Element::class, | ||
| WP_MathML_Annotation_Xml_Element::class, | ||
| WP_SVG_ForeignObject_Element::class, | ||
| WP_SVG_Description_Element::class, | ||
| WP_SVG_Title_Element::class, | ||
| ) ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether a given element is in list item scope. | ||
| * | ||
| * @see https://html.spec.whatwg.org/#has-an-element-in-list-item-scope | ||
| * | ||
| * @param $element | ||
| * @return void | ||
| */ | ||
| public function has_element_in_list_item_scope( $element ) { | ||
| return $this->has_element_in_specific_scope( $element, array( | ||
| WP_HTMLAppletElement::class, | ||
| WP_HTMLCaptionElement::class, | ||
| WP_HTMLHtmlElement::class, | ||
| WP_HTMLTableElement::class, | ||
| WP_HTMLTdElement::class, | ||
| WP_HTMLThElement::class, | ||
| WP_HTMLMarqueeElement::class, | ||
| WP_HTMLObjectElement::class, | ||
| WP_HTMLTemplateElement::class, | ||
| WP_MathML_Mi_Element::class, | ||
| WP_MathML_Mo_Element::class, | ||
| WP_MathML_Mn_Element::class, | ||
| WP_MathML_Ms_Element::class, | ||
| WP_MathML_Mtext_Element::class, | ||
| WP_MathML_Annotation_Xml_Element::class, | ||
| WP_SVG_ForeignObject_Element::class, | ||
| WP_SVG_Description_Element::class, | ||
| WP_SVG_Title_Element::class, | ||
|
|
||
| // Additionally these elements. | ||
| WP_HTMLOlElement::class, | ||
| WP_HTMLUlElement::class, | ||
| ) ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether a given element is in button scope. | ||
| * | ||
| * @see https://html.spec.whatwg.org/#has-an-element-in-button-scope | ||
| * | ||
| * @param string $element | ||
| * @return boolean | ||
| */ | ||
| public function has_element_in_button_scope( $element ) { | ||
| return $this->has_element_in_specific_scope( $element, array( | ||
| WP_HTMLAppletElement::class, | ||
| WP_HTMLCaptionElement::class, | ||
| WP_HTMLHtmlElement::class, | ||
| WP_HTMLTableElement::class, | ||
| WP_HTMLTdElement::class, | ||
| WP_HTMLThElement::class, | ||
| WP_HTMLMarqueeElement::class, | ||
| WP_HTMLObjectElement::class, | ||
| WP_HTMLTemplateElement::class, | ||
| WP_MathML_Mi_Element::class, | ||
| WP_MathML_Mo_Element::class, | ||
| WP_MathML_Mn_Element::class, | ||
| WP_MathML_Ms_Element::class, | ||
| WP_MathML_Mtext_Element::class, | ||
| WP_MathML_Annotation_Xml_Element::class, | ||
| WP_SVG_ForeignObject_Element::class, | ||
| WP_SVG_Description_Element::class, | ||
| WP_SVG_Title_Element::class, | ||
|
|
||
| // Additionally these elements. | ||
| WP_HTMLButtonElement::class, | ||
| ) ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether the given element is in table scope. | ||
| * | ||
| * @see https://html.spec.whatwg.org/#has-an-element-in-table-scope | ||
| * | ||
| * @param string $element | ||
| * @return bool | ||
| */ | ||
| public function has_element_in_table_scope( $element ) { | ||
| return $this->has_element_in_specific_scope( $element, array( | ||
| WP_HTMLAppletElement::class, | ||
| WP_HTMLCaptionElement::class, | ||
| WP_HTMLHtmlElement::class, | ||
| WP_HTMLTableElement::class, | ||
| WP_HTMLTdElement::class, | ||
| WP_HTMLThElement::class, | ||
| WP_HTMLMarqueeElement::class, | ||
| WP_HTMLObjectElement::class, | ||
| WP_HTMLTemplateElement::class, | ||
| WP_MathML_Mi_Element::class, | ||
| WP_MathML_Mo_Element::class, | ||
| WP_MathML_Mn_Element::class, | ||
| WP_MathML_Ms_Element::class, | ||
| WP_MathML_Mtext_Element::class, | ||
| WP_MathML_Annotation_Xml_Element::class, | ||
| WP_SVG_ForeignObject_Element::class, | ||
| WP_SVG_Description_Element::class, | ||
| WP_SVG_Title_Element::class, | ||
|
|
||
| // Additionally these elements. | ||
| WP_HTMLHtmlElement::class, | ||
| WP_HTMLTableElement::class, | ||
| WP_HTMLTemplateElement::class, | ||
| ) ); | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether a given element is in select scope. | ||
| * | ||
| * @see https://html.spec.whatwg.org/#has-an-element-in-select-scope | ||
| * | ||
| * @param string $element | ||
| * @return bool | ||
| */ | ||
| public function has_element_in_select_scope( $element ) { | ||
| return $this->has_element_in_specific_scope( $element, array( | ||
| WP_HTMLAppletElement::class, | ||
| WP_HTMLCaptionElement::class, | ||
| WP_HTMLHtmlElement::class, | ||
| WP_HTMLTableElement::class, | ||
| WP_HTMLTdElement::class, | ||
| WP_HTMLThElement::class, | ||
| WP_HTMLMarqueeElement::class, | ||
| WP_HTMLObjectElement::class, | ||
| WP_HTMLTemplateElement::class, | ||
| WP_MathML_Mi_Element::class, | ||
| WP_MathML_Mo_Element::class, | ||
| WP_MathML_Mn_Element::class, | ||
| WP_MathML_Ms_Element::class, | ||
| WP_MathML_Mtext_Element::class, | ||
| WP_MathML_Annotation_Xml_Element::class, | ||
| WP_SVG_ForeignObject_Element::class, | ||
| WP_SVG_Description_Element::class, | ||
| WP_SVG_Title_Element::class, | ||
|
|
||
| // Additionally these elements. | ||
| WP_HTMLOptgroupElement::class, | ||
| WP_HTMLOptionElement::class, | ||
| ) ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,12 +24,20 @@ class WP_HTML_Processor extends WP_HTML_Tag_Processor { | |
| * @return boolean Whether an element was found. | ||
| */ | ||
| public function step( $insertion_mode = null ) { | ||
| switch ( $insertion_mode ?: $this->insertion_mode ) { | ||
| case 'in-body': | ||
| return $this->step_in_body(); | ||
| try { | ||
| switch ( $insertion_mode ?: $this->insertion_mode ) { | ||
| case 'in-body': | ||
| return $this->step_in_body(); | ||
|
|
||
| default: | ||
| return self::NOT_IMPLEMENTED_YET; | ||
| default: | ||
| return self::NOT_IMPLEMENTED_YET; | ||
| } | ||
| } catch ( Exception $e ) { | ||
| /* | ||
| * Exceptions are used in this class to escape deep call stacks that | ||
| * otherwise might involve messier calling and return conventions. | ||
| */ | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -215,7 +223,7 @@ private function close_p_element() { | |
| * @return void | ||
| */ | ||
| private function generate_implied_end_tags( $except_for_this_element = null ) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! I can't remember why I used an array here, but it doesn't seem like one is needed.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you support "comprehensive" implied tags in the same function |
||
|
|
||
| } | ||
|
|
||
| /** | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Catching all exceptions might hide legitimate errors, it would be safer to have a HTML_Exception class or so
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point. at some point I wanted to go back and special-case known exceptions so we don't hide others, but I didn't do that yet because I wasn't entirely sure how I felt about the escaping mechanism as throwing Exceptions.