⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 175852 in webkit


Ignore:
Timestamp:
Nov 10, 2014, 11:19:53 PM (12 years ago)
Author:
Chris Dumez
Message:

Lazily create HTMLInputElement's inputType and shadow subtree
https://bugs.webkit.org/show_bug.cgi?id=138524

Reviewed by Ryosuke Niwa.

When an HTMLInputElement was created by the parser, we would first call
HTMLInputElement::create(), then call Element::parserSetAttributes() on
the constructed input. With the previous implementation, this was a bit
inefficient because HTMLInputElement::create() would construct a
TextInputType inputType (as this is the default) as well as its
corresponding shadow subtree. Then, parserSetAttributes() would often
set the |type| attribute and would need to destroy this input type as
well as its subtree if the new |type| is not 'text', to create a new
inputType / shadow subtree of the right type. The profiler showed that
this was fairly expensive.

To improve this, this patch delays the inputType / shadow subtree
creation when the HTMLInputElement is constructed by the parser, until
the attributes are actually set by the parser. This way, we directly
create an inputType / shadow subtree of the right type.

I see a 1.4% speed up on speedometer (73.95 -> 75.0).

No new tests, no behavior change.

  • dom/Element.cpp:

(WebCore::Element::parserSetAttributes):
(WebCore::Element::parserDidFinishParsingAttributes):

  • dom/Element.h:
  • html/HTMLInputElement.cpp:

(WebCore::HTMLInputElement::HTMLInputElement):
(WebCore::HTMLInputElement::create):
(WebCore::HTMLInputElement::updateType):
(WebCore::HTMLInputElement::runPostTypeUpdateTasks):
(WebCore::HTMLInputElement::ensureInputType):
(WebCore::HTMLInputElement::parseAttribute):
(WebCore::HTMLInputElement::parserDidFinishParsingAttributes):

  • html/HTMLInputElement.h:
