diff options
author | hanxi <hanxi@chromium.org> | 2014-09-11 14:57:18 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-09-11 22:25:47 +0000 |
commit | 149b92db00bc1deab22599e7d3da3fc8be2c0d8b (patch) | |
tree | 68a64107a7d6b9190fb1dc040b7f511eea92ff9d /components/web_cache | |
parent | 28d886c967e016a5d5812be43cd5916f577c2e10 (diff) | |
download | chromium_src-149b92db00bc1deab22599e7d3da3fc8be2c0d8b.zip chromium_src-149b92db00bc1deab22599e7d3da3fc8be2c0d8b.tar.gz chromium_src-149b92db00bc1deab22599e7d3da3fc8be2c0d8b.tar.bz2 |
WebCacheManager::ClearCacheOnNavigation() is used by webrequest api, which will be moved to //extensions. So we need to resolve its the dependency on WebCacheManager by moving WebCacheManager to //components.
There are two ipc messages sent in WebCacheManger:
-ChromeViewMsg_SetCacheCapacities
-ChromeViewMsg_ClearCache
Both ipc handlers were in ChromeRenderProcessObserver. These two ipcs are renamed as WebCacheMsg_XXX, and we introduce WebCacheRenderProcessObserver and handle these ipcs in //components.
BUG=352293,411437
TBR=agl@chromium.org
Review URL: https://codereview.chromium.org/528363002
Cr-Commit-Position: refs/heads/master@{#294475}
Diffstat (limited to 'components/web_cache')
-rw-r--r-- | components/web_cache/OWNERS | 10 | ||||
-rw-r--r-- | components/web_cache/browser/BUILD.gn | 18 | ||||
-rw-r--r-- | components/web_cache/browser/DEPS | 5 | ||||
-rw-r--r-- | components/web_cache/browser/web_cache_manager.cc | 436 | ||||
-rw-r--r-- | components/web_cache/browser/web_cache_manager.h | 241 | ||||
-rw-r--r-- | components/web_cache/browser/web_cache_manager_unittest.cc | 466 | ||||
-rw-r--r-- | components/web_cache/common/BUILD.gn | 16 | ||||
-rw-r--r-- | components/web_cache/common/DEPS | 3 | ||||
-rw-r--r-- | components/web_cache/common/OWNERS | 11 | ||||
-rw-r--r-- | components/web_cache/common/web_cache_message_generator.cc | 33 | ||||
-rw-r--r-- | components/web_cache/common/web_cache_message_generator.h | 7 | ||||
-rw-r--r-- | components/web_cache/common/web_cache_messages.h | 23 | ||||
-rw-r--r-- | components/web_cache/renderer/BUILD.gn | 17 | ||||
-rw-r--r-- | components/web_cache/renderer/DEPS | 4 | ||||
-rw-r--r-- | components/web_cache/renderer/web_cache_render_process_observer.cc | 84 | ||||
-rw-r--r-- | components/web_cache/renderer/web_cache_render_process_observer.h | 51 |
16 files changed, 1425 insertions, 0 deletions
diff --git a/components/web_cache/OWNERS b/components/web_cache/OWNERS new file mode 100644 index 0000000..de6caf7 --- /dev/null +++ b/components/web_cache/OWNERS @@ -0,0 +1,10 @@ +# Reviewers: +jochen@chromium.org +jhawkins@chromium.org +sky@chromium.org +thakis@chromium.org +thestig@chromium.org + +# Just owners: +darin@chromium.org +jam@chromium.org diff --git a/components/web_cache/browser/BUILD.gn b/components/web_cache/browser/BUILD.gn new file mode 100644 index 0000000..d001973 --- /dev/null +++ b/components/web_cache/browser/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright 2014 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. + +static_library("browser") { + output_name = "web_cache_browser" + sources = [ + "web_cache_manager.cc", + "web_cache_manager.h", + ] + + deps = [ + "//base", + "//components/web_cache/common", + "//content/public/browser", + "//third_party/WebKit/public:blink", + ] +} diff --git a/components/web_cache/browser/DEPS b/components/web_cache/browser/DEPS new file mode 100644 index 0000000..f6417ed --- /dev/null +++ b/components/web_cache/browser/DEPS @@ -0,0 +1,5 @@ +include_rules = [ + "+content/public/browser", + "+content/public", + '+third_party/WebKit/public/web', +] diff --git a/components/web_cache/browser/web_cache_manager.cc b/components/web_cache/browser/web_cache_manager.cc new file mode 100644 index 0000000..4acd209 --- /dev/null +++ b/components/web_cache/browser/web_cache_manager.cc @@ -0,0 +1,436 @@ +// 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 "components/web_cache/browser/web_cache_manager.h" + +#include <algorithm> + +#include "base/bind.h" +#include "base/compiler_specific.h" +#include "base/memory/singleton.h" +#include "base/message_loop/message_loop.h" +#include "base/metrics/histogram.h" +#include "base/prefs/pref_registry_simple.h" +#include "base/prefs/pref_service.h" +#include "base/sys_info.h" +#include "base/time/time.h" +#include "components/web_cache/common/web_cache_messages.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_types.h" +#include "content/public/browser/render_process_host.h" + +using base::Time; +using base::TimeDelta; +using blink::WebCache; + +namespace web_cache { + +static const int kReviseAllocationDelayMS = 200; + +// The default size limit of the in-memory cache is 8 MB +static const int kDefaultMemoryCacheSize = 8 * 1024 * 1024; + +namespace { + +int GetDefaultCacheSize() { + // Start off with a modest default + int default_cache_size = kDefaultMemoryCacheSize; + + // Check how much physical memory the OS has + int mem_size_mb = base::SysInfo::AmountOfPhysicalMemoryMB(); + if (mem_size_mb >= 1000) // If we have a GB of memory, set a larger default. + default_cache_size *= 4; + else if (mem_size_mb >= 512) // With 512 MB, set a slightly larger default. + default_cache_size *= 2; + + UMA_HISTOGRAM_MEMORY_MB("Cache.MaxCacheSizeMB", + default_cache_size / 1024 / 1024); + + return default_cache_size; +} + +} // anonymous namespace + +// static +WebCacheManager* WebCacheManager::GetInstance() { + return Singleton<WebCacheManager>::get(); +} + +WebCacheManager::WebCacheManager() + : global_size_limit_(GetDefaultGlobalSizeLimit()), + weak_factory_(this) { + registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED, + content::NotificationService::AllBrowserContextsAndSources()); + registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, + content::NotificationService::AllBrowserContextsAndSources()); +} + +WebCacheManager::~WebCacheManager() { +} + +void WebCacheManager::Add(int renderer_id) { + DCHECK(inactive_renderers_.count(renderer_id) == 0); + + // It is tempting to make the following DCHECK here, but it fails when a new + // tab is created as we observe activity from that tab because the + // RenderProcessHost is recreated and adds itself. + // + // DCHECK(active_renderers_.count(renderer_id) == 0); + // + // However, there doesn't seem to be much harm in receiving the calls in this + // order. + + active_renderers_.insert(renderer_id); + + RendererInfo* stats = &(stats_[renderer_id]); + memset(stats, 0, sizeof(*stats)); + stats->access = Time::Now(); + + // Revise our allocation strategy to account for this new renderer. + ReviseAllocationStrategyLater(); +} + +void WebCacheManager::Remove(int renderer_id) { + // Erase all knowledge of this renderer + active_renderers_.erase(renderer_id); + inactive_renderers_.erase(renderer_id); + stats_.erase(renderer_id); + + // Reallocate the resources used by this renderer + ReviseAllocationStrategyLater(); +} + +void WebCacheManager::ObserveActivity(int renderer_id) { + StatsMap::iterator item = stats_.find(renderer_id); + if (item == stats_.end()) + return; // We might see stats for a renderer that has been destroyed. + + // Record activity. + active_renderers_.insert(renderer_id); + item->second.access = Time::Now(); + + std::set<int>::iterator elmt = inactive_renderers_.find(renderer_id); + if (elmt != inactive_renderers_.end()) { + inactive_renderers_.erase(elmt); + + // A renderer that was inactive, just became active. We should make sure + // it is given a fair cache allocation, but we defer this for a bit in + // order to make this function call cheap. + ReviseAllocationStrategyLater(); + } +} + +void WebCacheManager::ObserveStats(int renderer_id, + const WebCache::UsageStats& stats) { + StatsMap::iterator entry = stats_.find(renderer_id); + if (entry == stats_.end()) + return; // We might see stats for a renderer that has been destroyed. + + // Record the updated stats. + entry->second.capacity = stats.capacity; + entry->second.deadSize = stats.deadSize; + entry->second.liveSize = stats.liveSize; + entry->second.maxDeadCapacity = stats.maxDeadCapacity; + entry->second.minDeadCapacity = stats.minDeadCapacity; +} + +void WebCacheManager::SetGlobalSizeLimit(size_t bytes) { + global_size_limit_ = bytes; + ReviseAllocationStrategyLater(); +} + +void WebCacheManager::ClearCache() { + // Tell each renderer process to clear the cache. + ClearRendererCache(active_renderers_, INSTANTLY); + ClearRendererCache(inactive_renderers_, INSTANTLY); +} + +void WebCacheManager::ClearCacheOnNavigation() { + // Tell each renderer process to clear the cache when a tab is reloaded or + // the user navigates to a new website. + ClearRendererCache(active_renderers_, ON_NAVIGATION); + ClearRendererCache(inactive_renderers_, ON_NAVIGATION); +} + +void WebCacheManager::Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + switch (type) { + case content::NOTIFICATION_RENDERER_PROCESS_CREATED: { + content::RenderProcessHost* process = + content::Source<content::RenderProcessHost>(source).ptr(); + Add(process->GetID()); + break; + } + case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: { + content::RenderProcessHost* process = + content::Source<content::RenderProcessHost>(source).ptr(); + Remove(process->GetID()); + break; + } + default: + NOTREACHED(); + break; + } +} + +// static +size_t WebCacheManager::GetDefaultGlobalSizeLimit() { + return GetDefaultCacheSize(); +} + +void WebCacheManager::GatherStats(const std::set<int>& renderers, + WebCache::UsageStats* stats) { + DCHECK(stats); + + memset(stats, 0, sizeof(WebCache::UsageStats)); + + std::set<int>::const_iterator iter = renderers.begin(); + while (iter != renderers.end()) { + StatsMap::iterator elmt = stats_.find(*iter); + if (elmt != stats_.end()) { + stats->minDeadCapacity += elmt->second.minDeadCapacity; + stats->maxDeadCapacity += elmt->second.maxDeadCapacity; + stats->capacity += elmt->second.capacity; + stats->liveSize += elmt->second.liveSize; + stats->deadSize += elmt->second.deadSize; + } + ++iter; + } +} + +// static +size_t WebCacheManager::GetSize(AllocationTactic tactic, + const WebCache::UsageStats& stats) { + switch (tactic) { + case DIVIDE_EVENLY: + // We aren't going to reserve any space for existing objects. + return 0; + case KEEP_CURRENT_WITH_HEADROOM: + // We need enough space for our current objects, plus some headroom. + return 3 * GetSize(KEEP_CURRENT, stats) / 2; + case KEEP_CURRENT: + // We need enough space to keep our current objects. + return stats.liveSize + stats.deadSize; + case KEEP_LIVE_WITH_HEADROOM: + // We need enough space to keep out live resources, plus some headroom. + return 3 * GetSize(KEEP_LIVE, stats) / 2; + case KEEP_LIVE: + // We need enough space to keep our live resources. + return stats.liveSize; + default: + NOTREACHED() << "Unknown cache allocation tactic"; + return 0; + } +} + +bool WebCacheManager::AttemptTactic( + AllocationTactic active_tactic, + const WebCache::UsageStats& active_stats, + AllocationTactic inactive_tactic, + const WebCache::UsageStats& inactive_stats, + AllocationStrategy* strategy) { + DCHECK(strategy); + + size_t active_size = GetSize(active_tactic, active_stats); + size_t inactive_size = GetSize(inactive_tactic, inactive_stats); + + // Give up if we don't have enough space to use this tactic. + if (global_size_limit_ < active_size + inactive_size) + return false; + + // Compute the unreserved space available. + size_t total_extra = global_size_limit_ - (active_size + inactive_size); + + // The plan for the extra space is to divide it evenly amoung the active + // renderers. + size_t shares = active_renderers_.size(); + + // The inactive renderers get one share of the extra memory to be divided + // among themselves. + size_t inactive_extra = 0; + if (!inactive_renderers_.empty()) { + ++shares; + inactive_extra = total_extra / shares; + } + + // The remaining memory is allocated to the active renderers. + size_t active_extra = total_extra - inactive_extra; + + // Actually compute the allocations for each renderer. + AddToStrategy(active_renderers_, active_tactic, active_extra, strategy); + AddToStrategy(inactive_renderers_, inactive_tactic, inactive_extra, strategy); + + // We succeeded in computing an allocation strategy. + return true; +} + +void WebCacheManager::AddToStrategy(const std::set<int>& renderers, + AllocationTactic tactic, + size_t extra_bytes_to_allocate, + AllocationStrategy* strategy) { + DCHECK(strategy); + + // Nothing to do if there are no renderers. It is common for there to be no + // inactive renderers if there is a single active tab. + if (renderers.empty()) + return; + + // Divide the extra memory evenly among the renderers. + size_t extra_each = extra_bytes_to_allocate / renderers.size(); + + std::set<int>::const_iterator iter = renderers.begin(); + while (iter != renderers.end()) { + size_t cache_size = extra_each; + + // Add in the space required to implement |tactic|. + StatsMap::iterator elmt = stats_.find(*iter); + if (elmt != stats_.end()) + cache_size += GetSize(tactic, elmt->second); + + // Record the allocation in our strategy. + strategy->push_back(Allocation(*iter, cache_size)); + ++iter; + } +} + +void WebCacheManager::EnactStrategy(const AllocationStrategy& strategy) { + // Inform each render process of its cache allocation. + AllocationStrategy::const_iterator allocation = strategy.begin(); + while (allocation != strategy.end()) { + content::RenderProcessHost* host = + content::RenderProcessHost::FromID(allocation->first); + if (host) { + // This is the capacity this renderer has been allocated. + size_t capacity = allocation->second; + + // We don't reserve any space for dead objects in the cache. Instead, we + // prefer to keep live objects around. There is probably some performance + // tuning to be done here. + size_t min_dead_capacity = 0; + + // We allow the dead objects to consume up to half of the cache capacity. + size_t max_dead_capacity = capacity / 2; + if (base::SysInfo::IsLowEndDevice()) { + max_dead_capacity = std::min(static_cast<size_t>(512 * 1024), + max_dead_capacity); + } + host->Send(new WebCacheMsg_SetCacheCapacities(min_dead_capacity, + max_dead_capacity, + capacity)); + } + ++allocation; + } +} + +void WebCacheManager::ClearRendererCache( + const std::set<int>& renderers, + WebCacheManager::ClearCacheOccasion occasion) { + std::set<int>::const_iterator iter = renderers.begin(); + for (; iter != renderers.end(); ++iter) { + content::RenderProcessHost* host = + content::RenderProcessHost::FromID(*iter); + if (host) + host->Send(new WebCacheMsg_ClearCache(occasion == ON_NAVIGATION)); + } +} + +void WebCacheManager::ReviseAllocationStrategy() { + DCHECK(stats_.size() <= + active_renderers_.size() + inactive_renderers_.size()); + + // Check if renderers have gone inactive. + FindInactiveRenderers(); + + // Gather statistics + WebCache::UsageStats active; + WebCache::UsageStats inactive; + GatherStats(active_renderers_, &active); + GatherStats(inactive_renderers_, &inactive); + + UMA_HISTOGRAM_COUNTS_100("Cache.ActiveTabs", active_renderers_.size()); + UMA_HISTOGRAM_COUNTS_100("Cache.InactiveTabs", inactive_renderers_.size()); + UMA_HISTOGRAM_MEMORY_MB("Cache.ActiveCapacityMB", + active.capacity / 1024 / 1024); + UMA_HISTOGRAM_MEMORY_MB("Cache.ActiveDeadSizeMB", + active.deadSize / 1024 / 1024); + UMA_HISTOGRAM_MEMORY_MB("Cache.ActiveLiveSizeMB", + active.liveSize / 1024 / 1024); + UMA_HISTOGRAM_MEMORY_MB("Cache.InactiveCapacityMB", + inactive.capacity / 1024 / 1024); + UMA_HISTOGRAM_MEMORY_MB("Cache.InactiveDeadSizeMB", + inactive.deadSize / 1024 / 1024); + UMA_HISTOGRAM_MEMORY_MB("Cache.InactiveLiveSizeMB", + inactive.liveSize / 1024 / 1024); + + // Compute an allocation strategy. + // + // We attempt various tactics in order of preference. Our first preference + // is not to evict any objects. If we don't have enough resources, we'll + // first try to evict dead data only. If that fails, we'll just divide the + // resources we have evenly. + // + // We always try to give the active renderers some head room in their + // allocations so they can take memory away from an inactive renderer with + // a large cache allocation. + // + // Notice the early exit will prevent attempting less desirable tactics once + // we've found a workable strategy. + AllocationStrategy strategy; + if ( // Ideally, we'd like to give the active renderers some headroom and + // keep all our current objects. + AttemptTactic(KEEP_CURRENT_WITH_HEADROOM, active, + KEEP_CURRENT, inactive, &strategy) || + // If we can't have that, then we first try to evict the dead objects in + // the caches of inactive renderers. + AttemptTactic(KEEP_CURRENT_WITH_HEADROOM, active, + KEEP_LIVE, inactive, &strategy) || + // Next, we try to keep the live objects in the active renders (with some + // room for new objects) and give whatever is left to the inactive + // renderers. + AttemptTactic(KEEP_LIVE_WITH_HEADROOM, active, + DIVIDE_EVENLY, inactive, &strategy) || + // If we've gotten this far, then we are very tight on memory. Let's try + // to at least keep around the live objects for the active renderers. + AttemptTactic(KEEP_LIVE, active, DIVIDE_EVENLY, inactive, &strategy) || + // We're basically out of memory. The best we can do is just divide up + // what we have and soldier on. + AttemptTactic(DIVIDE_EVENLY, active, DIVIDE_EVENLY, inactive, + &strategy)) { + // Having found a workable strategy, we enact it. + EnactStrategy(strategy); + } else { + // DIVIDE_EVENLY / DIVIDE_EVENLY should always succeed. + NOTREACHED() << "Unable to find a cache allocation"; + } +} + +void WebCacheManager::ReviseAllocationStrategyLater() { + // Ask to be called back in a few milliseconds to actually recompute our + // allocation. + base::MessageLoop::current()->PostDelayedTask(FROM_HERE, + base::Bind( + &WebCacheManager::ReviseAllocationStrategy, + weak_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds(kReviseAllocationDelayMS)); +} + +void WebCacheManager::FindInactiveRenderers() { + std::set<int>::const_iterator iter = active_renderers_.begin(); + while (iter != active_renderers_.end()) { + StatsMap::iterator elmt = stats_.find(*iter); + DCHECK(elmt != stats_.end()); + TimeDelta idle = Time::Now() - elmt->second.access; + if (idle >= TimeDelta::FromMinutes(kRendererInactiveThresholdMinutes)) { + // Moved to inactive status. This invalidates our iterator. + inactive_renderers_.insert(*iter); + active_renderers_.erase(*iter); + iter = active_renderers_.begin(); + continue; + } + ++iter; + } +} + +} // namespace web_cache diff --git a/components/web_cache/browser/web_cache_manager.h b/components/web_cache/browser/web_cache_manager.h new file mode 100644 index 0000000..0945ec5 --- /dev/null +++ b/components/web_cache/browser/web_cache_manager.h @@ -0,0 +1,241 @@ +// Copyright (c) 2011 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. + +// This is the browser side of the cache manager, it tracks the activity of the +// render processes and allocates available memory cache resources. + +#ifndef COMPONENTS_WEB_CACHE_BROWSER_WEB_CACHE_MANAGER_H_ +#define COMPONENTS_WEB_CACHE_BROWSER_WEB_CACHE_MANAGER_H_ + +#include <list> +#include <map> +#include <set> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" +#include "base/memory/weak_ptr.h" +#include "base/time/time.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "third_party/WebKit/public/web/WebCache.h" + +template<typename Type> +struct DefaultSingletonTraits; +class PrefRegistrySimple; + +namespace web_cache { + +class WebCacheManager : public content::NotificationObserver { + friend class WebCacheManagerTest; + FRIEND_TEST_ALL_PREFIXES( + WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_1); + FRIEND_TEST_ALL_PREFIXES( + WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_2); + FRIEND_TEST_ALL_PREFIXES( + WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_3); + FRIEND_TEST_ALL_PREFIXES( + WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_4); + FRIEND_TEST_ALL_PREFIXES( + WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_5); + FRIEND_TEST_ALL_PREFIXES( + WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_6); + + public: + // Gets the singleton WebCacheManager object. The first time this method + // is called, a WebCacheManager object is constructed and returned. + // Subsequent calls will return the same object. + static WebCacheManager* GetInstance(); + + // When a render process is created, it registers itself with the cache + // manager host, causing the renderer to be allocated cache resources. + void Add(int renderer_id); + + // When a render process ends, it removes itself from the cache manager host, + // freeing the manager to assign its cache resources to other renderers. + void Remove(int renderer_id); + + // The cache manager assigns more cache resources to active renderer. When a + // renderer is active, it should inform the cache manager to receive more + // cache resources. + // + // When a renderer moves from being inactive to being active, the cache + // manager may decide to adjust its resource allocation, but it will delay + // the recalculation, allowing ObserveActivity to return quickly. + void ObserveActivity(int renderer_id); + + // Periodically, renderers should inform the cache manager of their current + // statistics. The more up-to-date the cache manager's statistics, the + // better it can allocate cache resources. + void ObserveStats( + int renderer_id, const blink::WebCache::UsageStats& stats); + + // The global limit on the number of bytes in all the in-memory caches. + size_t global_size_limit() const { return global_size_limit_; } + + // Sets the global size limit, forcing a recalculation of cache allocations. + void SetGlobalSizeLimit(size_t bytes); + + // Clears all in-memory caches. + void ClearCache(); + + // Clears all in-memory caches when a tab is reloaded or the user navigates + // to a different website. + void ClearCacheOnNavigation(); + + // content::NotificationObserver implementation: + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + // Gets the default global size limit. This interrogates system metrics to + // tune the default size to the current system. + static size_t GetDefaultGlobalSizeLimit(); + + protected: + // The amount of idle time before we consider a tab to be "inactive" + static const int kRendererInactiveThresholdMinutes = 5; + + // Keep track of some renderer information. + struct RendererInfo : blink::WebCache::UsageStats { + // The access time for this renderer. + base::Time access; + }; + + typedef std::map<int, RendererInfo> StatsMap; + + // An allocation is the number of bytes a specific renderer should use for + // its cache. + typedef std::pair<int,size_t> Allocation; + + // An allocation strategy is a list of allocations specifying the resources + // each renderer is permitted to consume for its cache. + typedef std::list<Allocation> AllocationStrategy; + + // This class is a singleton. Do not instantiate directly. + WebCacheManager(); + friend struct DefaultSingletonTraits<WebCacheManager>; + + virtual ~WebCacheManager(); + + // Recomputes the allocation of cache resources among the renderers. Also + // informs the renderers of their new allocation. + void ReviseAllocationStrategy(); + + // Schedules a call to ReviseAllocationStrategy after a short delay. + void ReviseAllocationStrategyLater(); + + // The various tactics used as part of an allocation strategy. To decide + // how many resources a given renderer should be allocated, we consider its + // usage statistics. Each tactic specifies the function that maps usage + // statistics to resource allocations. + // + // Determining a resource allocation strategy amounts to picking a tactic + // for each renderer and checking that the total memory required fits within + // our |global_size_limit_|. + enum AllocationTactic { + // Ignore cache statistics and divide resources equally among the given + // set of caches. + DIVIDE_EVENLY, + + // Allow each renderer to keep its current set of cached resources, with + // some extra allocation to store new objects. + KEEP_CURRENT_WITH_HEADROOM, + + // Allow each renderer to keep its current set of cached resources. + KEEP_CURRENT, + + // Allow each renderer to keep cache resources it believes are currently + // being used, with some extra allocation to store new objects. + KEEP_LIVE_WITH_HEADROOM, + + // Allow each renderer to keep cache resources it believes are currently + // being used, but instruct the renderer to discard all other data. + KEEP_LIVE, + }; + + // Helper functions for devising an allocation strategy + + // Add up all the stats from the given set of renderers and place the result + // in |stats|. + void GatherStats(const std::set<int>& renderers, + blink::WebCache::UsageStats* stats); + + // Get the amount of memory that would be required to implement |tactic| + // using the specified allocation tactic. This function defines the + // semantics for each of the tactics. + static size_t GetSize(AllocationTactic tactic, + const blink::WebCache::UsageStats& stats); + + // Attempt to use the specified tactics to compute an allocation strategy + // and place the result in |strategy|. |active_stats| and |inactive_stats| + // are the aggregate statistics for |active_renderers_| and + // |inactive_renderers_|, respectively. + // + // Returns |true| on success and |false| on failure. Does not modify + // |strategy| on failure. + bool AttemptTactic(AllocationTactic active_tactic, + const blink::WebCache::UsageStats& active_stats, + AllocationTactic inactive_tactic, + const blink::WebCache::UsageStats& inactive_stats, + AllocationStrategy* strategy); + + // For each renderer in |renderers|, computes its allocation according to + // |tactic| and add the result to |strategy|. Any |extra_bytes_to_allocate| + // is divided evenly among the renderers. + void AddToStrategy(const std::set<int>& renderers, + AllocationTactic tactic, + size_t extra_bytes_to_allocate, + AllocationStrategy* strategy); + + // Enact an allocation strategy by informing the renderers of their + // allocations according to |strategy|. + void EnactStrategy(const AllocationStrategy& strategy); + + enum ClearCacheOccasion { + // Instructs to clear the cache instantly. + INSTANTLY, + // Instructs to clear the cache when a navigation takes place (this + // includes reloading a tab). + ON_NAVIGATION + }; + + // Inform all |renderers| to clear their cache. + void ClearRendererCache(const std::set<int>& renderers, + ClearCacheOccasion occation); + + // Check to see if any active renderers have fallen inactive. + void FindInactiveRenderers(); + + // The global size limit for all in-memory caches. + size_t global_size_limit_; + + // Maps every renderer_id our most recent copy of its statistics. + StatsMap stats_; + + // Every renderer we think is still around is in one of these two sets. + // + // Active renderers are those renderers that have been active more recently + // than they have been inactive. + std::set<int> active_renderers_; + // Inactive renderers are those renderers that have been inactive more + // recently than they have been active. + std::set<int> inactive_renderers_; + + base::WeakPtrFactory<WebCacheManager> weak_factory_; + + content::NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(WebCacheManager); +}; + +} // namespace web_cache + +#endif // COMPONENTS_WEB_CACHE_BROWSER_WEB_CACHE_MANAGER_H_ diff --git a/components/web_cache/browser/web_cache_manager_unittest.cc b/components/web_cache/browser/web_cache_manager_unittest.cc new file mode 100644 index 0000000..bc8bcbd --- /dev/null +++ b/components/web_cache/browser/web_cache_manager_unittest.cc @@ -0,0 +1,466 @@ +// Copyright (c) 2011 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 <string> + +#include "base/message_loop/message_loop.h" +#include "components/web_cache/browser/web_cache_manager.h" +#include "content/public/test/test_browser_thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::Time; +using base::TimeDelta; +using content::BrowserThread; +using blink::WebCache; + +namespace web_cache { + +class WebCacheManagerTest : public testing::Test { + protected: + typedef WebCacheManager::StatsMap StatsMap; + typedef WebCacheManager::Allocation Allocation; + typedef WebCacheManager::AllocationStrategy AllocationStrategy; + + static const int kRendererID; + static const int kRendererID2; + static const WebCache::UsageStats kStats; + static const WebCache::UsageStats kStats2; + + WebCacheManagerTest() + : ui_thread_(BrowserThread::UI, &message_loop_) { + } + + // Thunks to access protected members of WebCacheManager + static std::map<int, WebCacheManager::RendererInfo>& stats( + WebCacheManager* h) { + return h->stats_; + } + + static void SimulateInactivity(WebCacheManager* h, int renderer_id) { + stats(h)[renderer_id].access = Time::Now() - TimeDelta::FromMinutes( + WebCacheManager::kRendererInactiveThresholdMinutes); + h->FindInactiveRenderers(); + } + + static std::set<int>& active_renderers(WebCacheManager* h) { + return h->active_renderers_; + } + static std::set<int>& inactive_renderers(WebCacheManager* h) { + return h->inactive_renderers_; + } + static void GatherStats(WebCacheManager* h, + std::set<int> renderers, + WebCache::UsageStats* stats) { + h->GatherStats(renderers, stats); + } + static size_t GetSize(int tactic, + const WebCache::UsageStats& stats) { + return WebCacheManager::GetSize( + static_cast<WebCacheManager::AllocationTactic>(tactic), stats); + } + static bool AttemptTactic(WebCacheManager* h, + int active_tactic, + const WebCache::UsageStats& active_stats, + int inactive_tactic, + const WebCache::UsageStats& inactive_stats, + std::list< std::pair<int,size_t> >* strategy) { + return h->AttemptTactic( + static_cast<WebCacheManager::AllocationTactic>(active_tactic), + active_stats, + static_cast<WebCacheManager::AllocationTactic>(inactive_tactic), + inactive_stats, + strategy); + } + static void AddToStrategy(WebCacheManager* h, + std::set<int> renderers, + int tactic, + size_t extra_bytes_to_allocate, + std::list< std::pair<int,size_t> >* strategy) { + h->AddToStrategy(renderers, + static_cast<WebCacheManager::AllocationTactic>(tactic), + extra_bytes_to_allocate, + strategy); + } + + enum { + DIVIDE_EVENLY = WebCacheManager::DIVIDE_EVENLY, + KEEP_CURRENT_WITH_HEADROOM = WebCacheManager::KEEP_CURRENT_WITH_HEADROOM, + KEEP_CURRENT = WebCacheManager::KEEP_CURRENT, + KEEP_LIVE_WITH_HEADROOM = WebCacheManager::KEEP_LIVE_WITH_HEADROOM, + KEEP_LIVE = WebCacheManager::KEEP_LIVE, + }; + + WebCacheManager* manager() { return &manager_; } + + private: + WebCacheManager manager_; + base::MessageLoop message_loop_; + content::TestBrowserThread ui_thread_; +}; + +// static +const int WebCacheManagerTest::kRendererID = 146; + +// static +const int WebCacheManagerTest::kRendererID2 = 245; + +// static +const WebCache::UsageStats WebCacheManagerTest::kStats = { + 0, + 1024 * 1024, + 1024 * 1024, + 256 * 1024, + 512, + }; + +// static +const WebCache::UsageStats WebCacheManagerTest::kStats2 = { + 0, + 2 * 1024 * 1024, + 2 * 1024 * 1024, + 2 * 256 * 1024, + 2 * 512, + }; + +static bool operator==(const WebCache::UsageStats& lhs, + const WebCache::UsageStats& rhs) { + return !::memcmp(&lhs, &rhs, sizeof(WebCache::UsageStats)); +} + +TEST_F(WebCacheManagerTest, AddRemoveRendererTest) { + EXPECT_EQ(0U, active_renderers(manager()).size()); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + manager()->Add(kRendererID); + EXPECT_EQ(1U, active_renderers(manager()).count(kRendererID)); + EXPECT_EQ(0U, inactive_renderers(manager()).count(kRendererID)); + + manager()->Remove(kRendererID); + EXPECT_EQ(0U, active_renderers(manager()).size()); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); +} + +TEST_F(WebCacheManagerTest, ActiveInactiveTest) { + manager()->Add(kRendererID); + + manager()->ObserveActivity(kRendererID); + EXPECT_EQ(1U, active_renderers(manager()).count(kRendererID)); + EXPECT_EQ(0U, inactive_renderers(manager()).count(kRendererID)); + + SimulateInactivity(manager(), kRendererID); + EXPECT_EQ(0U, active_renderers(manager()).count(kRendererID)); + EXPECT_EQ(1U, inactive_renderers(manager()).count(kRendererID)); + + manager()->ObserveActivity(kRendererID); + EXPECT_EQ(1U, active_renderers(manager()).count(kRendererID)); + EXPECT_EQ(0U, inactive_renderers(manager()).count(kRendererID)); + + manager()->Remove(kRendererID); +} + +TEST_F(WebCacheManagerTest, ObserveStatsTest) { + manager()->Add(kRendererID); + + EXPECT_EQ(1U, stats(manager()).size()); + + manager()->ObserveStats(kRendererID, kStats); + + EXPECT_EQ(1U, stats(manager()).size()); + EXPECT_TRUE(kStats == stats(manager())[kRendererID]); + + manager()->Remove(kRendererID); +} + +TEST_F(WebCacheManagerTest, SetGlobalSizeLimitTest) { + size_t limit = manager()->GetDefaultGlobalSizeLimit(); + manager()->SetGlobalSizeLimit(limit); + EXPECT_EQ(limit, manager()->global_size_limit()); + + manager()->SetGlobalSizeLimit(0); + EXPECT_EQ(0U, manager()->global_size_limit()); +} + +TEST_F(WebCacheManagerTest, GatherStatsTest) { + manager()->Add(kRendererID); + manager()->Add(kRendererID2); + + manager()->ObserveStats(kRendererID, kStats); + manager()->ObserveStats(kRendererID2, kStats2); + + std::set<int> renderer_set; + renderer_set.insert(kRendererID); + + WebCache::UsageStats stats; + GatherStats(manager(), renderer_set, &stats); + + EXPECT_TRUE(kStats == stats); + + renderer_set.insert(kRendererID2); + GatherStats(manager(), renderer_set, &stats); + + WebCache::UsageStats expected_stats = kStats; + expected_stats.minDeadCapacity += kStats2.minDeadCapacity; + expected_stats.maxDeadCapacity += kStats2.maxDeadCapacity; + expected_stats.capacity += kStats2.capacity; + expected_stats.liveSize += kStats2.liveSize; + expected_stats.deadSize += kStats2.deadSize; + + EXPECT_TRUE(expected_stats == stats); + + manager()->Remove(kRendererID); + manager()->Remove(kRendererID2); +} + +TEST_F(WebCacheManagerTest, GetSizeTest) { + EXPECT_EQ(0U, GetSize(DIVIDE_EVENLY, kStats)); + EXPECT_LT(256 * 1024u + 512, GetSize(KEEP_CURRENT_WITH_HEADROOM, kStats)); + EXPECT_EQ(256 * 1024u + 512, GetSize(KEEP_CURRENT, kStats)); + EXPECT_LT(256 * 1024u, GetSize(KEEP_LIVE_WITH_HEADROOM, kStats)); + EXPECT_EQ(256 * 1024u, GetSize(KEEP_LIVE, kStats)); +} + +TEST_F(WebCacheManagerTest, AttemptTacticTest) { + manager()->Add(kRendererID); + manager()->Add(kRendererID2); + + manager()->ObserveActivity(kRendererID); + SimulateInactivity(manager(), kRendererID2); + + manager()->ObserveStats(kRendererID, kStats); + manager()->ObserveStats(kRendererID2, kStats2); + + manager()->SetGlobalSizeLimit(kStats.liveSize + kStats.deadSize + + kStats2.liveSize + kStats2.deadSize/2); + + AllocationStrategy strategy; + + EXPECT_FALSE(AttemptTactic(manager(), + KEEP_CURRENT, + kStats, + KEEP_CURRENT, + kStats2, + &strategy)); + EXPECT_TRUE(strategy.empty()); + + EXPECT_TRUE(AttemptTactic(manager(), + KEEP_CURRENT, + kStats, + KEEP_LIVE, + kStats2, + &strategy)); + EXPECT_EQ(2U, strategy.size()); + + AllocationStrategy::iterator iter = strategy.begin(); + while (iter != strategy.end()) { + if (iter->first == kRendererID) + EXPECT_LE(kStats.liveSize + kStats.deadSize, iter->second); + else if (iter->first == kRendererID2) + EXPECT_LE(kStats2.liveSize, iter->second); + else + ADD_FAILURE(); // Unexpected entry in strategy. + ++iter; + } + + manager()->Remove(kRendererID); + manager()->Remove(kRendererID2); +} + +TEST_F(WebCacheManagerTest, AddToStrategyTest) { + manager()->Add(kRendererID); + manager()->Add(kRendererID2); + + std::set<int> renderer_set; + renderer_set.insert(kRendererID); + renderer_set.insert(kRendererID2); + + manager()->ObserveStats(kRendererID, kStats); + manager()->ObserveStats(kRendererID2, kStats2); + + const size_t kExtraBytesToAllocate = 10 * 1024; + + AllocationStrategy strategy; + AddToStrategy(manager(), + renderer_set, + KEEP_CURRENT, + kExtraBytesToAllocate, + &strategy); + + EXPECT_EQ(2U, strategy.size()); + + size_t total_bytes = 0; + AllocationStrategy::iterator iter = strategy.begin(); + while (iter != strategy.end()) { + total_bytes += iter->second; + + if (iter->first == kRendererID) + EXPECT_LE(kStats.liveSize + kStats.deadSize, iter->second); + else if (iter->first == kRendererID2) + EXPECT_LE(kStats2.liveSize + kStats2.deadSize, iter->second); + else + ADD_FAILURE(); // Unexpected entry in strategy. + ++iter; + } + + size_t expected_total_bytes = kExtraBytesToAllocate + + kStats.liveSize + kStats.deadSize + + kStats2.liveSize + kStats2.deadSize; + + EXPECT_GE(expected_total_bytes, total_bytes); + + manager()->Remove(kRendererID); + manager()->Remove(kRendererID2); +} + +// Regression test for http://crbug.com/12362. +// There are three operations in the following order will cause the crash: +// Remove(kRendererID) -> ObserveActivity(kRendererID) -> Remove(kRendererID2) +// To prevent similar failures in the future, 6 tests are added in total to +// cover all the possible orderings of these three operations. +TEST_F(WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_1) { + EXPECT_EQ(0U, active_renderers(manager()).size()); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + // Add, Remove, and ObserveActivity trigger deferred + // calls to ReviseAllocationStrategy and that we call it directly after each + // operation to sidestep the need to wait for an unobservable background + // operation. + manager()->Add(kRendererID); + manager()->ReviseAllocationStrategy(); + manager()->Add(kRendererID2); + manager()->ReviseAllocationStrategy(); + + // The following order will cause a crash in http://crbug.com/12362. + manager()->Remove(kRendererID); + manager()->ReviseAllocationStrategy(); + + manager()->ObserveActivity(kRendererID); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID2); + manager()->ReviseAllocationStrategy(); +} + +TEST_F(WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_2) { + EXPECT_EQ(0U, active_renderers(manager()).size()); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + // Add, Remove, and ObserveActivity trigger deferred + // calls to ReviseAllocationStrategy and that we call it directly after each + // operation to sidestep the need to wait for an unobservable background + // operation. + manager()->Add(kRendererID); + manager()->ReviseAllocationStrategy(); + manager()->Add(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->ObserveActivity(kRendererID); + manager()->ReviseAllocationStrategy(); + +} + +TEST_F(WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_3) { + EXPECT_EQ(0U, active_renderers(manager()).size()); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + // Add, Remove, and ObserveActivity trigger deferred + // calls to ReviseAllocationStrategy and that we call it directly after each + // operation to sidestep the need to wait for an unobservable background + // operation. + manager()->Add(kRendererID); + manager()->ReviseAllocationStrategy(); + manager()->Add(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->ObserveActivity(kRendererID); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + manager()->Remove(kRendererID); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID2); + manager()->ReviseAllocationStrategy(); +} + +TEST_F(WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_4) { + EXPECT_EQ(0U, active_renderers(manager()).size()); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + // Add, Remove, and ObserveActivity trigger deferred + // calls to ReviseAllocationStrategy and that we call it directly after each + // operation to sidestep the need to wait for an unobservable background + // operation. + manager()->Add(kRendererID); + manager()->ReviseAllocationStrategy(); + manager()->Add(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->ObserveActivity(kRendererID); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + manager()->Remove(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID); + manager()->ReviseAllocationStrategy(); +} + +TEST_F(WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_5) { + EXPECT_EQ(0U, active_renderers(manager()).size()); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + // Add, Remove, and ObserveActivity trigger deferred + // calls to ReviseAllocationStrategy and that we call it directly after each + // operation to sidestep the need to wait for an unobservable background + // operation. + manager()->Add(kRendererID); + manager()->ReviseAllocationStrategy(); + manager()->Add(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->ObserveActivity(kRendererID); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID); + manager()->ReviseAllocationStrategy(); +} + +TEST_F(WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_6) { + EXPECT_EQ(0U, active_renderers(manager()).size()); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + // Add, Remove, and ObserveActivity trigger deferred + // calls to ReviseAllocationStrategy and that we call it directly after each + // operation to sidestep the need to wait for an unobservable background + // operation. + manager()->Add(kRendererID); + manager()->ReviseAllocationStrategy(); + manager()->Add(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID); + manager()->ReviseAllocationStrategy(); + + manager()->ObserveActivity(kRendererID); + manager()->ReviseAllocationStrategy(); +} + +} // namespace web_cache diff --git a/components/web_cache/common/BUILD.gn b/components/web_cache/common/BUILD.gn new file mode 100644 index 0000000..5c7b2ef --- /dev/null +++ b/components/web_cache/common/BUILD.gn @@ -0,0 +1,16 @@ +# Copyright 2014 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. + +static_library("common") { + output_name = "web_cache_common" + sources = [ + "web_cache_message_generator.cc", + "web_cache_message_generator.h", + "web_cache_messages.h", + ] + + deps = [ + "//ipc", + ] +} diff --git a/components/web_cache/common/DEPS b/components/web_cache/common/DEPS new file mode 100644 index 0000000..1c40d98 --- /dev/null +++ b/components/web_cache/common/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+ipc", +] diff --git a/components/web_cache/common/OWNERS b/components/web_cache/common/OWNERS new file mode 100644 index 0000000..5ecb406 --- /dev/null +++ b/components/web_cache/common/OWNERS @@ -0,0 +1,11 @@ +# Changes to IPC messages require a security review to avoid introducing +# new sandbox escapes. +per-file *_messages*.h=set noparent +per-file *_messages*.h=dcheng@chromium.org +per-file *_messages*.h=inferno@chromium.org +per-file *_messages*.h=jln@chromium.org +per-file *_messages*.h=jschuh@chromium.org +per-file *_messages*.h=kenrb@chromium.org +per-file *_messages*.h=nasko@chromium.org +per-file *_messages*.h=tsepez@chromium.org +per-file *_messages*.h=wfh@chromium.org diff --git a/components/web_cache/common/web_cache_message_generator.cc b/components/web_cache/common/web_cache_message_generator.cc new file mode 100644 index 0000000..b4b2d26 --- /dev/null +++ b/components/web_cache/common/web_cache_message_generator.cc @@ -0,0 +1,33 @@ +// Copyright 2014 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. + +// Get basic type definitions. +#define IPC_MESSAGE_IMPL +#include "components/web_cache/common/web_cache_message_generator.h" + +// Generate constructors. +#include "ipc/struct_constructor_macros.h" +#include "components/web_cache/common/web_cache_message_generator.h" + +// Generate destructors. +#include "ipc/struct_destructor_macros.h" +#include "components/web_cache/common/web_cache_message_generator.h" + +// Generate param traits write methods. +#include "ipc/param_traits_write_macros.h" +namespace IPC { +#include "components/web_cache/common/web_cache_message_generator.h" +} // namespace IPC + +// Generate param traits read methods. +#include "ipc/param_traits_read_macros.h" +namespace IPC { +#include "components/web_cache/common/web_cache_message_generator.h" +} // namespace IPC + +// Generate param traits log methods. +#include "ipc/param_traits_log_macros.h" +namespace IPC { +#include "components/web_cache/common/web_cache_message_generator.h" +} // namespace IPC diff --git a/components/web_cache/common/web_cache_message_generator.h b/components/web_cache/common/web_cache_message_generator.h new file mode 100644 index 0000000..d88eb86 --- /dev/null +++ b/components/web_cache/common/web_cache_message_generator.h @@ -0,0 +1,7 @@ +// Copyright 2014 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. + +// Multiply-included file, no traditional include guard. + +#include "components/web_cache/common/web_cache_messages.h" diff --git a/components/web_cache/common/web_cache_messages.h b/components/web_cache/common/web_cache_messages.h new file mode 100644 index 0000000..a521399 --- /dev/null +++ b/components/web_cache/common/web_cache_messages.h @@ -0,0 +1,23 @@ +// Copyright (c) 2014 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. + +// Multiply-included file, no traditional include guard. +#include "ipc/ipc_message_macros.h" +#include "ipc/ipc_message_utils.h" + +#define IPC_MESSAGE_START WebCacheMsgStart + +//----------------------------------------------------------------------------- +// RenderView messages +// These are messages sent from the browser to the renderer process. + +// Tells the renderer to set its maximum cache size to the supplied value. +IPC_MESSAGE_CONTROL3(WebCacheMsg_SetCacheCapacities, + size_t /* min_dead_capacity */, + size_t /* max_dead_capacity */, + size_t /* capacity */) + +// Tells the renderer to clear the cache. +IPC_MESSAGE_CONTROL1(WebCacheMsg_ClearCache, + bool /* on_navigation */) diff --git a/components/web_cache/renderer/BUILD.gn b/components/web_cache/renderer/BUILD.gn new file mode 100644 index 0000000..cdd703a --- /dev/null +++ b/components/web_cache/renderer/BUILD.gn @@ -0,0 +1,17 @@ +# Copyright 2014 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. + +static_library("renderer") { + output_name = "web_cache_renderer" + sources = [ + "web_cache_render_process_observer.cc", + "web_cache_render_process_observer.h", + ] + + deps = [ + "//components/web_cache/common", + "//content/public/renderer", + "//third_party/WebKit/public:blink", + ] +} diff --git a/components/web_cache/renderer/DEPS b/components/web_cache/renderer/DEPS new file mode 100644 index 0000000..100b3d3 --- /dev/null +++ b/components/web_cache/renderer/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+content/public/renderer", + "+third_party/WebKit/public/web", +] diff --git a/components/web_cache/renderer/web_cache_render_process_observer.cc b/components/web_cache/renderer/web_cache_render_process_observer.cc new file mode 100644 index 0000000..a273817 --- /dev/null +++ b/components/web_cache/renderer/web_cache_render_process_observer.cc @@ -0,0 +1,84 @@ +// Copyright 2014 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 "components/web_cache/renderer/web_cache_render_process_observer.h" + +#include <limits> + +#include "components/web_cache/common/web_cache_messages.h" +#include "third_party/WebKit/public/web/WebCache.h" + +using blink::WebCache; + +namespace web_cache { + +namespace { +const size_t kUnitializedCacheCapacity = UINT_MAX; +} + +WebCacheRenderProcessObserver::WebCacheRenderProcessObserver() + : clear_cache_pending_(false), + webkit_initialized_(false), + pending_cache_min_dead_capacity_(0), + pending_cache_max_dead_capacity_(0), + pending_cache_capacity_(kUnitializedCacheCapacity) { +} + +WebCacheRenderProcessObserver::~WebCacheRenderProcessObserver() { +} + +void WebCacheRenderProcessObserver::ExecutePendingClearCache() { + if (clear_cache_pending_ && webkit_initialized_) { + clear_cache_pending_ = false; + WebCache::clear(); + } +} + +bool WebCacheRenderProcessObserver::OnControlMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(WebCacheRenderProcessObserver, message) + IPC_MESSAGE_HANDLER(WebCacheMsg_SetCacheCapacities, OnSetCacheCapacities) + IPC_MESSAGE_HANDLER(WebCacheMsg_ClearCache, OnClearCache) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void WebCacheRenderProcessObserver::WebKitInitialized() { + webkit_initialized_ = true; + if (pending_cache_capacity_ != kUnitializedCacheCapacity) { + WebCache::setCapacities(pending_cache_min_dead_capacity_, + pending_cache_max_dead_capacity_, + pending_cache_capacity_); + } +} + +void WebCacheRenderProcessObserver::OnRenderProcessShutdown() { + webkit_initialized_ = false; +} + +void WebCacheRenderProcessObserver::OnSetCacheCapacities( + size_t min_dead_capacity, + size_t max_dead_capacity, + size_t capacity) { + if (!webkit_initialized_) { + pending_cache_min_dead_capacity_ = min_dead_capacity; + pending_cache_max_dead_capacity_ = max_dead_capacity; + pending_cache_capacity_ = capacity; + return; + } + + WebCache::setCapacities( + min_dead_capacity, max_dead_capacity, capacity); +} + +void WebCacheRenderProcessObserver::OnClearCache(bool on_navigation) { + if (on_navigation || !webkit_initialized_) + clear_cache_pending_ = true; + else + WebCache::clear(); +} + +} // namespace web_cache diff --git a/components/web_cache/renderer/web_cache_render_process_observer.h b/components/web_cache/renderer/web_cache_render_process_observer.h new file mode 100644 index 0000000..6f49a3c --- /dev/null +++ b/components/web_cache/renderer/web_cache_render_process_observer.h @@ -0,0 +1,51 @@ +// Copyright 2014 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. + +#ifndef COMPONENTS_WEB_CACHE_RENDERER_WEB_CACHE_RENDER_PROCESS_OBSERVER_H_ +#define COMPONENTS_WEB_CACHE_RENDERER_WEB_CACHE_RENDER_PROCESS_OBSERVER_H_ + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "content/public/renderer/render_process_observer.h" + +namespace web_cache { + +// This class filters the incoming cache related control messages. +class WebCacheRenderProcessObserver : public content::RenderProcessObserver { + public: + WebCacheRenderProcessObserver(); + virtual ~WebCacheRenderProcessObserver(); + + // Needs to be called by RenderViews in case of navigations to execute + // any 'clear cache' commands that were delayed until the next navigation. + void ExecutePendingClearCache(); + + private: + // RenderProcessObserver implementation. + virtual bool OnControlMessageReceived(const IPC::Message& message) OVERRIDE; + virtual void WebKitInitialized() OVERRIDE; + virtual void OnRenderProcessShutdown() OVERRIDE; + + // Message handlers. + void OnSetCacheCapacities(size_t min_dead_capacity, + size_t max_dead_capacity, + size_t capacity); + // If |on_navigation| is true, the clearing is delayed until the next + // navigation event. + void OnClearCache(bool on_navigation); + + // If true, the web cache shall be cleared before the next navigation event. + bool clear_cache_pending_; + bool webkit_initialized_; + size_t pending_cache_min_dead_capacity_; + size_t pending_cache_max_dead_capacity_; + size_t pending_cache_capacity_; + + DISALLOW_COPY_AND_ASSIGN(WebCacheRenderProcessObserver); +}; + +} // namespace web_cache + +#endif // COMPONENTS_WEB_CACHE_RENDERER_WEB_CACHE_RENDER_PROCESS_OBSERVER_H_ + |