Changeset 249969 in webkit


Ignore:
Timestamp:
Sep 17, 2019, 11:37:07 AM (6 years ago)
Author:
Joseph Pecoraro
Message:

Web Inspector: HTML Formatter - better handling for HTML specific tag cases (<p>/<li>)
https://bugs.webkit.org/show_bug.cgi?id=201757
<rdar://problem/55409987>

Reviewed by Devin Rousso.

Source/WebInspectorUI:

  • UserInterface/Workers/Formatter/HTMLFormatter.js:

(HTMLFormatter.prototype._after):
Handle a closing tag with different text than the opening tag.

  • UserInterface/Workers/Formatter/HTMLTreeBuilderFormatter.js:

(HTMLTreeBuilderFormatter.prototype._pushParserNodeTopLevel):
(HTMLTreeBuilderFormatter.prototype._pushParserNodeStack):
(HTMLTreeBuilderFormatter.prototype._implicitlyCloseHTMLNodesForOpenTag):
(HTMLTreeBuilderFormatter.prototype._implicitlyCloseTagNamesInsideParentTagNames):
(HTMLTreeBuilderFormatter.prototype._indexOfStackNodeMatchingTagNames):
Generalize the implicit closing a bit. Allow open tags to implicitly
close certain other open tags in the stack.

LayoutTests:

  • inspector/formatting/formatting-html-expected.txt:
  • inspector/formatting/formatting-html.html:
  • inspector/formatting/resources/html-tests/auto-close-normal-expected.html: Renamed from LayoutTests/inspector/formatting/resources/html-tests/auto-close-expected.html.
  • inspector/formatting/resources/html-tests/auto-close-normal.html: Renamed from LayoutTests/inspector/formatting/resources/html-tests/auto-close.html.
  • inspector/formatting/resources/html-tests/auto-close-special-expected.html: Added.
  • inspector/formatting/resources/html-tests/auto-close-special.html: Added.
  • inspector/formatting/resources/html-tests/list-expected.html:
  • inspector/formatting/resources/html-tests/list.html:
  • inspector/formatting/resources/html-tests/not-well-formed-1-expected.html:
  • inspector/formatting/resources/html-tests/not-well-formed-1.html:
  • inspector/formatting/resources/html-tests/p-expected.html: Added.
  • inspector/formatting/resources/html-tests/p.html: Added.
  • inspector/formatting/resources/html-tests/table-expected.html: Added.
  • inspector/formatting/resources/html-tests/table.html: Added.
  • inspector/formatting/resources/html-tests/tag-case-expected.html: Added.
  • inspector/formatting/resources/html-tests/tag-case.html: Added.

Tests for HTML specialties.

  • inspector/formatting/formatting-xml-expected.txt:
  • inspector/formatting/formatting-xml.html:
  • inspector/formatting/resources/xml-tests/tag-case-expected.xml: Added.
  • inspector/formatting/resources/xml-tests/tag-case.xml: Added.
  • inspector/formatting/resources/xml-tests/valid-html-invalid-xml-expected.xml:
  • inspector/formatting/resources/xml-tests/valid-html-invalid-xml.xml:

XML is case-sensitive. Ensure XML doesn't get more of the HTML specialties.

