Skip to content
Permalink
Browse files
WebCore:
        Reviewed by Darin.

        Make page loads go fast.

        http://bugs.webkit.org/show_bug.cgi?id=17480

        - Implement speculative preloading. When a script load blocks the main parser, use a side
          parser to pick up more resources.
        - Implement per-host load queues, prioritize scripts and stylesheets over images.

        Depending on content and network latency this may speed things up quite a bit.

        * WebCore.xcodeproj/project.pbxproj:
        * dom/Document.cpp:
        (WebCore::Document::implicitClose):
        Clear the preloads after laoding completes.

        * html/HTMLLinkElement.cpp:
        (WebCore::HTMLLinkElement::parseMappedAttribute):
        (WebCore::HTMLLinkElement::tokenizeRelAttribute):
        * html/HTMLLinkElement.h:
        Make tokenizeRelAttribute() public static so it can be used from elsewhere.
        Eliminate a pointless bitfield so I can get references.

        * html/HTMLTokenizer.cpp:
        (WebCore::HTMLTokenizer::scriptHandler):
        (WebCore::HTMLTokenizer::scriptExecution):
        (WebCore::HTMLTokenizer::write):
        * html/HTMLTokenizer.h:
        Spin up the preload scanner whenever a script load blocks the parser. One scanner tracks the end of
        the document while temporary ones are created as needed to scan document.write() output.

        * html/PreloadScanner.cpp: Added.
        (WebCore::PreloadScanner::PreloadScanner):
        (WebCore::PreloadScanner::~PreloadScanner):
        (WebCore::PreloadScanner::begin):
        (WebCore::PreloadScanner::end):
        (WebCore::PreloadScanner::reset):
        (WebCore::PreloadScanner::write):
        (WebCore::isWhitespace):
        (WebCore::PreloadScanner::clearLastCharacters):
        (WebCore::PreloadScanner::rememberCharacter):
        (WebCore::PreloadScanner::lastCharactersMatch):
        (WebCore::legalEntityFor):
        (WebCore::PreloadScanner::consumeEntity):
        (WebCore::PreloadScanner::tokenize):
        (WebCore::PreloadScanner::processAttribute):
        (WebCore::PreloadScanner::emitCharacter):
        (WebCore::PreloadScanner::tokenizeCSS):
        (WebCore::PreloadScanner::emitTag):
        (WebCore::PreloadScanner::emitCSSRule):
        * html/PreloadScanner.h: Added.
        (WebCore::PreloadScanner::inProgress):
        (WebCore::PreloadScanner::):
        HTML5 tokenization plus some glue code. Fake CSS parsing thrown in just for fun.

        * loader/Cache.cpp:
        (WebCore::Cache::pruneDeadResources):
        Preloads have zero refcount, avoid kicking them out too early.

        * loader/CachedResource.cpp:
        (WebCore::CachedResource::CachedResource):
        (WebCore::CachedResource::ref):
        * loader/CachedResource.h:
        (WebCore::CachedResource::):
        (WebCore::CachedResource::preloadResult):
        (WebCore::CachedResource::setRequestedFromNetworkingLayer):
        (WebCore::CachedResource::canDelete):
        (WebCore::CachedResource::isPreloaded):
        (WebCore::CachedResource::increasePreloadCount):
        (WebCore::CachedResource::decreasePreloadCount):
        Keep track which resources are preloads. Avoid deleting them. Track
        at which point of the loading preloads get utilized to enable some interesting
        statistics.

        * loader/DocLoader.cpp:
        (WebCore::DocLoader::~DocLoader):
        (WebCore::DocLoader::checkForReload):
        (WebCore::DocLoader::registerPreload):
        (WebCore::DocLoader::clearPreloads):
        (WebCore::DocLoader::printPreloadStats):
        * loader/DocLoader.h:
        Ensure we utilize preloaded resources during reloads.
        Keep a list of all preloads in the document. Clear the preloads after
        parsing is complete. Some debug statistics.

        * loader/DocumentLoader.cpp:
        (WebCore::DocumentLoader::isLoadingInAPISense):
        Avoid signaling that loading is complete too early.

        * loader/loader.cpp:
        (WebCore::Loader::Loader):
        (WebCore::Loader::~Loader):
        (WebCore::Loader::determinePriority):
        (WebCore::Loader::load):
        (WebCore::Loader::scheduleServePendingRequests):
        (WebCore::Loader::requestTimerFired):
        (WebCore::Loader::servePendingRequests):
        (WebCore::Loader::cancelRequests):
        (WebCore::Loader::Host::Host):
        (WebCore::Loader::Host::~Host):
        (WebCore::Loader::Host::addRequest):
        (WebCore::Loader::Host::hasRequests):
        (WebCore::Loader::Host::servePendingRequests):
        (WebCore::Loader::Host::didFinishLoading):
        (WebCore::Loader::Host::didFail):
        (WebCore::Loader::Host::didReceiveResponse):
        (WebCore::Loader::Host::didReceiveData):
        (WebCore::Loader::Host::cancelPendingRequests):
        (WebCore::Loader::Host::cancelRequests):
        * loader/loader.h:
        (WebCore::Loader::):
        Distribute load requests to per-host priority queues. Limit the number of loads issued to the
        networking layer so we have better changes of getting important requests through first.
        Prioritize scripts > stylesheets > images.

