private void OnStartTagClose(object sender, HtmlParserCloseTagEventArgs e) { // e.Token contain Token with range for > or /> if well formed // or IsGhost is set to true otherwise Debug.Assert(_currentElementRecord != null); // Close start tag of the current element and push element // on the stack if element is not self-closing (or self-closed via />). ElementNode currentElement = _currentElementRecord.Element; ReadOnlyCollection <AttributeNode> attributes = AttributeNode.EmptyCollection; if (_currentElementRecord.StartTagAttributes.Count > 0) { attributes = new ReadOnlyCollection <AttributeNode>(_currentElementRecord.StartTagAttributes); } if (currentElement.StartTag.NameToken == null || currentElement.Name.Equals("!doctype", StringComparison.OrdinalIgnoreCase)) { currentElement.StartTag.Complete(attributes, e.CloseAngleBracket, e.IsClosed, e.IsShorthand, true); currentElement.CompleteElement(e.CloseAngleBracket, e.IsClosed, ElementNode.EmptyCollection, attributes, AttributeNode.EmptyCollection); } else { // Check if element is self-closing by calling URI info provider for a given namespace. bool selfClosing = false; NameToken nameToken = currentElement.StartTag.NameToken; if (nameToken != null) { selfClosing = _tree.HtmlClosureProvider.IsSelfClosing(_tree.Text, nameToken.PrefixRange, nameToken.NameRange); } currentElement.StartTag.Complete(attributes, e.CloseAngleBracket, e.IsClosed, e.IsShorthand, selfClosing); currentElement.CompleteElement(e.CloseAngleBracket, e.IsClosed, ElementNode.EmptyCollection, attributes, AttributeNode.EmptyCollection); if (!selfClosing && !e.IsShorthand) { _elementStack.Push(_currentElementRecord); } } _currentElementRecord = null; }
private void OnEndTagClose(object sender, HtmlParserCloseTagEventArgs e) { if (_currentElementRecord != null) { ElementNode element = _currentElementRecord.Element; ReadOnlyCollection <ElementNode> children = ElementNode.EmptyCollection; if (_currentElementRecord.Children.Count > 0) { children = new ReadOnlyCollection <ElementNode>(_currentElementRecord.Children); } ReadOnlyCollection <AttributeNode> startTagAttributes = AttributeNode.EmptyCollection; if (_currentElementRecord.StartTagAttributes.Count > 0) { startTagAttributes = new ReadOnlyCollection <AttributeNode>(_currentElementRecord.StartTagAttributes); } ReadOnlyCollection <AttributeNode> endTagAttributes = AttributeNode.EmptyCollection; if (_currentElementRecord.EndTagAttributes.Count > 0) { endTagAttributes = new ReadOnlyCollection <AttributeNode>(_currentElementRecord.EndTagAttributes); } element.EndTag.Complete(endTagAttributes, e.CloseAngleBracket, e.IsClosed, false, false); element.CompleteElement(e.CloseAngleBracket, true, children, startTagAttributes, endTagAttributes); _currentElementRecord = null; } CloseOrphanedEndTag(e.CloseAngleBracket, e.IsClosed); }
private void OnParseEnd(object sender, HtmlParserRangeEventArgs e) { // Collect all orphaned end tags since they belong to the currently // CloseOrphanedEndTag(new TextRange(e.Range.End, 0), false); // Close all element that are still on the stack while (_elementStack.Count > 0) { ElementRecord elementRecord = _elementStack.Pop(); ElementNode element = elementRecord.Element; ReadOnlyCollection <ElementNode> children = ElementNode.EmptyCollection; if (elementRecord.Children.Count > 0) { children = new ReadOnlyCollection <ElementNode>(elementRecord.Children); } ReadOnlyCollection <AttributeNode> startTagAttributes = AttributeNode.EmptyCollection; if (elementRecord.StartTagAttributes.Count > 0) { startTagAttributes = new ReadOnlyCollection <AttributeNode>(elementRecord.StartTagAttributes); } ReadOnlyCollection <AttributeNode> endTagAttributes = AttributeNode.EmptyCollection; if (elementRecord.EndTagAttributes.Count > 0) { endTagAttributes = new ReadOnlyCollection <AttributeNode>(elementRecord.EndTagAttributes); } element.CompleteElement(TextRange.FromBounds(e.Range.End, e.Range.End), false, children, startTagAttributes, endTagAttributes); } _tree.RootNode = _rootNodeRecord.Element as RootNode; _rootNodeRecord = null; _tree.CommentCollection.Sort(); var parser = sender as HtmlParser; _tree.DocType = parser.DocType; TreeBuildingTime = DateTime.UtcNow - _startTime; }
private ElementRecord CloseElements(ITextRange nameRange, int position, string[] containerNames) { ElementRecord last = null; int count = 0; bool foundSameElement = false; bool foundContainer = false; bool testImplicitClosureWalkingUp = (containerNames.Length > 0); bool foundImplicitlyClosedElement = false; string name = _tree.Text.GetText(nameRange); // Dev12 764293: Match end tags regardless of parsing mode, and let validation ensure the casing. // var comparison = Parser.ParsingMode == ParsingMode.Html ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; var comparison = StringComparison.OrdinalIgnoreCase; // Walk up the stack looking for element name but stop at possible container // so we don't close outer <li> by inner <li> in <ol><li><ol><li> and rather // stop at the nearest <ol> which is a 'hard' container for <li>. foreach (var elementRecord in _elementStack) { ITextRange qualifiedNameRange = elementRecord.Element.QualifiedNameRange; string qualifiedName = _tree.Text.GetText(qualifiedNameRange); if (String.Compare(qualifiedName, name, comparison) == 0) { foundSameElement = true; count++; // Ensure we close the element with the same name break; } foreach (var containerName in containerNames) { if (String.Compare(qualifiedName, containerName, comparison) == 0) { foundContainer = true; break; } } if (foundContainer) { break; } if (testImplicitClosureWalkingUp && !foundImplicitlyClosedElement) { string[] containerNamesCurrentElement; if (!String.IsNullOrEmpty(qualifiedName) && _tree.HtmlClosureProvider.IsImplicitlyClosed(_tree.Text, qualifiedNameRange, out containerNamesCurrentElement)) { foundImplicitlyClosedElement = true; } } count++; } if (foundSameElement || (foundImplicitlyClosedElement && foundContainer)) { for (int i = 0; i < count; i++) { last = _elementStack.Pop(); ElementNode element = last.Element; ReadOnlyCollection <ElementNode> children = ElementNode.EmptyCollection; if (last.Children.Count > 0) { children = new ReadOnlyCollection <ElementNode>(last.Children); } ReadOnlyCollection <AttributeNode> startTagAttributes = AttributeNode.EmptyCollection; if (last.StartTagAttributes.Count > 0) { startTagAttributes = new ReadOnlyCollection <AttributeNode>(last.StartTagAttributes); } ReadOnlyCollection <AttributeNode> endTagAttributes = AttributeNode.EmptyCollection; if (last.EndTagAttributes.Count > 0) { endTagAttributes = new ReadOnlyCollection <AttributeNode>(last.EndTagAttributes); } element.CompleteElement(TextRange.FromBounds(position, position), true, children, startTagAttributes, endTagAttributes); } } return(last); }