diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:15 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:15 -0800 |
commit | 1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353 (patch) | |
tree | 4457a7306ea5acb43fe05bfe0973b1f7faf97ba2 /WebCore/loader | |
parent | 9364f22aed35e1a1e9d07c121510f80be3ab0502 (diff) | |
download | external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.zip external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.gz external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.bz2 |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'WebCore/loader')
93 files changed, 8363 insertions, 1769 deletions
diff --git a/WebCore/loader/Cache.cpp b/WebCore/loader/Cache.cpp index bb239fc..7d30e5f 100644 --- a/WebCore/loader/Cache.cpp +++ b/WebCore/loader/Cache.cpp @@ -30,11 +30,16 @@ #include "CachedXSLStyleSheet.h" #include "DocLoader.h" #include "Document.h" +#if USE(LOW_BANDWIDTH_DISPLAY) #include "Frame.h" +#endif #include "FrameLoader.h" +#include "FrameView.h" #include "Image.h" #include "ResourceHandle.h" #include "SystemTime.h" +#include <stdio.h> + using namespace std; @@ -43,6 +48,7 @@ namespace WebCore { static const int cDefaultCacheCapacity = 8192 * 1024; static const double cMinDelayBeforeLiveDecodedPrune = 1; // Seconds. static const float cTargetPrunePercentage = .95f; // Percentage of capacity toward which we prune, to avoid immediately pruning again. +static const double cDefaultDecodedDataDeletionInterval = 0; Cache* cache() { @@ -51,35 +57,36 @@ Cache* cache() } Cache::Cache() -: m_disabled(false) -, m_pruneEnabled(true) -, m_capacity(cDefaultCacheCapacity) -, m_minDeadCapacity(0) -, m_maxDeadCapacity(cDefaultCacheCapacity) -, m_liveSize(0) -, m_deadSize(0) + : m_disabled(false) + , m_pruneEnabled(true) + , m_inPruneDeadResources(false) + , m_capacity(cDefaultCacheCapacity) + , m_minDeadCapacity(0) + , m_maxDeadCapacity(cDefaultCacheCapacity) + , m_deadDecodedDataDeletionInterval(cDefaultDecodedDataDeletionInterval) + , m_liveSize(0) + , m_deadSize(0) { } -static CachedResource* createResource(CachedResource::Type type, DocLoader* docLoader, const KURL& url, const String* charset, bool skipCanLoadCheck = false, bool sendResourceLoadCallbacks = true) +static CachedResource* createResource(CachedResource::Type type, const KURL& url, const String& charset) { switch (type) { case CachedResource::ImageResource: - // User agent images need to null check the docloader. No other resources need to. - return new CachedImage(docLoader, url.string(), true /* for cache */); + return new CachedImage(url.string()); case CachedResource::CSSStyleSheet: - return new CachedCSSStyleSheet(docLoader, url.string(), *charset, skipCanLoadCheck, sendResourceLoadCallbacks); + return new CachedCSSStyleSheet(url.string(), charset); case CachedResource::Script: - return new CachedScript(docLoader, url.string(), *charset); + return new CachedScript(url.string(), charset); case CachedResource::FontResource: - return new CachedFont(docLoader, url.string()); + return new CachedFont(url.string()); #if ENABLE(XSLT) case CachedResource::XSLStyleSheet: - return new CachedXSLStyleSheet(docLoader, url.string()); + return new CachedXSLStyleSheet(url.string()); #endif #if ENABLE(XBL) case CachedResource::XBLStyleSheet: - return new CachedXBLDocument(docLoader, url.string()); + return new CachedXBLDocument(url.string()); #endif default: break; @@ -88,11 +95,7 @@ static CachedResource* createResource(CachedResource::Type type, DocLoader* docL return 0; } -#ifdef ANDROID_PRELOAD_CHANGES -CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Type type, const KURL& url, const String* charset, bool skipCanLoadCheck, bool sendResourceLoadCallbacks, bool isPreload) -#else -CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Type type, const KURL& url, const String* charset, bool skipCanLoadCheck, bool sendResourceLoadCallbacks) -#endif +CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Type type, const KURL& url, const String& charset, bool isPreload) { // FIXME: Do we really need to special-case an empty URL? // Would it be better to just go on with the cache code and let it fail later? @@ -103,43 +106,35 @@ CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Typ CachedResource* resource = m_resources.get(url.string()); if (resource) { -#ifdef ANDROID_PRELOAD_CHANGES if (isPreload && !resource->isPreloaded()) return 0; -#endif - if (!skipCanLoadCheck && FrameLoader::restrictAccessToLocal() && !FrameLoader::canLoad(*resource, docLoader->doc())) { + if (FrameLoader::restrictAccessToLocal() && !FrameLoader::canLoad(url, String(), docLoader->doc())) { Document* doc = docLoader->doc(); -#ifdef ANDROID_PRELOAD_CHANGES if(doc && !isPreload) -#else - if(doc) -#endif - FrameLoader::reportLocalLoadFailed(doc->page(), resource->url()); + FrameLoader::reportLocalLoadFailed(doc->frame(), resource->url()); return 0; } } else { - if (!skipCanLoadCheck && FrameLoader::restrictAccessToLocal() && !FrameLoader::canLoad(url, docLoader->doc())) { + if (FrameLoader::restrictAccessToLocal() && !FrameLoader::canLoad(url, String(), docLoader->doc())) { Document* doc = docLoader->doc(); -#ifdef ANDROID_PRELOAD_CHANGES if(doc && !isPreload) -#else - if(doc) -#endif - FrameLoader::reportLocalLoadFailed(doc->page(), url.string()); - + FrameLoader::reportLocalLoadFailed(doc->frame(), url.string()); return 0; } // The resource does not exist. Create it. - resource = createResource(type, docLoader, url, charset, skipCanLoadCheck, sendResourceLoadCallbacks); + resource = createResource(type, url, charset); ASSERT(resource); - ASSERT(resource->inCache()); - if (!disabled()) { + + // Pretend the resource is in the cache, to prevent it from being deleted during the load() call. + // FIXME: CachedResource should just use normal refcounting instead. + resource->setInCache(true); + + resource->load(docLoader); + + if (!disabled()) m_resources.set(url.string(), resource); // The size will be added in later once the resource is loaded and calls back to us with the new size. - - // This will move the resource to the front of its LRU list and increase its access count. - resourceAccessed(resource); - } else { + else { // Kick the resource out of the cache, because the cache is disabled. resource->setInCache(false); resource->setDocLoader(docLoader); @@ -163,8 +158,93 @@ CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Typ return 0; #endif + if (!disabled()) { + // This will move the resource to the front of its LRU list and increase its access count. + resourceAccessed(resource); + } + return resource; } + +CachedCSSStyleSheet* Cache::requestUserCSSStyleSheet(DocLoader* docLoader, const String& url, const String& charset) +{ + CachedCSSStyleSheet* userSheet; + if (CachedResource* existing = m_resources.get(url)) { + if (existing->type() != CachedResource::CSSStyleSheet) + return 0; + userSheet = static_cast<CachedCSSStyleSheet*>(existing); + } else { + userSheet = new CachedCSSStyleSheet(url, charset); + + // Pretend the resource is in the cache, to prevent it from being deleted during the load() call. + // FIXME: CachedResource should just use normal refcounting instead. + userSheet->setInCache(true); + // Don't load incrementally, skip load checks, don't send resource load callbacks. + userSheet->load(docLoader, false, true, false); + if (!disabled()) + m_resources.set(url, userSheet); + else + userSheet->setInCache(false); + } + + if (!disabled()) { + // This will move the resource to the front of its LRU list and increase its access count. + resourceAccessed(userSheet); + } + + return userSheet; +} + +void Cache::revalidateResource(CachedResource* resource, DocLoader* docLoader) +{ + ASSERT(resource); + ASSERT(!disabled()); + if (resource->resourceToRevalidate()) + return; + if (!resource->canUseCacheValidator()) { + evict(resource); + return; + } + const String& url = resource->url(); + CachedResource* newResource = createResource(resource->type(), KURL(url), resource->encoding()); + newResource->setResourceToRevalidate(resource); + evict(resource); + m_resources.set(url, newResource); + newResource->setInCache(true); + resourceAccessed(newResource); + newResource->load(docLoader); +} + +void Cache::revalidationSucceeded(CachedResource* revalidatingResource, const ResourceResponse& response) +{ + CachedResource* resource = revalidatingResource->resourceToRevalidate(); + ASSERT(resource); + ASSERT(!resource->inCache()); + ASSERT(resource->isLoaded()); + + evict(revalidatingResource); + + ASSERT(!m_resources.get(resource->url())); + m_resources.set(resource->url(), resource); + resource->setInCache(true); + resource->setExpirationDate(response.expirationDate()); + insertInLRUList(resource); + int delta = resource->size(); + if (resource->decodedSize() && resource->hasClients()) + insertInLiveDecodedResourcesList(resource); + if (delta) + adjustSize(resource->hasClients(), delta); + + revalidatingResource->switchClientsToRevalidatedResource(); + // this deletes the revalidating resource + revalidatingResource->clearResourceToRevalidate(); +} + +void Cache::revalidationFailed(CachedResource* revalidatingResource) +{ + ASSERT(revalidatingResource->resourceToRevalidate()); + revalidatingResource->clearResourceToRevalidate(); +} CachedResource* Cache::resourceForURL(const String& url) { @@ -192,11 +272,11 @@ void Cache::pruneLiveResources() return; unsigned capacity = liveCapacity(); - if (m_liveSize <= capacity) + if (capacity && m_liveSize <= capacity) return; unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again. - double currentTime = Frame::currentPaintTimeStamp(); + double currentTime = FrameView::currentPaintTimeStamp(); if (!currentTime) // In case prune is called directly, outside of a Frame paint. currentTime = WebCore::currentTime(); @@ -205,7 +285,7 @@ void Cache::pruneLiveResources() CachedResource* current = m_liveDecodedResources.m_tail; while (current) { CachedResource* prev = current->m_prevInLiveResourcesList; - ASSERT(current->referenced()); + ASSERT(current->hasClients()); if (current->isLoaded() && current->decodedSize()) { // Check to see if the remaining resources are too new to prune. double elapsedTime = currentTime - current->m_lastDecodedAccessTime; @@ -217,7 +297,7 @@ void Cache::pruneLiveResources() // list in m_allResources. current->destroyDecodedData(); - if (m_liveSize <= targetSize) + if (targetSize && m_liveSize <= targetSize) return; } current = prev; @@ -230,12 +310,13 @@ void Cache::pruneDeadResources() return; unsigned capacity = deadCapacity(); - if (m_deadSize <= capacity) + if (capacity && m_deadSize <= capacity) return; unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again. int size = m_allResources.size(); bool canShrinkLRULists = true; + m_inPruneDeadResources = true; for (int i = size - 1; i >= 0; i--) { // Remove from the tail, since this is the least frequently accessed of the objects. CachedResource* current = m_allResources[i].m_tail; @@ -243,18 +324,16 @@ void Cache::pruneDeadResources() // First flush all the decoded data in this queue. while (current) { CachedResource* prev = current->m_prevInAllResourcesList; -#ifdef ANDROID_PRELOAD_CHANGES - if (!current->referenced() && !current->isPreloaded() && current->isLoaded() && current->decodedSize()) { -#else - if (!current->referenced() && current->isLoaded() && current->decodedSize()) { -#endif + if (!current->hasClients() && !current->isPreloaded() && current->isLoaded() && current->decodedSize()) { // Destroy our decoded data. This will remove us from // m_liveDecodedResources, and possibly move us to a differnt // LRU list in m_allResources. current->destroyDecodedData(); - if (m_deadSize <= targetSize) + if (targetSize && m_deadSize <= targetSize) { + m_inPruneDeadResources = false; return; + } } current = prev; } @@ -263,18 +342,21 @@ void Cache::pruneDeadResources() current = m_allResources[i].m_tail; while (current) { CachedResource* prev = current->m_prevInAllResourcesList; -#ifdef ANDROID_PRELOAD_CHANGES - if (!current->referenced() && !current->isPreloaded()) { -#else - if (!current->referenced()) { -#endif - remove(current); + if (!current->hasClients() && !current->isPreloaded()) { + evict(current); + // If evict() caused pruneDeadResources() to be re-entered, bail out. This can happen when removing an + // SVG CachedImage that has subresources. + if (!m_inPruneDeadResources) + return; - if (m_deadSize <= targetSize) + if (targetSize && m_deadSize <= targetSize) { + m_inPruneDeadResources = false; return; + } } current = prev; } + // Shrink the vector back down so we don't waste time inspecting // empty LRU lists on future prunes. if (m_allResources[i].m_head) @@ -282,6 +364,7 @@ void Cache::pruneDeadResources() else if (canShrinkLRULists) m_allResources.resize(i); } + m_inPruneDeadResources = false; } void Cache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned totalBytes) @@ -294,7 +377,7 @@ void Cache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned prune(); } -void Cache::remove(CachedResource* resource) +void Cache::evict(CachedResource* resource) { // The resource may have already been removed by someone other than our caller, // who needed a fresh copy for a reload. See <http://bugs.webkit.org/show_bug.cgi?id=12479#c6>. @@ -316,8 +399,9 @@ void Cache::remove(CachedResource* resource) // Subtract from our size totals. int delta = -static_cast<int>(resource->size()); if (delta) - adjustSize(resource->referenced(), delta); - } + adjustSize(resource->hasClients(), delta); + } else + ASSERT(m_resources.get(resource->url()) != resource); if (resource->canDelete()) delete resource; @@ -415,6 +499,7 @@ void Cache::insertInLRUList(CachedResource* resource) // Make sure we aren't in some list already. ASSERT(!resource->m_nextInAllResourcesList && !resource->m_prevInAllResourcesList); ASSERT(resource->inCache()); + ASSERT(resource->accessCount() > 0); LRUList* list = lruListFor(resource); @@ -556,42 +641,42 @@ Cache::Statistics Cache::getStatistics() case CachedResource::ImageResource: stats.images.count++; stats.images.size += o->size(); - stats.images.liveSize += o->referenced() ? o->size() : 0; + stats.images.liveSize += o->hasClients() ? o->size() : 0; stats.images.decodedSize += o->decodedSize(); break; case CachedResource::CSSStyleSheet: stats.cssStyleSheets.count++; stats.cssStyleSheets.size += o->size(); - stats.cssStyleSheets.liveSize += o->referenced() ? o->size() : 0; + stats.cssStyleSheets.liveSize += o->hasClients() ? o->size() : 0; stats.cssStyleSheets.decodedSize += o->decodedSize(); break; case CachedResource::Script: stats.scripts.count++; stats.scripts.size += o->size(); - stats.scripts.liveSize += o->referenced() ? o->size() : 0; + stats.scripts.liveSize += o->hasClients() ? o->size() : 0; stats.scripts.decodedSize += o->decodedSize(); break; #if ENABLE(XSLT) case CachedResource::XSLStyleSheet: stats.xslStyleSheets.count++; stats.xslStyleSheets.size += o->size(); - stats.xslStyleSheets.liveSize += o->referenced() ? o->size() : 0; + stats.xslStyleSheets.liveSize += o->hasClients() ? o->size() : 0; stats.xslStyleSheets.decodedSize += o->decodedSize(); break; #endif case CachedResource::FontResource: stats.fonts.count++; stats.fonts.size += o->size(); - stats.fonts.liveSize += o->referenced() ? o->size() : 0; + stats.fonts.liveSize += o->hasClients() ? o->size() : 0; stats.fonts.decodedSize += o->decodedSize(); break; #if ENABLE(XBL) case CachedResource::XBL: stats.xblDocs.count++; stats.xblDocs.size += o->size(); - stats.xblDocs.liveSize += o->referenced() ? o->size() : 0; + stats.xblDocs.liveSize += o->hasClients() ? o->size() : 0; stats.xblDocs.decodedSize += o->decodedSize(); break; #endif @@ -613,7 +698,7 @@ void Cache::setDisabled(bool disabled) CachedResourceMap::iterator i = m_resources.begin(); if (i == m_resources.end()) break; - remove(i->second); + evict(i->second); } } @@ -628,8 +713,8 @@ void Cache::dumpLRULists(bool includeLive) const CachedResource* current = m_allResources[i].m_tail; while (current) { CachedResource* prev = current->m_prevInAllResourcesList; - if (includeLive || !current->referenced()) - printf("(%.1fK, %.1fK, %uA, %dR); ", current->decodedSize() / 1024.0f, current->encodedSize() / 1024.0f, current->accessCount(), current->referenced()); + if (includeLive || !current->hasClients()) + printf("(%.1fK, %.1fK, %uA, %dR); ", current->decodedSize() / 1024.0f, current->encodedSize() / 1024.0f, current->accessCount(), current->hasClients()); current = prev; } } diff --git a/WebCore/loader/Cache.h b/WebCore/loader/Cache.h index 8256708..ec0ea0e 100644 --- a/WebCore/loader/Cache.h +++ b/WebCore/loader/Cache.h @@ -3,7 +3,7 @@ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) Copyright (C) 2001 Dirk Mueller <mueller@kde.org> - Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -39,6 +39,7 @@ namespace WebCore { +class CachedCSSStyleSheet; class CachedResource; class DocLoader; class KURL; @@ -94,11 +95,14 @@ public: // Request resources from the cache. A load will be initiated and a cache object created if the object is not // found in the cache. -#ifdef ANDROID_PRELOAD_CHANGES - CachedResource* requestResource(DocLoader*, CachedResource::Type, const KURL& url, const String* charset = 0, bool skipCanLoadCheck = false, bool sendResourceLoadCallbacks = true, bool isPreload = false); -#else - CachedResource* requestResource(DocLoader*, CachedResource::Type, const KURL& url, const String* charset = 0, bool skipCanLoadCheck = false, bool sendResourceLoadCallbacks = true); -#endif + CachedResource* requestResource(DocLoader*, CachedResource::Type, const KURL& url, const String& charset, bool isPreload = false); + + CachedCSSStyleSheet* requestUserCSSStyleSheet(DocLoader*, const String& url, const String& charset); + + void revalidateResource(CachedResource*, DocLoader*); + void revalidationSucceeded(CachedResource* revalidatingResource, const ResourceResponse&); + void revalidationFailed(CachedResource* revalidatingResource); + // Sets the cache's memory capacities, in bytes. These will hold only approximately, // since the decoded cost of resources like scripts and stylesheets is not known. // - minDeadBytes: The maximum number of bytes that dead resources should consume when the cache is under pressure. @@ -114,15 +118,18 @@ public: void setPruneEnabled(bool enabled) { m_pruneEnabled = enabled; } void prune() { - if (m_liveSize + m_deadSize <= m_capacity && m_deadSize <= m_maxDeadCapacity) // Fast path. + if (m_liveSize + m_deadSize <= m_capacity && m_maxDeadCapacity && m_deadSize <= m_maxDeadCapacity) // Fast path. return; pruneDeadResources(); // Prune dead first, in case it was "borrowing" capacity from live. pruneLiveResources(); } + void setDeadDecodedDataDeletionInterval(double interval) { m_deadDecodedDataDeletionInterval = interval; } + double deadDecodedDataDeletionInterval() const { return m_deadDecodedDataDeletionInterval; } + // Remove an existing cache entry from both the resource map and from the LRU list. - void remove(CachedResource*); + void remove(CachedResource* resource) { evict(resource); } void addDocLoader(DocLoader*); void removeDocLoader(DocLoader*); @@ -167,16 +174,20 @@ private: void pruneDeadResources(); // Flush decoded and encoded data from resources not referenced by Web pages. void pruneLiveResources(); // Flush decoded data from resources still referenced by Web pages. + void evict(CachedResource*); + // Member variables. HashSet<DocLoader*> m_docLoaders; Loader m_loader; bool m_disabled; // Whether or not the cache is enabled. bool m_pruneEnabled; + bool m_inPruneDeadResources; unsigned m_capacity; unsigned m_minDeadCapacity; unsigned m_maxDeadCapacity; + double m_deadDecodedDataDeletionInterval; unsigned m_liveSize; // The number of bytes currently consumed by "live" resources in the cache. unsigned m_deadSize; // The number of bytes currently consumed by "dead" resources in the cache. diff --git a/WebCore/loader/CachedCSSStyleSheet.cpp b/WebCore/loader/CachedCSSStyleSheet.cpp index 649a16d..ace938b 100644 --- a/WebCore/loader/CachedCSSStyleSheet.cpp +++ b/WebCore/loader/CachedCSSStyleSheet.cpp @@ -27,7 +27,6 @@ #include "config.h" #include "CachedCSSStyleSheet.h" -#include "Cache.h" #include "CachedResourceClient.h" #include "CachedResourceClientWalker.h" #include "TextResourceDecoder.h" @@ -36,27 +35,25 @@ namespace WebCore { -CachedCSSStyleSheet::CachedCSSStyleSheet(DocLoader* dl, const String& url, const String& charset, bool skipCanLoadCheck, bool sendResourceLoadCallbacks) - : CachedResource(url, CSSStyleSheet, true, sendResourceLoadCallbacks) - , m_decoder(new TextResourceDecoder("text/css", charset)) +CachedCSSStyleSheet::CachedCSSStyleSheet(const String& url, const String& charset) + : CachedResource(url, CSSStyleSheet) + , m_decoder(TextResourceDecoder::create("text/css", charset)) { // Prefer text/css but accept any type (dell.com serves a stylesheet // as text/html; see <http://bugs.webkit.org/show_bug.cgi?id=11451>). setAccept("text/css,*/*;q=0.1"); - cache()->loader()->load(dl, this, false, skipCanLoadCheck, sendResourceLoadCallbacks); - m_loading = true; } CachedCSSStyleSheet::~CachedCSSStyleSheet() { } -void CachedCSSStyleSheet::ref(CachedResourceClient *c) +void CachedCSSStyleSheet::addClient(CachedResourceClient *c) { - CachedResource::ref(c); + CachedResource::addClient(c); if (!m_loading) - c->setCSSStyleSheet(m_url, m_decoder->encoding().name(), errorOccurred() ? "" : m_sheet); + c->setCSSStyleSheet(m_url, m_decoder->encoding().name(), this); } void CachedCSSStyleSheet::setEncoding(const String& chs) @@ -95,7 +92,7 @@ void CachedCSSStyleSheet::checkNotify() CachedResourceClientWalker w(m_clients); while (CachedResourceClient *c = w.next()) - c->setCSSStyleSheet(m_response.url().string(), m_decoder->encoding().name(), m_sheet); + c->setCSSStyleSheet(m_response.url().string(), m_decoder->encoding().name(), this); #if USE(LOW_BANDWIDTH_DISPLAY) // if checkNotify() is called from error(), client's setCSSStyleSheet(...) @@ -114,4 +111,17 @@ void CachedCSSStyleSheet::error() checkNotify(); } +bool CachedCSSStyleSheet::canUseSheet(bool enforceMIMEType) const +{ + if (errorOccurred()) + return false; + + if (!enforceMIMEType) + return true; + + // This check exactly matches Firefox. + String mimeType = response().mimeType(); + return mimeType.isEmpty() || equalIgnoringCase(mimeType, "text/css") || equalIgnoringCase(mimeType, "application/x-unknown-content-type"); +} + } diff --git a/WebCore/loader/CachedCSSStyleSheet.h b/WebCore/loader/CachedCSSStyleSheet.h index 50dddc2..b9129ba 100644 --- a/WebCore/loader/CachedCSSStyleSheet.h +++ b/WebCore/loader/CachedCSSStyleSheet.h @@ -39,12 +39,12 @@ namespace WebCore { class CachedCSSStyleSheet : public CachedResource { public: - CachedCSSStyleSheet(DocLoader*, const String& URL, const String& charset, bool skipCanLoadCheck = false, bool sendResourceLoadCallbacks = true); + CachedCSSStyleSheet(const String& URL, const String& charset); virtual ~CachedCSSStyleSheet(); - const String& sheet() const { return m_sheet; } + const String sheetText(bool enforceMIMEType = true) const { return canUseSheet(enforceMIMEType) ? m_sheet : ""; } - virtual void ref(CachedResourceClient*); + virtual void addClient(CachedResourceClient*); virtual void setEncoding(const String&); virtual String encoding() const; @@ -54,6 +54,9 @@ namespace WebCore { virtual bool schedule() const { return true; } void checkNotify(); + + private: + bool canUseSheet(bool enforceMIMEType) const; protected: String m_sheet; diff --git a/WebCore/loader/CachedFont.cpp b/WebCore/loader/CachedFont.cpp index 169adfa..20479ef 100644 --- a/WebCore/loader/CachedFont.cpp +++ b/WebCore/loader/CachedFont.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -48,16 +48,14 @@ namespace WebCore { -CachedFont::CachedFont(DocLoader* dl, const String &url) +CachedFont::CachedFont(const String &url) : CachedResource(url, FontResource) , m_fontData(0) + , m_loadInitiated(false) #if ENABLE(SVG_FONTS) , m_isSVGFont(false) #endif { - // Don't load the file yet. Wait for an access before triggering the load. - m_loading = true; - m_loadInitiated = false; } CachedFont::~CachedFont() @@ -67,9 +65,15 @@ CachedFont::~CachedFont() #endif } -void CachedFont::ref(CachedResourceClient *c) +void CachedFont::load(DocLoader* docLoader) +{ + // Don't load the file yet. Wait for an access before triggering the load. + m_loading = true; +} + +void CachedFont::addClient(CachedResourceClient* c) { - CachedResource::ref(c); + CachedResource::addClient(c); if (!m_loading) c->fontLoaded(this); @@ -109,7 +113,7 @@ bool CachedFont::ensureCustomFontData() return m_fontData; } -FontPlatformData CachedFont::platformDataFromCustomData(float size, bool bold, bool italic) +FontPlatformData CachedFont::platformDataFromCustomData(float size, bool bold, bool italic, FontRenderingMode renderingMode) { #if ENABLE(SVG_FONTS) if (m_externalSVGDocument) @@ -117,7 +121,7 @@ FontPlatformData CachedFont::platformDataFromCustomData(float size, bool bold, b #endif #if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(SGL) ASSERT(m_fontData); - return m_fontData->fontPlatformData(static_cast<int>(size), bold, italic); + return m_fontData->fontPlatformData(static_cast<int>(size), bold, italic, renderingMode); #else return FontPlatformData(); #endif @@ -128,11 +132,15 @@ bool CachedFont::ensureSVGFontData() { ASSERT(m_isSVGFont); if (!m_externalSVGDocument && !m_errorOccurred && !m_loading && m_data) { - m_externalSVGDocument = new SVGDocument(DOMImplementation::instance(), 0); + m_externalSVGDocument = SVGDocument::create(0); m_externalSVGDocument->open(); - TextResourceDecoder decoder("application/xml"); - m_externalSVGDocument->write(decoder.decode(m_data->data(), m_data->size())); + RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("application/xml"); + m_externalSVGDocument->write(decoder->decode(m_data->data(), m_data->size())); + if (decoder->sawError()) { + m_externalSVGDocument.clear(); + return 0; + } m_externalSVGDocument->finishParsing(); m_externalSVGDocument->close(); @@ -164,7 +172,7 @@ SVGFontElement* CachedFont::getSVGFontById(const String& fontName) const } #endif -void CachedFont::allReferencesRemoved() +void CachedFont::allClientsRemoved() { #if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) if (m_fontData) { diff --git a/WebCore/loader/CachedFont.h b/WebCore/loader/CachedFont.h index c2b06f7..fd19cdb 100644 --- a/WebCore/loader/CachedFont.h +++ b/WebCore/loader/CachedFont.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,6 +27,7 @@ #define CachedFont_h #include "CachedResource.h" +#include "FontRenderingMode.h" #include <wtf/Vector.h> #if ENABLE(SVG_FONTS) @@ -44,14 +45,16 @@ class SVGFontElement; class CachedFont : public CachedResource { public: - CachedFont(DocLoader*, const String& url); + CachedFont(const String& url); virtual ~CachedFont(); + + virtual void load(DocLoader* docLoader); - virtual void ref(CachedResourceClient*); + virtual void addClient(CachedResourceClient*); virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived); virtual void error(); - virtual void allReferencesRemoved(); + virtual void allClientsRemoved(); virtual bool schedule() const { return true; } @@ -60,7 +63,7 @@ public: void beginLoadIfNeeded(DocLoader* dl); bool ensureCustomFontData(); - FontPlatformData platformDataFromCustomData(float size, bool bold, bool italic); + FontPlatformData platformDataFromCustomData(float size, bool bold, bool italic, FontRenderingMode = NormalRenderingMode); #if ENABLE(SVG_FONTS) bool isSVGFont() const { return m_isSVGFont; } diff --git a/WebCore/loader/CachedImage.cpp b/WebCore/loader/CachedImage.cpp index f591d41..b915b17 100644 --- a/WebCore/loader/CachedImage.cpp +++ b/WebCore/loader/CachedImage.cpp @@ -30,7 +30,9 @@ #include "CachedResourceClientWalker.h" #include "DocLoader.h" #include "Frame.h" +#include "FrameView.h" #include "Request.h" +#include "Settings.h" #include "SystemTime.h" #include <wtf/Vector.h> @@ -46,66 +48,75 @@ using std::max; namespace WebCore { -CachedImage::CachedImage(DocLoader* docLoader, const String& url, bool forCache) - : CachedResource(url, ImageResource, forCache) +CachedImage::CachedImage(const String& url) + : CachedResource(url, ImageResource) + , m_image(0) + , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired) { - m_image = 0; m_status = Unknown; - if (!docLoader || docLoader->autoLoadImages()) { -#ifdef ANDROID_BLOCK_NETWORK_IMAGE - if (docLoader && docLoader->shouldBlockNetworkImage(url)) { - m_loading = false; - return; - } -#endif - m_loading = true; - cache()->loader()->load(docLoader, this, true); - } else - m_loading = false; } CachedImage::CachedImage(Image* image) - : CachedResource(String(), ImageResource, false /* not for cache */) + : CachedResource(String(), ImageResource) + , m_image(image) + , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired) { - m_image = image; m_status = Cached; m_loading = false; } CachedImage::~CachedImage() { - delete m_image; } -void CachedImage::ref(CachedResourceClient* c) +void CachedImage::decodedDataDeletionTimerFired(Timer<CachedImage>*) +{ + ASSERT(!hasClients()); + destroyDecodedData(); +} + +void CachedImage::load(DocLoader* docLoader) +{ + if (!docLoader || docLoader->autoLoadImages()) + CachedResource::load(docLoader, true, false, true); + else + m_loading = false; +} + +void CachedImage::addClient(CachedResourceClient* c) { - CachedResource::ref(c); + CachedResource::addClient(c); + + if (m_decodedDataDeletionTimer.isActive()) + m_decodedDataDeletionTimer.stop(); - if (!imageRect().isEmpty()) + if (m_image && !m_image->rect().isEmpty()) c->imageChanged(this); if (!m_loading) c->notifyFinished(this); } -void CachedImage::allReferencesRemoved() +void CachedImage::allClientsRemoved() { if (m_image && !m_errorOccurred) m_image->resetAnimation(); + if (double interval = cache()->deadDecodedDataDeletionInterval()) + m_decodedDataDeletionTimer.startOneShot(interval); } static Image* brokenImage() { - static Image* brokenImage; + static RefPtr<Image> brokenImage; if (!brokenImage) brokenImage = Image::loadPlatformResource("missingImage"); - return brokenImage; + return brokenImage.get(); } static Image* nullImage() { - static BitmapImage nullImage; - return &nullImage; + static RefPtr<BitmapImage> nullImage = BitmapImage::create(); + return nullImage.get(); } Image* CachedImage::image() const @@ -114,7 +125,7 @@ Image* CachedImage::image() const return brokenImage(); if (m_image) - return m_image; + return m_image.get(); return nullImage(); } @@ -149,27 +160,62 @@ bool CachedImage::imageHasRelativeHeight() const return false; } -IntSize CachedImage::imageSize() const +IntSize CachedImage::imageSize(float multiplier) const { - return (m_image ? m_image->size() : IntSize()); + if (!m_image) + return IntSize(); + if (multiplier == 1.0f) + return m_image->size(); + + // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. + bool hasWidth = m_image->size().width() > 0; + bool hasHeight = m_image->size().height() > 0; + int width = m_image->size().width() * (m_image->hasRelativeWidth() ? 1.0f : multiplier); + int height = m_image->size().height() * (m_image->hasRelativeHeight() ? 1.0f : multiplier); + if (hasWidth) + width = max(1, width); + if (hasHeight) + height = max(1, height); + return IntSize(width, height); } -IntRect CachedImage::imageRect() const +IntRect CachedImage::imageRect(float multiplier) const { - return (m_image ? m_image->rect() : IntRect()); + if (!m_image) + return IntRect(); + if (multiplier == 1.0f || (!m_image->hasRelativeWidth() && !m_image->hasRelativeHeight())) + return m_image->rect(); + + float widthMultiplier = (m_image->hasRelativeWidth() ? 1.0f : multiplier); + float heightMultiplier = (m_image->hasRelativeHeight() ? 1.0f : multiplier); + + // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. + bool hasWidth = m_image->rect().width() > 0; + bool hasHeight = m_image->rect().height() > 0; + + int width = static_cast<int>(m_image->rect().width() * widthMultiplier); + int height = static_cast<int>(m_image->rect().height() * heightMultiplier); + if (hasWidth) + width = max(1, width); + if (hasHeight) + height = max(1, height); + + int x = static_cast<int>(m_image->rect().x() * widthMultiplier); + int y = static_cast<int>(m_image->rect().y() * heightMultiplier); + + return IntRect(x, y, width, height); } void CachedImage::notifyObservers() { CachedResourceClientWalker w(m_clients); - while (CachedResourceClient *c = w.next()) + while (CachedResourceClient* c = w.next()) c->imageChanged(this); } void CachedImage::clear() { destroyDecodedData(); - delete m_image; m_image = 0; setEncodedSize(0); } @@ -181,22 +227,31 @@ inline void CachedImage::createImage() return; #if PLATFORM(CG) if (m_response.mimeType() == "application/pdf") { - m_image = new PDFDocumentImage; + m_image = PDFDocumentImage::create(); return; } #endif #if ENABLE(SVG_AS_IMAGE) if (m_response.mimeType() == "image/svg+xml") { - m_image = new SVGImage(this); + m_image = SVGImage::create(this); return; } #endif - m_image = new BitmapImage(this); + m_image = BitmapImage::create(this); #if PLATFORM(SGL) m_image->setURL(url()); #endif } +size_t CachedImage::maximumDecodedImageSize() +{ + Frame* frame = m_request ? m_request->docLoader()->frame() : 0; + if (!frame) + return 0; + Settings* settings = frame->settings(); + return settings ? settings->maximumDecodedImageSize() : 0; +} + void CachedImage::data(PassRefPtr<SharedBuffer> data, bool allDataReceived) { m_data = data; @@ -215,8 +270,10 @@ void CachedImage::data(PassRefPtr<SharedBuffer> data, bool allDataReceived) // network causes observers to repaint, which will force that chunk // to decode. if (sizeAvailable || allDataReceived) { - if (m_image->isNull()) { - // FIXME: I'm not convinced this case can even be hit. + size_t maxDecodedImageSize = maximumDecodedImageSize(); + IntSize s = imageSize(1.0f); + size_t estimatedDecodedImageSize = s.width() * s.height() * 4; // no overflow check + if (m_image->isNull() || (maxDecodedImageSize > 0 && estimatedDecodedImageSize > maxDecodedImageSize)) { error(); if (inCache()) cache()->remove(this); @@ -273,7 +330,7 @@ void CachedImage::didDraw(const Image* image) if (image != m_image) return; - double timeStamp = Frame::currentPaintTimeStamp(); + double timeStamp = FrameView::currentPaintTimeStamp(); if (!timeStamp) // If didDraw is called outside of a Frame paint. timeStamp = currentTime(); diff --git a/WebCore/loader/CachedImage.h b/WebCore/loader/CachedImage.h index 71e558c..f24e2fb 100644 --- a/WebCore/loader/CachedImage.h +++ b/WebCore/loader/CachedImage.h @@ -25,26 +25,29 @@ #include "CachedResource.h" #include "ImageObserver.h" +#include "Image.h" #include "IntRect.h" +#include "Timer.h" #include <wtf/Vector.h> namespace WebCore { class DocLoader; class Cache; -class Image; class CachedImage : public CachedResource, public ImageObserver { friend class Cache; public: - CachedImage(DocLoader*, const String& url, bool forCache); + CachedImage(const String& url); CachedImage(Image*); virtual ~CachedImage(); + + virtual void load(DocLoader* docLoader); Image* image() const; - bool canRender() const { return !errorOccurred() && imageSize().width() > 0 && imageSize().height() > 0; } + bool canRender(float multiplier) const { return !errorOccurred() && !imageSize(multiplier).isEmpty(); } // These are only used for SVGImage right now void setImageContainerSize(const IntSize&); @@ -52,12 +55,14 @@ public: bool imageHasRelativeWidth() const; bool imageHasRelativeHeight() const; - IntSize imageSize() const; // returns the size of the complete image - IntRect imageRect() const; // The size of the image. + // Both of these methods take a zoom multiplier that can be used to increase the natural size of the image by the + // zoom. + IntSize imageSize(float multiplier) const; // returns the size of the complete image. + IntRect imageRect(float multiplier) const; // The size of the currently decoded portion of the image. - virtual void ref(CachedResourceClient*); + virtual void addClient(CachedResourceClient*); - virtual void allReferencesRemoved(); + virtual void allClientsRemoved(); virtual void destroyDecodedData(); virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived); @@ -83,9 +88,12 @@ public: private: void createImage(); + size_t maximumDecodedImageSize(); void notifyObservers(); + void decodedDataDeletionTimerFired(Timer<CachedImage>*); - Image* m_image; + RefPtr<Image> m_image; + Timer<CachedImage> m_decodedDataDeletionTimer; }; } diff --git a/WebCore/loader/CachedResource.cpp b/WebCore/loader/CachedResource.cpp index 6c78f4b..6d0af9b 100644 --- a/WebCore/loader/CachedResource.cpp +++ b/WebCore/loader/CachedResource.cpp @@ -25,28 +25,43 @@ #include "CachedResource.h" #include "Cache.h" +#include "CachedResourceHandle.h" #include "DocLoader.h" #include "Frame.h" #include "FrameLoader.h" #include "KURL.h" #include "Request.h" #include "SystemTime.h" +#include <wtf/RefCountedLeakCounter.h> #include <wtf/Vector.h> +using namespace WTF; + namespace WebCore { -CachedResource::CachedResource(const String& url, Type type, bool forCache, bool sendResourceLoadCallbacks) +#ifndef NDEBUG +static RefCountedLeakCounter cachedResourceLeakCounter("CachedResource"); +#endif + +CachedResource::CachedResource(const String& url, Type type) : m_url(url) , m_lastDecodedAccessTime(0) - , m_sendResourceLoadCallbacks(sendResourceLoadCallbacks) -#ifdef ANDROID_PRELOAD_CHANGES + , m_sendResourceLoadCallbacks(true) , m_preloadCount(0) , m_preloadResult(PreloadNotReferenced) , m_requestedFromNetworkingLayer(false) -#endif - , m_inCache(forCache) + , m_inCache(false) + , m_loading(false) , m_docLoader(0) + , m_handleCount(0) + , m_resourceToRevalidate(0) + , m_isBeingRevalidated(false) + , m_expirationDate(0) { +#ifndef NDEBUG + cachedResourceLeakCounter.increment(); +#endif + m_type = type; m_status = Pending; m_encodedSize = 0; @@ -67,20 +82,31 @@ CachedResource::CachedResource(const String& url, Type type, bool forCache, bool m_lruIndex = 0; #endif m_errorOccurred = false; - m_shouldTreatAsLocal = FrameLoader::shouldTreatURLAsLocal(m_url); } CachedResource::~CachedResource() { ASSERT(!inCache()); ASSERT(!m_deleted); + ASSERT(url().isNull() || cache()->resourceForURL(url()) != this); #ifndef NDEBUG m_deleted = true; + cachedResourceLeakCounter.decrement(); #endif - + + if (m_resourceToRevalidate) + m_resourceToRevalidate->m_isBeingRevalidated = false; + if (m_docLoader) m_docLoader->removeCachedResource(this); } + +void CachedResource::load(DocLoader* docLoader, bool incremental, bool skipCanLoadCheck, bool sendResourceLoadCallbacks) +{ + m_sendResourceLoadCallbacks = sendResourceLoadCallbacks; + cache()->loader()->load(docLoader, this, incremental, skipCanLoadCheck, sendResourceLoadCallbacks); + m_loading = true; +} void CachedResource::finish() { @@ -89,12 +115,18 @@ void CachedResource::finish() bool CachedResource::isExpired() const { - if (!m_response.expirationDate()) + if (!m_expirationDate) return false; time_t now = time(0); - return (difftime(now, m_response.expirationDate()) >= 0); + return difftime(now, m_expirationDate) >= 0; } - + +void CachedResource::setResponse(const ResourceResponse& response) +{ + m_response = response; + m_expirationDate = response.expirationDate(); +} + void CachedResource::setRequest(Request* request) { if (request && !m_request) @@ -104,9 +136,8 @@ void CachedResource::setRequest(Request* request) delete this; } -void CachedResource::ref(CachedResourceClient *c) +void CachedResource::addClient(CachedResourceClient *c) { -#ifdef ANDROID_PRELOAD_CHANGES if (m_preloadResult == PreloadNotReferenced) { if (isLoaded()) m_preloadResult = PreloadReferencedWhileComplete; @@ -115,26 +146,31 @@ void CachedResource::ref(CachedResourceClient *c) else m_preloadResult = PreloadReferenced; } -#endif - if (!referenced() && inCache()) + if (!hasClients() && inCache()) cache()->addToLiveResourcesSize(this); m_clients.add(c); } -void CachedResource::deref(CachedResourceClient *c) +void CachedResource::removeClient(CachedResourceClient *c) { ASSERT(m_clients.contains(c)); m_clients.remove(c); if (canDelete() && !inCache()) delete this; - else if (!referenced() && inCache()) { + else if (!hasClients() && inCache()) { cache()->removeFromLiveResourcesSize(this); cache()->removeFromLiveDecodedResourcesList(this); - allReferencesRemoved(); + allClientsRemoved(); cache()->prune(); } } +void CachedResource::deleteIfPossible() +{ + if (canDelete() && !inCache()) + delete this; +} + void CachedResource::setDecodedSize(unsigned size) { if (size == m_decodedSize) @@ -155,13 +191,13 @@ void CachedResource::setDecodedSize(unsigned size) cache()->insertInLRUList(this); // Insert into or remove from the live decoded list if necessary. - if (m_decodedSize && !m_inLiveDecodedResourcesList && referenced()) + if (m_decodedSize && !m_inLiveDecodedResourcesList && hasClients()) cache()->insertInLiveDecodedResourcesList(this); else if (!m_decodedSize && m_inLiveDecodedResourcesList) cache()->removeFromLiveDecodedResourcesList(this); // Update the cache's size totals. - cache()->adjustSize(referenced(), delta); + cache()->adjustSize(hasClients(), delta); } } @@ -188,7 +224,7 @@ void CachedResource::setEncodedSize(unsigned size) cache()->insertInLRUList(this); // Update the cache's size totals. - cache()->adjustSize(referenced(), delta); + cache()->adjustSize(hasClients(), delta); } } @@ -204,5 +240,77 @@ void CachedResource::didAccessDecodedData(double timeStamp) cache()->prune(); } } + +void CachedResource::setResourceToRevalidate(CachedResource* resource) +{ + ASSERT(resource); + ASSERT(!m_resourceToRevalidate); + ASSERT(resource != this); + ASSERT(!resource->m_isBeingRevalidated); + ASSERT(m_handlesToRevalidate.isEmpty()); + ASSERT(resource->type() == type()); + resource->m_isBeingRevalidated = true; + m_resourceToRevalidate = resource; +} + +void CachedResource::clearResourceToRevalidate() +{ + ASSERT(m_resourceToRevalidate); + ASSERT(m_resourceToRevalidate->m_isBeingRevalidated); + m_resourceToRevalidate->m_isBeingRevalidated = false; + m_resourceToRevalidate->deleteIfPossible(); + m_handlesToRevalidate.clear(); + m_resourceToRevalidate = 0; + deleteIfPossible(); +} + +void CachedResource::switchClientsToRevalidatedResource() +{ + ASSERT(m_resourceToRevalidate); + ASSERT(!inCache()); + + HashSet<CachedResourceHandleBase*>::iterator end = m_handlesToRevalidate.end(); + for (HashSet<CachedResourceHandleBase*>::iterator it = m_handlesToRevalidate.begin(); it != end; ++it) { + CachedResourceHandleBase* handle = *it; + handle->m_resource = m_resourceToRevalidate; + m_resourceToRevalidate->registerHandle(handle); + --m_handleCount; + } + ASSERT(!m_handleCount); + m_handlesToRevalidate.clear(); + + Vector<CachedResourceClient*> clientsToMove; + HashCountedSet<CachedResourceClient*>::iterator end2 = m_clients.end(); + for (HashCountedSet<CachedResourceClient*>::iterator it = m_clients.begin(); it != end2; ++it) { + CachedResourceClient* client = it->first; + unsigned count = it->second; + while (count) { + clientsToMove.append(client); + --count; + } + } + // Equivalent of calling removeClient() for all clients + m_clients.clear(); + + unsigned moveCount = clientsToMove.size(); + for (unsigned n = 0; n < moveCount; ++n) + m_resourceToRevalidate->addClient(clientsToMove[n]); +} + +bool CachedResource::canUseCacheValidator() const +{ + return !m_loading && (!m_response.httpHeaderField("Last-Modified").isEmpty() || !m_response.httpHeaderField("ETag").isEmpty()); +} + +bool CachedResource::mustRevalidate(CachePolicy cachePolicy) const +{ + if (m_loading) + return false; + String cacheControl = m_response.httpHeaderField("Cache-Control"); + // FIXME: It would be better to tokenize the field. + if (cachePolicy == CachePolicyCache) + return !cacheControl.isEmpty() && (cacheControl.contains("no-cache", false) || (isExpired() && cacheControl.contains("must-revalidate", false))); + return isExpired() || cacheControl.contains("no-cache", false); +} } diff --git a/WebCore/loader/CachedResource.h b/WebCore/loader/CachedResource.h index bbfeac1..c56a889 100644 --- a/WebCore/loader/CachedResource.h +++ b/WebCore/loader/CachedResource.h @@ -23,10 +23,12 @@ #ifndef CachedResource_h #define CachedResource_h +#include "CachePolicy.h" #include "PlatformString.h" #include "ResourceResponse.h" #include "SharedBuffer.h" #include <wtf/HashCountedSet.h> +#include <wtf/HashSet.h> #include <wtf/Vector.h> #include <time.h> @@ -34,6 +36,7 @@ namespace WebCore { class Cache; class CachedResourceClient; +class CachedResourceHandleBase; class DocLoader; class Request; @@ -65,8 +68,11 @@ public: Cached // regular case }; - CachedResource(const String& url, Type, bool forCache = true, bool sendResourceLoadCallbacks = false); + CachedResource(const String& url, Type); virtual ~CachedResource(); + + virtual void load(DocLoader* docLoader) { load(docLoader, false, false, true); } + void load(DocLoader*, bool incremental, bool skipCanLoadCheck, bool sendResourceLoadCallbacks); virtual void setEncoding(const String&) { } virtual String encoding() const { return String(); } @@ -76,11 +82,11 @@ public: const String &url() const { return m_url; } Type type() const { return m_type; } - virtual void ref(CachedResourceClient*); - void deref(CachedResourceClient*); - bool referenced() const { return !m_clients.isEmpty(); } + virtual void addClient(CachedResourceClient*); + void removeClient(CachedResourceClient*); + bool hasClients() const { return !m_clients.isEmpty(); } + void deleteIfPossible(); -#ifdef ANDROID_PRELOAD_CHANGES enum PreloadResult { PreloadNotReferenced, PreloadReferenced, @@ -89,9 +95,8 @@ public: }; PreloadResult preloadResult() const { return m_preloadResult; } void setRequestedFromNetworkingLayer() { m_requestedFromNetworkingLayer = true; } -#endif - - virtual void allReferencesRemoved() {}; + + virtual void allClientsRemoved() { }; unsigned count() const { return m_clients.size(); } @@ -126,14 +131,10 @@ public: SharedBuffer* data() const { return m_data.get(); } - void setResponse(const ResourceResponse& response) { m_response = response; } + void setResponse(const ResourceResponse&); const ResourceResponse& response() const { return m_response; } -#ifdef ANDROID_PRELOAD_CHANGES - bool canDelete() const { return !referenced() && !m_request && !m_preloadCount; } -#else - bool canDelete() const { return !referenced() && !m_request; } -#endif + bool canDelete() const { return !hasClients() && !m_request && !m_preloadCount && !m_handleCount && !m_resourceToRevalidate && !m_isBeingRevalidated; } bool isExpired() const; @@ -145,24 +146,29 @@ public: void setAccept(const String& accept) { m_accept = accept; } bool errorOccurred() const { return m_errorOccurred; } - bool treatAsLocal() const { return m_shouldTreatAsLocal; } bool sendResourceLoadCallbacks() const { return m_sendResourceLoadCallbacks; } virtual void destroyDecodedData() {}; void setDocLoader(DocLoader* docLoader) { m_docLoader = docLoader; } - -#ifdef ANDROID_PRELOAD_CHANGES + bool isPreloaded() const { return m_preloadCount; } void increasePreloadCount() { ++m_preloadCount; } void decreasePreloadCount() { ASSERT(m_preloadCount); --m_preloadCount; } -#endif - - protected: + + void registerHandle(CachedResourceHandleBase* h) { ++m_handleCount; if (m_resourceToRevalidate) m_handlesToRevalidate.add(h); } + void unregisterHandle(CachedResourceHandleBase* h) { --m_handleCount; if (m_resourceToRevalidate) m_handlesToRevalidate.remove(h); if (!m_handleCount) deleteIfPossible(); } + + bool canUseCacheValidator() const; + bool mustRevalidate(CachePolicy) const; + bool isCacheValidator() const { return m_resourceToRevalidate; } + CachedResource* resourceToRevalidate() const { return m_resourceToRevalidate; } + +protected: void setEncodedSize(unsigned); void setDecodedSize(unsigned); void didAccessDecodedData(double timeStamp); - + HashCountedSet<CachedResourceClient*> m_clients; String m_url; @@ -178,6 +184,12 @@ public: bool m_errorOccurred; private: + // These are called by the friendly Cache only + void setResourceToRevalidate(CachedResource*); + void switchClientsToRevalidatedResource(); + void clearResourceToRevalidate(); + void setExpirationDate(time_t expirationDate) { m_expirationDate = expirationDate; } + unsigned m_encodedSize; unsigned m_decodedSize; unsigned m_accessCount; @@ -185,12 +197,10 @@ private: double m_lastDecodedAccessTime; // Used as a "thrash guard" in the cache bool m_sendResourceLoadCallbacks; - -#ifdef ANDROID_PRELOAD_CHANGES + unsigned m_preloadCount; PreloadResult m_preloadResult; bool m_requestedFromNetworkingLayer; -#endif protected: bool m_inCache; @@ -208,9 +218,19 @@ private: CachedResource* m_nextInLiveResourcesList; CachedResource* m_prevInLiveResourcesList; - bool m_shouldTreatAsLocal; - DocLoader* m_docLoader; // only non-0 for resources that are not in the cache + + unsigned m_handleCount; + // If this field is non-null we are using the resource as a proxy for checking whether an existing resource is still up to date + // using HTTP If-Modified-Since/If-None-Match headers. If the response is 304 all clients of this resource are moved + // to to be clients of m_resourceToRevalidate and the resource is deleted. If not, the field is zeroed and this + // resources becomes normal resource load. + CachedResource* m_resourceToRevalidate; + bool m_isBeingRevalidated; + // These handles will need to be updated to point to the m_resourceToRevalidate in case we get 304 response. + HashSet<CachedResourceHandleBase*> m_handlesToRevalidate; + + time_t m_expirationDate; }; } diff --git a/WebCore/loader/CachedResourceClient.h b/WebCore/loader/CachedResourceClient.h index 48631e3..1d8d45e 100644 --- a/WebCore/loader/CachedResourceClient.h +++ b/WebCore/loader/CachedResourceClient.h @@ -35,6 +35,7 @@ namespace XBL { namespace WebCore { + class CachedCSSStyleSheet; class CachedFont; class CachedResource; class CachedImage; @@ -64,7 +65,7 @@ namespace WebCore { // e.g., in the b/f cache or in a background tab). virtual bool willRenderImage(CachedImage*) { return false; } - virtual void setCSSStyleSheet(const String& /*URL*/, const String& /*charset*/, const String& /*sheet*/) { } + virtual void setCSSStyleSheet(const String& /*URL*/, const String& /*charset*/, const CachedCSSStyleSheet*) { } virtual void setXSLStyleSheet(const String& /*URL*/, const String& /*sheet*/) { } virtual void fontLoaded(CachedFont*) {}; diff --git a/WebCore/loader/android/DocumentLoaderAndroid.cpp b/WebCore/loader/CachedResourceHandle.cpp index f402495..871292c 100644 --- a/WebCore/loader/android/DocumentLoaderAndroid.cpp +++ b/WebCore/loader/CachedResourceHandle.cpp @@ -1,8 +1,5 @@ /* - * Copyright (C) 2006 Zack Rusin <zack@kde.org> - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * - * All rights reserved. + * Copyright (C) 2008 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -13,38 +10,33 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" -#include "DocumentLoader.h" - -#include "KURL.h" -#include <stdio.h> - -#define LOG_TAG "WebCore" -#undef LOG -#include <utils/Log.h> +#include "CachedResourceHandle.h" namespace WebCore { -#if 0 // FIXME: no longer defined -bool DocumentLoader::getResponseModifiedHeader(String& modified) const +void CachedResourceHandleBase::setResource(CachedResource* resource) { - LOGV("--- unimpl %s\n", __PRETTY_FUNCTION__); - return false; + if (resource == m_resource) + return; + if (m_resource) + m_resource->unregisterHandle(this); + m_resource = resource; + if (m_resource) + m_resource->registerHandle(this); } -#endif } - diff --git a/WebCore/loader/CachedResourceHandle.h b/WebCore/loader/CachedResourceHandle.h new file mode 100644 index 0000000..13c03c7 --- /dev/null +++ b/WebCore/loader/CachedResourceHandle.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 CachedResourceHandle_h +#define CachedResourceHandle_h + +#include "CachedResource.h" + +namespace WebCore { + + class CachedResourceHandleBase { + public: + ~CachedResourceHandleBase() { if (m_resource) m_resource->unregisterHandle(this); } + CachedResource* get() const { return m_resource; } + + bool operator!() const { return !m_resource; } + + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef CachedResource* CachedResourceHandleBase::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { return m_resource ? &CachedResourceHandleBase::m_resource : 0; } + + protected: + CachedResourceHandleBase() : m_resource(0) {} + CachedResourceHandleBase(CachedResource* res) { m_resource = res; if (m_resource) m_resource->registerHandle(this); } + CachedResourceHandleBase(const CachedResourceHandleBase& o) : m_resource(o.m_resource) { if (m_resource) m_resource->registerHandle(this); } + + void setResource(CachedResource*); + + private: + CachedResourceHandleBase& operator=(const CachedResourceHandleBase&) { return *this; } + + friend class CachedResource; + + CachedResource* m_resource; + }; + + template <class R> class CachedResourceHandle : public CachedResourceHandleBase { + public: + CachedResourceHandle() { } + CachedResourceHandle(R* res) : CachedResourceHandleBase(res) { } + CachedResourceHandle(const CachedResourceHandle<R>& o) : CachedResourceHandleBase(o) { } + + R* get() const { return reinterpret_cast<R*>(CachedResourceHandleBase::get()); } + R* operator->() const { return get(); } + + CachedResourceHandle& operator=(R* res) { setResource(res); return *this; } + CachedResourceHandle& operator=(const CachedResourceHandle& o) { setResource(o.get()); return *this; } + bool operator==(const CachedResourceHandleBase& o) const { return get() == o.get(); } + bool operator!=(const CachedResourceHandleBase& o) const { return get() != o.get(); } + }; + + template <class R, class RR> bool operator==(const CachedResourceHandle<R>& h, const RR* res) + { + return h.get() == res; + } + template <class R, class RR> bool operator==(const RR* res, const CachedResourceHandle<R>& h) + { + return h.get() == res; + } + template <class R, class RR> bool operator!=(const CachedResourceHandle<R>& h, const RR* res) + { + return h.get() != res; + } + template <class R, class RR> bool operator!=(const RR* res, const CachedResourceHandle<R>& h) + { + return h.get() != res; + } +} + +#endif diff --git a/WebCore/loader/CachedScript.cpp b/WebCore/loader/CachedScript.cpp index f8cde0e..9666eea 100644 --- a/WebCore/loader/CachedScript.cpp +++ b/WebCore/loader/CachedScript.cpp @@ -29,15 +29,13 @@ #include "config.h" #include "CachedScript.h" -#include "Cache.h" #include "CachedResourceClient.h" #include "CachedResourceClientWalker.h" -#include "loader.h" #include <wtf/Vector.h> namespace WebCore { -CachedScript::CachedScript(DocLoader* dl, const String& url, const String& charset) +CachedScript::CachedScript(const String& url, const String& charset) : CachedResource(url, Script) , m_encoding(charset) { @@ -45,9 +43,6 @@ CachedScript::CachedScript(DocLoader* dl, const String& url, const String& chars // But some websites think their scripts are <some wrong mimetype here> // and refuse to serve them if we only accept application/x-javascript. setAccept("*/*"); - // load the file - cache()->loader()->load(dl, this, false); - m_loading = true; if (!m_encoding.isValid()) m_encoding = Latin1Encoding(); } @@ -56,9 +51,9 @@ CachedScript::~CachedScript() { } -void CachedScript::ref(CachedResourceClient* c) +void CachedScript::addClient(CachedResourceClient* c) { - CachedResource::ref(c); + CachedResource::addClient(c); if (!m_loading) c->notifyFinished(this); } diff --git a/WebCore/loader/CachedScript.h b/WebCore/loader/CachedScript.h index 06e0376..4580cfe 100644 --- a/WebCore/loader/CachedScript.h +++ b/WebCore/loader/CachedScript.h @@ -37,12 +37,12 @@ namespace WebCore { class CachedScript : public CachedResource { public: - CachedScript(DocLoader*, const String& url, const String& charset); + CachedScript(const String& url, const String& charset); virtual ~CachedScript(); const String& script() const { return m_script; } - virtual void ref(CachedResourceClient*); + virtual void addClient(CachedResourceClient*); virtual void setEncoding(const String&); virtual String encoding() const; diff --git a/WebCore/loader/CachedXBLDocument.cpp b/WebCore/loader/CachedXBLDocument.cpp index da603a9..1ef3bae 100644 --- a/WebCore/loader/CachedXBLDocument.cpp +++ b/WebCore/loader/CachedXBLDocument.cpp @@ -32,23 +32,18 @@ #include "CachedXBLDocument.h" -#include "Cache.h" #include "CachedResourceClientWalker.h" #include "TextResourceDecoder.h" -#include "loader.h" #include <wtf/Vector.h> namespace WebCore { -CachedXBLDocument::CachedXBLDocument(DocLoader* dl, const String &url) +CachedXBLDocument::CachedXBLDocument(const String &url) : CachedResource(url, XBL), m_document(0) { // It's XML we want. setAccept("text/xml, application/xml, application/xhtml+xml, text/xsl, application/rss+xml, application/atom+xml"); - - // Load the file - Cache::loader()->load(dl, this, false); - m_loading = true; + m_decoder = new TextResourceDecoder("application/xml"); } diff --git a/WebCore/loader/CachedXBLDocument.h b/WebCore/loader/CachedXBLDocument.h index 692f20b..a66a0cb 100644 --- a/WebCore/loader/CachedXBLDocument.h +++ b/WebCore/loader/CachedXBLDocument.h @@ -41,12 +41,12 @@ namespace WebCore { #if ENABLE(XBL) class CachedXBLDocument : public CachedResource { public: - CachedXBLDocument(DocLoader*, const String& url); + CachedXBLDocument(const String& url); virtual ~CachedXBLDocument(); XBL::XBLDocument* document() const { return m_document; } - virtual void ref(CachedResourceClient*); + virtual void addClient(CachedResourceClient*); virtual void setEncoding(const String&); virtual String encoding() const; diff --git a/WebCore/loader/CachedXSLStyleSheet.cpp b/WebCore/loader/CachedXSLStyleSheet.cpp index 0836fb5..009b2af 100644 --- a/WebCore/loader/CachedXSLStyleSheet.cpp +++ b/WebCore/loader/CachedXSLStyleSheet.cpp @@ -29,33 +29,27 @@ #include "config.h" #include "CachedXSLStyleSheet.h" -#include "Cache.h" #include "CachedResourceClient.h" #include "CachedResourceClientWalker.h" #include "TextResourceDecoder.h" -#include "loader.h" #include <wtf/Vector.h> namespace WebCore { #if ENABLE(XSLT) -CachedXSLStyleSheet::CachedXSLStyleSheet(DocLoader* dl, const String &url) +CachedXSLStyleSheet::CachedXSLStyleSheet(const String &url) : CachedResource(url, XSLStyleSheet) - , m_decoder(new TextResourceDecoder("text/xsl")) + , m_decoder(TextResourceDecoder::create("text/xsl")) { // It's XML we want. // FIXME: This should accept more general xml formats */*+xml, image/svg+xml for example. setAccept("text/xml, application/xml, application/xhtml+xml, text/xsl, application/rss+xml, application/atom+xml"); - - // load the file - cache()->loader()->load(dl, this, false); - m_loading = true; } -void CachedXSLStyleSheet::ref(CachedResourceClient *c) +void CachedXSLStyleSheet::addClient(CachedResourceClient *c) { - CachedResource::ref(c); + CachedResource::addClient(c); if (!m_loading) c->setXSLStyleSheet(m_url, m_sheet); diff --git a/WebCore/loader/CachedXSLStyleSheet.h b/WebCore/loader/CachedXSLStyleSheet.h index cddf840..c5326fa 100644 --- a/WebCore/loader/CachedXSLStyleSheet.h +++ b/WebCore/loader/CachedXSLStyleSheet.h @@ -39,11 +39,11 @@ namespace WebCore { #if ENABLE(XSLT) class CachedXSLStyleSheet : public CachedResource { public: - CachedXSLStyleSheet(DocLoader*, const String& url); + CachedXSLStyleSheet(const String& url); const String& sheet() const { return m_sheet; } - virtual void ref(CachedResourceClient*); + virtual void addClient(CachedResourceClient*); virtual void setEncoding(const String&); virtual String encoding() const; diff --git a/WebCore/loader/DocLoader.cpp b/WebCore/loader/DocLoader.cpp index e2a2517..4dbc6a0 100644 --- a/WebCore/loader/DocLoader.cpp +++ b/WebCore/loader/DocLoader.cpp @@ -1,10 +1,8 @@ /* - This file is part of the KDE libraries - Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) Copyright (C) 2001 Dirk Mueller (mueller@kde.org) Copyright (C) 2002 Waldo Bastian (bastian@kde.org) - Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -34,19 +32,23 @@ #include "CachedImage.h" #include "CachedScript.h" #include "CachedXSLStyleSheet.h" +#include "Console.h" +#include "CString.h" #include "Document.h" +#include "DOMWindow.h" #include "Frame.h" #include "FrameLoader.h" #include "loader.h" +#include "SecurityOrigin.h" +#include "Settings.h" #define PRELOAD_DEBUG 0 namespace WebCore { -DocLoader::DocLoader(Frame *frame, Document* doc) +DocLoader::DocLoader(Document* doc) : m_cache(cache()) , m_cachePolicy(CachePolicyVerify) - , m_frame(frame) , m_doc(doc) , m_requestCount(0) #ifdef ANDROID_BLOCK_NETWORK_IMAGE @@ -61,51 +63,49 @@ DocLoader::DocLoader(Frame *frame, Document* doc) DocLoader::~DocLoader() { -#ifdef ANDROID_PRELOAD_CHANGES clearPreloads(); -#endif HashMap<String, CachedResource*>::iterator end = m_docResources.end(); for (HashMap<String, CachedResource*>::iterator it = m_docResources.begin(); it != end; ++it) it->second->setDocLoader(0); m_cache->removeDocLoader(this); } +Frame* DocLoader::frame() const +{ + return m_doc->frame(); +} + void DocLoader::checkForReload(const KURL& fullURL) { if (m_allowStaleResources) - return; //Don't reload resources while pasting - if (m_cachePolicy == CachePolicyVerify) { + return; // Don't reload resources while pasting + + if (fullURL.isEmpty()) + return; + + if (m_cachePolicy == CachePolicyVerify || m_cachePolicy == CachePolicyCache) { if (!m_reloadedURLs.contains(fullURL.string())) { CachedResource* existing = cache()->resourceForURL(fullURL.string()); -#ifdef ANDROID_PRELOAD_CHANGES - if (existing && existing->isExpired() && !existing->isPreloaded()) { -#else - if (existing && existing->isExpired()) { -#endif - cache()->remove(existing); - m_reloadedURLs.add(fullURL.string()); + if (existing && !existing->isPreloaded() && existing->mustRevalidate(m_cachePolicy)) { + cache()->revalidateResource(existing, this); + m_reloadedURLs.add(fullURL.string()); } } } else if ((m_cachePolicy == CachePolicyReload) || (m_cachePolicy == CachePolicyRefresh)) { if (!m_reloadedURLs.contains(fullURL.string())) { - CachedResource* existing = cache()->resourceForURL(fullURL.string()); -#ifdef ANDROID_PRELOAD_CHANGES - if (existing && !existing->isPreloaded()) { -#else - if (existing) -#endif - cache()->remove(existing); - m_reloadedURLs.add(fullURL.string()); -#ifdef ANDROID_PRELOAD_CHANGES - } -#endif + CachedResource* existing = cache()->resourceForURL(fullURL.string()); + if (existing && !existing->isPreloaded()) { + // FIXME: Use revalidateResource() to implement HTTP 1.1 "Specific end-to-end revalidation" for regular reloading + cache()->remove(existing); + m_reloadedURLs.add(fullURL.string()); + } } } } CachedImage* DocLoader::requestImage(const String& url) { - CachedImage* resource = static_cast<CachedImage*>(requestResource(CachedResource::ImageResource, url)); + CachedImage* resource = static_cast<CachedImage*>(requestResource(CachedResource::ImageResource, url, String())); if (autoLoadImages() && resource && resource->stillNeedsLoad()) { #ifdef ANDROID_BLOCK_NETWORK_IMAGE if (shouldBlockNetworkImage(url)) { @@ -120,57 +120,78 @@ CachedImage* DocLoader::requestImage(const String& url) CachedFont* DocLoader::requestFont(const String& url) { - return static_cast<CachedFont*>(requestResource(CachedResource::FontResource, url)); + return static_cast<CachedFont*>(requestResource(CachedResource::FontResource, url, String())); } -CachedCSSStyleSheet* DocLoader::requestCSSStyleSheet(const String& url, const String& charset, bool isUserStyleSheet) +CachedCSSStyleSheet* DocLoader::requestCSSStyleSheet(const String& url, const String& charset) { - // FIXME: Passing true for "skipCanLoadCheck" here in the isUserStyleSheet case won't have any effect - // if this resource is already in the cache. It's theoretically possible that what's in the cache already - // is a load that failed because of the canLoad check. Probably not an issue in practice. - CachedCSSStyleSheet *sheet = static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, url, &charset, isUserStyleSheet, !isUserStyleSheet)); - - // A user style sheet can outlive its DocLoader so don't store any pointers to it - if (sheet && isUserStyleSheet) { - sheet->setDocLoader(0); - m_docResources.remove(sheet->url()); - } - - return sheet; + return static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, url, charset)); } CachedCSSStyleSheet* DocLoader::requestUserCSSStyleSheet(const String& url, const String& charset) { - return requestCSSStyleSheet(url, charset, true); + return cache()->requestUserCSSStyleSheet(this, url, charset); } CachedScript* DocLoader::requestScript(const String& url, const String& charset) { - return static_cast<CachedScript*>(requestResource(CachedResource::Script, url, &charset)); + return static_cast<CachedScript*>(requestResource(CachedResource::Script, url, charset)); } #if ENABLE(XSLT) CachedXSLStyleSheet* DocLoader::requestXSLStyleSheet(const String& url) { - return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XSLStyleSheet, url)); + return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XSLStyleSheet, url, String())); } #endif #if ENABLE(XBL) CachedXBLDocument* DocLoader::requestXBLDocument(const String& url) { - return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XBL, url)); + return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XBL, url, String())); } #endif -#ifdef ANDROID_PRELOAD_CHANGES -CachedResource* DocLoader::requestResource(CachedResource::Type type, const String& url, const String* charset, bool skipCanLoadCheck, bool sendResourceLoadCallbacks, bool isPreload) -#else -CachedResource* DocLoader::requestResource(CachedResource::Type type, const String& url, const String* charset, bool skipCanLoadCheck, bool sendResourceLoadCallbacks) +bool DocLoader::canRequest(CachedResource::Type type, const KURL& url) +{ + // Some types of resources can be loaded only from the same origin. Other + // types of resources, like Images, Scripts, and CSS, can be loaded from + // any URL. + switch (type) { + case CachedResource::ImageResource: + case CachedResource::CSSStyleSheet: + case CachedResource::Script: + case CachedResource::FontResource: + // These types of resources can be loaded from any origin. + // FIXME: Are we sure about CachedResource::FontResource? + break; +#if ENABLE(XSLT) + case CachedResource::XSLStyleSheet: +#endif +#if ENABLE(XBL) + case CachedResource::XBL: +#endif +#if ENABLE(XSLT) || ENABLE(XBL) + if (!m_doc->securityOrigin()->canRequest(url)) { + printAccessDeniedMessage(url); + return false; + } + break; #endif + default: + ASSERT_NOT_REACHED(); + break; + } + return true; +} + +CachedResource* DocLoader::requestResource(CachedResource::Type type, const String& url, const String& charset, bool isPreload) { - KURL fullURL = m_doc->completeURL(url.deprecatedString()); - + KURL fullURL = m_doc->completeURL(url); + + if (!canRequest(type, fullURL)) + return 0; + if (cache()->disabled()) { HashMap<String, CachedResource*>::iterator it = m_docResources.find(fullURL.string()); @@ -179,23 +200,48 @@ CachedResource* DocLoader::requestResource(CachedResource::Type type, const Stri m_docResources.remove(it); } } - - if (m_frame && m_frame->loader()->isReloading()) + + if (frame() && frame()->loader()->isReloading()) setCachePolicy(CachePolicyReload); checkForReload(fullURL); -#ifdef ANDROID_PRELOAD_CHANGES - CachedResource* resource = cache()->requestResource(this, type, fullURL, charset, skipCanLoadCheck, sendResourceLoadCallbacks, isPreload); -#else - CachedResource* resource = cache()->requestResource(this, type, fullURL, charset, skipCanLoadCheck, sendResourceLoadCallbacks); -#endif + CachedResource* resource = cache()->requestResource(this, type, fullURL, charset, isPreload); if (resource) { + // Check final URL of resource to catch redirects. + // See <https://bugs.webkit.org/show_bug.cgi?id=21963>. + if (!canRequest(type, KURL(resource->url()))) + return 0; + m_docResources.set(resource->url(), resource); checkCacheObjectStatus(resource); } return resource; } +void DocLoader::printAccessDeniedMessage(const KURL& url) const +{ + if (url.isNull()) + return; + + if (!frame()) + return; + + Settings* settings = frame()->settings(); + if (!settings || settings->privateBrowsingEnabled()) + return; + + String message = m_doc->url().isNull() ? + String::format("Unsafe attempt to load URL %s.", + url.string().utf8().data()) : + String::format("Unsafe attempt to load URL %s from frame with URL %s. " + "Domains, protocols and ports must match.\n", + url.string().utf8().data(), + m_doc->url().string().utf8().data()); + + // FIXME: provide a real line number and source URL. + frame()->domWindow()->console()->addMessage(OtherMessageSource, ErrorMessageLevel, message, 1, String()); +} + void DocLoader::setAutoLoadImages(bool enable) { if (enable == m_autoLoadImages) @@ -228,7 +274,7 @@ bool DocLoader::shouldBlockNetworkImage(const String& url) const if (!m_blockNetworkImage) return false; - KURL kurl(url.deprecatedString()); + KURL kurl(url); if (kurl.protocolIs("http") || kurl.protocolIs("https")) return true; @@ -270,14 +316,14 @@ void DocLoader::removeCachedResource(CachedResource* resource) const void DocLoader::setLoadInProgress(bool load) { m_loadInProgress = load; - if (!load && m_frame) - m_frame->loader()->loadDone(); + if (!load && frame()) + frame()->loader()->loadDone(); } void DocLoader::checkCacheObjectStatus(CachedResource* resource) { // Return from the function for objects that we didn't load from the cache or if we don't have a frame. - if (!resource || !m_frame) + if (!resource || !frame()) return; switch (resource->status()) { @@ -291,7 +337,7 @@ void DocLoader::checkCacheObjectStatus(CachedResource* resource) } // FIXME: If the WebKit client changes or cancels the request, WebCore does not respect this and continues the load. - m_frame->loader()->loadedResourceFromMemoryCache(resource); + frame()->loader()->loadedResourceFromMemoryCache(resource); } void DocLoader::incrementRequestCount() @@ -311,38 +357,39 @@ int DocLoader::requestCount() return m_requestCount + 1; return m_requestCount; } - -#ifdef ANDROID_PRELOAD_CHANGES -void DocLoader::preload(CachedResource::Type type, const String& url, const String& charset, bool inBody) + +void DocLoader::preload(CachedResource::Type type, const String& url, const String& charset, bool referencedFromBody) { - if ((inBody || type == CachedResource::ImageResource) && (!m_doc->body() || !m_doc->body()->renderer())) { - // Don't preload images or body resources before we have the first rendering. + bool hasRendering = m_doc->body() && m_doc->body()->renderer(); + if (!hasRendering && (referencedFromBody || type == CachedResource::ImageResource)) { + // Don't preload images or body resources before we have something to draw. This prevents + // preloads from body delaying first display when bandwidth is limited. PendingPreload pendingPreload = { type, url, charset }; m_pendingPreloads.append(pendingPreload); return; } requestPreload(type, url, charset); } - + void DocLoader::checkForPendingPreloads() { unsigned count = m_pendingPreloads.size(); if (!count || !m_doc->body() || !m_doc->body()->renderer()) return; - for (unsigned n = 0; n < count; ++n) { - PendingPreload& preload = m_pendingPreloads[n]; + for (unsigned i = 0; i < count; ++i) { + PendingPreload& preload = m_pendingPreloads[i]; requestPreload(preload.m_type, preload.m_url, preload.m_charset); } m_pendingPreloads.clear(); } - + void DocLoader::requestPreload(CachedResource::Type type, const String& url, const String& charset) { String encoding; if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet) encoding = charset.isEmpty() ? m_doc->frame()->loader()->encoding() : charset; - CachedResource* resource = requestResource(type, url, &encoding, false, true, true); + CachedResource* resource = requestResource(type, url, encoding, true); if (!resource || m_preloads.contains(resource)) return; resource->increasePreloadCount(); @@ -417,5 +464,5 @@ void DocLoader::printPreloadStats() printf("IMAGES: %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images); } #endif -#endif // ANDROID_PRELOAD_CHANGES + } diff --git a/WebCore/loader/DocLoader.h b/WebCore/loader/DocLoader.h index 31fb5f8..407d85a 100644 --- a/WebCore/loader/DocLoader.h +++ b/WebCore/loader/DocLoader.h @@ -30,9 +30,7 @@ #include "StringHash.h" #include <wtf/HashMap.h> #include <wtf/HashSet.h> -#ifdef ANDROID_PRELOAD_CHANGES #include <wtf/ListHashSet.h> -#endif namespace WebCore { @@ -43,21 +41,21 @@ class CachedScript; class CachedXSLStyleSheet; class Document; class Frame; -class HTMLImageLoader; +class ImageLoader; class KURL; // The DocLoader manages the loading of scripts/images/stylesheets for a single document. class DocLoader { friend class Cache; -friend class HTMLImageLoader; +friend class ImageLoader; public: - DocLoader(Frame*, Document*); + DocLoader(Document*); ~DocLoader(); CachedImage* requestImage(const String& url); - CachedCSSStyleSheet* requestCSSStyleSheet(const String& url, const String& charset, bool isUserStyleSheet = false); + CachedCSSStyleSheet* requestCSSStyleSheet(const String& url, const String& charset); CachedCSSStyleSheet* requestUserCSSStyleSheet(const String& url, const String& charset); CachedScript* requestScript(const String& url, const String& charset); CachedFont* requestFont(const String& url); @@ -69,6 +67,9 @@ public: CachedXBLDocument* requestXBLDocument(const String &url); #endif + // Logs an access denied message to the console for the specified URL. + void printAccessDeniedMessage(const KURL& url) const; + CachedResource* cachedResource(const String& url) const { return m_docResources.get(url); } const HashMap<String, CachedResource*>& allCachedResources() const { return m_docResources; } @@ -84,7 +85,7 @@ public: CachePolicy cachePolicy() const { return m_cachePolicy; } void setCachePolicy(CachePolicy); - Frame* frame() const { return m_frame; } + Frame* frame() const; // Can be NULL Document* doc() const { return m_doc; } void removeCachedResource(CachedResource*) const; @@ -101,33 +102,28 @@ public: void incrementRequestCount(); void decrementRequestCount(); int requestCount(); - -#ifdef ANDROID_PRELOAD_CHANGES + void clearPreloads(); - void preload(CachedResource::Type type, const String& url, const String& charset, bool inBody); + void preload(CachedResource::Type, const String& url, const String& charset, bool referencedFromBody); void checkForPendingPreloads(); void printPreloadStats(); -#endif + private: -#ifdef ANDROID_PRELOAD_CHANGES - CachedResource* requestResource(CachedResource::Type, const String& url, const String* charset = 0, bool skipCanLoadCheck = false, bool sendResourceLoadCallbacks = true, bool isPreload = false); - void requestPreload(CachedResource::Type type, const String& url, const String& charset); -#else - CachedResource* requestResource(CachedResource::Type, const String& url, const String* charset = 0, bool skipCanLoadCheck = false, bool sendResourceLoadCallbacks = true); -#endif + CachedResource* requestResource(CachedResource::Type, const String& url, const String& charset, bool isPreload = false); + void requestPreload(CachedResource::Type, const String& url, const String& charset); + void checkForReload(const KURL&); void checkCacheObjectStatus(CachedResource*); + bool canRequest(CachedResource::Type, const KURL&); Cache* m_cache; HashSet<String> m_reloadedURLs; mutable HashMap<String, CachedResource*> m_docResources; CachePolicy m_cachePolicy; - Frame* m_frame; - Document *m_doc; + Document* m_doc; int m_requestCount; -#ifdef ANDROID_PRELOAD_CHANGES ListHashSet<CachedResource*> m_preloads; struct PendingPreload { CachedResource::Type m_type; @@ -135,7 +131,6 @@ private: String m_charset; }; Vector<PendingPreload> m_pendingPreloads; -#endif //29 bits left #ifdef ANDROID_BLOCK_NETWORK_IMAGE diff --git a/WebCore/loader/DocumentLoader.cpp b/WebCore/loader/DocumentLoader.cpp index b90ead6..d9d99ea 100644 --- a/WebCore/loader/DocumentLoader.cpp +++ b/WebCore/loader/DocumentLoader.cpp @@ -29,21 +29,34 @@ #include "config.h" #include "DocumentLoader.h" +#if ENABLE(OFFLINE_WEB_APPLICATIONS) +#include "ApplicationCache.h" +#include "ApplicationCacheGroup.h" +#include "ApplicationCacheResource.h" +#endif +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size +#include "ArchiveFactory.h" +#include "ArchiveResourceCollection.h" +#else +#include "SubstituteResource.h" +#endif #include "CachedPage.h" -#ifdef ANDROID_PRELOAD_CHANGES #include "DocLoader.h" -#endif #include "Document.h" #include "Event.h" #include "Frame.h" #include "FrameLoader.h" +#include "FrameTree.h" #include "HistoryItem.h" #include "Logging.h" #include "MainResourceLoader.h" +#include "Page.h" #include "PlatformString.h" +#include "Settings.h" #include "SharedBuffer.h" #include "StringBuffer.h" #include "XMLTokenizer.h" + #include <wtf/Assertions.h> #include <wtf/unicode/Unicode.h> @@ -140,6 +153,10 @@ DocumentLoader::DocumentLoader(const ResourceRequest& req, const SubstituteData& , m_isClientRedirect(false) , m_loadingFromCachedPage(false) , m_stopRecordingResponses(false) + , m_substituteResourceDeliveryTimer(this, &DocumentLoader::substituteResourceDeliveryTimerFired) +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + , m_candidateApplicationCacheGroup(0) +#endif { } @@ -153,6 +170,13 @@ FrameLoader* DocumentLoader::frameLoader() const DocumentLoader::~DocumentLoader() { ASSERT(!m_frame || frameLoader()->activeDocumentLoader() != this || !frameLoader()->isLoading()); + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (m_applicationCache) + m_applicationCache->group()->documentLoaderDestroyed(this); + else if (m_candidateApplicationCacheGroup) + m_candidateApplicationCacheGroup->documentLoaderDestroyed(this); +#endif } PassRefPtr<SharedBuffer> DocumentLoader::mainResourceData() const @@ -184,16 +208,6 @@ ResourceRequest& DocumentLoader::request() return m_request; } -const ResourceRequest& DocumentLoader::initialRequest() const -{ - return m_originalRequest; -} - -ResourceRequest& DocumentLoader::actualRequest() -{ - return m_request; -} - const KURL& DocumentLoader::url() const { return request().url(); @@ -243,6 +257,15 @@ void DocumentLoader::clearErrors() void DocumentLoader::mainReceivedError(const ResourceError& error, bool isComplete) { +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + ApplicationCacheGroup* group = m_candidateApplicationCacheGroup; + if (!group && m_applicationCache && !mainResourceApplicationCache()) + group = m_applicationCache->group(); + + if (group) + group->failedLoadingMainResource(this); +#endif + if (!frameLoader()) return; setMainDocumentError(error); @@ -325,26 +348,6 @@ void DocumentLoader::finishedLoading() } } -void DocumentLoader::setCommitted(bool f) -{ - m_committed = f; -} - -bool DocumentLoader::isCommitted() const -{ - return m_committed; -} - -void DocumentLoader::setLoading(bool f) -{ - m_loading = f; -} - -bool DocumentLoader::isLoading() const -{ - return m_loading; -} - void DocumentLoader::commitLoad(const char* data, int length) { // Both unloading the old page and parsing the new page may execute JavaScript which destroys the datasource @@ -395,8 +398,9 @@ void DocumentLoader::setupForReplaceByMIMEType(const String& newMIMEType) stopLoadingSubresources(); stopLoadingPlugIns(); - - frameLoader()->finalSetupForReplace(this); +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size + clearArchiveResources(); +#endif } void DocumentLoader::updateLoading() @@ -437,22 +441,15 @@ void DocumentLoader::prepareForLoadStart() frameLoader()->prepareForLoadStart(); } -void DocumentLoader::setIsClientRedirect(bool flag) -{ - m_isClientRedirect = flag; -} - -bool DocumentLoader::isClientRedirect() const -{ - return m_isClientRedirect; -} - void DocumentLoader::setPrimaryLoadComplete(bool flag) { m_primaryLoadComplete = flag; if (flag) { if (m_mainResourceLoader) { m_mainResourceData = m_mainResourceLoader->resourceData(); +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + m_mainResourceApplicationCache = m_mainResourceLoader->applicationCache(); +#endif m_mainResourceLoader = 0; } updateLoading(); @@ -469,10 +466,8 @@ bool DocumentLoader::isLoadingInAPISense() const if (!m_subresourceLoaders.isEmpty()) return true; if (Document* doc = m_frame->document()) { -#ifdef ANDROID_PRELOAD_CHANGES if (doc->docLoader()->requestCount()) return true; -#endif if (Tokenizer* tok = doc->tokenizer()) if (tok->processingData()) return true; @@ -481,55 +476,200 @@ bool DocumentLoader::isLoadingInAPISense() const return frameLoader()->subframeIsLoading(); } -void DocumentLoader::addResponse(const ResourceResponse& r) +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size +void DocumentLoader::addAllArchiveResources(Archive* archive) { - if (!m_stopRecordingResponses) - m_responses.append(r); + if (!m_archiveResourceCollection) + m_archiveResourceCollection.set(new ArchiveResourceCollection); + + ASSERT(archive); + if (!archive) + return; + + m_archiveResourceCollection->addAllResources(archive); } -void DocumentLoader::stopRecordingResponses() +// FIXME: Adding a resource directly to a DocumentLoader/ArchiveResourceCollection seems like bad design, but is API some apps rely on. +// Can we change the design in a manner that will let us deprecate that API without reducing functionality of those apps? +void DocumentLoader::addArchiveResource(PassRefPtr<ArchiveResource> resource) { - m_stopRecordingResponses = true; + if (!m_archiveResourceCollection) + m_archiveResourceCollection.set(new ArchiveResourceCollection); + + ASSERT(resource); + if (!resource) + return; + + m_archiveResourceCollection->addResource(resource); +} + +ArchiveResource* DocumentLoader::archiveResourceForURL(const KURL& url) const +{ + if (!m_archiveResourceCollection) + return 0; + + ArchiveResource* resource = m_archiveResourceCollection->archiveResourceForURL(url); + + return resource && !resource->shouldIgnoreWhenUnarchiving() ? resource : 0; +} + +PassRefPtr<Archive> DocumentLoader::popArchiveForSubframe(const String& frameName) +{ + return m_archiveResourceCollection ? m_archiveResourceCollection->popSubframeArchive(frameName) : 0; +} + +void DocumentLoader::clearArchiveResources() +{ + m_archiveResourceCollection.clear(); + m_substituteResourceDeliveryTimer.stop(); +} + +void DocumentLoader::setParsedArchiveData(PassRefPtr<SharedBuffer> data) +{ + m_parsedArchiveData = data; } -String DocumentLoader::title() const +SharedBuffer* DocumentLoader::parsedArchiveData() const { - return m_pageTitle; + return m_parsedArchiveData.get(); } -void DocumentLoader::setLastCheckedRequest(const ResourceRequest& req) +PassRefPtr<ArchiveResource> DocumentLoader::mainResource() const +{ + const ResourceResponse& r = response(); + RefPtr<SharedBuffer> mainResourceBuffer = mainResourceData(); + if (!mainResourceBuffer) + mainResourceBuffer = SharedBuffer::create(); + + return ArchiveResource::create(mainResourceBuffer, r.url(), r.mimeType(), r.textEncodingName(), frame()->tree()->name()); +} + +PassRefPtr<ArchiveResource> DocumentLoader::subresource(const KURL& url) const +{ + if (!isCommitted()) + return 0; + + Document* doc = m_frame->document(); + if (!doc) + return archiveResourceForURL(url); + + CachedResource* resource = doc->docLoader()->cachedResource(url); + if (!resource || resource->preloadResult() == CachedResource::PreloadReferenced) + return archiveResourceForURL(url); + + return ArchiveResource::create(resource->data(), url, resource->response()); +} + +void DocumentLoader::getSubresources(Vector<PassRefPtr<ArchiveResource> >& subresources) const +{ + if (!isCommitted()) + return; + + Document* document = m_frame->document(); + if (!document) + return; + + const HashMap<String, CachedResource*>& allResources = document->docLoader()->allCachedResources(); + HashMap<String, CachedResource*>::const_iterator end = allResources.end(); + for (HashMap<String, CachedResource*>::const_iterator it = allResources.begin(); it != end; ++it) { + RefPtr<ArchiveResource> subresource = this->subresource(KURL(it->second->url())); + if (subresource) + subresources.append(subresource.release()); + } + + return; +} +#endif + +void DocumentLoader::deliverSubstituteResourcesAfterDelay() { - m_lastCheckedRequest = req; + if (m_pendingSubstituteResources.isEmpty()) + return; + ASSERT(m_frame && m_frame->page()); + if (m_frame->page()->defersLoading()) + return; + if (!m_substituteResourceDeliveryTimer.isActive()) + m_substituteResourceDeliveryTimer.startOneShot(0); } -const ResourceRequest& DocumentLoader::lastCheckedRequest() const +void DocumentLoader::substituteResourceDeliveryTimerFired(Timer<DocumentLoader>*) { - return m_lastCheckedRequest; + if (m_pendingSubstituteResources.isEmpty()) + return; + ASSERT(m_frame && m_frame->page()); + if (m_frame->page()->defersLoading()) + return; + + SubstituteResourceMap copy; + copy.swap(m_pendingSubstituteResources); + + SubstituteResourceMap::const_iterator end = copy.end(); + for (SubstituteResourceMap::const_iterator it = copy.begin(); it != end; ++it) { + RefPtr<ResourceLoader> loader = it->first; + SubstituteResource* resource = it->second.get(); + + if (resource) { + SharedBuffer* data = resource->data(); + + loader->didReceiveResponse(resource->response()); + loader->didReceiveData(data->data(), data->size(), data->size(), true); + loader->didFinishLoading(); + } else { + // A null resource means that we should fail the load. + // FIXME: Maybe we should use another error here - something like "not in cache". + loader->didFail(loader->cannotShowURLError()); + } + } } -const NavigationAction& DocumentLoader::triggeringAction() const +#ifndef NDEBUG +bool DocumentLoader::isSubstituteLoadPending(ResourceLoader* loader) const { - return m_triggeringAction; + return m_pendingSubstituteResources.contains(loader); } +#endif -void DocumentLoader::setTriggeringAction(const NavigationAction& action) +void DocumentLoader::cancelPendingSubstituteLoad(ResourceLoader* loader) { - m_triggeringAction = action; + if (m_pendingSubstituteResources.isEmpty()) + return; + m_pendingSubstituteResources.remove(loader); + if (m_pendingSubstituteResources.isEmpty()) + m_substituteResourceDeliveryTimer.stop(); } -const ResponseVector& DocumentLoader::responses() const +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size +bool DocumentLoader::scheduleArchiveLoad(ResourceLoader* loader, const ResourceRequest& request, const KURL& originalURL) { - return m_responses; + ArchiveResource* resource = 0; + + if (request.url() == originalURL) + resource = archiveResourceForURL(originalURL); + + if (!resource) { + // WebArchiveDebugMode means we fail loads instead of trying to fetch them from the network if they're not in the archive. + bool shouldFailLoad = m_frame->settings()->webArchiveDebugModeEnabled() && ArchiveFactory::isArchiveMimeType(responseMIMEType()); + + if (!shouldFailLoad) + return false; + } + + m_pendingSubstituteResources.set(loader, resource); + deliverSubstituteResourcesAfterDelay(); + + return true; } +#endif -void DocumentLoader::setOverrideEncoding(const String& enc) +void DocumentLoader::addResponse(const ResourceResponse& r) { - m_overrideEncoding = enc; + if (!m_stopRecordingResponses) + m_responses.append(r); } -String DocumentLoader::overrideEncoding() const +void DocumentLoader::stopRecordingResponses() { - return m_overrideEncoding; + m_stopRecordingResponses = true; } void DocumentLoader::setTitle(const String& title) @@ -566,52 +706,22 @@ void DocumentLoader::loadFromCachedPage(PassRefPtr<CachedPage> cachedPage) frameLoader()->commitProvisionalLoad(cachedPage); } -const ResourceResponse& DocumentLoader::response() const -{ - return m_response; -} - -void DocumentLoader::setLoadingFromCachedPage(bool loading) -{ - m_loadingFromCachedPage = loading; -} - -bool DocumentLoader::isLoadingFromCachedPage() const -{ - return m_loadingFromCachedPage; -} - -void DocumentLoader::setResponse(const ResourceResponse& response) -{ - m_response = response; -} - -bool DocumentLoader::isStopping() const -{ - return m_isStopping; -} - -const ResourceError& DocumentLoader::mainDocumentError() const -{ - return m_mainDocumentError; -} - -KURL DocumentLoader::originalURL() const +const KURL& DocumentLoader::originalURL() const { return m_originalRequestCopy.url(); } -KURL DocumentLoader::requestURL() const +const KURL& DocumentLoader::requestURL() const { return request().url(); } -KURL DocumentLoader::responseURL() const +const KURL& DocumentLoader::responseURL() const { return m_response.url(); } -String DocumentLoader::responseMIMEType() const +const String& DocumentLoader::responseMIMEType() const { return m_response.mimeType(); } @@ -627,6 +737,8 @@ void DocumentLoader::setDefersLoading(bool defers) m_mainResourceLoader->setDefersLoading(defers); setAllDefersLoading(m_subresourceLoaders, defers); setAllDefersLoading(m_plugInStreamLoaders, defers); + if (!defers) + deliverSubstituteResourcesAfterDelay(); } void DocumentLoader::stopLoadingPlugIns() @@ -726,4 +838,87 @@ void DocumentLoader::iconLoadDecisionAvailable() m_frame->loader()->iconLoadDecisionAvailable(); } +#if ENABLE(OFFLINE_WEB_APPLICATIONS) +void DocumentLoader::setCandidateApplicationCacheGroup(ApplicationCacheGroup* group) +{ + ASSERT(!m_applicationCache); + m_candidateApplicationCacheGroup = group; +} + +void DocumentLoader::setApplicationCache(PassRefPtr<ApplicationCache> applicationCache) +{ + if (m_candidateApplicationCacheGroup) { + ASSERT(!m_applicationCache); + m_candidateApplicationCacheGroup = 0; + } + + m_applicationCache = applicationCache; +} + +ApplicationCache* DocumentLoader::topLevelApplicationCache() const +{ + if (!m_frame) + return 0; + + if (m_applicationCache) + return m_applicationCache.get(); + + if (Page* page = m_frame->page()) + return page->mainFrame()->loader()->documentLoader()->applicationCache(); + + return 0; +} + +ApplicationCache* DocumentLoader::mainResourceApplicationCache() const +{ + if (m_mainResourceApplicationCache) + return m_mainResourceApplicationCache.get(); + if (m_mainResourceLoader) + return m_mainResourceLoader->applicationCache(); + return 0; +} + +bool DocumentLoader::shouldLoadResourceFromApplicationCache(const ResourceRequest& request, ApplicationCacheResource*& resource) +{ + ApplicationCache* cache = topLevelApplicationCache(); + if (!cache) + return false; + + // If the resource is not a HTTP/HTTPS GET, then abort + if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) + return false; + + if (cache->isURLInOnlineWhitelist(request.url())) + return false; + + resource = cache->resourceForURL(request.url()); + + // Don't load foreign resources. + if (resource && (resource->type() & ApplicationCacheResource::Foreign)) + resource = 0; + + return true; +} + +bool DocumentLoader::scheduleApplicationCacheLoad(ResourceLoader* loader, const ResourceRequest& request, const KURL& originalURL) +{ + if (!frameLoader()->frame()->settings() || !frameLoader()->frame()->settings()->offlineWebApplicationCacheEnabled()) + return false; + + if (request.url() != originalURL) + return false; + + ApplicationCacheResource* resource; + if (!shouldLoadResourceFromApplicationCache(request, resource)) + // FIXME: Handle opportunistic caching namespaces + return false; + + m_pendingSubstituteResources.set(loader, resource); + deliverSubstituteResourcesAfterDelay(); + + return true; +} + +#endif // ENABLE(OFFLINE_WEB_APPLICATIONS) + } diff --git a/WebCore/loader/DocumentLoader.h b/WebCore/loader/DocumentLoader.h index 15303fe..aef4f49 100644 --- a/WebCore/loader/DocumentLoader.h +++ b/WebCore/loader/DocumentLoader.h @@ -43,6 +43,16 @@ namespace WebCore { +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + class ApplicationCache; + class ApplicationCacheGroup; + class ApplicationCacheResource; +#endif +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size + class Archive; + class ArchiveResource; + class ArchiveResourceCollection; +#endif class CachedPage; class Frame; class FrameLoader; @@ -50,15 +60,20 @@ namespace WebCore { class KURL; class MainResourceLoader; class ResourceLoader; + class SchedulePair; class SharedBuffer; class SubstituteData; + class SubstituteResource; typedef HashSet<RefPtr<ResourceLoader> > ResourceLoaderSet; typedef Vector<ResourceResponse> ResponseVector; class DocumentLoader : public RefCounted<DocumentLoader> { public: - DocumentLoader(const ResourceRequest&, const SubstituteData&); + static PassRefPtr<DocumentLoader> create(const ResourceRequest& request, const SubstituteData& data) + { + return adoptRef(new DocumentLoader(request, data)); + } virtual ~DocumentLoader(); void setFrame(Frame*); @@ -77,59 +92,86 @@ namespace WebCore { const ResourceRequest& request() const; ResourceRequest& request(); void setRequest(const ResourceRequest&); - const ResourceRequest& actualRequest() const; - ResourceRequest& actualRequest(); - const ResourceRequest& initialRequest() const; const SubstituteData& substituteData() const { return m_substituteData; } const KURL& url() const; const KURL& unreachableURL() const; - KURL originalURL() const; - KURL requestURL() const; - KURL responseURL() const; - String responseMIMEType() const; + const KURL& originalURL() const; + const KURL& requestURL() const; + const KURL& responseURL() const; + const String& responseMIMEType() const; void replaceRequestURLForAnchorScroll(const KURL&); - bool isStopping() const; + bool isStopping() const { return m_isStopping; } void stopLoading(); - void setCommitted(bool); - bool isCommitted() const; - bool isLoading() const; - void setLoading(bool); + void setCommitted(bool committed) { m_committed = committed; } + bool isCommitted() const { return m_committed; } + bool isLoading() const { return m_loading; } + void setLoading(bool loading) { m_loading = loading; } void updateLoading(); void receivedData(const char*, int); void setupForReplaceByMIMEType(const String& newMIMEType); void finishedLoading(); - const ResourceResponse& response() const; - const ResourceError& mainDocumentError() const; + const ResourceResponse& response() const { return m_response; } + const ResourceError& mainDocumentError() const { return m_mainDocumentError; } void mainReceivedError(const ResourceError&, bool isComplete); - void setResponse(const ResourceResponse& response); + void setResponse(const ResourceResponse& response) { m_response = response; } void prepareForLoadStart(); - bool isClientRedirect() const; - void setIsClientRedirect(bool); + bool isClientRedirect() const { return m_isClientRedirect; } + void setIsClientRedirect(bool isClientRedirect) { m_isClientRedirect = isClientRedirect; } bool isLoadingInAPISense() const; void setPrimaryLoadComplete(bool); void setTitle(const String&); - String overrideEncoding() const; + const String& overrideEncoding() const { return m_overrideEncoding; } + +#if PLATFORM(MAC) + void schedule(SchedulePair*); + void unschedule(SchedulePair*); +#endif +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size + void addAllArchiveResources(Archive*); + void addArchiveResource(PassRefPtr<ArchiveResource>); + + // Return an ArchiveResource for the URL, either creating from live data or + // pulling from the ArchiveResourceCollection + PassRefPtr<ArchiveResource> subresource(const KURL&) const; + // Return the ArchiveResource for the URL only when loading an Archive + ArchiveResource* archiveResourceForURL(const KURL&) const; + + PassRefPtr<Archive> popArchiveForSubframe(const String& frameName); + void clearArchiveResources(); + void setParsedArchiveData(PassRefPtr<SharedBuffer>); + SharedBuffer* parsedArchiveData() const; + + PassRefPtr<ArchiveResource> mainResource() const; + void getSubresources(Vector<PassRefPtr<ArchiveResource> >&) const; + + bool scheduleArchiveLoad(ResourceLoader*, const ResourceRequest&, const KURL&); +#endif +#ifndef NDEBUG + bool isSubstituteLoadPending(ResourceLoader*) const; +#endif + void cancelPendingSubstituteLoad(ResourceLoader*); + void addResponse(const ResourceResponse&); - const ResponseVector& responses() const; + const ResponseVector& responses() const { return m_responses; } - const NavigationAction& triggeringAction() const; - void setTriggeringAction(const NavigationAction&); - void setOverrideEncoding(const String&); - void setLastCheckedRequest(const ResourceRequest& request); - const ResourceRequest& lastCheckedRequest() const; + const NavigationAction& triggeringAction() const { return m_triggeringAction; } + void setTriggeringAction(const NavigationAction& action) { m_triggeringAction = action; } + void setOverrideEncoding(const String& encoding) { m_overrideEncoding = encoding; } + void setLastCheckedRequest(const ResourceRequest& request) { m_lastCheckedRequest = request; } + const ResourceRequest& lastCheckedRequest() { return m_lastCheckedRequest; } void stopRecordingResponses(); - String title() const; + const String& title() const { return m_pageTitle; } KURL urlForHistory() const; void loadFromCachedPage(PassRefPtr<CachedPage>); - void setLoadingFromCachedPage(bool); - bool isLoadingFromCachedPage() const; + void setLoadingFromCachedPage(bool loading) { m_loadingFromCachedPage = loading; } + bool isLoadingFromCachedPage() const { return m_loadingFromCachedPage; } void setDefersLoading(bool); @@ -153,8 +195,26 @@ namespace WebCore { void subresourceLoaderFinishedLoadingOnePart(ResourceLoader*); + void setDeferMainResourceDataLoad(bool defer) { m_deferMainResourceDataLoad = defer; } bool deferMainResourceDataLoad() const { return m_deferMainResourceDataLoad; } + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + bool scheduleApplicationCacheLoad(ResourceLoader*, const ResourceRequest&, const KURL& originalURL); + bool shouldLoadResourceFromApplicationCache(const ResourceRequest&, ApplicationCacheResource*&); + + void setCandidateApplicationCacheGroup(ApplicationCacheGroup* group); + ApplicationCacheGroup* candidateApplicationCacheGroup() const { return m_candidateApplicationCacheGroup; } + + void setApplicationCache(PassRefPtr<ApplicationCache> applicationCache); + ApplicationCache* applicationCache() const { return m_applicationCache.get(); } + ApplicationCache* topLevelApplicationCache() const; + + ApplicationCache* mainResourceApplicationCache() const; +#endif + protected: + DocumentLoader(const ResourceRequest&, const SubstituteData&); + bool m_deferMainResourceDataLoad; private: @@ -165,6 +225,9 @@ namespace WebCore { void commitLoad(const char*, int); bool doesProgressiveLoad(const String& MIMEType) const; + void deliverSubstituteResourcesAfterDelay(); + void substituteResourceDeliveryTimerFired(Timer<DocumentLoader>*); + Frame* m_frame; RefPtr<MainResourceLoader> m_mainResourceLoader; @@ -191,8 +254,6 @@ namespace WebCore { // headers, cookie information, canonicalization and redirects. ResourceRequest m_request; - mutable ResourceRequest m_externalRequest; - ResourceResponse m_response; ResourceError m_mainDocumentError; @@ -222,6 +283,27 @@ namespace WebCore { // page cache. ResponseVector m_responses; bool m_stopRecordingResponses; + + typedef HashMap<RefPtr<ResourceLoader>, RefPtr<SubstituteResource> > SubstituteResourceMap; + SubstituteResourceMap m_pendingSubstituteResources; + Timer<DocumentLoader> m_substituteResourceDeliveryTimer; + +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size + OwnPtr<ArchiveResourceCollection> m_archiveResourceCollection; + RefPtr<SharedBuffer> m_parsedArchiveData; +#endif + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + // The application cache that the document loader is associated with (if any). + RefPtr<ApplicationCache> m_applicationCache; + + // Before an application cache has finished loading, this will be the candidate application + // group that the document loader is associated with. + ApplicationCacheGroup* m_candidateApplicationCacheGroup; + + // Once the main resource has finished loading, this is the application cache it was loaded from (if any). + RefPtr<ApplicationCache> m_mainResourceApplicationCache; +#endif }; } diff --git a/WebCore/loader/EmptyClients.h b/WebCore/loader/EmptyClients.h new file mode 100644 index 0000000..8cab747 --- /dev/null +++ b/WebCore/loader/EmptyClients.h @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2006 Eric Seidel (eric@webkit.org) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 EmptyClients_h +#define EmptyClients_h + +#include "ChromeClient.h" +#include "ContextMenuClient.h" +#include "DragClient.h" +#include "DocumentLoader.h" +#include "EditCommand.h" +#include "EditorClient.h" +#include "FocusDirection.h" +#include "FloatRect.h" +#include "FrameLoaderClient.h" +#include "InspectorClient.h" +#include "ResourceError.h" +#include "SharedBuffer.h" + +/* + This file holds empty Client stubs for use by WebCore. + Viewless element needs to create a dummy Page->Frame->FrameView tree for use in parsing or executing JavaScript. + This tree depends heavily on Clients (usually provided by WebKit classes). + + This file was first created for SVGImage as it had no way to access the current Page (nor should it, + since Images are not tied to a page). + See http://bugs.webkit.org/show_bug.cgi?id=5971 for the original discussion about this file. + + Ideally, whenever you change a Client class, you should add a stub here. + Brittle, yes. Unfortunate, yes. Hopefully temporary. +*/ + +namespace WebCore { + +class EmptyChromeClient : public ChromeClient { +public: + virtual ~EmptyChromeClient() { } + virtual void chromeDestroyed() { } + + virtual void setWindowRect(const FloatRect&) { } + virtual FloatRect windowRect() { return FloatRect(); } + + virtual FloatRect pageRect() { return FloatRect(); } + + virtual float scaleFactor() { return 1.f; } + + virtual void focus() { } + virtual void unfocus() { } + + virtual bool canTakeFocus(FocusDirection) { return false; } + virtual void takeFocus(FocusDirection) { } + + virtual Page* createWindow(Frame*, const FrameLoadRequest&, const WindowFeatures&) { return 0; } + virtual void show() { } + + virtual bool canRunModal() { return false; } + virtual void runModal() { } + + virtual void setToolbarsVisible(bool) { } + virtual bool toolbarsVisible() { return false; } + + virtual void setStatusbarVisible(bool) { } + virtual bool statusbarVisible() { return false; } + + virtual void setScrollbarsVisible(bool) { } + virtual bool scrollbarsVisible() { return false; } + + virtual void setMenubarVisible(bool) { } + virtual bool menubarVisible() { return false; } + + virtual void setResizable(bool) { } + + virtual void addMessageToConsole(const String& message, unsigned int lineNumber, const String& sourceID) { } + + virtual bool canRunBeforeUnloadConfirmPanel() { return false; } + virtual bool runBeforeUnloadConfirmPanel(const String& message, Frame* frame) { return true; } + + virtual void closeWindowSoon() { } + + virtual void runJavaScriptAlert(Frame*, const String&) { } + virtual bool runJavaScriptConfirm(Frame*, const String&) { return false; } + virtual bool runJavaScriptPrompt(Frame*, const String& message, const String& defaultValue, String& result) { return false; } + virtual bool shouldInterruptJavaScript() { return false; } + + virtual void setStatusbarText(const String&) { } + + virtual bool tabsToLinks() const { return false; } + + virtual IntRect windowResizerRect() const { return IntRect(); } + virtual void addToDirtyRegion(const IntRect&) { } + virtual void scrollBackingStore(int dx, int dy, const IntRect& scrollViewRect, const IntRect& clipRect) { } + virtual void updateBackingStore() { } + + virtual void repaint(const IntRect&, bool contentChanged, bool immediate = false, bool repaintContentOnly = false) { } + virtual void scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect) { } + virtual IntPoint screenToWindow(const IntPoint& p) const { return p; } + virtual IntRect windowToScreen(const IntRect& r) const { return r; } + virtual PlatformWidget platformWindow() const { return 0; } + + virtual void mouseDidMoveOverElement(const HitTestResult&, unsigned modifierFlags) { } + + virtual void setToolTip(const String&) { } + + virtual void print(Frame*) { } + + virtual void exceededDatabaseQuota(Frame*, const String&) { } + + virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>) { } +}; + +class EmptyFrameLoaderClient : public FrameLoaderClient { +public: + virtual ~EmptyFrameLoaderClient() { } + virtual void frameLoaderDestroyed() { } + + virtual bool hasWebView() const { return true; } // mainly for assertions + + virtual void makeRepresentation(DocumentLoader*) { } + virtual void forceLayout() { } + virtual void forceLayoutForNonHTML() { } + + virtual void updateHistoryForCommit() { } + + virtual void updateHistoryForBackForwardNavigation() { } + virtual void updateHistoryForReload() { } + virtual void updateHistoryForStandardLoad() { } + virtual void updateHistoryForInternalLoad() { } + + virtual void updateHistoryAfterClientRedirect() { } + + virtual void setCopiesOnScroll() { } + + virtual void detachedFromParent2() { } + virtual void detachedFromParent3() { } + + virtual void download(ResourceHandle*, const ResourceRequest&, const ResourceRequest&, const ResourceResponse&) { } + + virtual void assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&) { } + virtual void dispatchWillSendRequest(DocumentLoader*, unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse) { } + virtual void dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) { } + virtual void dispatchDidCancelAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) { } + virtual void dispatchDidReceiveResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&) { } + virtual void dispatchDidReceiveContentLength(DocumentLoader*, unsigned long identifier, int lengthReceived) { } + virtual void dispatchDidFinishLoading(DocumentLoader*, unsigned long identifier) { } + virtual void dispatchDidFailLoading(DocumentLoader*, unsigned long identifier, const ResourceError&) { } + virtual bool dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int length) { return false; } + + virtual void dispatchDidHandleOnloadEvents() { } + virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() { } + virtual void dispatchDidCancelClientRedirect() { } + virtual void dispatchWillPerformClientRedirect(const KURL&, double interval, double fireDate) { } + virtual void dispatchDidChangeLocationWithinPage() { } + virtual void dispatchWillClose() { } + virtual void dispatchDidReceiveIcon() { } + virtual void dispatchDidStartProvisionalLoad() { } + virtual void dispatchDidReceiveTitle(const String& title) { } + virtual void dispatchDidCommitLoad() { } + virtual void dispatchDidFailProvisionalLoad(const ResourceError&) { } + virtual void dispatchDidFailLoad(const ResourceError&) { } + virtual void dispatchDidFinishDocumentLoad() { } + virtual void dispatchDidFinishLoad() { } + virtual void dispatchDidFirstLayout() { } + + virtual Frame* dispatchCreatePage() { return 0; } + virtual void dispatchShow() { } + + virtual void dispatchDecidePolicyForMIMEType(FramePolicyFunction, const String& MIMEType, const ResourceRequest&) { } + virtual void dispatchDecidePolicyForNewWindowAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, const String& frameName) { } + virtual void dispatchDecidePolicyForNavigationAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>) { } + virtual void cancelPolicyCheck() { } + + virtual void dispatchUnableToImplementPolicy(const ResourceError&) { } + + virtual void dispatchWillSubmitForm(FramePolicyFunction, PassRefPtr<FormState>) { } + + virtual void dispatchDidLoadMainResource(DocumentLoader*) { } + virtual void revertToProvisionalState(DocumentLoader*) { } + virtual void setMainDocumentError(DocumentLoader*, const ResourceError&) { } + + virtual void willChangeEstimatedProgress() { } + virtual void didChangeEstimatedProgress() { } + virtual void postProgressStartedNotification() { } + virtual void postProgressEstimateChangedNotification() { } + virtual void postProgressFinishedNotification() { } + + virtual void setMainFrameDocumentReady(bool) { } + + virtual void startDownload(const ResourceRequest&) { } + + virtual void willChangeTitle(DocumentLoader*) { } + virtual void didChangeTitle(DocumentLoader*) { } + + virtual void committedLoad(DocumentLoader*, const char*, int) { } + virtual void finishedLoading(DocumentLoader*) { } + + virtual ResourceError cancelledError(const ResourceRequest&) { return ResourceError(); } + virtual ResourceError blockedError(const ResourceRequest&) { return ResourceError(); } + virtual ResourceError cannotShowURLError(const ResourceRequest&) { return ResourceError(); } + virtual ResourceError interruptForPolicyChangeError(const ResourceRequest&) { return ResourceError(); } + + virtual ResourceError cannotShowMIMETypeError(const ResourceResponse&) { return ResourceError(); } + virtual ResourceError fileDoesNotExistError(const ResourceResponse&) { return ResourceError(); } + virtual ResourceError pluginWillHandleLoadError(const ResourceResponse&) { return ResourceError(); } + + virtual bool shouldFallBack(const ResourceError&) { return false; } + + virtual bool canHandleRequest(const ResourceRequest&) const { return false; } + virtual bool canShowMIMEType(const String& MIMEType) const { return false; } + virtual bool representationExistsForURLScheme(const String& URLScheme) const { return false; } + virtual String generatedMIMETypeForURLScheme(const String& URLScheme) const { return ""; } + + virtual void frameLoadCompleted() { } + virtual void restoreViewState() { } + virtual void provisionalLoadStarted() { } + virtual bool shouldTreatURLAsSameAsCurrent(const KURL&) const { return false; } + virtual void addHistoryItemForFragmentScroll() { } + virtual void didFinishLoad() { } + virtual void prepareForDataSourceReplacement() { } + + virtual PassRefPtr<DocumentLoader> createDocumentLoader(const ResourceRequest& request, const SubstituteData& substituteData) { return DocumentLoader::create(request, substituteData); } + virtual void setTitle(const String& title, const KURL&) { } + + virtual String userAgent(const KURL&) { return ""; } + + virtual void savePlatformDataToCachedPage(CachedPage*) { } + virtual void transitionToCommittedFromCachedPage(CachedPage*) { } + virtual void transitionToCommittedForNewPage() { } + + virtual void updateGlobalHistory(const KURL&) { } + virtual bool shouldGoToHistoryItem(HistoryItem*) const { return false; } + virtual void saveViewStateToItem(HistoryItem*) { } + virtual bool canCachePage() const { return false; } + + virtual PassRefPtr<Frame> createFrame(const KURL& url, const String& name, HTMLFrameOwnerElement* ownerElement, + const String& referrer, bool allowsScrolling, int marginWidth, int marginHeight) { return 0; } + virtual Widget* createPlugin(const IntSize&,Element*, const KURL&, const Vector<String>&, const Vector<String>&, const String&, bool) { return 0; } + virtual Widget* createJavaAppletWidget(const IntSize&, Element*, const KURL&, const Vector<String>&, const Vector<String>&) { return 0; } + + virtual ObjectContentType objectContentType(const KURL& url, const String& mimeType) { return ObjectContentType(); } + virtual String overrideMediaType() const { return String(); } + + virtual void redirectDataToPlugin(Widget*) {} + virtual void windowObjectCleared() {} + virtual void didPerformFirstNavigation() const {} + + virtual void registerForIconNotification(bool listen) {} + +#if PLATFORM(MAC) + virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long identifier, NSCachedURLResponse* response) const { return response; } +#endif + +}; + +class EmptyEditorClient : public EditorClient { +public: + virtual ~EmptyEditorClient() { } + virtual void pageDestroyed() { } + + virtual bool shouldDeleteRange(Range*) { return false; } + virtual bool shouldShowDeleteInterface(HTMLElement*) { return false; } + virtual bool smartInsertDeleteEnabled() { return false; } + virtual bool isContinuousSpellCheckingEnabled() { return false; } + virtual void toggleContinuousSpellChecking() { } + virtual bool isGrammarCheckingEnabled() { return false; } + virtual void toggleGrammarChecking() { } + virtual int spellCheckerDocumentTag() { return -1; } + + virtual bool selectWordBeforeMenuEvent() { return false; } + virtual bool isEditable() { return false; } + + virtual bool shouldBeginEditing(Range*) { return false; } + virtual bool shouldEndEditing(Range*) { return false; } + virtual bool shouldInsertNode(Node*, Range*, EditorInsertAction) { return false; } + // virtual bool shouldInsertNode(Node*, Range* replacingRange, WebViewInsertAction) { return false; } + virtual bool shouldInsertText(const String&, Range*, EditorInsertAction) { return false; } + virtual bool shouldChangeSelectedRange(Range* fromRange, Range* toRange, EAffinity, bool stillSelecting) { return false; } + + virtual bool shouldApplyStyle(CSSStyleDeclaration*, Range*) { return false; } + virtual bool shouldMoveRangeAfterDelete(Range*, Range*) { return false; } + // virtual bool shouldChangeTypingStyle(CSSStyleDeclaration* fromStyle, CSSStyleDeclaration* toStyle) { return false; } + // virtual bool doCommandBySelector(SEL selector) { return false; } + // + virtual void didBeginEditing() { } + virtual void respondToChangedContents() { } + virtual void respondToChangedSelection() { } + virtual void didEndEditing() { } + virtual void didWriteSelectionToPasteboard() { } + virtual void didSetSelectionTypesForPasteboard() { } + // virtual void webViewDidChangeTypingStyle:(NSNotification *)notification { } + // virtual void webViewDidChangeSelection:(NSNotification *)notification { } + // virtual NSUndoManager* undoManagerForWebView:(WebView *)webView { return 0; } + + virtual void registerCommandForUndo(PassRefPtr<EditCommand>) { } + virtual void registerCommandForRedo(PassRefPtr<EditCommand>) { } + virtual void clearUndoRedoOperations() { } + + virtual bool canUndo() const { return false; } + virtual bool canRedo() const { return false; } + + virtual void undo() { } + virtual void redo() { } + + virtual void handleKeyboardEvent(KeyboardEvent*) { } + virtual void handleInputMethodKeydown(KeyboardEvent*) { } + + virtual void textFieldDidBeginEditing(Element*) { } + virtual void textFieldDidEndEditing(Element*) { } + virtual void textDidChangeInTextField(Element*) { } + virtual bool doTextFieldCommandFromEvent(Element*, KeyboardEvent*) { return false; } + virtual void textWillBeDeletedInTextField(Element*) { } + virtual void textDidChangeInTextArea(Element*) { } + +#if PLATFORM(MAC) + virtual void markedTextAbandoned(Frame*) { } + + virtual NSString* userVisibleString(NSURL*) { return 0; } +#ifdef BUILDING_ON_TIGER + virtual NSArray* pasteboardTypesForSelection(Frame*) { return 0; } +#endif +#endif + virtual void ignoreWordInSpellDocument(const String&) { } + virtual void learnWord(const String&) { } + virtual void checkSpellingOfString(const UChar*, int length, int* misspellingLocation, int* misspellingLength) { } + virtual void checkGrammarOfString(const UChar*, int length, Vector<GrammarDetail>&, int* badGrammarLocation, int* badGrammarLength) { } + virtual void updateSpellingUIWithGrammarString(const String&, const GrammarDetail&) { } + virtual void updateSpellingUIWithMisspelledWord(const String&) { } + virtual void showSpellingUI(bool show) { } + virtual bool spellingUIIsShowing() { return false; } + virtual void getGuessesForWord(const String&, Vector<String>& guesses) { } + virtual void setInputMethodState(bool enabled) { } + + +}; + +class EmptyContextMenuClient : public ContextMenuClient { +public: + virtual ~EmptyContextMenuClient() { } + virtual void contextMenuDestroyed() { } + + virtual PlatformMenuDescription getCustomMenuFromDefaultItems(ContextMenu*) { return 0; } + virtual void contextMenuItemSelected(ContextMenuItem*, const ContextMenu*) { } + + virtual void downloadURL(const KURL& url) { } + virtual void copyImageToClipboard(const HitTestResult&) { } + virtual void searchWithGoogle(const Frame*) { } + virtual void lookUpInDictionary(Frame*) { } + virtual void speak(const String&) { } + virtual void stopSpeaking() { } + +#if PLATFORM(MAC) + virtual void searchWithSpotlight() { } +#endif +}; + +class EmptyDragClient : public DragClient { +public: + virtual ~EmptyDragClient() {} + virtual void willPerformDragDestinationAction(DragDestinationAction, DragData*) { } + virtual void willPerformDragSourceAction(DragSourceAction, const IntPoint&, Clipboard*) { } + virtual DragDestinationAction actionMaskForDrag(DragData*) { return DragDestinationActionNone; } + virtual DragSourceAction dragSourceActionMaskForPoint(const IntPoint&) { return DragSourceActionNone; } + virtual void startDrag(DragImageRef, const IntPoint&, const IntPoint&, Clipboard*, Frame*, bool) { } + virtual DragImageRef createDragImageForLink(KURL&, const String& label, Frame*) { return 0; } + virtual void dragControllerDestroyed() { } +}; + +class EmptyInspectorClient : public InspectorClient { +public: + virtual ~EmptyInspectorClient() { } + + virtual void inspectorDestroyed() { } + + virtual Page* createPage() { return 0; }; + + virtual String localizedStringsURL() { return String(); } + + virtual void showWindow() { } + virtual void closeWindow() { } + + virtual void attachWindow() { } + virtual void detachWindow() { } + + virtual void setAttachedWindowHeight(unsigned) { } + + virtual void highlight(Node*) { } + virtual void hideHighlight() { } + virtual void inspectedURLChanged(const String& newURL) { } + + virtual void populateSetting(const String& key, InspectorController::Setting&) { } + virtual void storeSetting(const String& key, const InspectorController::Setting&) { } + virtual void removeSetting(const String& key) { } +}; + +} + +#endif // EmptyClients_h diff --git a/WebCore/loader/FTPDirectoryDocument.cpp b/WebCore/loader/FTPDirectoryDocument.cpp index beab88c..0a7e91e 100644 --- a/WebCore/loader/FTPDirectoryDocument.cpp +++ b/WebCore/loader/FTPDirectoryDocument.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,12 +28,9 @@ #include "CharacterNames.h" #include "CString.h" -#include "Element.h" #include "HTMLNames.h" #include "HTMLTableElement.h" -#include "HTMLTableSectionElement.h" #include "HTMLTokenizer.h" -#include "KURL.h" #include "LocalizedStrings.h" #include "Logging.h" #include "FTPDirectoryParser.h" @@ -41,15 +38,12 @@ #include "Settings.h" #include "SharedBuffer.h" #include "Text.h" -#include "XMLTokenizer.h" - -// On Win, the threadsafe *_r functions need to be gotten from pthreads. -#if COMPILER(MSVC) && USE(PTHREADS) -#include <pthread.h> -#endif #if PLATFORM(QT) #include <QDateTime> +// On Windows, use the threadsafe *_r functions provided by pthread. +#elif PLATFORM(WIN_OS) && (USE(PTHREADS) || HAVE(PTHREAD_H)) +#include <pthread.h> #endif using namespace std; @@ -60,7 +54,7 @@ using namespace HTMLNames; class FTPDirectoryTokenizer : public HTMLTokenizer { public: - FTPDirectoryTokenizer(HTMLDocument* doc); + FTPDirectoryTokenizer(HTMLDocument*); virtual bool write(const SegmentedString&, bool appendData); virtual void finish(); @@ -149,7 +143,7 @@ PassRefPtr<Element> FTPDirectoryTokenizer::createTDForFilename(const String& fil { ExceptionCode ec; - String fullURL = m_doc->baseURL(); + String fullURL = m_doc->baseURL().string(); if (fullURL[fullURL.length() - 1] == '/') fullURL.append(filename); else @@ -239,6 +233,8 @@ static struct tm *localTimeQt(const time_t *const timep, struct tm *result) } #define localtime_r(x, y) localTimeQt(x, y) +#elif PLATFORM(WIN_OS) && !defined(localtime_r) +#define localtime_r(x, y) localtime_s((y), (x)) #endif static String processFileDateString(const FTPTime& fileTime) @@ -307,10 +303,7 @@ void FTPDirectoryTokenizer::parseAndAppendOneLine(const String& inputLine) { ListResult result; - DeprecatedString depString = inputLine.deprecatedString(); - const char* line = depString.ascii(); - - FTPEntryType typeResult = parseOneFTPLine(line, m_listState, result); + FTPEntryType typeResult = parseOneFTPLine(inputLine.latin1().data(), m_listState, result); // FTPMiscEntry is a comment or usage statistic which we don't care about, and junk is invalid data - bail in these 2 cases if (typeResult == FTPMiscEntry || typeResult == FTPJunkEntry) @@ -482,8 +475,8 @@ void FTPDirectoryTokenizer::finish() HTMLTokenizer::finish(); } -FTPDirectoryDocument::FTPDirectoryDocument(DOMImplementation* implementation, Frame* frame) - : HTMLDocument(implementation, frame) +FTPDirectoryDocument::FTPDirectoryDocument(Frame* frame) + : HTMLDocument(frame) { #ifndef NDEBUG LogFTP.state = WTFLogChannelOn; diff --git a/WebCore/loader/FTPDirectoryDocument.h b/WebCore/loader/FTPDirectoryDocument.h index bea228d..ecc6f73 100644 --- a/WebCore/loader/FTPDirectoryDocument.h +++ b/WebCore/loader/FTPDirectoryDocument.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,9 +33,13 @@ class DOMImplementation; class FTPDirectoryDocument : public HTMLDocument { public: - FTPDirectoryDocument(DOMImplementation*, Frame*); - - virtual bool isImageDocument() const { return false; } + static PassRefPtr<FTPDirectoryDocument> create(Frame* frame) + { + return new FTPDirectoryDocument(frame); + } + +private: + FTPDirectoryDocument(Frame*); virtual Tokenizer* createTokenizer(); }; diff --git a/WebCore/loader/FTPDirectoryParser.cpp b/WebCore/loader/FTPDirectoryParser.cpp index fdda0b6..8c76e97 100644 --- a/WebCore/loader/FTPDirectoryParser.cpp +++ b/WebCore/loader/FTPDirectoryParser.cpp @@ -24,19 +24,18 @@ #if ENABLE(FTPDIR) #include "FTPDirectoryParser.h" -// On Win, the threadsafe *_r functions need to be gotten from pthreads. -#if COMPILER(MSVC) && USE(PTHREADS) +#if PLATFORM(QT) +#include <QDateTime> +// On Windows, use the threadsafe *_r functions provided by pthread. +#elif PLATFORM(WIN_OS) && (USE(PTHREADS) || HAVE(PTHREAD_H)) #include <pthread.h> #endif #include <wtf/ASCIICType.h> +#include <stdio.h> using namespace WTF; -#if PLATFORM(QT) -#include <QDateTime> -#endif - namespace WebCore { #if PLATFORM(QT) && defined(Q_WS_WIN32) // Defined in FTPDirectoryDocument.cpp. @@ -50,9 +49,10 @@ static struct tm *gmtimeQt(const time_t *const timep, struct tm *result) } #define gmtime_r(x, y) gmtimeQt(x, y) +#elif PLATFORM(WIN_OS) && !defined(gmtime_r) +#define gmtime_r(x, y) gmtime_s((y), (x)) #endif - FTPEntryType parseOneFTPLine(const char* line, ListState& state, ListResult& result) { result.clear(); diff --git a/WebCore/loader/FormState.cpp b/WebCore/loader/FormState.cpp index f5ea1e9..c55b8ac 100644 --- a/WebCore/loader/FormState.cpp +++ b/WebCore/loader/FormState.cpp @@ -36,7 +36,7 @@ namespace WebCore { PassRefPtr<FormState> FormState::create(PassRefPtr<HTMLFormElement> form, const HashMap<String, String>& values, PassRefPtr<Frame> sourceFrame) { - return new FormState(form, values, sourceFrame); + return adoptRef(new FormState(form, values, sourceFrame)); } FormState::FormState(PassRefPtr<HTMLFormElement> form, const HashMap<String, String>& values, PassRefPtr<Frame> sourceFrame) diff --git a/WebCore/loader/FrameLoader.cpp b/WebCore/loader/FrameLoader.cpp index b0b4f90..2fdb8f5 100644 --- a/WebCore/loader/FrameLoader.cpp +++ b/WebCore/loader/FrameLoader.cpp @@ -1,6 +1,6 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. - * Copyright (C) 2007 Trolltech ASA + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,15 +30,19 @@ #include "config.h" #include "FrameLoader.h" +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size +#include "Archive.h" +#include "ArchiveFactory.h" +#endif #include "CString.h" #include "Cache.h" #include "CachedPage.h" #include "Chrome.h" #include "DOMImplementation.h" +#include "DOMWindow.h" #include "DocLoader.h" #include "Document.h" #include "DocumentLoader.h" -#include "EditCommand.h" #include "Editor.h" #include "EditorClient.h" #include "Element.h" @@ -66,10 +70,12 @@ #include "MainResourceLoader.h" #include "Page.h" #include "PageCache.h" -#include "PluginInfoStore.h" +#include "PageGroup.h" +#include "PluginData.h" #include "ProgressTracker.h" #include "RenderPart.h" #include "RenderWidget.h" +#include "RenderView.h" #include "ResourceHandle.h" #include "ResourceRequest.h" #include "SecurityOrigin.h" @@ -80,11 +86,15 @@ #include "WindowFeatures.h" #include "XMLHttpRequest.h" #include "XMLTokenizer.h" -#include "kjs_binding.h" -#include "kjs_proxy.h" -#include "kjs_window.h" -#include <kjs/JSLock.h> -#include <kjs/object.h> +#include "JSDOMBinding.h" +#include "ScriptController.h" +#include <runtime/JSLock.h> +#include <runtime/JSObject.h> + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) +#include "ApplicationCache.h" +#include "ApplicationCacheResource.h" +#endif #if ENABLE(SVG) #include "SVGDocument.h" @@ -96,9 +106,7 @@ #include "SVGViewSpec.h" #endif -using KJS::UString; -using KJS::JSLock; -using KJS::JSValue; +using namespace JSC; namespace WebCore { @@ -106,7 +114,6 @@ namespace WebCore { using namespace SVGNames; #endif using namespace HTMLNames; -using namespace EventNames; #if USE(LOW_BANDWIDTH_DISPLAY) const unsigned int cMaxPendingSourceLengthInLowBandwidthDisplay = 128 * 1024; @@ -183,13 +190,13 @@ struct ScheduledRedirection { }; static double storedTimeOfLastCompletedLoad; -static bool m_restrictAccessToLocal = true; +static FrameLoader::LocalLoadPolicy localLoadPolicy = FrameLoader::AllowLocalLoadsForLocalOnly; static bool getString(JSValue* result, String& string) { if (!result) return false; - JSLock lock; + JSLock lock(false); UString ustring; if (!result->getString(ustring)) return false; @@ -306,30 +313,34 @@ void FrameLoader::setDefersLoading(bool defers) m_provisionalDocumentLoader->setDefersLoading(defers); if (m_policyDocumentLoader) m_policyDocumentLoader->setDefersLoading(defers); - m_client->setDefersLoading(defers); } -Frame* FrameLoader::createWindow(const FrameLoadRequest& request, const WindowFeatures& features, bool& created) +Frame* FrameLoader::createWindow(FrameLoader* frameLoaderForFrameLookup, const FrameLoadRequest& request, const WindowFeatures& features, bool& created) { ASSERT(!features.dialog || request.frameName().isEmpty()); - if (!request.frameName().isEmpty() && request.frameName() != "_blank") - if (Frame* frame = findFrameForNavigation(request.frameName())) { + if (!request.frameName().isEmpty() && request.frameName() != "_blank") { + Frame* frame = frameLoaderForFrameLookup->frame()->tree()->find(request.frameName()); + if (frame && shouldAllowNavigation(frame)) { if (!request.resourceRequest().url().isEmpty()) - frame->loader()->load(request, false, true, 0, 0, HashMap<String, String>()); + frame->loader()->loadFrameRequestWithFormAndValues(request, false, 0, 0, HashMap<String, String>()); if (Page* page = frame->page()) page->chrome()->focus(); created = false; return frame; } - + } + // FIXME: Setting the referrer should be the caller's responsibility. FrameLoadRequest requestWithReferrer = request; requestWithReferrer.resourceRequest().setHTTPReferrer(m_outgoingReferrer); - - Page* page = m_frame->page(); - if (page) - page = page->chrome()->createWindow(m_frame, requestWithReferrer, features); + addHTTPOriginIfNeeded(requestWithReferrer.resourceRequest(), outgoingOrigin()); + + Page* oldPage = m_frame->page(); + if (!oldPage) + return 0; + + Page* page = oldPage->chrome()->createWindow(m_frame, requestWithReferrer, features); if (!page) return 0; @@ -375,11 +386,17 @@ void FrameLoader::changeLocation(const String& url, const String& referrer, bool changeLocation(completeURL(url), referrer, lockHistory, userGesture); } + void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool lockHistory, bool userGesture) { + RefPtr<Frame> protect(m_frame); + ResourceRequestCachePolicy policy = (m_cachePolicy == CachePolicyReload) || (m_cachePolicy == CachePolicyRefresh) ? ReloadIgnoringCacheData : UseProtocolCachePolicy; ResourceRequest request(url, referrer, policy); +#ifdef ANDROID_USER_GESTURE + request.setUserGesture(userGesture); +#endif if (executeIfJavaScriptURL(request.url(), userGesture)) return; @@ -387,6 +404,16 @@ void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool l urlSelected(request, "_self", 0, lockHistory, userGesture); } +void FrameLoader::urlSelected(const FrameLoadRequest& request, Event* event, bool lockHistory) +{ + FrameLoadRequest copy = request; + if (copy.resourceRequest().httpReferrer().isEmpty()) + copy.resourceRequest().setHTTPReferrer(m_outgoingReferrer); + addHTTPOriginIfNeeded(copy.resourceRequest(), outgoingOrigin()); + + loadFrameRequestWithFormAndValues(copy, lockHistory, event, 0, HashMap<String, String>()); +} + void FrameLoader::urlSelected(const ResourceRequest& request, const String& _target, Event* triggeringEvent, bool lockHistory, bool userGesture) { if (executeIfJavaScriptURL(request.url(), userGesture, false)) @@ -397,11 +424,11 @@ void FrameLoader::urlSelected(const ResourceRequest& request, const String& _tar target = m_frame->document()->baseTarget(); FrameLoadRequest frameRequest(request, target); +#ifdef ANDROID_USER_GESTURE + frameRequest.setWasUserGesture(userGesture); +#endif - if (frameRequest.resourceRequest().httpReferrer().isEmpty()) - frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer); - - urlSelected(frameRequest, triggeringEvent, lockHistory, userGesture); + urlSelected(frameRequest, triggeringEvent, lockHistory); } bool FrameLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName) @@ -417,13 +444,9 @@ bool FrameLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String // Support for <frame src="javascript:string"> KURL scriptURL; KURL url; -#ifdef ANDROID_JAVASCRIPT_SECURITY if (protocolIs(urlString, "javascript")) { -#else - if (urlString.startsWith("javascript:", false)) { -#endif - scriptURL = urlString.deprecatedString(); - url = "about:blank"; + scriptURL = KURL(urlString); + url = blankURL(); } else url = completeURL(urlString); @@ -455,7 +478,7 @@ Frame* FrameLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL } if (!canLoad(url, referrer)) { - FrameLoader::reportLocalLoadFailed(m_frame->page(), url.string()); + FrameLoader::reportLocalLoadFailed(m_frame, url.string()); return 0; } @@ -469,9 +492,11 @@ Frame* FrameLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL } frame->loader()->m_isComplete = false; - - if (ownerElement->renderer() && frame->view()) - static_cast<RenderWidget*>(ownerElement->renderer())->setWidget(frame->view()); + + RenderObject* renderer = ownerElement->renderer(); + FrameView* view = frame->view(); + if (renderer && renderer->isWidget() && view) + static_cast<RenderWidget*>(renderer)->setWidget(view); checkCallImplicitClose(); @@ -481,7 +506,7 @@ Frame* FrameLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL // FIXME: In this case the Frame will have finished loading before // it's being added to the child list. It would be a good idea to // create the child first, then invoke the loader separately. - if (url.isEmpty() || url == "about:blank") { + if (url.isEmpty() || url == blankURL()) { frame->loader()->completed(); frame->loader()->checkCompleted(); } @@ -504,18 +529,16 @@ void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<F { ASSERT(formData); + if (!m_frame->page()) + return; + KURL u = completeURL(url.isNull() ? "" : url); // FIXME: Do we really need to special-case an empty URL? // Would it be better to just go on with the form submisson and let the I/O fail? if (u.isEmpty()) return; -#ifdef ANDROID_JAVASCRIPT_SECURITY if (u.protocolIs("javascript")) { -#else - DeprecatedString urlString = u.deprecatedString(); - if (urlString.startsWith("javascript:", false)) { -#endif m_isExecutingJavaScriptFormAction = true; executeIfJavaScriptURL(u, false, false); m_isExecutingJavaScriptFormAction = false; @@ -530,7 +553,12 @@ void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<F return; } + formData->generateFiles(m_frame->page()->chrome()->client()); + FrameLoadRequest frameRequest; +#ifdef ANDROID_USER_GESTURE + frameRequest.setWasUserGesture(userGestureHint()); +#endif if (!m_outgoingReferrer.isEmpty()) frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer); @@ -545,11 +573,11 @@ void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<F String query = u.query(); if (!query.isEmpty()) query.append('&'); - u.setQuery((query + body).deprecatedString()); + u.setQuery(query + body); } if (strcmp(action, "GET") == 0) { - u.setQuery(formData->flattenToString().deprecatedString()); + u.setQuery(formData->flattenToString()); } else { if (!isMailtoForm) frameRequest.resourceRequest().setHTTPBody(formData.get()); @@ -563,6 +591,7 @@ void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<F } frameRequest.resourceRequest().setURL(u); + addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin()); submitForm(frameRequest, event); } @@ -578,10 +607,14 @@ void FrameLoader::stopLoading(bool sendUnload) Node* currentFocusedNode = m_frame->document()->focusedNode(); if (currentFocusedNode) currentFocusedNode->aboutToUnload(); - m_frame->document()->dispatchWindowEvent(unloadEvent, false, false); + m_frame->document()->dispatchWindowEvent(eventNames().unloadEvent, false, false); if (m_frame->document()) m_frame->document()->updateRendering(); m_wasUnloadEventEmitted = true; + if (m_frame->eventHandler()->pendingFrameUnloadEventCount()) + m_frame->eventHandler()->clearPendingFrameUnloadEventCount(); + if (m_frame->eventHandler()->pendingFrameBeforeUnloadEventCount()) + m_frame->eventHandler()->clearPendingFrameBeforeUnloadEventCount(); } } if (m_frame->document() && !m_frame->document()->inPageCache()) @@ -603,10 +636,9 @@ void FrameLoader::stopLoading(bool sendUnload) if (Document* doc = m_frame->document()) { if (DocLoader* docLoader = doc->docLoader()) cache()->loader()->cancelRequests(docLoader); + + doc->stopActiveDOMObjects(); - XMLHttpRequest::cancelRequests(doc); - -// ANDROID_FIX ENABLE(DATABASE) missing from WebKit sources #if ENABLE(DATABASE) doc->stopDatabases(); #endif @@ -668,16 +700,16 @@ KURL FrameLoader::iconURL() { // If this isn't a top level frame, return nothing if (m_frame->tree() && m_frame->tree()->parent()) - return ""; - + return KURL(); + // If we have an iconURL from a Link element, return that if (m_frame->document() && !m_frame->document()->iconURL().isEmpty()) - return m_frame->document()->iconURL().deprecatedString(); - + return KURL(m_frame->document()->iconURL()); + // Don't return a favicon iconURL unless we're http or https - if (m_URL.protocol() != "http" && m_URL.protocol() != "https") - return ""; - + if (!m_URL.protocolIs("http") && !m_URL.protocolIs("https")) + return KURL(); + KURL url; url.setProtocol(m_URL.protocol()); url.setHost(m_URL.host()); @@ -695,7 +727,7 @@ bool FrameLoader::didOpenURL(const KURL& url) return false; cancelRedirection(); - m_frame->editor()->setLastEditCommand(0); + m_frame->editor()->clearLastEditCommand(); closeURL(); m_isComplete = false; @@ -706,7 +738,7 @@ bool FrameLoader::didOpenURL(const KURL& url) m_frame->setJSDefaultStatusBarText(String()); m_URL = url; - if (m_URL.protocol().startsWith("http") && !m_URL.host().isEmpty() && m_URL.path().isEmpty()) + if ((m_URL.protocolIs("http") || m_URL.protocolIs("https")) && !m_URL.host().isEmpty() && m_URL.path().isEmpty()) m_URL.setPath("/"); m_workingURL = m_URL; @@ -728,20 +760,16 @@ void FrameLoader::didExplicitOpen() // Cancelling redirection here works for all cases because document.open // implicitly precedes document.write. cancelRedirection(); - if (m_frame->document()->url() != "about:blank") + if (m_frame->document()->url() != blankURL()) m_URL = m_frame->document()->url(); } bool FrameLoader::executeIfJavaScriptURL(const KURL& url, bool userGesture, bool replaceDocument) { -#ifdef ANDROID_JAVASCRIPT_SECURITY if (!url.protocolIs("javascript")) -#else - if (!url.deprecatedString().startsWith("javascript:", false)) -#endif return false; - String script = KURL::decode_string(url.deprecatedString().mid(strlen("javascript:"))); + String script = decodeURLEscapeSequences(url.string().substring(strlen("javascript:"))); JSValue* result = executeScript(script, userGesture); String scriptResult; @@ -766,18 +794,18 @@ bool FrameLoader::executeIfJavaScriptURL(const KURL& url, bool userGesture, bool JSValue* FrameLoader::executeScript(const String& script, bool forceUserGesture) { - return executeScript(forceUserGesture ? String() : m_URL.string(), 0, script); + return executeScript(forceUserGesture ? String() : m_URL.string(), 1, script); } JSValue* FrameLoader::executeScript(const String& url, int baseLine, const String& script) { - if (!m_frame->scriptProxy()->isEnabled()) - return 0; + if (!m_frame->script()->isEnabled() || m_frame->script()->isPaused()) + return noValue(); bool wasRunningScript = m_isRunningScript; m_isRunningScript = true; - JSValue* result = m_frame->scriptProxy()->evaluate(url, baseLine, script); + JSValue* result = m_frame->script()->evaluate(url, baseLine, script); if (!wasRunningScript) { m_isRunningScript = false; @@ -822,27 +850,27 @@ void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects) // Do this after detaching the document so that the unload event works. if (clearWindowProperties) { - m_frame->clearScriptProxy(); m_frame->clearDOMWindow(); + m_frame->script()->clearWindowShell(); } - m_frame->selectionController()->clear(); + m_frame->selection()->clear(); m_frame->eventHandler()->clear(); if (m_frame->view()) m_frame->view()->clear(); - + m_frame->setSelectionGranularity(CharacterGranularity); - // Do not drop the document before the script proxy and view are cleared, as some destructors - // might still try to access the document. + // Do not drop the document before the ScriptController and view are cleared + // as some destructors might still try to access the document. m_frame->setDocument(0); m_decoder = 0; m_containsPlugIns = false; - + if (clearScriptObjects) - m_frame->clearScriptObjects(); - + m_frame->script()->clearScriptObjects(); + m_redirectionTimer.stop(); m_scheduledRedirection.clear(); @@ -881,7 +909,7 @@ void FrameLoader::receivedFirstData() if (url.isEmpty()) url = m_URL.string(); else - url = m_frame->document()->completeURL(url); + url = m_frame->document()->completeURL(url).string(); scheduleHTTPRedirection(delay, url); } @@ -919,28 +947,24 @@ void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin) m_isDisplayingInitialEmptyDocument = m_creatingInitialEmptyDocument; KURL ref(url); - ref.setUser(DeprecatedString()); - ref.setPass(DeprecatedString()); - ref.setRef(DeprecatedString()); + ref.setUser(String()); + ref.setPass(String()); + ref.setRef(String()); m_outgoingReferrer = ref.string(); m_URL = url; - KURL baseurl; - if (!m_URL.isEmpty()) - baseurl = m_URL; - - RefPtr<Document> document = DOMImplementation::instance()->createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode()); + RefPtr<Document> document = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode()); m_frame->setDocument(document); - document->setURL(m_URL.deprecatedString()); - // We prefer m_baseURL over m_URL because m_URL changes when we are - // about to load a new page. - document->setBaseURL(baseurl.deprecatedString()); + document->setURL(m_URL); if (m_decoder) document->setDecoder(m_decoder.get()); if (forcedSecurityOrigin) document->setSecurityOrigin(forcedSecurityOrigin.get()); + m_frame->domWindow()->setURL(document->url()); + m_frame->domWindow()->setSecurityOrigin(document->securityOrigin()); + updatePolicyBaseURL(); Settings* settings = document->settings(); @@ -949,6 +973,12 @@ void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin) document->docLoader()->setBlockNetworkImage(settings && settings->blockNetworkImage()); #endif + if (m_documentLoader) { + String dnsPrefetchControl = m_documentLoader->response().httpHeaderField("X-DNS-Prefetch-Control"); + if (!dnsPrefetchControl.isEmpty()) + document->parseDNSPrefetchControlHeader(dnsPrefetchControl); + } + #if FRAME_LOADS_USER_STYLESHEET KURL userStyleSheet = settings ? settings->userStyleSheetLocation() : KURL(); if (!userStyleSheet.isEmpty()) @@ -960,7 +990,7 @@ void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin) document->implicitOpen(); if (m_frame->view()) - m_frame->view()->resizeContents(0, 0); + m_frame->view()->setContentsSize(IntSize()); #if USE(LOW_BANDWIDTH_DISPLAY) // Low bandwidth display is a first pass display without external resources @@ -992,12 +1022,16 @@ void FrameLoader::write(const char* str, int len, bool flush) if (!m_decoder) { Settings* settings = m_frame->settings(); - m_decoder = new TextResourceDecoder(m_responseMIMEType, settings ? settings->defaultTextEncodingName() : String()); - if (!m_encoding.isNull()) + m_decoder = TextResourceDecoder::create(m_responseMIMEType, settings ? settings->defaultTextEncodingName() : String()); + if (m_encoding.isEmpty()) { + Frame* parentFrame = m_frame->tree()->parent(); + if (parentFrame && parentFrame->document()->securityOrigin()->canAccess(m_frame->document()->securityOrigin())) + m_decoder->setEncoding(parentFrame->document()->inputEncoding(), TextResourceDecoder::DefaultEncoding); + } else { m_decoder->setEncoding(m_encoding, m_encodingWasChosenByUser ? TextResourceDecoder::UserChosenEncoding : TextResourceDecoder::EncodingFromHTTPHeader); - if (m_frame->document()) - m_frame->document()->setDecoder(m_decoder.get()); + } + m_frame->document()->setDecoder(m_decoder.get()); } String decoded = m_decoder->decode(str, len); @@ -1015,7 +1049,6 @@ void FrameLoader::write(const char* str, int len, bool flush) if (!m_receivedData) { m_receivedData = true; - m_frame->document()->determineParseMode(decoded); if (m_decoder->encoding().usesVisualOrdering()) m_frame->document()->setVisuallyOrdered(); m_frame->document()->recalcStyle(Node::Force); @@ -1049,7 +1082,7 @@ void FrameLoader::end() void FrameLoader::endIfNotLoadingMainResource() { - if (m_isLoadingMainResource) + if (m_isLoadingMainResource || !m_frame->page()) return; // http://bugs.webkit.org/show_bug.cgi?id=10854 @@ -1073,9 +1106,6 @@ void FrameLoader::endIfNotLoadingMainResource() // become true. An example is when a subframe is a pure text doc, and that subframe is the // last one to complete. checkCompleted(); - - if (m_documentLoader && !m_documentLoader->isLoadingFromCachedPage()) - startIconLoader(); } void FrameLoader::iconLoadDecisionAvailable() @@ -1144,14 +1174,19 @@ void FrameLoader::startIconLoader() m_iconLoader->startLoading(); } +void FrameLoader::setLocalLoadPolicy(LocalLoadPolicy policy) +{ + localLoadPolicy = policy; +} + bool FrameLoader::restrictAccessToLocal() { - return m_restrictAccessToLocal; + return localLoadPolicy != FrameLoader::AllowLocalLoadsForAll; } -void FrameLoader::setRestrictAccessToLocal(bool access) +bool FrameLoader::allowSubstituteDataAccessToLocal() { - m_restrictAccessToLocal = access; + return localLoadPolicy != FrameLoader::AllowLocalLoadsForLocalOnly; } static HashSet<String, CaseFoldingHash>& localSchemes() @@ -1215,21 +1250,19 @@ void FrameLoader::restoreDocumentState() void FrameLoader::gotoAnchor() { // If our URL has no ref, then we have no place we need to jump to. - // OTOH if css target was set previously, we want to set it to 0, recalc + // OTOH If CSS target was set previously, we want to set it to 0, recalc // and possibly repaint because :target pseudo class may have been - // set(See bug 11321) - if (!m_URL.hasRef() && - !(m_frame->document() && m_frame->document()->getCSSTarget())) + // set (see bug 11321). + if (!m_URL.hasRef() && !(m_frame->document() && m_frame->document()->getCSSTarget())) return; - DeprecatedString ref = m_URL.encodedHtmlRef(); - if (!gotoAnchor(ref)) { - // Can't use htmlRef() here because it doesn't know which encoding to use to decode. - // Decoding here has to match encoding in completeURL, which means it has to use the - // page's encoding rather than UTF-8. - if (m_decoder) - gotoAnchor(KURL::decode_string(ref, m_decoder->encoding())); - } + String ref = m_URL.ref(); + if (gotoAnchor(ref)) + return; + + // Try again after decoding the ref, based on the document's encoding. + if (m_decoder) + gotoAnchor(decodeURLEscapeSequences(ref, m_decoder->encoding())); } void FrameLoader::finishedParsing() @@ -1239,7 +1272,8 @@ void FrameLoader::finishedParsing() // This can be called from the Frame's destructor, in which case we shouldn't protect ourselves // because doing so will cause us to re-enter the destructor when protector goes out of scope. - RefPtr<Frame> protector = m_frame->refCount() > 0 ? m_frame : 0; + // Null-checking the FrameView indicates whether or not we're in the destructor. + RefPtr<Frame> protector = m_frame->view() ? m_frame : 0; checkCompleted(); @@ -1316,8 +1350,9 @@ void FrameLoader::scheduleCheckCompleted() void FrameLoader::checkLoadCompleteTimerFired(Timer<FrameLoader>*) { - if (m_frame->page()) - checkLoadComplete(); + if (!m_frame->page()) + return; + checkLoadComplete(); } void FrameLoader::scheduleCheckLoadComplete() @@ -1356,7 +1391,7 @@ String FrameLoader::baseTarget() const KURL FrameLoader::completeURL(const String& url) { ASSERT(m_frame->document()); - return m_frame->document()->completeURL(url).deprecatedString(); + return m_frame->document()->completeURL(url); } void FrameLoader::scheduleHTTPRedirection(double delay, const String& url) @@ -1364,17 +1399,33 @@ void FrameLoader::scheduleHTTPRedirection(double delay, const String& url) if (delay < 0 || delay > INT_MAX / 1000) return; + if (!m_frame->page()) + return; + // We want a new history item if the refresh timeout is > 1 second. if (!m_scheduledRedirection || delay <= m_scheduledRedirection->delay) +#ifdef ANDROID_USER_GESTURE + { + bool wasUserGesture = false; + DocumentLoader* docLoader = activeDocumentLoader(); + if (docLoader) + wasUserGesture = docLoader->request().userGesture(); + scheduleRedirection(new ScheduledRedirection(delay, url, delay <= 1, wasUserGesture)); + } +#else scheduleRedirection(new ScheduledRedirection(delay, url, delay <= 1, false)); +#endif } void FrameLoader::scheduleLocationChange(const String& url, const String& referrer, bool lockHistory, bool wasUserGesture) -{ +{ + if (!m_frame->page()) + return; + // If the URL we're going to navigate to is the same as the current one, except for the // fragment part, we don't need to schedule the location change. - KURL u(url.deprecatedString()); - if (u.hasRef() && equalIgnoringRef(m_URL, u)) { + KURL parsedURL(url); + if (parsedURL.hasRef() && equalIgnoringRef(m_URL, parsedURL)) { changeLocation(url, referrer, lockHistory, wasUserGesture); return; } @@ -1399,6 +1450,9 @@ void FrameLoader::scheduleLocationChange(const String& url, const String& referr void FrameLoader::scheduleRefresh(bool wasUserGesture) { + if (!m_frame->page()) + return; + // Handle a location change of a page with no document as a special case. // This may happen when a frame requests a refresh of another frame. bool duringLoad = !m_frame->document(); @@ -1431,8 +1485,10 @@ bool FrameLoader::isLocationChange(const ScheduledRedirection& redirection) void FrameLoader::scheduleHistoryNavigation(int steps) { - // navigation will always be allowed in the 0 steps case, which is OK because - // that's supposed to force a reload. + if (!m_frame->page()) + return; + + // navigation will always be allowed in the 0 steps case, which is OK because that's supposed to force a reload. if (!canGoBackOrForward(steps)) { cancelRedirection(); return; @@ -1485,6 +1541,8 @@ void FrameLoader::goBackOrForward(int distance) void FrameLoader::redirectionTimerFired(Timer<FrameLoader>*) { + ASSERT(m_frame->page()); + OwnPtr<ScheduledRedirection> redirection(m_scheduledRedirection.release()); switch (redirection->type) { @@ -1505,9 +1563,87 @@ void FrameLoader::redirectionTimerFired(Timer<FrameLoader>*) goBackOrForward(redirection->historySteps); return; } + ASSERT_NOT_REACHED(); } +/* + In the case of saving state about a page with frames, we store a tree of items that mirrors the frame tree. + The item that was the target of the user's navigation is designated as the "targetItem". + When this method is called with doClip=YES we're able to create the whole tree except for the target's children, + which will be loaded in the future. That part of the tree will be filled out as the child loads are committed. +*/ +void FrameLoader::loadURLIntoChildFrame(const KURL& url, const String& referer, Frame* childFrame) +{ + ASSERT(childFrame); + HistoryItem* parentItem = currentHistoryItem(); + FrameLoadType loadType = this->loadType(); + FrameLoadType childLoadType = FrameLoadTypeRedirectWithLockedHistory; + + KURL workingURL = url; + + // If we're moving in the backforward list, we might want to replace the content + // of this child frame with whatever was there at that point. + // Reload will maintain the frame contents, LoadSame will not. + if (parentItem && parentItem->children().size() && + (isBackForwardLoadType(loadType) || loadType == FrameLoadTypeReloadAllowingStaleData)) + { + HistoryItem* childItem = parentItem->childItemWithName(childFrame->tree()->name()); + if (childItem) { + // Use the original URL to ensure we get all the side-effects, such as + // onLoad handlers, of any redirects that happened. An example of where + // this is needed is Radar 3213556. + workingURL = KURL(childItem->originalURLString()); + // These behaviors implied by these loadTypes should apply to the child frames + childLoadType = loadType; + + if (isBackForwardLoadType(loadType)) { + // For back/forward, remember this item so we can traverse any child items as child frames load + childFrame->loader()->setProvisionalHistoryItem(childItem); + } else { + // For reload, just reinstall the current item, since a new child frame was created but we won't be creating a new BF item + childFrame->loader()->setCurrentHistoryItem(childItem); + } + } + } + +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size + RefPtr<Archive> subframeArchive = activeDocumentLoader()->popArchiveForSubframe(childFrame->tree()->name()); + + if (subframeArchive) + childFrame->loader()->loadArchive(subframeArchive.release()); + else +#endif +#ifdef ANDROID_USER_GESTURE + childFrame->loader()->loadURL(workingURL, referer, String(), childLoadType, 0, 0, false); +#else + childFrame->loader()->loadURL(workingURL, referer, String(), childLoadType, 0, 0); +#endif +} + +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size +void FrameLoader::loadArchive(PassRefPtr<Archive> prpArchive) +{ + RefPtr<Archive> archive = prpArchive; + + ArchiveResource* mainResource = archive->mainResource(); + ASSERT(mainResource); + if (!mainResource) + return; + + SubstituteData substituteData(mainResource->data(), mainResource->mimeType(), mainResource->textEncoding(), KURL()); + + ResourceRequest request(mainResource->url()); +#if PLATFORM(MAC) + request.applyWebArchiveHackForMail(); +#endif + + RefPtr<DocumentLoader> documentLoader = m_client->createDocumentLoader(request, substituteData); + documentLoader->addAllArchiveResources(archive.get()); + load(documentLoader.get()); +} +#endif + String FrameLoader::encoding() const { if (m_encodingWasChosenByUser && !m_encoding.isEmpty()) @@ -1530,7 +1666,7 @@ bool FrameLoader::gotoAnchor(const String& name) m_frame->document()->setGotoAnchorNeededAfterStylesheetsLoad(false); Node* anchorNode = m_frame->document()->getElementById(AtomicString(name)); - if (!anchorNode) + if (!anchorNode && !name.isEmpty()) anchorNode = m_frame->document()->anchors()->namedItem(name, !m_frame->document()->inCompatMode()); #if ENABLE(SVG) @@ -1566,8 +1702,8 @@ bool FrameLoader::gotoAnchor(const String& name) // really mess things up if an anchor scroll comes at a bad moment. if (m_frame->document()) { m_frame->document()->updateRendering(); - // Only do a layout if changes have occurred that make it necessary. - if (m_frame->view() && m_frame->document()->renderer() && m_frame->document()->renderer()->needsLayout()) + // Only do a layout if changes have occurred that make it necessary. + if (m_frame->view() && m_frame->contentRenderer() && m_frame->contentRenderer()->needsLayout()) m_frame->view()->layout(); } @@ -1582,7 +1718,7 @@ bool FrameLoader::gotoAnchor(const String& name) rect = anchorNode->getRect(); } if (renderer) - renderer->enclosingLayer()->scrollRectToVisible(rect, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignTopAlways); + renderer->enclosingLayer()->scrollRectToVisible(rect, true, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignTopAlways); return true; } @@ -1625,8 +1761,8 @@ bool FrameLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool { // Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that // can handle TIFF (which QuickTime can also handle) they probably intended to override QT. - if ((mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) { - String pluginName = PluginInfoStore::pluginNameForMIMEType(mimeType); + if (m_frame->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) { + String pluginName = m_frame->page()->pluginData()->pluginNameForMimeType(mimeType); if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false)) return true; } @@ -1648,8 +1784,8 @@ bool FrameLoader::loadPlugin(RenderPart* renderer, const KURL& url, const String if (renderer->node() && renderer->node()->isElementNode()) pluginElement = static_cast<Element*>(renderer->node()); - if (!canLoad(url, frame()->document())) { - FrameLoader::reportLocalLoadFailed(m_frame->page(), url.string()); + if (!canLoad(url, String(), frame()->document())) { + FrameLoader::reportLocalLoadFailed(m_frame, url.string()); return false; } @@ -1671,9 +1807,13 @@ void FrameLoader::clearRecordedFormValues() m_formValuesAboutToBeSubmitted.clear(); } -void FrameLoader::recordFormValue(const String& name, const String& value, PassRefPtr<HTMLFormElement> element) +void FrameLoader::setFormAboutToBeSubmitted(PassRefPtr<HTMLFormElement> element) { m_formAboutToBeSubmitted = element; +} + +void FrameLoader::recordFormValue(const String& name, const String& value) +{ m_formValuesAboutToBeSubmitted.set(name, value); } @@ -1688,6 +1828,14 @@ String FrameLoader::outgoingReferrer() const return m_outgoingReferrer; } +String FrameLoader::outgoingOrigin() const +{ + if (m_frame->document()) + return m_frame->document()->securityOrigin()->toString(); + + return SecurityOrigin::createEmpty()->toString(); +} + Frame* FrameLoader::opener() { return m_opener; @@ -1701,8 +1849,10 @@ void FrameLoader::setOpener(Frame* opener) opener->loader()->m_openedFrames.add(m_frame); m_opener = opener; - if (m_frame->document()) - m_frame->document()->initSecurityOrigin(); + if (m_frame->document()) { + m_frame->document()->initSecurityContext(); + m_frame->domWindow()->setSecurityOrigin(m_frame->document()->securityOrigin()); + } } bool FrameLoader::openedByDOM() const @@ -1754,8 +1904,8 @@ bool FrameLoader::userGestureHint() while (rootFrame->tree()->parent()) rootFrame = rootFrame->tree()->parent(); - if (rootFrame->scriptProxy()->isEnabled()) - return rootFrame->scriptProxy()->processingUserGesture(); + if (rootFrame->script()->isEnabled()) + return rootFrame->script()->processingUserGesture(); return true; // If JavaScript is disabled, a user gesture must have initiated the navigation } @@ -1808,13 +1958,13 @@ bool FrameLoader::canCachePage() // they would need to be destroyed and then recreated, and there is no way that we can recreate // the right NPObjects. See <rdar://problem/5197041> for more information. && !m_containsPlugIns - && !m_URL.protocol().startsWith("https") + && !m_URL.protocolIs("https") && m_frame->document() - && !m_frame->document()->applets()->length() - && !m_frame->document()->hasWindowEventListener(unloadEvent) + && !m_frame->document()->hasWindowEventListener(eventNames().unloadEvent) #if ENABLE(DATABASE) && !m_frame->document()->hasOpenDatabases() #endif + && !m_frame->document()->usingGeolocation() && m_frame->page() && m_frame->page()->backForwardList()->enabled() && m_frame->page()->backForwardList()->capacity() > 0 @@ -1825,7 +1975,14 @@ bool FrameLoader::canCachePage() && loadType != FrameLoadTypeReloadAllowingStaleData && loadType != FrameLoadTypeSame && !m_documentLoader->isLoadingInAPISense() - && !m_documentLoader->isStopping(); + && !m_documentLoader->isStopping() +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + // FIXME: We should investigating caching pages that have an associated + // application cache. <rdar://problem/5917899> tracks that work. + && !m_documentLoader->applicationCache() + && !m_documentLoader->candidateApplicationCacheGroup() +#endif + ; } void FrameLoader::updatePolicyBaseURL() @@ -1833,24 +1990,27 @@ void FrameLoader::updatePolicyBaseURL() if (m_frame->tree()->parent() && m_frame->tree()->parent()->document()) setPolicyBaseURL(m_frame->tree()->parent()->document()->policyBaseURL()); else - setPolicyBaseURL(m_URL.string()); + setPolicyBaseURL(m_URL); } -void FrameLoader::setPolicyBaseURL(const String& s) +void FrameLoader::setPolicyBaseURL(const KURL& url) { if (m_frame->document()) - m_frame->document()->setPolicyBaseURL(s); + m_frame->document()->setPolicyBaseURL(url); for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) - child->loader()->setPolicyBaseURL(s); + child->loader()->setPolicyBaseURL(url); } -// This does the same kind of work that FrameLoader::openURL does, except it relies on the fact +// This does the same kind of work that didOpenURL does, except it relies on the fact // that a higher level already checked that the URLs match and the scrolling is the right thing to do. void FrameLoader::scrollToAnchor(const KURL& url) { m_URL = url; - started(); + updateHistoryForAnchorScroll(); + // If we were in the autoscroll/panScroll mode we want to stop it before following the link to the anchor + m_frame->eventHandler()->stopAutoscrollTimer(); + started(); gotoAnchor(); // It's important to model this as a load that starts and immediately finishes. @@ -1866,6 +2026,8 @@ bool FrameLoader::isComplete() const void FrameLoader::scheduleRedirection(ScheduledRedirection* redirection) { + ASSERT(m_frame->page()); + stopRedirectionTimer(); m_scheduledRedirection.set(redirection); if (!m_isComplete && redirection->type != ScheduledRedirection::redirection) @@ -1876,6 +2038,7 @@ void FrameLoader::scheduleRedirection(ScheduledRedirection* redirection) void FrameLoader::startRedirectionTimer() { + ASSERT(m_frame->page()); ASSERT(m_scheduledRedirection); m_redirectionTimer.stop(); @@ -1885,7 +2048,7 @@ void FrameLoader::startRedirectionTimer() case ScheduledRedirection::redirection: case ScheduledRedirection::locationChange: case ScheduledRedirection::locationChangeDuringLoad: - clientRedirected(m_scheduledRedirection->url.deprecatedString(), + clientRedirected(KURL(m_scheduledRedirection->url), m_scheduledRedirection->delay, currentTime() + m_redirectionTimer.nextFireInterval(), m_scheduledRedirection->lockHistory, @@ -1961,19 +2124,9 @@ void FrameLoader::setupForReplaceByMIMEType(const String& newMIMEType) activeDocumentLoader()->setupForReplaceByMIMEType(newMIMEType); } -void FrameLoader::finalSetupForReplace(DocumentLoader* loader) -{ - m_client->clearUnarchivingState(loader); -} - -void FrameLoader::load(const KURL& url, Event* event) -{ - load(ResourceRequest(url), false, true, event, 0, HashMap<String, String>()); -} - -void FrameLoader::load(const FrameLoadRequest& request, bool lockHistory, bool userGesture, Event* event, - HTMLFormElement* submitForm, const HashMap<String, String>& formValues) +void FrameLoader::loadFrameRequestWithFormState(const FrameLoadRequest& request, bool lockHistory, Event* event, PassRefPtr<FormState> prpFormState) { + RefPtr<FormState> formState = prpFormState; KURL url = request.resourceRequest().url(); String referrer; @@ -1984,9 +2137,9 @@ void FrameLoader::load(const FrameLoadRequest& request, bool lockHistory, bool u referrer = m_outgoingReferrer; ASSERT(frame()->document()); - if (url.deprecatedString().startsWith("file:", false)) { - if (!canLoad(url, frame()->document()) && !canLoad(url, referrer)) { - FrameLoader::reportLocalLoadFailed(m_frame->page(), url.string()); + if (url.protocolIs("file")) { + if (!canLoad(url, String(), frame()->document()) && !canLoad(url, referrer)) { + FrameLoader::reportLocalLoadFailed(m_frame, url.string()); return; } } @@ -1995,7 +2148,7 @@ void FrameLoader::load(const FrameLoadRequest& request, bool lockHistory, bool u referrer = String(); Frame* targetFrame = findFrameForNavigation(request.frameName()); - + if (request.resourceRequest().httpMethod() != "POST") { FrameLoadType loadType; if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData) @@ -2005,24 +2158,18 @@ void FrameLoader::load(const FrameLoadRequest& request, bool lockHistory, bool u else loadType = FrameLoadTypeStandard; - RefPtr<FormState> formState; - if (submitForm && !formValues.isEmpty()) - formState = FormState::create(submitForm, formValues, m_frame); - #ifdef ANDROID_USER_GESTURE - load(request.resourceRequest().url(), referrer, loadType, - request.frameName(), event, formState.release(), userGesture); + loadURL(request.resourceRequest().url(), referrer, request.frameName(), loadType, + event, formState.release(), request.wasUserGesture()); #else - load(request.resourceRequest().url(), referrer, loadType, - request.frameName(), event, formState.release()); + loadURL(request.resourceRequest().url(), referrer, request.frameName(), loadType, + event, formState.release()); #endif } else #ifdef ANDROID_USER_GESTURE - post(request.resourceRequest().url(), referrer, request.frameName(), - request.resourceRequest().httpBody(), request.resourceRequest().httpContentType(), event, submitForm, formValues, userGesture); + loadPostRequest(request.resourceRequest(), referrer, request.frameName(), event, formState.release(), request.wasUserGesture()); #else - post(request.resourceRequest().url(), referrer, request.frameName(), - request.resourceRequest().httpBody(), request.resourceRequest().httpContentType(), event, submitForm, formValues); + loadPostRequest(request.resourceRequest(), referrer, request.frameName(), event, formState.release()); #endif if (targetFrame && targetFrame != m_frame) @@ -2030,22 +2177,36 @@ void FrameLoader::load(const FrameLoadRequest& request, bool lockHistory, bool u page->chrome()->focus(); } +void FrameLoader::loadFrameRequestWithFormAndValues(const FrameLoadRequest& request, bool lockHistory, Event* event, + HTMLFormElement* submitForm, const HashMap<String, String>& formValues) +{ + RefPtr<FormState> formState; + if (submitForm) + formState = FormState::create(submitForm, formValues, m_frame); + + loadFrameRequestWithFormState(request, lockHistory, event, formState.release()); +} + #ifdef ANDROID_USER_GESTURE -void FrameLoader::load(const KURL& newURL, const String& referrer, FrameLoadType newLoadType, - const String& frameName, Event* event, PassRefPtr<FormState> formState, bool userGesture) +void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, FrameLoadType newLoadType, + Event* event, PassRefPtr<FormState> prpFormState, bool userGesture) #else -void FrameLoader::load(const KURL& newURL, const String& referrer, FrameLoadType newLoadType, - const String& frameName, Event* event, PassRefPtr<FormState> formState) +void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, FrameLoadType newLoadType, + Event* event, PassRefPtr<FormState> prpFormState) #endif { + RefPtr<FormState> formState = prpFormState; bool isFormSubmission = formState; ResourceRequest request(newURL); #ifdef ANDROID_USER_GESTURE request.setUserGesture(userGesture); #endif - if (!referrer.isEmpty()) + if (!referrer.isEmpty()) { request.setHTTPReferrer(referrer); + RefPtr<SecurityOrigin> referrerOrigin = SecurityOrigin::createFromString(referrer); + addHTTPOriginIfNeeded(request, referrerOrigin->toString()); + } addExtraFieldsToRequest(request, true, event || isFormSubmission); if (newLoadType == FrameLoadTypeReload) request.setCachePolicy(ReloadIgnoringCacheData); @@ -2057,9 +2218,9 @@ void FrameLoader::load(const KURL& newURL, const String& referrer, FrameLoadType if (!frameName.isEmpty()) { if (Frame* targetFrame = findFrameForNavigation(frameName)) #ifdef ANDROID_USER_GESTURE - targetFrame->loader()->load(newURL, referrer, newLoadType, String(), event, formState, userGesture); + targetFrame->loader()->loadURL(newURL, referrer, String(), newLoadType, event, formState, userGesture); #else - targetFrame->loader()->load(newURL, referrer, newLoadType, String(), event, formState); + targetFrame->loader()->loadURL(newURL, referrer, String(), newLoadType, event, formState); #endif else checkNewWindowPolicy(action, request, formState, frameName); @@ -2073,22 +2234,7 @@ void FrameLoader::load(const KURL& newURL, const String& referrer, FrameLoadType // Make sure to do scroll to anchor processing even if the URL is // exactly the same so pages with '#' links and DHTML side effects // work properly. - if (!isFormSubmission - && newLoadType != FrameLoadTypeReload - && newLoadType != FrameLoadTypeSame - && !shouldReload(newURL, url()) - // We don't want to just scroll if a link from within a - // frameset is trying to reload the frameset into _top. - && !m_frame->isFrameSet()) { - - // Just do anchor navigation within the existing content. - - // We don't do this if we are submitting a form, explicitly reloading, - // currently displaying a frameset, or if the new URL does not have a fragment. - // These rules are based on what KHTML was doing in KHTMLPart::openURL. - - // FIXME: What about load types other than Standard and Reload? - + if (shouldScrollToAnchor(isFormSubmission, newLoadType, newURL)) { oldDocumentLoader->setTriggeringAction(action); stopPolicyCheck(); checkNavigationPolicy(request, oldDocumentLoader.get(), formState, @@ -2096,7 +2242,7 @@ void FrameLoader::load(const KURL& newURL, const String& referrer, FrameLoadType } else { // must grab this now, since this load may stop the previous load and clear this flag bool isRedirect = m_quickRedirectComing; - load(request, action, newLoadType, formState); + loadWithNavigationAction(request, action, newLoadType, formState); if (isRedirect) { m_quickRedirectComing = false; if (m_provisionalDocumentLoader) @@ -2144,7 +2290,7 @@ void FrameLoader::load(const ResourceRequest& request, const String& frameName) checkNewWindowPolicy(NavigationAction(request.url(), NavigationTypeOther), request, 0, frameName); } -void FrameLoader::load(const ResourceRequest& request, const NavigationAction& action, FrameLoadType type, PassRefPtr<FormState> formState) +void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, FrameLoadType type, PassRefPtr<FormState> formState) { RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData()); @@ -2152,7 +2298,7 @@ void FrameLoader::load(const ResourceRequest& request, const NavigationAction& a if (m_documentLoader) loader->setOverrideEncoding(m_documentLoader->overrideEncoding()); - load(loader.get(), type, formState); + loadWithDocumentLoader(loader.get(), type, formState); } void FrameLoader::load(DocumentLoader* newDocumentLoader) @@ -2171,73 +2317,84 @@ void FrameLoader::load(DocumentLoader* newDocumentLoader) newDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding()); // When we loading alternate content for an unreachable URL that we're - // visiting in the b/f list, we treat it as a reload so the b/f list + // visiting in the history list, we treat it as a reload so the history list // is appropriately maintained. + // + // FIXME: This seems like a dangerous overloading of the meaning of "FrameLoadTypeReload" ... + // shouldn't a more explicit type of reload be defined, that means roughly + // "load without affecting history" ? if (shouldReloadToHandleUnreachableURL(newDocumentLoader)) { ASSERT(type == FrameLoadTypeStandard); type = FrameLoadTypeReload; } - load(newDocumentLoader, type, 0); + loadWithDocumentLoader(newDocumentLoader, type, 0); } -void FrameLoader::load(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> formState) +void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState) { ASSERT(m_client->hasWebView()); // Unfortunately the view must be non-nil, this is ultimately due // to parser requiring a FrameView. We should fix this dependency. - ASSERT(m_client->hasFrameView()); + ASSERT(m_frame->view()); m_policyLoadType = type; + RefPtr<FormState> formState = prpFormState; + bool isFormSubmission = formState; - if (Frame* parent = m_frame->tree()->parent()) - loader->setOverrideEncoding(parent->loader()->documentLoader()->overrideEncoding()); + const KURL& newURL = loader->request().url(); - stopPolicyCheck(); - setPolicyDocumentLoader(loader); + if (shouldScrollToAnchor(isFormSubmission, m_policyLoadType, newURL)) { + RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader; + NavigationAction action(newURL, m_policyLoadType, isFormSubmission); - checkNavigationPolicy(loader->request(), loader, formState, - callContinueLoadAfterNavigationPolicy, this); -} + oldDocumentLoader->setTriggeringAction(action); + stopPolicyCheck(); + checkNavigationPolicy(loader->request(), oldDocumentLoader.get(), formState, + callContinueFragmentScrollAfterNavigationPolicy, this); + } else { + if (Frame* parent = m_frame->tree()->parent()) + loader->setOverrideEncoding(parent->loader()->documentLoader()->overrideEncoding()); -// FIXME: It would be nice if we could collapse these into one or two functions. -bool FrameLoader::canLoad(const KURL& url, const String& referrer) -{ - if (!shouldTreatURLAsLocal(url.string())) - return true; + stopPolicyCheck(); + setPolicyDocumentLoader(loader); - return shouldTreatURLAsLocal(referrer); + checkNavigationPolicy(loader->request(), loader, formState, + callContinueLoadAfterNavigationPolicy, this); + } } -bool FrameLoader::canLoad(const KURL& url, const Document* doc) +bool FrameLoader::canLoad(const KURL& url, const String& referrer, const Document* doc) { + // We can always load any URL that isn't considered local (e.g. http URLs) if (!shouldTreatURLAsLocal(url.string())) return true; - return doc && doc->isAllowedToLoadLocalResources(); -} - -bool FrameLoader::canLoad(const CachedResource& resource, const Document* doc) -{ - if (!resource.treatAsLocal()) - return true; - - return doc && doc->isAllowedToLoadLocalResources(); + // If we were provided a document, we let its local file policy dictate the result, + // otherwise we allow local loads only if the supplied referrer is also local. + if (doc) + return doc->securityOrigin()->canLoadLocalResources(); + else if (!referrer.isEmpty()) + return shouldTreatURLAsLocal(referrer); + else + return false; } -void FrameLoader::reportLocalLoadFailed(const Page* page, const String& url) +void FrameLoader::reportLocalLoadFailed(Frame* frame, const String& url) { ASSERT(!url.isEmpty()); - if (page) - page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, "Not allowed to load local resource: " + url, 0, String()); + if (!frame) + return; + + frame->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, "Not allowed to load local resource: " + url, 0, String()); } bool FrameLoader::shouldHideReferrer(const KURL& url, const String& referrer) { - bool referrerIsSecureURL = referrer.startsWith("https:", false); - bool referrerIsWebURL = referrerIsSecureURL || referrer.startsWith("http:", false); + bool referrerIsSecureURL = protocolIs(referrer, "https"); + bool referrerIsWebURL = referrerIsSecureURL || protocolIs(referrer, "http"); if (!referrerIsWebURL) return true; @@ -2245,14 +2402,14 @@ bool FrameLoader::shouldHideReferrer(const KURL& url, const String& referrer) if (!referrerIsSecureURL) return false; - bool URLIsSecureURL = url.deprecatedString().startsWith("https:", false); + bool URLIsSecureURL = url.protocolIs("https"); return !URLIsSecureURL; } const ResourceRequest& FrameLoader::initialRequest() const { - return activeDocumentLoader()->initialRequest(); + return activeDocumentLoader()->originalRequest(); } void FrameLoader::receivedData(const char* data, int length) @@ -2260,11 +2417,6 @@ void FrameLoader::receivedData(const char* data, int length) activeDocumentLoader()->receivedData(data, length); } -bool FrameLoader::willUseArchive(ResourceLoader* loader, const ResourceRequest& request, const KURL& originalURL) const -{ - return m_client->willUseArchive(loader, request, originalURL); -} - void FrameLoader::handleUnimplementablePolicy(const ResourceError& error) { m_delegateIsHandlingUnimplementablePolicy = true; @@ -2352,7 +2504,7 @@ void FrameLoader::reloadAllowingStaleData(const String& encoding) loader->setOverrideEncoding(encoding); - load(loader.get(), FrameLoadTypeReloadAllowingStaleData, 0); + loadWithDocumentLoader(loader.get(), FrameLoadTypeReloadAllowingStaleData, 0); } void FrameLoader::reload() @@ -2372,6 +2524,8 @@ void FrameLoader::reload() if (!unreachableURL.isEmpty()) initialRequest = ResourceRequest(unreachableURL); + // Create a new document loader for the reload, this will become m_documentLoader eventually, + // but first it has to be the "policy" document loader, and then the "provisional" document loader. RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(initialRequest, SubstituteData()); ResourceRequest& request = loader->request(); @@ -2385,51 +2539,73 @@ void FrameLoader::reload() loader->setOverrideEncoding(m_documentLoader->overrideEncoding()); - load(loader.get(), FrameLoadTypeReload, 0); + loadWithDocumentLoader(loader.get(), FrameLoadTypeReload, 0); +} + +static bool canAccessAncestor(const SecurityOrigin* activeSecurityOrigin, Frame* targetFrame) +{ + // targetFrame can be NULL when we're trying to navigate a top-level frame + // that has a NULL opener. + if (!targetFrame) + return false; + + for (Frame* ancestorFrame = targetFrame; ancestorFrame; ancestorFrame = ancestorFrame->tree()->parent()) { + Document* ancestorDocument = ancestorFrame->document(); + if (!ancestorDocument) + return true; + + const SecurityOrigin* ancestorSecurityOrigin = ancestorDocument->securityOrigin(); + if (activeSecurityOrigin->canAccess(ancestorSecurityOrigin)) + return true; + } + + return false; } bool FrameLoader::shouldAllowNavigation(Frame* targetFrame) const { // The navigation change is safe if the active frame is: - // - in the same security origin as the target or one of the target's ancestors + // - in the same security origin as the target or one of the target's + // ancestors. + // // Or the target frame is: - // - a top-level frame in the frame hierarchy + // - a top-level frame in the frame hierarchy and the active frame can + // navigate the target frame's opener per above. if (!targetFrame) return true; + // Performance optimization. if (m_frame == targetFrame) return true; - if (!targetFrame->tree()->parent()) + // Let a frame navigate the top-level window that contains it. This is + // important to allow because it lets a site "frame-bust" (escape from a + // frame created by another web site). + if (targetFrame == m_frame->tree()->top()) return true; Document* activeDocument = m_frame->document(); ASSERT(activeDocument); const SecurityOrigin* activeSecurityOrigin = activeDocument->securityOrigin(); - for (Frame* ancestorFrame = targetFrame; ancestorFrame; ancestorFrame = ancestorFrame->tree()->parent()) { - Document* ancestorDocument = ancestorFrame->document(); - if (!ancestorDocument) - return true; - SecurityOrigin::Reason reason; - const SecurityOrigin* ancestorSecurityOrigin = ancestorDocument->securityOrigin(); - if (activeSecurityOrigin->canAccess(ancestorSecurityOrigin, reason)) - return true; - } + // For top-level windows, check the opener. + if (!targetFrame->tree()->parent() && canAccessAncestor(activeSecurityOrigin, targetFrame->loader()->opener())) + return true; + + // In general, check the frame's ancestors. + if (canAccessAncestor(activeSecurityOrigin, targetFrame)) + return true; - if (!targetFrame->settings()->privateBrowsingEnabled()) { + Settings* settings = targetFrame->settings(); + if (settings && !settings->privateBrowsingEnabled()) { Document* targetDocument = targetFrame->document(); // FIXME: this error message should contain more specifics of why the navigation change is not allowed. String message = String::format("Unsafe JavaScript attempt to initiate a navigation change for frame with URL %s from frame with URL %s.\n", - targetDocument->url().utf8().data(), activeDocument->url().utf8().data()); - - if (KJS::Interpreter::shouldPrintExceptions()) - printf("%s", message.utf8().data()); + targetDocument->url().string().utf8().data(), activeDocument->url().string().utf8().data()); // FIXME: should we print to the console of the activeFrame as well? - if (Page* page = targetFrame->page()) - page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 1, String()); + targetFrame->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, message, 1, String()); } return false; @@ -2456,8 +2632,13 @@ void FrameLoader::stopAllLoaders() m_provisionalDocumentLoader->stopLoading(); if (m_documentLoader) m_documentLoader->stopLoading(); + setProvisionalDocumentLoader(0); - m_client->clearArchivedResources(); + +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size + if (m_documentLoader) + m_documentLoader->clearArchiveResources(); +#endif m_inStopAllLoaders = false; } @@ -2472,11 +2653,6 @@ void FrameLoader::stopForUserCancel(bool deferCheckLoadComplete) checkLoadComplete(); } -void FrameLoader::cancelPendingArchiveLoad(ResourceLoader* loader) -{ - m_client->cancelPendingArchiveLoad(loader); -} - DocumentLoader* FrameLoader::activeDocumentLoader() const { if (m_state == FrameStateProvisional) @@ -2533,8 +2709,13 @@ void FrameLoader::setPolicyDocumentLoader(DocumentLoader* loader) m_policyDocumentLoader = loader; } + +DocumentLoader* FrameLoader::policyDocumentLoader() const +{ + return m_policyDocumentLoader.get(); +} -DocumentLoader* FrameLoader::provisionalDocumentLoader() +DocumentLoader* FrameLoader::provisionalDocumentLoader() const { return m_provisionalDocumentLoader.get(); } @@ -2596,7 +2777,16 @@ void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage) // We are doing this here because we know for sure that a new page is about to be loaded. if (canCachePage() && m_client->canCachePage() && !m_currentHistoryItem->isInPageCache()) cachePageForHistoryItem(m_currentHistoryItem.get()); - + else if (m_frame->page() && m_frame == m_frame->page()->mainFrame()) { + // If the main frame installs a timeout late enough (for example in its onunload handler) + // it could sometimes fire when transitioning to a non-HTML document representation (such as the Mac bookmarks view). + // To avoid this, we clear all timeouts if the page is not to be cached in the back forward list. + // Cached pages have their timers paused so they are fine. + ScriptController* proxy = m_frame->script(); + if (proxy->haveWindowShell()) + proxy->windowShell()->window()->clearAllTimeouts(); + } + if (m_loadType != FrameLoadTypeReplace) closeOldDataSources(); @@ -2622,7 +2812,7 @@ void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage) if (url.isEmpty()) url = pdl->responseURL(); if (url.isEmpty()) - url = "about:blank"; + url = blankURL(); didOpenURL(url); } @@ -2701,7 +2891,7 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) // This code was originally added for a Leopard performance imporvement. We decided to // ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>. if (m_frame->view()) - m_frame->view()->suppressScrollbars(true); + m_frame->view()->setScrollbarsSuppressed(true); #endif m_client->transitionToCommittedForNewPage(); break; @@ -2720,7 +2910,7 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) m_responseMIMEType = dl->responseMIMEType(); // Tell the client we've committed this URL. - ASSERT(m_client->hasFrameView()); + ASSERT(m_frame->view()); if (m_creatingInitialEmptyDocument) return; @@ -2769,7 +2959,7 @@ bool FrameLoader::shouldReload(const KURL& currentURL, const KURL& destinationUR // This function implements the rule: "Don't reload if navigating by fragment within // the same URL, but do reload if going to a new URL or to the same URL with no // fragment identifier at all." - if (!currentURL.hasRef() && !destinationURL.hasRef()) + if (!destinationURL.hasRef()) return true; return !equalIgnoringRef(currentURL, destinationURL); } @@ -2804,14 +2994,14 @@ void FrameLoader::open(CachedPage& cachedPage) m_didCallImplicitClose = true; // Delete old status bar messages (if it _was_ activated on last URL). - if (m_frame->scriptProxy()->isEnabled()) { + if (m_frame->script()->isEnabled()) { m_frame->setJSStatusBarText(String()); m_frame->setJSDefaultStatusBarText(String()); } KURL url = cachedPage.url(); - if (url.protocol().startsWith("http") && !url.host().isEmpty() && url.path().isEmpty()) + if ((url.protocolIs("http") || url.protocolIs("https")) && !url.host().isEmpty() && url.path().isEmpty()) url.setPath("/"); m_URL = url; @@ -2836,6 +3026,9 @@ void FrameLoader::open(CachedPage& cachedPage) m_frame->setView(view); m_frame->setDocument(document); + m_frame->domWindow()->setURL(document->url()); + m_frame->domWindow()->setSecurityOrigin(document->securityOrigin()); + m_decoder = document->decoder(); updatePolicyBaseURL(); @@ -2864,11 +3057,6 @@ void FrameLoader::finishedLoading() checkLoadComplete(); } -bool FrameLoader::isArchiveLoadPending(ResourceLoader* loader) const -{ - return m_client->isArchiveLoadPending(loader); -} - bool FrameLoader::isHostedByObjectElement() const { HTMLFrameOwnerElement* owner = m_frame->ownerElement(); @@ -2909,10 +3097,42 @@ void FrameLoader::didReceiveServerRedirectForProvisionalLoadForFrame() void FrameLoader::finishedLoadingDocument(DocumentLoader* loader) { -#if PLATFORM(WIN) || defined(ANDROID) - if (!m_creatingInitialEmptyDocument) + // FIXME: Platforms shouldn't differ here! +#if PLATFORM(WIN) || PLATFORM(CHROMIUM) || defined(ANDROID) + if (m_creatingInitialEmptyDocument) + return; #endif + +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size + // If loading a webarchive, run through webarchive machinery + const String& responseMIMEType = loader->responseMIMEType(); + + // FIXME: Mac's FrameLoaderClient::finishedLoading() method does work that is required even with Archive loads + // so we still need to call it. Other platforms should only call finishLoading for non-archive loads + // That work should be factored out so this #ifdef can be removed +#if PLATFORM(MAC) + m_client->finishedLoading(loader); + if (!ArchiveFactory::isArchiveMimeType(responseMIMEType)) + return; +#else + if (!ArchiveFactory::isArchiveMimeType(responseMIMEType)) { m_client->finishedLoading(loader); + return; + } +#endif + + RefPtr<Archive> archive(ArchiveFactory::create(loader->mainResourceData().get(), responseMIMEType)); + if (!archive) + return; + + loader->addAllArchiveResources(archive.get()); + + ArchiveResource* mainResource = archive->mainResource(); + loader->setParsedArchiveData(mainResource->data()); + continueLoadWithData(mainResource->data(), mainResource->mimeType(), mainResource->textEncoding(), mainResource->url()); +#else + m_client->finishedLoading(loader); +#endif } bool FrameLoader::isReplacing() const @@ -3050,7 +3270,7 @@ void FrameLoader::checkLoadCompleteForThisFrame() #ifdef ANDROID_INSTRUMENT if (!m_frame->tree()->parent()) { - m_frame->reportTimeCounter(m_URL.deprecatedString(), + m_frame->reportTimeCounter(m_URL, static_cast<int>((currentTime() - sCurrentTime) * 1000), (get_thread_msec() - sCurrentThreadTime)); } @@ -3080,7 +3300,13 @@ void FrameLoader::continueLoadAfterWillSubmitForm(PolicyAction) if (!m_provisionalDocumentLoader) return; + // DocumentLoader calls back to our prepareForLoadStart m_provisionalDocumentLoader->prepareForLoadStart(); + + // The load might be cancelled inside of prepareForLoadStart(), nulling out the m_provisionalDocumentLoader, + // so we need to null check it again. + if (!m_provisionalDocumentLoader) + return; DocumentLoader* activeDocLoader = activeDocumentLoader(); if (activeDocLoader && activeDocLoader->isLoadingMainResource()) @@ -3206,20 +3432,11 @@ void FrameLoader::submitForm(const FrameLoadRequest& request, Event* event) } // FIXME: We should probably call userGestureHint() to tell whether this form submission was the result of a user gesture. - load(request, false, true, event, m_formAboutToBeSubmitted.get(), m_formValuesAboutToBeSubmitted); + loadFrameRequestWithFormAndValues(request, false, event, m_formAboutToBeSubmitted.get(), m_formValuesAboutToBeSubmitted); clearRecordedFormValues(); } -void FrameLoader::urlSelected(const FrameLoadRequest& request, Event* event, bool lockHistory, bool userGesture) -{ - FrameLoadRequest copy = request; - if (copy.resourceRequest().httpReferrer().isEmpty()) - copy.resourceRequest().setHTTPReferrer(m_outgoingReferrer); - - load(copy, lockHistory, userGesture, event, 0, HashMap<String, String>()); -} - String FrameLoader::userAgent(const KURL& url) const { return m_client->userAgent(url); @@ -3276,10 +3493,6 @@ void FrameLoader::detachFromParent() m_frame->setView(0); m_frame->pageDestroyed(); } -#if PLATFORM(MAC) - [m_frame->bridge() close]; -#endif - m_client->detachedFromParent4(); } void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, bool mainResource, bool alwaysFromRequest) @@ -3300,80 +3513,95 @@ void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, bool mainRes } if (mainResource) - request.setHTTPAccept("text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"); + request.setHTTPAccept("application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"); + + // Make sure we send the Origin header. + addHTTPOriginIfNeeded(request, String()); +} + +void FrameLoader::addHTTPOriginIfNeeded(ResourceRequest& request, String origin) +{ + if (!request.httpOrigin().isEmpty()) + return; // Request already has an Origin header. + + // Don't send an Origin header for GET or HEAD to avoid privacy issues. + // For example, if an intranet page has a hyperlink to an external web + // site, we don't want to include the Origin of the request because it + // will leak the internal host name. Similar privacy concerns have lead + // to the widespread suppression of the Referer header at the network + // layer. + if (request.httpMethod() == "GET" || request.httpMethod() == "HEAD") + return; + + // For non-GET and non-HEAD methods, always send an Origin header so the + // server knows we support this feature. + + if (origin.isEmpty()) { + // If we don't know what origin header to attach, we attach the value + // for an empty origin. + origin = SecurityOrigin::createEmpty()->toString(); + } + + request.setHTTPOrigin(origin); } void FrameLoader::committedLoad(DocumentLoader* loader, const char* data, int length) { +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size + if (ArchiveFactory::isArchiveMimeType(loader->response().mimeType())) + return; +#endif m_client->committedLoad(loader, data, length); } #ifdef ANDROID_USER_GESTURE -void FrameLoader::post(const KURL& url, const String& referrer, const String& frameName, PassRefPtr<FormData> formData, - const String& contentType, Event* event, HTMLFormElement* form, const HashMap<String, String>& formValues, bool userGesture) +void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, + Event* event, PassRefPtr<FormState> prpFormState, bool userGesture) #else -void FrameLoader::post(const KURL& url, const String& referrer, const String& frameName, PassRefPtr<FormData> formData, - const String& contentType, Event* event, HTMLFormElement* form, const HashMap<String, String>& formValues) +void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, + Event* event, PassRefPtr<FormState> prpFormState) #endif { + RefPtr<FormState> formState = prpFormState; + // When posting, use the NSURLRequestReloadIgnoringCacheData load flag. // This prevents a potential bug which may cause a page with a form that uses itself // as an action to be returned from the cache without submitting. // FIXME: Where's the code that implements what the comment above says? - ResourceRequest request(url); + // Previously when this method was reached, the original FrameLoadRequest had been deconstructed to build a + // bunch of parameters that would come in here and then be built back up to a ResourceRequest. In case + // any caller depends on the immutability of the original ResourceRequest, I'm rebuilding a ResourceRequest + // from scratch as it did all along. + const KURL& url = inRequest.url(); + RefPtr<FormData> formData = inRequest.httpBody(); + const String& contentType = inRequest.httpContentType(); + String origin = inRequest.httpOrigin(); + + ResourceRequest workingResourceRequest(url); #ifdef ANDROID_USER_GESTURE - request.setUserGesture(userGesture); + workingResourceRequest.setUserGesture(userGesture); #endif - addExtraFieldsToRequest(request, true, true); if (!referrer.isEmpty()) - request.setHTTPReferrer(referrer); - request.setHTTPMethod("POST"); - request.setHTTPBody(formData); - request.setHTTPContentType(contentType); + workingResourceRequest.setHTTPReferrer(referrer); + workingResourceRequest.setHTTPOrigin(origin); + workingResourceRequest.setHTTPMethod("POST"); + workingResourceRequest.setHTTPBody(formData); + workingResourceRequest.setHTTPContentType(contentType); + addExtraFieldsToRequest(workingResourceRequest, true, true); NavigationAction action(url, FrameLoadTypeStandard, true, event); - RefPtr<FormState> formState; - if (form && !formValues.isEmpty()) - formState = FormState::create(form, formValues, m_frame); - if (!frameName.isEmpty()) { if (Frame* targetFrame = findFrameForNavigation(frameName)) - targetFrame->loader()->load(request, action, FrameLoadTypeStandard, formState.release()); + targetFrame->loader()->loadWithNavigationAction(workingResourceRequest, action, FrameLoadTypeStandard, formState.release()); else - checkNewWindowPolicy(action, request, formState.release(), frameName); + checkNewWindowPolicy(action, workingResourceRequest, formState.release(), frameName); } else - load(request, action, FrameLoadTypeStandard, formState.release()); -} - -#ifdef ANDROID_FIX -// Fix android bug http://b/issue?id=1131683 and -// webkit bug https://bugs.webkit.org/show_bug.cgi?id=18674 -// when doing history navigation for subframe, it needs a way to do post. -// The following code is similar to loadItem(HistoryItem* item, FrameLoadType loadType) -// when there is form data. -void FrameLoader::postFromHistory(const KURL& url, PassRefPtr<FormData> formData, - const String& contentType, const String& referrer, FrameLoadType loadType) -{ - ASSERT(isBackForwardLoadType(loadType) || - loadType == FrameLoadTypeReload || - loadType == FrameLoadTypeReloadAllowingStaleData); - - ResourceRequest request(url); - addExtraFieldsToRequest(request, true, true); - - request.setHTTPMethod("POST"); - request.setHTTPBody(formData); - request.setHTTPContentType(contentType); - request.setHTTPReferrer(referrer); - - NavigationAction action(url, loadType, false); - load(request, action, loadType, 0); + loadWithNavigationAction(workingResourceRequest, action, FrameLoadTypeStandard, formState.release()); } -#endif bool FrameLoader::isReloading() const { @@ -3386,7 +3614,7 @@ void FrameLoader::loadEmptyDocumentSynchronously() load(request); } -void FrameLoader::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) +unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) { // Since this is a subresource, we can load any URL (we ignore the return value). // But we still want to know whether we should hide the referrer or not, so we call the canLoad method. @@ -3404,6 +3632,7 @@ void FrameLoader::loadResourceSynchronously(const ResourceRequest& request, Reso if (!referrer.isEmpty()) initialRequest.setHTTPReferrer(referrer); + addHTTPOriginIfNeeded(initialRequest, outgoingOrigin()); if (Page* page = m_frame->page()) initialRequest.setMainDocumentURL(page->mainFrame()->loader()->documentLoader()->request().url()); @@ -3416,10 +3645,22 @@ void FrameLoader::loadResourceSynchronously(const ResourceRequest& request, Reso if (error.isNull()) { ASSERT(!newRequest.isNull()); didTellClientAboutLoad(newRequest.url().string()); - ResourceHandle::loadResourceSynchronously(newRequest, error, response, data, m_frame); + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + ApplicationCacheResource* resource; + if (documentLoader()->shouldLoadResourceFromApplicationCache(newRequest, resource)) { + if (resource) { + response = resource->response(); + data.append(resource->data()->data(), resource->data()->size()); + } else + error = cannotShowURLError(newRequest); + } else +#endif + ResourceHandle::loadResourceSynchronously(newRequest, error, response, data, m_frame); } sendRemainingDelegateMessages(identifier, response, data.size(), error); + return identifier; } void FrameLoader::assignIdentifierToInitialRequest(unsigned long identifier, const ResourceRequest& clientRequest) @@ -3547,13 +3788,32 @@ void FrameLoader::continueFragmentScrollAfterNavigationPolicy(const ResourceRequ m_client->didFinishLoad(); } +bool FrameLoader::shouldScrollToAnchor(bool isFormSubmission, FrameLoadType loadType, const KURL& url) +{ + // Should we do anchor navigation within the existing content? + + // We don't do this if we are submitting a form, explicitly reloading, + // currently displaying a frameset, or if the URL does not have a fragment. + // These rules were originally based on what KHTML was doing in KHTMLPart::openURL. + + // FIXME: What about load types other than Standard and Reload? + + return !isFormSubmission + && loadType != FrameLoadTypeReload + && loadType != FrameLoadTypeSame + && !shouldReload(this->url(), url) + // We don't want to just scroll if a link from within a + // frameset is trying to reload the frameset into _top. + && !m_frame->isFrameSet(); +} + void FrameLoader::opened() { if (m_loadType == FrameLoadTypeStandard && m_documentLoader->isClientRedirect()) updateHistoryForClientRedirect(); if (m_documentLoader->isLoadingFromCachedPage()) { - m_frame->document()->didRestoreFromCache(); + m_frame->document()->documentDidBecomeActive(); // Force a layout to update view size and thereby update scrollbars. m_client->forceLayout(); @@ -3588,7 +3848,7 @@ void FrameLoader::checkNewWindowPolicy(const NavigationAction& action, const Res m_policyCheck.set(request, formState, frameName, callContinueLoadAfterNewWindowPolicy, this); m_client->dispatchDecidePolicyForNewWindowAction(&FrameLoader::continueAfterNewWindowPolicy, - action, request, frameName); + action, request, formState, frameName); } void FrameLoader::continueAfterNewWindowPolicy(PolicyAction policy) @@ -3639,11 +3899,11 @@ void FrameLoader::checkNavigationPolicy(const ResourceRequest& request, Document loader->setLastCheckedRequest(request); - m_policyCheck.set(request, formState, function, argument); + m_policyCheck.set(request, formState.get(), function, argument); m_delegateIsDecidingNavigationPolicy = true; m_client->dispatchDecidePolicyForNavigationAction(&FrameLoader::continueAfterNavigationPolicy, - action, request); + action, request, formState); m_delegateIsDecidingNavigationPolicy = false; } @@ -3722,6 +3982,12 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest& reque FrameLoadType type = m_policyLoadType; stopAllLoaders(); + + // <rdar://problem/6250856> - In certain circumstances on pages with multiple frames, stopAllLoaders() + // might detach the current FrameLoader, in which case we should bail on this newly defunct load. + if (!m_frame->page()) + return; + setProvisionalDocumentLoader(m_policyDocumentLoader.get()); m_loadType = type; setState(FrameStateProvisional); @@ -3762,7 +4028,7 @@ void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& reques mainFrame->loader()->setOpenedByDOM(); mainFrame->loader()->m_client->dispatchShow(); mainFrame->loader()->setOpener(frame.get()); - mainFrame->loader()->load(request, NavigationAction(), FrameLoadTypeStandard, formState); + mainFrame->loader()->loadWithNavigationAction(request, NavigationAction(), FrameLoadTypeStandard, formState); } void FrameLoader::sendRemainingDelegateMessages(unsigned long identifier, const ResourceResponse& response, int length, const ResourceError& error) @@ -3793,7 +4059,7 @@ void FrameLoader::requestFromDelegate(ResourceRequest& request, unsigned long& i dispatchWillSendRequest(m_documentLoader.get(), identifier, newRequest, ResourceResponse()); if (newRequest.isNull()) - error = m_client->cancelledError(request); + error = cancelledError(request); else error = ResourceError(); @@ -3937,11 +4203,15 @@ PassRefPtr<HistoryItem> FrameLoader::createHistoryItem(bool useOriginal) // Later we may want to learn to live with nil for URL. // See bug 3368236 and related bugs for more information. if (url.isEmpty()) - url = KURL("about:blank"); + url = blankURL(); if (originalURL.isEmpty()) - originalURL = KURL("about:blank"); + originalURL = blankURL(); - RefPtr<HistoryItem> item = new HistoryItem(url, m_frame->tree()->name(), m_frame->tree()->parent() ? m_frame->tree()->parent()->tree()->name() : "", docLoader ? docLoader->title() : ""); + Frame* parentFrame = m_frame->tree()->parent(); + String parent = parentFrame ? parentFrame->tree()->name() : ""; + String title = docLoader ? docLoader->title() : ""; + + RefPtr<HistoryItem> item = HistoryItem::create(url, m_frame->tree()->name(), parent, title); item->setOriginalURLString(originalURL.string()); // Save form state if this is a POST @@ -3961,21 +4231,25 @@ PassRefPtr<HistoryItem> FrameLoader::createHistoryItem(bool useOriginal) void FrameLoader::addBackForwardItemClippedAtTarget(bool doClip) { - if (Page* page = m_frame->page()) - if (!documentLoader()->urlForHistory().isEmpty()) { - Frame* mainFrame = page->mainFrame(); - ASSERT(mainFrame); - FrameLoader* frameLoader = mainFrame->loader(); - - if (!frameLoader->m_didPerformFirstNavigation && page->backForwardList()->entries().size() == 1) { - frameLoader->m_didPerformFirstNavigation = true; - m_client->didPerformFirstNavigation(); - } + Page* page = m_frame->page(); + if (!page) + return; - RefPtr<HistoryItem> item = frameLoader->createHistoryItemTree(m_frame, doClip); - LOG(BackForward, "WebCoreBackForward - Adding backforward item %p for frame %s", item.get(), documentLoader()->url().string().ascii().data()); - page->backForwardList()->addItem(item); - } + if (documentLoader()->urlForHistory().isEmpty()) + return; + + Frame* mainFrame = page->mainFrame(); + ASSERT(mainFrame); + FrameLoader* frameLoader = mainFrame->loader(); + + if (!frameLoader->m_didPerformFirstNavigation && page->backForwardList()->entries().size() == 1) { + frameLoader->m_didPerformFirstNavigation = true; + m_client->didPerformFirstNavigation(); + } + + RefPtr<HistoryItem> item = frameLoader->createHistoryItemTree(m_frame, doClip); + LOG(BackForward, "WebCoreBackForward - Adding backforward item %p for frame %s", item.get(), documentLoader()->url().string().ascii().data()); + page->backForwardList()->addItem(item); } PassRefPtr<HistoryItem> FrameLoader::createHistoryItemTree(Frame* targetFrame, bool clipAtTarget) @@ -3986,8 +4260,17 @@ PassRefPtr<HistoryItem> FrameLoader::createHistoryItemTree(Frame* targetFrame, b if (!(clipAtTarget && m_frame == targetFrame)) { // save frame state for items that aren't loading (khtml doesn't save those) saveDocumentState(); - for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) - bfItem->addChildItem(child->loader()->createHistoryItemTree(targetFrame, clipAtTarget)); + for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) { + FrameLoader* childLoader = child->loader(); + bool hasChildLoaded = childLoader->frameHasLoaded(); + + // If the child is a frame corresponding to an <object> element that never loaded, + // we don't want to create a history item, because that causes fallback content + // to be ignored on reload. + + if (!(!hasChildLoaded && childLoader->isHostedByObjectElement())) + bfItem->addChildItem(childLoader->createHistoryItemTree(targetFrame, clipAtTarget)); + } } if (m_frame == targetFrame) bfItem->setIsTargetItem(true); @@ -4007,7 +4290,7 @@ void FrameLoader::saveScrollPositionAndViewStateToItem(HistoryItem* item) if (!item || !m_frame->view()) return; - item->setScrollPoint(IntPoint(m_frame->view()->contentsX(), m_frame->view()->contentsY())); + item->setScrollPoint(m_frame->view()->scrollPosition()); // FIXME: It would be great to work out a way to put this code in WebCore instead of calling through to the client. m_client->saveViewStateToItem(item); } @@ -4043,10 +4326,8 @@ void FrameLoader::restoreScrollPositionAndViewState() m_client->restoreViewState(); if (FrameView* view = m_frame->view()) - if (!view->wasScrolledByUser()) { - const IntPoint& scrollPoint = m_currentHistoryItem->scrollPoint(); - view->setContentsPos(scrollPoint.x(), scrollPoint.y()); - } + if (!view->wasScrolledByUser()) + view->setScrollPosition(m_currentHistoryItem->scrollPoint()); } void FrameLoader::invalidateCurrentItemCachedPage() @@ -4092,7 +4373,7 @@ void FrameLoader::saveDocumentState() ASSERT(document); if (document && item->isCurrentDocument(document)) { - LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree()->name().domString().utf8().data(), item); + LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree()->name().string().utf8().data(), item); item->setDocumentState(document->formElementsState()); } } @@ -4100,6 +4381,9 @@ void FrameLoader::saveDocumentState() // Loads content into this frame, as specified by history item void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) { + if (!m_frame->page()) + return; + KURL itemURL = item->url(); KURL itemOriginalURL = item->originalURL(); KURL currentURL; @@ -4113,7 +4397,7 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) // check for all that as an additional optimization. // We also do not do anchor-style navigation if we're posting a form. - if (!formData && !shouldReload(itemURL, currentURL) && urlsMatchItem(item)) { + if (!formData && urlsMatchItem(item)) { // Must do this maintenance here, since we don't go through a real page reload saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get()); @@ -4155,7 +4439,7 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) // WebKitBackForwardCacheExpirationIntervalKey in WebKit. // Or we should remove WebKitBackForwardCacheExpirationIntervalKey. if (interval <= 1800) { - load(cachedPage->documentLoader(), loadType, 0); + loadWithDocumentLoader(cachedPage->documentLoader(), loadType, 0); inPageCache = true; } else { LOG(PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", m_provisionalHistoryItem->url().string().ascii().data()); @@ -4166,15 +4450,18 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) if (!inPageCache) { ResourceRequest request(itemURL); - addExtraFieldsToRequest(request, true, formData); - // If this was a repost that failed the page cache, we might try to repost the form. NavigationAction action; if (formData) { + + formData->generateFiles(m_frame->page()->chrome()->client()); + request.setHTTPMethod("POST"); request.setHTTPReferrer(item->formReferrer()); request.setHTTPBody(formData); request.setHTTPContentType(item->formContentType()); + RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::createFromString(item->formReferrer()); + addHTTPOriginIfNeeded(request, securityOrigin->toString()); // FIXME: Slight hack to test if the NSURL cache contains the page we're going to. // We want to know this before talking to the policy delegate, since it affects whether @@ -4215,7 +4502,8 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) action = NavigationAction(itemOriginalURL, loadType, false); } - load(request, action, loadType, 0); + addExtraFieldsToRequest(request, true, formData); + loadWithNavigationAction(request, action, loadType, 0); } } } @@ -4223,13 +4511,12 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) // Walk the frame tree and ensure that the URLs match the URLs in the item. bool FrameLoader::urlsMatchItem(HistoryItem* item) const { - KURL currentURL = documentLoader()->url(); - + const KURL& currentURL = documentLoader()->url(); if (!equalIgnoringRef(currentURL, item->url())) return false; - + const HistoryItemVector& childItems = item->children(); - + unsigned size = childItems.size(); for (unsigned i = 0; i < size; ++i) { Frame* childFrame = m_frame->tree()->child(childItems[i]->target()); @@ -4250,17 +4537,19 @@ void FrameLoader::goToItem(HistoryItem* targetItem, FrameLoadType type) // <rdar://problem/3951283> can view pages from the back/forward cache that should be disallowed by Parental Controls // Ultimately, history item navigations should go through the policy delegate. That's covered in: // <rdar://problem/3979539> back/forward cache navigations should consult policy delegate - if (Page* page = m_frame->page()) - if (m_client->shouldGoToHistoryItem(targetItem)) { - BackForwardList* bfList = page->backForwardList(); - HistoryItem* currentItem = bfList->currentItem(); - - // Set the BF cursor before commit, which lets the user quickly click back/forward again. - // - plus, it only makes sense for the top level of the operation through the frametree, - // as opposed to happening for some/one of the page commits that might happen soon - bfList->goToItem(targetItem); - recursiveGoToItem(targetItem, currentItem, type); - } + Page* page = m_frame->page(); + if (!page) + return; + if (!m_client->shouldGoToHistoryItem(targetItem)) + return; + + // Set the BF cursor before commit, which lets the user quickly click back/forward again. + // - plus, it only makes sense for the top level of the operation through the frametree, + // as opposed to happening for some/one of the page commits that might happen soon + BackForwardList* bfList = page->backForwardList(); + HistoryItem* currentItem = bfList->currentItem(); + bfList->goToItem(targetItem); + recursiveGoToItem(targetItem, currentItem, type); } // The general idea here is to traverse the frame tree and the item tree in parallel, @@ -4338,40 +4627,45 @@ bool FrameLoader::childFramesMatchItem(HistoryItem* item) const return true; } -void FrameLoader::addHistoryForCurrentLocation() -{ - if (!m_frame->settings()->privateBrowsingEnabled()) { - // FIXME: <rdar://problem/4880065> - This will be a hook into the WebCore global history, and this loader/client call will be removed - m_client->updateGlobalHistoryForStandardLoad(documentLoader()->urlForHistory()); - } - addBackForwardItemClippedAtTarget(true); -} +// There are 3 things you might think of as "history", all of which are handled by these functions. +// +// 1) Back/forward: The m_currentHistoryItem is part of this mechanism. +// 2) Global history: Handled by the client. +// 3) Visited links: Handled by the PageGroup. void FrameLoader::updateHistoryForStandardLoad() { LOG(History, "WebCoreHistory: Updating History for Standard Load in frame %s", documentLoader()->url().string().ascii().data()); - - bool frameNavigationOnLoad = false; - - // if the navigation occured during on load and this is a subframe - // update the current history item rather than adding a new one - // <rdar://problem/5333496> + + Settings* settings = m_frame->settings(); + bool needPrivacy = !settings || settings->privateBrowsingEnabled(); + const KURL& historyURL = documentLoader()->urlForHistory(); + + // If the navigation occured during load and this is a subframe, update the current + // back/forward item rather than adding a new one and don't add the new URL to global + // history at all. But do add it to visited links. <rdar://problem/5333496> + bool frameNavigationDuringLoad = false; if (m_navigationDuringLoad) { HTMLFrameOwnerElement* owner = m_frame->ownerElement(); - frameNavigationOnLoad = owner && !owner->createdByParser(); + frameNavigationDuringLoad = owner && !owner->createdByParser(); + m_navigationDuringLoad = false; } - - if (!frameNavigationOnLoad && !documentLoader()->isClientRedirect()) { - if (!documentLoader()->urlForHistory().isEmpty()) - addHistoryForCurrentLocation(); + + if (!frameNavigationDuringLoad && !documentLoader()->isClientRedirect()) { + if (!historyURL.isEmpty()) { + addBackForwardItemClippedAtTarget(true); + if (!needPrivacy) + m_client->updateGlobalHistory(historyURL); + } } else if (documentLoader()->unreachableURL().isEmpty() && m_currentHistoryItem) { m_currentHistoryItem->setURL(documentLoader()->url()); m_currentHistoryItem->setFormInfoFromRequest(documentLoader()->request()); } - - // reset navigation during on load since we no longer - // need it past thsi point - m_navigationDuringLoad = false; + + if (!historyURL.isEmpty() && !needPrivacy) { + if (Page* page = m_frame->page()) + page->group().addVisitedLink(historyURL); + } } void FrameLoader::updateHistoryForClientRedirect() @@ -4387,6 +4681,15 @@ void FrameLoader::updateHistoryForClientRedirect() m_currentHistoryItem->clearDocumentState(); m_currentHistoryItem->clearScrollPoint(); } + + Settings* settings = m_frame->settings(); + bool needPrivacy = !settings || settings->privateBrowsingEnabled(); + const KURL& historyURL = documentLoader()->urlForHistory(); + + if (!historyURL.isEmpty() && !needPrivacy) { + if (Page* page = m_frame->page()) + page->group().addVisitedLink(historyURL); + } } void FrameLoader::updateHistoryForBackForwardNavigation() @@ -4417,10 +4720,6 @@ void FrameLoader::updateHistoryForReload() if (documentLoader()->unreachableURL().isEmpty()) m_currentHistoryItem->setURL(documentLoader()->requestURL()); } - - // FIXME: <rdar://problem/4880065> - This will be a hook into the WebCore global history, and this loader/client call will be removed - // Update the last visited time. Mostly interesting for URL autocompletion statistics. - m_client->updateGlobalHistoryForReload(documentLoader()->originalURL()); } void FrameLoader::updateHistoryForRedirectWithLockedHistory() @@ -4430,17 +4729,17 @@ void FrameLoader::updateHistoryForRedirectWithLockedHistory() LOG(History, "WebCoreHistory: Updating History for internal load in frame %s", documentLoader()->title().utf8().data()); #endif + Settings* settings = m_frame->settings(); + bool needPrivacy = !settings || settings->privateBrowsingEnabled(); + const KURL& historyURL = documentLoader()->urlForHistory(); + if (documentLoader()->isClientRedirect()) { - if (!m_currentHistoryItem && !m_frame->tree()->parent()) - addHistoryForCurrentLocation(); + if (!m_currentHistoryItem && !m_frame->tree()->parent()) { + addBackForwardItemClippedAtTarget(true); + if (!needPrivacy && !historyURL.isEmpty()) + m_client->updateGlobalHistory(historyURL); + } if (m_currentHistoryItem) { -#ifdef ANDROID_FIX - if (m_currentHistoryItem->formData()) { - m_currentHistoryItem->setOriginalFormInfo(m_currentHistoryItem->formData()->copy(), - m_currentHistoryItem->formContentType(), - m_currentHistoryItem->formReferrer()); - } -#endif m_currentHistoryItem->setURL(documentLoader()->url()); m_currentHistoryItem->setFormInfoFromRequest(documentLoader()->request()); } @@ -4449,6 +4748,11 @@ void FrameLoader::updateHistoryForRedirectWithLockedHistory() if (parentFrame && parentFrame->loader()->m_currentHistoryItem) parentFrame->loader()->m_currentHistoryItem->addChildItem(createHistoryItem(true)); } + + if (!historyURL.isEmpty() && !needPrivacy) { + if (Page* page = m_frame->page()) + page->group().addVisitedLink(historyURL); + } } void FrameLoader::updateHistoryForCommit() @@ -4471,6 +4775,22 @@ void FrameLoader::updateHistoryForCommit() } } +void FrameLoader::updateHistoryForAnchorScroll() +{ + if (m_URL.isEmpty()) + return; + + Settings* settings = m_frame->settings(); + if (!settings || settings->privateBrowsingEnabled()) + return; + + Page* page = m_frame->page(); + if (!page) + return; + + page->group().addVisitedLink(m_URL); +} + // Walk the frame tree, telling all frames to save their form state into their current // history item. void FrameLoader::saveDocumentAndScrollState() @@ -4534,7 +4854,9 @@ void FrameLoader::mainReceivedError(const ResourceError& error, bool isComplete) ResourceError FrameLoader::cancelledError(const ResourceRequest& request) const { - return m_client->cancelledError(request); + ResourceError error = m_client->cancelledError(request); + error.setIsCancellation(true); + return error; } ResourceError FrameLoader::blockedError(const ResourceRequest& request) const @@ -4542,6 +4864,11 @@ ResourceError FrameLoader::blockedError(const ResourceRequest& request) const return m_client->blockedError(request); } +ResourceError FrameLoader::cannotShowURLError(const ResourceRequest& request) const +{ + return m_client->cannotShowURLError(request); +} + ResourceError FrameLoader::fileDoesNotExistError(const ResourceResponse& response) const { return m_client->fileDoesNotExistError(response); @@ -4659,7 +4986,7 @@ void FrameLoader::setTitle(const String& title) KURL FrameLoader::originalRequestURL() const { - return activeDocumentLoader()->initialRequest().url(); + return activeDocumentLoader()->originalRequest().url(); } String FrameLoader::referrer() const @@ -4669,13 +4996,17 @@ String FrameLoader::referrer() const void FrameLoader::dispatchWindowObjectAvailable() { - if (!m_frame->scriptProxy()->isEnabled() || !m_frame->scriptProxy()->haveGlobalObject()) + if (!m_frame->script()->isEnabled() || !m_frame->script()->haveWindowShell()) return; m_client->windowObjectCleared(); - if (Page* page = m_frame->page()) + + if (Page* page = m_frame->page()) { + if (InspectorController* inspector = page->inspectorController()) + inspector->inspectedWindowScriptObjectCleared(m_frame); if (InspectorController* inspector = page->parentInspectorController()) inspector->windowScriptObjectAvailable(); + } } Widget* FrameLoader::createJavaAppletWidget(const IntSize& size, Element* element, const HashMap<String, String>& args) @@ -4685,17 +5016,21 @@ Widget* FrameLoader::createJavaAppletWidget(const IntSize& size, Element* elemen Vector<String> paramValues; HashMap<String, String>::const_iterator end = args.end(); for (HashMap<String, String>::const_iterator it = args.begin(); it != end; ++it) { - if (it->first.lower() == "baseurl") + if (equalIgnoringCase(it->first, "baseurl")) baseURLString = it->second; paramNames.append(it->first); paramValues.append(it->second); } if (baseURLString.isEmpty()) - baseURLString = m_frame->document()->baseURL(); + baseURLString = m_frame->document()->baseURL().string(); KURL baseURL = completeURL(baseURLString); - return m_client->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues); + Widget* widget = m_client->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues); + if (widget) + m_containsPlugIns = true; + + return widget; } void FrameLoader::didChangeTitle(DocumentLoader* loader) @@ -4846,7 +5181,7 @@ bool FrameLoader::addLowBandwidthDisplayRequest(CachedResource* cache) case CachedResource::Script: m_needToSwitchOutLowBandwidthDisplay = true; m_externalRequestsInLowBandwidthDisplay.add(cache); - cache->ref(this); + cache->addClient(this); return true; case CachedResource::ImageResource: case CachedResource::FontResource: @@ -4867,7 +5202,7 @@ void FrameLoader::removeAllLowBandwidthDisplayRequests() { HashSet<CachedResource*>::iterator end = m_externalRequestsInLowBandwidthDisplay.end(); for (HashSet<CachedResource*>::iterator it = m_externalRequestsInLowBandwidthDisplay.begin(); it != end; ++it) - (*it)->deref(this); + (*it)->removeClient(this); m_externalRequestsInLowBandwidthDisplay.clear(); } @@ -4875,7 +5210,7 @@ void FrameLoader::notifyFinished(CachedResource* script) { HashSet<CachedResource*>::iterator it = m_externalRequestsInLowBandwidthDisplay.find(script); if (it != m_externalRequestsInLowBandwidthDisplay.end()) { - (*it)->deref(this); + (*it)->removeClient(this); m_externalRequestsInLowBandwidthDisplay.remove(it); switchOutLowBandwidthDisplayIfReady(); } @@ -4900,17 +5235,15 @@ void FrameLoader::switchOutLowBandwidthDisplayIfReady() // similar to clear(), should be refactored to share more code oldDoc->cancelParsing(); oldDoc->detach(); - if (m_frame->scriptProxy()->isEnabled()) - m_frame->scriptProxy()->clear(); + if (m_frame->script()->isEnabled()) + m_frame->script()->clearWindowShell(); if (m_frame->view()) m_frame->view()->clear(); // similar to begin(), should be refactored to share more code - RefPtr<Document> newDoc = DOMImplementation::instance()-> - createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode()); + RefPtr<Document> newDoc = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode()); m_frame->setDocument(newDoc); - newDoc->setURL(m_URL.deprecatedString()); - newDoc->setBaseURL(m_URL.deprecatedString()); + newDoc->setURL(m_URL); if (m_decoder) newDoc->setDecoder(m_decoder.get()); restoreDocumentState(); @@ -4931,7 +5264,6 @@ void FrameLoader::switchOutLowBandwidthDisplayIfReady() if (m_pendingSourceInLowBandwidthDisplay.length()) { // set cachePolicy to Cache to use the loaded resource newDoc->docLoader()->setCachePolicy(CachePolicyCache); - newDoc->determineParseMode(m_pendingSourceInLowBandwidthDisplay); if (m_decoder->encoding().usesVisualOrdering()) newDoc->setVisuallyOrdered(); newDoc->recalcStyle(Node::Force); diff --git a/WebCore/loader/FrameLoader.h b/WebCore/loader/FrameLoader.h index a8a9735..47a56af 100644 --- a/WebCore/loader/FrameLoader.h +++ b/WebCore/loader/FrameLoader.h @@ -47,12 +47,12 @@ #include "CachedResourceClient.h" #endif -namespace KJS { - class JSValue; -} - namespace WebCore { +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size + class Archive; + class ArchiveResource; +#endif class AuthenticationChallenge; class CachedPage; class Document; @@ -136,57 +136,62 @@ namespace WebCore { Frame* frame() const { return m_frame; } - // FIXME: This is not cool, people. + // FIXME: This is not cool, people. We should aim to consolidate these variety of loading related methods into a smaller set, + // and try to reuse more of the same logic by extracting common code paths. void prepareForLoadStart(); void setupForReplace(); void setupForReplaceByMIMEType(const String& newMIMEType); - void finalSetupForReplace(DocumentLoader*); - void load(const KURL&, Event*); - void load(const FrameLoadRequest&, bool lockHistory, bool userGesture, - Event*, HTMLFormElement*, const HashMap<String, String>& formValues); + + void loadWithDocumentLoader(DocumentLoader*, FrameLoadType, PassRefPtr<FormState>); // Calls continueLoadAfterNavigationPolicy + void load(DocumentLoader*); // Calls loadWithDocumentLoader + + void loadWithNavigationAction(const ResourceRequest&, const NavigationAction&, // Calls loadWithDocumentLoader() + FrameLoadType, PassRefPtr<FormState>); + #ifdef ANDROID_USER_GESTURE - void load(const KURL&, const String& referrer, FrameLoadType, const String& target, - Event*, PassRefPtr<FormState>, bool userGesture); - void post(const KURL&, const String& referrer, const String& target, - PassRefPtr<FormData>, const String& contentType, - Event*, HTMLFormElement*, const HashMap<String, String>& formValues, bool userGesture); + void loadPostRequest(const ResourceRequest& inRequest, const String& referrer, // Called by loadFrameRequestWithFormAndValues(), calls loadWithNavigationAction + const String& frameName, Event* event, PassRefPtr<FormState> prpFormState, bool userGesture); + + void loadURL(const KURL& newURL, const String& referrer, const String& frameName, // Called by loadFrameRequestWithFormAndValues(), calls loadWithNavigationAction or else dispatches to navigation policy delegate + FrameLoadType, Event* event, PassRefPtr<FormState> prpFormState, bool userGesture); #else - void load(const KURL&, const String& referrer, FrameLoadType, const String& target, - Event*, PassRefPtr<FormState>); - void post(const KURL&, const String& referrer, const String& target, - PassRefPtr<FormData>, const String& contentType, - Event*, HTMLFormElement*, const HashMap<String, String>& formValues); -#endif -#ifdef ANDROID_FIX - void postFromHistory(const KURL& url, PassRefPtr<FormData> formData, - const String& contentType, const String& referrer, FrameLoadType loadType); + void loadPostRequest(const ResourceRequest& inRequest, const String& referrer, // Called by loadFrameRequestWithFormAndValues(), calls loadWithNavigationAction + const String& frameName, Event* event, PassRefPtr<FormState> prpFormState); + + void loadURL(const KURL& newURL, const String& referrer, const String& frameName, // Called by loadFrameRequestWithFormAndValues(), calls loadWithNavigationAction or else dispatches to navigation policy delegate + FrameLoadType, Event* event, PassRefPtr<FormState> prpFormState); #endif + void loadURLIntoChildFrame(const KURL&, const String& referer, Frame*); - void load(const ResourceRequest&); - void load(const ResourceRequest&, const SubstituteData&); - void load(const ResourceRequest&, const String& frameName); - void load(const ResourceRequest&, const NavigationAction&, FrameLoadType, PassRefPtr<FormState>); + void loadFrameRequestWithFormState(const FrameLoadRequest&, bool lockHistory, Event*, PassRefPtr<FormState>); + void loadFrameRequestWithFormAndValues(const FrameLoadRequest&, bool lockHistory, // Called by submitForm, calls loadPostRequest() + Event*, HTMLFormElement*, const HashMap<String, String>& formValues); + + void load(const ResourceRequest&); // Called by WebFrame, calls (ResourceRequest, SubstituteData) + void load(const ResourceRequest&, const SubstituteData&); // Called both by WebFrame and internally, calls (DocumentLoader*) + void load(const ResourceRequest&, const String& frameName); // Called by WebPluginController - void load(DocumentLoader*); - void load(DocumentLoader*, FrameLoadType, PassRefPtr<FormState>); +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size + void loadArchive(PassRefPtr<Archive> archive); +#endif - static bool canLoad(const KURL&, const String& referrer); - static bool canLoad(const KURL&, const Document*); - static bool canLoad(const CachedResource&, const Document*); - static void reportLocalLoadFailed(const Page*, const String& url); + // Returns true for any non-local URL. If Document parameter is supplied, its local load policy dictates, + // otherwise if referrer is non-empty and represents a local file, then the local load is allowed. + static bool canLoad(const KURL&, const String& referrer, const Document* theDocument = 0); + static void reportLocalLoadFailed(Frame*, const String& url); static bool shouldHideReferrer(const KURL& url, const String& referrer); - Frame* createWindow(const FrameLoadRequest&, const WindowFeatures&, bool& created); + // Called by createWindow in JSDOMWindowBase.cpp, e.g. to fulfill a modal dialog creation + Frame* createWindow(FrameLoader* frameLoaderForFrameLookup, const FrameLoadRequest&, const WindowFeatures&, bool& created); - void loadResourceSynchronously(const ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>& data); + unsigned long loadResourceSynchronously(const ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>& data); bool canHandleRequest(const ResourceRequest&); // Also not cool. void stopAllLoaders(); void stopForUserCancel(bool deferCheckLoadComplete = false); - void cancelPendingArchiveLoad(ResourceLoader*); bool isLoadingMainResource() const { return m_isLoadingMainResource; } bool isLoading() const; @@ -196,11 +201,13 @@ namespace WebCore { bool isReloading() const; String referrer() const; String outgoingReferrer() const; + String outgoingOrigin() const; void loadEmptyDocumentSynchronously(); DocumentLoader* activeDocumentLoader() const; DocumentLoader* documentLoader() const; - DocumentLoader* provisionalDocumentLoader(); + DocumentLoader* policyDocumentLoader() const; + DocumentLoader* provisionalDocumentLoader() const; FrameState state() const; static double timeOfLastCompletedLoad(); @@ -226,8 +233,8 @@ namespace WebCore { ResourceError cancelledError(const ResourceRequest&) const; ResourceError fileDoesNotExistError(const ResourceResponse&) const; ResourceError blockedError(const ResourceRequest&) const; - bool willUseArchive(ResourceLoader*, const ResourceRequest&, const KURL&) const; - bool isArchiveLoadPending(ResourceLoader*) const; + ResourceError cannotShowURLError(const ResourceRequest&) const; + void cannotShowMIMEType(const ResourceResponse&); ResourceError interruptionForPolicyChangeError(const ResourceRequest&); @@ -279,6 +286,7 @@ namespace WebCore { void detachChildren(); void addExtraFieldsToRequest(ResourceRequest&, bool isMainResource, bool alwaysFromRequest); + static void addHTTPOriginIfNeeded(ResourceRequest&, String origin); FrameLoaderClient* client() const; @@ -287,7 +295,7 @@ namespace WebCore { void changeLocation(const String& url, const String& referrer, bool lockHistory = true, bool userGesture = false); void changeLocation(const KURL&, const String& referrer, bool lockHistory = true, bool userGesture = false); void urlSelected(const ResourceRequest&, const String& target, Event*, bool lockHistory, bool userGesture); - void urlSelected(const FrameLoadRequest&, Event*, bool lockHistory, bool userGesture); + void urlSelected(const FrameLoadRequest&, Event*, bool lockHistory); bool requestFrame(HTMLFrameOwnerElement*, const String& url, const AtomicString& frameName); Frame* loadSubframe(HTMLFrameOwnerElement*, const KURL&, const String& name, const String& referrer); @@ -334,8 +342,8 @@ namespace WebCore { // Returns true if url is a JavaScript URL. bool executeIfJavaScriptURL(const KURL& url, bool userGesture = false, bool replaceDocument = true); - KJS::JSValue* executeScript(const String& url, int baseLine, const String& script); - KJS::JSValue* executeScript(const String& script, bool forceUserGesture = false); + JSC::JSValue* executeScript(const String& url, int baseLine, const String& script); + JSC::JSValue* executeScript(const String& script, bool forceUserGesture = false); void gotoAnchor(); bool gotoAnchor(const String& name); // returns true if the anchor was found @@ -388,7 +396,8 @@ namespace WebCore { void scheduleCheckLoadComplete(); void clearRecordedFormValues(); - void recordFormValue(const String& name, const String& value, PassRefPtr<HTMLFormElement>); + void setFormAboutToBeSubmitted(PassRefPtr<HTMLFormElement> element); + void recordFormValue(const String& name, const String& value); bool isComplete() const; @@ -425,9 +434,16 @@ namespace WebCore { void continueLoadWithData(SharedBuffer*, const String& mimeType, const String& textEncoding, const KURL&); - static void registerURLSchemeAsLocal(const String& scheme); + enum LocalLoadPolicy { + AllowLocalLoadsForAll, // No restriction on local loads. + AllowLocalLoadsForLocalAndSubstituteData, + AllowLocalLoadsForLocalOnly, + }; + static void setLocalLoadPolicy(LocalLoadPolicy); static bool restrictAccessToLocal(); - static void setRestrictAccessToLocal(bool); + static bool allowSubstituteDataAccessToLocal(); + + static void registerURLSchemeAsLocal(const String& scheme); static bool shouldTreatURLAsLocal(const String&); static bool shouldTreatSchemeAsLocal(const String&); @@ -448,6 +464,10 @@ namespace WebCore { bool shouldAllowNavigation(Frame* targetFrame) const; Frame* findFrameForNavigation(const AtomicString& name); + void startIconLoader(); + + void applyUserAgent(ResourceRequest& request); + private: PassRefPtr<HistoryItem> createHistoryItem(bool useOriginal); PassRefPtr<HistoryItem> createHistoryItemTree(Frame* targetFrame, bool clipAtTarget); @@ -461,18 +481,18 @@ namespace WebCore { void recursiveGoToItem(HistoryItem*, HistoryItem*, FrameLoadType); bool childFramesMatchItem(HistoryItem*) const; - void addHistoryForCurrentLocation(); void updateHistoryForBackForwardNavigation(); void updateHistoryForReload(); void updateHistoryForStandardLoad(); void updateHistoryForRedirectWithLockedHistory(); void updateHistoryForClientRedirect(); void updateHistoryForCommit(); + void updateHistoryForAnchorScroll(); void redirectionTimerFired(Timer<FrameLoader>*); void checkCompletedTimerFired(Timer<FrameLoader>*); void checkLoadCompleteTimerFired(Timer<FrameLoader>*); - + void cancelRedirection(bool newLoadInProgress = false); void started(); @@ -490,7 +510,7 @@ namespace WebCore { void receivedFirstData(); void updatePolicyBaseURL(); - void setPolicyBaseURL(const String&); + void setPolicyBaseURL(const KURL&); // Also not cool. void stopLoadingSubframes(); @@ -520,6 +540,7 @@ namespace WebCore { void continueLoadAfterNewWindowPolicy(const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, bool shouldContinue); static void callContinueFragmentScrollAfterNavigationPolicy(void*, const ResourceRequest&, PassRefPtr<FormState>, bool shouldContinue); void continueFragmentScrollAfterNavigationPolicy(const ResourceRequest&, bool shouldContinue); + bool shouldScrollToAnchor(bool isFormSubmission, FrameLoadType loadType, const KURL& url); void addHistoryItemForFragmentScroll(); void stopPolicyCheck(); @@ -544,14 +565,10 @@ namespace WebCore { bool shouldReloadToHandleUnreachableURL(DocumentLoader*); void handleUnimplementablePolicy(const ResourceError&); - void applyUserAgent(ResourceRequest& request); - void scheduleRedirection(ScheduledRedirection*); void startRedirectionTimer(); void stopRedirectionTimer(); - void startIconLoader(); - #if USE(LOW_BANDWIDTH_DISPLAY) // implementation of CachedResourceClient virtual void notifyFinished(CachedResource*); @@ -575,10 +592,17 @@ namespace WebCore { FrameState m_state; FrameLoadType m_loadType; + // Document loaders for the three phases of frame loading. Note that while + // a new request is being loaded, the old document loader may still be referenced. + // E.g. while a new request is in the "policy" state, the old document loader may + // be consulted in particular as it makes sense to imply certain settings on the new loader. RefPtr<DocumentLoader> m_documentLoader; RefPtr<DocumentLoader> m_provisionalDocumentLoader; RefPtr<DocumentLoader> m_policyDocumentLoader; + // This identifies the type of navigation action which prompted this load. Note + // that WebKit conveys this value as the WebActionNavigationTypeKey value + // on navigation action delegate callbacks. FrameLoadType m_policyLoadType; PolicyCheck m_policyCheck; @@ -651,7 +675,7 @@ namespace WebCore { RefPtr<HistoryItem> m_provisionalHistoryItem; bool m_didPerformFirstNavigation; - + #ifndef NDEBUG bool m_didDispatchDidCommitLoad; #endif diff --git a/WebCore/loader/FrameLoaderClient.h b/WebCore/loader/FrameLoaderClient.h index d2d31ee..6e2aba9 100644 --- a/WebCore/loader/FrameLoaderClient.h +++ b/WebCore/loader/FrameLoaderClient.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,20 +25,20 @@ * (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 FrameLoaderClient_h #define FrameLoaderClient_h #include "FrameLoaderTypes.h" -#include "StringHash.h" #include <wtf/Forward.h> #include <wtf/Platform.h> +#include <wtf/Vector.h> -#if PLATFORM(MAC) -#ifdef __OBJC__ -@class NSCachedURLResponse; -#else +typedef class _jobject* jobject; + +#if PLATFORM(MAC) && !defined(__OBJC__) class NSCachedURLResponse; -#endif +class NSView; #endif namespace WebCore { @@ -74,11 +74,10 @@ namespace WebCore { class FrameLoaderClient { public: - virtual ~FrameLoaderClient() { } + virtual ~FrameLoaderClient() { } virtual void frameLoaderDestroyed() = 0; virtual bool hasWebView() const = 0; // mainly for assertions - virtual bool hasFrameView() const = 0; // ditto virtual bool hasHTMLView() const { return true; } @@ -90,7 +89,6 @@ namespace WebCore { virtual void detachedFromParent2() = 0; virtual void detachedFromParent3() = 0; - virtual void detachedFromParent4() = 0; virtual void assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&) = 0; @@ -123,8 +121,8 @@ namespace WebCore { virtual void dispatchShow() = 0; virtual void dispatchDecidePolicyForMIMEType(FramePolicyFunction, const String& MIMEType, const ResourceRequest&) = 0; - virtual void dispatchDecidePolicyForNewWindowAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, const String& frameName) = 0; - virtual void dispatchDecidePolicyForNavigationAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&) = 0; + virtual void dispatchDecidePolicyForNewWindowAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, const String& frameName) = 0; + virtual void dispatchDecidePolicyForNavigationAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>) = 0; virtual void cancelPolicyCheck() = 0; virtual void dispatchUnableToImplementPolicy(const ResourceError&) = 0; @@ -134,7 +132,6 @@ namespace WebCore { virtual void dispatchDidLoadMainResource(DocumentLoader*) = 0; virtual void revertToProvisionalState(DocumentLoader*) = 0; virtual void setMainDocumentError(DocumentLoader*, const ResourceError&) = 0; - virtual void clearUnarchivingState(DocumentLoader*) = 0; // Maybe these should go into a ProgressTrackerClient some day virtual void willChangeEstimatedProgress() { } @@ -152,10 +149,8 @@ namespace WebCore { virtual void committedLoad(DocumentLoader*, const char*, int) = 0; virtual void finishedLoading(DocumentLoader*) = 0; - virtual void finalSetupForReplace(DocumentLoader*) = 0; - virtual void updateGlobalHistoryForStandardLoad(const KURL&) = 0; - virtual void updateGlobalHistoryForReload(const KURL&) = 0; + virtual void updateGlobalHistory(const KURL&) = 0; virtual bool shouldGoToHistoryItem(HistoryItem*) const = 0; #ifdef ANDROID_HISTORY_CLIENT virtual void dispatchDidAddHistoryItem(HistoryItem*) const = 0; @@ -170,16 +165,10 @@ namespace WebCore { virtual ResourceError cannotShowMIMETypeError(const ResourceResponse&) = 0; virtual ResourceError fileDoesNotExistError(const ResourceResponse&) = 0; + virtual ResourceError pluginWillHandleLoadError(const ResourceResponse&) = 0; virtual bool shouldFallBack(const ResourceError&) = 0; - virtual void setDefersLoading(bool) = 0; - - virtual bool willUseArchive(ResourceLoader*, const ResourceRequest&, const KURL& originalURL) const = 0; - virtual bool isArchiveLoadPending(ResourceLoader*) const = 0; - virtual void cancelPendingArchiveLoad(ResourceLoader*) = 0; - virtual void clearArchivedResources() = 0; - virtual bool canHandleRequest(const ResourceRequest&) const = 0; virtual bool canShowMIMEType(const String& MIMEType) const = 0; virtual bool representationExistsForURLScheme(const String& URLScheme) const = 0; @@ -220,6 +209,9 @@ namespace WebCore { virtual void registerForIconNotification(bool listen = true) = 0; #if PLATFORM(MAC) +#if ENABLE(MAC_JAVA_BRIDGE) + virtual jobject javaApplet(NSView*) { return 0; } +#endif virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long identifier, NSCachedURLResponse*) const = 0; #endif }; diff --git a/WebCore/loader/ImageDocument.cpp b/WebCore/loader/ImageDocument.cpp index f622a52..39b1db6 100644 --- a/WebCore/loader/ImageDocument.cpp +++ b/WebCore/loader/ImageDocument.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,6 +35,7 @@ #include "FrameView.h" #include "HTMLImageElement.h" #include "HTMLNames.h" +#include "LocalizedStrings.h" #include "MouseEvent.h" #include "Page.h" #include "SegmentedString.h" @@ -42,23 +43,19 @@ #include "Text.h" #include "XMLTokenizer.h" -#if PLATFORM(MAC) -#include "ImageDocumentMac.h" -#endif - using std::min; namespace WebCore { -using namespace EventNames; using namespace HTMLNames; class ImageEventListener : public EventListener { public: - ImageEventListener(ImageDocument* doc) : m_doc(doc) { } + static PassRefPtr<ImageEventListener> create(ImageDocument* document) { return adoptRef(new ImageEventListener(document)); } virtual void handleEvent(Event*, bool isWindowEvent); private: + ImageEventListener(ImageDocument* document) : m_doc(document) { } ImageDocument* m_doc; }; @@ -114,17 +111,23 @@ void ImageTokenizer::finish() // If this is a multipart image, make a copy of the current part, since the resource data // will be overwritten by the next part. if (m_doc->frame()->loader()->documentLoader()->isLoadingMultipartContent()) - data = new SharedBuffer(data->data(), data->size()); + data = data->copy(); cachedImage->data(data.release(), true); cachedImage->finish(); cachedImage->setResponse(m_doc->frame()->loader()->documentLoader()->response()); - - // FIXME: Need code to set the title for platforms other than Mac OS X. -#if PLATFORM(MAC) - finishImageLoad(m_doc, cachedImage); -#endif + + IntSize size = cachedImage->imageSize(m_doc->frame()->pageZoomFactor()); + if (size.width()) { + // Compute the title, we use the filename of the resource, falling + // back on the hostname if there is no path. + String fileName = m_doc->url().lastPathComponent(); + if (fileName.isEmpty()) + fileName = m_doc->url().host(); + m_doc->setTitle(imageTitle(fileName, size)); + } + m_doc->imageChanged(); } @@ -139,8 +142,8 @@ bool ImageTokenizer::isWaitingForScripts() const // -------- -ImageDocument::ImageDocument(DOMImplementation* implementation, Frame* frame) - : HTMLDocument(implementation, frame) +ImageDocument::ImageDocument(Frame* frame) + : HTMLDocument(frame) , m_imageElement(0) , m_imageSizeIsKnown(false) , m_didShrinkImage(false) @@ -170,13 +173,13 @@ void ImageDocument::createDocumentStructure() imageElement->setAttribute(styleAttr, "-webkit-user-select: none"); imageElement->setLoadManually(true); - imageElement->setSrc(url()); + imageElement->setSrc(url().string()); body->appendChild(imageElement, ec); if (shouldShrinkToFit()) { // Add event listeners - RefPtr<EventListener> listener = new ImageEventListener(this); + RefPtr<EventListener> listener = ImageEventListener::create(this); addWindowEventListener("resize", listener, false); imageElement->addEventListener("click", listener.release(), false); } @@ -189,7 +192,7 @@ float ImageDocument::scale() const if (!m_imageElement) return 1.0f; - IntSize imageSize = m_imageElement->cachedImage()->imageSize(); + IntSize imageSize = m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor()); IntSize windowSize = IntSize(frame()->view()->width(), frame()->view()->height()); float widthScale = (float)windowSize.width() / imageSize.width(); @@ -203,7 +206,7 @@ void ImageDocument::resizeImageToFit() if (!m_imageElement) return; - IntSize imageSize = m_imageElement->cachedImage()->imageSize(); + IntSize imageSize = m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor()); float scale = this->scale(); m_imageElement->setWidth(static_cast<int>(imageSize.width() * scale)); @@ -232,7 +235,7 @@ void ImageDocument::imageClicked(int x, int y) int scrollX = static_cast<int>(x / scale - (float)frame()->view()->width() / 2); int scrollY = static_cast<int>(y / scale - (float)frame()->view()->height() / 2); - frame()->view()->setContentsPos(scrollX, scrollY); + frame()->view()->setScrollPosition(IntPoint(scrollX, scrollY)); } } @@ -243,7 +246,7 @@ void ImageDocument::imageChanged() if (m_imageSizeIsKnown) return; - if (m_imageElement->cachedImage()->imageSize().isEmpty()) + if (m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor()).isEmpty()) return; m_imageSizeIsKnown = true; @@ -259,8 +262,8 @@ void ImageDocument::restoreImageSize() if (!m_imageElement || !m_imageSizeIsKnown) return; - m_imageElement->setWidth(m_imageElement->cachedImage()->imageSize().width()); - m_imageElement->setHeight(m_imageElement->cachedImage()->imageSize().height()); + m_imageElement->setWidth(m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor()).width()); + m_imageElement->setHeight(m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor()).height()); ExceptionCode ec; if (imageFitsInWindow()) @@ -276,7 +279,7 @@ bool ImageDocument::imageFitsInWindow() const if (!m_imageElement) return true; - IntSize imageSize = m_imageElement->cachedImage()->imageSize(); + IntSize imageSize = m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor()); IntSize windowSize = IntSize(frame()->view()->width(), frame()->view()->height()); return imageSize.width() <= windowSize.width() && imageSize.height() <= windowSize.height(); @@ -335,9 +338,9 @@ bool ImageDocument::shouldShrinkToFit() const void ImageEventListener::handleEvent(Event* event, bool isWindowEvent) { - if (event->type() == resizeEvent) + if (event->type() == eventNames().resizeEvent) m_doc->windowSizeChanged(); - else if (event->type() == clickEvent) { + else if (event->type() == eventNames().clickEvent) { MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); m_doc->imageClicked(mouseEvent->x(), mouseEvent->y()); } diff --git a/WebCore/loader/ImageDocument.h b/WebCore/loader/ImageDocument.h index 0cca1a7..1bc5245 100644 --- a/WebCore/loader/ImageDocument.h +++ b/WebCore/loader/ImageDocument.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,12 +33,11 @@ class ImageDocumentElement; class ImageDocument : public HTMLDocument { public: - ImageDocument(DOMImplementation*, Frame*); + static PassRefPtr<ImageDocument> create(Frame* frame) + { + return new ImageDocument(frame); + } - virtual bool isImageDocument() const { return true; } - - virtual Tokenizer* createTokenizer(); - CachedImage* cachedImage(); ImageDocumentElement* imageElement() const { return m_imageElement; } void disconnectImageElement() { m_imageElement = 0; } @@ -48,6 +47,11 @@ public: void imageClicked(int x, int y); private: + ImageDocument(Frame*); + + virtual Tokenizer* createTokenizer(); + virtual bool isImageDocument() const { return true; } + void createDocumentStructure(); void resizeImageToFit(); void restoreImageSize(); diff --git a/WebCore/loader/ImageLoader.cpp b/WebCore/loader/ImageLoader.cpp new file mode 100644 index 0000000..da159b4 --- /dev/null +++ b/WebCore/loader/ImageLoader.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "ImageLoader.h" + +#include "CSSHelper.h" +#include "CachedImage.h" +#include "DocLoader.h" +#include "Document.h" +#include "Element.h" +#include "RenderImage.h" + +namespace WebCore { + +ImageLoader::ImageLoader(Element* elt) + : m_element(elt) + , m_image(0) + , m_firedLoad(true) + , m_imageComplete(true) + , m_loadManually(false) +{ +} + +ImageLoader::~ImageLoader() +{ + if (m_image) + m_image->removeClient(this); + m_element->document()->removeImage(this); +} + +void ImageLoader::setImage(CachedImage* newImage) +{ + CachedImage* oldImage = m_image.get(); + if (newImage != oldImage) { + setLoadingImage(newImage); + m_firedLoad = true; + m_imageComplete = true; + if (newImage) + newImage->addClient(this); + if (oldImage) + oldImage->removeClient(this); + } + + if (RenderObject* renderer = element()->renderer()) { + if (!renderer->isImage()) + return; + + static_cast<RenderImage*>(renderer)->resetAnimation(); + } +} + +void ImageLoader::setLoadingImage(CachedImage* loadingImage) +{ + m_firedLoad = false; + m_imageComplete = false; + m_image = loadingImage; +} + +void ImageLoader::updateFromElement() +{ + // If we're not making renderers for the page, then don't load images. We don't want to slow + // down the raw HTML parsing case by loading images we don't intend to display. + Element* elem = element(); + Document* doc = elem->document(); + if (!doc->renderer()) + return; + + AtomicString attr = elem->getAttribute(elem->imageSourceAttributeName()); + + // Do not load any image if the 'src' attribute is missing or if it is + // an empty string referring to a local file. The latter condition is + // a quirk that preserves old behavior that Dashboard widgets + // need (<rdar://problem/5994621>). + CachedImage* newImage = 0; + if (!(attr.isNull() || attr.isEmpty() && doc->baseURI().isLocalFile())) { + if (m_loadManually) { + doc->docLoader()->setAutoLoadImages(false); + newImage = new CachedImage(sourceURI(attr)); + newImage->setLoading(true); + newImage->setDocLoader(doc->docLoader()); + doc->docLoader()->m_docResources.set(newImage->url(), newImage); + } else + newImage = doc->docLoader()->requestImage(sourceURI(attr)); + } + + CachedImage* oldImage = m_image.get(); + if (newImage != oldImage) { + setLoadingImage(newImage); + if (newImage) + newImage->addClient(this); + if (oldImage) + oldImage->removeClient(this); + } + + if (RenderObject* renderer = elem->renderer()) { + if (!renderer->isImage()) + return; + + static_cast<RenderImage*>(renderer)->resetAnimation(); + } +} + +void ImageLoader::notifyFinished(CachedResource *image) +{ + m_imageComplete = true; + + Element* elem = element(); + elem->document()->dispatchImageLoadEventSoon(this); + + if (RenderObject* renderer = elem->renderer()) { + if (!renderer->isImage()) + return; + + static_cast<RenderImage*>(renderer)->setCachedImage(m_image.get()); + } +} + +} diff --git a/WebCore/loader/ImageLoader.h b/WebCore/loader/ImageLoader.h new file mode 100644 index 0000000..39b5cc4 --- /dev/null +++ b/WebCore/loader/ImageLoader.h @@ -0,0 +1,73 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef ImageLoader_h +#define ImageLoader_h + +#include "CachedResourceClient.h" +#include "CachedResourceHandle.h" + +namespace WebCore { + +class AtomicString; +class Element; + +class ImageLoader : public CachedResourceClient { +public: + ImageLoader(Element*); + virtual ~ImageLoader(); + + void updateFromElement(); + + virtual void dispatchLoadEvent() = 0; + virtual String sourceURI(const AtomicString&) const = 0; + + Element* element() const { return m_element; } + bool imageComplete() const { return m_imageComplete; } + + CachedImage* image() const { return m_image.get(); } + void setImage(CachedImage*); + + void setLoadManually(bool loadManually) { m_loadManually = loadManually; } + + // CachedResourceClient API + virtual void notifyFinished(CachedResource*); + + bool haveFiredLoadEvent() const { return m_firedLoad; } +protected: + void setLoadingImage(CachedImage*); + + void setHaveFiredLoadEvent(bool firedLoad) { m_firedLoad = firedLoad; } + +private: + Element* m_element; + CachedResourceHandle<CachedImage> m_image; + bool m_firedLoad : 1; + bool m_imageComplete : 1; + bool m_loadManually : 1; +}; + +} + +#endif diff --git a/WebCore/loader/MainResourceLoader.cpp b/WebCore/loader/MainResourceLoader.cpp index c591c2f..079bd79 100644 --- a/WebCore/loader/MainResourceLoader.cpp +++ b/WebCore/loader/MainResourceLoader.cpp @@ -29,13 +29,20 @@ #include "config.h" #include "MainResourceLoader.h" +#if ENABLE(OFFLINE_WEB_APPLICATIONS) +#include "ApplicationCache.h" +#include "ApplicationCacheGroup.h" +#include "ApplicationCacheResource.h" +#endif #include "DocumentLoader.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" #include "HTMLFormElement.h" +#include "Page.h" #include "ResourceError.h" #include "ResourceHandle.h" +#include "Settings.h" // FIXME: More that is in common with SubresourceLoader should move up into ResourceLoader. @@ -55,7 +62,7 @@ MainResourceLoader::~MainResourceLoader() PassRefPtr<MainResourceLoader> MainResourceLoader::create(Frame* frame) { - return new MainResourceLoader(frame); + return adoptRef(new MainResourceLoader(frame)); } void MainResourceLoader::receivedError(const ResourceError& error) @@ -162,10 +169,7 @@ void MainResourceLoader::willSendRequest(ResourceRequest& newRequest, const Reso if (newRequest.cachePolicy() == UseProtocolCachePolicy && isPostOrRedirectAfterPost(newRequest, redirectResponse)) newRequest.setCachePolicy(ReloadIgnoringCacheData); - if (!newRequest.isNull()) { - ResourceLoader::willSendRequest(newRequest, redirectResponse); - setRequest(newRequest); - } + ResourceLoader::willSendRequest(newRequest, redirectResponse); // Don't set this on the first request. It is set when the main load was started. m_documentLoader->setRequest(newRequest); @@ -205,7 +209,9 @@ void MainResourceLoader::continueAfterContentPolicy(PolicyAction contentPolicy, case PolicyDownload: frameLoader()->client()->download(m_handle.get(), request(), m_handle.get()->request(), r); - receivedError(interruptionForPolicyChangeError()); + // It might have gone missing + if (frameLoader()) + receivedError(interruptionForPolicyChangeError()); return; case PolicyIgnore: @@ -262,7 +268,11 @@ void MainResourceLoader::continueAfterContentPolicy(PolicyAction policy) void MainResourceLoader::didReceiveResponse(const ResourceResponse& r) { + // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. + // See <rdar://problem/6304600> for more details. +#if !PLATFORM(CF) ASSERT(shouldLoadAsEmptyDocument(r.url()) || !defersLoading()); +#endif if (m_loadingMultipartContent) { frameLoader()->setupForReplaceByMIMEType(r.mimeType()); @@ -290,7 +300,12 @@ void MainResourceLoader::didReceiveData(const char* data, int length, long long { ASSERT(data); ASSERT(length != 0); + + // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. + // See <rdar://problem/6304600> for more details. +#if !PLATFORM(CF) ASSERT(!defersLoading()); +#endif // The additional processing can do anything including possibly removing the last // reference to this object; one example of this is 3266216. @@ -301,20 +316,41 @@ void MainResourceLoader::didReceiveData(const char* data, int length, long long void MainResourceLoader::didFinishLoading() { + // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. + // See <rdar://problem/6304600> for more details. +#if !PLATFORM(CF) ASSERT(shouldLoadAsEmptyDocument(frameLoader()->activeDocumentLoader()->url()) || !defersLoading()); - +#endif + // The additional processing can do anything including possibly removing the last // reference to this object. RefPtr<MainResourceLoader> protect(this); +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + RefPtr<DocumentLoader> dl = documentLoader(); +#endif + frameLoader()->finishedLoading(); ResourceLoader::didFinishLoading(); + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + ApplicationCacheGroup* group = dl->candidateApplicationCacheGroup(); + if (!group && dl->applicationCache() && !dl->mainResourceApplicationCache()) + group = dl->applicationCache()->group(); + + if (group) + group->finishedLoadingMainResource(dl.get()); +#endif } void MainResourceLoader::didFail(const ResourceError& error) { + // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. + // See <rdar://problem/6304600> for more details. +#if !PLATFORM(CF) ASSERT(!defersLoading()); - +#endif + receivedError(error); } @@ -385,12 +421,36 @@ bool MainResourceLoader::loadNow(ResourceRequest& r) return false; } -bool MainResourceLoader::load(const ResourceRequest& r, const SubstituteData& substituteData) +bool MainResourceLoader::load(const ResourceRequest& r, const SubstituteData& substituteData) { ASSERT(!m_handle); m_substituteData = substituteData; +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + // Check if this request should be loaded from the application cache + if (!m_substituteData.isValid() && frameLoader()->frame()->settings() && frameLoader()->frame()->settings()->offlineWebApplicationCacheEnabled()) { + ASSERT(!m_applicationCache); + + if (Page* page = frameLoader()->frame()->page()) { + if (frameLoader()->frame() == page->mainFrame()) + m_applicationCache = ApplicationCacheGroup::cacheForMainRequest(r, m_documentLoader.get()); + else + m_applicationCache = frameLoader()->documentLoader()->topLevelApplicationCache(); + } + + if (m_applicationCache) { + // Get the resource from the application cache. + // FIXME: If the resource does not exist, the load should fail. + if (ApplicationCacheResource* resource = m_applicationCache->resourceForRequest(r)) { + m_substituteData = SubstituteData(resource->data(), + resource->response().mimeType(), + resource->response().textEncodingName(), KURL()); + } + } + } +#endif + ResourceRequest request(r); bool defer = defersLoading(); if (defer) { diff --git a/WebCore/loader/MainResourceLoader.h b/WebCore/loader/MainResourceLoader.h index 5d1e42e..6c69c1f 100644 --- a/WebCore/loader/MainResourceLoader.h +++ b/WebCore/loader/MainResourceLoader.h @@ -34,6 +34,9 @@ namespace WebCore { +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + class ApplicationCache; +#endif class FormState; class ResourceRequest; @@ -57,6 +60,10 @@ namespace WebCore { bool isLoadingMultipartContent() const { return m_loadingMultipartContent; } +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + ApplicationCache* applicationCache() const { return m_applicationCache.get(); } +#endif + private: MainResourceLoader(Frame*); @@ -85,6 +92,11 @@ namespace WebCore { SubstituteData m_substituteData; Timer<MainResourceLoader> m_dataLoadTimer; +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + // The application cache that the main resource was loaded from (if any). + RefPtr<ApplicationCache> m_applicationCache; +#endif + bool m_loadingMultipartContent; bool m_waitingForContentPolicy; }; diff --git a/WebCore/loader/MediaDocument.cpp b/WebCore/loader/MediaDocument.cpp new file mode 100644 index 0000000..5689457 --- /dev/null +++ b/WebCore/loader/MediaDocument.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#include "config.h" + +#if ENABLE(VIDEO) +#include "MediaDocument.h" + +#include "DocumentLoader.h" +#include "Element.h" +#include "Event.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameLoaderClient.h" +#include "HTMLEmbedElement.h" +#include "HTMLNames.h" +#include "HTMLVideoElement.h" +#include "MainResourceLoader.h" +#include "Page.h" +#include "SegmentedString.h" +#include "Settings.h" +#include "Text.h" +#include "XMLTokenizer.h" + +namespace WebCore { + +using namespace HTMLNames; + +class MediaTokenizer : public Tokenizer { +public: + MediaTokenizer(Document* doc) : m_doc(doc), m_mediaElement(0) {} + + virtual bool write(const SegmentedString&, bool appendData); + virtual void stopParsing(); + virtual void finish(); + virtual bool isWaitingForScripts() const; + + virtual bool wantsRawData() const { return true; } + virtual bool writeRawData(const char* data, int len); + + void createDocumentStructure(); +private: + Document* m_doc; + HTMLMediaElement* m_mediaElement; +}; + +bool MediaTokenizer::write(const SegmentedString& s, bool appendData) +{ + ASSERT_NOT_REACHED(); + return false; +} + +void MediaTokenizer::createDocumentStructure() +{ + ExceptionCode ec; + RefPtr<Element> rootElement = m_doc->createElementNS(xhtmlNamespaceURI, "html", ec); + m_doc->appendChild(rootElement, ec); + + RefPtr<Element> body = m_doc->createElementNS(xhtmlNamespaceURI, "body", ec); + body->setAttribute(styleAttr, "background-color: rgb(38,38,38);"); + + rootElement->appendChild(body, ec); + + RefPtr<Element> mediaElement = m_doc->createElementNS(xhtmlNamespaceURI, "video", ec); + + m_mediaElement = static_cast<HTMLVideoElement*>(mediaElement.get()); + m_mediaElement->setAttribute(controlsAttr, ""); + m_mediaElement->setAttribute(autoplayAttr, ""); + m_mediaElement->setAttribute(styleAttr, "margin: auto; position: absolute; top: 0; right: 0; bottom: 0; left: 0;"); + + m_mediaElement->setAttribute(nameAttr, "media"); + m_mediaElement->setSrc(m_doc->url()); + + body->appendChild(mediaElement, ec); + + Frame* frame = m_doc->frame(); + if (!frame) + return; + + frame->loader()->activeDocumentLoader()->mainResourceLoader()->setShouldBufferData(false); +} + +bool MediaTokenizer::writeRawData(const char* data, int len) +{ + ASSERT(!m_mediaElement); + if (m_mediaElement) + return false; + + createDocumentStructure(); + finish(); + return false; +} + +void MediaTokenizer::stopParsing() +{ + Tokenizer::stopParsing(); +} + +void MediaTokenizer::finish() +{ + if (!m_parserStopped) + m_doc->finishedParsing(); +} + +bool MediaTokenizer::isWaitingForScripts() const +{ + // A media document is never waiting for scripts + return false; +} + +MediaDocument::MediaDocument(Frame* frame) + : HTMLDocument(frame) +{ + setParseMode(Compat); +} + +Tokenizer* MediaDocument::createTokenizer() +{ + return new MediaTokenizer(this); +} + +void MediaDocument::defaultEventHandler(Event* event) +{ + // Match the default Quicktime plugin behavior to allow + // clicking and double-clicking to pause and play the media. + EventTargetNode* targetNode = event->target()->toNode(); + if (targetNode && targetNode->hasTagName(videoTag)) { + HTMLVideoElement* video = static_cast<HTMLVideoElement*>(targetNode); + ExceptionCode ec; + if (event->type() == eventNames().clickEvent) { + if (!video->canPlay()) { + video->pause(ec); + event->setDefaultHandled(); + } + } else if (event->type() == eventNames().dblclickEvent) { + if (video->canPlay()) { + video->play(ec); + event->setDefaultHandled(); + } + } + } +} + +} +#endif diff --git a/WebCore/loader/mac/ImageDocumentMac.h b/WebCore/loader/MediaDocument.h index 3c7c509..e5167e4 100644 --- a/WebCore/loader/mac/ImageDocumentMac.h +++ b/WebCore/loader/MediaDocument.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,28 +10,45 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 ImageDocumentMac_h -#define ImageDocumentMac_h +#ifndef MediaDocument_h +#define MediaDocument_h + +#if ENABLE(VIDEO) + +#include "HTMLDocument.h" namespace WebCore { - class CachedImage; - class Document; +class MediaDocument : public HTMLDocument { +public: + static PassRefPtr<MediaDocument> create(Frame* frame) + { + return new MediaDocument(frame); + } - void finishImageLoad(Document*, CachedImage*); + virtual void defaultEventHandler(Event*); +private: + MediaDocument(Frame*); + + virtual bool isMediaDocument() const { return true; } + virtual Tokenizer* createTokenizer(); +}; + } #endif +#endif diff --git a/WebCore/loader/NetscapePlugInStreamLoader.cpp b/WebCore/loader/NetscapePlugInStreamLoader.cpp index 47c9599..e12ed07 100644 --- a/WebCore/loader/NetscapePlugInStreamLoader.cpp +++ b/WebCore/loader/NetscapePlugInStreamLoader.cpp @@ -46,7 +46,7 @@ NetscapePlugInStreamLoader::~NetscapePlugInStreamLoader() PassRefPtr<NetscapePlugInStreamLoader> NetscapePlugInStreamLoader::create(Frame* frame, NetscapePlugInStreamLoaderClient* client) { - return new NetscapePlugInStreamLoader(frame, client); + return adoptRef(new NetscapePlugInStreamLoader(frame, client)); } bool NetscapePlugInStreamLoader::isDone() const @@ -76,7 +76,13 @@ void NetscapePlugInStreamLoader::didReceiveResponse(const ResourceResponse& resp if (!m_client) return; - if (response.isHTTP() && (response.httpStatusCode() < 100 || response.httpStatusCode() >= 400)) + if (!response.isHTTP()) + return; + + if (m_client->wantsAllStreams()) + return; + + if (response.httpStatusCode() < 100 || response.httpStatusCode() >= 400) didCancel(frameLoader()->fileDoesNotExistError(response)); } @@ -111,8 +117,13 @@ void NetscapePlugInStreamLoader::didCancel(const ResourceError& error) { RefPtr<NetscapePlugInStreamLoader> protect(this); - m_documentLoader->removePlugInStreamLoader(this); m_client->didFail(this, error); + + // We need to remove the stream loader after the call to didFail, since didFail can + // spawn a new run loop and if the loader has been removed it won't be deferred when + // the document loader is asked to defer loading. + m_documentLoader->removePlugInStreamLoader(this); + ResourceLoader::didCancel(error); } diff --git a/WebCore/loader/NetscapePlugInStreamLoader.h b/WebCore/loader/NetscapePlugInStreamLoader.h index 19b0428..092c6fc 100644 --- a/WebCore/loader/NetscapePlugInStreamLoader.h +++ b/WebCore/loader/NetscapePlugInStreamLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,15 +30,19 @@ #include <wtf/Forward.h> namespace WebCore { + class NetscapePlugInStreamLoader; class NetscapePlugInStreamLoaderClient { public: - virtual ~NetscapePlugInStreamLoaderClient() { } virtual void didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse&) = 0; virtual void didReceiveData(NetscapePlugInStreamLoader*, const char*, int) = 0; virtual void didFail(NetscapePlugInStreamLoader*, const ResourceError&) = 0; virtual void didFinishLoading(NetscapePlugInStreamLoader*) { } + virtual bool wantsAllStreams() const { return false; } + + protected: + virtual ~NetscapePlugInStreamLoaderClient() { } }; class NetscapePlugInStreamLoader : public ResourceLoader { @@ -48,6 +52,7 @@ namespace WebCore { bool isDone() const; + private: virtual void didReceiveResponse(const ResourceResponse&); virtual void didReceiveData(const char*, int, long long lengthReceived, bool allAtOnce); virtual void didFinishLoading(); @@ -55,7 +60,6 @@ namespace WebCore { virtual void releaseResources(); - private: NetscapePlugInStreamLoader(Frame*, NetscapePlugInStreamLoaderClient*); virtual void didCancel(const ResourceError& error); diff --git a/WebCore/loader/PluginDocument.cpp b/WebCore/loader/PluginDocument.cpp index 492c0f6..8be6ae2 100644 --- a/WebCore/loader/PluginDocument.cpp +++ b/WebCore/loader/PluginDocument.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -88,7 +88,7 @@ void PluginTokenizer::createDocumentStructure() m_embedElement->setAttribute(heightAttr, "100%"); m_embedElement->setAttribute(nameAttr, "plugin"); - m_embedElement->setSrc(m_doc->url()); + m_embedElement->setSrc(m_doc->url().string()); m_embedElement->setType(m_doc->frame()->loader()->responseMIMEType()); body->appendChild(embedElement, ec); @@ -96,28 +96,26 @@ void PluginTokenizer::createDocumentStructure() bool PluginTokenizer::writeRawData(const char* data, int len) { - if (!m_embedElement) { - createDocumentStructure(); + ASSERT(!m_embedElement); + if (m_embedElement) + return false; + + createDocumentStructure(); - if (Frame* frame = m_doc->frame()) { - Settings* settings = frame->settings(); - if (settings && settings->arePluginsEnabled()) { - m_doc->updateLayout(); - - if (RenderWidget* renderer = static_cast<RenderWidget*>(m_embedElement->renderer())) { - frame->loader()->client()->redirectDataToPlugin(renderer->widget()); - frame->loader()->activeDocumentLoader()->mainResourceLoader()->setShouldBufferData(false); - } - - finish(); + if (Frame* frame = m_doc->frame()) { + Settings* settings = frame->settings(); + if (settings && settings->arePluginsEnabled()) { + m_doc->updateLayout(); + + if (RenderWidget* renderer = static_cast<RenderWidget*>(m_embedElement->renderer())) { + frame->loader()->client()->redirectDataToPlugin(renderer->widget()); + frame->loader()->activeDocumentLoader()->mainResourceLoader()->setShouldBufferData(false); } - } - return false; + finish(); + } } - - ASSERT_NOT_REACHED(); - + return false; } @@ -138,8 +136,8 @@ bool PluginTokenizer::isWaitingForScripts() const return false; } -PluginDocument::PluginDocument(DOMImplementation* implementation, Frame* frame) - : HTMLDocument(implementation, frame) +PluginDocument::PluginDocument(Frame* frame) + : HTMLDocument(frame) { setParseMode(Compat); } diff --git a/WebCore/loader/PluginDocument.h b/WebCore/loader/PluginDocument.h index 9e62753..35e4038 100644 --- a/WebCore/loader/PluginDocument.h +++ b/WebCore/loader/PluginDocument.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -21,6 +21,7 @@ * (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 PluginDocument_h #define PluginDocument_h @@ -28,19 +29,20 @@ namespace WebCore { -class DOMImplementation; -class FrameView; - -class PluginDocument : public HTMLDocument -{ +class PluginDocument : public HTMLDocument { public: - PluginDocument(DOMImplementation*, Frame*); + static PassRefPtr<PluginDocument> create(Frame* frame) + { + return new PluginDocument(frame); + } + +private: + PluginDocument(Frame*); virtual bool isPluginDocument() const { return true; } - virtual Tokenizer* createTokenizer(); }; } -#endif // ImageDocument_h +#endif // PluginDocument_h diff --git a/WebCore/loader/ResourceLoader.cpp b/WebCore/loader/ResourceLoader.cpp index ea96e34..a6f90b3 100644 --- a/WebCore/loader/ResourceLoader.cpp +++ b/WebCore/loader/ResourceLoader.cpp @@ -54,16 +54,19 @@ PassRefPtr<SharedBuffer> ResourceLoader::resourceData() } ResourceLoader::ResourceLoader(Frame* frame, bool sendResourceLoadCallbacks, bool shouldContentSniff) - : m_reachedTerminalState(false) + : m_frame(frame) + , m_documentLoader(frame->loader()->activeDocumentLoader()) + , m_identifier(0) + , m_reachedTerminalState(false) , m_cancelled(false) , m_calledDidFinishLoad(false) , m_sendResourceLoadCallbacks(sendResourceLoadCallbacks) , m_shouldContentSniff(shouldContentSniff) , m_shouldBufferData(true) - , m_frame(frame) - , m_documentLoader(frame->loader()->activeDocumentLoader()) - , m_identifier(0) , m_defersLoading(frame->page()->defersLoading()) +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + , m_wasLoadedFromApplicationCache(false) +#endif { } @@ -106,9 +109,7 @@ bool ResourceLoader::load(const ResourceRequest& r) { ASSERT(!m_handle); ASSERT(m_deferredRequest.isNull()); - ASSERT(!frameLoader()->isArchiveLoadPending(this)); - - m_originalURL = r.url(); + ASSERT(!m_documentLoader->isSubstituteLoadPending(this)); ResourceRequest clientRequest(r); willSendRequest(clientRequest, ResourceResponse()); @@ -117,9 +118,18 @@ bool ResourceLoader::load(const ResourceRequest& r) return false; } - if (frameLoader()->willUseArchive(this, clientRequest, m_originalURL)) +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size + if (m_documentLoader->scheduleArchiveLoad(this, clientRequest, r.url())) return true; +#endif +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (m_documentLoader->scheduleApplicationCacheLoad(this, clientRequest, r.url())) { + m_wasLoadedFromApplicationCache = true; + return true; + } +#endif + if (m_defersLoading) { m_deferredRequest = clientRequest; return true; @@ -165,7 +175,7 @@ void ResourceLoader::addData(const char* data, int length, bool allAtOnce) return; if (allAtOnce) { - m_resourceData = new SharedBuffer(data, length); + m_resourceData = SharedBuffer::create(data, length); return; } @@ -175,7 +185,7 @@ void ResourceLoader::addData(const char* data, int length, bool allAtOnce) m_resourceData->append(data, length); } else { if (!m_resourceData) - m_resourceData = new SharedBuffer(data, length); + m_resourceData = SharedBuffer::create(data, length); else m_resourceData->append(data, length); } @@ -207,6 +217,10 @@ void ResourceLoader::willSendRequest(ResourceRequest& request, const ResourceRes m_request = request; } +void ResourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) +{ +} + void ResourceLoader::didReceiveResponse(const ResourceResponse& r) { ASSERT(!m_reachedTerminalState); @@ -217,6 +231,9 @@ void ResourceLoader::didReceiveResponse(const ResourceResponse& r) m_response = r; + if (FormData* data = m_request.httpBody()) + data->removeGeneratedFilesIfNeeded(); + if (m_sendResourceLoadCallbacks) frameLoader()->didReceiveResponse(this, m_response); } @@ -247,7 +264,7 @@ void ResourceLoader::willStopBufferingData(const char* data, int length) return; ASSERT(!m_resourceData); - m_resourceData = new SharedBuffer(data, length); + m_resourceData = SharedBuffer::create(data, length); } void ResourceLoader::didFinishLoading() @@ -285,22 +302,23 @@ void ResourceLoader::didFail(const ResourceError& error) // anything including possibly derefing this; one example of this is Radar 3266216. RefPtr<ResourceLoader> protector(this); + if (FormData* data = m_request.httpBody()) + data->removeGeneratedFilesIfNeeded(); + if (m_sendResourceLoadCallbacks && !m_calledDidFinishLoad) frameLoader()->didFailToLoad(this, error); releaseResources(); } -void ResourceLoader::wasBlocked() -{ - didFail(blockedError()); -} - void ResourceLoader::didCancel(const ResourceError& error) { ASSERT(!m_cancelled); ASSERT(!m_reachedTerminalState); + if (FormData* data = m_request.httpBody()) + data->removeGeneratedFilesIfNeeded(); + // This flag prevents bad behavior when loads that finish cause the // load itself to be cancelled (which could happen with a javascript that // changes the window location). This is used to prevent both the body @@ -311,7 +329,7 @@ void ResourceLoader::didCancel(const ResourceError& error) if (m_handle) m_handle->clearAuthentication(); - frameLoader()->cancelPendingArchiveLoad(this); + m_documentLoader->cancelPendingSubstituteLoad(this); if (m_handle) { m_handle->cancel(); m_handle = 0; @@ -352,11 +370,21 @@ ResourceError ResourceLoader::blockedError() return frameLoader()->blockedError(m_request); } +ResourceError ResourceLoader::cannotShowURLError() +{ + return frameLoader()->cannotShowURLError(m_request); +} + void ResourceLoader::willSendRequest(ResourceHandle*, ResourceRequest& request, const ResourceResponse& redirectResponse) { willSendRequest(request, redirectResponse); } +void ResourceLoader::didSendData(ResourceHandle*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) +{ + didSendData(bytesSent, totalBytesToBeSent); +} + void ResourceLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response) { didReceiveResponse(response); @@ -379,7 +407,12 @@ void ResourceLoader::didFail(ResourceHandle*, const ResourceError& error) void ResourceLoader::wasBlocked(ResourceHandle*) { - wasBlocked(); + didFail(blockedError()); +} + +void ResourceLoader::cannotShowURL(ResourceHandle*) +{ + didFail(cannotShowURLError()); } void ResourceLoader::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge) diff --git a/WebCore/loader/ResourceLoader.h b/WebCore/loader/ResourceLoader.h index 3f1784f..722f5fc 100644 --- a/WebCore/loader/ResourceLoader.h +++ b/WebCore/loader/ResourceLoader.h @@ -32,7 +32,6 @@ #include "ResourceHandleClient.h" #include "ResourceRequest.h" #include "ResourceResponse.h" -#include "ResourceLoader.h" #include <wtf/RefCounted.h> #include "AuthenticationChallenge.h" #include "KURL.h" @@ -61,6 +60,7 @@ namespace WebCore { virtual void cancel(const ResourceError&); ResourceError cancelledError(); ResourceError blockedError(); + ResourceError cannotShowURLError(); virtual void setDefersLoading(bool); @@ -75,24 +75,26 @@ namespace WebCore { void clearResourceData(); virtual void willSendRequest(ResourceRequest&, const ResourceResponse& redirectResponse); + virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent); virtual void didReceiveResponse(const ResourceResponse&); virtual void didReceiveData(const char*, int, long long lengthReceived, bool allAtOnce); void willStopBufferingData(const char*, int); virtual void didFinishLoading(); virtual void didFail(const ResourceError&); - virtual void wasBlocked(); virtual void didReceiveAuthenticationChallenge(const AuthenticationChallenge&); void didCancelAuthenticationChallenge(const AuthenticationChallenge&); virtual void receivedCancellation(const AuthenticationChallenge&); // ResourceHandleClient - virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& redirectResponse); + virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& redirectResponse); + virtual void didSendData(ResourceHandle*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent); virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived); virtual void didFinishLoading(ResourceHandle*); virtual void didFail(ResourceHandle*, const ResourceError&); virtual void wasBlocked(ResourceHandle*); + virtual void cannotShowURL(ResourceHandle*); virtual void willStopBufferingData(ResourceHandle*, const char* data, int length) { willStopBufferingData(data, length); } virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge) { didReceiveAuthenticationChallenge(challenge); } virtual void didCancelAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge) { didCancelAuthenticationChallenge(challenge); } @@ -114,15 +116,20 @@ namespace WebCore { void didFinishLoadingOnePart(); const ResourceRequest& request() const { return m_request; } - void setRequest(const ResourceRequest& request) { m_request = request; } bool reachedTerminalState() const { return m_reachedTerminalState; } bool cancelled() const { return m_cancelled; } bool defersLoading() const { return m_defersLoading; } RefPtr<ResourceHandle> m_handle; - + RefPtr<Frame> m_frame; + RefPtr<DocumentLoader> m_documentLoader; + ResourceResponse m_response; + private: ResourceRequest m_request; + RefPtr<SharedBuffer> m_resourceData; + + unsigned long m_identifier; bool m_reachedTerminalState; bool m_cancelled; @@ -131,16 +138,10 @@ namespace WebCore { bool m_sendResourceLoadCallbacks; bool m_shouldContentSniff; bool m_shouldBufferData; -protected: - // FIXME: Once everything is made cross platform, these can be private instead of protected - RefPtr<Frame> m_frame; - RefPtr<DocumentLoader> m_documentLoader; - ResourceResponse m_response; - unsigned long m_identifier; - - KURL m_originalURL; - RefPtr<SharedBuffer> m_resourceData; bool m_defersLoading; +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + bool m_wasLoadedFromApplicationCache; +#endif ResourceRequest m_deferredRequest; }; diff --git a/WebCore/loader/SubresourceLoader.cpp b/WebCore/loader/SubresourceLoader.cpp index 44f8548..bff48b2 100644 --- a/WebCore/loader/SubresourceLoader.cpp +++ b/WebCore/loader/SubresourceLoader.cpp @@ -38,23 +38,12 @@ #include "ResourceRequest.h" #include "SubresourceLoaderClient.h" #include "SharedBuffer.h" +#include <wtf/RefCountedLeakCounter.h> namespace WebCore { -#ifndef NDEBUG -WTFLogChannel LogWebCoreSubresourceLoaderLeaks = { 0x00000000, "", WTFLogChannelOn }; - -struct SubresourceLoaderCounter { - static unsigned count; - - ~SubresourceLoaderCounter() - { - if (count) - LOG(WebCoreSubresourceLoaderLeaks, "LEAK: %u SubresourceLoader\n", count); - } -}; -unsigned SubresourceLoaderCounter::count = 0; -static SubresourceLoaderCounter subresourceLoaderCounter; +#ifndef NDEBUG +static WTF::RefCountedLeakCounter subresourceLoaderCounter("SubresourceLoader"); #endif SubresourceLoader::SubresourceLoader(Frame* frame, SubresourceLoaderClient* client, bool sendResourceLoadCallbacks, bool shouldContentSniff) @@ -63,7 +52,7 @@ SubresourceLoader::SubresourceLoader(Frame* frame, SubresourceLoaderClient* clie , m_loadingMultipartContent(false) { #ifndef NDEBUG - ++SubresourceLoaderCounter::count; + subresourceLoaderCounter.increment(); #endif m_documentLoader->addSubresourceLoader(this); } @@ -71,7 +60,7 @@ SubresourceLoader::SubresourceLoader(Frame* frame, SubresourceLoaderClient* clie SubresourceLoader::~SubresourceLoader() { #ifndef NDEBUG - --SubresourceLoaderCounter::count; + subresourceLoaderCounter.decrement(); #endif } @@ -95,8 +84,8 @@ PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, Subresourc if (!skipCanLoadCheck && FrameLoader::restrictAccessToLocal() - && !FrameLoader::canLoad(request.url(), frame->document())) { - FrameLoader::reportLocalLoadFailed(frame->page(), request.url().string()); + && !FrameLoader::canLoad(request.url(), String(), frame->document())) { + FrameLoader::reportLocalLoadFailed(frame, request.url().string()); return 0; } @@ -104,6 +93,7 @@ PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, Subresourc newRequest.clearHTTPReferrer(); else if (!request.httpReferrer()) newRequest.setHTTPReferrer(fl->outgoingReferrer()); + FrameLoader::addHTTPOriginIfNeeded(newRequest, fl->outgoingOrigin()); // Use the original request's cache policy for two reasons: // 1. For POST requests, we mutate the cache policy for the main resource, @@ -118,7 +108,7 @@ PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, Subresourc fl->addExtraFieldsToRequest(newRequest, false, false); - RefPtr<SubresourceLoader> subloader(new SubresourceLoader(frame, client, sendResourceLoadCallbacks, shouldContentSniff)); + RefPtr<SubresourceLoader> subloader(adoptRef(new SubresourceLoader(frame, client, sendResourceLoadCallbacks, shouldContentSniff))); if (!subloader->load(newRequest)) return 0; @@ -127,11 +117,22 @@ PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, Subresourc void SubresourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse) { + // Store the previous URL because the call to ResourceLoader::willSendRequest will modify it. + KURL previousURL = request().url(); + ResourceLoader::willSendRequest(newRequest, redirectResponse); - if (!newRequest.isNull() && m_originalURL != newRequest.url() && m_client) + if (!previousURL.isNull() && !newRequest.isNull() && previousURL != newRequest.url() && m_client) m_client->willSendRequest(this, newRequest, redirectResponse); } +void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) +{ + RefPtr<SubresourceLoader> protect(this); + + if (m_client) + m_client->didSendData(this, bytesSent, totalBytesToBeSent); +} + void SubresourceLoader::didReceiveResponse(const ResourceResponse& r) { ASSERT(!r.isNull()); diff --git a/WebCore/loader/SubresourceLoader.h b/WebCore/loader/SubresourceLoader.h index 1811c30..b5bd34a 100644 --- a/WebCore/loader/SubresourceLoader.h +++ b/WebCore/loader/SubresourceLoader.h @@ -33,12 +33,6 @@ #include "ResourceLoader.h" #include <wtf/PassRefPtr.h> -#ifndef __OBJC__ -class NSArray; -class NSDictionary; -class NSMutableURLRequest; -#endif - namespace WebCore { class FormData; @@ -56,12 +50,15 @@ namespace WebCore { virtual bool load(const ResourceRequest&); virtual void willSendRequest(ResourceRequest&, const ResourceResponse& redirectResponse); + virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent); virtual void didReceiveResponse(const ResourceResponse&); virtual void didReceiveData(const char*, int, long long lengthReceived, bool allAtOnce); virtual void didFinishLoading(); virtual void didFail(const ResourceError&); virtual void didReceiveAuthenticationChallenge(const AuthenticationChallenge&); virtual void receivedCancellation(const AuthenticationChallenge&); + + void clearClient() { m_client = 0; } private: SubresourceLoader(Frame*, SubresourceLoaderClient*, bool sendResourceLoadCallbacks, bool shouldContentSniff); diff --git a/WebCore/loader/SubresourceLoaderClient.h b/WebCore/loader/SubresourceLoaderClient.h index 8abe169..d2b9a12 100644 --- a/WebCore/loader/SubresourceLoaderClient.h +++ b/WebCore/loader/SubresourceLoaderClient.h @@ -40,9 +40,10 @@ class SubresourceLoader; class SubresourceLoaderClient { public: virtual ~SubresourceLoaderClient() { } - + // request may be modified virtual void willSendRequest(SubresourceLoader*, ResourceRequest&, const ResourceResponse& redirectResponse) { } + virtual void didSendData(SubresourceLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) { } virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&) { } virtual void didReceiveData(SubresourceLoader*, const char*, int) { } @@ -54,6 +55,6 @@ public: }; -} +} // namespace WebCore -#endif +#endif // SubresourceLoaderClient_h diff --git a/WebCore/loader/SubstituteData.h b/WebCore/loader/SubstituteData.h index d37da56..0b87b62 100644 --- a/WebCore/loader/SubstituteData.h +++ b/WebCore/loader/SubstituteData.h @@ -1,4 +1,3 @@ -// -*- mode: c++; c-basic-offset: 4 -*- /* * Copyright (C) 2007 Apple Inc. All rights reserved. * diff --git a/WebCore/loader/SubstituteResource.h b/WebCore/loader/SubstituteResource.h new file mode 100644 index 0000000..15cbc6f --- /dev/null +++ b/WebCore/loader/SubstituteResource.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 SubstituteResource_h +#define SubstituteResource_h + +#include <wtf/RefCounted.h> + +#include "KURL.h" +#include "ResourceResponse.h" +#include "SharedBuffer.h" + +#include <wtf/RefPtr.h> + +namespace WebCore { + +class SubstituteResource : public RefCounted<SubstituteResource> { +public: + virtual ~SubstituteResource() { } + + const KURL& url() const { return m_url; } + const ResourceResponse& response() const { return m_response; } + SharedBuffer* data() const { return m_data.get(); } + +protected: + SubstituteResource(const KURL& url, const ResourceResponse& response, PassRefPtr<SharedBuffer> data) + : m_url(url) + , m_response(response) + , m_data(data) + { + ASSERT(m_data); + } + +private: + KURL m_url; + ResourceResponse m_response; + RefPtr<SharedBuffer> m_data; +}; + +} + +#endif // SubstituteResource_h diff --git a/WebCore/loader/TextDocument.cpp b/WebCore/loader/TextDocument.cpp index b5124b4..6b06ede 100644 --- a/WebCore/loader/TextDocument.cpp +++ b/WebCore/loader/TextDocument.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -38,6 +38,38 @@ namespace WebCore { using namespace HTMLNames; +class TextTokenizer : public Tokenizer { +public: + TextTokenizer(Document*); + TextTokenizer(HTMLViewSourceDocument*); + + virtual bool write(const SegmentedString&, bool appendData); + virtual void finish(); + virtual bool isWaitingForScripts() const; + + inline void checkBuffer(int len = 10) + { + if ((m_dest - m_buffer) > m_size - len) { + // Enlarge buffer + int newSize = std::max(m_size * 2, m_size + len); + int oldOffset = m_dest - m_buffer; + m_buffer = static_cast<UChar*>(fastRealloc(m_buffer, newSize * sizeof(UChar))); + m_dest = m_buffer + oldOffset; + m_size = newSize; + } + } + +private: + Document* m_doc; + Element* m_preElement; + + bool m_skipLF; + + int m_size; + UChar* m_buffer; + UChar* m_dest; +}; + TextTokenizer::TextTokenizer(Document* doc) : m_doc(doc) , m_preElement(0) @@ -137,8 +169,8 @@ bool TextTokenizer::isWaitingForScripts() const return false; } -TextDocument::TextDocument(DOMImplementation* implementation, Frame* frame) - : HTMLDocument(implementation, frame) +TextDocument::TextDocument(Frame* frame) + : HTMLDocument(frame) { } @@ -147,4 +179,9 @@ Tokenizer* TextDocument::createTokenizer() return new TextTokenizer(this); } +Tokenizer* createTextTokenizer(HTMLViewSourceDocument* document) +{ + return new TextTokenizer(document); +} + } diff --git a/WebCore/loader/TextDocument.h b/WebCore/loader/TextDocument.h index 0c7e60b..c67fea5 100644 --- a/WebCore/loader/TextDocument.h +++ b/WebCore/loader/TextDocument.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -21,58 +21,31 @@ * (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 TextDocument_h #define TextDocument_h #include "HTMLDocument.h" -#include "Tokenizer.h" namespace WebCore { -class DOMImplementation; -class FrameView; class HTMLViewSourceDocument; -class TextTokenizer : public Tokenizer { +class TextDocument : public HTMLDocument { public: - TextTokenizer(Document* doc); - TextTokenizer(HTMLViewSourceDocument* doc); - - virtual bool write(const SegmentedString&, bool appendData); - virtual void finish(); - virtual bool isWaitingForScripts() const; - - inline void checkBuffer(int len = 10) + static PassRefPtr<TextDocument> create(Frame* frame) { - if ((m_dest - m_buffer) > m_size - len) { - // Enlarge buffer - int newSize = std::max(m_size * 2, m_size + len); - int oldOffset = m_dest - m_buffer; - m_buffer = static_cast<UChar*>(fastRealloc(m_buffer, newSize * sizeof(UChar))); - m_dest = m_buffer + oldOffset; - m_size = newSize; - } + return new TextDocument(frame); } - -private: - Document* m_doc; - Element* m_preElement; - - bool m_skipLF; - - int m_size; - UChar* m_buffer; - UChar* m_dest; -}; -class TextDocument : public HTMLDocument -{ -public: - TextDocument(DOMImplementation*, Frame*); +private: + TextDocument(Frame*); virtual Tokenizer* createTokenizer(); }; +Tokenizer* createTextTokenizer(HTMLViewSourceDocument*); + } #endif // TextDocument_h diff --git a/WebCore/loader/TextResourceDecoder.cpp b/WebCore/loader/TextResourceDecoder.cpp index ef067f8..4a0caa0 100644 --- a/WebCore/loader/TextResourceDecoder.cpp +++ b/WebCore/loader/TextResourceDecoder.cpp @@ -23,12 +23,11 @@ #include "config.h" #include "TextResourceDecoder.h" -#include "CString.h" #include "DOMImplementation.h" -#include "DeprecatedCString.h" #include "HTMLNames.h" #include "TextCodec.h" #include <wtf/ASCIICType.h> +#include <wtf/StringExtras.h> using namespace WTF; @@ -36,6 +35,60 @@ namespace WebCore { using namespace HTMLNames; +// You might think we should put these find functions elsewhere, perhaps with the +// similar functions that operate on UChar, but arguably only the decoder has +// a reason to process strings of char rather than UChar. + +static int find(const char* subject, size_t subjectLength, const char* target) +{ + size_t targetLength = strlen(target); + if (targetLength > subjectLength) + return -1; + for (size_t i = 0; i <= subjectLength - targetLength; ++i) { + bool match = true; + for (size_t j = 0; j < targetLength; ++j) { + if (subject[i + j] != target[j]) { + match = false; + break; + } + } + if (match) + return i; + } + return -1; +} + +static int findIgnoringCase(const char* subject, size_t subjectLength, const char* target) +{ + size_t targetLength = strlen(target); + if (targetLength > subjectLength) + return -1; +#ifndef NDEBUG + for (size_t i = 0; i < targetLength; ++i) + ASSERT(isASCIILower(target[i])); +#endif + for (size_t i = 0; i <= subjectLength - targetLength; ++i) { + bool match = true; + for (size_t j = 0; j < targetLength; ++j) { + if (toASCIILower(subject[i + j]) != target[j]) { + match = false; + break; + } + } + if (match) + return i; + } + return -1; +} + +static TextEncoding findTextEncoding(const char* encodingName, int length) +{ + Vector<char, 64> buffer(length + 1); + memcpy(buffer.data(), encodingName, length); + buffer[length] = '\0'; + return buffer.data(); +} + class KanjiCode { public: enum Type { ASCII, JIS, EUC, SJIS, UTF16, UTF8 }; @@ -274,6 +327,7 @@ TextResourceDecoder::TextResourceDecoder(const String& mimeType, const TextEncod , m_checkedForBOM(false) , m_checkedForCSSCharset(false) , m_checkedForHeadCharset(false) + , m_sawError(false) { } @@ -287,7 +341,11 @@ void TextResourceDecoder::setEncoding(const TextEncoding& encoding, EncodingSour if (!encoding.isValid()) return; - if (source == EncodingFromMetaTag || source == EncodingFromXMLHeader || source == EncodingFromCSSCharset) + // When encoding comes from meta tag (i.e. it cannot be XML files sent via XHR), + // treat x-user-defined as windows-1252 (bug 18270) + if (source == EncodingFromMetaTag && strcasecmp(encoding.name(), "x-user-defined") == 0) + m_decoder.reset("windows-1252"); + else if (source == EncodingFromMetaTag || source == EncodingFromXMLHeader || source == EncodingFromCSSCharset) m_decoder.reset(encoding.closest8BitEquivalent()); else m_decoder.reset(encoding); @@ -296,29 +354,29 @@ void TextResourceDecoder::setEncoding(const TextEncoding& encoding, EncodingSour } // Returns the position of the encoding string. -static int findXMLEncoding(const DeprecatedCString &str, int &encodingLength) +static int findXMLEncoding(const char* str, int len, int& encodingLength) { - int len = str.length(); - - int pos = str.find("encoding"); + int pos = find(str, len, "encoding"); if (pos == -1) return -1; pos += 8; // Skip spaces and stray control characters. - while (str[pos] <= ' ' && pos != len) + while (pos < len && str[pos] <= ' ') ++pos; // Skip equals sign. - if (str[pos] != '=') + if (pos >= len || str[pos] != '=') return -1; ++pos; // Skip spaces and stray control characters. - while (str[pos] <= ' ' && pos != len) + while (pos < len && str[pos] <= ' ') ++pos; // Skip quotation mark. + if (pos >= len) + return - 1; char quoteMark = str[pos]; if (quoteMark != '"' && quoteMark != '\'') return -1; @@ -326,12 +384,11 @@ static int findXMLEncoding(const DeprecatedCString &str, int &encodingLength) // Find the trailing quotation mark. int end = pos; - while (str[end] != quoteMark) + while (end < len && str[end] != quoteMark) ++end; - - if (end == len) + if (end >= len) return -1; - + encodingLength = end - pos; return pos; } @@ -421,14 +478,14 @@ bool TextResourceDecoder::checkForCSSCharset(const char* data, size_t len, bool& if (pos == dataEnd) return false; - CString encodingName(dataStart, pos - dataStart + 1); + int encodingNameLength = pos - dataStart + 1; ++pos; if (!skipWhitespace(pos, dataEnd)) return false; if (*pos == ';') - setEncoding(TextEncoding(encodingName.data()), EncodingFromCSSCharset); + setEncoding(findTextEncoding(dataStart, encodingNameLength), EncodingFromCSSCharset); } } m_checkedForCSSCharset = true; @@ -465,6 +522,8 @@ static inline void skipComment(const char*& ptr, const char* pEnd) ptr = p; } +const int bytesToCheckUnconditionally = 1024; // That many input bytes will be checked for meta charset even if <head> section is over. + bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool& movedDataToBuffer) { if (m_source != DefaultEncoding) { @@ -496,11 +555,11 @@ bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool ++xmlDeclarationEnd; if (xmlDeclarationEnd == pEnd) return false; - DeprecatedCString str(ptr, xmlDeclarationEnd - ptr); // No need for +1, because we have an extra "?" to lose at the end of XML declaration. - int len = 0; - int pos = findXMLEncoding(str, len); + // No need for +1, because we have an extra "?" to lose at the end of XML declaration. + int len; + int pos = findXMLEncoding(ptr, xmlDeclarationEnd - ptr, len); if (pos != -1) - setEncoding(TextEncoding(str.mid(pos, len)), EncodingFromXMLHeader); + setEncoding(findTextEncoding(ptr + pos, len), EncodingFromXMLHeader); // continue looking for a charset - it may be specified in an HTTP-Equiv meta } else if (ptr[0] == '<' && ptr[1] == 0 && ptr[2] == '?' && ptr[3] == 0 && ptr[4] == 'x' && ptr[5] == 0) { setEncoding(UTF16LittleEndianEncoding(), AutoDetectedEncoding); @@ -530,9 +589,14 @@ bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool // and <http://bugs.webkit.org/show_bug.cgi?id=12389>. // Since many sites have charset declarations after <body> or other tags that are disallowed in <head>, - // we don't bail out until we've checked at least 512 bytes of input. + // we don't bail out until we've checked at least bytesToCheckUnconditionally bytes of input. AtomicStringImpl* enclosingTagName = 0; + bool inHeadSection = true; // Becomes false when </head> or any tag not allowed in head is encountered. + + // the HTTP-EQUIV meta has no effect on XHTML + if (m_contentType == XML) + return true; while (ptr + 3 < pEnd) { // +3 guarantees that "<!--" fits in the buffer - and certainly we aren't going to lose any "charset" that way. if (*ptr == '<') { @@ -543,13 +607,16 @@ bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool if (ptr[0] == '!' && ptr[1] == '-' && ptr[2] == '-') { ptr += 3; skipComment(ptr, pEnd); + if (ptr - m_buffer.data() >= bytesToCheckUnconditionally && !inHeadSection) { + // Some pages that test bandwidth from within the browser do it by having + // huge comments and measuring the time they take to load. Repeatedly scanning + // these comments can take a lot of CPU time. + m_checkedForHeadCharset = true; + return true; + } continue; } - // the HTTP-EQUIV meta has no effect on XHTML - if (m_contentType == XML) - return true; - if (*ptr == '/') { ++ptr; end = true; @@ -613,52 +680,56 @@ bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool } if (!end && tag == metaTag && !sawNamespace) { - DeprecatedCString str(tagContentStart, ptr - tagContentStart); - str = str.lower(); + const char* str = tagContentStart; + int length = ptr - tagContentStart; int pos = 0; - while (pos < (int)str.length()) { - if ((pos = str.find("charset", pos, false)) == -1) + while (pos < length) { + int charsetPos = findIgnoringCase(str + pos, length - pos, "charset"); + if (charsetPos == -1) break; - pos += 7; + pos += charsetPos + 7; // skip whitespace - while (pos < (int)str.length() && str[pos] <= ' ') + while (pos < length && str[pos] <= ' ') pos++; - if (pos == (int)str.length()) + if (pos == length) break; if (str[pos++] != '=') continue; - while (pos < (int)str.length() && + while (pos < length && (str[pos] <= ' ') || str[pos] == '=' || str[pos] == '"' || str[pos] == '\'') pos++; // end ? - if (pos == (int)str.length()) + if (pos == length) break; - unsigned endpos = pos; - while (endpos < str.length() && - str[endpos] != ' ' && str[endpos] != '"' && str[endpos] != '\'' && - str[endpos] != ';' && str[endpos] != '>') - endpos++; - setEncoding(TextEncoding(str.mid(pos, endpos - pos)), EncodingFromMetaTag); + int end = pos; + while (end < length && + str[end] != ' ' && str[end] != '"' && str[end] != '\'' && + str[end] != ';' && str[end] != '>') + end++; + setEncoding(findTextEncoding(str + pos, end - pos), EncodingFromMetaTag); if (m_source == EncodingFromMetaTag) return true; - if (endpos >= str.length() || str[endpos] == '/' || str[endpos] == '>') + if (end >= length || str[end] == '/' || str[end] == '>') break; - pos = endpos + 1; + pos = end + 1; + } + } else { + if (!enclosingTagName && tag != scriptTag && tag != noscriptTag && tag != styleTag + && tag != linkTag && tag != metaTag && tag != objectTag && tag != titleTag && tag != baseTag + && (end || tag != htmlTag) && (end || tag != headTag) && isASCIIAlpha(tagBuffer[0])) { + inHeadSection = false; + } + + if (ptr - m_buffer.data() >= bytesToCheckUnconditionally && !inHeadSection) { + m_checkedForHeadCharset = true; + return true; } - } else if (ptr - m_buffer.data() >= 512 && tag != scriptTag && tag != noscriptTag && tag != styleTag && - tag != linkTag && tag != metaTag && tag != objectTag && - tag != titleTag && tag != baseTag && - (end || tag != htmlTag) && !enclosingTagName && - (tag != headTag) && isASCIIAlpha(tagBuffer[0])) { - m_checkedForHeadCharset = true; - return true; } - } - else - ptr++; + } else + ++ptr; } return false; } @@ -705,7 +776,7 @@ String TextResourceDecoder::decode(const char* data, size_t len) ASSERT(encoding().isValid()); if (m_buffer.isEmpty()) - return m_decoder.decode(data, len); + return m_decoder.decode(data, len, false, m_contentType == XML, m_sawError); if (!movedDataToBuffer) { size_t oldSize = m_buffer.size(); @@ -713,14 +784,14 @@ String TextResourceDecoder::decode(const char* data, size_t len) memcpy(m_buffer.data() + oldSize, data, len); } - String result = m_decoder.decode(m_buffer.data(), m_buffer.size()); + String result = m_decoder.decode(m_buffer.data(), m_buffer.size(), false, m_contentType == XML, m_sawError); m_buffer.clear(); return result; } String TextResourceDecoder::flush() { - String result = m_decoder.decode(m_buffer.data(), m_buffer.size(), true); + String result = m_decoder.decode(m_buffer.data(), m_buffer.size(), true, m_contentType == XML, m_sawError); m_buffer.clear(); return result; } diff --git a/WebCore/loader/TextResourceDecoder.h b/WebCore/loader/TextResourceDecoder.h index 6917ab5..8bbe85e 100644 --- a/WebCore/loader/TextResourceDecoder.h +++ b/WebCore/loader/TextResourceDecoder.h @@ -1,9 +1,7 @@ /* - This file is part of the KDE libraries - Copyright (C) 1999 Lars Knoll (knoll@mpi-hd.mpg.de) Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) - Copyright (C) 2006 Apple Computer, Inc. + Copyright (C) 2006, 2008 Apple Inc. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -25,10 +23,7 @@ #ifndef TextResourceDecoder_h #define TextResourceDecoder_h -#include "PlatformString.h" -#include <wtf/RefCounted.h> #include "TextDecoder.h" -#include <wtf/Vector.h> namespace WebCore { @@ -44,7 +39,10 @@ public: UserChosenEncoding }; - TextResourceDecoder(const String& mimeType, const TextEncoding& defaultEncoding = TextEncoding()); + static PassRefPtr<TextResourceDecoder> create(const String& mimeType, const TextEncoding& defaultEncoding = TextEncoding()) + { + return adoptRef(new TextResourceDecoder(mimeType, defaultEncoding)); + } ~TextResourceDecoder(); void setEncoding(const TextEncoding&, EncodingSource); @@ -52,8 +50,12 @@ public: String decode(const char* data, size_t length); String flush(); + + bool sawError() const { return m_sawError; } private: + TextResourceDecoder(const String& mimeType, const TextEncoding& defaultEncoding); + enum ContentType { PlainText, HTML, XML, CSS }; // PlainText is equivalent to directly using TextDecoder. static ContentType determineContentType(const String& mimeType); static const TextEncoding& defaultEncoding(ContentType, const TextEncoding& defaultEncoding); @@ -70,6 +72,7 @@ private: bool m_checkedForBOM; bool m_checkedForCSSCharset; bool m_checkedForHeadCharset; + bool m_sawError; }; } diff --git a/WebCore/loader/mac/UserStyleSheetLoader.cpp b/WebCore/loader/UserStyleSheetLoader.cpp index 349a994..3afbc15 100644 --- a/WebCore/loader/mac/UserStyleSheetLoader.cpp +++ b/WebCore/loader/UserStyleSheetLoader.cpp @@ -41,7 +41,7 @@ UserStyleSheetLoader::UserStyleSheetLoader(PassRefPtr<Document> document, const { if (m_cachedSheet) { m_document->addPendingSheet(); - m_cachedSheet->ref(this); + m_cachedSheet->addClient(this); } } @@ -50,13 +50,13 @@ UserStyleSheetLoader::~UserStyleSheetLoader() if (m_cachedSheet) { if (!m_cachedSheet->isLoaded()) m_document->removePendingSheet(); - m_cachedSheet->deref(this); + m_cachedSheet->removeClient(this); } } -void UserStyleSheetLoader::setCSSStyleSheet(const String& /*URL*/, const String& /*charset*/, const String& sheet) +void UserStyleSheetLoader::setCSSStyleSheet(const String& /*URL*/, const String& /*charset*/, const CachedCSSStyleSheet* sheet) { m_document->removePendingSheet(); if (Frame* frame = m_document->frame()) - frame->setUserStyleSheet(sheet); + frame->setUserStyleSheet(sheet->sheetText()); } diff --git a/WebCore/loader/mac/UserStyleSheetLoader.h b/WebCore/loader/UserStyleSheetLoader.h index 9159273..6e7a1ba 100644 --- a/WebCore/loader/mac/UserStyleSheetLoader.h +++ b/WebCore/loader/UserStyleSheetLoader.h @@ -30,6 +30,7 @@ #define UserStyleSheetLoader_h #include "CachedResourceClient.h" +#include "CachedResourceHandle.h" #include "Document.h" @@ -45,10 +46,10 @@ namespace WebCore { ~UserStyleSheetLoader(); private: - virtual void setCSSStyleSheet(const String& URL, const String& charset, const String& sheet); + virtual void setCSSStyleSheet(const String& URL, const String& charset, const CachedCSSStyleSheet* sheet); RefPtr<Document> m_document; - CachedCSSStyleSheet* m_cachedSheet; + CachedResourceHandle<CachedCSSStyleSheet> m_cachedSheet; }; } // namespace WebCore diff --git a/WebCore/loader/appcache/ApplicationCache.cpp b/WebCore/loader/appcache/ApplicationCache.cpp new file mode 100644 index 0000000..d29eda8 --- /dev/null +++ b/WebCore/loader/appcache/ApplicationCache.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#include "config.h" +#include "ApplicationCache.h" + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + +#include "ApplicationCacheGroup.h" +#include "ApplicationCacheResource.h" +#include "ApplicationCacheStorage.h" +#include "ResourceRequest.h" +#include <stdio.h> + +namespace WebCore { + +ApplicationCache::ApplicationCache() + : m_group(0) + , m_manifest(0) + , m_storageID(0) +{ +} + +ApplicationCache::~ApplicationCache() +{ + if (m_group && !m_group->isCopy()) + m_group->cacheDestroyed(this); +} + +void ApplicationCache::setGroup(ApplicationCacheGroup* group) +{ + ASSERT(!m_group); + m_group = group; +} + +void ApplicationCache::setManifestResource(PassRefPtr<ApplicationCacheResource> manifest) +{ + ASSERT(manifest); + ASSERT(!m_manifest); + ASSERT(manifest->type() & ApplicationCacheResource::Manifest); + + m_manifest = manifest.get(); + + addResource(manifest); +} + +void ApplicationCache::addResource(PassRefPtr<ApplicationCacheResource> resource) +{ + ASSERT(resource); + + const String& url = resource->url(); + + ASSERT(!m_resources.contains(url)); + + if (m_storageID) { + ASSERT(!resource->storageID()); + + // Add the resource to the storage. + cacheStorage().store(resource.get(), this); + } + + m_resources.set(url, resource); +} + +unsigned ApplicationCache::removeResource(const String& url) +{ + HashMap<String, RefPtr<ApplicationCacheResource> >::iterator it = m_resources.find(url); + if (it == m_resources.end()) + return 0; + + // The resource exists, get its type so we can return it. + unsigned type = it->second->type(); + + m_resources.remove(it); + + return type; +} + +ApplicationCacheResource* ApplicationCache::resourceForURL(const String& url) +{ + return m_resources.get(url).get(); +} + +bool ApplicationCache::requestIsHTTPOrHTTPSGet(const ResourceRequest& request) +{ + if (!request.url().protocolIs("http") && !request.url().protocolIs("https")) + return false; + + if (!equalIgnoringCase(request.httpMethod(), "GET")) + return false; + + return true; +} + +ApplicationCacheResource* ApplicationCache::resourceForRequest(const ResourceRequest& request) +{ + // We only care about HTTP/HTTPS GET requests. + if (!requestIsHTTPOrHTTPSGet(request)) + return false; + + return resourceForURL(request.url()); +} + +unsigned ApplicationCache::numDynamicEntries() const +{ + // FIXME: Implement + return 0; +} + +String ApplicationCache::dynamicEntry(unsigned index) const +{ + // FIXME: Implement + return String(); +} + +bool ApplicationCache::addDynamicEntry(const String& url) +{ + if (!equalIgnoringCase(m_group->manifestURL().protocol(), + KURL(url).protocol())) + return false; + + // FIXME: Implement + return true; +} + +void ApplicationCache::removeDynamicEntry(const String& url) +{ + // FIXME: Implement +} + +void ApplicationCache::setOnlineWhitelist(const HashSet<String>& onlineWhitelist) +{ + ASSERT(m_onlineWhitelist.isEmpty()); + m_onlineWhitelist = onlineWhitelist; +} + +bool ApplicationCache::isURLInOnlineWhitelist(const KURL& url) +{ + if (!url.hasRef()) + return m_onlineWhitelist.contains(url); + + KURL copy(url); + copy.setRef(String()); + return m_onlineWhitelist.contains(copy); +} + +void ApplicationCache::clearStorageID() +{ + m_storageID = 0; + + ResourceMap::const_iterator end = m_resources.end(); + for (ResourceMap::const_iterator it = m_resources.begin(); it != end; ++it) + it->second->clearStorageID(); +} + +#ifndef NDEBUG +void ApplicationCache::dump() +{ + HashMap<String, RefPtr<ApplicationCacheResource> >::const_iterator end = m_resources.end(); + + for (HashMap<String, RefPtr<ApplicationCacheResource> >::const_iterator it = m_resources.begin(); it != end; ++it) { + printf("%s ", it->first.ascii().data()); + ApplicationCacheResource::dumpType(it->second->type()); + } +} +#endif + +} + +#endif // ENABLE(OFFLINE_WEB_APPLICATIONS) diff --git a/WebCore/loader/appcache/ApplicationCache.h b/WebCore/loader/appcache/ApplicationCache.h new file mode 100644 index 0000000..e536cbe --- /dev/null +++ b/WebCore/loader/appcache/ApplicationCache.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 ApplicationCache_h +#define ApplicationCache_h + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +#include "StringHash.h" +#include "PlatformString.h" + +namespace WebCore { + +class ApplicationCacheGroup; +class ApplicationCacheResource; +class DocumentLoader; +class KURL; +class ResourceRequest; + +class ApplicationCache : public RefCounted<ApplicationCache> { +public: + static PassRefPtr<ApplicationCache> create() { return adoptRef(new ApplicationCache); } + ~ApplicationCache(); + + void addResource(PassRefPtr<ApplicationCacheResource> resource); + unsigned removeResource(const String& url); + + void setManifestResource(PassRefPtr<ApplicationCacheResource> manifest); + ApplicationCacheResource* manifestResource() const { return m_manifest; } + + void setGroup(ApplicationCacheGroup*); + ApplicationCacheGroup* group() const { return m_group; } + + ApplicationCacheResource* resourceForRequest(const ResourceRequest&); + ApplicationCacheResource* resourceForURL(const String& url); + + unsigned numDynamicEntries() const; + String dynamicEntry(unsigned index) const; + + bool addDynamicEntry(const String& url); + void removeDynamicEntry(const String& url); + + void setOnlineWhitelist(const HashSet<String>& onlineWhitelist); + const HashSet<String>& onlineWhitelist() const { return m_onlineWhitelist; } + bool isURLInOnlineWhitelist(const KURL&); + +#ifndef NDEBUG + void dump(); +#endif + + typedef HashMap<String, RefPtr<ApplicationCacheResource> > ResourceMap; + ResourceMap::const_iterator begin() const { return m_resources.begin(); } + ResourceMap::const_iterator end() const { return m_resources.end(); } + + void setStorageID(unsigned storageID) { m_storageID = storageID; } + unsigned storageID() const { return m_storageID; } + void clearStorageID(); + + static bool requestIsHTTPOrHTTPSGet(const ResourceRequest&); +private: + ApplicationCache(); + + ApplicationCacheGroup* m_group; + ResourceMap m_resources; + ApplicationCacheResource* m_manifest; + + HashSet<String> m_onlineWhitelist; + + unsigned m_storageID; +}; + +} // namespace WebCore + +#endif // ENABLE(OFFLINE_WEB_APPLICATIONS) + +#endif // ApplicationCache_h diff --git a/WebCore/loader/appcache/ApplicationCacheGroup.cpp b/WebCore/loader/appcache/ApplicationCacheGroup.cpp new file mode 100644 index 0000000..ff26767 --- /dev/null +++ b/WebCore/loader/appcache/ApplicationCacheGroup.cpp @@ -0,0 +1,715 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#include "config.h" +#include "ApplicationCacheGroup.h" + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + +#include "ApplicationCache.h" +#include "ApplicationCacheResource.h" +#include "ApplicationCacheStorage.h" +#include "DocumentLoader.h" +#include "DOMApplicationCache.h" +#include "DOMWindow.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "MainResourceLoader.h" +#include "ManifestParser.h" +#include "Page.h" +#include "Settings.h" +#include <wtf/HashMap.h> + +namespace WebCore { + +ApplicationCacheGroup::ApplicationCacheGroup(const KURL& manifestURL, bool isCopy) + : m_manifestURL(manifestURL) + , m_status(Idle) + , m_savedNewestCachePointer(0) + , m_frame(0) + , m_storageID(0) + , m_isCopy(isCopy) +{ +} + +ApplicationCacheGroup::~ApplicationCacheGroup() +{ + if (m_isCopy) { + ASSERT(m_newestCache); + ASSERT(m_caches.size() == 1); + ASSERT(m_caches.contains(m_newestCache.get())); + ASSERT(!m_cacheBeingUpdated); + ASSERT(m_associatedDocumentLoaders.isEmpty()); + ASSERT(m_cacheCandidates.isEmpty()); + ASSERT(m_newestCache->group() == this); + + return; + } + + ASSERT(!m_newestCache); + ASSERT(m_caches.isEmpty()); + + stopLoading(); + + cacheStorage().cacheGroupDestroyed(this); +} + +ApplicationCache* ApplicationCacheGroup::cacheForMainRequest(const ResourceRequest& request, DocumentLoader* loader) +{ + if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) + return 0; + + ASSERT(loader->frame()); + ASSERT(loader->frame()->page()); + if (loader->frame() != loader->frame()->page()->mainFrame()) + return 0; + + if (ApplicationCacheGroup* group = cacheStorage().cacheGroupForURL(request.url())) { + ASSERT(group->newestCache()); + + return group->newestCache(); + } + + return 0; +} + +void ApplicationCacheGroup::selectCache(Frame* frame, const KURL& manifestURL) +{ + ASSERT(frame && frame->page()); + + if (!frame->settings()->offlineWebApplicationCacheEnabled()) + return; + + DocumentLoader* documentLoader = frame->loader()->documentLoader(); + ASSERT(!documentLoader->applicationCache()); + + if (manifestURL.isNull()) { + selectCacheWithoutManifestURL(frame); + return; + } + + ApplicationCache* mainResourceCache = documentLoader->mainResourceApplicationCache(); + + // Check if the main resource is being loaded as part of navigation of the main frame + bool isMainFrame = frame->page()->mainFrame() == frame; + + if (!isMainFrame) { + if (mainResourceCache && manifestURL != mainResourceCache->group()->manifestURL()) { + ApplicationCacheResource* resource = mainResourceCache->resourceForURL(documentLoader->originalURL()); + ASSERT(resource); + + resource->addType(ApplicationCacheResource::Foreign); + } + + return; + } + + if (mainResourceCache) { + if (manifestURL == mainResourceCache->group()->m_manifestURL) { + mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); + mainResourceCache->group()->update(frame); + } else { + // FIXME: If the resource being loaded was loaded from an application cache and the URI of + // that application cache's manifest is not the same as the manifest URI with which the algorithm was invoked + // then we should "undo" the navigation. + } + + return; + } + + // The resource was loaded from the network, check if it is a HTTP/HTTPS GET. + const ResourceRequest& request = frame->loader()->activeDocumentLoader()->request(); + + if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) { + selectCacheWithoutManifestURL(frame); + return; + } + + // Check that the resource URL has the same scheme/host/port as the manifest URL. + if (!protocolHostAndPortAreEqual(manifestURL, request.url())) { + selectCacheWithoutManifestURL(frame); + return; + } + + ApplicationCacheGroup* group = cacheStorage().findOrCreateCacheGroup(manifestURL); + + if (ApplicationCache* cache = group->newestCache()) { + ASSERT(cache->manifestResource()); + + group->associateDocumentLoaderWithCache(frame->loader()->documentLoader(), cache); + + if (!frame->loader()->documentLoader()->isLoadingMainResource()) + group->finishedLoadingMainResource(frame->loader()->documentLoader()); + + group->update(frame); + } else { + bool isUpdating = group->m_cacheBeingUpdated; + + if (!isUpdating) + group->m_cacheBeingUpdated = ApplicationCache::create(); + documentLoader->setCandidateApplicationCacheGroup(group); + group->m_cacheCandidates.add(documentLoader); + + const KURL& url = frame->loader()->documentLoader()->originalURL(); + + unsigned type = 0; + + // If the resource has already been downloaded, remove it so that it will be replaced with the implicit resource + if (isUpdating) + type = group->m_cacheBeingUpdated->removeResource(url); + + // Add the main resource URL as an implicit entry. + group->addEntry(url, type | ApplicationCacheResource::Implicit); + + if (!frame->loader()->documentLoader()->isLoadingMainResource()) + group->finishedLoadingMainResource(frame->loader()->documentLoader()); + + if (!isUpdating) + group->update(frame); + } +} + +void ApplicationCacheGroup::selectCacheWithoutManifestURL(Frame* frame) +{ + if (!frame->settings()->offlineWebApplicationCacheEnabled()) + return; + + DocumentLoader* documentLoader = frame->loader()->documentLoader(); + ASSERT(!documentLoader->applicationCache()); + + ApplicationCache* mainResourceCache = documentLoader->mainResourceApplicationCache(); + bool isMainFrame = frame->page()->mainFrame() == frame; + + if (isMainFrame && mainResourceCache) { + mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); + mainResourceCache->group()->update(frame); + } +} + +void ApplicationCacheGroup::finishedLoadingMainResource(DocumentLoader* loader) +{ + const KURL& url = loader->originalURL(); + + if (ApplicationCache* cache = loader->applicationCache()) { + RefPtr<ApplicationCacheResource> resource = ApplicationCacheResource::create(url, loader->response(), ApplicationCacheResource::Implicit, loader->mainResourceData()); + cache->addResource(resource.release()); + + if (!m_cacheBeingUpdated) + return; + } + + ASSERT(m_pendingEntries.contains(url)); + + EntryMap::iterator it = m_pendingEntries.find(url); + ASSERT(it->second & ApplicationCacheResource::Implicit); + + RefPtr<ApplicationCacheResource> resource = ApplicationCacheResource::create(url, loader->response(), it->second, loader->mainResourceData()); + + ASSERT(m_cacheBeingUpdated); + m_cacheBeingUpdated->addResource(resource.release()); + + m_pendingEntries.remove(it); + + checkIfLoadIsComplete(); +} + +void ApplicationCacheGroup::failedLoadingMainResource(DocumentLoader* loader) +{ + ASSERT(m_cacheCandidates.contains(loader) || m_associatedDocumentLoaders.contains(loader)); + + // Note that cacheUpdateFailed() can cause the cache group to be deleted. + cacheUpdateFailed(); +} + +void ApplicationCacheGroup::stopLoading() +{ + if (m_manifestHandle) { + ASSERT(!m_currentHandle); + ASSERT(!m_cacheBeingUpdated); + + m_manifestHandle->setClient(0); + m_manifestHandle->cancel(); + m_manifestHandle = 0; + } + + if (m_currentHandle) { + ASSERT(!m_manifestHandle); + ASSERT(m_cacheBeingUpdated); + + m_currentHandle->setClient(0); + m_currentHandle->cancel(); + m_currentHandle = 0; + } + + m_cacheBeingUpdated = 0; +} + +void ApplicationCacheGroup::documentLoaderDestroyed(DocumentLoader* loader) +{ + HashSet<DocumentLoader*>::iterator it = m_associatedDocumentLoaders.find(loader); + + if (it != m_associatedDocumentLoaders.end()) { + ASSERT(!m_cacheCandidates.contains(loader)); + + m_associatedDocumentLoaders.remove(it); + } else { + ASSERT(m_cacheCandidates.contains(loader)); + m_cacheCandidates.remove(loader); + } + + if (!m_associatedDocumentLoaders.isEmpty() || !m_cacheCandidates.isEmpty()) + return; + + // We should only have the newest cache remaining, or there is an initial cache attempt in progress. + ASSERT(m_caches.size() == 1 || m_cacheBeingUpdated); + + // If a cache update is in progress, stop it. + if (m_caches.size() == 1) { + ASSERT(m_caches.contains(m_newestCache.get())); + + // Release our reference to the newest cache. + m_savedNewestCachePointer = m_newestCache.get(); + + // This could cause us to be deleted. + m_newestCache = 0; + + return; + } + + // There is an initial cache attempt in progress + ASSERT(m_cacheBeingUpdated); + ASSERT(m_caches.size() == 0); + + // Delete ourselves, causing the cache attempt to be stopped. + delete this; +} + +void ApplicationCacheGroup::cacheDestroyed(ApplicationCache* cache) +{ + ASSERT(m_caches.contains(cache)); + + m_caches.remove(cache); + + if (cache != m_savedNewestCachePointer) + cacheStorage().remove(cache); + + if (m_caches.isEmpty()) + delete this; +} + +void ApplicationCacheGroup::setNewestCache(PassRefPtr<ApplicationCache> newestCache) +{ + ASSERT(!m_newestCache); + ASSERT(!m_caches.contains(newestCache.get())); + ASSERT(!newestCache->group()); + + m_newestCache = newestCache; + m_caches.add(m_newestCache.get()); + m_newestCache->setGroup(this); +} + +void ApplicationCacheGroup::update(Frame* frame) +{ + if (m_status == Checking || m_status == Downloading) + return; + + ASSERT(!m_frame); + m_frame = frame; + + m_status = Checking; + + callListenersOnAssociatedDocuments(&DOMApplicationCache::callCheckingListener); + + ASSERT(!m_manifestHandle); + ASSERT(!m_manifestResource); + + // FIXME: Handle defer loading + + ResourceRequest request(m_manifestURL); + m_frame->loader()->applyUserAgent(request); + + m_manifestHandle = ResourceHandle::create(request, this, m_frame, false, true, false); +} + +void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response) +{ + if (handle == m_manifestHandle) { + didReceiveManifestResponse(response); + return; + } + + ASSERT(handle == m_currentHandle); + + int statusCode = response.httpStatusCode() / 100; + if (statusCode == 4 || statusCode == 5) { + // Note that cacheUpdateFailed() can cause the cache group to be deleted. + cacheUpdateFailed(); + return; + } + + const KURL& url = handle->request().url(); + + ASSERT(!m_currentResource); + ASSERT(m_pendingEntries.contains(url)); + + unsigned type = m_pendingEntries.get(url); + + // If this is an initial cache attempt, we should not get implicit resources delivered here. + if (!m_newestCache) + ASSERT(!(type & ApplicationCacheResource::Implicit)); + + m_currentResource = ApplicationCacheResource::create(url, response, type); +} + +void ApplicationCacheGroup::didReceiveData(ResourceHandle* handle, const char* data, int length, int lengthReceived) +{ + if (handle == m_manifestHandle) { + didReceiveManifestData(data, length); + return; + } + + ASSERT(handle == m_currentHandle); + + ASSERT(m_currentResource); + m_currentResource->data()->append(data, length); +} + +void ApplicationCacheGroup::didFinishLoading(ResourceHandle* handle) +{ + if (handle == m_manifestHandle) { + didFinishLoadingManifest(); + return; + } + + ASSERT(m_currentHandle == handle); + ASSERT(m_pendingEntries.contains(handle->request().url())); + + m_pendingEntries.remove(handle->request().url()); + + ASSERT(m_cacheBeingUpdated); + + m_cacheBeingUpdated->addResource(m_currentResource.release()); + m_currentHandle = 0; + + // Load the next file. + if (!m_pendingEntries.isEmpty()) { + startLoadingEntry(); + return; + } + + checkIfLoadIsComplete(); +} + +void ApplicationCacheGroup::didFail(ResourceHandle* handle, const ResourceError&) +{ + if (handle == m_manifestHandle) { + didFailToLoadManifest(); + return; + } + + // Note that cacheUpdateFailed() can cause the cache group to be deleted. + cacheUpdateFailed(); +} + +void ApplicationCacheGroup::didReceiveManifestResponse(const ResourceResponse& response) +{ + int statusCode = response.httpStatusCode() / 100; + + if (statusCode == 4 || statusCode == 5 || + !equalIgnoringCase(response.mimeType(), "text/cache-manifest")) { + didFailToLoadManifest(); + return; + } + + ASSERT(!m_manifestResource); + ASSERT(m_manifestHandle); + m_manifestResource = ApplicationCacheResource::create(m_manifestHandle->request().url(), response, + ApplicationCacheResource::Manifest); +} + +void ApplicationCacheGroup::didReceiveManifestData(const char* data, int length) +{ + ASSERT(m_manifestResource); + m_manifestResource->data()->append(data, length); +} + +void ApplicationCacheGroup::didFinishLoadingManifest() +{ + if (!m_manifestResource) { + didFailToLoadManifest(); + return; + } + + bool isUpgradeAttempt = m_newestCache; + + m_manifestHandle = 0; + + // Check if the manifest is byte-for-byte identical. + if (isUpgradeAttempt) { + ApplicationCacheResource* newestManifest = m_newestCache->manifestResource(); + ASSERT(newestManifest); + + if (newestManifest->data()->size() == m_manifestResource->data()->size() && + !memcmp(newestManifest->data()->data(), m_manifestResource->data()->data(), newestManifest->data()->size())) { + + callListenersOnAssociatedDocuments(&DOMApplicationCache::callNoUpdateListener); + + m_status = Idle; + m_frame = 0; + m_manifestResource = 0; + return; + } + } + + Manifest manifest; + if (!parseManifest(m_manifestURL, m_manifestResource->data()->data(), m_manifestResource->data()->size(), manifest)) { + didFailToLoadManifest(); + return; + } + + // FIXME: Add the opportunistic caching namespaces and their fallbacks. + + // We have the manifest, now download the resources. + m_status = Downloading; + + callListenersOnAssociatedDocuments(&DOMApplicationCache::callDownloadingListener); + +#ifndef NDEBUG + // We should only have implicit entries. + { + EntryMap::const_iterator end = m_pendingEntries.end(); + for (EntryMap::const_iterator it = m_pendingEntries.begin(); it != end; ++it) + ASSERT(it->second & ApplicationCacheResource::Implicit); + } +#endif + + if (isUpgradeAttempt) { + ASSERT(!m_cacheBeingUpdated); + + m_cacheBeingUpdated = ApplicationCache::create(); + + ApplicationCache::ResourceMap::const_iterator end = m_newestCache->end(); + for (ApplicationCache::ResourceMap::const_iterator it = m_newestCache->begin(); it != end; ++it) { + unsigned type = it->second->type(); + if (type & (ApplicationCacheResource::Opportunistic | ApplicationCacheResource::Implicit | ApplicationCacheResource::Dynamic)) + addEntry(it->first, type); + } + } + + HashSet<String>::const_iterator end = manifest.explicitURLs.end(); + for (HashSet<String>::const_iterator it = manifest.explicitURLs.begin(); it != end; ++it) + addEntry(*it, ApplicationCacheResource::Explicit); + + m_cacheBeingUpdated->setOnlineWhitelist(manifest.onlineWhitelistedURLs); + + startLoadingEntry(); +} + +void ApplicationCacheGroup::cacheUpdateFailed() +{ + stopLoading(); + + callListenersOnAssociatedDocuments(&DOMApplicationCache::callErrorListener); + + m_pendingEntries.clear(); + m_manifestResource = 0; + + while (!m_cacheCandidates.isEmpty()) { + HashSet<DocumentLoader*>::iterator it = m_cacheCandidates.begin(); + + ASSERT((*it)->candidateApplicationCacheGroup() == this); + (*it)->setCandidateApplicationCacheGroup(0); + m_cacheCandidates.remove(it); + } + + m_status = Idle; + m_frame = 0; + + // If there are no associated caches, delete ourselves + if (m_associatedDocumentLoaders.isEmpty()) + delete this; +} + + +void ApplicationCacheGroup::didFailToLoadManifest() +{ + // Note that cacheUpdateFailed() can cause the cache group to be deleted. + cacheUpdateFailed(); +} + +void ApplicationCacheGroup::checkIfLoadIsComplete() +{ + ASSERT(m_cacheBeingUpdated); + + if (m_manifestHandle) + return; + + if (!m_pendingEntries.isEmpty()) + return; + + // We're done + bool isUpgradeAttempt = m_newestCache; + + m_cacheBeingUpdated->setManifestResource(m_manifestResource.release()); + + m_status = Idle; + m_frame = 0; + + Vector<RefPtr<DocumentLoader> > documentLoaders; + + if (isUpgradeAttempt) { + ASSERT(m_cacheCandidates.isEmpty()); + + copyToVector(m_associatedDocumentLoaders, documentLoaders); + } else { + while (!m_cacheCandidates.isEmpty()) { + HashSet<DocumentLoader*>::iterator it = m_cacheCandidates.begin(); + + DocumentLoader* loader = *it; + ASSERT(!loader->applicationCache()); + ASSERT(loader->candidateApplicationCacheGroup() == this); + + associateDocumentLoaderWithCache(loader, m_cacheBeingUpdated.get()); + + documentLoaders.append(loader); + + m_cacheCandidates.remove(it); + } + } + + setNewestCache(m_cacheBeingUpdated.release()); + + // Store the cache + cacheStorage().storeNewestCache(this); + + callListeners(isUpgradeAttempt ? &DOMApplicationCache::callUpdateReadyListener : &DOMApplicationCache::callCachedListener, + documentLoaders); +} + +void ApplicationCacheGroup::startLoadingEntry() +{ + ASSERT(m_cacheBeingUpdated); + + if (m_pendingEntries.isEmpty()) { + checkIfLoadIsComplete(); + return; + } + + EntryMap::const_iterator it = m_pendingEntries.begin(); + + // If this is an initial cache attempt, we do not want to fetch any implicit entries, + // since those are fed to us by the normal loader machinery. + if (!m_newestCache) { + // Get the first URL in the entry table that is not implicit + EntryMap::const_iterator end = m_pendingEntries.end(); + + while (it->second & ApplicationCacheResource::Implicit) { + ++it; + + if (it == end) + return; + } + } + + callListenersOnAssociatedDocuments(&DOMApplicationCache::callProgressListener); + + // FIXME: If this is an upgrade attempt, the newest cache should be used as an HTTP cache. + + ASSERT(!m_currentHandle); + + ResourceRequest request(it->first); + m_frame->loader()->applyUserAgent(request); + + m_currentHandle = ResourceHandle::create(request, this, m_frame, false, true, false); +} + +void ApplicationCacheGroup::addEntry(const String& url, unsigned type) +{ + ASSERT(m_cacheBeingUpdated); + + // Don't add the URL if we already have an implicit resource in the cache + if (ApplicationCacheResource* resource = m_cacheBeingUpdated->resourceForURL(url)) { + ASSERT(resource->type() & ApplicationCacheResource::Implicit); + + resource->addType(type); + return; + } + + // Don't add the URL if it's the same as the manifest URL. + if (m_manifestResource && m_manifestResource->url() == url) { + m_manifestResource->addType(type); + return; + } + + pair<EntryMap::iterator, bool> result = m_pendingEntries.add(url, type); + + if (!result.second) + result.first->second |= type; +} + +void ApplicationCacheGroup::associateDocumentLoaderWithCache(DocumentLoader* loader, ApplicationCache* cache) +{ + loader->setApplicationCache(cache); + + ASSERT(!m_associatedDocumentLoaders.contains(loader)); + m_associatedDocumentLoaders.add(loader); +} + +void ApplicationCacheGroup::callListenersOnAssociatedDocuments(ListenerFunction listenerFunction) +{ + Vector<RefPtr<DocumentLoader> > loaders; + copyToVector(m_associatedDocumentLoaders, loaders); + + callListeners(listenerFunction, loaders); +} + +void ApplicationCacheGroup::callListeners(ListenerFunction listenerFunction, const Vector<RefPtr<DocumentLoader> >& loaders) +{ + for (unsigned i = 0; i < loaders.size(); i++) { + Frame* frame = loaders[i]->frame(); + if (!frame) + continue; + + ASSERT(frame->loader()->documentLoader() == loaders[i]); + DOMWindow* window = frame->domWindow(); + + if (DOMApplicationCache* domCache = window->optionalApplicationCache()) + (domCache->*listenerFunction)(); + } +} + +void ApplicationCacheGroup::clearStorageID() +{ + m_storageID = 0; + + HashSet<ApplicationCache*>::const_iterator end = m_caches.end(); + for (HashSet<ApplicationCache*>::const_iterator it = m_caches.begin(); it != end; ++it) + (*it)->clearStorageID(); +} + + +} + +#endif // ENABLE(OFFLINE_WEB_APPLICATIONS) diff --git a/WebCore/loader/appcache/ApplicationCacheGroup.h b/WebCore/loader/appcache/ApplicationCacheGroup.h new file mode 100644 index 0000000..d5b7563 --- /dev/null +++ b/WebCore/loader/appcache/ApplicationCacheGroup.h @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 ApplicationCacheGroup_h +#define ApplicationCacheGroup_h + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + +#include <wtf/Noncopyable.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> + +#include "KURL.h" +#include "PlatformString.h" +#include "ResourceHandle.h" +#include "ResourceHandleClient.h" +#include "SharedBuffer.h" + +namespace WebCore { + +class ApplicationCache; +class ApplicationCacheResource; +class DOMApplicationCache; +class Document; +class DocumentLoader; +class Frame; + +class ApplicationCacheGroup : Noncopyable, ResourceHandleClient { +public: + ApplicationCacheGroup(const KURL& manifestURL, bool isCopy = false); + ~ApplicationCacheGroup(); + + enum Status { Idle, Checking, Downloading }; + + static ApplicationCache* cacheForMainRequest(const ResourceRequest&, DocumentLoader*); + + static void selectCache(Frame*, const KURL& manifestURL); + static void selectCacheWithoutManifestURL(Frame*); + + const KURL& manifestURL() const { return m_manifestURL; } + Status status() const { return m_status; } + + void setStorageID(unsigned storageID) { m_storageID = storageID; } + unsigned storageID() const { return m_storageID; } + void clearStorageID(); + + void update(Frame*); + void cacheDestroyed(ApplicationCache*); + + ApplicationCache* newestCache() const { return m_newestCache.get(); } + ApplicationCache* savedNewestCachePointer() const { return m_savedNewestCachePointer; } + + void finishedLoadingMainResource(DocumentLoader*); + void failedLoadingMainResource(DocumentLoader*); + void documentLoaderDestroyed(DocumentLoader*); + + void setNewestCache(PassRefPtr<ApplicationCache> newestCache); + + bool isCopy() const { return m_isCopy; } +private: + typedef void (DOMApplicationCache::*ListenerFunction)(); + void callListenersOnAssociatedDocuments(ListenerFunction); + void callListeners(ListenerFunction, const Vector<RefPtr<DocumentLoader> >& loaders); + + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); + virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived); + virtual void didFinishLoading(ResourceHandle*); + virtual void didFail(ResourceHandle*, const ResourceError&); + + void didReceiveManifestResponse(const ResourceResponse&); + void didReceiveManifestData(const char*, int); + void didFinishLoadingManifest(); + void didFailToLoadManifest(); + + void startLoadingEntry(); + void checkIfLoadIsComplete(); + void cacheUpdateFailed(); + + void addEntry(const String&, unsigned type); + + void associateDocumentLoaderWithCache(DocumentLoader*, ApplicationCache*); + + void stopLoading(); + + KURL m_manifestURL; + Status m_status; + + // This is the newest cache in the group. + RefPtr<ApplicationCache> m_newestCache; + + // During tear-down we save the pointer to the newest cache to prevent reference cycles. + ApplicationCache* m_savedNewestCachePointer; + + // The caches in this cache group. + HashSet<ApplicationCache*> m_caches; + + // The cache being updated (if any). + RefPtr<ApplicationCache> m_cacheBeingUpdated; + + // When a cache group does not yet have a complete cache, this contains the document loaders + // that should be associated with the cache once it has been downloaded. + HashSet<DocumentLoader*> m_cacheCandidates; + + // These are all the document loaders that are associated with a cache in this group. + HashSet<DocumentLoader*> m_associatedDocumentLoaders; + + // The URLs and types of pending cache entries. + typedef HashMap<String, unsigned> EntryMap; + EntryMap m_pendingEntries; + + // Frame used for fetching resources when updating + Frame* m_frame; + + unsigned m_storageID; + + // Whether this cache group is a copy that's only used for transferring the cache to another file. + bool m_isCopy; + + RefPtr<ResourceHandle> m_currentHandle; + RefPtr<ApplicationCacheResource> m_currentResource; + + RefPtr<ApplicationCacheResource> m_manifestResource; + RefPtr<ResourceHandle> m_manifestHandle; +}; + +} // namespace WebCore + +#endif // ENABLE(OFFLINE_WEB_APPLICATIONS) + +#endif // ApplicationCacheGroup_h diff --git a/WebCore/loader/appcache/ApplicationCacheResource.cpp b/WebCore/loader/appcache/ApplicationCacheResource.cpp new file mode 100644 index 0000000..ee82ff5 --- /dev/null +++ b/WebCore/loader/appcache/ApplicationCacheResource.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#include "config.h" +#include "ApplicationCacheResource.h" +#include <stdio.h> + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + +namespace WebCore { + +ApplicationCacheResource::ApplicationCacheResource(const KURL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> data) + : SubstituteResource(url, response, data) + , m_type(type) + , m_storageID(0) +{ +} + +void ApplicationCacheResource::addType(unsigned type) +{ + ASSERT(!m_storageID); + m_type |= type; +} + +#ifndef NDEBUG +void ApplicationCacheResource::dumpType(unsigned type) +{ + if (type & Implicit) + printf("implicit "); + if (type & Manifest) + printf("manifest "); + if (type & Explicit) + printf("explicit "); + if (type & Foreign) + printf("foreign "); + if (type & Fallback) + printf("fallback "); + if (type & Opportunistic) + printf("opportunistic "); + if (type & Dynamic) + printf("dynamic "); + + printf("\n"); +} +#endif + +} // namespace WebCore + +#endif // ENABLE(OFFLINE_WEB_APPLICATIONS) diff --git a/WebCore/loader/appcache/ApplicationCacheResource.h b/WebCore/loader/appcache/ApplicationCacheResource.h new file mode 100644 index 0000000..1d7b853 --- /dev/null +++ b/WebCore/loader/appcache/ApplicationCacheResource.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 ApplicationCacheResource_h +#define ApplicationCacheResource_h + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + +#include "SubstituteResource.h" + +namespace WebCore { + +class ApplicationCacheResource : public SubstituteResource { +public: + enum Type { + Implicit = 1 << 0, + Manifest = 1 << 1, + Explicit = 1 << 2, + Foreign = 1 << 3, + Fallback = 1 << 4, + Opportunistic = 1 << 5, + Dynamic = 1 << 6 + }; + + static PassRefPtr<ApplicationCacheResource> create(const KURL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> buffer = SharedBuffer::create()) + { + return adoptRef(new ApplicationCacheResource(url, response, type, buffer)); + } + + unsigned type() const { return m_type; } + void addType(unsigned type); + + void setStorageID(unsigned storageID) { m_storageID = storageID; } + unsigned storageID() const { return m_storageID; } + void clearStorageID() { m_storageID = 0; } + +#ifndef NDEBUG + static void dumpType(unsigned type); +#endif + +private: + ApplicationCacheResource(const KURL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> buffer); + + unsigned m_type; + unsigned m_storageID; +}; + +} // namespace WebCore + +#endif // ENABLE(OFFLINE_WEB_APPLICATIONS) + +#endif // ApplicationCacheResource_h diff --git a/WebCore/loader/appcache/ApplicationCacheStorage.cpp b/WebCore/loader/appcache/ApplicationCacheStorage.cpp new file mode 100644 index 0000000..910d00c --- /dev/null +++ b/WebCore/loader/appcache/ApplicationCacheStorage.cpp @@ -0,0 +1,663 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#include "config.h" +#include "ApplicationCacheStorage.h" + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + +#include "ApplicationCache.h" +#include "ApplicationCacheGroup.h" +#include "ApplicationCacheResource.h" +#include "FileSystem.h" +#include "CString.h" +#include "KURL.h" +#include "SQLiteStatement.h" +#include "SQLiteTransaction.h" + +namespace WebCore { + +static unsigned urlHostHash(const KURL& url) +{ + unsigned hostStart = url.hostStart(); + unsigned hostEnd = url.hostEnd(); + + return AlreadyHashed::avoidDeletedValue(StringImpl::computeHash(url.string().characters() + hostStart, hostEnd - hostStart)); +} + +ApplicationCacheGroup* ApplicationCacheStorage::loadCacheGroup(const KURL& manifestURL) +{ + openDatabase(false); + if (!m_database.isOpen()) + return 0; + + SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL AND manifestURL=?"); + if (statement.prepare() != SQLResultOk) + return 0; + + statement.bindText(1, manifestURL); + + int result = statement.step(); + if (result == SQLResultDone) + return 0; + + if (result != SQLResultRow) { + LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg()); + return 0; + } + + unsigned newestCacheStorageID = (unsigned)statement.getColumnInt64(2); + + RefPtr<ApplicationCache> cache = loadCache(newestCacheStorageID); + if (!cache) + return 0; + + ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL); + + group->setStorageID((unsigned)statement.getColumnInt64(0)); + group->setNewestCache(cache.release()); + + return group; +} + +ApplicationCacheGroup* ApplicationCacheStorage::findOrCreateCacheGroup(const KURL& manifestURL) +{ + std::pair<CacheGroupMap::iterator, bool> result = m_cachesInMemory.add(manifestURL, 0); + + if (!result.second) { + ASSERT(result.first->second); + + return result.first->second; + } + + // Look up the group in the database + ApplicationCacheGroup* group = loadCacheGroup(manifestURL); + + // If the group was not found we need to create it + if (!group) { + group = new ApplicationCacheGroup(manifestURL); + m_cacheHostSet.add(urlHostHash(manifestURL)); + } + + result.first->second = group; + + return group; +} + +void ApplicationCacheStorage::loadManifestHostHashes() +{ + static bool hasLoadedHashes = false; + + if (hasLoadedHashes) + return; + + // We set this flag to true before the database has been opened + // to avoid trying to open the database over and over if it doesn't exist. + hasLoadedHashes = true; + + openDatabase(false); + if (!m_database.isOpen()) + return; + + // Fetch the host hashes. + SQLiteStatement statement(m_database, "SELECT manifestHostHash FROM CacheGroups"); + if (statement.prepare() != SQLResultOk) + return; + + int result; + while ((result = statement.step()) == SQLResultRow) + m_cacheHostSet.add((unsigned)statement.getColumnInt64(0)); +} + +ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url) +{ + loadManifestHostHashes(); + + // Hash the host name and see if there's a manifest with the same host. + if (!m_cacheHostSet.contains(urlHostHash(url))) + return 0; + + // Check if a cache already exists in memory. + CacheGroupMap::const_iterator end = m_cachesInMemory.end(); + for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it) { + ApplicationCacheGroup* group = it->second; + + if (!protocolHostAndPortAreEqual(url, group->manifestURL())) + continue; + + if (ApplicationCache* cache = group->newestCache()) { + if (cache->resourceForURL(url)) + return group; + } + } + + if (!m_database.isOpen()) + return 0; + + // Check the database. Look for all cache groups with a newest cache. + SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL"); + if (statement.prepare() != SQLResultOk) + return 0; + + int result; + while ((result = statement.step()) == SQLResultRow) { + KURL manifestURL = KURL(statement.getColumnText(1)); + + if (!protocolHostAndPortAreEqual(url, manifestURL)) + continue; + + // We found a cache group that matches. Now check if the newest cache has a resource with + // a matching URL. + unsigned newestCacheID = (unsigned)statement.getColumnInt64(2); + RefPtr<ApplicationCache> cache = loadCache(newestCacheID); + + if (!cache->resourceForURL(url)) + continue; + + ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL); + + group->setStorageID((unsigned)statement.getColumnInt64(0)); + group->setNewestCache(cache.release()); + + ASSERT(!m_cachesInMemory.contains(manifestURL)); + m_cachesInMemory.set(group->manifestURL(), group); + + return group; + } + + if (result != SQLResultDone) + LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg()); + + return 0; +} + +void ApplicationCacheStorage::cacheGroupDestroyed(ApplicationCacheGroup* group) +{ + ASSERT(m_cachesInMemory.get(group->manifestURL()) == group); + + m_cachesInMemory.remove(group->manifestURL()); + + // If the cache is half-created, we don't want it in the saved set. + if (!group->savedNewestCachePointer()) + m_cacheHostSet.remove(urlHostHash(group->manifestURL())); +} + +void ApplicationCacheStorage::setCacheDirectory(const String& cacheDirectory) +{ + ASSERT(m_cacheDirectory.isNull()); + ASSERT(!cacheDirectory.isNull()); + + m_cacheDirectory = cacheDirectory; +} + +bool ApplicationCacheStorage::executeSQLCommand(const String& sql) +{ + ASSERT(m_database.isOpen()); + + bool result = m_database.executeCommand(sql); + if (!result) + LOG_ERROR("Application Cache Storage: failed to execute statement \"%s\" error \"%s\"", + sql.utf8().data(), m_database.lastErrorMsg()); + + return result; +} + +static const int SchemaVersion = 2; + +void ApplicationCacheStorage::verifySchemaVersion() +{ + if (m_database.tableExists("SchemaVersion")) { + int version = SQLiteStatement(m_database, "SELECT version from SchemaVersion").getColumnInt(0); + + if (version == SchemaVersion) + return; + } + + m_database.clearAllTables(); + + SQLiteTransaction createSchemaVersionTable(m_database); + createSchemaVersionTable.begin(); + + executeSQLCommand("CREATE TABLE SchemaVersion (version INTEGER NOT NULL)"); + SQLiteStatement statement(m_database, "INSERT INTO SchemaVersion (version) VALUES (?)"); + if (statement.prepare() != SQLResultOk) + return; + + statement.bindInt64(1, SchemaVersion); + executeStatement(statement); + createSchemaVersionTable.commit(); +} + +void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist) +{ + if (m_database.isOpen()) + return; + + // The cache directory should never be null, but if it for some weird reason is we bail out. + if (m_cacheDirectory.isNull()) + return; + + String applicationCachePath = pathByAppendingComponent(m_cacheDirectory, "ApplicationCache.db"); + if (!createIfDoesNotExist && !fileExists(applicationCachePath)) + return; + + makeAllDirectories(m_cacheDirectory); + m_database.open(applicationCachePath); + + if (!m_database.isOpen()) + return; + + verifySchemaVersion(); + + // Create tables + executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheGroups (id INTEGER PRIMARY KEY AUTOINCREMENT, " + "manifestHostHash INTEGER NOT NULL ON CONFLICT FAIL, manifestURL TEXT UNIQUE ON CONFLICT FAIL, newestCache INTEGER)"); + executeSQLCommand("CREATE TABLE IF NOT EXISTS Caches (id INTEGER PRIMARY KEY AUTOINCREMENT, cacheGroup INTEGER)"); + executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheWhitelistURLs (url TEXT NOT NULL ON CONFLICT FAIL, cache INTEGER NOT NULL ON CONFLICT FAIL)"); + executeSQLCommand("CREATE TABLE IF NOT EXISTS FallbackURLs (namespace TEXT NOT NULL ON CONFLICT FAIL, fallbackURL TEXT NOT NULL ON CONFLICT FAIL, " + "cache INTEGER NOT NULL ON CONFLICT FAIL)"); + executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheEntries (cache INTEGER NOT NULL ON CONFLICT FAIL, type INTEGER, resource INTEGER NOT NULL)"); + executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResources (id INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT NOT NULL ON CONFLICT FAIL, " + "statusCode INTEGER NOT NULL, responseURL TEXT NOT NULL, mimeType TEXT, textEncodingName TEXT, headers TEXT, data INTEGER NOT NULL ON CONFLICT FAIL)"); + executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResourceData (id INTEGER PRIMARY KEY AUTOINCREMENT, data BLOB)"); + + // When a cache is deleted, all its entries and its whitelist should be deleted. + executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheDeleted AFTER DELETE ON Caches" + " FOR EACH ROW BEGIN" + " DELETE FROM CacheEntries WHERE cache = OLD.id;" + " DELETE FROM CacheWhitelistURLs WHERE cache = OLD.id;" + " DELETE FROM FallbackURLs WHERE cache = OLD.id;" + " END"); + + // When a cache resource is deleted, its data blob should also be deleted. + executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheResourceDeleted AFTER DELETE ON CacheResources" + " FOR EACH ROW BEGIN" + " DELETE FROM CacheResourceData WHERE id = OLD.data;" + " END"); +} + +bool ApplicationCacheStorage::executeStatement(SQLiteStatement& statement) +{ + bool result = statement.executeCommand(); + if (!result) + LOG_ERROR("Application Cache Storage: failed to execute statement \"%s\" error \"%s\"", + statement.query().utf8().data(), m_database.lastErrorMsg()); + + return result; +} + +bool ApplicationCacheStorage::store(ApplicationCacheGroup* group) +{ + ASSERT(group->storageID() == 0); + + SQLiteStatement statement(m_database, "INSERT INTO CacheGroups (manifestHostHash, manifestURL) VALUES (?, ?)"); + if (statement.prepare() != SQLResultOk) + return false; + + statement.bindInt64(1, urlHostHash(group->manifestURL())); + statement.bindText(2, group->manifestURL()); + + if (!executeStatement(statement)) + return false; + + group->setStorageID((unsigned)m_database.lastInsertRowID()); + return true; +} + +bool ApplicationCacheStorage::store(ApplicationCache* cache) +{ + ASSERT(cache->storageID() == 0); + ASSERT(cache->group()->storageID() != 0); + + SQLiteStatement statement(m_database, "INSERT INTO Caches (cacheGroup) VALUES (?)"); + if (statement.prepare() != SQLResultOk) + return false; + + statement.bindInt64(1, cache->group()->storageID()); + + if (!executeStatement(statement)) + return false; + + unsigned cacheStorageID = (unsigned)m_database.lastInsertRowID(); + + // Store all resources + { + ApplicationCache::ResourceMap::const_iterator end = cache->end(); + for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) { + if (!store(it->second.get(), cacheStorageID)) + return false; + } + } + + // Store the online whitelist + const HashSet<String>& onlineWhitelist = cache->onlineWhitelist(); + { + HashSet<String>::const_iterator end = onlineWhitelist.end(); + for (HashSet<String>::const_iterator it = onlineWhitelist.begin(); it != end; ++it) { + SQLiteStatement statement(m_database, "INSERT INTO CacheWhitelistURLs (url, cache) VALUES (?, ?)"); + statement.prepare(); + + statement.bindText(1, *it); + statement.bindInt64(2, cacheStorageID); + + if (!executeStatement(statement)) + return false; + } + } + + cache->setStorageID(cacheStorageID); + return true; +} + +bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned cacheStorageID) +{ + ASSERT(cacheStorageID); + ASSERT(!resource->storageID()); + + openDatabase(true); + + // First, insert the data + SQLiteStatement dataStatement(m_database, "INSERT INTO CacheResourceData (data) VALUES (?)"); + if (dataStatement.prepare() != SQLResultOk) + return false; + + if (resource->data()->size()) + dataStatement.bindBlob(1, resource->data()->data(), resource->data()->size()); + + if (!dataStatement.executeCommand()) + return false; + + unsigned dataId = (unsigned)m_database.lastInsertRowID(); + + // Then, insert the resource + + // Serialize the headers + Vector<UChar> stringBuilder; + + HTTPHeaderMap::const_iterator end = resource->response().httpHeaderFields().end(); + for (HTTPHeaderMap::const_iterator it = resource->response().httpHeaderFields().begin(); it!= end; ++it) { + stringBuilder.append(it->first.characters(), it->first.length()); + stringBuilder.append((UChar)':'); + stringBuilder.append(it->second.characters(), it->second.length()); + stringBuilder.append((UChar)'\n'); + } + + String headers = String::adopt(stringBuilder); + + SQLiteStatement resourceStatement(m_database, "INSERT INTO CacheResources (url, statusCode, responseURL, headers, data, mimeType, textEncodingName) VALUES (?, ?, ?, ?, ?, ?, ?)"); + if (resourceStatement.prepare() != SQLResultOk) + return false; + + resourceStatement.bindText(1, resource->url()); + resourceStatement.bindInt64(2, resource->response().httpStatusCode()); + resourceStatement.bindText(3, resource->response().url()); + resourceStatement.bindText(4, headers); + resourceStatement.bindInt64(5, dataId); + resourceStatement.bindText(6, resource->response().mimeType()); + resourceStatement.bindText(7, resource->response().textEncodingName()); + + if (!executeStatement(resourceStatement)) + return false; + + unsigned resourceId = (unsigned)m_database.lastInsertRowID(); + + // Finally, insert the cache entry + SQLiteStatement entryStatement(m_database, "INSERT INTO CacheEntries (cache, type, resource) VALUES (?, ?, ?)"); + if (entryStatement.prepare() != SQLResultOk) + return false; + + entryStatement.bindInt64(1, cacheStorageID); + entryStatement.bindInt64(2, resource->type()); + entryStatement.bindInt64(3, resourceId); + + if (!executeStatement(entryStatement)) + return false; + + resource->setStorageID(resourceId); + return true; +} + +void ApplicationCacheStorage::store(ApplicationCacheResource* resource, ApplicationCache* cache) +{ + ASSERT(cache->storageID()); + + openDatabase(true); + + SQLiteTransaction storeResourceTransaction(m_database); + storeResourceTransaction.begin(); + + if (!store(resource, cache->storageID())) + return; + + storeResourceTransaction.commit(); +} + +bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group) +{ + openDatabase(true); + + SQLiteTransaction storeCacheTransaction(m_database); + + storeCacheTransaction.begin(); + + if (!group->storageID()) { + // Store the group + if (!store(group)) + return false; + } + + ASSERT(group->newestCache()); + ASSERT(!group->newestCache()->storageID()); + + // Store the newest cache + if (!store(group->newestCache())) + return false; + + // Update the newest cache in the group. + + SQLiteStatement statement(m_database, "UPDATE CacheGroups SET newestCache=? WHERE id=?"); + if (statement.prepare() != SQLResultOk) + return false; + + statement.bindInt64(1, group->newestCache()->storageID()); + statement.bindInt64(2, group->storageID()); + + if (!executeStatement(statement)) + return false; + + storeCacheTransaction.commit(); + return true; +} + +static inline void parseHeader(const UChar* header, size_t headerLength, ResourceResponse& response) +{ + int pos = find(header, headerLength, ':'); + ASSERT(pos != -1); + + String headerName = String(header, pos); + String headerValue = String(header + pos + 1, headerLength - pos - 1); + + response.setHTTPHeaderField(headerName, headerValue); +} + +static inline void parseHeaders(const String& headers, ResourceResponse& response) +{ + int startPos = 0; + int endPos; + while ((endPos = headers.find('\n', startPos)) != -1) { + ASSERT(startPos != endPos); + + parseHeader(headers.characters() + startPos, endPos - startPos, response); + + startPos = endPos + 1; + } + + if (startPos != static_cast<int>(headers.length())) + parseHeader(headers.characters(), headers.length(), response); +} + +PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storageID) +{ + SQLiteStatement cacheStatement(m_database, + "SELECT url, type, mimeType, textEncodingName, headers, CacheResourceData.data FROM CacheEntries INNER JOIN CacheResources ON CacheEntries.resource=CacheResources.id " + "INNER JOIN CacheResourceData ON CacheResourceData.id=CacheResources.data WHERE CacheEntries.cache=?"); + if (cacheStatement.prepare() != SQLResultOk) { + LOG_ERROR("Could not prepare cache statement, error \"%s\"", m_database.lastErrorMsg()); + return 0; + } + + cacheStatement.bindInt64(1, storageID); + + RefPtr<ApplicationCache> cache = ApplicationCache::create(); + + int result; + while ((result = cacheStatement.step()) == SQLResultRow) { + KURL url(cacheStatement.getColumnText(0)); + + unsigned type = (unsigned)cacheStatement.getColumnInt64(1); + + Vector<char> blob; + cacheStatement.getColumnBlobAsVector(5, blob); + + RefPtr<SharedBuffer> data = SharedBuffer::adoptVector(blob); + + String mimeType = cacheStatement.getColumnText(2); + String textEncodingName = cacheStatement.getColumnText(3); + + ResourceResponse response(url, mimeType, data->size(), textEncodingName, ""); + + String headers = cacheStatement.getColumnText(4); + parseHeaders(headers, response); + + RefPtr<ApplicationCacheResource> resource = ApplicationCacheResource::create(url, response, type, data.release()); + + if (type & ApplicationCacheResource::Manifest) + cache->setManifestResource(resource.release()); + else + cache->addResource(resource.release()); + } + + if (result != SQLResultDone) + LOG_ERROR("Could not load cache resources, error \"%s\"", m_database.lastErrorMsg()); + + // Load the online whitelist + SQLiteStatement whitelistStatement(m_database, "SELECT url FROM CacheWhitelistURLs WHERE cache=?"); + if (whitelistStatement.prepare() != SQLResultOk) + return 0; + whitelistStatement.bindInt64(1, storageID); + + HashSet<String> whitelist; + while ((result = whitelistStatement.step()) == SQLResultRow) + whitelist.add(whitelistStatement.getColumnText(0)); + + if (result != SQLResultDone) + LOG_ERROR("Could not load cache online whitelist, error \"%s\"", m_database.lastErrorMsg()); + + cache->setOnlineWhitelist(whitelist); + + cache->setStorageID(storageID); + + return cache.release(); +} + +void ApplicationCacheStorage::remove(ApplicationCache* cache) +{ + if (!cache->storageID()) + return; + + openDatabase(false); + if (!m_database.isOpen()) + return; + + SQLiteStatement statement(m_database, "DELETE FROM Caches WHERE id=?"); + if (statement.prepare() != SQLResultOk) + return; + + statement.bindInt64(1, cache->storageID()); + executeStatement(statement); +} + +void ApplicationCacheStorage::empty() +{ + openDatabase(false); + + if (!m_database.isOpen()) + return; + + // Clear cache groups, caches and cache resources. + executeSQLCommand("DELETE FROM CacheGroups"); + executeSQLCommand("DELETE FROM Caches"); + executeSQLCommand("DELETE FROM CacheResources"); + + // Clear the storage IDs for the caches in memory. + // The caches will still work, but cached resources will not be saved to disk + // until a cache update process has been initiated. + CacheGroupMap::const_iterator end = m_cachesInMemory.end(); + for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it) + it->second->clearStorageID(); +} + +bool ApplicationCacheStorage::storeCopyOfCache(const String& cacheDirectory, ApplicationCache* cache) +{ + // Create a new cache. + RefPtr<ApplicationCache> cacheCopy = ApplicationCache::create(); + + // Set the online whitelist + cacheCopy->setOnlineWhitelist(cache->onlineWhitelist()); + + // Traverse the cache and add copies of all resources. + ApplicationCache::ResourceMap::const_iterator end = cache->end(); + for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) { + ApplicationCacheResource* resource = it->second.get(); + + RefPtr<ApplicationCacheResource> resourceCopy = ApplicationCacheResource::create(resource->url(), resource->response(), resource->type(), resource->data()); + + cacheCopy->addResource(resourceCopy.release()); + } + + // Now create a new cache group. + OwnPtr<ApplicationCacheGroup> groupCopy(new ApplicationCacheGroup(cache->group()->manifestURL(), true)); + + groupCopy->setNewestCache(cacheCopy); + + ApplicationCacheStorage copyStorage; + copyStorage.setCacheDirectory(cacheDirectory); + + // Empty the cache in case something was there before. + copyStorage.empty(); + + return copyStorage.storeNewestCache(groupCopy.get()); +} + +ApplicationCacheStorage& cacheStorage() +{ + static ApplicationCacheStorage storage; + + return storage; +} + +} // namespace WebCore + +#endif // ENABLE(OFFLINE_WEB_APPLICATIONS) diff --git a/WebCore/loader/appcache/ApplicationCacheStorage.h b/WebCore/loader/appcache/ApplicationCacheStorage.h new file mode 100644 index 0000000..6bd9ba1 --- /dev/null +++ b/WebCore/loader/appcache/ApplicationCacheStorage.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 ApplicationCacheStorage_h +#define ApplicationCacheStorage_h + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + +#include "PlatformString.h" +#include "SQLiteDatabase.h" +#include "StringHash.h" + +#include <wtf/HashCountedSet.h> + +namespace WebCore { + +class ApplicationCache; +class ApplicationCacheGroup; +class ApplicationCacheResource; +class KURL; + +class ApplicationCacheStorage { +public: + void setCacheDirectory(const String&); + + ApplicationCacheGroup* cacheGroupForURL(const KURL&); + + ApplicationCacheGroup* findOrCreateCacheGroup(const KURL& manifestURL); + void cacheGroupDestroyed(ApplicationCacheGroup*); + + bool storeNewestCache(ApplicationCacheGroup*); + void store(ApplicationCacheResource*, ApplicationCache*); + + void remove(ApplicationCache*); + + void empty(); + + static bool storeCopyOfCache(const String& cacheDirectory, ApplicationCache*); +private: + PassRefPtr<ApplicationCache> loadCache(unsigned storageID); + ApplicationCacheGroup* loadCacheGroup(const KURL& manifestURL); + + bool store(ApplicationCacheGroup*); + bool store(ApplicationCache*); + bool store(ApplicationCacheResource*, unsigned cacheStorageID); + + void loadManifestHostHashes(); + + void verifySchemaVersion(); + + void openDatabase(bool createIfDoesNotExist); + + bool executeStatement(SQLiteStatement&); + bool executeSQLCommand(const String&); + + String m_cacheDirectory; + + SQLiteDatabase m_database; + + // In order to quickly determinate if a given resource exists in an application cache, + // we keep a hash set of the hosts of the manifest URLs of all cache groups. + HashCountedSet<unsigned, AlreadyHashed> m_cacheHostSet; + + typedef HashMap<String, ApplicationCacheGroup*> CacheGroupMap; + CacheGroupMap m_cachesInMemory; +}; + +ApplicationCacheStorage& cacheStorage(); + +} // namespace WebCore + +#endif // ENABLE(OFFLINE_WEB_APPLICATIONS) + +#endif // ApplicationCacheStorage_h diff --git a/WebCore/loader/appcache/DOMApplicationCache.cpp b/WebCore/loader/appcache/DOMApplicationCache.cpp new file mode 100644 index 0000000..f872a50 --- /dev/null +++ b/WebCore/loader/appcache/DOMApplicationCache.cpp @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#include "config.h" +#include "DOMApplicationCache.h" + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + +#include "ApplicationCache.h" +#include "ApplicationCacheGroup.h" +#include "DocumentLoader.h" +#include "Event.h" +#include "EventException.h" +#include "EventListener.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameLoader.h" + +namespace WebCore { + +DOMApplicationCache::DOMApplicationCache(Frame* frame) + : m_frame(frame) +{ +} + +void DOMApplicationCache::disconnectFrame() +{ + m_frame = 0; +} + +ApplicationCache* DOMApplicationCache::associatedCache() const +{ + if (!m_frame) + return 0; + + return m_frame->loader()->documentLoader()->topLevelApplicationCache(); +} + +unsigned short DOMApplicationCache::status() const +{ + ApplicationCache* cache = associatedCache(); + if (!cache) + return UNCACHED; + + switch (cache->group()->status()) { + case ApplicationCacheGroup::Checking: + return CHECKING; + case ApplicationCacheGroup::Downloading: + return DOWNLOADING; + case ApplicationCacheGroup::Idle: { + if (cache != cache->group()->newestCache()) + return UPDATEREADY; + + return IDLE; + } + default: + ASSERT_NOT_REACHED(); + } + + return 0; +} + +void DOMApplicationCache::update(ExceptionCode& ec) +{ + ApplicationCache* cache = associatedCache(); + if (!cache) { + ec = INVALID_STATE_ERR; + return; + } + + cache->group()->update(m_frame); +} + +bool DOMApplicationCache::swapCache() +{ + if (!m_frame) + return false; + + ApplicationCache* cache = m_frame->loader()->documentLoader()->applicationCache(); + if (!cache) + return false; + + // Check if we already have the newest cache + ApplicationCache* newestCache = cache->group()->newestCache(); + if (cache == newestCache) + return false; + + ASSERT(cache->group() == newestCache->group()); + m_frame->loader()->documentLoader()->setApplicationCache(newestCache); + + return true; +} + +void DOMApplicationCache::swapCache(ExceptionCode& ec) +{ + if (!swapCache()) + ec = INVALID_STATE_ERR; +} + +unsigned DOMApplicationCache::length() const +{ + ApplicationCache* cache = associatedCache(); + if (!cache) + return 0; + + return cache->numDynamicEntries(); +} + +String DOMApplicationCache::item(unsigned item, ExceptionCode& ec) +{ + ApplicationCache* cache = associatedCache(); + if (!cache) { + ec = INVALID_STATE_ERR; + return String(); + } + + if (item >= length()) { + ec = INDEX_SIZE_ERR; + return String(); + } + + return cache->dynamicEntry(item); +} + +void DOMApplicationCache::add(const KURL& url, ExceptionCode& ec) +{ + ApplicationCache* cache = associatedCache(); + if (!cache) { + ec = INVALID_STATE_ERR; + return; + } + + if (!url.isValid()) { + ec = SYNTAX_ERR; + return; + } + + if (!cache->addDynamicEntry(url)) { + // This should use the (currently not specified) security exceptions in HTML5 4.3.4 + ec = SECURITY_ERR; + } +} + +void DOMApplicationCache::remove(const KURL& url, ExceptionCode& ec) +{ + ApplicationCache* cache = associatedCache(); + if (!cache) { + ec = INVALID_STATE_ERR; + return; + } + + cache->removeDynamicEntry(url); +} + +ScriptExecutionContext* DOMApplicationCache::scriptExecutionContext() const +{ + return m_frame->document(); +} + +void DOMApplicationCache::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool) +{ + EventListenersMap::iterator iter = m_eventListeners.find(eventType); + if (iter == m_eventListeners.end()) { + ListenerVector listeners; + listeners.append(eventListener); + m_eventListeners.add(eventType, listeners); + } else { + ListenerVector& listeners = iter->second; + for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) { + if (*listenerIter == eventListener) + return; + } + + listeners.append(eventListener); + m_eventListeners.add(eventType, listeners); + } +} + +void DOMApplicationCache::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool useCapture) +{ + EventListenersMap::iterator iter = m_eventListeners.find(eventType); + if (iter == m_eventListeners.end()) + return; + + ListenerVector& listeners = iter->second; + for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) { + if (*listenerIter == eventListener) { + listeners.remove(listenerIter - listeners.begin()); + return; + } + } +} + +bool DOMApplicationCache::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec) +{ + if (event->type().isEmpty()) { + ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR; + return true; + } + + ListenerVector listenersCopy = m_eventListeners.get(event->type()); + for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) { + event->setTarget(this); + event->setCurrentTarget(this); + listenerIter->get()->handleEvent(event.get(), false); + } + + return !event->defaultPrevented(); +} + +void DOMApplicationCache::callListener(const AtomicString& eventType, EventListener* listener) +{ + ASSERT(m_frame); + + RefPtr<Event> event = Event::create(eventType, false, false); + if (listener) { + event->setTarget(this); + event->setCurrentTarget(this); + listener->handleEvent(event.get(), false); + } + + ExceptionCode ec = 0; + dispatchEvent(event.release(), ec); + ASSERT(!ec); +} + +void DOMApplicationCache::callCheckingListener() +{ + callListener(eventNames().checkingEvent, m_onCheckingListener.get()); +} + +void DOMApplicationCache::callErrorListener() +{ + callListener(eventNames().errorEvent, m_onErrorListener.get()); +} + +void DOMApplicationCache::callNoUpdateListener() +{ + callListener(eventNames().noupdateEvent, m_onNoUpdateListener.get()); +} + +void DOMApplicationCache::callDownloadingListener() +{ + callListener(eventNames().downloadingEvent, m_onDownloadingListener.get()); +} + +void DOMApplicationCache::callProgressListener() +{ + callListener(eventNames().progressEvent, m_onProgressListener.get()); +} + +void DOMApplicationCache::callUpdateReadyListener() +{ + callListener(eventNames().updatereadyEvent, m_onUpdateReadyListener.get()); +} + +void DOMApplicationCache::callCachedListener() +{ + callListener(eventNames().cachedEvent, m_onCachedListener.get()); +} + +} // namespace WebCore + +#endif // ENABLE(OFFLINE_WEB_APPLICATIONS) diff --git a/WebCore/loader/appcache/DOMApplicationCache.h b/WebCore/loader/appcache/DOMApplicationCache.h new file mode 100644 index 0000000..30c0b7e --- /dev/null +++ b/WebCore/loader/appcache/DOMApplicationCache.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 DOMApplicationCache_h +#define DOMApplicationCache_h + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + +#include "AtomicStringHash.h" +#include "EventTarget.h" +#include "EventListener.h" +#include <wtf/HashMap.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class ApplicationCache; +class AtomicStringImpl; +class Frame; +class KURL; +class String; + +class DOMApplicationCache : public RefCounted<DOMApplicationCache>, public EventTarget { +public: + static PassRefPtr<DOMApplicationCache> create(Frame* frame) { return adoptRef(new DOMApplicationCache(frame)); } + void disconnectFrame(); + + enum Status { + UNCACHED = 0, + IDLE = 1, + CHECKING = 2, + DOWNLOADING = 3, + UPDATEREADY = 4, + }; + + unsigned short status() const; + + void update(ExceptionCode&); + void swapCache(ExceptionCode&); + + unsigned length() const; + String item(unsigned item, ExceptionCode&); + void add(const KURL&, ExceptionCode&); + void remove(const KURL&, ExceptionCode&); + + virtual void addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); + virtual void removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture); + virtual bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&); + + typedef Vector<RefPtr<EventListener> > ListenerVector; + typedef HashMap<AtomicString, ListenerVector> EventListenersMap; + EventListenersMap& eventListeners() { return m_eventListeners; } + + using RefCounted<DOMApplicationCache>::ref; + using RefCounted<DOMApplicationCache>::deref; + + void setOnchecking(PassRefPtr<EventListener> eventListener) { m_onCheckingListener = eventListener; } + EventListener* onchecking() const { return m_onCheckingListener.get(); } + + void setOnerror(PassRefPtr<EventListener> eventListener) { m_onErrorListener = eventListener; } + EventListener* onerror() const { return m_onErrorListener.get(); } + + void setOnnoupdate(PassRefPtr<EventListener> eventListener) { m_onNoUpdateListener = eventListener; } + EventListener* onnoupdate() const { return m_onNoUpdateListener.get(); } + + void setOndownloading(PassRefPtr<EventListener> eventListener) { m_onDownloadingListener = eventListener; } + EventListener* ondownloading() const { return m_onDownloadingListener.get(); } + + void setOnprogress(PassRefPtr<EventListener> eventListener) { m_onProgressListener = eventListener; } + EventListener* onprogress() const { return m_onProgressListener.get(); } + + void setOnupdateready(PassRefPtr<EventListener> eventListener) { m_onUpdateReadyListener = eventListener; } + EventListener* onupdateready() const { return m_onUpdateReadyListener.get(); } + + void setOncached(PassRefPtr<EventListener> eventListener) { m_onCachedListener = eventListener; } + EventListener* oncached() const { return m_onCachedListener.get(); } + + virtual ScriptExecutionContext* scriptExecutionContext() const; + DOMApplicationCache* toDOMApplicationCache() { return this; } + + void callCheckingListener(); + void callErrorListener(); + void callNoUpdateListener(); + void callDownloadingListener(); + void callProgressListener(); + void callUpdateReadyListener(); + void callCachedListener(); + +private: + DOMApplicationCache(Frame*); + void callListener(const AtomicString& eventType, EventListener*); + + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + + ApplicationCache* associatedCache() const; + bool swapCache(); + + RefPtr<EventListener> m_onCheckingListener; + RefPtr<EventListener> m_onErrorListener; + RefPtr<EventListener> m_onNoUpdateListener; + RefPtr<EventListener> m_onDownloadingListener; + RefPtr<EventListener> m_onProgressListener; + RefPtr<EventListener> m_onUpdateReadyListener; + RefPtr<EventListener> m_onCachedListener; + + EventListenersMap m_eventListeners; + + Frame* m_frame; +}; + +} // namespace WebCore + +#endif // ENABLE(OFFLINE_WEB_APPLICATIONS) + +#endif // DOMApplicationCache_h diff --git a/WebCore/loader/appcache/DOMApplicationCache.idl b/WebCore/loader/appcache/DOMApplicationCache.idl new file mode 100644 index 0000000..94326f3 --- /dev/null +++ b/WebCore/loader/appcache/DOMApplicationCache.idl @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +module offline { + + interface [ + Conditional=OFFLINE_WEB_APPLICATIONS, + CustomMarkFunction + ] DOMApplicationCache { + // update status + const unsigned short UNCACHED = 0; + const unsigned short IDLE = 1; + const unsigned short CHECKING = 2; + const unsigned short DOWNLOADING = 3; + const unsigned short UPDATEREADY = 4; + readonly attribute unsigned short status; + + void update() + raises(DOMException); + void swapCache() + raises(DOMException); + + // dynamic entries + readonly attribute unsigned long length; + DOMString item(in [IsIndex] unsigned long index) + raises(DOMException); + [Custom] void add(in DOMString uri) + raises(DOMException); + [Custom] void remove(in DOMString uri) + raises(DOMException); + + // events + attribute EventListener onchecking; + attribute EventListener onerror; + attribute EventListener onnoupdate; + attribute EventListener ondownloading; + attribute EventListener onprogress; + attribute EventListener onupdateready; + attribute EventListener oncached; + + // EventTarget interface + [Custom] void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + [Custom] void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + boolean dispatchEvent(in Event evt) + raises(EventException); + }; + +} diff --git a/WebCore/loader/appcache/ManifestParser.cpp b/WebCore/loader/appcache/ManifestParser.cpp new file mode 100644 index 0000000..778d22d --- /dev/null +++ b/WebCore/loader/appcache/ManifestParser.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#include "config.h" +#include "ManifestParser.h" + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + +#include "CharacterNames.h" +#include "KURL.h" +#include "TextEncoding.h" + +namespace WebCore { + +enum Mode { Explicit, Fallback, OnlineWhitelist }; + +bool parseManifest(const KURL& manifestURL, const char* data, int length, Manifest& manifest) +{ + ASSERT(manifest.explicitURLs.isEmpty()); + ASSERT(manifest.onlineWhitelistedURLs.isEmpty()); + ASSERT(manifest.fallbackURLs.isEmpty()); + + Mode mode = Explicit; + String s = UTF8Encoding().decode(data, length); + + if (s.isEmpty()) + return false; + + // Replace nulls with U+FFFD REPLACEMENT CHARACTER + s.replace(0, replacementCharacter); + + // Look for the magic signature + if (!s.startsWith("CACHE MANIFEST")) { + // The magic signature was not found. + return false; + } + + const UChar* end = s.characters() + s.length(); + const UChar* p = s.characters() + 14; // "CACHE MANIFEST" is 14 characters. + + while (p < end) { + // Skip whitespace + if (*p == ' ' || *p == '\t') { + p++; + } else + break; + } + + if (p < end && *p != '\n' && *p != '\r') { + // The magic signature was invalid + return false; + } + + while (1) { + // Skip whitespace + while (p < end && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t')) + p++; + + if (p == end) + break; + + const UChar* lineStart = p; + + // Find the end of the line + while (p < end && *p != '\r' && *p != '\n') + p++; + + // Check if we have a comment + if (*lineStart == '#') + continue; + + // Get rid of trailing whitespace + const UChar* tmp = p - 1; + while (tmp > lineStart && (*tmp == ' ' || *tmp == '\t')) + tmp--; + + String line(lineStart, tmp - lineStart + 1); + + if (line == "CACHE:") + mode = Explicit; + else if (line == "FALLBACK:") + mode = Fallback; + else if (line == "NETWORK:") + mode = OnlineWhitelist; + else if (mode == Explicit || mode == OnlineWhitelist) { + KURL url(manifestURL, line); + + if (!url.isValid()) + continue; + + if (url.hasRef()) + url.setRef(String()); + + if (!equalIgnoringCase(url.protocol(), manifestURL.protocol())) + continue; + + if (mode == Explicit) + manifest.explicitURLs.add(url.string()); + else + manifest.onlineWhitelistedURLs.add(url.string()); + + } else if (mode == Fallback) { + const UChar *p = line.characters(); + const UChar *lineEnd = p + line.length(); + + // Look for whitespace separating the two URLs + while (p < lineEnd && *p != '\t' && *p != ' ') + p++; + + if (p == lineEnd) { + // There was no whitespace separating the URLs. + continue; + } + + KURL namespaceURL(manifestURL, String(line.characters(), p - line.characters())); + if (!namespaceURL.isValid()) + continue; + + // Check that the namespace URL has the same scheme/host/port as the manifest URL. + if (!protocolHostAndPortAreEqual(manifestURL, namespaceURL)) + continue; + + while (p < lineEnd && (*p == '\t' || *p == ' ')) + p++; + + KURL fallbackURL(String(p, line.length() - (p - line.characters()))); + + if (!fallbackURL.isValid()) + continue; + + if (!equalIgnoringCase(fallbackURL.protocol(), manifestURL.protocol())) + continue; + + manifest.fallbackURLs.add(namespaceURL, fallbackURL); + } else + ASSERT_NOT_REACHED(); + } + + return true; +} + +} + +#endif // ENABLE(OFFLINE_WEB_APPLICATIONS) diff --git a/WebCore/loader/mac/ImageDocumentMac.mm b/WebCore/loader/appcache/ManifestParser.h index 755f448..f4fe31a 100644 --- a/WebCore/loader/mac/ImageDocumentMac.mm +++ b/WebCore/loader/appcache/ManifestParser.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,38 +10,43 @@ * 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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. */ -#import "config.h" -#import "ImageDocumentMac.h" +#ifndef ManifestParser_h +#define ManifestParser_h -#include "CachedImage.h" -#include "Document.h" -#include "FrameLoader.h" -#include "Frame.h" -#include "WebCoreFrameBridge.h" -#include "DocumentLoader.h" +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include "StringHash.h" +#include "PlatformString.h" namespace WebCore { - -void finishImageLoad(Document* document, CachedImage* image) -{ - Frame* frame = document->frame(); - const ResourceResponse& response = frame->loader()->documentLoader()->response(); - - IntSize size = image->imageSize(); - if (size.width()) - document->setTitle([frame->bridge() imageTitleForFilename:response.suggestedFilename() size:size]); -} - + +class KURL; + +struct Manifest { + HashSet<String> onlineWhitelistedURLs; + HashSet<String> explicitURLs; + HashMap<String, String> fallbackURLs; +}; + +bool parseManifest(const KURL& manifestURL, const char* data, int length, Manifest&); + } + +#endif // ENABLE(OFFLINE_WEB_APPLICATIONS) + +#endif // ManifestParser_h diff --git a/WebCore/loader/archive/Archive.h b/WebCore/loader/archive/Archive.h new file mode 100644 index 0000000..af3d8b1 --- /dev/null +++ b/WebCore/loader/archive/Archive.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 Archive_h +#define Archive_h + +#include "ArchiveResource.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Archive : public RefCounted<Archive> { +public: + ArchiveResource* mainResource() { return m_mainResource.get(); } + const Vector<RefPtr<ArchiveResource> >& subresources() const { return m_subresources; } + const Vector<RefPtr<Archive> >& subframeArchives() const { return m_subframeArchives; } + +protected: + // These methods are meant for subclasses for different archive types to add resources in to the archive, + // and should not be exposed as archives should be immutable to clients + void setMainResource(PassRefPtr<ArchiveResource> mainResource) { m_mainResource = mainResource; } + void addSubresource(PassRefPtr<ArchiveResource> subResource) { m_subresources.append(subResource); } + void addSubframeArchive(PassRefPtr<Archive> subframeArchive) { m_subframeArchives.append(subframeArchive); } + +private: + RefPtr<ArchiveResource> m_mainResource; + Vector<RefPtr<ArchiveResource> > m_subresources; + Vector<RefPtr<Archive> > m_subframeArchives; +}; + +} + +#endif // Archive diff --git a/WebCore/loader/archive/ArchiveFactory.cpp b/WebCore/loader/archive/ArchiveFactory.cpp new file mode 100644 index 0000000..c42b5df --- /dev/null +++ b/WebCore/loader/archive/ArchiveFactory.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#include "config.h" +#include "ArchiveFactory.h" + +#if PLATFORM(CF) +#include "LegacyWebArchive.h" +#endif +#include "MIMETypeRegistry.h" +#include "PlatformString.h" + +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> + +namespace WebCore { + +typedef PassRefPtr<Archive> RawDataCreationFunction(SharedBuffer*); + +// The create functions in the archive classes return PassRefPtr to concrete subclasses +// of Archive. This adaptor makes the functions have a uniform return type. +template <typename ArchiveClass> static PassRefPtr<Archive> archiveFactoryCreate(SharedBuffer* buffer) +{ + return ArchiveClass::create(buffer); +} + +static HashMap<String, RawDataCreationFunction*, CaseFoldingHash>& archiveMIMETypes() +{ + static HashMap<String, RawDataCreationFunction*, CaseFoldingHash> mimeTypes; + static bool initialized = false; + + if (initialized) + return mimeTypes; + +#if PLATFORM(CF) + mimeTypes.set("application/x-webarchive", archiveFactoryCreate<LegacyWebArchive>); +#endif + + initialized = true; + return mimeTypes; +} + +bool ArchiveFactory::isArchiveMimeType(const String& mimeType) +{ + return archiveMIMETypes().contains(mimeType); +} + +PassRefPtr<Archive> ArchiveFactory::create(SharedBuffer* data, const String& mimeType) +{ + RawDataCreationFunction* function = archiveMIMETypes().get(mimeType); + return function ? function(data) : 0; +} + +void ArchiveFactory::registerKnownArchiveMIMETypes() +{ + HashSet<String>& mimeTypes = MIMETypeRegistry::getSupportedNonImageMIMETypes(); + HashMap<String, RawDataCreationFunction*, CaseFoldingHash>::iterator i = archiveMIMETypes().begin(); + HashMap<String, RawDataCreationFunction*, CaseFoldingHash>::iterator end = archiveMIMETypes().end(); + + for (; i != end; ++i) + mimeTypes.add(i->first); +} + +} diff --git a/WebCore/loader/archive/ArchiveFactory.h b/WebCore/loader/archive/ArchiveFactory.h new file mode 100644 index 0000000..bf1d5c6 --- /dev/null +++ b/WebCore/loader/archive/ArchiveFactory.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 ArchiveFactory_h +#define ArchiveFactory_h + +#include "Archive.h" + +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class SharedBuffer; +class String; + +class ArchiveFactory { +public: + static bool isArchiveMimeType(const String&); + static PassRefPtr<Archive> create(SharedBuffer* data, const String& mimeType); + static void registerKnownArchiveMIMETypes(); +}; + +} + +#endif // ArchiveFactory_h diff --git a/WebCore/loader/archive/ArchiveResource.cpp b/WebCore/loader/archive/ArchiveResource.cpp new file mode 100644 index 0000000..691f66a --- /dev/null +++ b/WebCore/loader/archive/ArchiveResource.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#include "config.h" +#include "ArchiveResource.h" + +#include "SharedBuffer.h" + +namespace WebCore { + +PassRefPtr<ArchiveResource> ArchiveResource::create(PassRefPtr<SharedBuffer> data, const KURL& url, const ResourceResponse& response) +{ + return data ? adoptRef(new ArchiveResource(data, url, response)) : 0; +} + +PassRefPtr<ArchiveResource> ArchiveResource::create(PassRefPtr<SharedBuffer> data, const KURL& url, const String& mimeType, const String& textEncoding, const String& frameName) +{ + return data ? adoptRef(new ArchiveResource(data, url, mimeType, textEncoding, frameName)) : 0; +} + +PassRefPtr<ArchiveResource> ArchiveResource::create(PassRefPtr<SharedBuffer> data, const KURL& url, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse& resourceResponse) +{ + return data ? adoptRef(new ArchiveResource(data, url, mimeType, textEncoding, frameName, resourceResponse)) : 0; +} + +ArchiveResource::ArchiveResource(PassRefPtr<SharedBuffer> data, const KURL& url, const ResourceResponse& response) + : SubstituteResource(url, response, data) + , m_mimeType(response.mimeType()) + , m_textEncoding(response.textEncodingName()) + , m_shouldIgnoreWhenUnarchiving(false) +{ +} + +ArchiveResource::ArchiveResource(PassRefPtr<SharedBuffer> data, const KURL& url, const String& mimeType, const String& textEncoding, const String& frameName) + : SubstituteResource(url, ResourceResponse(url, mimeType, data ? data->size() : 0, textEncoding, String()), data) + , m_mimeType(mimeType) + , m_textEncoding(textEncoding) + , m_frameName(frameName) + , m_shouldIgnoreWhenUnarchiving(false) +{ +} + +ArchiveResource::ArchiveResource(PassRefPtr<SharedBuffer> data, const KURL& url, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse& response) + : SubstituteResource(url, response.isNull() ? ResourceResponse(url, mimeType, data ? data->size() : 0, textEncoding, String()) : response, data) + , m_mimeType(mimeType) + , m_textEncoding(textEncoding) + , m_frameName(frameName) + , m_shouldIgnoreWhenUnarchiving(false) +{ +} + +} diff --git a/WebCore/loader/archive/ArchiveResource.h b/WebCore/loader/archive/ArchiveResource.h new file mode 100644 index 0000000..d975e04 --- /dev/null +++ b/WebCore/loader/archive/ArchiveResource.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 ArchiveResource_h +#define ArchiveResource_h + +#include "SubstituteResource.h" + +#include "PlatformString.h" + +namespace WebCore { + +class ArchiveResource : public SubstituteResource { +public: + static PassRefPtr<ArchiveResource> create(PassRefPtr<SharedBuffer>, const KURL&, const ResourceResponse&); + static PassRefPtr<ArchiveResource> create(PassRefPtr<SharedBuffer>, const KURL&, const String& mimeType, const String& textEncoding, const String& frameName); + static PassRefPtr<ArchiveResource> create(PassRefPtr<SharedBuffer>, const KURL&, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse&); + + const String& mimeType() const { return m_mimeType; } + const String& textEncoding() const { return m_textEncoding; } + const String& frameName() const { return m_frameName; } + + void ignoreWhenUnarchiving() { m_shouldIgnoreWhenUnarchiving = true; } + bool shouldIgnoreWhenUnarchiving() const { return m_shouldIgnoreWhenUnarchiving; } + +private: + ArchiveResource(PassRefPtr<SharedBuffer>, const KURL&, const ResourceResponse&); + ArchiveResource(PassRefPtr<SharedBuffer>, const KURL&, const String& mimeType, const String& textEncoding, const String& frameName); + ArchiveResource(PassRefPtr<SharedBuffer>, const KURL&, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse&); + + String m_mimeType; + String m_textEncoding; + String m_frameName; + + bool m_shouldIgnoreWhenUnarchiving; +}; + +} + +#endif // ArchiveResource_h diff --git a/WebCore/loader/archive/ArchiveResourceCollection.cpp b/WebCore/loader/archive/ArchiveResourceCollection.cpp new file mode 100644 index 0000000..6eb1237 --- /dev/null +++ b/WebCore/loader/archive/ArchiveResourceCollection.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#include "config.h" +#include "ArchiveResourceCollection.h" + +namespace WebCore { + +ArchiveResourceCollection::ArchiveResourceCollection() +{ +} + +void ArchiveResourceCollection::addAllResources(Archive* archive) +{ + ASSERT(archive); + if (!archive) + return; + + const Vector<RefPtr<ArchiveResource> >& subresources = archive->subresources(); + Vector<RefPtr<ArchiveResource> >::const_iterator iRes = subresources.begin(); + Vector<RefPtr<ArchiveResource> >::const_iterator endRes = subresources.end(); + + for (; iRes != endRes; ++iRes) + m_subresources.set((*iRes)->url(), iRes->get()); + + const Vector<RefPtr<Archive> >& subframes = archive->subframeArchives(); + Vector<RefPtr<Archive> >::const_iterator iFrame = subframes.begin(); + Vector<RefPtr<Archive> >::const_iterator endFrame = subframes.end(); + + for (; iFrame != endFrame; ++iFrame) { + ASSERT((*iFrame)->mainResource()); + const String& frameName = (*iFrame)->mainResource()->frameName(); + if (!frameName.isNull()) + m_subframes.set(frameName, iFrame->get()); + } +} + +// FIXME: Adding a resource directly to a DocumentLoader/ArchiveResourceCollection seems like bad design, but is API some apps rely on. +// Can we change the design in a manner that will let us deprecate that API without reducing functionality of those apps? +void ArchiveResourceCollection::addResource(PassRefPtr<ArchiveResource> resource) +{ + ASSERT(resource); + if (!resource) + return; + + const KURL& url = resource->url(); // get before passing PassRefPtr (which sets it to 0) + m_subresources.set(url, resource); +} + +ArchiveResource* ArchiveResourceCollection::archiveResourceForURL(const KURL& url) +{ + ArchiveResource* resource = m_subresources.get(url).get(); + if (!resource) + return 0; + + return resource; +} + +PassRefPtr<Archive> ArchiveResourceCollection::popSubframeArchive(const String& frameName) +{ + return m_subframes.take(frameName); +} + +} diff --git a/WebCore/loader/archive/ArchiveResourceCollection.h b/WebCore/loader/archive/ArchiveResourceCollection.h new file mode 100644 index 0000000..f898a8d --- /dev/null +++ b/WebCore/loader/archive/ArchiveResourceCollection.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 ArchiveResourceCollection_h +#define ArchiveResourceCollection_h + +#include "Archive.h" +#include "ArchiveResource.h" +#include "KURL.h" +#include "PlatformString.h" + +#include <wtf/HashMap.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class ArchiveResourceCollection : Noncopyable { +public: + ArchiveResourceCollection(); + + void addResource(PassRefPtr<ArchiveResource>); + void addAllResources(Archive*); + + ArchiveResource* archiveResourceForURL(const KURL&); + PassRefPtr<Archive> popSubframeArchive(const String& frameName); + +private: + HashMap<String, RefPtr<ArchiveResource> > m_subresources; + HashMap<String, RefPtr<Archive> > m_subframes; +}; + +} + +#endif diff --git a/WebCore/loader/archive/cf/LegacyWebArchive.cpp b/WebCore/loader/archive/cf/LegacyWebArchive.cpp new file mode 100644 index 0000000..9d99588 --- /dev/null +++ b/WebCore/loader/archive/cf/LegacyWebArchive.cpp @@ -0,0 +1,562 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#include "config.h" +#include "LegacyWebArchive.h" + +#include "CString.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameTree.h" +#include "HTMLFrameOwnerElement.h" +#include "HTMLNames.h" +#include "KURL.h" +#include "Logging.h" +#include "markup.h" +#include "Node.h" +#include "Range.h" +#include "SelectionController.h" +#include "SharedBuffer.h" + +#include <wtf/RetainPtr.h> + +namespace WebCore { + +static const CFStringRef LegacyWebArchiveMainResourceKey = CFSTR("WebMainResource"); +static const CFStringRef LegacyWebArchiveSubresourcesKey = CFSTR("WebSubresources"); +static const CFStringRef LegacyWebArchiveSubframeArchivesKey = CFSTR("WebSubframeArchives"); +static const CFStringRef LegacyWebArchiveResourceDataKey = CFSTR("WebResourceData"); +static const CFStringRef LegacyWebArchiveResourceFrameNameKey = CFSTR("WebResourceFrameName"); +static const CFStringRef LegacyWebArchiveResourceMIMETypeKey = CFSTR("WebResourceMIMEType"); +static const CFStringRef LegacyWebArchiveResourceURLKey = CFSTR("WebResourceURL"); +static const CFStringRef LegacyWebArchiveResourceTextEncodingNameKey = CFSTR("WebResourceTextEncodingName"); +static const CFStringRef LegacyWebArchiveResourceResponseKey = CFSTR("WebResourceResponse"); +static const CFStringRef LegacyWebArchiveResourceResponseVersionKey = CFSTR("WebResourceResponseVersion"); + +static RetainPtr<CFDictionaryRef> createPropertyListRepresentationFromResource(ArchiveResource* resource, bool mainResource) +{ + if (!resource) { + // The property list representation of a null/empty WebResource has the following 3 objects stored as nil + RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 3, 0, 0)); + CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceDataKey, 0); + CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceURLKey, 0); + CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceMIMETypeKey, 0); + + return propertyList; + } + + RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 6, 0, &kCFTypeDictionaryValueCallBacks)); + + // Resource data can be empty, but must be represented by an empty CFDataRef + SharedBuffer* data = resource->data(); + RetainPtr<CFDataRef> cfData; + if (data) + cfData.adoptCF(data->createCFData()); + else + cfData.adoptCF(CFDataCreate(0, 0, 0)); + CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceDataKey, cfData.get()); + + // Resource URL cannot be null + RetainPtr<CFStringRef> cfURL(AdoptCF, resource->url().string().createCFString()); + if (cfURL) + CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceURLKey, cfURL.get()); + else { + LOG(Archives, "LegacyWebArchive - NULL resource URL is invalid - returning null property list"); + return 0; + } + + // FrameName should be left out if empty for subresources, but always included for main resources + const String& frameName(resource->frameName()); + if (!frameName.isEmpty() || mainResource) { + RetainPtr<CFStringRef> cfFrameName(AdoptCF, frameName.createCFString()); + CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceFrameNameKey, cfFrameName.get()); + } + + // Set MIMEType, TextEncodingName, and ResourceResponse only if they actually exist + const String& mimeType(resource->mimeType()); + if (!mimeType.isEmpty()) { + RetainPtr<CFStringRef> cfMIMEType(AdoptCF, mimeType.createCFString()); + CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceMIMETypeKey, cfMIMEType.get()); + } + + const String& textEncoding(resource->textEncoding()); + if (!textEncoding.isEmpty()) { + RetainPtr<CFStringRef> cfTextEncoding(AdoptCF, textEncoding.createCFString()); + CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceTextEncodingNameKey, cfTextEncoding.get()); + } + + // Don't include the resource response for the main resource + if (!mainResource) { + RetainPtr<CFDataRef> resourceResponseData = propertyListDataFromResourceResponse(resource->response()); + if (resourceResponseData) + CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceResponseKey, resourceResponseData.get()); + } + + return propertyList; +} + +static RetainPtr<CFDictionaryRef> createPropertyListRep(Archive* archive) +{ + RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 3, 0, &kCFTypeDictionaryValueCallBacks)); + + RetainPtr<CFDictionaryRef> mainResourceDict = createPropertyListRepresentationFromResource(archive->mainResource(), true); + if (!mainResourceDict) + return 0; + CFDictionarySetValue(propertyList.get(), LegacyWebArchiveMainResourceKey, mainResourceDict.get()); + + RetainPtr<CFMutableArrayRef> subresourcesArray(AdoptCF, CFArrayCreateMutable(0, archive->subresources().size(), &kCFTypeArrayCallBacks)); + const Vector<RefPtr<ArchiveResource> >& subresources(archive->subresources()); + for (unsigned i = 0; i < subresources.size(); ++i) { + RetainPtr<CFDictionaryRef> subresource = createPropertyListRepresentationFromResource(subresources[i].get(), false); + if (subresource) + CFArrayAppendValue(subresourcesArray.get(), subresource.get()); + else + LOG(Archives, "LegacyWebArchive - Failed to create property list for subresource"); + } + if (CFArrayGetCount(subresourcesArray.get())) + CFDictionarySetValue(propertyList.get(), LegacyWebArchiveSubresourcesKey, subresourcesArray.get()); + + RetainPtr<CFMutableArrayRef> subframesArray(AdoptCF, CFArrayCreateMutable(0, archive->subframeArchives().size(), &kCFTypeArrayCallBacks)); + const Vector<RefPtr<Archive> >& subframeArchives(archive->subframeArchives()); + for (unsigned i = 0; i < subframeArchives.size(); ++i) { + RetainPtr<CFDictionaryRef> subframeArchive = createPropertyListRep(subframeArchives[i].get()); + if (subframeArchive) + CFArrayAppendValue(subframesArray.get(), subframeArchive.get()); + else + LOG(Archives, "LegacyWebArchive - Failed to create property list for subframe archive"); + } + if (CFArrayGetCount(subframesArray.get())) + CFDictionarySetValue(propertyList.get(), LegacyWebArchiveSubframeArchivesKey, subframesArray.get()); + + return propertyList; +} + +static ResourceResponse createResourceResponseFromPropertyListData(CFDataRef data, CFStringRef responseDataType) +{ + ASSERT(data); + if (!data) + return ResourceResponse(); + + // If the ResourceResponseVersion (passed in as responseDataType) exists at all, this is a "new" webarchive that we can parse well in a cross platform manner + // If it doesn't exist, we will assume this is an "old" Cocoa-based WebArchive, and parse the ResourceResponse as such + if (!responseDataType) + return createResourceResponseFromMacArchivedData(data); + + // FIXME: Parse the "new" format that the above comment references here + return ResourceResponse(); +} + +static PassRefPtr<ArchiveResource> createResource(CFDictionaryRef dictionary) +{ + ASSERT(dictionary); + if (!dictionary) + return 0; + + CFDataRef resourceData = static_cast<CFDataRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceDataKey)); + if (resourceData && CFGetTypeID(resourceData) != CFDataGetTypeID()) { + LOG(Archives, "LegacyWebArchive - Resource data is not of type CFData, cannot create invalid resource"); + return 0; + } + + CFStringRef frameName = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceFrameNameKey)); + if (frameName && CFGetTypeID(frameName) != CFStringGetTypeID()) { + LOG(Archives, "LegacyWebArchive - Frame name is not of type CFString, cannot create invalid resource"); + return 0; + } + + CFStringRef mimeType = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceMIMETypeKey)); + if (mimeType && CFGetTypeID(mimeType) != CFStringGetTypeID()) { + LOG(Archives, "LegacyWebArchive - MIME type is not of type CFString, cannot create invalid resource"); + return 0; + } + + CFStringRef url = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceURLKey)); + if (url && CFGetTypeID(url) != CFStringGetTypeID()) { + LOG(Archives, "LegacyWebArchive - URL is not of type CFString, cannot create invalid resource"); + return 0; + } + + CFStringRef textEncoding = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceTextEncodingNameKey)); + if (textEncoding && CFGetTypeID(textEncoding) != CFStringGetTypeID()) { + LOG(Archives, "LegacyWebArchive - Text encoding is not of type CFString, cannot create invalid resource"); + return 0; + } + + ResourceResponse response; + + CFDataRef resourceResponseData = static_cast<CFDataRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceResponseKey)); + if (resourceResponseData) { + if (CFGetTypeID(resourceResponseData) != CFDataGetTypeID()) { + LOG(Archives, "LegacyWebArchive - Resource response data is not of type CFData, cannot create invalid resource"); + return 0; + } + + CFStringRef resourceResponseVersion = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceResponseVersionKey)); + if (resourceResponseVersion && CFGetTypeID(resourceResponseVersion) != CFStringGetTypeID()) { + LOG(Archives, "LegacyWebArchive - Resource response version is not of type CFString, cannot create invalid resource"); + return 0; + } + + response = createResourceResponseFromPropertyListData(resourceResponseData, resourceResponseVersion); + } + + return ArchiveResource::create(SharedBuffer::create(CFDataGetBytePtr(resourceData), CFDataGetLength(resourceData)), KURL(url), mimeType, textEncoding, frameName, response); +} + +PassRefPtr<LegacyWebArchive> LegacyWebArchive::create() +{ + return adoptRef(new LegacyWebArchive); +} + +PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(SharedBuffer* data) +{ + LOG(Archives, "LegacyWebArchive - Creating from raw data"); + + RefPtr<LegacyWebArchive> archive = create(); + if (!archive->init(data)) + return 0; + + return archive.release(); +} + +PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(PassRefPtr<ArchiveResource> mainResource, Vector<PassRefPtr<ArchiveResource> >& subresources, Vector<PassRefPtr<LegacyWebArchive> >& subframeArchives) +{ + ASSERT(mainResource); + if (!mainResource) + return 0; + + RefPtr<LegacyWebArchive> archive = create(); + archive->setMainResource(mainResource); + + for (unsigned i = 0; i < subresources.size(); ++i) + archive->addSubresource(subresources[i]); + + for (unsigned i = 0; i < subframeArchives.size(); ++i) + archive->addSubframeArchive(subframeArchives[i]); + + return archive.release(); +} + +LegacyWebArchive::LegacyWebArchive() +{ +} + +bool LegacyWebArchive::init(SharedBuffer* data) +{ + ASSERT(data); + if (!data) + return false; + + RetainPtr<CFDataRef> cfData(AdoptCF, data->createCFData()); + if (!cfData) + return false; + + CFStringRef errorString = 0; + + RetainPtr<CFDictionaryRef> plist(AdoptCF, static_cast<CFDictionaryRef>(CFPropertyListCreateFromXMLData(0, cfData.get(), kCFPropertyListImmutable, &errorString))); + if (!plist) { +#ifndef NDEBUG + const char* cError = errorString ? CFStringGetCStringPtr(errorString, kCFStringEncodingUTF8) : "unknown error"; + LOG(Archives, "LegacyWebArchive - Error parsing PropertyList from archive data - %s", cError); +#endif + if (errorString) + CFRelease(errorString); + return false; + } + + if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) { + LOG(Archives, "LegacyWebArchive - Archive property list is not the expected CFDictionary, aborting invalid WebArchive"); + return false; + } + + return extract(plist.get()); +} + +bool LegacyWebArchive::extract(CFDictionaryRef dictionary) +{ + ASSERT(dictionary); + if (!dictionary) { + LOG(Archives, "LegacyWebArchive - Null root CFDictionary, aborting invalid WebArchive"); + return false; + } + + CFDictionaryRef mainResourceDict = static_cast<CFDictionaryRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveMainResourceKey)); + if (!mainResourceDict) { + LOG(Archives, "LegacyWebArchive - No main resource in archive, aborting invalid WebArchive"); + return false; + } + if (CFGetTypeID(mainResourceDict) != CFDictionaryGetTypeID()) { + LOG(Archives, "LegacyWebArchive - Main resource is not the expected CFDictionary, aborting invalid WebArchive"); + return false; + } + + setMainResource(createResource(mainResourceDict)); + if (!mainResource()) { + LOG(Archives, "LegacyWebArchive - Failed to parse main resource from CFDictionary or main resource does not exist, aborting invalid WebArchive"); + return false; + } + + CFArrayRef subresourceArray = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveSubresourcesKey)); + if (subresourceArray && CFGetTypeID(subresourceArray) != CFArrayGetTypeID()) { + LOG(Archives, "LegacyWebArchive - Subresources is not the expected Array, aborting invalid WebArchive"); + return false; + } + + if (subresourceArray) { + CFIndex count = CFArrayGetCount(subresourceArray); + for (CFIndex i = 0; i < count; ++i) { + CFDictionaryRef subresourceDict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(subresourceArray, i)); + if (CFGetTypeID(subresourceDict) != CFDictionaryGetTypeID()) { + LOG(Archives, "LegacyWebArchive - Subresource is not expected CFDictionary, aborting invalid WebArchive"); + return false; + } + addSubresource(createResource(subresourceDict)); + } + } + + CFArrayRef subframeArray = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveSubframeArchivesKey)); + if (subframeArray && CFGetTypeID(subframeArray) != CFArrayGetTypeID()) { + LOG(Archives, "LegacyWebArchive - Subframe archives is not the expected Array, aborting invalid WebArchive"); + return false; + } + + if (subframeArray) { + CFIndex count = CFArrayGetCount(subframeArray); + for (CFIndex i = 0; i < count; ++i) { + CFDictionaryRef subframeDict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(subframeArray, i)); + if (CFGetTypeID(subframeDict) != CFDictionaryGetTypeID()) { + LOG(Archives, "LegacyWebArchive - Subframe array is not expected CFDictionary, aborting invalid WebArchive"); + return false; + } + + RefPtr<LegacyWebArchive> subframeArchive = create(); + if (subframeArchive->extract(subframeDict)) + addSubframeArchive(subframeArchive.release()); + else + LOG(Archives, "LegacyWebArchive - Invalid subframe archive skipped"); + } + } + + return true; +} + +RetainPtr<CFDataRef> LegacyWebArchive::rawDataRepresentation() +{ + RetainPtr<CFDictionaryRef> propertyList = createPropertyListRep(this); + if (!propertyList) { + LOG(Archives, "LegacyWebArchive - Failed to create property list for archive, returning no data"); + return 0; + } + + // FIXME: On Mac, WebArchives have been written out as Binary Property Lists until this change. + // Unless we jump through CFWriteStream hoops, they'll now be textual XML data. Is this okay? + RetainPtr<CFDataRef> plistData(AdoptCF, CFPropertyListCreateXMLData(0, propertyList.get())); + if (!plistData) { + LOG(Archives, "LegacyWebArchive - Failed to convert property list into raw data, returning no data"); + return 0; + } + + return plistData; +} + +#if !PLATFORM(MAC) +// FIXME: Is it possible to parse in a Cocoa-style resource response manually, +// without NSKeyed(Un)Archiver, manipulating plists directly? +// If so, the code that does it will go here. +// In the meantime, Mac will continue to NSKeyed(Un)Archive the response as it always has +ResourceResponse createResourceResponseFromMacArchivedData(CFDataRef responseData) +{ + return ResourceResponse(); +} + +RetainPtr<CFDataRef> propertyListDataFromResourceResponse(const ResourceResponse& response) +{ + // FIXME: Write out the "new" format described in ::createResourceResponseFromPropertyListData() up above + return 0; +} +#endif + +PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Node* node) +{ + ASSERT(node); + if (!node) + return create(); + + Document* document = node->document(); + Frame* frame = document ? document->frame() : 0; + if (!frame) + return create(); + + Vector<Node*> nodeList; + String markupString = createMarkup(node, IncludeNode, &nodeList); + Node::NodeType nodeType = node->nodeType(); + if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE) + markupString = frame->documentTypeString() + markupString; + + return create(markupString, frame, nodeList); +} + +PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Frame* frame) +{ + ASSERT(frame); + + DocumentLoader* documentLoader = frame->loader()->documentLoader(); + + if (!documentLoader) + return 0; + + Vector<PassRefPtr<LegacyWebArchive> > subframeArchives; + + unsigned children = frame->tree()->childCount(); + for (unsigned i = 0; i < children; ++i) { + RefPtr<LegacyWebArchive> childFrameArchive = create(frame->tree()->child(i)); + if (childFrameArchive) + subframeArchives.append(childFrameArchive.release()); + } + + Vector<PassRefPtr<ArchiveResource> > subresources; + documentLoader->getSubresources(subresources); + + return LegacyWebArchive::create(documentLoader->mainResource(), subresources, subframeArchives); +} + +PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Range* range) +{ + if (!range) + return 0; + + Node* startContainer = range->startContainer(); + if (!startContainer) + return 0; + + Document* document = startContainer->document(); + if (!document) + return 0; + + Frame* frame = document->frame(); + if (!frame) + return 0; + + Vector<Node*> nodeList; + + // FIXME: This is always "for interchange". Is that right? See the previous method. + String markupString = frame->documentTypeString() + createMarkup(range, &nodeList, AnnotateForInterchange); + + return create(markupString, frame, nodeList); +} + +PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString, Frame* frame, Vector<Node*>& nodes) +{ + ASSERT(frame); + + const ResourceResponse& response = frame->loader()->documentLoader()->response(); + KURL responseURL = response.url(); + + // it's possible to have a response without a URL here + // <rdar://problem/5454935> + if (responseURL.isNull()) + responseURL = KURL(""); + + PassRefPtr<ArchiveResource> mainResource = ArchiveResource::create(utf8Buffer(markupString), responseURL, response.mimeType(), "UTF-8", frame->tree()->name()); + + Vector<PassRefPtr<LegacyWebArchive> > subframeArchives; + Vector<PassRefPtr<ArchiveResource> > subresources; + HashSet<String> uniqueSubresources; + + Vector<Node*>::iterator it = nodes.begin(); + Vector<Node*>::iterator end = nodes.end(); + + for (; it != end; ++it) { + Frame* childFrame; + if (((*it)->hasTagName(HTMLNames::frameTag) || (*it)->hasTagName(HTMLNames::iframeTag) || (*it)->hasTagName(HTMLNames::objectTag)) && + (childFrame = static_cast<HTMLFrameOwnerElement*>(*it)->contentFrame())) { + RefPtr<LegacyWebArchive> subframeArchive; + if (Document* document = childFrame->document()) + subframeArchive = LegacyWebArchive::create(document); + else + subframeArchive = create(childFrame); + + if (subframeArchive) + subframeArchives.append(subframeArchive); + else + LOG_ERROR("Unabled to archive subframe %s", childFrame->tree()->name().string().utf8().data()); + } else { + Vector<KURL> subresourceURLs; + (*it)->getSubresourceURLs(subresourceURLs); + + DocumentLoader* documentLoader = frame->loader()->documentLoader(); + for (unsigned i = 0; i < subresourceURLs.size(); ++i) { + if (uniqueSubresources.contains(subresourceURLs[i].string())) + continue; + uniqueSubresources.add(subresourceURLs[i].string()); + RefPtr<ArchiveResource> resource = documentLoader->subresource(subresourceURLs[i]); + if (resource) + subresources.append(resource.release()); + else + // FIXME: should do something better than spew to console here + LOG_ERROR("Failed to archive subresource for %s", subresourceURLs[i].string().utf8().data()); + } + } + } + + return create(mainResource, subresources, subframeArchives); +} + +PassRefPtr<LegacyWebArchive> LegacyWebArchive::createFromSelection(Frame* frame) +{ + if (!frame) + return 0; + + RefPtr<Range> selectionRange = frame->selection()->toRange(); + Vector<Node*> nodeList; + String markupString = frame->documentTypeString() + createMarkup(selectionRange.get(), &nodeList, AnnotateForInterchange); + + RefPtr<LegacyWebArchive> archive = create(markupString, frame, nodeList); + + if (!frame->isFrameSet()) + return archive.release(); + + // Wrap the frameset document in an iframe so it can be pasted into + // another document (which will have a body or frameset of its own). + String iframeMarkup = String::format("<iframe frameborder=\"no\" marginwidth=\"0\" marginheight=\"0\" width=\"98%%\" height=\"98%%\" src=\"%s\"></iframe>", + frame->loader()->documentLoader()->response().url().string().utf8().data()); + RefPtr<ArchiveResource> iframeResource = ArchiveResource::create(utf8Buffer(iframeMarkup), blankURL(), "text/html", "UTF-8", String()); + + Vector<PassRefPtr<ArchiveResource> > subresources; + + Vector<PassRefPtr<LegacyWebArchive> > subframeArchives; + subframeArchives.append(archive); + + archive = LegacyWebArchive::create(iframeResource.release(), subresources, subframeArchives); + + return archive.release(); +} + +} diff --git a/WebCore/loader/archive/cf/LegacyWebArchive.h b/WebCore/loader/archive/cf/LegacyWebArchive.h new file mode 100644 index 0000000..70faba5 --- /dev/null +++ b/WebCore/loader/archive/cf/LegacyWebArchive.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 LegacyWebArchive_h +#define LegacyWebArchive_h + +#include "Archive.h" + +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class Frame; +class Node; +class Range; + +class LegacyWebArchive : public Archive { +public: + static PassRefPtr<LegacyWebArchive> create(); + static PassRefPtr<LegacyWebArchive> create(SharedBuffer*); + static PassRefPtr<LegacyWebArchive> create(PassRefPtr<ArchiveResource> mainResource, Vector<PassRefPtr<ArchiveResource> >& subresources, Vector<PassRefPtr<LegacyWebArchive> >& subframeArchives); + static PassRefPtr<LegacyWebArchive> create(Node*); + static PassRefPtr<LegacyWebArchive> create(Frame*); + static PassRefPtr<LegacyWebArchive> createFromSelection(Frame* frame); + static PassRefPtr<LegacyWebArchive> create(Range*); + static PassRefPtr<LegacyWebArchive> create(const String& markupString, Frame*, Vector<Node*>& nodes); + + RetainPtr<CFDataRef> rawDataRepresentation(); + +private: + LegacyWebArchive(); + bool init(SharedBuffer*); + bool extract(CFDictionaryRef); + +}; + +ResourceResponse createResourceResponseFromMacArchivedData(CFDataRef); +RetainPtr<CFDataRef> propertyListDataFromResourceResponse(const ResourceResponse&); + +} + +#endif // Archive diff --git a/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm b/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm new file mode 100644 index 0000000..b853a15 --- /dev/null +++ b/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#include "config.h" +#include "LegacyWebArchive.h" + +namespace WebCore { + +static const NSString *LegacyWebArchiveResourceResponseKey = @"WebResourceResponse"; + +// FIXME: Is it possible to parse in a Cocoa-style resource response manually, +// without NSKeyed(Un)Archiver, manipulating plists directly? +ResourceResponse createResourceResponseFromMacArchivedData(CFDataRef responseData) +{ + ASSERT(responseData); + if (!responseData) + return ResourceResponse(); + + NSURLResponse *response = nil; + NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:(NSData *)responseData]; + @try { + id responseObject = [unarchiver decodeObjectForKey:LegacyWebArchiveResourceResponseKey]; + if ([responseObject isKindOfClass:[NSURLResponse class]]) + response = responseObject; + [unarchiver finishDecoding]; + } @catch(id) { + response = nil; + } + [unarchiver release]; + + return ResourceResponse(response); +} + +RetainPtr<CFDataRef> propertyListDataFromResourceResponse(const ResourceResponse& response) +{ + NSURLResponse *nsResponse = response.nsURLResponse(); + if (!nsResponse) + return 0; + + NSMutableData *responseData = (NSMutableData *)CFDataCreateMutable(0, 0); + NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:responseData]; + [archiver encodeObject:nsResponse forKey:LegacyWebArchiveResourceResponseKey]; + [archiver finishEncoding]; + [archiver release]; + + return RetainPtr<CFDataRef>(AdoptCF, (CFDataRef)responseData); +} + +} diff --git a/WebCore/loader/icon/IconDatabase.cpp b/WebCore/loader/icon/IconDatabase.cpp index 79250b8..a47fb08 100644 --- a/WebCore/loader/icon/IconDatabase.cpp +++ b/WebCore/loader/icon/IconDatabase.cpp @@ -41,6 +41,8 @@ #include "SQLiteStatement.h" #include "SQLiteTransaction.h" #include "SystemTime.h" +#include <runtime/InitializeThreading.h> +#include <wtf/MainThread.h> #if PLATFORM(WIN_OS) #include <windows.h> @@ -63,6 +65,10 @@ #define IS_ICON_SYNC_THREAD() (m_syncThread == currentThread()) #define ASSERT_ICON_SYNC_THREAD() ASSERT(IS_ICON_SYNC_THREAD()) +#if PLATFORM(QT) +#define CAN_THEME_URL_ICON +#endif + namespace WebCore { static IconDatabase* sharedIconDatabase = 0; @@ -101,7 +107,7 @@ static IconDatabaseClient* defaultClient() IconDatabase* iconDatabase() { if (!sharedIconDatabase) { - initializeThreading(); + JSC::initializeThreading(); sharedIconDatabase = new IconDatabase; } return sharedIconDatabase; @@ -144,7 +150,7 @@ bool IconDatabase::open(const String& databasePath) // Lock here as well as first thing in the thread so the thread doesn't actually commence until the createThread() call // completes and m_syncThreadRunning is properly set m_syncLock.lock(); - m_syncThread = createThread(IconDatabase::iconDatabaseSyncThreadStart, this); + m_syncThread = createThread(IconDatabase::iconDatabaseSyncThreadStart, this, "WebCore::IconDatabase"); m_syncLock.unlock(); if (!m_syncThread) return false; @@ -220,16 +226,14 @@ void IconDatabase::removeAllIcons() wakeSyncThread(); } -Image* IconDatabase::iconForPageURL(const String& pageURLOriginal, const IntSize& size, bool cache) +Image* IconDatabase::iconForPageURL(const String& pageURLOriginal, const IntSize& size) { ASSERT_NOT_SYNC_THREAD(); - - ASSERT(!pageURLOriginal.isNull()); - // pageURLOriginal can not be stored without being deep copied first. + // pageURLOriginal cannot be stored without being deep copied first. // We should go our of our way to only copy it if we have to store it - if (!isOpen()) + if (!isOpen() || pageURLOriginal.isEmpty()) return defaultIcon(size); MutexLocker locker(m_urlAndIconLock); @@ -335,10 +339,14 @@ String IconDatabase::iconURLForPageURL(const String& pageURLOriginal) return pageRecord->iconRecord() ? pageRecord->iconRecord()->iconURL().copy() : String(); } -Image* IconDatabase::defaultIcon(const IntSize& size) +#ifdef CAN_THEME_URL_ICON +static inline void loadDefaultIconRecord(IconRecord* defaultIconRecord) +{ + defaultIconRecord->loadImageFromResource("urlIcon"); +} +#else +static inline void loadDefaultIconRecord(IconRecord* defaultIconRecord) { - ASSERT_NOT_SYNC_THREAD(); - static const unsigned char defaultIconData[] = { 0x4D, 0x4D, 0x00, 0x2A, 0x00, 0x00, 0x03, 0x32, 0x80, 0x00, 0x20, 0x50, 0x38, 0x24, 0x16, 0x0D, 0x07, 0x84, 0x42, 0x61, 0x50, 0xB8, 0x64, 0x08, 0x18, 0x0D, 0x0A, 0x0B, 0x84, 0xA2, 0xA1, 0xE2, 0x08, 0x5E, 0x39, 0x28, 0xAF, 0x48, 0x24, 0xD3, 0x53, 0x9A, 0x37, 0x1D, 0x18, 0x0E, 0x8A, 0x4B, 0xD1, 0x38, 0xB0, 0x7C, 0x82, 0x07, 0x03, 0x82, 0xA2, 0xE8, 0x6C, 0x2C, 0x03, 0x2F, 0x02, 0x82, 0x41, 0xA1, 0xE2, 0xF8, 0xC8, 0x84, 0x68, 0x6D, 0x1C, 0x11, 0x0A, 0xB7, 0xFA, 0x91, @@ -377,11 +385,19 @@ Image* IconDatabase::defaultIcon(const IntSize& size) 0x00, 0x00, 0x01, 0x52, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0A, 0xFC, 0x80, 0x00, 0x00, 0x27, 0x10, 0x00, 0x0A, 0xFC, 0x80, 0x00, 0x00, 0x27, 0x10 }; - static RefPtr<SharedBuffer> defaultIconBuffer(new SharedBuffer(defaultIconData, sizeof(defaultIconData))); + static RefPtr<SharedBuffer> defaultIconBuffer(SharedBuffer::create(defaultIconData, sizeof(defaultIconData))); + defaultIconRecord->setImageData(defaultIconBuffer); +} +#endif + +Image* IconDatabase::defaultIcon(const IntSize& size) +{ + ASSERT_NOT_SYNC_THREAD(); + if (!m_defaultIconRecord) { - m_defaultIconRecord = new IconRecord("urlIcon"); - m_defaultIconRecord->setImageData(defaultIconBuffer); + m_defaultIconRecord = IconRecord::create("urlIcon"); + loadDefaultIconRecord(m_defaultIconRecord.get()); } return m_defaultIconRecord->image(size); @@ -518,10 +534,10 @@ void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> dataOriginal, MutexLocker locker(m_urlAndIconLock); // If this icon was pending a read, remove it from that set because this new data should override what is on disk - IconRecord* icon = m_iconURLToRecordMap.get(iconURL); + RefPtr<IconRecord> icon = m_iconURLToRecordMap.get(iconURL); if (icon) { MutexLocker locker(m_pendingReadingLock); - m_iconsPendingReading.remove(icon); + m_iconsPendingReading.remove(icon.get()); } else icon = getOrCreateIconRecord(iconURL); @@ -537,6 +553,12 @@ void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> dataOriginal, MutexLocker locker(m_pendingSyncLock); m_iconsPendingSync.set(iconURL, icon->snapshot()); } + + if (icon->hasOneRef()) { + ASSERT(icon->retainingPageURLs().isEmpty()); + LOG(IconDatabase, "Icon for icon url %s is about to be destroyed - removing mapping for it", urlForLogging(icon->iconURL()).ascii().data()); + m_iconURLToRecordMap.remove(icon->iconURL()); + } } // Send notification out regarding all PageURLs that retain this icon @@ -593,8 +615,7 @@ void IconDatabase::setIconURLForPageURL(const String& iconURLOriginal, const Str RefPtr<IconRecord> iconRecord = pageRecord->iconRecord(); // Otherwise, set the new icon record for this page - IconRecord* newIconRecord = getOrCreateIconRecord(iconURL); - pageRecord->setIconRecord(newIconRecord); + pageRecord->setIconRecord(getOrCreateIconRecord(iconURL)); // If the current icon has only a single ref left, it is about to get wiped out. // Remove it from the in-memory records and don't bother reading it in from disk anymore @@ -611,7 +632,7 @@ void IconDatabase::setIconURLForPageURL(const String& iconURLOriginal, const Str MutexLocker locker(m_pendingSyncLock); m_pageURLsPendingSync.set(pageURL, pageRecord->snapshot()); - // If the icon is on it's last ref, mark it for deletion + // If the icon is on its last ref, mark it for deletion if (iconRecord && iconRecord->hasOneRef()) m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true)); } @@ -839,18 +860,18 @@ String IconDatabase::defaultDatabaseFilename() } // Unlike getOrCreatePageURLRecord(), getOrCreateIconRecord() does not mark the icon as "interested in import" -IconRecord* IconDatabase::getOrCreateIconRecord(const String& iconURL) +PassRefPtr<IconRecord> IconDatabase::getOrCreateIconRecord(const String& iconURL) { // Clients of getOrCreateIconRecord() are required to acquire the m_urlAndIconLock before calling this method ASSERT(!m_urlAndIconLock.tryLock()); if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL)) return icon; - - IconRecord* newIcon = new IconRecord(iconURL); - m_iconURLToRecordMap.set(iconURL, newIcon); - - return newIcon; + + RefPtr<IconRecord> newIcon = IconRecord::create(iconURL); + m_iconURLToRecordMap.set(iconURL, newIcon.get()); + + return newIcon.release(); } // This method retrieves the existing PageURLRecord, or creates a new one and marks it as "interested in the import" for later notification @@ -1205,8 +1226,8 @@ void IconDatabase::performURLImport() IconRecord* currentIcon = pageRecord->iconRecord(); if (!currentIcon || currentIcon->iconURL() != iconURL) { - currentIcon = getOrCreateIconRecord(iconURL); - pageRecord->setIconRecord(currentIcon); + pageRecord->setIconRecord(getOrCreateIconRecord(iconURL)); + currentIcon = pageRecord->iconRecord(); } // Regardless, the time stamp from disk still takes precedence. Until we read this icon from disk, we didn't think we'd seen it before @@ -1335,6 +1356,18 @@ void* IconDatabase::syncThreadMainLoop() bool didAnyWork = true; while (didAnyWork) { +#ifdef ANDROID_FIX + // We should write the pending icons to the database before trying + // to read any requested icons to ensure that a requested icon has + // the correct data. + bool didWrite = writeToDatabase(); + if (shouldStopThreadActivity()) + break; + + didAnyWork = readFromDatabase(); + if (shouldStopThreadActivity()) + break; +#else didAnyWork = readFromDatabase(); if (shouldStopThreadActivity()) break; @@ -1342,6 +1375,8 @@ void* IconDatabase::syncThreadMainLoop() bool didWrite = writeToDatabase(); if (shouldStopThreadActivity()) break; +#endif + // Prune unretained icons after the first time we sync anything out to the database // This way, pruning won't be the only operation we perform to the database by itself @@ -1536,22 +1571,20 @@ bool IconDatabase::writeToDatabase() } for (unsigned i = 0; i < pageSnapshots.size(); ++i) { - String iconURL = pageSnapshots[i].iconURL; - // If the icon URL is empty, this page is meant to be deleted // ASSERTs are sanity checks to make sure the mappings exist if they should and don't if they shouldn't if (pageSnapshots[i].iconURL.isEmpty()) removePageURLFromSQLDatabase(pageSnapshots[i].pageURL); - else { + else setIconURLForPageURLInSQLDatabase(pageSnapshots[i].iconURL, pageSnapshots[i].pageURL); - } LOG(IconDatabase, "Committed IconURL for PageURL %s to database", urlForLogging(pageSnapshots[i].pageURL).ascii().data()); } syncTransaction.commit(); // Check to make sure there are no dangling PageURLs - If there are, we want to output one log message but not spam the console potentially every few seconds - checkForDanglingPageURLs(false); + if (didAnyWork) + checkForDanglingPageURLs(false); LOG(IconDatabase, "Updating the database took %.4f seconds", currentTime() - timeStamp); @@ -1638,19 +1671,21 @@ void IconDatabase::pruneUnretainedIcons() void IconDatabase::checkForDanglingPageURLs(bool pruneIfFound) { ASSERT_ICON_SYNC_THREAD(); - - // We don't want to keep performing this check and reporting this error if it has already found danglers so we keep track + + // This check can be relatively expensive so we don't do it in a release build unless the caller has asked us to prune any dangling + // entries. We also don't want to keep performing this check and reporting this error if it has already found danglers before so we + // keep track of whether we've found any. We skip the check in the release build pretending to have already found danglers already. +#ifndef NDEBUG + static bool danglersFound = true; +#else static bool danglersFound = false; - - // However, if the caller wants us to prune the danglers, we will reset this flag and prune every time - if (pruneIfFound) - danglersFound = false; - - if (!danglersFound && SQLiteStatement(m_syncDB, "SELECT url FROM PageURL WHERE PageURL.iconID NOT IN (SELECT iconID FROM IconInfo) LIMIT 1;").returnsAtLeastOneResult()) { +#endif + + if ((pruneIfFound || !danglersFound) && SQLiteStatement(m_syncDB, "SELECT url FROM PageURL WHERE PageURL.iconID NOT IN (SELECT iconID FROM IconInfo) LIMIT 1;").returnsAtLeastOneResult()) { danglersFound = true; - LOG_ERROR("Dangling PageURL entries found"); + LOG(IconDatabase, "Dangling PageURL entries found"); if (pruneIfFound && !m_syncDB.executeCommand("DELETE FROM PageURL WHERE iconID NOT IN (SELECT iconID FROM IconInfo);")) - LOG_ERROR("Unable to prune dangling PageURLs"); + LOG(IconDatabase, "Unable to prune dangling PageURLs"); } } @@ -1905,8 +1940,7 @@ PassRefPtr<SharedBuffer> IconDatabase::getImageDataForIconURLFromSQLDatabase(con if (result == SQLResultRow) { Vector<char> data; m_getImageDataForIconURLStatement->getColumnBlobAsVector(0, data); - imageData = new SharedBuffer; - imageData->append(data.data(), data.size()); + imageData = SharedBuffer::create(data.data(), data.size()); } else if (result != SQLResultDone) LOG_ERROR("getImageDataForIconURLFromSQLDatabase failed for url %s", urlForLogging(iconURL).ascii().data()); diff --git a/WebCore/loader/icon/IconDatabase.h b/WebCore/loader/icon/IconDatabase.h index ea48c16..4303ae1 100644 --- a/WebCore/loader/icon/IconDatabase.h +++ b/WebCore/loader/icon/IconDatabase.h @@ -32,13 +32,14 @@ #endif #include "StringHash.h" -#if ENABLE(ICONDATABASE) -#include "Threading.h" -#endif #include "Timer.h" +#include <wtf/HashMap.h> #include <wtf/HashSet.h> #include <wtf/Noncopyable.h> #include <wtf/OwnPtr.h> +#if ENABLE(ICONDATABASE) +#include <wtf/Threading.h> +#endif namespace WebCore { @@ -74,7 +75,7 @@ public: void removeAllIcons(); - Image* iconForPageURL(const String&, const IntSize&, bool cache = true); + Image* iconForPageURL(const String&, const IntSize&); void readIconForPageURLFromDisk(const String&); String iconURLForPageURL(const String&); Image* defaultIcon(const IntSize&); @@ -123,7 +124,7 @@ private: HashSet<RefPtr<DocumentLoader> > m_loadersPendingDecision; - IconRecord* m_defaultIconRecord; + RefPtr<IconRecord> m_defaultIconRecord; #endif // ENABLE(ICONDATABASE) // *** Any Thread *** @@ -134,7 +135,7 @@ public: #if ENABLE(ICONDATABASE) private: - IconRecord* getOrCreateIconRecord(const String& iconURL); + PassRefPtr<IconRecord> getOrCreateIconRecord(const String& iconURL); PageURLRecord* getOrCreatePageURLRecord(const String& pageURL); bool m_isEnabled; diff --git a/WebCore/loader/icon/IconDatabaseNone.cpp b/WebCore/loader/icon/IconDatabaseNone.cpp index 734e119..c76a2c4 100644 --- a/WebCore/loader/icon/IconDatabaseNone.cpp +++ b/WebCore/loader/icon/IconDatabaseNone.cpp @@ -99,7 +99,7 @@ void IconDatabase::readIconForPageURLFromDisk(const String&) } -Image* IconDatabase::iconForPageURL(const String& pageURL, const IntSize& size, bool cache) +Image* IconDatabase::iconForPageURL(const String& pageURL, const IntSize& size) { return defaultIcon(size); } diff --git a/WebCore/loader/icon/IconFetcher.cpp b/WebCore/loader/icon/IconFetcher.cpp new file mode 100644 index 0000000..efa7e14 --- /dev/null +++ b/WebCore/loader/icon/IconFetcher.cpp @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#include "config.h" +#include "IconFetcher.h" + +#include "Document.h" +#include "Frame.h" +#include "HTMLHeadElement.h" +#include "HTMLLinkElement.h" +#include "HTMLNames.h" +#include "MIMETypeRegistry.h" +#include "ResourceHandle.h" +#include "ResourceRequest.h" +#include "SharedBuffer.h" +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +using namespace HTMLNames; + +struct IconLinkEntry { +public: + enum IconType { + Unknown, + ICNS, + ICO, + }; + + IconLinkEntry(IconType type, const KURL& url) + : m_type(type) + , m_url(url) + { + } + + IconType type() const { return m_type; } + const KURL& url() const { return m_url; } + + SharedBuffer* buffer() + { + if (!m_buffer) + m_buffer = SharedBuffer::create(); + + return m_buffer.get(); + } + +private: + RefPtr<SharedBuffer> m_buffer; + IconType m_type; + KURL m_url; +}; + +#if PLATFORM(MAC) +static const IconLinkEntry::IconType NativeIconType = IconLinkEntry::ICNS; +#elif PLATFORM(WIN) +static const IconLinkEntry::IconType NativeIconType = IconLinkEntry::ICO; +#else +static const IconLinkEntry::IconType NativeIconType = IconLinkEntry::Unknown; +#endif + +static void parseIconLink(HTMLLinkElement* link, Vector<IconLinkEntry>& entries) +{ + // FIXME: Parse the size attribute too. + + IconLinkEntry::IconType type = IconLinkEntry::Unknown; + const KURL& url = link->href(); + + // Try to determine the file type. + String path = url.path(); + + int pos = path.reverseFind('.'); + if (pos >= 0) { + String extension = path.substring(pos + 1); + if (equalIgnoringCase(extension, "icns")) + type = IconLinkEntry::ICNS; + else if (equalIgnoringCase(extension, "ico")) + type = IconLinkEntry::ICO; + } + + entries.append(IconLinkEntry(type, url)); +} + +PassRefPtr<IconFetcher> IconFetcher::create(Frame* frame, IconFetcherClient* client) +{ + Document* document = frame->document(); + if (!document) + return 0; + + HTMLHeadElement* head = document->head(); + if (!head) + return 0; + + Vector<IconLinkEntry> entries; + + for (Node* n = head; n; n = n->traverseNextNode()) { + if (!n->hasTagName(linkTag)) + continue; + + HTMLLinkElement* link = static_cast<HTMLLinkElement*>(n); + if (!link->isIcon()) + continue; + + parseIconLink(link, entries); + } + + if (entries.isEmpty()) + return 0; + + // Check if any of the entries have the same type as the native icon type. + + // FIXME: This should be way more sophisticated, and handle conversion + // of multisize formats for example. + for (unsigned i = 0; i < entries.size(); i++) { + const IconLinkEntry& entry = entries[i]; + if (entry.type() == NativeIconType) { + RefPtr<IconFetcher> iconFetcher = adoptRef(new IconFetcher(frame, client)); + + iconFetcher->m_entries.append(entry); + iconFetcher->loadEntry(); + + return iconFetcher.release(); + } + } + + return 0; +} + +IconFetcher::IconFetcher(Frame* frame, IconFetcherClient* client) + : m_frame(frame) + , m_client(client) + , m_currentEntry(0) +{ +} + +IconFetcher::~IconFetcher() +{ + cancel(); +} + +void IconFetcher::cancel() +{ + if (m_handle) + m_handle->cancel(); +} + +PassRefPtr<SharedBuffer> IconFetcher::createIcon() +{ + ASSERT(!m_entries.isEmpty()); + + // For now, just return the data of the first entry. + return m_entries.first().buffer(); +} + + +void IconFetcher::loadEntry() +{ + ASSERT(m_currentEntry < m_entries.size()); + ASSERT(!m_handle); + + m_handle = ResourceHandle::create(m_entries[m_currentEntry].url(), this, m_frame, false, false); +} + +void IconFetcher::loadFailed() +{ + m_handle = 0; + + m_client->finishedFetchingIcon(0); +} + +void IconFetcher::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response) +{ + ASSERT(m_handle == handle); + + int statusCode = response.httpStatusCode() / 100; + if (statusCode == 4 || statusCode == 5) { + loadFailed(); + return; + } +} + +void IconFetcher::didReceiveData(ResourceHandle* handle, const char* data, int length, int lengthReceived) +{ + ASSERT(m_handle == handle); + + m_entries[m_currentEntry].buffer()->append(data, length); +} + +void IconFetcher::didFinishLoading(ResourceHandle* handle) +{ + ASSERT(m_handle == handle); + + if (m_currentEntry == m_entries.size() - 1) { + // We finished loading, create the icon + RefPtr<SharedBuffer> iconData = createIcon(); + + m_client->finishedFetchingIcon(iconData.release()); + return; + } + + // Load the next entry + m_currentEntry++; + + loadEntry(); +} + +void IconFetcher::didFail(ResourceHandle* handle, const ResourceError&) +{ + ASSERT(m_handle == handle); + + loadFailed(); +} + + +} // namespace WebCore diff --git a/WebCore/loader/icon/IconFetcher.h b/WebCore/loader/icon/IconFetcher.h new file mode 100644 index 0000000..5327693 --- /dev/null +++ b/WebCore/loader/icon/IconFetcher.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 IconFetcher_h +#define IconFetcher_h + +#include <wtf/RefCounted.h> +#include <wtf/Forward.h> +#include <wtf/Vector.h> + +#include "ResourceHandleClient.h" + +namespace WebCore { + +class Frame; +struct IconLinkEntry; +class ResourceHandle; +class SharedBuffer; + +class IconFetcherClient { +public: + virtual void finishedFetchingIcon(PassRefPtr<SharedBuffer> iconData) = 0; + + virtual ~IconFetcherClient() { } +}; + +class IconFetcher : public RefCounted<IconFetcher>, ResourceHandleClient { +public: + static PassRefPtr<IconFetcher> create(Frame*, IconFetcherClient*); + ~IconFetcher(); + + void cancel(); + +private: + IconFetcher(Frame*, IconFetcherClient*); + void loadEntry(); + void loadFailed(); + + PassRefPtr<SharedBuffer> createIcon(); + + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); + virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived); + virtual void didFinishLoading(ResourceHandle*); + virtual void didFail(ResourceHandle*, const ResourceError&); + + Frame* m_frame; + IconFetcherClient* m_client; + + unsigned m_currentEntry; + RefPtr<ResourceHandle> m_handle; + Vector<IconLinkEntry> m_entries; +}; + +} // namespace WebCore + +#endif // IconFetcher_h diff --git a/WebCore/loader/icon/IconLoader.h b/WebCore/loader/icon/IconLoader.h index aba70b4..a7194d8 100644 --- a/WebCore/loader/icon/IconLoader.h +++ b/WebCore/loader/icon/IconLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,15 +26,16 @@ #ifndef IconLoader_h #define IconLoader_h -#include "KURL.h" #include "SubresourceLoaderClient.h" #include <memory> +#include <wtf/Forward.h> #include <wtf/Noncopyable.h> -#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> namespace WebCore { class Frame; +class KURL; class SharedBuffer; class IconLoader : private SubresourceLoaderClient, Noncopyable { diff --git a/WebCore/loader/icon/IconRecord.cpp b/WebCore/loader/icon/IconRecord.cpp index 8b3d5f9..f070cc9 100644 --- a/WebCore/loader/icon/IconRecord.cpp +++ b/WebCore/loader/icon/IconRecord.cpp @@ -66,12 +66,12 @@ void IconRecord::setImageData(PassRefPtr<SharedBuffer> data) { // It's okay to delete the raw image here. Any existing clients using this icon will be // managing an image that was created with a copy of this raw image data. - m_image.set(new BitmapImage()); + m_image = BitmapImage::create(); // Copy the provided data into the buffer of the new Image object. if (!m_image->setData(data, true)) { LOG(IconDatabase, "Manual image data for iconURL '%s' FAILED - it was probably invalid image data", m_iconURL.ascii().data()); - m_image.set(0); + m_image.clear(); } m_dataSet = true; @@ -82,7 +82,7 @@ void IconRecord::loadImageFromResource(const char* resource) if (!resource) return; - m_image.set(Image::loadPlatformResource(resource)); + m_image = Image::loadPlatformResource(resource); m_dataSet = true; } diff --git a/WebCore/loader/icon/IconRecord.h b/WebCore/loader/icon/IconRecord.h index c6da7a6..aaea787 100644 --- a/WebCore/loader/icon/IconRecord.h +++ b/WebCore/loader/icon/IconRecord.h @@ -67,7 +67,10 @@ public: class IconRecord : public RefCounted<IconRecord> { friend class PageURLRecord; public: - IconRecord(const String& url); + static PassRefPtr<IconRecord> create(const String& url) + { + return adoptRef(new IconRecord(url)); + } ~IconRecord(); time_t getTimestamp() { return m_stamp; } @@ -85,10 +88,13 @@ public: const HashSet<String>& retainingPageURLs() { return m_retainingPageURLs; } IconSnapshot snapshot(bool forDeletion = false) const; + private: + IconRecord(const String& url); + String m_iconURL; time_t m_stamp; - OwnPtr<Image> m_image; + RefPtr<Image> m_image; HashSet<String> m_retainingPageURLs; diff --git a/WebCore/loader/loader.cpp b/WebCore/loader/loader.cpp index 0c7545f..6221a6a 100644 --- a/WebCore/loader/loader.cpp +++ b/WebCore/loader/loader.cpp @@ -3,7 +3,7 @@ Copyright (C) 2001 Dirk Mueller (mueller@kde.org) Copyright (C) 2002 Waldo Bastian (bastian@kde.org) Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -27,6 +27,7 @@ #include "Cache.h" #include "CachedImage.h" #include "CachedResource.h" +#include "CString.h" #include "DocLoader.h" #include "Frame.h" #include "FrameLoader.h" @@ -35,225 +36,453 @@ #include "ResourceHandle.h" #include "ResourceRequest.h" #include "ResourceResponse.h" +#include "SecurityOrigin.h" #include "SubresourceLoader.h" #include <wtf/Assertions.h> #include <wtf/Vector.h> +#define REQUEST_MANAGEMENT_ENABLED 0 +#define REQUEST_DEBUG 0 + namespace WebCore { +#if REQUEST_MANAGEMENT_ENABLED +// Match the parallel connection count used by the networking layer +// FIXME should not hardcode something like this +static const unsigned maxRequestsInFlightPerHost = 4; +// Having a limit might still help getting more important resources first +static const unsigned maxRequestsInFlightForNonHTTPProtocols = 20; +#else +static const unsigned maxRequestsInFlightPerHost = 10000; +static const unsigned maxRequestsInFlightForNonHTTPProtocols = 10000; +#endif + + Loader::Loader() + : m_nonHTTPProtocolHost(AtomicString(), maxRequestsInFlightForNonHTTPProtocols) + , m_requestTimer(this, &Loader::requestTimerFired) { - m_requestsPending.setAutoDelete(true); } Loader::~Loader() +{ + ASSERT_NOT_REACHED(); +} + +Loader::Priority Loader::determinePriority(const CachedResource* resource) const +{ +#if REQUEST_MANAGEMENT_ENABLED + switch (resource->type()) { + case CachedResource::CSSStyleSheet: +#if ENABLE(XSLT) + case CachedResource::XSLStyleSheet: +#endif +#if ENABLE(XBL) + case CachedResource::XBL: +#endif + return High; + case CachedResource::Script: + case CachedResource::FontResource: + return Medium; + case CachedResource::ImageResource: + return Low; + } + ASSERT_NOT_REACHED(); + return Low; +#else + return High; +#endif +} + +void Loader::load(DocLoader* docLoader, CachedResource* resource, bool incremental, bool skipCanLoadCheck, bool sendResourceLoadCallbacks) { - deleteAllValues(m_requestsLoading); + ASSERT(docLoader); + Request* request = new Request(docLoader, resource, incremental, skipCanLoadCheck, sendResourceLoadCallbacks); + + Host* host; + KURL url(resource->url()); + bool isHTTP = url.protocolIs("http") || url.protocolIs("https"); + if (isHTTP) { + AtomicString hostName = url.host(); + host = m_hosts.get(hostName.impl()); + if (!host) { + host = new Host(hostName, maxRequestsInFlightPerHost); + m_hosts.add(hostName.impl(), host); + } + } else + host = &m_nonHTTPProtocolHost; + + bool hadRequests = host->hasRequests(); + Priority priority = determinePriority(resource); + host->addRequest(request, priority); + docLoader->incrementRequestCount(); + + if (priority > Low || !isHTTP || !hadRequests) { + // Try to request important resources immediately + host->servePendingRequests(priority); + } else { + // Handle asynchronously so early low priority requests don't get scheduled before later high priority ones + scheduleServePendingRequests(); + } +} + +void Loader::scheduleServePendingRequests() +{ + if (!m_requestTimer.isActive()) + m_requestTimer.startOneShot(0); } -void Loader::load(DocLoader* dl, CachedResource* object, bool incremental, bool skipCanLoadCheck, bool sendResourceLoadCallbacks) +void Loader::requestTimerFired(Timer<Loader>*) { - ASSERT(dl); - Request* req = new Request(dl, object, incremental, skipCanLoadCheck, sendResourceLoadCallbacks); - m_requestsPending.append(req); - dl->incrementRequestCount(); servePendingRequests(); } -void Loader::servePendingRequests() +void Loader::servePendingRequests(Priority minimumPriority) { - while (!m_requestsPending.isEmpty()) { - // get the first pending request - Request* req = m_requestsPending.take(0); - DocLoader* dl = req->docLoader(); -#ifndef ANDROID_PRELOAD_CHANGES - dl->decrementRequestCount(); -#endif + m_requestTimer.stop(); + + m_nonHTTPProtocolHost.servePendingRequests(minimumPriority); + + Vector<Host*> hostsToServe; + copyValuesToVector(m_hosts, hostsToServe); + for (unsigned n = 0; n < hostsToServe.size(); ++n) { + Host* host = hostsToServe[n]; + if (host->hasRequests()) + host->servePendingRequests(minimumPriority); + else if (!host->processingResource()){ + AtomicString name = host->name(); + delete host; + m_hosts.remove(name.impl()); + } + } +} + +void Loader::cancelRequests(DocLoader* docLoader) +{ + if (m_nonHTTPProtocolHost.hasRequests()) + m_nonHTTPProtocolHost.cancelRequests(docLoader); + + Vector<Host*> hostsToCancel; + copyValuesToVector(m_hosts, hostsToCancel); + for (unsigned n = 0; n < hostsToCancel.size(); ++n) { + Host* host = hostsToCancel[n]; + if (host->hasRequests()) + host->cancelRequests(docLoader); + } - ResourceRequest request(req->cachedResource()->url()); -#ifdef ANDROID - request.setCachedResource(req->cachedResource()); -#endif + scheduleServePendingRequests(); + + if (docLoader->loadInProgress()) + ASSERT(docLoader->requestCount() == 1); + else + ASSERT(docLoader->requestCount() == 0); +} + +Loader::Host::Host(const AtomicString& name, unsigned maxRequestsInFlight) + : m_name(name) + , m_maxRequestsInFlight(maxRequestsInFlight) + , m_processingResource(false) +{ +} - if (!req->cachedResource()->accept().isEmpty()) - request.setHTTPAccept(req->cachedResource()->accept()); +Loader::Host::~Host() +{ + ASSERT(m_requestsLoading.isEmpty()); + for (unsigned p = 0; p <= High; p++) + ASSERT(m_requestsPending[p].isEmpty()); +} + +void Loader::Host::addRequest(Request* request, Priority priority) +{ + m_requestsPending[priority].append(request); +} + +bool Loader::Host::hasRequests() const +{ + if (!m_requestsLoading.isEmpty()) + return true; + for (unsigned p = 0; p <= High; p++) { + if (!m_requestsPending[p].isEmpty()) + return true; + } + return false; +} - KURL r = dl->doc()->url(); - if (r.protocol().startsWith("http") && r.path().isEmpty()) - r.setPath("/"); - request.setHTTPReferrer(r.string()); - DeprecatedString domain = r.host(); - if (dl->doc()->isHTMLDocument()) - domain = static_cast<HTMLDocument*>(dl->doc())->domain().deprecatedString(); - - RefPtr<SubresourceLoader> loader = SubresourceLoader::create(dl->doc()->frame(), - this, request, req->shouldSkipCanLoadCheck(), req->sendResourceLoadCallbacks()); +void Loader::Host::servePendingRequests(Loader::Priority minimumPriority) +{ + bool serveMore = true; + for (int priority = High; priority >= minimumPriority && serveMore; --priority) + servePendingRequests(m_requestsPending[priority], serveMore); +} - if (loader) { - m_requestsLoading.add(loader.release(), req); -#ifdef ANDROID_PRELOAD_CHANGES - req->cachedResource()->setRequestedFromNetworkingLayer(); - } else { - dl->decrementRequestCount(); - dl->setLoadInProgress(true); - req->cachedResource()->error(); - dl->setLoadInProgress(false); - - delete req; +void Loader::Host::servePendingRequests(RequestQueue& requestsPending, bool& serveLowerPriority) +{ + while (!requestsPending.isEmpty()) { + Request* request = requestsPending.first(); + DocLoader* docLoader = request->docLoader(); + bool resourceIsCacheValidator = request->cachedResource()->isCacheValidator(); + // If the document is fully parsed and there are no pending stylesheets there won't be any more + // resources that we would want to push to the front of the queue. Just hand off the remaining resources + // to the networking layer. + bool parsedAndStylesheetsKnown = !docLoader->doc()->parsing() && docLoader->doc()->haveStylesheetsLoaded(); + if (!parsedAndStylesheetsKnown && !resourceIsCacheValidator && m_requestsLoading.size() >= m_maxRequestsInFlight) { + serveLowerPriority = false; + return; } -#else - dl->incrementRequestCount(); - break; + requestsPending.removeFirst(); + + ResourceRequest resourceRequest(request->cachedResource()->url()); +#ifdef ANDROID + resourceRequest.setCachedResource(request->cachedResource()); +#endif + + if (!request->cachedResource()->accept().isEmpty()) + resourceRequest.setHTTPAccept(request->cachedResource()->accept()); + + KURL referrer = docLoader->doc()->url(); + if ((referrer.protocolIs("http") || referrer.protocolIs("https")) && referrer.path().isEmpty()) + referrer.setPath("/"); + resourceRequest.setHTTPReferrer(referrer.string()); + FrameLoader::addHTTPOriginIfNeeded(resourceRequest, docLoader->doc()->securityOrigin()->toString()); + + if (resourceIsCacheValidator) { + CachedResource* resourceToRevalidate = request->cachedResource()->resourceToRevalidate(); + ASSERT(resourceToRevalidate->canUseCacheValidator()); + ASSERT(resourceToRevalidate->isLoaded()); + const String& lastModified = resourceToRevalidate->response().httpHeaderField("Last-Modified"); + const String& eTag = resourceToRevalidate->response().httpHeaderField("ETag"); + if (!lastModified.isEmpty() || !eTag.isEmpty()) { + if (docLoader->cachePolicy() == CachePolicyReload || docLoader->cachePolicy() == CachePolicyRefresh) + resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0"); + if (!lastModified.isEmpty()) + resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified); + if (!eTag.isEmpty()) + resourceRequest.setHTTPHeaderField("If-None-Match", eTag); + } } - dl->setLoadInProgress(true); - req->cachedResource()->error(); - dl->setLoadInProgress(false); - - delete req; + RefPtr<SubresourceLoader> loader = SubresourceLoader::create(docLoader->doc()->frame(), + this, resourceRequest, request->shouldSkipCanLoadCheck(), request->sendResourceLoadCallbacks()); + if (loader) { + m_requestsLoading.add(loader.release(), request); + request->cachedResource()->setRequestedFromNetworkingLayer(); +#if REQUEST_DEBUG + 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->setLoadInProgress(true); + request->cachedResource()->error(); + docLoader->setLoadInProgress(false); + delete request; + } } } -void Loader::didFinishLoading(SubresourceLoader* loader) +void Loader::Host::didFinishLoading(SubresourceLoader* loader) { RequestMap::iterator i = m_requestsLoading.find(loader); if (i == m_requestsLoading.end()) return; + + m_processingResource = true; - Request* req = i->second; + Request* request = i->second; m_requestsLoading.remove(i); - DocLoader* docLoader = req->docLoader(); - if (!req->isMultipart()) + DocLoader* docLoader = request->docLoader(); + if (!request->isMultipart()) docLoader->decrementRequestCount(); - CachedResource* object = req->cachedResource(); + CachedResource* resource = request->cachedResource(); + ASSERT(!resource->resourceToRevalidate()); - docLoader->setLoadInProgress(true); - object->data(loader->resourceData(), true); - docLoader->setLoadInProgress(false); - object->finish(); + // If we got a 4xx response, we're pretending to have received a network + // error, so we can't send the successful data() and finish() callbacks. + if (!resource->errorOccurred()) { + docLoader->setLoadInProgress(true); + resource->data(loader->resourceData(), true); + resource->finish(); + } + + delete request; - delete req; + docLoader->setLoadInProgress(false); -#ifdef ANDROID_PRELOAD_CHANGES docLoader->checkForPendingPreloads(); -#endif +#if REQUEST_DEBUG + KURL u(resource->url()); + printf("HOST %s COUNT %d RECEIVED %s\n", u.host().latin1().data(), m_requestsLoading.size(), resource->url().latin1().data()); +#endif servePendingRequests(); + + m_processingResource = false; } -void Loader::didFail(SubresourceLoader* loader, const ResourceError&) +void Loader::Host::didFail(SubresourceLoader* loader, const ResourceError&) { didFail(loader); } -void Loader::didFail(SubresourceLoader* loader, bool cancelled) +void Loader::Host::didFail(SubresourceLoader* loader, bool cancelled) { + loader->clearClient(); + RequestMap::iterator i = m_requestsLoading.find(loader); if (i == m_requestsLoading.end()) return; - Request* req = i->second; + m_processingResource = true; + + Request* request = i->second; m_requestsLoading.remove(i); - DocLoader* docLoader = req->docLoader(); - if (!req->isMultipart()) + DocLoader* docLoader = request->docLoader(); + if (!request->isMultipart()) docLoader->decrementRequestCount(); - CachedResource* object = req->cachedResource(); + CachedResource* resource = request->cachedResource(); + + if (resource->resourceToRevalidate()) + cache()->revalidationFailed(resource); if (!cancelled) { docLoader->setLoadInProgress(true); - object->error(); + resource->error(); } docLoader->setLoadInProgress(false); -#ifdef ANDROID_PRELOAD_CHANGES - if (cancelled || !object->isPreloaded()) -#endif - cache()->remove(object); - - delete req; + if (cancelled || !resource->isPreloaded()) + cache()->remove(resource); + + delete request; -#ifdef ANDROID_PRELOAD_CHANGES docLoader->checkForPendingPreloads(); -#endif servePendingRequests(); + + m_processingResource = false; } -void Loader::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response) +void Loader::Host::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response) { - Request* req = m_requestsLoading.get(loader); + Request* request = m_requestsLoading.get(loader); // FIXME: This is a workaround for <rdar://problem/5236843> // If a load starts while the frame is still in the provisional state // (this can be the case when loading the user style sheet), committing the load then causes all - // requests to be removed from the m_requestsLoading map. This means that req might be null here. + // requests to be removed from the m_requestsLoading map. This means that request might be null here. // In that case we just return early. - // ASSERT(req); - if (!req) + // ASSERT(request); + if (!request) return; - req->cachedResource()->setResponse(response); + + CachedResource* resource = request->cachedResource(); + + if (resource->isCacheValidator()) { + if (response.httpStatusCode() == 304) { + // 304 Not modified / Use local copy + m_requestsLoading.remove(loader); + request->docLoader()->decrementRequestCount(); + + // Existing resource is ok, just use it updating the expiration time. + cache()->revalidationSucceeded(resource, response); + + if (request->docLoader()->frame()) + request->docLoader()->frame()->loader()->checkCompleted(); + + delete request; + + servePendingRequests(); + return; + } + // Did not get 304 response, continue as a regular resource load. + cache()->revalidationFailed(resource); + } + + resource->setResponse(response); String encoding = response.textEncodingName(); if (!encoding.isNull()) - req->cachedResource()->setEncoding(encoding); + request->cachedResource()->setEncoding(encoding); - if (req->isMultipart()) { - ASSERT(req->cachedResource()->isImage()); - static_cast<CachedImage*>(req->cachedResource())->clear(); - if (req->docLoader()->frame()) - req->docLoader()->frame()->loader()->checkCompleted(); + if (request->isMultipart()) { + ASSERT(request->cachedResource()->isImage()); + static_cast<CachedImage*>(request->cachedResource())->clear(); + if (request->docLoader()->frame()) + request->docLoader()->frame()->loader()->checkCompleted(); } else if (response.isMultipart()) { - req->setIsMultipart(true); + request->setIsMultipart(true); // We don't count multiParts in a DocLoader's request count - req->docLoader()->decrementRequestCount(); + request->docLoader()->decrementRequestCount(); // If we get a multipart response, we must have a handle ASSERT(loader->handle()); - if (!req->cachedResource()->isImage()) + if (!request->cachedResource()->isImage()) loader->handle()->cancel(); } } -void Loader::didReceiveData(SubresourceLoader* loader, const char* data, int size) +void Loader::Host::didReceiveData(SubresourceLoader* loader, const char* data, int size) { Request* request = m_requestsLoading.get(loader); if (!request) return; - CachedResource* object = request->cachedResource(); + CachedResource* resource = request->cachedResource(); + ASSERT(!resource->isCacheValidator()); + + if (resource->errorOccurred()) + return; + + m_processingResource = true; + + if (resource->response().httpStatusCode() / 100 == 4) { + // Treat a 4xx response like a network error. + resource->error(); + m_processingResource = false; + return; + } // Set the data. if (request->isMultipart()) { // The loader delivers the data in a multipart section all at once, send eof. // The resource data will change as the next part is loaded, so we need to make a copy. - SharedBuffer* copiedData = new SharedBuffer(data, size); - object->data(copiedData, true); + RefPtr<SharedBuffer> copiedData = SharedBuffer::create(data, size); + resource->data(copiedData.release(), true); } else if (request->isIncremental()) - object->data(loader->resourceData(), false); + resource->data(loader->resourceData(), false); + + m_processingResource = false; } - -void Loader::cancelRequests(DocLoader* dl) + +void Loader::Host::cancelPendingRequests(RequestQueue& requestsPending, DocLoader* docLoader) { - DeprecatedPtrListIterator<Request> pIt(m_requestsPending); - while (pIt.current()) { - if (pIt.current()->docLoader() == dl) { - cache()->remove(pIt.current()->cachedResource()); - m_requestsPending.remove(pIt); - dl->decrementRequestCount(); + RequestQueue remaining; + RequestQueue::iterator end = requestsPending.end(); + for (RequestQueue::iterator it = requestsPending.begin(); it != end; ++it) { + Request* request = *it; + if (request->docLoader() == docLoader) { + cache()->remove(request->cachedResource()); + delete request; + docLoader->decrementRequestCount(); } else - ++pIt; + remaining.append(request); } + requestsPending.swap(remaining); +} + +void Loader::Host::cancelRequests(DocLoader* docLoader) +{ + for (unsigned p = 0; p <= High; p++) + cancelPendingRequests(m_requestsPending[p], docLoader); Vector<SubresourceLoader*, 256> loadersToCancel; RequestMap::iterator end = m_requestsLoading.end(); for (RequestMap::iterator i = m_requestsLoading.begin(); i != end; ++i) { Request* r = i->second; - if (r->docLoader() == dl) + if (r->docLoader() == docLoader) loadersToCancel.append(i->first.get()); } @@ -261,11 +490,6 @@ void Loader::cancelRequests(DocLoader* dl) SubresourceLoader* loader = loadersToCancel[i]; didFail(loader, true); } - - if (dl->loadInProgress()) - ASSERT(dl->requestCount() == 1); - else - ASSERT(dl->requestCount() == 0); } } //namespace WebCore diff --git a/WebCore/loader/loader.h b/WebCore/loader/loader.h index 7ad47f8..c51374c 100644 --- a/WebCore/loader/loader.h +++ b/WebCore/loader/loader.h @@ -1,7 +1,7 @@ /* Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) Copyright (C) 2001 Dirk Mueller <mueller@kde.org> - Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. + Copyright (C) 2004, 2006, 2007, 2008 Apple Inc. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -22,9 +22,14 @@ #ifndef loader_h #define loader_h -#include "DeprecatedPtrList.h" +#include "AtomicString.h" +#include "AtomicStringImpl.h" +#include "PlatformString.h" #include "SubresourceLoaderClient.h" +#include "Timer.h" +#include <wtf/Deque.h> #include <wtf/HashMap.h> +#include <wtf/Noncopyable.h> namespace WebCore { @@ -32,7 +37,7 @@ namespace WebCore { class DocLoader; class Request; - class Loader : private SubresourceLoaderClient { + class Loader : Noncopyable { public: Loader(); ~Loader(); @@ -40,19 +45,51 @@ namespace WebCore { void load(DocLoader*, CachedResource*, bool incremental = true, bool skipCanLoadCheck = false, bool sendResourceLoadCallbacks = true); void cancelRequests(DocLoader*); + + enum Priority { Low, Medium, High }; + void servePendingRequests(Priority minimumPriority = Low); private: - virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&); - virtual void didReceiveData(SubresourceLoader*, const char*, int); - virtual void didFinishLoading(SubresourceLoader*); - virtual void didFail(SubresourceLoader*, const ResourceError&); - void didFail(SubresourceLoader*, bool cancelled = false); + Priority determinePriority(const CachedResource*) const; + void scheduleServePendingRequests(); + + void requestTimerFired(Timer<Loader>*); - void servePendingRequests(); - - DeprecatedPtrList<Request> m_requestsPending; - typedef HashMap<RefPtr<SubresourceLoader>, Request*> RequestMap; - RequestMap m_requestsLoading; + class Host : private SubresourceLoaderClient { + public: + Host(const AtomicString& name, unsigned maxRequestsInFlight); + ~Host(); + + const AtomicString& name() const { return m_name; } + void addRequest(Request*, Priority); + void servePendingRequests(Priority minimumPriority = Low); + void cancelRequests(DocLoader*); + bool hasRequests() const; + bool processingResource() const { return m_processingResource; } + + private: + virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&); + virtual void didReceiveData(SubresourceLoader*, const char*, int); + virtual void didFinishLoading(SubresourceLoader*); + virtual void didFail(SubresourceLoader*, const ResourceError&); + + typedef Deque<Request*> RequestQueue; + void servePendingRequests(RequestQueue& requestsPending, bool& serveLowerPriority); + void didFail(SubresourceLoader*, bool cancelled = false); + void cancelPendingRequests(RequestQueue& requestsPending, DocLoader*); + + RequestQueue m_requestsPending[High + 1]; + typedef HashMap<RefPtr<SubresourceLoader>, Request*> RequestMap; + RequestMap m_requestsLoading; + const AtomicString m_name; + const int m_maxRequestsInFlight; + bool m_processingResource; + }; + typedef HashMap<AtomicStringImpl*, Host*> HostMap; + HostMap m_hosts; + Host m_nonHTTPProtocolHost; + + Timer<Loader> m_requestTimer; }; } diff --git a/WebCore/loader/mac/DocumentLoaderMac.cpp b/WebCore/loader/mac/DocumentLoaderMac.cpp new file mode 100644 index 0000000..05c6e26 --- /dev/null +++ b/WebCore/loader/mac/DocumentLoaderMac.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#include "config.h" +#include "DocumentLoader.h" +#include "MainResourceLoader.h" +#include "ResourceHandle.h" +#include "ResourceLoader.h" + +namespace WebCore { + +#ifndef BUILDING_ON_TIGER +static void scheduleAll(const ResourceLoaderSet& loaders, SchedulePair* pair) +{ + const ResourceLoaderSet copy = loaders; + ResourceLoaderSet::const_iterator end = copy.end(); + for (ResourceLoaderSet::const_iterator it = copy.begin(); it != end; ++it) + if (ResourceHandle* handle = (*it)->handle()) + handle->schedule(pair); +} + +static void unscheduleAll(const ResourceLoaderSet& loaders, SchedulePair* pair) +{ + const ResourceLoaderSet copy = loaders; + ResourceLoaderSet::const_iterator end = copy.end(); + for (ResourceLoaderSet::const_iterator it = copy.begin(); it != end; ++it) + if (ResourceHandle* handle = (*it)->handle()) + handle->unschedule(pair); +} +#endif + +void DocumentLoader::schedule(SchedulePair* pair) +{ +#ifndef BUILDING_ON_TIGER + if (m_mainResourceLoader && m_mainResourceLoader->handle()) + m_mainResourceLoader->handle()->schedule(pair); + scheduleAll(m_subresourceLoaders, pair); + scheduleAll(m_plugInStreamLoaders, pair); + scheduleAll(m_multipartSubresourceLoaders, pair); +#endif +} + +void DocumentLoader::unschedule(SchedulePair* pair) +{ +#ifndef BUILDING_ON_TIGER + if (m_mainResourceLoader && m_mainResourceLoader->handle()) + m_mainResourceLoader->handle()->unschedule(pair); + unscheduleAll(m_subresourceLoaders, pair); + unscheduleAll(m_plugInStreamLoaders, pair); + unscheduleAll(m_multipartSubresourceLoaders, pair); +#endif +} + +} // namespace diff --git a/WebCore/loader/mac/LoaderNSURLExtras.h b/WebCore/loader/mac/LoaderNSURLExtras.h index 39ef222..ce5a490 100644 --- a/WebCore/loader/mac/LoaderNSURLExtras.h +++ b/WebCore/loader/mac/LoaderNSURLExtras.h @@ -32,17 +32,6 @@ extern "C" { #endif -NSURL *urlByRemovingComponent(NSURL *url, CFURLComponentType component); -NSURL *urlByRemovingFragment(NSURL *url); -NSString *urlOriginalDataAsString(NSURL *url); -NSData *urlOriginalData(NSURL *url); -NSURL *urlWithData(NSData *data); -NSURL *urlWithDataRelativeToURL(NSData *data, NSURL *baseURL); -NSURL *urlByRemovingResourceSpecifier(NSURL *url); -BOOL urlIsFileURL(NSURL *url); -BOOL stringIsFileURL(NSString *urlString); -BOOL urlIsEmpty(NSURL *url); -NSURL *canonicalURL(NSURL *url); NSString *suggestedFilenameWithMIMEType(NSURL *url, NSString *MIMEType); #ifdef __cplusplus diff --git a/WebCore/loader/mac/LoaderNSURLExtras.m b/WebCore/loader/mac/LoaderNSURLExtras.m deleted file mode 100644 index 76ad5c7..0000000 --- a/WebCore/loader/mac/LoaderNSURLExtras.m +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. - */ - -#import "config.h" -#import "LoaderNSURLExtras.h" - -#import <wtf/Assertions.h> -#import <wtf/Vector.h> -#import "KURL.h" -#import "LocalizedStrings.h" -#import "MIMETypeRegistry.h" -#import "PlatformString.h" -#import "WebCoreNSStringExtras.h" -#import "WebCoreSystemInterface.h" - -using namespace WebCore; - -NSURL *urlByRemovingComponent(NSURL *url, CFURLComponentType component) -{ - if (!url) - return nil; - - CFRange fragRg = CFURLGetByteRangeForComponent((CFURLRef)url, component, NULL); - // Check to see if a fragment exists before decomposing the URL. - if (fragRg.location == kCFNotFound) - return url; - - UInt8 *urlBytes, buffer[2048]; - CFIndex numBytes = CFURLGetBytes((CFURLRef)url, buffer, 2048); - if (numBytes == -1) { - numBytes = CFURLGetBytes((CFURLRef)url, NULL, 0); - urlBytes = static_cast<UInt8*>(malloc(numBytes)); - CFURLGetBytes((CFURLRef)url, urlBytes, numBytes); - } else - urlBytes = buffer; - - NSURL *result = (NSURL *)CFMakeCollectable(CFURLCreateWithBytes(NULL, urlBytes, fragRg.location - 1, kCFStringEncodingUTF8, NULL)); - if (!result) - result = (NSURL *)CFMakeCollectable(CFURLCreateWithBytes(NULL, urlBytes, fragRg.location - 1, kCFStringEncodingISOLatin1, NULL)); - - if (urlBytes != buffer) free(urlBytes); - return result ? [result autorelease] : url; -} - -NSURL *urlByRemovingFragment(NSURL *url) -{ - return urlByRemovingComponent(url, kCFURLComponentFragment); -} - -NSString *urlOriginalDataAsString(NSURL *url) -{ - return [[[NSString alloc] initWithData:urlOriginalData(url) encoding:NSISOLatin1StringEncoding] autorelease]; -} - -#define URL_BYTES_BUFFER_LENGTH 2048 - -NSData *urlOriginalData(NSURL *url) -{ - if (!url) - return nil; - - UInt8 *buffer = (UInt8 *)malloc(URL_BYTES_BUFFER_LENGTH); - CFIndex bytesFilled = CFURLGetBytes((CFURLRef)url, buffer, URL_BYTES_BUFFER_LENGTH); - if (bytesFilled == -1) { - CFIndex bytesToAllocate = CFURLGetBytes((CFURLRef)url, NULL, 0); - buffer = (UInt8 *)realloc(buffer, bytesToAllocate); - bytesFilled = CFURLGetBytes((CFURLRef)url, buffer, bytesToAllocate); - ASSERT(bytesFilled == bytesToAllocate); - } - - // buffer is adopted by the NSData - NSData *data = [NSData dataWithBytesNoCopy:buffer length:bytesFilled freeWhenDone:YES]; - - NSURL *baseURL = (NSURL *)CFURLGetBaseURL((CFURLRef)url); - if (baseURL) - return urlOriginalData(urlWithDataRelativeToURL(data, baseURL)); - return data; -} - -NSURL *urlWithData(NSData *data) -{ - if (data == nil) - return nil; - - return urlWithDataRelativeToURL(data, nil); -} - -static inline id WebCFAutorelease(CFTypeRef obj) -{ - if (obj) - CFMakeCollectable(obj); - [(id)obj autorelease]; - return (id)obj; -} - -NSURL *urlWithDataRelativeToURL(NSData *data, NSURL *baseURL) -{ - if (data == nil) - return nil; - - NSURL *result = nil; - size_t length = [data length]; - if (length > 0) { - // work around <rdar://4470771>: CFURLCreateAbsoluteURLWithBytes(.., TRUE) doesn't remove non-path components. - baseURL = urlByRemovingResourceSpecifier(baseURL); - - const UInt8 *bytes = static_cast<const UInt8*>([data bytes]); - // NOTE: We use UTF-8 here since this encoding is used when computing strings when returning URL components - // (e.g calls to NSURL -path). However, this function is not tolerant of illegal UTF-8 sequences, which - // could either be a malformed string or bytes in a different encoding, like shift-jis, so we fall back - // onto using ISO Latin 1 in those cases. - result = WebCFAutorelease(CFURLCreateAbsoluteURLWithBytes(NULL, bytes, length, kCFStringEncodingUTF8, (CFURLRef)baseURL, YES)); - if (!result) - result = WebCFAutorelease(CFURLCreateAbsoluteURLWithBytes(NULL, bytes, length, kCFStringEncodingISOLatin1, (CFURLRef)baseURL, YES)); - } else - result = [NSURL URLWithString:@""]; - - return result; -} - -NSURL *urlByRemovingResourceSpecifier(NSURL *url) -{ - return urlByRemovingComponent(url, kCFURLComponentResourceSpecifier); -} - - -BOOL urlIsFileURL(NSURL *url) -{ - if (!url) - return false; - - return stringIsFileURL(urlOriginalDataAsString(url)); -} - -BOOL stringIsFileURL(NSString *urlString) -{ - return [urlString rangeOfString:@"file:" options:(NSCaseInsensitiveSearch | NSAnchoredSearch)].location != NSNotFound; -} - -BOOL urlIsEmpty(NSURL *url) -{ - if (!url) - return false; - if (!CFURLGetBaseURL((CFURLRef)url)) - return CFURLGetBytes((CFURLRef)url, NULL, 0) == 0; - return [urlOriginalData(url) length] == 0; -} - -NSURL *canonicalURL(NSURL *url) -{ - if (!url) - return nil; - - NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url]; - Class concreteClass = wkNSURLProtocolClassForReqest(request); - if (!concreteClass) { - [request release]; - return url; - } - - // This applies NSURL's concept of canonicalization, but not KURL's concept. It would - // make sense to apply both, but when we tried that it caused a performance degradation - // (see 5315926). It might make sense to apply only the KURL concept and not the NSURL - // concept, but it's too risky to make that change for WebKit 3.0. - NSURLRequest *newRequest = [concreteClass canonicalRequestForRequest:request]; - NSURL *newURL = [newRequest URL]; - NSURL *result = [[newURL retain] autorelease]; - [request release]; - - return result; -} - -static bool vectorContainsString(Vector<String> vector, String string) -{ - int size = vector.size(); - for (int i = 0; i < size; i++) - if (vector[i] == string) - return true; - return false; -} - -NSString *suggestedFilenameWithMIMEType(NSURL *url, NSString *MIMEType) -{ - // Get the filename from the URL. Try the lastPathComponent first. - NSString *lastPathComponent = [[url path] lastPathComponent]; - NSString *filename = filenameByFixingIllegalCharacters(lastPathComponent); - NSString *extension = nil; - - if ([filename length] == 0 || [lastPathComponent isEqualToString:@"/"]) { - // lastPathComponent is no good, try the host. - NSString *host = (NSString *)(KURL(url).host()); - filename = filenameByFixingIllegalCharacters(host); - if ([filename length] == 0) { - // Can't make a filename using this URL, use "unknown". - filename = copyImageUnknownFileLabel(); - } - } else { - // Save the extension for later correction. Only correct the extension of the lastPathComponent. - // For example, if the filename ends up being the host, we wouldn't want to correct ".com" in "www.apple.com". - extension = [filename pathExtension]; - } - - // No mime type reported. Just return the filename we have now. - if (!MIMEType) { - return filename; - } - - // Do not correct filenames that are reported with a mime type of tar, and - // have a filename which has .tar in it or ends in .tgz - if (([MIMEType isEqualToString:@"application/tar"] || [MIMEType isEqualToString:@"application/x-tar"]) - && (hasCaseInsensitiveSubstring(filename, @".tar") - || hasCaseInsensitiveSuffix(filename, @".tgz"))) { - return filename; - } - - // I don't think we need to worry about this for the image case - // If the type is known, check the extension and correct it if necessary. - if (![MIMEType isEqualToString:@"application/octet-stream"] && ![MIMEType isEqualToString:@"text/plain"]) { - Vector<String> extensions = MIMETypeRegistry::getExtensionsForMIMEType(MIMEType); - - if (extensions.isEmpty() || !vectorContainsString(extensions, extension)) { - // The extension doesn't match the MIME type. Correct this. - NSString *correctExtension = MIMETypeRegistry::getPreferredExtensionForMIMEType(MIMEType); - if ([correctExtension length] != 0) { - // Append the correct extension. - filename = [filename stringByAppendingPathExtension:correctExtension]; - } - } - } - - return filename; -} diff --git a/WebCore/loader/mac/LoaderNSURLExtras.mm b/WebCore/loader/mac/LoaderNSURLExtras.mm new file mode 100644 index 0000000..9a507f5 --- /dev/null +++ b/WebCore/loader/mac/LoaderNSURLExtras.mm @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2005, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#import "config.h" +#import "LoaderNSURLExtras.h" + +#import <wtf/Assertions.h> +#import <wtf/Vector.h> +#import "KURL.h" +#import "LocalizedStrings.h" +#import "MIMETypeRegistry.h" +#import "PlatformString.h" +#import "WebCoreNSStringExtras.h" + +using namespace WebCore; + +static bool vectorContainsString(const Vector<String>& vector, const String& string) +{ + int size = vector.size(); + for (int i = 0; i < size; i++) + if (vector[i] == string) + return true; + return false; +} + +NSString *suggestedFilenameWithMIMEType(NSURL *url, NSString *MIMEType) +{ + // Get the filename from the URL. Try the lastPathComponent first. + NSString *lastPathComponent = [[url path] lastPathComponent]; + NSString *filename = filenameByFixingIllegalCharacters(lastPathComponent); + NSString *extension = nil; + + if ([filename length] == 0 || [lastPathComponent isEqualToString:@"/"]) { + // lastPathComponent is no good, try the host. + NSString *host = KURL(url).host(); + filename = filenameByFixingIllegalCharacters(host); + if ([filename length] == 0) { + // Can't make a filename using this URL, use "unknown". + filename = copyImageUnknownFileLabel(); + } + } else { + // Save the extension for later correction. Only correct the extension of the lastPathComponent. + // For example, if the filename ends up being the host, we wouldn't want to correct ".com" in "www.apple.com". + extension = [filename pathExtension]; + } + + // No mime type reported. Just return the filename we have now. + if (!MIMEType) { + return filename; + } + + // Do not correct filenames that are reported with a mime type of tar, and + // have a filename which has .tar in it or ends in .tgz + if (([MIMEType isEqualToString:@"application/tar"] || [MIMEType isEqualToString:@"application/x-tar"]) + && (hasCaseInsensitiveSubstring(filename, @".tar") + || hasCaseInsensitiveSuffix(filename, @".tgz"))) { + return filename; + } + + // I don't think we need to worry about this for the image case + // If the type is known, check the extension and correct it if necessary. + if (![MIMEType isEqualToString:@"application/octet-stream"] && ![MIMEType isEqualToString:@"text/plain"]) { + Vector<String> extensions = MIMETypeRegistry::getExtensionsForMIMEType(MIMEType); + + if (extensions.isEmpty() || !vectorContainsString(extensions, extension)) { + // The extension doesn't match the MIME type. Correct this. + NSString *correctExtension = MIMETypeRegistry::getPreferredExtensionForMIMEType(MIMEType); + if ([correctExtension length] != 0) { + // Append the correct extension. + filename = [filename stringByAppendingPathExtension:correctExtension]; + } + } + } + + return filename; +} |