WebKit/mac:

        Reviewed by Darin.

        * ForwardingHeaders/wtf/Deque.h: Added.



Canonical link: https://commits.webkit.org/24721@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@31038 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
anttijk committed Mar 13, 2008
1 parent ab0deb3 commit 2495e8dad689d876a60a34a36a0c6aa763d90af3
@@ -1,3 +1,121 @@
2008-03-13 Antti Koivisto <antti@apple.com>

Reviewed by Darin.

Make page loads go fast.

http://bugs.webkit.org/show_bug.cgi?id=17480

- Implement speculative preloading. When a script load blocks the main parser, use a side
parser to pick up more resources.
- Implement per-host load queues, prioritize scripts and stylesheets over images.

Depending on content and network latency this may speed things up quite a bit.

* WebCore.xcodeproj/project.pbxproj:
* dom/Document.cpp:
(WebCore::Document::implicitClose):
Clear the preloads after laoding completes.

* html/HTMLLinkElement.cpp:
(WebCore::HTMLLinkElement::parseMappedAttribute):
(WebCore::HTMLLinkElement::tokenizeRelAttribute):
* html/HTMLLinkElement.h:
Make tokenizeRelAttribute() public static so it can be used from elsewhere.
Eliminate a pointless bitfield so I can get references.

* html/HTMLTokenizer.cpp:
(WebCore::HTMLTokenizer::scriptHandler):
(WebCore::HTMLTokenizer::scriptExecution):
(WebCore::HTMLTokenizer::write):
* html/HTMLTokenizer.h:
Spin up the preload scanner whenever a script load blocks the parser. One scanner tracks the end of
the document while temporary ones are created as needed to scan document.write() output.

* html/PreloadScanner.cpp: Added.
(WebCore::PreloadScanner::PreloadScanner):
(WebCore::PreloadScanner::~PreloadScanner):
(WebCore::PreloadScanner::begin):
(WebCore::PreloadScanner::end):
(WebCore::PreloadScanner::reset):
(WebCore::PreloadScanner::write):
(WebCore::isWhitespace):
(WebCore::PreloadScanner::clearLastCharacters):
(WebCore::PreloadScanner::rememberCharacter):
(WebCore::PreloadScanner::lastCharactersMatch):
(WebCore::legalEntityFor):
(WebCore::PreloadScanner::consumeEntity):
(WebCore::PreloadScanner::tokenize):
(WebCore::PreloadScanner::processAttribute):
(WebCore::PreloadScanner::emitCharacter):
(WebCore::PreloadScanner::tokenizeCSS):
(WebCore::PreloadScanner::emitTag):
(WebCore::PreloadScanner::emitCSSRule):
* html/PreloadScanner.h: Added.
(WebCore::PreloadScanner::inProgress):
(WebCore::PreloadScanner::):
HTML5 tokenization plus some glue code. Fake CSS parsing thrown in just for fun.

* loader/Cache.cpp:
(WebCore::Cache::pruneDeadResources):
Preloads have zero refcount, avoid kicking them out too early.

* loader/CachedResource.cpp:
(WebCore::CachedResource::CachedResource):
(WebCore::CachedResource::ref):
* loader/CachedResource.h:
(WebCore::CachedResource::):
(WebCore::CachedResource::preloadResult):
(WebCore::CachedResource::setRequestedFromNetworkingLayer):
(WebCore::CachedResource::canDelete):
(WebCore::CachedResource::isPreloaded):
(WebCore::CachedResource::increasePreloadCount):
(WebCore::CachedResource::decreasePreloadCount):
Keep track which resources are preloads. Avoid deleting them. Track
at which point of the loading preloads get utilized to enable some interesting
statistics.