Location:
trunk
Files:
10 added
14 edited
2 moved

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r249963 r249969  
     12019-09-17  Joseph Pecoraro  <pecoraro@apple.com>
     2
     3        Web Inspector: HTML Formatter - better handling for HTML specific tag cases (<p>/<li>)
     4        https://bugs.webkit.org/show_bug.cgi?id=201757
     5        <rdar://problem/55409987>
     6
     7        Reviewed by Devin Rousso.
     8
     9        * inspector/formatting/formatting-html-expected.txt:
     10        * inspector/formatting/formatting-html.html:
     11        * inspector/formatting/resources/html-tests/auto-close-normal-expected.html: Renamed from LayoutTests/inspector/formatting/resources/html-tests/auto-close-expected.html.
     12        * inspector/formatting/resources/html-tests/auto-close-normal.html: Renamed from LayoutTests/inspector/formatting/resources/html-tests/auto-close.html.
     13        * inspector/formatting/resources/html-tests/auto-close-special-expected.html: Added.
     14        * inspector/formatting/resources/html-tests/auto-close-special.html: Added.
     15        * inspector/formatting/resources/html-tests/list-expected.html:
     16        * inspector/formatting/resources/html-tests/list.html:
     17        * inspector/formatting/resources/html-tests/not-well-formed-1-expected.html:
     18        * inspector/formatting/resources/html-tests/not-well-formed-1.html:
     19        * inspector/formatting/resources/html-tests/p-expected.html: Added.
     20        * inspector/formatting/resources/html-tests/p.html: Added.
     21        * inspector/formatting/resources/html-tests/table-expected.html: Added.
     22        * inspector/formatting/resources/html-tests/table.html: Added.
     23        * inspector/formatting/resources/html-tests/tag-case-expected.html: Added.
     24        * inspector/formatting/resources/html-tests/tag-case.html: Added.
     25        Tests for HTML specialties.
     26
     27        * inspector/formatting/formatting-xml-expected.txt:
     28        * inspector/formatting/formatting-xml.html:
     29        * inspector/formatting/resources/xml-tests/tag-case-expected.xml: Added.
     30        * inspector/formatting/resources/xml-tests/tag-case.xml: Added.
     31        * inspector/formatting/resources/xml-tests/valid-html-invalid-xml-expected.xml:
     32        * inspector/formatting/resources/xml-tests/valid-html-invalid-xml.xml:
     33        XML is case-sensitive. Ensure XML doesn't get more of the HTML specialties.
     34
    1352019-09-17  Antti Koivisto  <antti@apple.com>
    236
  • trunk/LayoutTests/inspector/formatting/formatting-html-expected.txt

    r249866 r249969  
    55-- Running test case: HTMLFormatter
    66PASS: attributes.html
    7 PASS: auto-close.html
     7PASS: auto-close-normal.html
     8PASS: auto-close-special.html
    89PASS: basic-1.html
    910PASS: basic-2.html
     
    2425PASS: not-well-formed-2.html
    2526PASS: not-well-formed-3.html
     27PASS: p.html
    2628PASS: self-closing.html
     29PASS: table.html
     30PASS: tag-case.html
    2731
  • trunk/LayoutTests/inspector/formatting/formatting-html.html

    r249866 r249969  
    1111    addFormattingTests(suite, "text/html", [
    1212        "resources/html-tests/attributes.html",
    13         "resources/html-tests/auto-close.html",
     13        "resources/html-tests/auto-close-normal.html",
     14        "resources/html-tests/auto-close-special.html",
    1415        "resources/html-tests/basic-1.html",
    1516        "resources/html-tests/basic-2.html",
     
    3031        "resources/html-tests/not-well-formed-2.html",
    3132        "resources/html-tests/not-well-formed-3.html",
     33        "resources/html-tests/p.html",
    3234        "resources/html-tests/self-closing.html",
     35        "resources/html-tests/table.html",
     36        "resources/html-tests/tag-case.html",
    3337    ]);
    3438
  • trunk/LayoutTests/inspector/formatting/formatting-xml-expected.txt

    r249867 r249969  
    77PASS: basic.xml
    88PASS: rss.xml
     9PASS: tag-case.xml
    910PASS: valid-html-invalid-xml.xml
    1011PASS: xslt.xml
  • trunk/LayoutTests/inspector/formatting/formatting-xml.html

    r249867 r249969  
    1313        "resources/xml-tests/basic.xml",
    1414        "resources/xml-tests/rss.xml",
     15        "resources/xml-tests/tag-case.xml",
    1516        "resources/xml-tests/valid-html-invalid-xml.xml",
    1617        "resources/xml-tests/xslt.xml",
  • trunk/LayoutTests/inspector/formatting/resources/html-tests/list-expected.html

    r249831 r249969  
    1111
    1212<ul>
     13    <li>One
     14    <li>Two
     15    <li>Three
     16</ul>
     17<ol>
     18    <li>One
     19    <li>Two
     20    <li>Three
     21</ol>
     22
     23<!-- Nested -->
     24<ol>
    1325    <li>
    14         One
    15         <li>
    16             Two
    17             <li>Three
     26        <ul>
     27            <li>1
     28            <li>2
     29        </ul>
     30    <li>Test
     31</ol>
     32<ul>
     33    <li>
     34        <ol>
     35            <li>1
     36            <li>2
     37        </ol>
     38    <li>Test
    1839</ul>
    1940<ol>
    2041    <li>
    21         One
    22         <li>
    23             Two
    24             <li>Three
     42        <ol>
     43            <li>1
     44            <li>2
     45        </ol>
     46    <li>Test
    2547</ol>
     48<ul>
     49    <li>
     50        <ul>
     51            <li>1
     52            <li>2
     53        </ul>
     54    <li>Test
     55</ul>
  • trunk/LayoutTests/inspector/formatting/resources/html-tests/list.html

    r249831 r249969  
    44<ul><li>One<li>Two<li>Three</ul>
    55<ol><li>One<li>Two<li>Three</ol>
     6
     7<!-- Nested -->
     8<ol><li><ul><li>1<li>2</ul><li>Test</ol>
     9<ul><li><ol><li>1<li>2</ol><li>Test</ul>
     10<ol><li><ol><li>1<li>2</ol><li>Test</ol>
     11<ul><li><ul><li>1<li>2</ul><li>Test</ul>
  • trunk/LayoutTests/inspector/formatting/resources/html-tests/not-well-formed-1-expected.html

    r249831 r249969  
    1 <p>
     1<x>
    22    </br>
    3     <p>Test</p>
     3    <x>Test</x>
  • trunk/LayoutTests/inspector/formatting/resources/html-tests/not-well-formed-1.html

    r249831 r249969  
    1 <p></br><p>Test</p>
     1<x></br><x>Test</x>
  • trunk/LayoutTests/inspector/formatting/resources/xml-tests/valid-html-invalid-xml-expected.xml

    r249867 r249969  
    22    <img src="1">
    33        <img src="2">
     4            <ol>
     5                <li>
     6                    One
     7                    <li>
     8                        Two
     9                        <li>Three
     10            </ol>
    411</outer>
  • trunk/LayoutTests/inspector/formatting/resources/xml-tests/valid-html-invalid-xml.xml

    r249867 r249969  
    1 <outer><img src="1"><img src="2"></outer>
     1<outer>
     2<img src="1"><img src="2">
     3<ol><li>One<li>Two<li>Three</ol>
     4</outer>
  • trunk/Source/WebInspectorUI/ChangeLog

    r249867 r249969  
     12019-09-17  Joseph Pecoraro  <pecoraro@apple.com>
     2
     3        Web Inspector: HTML Formatter - better handling for HTML specific tag cases (<p>/<li>)
     4        https://bugs.webkit.org/show_bug.cgi?id=201757
     5        <rdar://problem/55409987>
     6
     7        Reviewed by Devin Rousso.
     8
     9        * UserInterface/Workers/Formatter/HTMLFormatter.js:
     10        (HTMLFormatter.prototype._after):
     11        Handle a closing tag with different text than the opening tag.
     12
     13        * UserInterface/Workers/Formatter/HTMLTreeBuilderFormatter.js:
     14        (HTMLTreeBuilderFormatter.prototype._pushParserNodeTopLevel):
     15        (HTMLTreeBuilderFormatter.prototype._pushParserNodeStack):
     16        (HTMLTreeBuilderFormatter.prototype._implicitlyCloseHTMLNodesForOpenTag):
     17        (HTMLTreeBuilderFormatter.prototype._implicitlyCloseTagNamesInsideParentTagNames):
     18        (HTMLTreeBuilderFormatter.prototype._indexOfStackNodeMatchingTagNames):
     19        Generalize the implicit closing a bit. Allow open tags to implicitly
     20        close certain other open tags in the stack.
     21
    1222019-09-13  Joseph Pecoraro  <pecoraro@apple.com>
    223
  • trunk/Source/WebInspectorUI/UserInterface/Workers/Formatter/HTMLFormatter.js

    r249867 r249969  
    290290            }
    291291            if (!node.implicitClose) {
     292                console.assert(node.closeTagName);
    292293                console.assert(node.closeTagPos);
    293                 this._builder.appendToken("</" + node.name + ">", node.closeTagPos);
     294                this._builder.appendToken("</" + node.closeTagName + ">", node.closeTagPos);
    294295            }
    295296            this._builder.appendNewline();
  • trunk/Source/WebInspectorUI/UserInterface/Workers/Formatter/HTMLTreeBuilderFormatter.js

    r249867 r249969  
    2525
    2626// This tree builder attempts to match input text to output DOM node.
    27 // This therefore doesn't do HTML5 tree construction like auto-closing
     27// This therefore doesn't do HTML5 tree construction like implicitly-closing
    2828// specific HTML parent nodes depending on being in a particular node,
    29 // it only does basic auto-closing. In general this tries to be a
     29// it only does basic implicitly-closing. In general this tries to be a
    3030// whitespace reformatter for input text and not generate the ultimate
    3131// html tree that a browser would generate.
     
    5252    pushParserNode(parserNode)
    5353    {
    54         let containerNode = this._stackOfOpenElements[this._stackOfOpenElements.length - 1];
     54        let containerNode = this._stackOfOpenElements.lastValue;
    5555        if (!containerNode)
    5656            this._pushParserNodeTopLevel(parserNode);
     
    9191        if (parserNode.type === HTMLParser.NodeType.OpenTag) {
    9292            let node = this._buildDOMNodeFromOpenTag(parserNode);
    93             containerNode.children.push(node);
     93            let childrenArray = containerNode.children;
     94            if (!this._isXML) {
     95                this._implicitlyCloseHTMLNodesForOpenTag(parserNode, node);
     96                containerNode = this._stackOfOpenElements.lastValue;
     97                childrenArray = containerNode ? containerNode.children : this._dom;
     98            }
     99            childrenArray.push(node);
    94100            if (!this._isEmptyNode(parserNode, node))
    95101                this._stackOfOpenElements.push(node);
     
    98104
    99105        if (parserNode.type === HTMLParser.NodeType.CloseTag) {
    100             let found = false;
    101             let nodesToPop = 0;
    102             for (let i = this._stackOfOpenElements.length - 1; i >= 0; --i) {
    103                 nodesToPop++;
    104                 let stackNode = this._stackOfOpenElements[i];
    105                 if (stackNode.name === parserNode.name) {
    106                     found = true;
    107                     break;
     106            let tagName = this._isXML ? parserNode.name : parserNode.name.toLowerCase();
     107            let matchingOpenTagIndex = this._indexOfStackNodeMatchingTagNames([tagName]);
     108
     109            // Found a matching tag, implicitly-close nodes.
     110            if (matchingOpenTagIndex !== -1) {
     111                let nodesToPop = this._stackOfOpenElements.length - matchingOpenTagIndex;
     112                for (let i = 0; i < nodesToPop - 1; ++i) {
     113                    let implicitlyClosingNode = this._stackOfOpenElements.pop();
     114                    implicitlyClosingNode.implicitClose = true;
    108115                }
    109             }
    110 
    111             // Found a matching tag, auto-close nodes.
    112             if (found) {
    113                 console.assert(nodesToPop > 0);
    114                 for (let i = 0; i < nodesToPop - 1; ++i) {
    115                     let autoClosingNode = this._stackOfOpenElements.pop();
    116                     autoClosingNode.implicitClose = true;
     116                let implicitlyClosingNode = this._stackOfOpenElements.pop();
     117                if (parserNode.pos) {
     118                    implicitlyClosingNode.closeTagPos = parserNode.pos;
     119                    implicitlyClosingNode.closeTagName = parserNode.name;
    117120                }
    118                 let autoClosingNode = this._stackOfOpenElements.pop();
    119                 if (parserNode.pos)
    120                     autoClosingNode.closeTagPos = parserNode.pos;
    121121                return;
    122122            }
     
    131131        let node = this._buildSimpleNodeFromParserNode(parserNode);
    132132        containerNode.children.push(node);
     133    }
     134
     135    _implicitlyCloseHTMLNodesForOpenTag(parserNode, node)
     136    {
     137        if (parserNode.closed)
     138            return;
     139
     140        switch (node.lowercaseName) {
     141        // <body> closes <head>.
     142        case "body":
     143            this._implicitlyCloseTagNamesInsideParentTagNames(["head"]);
     144            break;
     145
     146        // Inside <select>.
     147        case "option":
     148            this._implicitlyCloseTagNamesInsideParentTagNames(["option"], ["select"]);
     149            break;
     150        case "optgroup": {
     151            let didClose = this._implicitlyCloseTagNamesInsideParentTagNames(["optgroup"], ["select"]);;
     152            if (!didClose)
     153                this._implicitlyCloseTagNamesInsideParentTagNames(["option"], ["select"]);
     154            break;
     155        }
     156
     157        // Inside <ol>/<ul>.
     158        case "li":
     159            this._implicitlyCloseTagNamesInsideParentTagNames(["li"], ["ol", "ul"]);
     160            break;
     161
     162        // Inside <dl>.
     163        case "dd":
     164        case "dt":
     165            this._implicitlyCloseTagNamesInsideParentTagNames(["dd", "dt"], ["dl"]);
     166            break;
     167
     168        // Inside <table>.
     169        case "tr": {
     170            let didClose = this._implicitlyCloseTagNamesInsideParentTagNames(["tr"], ["table"]);
     171            if (!didClose)
     172                this._implicitlyCloseTagNamesInsideParentTagNames(["td", "th"], ["table"]);
     173            break;
     174        }
     175        case "td":
     176        case "th":
     177            this._implicitlyCloseTagNamesInsideParentTagNames(["td", "th"], ["table"]);
     178            break;
     179        case "tbody": {
     180            let didClose = this._implicitlyCloseTagNamesInsideParentTagNames(["thead"], ["table"]);
     181            if (!didClose)
     182                didClose = this._implicitlyCloseTagNamesInsideParentTagNames(["tr"], ["table"]);
     183            break;
     184        }
     185        case "tfoot": {
     186            let didClose = this._implicitlyCloseTagNamesInsideParentTagNames(["tbody"], ["table"]);
     187            if (!didClose)
     188                didClose = this._implicitlyCloseTagNamesInsideParentTagNames(["tr"], ["table"]);
     189            break;
     190        }
     191        case "colgroup":
     192            this._implicitlyCloseTagNamesInsideParentTagNames(["colgroup"], ["table"]);
     193            break;
     194
     195        // Nodes that implicitly close a <p>. Normally this is only in <body> but we simplify to always.
     196        // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inbody
     197        case "address":
     198        case "article":
     199        case "aside":
     200        case "blockquote":
     201        case "center":
     202        case "details":
     203        case "dialog":
     204        case "dir":
     205        case "div":
     206        case "dl":
     207        case "fieldset":
     208        case "figcaption":
     209        case "figure":
     210        case "footer":
     211        case "form":
     212        case "h1":
     213        case "h2":
     214        case "h3":
     215        case "h4":
     216        case "h5":
     217        case "h6":
     218        case "header":
     219        case "hgroup":
     220        case "hr":
     221        case "listing":
     222        case "main":
     223        case "menu":
     224        case "nav":
     225        case "ol":
     226        case "p":
     227        case "plaintext":
     228        case "pre":
     229        case "section":
     230        case "summary":
     231        case "table":
     232        case "ul":
     233        case "xmp":
     234            this._implicitlyCloseTagNamesInsideParentTagNames(["p"]);
     235            break;
     236        }
     237    }
     238
     239    _implicitlyCloseTagNamesInsideParentTagNames(tagNames, containerScopeTagNames)
     240    {
     241        console.assert(!this._isXML, "Implicitly closing only happens in HTML. Also, names are compared case insensitively which would be invalid for XML.");
     242
     243        let existingOpenTagIndex = this._indexOfStackNodeMatchingTagNames(tagNames);
     244        if (existingOpenTagIndex === -1)
     245            return false;
     246
     247        // Disallow impliticly closing beyond the container tag boundary.
     248        if (containerScopeTagNames) {
     249            for (let i = existingOpenTagIndex + 1; i < this._stackOfOpenElements.length; ++i) {
     250                let stackNode = this._stackOfOpenElements[i];
     251                let name = stackNode.lowercaseName;
     252                if (containerScopeTagNames.includes(name))
     253                    return false;
     254            }
     255        }
     256
     257        // Implicitly close tags.
     258        let nodesToPop = this._stackOfOpenElements.length - existingOpenTagIndex;
     259        for (let i = 0; i < nodesToPop; ++i) {
     260            let implicitlyClosingNode = this._stackOfOpenElements.pop();
     261            implicitlyClosingNode.implicitClose = true;
     262        }
     263
     264        return true;
     265    }
     266
     267    _indexOfStackNodeMatchingTagNames(tagNames)
     268    {
     269        for (let i = this._stackOfOpenElements.length - 1; i >= 0; --i) {
     270            let stackNode = this._stackOfOpenElements[i];
     271            let name = this._isXML ? stackNode.name : stackNode.lowercaseName;
     272            if (tagNames.includes(name))
     273                return i;
     274        }
     275
     276        return -1;
    133277    }
    134278
Note: See TracChangeset for help on using the changeset viewer.