summaryrefslogtreecommitdiffstats
path: root/components/web_cache
diff options
context:
space:
mode:
authorhanxi <hanxi@chromium.org>2014-09-11 14:57:18 -0700
committerCommit bot <commit-bot@chromium.org>2014-09-11 22:25:47 +0000
commit149b92db00bc1deab22599e7d3da3fc8be2c0d8b (patch)
tree68a64107a7d6b9190fb1dc040b7f511eea92ff9d /components/web_cache
parent28d886c967e016a5d5812be43cd5916f577c2e10 (diff)
downloadchromium_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/OWNERS10
-rw-r--r--components/web_cache/browser/BUILD.gn18
-rw-r--r--components/web_cache/browser/DEPS5
-rw-r--r--components/web_cache/browser/web_cache_manager.cc436
-rw-r--r--components/web_cache/browser/web_cache_manager.h241
-rw-r--r--components/web_cache/browser/web_cache_manager_unittest.cc466
-rw-r--r--components/web_cache/common/BUILD.gn16
-rw-r--r--components/web_cache/common/DEPS3
-rw-r--r--components/web_cache/common/OWNERS11
-rw-r--r--components/web_cache/common/web_cache_message_generator.cc33
-rw-r--r--components/web_cache/common/web_cache_message_generator.h7
-rw-r--r--components/web_cache/common/web_cache_messages.h23
-rw-r--r--components/web_cache/renderer/BUILD.gn17
-rw-r--r--components/web_cache/renderer/DEPS4
-rw-r--r--components/web_cache/renderer/web_cache_render_process_observer.cc84
-rw-r--r--components/web_cache/renderer/web_cache_render_process_observer.h51
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_
+