Changeset 31038 in webkit
- Timestamp:
- Mar 13, 2008 2:20:31 PM (16 years ago)
- Location:
- trunk
- Files:
-
- 3 added
- 16 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebCore/ChangeLog
r31037 r31038 1 2008-03-13 Antti Koivisto <antti@apple.com> 2 3 Reviewed by Darin. 4 5 Make page loads go fast. 6 7 http://bugs.webkit.org/show_bug.cgi?id=17480 8 9 - Implement speculative preloading. When a script load blocks the main parser, use a side 10 parser to pick up more resources. 11 - Implement per-host load queues, prioritize scripts and stylesheets over images. 12 13 Depending on content and network latency this may speed things up quite a bit. 14 15 * WebCore.xcodeproj/project.pbxproj: 16 * dom/Document.cpp: 17 (WebCore::Document::implicitClose): 18 Clear the preloads after laoding completes. 19 20 * html/HTMLLinkElement.cpp: 21 (WebCore::HTMLLinkElement::parseMappedAttribute): 22 (WebCore::HTMLLinkElement::tokenizeRelAttribute): 23 * html/HTMLLinkElement.h: 24 Make tokenizeRelAttribute() public static so it can be used from elsewhere. 25 Eliminate a pointless bitfield so I can get references. 26 27 * html/HTMLTokenizer.cpp: 28 (WebCore::HTMLTokenizer::scriptHandler): 29 (WebCore::HTMLTokenizer::scriptExecution): 30 (WebCore::HTMLTokenizer::write): 31 * html/HTMLTokenizer.h: 32 Spin up the preload scanner whenever a script load blocks the parser. One scanner tracks the end of 33 the document while temporary ones are created as needed to scan document.write() output. 34 35 * html/PreloadScanner.cpp: Added. 36 (WebCore::PreloadScanner::PreloadScanner): 37 (WebCore::PreloadScanner::~PreloadScanner): 38 (WebCore::PreloadScanner::begin): 39 (WebCore::PreloadScanner::end): 40 (WebCore::PreloadScanner::reset): 41 (WebCore::PreloadScanner::write): 42 (WebCore::isWhitespace): 43 (WebCore::PreloadScanner::clearLastCharacters): 44 (WebCore::PreloadScanner::rememberCharacter): 45 (WebCore::PreloadScanner::lastCharactersMatch): 46 (WebCore::legalEntityFor): 47 (WebCore::PreloadScanner::consumeEntity): 48 (WebCore::PreloadScanner::tokenize): 49 (WebCore::PreloadScanner::processAttribute): 50 (WebCore::PreloadScanner::emitCharacter): 51 (WebCore::PreloadScanner::tokenizeCSS): 52 (WebCore::PreloadScanner::emitTag): 53 (WebCore::PreloadScanner::emitCSSRule): 54 * html/PreloadScanner.h: Added. 55 (WebCore::PreloadScanner::inProgress): 56 (WebCore::PreloadScanner::): 57 HTML5 tokenization plus some glue code. Fake CSS parsing thrown in just for fun. 58 59 * loader/Cache.cpp: 60 (WebCore::Cache::pruneDeadResources): 61 Preloads have zero refcount, avoid kicking them out too early. 62 63 * loader/CachedResource.cpp: 64 (WebCore::CachedResource::CachedResource): 65 (WebCore::CachedResource::ref): 66 * loader/CachedResource.h: 67 (WebCore::CachedResource::): 68 (WebCore::CachedResource::preloadResult): 69 (WebCore::CachedResource::setRequestedFromNetworkingLayer): 70 (WebCore::CachedResource::canDelete): 71 (WebCore::CachedResource::isPreloaded): 72 (WebCore::CachedResource::increasePreloadCount): 73 (WebCore::CachedResource::decreasePreloadCount): 74 Keep track which resources are preloads. Avoid deleting them. Track 75 at which point of the loading preloads get utilized to enable some interesting 76 statistics. 77 78 * loader/DocLoader.cpp: 79 (WebCore::DocLoader::~DocLoader): 80 (WebCore::DocLoader::checkForReload): 81 (WebCore::DocLoader::registerPreload): 82 (WebCore::DocLoader::clearPreloads): 83 (WebCore::DocLoader::printPreloadStats): 84 * loader/DocLoader.h: 85 Ensure we utilize preloaded resources during reloads. 86 Keep a list of all preloads in the document. Clear the preloads after 87 parsing is complete. Some debug statistics. 88 89 * loader/DocumentLoader.cpp: 90 (WebCore::DocumentLoader::isLoadingInAPISense): 91 Avoid signaling that loading is complete too early. 92 93 * loader/loader.cpp: 94 (WebCore::Loader::Loader): 95 (WebCore::Loader::~Loader): 96 (WebCore::Loader::determinePriority): 97 (WebCore::Loader::load): 98 (WebCore::Loader::scheduleServePendingRequests): 99 (WebCore::Loader::requestTimerFired): 100 (WebCore::Loader::servePendingRequests): 101 (WebCore::Loader::cancelRequests): 102 (WebCore::Loader::Host::Host): 103 (WebCore::Loader::Host::~Host): 104 (WebCore::Loader::Host::addRequest): 105 (WebCore::Loader::Host::hasRequests): 106 (WebCore::Loader::Host::servePendingRequests): 107 (WebCore::Loader::Host::didFinishLoading): 108 (WebCore::Loader::Host::didFail): 109 (WebCore::Loader::Host::didReceiveResponse): 110 (WebCore::Loader::Host::didReceiveData): 111 (WebCore::Loader::Host::cancelPendingRequests): 112 (WebCore::Loader::Host::cancelRequests): 113 * loader/loader.h: 114 (WebCore::Loader::): 115 Distribute load requests to per-host priority queues. Limit the number of loads issued to the 116 networking layer so we have better changes of getting important requests through first. 117 Prioritize scripts > stylesheets > images. 118 1 119 2008-03-13 David Hyatt <hyatt@apple.com> 2 120 -
trunk/WebCore/WebCore.xcodeproj/project.pbxproj
r31014 r31038 3842 3842 E44614510CD68A3500FADA75 /* RenderVideo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4B41E330CBFB60900AF2ECE /* RenderVideo.cpp */; }; 3843 3843 E44614520CD68A3500FADA75 /* RenderVideo.h in Headers */ = {isa = PBXBuildFile; fileRef = E4B41E340CBFB60900AF2ECE /* RenderVideo.h */; }; 3844 E49626C20D80D94800E3405C /* PreloadScanner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4D4ABE00D7542F000F96869 /* PreloadScanner.cpp */; }; 3845 E49626C30D80D94900E3405C /* PreloadScanner.h in Headers */ = {isa = PBXBuildFile; fileRef = E4D4ABE10D7542F100F96869 /* PreloadScanner.h */; }; 3844 3846 E4C279580CF9741900E97B98 /* RenderMedia.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4C279560CF9741900E97B98 /* RenderMedia.cpp */; }; 3845 3847 E4C279590CF9741900E97B98 /* RenderMedia.h in Headers */ = {isa = PBXBuildFile; fileRef = E4C279570CF9741900E97B98 /* RenderMedia.h */; }; … … 8042 8044 E4C279560CF9741900E97B98 /* RenderMedia.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderMedia.cpp; sourceTree = "<group>"; }; 8043 8045 E4C279570CF9741900E97B98 /* RenderMedia.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderMedia.h; sourceTree = "<group>"; }; 8046 E4D4ABE00D7542F000F96869 /* PreloadScanner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PreloadScanner.cpp; sourceTree = "<group>"; }; 8047 E4D4ABE10D7542F100F96869 /* PreloadScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreloadScanner.h; sourceTree = "<group>"; }; 8044 8048 E4EEFFC60D34550C00469A58 /* JSAudioConstructor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSAudioConstructor.cpp; sourceTree = "<group>"; }; 8045 8049 E4EEFFC70D34550C00469A58 /* JSAudioConstructor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSAudioConstructor.h; sourceTree = "<group>"; }; … … 10249 10253 E446139B0CD6331000FADA75 /* MediaError.h */, 10250 10254 E446139C0CD6331000FADA75 /* MediaError.idl */, 10255 E4D4ABE00D7542F000F96869 /* PreloadScanner.cpp */, 10256 E4D4ABE10D7542F100F96869 /* PreloadScanner.h */, 10251 10257 E446139D0CD6331000FADA75 /* TimeRanges.cpp */, 10252 10258 E446139E0CD6331000FADA75 /* TimeRanges.h */, … … 14523 14529 1A569D250D7E2B82007C3983 /* runtime_root.h in Headers */, 14524 14530 BC6932740D7E293900AE44D1 /* JSDOMWindowBase.h in Headers */, 14531 E49626C30D80D94900E3405C /* PreloadScanner.h in Headers */, 14525 14532 C02B14C20D81E02A00D8A970 /* JavaScriptDebugListener.h in Headers */, 14526 14533 C02B14C40D81E02A00D8A970 /* JavaScriptDebugServer.h in Headers */, … … 16154 16161 1A569D240D7E2B82007C3983 /* runtime_root.cpp in Sources */, 16155 16162 BC6932730D7E293900AE44D1 /* JSDOMWindowBase.cpp in Sources */, 16163 E49626C20D80D94800E3405C /* PreloadScanner.cpp in Sources */, 16156 16164 C02B14C30D81E02A00D8A970 /* JavaScriptDebugServer.cpp in Sources */, 16157 16165 A9C6E4E30D745E05006442E9 /* MimeType.cpp in Sources */, -
trunk/WebCore/dom/Document.cpp
r31037 r31038 1504 1504 m_tokenizer = 0; 1505 1505 1506 // Parser should have picked up all preloads by now 1507 m_docLoader->clearPreloads(); 1508 1506 1509 // Create a body element if we don't already have one. See Radar 3758785. 1507 1510 if (!this->body() && isHTMLDocument()) { -
trunk/WebCore/html/HTMLLinkElement.cpp
r30673 r31038 105 105 { 106 106 if (attr->name() == relAttr) { 107 tokenizeRelAttribute(attr->value() );107 tokenizeRelAttribute(attr->value(), m_isStyleSheet, m_alternate, m_isIcon); 108 108 process(); 109 109 } else if (attr->name() == hrefAttr) { … … 125 125 } 126 126 127 void HTMLLinkElement::tokenizeRelAttribute(const AtomicString& relStr) 128 { 129 m_isStyleSheet = m_isIcon = m_alternate = false; 127 void HTMLLinkElement::tokenizeRelAttribute(const AtomicString& relStr, bool& styleSheet, bool& alternate, bool& icon) 128 { 129 styleSheet = false; 130 icon = false; 131 alternate = false; 130 132 String rel = relStr.string().lower(); 131 133 if (rel == "stylesheet") 132 m_isStyleSheet = true;134 styleSheet = true; 133 135 else if (rel == "icon" || rel == "shortcut icon") 134 m_isIcon = true; 135 else if (rel == "alternate stylesheet" || rel == "stylesheet alternate") 136 m_isStyleSheet = m_alternate = true; 137 else { 136 icon = true; 137 else if (rel == "alternate stylesheet" || rel == "stylesheet alternate") { 138 styleSheet = true; 139 alternate = true; 140 } else { 138 141 // Tokenize the rel attribute and set bits based on specific keywords that we find. 139 142 rel.replace('\n', ' '); … … 143 146 for (Vector<String>::const_iterator it = list.begin(); it != end; ++it) { 144 147 if (*it == "stylesheet") 145 m_isStyleSheet = true;148 styleSheet = true; 146 149 else if (*it == "alternate") 147 m_alternate = true;150 alternate = true; 148 151 else if (*it == "icon") 149 m_isIcon = true;152 icon = true; 150 153 } 151 154 } -
trunk/WebCore/html/HTMLLinkElement.h
r30243 r31038 92 92 virtual bool isURLAttribute(Attribute*) const; 93 93 94 void tokenizeRelAttribute(const AtomicString& rel);94 static void tokenizeRelAttribute(const AtomicString& value, bool& stylesheet, bool& alternate, bool& icon); 95 95 96 96 protected: … … 101 101 String m_media; 102 102 int m_disabledState; // 0=unset(default), 1=enabled via script, 2=disabled 103 bool m_loading : 1;104 bool m_alternate : 1;105 bool m_isStyleSheet : 1;106 bool m_isIcon : 1;103 bool m_loading; 104 bool m_alternate; 105 bool m_isStyleSheet; 106 bool m_isIcon; 107 107 }; 108 108 -
trunk/WebCore/html/HTMLTokenizer.cpp
r30928 r31038 42 42 #include "HTMLScriptElement.h" 43 43 #include "HTMLViewSourceDocument.h" 44 #include "PreloadScanner.h" 44 45 #include "Settings.h" 45 46 #include "SystemTime.h" … … 500 501 state = m_state; 501 502 } 502 } 503 503 } 504 505 #if PRELOAD_SCANNER_ENABLED 506 if (!pendingScripts.isEmpty() && !m_executingScript) { 507 if (!m_preloadScanner) 508 m_preloadScanner.set(new PreloadScanner(m_doc)); 509 if (!m_preloadScanner->inProgress()) { 510 m_preloadScanner->begin(); 511 m_preloadScanner->write(pendingSrc); 512 } 513 } 514 #endif 504 515 currentPrependingSrc = savedPrependingSrc; 505 516 … … 553 564 else 554 565 pendingSrc.prepend(prependingSrc); 566 567 #if PRELOAD_SCANNER_ENABLED 568 // We are stuck waiting for another script. Lets check the source that 569 // was just document.write()n for anything to load. 570 PreloadScanner documentWritePreloadScanner(m_doc); 571 documentWritePreloadScanner.begin(); 572 documentWritePreloadScanner.write(prependingSrc); 573 documentWritePreloadScanner.end(); 574 #endif 555 575 } else { 556 576 m_state = state; … … 1583 1603 if (currentPrependingSrc) 1584 1604 currentPrependingSrc->append(source); 1585 else 1605 else { 1586 1606 pendingSrc.append(source); 1607 #if PRELOAD_SCANNER_ENABLED 1608 if (m_preloadScanner && m_preloadScanner->inProgress() && appendData) 1609 m_preloadScanner->write(source); 1610 #endif 1611 } 1587 1612 return false; 1588 1613 } 1614 1615 #if PRELOAD_SCANNER_ENABLED 1616 if (m_preloadScanner && m_preloadScanner->inProgress() && appendData) 1617 m_preloadScanner->end(); 1618 #endif 1589 1619 1590 1620 if (!src.isEmpty()) -
trunk/WebCore/html/HTMLTokenizer.h
r30431 r31038 34 34 #include <wtf/OwnPtr.h> 35 35 36 // FIXME: temporary, add PreloadScanner to all build files 37 #if PLATFORM(MAC) 38 #define PRELOAD_SCANNER_ENABLED 1 39 #else 40 #define PRELOAD_SCANNER_ENABLED 0 41 #endif 42 36 43 namespace WebCore { 37 44 … … 44 51 class HTMLParser; 45 52 class Node; 53 class PreloadScanner; 46 54 47 55 /** … … 403 411 bool inWrite; 404 412 bool m_fragment; 413 414 #if PRELOAD_SCANNER_ENABLED 415 OwnPtr<PreloadScanner> m_preloadScanner; 416 #endif 405 417 }; 406 418 -
trunk/WebCore/loader/Cache.cpp
r30564 r31038 230 230 while (current) { 231 231 CachedResource* prev = current->m_prevInAllResourcesList; 232 if (!current->referenced() && current->isLoaded() && current->decodedSize()) {232 if (!current->referenced() && !current->isPreloaded() && current->isLoaded() && current->decodedSize()) { 233 233 // Destroy our decoded data. This will remove us from 234 234 // m_liveDecodedResources, and possibly move us to a differnt … … 246 246 while (current) { 247 247 CachedResource* prev = current->m_prevInAllResourcesList; 248 if (!current->referenced() ) {248 if (!current->referenced() && !current->isPreloaded()) { 249 249 remove(current); 250 250 -
trunk/WebCore/loader/CachedResource.cpp
r28639 r31038 40 40 , m_lastDecodedAccessTime(0) 41 41 , m_sendResourceLoadCallbacks(sendResourceLoadCallbacks) 42 , m_preloadCount(0) 43 , m_preloadResult(PreloadNotReferenced) 44 , m_requestedFromNetworkingLayer(false) 42 45 , m_inCache(forCache) 43 46 , m_docLoader(0) … … 102 105 void CachedResource::ref(CachedResourceClient *c) 103 106 { 107 if (m_preloadResult == PreloadNotReferenced) { 108 if (isLoaded()) 109 m_preloadResult = PreloadReferencedWhileComplete; 110 else if (m_requestedFromNetworkingLayer) 111 m_preloadResult = PreloadReferencedWhileLoading; 112 else 113 m_preloadResult = PreloadReferenced; 114 } 104 115 if (!referenced() && inCache()) 105 116 cache()->addToLiveResourcesSize(this); -
trunk/WebCore/loader/CachedResource.h
r28639 r31038 80 80 void deref(CachedResourceClient*); 81 81 bool referenced() const { return !m_clients.isEmpty(); } 82 83 enum PreloadResult { 84 PreloadNotReferenced, 85 PreloadReferenced, 86 PreloadReferencedWhileLoading, 87 PreloadReferencedWhileComplete 88 }; 89 PreloadResult preloadResult() const { return m_preloadResult; } 90 void setRequestedFromNetworkingLayer() { m_requestedFromNetworkingLayer = true; } 91 82 92 virtual void allReferencesRemoved() {}; 83 93 … … 118 128 const ResourceResponse& response() const { return m_response; } 119 129 120 bool canDelete() const { return !referenced() && !m_request ; }130 bool canDelete() const { return !referenced() && !m_request && !m_preloadCount; } 121 131 122 132 bool isExpired() const; … … 136 146 137 147 void setDocLoader(DocLoader* docLoader) { m_docLoader = docLoader; } 148 149 bool isPreloaded() const { return m_preloadCount; } 150 void increasePreloadCount() { ++m_preloadCount; } 151 void decreasePreloadCount() { ASSERT(m_preloadCount); --m_preloadCount; } 138 152 139 153 protected: … … 164 178 165 179 bool m_sendResourceLoadCallbacks; 180 181 unsigned m_preloadCount; 182 PreloadResult m_preloadResult; 183 bool m_requestedFromNetworkingLayer; 184 166 185 protected: 167 186 bool m_inCache; -
trunk/WebCore/loader/DocLoader.cpp
r30243 r31038 33 33 #include "CachedScript.h" 34 34 #include "CachedXSLStyleSheet.h" 35 #include "CString.h" 35 36 #include "Document.h" 36 37 #include "Frame.h" 37 38 #include "FrameLoader.h" 38 39 #include "loader.h" 40 41 #define PRELOAD_DEBUG 0 39 42 40 43 namespace WebCore { … … 55 58 DocLoader::~DocLoader() 56 59 { 60 clearPreloads(); 57 61 HashMap<String, CachedResource*>::iterator end = m_docResources.end(); 58 62 for (HashMap<String, CachedResource*>::iterator it = m_docResources.begin(); it != end; ++it) … … 72 76 if (!m_reloadedURLs.contains(fullURL.string())) { 73 77 CachedResource* existing = cache()->resourceForURL(fullURL.string()); 74 if (existing && existing->isExpired() ) {78 if (existing && existing->isExpired() && !existing->isPreloaded()) { 75 79 cache()->remove(existing); 76 80 m_reloadedURLs.add(fullURL.string()); … … 79 83 } else if ((m_cachePolicy == CachePolicyReload) || (m_cachePolicy == CachePolicyRefresh)) { 80 84 if (!m_reloadedURLs.contains(fullURL.string())) { 81 CachedResource* existing = cache()->resourceForURL(fullURL.string()); 82 if (existing) 83 cache()->remove(existing); 84 m_reloadedURLs.add(fullURL.string()); 85 CachedResource* existing = cache()->resourceForURL(fullURL.string()); 86 if (existing && !existing->isPreloaded()) { 87 cache()->remove(existing); 88 m_reloadedURLs.add(fullURL.string()); 89 } 85 90 } 86 91 } … … 244 249 return m_requestCount; 245 250 } 246 247 } 251 252 void DocLoader::registerPreload(CachedResource* resource) 253 { 254 if (!resource || resource->isLoaded() || m_preloads.contains(resource)) 255 return; 256 resource->increasePreloadCount(); 257 m_preloads.add(resource); 258 #if PRELOAD_DEBUG 259 printf("PRELOADING %s\n", resource->url().latin1().data()); 260 #endif 261 } 262 263 void DocLoader::clearPreloads() 264 { 265 #if PRELOAD_DEBUG 266 printPreloadStats(); 267 #endif 268 ListHashSet<CachedResource*>::iterator end = m_preloads.end(); 269 for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) { 270 CachedResource* res = *it; 271 if (res->preloadResult() == CachedResource::PreloadNotReferenced) 272 cache()->remove(res); 273 res->decreasePreloadCount(); 274 } 275 m_preloads.clear(); 276 } 277 278 #if PRELOAD_DEBUG 279 void DocLoader::printPreloadStats() 280 { 281 unsigned scripts = 0; 282 unsigned scriptMisses = 0; 283 unsigned stylesheets = 0; 284 unsigned stylesheetMisses = 0; 285 unsigned images = 0; 286 unsigned imageMisses = 0; 287 ListHashSet<CachedResource*>::iterator end = m_preloads.end(); 288 for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) { 289 CachedResource* res = *it; 290 if (res->preloadResult() == CachedResource::PreloadNotReferenced) 291 printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data()); 292 else if (res->preloadResult() == CachedResource::PreloadReferencedWhileComplete) 293 printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data()); 294 else if (res->preloadResult() == CachedResource::PreloadReferencedWhileLoading 295 printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data()); 296 297 if (res->type() == CachedResource::Script) { 298 scripts++; 299 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading) 300 scriptMisses++; 301 } else if (res->type() == CachedResource::CSSStyleSheet) { 302 stylesheets++; 303 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading) 304 stylesheetMisses++; 305 } else { 306 images++; 307 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading) 308 imageMisses++; 309 } 310 311 if (res->errorOccurred()) 312 cache()->remove(res); 313 314 res->decreasePreloadCount(); 315 } 316 m_preloads.clear(); 317 318 if (scripts) 319 printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts); 320 if (stylesheets) 321 printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets); 322 if (images) 323 printf("IMAGES: %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images); 324 } 325 #endif 326 327 } -
trunk/WebCore/loader/DocLoader.h
r26484 r31038 31 31 #include <wtf/HashMap.h> 32 32 #include <wtf/HashSet.h> 33 #include <wtf/ListHashSet.h> 33 34 34 35 namespace WebCore { … … 93 94 void decrementRequestCount(); 94 95 int requestCount(); 96 97 void clearPreloads(); 98 void registerPreload(CachedResource*); 99 void printPreloadStats(); 100 95 101 private: 96 102 CachedResource* requestResource(CachedResource::Type, const String& url, const String* charset = 0, bool skipCanLoadCheck = false, bool sendResourceLoadCallbacks = true); … … 108 114 int m_requestCount; 109 115 116 ListHashSet<CachedResource*> m_preloads; 117 110 118 //29 bits left 111 119 bool m_autoLoadImages : 1; -
trunk/WebCore/loader/DocumentLoader.cpp
r31035 r31038 31 31 32 32 #include "CachedPage.h" 33 #include "DocLoader.h" 33 34 #include "Document.h" 34 35 #include "Event.h" … … 427 428 if (!m_subresourceLoaders.isEmpty()) 428 429 return true; 429 if (Document* doc = m_frame->document()) 430 if (Document* doc = m_frame->document()) { 431 if (doc->docLoader()->requestCount()) 432 return true; 430 433 if (Tokenizer* tok = doc->tokenizer()) 431 434 if (tok->processingData()) 432 435 return true; 436 } 433 437 } 434 438 return frameLoader()->subframeIsLoading(); -
trunk/WebCore/loader/loader.cpp
r31034 r31038 28 28 #include "CachedImage.h" 29 29 #include "CachedResource.h" 30 #include "CString.h" 30 31 #include "DocLoader.h" 31 32 #include "Frame.h" … … 40 41 #include <wtf/Vector.h> 41 42 43 #define REQUEST_MANAGEMENT_ENABLED 1 44 #define REQUEST_DEBUG 0 45 42 46 namespace WebCore { 43 47 48 #if REQUEST_MANAGEMENT_ENABLED 49 // Match the parallel connection count used by the networking layer 50 // FIXME should not hardcode something like this 51 static const unsigned maxRequestsInFlightPerHost = 4; 52 // Having a limit might still help getting more important resources first 53 static const unsigned maxRequestsInFlightForNonHTTPProtocols = 20; 54 #else 55 static const unsigned maxRequestsInFlightPerHost = 10000; 56 static const unsigned maxRequestsInFlightForNonHTTPProtocols = 10000; 57 #endif 58 59 44 60 Loader::Loader() 45 { 46 m_requestsPending.setAutoDelete(true); 61 : m_nonHTTPProtocolHost(maxRequestsInFlightForNonHTTPProtocols) 62 , m_requestTimer(this, &Loader::requestTimerFired) 63 { 47 64 } 48 65 49 66 Loader::~Loader() 50 { 51 deleteAllValues(m_requestsLoading); 52 } 53 54 void Loader::load(DocLoader* dl, CachedResource* object, bool incremental, bool skipCanLoadCheck, bool sendResourceLoadCallbacks) 55 { 56 ASSERT(dl); 57 Request* req = new Request(dl, object, incremental, skipCanLoadCheck, sendResourceLoadCallbacks); 58 m_requestsPending.append(req); 59 dl->incrementRequestCount(); 67 { 68 ASSERT_NOT_REACHED(); 69 } 70 71 Loader::Priority Loader::determinePriority(const CachedResource* resource) const 72 { 73 #if REQUEST_MANAGEMENT_ENABLED 74 switch (resource->type()) { 75 case CachedResource::Script: 76 #if ENABLE(XBL) 77 case CachedResource::XBL: 78 #endif 79 return High; 80 case CachedResource::CSSStyleSheet: 81 case CachedResource::FontResource: 82 #if ENABLE(XSLT) 83 case CachedResource::XSLStyleSheet: 84 #endif 85 return Medium; 86 case CachedResource::ImageResource: 87 return Low; 88 } 89 ASSERT_NOT_REACHED(); 90 return Low; 91 #else 92 return High; 93 #endif 94 } 95 96 void Loader::load(DocLoader* docLoader, CachedResource* resource, bool incremental, bool skipCanLoadCheck, bool sendResourceLoadCallbacks) 97 { 98 ASSERT(docLoader); 99 Request* request = new Request(docLoader, resource, incremental, skipCanLoadCheck, sendResourceLoadCallbacks); 100 101 Host* host; 102 KURL url(resource->url()); 103 bool isHTTP = url.protocolIs("http") || url.protocolIs("https"); 104 if (isHTTP) { 105 String hostName = url.host(); 106 host = m_hosts.get(hostName); 107 if (!host) { 108 host = new Host(maxRequestsInFlightPerHost); 109 m_hosts.add(hostName, host); 110 } 111 } else 112 host = &m_nonHTTPProtocolHost; 113 114 Priority priority = determinePriority(resource); 115 host->addRequest(request, priority); 116 docLoader->incrementRequestCount(); 117 118 if (priority > Low || !isHTTP) { 119 // Try to request important resources immediately 120 host->servePendingRequests(priority); 121 } else { 122 // Handle asynchronously so early low priority requests don't get scheduled before later high priority ones 123 scheduleServePendingRequests(); 124 } 125 } 126 127 void Loader::scheduleServePendingRequests() 128 { 129 if (!m_requestTimer.isActive()) 130 m_requestTimer.startOneShot(0); 131 } 132 133 void Loader::requestTimerFired(Timer<Loader>*) 134 { 60 135 servePendingRequests(); 61 136 } 62 137 63 void Loader::servePendingRequests() 64 { 65 while (!m_requestsPending.isEmpty()) { 66 // get the first pending request 67 Request* req = m_requestsPending.take(0); 68 DocLoader* dl = req->docLoader(); 69 dl->decrementRequestCount(); 70 71 ResourceRequest request(req->cachedResource()->url()); 72 73 if (!req->cachedResource()->accept().isEmpty()) 74 request.setHTTPAccept(req->cachedResource()->accept()); 75 76 KURL r = dl->doc()->url(); 77 if ((r.protocolIs("http") || r.protocolIs("https")) && r.path().isEmpty()) 78 r.setPath("/"); 79 request.setHTTPReferrer(r.string()); 80 81 RefPtr<SubresourceLoader> loader = SubresourceLoader::create(dl->doc()->frame(), 82 this, request, req->shouldSkipCanLoadCheck(), req->sendResourceLoadCallbacks()); 83 138 void Loader::servePendingRequests(Priority minimumPriority) 139 { 140 m_requestTimer.stop(); 141 142 m_nonHTTPProtocolHost.servePendingRequests(minimumPriority); 143 144 HostMap::iterator end = m_hosts.end(); 145 Vector<String> toRemove; 146 for (HostMap::iterator it = m_hosts.begin(); it != end; ++it) { 147 Host* host = it->second; 148 if (host->hasRequests()) 149 host->servePendingRequests(minimumPriority); 150 else 151 toRemove.append(it->first); 152 } 153 for (unsigned n = 0; n < toRemove.size(); ++n) { 154 HostMap::iterator it = m_hosts.find(toRemove[n]); 155 delete it->second; 156 m_hosts.remove(it); 157 } 158 } 159 160 void Loader::cancelRequests(DocLoader* docLoader) 161 { 162 if (m_nonHTTPProtocolHost.hasRequests()) 163 m_nonHTTPProtocolHost.cancelRequests(docLoader); 164 165 HostMap::iterator end = m_hosts.end(); 166 for (HostMap::iterator it = m_hosts.begin(); it != end; ++it) { 167 Host* host = it->second; 168 if (host->hasRequests()); 169 host->cancelRequests(docLoader); 170 } 171 172 scheduleServePendingRequests(); 173 174 if (docLoader->loadInProgress()) 175 ASSERT(docLoader->requestCount() == 1); 176 else 177 ASSERT(docLoader->requestCount() == 0); 178 } 179 180 Loader::Host::Host(unsigned maxRequestsInFlight) 181 : m_maxRequestsInFlight(maxRequestsInFlight) 182 { 183 } 184 185 Loader::Host::~Host() 186 { 187 ASSERT(m_requestsLoading.isEmpty()); 188 for (unsigned p = 0; p <= High; p++) 189 ASSERT(m_requestsPending[p].isEmpty()); 190 } 191 192 void Loader::Host::addRequest(Request* request, Priority priority) 193 { 194 m_requestsPending[priority].append(request); 195 } 196 197 bool Loader::Host::hasRequests() const 198 { 199 if (!m_requestsLoading.isEmpty()) 200 return true; 201 for (unsigned p = 0; p <= High; p++) { 202 if (!m_requestsPending[p].isEmpty()) 203 return true; 204 } 205 return false; 206 } 207 208 void Loader::Host::servePendingRequests(Loader::Priority minimumPriority) 209 { 210 for (int priority = High; priority >= minimumPriority; --priority) 211 servePendingRequests(m_requestsPending[priority]); 212 } 213 214 void Loader::Host::servePendingRequests(RequestQueue& requestsPending) 215 { 216 while (m_requestsLoading.size() < m_maxRequestsInFlight && !requestsPending.isEmpty()) { 217 Request* request = requestsPending.first(); 218 requestsPending.removeFirst(); 219 220 DocLoader* docLoader = request->docLoader(); 221 222 ResourceRequest resourceRequest(request->cachedResource()->url()); 223 224 if (!request->cachedResource()->accept().isEmpty()) 225 resourceRequest.setHTTPAccept(request->cachedResource()->accept()); 226 227 KURL referrer = docLoader->doc()->url(); 228 if ((referrer.protocolIs("http") || referrer.protocolIs("https")) && referrer.path().isEmpty()) 229 referrer.setPath("/"); 230 resourceRequest.setHTTPReferrer(referrer.string()); 231 232 RefPtr<SubresourceLoader> loader = SubresourceLoader::create(docLoader->doc()->frame(), 233 this, resourceRequest, request->shouldSkipCanLoadCheck(), request->sendResourceLoadCallbacks()); 84 234 if (loader) { 85 m_requestsLoading.add(loader.release(), req); 86 dl->incrementRequestCount(); 87 break; 235 m_requestsLoading.add(loader.release(), request); 236 request->cachedResource()->setRequestedFromNetworkingLayer(); 237 #if REQUEST_DEBUG 238 printf("HOST %s COUNT %d LOADING %s\n", resourceRequest.url().host().latin1().data(), m_requestsLoading.size(), req->cachedResource()->url().latin1().data()); 239 #endif 240 } else { 241 docLoader->decrementRequestCount(); 242 docLoader->setLoadInProgress(true); 243 request->cachedResource()->error(); 244 docLoader->setLoadInProgress(false); 245 delete request; 88 246 } 89 90 dl->setLoadInProgress(true); 91 req->cachedResource()->error(); 92 dl->setLoadInProgress(false); 93 94 delete req; 95 } 96 } 97 98 void Loader::didFinishLoading(SubresourceLoader* loader) 247 } 248 } 249 250 void Loader::Host::didFinishLoading(SubresourceLoader* loader) 99 251 { 100 252 RequestMap::iterator i = m_requestsLoading.find(loader); … … 102 254 return; 103 255 104 Request* req = i->second;256 Request* request = i->second; 105 257 m_requestsLoading.remove(i); 106 DocLoader* docLoader = req ->docLoader();107 if (!req ->isMultipart())258 DocLoader* docLoader = request->docLoader(); 259 if (!request->isMultipart()) 108 260 docLoader->decrementRequestCount(); 109 261 110 CachedResource* object = req->cachedResource(); 262 CachedResource* resource = request->cachedResource(); 263 delete request; 111 264 112 265 // If we got a 4xx response, we're pretending to have received a network 113 266 // error, so we can't send the successful data() and finish() callbacks. 114 if (! object->errorOccurred()) {267 if (!resource->errorOccurred()) { 115 268 docLoader->setLoadInProgress(true); 116 object->data(loader->resourceData(), true);117 object->finish();269 resource->data(loader->resourceData(), true); 270 resource->finish(); 118 271 } 119 272 120 273 docLoader->setLoadInProgress(false); 121 274 122 delete req; 123 275 #if REQUEST_DEBUG 276 KURL u(resource->url()); 277 printf("HOST %s COUNT %d RECEIVED %s\n", u.host().latin1().data(), m_requestsLoading.size(), resource->url().latin1().data()); 278 #endif 124 279 servePendingRequests(); 125 280 } 126 281 127 void Loader:: didFail(SubresourceLoader* loader, const ResourceError&)282 void Loader::Host::didFail(SubresourceLoader* loader, const ResourceError&) 128 283 { 129 284 didFail(loader); 130 285 } 131 286 132 void Loader:: didFail(SubresourceLoader* loader, bool cancelled)287 void Loader::Host::didFail(SubresourceLoader* loader, bool cancelled) 133 288 { 134 289 RequestMap::iterator i = m_requestsLoading.find(loader); … … 136 291 return; 137 292 138 Request* req = i->second;293 Request* request = i->second; 139 294 m_requestsLoading.remove(i); 140 DocLoader* docLoader = req ->docLoader();141 if (!req ->isMultipart())295 DocLoader* docLoader = request->docLoader(); 296 if (!request->isMultipart()) 142 297 docLoader->decrementRequestCount(); 143 298 144 CachedResource* object = req->cachedResource(); 299 CachedResource* resource = request->cachedResource(); 300 delete request; 145 301 146 302 if (!cancelled) { 147 303 docLoader->setLoadInProgress(true); 148 object->error();304 resource->error(); 149 305 } 150 306 151 307 docLoader->setLoadInProgress(false); 152 cache()->remove(object); 153 154 delete req; 308 if (cancelled || !resource->isPreloaded()) 309 cache()->remove(resource); 155 310 156 311 servePendingRequests(); 157 312 } 158 313 159 void Loader:: didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response)160 { 161 Request* req = m_requestsLoading.get(loader);314 void Loader::Host::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response) 315 { 316 Request* request = m_requestsLoading.get(loader); 162 317 163 318 // FIXME: This is a workaround for <rdar://problem/5236843> 164 319 // If a load starts while the frame is still in the provisional state 165 320 // (this can be the case when loading the user style sheet), committing the load then causes all 166 // requests to be removed from the m_requestsLoading map. This means that req might be null here.321 // requests to be removed from the m_requestsLoading map. This means that request might be null here. 167 322 // In that case we just return early. 168 // ASSERT(req );169 if (!req )170 return; 171 req ->cachedResource()->setResponse(response);323 // ASSERT(request); 324 if (!request) 325 return; 326 request->cachedResource()->setResponse(response); 172 327 173 328 String encoding = response.textEncodingName(); 174 329 if (!encoding.isNull()) 175 req ->cachedResource()->setEncoding(encoding);176 177 if (req ->isMultipart()) {178 ASSERT(req ->cachedResource()->isImage());179 static_cast<CachedImage*>(req ->cachedResource())->clear();180 if (req ->docLoader()->frame())181 req ->docLoader()->frame()->loader()->checkCompleted();330 request->cachedResource()->setEncoding(encoding); 331 332 if (request->isMultipart()) { 333 ASSERT(request->cachedResource()->isImage()); 334 static_cast<CachedImage*>(request->cachedResource())->clear(); 335 if (request->docLoader()->frame()) 336 request->docLoader()->frame()->loader()->checkCompleted(); 182 337 } else if (response.isMultipart()) { 183 req ->setIsMultipart(true);338 request->setIsMultipart(true); 184 339 185 340 // We don't count multiParts in a DocLoader's request count 186 req ->docLoader()->decrementRequestCount();341 request->docLoader()->decrementRequestCount(); 187 342 188 343 // If we get a multipart response, we must have a handle 189 344 ASSERT(loader->handle()); 190 if (!req ->cachedResource()->isImage())345 if (!request->cachedResource()->isImage()) 191 346 loader->handle()->cancel(); 192 347 } 193 348 } 194 349 195 void Loader:: didReceiveData(SubresourceLoader* loader, const char* data, int size)350 void Loader::Host::didReceiveData(SubresourceLoader* loader, const char* data, int size) 196 351 { 197 352 Request* request = m_requestsLoading.get(loader); … … 199 354 return; 200 355 201 CachedResource* object= request->cachedResource();202 if ( object->errorOccurred())203 return; 204 205 if ( object->response().httpStatusCode() / 100 == 4) {356 CachedResource* resource = request->cachedResource(); 357 if (resource->errorOccurred()) 358 return; 359 360 if (resource->response().httpStatusCode() / 100 == 4) { 206 361 // Treat a 4xx response like a network error. 207 object->error();362 resource->error(); 208 363 return; 209 364 } … … 214 369 // The resource data will change as the next part is loaded, so we need to make a copy. 215 370 RefPtr<SharedBuffer> copiedData = SharedBuffer::create(data, size); 216 object->data(copiedData.release(), true);371 resource->data(copiedData.release(), true); 217 372 } else if (request->isIncremental()) 218 object->data(loader->resourceData(), false); 219 } 220 221 void Loader::cancelRequests(DocLoader* dl) 222 { 223 DeprecatedPtrListIterator<Request> pIt(m_requestsPending); 224 while (pIt.current()) { 225 if (pIt.current()->docLoader() == dl) { 226 cache()->remove(pIt.current()->cachedResource()); 227 m_requestsPending.remove(pIt); 228 dl->decrementRequestCount(); 373 resource->data(loader->resourceData(), false); 374 } 375 376 void Loader::Host::cancelPendingRequests(RequestQueue& requestsPending, DocLoader* docLoader) 377 { 378 RequestQueue remaining; 379 RequestQueue::iterator end = requestsPending.end(); 380 for (RequestQueue::iterator it = requestsPending.begin(); it != end; ++it) { 381 Request* request = *it; 382 if (request->docLoader() == docLoader) { 383 cache()->remove(request->cachedResource()); 384 delete request; 385 docLoader->decrementRequestCount(); 229 386 } else 230 ++pIt; 231 } 387 remaining.append(request); 388 } 389 requestsPending.swap(remaining); 390 } 391 392 void Loader::Host::cancelRequests(DocLoader* docLoader) 393 { 394 for (unsigned p = 0; p <= High; p++) 395 cancelPendingRequests(m_requestsPending[p], docLoader); 232 396 233 397 Vector<SubresourceLoader*, 256> loadersToCancel; … … 236 400 for (RequestMap::iterator i = m_requestsLoading.begin(); i != end; ++i) { 237 401 Request* r = i->second; 238 if (r->docLoader() == d l)402 if (r->docLoader() == docLoader) 239 403 loadersToCancel.append(i->first.get()); 240 404 } … … 244 408 didFail(loader, true); 245 409 } 246 247 if (dl->loadInProgress())248 ASSERT(dl->requestCount() == 1);249 else250 ASSERT(dl->requestCount() == 0);251 410 } 252 411 -
trunk/WebCore/loader/loader.h
r25754 r31038 2 2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) 3 3 Copyright (C) 2001 Dirk Mueller <mueller@kde.org> 4 Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.4 Copyright (C) 2004, 2006, 2007, 2008 Apple Inc. All rights reserved. 5 5 6 6 This library is free software; you can redistribute it and/or … … 23 23 #define loader_h 24 24 25 #include "DeprecatedPtrList.h" 25 #include "PlatformString.h" 26 #include "StringHash.h" 26 27 #include "SubresourceLoaderClient.h" 28 #include "Timer.h" 29 #include <wtf/Deque.h> 27 30 #include <wtf/HashMap.h> 31 #include <wtf/Noncopyable.h> 28 32 29 33 namespace WebCore { … … 33 37 class Request; 34 38 35 class Loader : private SubresourceLoaderClient{39 class Loader : Noncopyable { 36 40 public: 37 41 Loader(); … … 41 45 42 46 void cancelRequests(DocLoader*); 47 48 enum Priority { Low, Medium, High }; 49 void servePendingRequests(Priority minimumPriority = Low); 43 50 44 51 private: 45 virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&); 46 virtual void didReceiveData(SubresourceLoader*, const char*, int); 47 virtual void didFinishLoading(SubresourceLoader*); 48 virtual void didFail(SubresourceLoader*, const ResourceError&); 49 void didFail(SubresourceLoader*, bool cancelled = false); 52 Priority determinePriority(const CachedResource*) const; 53 void scheduleServePendingRequests(); 54 55 void requestTimerFired(Timer<Loader>*); 50 56 51 void servePendingRequests(); 52 53 DeprecatedPtrList<Request> m_requestsPending; 54 typedef HashMap<RefPtr<SubresourceLoader>, Request*> RequestMap; 55 RequestMap m_requestsLoading; 57 class Host : private SubresourceLoaderClient { 58 public: 59 Host(unsigned maxRequestsInFlight); 60 ~Host(); 61 62 void addRequest(Request*, Priority); 63 void servePendingRequests(Priority minimumPriority = Low); 64 void cancelRequests(DocLoader*); 65 bool hasRequests() const; 66 67 private: 68 virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&); 69 virtual void didReceiveData(SubresourceLoader*, const char*, int); 70 virtual void didFinishLoading(SubresourceLoader*); 71 virtual void didFail(SubresourceLoader*, const ResourceError&); 72 73 typedef Deque<Request*> RequestQueue; 74 void servePendingRequests(RequestQueue& requestsPending); 75 void didFail(SubresourceLoader*, bool cancelled = false); 76 void cancelPendingRequests(RequestQueue& requestsPending, DocLoader*); 77 78 RequestQueue m_requestsPending[High + 1]; 79 typedef HashMap<RefPtr<SubresourceLoader>, Request*> RequestMap; 80 RequestMap m_requestsLoading; 81 const int m_maxRequestsInFlight; 82 }; 83 typedef HashMap<String, Host*> HostMap; 84 HostMap m_hosts; 85 Host m_nonHTTPProtocolHost; 86 87 Timer<Loader> m_requestTimer; 56 88 }; 57 89 -
trunk/WebKit/mac/ChangeLog
r31035 r31038 1 2008-03-13 Antti Koivisto <antti@apple.com> 2 3 Reviewed by Darin. 4 5 * ForwardingHeaders/wtf/Deque.h: Added. 6 1 7 2008-03-13 Anders Carlsson <andersca@apple.com> 2 8
Note: See TracChangeset
for help on using the changeset viewer.