Location:
trunk/Source/WebCore
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r175848 r175852  
     12014-11-10  Chris Dumez  <cdumez@apple.com>
     2
     3        Lazily create HTMLInputElement's inputType and shadow subtree
     4        https://bugs.webkit.org/show_bug.cgi?id=138524
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        When an HTMLInputElement was created by the parser, we would first call
     9        HTMLInputElement::create(), then call Element::parserSetAttributes() on
     10        the constructed input. With the previous implementation, this was a bit
     11        inefficient because HTMLInputElement::create() would construct a
     12        TextInputType inputType (as this is the default) as well as its
     13        corresponding shadow subtree. Then, parserSetAttributes() would often
     14        set the |type| attribute and would need to destroy this input type as
     15        well as its subtree if the new |type| is not 'text', to create a new
     16        inputType / shadow subtree of the right type. The profiler showed that
     17        this was fairly expensive.
     18
     19        To improve this, this patch delays the inputType / shadow subtree
     20        creation when the HTMLInputElement is constructed by the parser, until
     21        the attributes are actually set by the parser. This way, we directly
     22        create an inputType / shadow subtree of the right type.
     23
     24        I see a 1.4% speed up on speedometer (73.95 -> 75.0).
     25
     26        No new tests, no behavior change.
     27
     28        * dom/Element.cpp:
     29        (WebCore::Element::parserSetAttributes):
     30        (WebCore::Element::parserDidFinishParsingAttributes):
     31        * dom/Element.h:
     32        * html/HTMLInputElement.cpp:
     33        (WebCore::HTMLInputElement::HTMLInputElement):
     34        (WebCore::HTMLInputElement::create):
     35        (WebCore::HTMLInputElement::updateType):
     36        (WebCore::HTMLInputElement::runPostTypeUpdateTasks):
     37        (WebCore::HTMLInputElement::ensureInputType):
     38        (WebCore::HTMLInputElement::parseAttribute):
     39        (WebCore::HTMLInputElement::parserDidFinishParsingAttributes):
     40        * html/HTMLInputElement.h:
     41
    1422014-11-10  Benjamin Poulain  <bpoulain@apple.com>
    243
  • trunk/Source/WebCore/dom/Element.cpp

    r175212 r175852  
    12341234    ASSERT(!m_elementData);
    12351235
    1236     if (attributeVector.isEmpty())
    1237         return;
    1238 
    1239     if (document().sharedObjectPool())
    1240         m_elementData = document().sharedObjectPool()->cachedShareableElementDataWithAttributes(attributeVector);
    1241     else
    1242         m_elementData = ShareableElementData::createWithAttributes(attributeVector);
    1243 
    1244     // Use attributeVector instead of m_elementData because attributeChanged might modify m_elementData.
    1245     for (unsigned i = 0; i < attributeVector.size(); ++i)
    1246         attributeChanged(attributeVector[i].name(), nullAtom, attributeVector[i].value(), ModifiedDirectly);
     1236    if (!attributeVector.isEmpty()) {
     1237        if (document().sharedObjectPool())
     1238            m_elementData = document().sharedObjectPool()->cachedShareableElementDataWithAttributes(attributeVector);
     1239        else
     1240            m_elementData = ShareableElementData::createWithAttributes(attributeVector);
     1241
     1242        // Use attributeVector instead of m_elementData because attributeChanged might modify m_elementData.
     1243        for (const auto& attribute : attributeVector)
     1244            attributeChanged(attribute.name(), nullAtom, attribute.value(), ModifiedDirectly);
     1245    }
     1246
     1247    parserDidFinishParsingAttributes();
     1248}
     1249
     1250void Element::parserDidFinishParsingAttributes()
     1251{
    12471252}
    12481253
  • trunk/Source/WebCore/dom/Element.h

    r175212 r175852  
    556556    virtual void childrenChanged(const ChildChange&) override;
    557557    virtual void removeAllEventListeners() override final;
     558    virtual void parserDidFinishParsingAttributes();
    558559
    559560    void clearTabIndexExplicitlyIfNeeded();   
  • trunk/Source/WebCore/html/HTMLInputElement.cpp

    r175787 r175852  
    122122    , m_hasTouchEventHandler(false)
    123123#endif
    124     , m_inputType(InputType::createText(*this))
     124    // m_inputType is lazily created when constructed by the parser to avoid constructing unnecessarily a text inputType and
     125    // its shadow subtree, just to destroy them when the |type| attribute gets set by the parser to something else than 'text'.
     126    , m_inputType(createdByParser ? nullptr : InputType::createText(*this))
    125127{
    126128    ASSERT(hasTagName(inputTag) || hasTagName(isindexTag));
     
    130132PassRefPtr<HTMLInputElement> HTMLInputElement::create(const QualifiedName& tagName, Document& document, HTMLFormElement* form, bool createdByParser)
    131133{
     134    bool shouldCreateShadowRootLazily = createdByParser;
    132135    RefPtr<HTMLInputElement> inputElement = adoptRef(new HTMLInputElement(tagName, document, form, createdByParser));
    133     inputElement->ensureUserAgentShadowRoot();
     136    if (!shouldCreateShadowRootLazily)
     137        inputElement->ensureUserAgentShadowRoot();
    134138    return inputElement.release();
    135139}
     
    433437void HTMLInputElement::updateType()
    434438{
     439    ASSERT(m_inputType);
    435440    auto newType = InputType::create(*this, fastGetAttribute(typeAttr));
    436441    bool hadType = m_hasType;
     
    457462    m_inputType->createShadowSubtree();
    458463    updateInnerTextElementEditability();
    459 
    460 #if ENABLE(TOUCH_EVENTS)
    461     bool hasTouchEventHandler = m_inputType->hasTouchEventHandler();
    462     if (hasTouchEventHandler != m_hasTouchEventHandler) {
    463         if (hasTouchEventHandler)
    464             document().didAddTouchEventHandler(this);
    465         else
    466             document().didRemoveTouchEventHandler(this);
    467         m_hasTouchEventHandler = hasTouchEventHandler;
    468     }
    469 #endif
    470464
    471465    setNeedsWillValidateCheck();
     
    503497            attributeChanged(alignAttr, nullAtom, align->value());
    504498    }
     499
     500    runPostTypeUpdateTasks();
     501}
     502
     503inline void HTMLInputElement::runPostTypeUpdateTasks()
     504{
     505    ASSERT(m_inputType);
     506#if ENABLE(TOUCH_EVENTS)
     507    bool hasTouchEventHandler = m_inputType->hasTouchEventHandler();
     508    if (hasTouchEventHandler != m_hasTouchEventHandler) {
     509        if (hasTouchEventHandler)
     510            document().didAddTouchEventHandler(this);
     511        else
     512            document().didRemoveTouchEventHandler(this);
     513        m_hasTouchEventHandler = hasTouchEventHandler;
     514    }
     515#endif
    505516
    506517    if (renderer())
     
    597608}
    598609
     610inline void HTMLInputElement::ensureInputType()
     611{
     612    ASSERT(m_parsingInProgress);
     613    if (m_inputType)
     614        return;
     615
     616    if (!hasAttribute(typeAttr)) {
     617        m_inputType = InputType::createText(*this);
     618        ensureUserAgentShadowRoot();
     619        return;
     620    }
     621
     622    m_hasType = true;
     623    m_inputType = InputType::create(*this, fastGetAttribute(typeAttr));
     624    ensureUserAgentShadowRoot();
     625    registerForSuspensionCallbackIfNeeded();
     626    runPostTypeUpdateTasks();
     627}
     628
    599629void HTMLInputElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
    600630{
     631    if (m_parsingInProgress) {
     632        // A lot of the code below requires m_inputType to be initialized so make sure we do.
     633        // By the time parseAttribute() is called during parsing anyway, all attributes have
     634        // been set on the element already so there is no point in delaying m_inputType
     635        // initialization further.
     636        ensureInputType();
     637    }
     638
    601639    if (name == nameAttr) {
    602640        removeFromRadioButtonGroup();
     
    706744}
    707745
     746void HTMLInputElement::parserDidFinishParsingAttributes()
     747{
     748    ASSERT(m_inputType || !hasAttributes());
     749    ensureInputType();
     750}
     751
    708752void HTMLInputElement::finishParsingChildren()
    709753{
  • trunk/Source/WebCore/html/HTMLInputElement.h

    r175778 r175852  
    360360    virtual void collectStyleForPresentationAttribute(const QualifiedName&, const AtomicString&, MutableStyleProperties&) override;
    361361    virtual void finishParsingChildren() override;
     362    virtual void parserDidFinishParsingAttributes() override final;
    362363
    363364    virtual void copyNonAttributePropertiesFromElement(const Element&) override;
     
    399400    virtual void requiredAttributeChanged() override;
    400401
     402    void ensureInputType();
    401403    void updateType();
     404    void runPostTypeUpdateTasks();
    402405   
    403406    virtual void subtreeHasChanged() override;
Note: See TracChangeset for help on using the changeset viewer.