// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/appcache/appcache.h" #include #include #include #include "base/check_op.h" #include "base/notreached.h" #include "base/stl_util.h" #include "content/browser/appcache/appcache_database.h" #include "content/browser/appcache/appcache_group.h" #include "content/browser/appcache/appcache_host.h" #include "content/browser/appcache/appcache_storage.h" #include "content/common/appcache_interfaces.h" #include "third_party/blink/public/mojom/appcache/appcache.mojom.h" #include "url/origin.h" namespace content { // static bool AppCache::CheckValidManifestScope(const GURL& manifest_url, const std::string& manifest_scope) { if (manifest_scope.empty()) return false; const GURL url = manifest_url.Resolve(manifest_scope); return url.is_valid() && !url.has_ref() && !url.has_query() && url.spec().back() == '/'; } // static std::string AppCache::GetManifestScope(const GURL& manifest_url, std::string optional_scope) { DCHECK(manifest_url.is_valid()); if (!optional_scope.empty()) { std::string scope = manifest_url.Resolve(optional_scope).path(); if (CheckValidManifestScope(manifest_url, scope)) { return optional_scope; } } // The default manifest scope is the path to the manifest URL's containing // directory. const GURL manifest_scope_url = manifest_url.GetWithoutFilename(); DCHECK(manifest_scope_url.is_valid()); DCHECK(CheckValidManifestScope(manifest_url, manifest_scope_url.path())); return manifest_scope_url.path(); } AppCache::AppCache(AppCacheStorage* storage, int64_t cache_id) : cache_id_(cache_id), owning_group_(nullptr), online_safelist_all_(false), is_complete_(false), cache_size_(0), padding_size_(0), manifest_parser_version_(-1), manifest_scope_(""), storage_(storage) { storage_->working_set()->AddCache(this); } AppCache::~AppCache() { DCHECK(associated_hosts_.empty()); if (owning_group_.get()) { DCHECK(is_complete_); owning_group_->RemoveCache(this); } DCHECK(!owning_group_.get()); storage_->working_set()->RemoveCache(this); } void AppCache::UnassociateHost(AppCacheHost* host) { associated_hosts_.erase(host); } void AppCache::AddEntry(const GURL& url, const AppCacheEntry& entry) { DCHECK(entries_.find(url) == entries_.end()); entries_.insert(EntryMap::value_type(url, entry)); cache_size_ += entry.response_size(); padding_size_ += entry.padding_size(); } bool AppCache::AddOrModifyEntry(const GURL& url, const AppCacheEntry& entry) { std::pair ret = entries_.insert(EntryMap::value_type(url, entry)); // Entry already exists. Merge the types and token expiration of the new and // existing entries. if (!ret.second) { ret.first->second.add_types(entry.types()); } else { cache_size_ += entry.response_size(); // New entry. Add to cache size. padding_size_ += entry.padding_size(); } return ret.second; } void AppCache::RemoveEntry(const GURL& url) { auto found = entries_.find(url); DCHECK(found != entries_.end()); DCHECK_GE(cache_size_, found->second.response_size()); DCHECK_GE(padding_size_, found->second.padding_size()); cache_size_ -= found->second.response_size(); padding_size_ -= found->second.padding_size(); entries_.erase(found); } AppCacheEntry* AppCache::GetEntry(const GURL& url) { auto it = entries_.find(url); return (it != entries_.end()) ? &(it->second) : nullptr; } const AppCacheEntry* AppCache::GetEntryAndUrlWithResponseId( int64_t response_id, GURL* optional_url_out) { for (const auto& pair : entries_) { if (pair.second.response_id() == response_id) { if (optional_url_out) *optional_url_out = pair.first; return &pair.second; } } return nullptr; } GURL AppCache::GetNamespaceEntryUrl( const std::vector& namespaces, const GURL& namespace_url) const { size_t count = namespaces.size(); for (size_t i = 0; i < count; ++i) { if (namespaces[i].namespace_url == namespace_url) return namespaces[i].target_url; } NOTREACHED(); return GURL(); } namespace { bool SortNamespacesByLength( const AppCacheNamespace& lhs, const AppCacheNamespace& rhs) { return lhs.namespace_url.spec().length() > rhs.namespace_url.spec().length(); } } void AppCache::InitializeWithManifest(AppCacheManifest* manifest) { DCHECK(manifest); manifest_parser_version_ = manifest->parser_version; manifest_scope_ = manifest->scope; intercept_namespaces_.swap(manifest->intercept_namespaces); fallback_namespaces_.swap(manifest->fallback_namespaces); online_safelist_namespaces_.swap(manifest->online_safelist_namespaces); online_safelist_all_ = manifest->online_safelist_all; token_expires_ = manifest->token_expires; // Sort the namespaces by url string length, longest to shortest, // since longer matches trump when matching a url to a namespace. std::sort(intercept_namespaces_.begin(), intercept_namespaces_.end(), SortNamespacesByLength); std::sort(fallback_namespaces_.begin(), fallback_namespaces_.end(), SortNamespacesByLength); } void AppCache::InitializeWithDatabaseRecords( const AppCacheDatabase::CacheRecord& cache_record, const std::vector& entries, const std::vector& intercepts, const std::vector& fallbacks, const std::vector& safelists) { DCHECK_EQ(cache_id_, cache_record.cache_id); manifest_parser_version_ = cache_record.manifest_parser_version; manifest_scope_ = cache_record.manifest_scope; online_safelist_all_ = cache_record.online_wildcard; update_time_ = cache_record.update_time; token_expires_ = cache_record.token_expires; for (const AppCacheDatabase::EntryRecord& entry : entries) { AddEntry(entry.url, AppCacheEntry(entry.flags, entry.response_id, entry.response_size, entry.padding_size)); } DCHECK_EQ(cache_size_, cache_record.cache_size); DCHECK_EQ(padding_size_, cache_record.padding_size); for (const auto& intercept : intercepts) intercept_namespaces_.push_back(intercept.namespace_); for (const auto& fallback : fallbacks) fallback_namespaces_.push_back(fallback.namespace_); // Sort the fallback namespaces by url string length, longest to shortest, // since longer matches trump when matching a url to a namespace. std::sort(intercept_namespaces_.begin(), intercept_namespaces_.end(), SortNamespacesByLength); std::sort(fallback_namespaces_.begin(), fallback_namespaces_.end(), SortNamespacesByLength); for (const auto& record : safelists) { online_safelist_namespaces_.emplace_back(APPCACHE_NETWORK_NAMESPACE, record.namespace_url, GURL()); } } void AppCache::ToDatabaseRecords( const AppCacheGroup* group, AppCacheDatabase::CacheRecord* cache_record, std::vector* entries, std::vector* intercepts, std::vector* fallbacks, std::vector* safelists) { DCHECK(group && cache_record && entries && fallbacks && safelists); DCHECK(entries->empty() && fallbacks->empty() && safelists->empty()); cache_record->cache_id = cache_id_; cache_record->group_id = group->group_id(); cache_record->online_wildcard = online_safelist_all_; cache_record->update_time = update_time_; cache_record->cache_size = cache_size_; cache_record->padding_size = padding_size_; cache_record->manifest_parser_version = manifest_parser_version_; cache_record->manifest_scope = manifest_scope_; cache_record->token_expires = token_expires_; for (const auto& pair : entries_) { entries->push_back(AppCacheDatabase::EntryRecord()); AppCacheDatabase::EntryRecord& record = entries->back(); record.url = pair.first; record.cache_id = cache_id_; record.flags = pair.second.types(); record.response_id = pair.second.response_id(); record.response_size = pair.second.response_size(); record.padding_size = pair.second.padding_size(); } const url::Origin origin = url::Origin::Create(group->manifest_url()); for (const AppCacheNamespace& intercept_namespace : intercept_namespaces_) { intercepts->push_back(AppCacheDatabase::NamespaceRecord()); AppCacheDatabase::NamespaceRecord& record = intercepts->back(); record.cache_id = cache_id_; record.origin = origin; record.namespace_ = intercept_namespace; } for (const AppCacheNamespace& fallback_namespace : fallback_namespaces_) { fallbacks->push_back(AppCacheDatabase::NamespaceRecord()); AppCacheDatabase::NamespaceRecord& record = fallbacks->back(); record.cache_id = cache_id_; record.origin = origin; record.namespace_ = fallback_namespace; } for (const AppCacheNamespace& online_namespace : online_safelist_namespaces_) { safelists->push_back(AppCacheDatabase::OnlineSafeListRecord()); AppCacheDatabase::OnlineSafeListRecord& record = safelists->back(); record.cache_id = cache_id_; record.namespace_url = online_namespace.namespace_url; } } bool AppCache::FindResponseForRequest(const GURL& url, AppCacheEntry* found_entry, GURL* found_intercept_namespace, AppCacheEntry* found_fallback_entry, GURL* found_fallback_namespace, bool* found_network_namespace) { // Ignore fragments when looking up URL in the cache. GURL url_no_ref; if (url.has_ref()) { GURL::Replacements replacements; replacements.ClearRef(); url_no_ref = url.ReplaceComponents(replacements); } else { url_no_ref = url; } // 6.6.6 Changes to the networking model AppCacheEntry* entry = GetEntry(url_no_ref); if (entry) { *found_entry = *entry; return true; } *found_network_namespace = IsInNetworkNamespace(url_no_ref); if (*found_network_namespace) return true; const AppCacheNamespace* intercept_namespace = FindInterceptNamespace(url_no_ref); if (intercept_namespace) { entry = GetEntry(intercept_namespace->target_url); DCHECK(entry); *found_entry = *entry; *found_intercept_namespace = intercept_namespace->namespace_url; return true; } const AppCacheNamespace* fallback_namespace = FindFallbackNamespace(url_no_ref); if (fallback_namespace) { entry = GetEntry(fallback_namespace->target_url); DCHECK(entry); *found_fallback_entry = *entry; *found_fallback_namespace = fallback_namespace->namespace_url; return true; } *found_network_namespace = online_safelist_all_; return *found_network_namespace; } void AppCache::ToResourceInfoVector( std::vector* infos) const { DCHECK(infos && infos->empty()); for (const auto& pair : entries_) { infos->push_back(blink::mojom::AppCacheResourceInfo()); blink::mojom::AppCacheResourceInfo& info = infos->back(); info.url = pair.first; info.is_master = pair.second.IsMaster(); info.is_manifest = pair.second.IsManifest(); info.is_intercept = pair.second.IsIntercept(); info.is_fallback = pair.second.IsFallback(); info.is_foreign = pair.second.IsForeign(); info.is_explicit = pair.second.IsExplicit(); info.response_size = pair.second.response_size(); info.padding_size = pair.second.padding_size(); info.response_id = pair.second.response_id(); } } // static const AppCacheNamespace* AppCache::FindNamespace( const std::vector& namespaces, const GURL& url) { size_t count = namespaces.size(); for (size_t i = 0; i < count; ++i) { if (namespaces[i].IsMatch(url)) return &namespaces[i]; } return nullptr; } } // namespace content