* loader/DocLoader.cpp:
(WebCore::DocLoader::~DocLoader):
(WebCore::DocLoader::checkForReload):
(WebCore::DocLoader::registerPreload):
(WebCore::DocLoader::clearPreloads):
(WebCore::DocLoader::printPreloadStats):
* loader/DocLoader.h:
Ensure we utilize preloaded resources during reloads.
Keep a list of all preloads in the document. Clear the preloads after
parsing is complete. Some debug statistics.

* loader/DocumentLoader.cpp:
(WebCore::DocumentLoader::isLoadingInAPISense):
Avoid signaling that loading is complete too early.

* loader/loader.cpp:
(WebCore::Loader::Loader):
(WebCore::Loader::~Loader):
(WebCore::Loader::determinePriority):
(WebCore::Loader::load):
(WebCore::Loader::scheduleServePendingRequests):
(WebCore::Loader::requestTimerFired):
(WebCore::Loader::servePendingRequests):
(WebCore::Loader::cancelRequests):
(WebCore::Loader::Host::Host):
(WebCore::Loader::Host::~Host):
(WebCore::Loader::Host::addRequest):
(WebCore::Loader::Host::hasRequests):
(WebCore::Loader::Host::servePendingRequests):
(WebCore::Loader::Host::didFinishLoading):
(WebCore::Loader::Host::didFail):
(WebCore::Loader::Host::didReceiveResponse):
(WebCore::Loader::Host::didReceiveData):
(WebCore::Loader::Host::cancelPendingRequests):
(WebCore::Loader::Host::cancelRequests):
* loader/loader.h:
(WebCore::Loader::):
Distribute load requests to per-host priority queues. Limit the number of loads issued to the
networking layer so we have better changes of getting important requests through first.
Prioritize scripts > stylesheets > images.

2008-03-13 David Hyatt <hyatt@apple.com>

