Changeset 212614 in webkit


Ignore:
Timestamp:
Feb 19, 2017 4:12:07 AM (7 years ago)
Author:
Antti Koivisto
Message:

Execute pending scripts asynchronously after stylesheet loads complete
https://bugs.webkit.org/show_bug.cgi?id=168367
rdar://problem/30561379

Reviewed by Andreas Kling.

The current synchronous execution is fragile and creates various problems.

  • css/StyleSheetContents.cpp:

(WebCore::StyleSheetContents::checkLoaded):

  • dom/ContainerNode.cpp:

(WebCore::ContainerNode::takeAllChildrenFrom):
(WebCore::ContainerNode::notifyChildInserted):
(WebCore::ContainerNode::removeChild):
(WebCore::ContainerNode::parserRemoveChild):
(WebCore::ContainerNode::removeChildren):

Remove various places where we would trigger delayed synchronous execution.

  • dom/Document.cpp:

(WebCore::Document::Document):
(WebCore::Document::recalcStyle):

Trigger scroll to anchor at the end of style resolution instead of when style sheet load completes.

(WebCore::Document::didRemoveAllPendingStylesheet):

Call asynchronous script execution function.

  • dom/Document.h:

(WebCore::Document::setNeedsNotifyRemoveAllPendingStylesheet): Deleted.
(WebCore::Document::notifyRemovePendingSheetIfNeeded): Deleted.

  • dom/ScriptableDocumentParser.cpp:

(WebCore::ScriptableDocumentParser::ScriptableDocumentParser):
(WebCore::ScriptableDocumentParser::executeScriptsWaitingForStylesheetsSoon):
(WebCore::ScriptableDocumentParser::scriptsWaitingForStylesheetsExecutionTimerFired):

Add a timer for executing pending scripts.

(WebCore::ScriptableDocumentParser::detach):

  • dom/ScriptableDocumentParser.h:

(WebCore::ScriptableDocumentParser::executeScriptsWaitingForStylesheets):

  • html/HTMLLinkElement.cpp:

(WebCore::HTMLLinkElement::removedFrom):
(WebCore::HTMLLinkElement::removePendingSheet):

  • html/HTMLLinkElement.h:
  • html/parser/HTMLDocumentParser.cpp:

(WebCore::HTMLDocumentParser::detach):

  • loader/DocumentLoader.cpp:

(WebCore::DocumentLoader::isLoadingInAPISense):

Stay in loading state if we have a pending script. This matches existing behavior.

  • style/StyleScope.cpp:

(WebCore::Style::Scope::removePendingSheet):

  • style/StyleScope.h:
Location:
trunk/Source/WebCore
Files:
15 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r212610 r212614  
     12017-02-19  Antti Koivisto  <antti@apple.com>
     2
     3        Execute pending scripts asynchronously after stylesheet loads complete
     4        https://bugs.webkit.org/show_bug.cgi?id=168367
     5        rdar://problem/30561379
     6
     7        Reviewed by Andreas Kling.
     8
     9        The current synchronous execution is fragile and creates various problems.
     10
     11        * css/StyleSheetContents.cpp:
     12        (WebCore::StyleSheetContents::checkLoaded):
     13        * dom/ContainerNode.cpp:
     14        (WebCore::ContainerNode::takeAllChildrenFrom):
     15        (WebCore::ContainerNode::notifyChildInserted):
     16        (WebCore::ContainerNode::removeChild):
     17        (WebCore::ContainerNode::parserRemoveChild):
     18        (WebCore::ContainerNode::removeChildren):
     19
     20            Remove various places where we would trigger delayed synchronous execution.
     21
     22        * dom/Document.cpp:
     23        (WebCore::Document::Document):
     24        (WebCore::Document::recalcStyle):
     25
     26            Trigger scroll to anchor at the end of style resolution instead of when style sheet load completes.
     27
     28        (WebCore::Document::didRemoveAllPendingStylesheet):
     29
     30            Call asynchronous script execution function.
     31
     32        * dom/Document.h:
     33        (WebCore::Document::setNeedsNotifyRemoveAllPendingStylesheet): Deleted.
     34        (WebCore::Document::notifyRemovePendingSheetIfNeeded): Deleted.
     35        * dom/ScriptableDocumentParser.cpp:
     36        (WebCore::ScriptableDocumentParser::ScriptableDocumentParser):
     37        (WebCore::ScriptableDocumentParser::executeScriptsWaitingForStylesheetsSoon):
     38        (WebCore::ScriptableDocumentParser::scriptsWaitingForStylesheetsExecutionTimerFired):
     39
     40            Add a timer for executing pending scripts.
     41
     42        (WebCore::ScriptableDocumentParser::detach):
     43        * dom/ScriptableDocumentParser.h:
     44        (WebCore::ScriptableDocumentParser::executeScriptsWaitingForStylesheets):
     45        * html/HTMLLinkElement.cpp:
     46        (WebCore::HTMLLinkElement::removedFrom):
     47        (WebCore::HTMLLinkElement::removePendingSheet):
     48        * html/HTMLLinkElement.h:
     49        * html/parser/HTMLDocumentParser.cpp:
     50        (WebCore::HTMLDocumentParser::detach):
     51        * loader/DocumentLoader.cpp:
     52        (WebCore::DocumentLoader::isLoadingInAPISense):
     53
     54            Stay in loading state if we have a pending script. This matches existing behavior.
     55
     56        * style/StyleScope.cpp:
     57        (WebCore::Style::Scope::removePendingSheet):
     58        * style/StyleScope.h:
     59
    1602017-02-18  Chris Dumez  <cdumez@apple.com>
    261
  • trunk/Source/WebCore/css/StyleSheetContents.cpp

    r212556 r212614  
    365365        return;
    366366
    367     // Avoid |this| being deleted by scripts that run via
    368     // ScriptableDocumentParser::executeScriptsWaitingForStylesheets().
    369     // See <rdar://problem/6622300>.
    370367    Ref<StyleSheetContents> protectedThis(*this);
    371368    StyleSheetContents* parentSheet = parentStyleSheet();
  • trunk/Source/WebCore/dom/ContainerNode.cpp

    r212556 r212614  
    146146    }
    147147
    148     document().notifyRemovePendingSheetIfNeeded();
    149 
    150148    // FIXME: assert that we don't dispatch events here since this container node is still disconnected.
    151149    for (auto& child : children) {
     
    365363    childrenChanged(change);
    366364
    367     // Some call sites may have deleted child nodes. Calling it earlier results in bugs like webkit.org/b/168069.
    368     document().notifyRemovePendingSheetIfNeeded();
    369 
    370365    for (auto& target : postInsertionNotificationTargets)
    371366        target->finishedInsertingSubtree();
     
    555550    }
    556551
    557     document().notifyRemovePendingSheetIfNeeded();
    558552    rebuildSVGExtensionsElementsIfNecessary();
    559553    dispatchSubtreeModifiedEvent();
     
    616610
    617611    notifyChildRemoved(oldChild, prev, next, ChildChangeSourceParser);
    618     document().notifyRemovePendingSheetIfNeeded();
    619612}
    620613
     
    710703    }
    711704
    712     document().notifyRemovePendingSheetIfNeeded();
    713705    rebuildSVGExtensionsElementsIfNecessary();
    714706    dispatchSubtreeModifiedEvent();
  • trunk/Source/WebCore/dom/Document.cpp

    r212556 r212614  
    444444    , m_settings(frame ? Ref<Settings>(frame->settings()) : Settings::create(nullptr))
    445445    , m_hasNodesWithPlaceholderStyle(false)
    446     , m_needsNotifyRemoveAllPendingStylesheet(false)
    447446    , m_ignorePendingStylesheets(false)
    448447    , m_pendingSheetLayout(NoLayoutWithPendingSheets)
     
    18471846    if (m_hoveredElement && !m_hoveredElement->renderer())
    18481847        frameView.frame().mainFrame().eventHandler().dispatchFakeMouseMoveEventSoon();
     1848
     1849    if (m_gotoAnchorNeededAfterStylesheetsLoad && !styleScope().hasPendingSheets())
     1850        frameView.scrollToFragment(m_url);
    18491851}
    18501852
     
    26242626    }
    26252627
    2626     m_frame->loader().checkCompleted();
     2628    checkCompleted();
    26272629}
    26282630
     
    30743076void Document::didRemoveAllPendingStylesheet()
    30753077{
    3076     m_needsNotifyRemoveAllPendingStylesheet = false;
    3077 
    30783078    if (m_pendingSheetLayout == DidLayoutWithPendingSheets) {
     3079        // Painting is disabled when doing layouts with pending sheets to avoid FOUC.
     3080        // We need to force paint when coming out from this state.
     3081        // FIXME: This is not very elegant.
    30793082        m_pendingSheetLayout = IgnoreLayoutWithPendingSheets;
    30803083        if (renderView())
     
    30823085    }
    30833086
    3084     if (ScriptableDocumentParser* parser = scriptableDocumentParser())
    3085         parser->executeScriptsWaitingForStylesheets();
    3086 
    3087     if (m_gotoAnchorNeededAfterStylesheetsLoad && view())
    3088         view()->scrollToFragment(m_url);
     3087    if (auto* parser = scriptableDocumentParser())
     3088        parser->executeScriptsWaitingForStylesheetsSoon();
    30893089}
    30903090
     
    60396039void Document::loadEventDelayTimerFired()
    60406040{
    6041     if (frame())
    6042         frame()->loader().checkCompleted();
     6041    checkCompleted();
     6042}
     6043
     6044void Document::checkCompleted()
     6045{
     6046    if (auto* frame = this->frame())
     6047        frame->loader().checkCompleted();
    60436048}
    60446049
  • trunk/Source/WebCore/dom/Document.h

    r212556 r212614  
    487487    CSSFontSelector& fontSelector() { return m_fontSelector; }
    488488
    489     void notifyRemovePendingSheetIfNeeded();
    490 
    491489    WEBCORE_EXPORT bool haveStylesheetsLoaded() const;
    492490
     
    11171115    void decrementLoadEventDelayCount();
    11181116    bool isDelayingLoadEvent() const { return m_loadEventDelayCount; }
     1117    void checkCompleted();
    11191118
    11201119#if ENABLE(IOS_TOUCH_EVENTS)
     
    12071206
    12081207    void didRemoveAllPendingStylesheet();
    1209     void setNeedsNotifyRemoveAllPendingStylesheet() { m_needsNotifyRemoveAllPendingStylesheet = true; }
    12101208    void didClearStyleResolver();
    12111209
     
    13921390    std::unique_ptr<StyleResolver> m_userAgentShadowTreeStyleResolver;
    13931391    bool m_hasNodesWithPlaceholderStyle;
    1394     bool m_needsNotifyRemoveAllPendingStylesheet;
    13951392    // But sometimes you need to ignore pending stylesheet count to
    13961393    // force an immediate layout when requested by JS.
     
    17581755Element* eventTargetElementForDocument(Document*);
    17591756
    1760 inline void Document::notifyRemovePendingSheetIfNeeded()
    1761 {
    1762     if (m_needsNotifyRemoveAllPendingStylesheet)
    1763         didRemoveAllPendingStylesheet();
    1764 }
    1765 
    17661757inline TextEncoding Document::textEncoding() const
    17671758{
  • trunk/Source/WebCore/dom/ScriptableDocumentParser.cpp

    r212556 r212614  
    2929#include "Document.h"
    3030#include "Settings.h"
     31#include "StyleScope.h"
    3132
    3233namespace WebCore {
     
    3637    , m_wasCreatedByScript(false)
    3738    , m_parserContentPolicy(parserContentPolicy)
     39    , m_scriptsWaitingForStylesheetsExecutionTimer(*this, &ScriptableDocumentParser::scriptsWaitingForStylesheetsExecutionTimerFired)
    3840{
    3941    if (!pluginContentIsAllowed(m_parserContentPolicy))
     
    4446}
    4547
     48void ScriptableDocumentParser::executeScriptsWaitingForStylesheetsSoon()
     49{
     50    ASSERT(!document()->styleScope().hasPendingSheets());
     51
     52    if (m_scriptsWaitingForStylesheetsExecutionTimer.isActive())
     53        return;
     54    if (!hasScriptsWaitingForStylesheets())
     55        return;
     56
     57    m_scriptsWaitingForStylesheetsExecutionTimer.startOneShot(0);
     58}
     59
     60void ScriptableDocumentParser::scriptsWaitingForStylesheetsExecutionTimerFired()
     61{
     62    ASSERT(!isDetached());
     63
     64    Ref<ScriptableDocumentParser> protectedThis(*this);
     65
     66    if (!document()->styleScope().hasPendingSheets())
     67        executeScriptsWaitingForStylesheets();
     68
     69    if (!isDetached())
     70        document()->checkCompleted();
     71}
     72
     73void ScriptableDocumentParser::detach()
     74{
     75    m_scriptsWaitingForStylesheetsExecutionTimer.stop();
     76
     77    DecodedDataDocumentParser::detach();
     78}
     79
    4680};
  • trunk/Source/WebCore/dom/ScriptableDocumentParser.h

    r212556 r212614  
    2828#include "DecodedDataDocumentParser.h"
    2929#include "FragmentScriptingPermission.h"
     30#include "Timer.h"
    3031#include <wtf/text/TextPosition.h>
    3132
     
    3839    virtual bool isExecutingScript() const { return false; }
    3940
    40     // FIXME: Only the HTMLDocumentParser ever blocks script execution on
    41     // stylesheet load, which is likely a bug in the XMLDocumentParser.
    42     virtual void executeScriptsWaitingForStylesheets() { }
    43 
    4441    virtual bool isWaitingForScripts() const = 0;
    4542
    4643    virtual TextPosition textPosition() const = 0;
     44
     45    virtual bool hasScriptsWaitingForStylesheets() const { return false; }
     46
     47    void executeScriptsWaitingForStylesheetsSoon();
    4748
    4849    // Returns true if the parser didn't yield or pause or synchronously execute a script,
     
    5859    explicit ScriptableDocumentParser(Document&, ParserContentPolicy = AllowScriptingContent);
    5960
     61    virtual void executeScriptsWaitingForStylesheets() { }
     62
     63    void detach() override;
     64
    6065private:
    6166    ScriptableDocumentParser* asScriptableDocumentParser() final { return this; }
     67
     68    void scriptsWaitingForStylesheetsExecutionTimerFired();
    6269
    6370    // http://www.whatwg.org/specs/web-apps/current-work/#script-created-parser
    6471    bool m_wasCreatedByScript;
    6572    ParserContentPolicy m_parserContentPolicy;
     73    Timer m_scriptsWaitingForStylesheetsExecutionTimer;
    6674};
    6775
  • trunk/Source/WebCore/html/HTMLLinkElement.cpp

    r212556 r212614  
    328328
    329329    if (styleSheetIsLoading())
    330         removePendingSheet(RemovePendingSheetNotifyLater);
     330        removePendingSheet();
    331331   
    332332    if (m_styleScope) {
     
    555555}
    556556
    557 void HTMLLinkElement::removePendingSheet(RemovePendingSheetNotificationType notification)
     557void HTMLLinkElement::removePendingSheet()
    558558{
    559559    PendingSheetType type = m_pendingSheetType;
     
    570570    }
    571571
    572     m_styleScope->removePendingSheet(
    573         notification == RemovePendingSheetNotifyImmediately
    574         ? Style::Scope::RemovePendingSheetNotifyImmediately
    575         : Style::Scope::RemovePendingSheetNotifyLater);
     572    m_styleScope->removePendingSheet();
    576573}
    577574
  • trunk/Source/WebCore/html/HTMLLinkElement.h

    r212556 r212614  
    112112    void addPendingSheet(PendingSheetType);
    113113
    114     enum RemovePendingSheetNotificationType {
    115         RemovePendingSheetNotifyImmediately,
    116         RemovePendingSheetNotifyLater
    117     };
    118 
    119     void removePendingSheet(RemovePendingSheetNotificationType = RemovePendingSheetNotifyImmediately);
     114    void removePendingSheet();
    120115
    121116    LinkLoader m_linkLoader;
  • trunk/Source/WebCore/html/parser/HTMLDocumentParser.cpp

    r212556 r212614  
    8888void HTMLDocumentParser::detach()
    8989{
    90     DocumentParser::detach();
     90    ScriptableDocumentParser::detach();
    9191
    9292    if (m_scriptRunner)
     
    539539}
    540540
     541bool HTMLDocumentParser::hasScriptsWaitingForStylesheets() const
     542{
     543    return m_scriptRunner && m_scriptRunner->hasScriptsWaitingForStylesheets();
     544}
     545
    541546void HTMLDocumentParser::executeScriptsWaitingForStylesheets()
    542547{
  • trunk/Source/WebCore/html/parser/HTMLDocumentParser.h

    r212556 r212614  
    8484    bool isWaitingForScripts() const override;
    8585    bool isExecutingScript() const final;
     86    bool hasScriptsWaitingForStylesheets() const final;
    8687    void executeScriptsWaitingForStylesheets() final;
    8788    void suspendScheduledTasks() final;
  • trunk/Source/WebCore/loader/DocumentLoader.cpp

    r212556 r212614  
    6969#include "ResourceLoadObserver.h"
    7070#include "SchemeRegistry.h"
     71#include "ScriptableDocumentParser.h"
    7172#include "SecurityPolicy.h"
    7273#include "Settings.h"
     
    10581059        if (document.hasActiveParser())
    10591060            return true;
     1061        auto* scriptableParser = document.scriptableDocumentParser();
     1062        if (scriptableParser && scriptableParser->hasScriptsWaitingForStylesheets())
     1063            return true;
    10601064    }
    10611065    return frameLoader()->subframeIsLoading();
  • trunk/Source/WebCore/loader/FrameLoader.cpp

    r212556 r212614  
    788788        return;
    789789
     790    auto* scriptableParser = m_frame.document()->scriptableDocumentParser();
     791    if (scriptableParser && scriptableParser->hasScriptsWaitingForStylesheets())
     792        return;
     793
    790794    // Any frame that hasn't completed yet?
    791795    if (!allChildrenAreComplete())
  • trunk/Source/WebCore/style/StyleScope.cpp

    r212556 r212614  
    171171
    172172// This method is called whenever a top-level stylesheet has finished loading.
    173 void Scope::removePendingSheet(RemovePendingSheetNotificationType notification)
     173void Scope::removePendingSheet()
    174174{
    175175    // Make sure we knew this sheet was pending, and that our count isn't out of sync.
     
    179179    if (m_pendingStyleSheetCount)
    180180        return;
    181 
    182     if (notification == RemovePendingSheetNotifyLater) {
    183         m_document.setNeedsNotifyRemoveAllPendingStylesheet();
    184         return;
    185     }
    186181
    187182    didChangeActiveStyleSheetCandidates();
  • trunk/Source/WebCore/style/StyleScope.h

    r212556 r212614  
    8383
    8484    void addPendingSheet() { m_pendingStyleSheetCount++; }
    85     enum RemovePendingSheetNotificationType {
    86         RemovePendingSheetNotifyImmediately,
    87         RemovePendingSheetNotifyLater
    88     };
    89     void removePendingSheet(RemovePendingSheetNotificationType = RemovePendingSheetNotifyImmediately);
     85    void removePendingSheet();
    9086
    9187    bool hasPendingSheets() const { return m_pendingStyleSheetCount > 0; }
Note: See TracChangeset for help on using the changeset viewer.