Bug 207816 - Follow up element iterator work by reducing includes and using is<> in a few more places
Summary: Follow up element iterator work by reducing includes and using is<> in a few ...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebCore Misc. (show other bugs)
Version: WebKit Nightly Build
Hardware: All All
: P2 Normal
Assignee: Darin Adler
URL:
Keywords: InRadar
Depends on: 208092 208097 208098 208099 208100
Blocks:
  Show dependency treegraph
 
Reported: 2020-02-15 15:37 PST by Darin Adler
Modified: 2020-02-23 13:42 PST (History)
42 users (show)

See Also:


Attachments
Patch (195.89 KB, patch)
2020-02-15 16:59 PST, Darin Adler
no flags Details | Formatted Diff | Diff
Patch (195.90 KB, patch)
2020-02-15 17:23 PST, Darin Adler
no flags Details | Formatted Diff | Diff
Patch (196.00 KB, patch)
2020-02-15 17:29 PST, Darin Adler
no flags Details | Formatted Diff | Diff
Patch (206.77 KB, patch)
2020-02-15 20:25 PST, Darin Adler
no flags Details | Formatted Diff | Diff
Patch (241.87 KB, patch)
2020-02-16 12:00 PST, Darin Adler
no flags Details | Formatted Diff | Diff
Patch (264.15 KB, patch)
2020-02-16 21:55 PST, Darin Adler
no flags Details | Formatted Diff | Diff
Patch (265.22 KB, patch)
2020-02-18 20:15 PST, Darin Adler
no flags Details | Formatted Diff | Diff
Patch (270.79 KB, patch)
2020-02-19 20:34 PST, Darin Adler
no flags Details | Formatted Diff | Diff
Patch (270.90 KB, patch)
2020-02-19 20:56 PST, Darin Adler
no flags Details | Formatted Diff | Diff
Patch (270.93 KB, patch)
2020-02-20 19:35 PST, Darin Adler
no flags Details | Formatted Diff | Diff
Patch (234.28 KB, patch)
2020-02-22 09:11 PST, Darin Adler
no flags Details | Formatted Diff | Diff
Patch (16.46 KB, patch)
2020-02-23 08:01 PST, Darin Adler
no flags Details | Formatted Diff | Diff
Patch (17.01 KB, patch)
2020-02-23 08:10 PST, Darin Adler
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Darin Adler 2020-02-15 15:37:05 PST
Revise DOM iterators implementation and usage
Comment 1 Darin Adler 2020-02-15 16:59:42 PST Comment hidden (obsolete)
Comment 2 Darin Adler 2020-02-15 17:23:43 PST Comment hidden (obsolete)
Comment 3 Darin Adler 2020-02-15 17:29:49 PST Comment hidden (obsolete)
Comment 4 Darin Adler 2020-02-15 20:25:48 PST Comment hidden (obsolete)
Comment 5 Antti Koivisto 2020-02-16 02:56:14 PST
It would also be nice to get rid of the clunky OfType suffix, though children<> might be too generic.
Comment 6 Darin Adler 2020-02-16 08:53:17 PST
(In reply to Antti Koivisto from comment #5)
> It would also be nice to get rid of the clunky OfType suffix, though
> children<> might be too generic.

I love that idea; I will explore it a bit.
Comment 7 Darin Adler 2020-02-16 12:00:46 PST Comment hidden (obsolete)
Comment 8 Darin Adler 2020-02-16 21:55:10 PST Comment hidden (obsolete)
Comment 9 Darin Adler 2020-02-18 20:15:18 PST Comment hidden (obsolete)
Comment 10 Darin Adler 2020-02-19 20:34:20 PST Comment hidden (obsolete)
Comment 11 Darin Adler 2020-02-19 20:56:09 PST Comment hidden (obsolete)
Comment 12 Darin Adler 2020-02-20 19:35:53 PST
Created attachment 391374 [details]
Patch
Comment 13 Darin Adler 2020-02-21 08:00:48 PST
OK, all tests pass now so I’m ready to have someone review
Comment 14 Antti Koivisto 2020-02-21 10:15:17 PST
Comment on attachment 391374 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=391374&action=review

> Source/WebCore/ChangeLog:97
> +        another even if the root element is not the same. Deleted ElementConstIterator.

Why? We'll lose a significant amount compile time checking against serious programming errors (allowing mutations of elements that are not supposed to be mutated in the context).
Comment 15 Antti Koivisto 2020-02-21 10:24:49 PST
Comment on attachment 391374 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=391374&action=review

While this patch has lot of good I don't think we want to regress in const correctness in WebKit. I know I'm not the only one who feels it is a valuable tool that we should be using more, not less.

> Source/WebCore/dom/TypedElementDescendantIterator.h:65
> -    const ElementType* first() const;
> -    const ElementType* last() const;
> +    ElementType* first() const;
> +    ElementType* last() const;

Gross.
Comment 16 Darin Adler 2020-02-21 12:44:05 PST
Comment on attachment 391374 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=391374&action=review

>> Source/WebCore/ChangeLog:97
>> +        another even if the root element is not the same. Deleted ElementConstIterator.
> 
> Why? We'll lose a significant amount compile time checking against serious programming errors (allowing mutations of elements that are not supposed to be mutated in the context).

Everything is still const-correct. We just don’t need a separate template to do it. We use ElementIterator<const HTMLElement>. The ElementType itself has const in it.

>> Source/WebCore/dom/TypedElementDescendantIterator.h:65
>> +    ElementType* last() const;
> 
> Gross.

We are not regressing in const-correctness! This diff is giving you the wrong impression.
Comment 17 Darin Adler 2020-02-21 12:45:50 PST
Comment on attachment 391374 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=391374&action=review

>>> Source/WebCore/dom/TypedElementDescendantIterator.h:65
>>> +    ElementType* last() const;
>> 
>> Gross.
> 
> We are not regressing in const-correctness! This diff is giving you the wrong impression.

There is a const version of Range, Range<HTMLElement> that gives you iterators with const pointers, and a non-const version of Range, Range<const HTMLElement> that gives you iterators with non-const pointers. In both of those, the begin/end/first/last functions are const because they don’t modify the *Range*.
Comment 18 Darin Adler 2020-02-21 12:46:50 PST
Antti, I hope you have a chance to take another look. I did not get rid of const-correctness. I got rid of unneeded duplicate copies of many class templates while keeping all the const-correctness.
Comment 19 Darin Adler 2020-02-21 12:50:51 PST
Comment on attachment 391374 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=391374&action=review

I’m going to change this back to review?, since the only reason you gave for review- is a misunderstanding. I hope you will take another look.

> Source/WebCore/dom/ElementAncestorIterator.h:114
> +        return ElementAncestorRange<const ElementType>(&downcast<ElementType>(first));

Here’s where we make a code change for lineageOfType so we retain the const correctness. Note here that because the argument is const, the template argument for the range (and indirectly for the iterator) is a const type. This is what makes the const correctness work.

> Source/WebCore/dom/ElementAncestorIterator.h:127
> +    return ElementAncestorRange<const ElementType>(findElementAncestorOfType<const ElementType>(descendant));

And here is where we do it for ancestorsOfType.

> Source/WebCore/dom/ElementChildIterator.h:129
> +    return ElementChildRange<const ElementType>(parent);

Here is where we do it for childrenOfType.

> Source/WebCore/dom/TypedElementDescendantIterator.h:256
> +    return ElementDescendantRange<const ElementType>(root);

Here’s where we make a code change so we retain the const correctness. Note here that because the argument is const, the template argument for the range (and indirectly for the iterator) is a const type. This is what makes the const correctness work.
Comment 20 Antti Koivisto 2020-02-22 00:58:54 PST
Comment on attachment 391374 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=391374&action=review

> Source/WebCore/ChangeLog:84
> +        * dom/ElementAncestorIterator.h: Moved the declarations of the functions
> +        to the top of the file, since the classes are really implementation details.
> +        Removed the separate "Const" versions of everything since we can just use
> +        const in the template argument type instead. Use the term "Range" instead
> +        of the term "IteratorAdapter" for the class templates for use in range-based
> +        for loops since that's closer to modern C++ terminology. Use nullptr as a
> +        sentinel for end, which works well with range-based for loops. Made many function
> +        members const where appropriate. Specialized lineageOfType and ancestorsOfType
> +        for Element to slightly optimize that case. Use downcast instead of static_cast.
> +        Deleted elementLineage and elementAncestors.
> +
> +        * dom/ElementAndTextDescendantIterator.h: Renamed IteratorAdapter to Range.
> +        Made more function members const.
> +
> +        * dom/ElementChildIterator.h: Moved the declarations of the functions
> +        to the top of the file, since the classes are really implementation details.
> +        Removed the separate "Const" versions of everything since we can just use
> +        const in the template argument type instead. Use the term "Range" instead
> +        of the term "IteratorAdapter" for the class templates for use in range-based
> +        for loops since that's closer to modern C++ terminology. Use nullptr as a
> +        sentinel for end, which works well with range-based for loops. Made many
> +        function members const where appropriate. Deleted elementChildren.

Can you reorganize the ChangeLog so that interesting design issues (like how const correctness is maintained without separate const iterators) are on the top section instead of being mixed (repeatedly!) with the minutiae? 

In general I don't think is is helpful to describe mechanical code changes obviously falling out from design changes.

> Source/WebCore/ChangeLog:254
> +        * html/HTMLOptionsCollection.idl: Specify [RequiresExistingAtomString] on

The patch is pretty large. Could for example some of these collection changes be done separately?
Comment 21 Darin Adler 2020-02-22 08:07:36 PST
Comment on attachment 391374 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=391374&action=review

>> Source/WebCore/ChangeLog:84
>> +        function members const where appropriate. Deleted elementChildren.
> 
> Can you reorganize the ChangeLog so that interesting design issues (like how const correctness is maintained without separate const iterators) are on the top section instead of being mixed (repeatedly!) with the minutiae? 
> 
> In general I don't think is is helpful to describe mechanical code changes obviously falling out from design changes.

Sure, happy to.

>> Source/WebCore/ChangeLog:254
>> +        * html/HTMLOptionsCollection.idl: Specify [RequiresExistingAtomString] on
> 
> The patch is pretty large. Could for example some of these collection changes be done separately?

Yes. This could be broken in smaller pieces. It’s hard for me to decide how many to break it into. It’s a lot of work but I would be happy to do it.

For example the “RequiresExistingAtomString” changes are completely separable. I just need some guidance how much to break this down; how many different pieces.
Comment 22 Darin Adler 2020-02-22 08:07:37 PST Comment hidden (obsolete)
Comment 23 Darin Adler 2020-02-22 08:07:38 PST Comment hidden (obsolete)
Comment 24 Darin Adler 2020-02-22 08:50:14 PST
I broke out a piece as bug 208092. Also planning to write better change log as per Antti’s suggestion. Not sure how many pieces to break this into. Would appreciate other specific suggestions for things to land separately.
Comment 25 Darin Adler 2020-02-22 09:11:40 PST
Created attachment 391461 [details]
Patch
Comment 26 Darin Adler 2020-02-22 09:14:11 PST
OK, uploaded a new patch with a WebCore change log that puts the interesting stuff on the top.

I didn't yet go down to the rest of the per-file comments and cut them all down. Not sure how much to shorten them. I suppose I could just not mention the specific changes at all since they're all at the top now. Seems like a lot of work. Just leaving it alone for now.

Open to suggestions about splitting more parts of the patch separately.

I am happy to do whatever it takes to make this better and get it landed so I can move on to something else.
Comment 27 Antti Koivisto 2020-02-22 09:38:55 PST
Comment on attachment 391461 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=391461&action=review

I like red patches.

> Source/WebCore/ChangeLog:39
> +        - Removed the duplicate descendant iterator, keeping the one that matches
> +          the style of the ancestor and child iterators.
> +        - Removed the non-template elementAncestors, elementChildren, elementDescendants,
> +          and elementLineage functions and changed callers to use xxxOfType<Element> instead.
> +        - Renamed "IteratorAdapter" templates to "Range", choosing that term to match the
> +          upcoming C++20 Ranges library and range-based for loops.
> +        - Changed the iterators to use an actual "nullptr" for end, following the "sentinel"
> +          design pattern from the Ranges library. Still kept a tiny bit of using an iterator
> +          for end around, only so we can use iterator library functions like std::distance
> +          while waiting for std::ranges::distance, which is compatible with sentinels.
> +        - Implemented const correctness by using const types instead of separate "Const"
> +          class templates. This cut down on source code size a lot. These element iterators
> +          don't need whole separate templates to implement the const correctness the way
> +          collection classes like HashMap do.
> +        - Improved some other details, like using more const and constexpr on members.
> +          All the functions on a range are const, because the range itself doesn't ever
> +          get modified, and all functions on an iterator are also const, because only
> +          operations like ++ and -- actually modify the iterator.
> +        - For now at least, removed extra code we don't need in practice. We never need to
> +          compare iterators to each other except when iterating a range, for example, so
> +          kept the != used for range iteration but not ==.
> +        - Simplified the HTMLCollection implementations by taking advantage of the null-
> +          based and sentinel designs. There are various places where we can write
> +          simpler code and pass around fewer arguments.
> +        - Added a new descendantsOfType template that takes a predicate and filters to only
> +          the elements that match that predicate. Similar concept to how we implement HTML
> +          collections, and possibly could be used even more eventually.
> +        - Use std::iterator in ElementIterator so we don't need to do that in derived
> +          classes. Also made more of ElementIterator protected to make it more explicit
> +          that it's an abstract class template and not something to be used directly.

This is informative!

> Source/WebCore/dom/CollectionIndexCache.h:45
> -    explicit CollectionIndexCache(const Collection&);
> +    CollectionIndexCache();
>  
>      typedef typename std::iterator_traits<Iterator>::value_type NodeType;
>  
>      unsigned nodeCount(const Collection&);
>      NodeType* nodeAt(const Collection&, unsigned index);
>  
> -    bool hasValidCache(const Collection& collection) const { return m_current != collection.collectionEnd() || m_nodeCountValid || m_listValid; }
> -    void invalidate(const Collection&);
> +    bool hasValidCache() const { return m_current || m_nodeCountValid || m_listValid; }
> +    void invalidate();

This sort of work that builds on top of the refactoring would be better done as an incremental step (making the patch less unwieldy).

> Source/WebCore/dom/ElementAncestorIterator.h:54
> +    static constexpr std::nullptr_t end() { return nullptr; }

This is a nice C++17 feature.

> Source/WebCore/html/HTMLTableElement.h:98
> -    RefPtr<StyleProperties> m_sharedCellStyle;
> +    mutable RefPtr<StyleProperties> m_sharedCellStyle;

Could probably be mutable RefPtr<const StyleProperties>

> Source/WebCore/xml/parser/XMLDocumentParser.cpp:296
> +    if (contextElement) {
> +        bool stopLookingForDefaultNamespaceURI = false;
> +
> +        for (auto& element : lineageOfType<Element>(*contextElement)) {
> +            if (is<SVGForeignObjectElement>(element))
> +                stopLookingForDefaultNamespaceURI = true;
> +            else if (!stopLookingForDefaultNamespaceURI)
> +                defaultNamespaceURI = element.namespaceURI();
> +
> +            if (!element.hasAttributes())
> +                continue;
> +
> +            for (const Attribute& attribute : element.attributesIterator()) {
> +                if (attribute.prefix() == xmlnsAtom())
> +                    prefixToNamespaceMap.set(attribute.localName(), attribute.value());
> +                else if (!stopLookingForDefaultNamespaceURI && attribute.prefix() == xmlnsAtom())
> +                    defaultNamespaceURI = attribute.value();
> +            }
>          }
>      }

This looks likea good candidate for a lambda.
Comment 28 Antti Koivisto 2020-02-22 09:53:15 PST
Comment on attachment 391461 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=391461&action=review

> Source/WebCore/dom/TypedElementDescendantIterator.h:42
> +// Range that skips elements where the filter returns false.
> +template<typename ElementType, bool filter(const ElementType&)> FilteredElementDescendantRange<const ElementType, filter> descendantsOfType(const ContainerNode&);

filteredDescendants(OfType)?

The filter template would have been another separable feature.
Comment 29 Darin Adler 2020-02-22 16:17:48 PST
I’ll follow up on all those comments. A few specific things:

- I’ll keep breaking up the patch more and land in smaller pieces. For example, I think the next one will be more adoption of the element iterators but no changes to the iterator implementation. Given they are all excerpts of this one, I might not wait for separate review of all pieces, just claim Antti reviewed. Judgement call depending on if they seem obvious or complex.

- I will use the name filteredDescendants instead of descendantsOfType.

- I plan to follow up afterwards and rename descendantsOfType to just descendants, same for lineageOfType and ancestryOfType, but I'll use the do-webcore-rename script to do it. I’ll wait on childrenOfType for now.
Comment 30 Darin Adler 2020-02-22 21:23:21 PST
Landed another piece as bug 208097, covering more adoption of the element iterators.

Separated out a third piece as bug 208098, covering a small change in behavior, where a piece of the datalist element implementation was outside ENABLE(DATALIST_ELEMENT), and I had to move it in so I could use the element iterator in its implementation.

More separate pieces to come.
Comment 31 Darin Adler 2020-02-22 21:48:11 PST
Broke out a fourth pieces as bug 208099, covering the changes outside WebCore.
Comment 32 Darin Adler 2020-02-22 22:23:24 PST
Broke out a fifth piece as bug 208100; this has the new revised versions of the implementations of the iterators. That pretty much covers what the title of this bug takes about, but still leaves some non-trivial parts of the patch attached here once these are all landed.
Comment 33 Darin Adler 2020-02-23 08:00:07 PST
Now that most of this has landed, using this bug for the last bit, and retitling accordingly. These bits are not identical to when Antti reviewed, but I still think I can probably land it without another round of review.
Comment 34 Darin Adler 2020-02-23 08:01:04 PST
Created attachment 391490 [details]
Patch
Comment 35 Darin Adler 2020-02-23 08:10:42 PST
Created attachment 391491 [details]
Patch
Comment 36 Darin Adler 2020-02-23 13:41:48 PST
Committed r257196: <https://trac.webkit.org/changeset/257196>
Comment 37 Radar WebKit Bug Importer 2020-02-23 13:42:16 PST
<rdar://problem/59709547>