This patch makes full page zoom work pretty well. It fixes repainting so that it works when transforms
@@ -3841,6 +3841,8 @@
E446143C0CD689CC00FADA75 /* JSHTMLSourceElement.h in Headers */ = {isa = PBXBuildFile; fileRef = E4B423720CBFB6E000AF2ECE /* JSHTMLSourceElement.h */; };
E44614510CD68A3500FADA75 /* RenderVideo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4B41E330CBFB60900AF2ECE /* RenderVideo.cpp */; };
E44614520CD68A3500FADA75 /* RenderVideo.h in Headers */ = {isa = PBXBuildFile; fileRef = E4B41E340CBFB60900AF2ECE /* RenderVideo.h */; };
E49626C20D80D94800E3405C /* PreloadScanner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4D4ABE00D7542F000F96869 /* PreloadScanner.cpp */; };
E49626C30D80D94900E3405C /* PreloadScanner.h in Headers */ = {isa = PBXBuildFile; fileRef = E4D4ABE10D7542F100F96869 /* PreloadScanner.h */; };
E4C279580CF9741900E97B98 /* RenderMedia.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4C279560CF9741900E97B98 /* RenderMedia.cpp */; };
E4C279590CF9741900E97B98 /* RenderMedia.h in Headers */ = {isa = PBXBuildFile; fileRef = E4C279570CF9741900E97B98 /* RenderMedia.h */; };
E4EEFFC80D34550C00469A58 /* JSAudioConstructor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4EEFFC60D34550C00469A58 /* JSAudioConstructor.cpp */; };
@@ -8041,6 +8043,8 @@
E4B423860CBFB73C00AF2ECE /* JSProgressEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSProgressEvent.h; sourceTree = "<group>"; };
E4C279560CF9741900E97B98 /* RenderMedia.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderMedia.cpp; sourceTree = "<group>"; };
E4C279570CF9741900E97B98 /* RenderMedia.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderMedia.h; sourceTree = "<group>"; };
E4D4ABE00D7542F000F96869 /* PreloadScanner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PreloadScanner.cpp; sourceTree = "<group>"; };
E4D4ABE10D7542F100F96869 /* PreloadScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreloadScanner.h; sourceTree = "<group>"; };
E4EEFFC60D34550C00469A58 /* JSAudioConstructor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSAudioConstructor.cpp; sourceTree = "<group>"; };
E4EEFFC70D34550C00469A58 /* JSAudioConstructor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSAudioConstructor.h; sourceTree = "<group>"; };
ED048ABB0833F132006E1E67 /* textAreaResizeCorner.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = textAreaResizeCorner.tiff; sourceTree = "<group>"; };
@@ -10248,6 +10252,8 @@
BCCD74DB0A4C8D35005FDA6D /* HTMLViewSourceDocument.h */,
E446139B0CD6331000FADA75 /* MediaError.h */,
E446139C0CD6331000FADA75 /* MediaError.idl */,
E4D4ABE00D7542F000F96869 /* PreloadScanner.cpp */,
E4D4ABE10D7542F100F96869 /* PreloadScanner.h */,
E446139D0CD6331000FADA75 /* TimeRanges.cpp */,
E446139E0CD6331000FADA75 /* TimeRanges.h */,
E446139F0CD6331000FADA75 /* TimeRanges.idl */,
@@ -14522,6 +14528,7 @@
1A569D230D7E2B82007C3983 /* runtime_object.h in Headers */,
1A569D250D7E2B82007C3983 /* runtime_root.h in Headers */,
BC6932740D7E293900AE44D1 /* JSDOMWindowBase.h in Headers */,
E49626C30D80D94900E3405C /* PreloadScanner.h in Headers */,
C02B14C20D81E02A00D8A970 /* JavaScriptDebugListener.h in Headers */,
C02B14C40D81E02A00D8A970 /* JavaScriptDebugServer.h in Headers */,
A9C6E4E40D745E05006442E9 /* MimeType.h in Headers */,
@@ -16153,6 +16160,7 @@
1A569D220D7E2B82007C3983 /* runtime_object.cpp in Sources */,
1A569D240D7E2B82007C3983 /* runtime_root.cpp in Sources */,
BC6932730D7E293900AE44D1 /* JSDOMWindowBase.cpp in Sources */,
E49626C20D80D94800E3405C /* PreloadScanner.cpp in Sources */,
C02B14C30D81E02A00D8A970 /* JavaScriptDebugServer.cpp in Sources */,
A9C6E4E30D745E05006442E9 /* MimeType.cpp in Sources */,
A9C6E4E70D745E18006442E9 /* MimeTypeArray.cpp in Sources */,
@@ -1503,6 +1503,9 @@ void Document::implicitClose()
delete m_tokenizer;
m_tokenizer = 0;

// Parser should have picked up all preloads by now
m_docLoader->clearPreloads();

