| Index: third_party/WebKit/Source/devtools/front_end/formatter_worker/HTMLFormatter.js
|
| diff --git a/third_party/WebKit/Source/devtools/front_end/formatter_worker/HTMLFormatter.js b/third_party/WebKit/Source/devtools/front_end/formatter_worker/HTMLFormatter.js
|
| index 0ae831f6f3d52f434396e92837d7158e25245b6f..23590a81ef799bede1803d6b983fce726883e88d 100644
|
| --- a/third_party/WebKit/Source/devtools/front_end/formatter_worker/HTMLFormatter.js
|
| +++ b/third_party/WebKit/Source/devtools/front_end/formatter_worker/HTMLFormatter.js
|
| @@ -1,468 +1,451 @@
|
| // Copyright 2016 The Chromium Authors. All rights reserved.
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
| -
|
| /**
|
| - * @constructor
|
| - * @param {!WebInspector.FormattedContentBuilder} builder
|
| + * @unrestricted
|
| */
|
| -WebInspector.HTMLFormatter = function(builder)
|
| -{
|
| +WebInspector.HTMLFormatter = class {
|
| + /**
|
| + * @param {!WebInspector.FormattedContentBuilder} builder
|
| + */
|
| + constructor(builder) {
|
| this._builder = builder;
|
| this._jsFormatter = new WebInspector.JavaScriptFormatter(builder);
|
| this._cssFormatter = new WebInspector.CSSFormatter(builder);
|
| -};
|
| -
|
| -WebInspector.HTMLFormatter.SupportedJavaScriptMimeTypes = new Set([
|
| - "text/javascript",
|
| - "text/ecmascript",
|
| - "application/javascript",
|
| - "application/ecmascript"
|
| -]);
|
| -
|
| -WebInspector.HTMLFormatter.prototype = {
|
| - /**
|
| - * @param {string} text
|
| - * @param {!Array<number>} lineEndings
|
| - */
|
| - format: function(text, lineEndings)
|
| - {
|
| - this._text = text;
|
| - this._lineEndings = lineEndings;
|
| - this._model = new WebInspector.HTMLModel(text);
|
| - this._walk(this._model.document());
|
| - },
|
| -
|
| - /**
|
| - * @param {!WebInspector.HTMLModel.Element} element
|
| - * @param {number} offset
|
| - */
|
| - _formatTokensTill: function(element, offset)
|
| - {
|
| - while (this._model.peekToken() && this._model.peekToken().startOffset < offset) {
|
| - var token = this._model.nextToken();
|
| - this._formatToken(element, token);
|
| - }
|
| - },
|
| -
|
| - /**
|
| - * @param {!WebInspector.HTMLModel.Element} element
|
| - */
|
| - _walk: function(element)
|
| - {
|
| - if (element.parent)
|
| - this._formatTokensTill(element.parent, element.openTag.startOffset);
|
| - this._beforeOpenTag(element);
|
| - this._formatTokensTill(element, element.openTag.endOffset);
|
| - this._afterOpenTag(element);
|
| - for (var i = 0; i < element.children.length; ++i)
|
| - this._walk(element.children[i]);
|
| -
|
| - this._formatTokensTill(element, element.closeTag.startOffset);
|
| - this._beforeCloseTag(element);
|
| - this._formatTokensTill(element, element.closeTag.endOffset);
|
| - this._afterCloseTag(element);
|
| - },
|
| -
|
| - /**
|
| - * @param {!WebInspector.HTMLModel.Element} element
|
| - */
|
| - _beforeOpenTag: function(element)
|
| - {
|
| - if (!element.children.length || element === this._model.document())
|
| - return;
|
| - this._builder.addNewLine();
|
| - },
|
| -
|
| - /**
|
| - * @param {!WebInspector.HTMLModel.Element} element
|
| - */
|
| - _afterOpenTag: function(element)
|
| - {
|
| - if (!element.children.length || element === this._model.document())
|
| - return;
|
| - this._builder.increaseNestingLevel();
|
| - this._builder.addNewLine();
|
| - },
|
| -
|
| - /**
|
| - * @param {!WebInspector.HTMLModel.Element} element
|
| - */
|
| - _beforeCloseTag: function(element)
|
| - {
|
| - if (!element.children.length || element === this._model.document())
|
| - return;
|
| - this._builder.decreaseNestingLevel();
|
| - this._builder.addNewLine();
|
| - },
|
| + }
|
| +
|
| + /**
|
| + * @param {string} text
|
| + * @param {!Array<number>} lineEndings
|
| + */
|
| + format(text, lineEndings) {
|
| + this._text = text;
|
| + this._lineEndings = lineEndings;
|
| + this._model = new WebInspector.HTMLModel(text);
|
| + this._walk(this._model.document());
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.HTMLModel.Element} element
|
| + * @param {number} offset
|
| + */
|
| + _formatTokensTill(element, offset) {
|
| + while (this._model.peekToken() && this._model.peekToken().startOffset < offset) {
|
| + var token = this._model.nextToken();
|
| + this._formatToken(element, token);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.HTMLModel.Element} element
|
| + */
|
| + _walk(element) {
|
| + if (element.parent)
|
| + this._formatTokensTill(element.parent, element.openTag.startOffset);
|
| + this._beforeOpenTag(element);
|
| + this._formatTokensTill(element, element.openTag.endOffset);
|
| + this._afterOpenTag(element);
|
| + for (var i = 0; i < element.children.length; ++i)
|
| + this._walk(element.children[i]);
|
| +
|
| + this._formatTokensTill(element, element.closeTag.startOffset);
|
| + this._beforeCloseTag(element);
|
| + this._formatTokensTill(element, element.closeTag.endOffset);
|
| + this._afterCloseTag(element);
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.HTMLModel.Element} element
|
| + */
|
| + _beforeOpenTag(element) {
|
| + if (!element.children.length || element === this._model.document())
|
| + return;
|
| + this._builder.addNewLine();
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.HTMLModel.Element} element
|
| + */
|
| + _afterOpenTag(element) {
|
| + if (!element.children.length || element === this._model.document())
|
| + return;
|
| + this._builder.increaseNestingLevel();
|
| + this._builder.addNewLine();
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.HTMLModel.Element} element
|
| + */
|
| + _beforeCloseTag(element) {
|
| + if (!element.children.length || element === this._model.document())
|
| + return;
|
| + this._builder.decreaseNestingLevel();
|
| + this._builder.addNewLine();
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.HTMLModel.Element} element
|
| + */
|
| + _afterCloseTag(element) {
|
| + this._builder.addNewLine();
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.HTMLModel.Element} element
|
| + * @param {!WebInspector.HTMLModel.Token} token
|
| + */
|
| + _formatToken(element, token) {
|
| + if (token.value.isWhitespace())
|
| + return;
|
| + if (token.type.has('comment') || token.type.has('meta')) {
|
| + this._builder.addNewLine();
|
| + this._builder.addToken(token.value.trim(), token.startOffset);
|
| + this._builder.addNewLine();
|
| + return;
|
| + }
|
|
|
| - /**
|
| - * @param {!WebInspector.HTMLModel.Element} element
|
| - */
|
| - _afterCloseTag: function(element)
|
| - {
|
| + var isBodyToken =
|
| + element.openTag.endOffset <= token.startOffset && token.startOffset < element.closeTag.startOffset;
|
| + if (isBodyToken && element.name === 'style') {
|
| + this._builder.addNewLine();
|
| + this._builder.increaseNestingLevel();
|
| + this._cssFormatter.format(this._text, this._lineEndings, token.startOffset, token.endOffset);
|
| + this._builder.decreaseNestingLevel();
|
| + return;
|
| + }
|
| + if (isBodyToken && element.name === 'script') {
|
| + this._builder.addNewLine();
|
| + this._builder.increaseNestingLevel();
|
| + var mimeType =
|
| + element.openTag.attributes.has('type') ? element.openTag.attributes.get('type').toLowerCase() : null;
|
| + if (!mimeType || WebInspector.HTMLFormatter.SupportedJavaScriptMimeTypes.has(mimeType)) {
|
| + this._jsFormatter.format(this._text, this._lineEndings, token.startOffset, token.endOffset);
|
| + } else {
|
| + this._builder.addToken(token.value, token.startOffset);
|
| this._builder.addNewLine();
|
| - },
|
| -
|
| - /**
|
| - * @param {!WebInspector.HTMLModel.Element} element
|
| - * @param {!WebInspector.HTMLModel.Token} token
|
| - */
|
| - _formatToken: function(element, token)
|
| - {
|
| - if (token.value.isWhitespace())
|
| - return;
|
| - if (token.type.has("comment") || token.type.has("meta")) {
|
| - this._builder.addNewLine();
|
| - this._builder.addToken(token.value.trim(), token.startOffset);
|
| - this._builder.addNewLine();
|
| - return;
|
| - }
|
| -
|
| - var isBodyToken = element.openTag.endOffset <= token.startOffset && token.startOffset < element.closeTag.startOffset;
|
| - if (isBodyToken && element.name === "style") {
|
| - this._builder.addNewLine();
|
| - this._builder.increaseNestingLevel();
|
| - this._cssFormatter.format(this._text, this._lineEndings, token.startOffset, token.endOffset);
|
| - this._builder.decreaseNestingLevel();
|
| - return;
|
| - }
|
| - if (isBodyToken && element.name === "script") {
|
| - this._builder.addNewLine();
|
| - this._builder.increaseNestingLevel();
|
| - var mimeType = element.openTag.attributes.has("type") ? element.openTag.attributes.get("type").toLowerCase() : null;
|
| - if (!mimeType || WebInspector.HTMLFormatter.SupportedJavaScriptMimeTypes.has(mimeType)) {
|
| - this._jsFormatter.format(this._text, this._lineEndings, token.startOffset, token.endOffset);
|
| - } else {
|
| - this._builder.addToken(token.value, token.startOffset);
|
| - this._builder.addNewLine();
|
| - }
|
| - this._builder.decreaseNestingLevel();
|
| - return;
|
| - }
|
| + }
|
| + this._builder.decreaseNestingLevel();
|
| + return;
|
| + }
|
|
|
| - if (!isBodyToken && token.type.has("attribute"))
|
| - this._builder.addSoftSpace();
|
| + if (!isBodyToken && token.type.has('attribute'))
|
| + this._builder.addSoftSpace();
|
|
|
| - this._builder.addToken(token.value, token.startOffset);
|
| - }
|
| + this._builder.addToken(token.value, token.startOffset);
|
| + }
|
| };
|
|
|
| +WebInspector.HTMLFormatter.SupportedJavaScriptMimeTypes =
|
| + new Set(['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript']);
|
| +
|
| /**
|
| - * @constructor
|
| - * @param {string} text
|
| + * @unrestricted
|
| */
|
| -WebInspector.HTMLModel = function(text)
|
| -{
|
| +WebInspector.HTMLModel = class {
|
| + /**
|
| + * @param {string} text
|
| + */
|
| + constructor(text) {
|
| this._state = WebInspector.HTMLModel.ParseState.Initial;
|
| - this._document = new WebInspector.HTMLModel.Element("document");
|
| - this._document.openTag = new WebInspector.HTMLModel.Tag("document", 0, 0, new Map(), true, false);
|
| - this._document.closeTag = new WebInspector.HTMLModel.Tag("document", text.length, text.length, new Map(), false, false);
|
| + this._document = new WebInspector.HTMLModel.Element('document');
|
| + this._document.openTag = new WebInspector.HTMLModel.Tag('document', 0, 0, new Map(), true, false);
|
| + this._document.closeTag =
|
| + new WebInspector.HTMLModel.Tag('document', text.length, text.length, new Map(), false, false);
|
|
|
| this._stack = [this._document];
|
|
|
| this._tokens = [];
|
| this._tokenIndex = 0;
|
| this._build(text);
|
| -};
|
| -
|
| -WebInspector.HTMLModel.SelfClosingTags = new Set([
|
| - "area",
|
| - "base",
|
| - "br",
|
| - "col",
|
| - "command",
|
| - "embed",
|
| - "hr",
|
| - "img",
|
| - "input",
|
| - "keygen",
|
| - "link",
|
| - "meta",
|
| - "param",
|
| - "source",
|
| - "track",
|
| - "wbr"
|
| -]);
|
| -
|
| -// @see https://www.w3.org/TR/html/syntax.html 8.1.2.4 Optional tags
|
| -WebInspector.HTMLModel.AutoClosingTags = {
|
| - "head": new Set(["body"]),
|
| - "li": new Set(["li"]),
|
| - "dt": new Set(["dt", "dd"]),
|
| - "dd": new Set(["dt", "dd"]),
|
| - "p": new Set(["address", "article", "aside", "blockquote", "div", "dl", "fieldset", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hgroup", "hr", "main", "nav", "ol", "p", "pre", "section", "table", "ul"]),
|
| - "rb": new Set(["rb", "rt", "rtc", "rp"]),
|
| - "rt": new Set(["rb", "rt", "rtc", "rp"]),
|
| - "rtc": new Set(["rb", "rtc", "rp"]),
|
| - "rp": new Set(["rb", "rt", "rtc", "rp"]),
|
| - "optgroup": new Set(["optgroup"]),
|
| - "option": new Set(["option", "optgroup"]),
|
| - "colgroup": new Set(["colgroup"]),
|
| - "thead": new Set(["tbody", "tfoot"]),
|
| - "tbody": new Set(["tbody", "tfoot"]),
|
| - "tfoot": new Set(["tbody"]),
|
| - "tr": new Set(["tr"]),
|
| - "td": new Set(["td", "th"]),
|
| - "th": new Set(["td", "th"]),
|
| -};
|
| + }
|
| +
|
| + /**
|
| + * @param {string} text
|
| + */
|
| + _build(text) {
|
| + var tokenizer = WebInspector.createTokenizer('text/html');
|
| + var lastOffset = 0;
|
| + var lowerCaseText = text.toLowerCase();
|
| +
|
| + while (true) {
|
| + tokenizer(text.substring(lastOffset), processToken.bind(this, lastOffset));
|
| + if (lastOffset >= text.length)
|
| + break;
|
| + var element = this._stack.peekLast();
|
| + lastOffset = lowerCaseText.indexOf('</' + element.name, lastOffset);
|
| + if (lastOffset === -1)
|
| + lastOffset = text.length;
|
| + var tokenStart = element.openTag.endOffset;
|
| + var tokenEnd = lastOffset;
|
| + var tokenValue = text.substring(tokenStart, tokenEnd);
|
| + this._tokens.push(new WebInspector.HTMLModel.Token(tokenValue, new Set(), tokenStart, tokenEnd));
|
| + }
|
|
|
| -/** @enum {string} */
|
| -WebInspector.HTMLModel.ParseState = {
|
| - Initial: "Initial",
|
| - Tag: "Tag",
|
| - AttributeName: "AttributeName",
|
| - AttributeValue: "AttributeValue"
|
| -};
|
| + while (this._stack.length > 1) {
|
| + var element = this._stack.peekLast();
|
| + this._popElement(new WebInspector.HTMLModel.Tag(element.name, text.length, text.length, new Map(), false, false));
|
| + }
|
|
|
| -WebInspector.HTMLModel.prototype = {
|
| /**
|
| - * @param {string} text
|
| + * @param {number} baseOffset
|
| + * @param {string} tokenValue
|
| + * @param {?string} type
|
| + * @param {number} tokenStart
|
| + * @param {number} tokenEnd
|
| + * @return {(!Object|undefined)}
|
| + * @this {WebInspector.HTMLModel}
|
| */
|
| - _build: function(text)
|
| - {
|
| - var tokenizer = WebInspector.createTokenizer("text/html");
|
| - var lastOffset = 0;
|
| - var lowerCaseText = text.toLowerCase();
|
| -
|
| - while (true) {
|
| - tokenizer(text.substring(lastOffset), processToken.bind(this, lastOffset));
|
| - if (lastOffset >= text.length)
|
| - break;
|
| - var element = this._stack.peekLast();
|
| - lastOffset = lowerCaseText.indexOf("</" + element.name, lastOffset);
|
| - if (lastOffset === -1)
|
| - lastOffset = text.length;
|
| - var tokenStart = element.openTag.endOffset;
|
| - var tokenEnd = lastOffset;
|
| - var tokenValue = text.substring(tokenStart, tokenEnd);
|
| - this._tokens.push(new WebInspector.HTMLModel.Token(tokenValue, new Set(), tokenStart, tokenEnd));
|
| - }
|
| -
|
| - while (this._stack.length > 1) {
|
| - var element = this._stack.peekLast();
|
| - this._popElement(new WebInspector.HTMLModel.Tag(element.name, text.length, text.length, new Map(), false, false));
|
| - }
|
| -
|
| - /**
|
| - * @param {number} baseOffset
|
| - * @param {string} tokenValue
|
| - * @param {?string} type
|
| - * @param {number} tokenStart
|
| - * @param {number} tokenEnd
|
| - * @return {(!Object|undefined)}
|
| - * @this {WebInspector.HTMLModel}
|
| - */
|
| - function processToken(baseOffset, tokenValue, type, tokenStart, tokenEnd)
|
| - {
|
| - tokenStart += baseOffset;
|
| - tokenEnd += baseOffset;
|
| - lastOffset = tokenEnd;
|
| -
|
| - var tokenType = type ? new Set(type.split(" ")) : new Set();
|
| - var token = new WebInspector.HTMLModel.Token(tokenValue, tokenType, tokenStart, tokenEnd);
|
| - this._tokens.push(token);
|
| - this._updateDOM(token);
|
| -
|
| - var element = this._stack.peekLast();
|
| - if (element && (element.name === "script" || element.name === "style") && element.openTag.endOffset === lastOffset)
|
| - return WebInspector.AbortTokenization;
|
| + function processToken(baseOffset, tokenValue, type, tokenStart, tokenEnd) {
|
| + tokenStart += baseOffset;
|
| + tokenEnd += baseOffset;
|
| + lastOffset = tokenEnd;
|
| +
|
| + var tokenType = type ? new Set(type.split(' ')) : new Set();
|
| + var token = new WebInspector.HTMLModel.Token(tokenValue, tokenType, tokenStart, tokenEnd);
|
| + this._tokens.push(token);
|
| + this._updateDOM(token);
|
| +
|
| + var element = this._stack.peekLast();
|
| + if (element && (element.name === 'script' || element.name === 'style') &&
|
| + element.openTag.endOffset === lastOffset)
|
| + return WebInspector.AbortTokenization;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.HTMLModel.Token} token
|
| + */
|
| + _updateDOM(token) {
|
| + var S = WebInspector.HTMLModel.ParseState;
|
| + var value = token.value;
|
| + var type = token.type;
|
| + switch (this._state) {
|
| + case S.Initial:
|
| + if (type.has('bracket') && (value === '<' || value === '</')) {
|
| + this._onStartTag(token);
|
| + this._state = S.Tag;
|
| }
|
| - },
|
| -
|
| - /**
|
| - * @param {!WebInspector.HTMLModel.Token} token
|
| - */
|
| - _updateDOM: function(token)
|
| - {
|
| - var S = WebInspector.HTMLModel.ParseState;
|
| - var value = token.value;
|
| - var type = token.type;
|
| - switch (this._state) {
|
| - case S.Initial:
|
| - if (type.has("bracket") && (value === "<" || value === "</")) {
|
| - this._onStartTag(token);
|
| - this._state = S.Tag;
|
| - }
|
| - return;
|
| - case S.Tag:
|
| - if (type.has("tag") && !type.has("bracket")) {
|
| - this._tagName = value.trim().toLowerCase();
|
| - } else if (type.has("attribute")) {
|
| - this._attributeName = value.trim().toLowerCase();
|
| - this._attributes.set(this._attributeName, "");
|
| - this._state = S.AttributeName;
|
| - } else if (type.has("bracket") && (value === ">" || value === "/>")) {
|
| - this._onEndTag(token);
|
| - this._state = S.Initial;
|
| - }
|
| - return;
|
| - case S.AttributeName:
|
| - if (!type.size && value === "=") {
|
| - this._state = S.AttributeValue;
|
| - } else if (type.has("bracket") && (value === ">" || value === "/>")) {
|
| - this._onEndTag(token);
|
| - this._state = S.Initial;
|
| - }
|
| - return;
|
| - case S.AttributeValue:
|
| - if (type.has("string")) {
|
| - this._attributes.set(this._attributeName, value);
|
| - this._state = S.Tag;
|
| - } else if (type.has("bracket") && (value === ">" || value === "/>")) {
|
| - this._onEndTag(token);
|
| - this._state = S.Initial;
|
| - }
|
| - return;
|
| + return;
|
| + case S.Tag:
|
| + if (type.has('tag') && !type.has('bracket')) {
|
| + this._tagName = value.trim().toLowerCase();
|
| + } else if (type.has('attribute')) {
|
| + this._attributeName = value.trim().toLowerCase();
|
| + this._attributes.set(this._attributeName, '');
|
| + this._state = S.AttributeName;
|
| + } else if (type.has('bracket') && (value === '>' || value === '/>')) {
|
| + this._onEndTag(token);
|
| + this._state = S.Initial;
|
| }
|
| - },
|
| -
|
| - /**
|
| - * @param {!WebInspector.HTMLModel.Token} token
|
| - */
|
| - _onStartTag: function(token)
|
| - {
|
| - this._tagName = "";
|
| - this._tagStartOffset = token.startOffset;
|
| - this._tagEndOffset = null;
|
| - this._attributes = new Map();
|
| - this._attributeName = "";
|
| - this._isOpenTag = token.value === "<";
|
| - },
|
| -
|
| - /**
|
| - * @param {!WebInspector.HTMLModel.Token} token
|
| - */
|
| - _onEndTag: function(token)
|
| - {
|
| - this._tagEndOffset = token.endOffset;
|
| - var selfClosingTag = token.value === "/>" || WebInspector.HTMLModel.SelfClosingTags.has(this._tagName);
|
| - var tag = new WebInspector.HTMLModel.Tag(this._tagName, this._tagStartOffset, this._tagEndOffset, this._attributes, this._isOpenTag, selfClosingTag);
|
| - this._onTagComplete(tag);
|
| - },
|
| -
|
| - /**
|
| - * @param {!WebInspector.HTMLModel.Tag} tag
|
| - */
|
| - _onTagComplete: function(tag)
|
| - {
|
| - if (tag.isOpenTag) {
|
| - var topElement = this._stack.peekLast();
|
| - if (topElement !== this._document && topElement.openTag.selfClosingTag)
|
| - this._popElement(autocloseTag(topElement, topElement.openTag.endOffset));
|
| - else if ((topElement.name in WebInspector.HTMLModel.AutoClosingTags) && WebInspector.HTMLModel.AutoClosingTags[topElement.name].has(tag.name))
|
| - this._popElement(autocloseTag(topElement, tag.startOffset));
|
| - this._pushElement(tag);
|
| - return;
|
| + return;
|
| + case S.AttributeName:
|
| + if (!type.size && value === '=') {
|
| + this._state = S.AttributeValue;
|
| + } else if (type.has('bracket') && (value === '>' || value === '/>')) {
|
| + this._onEndTag(token);
|
| + this._state = S.Initial;
|
| }
|
| -
|
| - while (this._stack.length > 1 && this._stack.peekLast().name !== tag.name)
|
| - this._popElement(autocloseTag(this._stack.peekLast(), tag.startOffset));
|
| - if (this._stack.length === 1)
|
| - return;
|
| - this._popElement(tag);
|
| -
|
| - /**
|
| - * @param {!WebInspector.HTMLModel.Element} element
|
| - * @param {number} offset
|
| - * @return {!WebInspector.HTMLModel.Tag}
|
| - */
|
| - function autocloseTag(element, offset)
|
| - {
|
| - return new WebInspector.HTMLModel.Tag(element.name, offset, offset, new Map(), false, false);
|
| + return;
|
| + case S.AttributeValue:
|
| + if (type.has('string')) {
|
| + this._attributes.set(this._attributeName, value);
|
| + this._state = S.Tag;
|
| + } else if (type.has('bracket') && (value === '>' || value === '/>')) {
|
| + this._onEndTag(token);
|
| + this._state = S.Initial;
|
| }
|
| - },
|
| + return;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.HTMLModel.Token} token
|
| + */
|
| + _onStartTag(token) {
|
| + this._tagName = '';
|
| + this._tagStartOffset = token.startOffset;
|
| + this._tagEndOffset = null;
|
| + this._attributes = new Map();
|
| + this._attributeName = '';
|
| + this._isOpenTag = token.value === '<';
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.HTMLModel.Token} token
|
| + */
|
| + _onEndTag(token) {
|
| + this._tagEndOffset = token.endOffset;
|
| + var selfClosingTag = token.value === '/>' || WebInspector.HTMLModel.SelfClosingTags.has(this._tagName);
|
| + var tag = new WebInspector.HTMLModel.Tag(
|
| + this._tagName, this._tagStartOffset, this._tagEndOffset, this._attributes, this._isOpenTag, selfClosingTag);
|
| + this._onTagComplete(tag);
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.HTMLModel.Tag} tag
|
| + */
|
| + _onTagComplete(tag) {
|
| + if (tag.isOpenTag) {
|
| + var topElement = this._stack.peekLast();
|
| + if (topElement !== this._document && topElement.openTag.selfClosingTag)
|
| + this._popElement(autocloseTag(topElement, topElement.openTag.endOffset));
|
| + else if (
|
| + (topElement.name in WebInspector.HTMLModel.AutoClosingTags) &&
|
| + WebInspector.HTMLModel.AutoClosingTags[topElement.name].has(tag.name))
|
| + this._popElement(autocloseTag(topElement, tag.startOffset));
|
| + this._pushElement(tag);
|
| + return;
|
| + }
|
|
|
| - /**
|
| - * @param {!WebInspector.HTMLModel.Tag} closeTag
|
| - */
|
| - _popElement: function(closeTag)
|
| - {
|
| - var element = this._stack.pop();
|
| - element.closeTag = closeTag;
|
| - },
|
| + while (this._stack.length > 1 && this._stack.peekLast().name !== tag.name)
|
| + this._popElement(autocloseTag(this._stack.peekLast(), tag.startOffset));
|
| + if (this._stack.length === 1)
|
| + return;
|
| + this._popElement(tag);
|
|
|
| /**
|
| - * @param {!WebInspector.HTMLModel.Tag} openTag
|
| + * @param {!WebInspector.HTMLModel.Element} element
|
| + * @param {number} offset
|
| + * @return {!WebInspector.HTMLModel.Tag}
|
| */
|
| - _pushElement: function(openTag)
|
| - {
|
| - var topElement = this._stack.peekLast();
|
| - var newElement = new WebInspector.HTMLModel.Element(openTag.name);
|
| - newElement.parent = topElement;
|
| - topElement.children.push(newElement);
|
| - newElement.openTag = openTag;
|
| - this._stack.push(newElement);
|
| - },
|
| + function autocloseTag(element, offset) {
|
| + return new WebInspector.HTMLModel.Tag(element.name, offset, offset, new Map(), false, false);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.HTMLModel.Tag} closeTag
|
| + */
|
| + _popElement(closeTag) {
|
| + var element = this._stack.pop();
|
| + element.closeTag = closeTag;
|
| + }
|
| +
|
| + /**
|
| + * @param {!WebInspector.HTMLModel.Tag} openTag
|
| + */
|
| + _pushElement(openTag) {
|
| + var topElement = this._stack.peekLast();
|
| + var newElement = new WebInspector.HTMLModel.Element(openTag.name);
|
| + newElement.parent = topElement;
|
| + topElement.children.push(newElement);
|
| + newElement.openTag = openTag;
|
| + this._stack.push(newElement);
|
| + }
|
| +
|
| + /**
|
| + * @return {?WebInspector.HTMLModel.Token}
|
| + */
|
| + peekToken() {
|
| + return this._tokenIndex < this._tokens.length ? this._tokens[this._tokenIndex] : null;
|
| + }
|
| +
|
| + /**
|
| + * @return {?WebInspector.HTMLModel.Token}
|
| + */
|
| + nextToken() {
|
| + return this._tokens[this._tokenIndex++];
|
| + }
|
| +
|
| + /**
|
| + * @return {!WebInspector.HTMLModel.Element}
|
| + */
|
| + document() {
|
| + return this._document;
|
| + }
|
| +};
|
|
|
| - /**
|
| - * @return {?WebInspector.HTMLModel.Token}
|
| - */
|
| - peekToken: function()
|
| - {
|
| - return this._tokenIndex < this._tokens.length ? this._tokens[this._tokenIndex] : null;
|
| - },
|
| +WebInspector.HTMLModel.SelfClosingTags = new Set([
|
| + 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source',
|
| + 'track', 'wbr'
|
| +]);
|
|
|
| - /**
|
| - * @return {?WebInspector.HTMLModel.Token}
|
| - */
|
| - nextToken: function()
|
| - {
|
| - return this._tokens[this._tokenIndex++];
|
| - },
|
| +// @see https://www.w3.org/TR/html/syntax.html 8.1.2.4 Optional tags
|
| +WebInspector.HTMLModel.AutoClosingTags = {
|
| + 'head': new Set(['body']),
|
| + 'li': new Set(['li']),
|
| + 'dt': new Set(['dt', 'dd']),
|
| + 'dd': new Set(['dt', 'dd']),
|
| + 'p': new Set([
|
| + 'address', 'article', 'aside', 'blockquote', 'div', 'dl', 'fieldset', 'footer', 'form',
|
| + 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr',
|
| + 'main', 'nav', 'ol', 'p', 'pre', 'section', 'table', 'ul'
|
| + ]),
|
| + 'rb': new Set(['rb', 'rt', 'rtc', 'rp']),
|
| + 'rt': new Set(['rb', 'rt', 'rtc', 'rp']),
|
| + 'rtc': new Set(['rb', 'rtc', 'rp']),
|
| + 'rp': new Set(['rb', 'rt', 'rtc', 'rp']),
|
| + 'optgroup': new Set(['optgroup']),
|
| + 'option': new Set(['option', 'optgroup']),
|
| + 'colgroup': new Set(['colgroup']),
|
| + 'thead': new Set(['tbody', 'tfoot']),
|
| + 'tbody': new Set(['tbody', 'tfoot']),
|
| + 'tfoot': new Set(['tbody']),
|
| + 'tr': new Set(['tr']),
|
| + 'td': new Set(['td', 'th']),
|
| + 'th': new Set(['td', 'th']),
|
| +};
|
|
|
| - /**
|
| - * @return {!WebInspector.HTMLModel.Element}
|
| - */
|
| - document: function()
|
| - {
|
| - return this._document;
|
| - }
|
| +/** @enum {string} */
|
| +WebInspector.HTMLModel.ParseState = {
|
| + Initial: 'Initial',
|
| + Tag: 'Tag',
|
| + AttributeName: 'AttributeName',
|
| + AttributeValue: 'AttributeValue'
|
| };
|
|
|
| /**
|
| - * @constructor
|
| - * @param {string} value
|
| - * @param {!Set<string>} type
|
| - * @param {number} startOffset
|
| - * @param {number} endOffset
|
| + * @unrestricted
|
| */
|
| -WebInspector.HTMLModel.Token = function(value, type, startOffset, endOffset)
|
| -{
|
| +WebInspector.HTMLModel.Token = class {
|
| + /**
|
| + * @param {string} value
|
| + * @param {!Set<string>} type
|
| + * @param {number} startOffset
|
| + * @param {number} endOffset
|
| + */
|
| + constructor(value, type, startOffset, endOffset) {
|
| this.value = value;
|
| this.type = type;
|
| this.startOffset = startOffset;
|
| this.endOffset = endOffset;
|
| + }
|
| };
|
|
|
| /**
|
| - * @constructor
|
| - * @param {string} name
|
| - * @param {number} startOffset
|
| - * @param {number} endOffset
|
| - * @param {!Map<string, string>} attributes
|
| - * @param {boolean} isOpenTag
|
| - * @param {boolean} selfClosingTag
|
| + * @unrestricted
|
| */
|
| -WebInspector.HTMLModel.Tag = function(name, startOffset, endOffset, attributes, isOpenTag, selfClosingTag)
|
| -{
|
| +WebInspector.HTMLModel.Tag = class {
|
| + /**
|
| + * @param {string} name
|
| + * @param {number} startOffset
|
| + * @param {number} endOffset
|
| + * @param {!Map<string, string>} attributes
|
| + * @param {boolean} isOpenTag
|
| + * @param {boolean} selfClosingTag
|
| + */
|
| + constructor(name, startOffset, endOffset, attributes, isOpenTag, selfClosingTag) {
|
| this.name = name;
|
| this.startOffset = startOffset;
|
| this.endOffset = endOffset;
|
| this.attributes = attributes;
|
| this.isOpenTag = isOpenTag;
|
| this.selfClosingTag = selfClosingTag;
|
| + }
|
| };
|
|
|
| /**
|
| - * @constructor
|
| - * @param {string} name
|
| + * @unrestricted
|
| */
|
| -WebInspector.HTMLModel.Element = function(name)
|
| -{
|
| +WebInspector.HTMLModel.Element = class {
|
| + /**
|
| + * @param {string} name
|
| + */
|
| + constructor(name) {
|
| this.name = name;
|
| this.children = [];
|
| this.parent = null;
|
| this.openTag = null;
|
| this.closeTag = null;
|
| + }
|
| };
|
|
|