diff options
20 files changed, 402 insertions, 90 deletions
diff --git a/LayoutTests/dom/html/level2/html/HTMLLinkElement10-expected.txt b/LayoutTests/dom/html/level2/html/HTMLLinkElement10-expected.txt new file mode 100644 index 0000000..6f009e8 --- /dev/null +++ b/LayoutTests/dom/html/level2/html/HTMLLinkElement10-expected.txt @@ -0,0 +1,2 @@ +Test: http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLLinkElement10 +Status: Success diff --git a/LayoutTests/dom/html/level2/html/HTMLLinkElement10-link.file b/LayoutTests/dom/html/level2/html/HTMLLinkElement10-link.file new file mode 100644 index 0000000..4a94b89 --- /dev/null +++ b/LayoutTests/dom/html/level2/html/HTMLLinkElement10-link.file @@ -0,0 +1,12 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<HTML> +<HEAD> +<META HTTP-EQUIV="Content-Type" CONTENT="text/html; CHARSET=utf-8"> +<TITLE>NIST DOM HTML Test - LINK</TITLE> +<BODY> +<P> +This is the test link that's pre-fetched by the prefetch test. +</P> +</BODY> +</HTML> + diff --git a/LayoutTests/dom/html/level2/html/HTMLLinkElement10.html b/LayoutTests/dom/html/level2/html/HTMLLinkElement10.html new file mode 100644 index 0000000..dcf20e1 --- /dev/null +++ b/LayoutTests/dom/html/level2/html/HTMLLinkElement10.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<HTML> +<HEAD> +<META HTTP-EQUIV="Content-Type" CONTENT="text/html; CHARSET=utf-8"> +<TITLE>NIST DOM HTML Test - LINK</TITLE> +<script charset='UTF-8' type='text/javascript' src='selfhtml.js'></script> +<script charset='UTF-8' type='text/javascript' src='HTMLLinkElement10.js'></script> +<LINK CHARSET="Latin-1" HREF="HTMLLinkElement10-link.file" HREFLANG="en" MEDIA="screen" REL="prefetch" TYPE="text/html" onload="prefetchComplete()"> +<LINK CHARSET="Latin-1" HREF="./files/style1.css" HREFLANG="en" MEDIA="screen" REV="stylesheet" TYPE="text/css"> +<script type='text/javascript'>startTest10(); +</script></HEAD> +<BODY onload="loadComplete()"> +<P> +<BR> +</P> +</BODY> +</HTML> + diff --git a/LayoutTests/dom/html/level2/html/HTMLLinkElement10.js b/LayoutTests/dom/html/level2/html/HTMLLinkElement10.js new file mode 100644 index 0000000..c0ac4a7 --- /dev/null +++ b/LayoutTests/dom/html/level2/html/HTMLLinkElement10.js @@ -0,0 +1,51 @@ + + + /** + * Gets URI that identifies the test. + * @return uri identifier of test + */ +function getTargetURI() { + return "http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLLinkElement10"; + } + +var docsLoaded = 0; +var mainLoaded = false; +var prefetchLoaded = false; + +function finishTest() { + if (mainLoaded && prefetchLoaded) { + setResult(null, null); + } else { + if (!prefetchLoaded) { + setResult("fail", "No prefetch onload fired"); + } else { + setResult("fail", "Prefetch fired, but maybe the document onload didn't"); + } + } + if (window.layoutTestController) { + layoutTestController.notifyDone(); + } +} + +function loadComplete() { + mainLoaded = true; + if (++docsLoaded == 2) { + finishTest(); + } +} + +function prefetchComplete() { + prefetchLoaded = true; + if (++docsLoaded == 2) { + finishTest(); + } +} + + + +function startTest10() { + if (window.layoutTestController) { + layoutTestController.dumpAsText(); + layoutTestController.waitUntilDone(); + } +} diff --git a/LayoutTests/dom/html/level2/html/HTMLLinkElement11-expected.txt b/LayoutTests/dom/html/level2/html/HTMLLinkElement11-expected.txt new file mode 100644 index 0000000..d53710a --- /dev/null +++ b/LayoutTests/dom/html/level2/html/HTMLLinkElement11-expected.txt @@ -0,0 +1,2 @@ +Test: http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLLinkElement11 +Status: Success diff --git a/LayoutTests/dom/html/level2/html/HTMLLinkElement11.css b/LayoutTests/dom/html/level2/html/HTMLLinkElement11.css new file mode 100644 index 0000000..6a5dab8 --- /dev/null +++ b/LayoutTests/dom/html/level2/html/HTMLLinkElement11.css @@ -0,0 +1 @@ +div { font-family: Arial, Helvetica, sans-serif; color: red; } diff --git a/LayoutTests/dom/html/level2/html/HTMLLinkElement11.html b/LayoutTests/dom/html/level2/html/HTMLLinkElement11.html new file mode 100644 index 0000000..9b9a5e4 --- /dev/null +++ b/LayoutTests/dom/html/level2/html/HTMLLinkElement11.html @@ -0,0 +1,15 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<HTML> +<HEAD> +<META HTTP-EQUIV="Content-Type" CONTENT="text/html; CHARSET=utf-8"> +<TITLE>NIST DOM HTML Test - LINK</TITLE> +<script charset='UTF-8' type='text/javascript' src='selfhtml.js'></script> +<script charset='UTF-8' type='text/javascript' src='HTMLLinkElement11.js'></script><script type='text/javascript'>startTest11();</script></HEAD> +<LINK CHARSET="Latin-1" HREF="HTMLLinkElement11.css" HREFLANG="en" MEDIA="screen" REL="stylesheet" TYPE="text/css" onload="cssComplete()"> +<BODY onload="loadComplete()"> +<P> +<BR> +</P> +</BODY> +</HTML> + diff --git a/LayoutTests/dom/html/level2/html/HTMLLinkElement11.js b/LayoutTests/dom/html/level2/html/HTMLLinkElement11.js new file mode 100644 index 0000000..b8bf1cb --- /dev/null +++ b/LayoutTests/dom/html/level2/html/HTMLLinkElement11.js @@ -0,0 +1,51 @@ + + + /** + * Gets URI that identifies the test. + * @return uri identifier of test + */ +function getTargetURI() { + return "http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLLinkElement11"; + } + +var docsLoaded = 0; +var mainLoaded = false; +var cssLoaded = false; + +function finishTest() { + if (mainLoaded && cssLoaded) { + setResult(null, null); + } else { + if (!cssLoaded) { + setResult("fail", "No css onload fired"); + } else { + setResult("fail", "Css fired, but maybe the document onload didn't"); + } + } + if (window.layoutTestController) { + layoutTestController.notifyDone(); + } +} + +function loadComplete() { + mainLoaded = true; + if (++docsLoaded == 2) { + finishTest(); + } +} + +function cssComplete() { + cssLoaded = true; + if (++docsLoaded == 2) { + finishTest(); + } +} + + + +function startTest11() { + if (window.layoutTestController) { + layoutTestController.dumpAsText(); + layoutTestController.waitUntilDone(); + } +} diff --git a/WebCore/config.h b/WebCore/config.h index 863423b..b61c2ad 100644 --- a/WebCore/config.h +++ b/WebCore/config.h @@ -194,6 +194,9 @@ // or if javascript tried to change the location. #define ANDROID_USER_GESTURE +// Enable prefetching when specified via the rel element of <link> elements. +#define ENABLE_LINK_PREFETCH 1 + #endif /* PLATFORM(ANDROID) */ #ifdef __cplusplus diff --git a/WebCore/html/HTMLLinkElement.cpp b/WebCore/html/HTMLLinkElement.cpp index 5376611..daa1702 100644 --- a/WebCore/html/HTMLLinkElement.cpp +++ b/WebCore/html/HTMLLinkElement.cpp @@ -41,6 +41,9 @@ #include "ScriptEventListener.h" #include "Settings.h" #include <wtf/StdLibExtras.h> +#if ENABLE(LINK_PREFETCH) +#include "CachedLinkPrefetch.h" +#endif namespace WebCore { @@ -51,15 +54,8 @@ HTMLLinkElement::HTMLLinkElement(const QualifiedName& qName, Document *doc, bool , m_cachedSheet(0) , m_disabledState(0) , m_loading(false) - , m_alternate(false) - , m_isStyleSheet(false) - , m_isIcon(false) -#ifdef ANDROID_APPLE_TOUCH_ICON - , m_isTouchIcon(false) - , m_isPrecomposedTouchIcon(false) -#endif - , m_isDNSPrefetch(false) , m_createdByParser(createdByParser) + , m_timer(this, &HTMLLinkElement::timerFired) { ASSERT(hasTagName(linkTag)); } @@ -84,11 +80,11 @@ void HTMLLinkElement::setDisabledState(bool _disabled) // Check #1: If the sheet becomes disabled while it was loading, and if it was either // a main sheet or a sheet that was previously enabled via script, then we need // to remove it from the list of pending sheets. - if (m_disabledState == 2 && (!m_alternate || oldDisabledState == 1)) + if (m_disabledState == 2 && (!m_rel.m_isAlternate || oldDisabledState == 1)) document()->removePendingSheet(); // Check #2: An alternate sheet becomes enabled while it is still loading. - if (m_alternate && m_disabledState == 1) + if (m_rel.m_isAlternate && m_disabledState == 1) document()->addPendingSheet(); // Check #3: A main sheet becomes enabled while it was still loading and @@ -96,7 +92,7 @@ void HTMLLinkElement::setDisabledState(bool _disabled) // happen (a double toggle for no reason essentially). This happens on // virtualplastic.net, which manages to do about 12 enable/disables on only 3 // sheets. :) - if (!m_alternate && m_disabledState == 1 && oldDisabledState == 2) + if (!m_rel.m_isAlternate && m_disabledState == 1 && oldDisabledState == 2) document()->addPendingSheet(); // If the sheet is already loading just bail. @@ -119,11 +115,7 @@ StyleSheet* HTMLLinkElement::sheet() const void HTMLLinkElement::parseMappedAttribute(MappedAttribute *attr) { if (attr->name() == relAttr) { -#ifdef ANDROID_APPLE_TOUCH_ICON - tokenizeRelAttribute(attr->value(), m_isStyleSheet, m_alternate, m_isIcon, m_isTouchIcon, m_isPrecomposedTouchIcon, m_isDNSPrefetch); -#else - tokenizeRelAttribute(attr->value(), m_isStyleSheet, m_alternate, m_isIcon, m_isDNSPrefetch); -#endif + tokenizeRelAttribute(attr->value(), m_rel); process(); } else if (attr->name() == hrefAttr) { m_url = document()->completeURL(deprecatedParseURL(attr->value())); @@ -136,44 +128,49 @@ void HTMLLinkElement::parseMappedAttribute(MappedAttribute *attr) process(); } else if (attr->name() == disabledAttr) { setDisabledState(!attr->isNull()); - } else if (attr->name() == onbeforeloadAttr) + } else if (attr->name() == onbeforeloadAttr) { setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr)); - else { + } else if (attr->name() == onloadAttr) { + setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr)); + } else { if (attr->name() == titleAttr && m_sheet) m_sheet->setTitle(attr->value()); HTMLElement::parseMappedAttribute(attr); } } -#ifdef ANDROID_APPLE_TOUCH_ICON -void HTMLLinkElement::tokenizeRelAttribute(const AtomicString& rel, bool& styleSheet, bool& alternate, bool& icon, bool& touchIcon, bool& precomposedTouchIcon, bool& dnsPrefetch) -#else -void HTMLLinkElement::tokenizeRelAttribute(const AtomicString& rel, bool& styleSheet, bool& alternate, bool& icon, bool& dnsPrefetch) -#endif +void HTMLLinkElement::tokenizeRelAttribute(const AtomicString& rel, RelAttribute& attribute) { - styleSheet = false; - icon = false; - alternate = false; - dnsPrefetch = false; + attribute.m_isStyleSheet = false; + attribute.m_isIcon = false; + attribute.m_isAlternate = false; + attribute.m_isDNSPrefetch = false; +#if ENABLE(LINK_PREFETCH) + attribute.m_isLinkPrefetch = false; +#endif #ifdef ANDROID_APPLE_TOUCH_ICON - touchIcon = false; - precomposedTouchIcon = false; + attribute.m_isTouchIcon = false; + attribute.m_isPrecomposedTouchIcon = false; #endif if (equalIgnoringCase(rel, "stylesheet")) - styleSheet = true; + attribute.m_isStyleSheet = true; else if (equalIgnoringCase(rel, "icon") || equalIgnoringCase(rel, "shortcut icon")) - icon = true; + attribute.m_isIcon = true; #ifdef ANDROID_APPLE_TOUCH_ICON else if (equalIgnoringCase(rel, "apple-touch-icon")) - touchIcon = true; + attribute.m_isTouchIcon = true; else if (equalIgnoringCase(rel, "apple-touch-icon-precomposed")) - precomposedTouchIcon = true; + attribute.m_isPrecomposedTouchIcon = true; #endif else if (equalIgnoringCase(rel, "dns-prefetch")) - dnsPrefetch = true; + attribute.m_isDNSPrefetch = true; +#if ENABLE(LINK_PREFETCH) + else if (equalIgnoringCase(rel, "prefetch")) + attribute.m_isLinkPrefetch = true; +#endif else if (equalIgnoringCase(rel, "alternate stylesheet") || equalIgnoringCase(rel, "stylesheet alternate")) { - styleSheet = true; - alternate = true; + attribute.m_isStyleSheet = true; + attribute.m_isAlternate = true; } else { // Tokenize the rel attribute and set bits based on specific keywords that we find. String relString = rel.string(); @@ -183,11 +180,11 @@ void HTMLLinkElement::tokenizeRelAttribute(const AtomicString& rel, bool& styleS Vector<String>::const_iterator end = list.end(); for (Vector<String>::const_iterator it = list.begin(); it != end; ++it) { if (equalIgnoringCase(*it, "stylesheet")) - styleSheet = true; + attribute.m_isStyleSheet = true; else if (equalIgnoringCase(*it, "alternate")) - alternate = true; + attribute.m_isAlternate = true; else if (equalIgnoringCase(*it, "icon")) - icon = true; + attribute.m_isIcon = true; } } } @@ -201,25 +198,35 @@ void HTMLLinkElement::process() // IE extension: location of small icon for locationbar / bookmarks // We'll record this URL per document, even if we later only use it in top level frames - if (m_isIcon && m_url.isValid() && !m_url.isEmpty()) + if (m_rel.m_isIcon && m_url.isValid() && !m_url.isEmpty()) document()->setIconURL(m_url.string(), type); #ifdef ANDROID_APPLE_TOUCH_ICON - if ((m_isTouchIcon || m_isPrecomposedTouchIcon) && m_url.isValid() + if ((m_rel.m_isTouchIcon || m_rel.m_isPrecomposedTouchIcon) && m_url.isValid() && !m_url.isEmpty()) document()->frame()->loader()->client() ->dispatchDidReceiveTouchIconURL(m_url.string(), - m_isPrecomposedTouchIcon); + m_rel.m_isPrecomposedTouchIcon); #endif - if (m_isDNSPrefetch && m_url.isValid() && !m_url.isEmpty()) + if (m_rel.m_isDNSPrefetch && m_url.isValid() && !m_url.isEmpty()) prefetchDNS(m_url.host()); +#if ENABLE(LINK_PREFETCH) + if (m_rel.m_isLinkPrefetch && m_url.isValid()) { + m_cachedLinkPrefetch = document()->docLoader()->requestLinkPrefetch(m_url); + m_loading = true; + + if (m_cachedLinkPrefetch) + m_cachedLinkPrefetch->addClient(this); + } +#endif + bool acceptIfTypeContainsTextCSS = document()->page() && document()->page()->settings() && document()->page()->settings()->treatsAnyTextCSSLinkAsStylesheet(); // Stylesheet // This was buggy and would incorrectly match <link rel="alternate">, which has a different specified meaning. -dwh - if (m_disabledState != 2 && (m_isStyleSheet || (acceptIfTypeContainsTextCSS && type.contains("text/css"))) && document()->frame() && m_url.isValid()) { + if (m_disabledState != 2 && (m_rel.m_isStyleSheet || (acceptIfTypeContainsTextCSS && type.contains("text/css"))) && document()->frame() && m_url.isValid()) { // also, don't load style sheets for standalone documents String charset = getAttribute(charsetAttr); @@ -353,6 +360,17 @@ bool HTMLLinkElement::isLoading() const return static_cast<CSSStyleSheet *>(m_sheet.get())->isLoading(); } +void HTMLLinkElement::notifyFinished(CachedResource*) +{ + if (!m_timer.isActive()) + m_timer.startOneShot(0); +} + +void HTMLLinkElement::timerFired(Timer<HTMLLinkElement>*) +{ + dispatchEvent(Event::create(eventNames().loadEvent, false, false)); +} + bool HTMLLinkElement::sheetLoaded() { if (!isLoading() && !isDisabled() && !isAlternate()) { @@ -462,10 +480,10 @@ void HTMLLinkElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const HTMLElement::addSubresourceAttributeURLs(urls); // Favicons are handled by a special case in LegacyWebArchive::create() - if (m_isIcon) + if (m_rel.m_isIcon) return; - if (!m_isStyleSheet) + if (!m_rel.m_isStyleSheet) return; // Append the URL of this link element. diff --git a/WebCore/html/HTMLLinkElement.h b/WebCore/html/HTMLLinkElement.h index 324c244..7ff47dc 100644 --- a/WebCore/html/HTMLLinkElement.h +++ b/WebCore/html/HTMLLinkElement.h @@ -27,14 +27,39 @@ #include "CachedResourceClient.h" #include "CachedResourceHandle.h" #include "HTMLElement.h" +#include "Timer.h" namespace WebCore { class CachedCSSStyleSheet; +class CachedLinkPrefetch; class KURL; class HTMLLinkElement : public HTMLElement, public CachedResourceClient { public: + struct RelAttribute { + bool m_isStyleSheet; + bool m_isIcon; + bool m_isAlternate; + bool m_isDNSPrefetch; +#ifdef ANDROID_APPLE_TOUCH_ICON + bool m_isTouchIcon; + bool m_isPrecomposedTouchIcon; +#endif +#if ENABLE(LINK_PREFETCH) + bool m_isLinkPrefetch; +#endif + + RelAttribute() : m_isStyleSheet(false), m_isIcon(false), m_isAlternate(false), m_isDNSPrefetch(false) +#ifdef ANDROID_APPLE_TOUCH_ICON + , m_isTouchIcon(false), m_isPrecomposedTouchIcon(false) +#endif +#if ENABLE(LINK_PREFETCH) + , m_isLinkPrefetch(false) +#endif + { }; + }; + HTMLLinkElement(const QualifiedName&, Document*, bool createdByParser); ~HTMLLinkElement(); @@ -80,24 +105,22 @@ public: // from CachedResourceClient virtual void setCSSStyleSheet(const String& href, const KURL& baseURL, const String& charset, const CachedCSSStyleSheet* sheet); + virtual void notifyFinished(CachedResource*); + bool isLoading() const; virtual bool sheetLoaded(); - bool isAlternate() const { return m_disabledState == 0 && m_alternate; } + bool isAlternate() const { return m_disabledState == 0 && m_rel.m_isAlternate; } bool isDisabled() const { return m_disabledState == 2; } bool isEnabledViaScript() const { return m_disabledState == 1; } - bool isIcon() const { return m_isIcon; } + bool isIcon() const { return m_rel.m_isIcon; } int disabledState() { return m_disabledState; } void setDisabledState(bool _disabled); virtual bool isURLAttribute(Attribute*) const; -#ifdef ANDROID_APPLE_TOUCH_ICON - static void tokenizeRelAttribute(const AtomicString& value, bool& stylesheet, bool& alternate, bool& icon, bool& touchIcon, bool& precomposedTouchIcon, bool& dnsPrefetch); -#else - static void tokenizeRelAttribute(const AtomicString& value, bool& stylesheet, bool& alternate, bool& icon, bool& dnsPrefetch); -#endif + static void tokenizeRelAttribute(const AtomicString& value, RelAttribute& attribute); virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; @@ -112,22 +135,21 @@ public: #endif protected: + void timerFired(Timer<HTMLLinkElement>*); + CachedResourceHandle<CachedCSSStyleSheet> m_cachedSheet; RefPtr<CSSStyleSheet> m_sheet; +#if ENABLE(LINK_PREFETCH) + CachedResourceHandle<CachedLinkPrefetch> m_cachedLinkPrefetch; +#endif KURL m_url; String m_type; String m_media; int m_disabledState; // 0=unset(default), 1=enabled via script, 2=disabled + RelAttribute m_rel; bool m_loading; - bool m_alternate; - bool m_isStyleSheet; - bool m_isIcon; -#ifdef ANDROID_APPLE_TOUCH_ICON - bool m_isTouchIcon; - bool m_isPrecomposedTouchIcon; -#endif - bool m_isDNSPrefetch; bool m_createdByParser; + Timer<HTMLLinkElement> m_timer; }; } //namespace diff --git a/WebCore/html/PreloadScanner.cpp b/WebCore/html/PreloadScanner.cpp index 624f2ae..527b9f8 100644 --- a/WebCore/html/PreloadScanner.cpp +++ b/WebCore/html/PreloadScanner.cpp @@ -703,19 +703,9 @@ void PreloadScanner::processAttribute() if (attribute == hrefAttr && m_urlToLoad.isEmpty()) m_urlToLoad = deprecatedParseURL(value); else if (attribute == relAttr) { - bool styleSheet = false; - bool alternate = false; - bool icon = false; - bool dnsPrefetch = false; -#ifdef ANDROID_APPLE_TOUCH_ICON - bool touchIcon = false; - bool precomposedTouchIcon = false; - HTMLLinkElement::tokenizeRelAttribute(value, styleSheet, alternate, icon, touchIcon, precomposedTouchIcon, dnsPrefetch); - m_linkIsStyleSheet = styleSheet && !alternate && !icon && !touchIcon && !precomposedTouchIcon && !dnsPrefetch; -#else - HTMLLinkElement::tokenizeRelAttribute(value, styleSheet, alternate, icon, dnsPrefetch); - m_linkIsStyleSheet = styleSheet && !alternate && !icon && !dnsPrefetch; -#endif + HTMLLinkElement::RelAttribute rel; + HTMLLinkElement::tokenizeRelAttribute(value, rel); + m_linkIsStyleSheet = rel.m_isStyleSheet && !rel.m_isAlternate && !rel.m_isIcon && !rel.m_isDNSPrefetch; } else if (attribute == charsetAttr) m_charset = value; } diff --git a/WebCore/loader/Cache.cpp b/WebCore/loader/Cache.cpp index fdd9b25..5f4d0e7 100644 --- a/WebCore/loader/Cache.cpp +++ b/WebCore/loader/Cache.cpp @@ -38,6 +38,9 @@ #include "SecurityOrigin.h" #include <stdio.h> #include <wtf/CurrentTime.h> +#if ENABLE(LINK_PREFETCH) +#include "CachedLinkPrefetch.h" +#endif using namespace std; @@ -86,6 +89,10 @@ static CachedResource* createResource(CachedResource::Type type, const KURL& url case CachedResource::XBLStyleSheet: return new CachedXBLDocument(url.string()); #endif +#if ENABLE(LINK_PREFETCH) + case CachedResource::LinkPrefetch: + return new CachedLinkPrefetch(url.string()); +#endif default: break; } diff --git a/WebCore/loader/CachedCSSStyleSheet.cpp b/WebCore/loader/CachedCSSStyleSheet.cpp index b2e03b9..3fc28ff 100644 --- a/WebCore/loader/CachedCSSStyleSheet.cpp +++ b/WebCore/loader/CachedCSSStyleSheet.cpp @@ -51,8 +51,10 @@ CachedCSSStyleSheet::~CachedCSSStyleSheet() void CachedCSSStyleSheet::didAddClient(CachedResourceClient *c) { - if (!m_loading) + if (!m_loading) { c->setCSSStyleSheet(m_url, m_response.url(), m_decoder->encoding().name(), this); + c->notifyFinished(this); + } } void CachedCSSStyleSheet::allClientsRemoved() @@ -103,6 +105,10 @@ void CachedCSSStyleSheet::data(PassRefPtr<SharedBuffer> data, bool allDataReceiv checkNotify(); // Clear the decoded text as it is unlikely to be needed immediately again and is cheap to regenerate. m_decodedSheetText = String(); + + CachedResourceClientWalker w(m_clients); + while (CachedResourceClient* c = w.next()) + c->notifyFinished(this); } void CachedCSSStyleSheet::checkNotify() diff --git a/WebCore/loader/CachedLinkPrefetch.h b/WebCore/loader/CachedLinkPrefetch.h new file mode 100644 index 0000000..4ffa5fe --- /dev/null +++ b/WebCore/loader/CachedLinkPrefetch.h @@ -0,0 +1,67 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef CachedLinkPrefetch_h +#define CachedLinkPrefetch_h + +#include "CachedResource.h" +#include "CachedResourceClient.h" +#include "CachedResourceClientWalker.h" + +namespace WebCore { + +class DocLoader; + +class CachedLinkPrefetch : public CachedResource { +public: + CachedLinkPrefetch(const String& URL) : CachedResource(URL, LinkPrefetch) { }; + virtual ~CachedLinkPrefetch() { }; + + virtual void didAddClient(CachedResourceClient* c) { + if (!m_loading) + c->notifyFinished(this); + }; + + virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived) { + if (!allDataReceived) + return; + + m_data = data; + + CachedResourceClientWalker w(m_clients); + while (CachedResourceClient* c = w.next()) + c->notifyFinished(this); + }; + virtual void error() { }; + + virtual bool schedule() const { return true; } + + virtual bool isPrefetch() const { return true; } +}; +} + +#endif + diff --git a/WebCore/loader/CachedResource.h b/WebCore/loader/CachedResource.h index 0f46a62..bad632b 100644 --- a/WebCore/loader/CachedResource.h +++ b/WebCore/loader/CachedResource.h @@ -63,6 +63,9 @@ public: #if ENABLE(XBL) , XBL #endif +#if ENABLE(LINK_PREFETCH) + , LinkPrefetch +#endif }; enum Status { @@ -118,6 +121,7 @@ public: void setLoading(bool b) { m_loading = b; } virtual bool isImage() const { return false; } + virtual bool isPrefetch() const { return false; } unsigned accessCount() const { return m_accessCount; } void increaseAccessCount() { m_accessCount++; } diff --git a/WebCore/loader/DocLoader.cpp b/WebCore/loader/DocLoader.cpp index 8c0a1b2..08dfa33 100644 --- a/WebCore/loader/DocLoader.cpp +++ b/WebCore/loader/DocLoader.cpp @@ -44,6 +44,9 @@ #include "loader.h" #include "SecurityOrigin.h" #include "Settings.h" +#if ENABLE(LINK_PREFETCH) +#include "CachedLinkPrefetch.h" +#endif #define PRELOAD_DEBUG 0 @@ -176,6 +179,13 @@ CachedXBLDocument* DocLoader::requestXBLDocument(const String& url) } #endif +#if ENABLE(LINK_PREFETCH) +CachedLinkPrefetch* DocLoader::requestLinkPrefetch(const String& url) +{ + return static_cast<CachedLinkPrefetch*>(requestResource(CachedResource::LinkPrefetch, url, String())); +} +#endif + bool DocLoader::canRequest(CachedResource::Type type, const KURL& url) { // Some types of resources can be loaded only from the same origin. Other @@ -186,6 +196,9 @@ bool DocLoader::canRequest(CachedResource::Type type, const KURL& url) case CachedResource::CSSStyleSheet: case CachedResource::Script: case CachedResource::FontResource: +#if ENABLE(LINK_PREFETCH) + case CachedResource::LinkPrefetch: +#endif // These types of resources can be loaded from any origin. // FIXME: Are we sure about CachedResource::FontResource? break; @@ -228,6 +241,9 @@ bool DocLoader::canRequest(CachedResource::Type type, const KURL& url) break; case CachedResource::ImageResource: case CachedResource::CSSStyleSheet: +#if ENABLE(LINK_PREFETCH) + case CachedResource::LinkPrefetch: +#endif case CachedResource::FontResource: { // These resources can corrupt only the frame's pixels. if (Frame* f = frame()) { @@ -402,13 +418,19 @@ void DocLoader::checkCacheObjectStatus(CachedResource* resource) frame()->loader()->loadedResourceFromMemoryCache(resource); } -void DocLoader::incrementRequestCount() +void DocLoader::incrementRequestCount(const CachedResource* res) { + if (res->isPrefetch()) + return; + ++m_requestCount; } -void DocLoader::decrementRequestCount() +void DocLoader::decrementRequestCount(const CachedResource* res) { + if (res->isPrefetch()) + return; + --m_requestCount; ASSERT(m_requestCount > -1); } diff --git a/WebCore/loader/DocLoader.h b/WebCore/loader/DocLoader.h index 06d8a47..2f8f639 100644 --- a/WebCore/loader/DocLoader.h +++ b/WebCore/loader/DocLoader.h @@ -45,6 +45,9 @@ class Document; class Frame; class ImageLoader; class KURL; +#if ENABLE(LINK_PREFETCH) +class CachedLinkPrefetch; +#endif // The DocLoader manages the loading of scripts/images/stylesheets for a single document. class DocLoader : public Noncopyable @@ -68,6 +71,9 @@ public: #if ENABLE(XBL) CachedXBLDocument* requestXBLDocument(const String &url); #endif +#if ENABLE(LINK_PREFETCH) + CachedLinkPrefetch* requestLinkPrefetch(const String &url); +#endif // Logs an access denied message to the console for the specified URL. void printAccessDeniedMessage(const KURL& url) const; @@ -98,8 +104,8 @@ public: void setAllowStaleResources(bool allowStaleResources) { m_allowStaleResources = allowStaleResources; } - void incrementRequestCount(); - void decrementRequestCount(); + void incrementRequestCount(const CachedResource*); + void decrementRequestCount(const CachedResource*); int requestCount(); void clearPreloads(); diff --git a/WebCore/loader/loader.cpp b/WebCore/loader/loader.cpp index 4d2b474..24f141f 100644 --- a/WebCore/loader/loader.cpp +++ b/WebCore/loader/loader.cpp @@ -88,7 +88,12 @@ static ResourceRequest::TargetType cachedResourceTypeToTargetType(CachedResource return ResourceRequest::TargetIsFontResource; case CachedResource::ImageResource: return ResourceRequest::TargetIsImage; +#if ENABLE(LINK_PREFETCH) + case CachedResource::LinkPrefetch: + return ResourceRequest::TargetIsSubresource; +#endif } + ASSERT_NOT_REACHED(); return ResourceRequest::TargetIsSubresource; } @@ -109,6 +114,10 @@ Loader::Priority Loader::determinePriority(const CachedResource* resource) const return Medium; case CachedResource::ImageResource: return Low; +#if ENABLE(LINK_PREFETCH) + case CachedResource::LinkPrefetch: + return VeryLow; +#endif } ASSERT_NOT_REACHED(); return Low; @@ -138,9 +147,9 @@ void Loader::load(DocLoader* docLoader, CachedResource* resource, bool increment bool hadRequests = host->hasRequests(); Priority priority = determinePriority(resource); host->addRequest(request, priority); - docLoader->incrementRequestCount(); + docLoader->incrementRequestCount(request->cachedResource()); - if (priority > Low || !url.protocolInHTTPFamily() || !hadRequests) { + if (priority > Low || !url.protocolInHTTPFamily() || (priority == Low && !hadRequests)) { // Try to request important resources immediately host->servePendingRequests(priority); } else { @@ -352,6 +361,12 @@ void Loader::Host::servePendingRequests(RequestQueue& requestsPending, bool& ser } } +#if ENABLE(LINK_PREFETCH) + if (request->cachedResource()->type() == CachedResource::LinkPrefetch) { + resourceRequest.setHTTPHeaderField("X-Moz", "prefetch"); + } +#endif + RefPtr<SubresourceLoader> loader = SubresourceLoader::create(docLoader->doc()->frame(), this, resourceRequest, request->shouldDoSecurityCheck(), request->sendResourceLoadCallbacks()); if (loader) { @@ -361,7 +376,7 @@ void Loader::Host::servePendingRequests(RequestQueue& requestsPending, bool& ser printf("HOST %s COUNT %d LOADING %s\n", resourceRequest.url().host().latin1().data(), m_requestsLoading.size(), request->cachedResource()->url().latin1().data()); #endif } else { - docLoader->decrementRequestCount(); + docLoader->decrementRequestCount(request->cachedResource()); docLoader->setLoadInProgress(true); request->cachedResource()->error(); docLoader->setLoadInProgress(false); @@ -385,7 +400,7 @@ void Loader::Host::didFinishLoading(SubresourceLoader* loader) // the docLoader that it will delete when the document gets deleted. RefPtr<Document> protector(docLoader->doc()); if (!request->isMultipart()) - docLoader->decrementRequestCount(); + docLoader->decrementRequestCount(request->cachedResource()); CachedResource* resource = request->cachedResource(); ASSERT(!resource->resourceToRevalidate()); @@ -433,7 +448,7 @@ void Loader::Host::didFail(SubresourceLoader* loader, bool cancelled) // the docLoader that it will delete when the document gets deleted. RefPtr<Document> protector(docLoader->doc()); if (!request->isMultipart()) - docLoader->decrementRequestCount(); + docLoader->decrementRequestCount(request->cachedResource()); CachedResource* resource = request->cachedResource(); @@ -478,7 +493,7 @@ void Loader::Host::didReceiveResponse(SubresourceLoader* loader, const ResourceR // 304 Not modified / Use local copy m_requestsLoading.remove(loader); loader->clearClient(); - request->docLoader()->decrementRequestCount(); + request->docLoader()->decrementRequestCount(request->cachedResource()); // Existing resource is ok, just use it updating the expiration time. cache()->revalidationSucceeded(resource, response); @@ -510,7 +525,7 @@ void Loader::Host::didReceiveResponse(SubresourceLoader* loader, const ResourceR request->setIsMultipart(true); // We don't count multiParts in a DocLoader's request count - request->docLoader()->decrementRequestCount(); + request->docLoader()->decrementRequestCount(request->cachedResource()); // If we get a multipart response, we must have a handle ASSERT(loader->handle()); @@ -559,7 +574,7 @@ void Loader::Host::cancelPendingRequests(RequestQueue& requestsPending, DocLoade if (request->docLoader() == docLoader) { cache()->remove(request->cachedResource()); delete request; - docLoader->decrementRequestCount(); + docLoader->decrementRequestCount(request->cachedResource()); } else remaining.append(request); } diff --git a/WebCore/loader/loader.h b/WebCore/loader/loader.h index a9de032..9393a60 100644 --- a/WebCore/loader/loader.h +++ b/WebCore/loader/loader.h @@ -48,8 +48,8 @@ namespace WebCore { void cancelRequests(DocLoader*); - enum Priority { Low, Medium, High }; - void servePendingRequests(Priority minimumPriority = Low); + enum Priority { VeryLow, Low, Medium, High }; + void servePendingRequests(Priority minimumPriority = VeryLow); bool isSuspendingPendingRequests() { return m_isSuspendingPendingRequests; } void suspendPendingRequests(); @@ -76,7 +76,7 @@ namespace WebCore { void addRequest(Request*, Priority); void nonCacheRequestInFlight(); void nonCacheRequestComplete(); - void servePendingRequests(Priority minimumPriority = Low); + void servePendingRequests(Priority minimumPriority = VeryLow); void cancelRequests(DocLoader*); bool hasRequests() const; |