// Create a body element if we don't already have one. See Radar 3758785.
if (!this->body() && isHTMLDocument()) {
if (Node* documentElement = this->documentElement()) {
@@ -104,7 +104,7 @@ StyleSheet* HTMLLinkElement::sheet() const
void HTMLLinkElement::parseMappedAttribute(MappedAttribute *attr)
{
if (attr->name() == relAttr) {
tokenizeRelAttribute(attr->value());
tokenizeRelAttribute(attr->value(), m_isStyleSheet, m_alternate, m_isIcon);
process();
} else if (attr->name() == hrefAttr) {
m_url = document()->completeURL(parseURL(attr->value())).string();
@@ -124,29 +124,32 @@ void HTMLLinkElement::parseMappedAttribute(MappedAttribute *attr)
}
}

void HTMLLinkElement::tokenizeRelAttribute(const AtomicString& relStr)
void HTMLLinkElement::tokenizeRelAttribute(const AtomicString& relStr, bool& styleSheet, bool& alternate, bool& icon)
{
m_isStyleSheet = m_isIcon = m_alternate = false;
styleSheet = false;
icon = false;
alternate = false;
String rel = relStr.string().lower();
if (rel == "stylesheet")
m_isStyleSheet = true;
styleSheet = true;
else if (rel == "icon" || rel == "shortcut icon")
m_isIcon = true;
else if (rel == "alternate stylesheet" || rel == "stylesheet alternate")
m_isStyleSheet = m_alternate = true;
else {
icon = true;
else if (rel == "alternate stylesheet" || rel == "stylesheet alternate") {
styleSheet = true;
alternate = true;
} else {
// Tokenize the rel attribute and set bits based on specific keywords that we find.
rel.replace('\n', ' ');
Vector<String> list;
rel.split(' ', list);
Vector<String>::const_iterator end = list.end();
for (Vector<String>::const_iterator it = list.begin(); it != end; ++it) {
if (*it == "stylesheet")
m_isStyleSheet = true;
styleSheet = true;
else if (*it == "alternate")
m_alternate = true;
alternate = true;
else if (*it == "icon")
m_isIcon = true;
icon = true;
}
}
}
@@ -91,7 +91,7 @@ class HTMLLinkElement : public HTMLElement, public CachedResourceClient {

virtual bool isURLAttribute(Attribute*) const;

void tokenizeRelAttribute(const AtomicString& rel);
static void tokenizeRelAttribute(const AtomicString& value, bool& stylesheet, bool& alternate, bool& icon);

protected:
CachedCSSStyleSheet* m_cachedSheet;
@@ -100,10 +100,10 @@ class HTMLLinkElement : public HTMLElement, public CachedResourceClient {
String m_type;
String m_media;
int m_disabledState; // 0=unset(default), 1=enabled via script, 2=disabled
bool m_loading : 1;
bool m_alternate : 1;
bool m_isStyleSheet : 1;
bool m_isIcon : 1;
bool m_loading;
bool m_alternate;
bool m_isStyleSheet;
bool m_isIcon;
};

} //namespace
@@ -41,6 +41,7 @@
#include "HTMLParser.h"
#include "HTMLScriptElement.h"
#include "HTMLViewSourceDocument.h"
#include "PreloadScanner.h"
#include "Settings.h"
#include "SystemTime.h"
#include "kjs_proxy.h"
@@ -499,8 +500,18 @@ HTMLTokenizer::State HTMLTokenizer::scriptHandler(State state)
write(prependingSrc, false);
state = m_state;
}
}

#if PRELOAD_SCANNER_ENABLED
if (!pendingScripts.isEmpty() && !m_executingScript) {
if (!m_preloadScanner)
m_preloadScanner.set(new PreloadScanner(m_doc));
if (!m_preloadScanner->inProgress()) {
m_preloadScanner->begin();
m_preloadScanner->write(pendingSrc);
}
}

#endif
currentPrependingSrc = savedPrependingSrc;

return state;
@@ -552,6 +563,15 @@ HTMLTokenizer::State HTMLTokenizer::scriptExecution(const String& str, State sta
currentPrependingSrc->append(prependingSrc);
else
pendingSrc.prepend(prependingSrc);

#if PRELOAD_SCANNER_ENABLED
// We are stuck waiting for another script. Lets check the source that
// was just document.write()n for anything to load.
PreloadScanner documentWritePreloadScanner(m_doc);
documentWritePreloadScanner.begin();
documentWritePreloadScanner.write(prependingSrc);
documentWritePreloadScanner.end();
#endif
} else {
m_state = state;
write(prependingSrc, false);
@@ -1582,10 +1602,20 @@ bool HTMLTokenizer::write(const SegmentedString& str, bool appendData)
// don't parse; we will do this later
if (currentPrependingSrc)
currentPrependingSrc->append(source);
else
else {
pendingSrc.append(source);
#if PRELOAD_SCANNER_ENABLED
if (m_preloadScanner && m_preloadScanner->inProgress() && appendData)
m_preloadScanner->write(source);
#endif
}
return false;
}

#if PRELOAD_SCANNER_ENABLED
if (m_preloadScanner && m_preloadScanner->inProgress() && appendData)
m_preloadScanner->end();
#endif

if (!src.isEmpty())
src.append(source);
@@ -33,6 +33,13 @@
#include <wtf/Vector.h>
#include <wtf/OwnPtr.h>

// FIXME: temporary, add PreloadScanner to all build files
#if PLATFORM(MAC)
#define PRELOAD_SCANNER_ENABLED 1
#else
#define PRELOAD_SCANNER_ENABLED 0
#endif

namespace WebCore {

class CachedScript;
@@ -43,6 +50,7 @@ class HTMLViewSourceDocument;
class FrameView;
class HTMLParser;
class Node;
class PreloadScanner;

/**
* @internal
@@ -402,6 +410,10 @@ class HTMLTokenizer : public Tokenizer, public CachedResourceClient {
HTMLParser* parser;
bool inWrite;
bool m_fragment;

#if PRELOAD_SCANNER_ENABLED
OwnPtr<PreloadScanner> m_preloadScanner;
#endif
};

void parseHTMLDocumentFragment(const String&, DocumentFragment*);

0 comments on commit 2495e8d

Please sign in to comment.