Suppress sending mousedown / mouseup when in fling
[chromium-blink-merge.git] / content / browser / renderer_host / backing_store_manager.cc
blob4d79b0ff4f2fec1432eb0e326b15bee0b449e383
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/renderer_host/backing_store_manager.h"
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/containers/mru_cache.h"
10 #include "base/sys_info.h"
11 #include "content/browser/renderer_host/backing_store.h"
12 #include "content/browser/renderer_host/render_widget_host_impl.h"
13 #include "content/public/common/content_switches.h"
15 namespace content {
16 namespace {
18 // There are two separate caches, |large_cache| and |small_cache|. large_cache
19 // is meant for large items (tabs, popup windows), while small_cache is meant
20 // for small items (extension popups, HTML5 notifications). The idea is that
21 // we'll almost always try to evict from large_cache first since small_cache
22 // items will tend to be visible more of the time.
23 typedef base::OwningMRUCache<RenderWidgetHost*, BackingStore*>
24 BackingStoreCache;
25 BackingStoreCache* large_cache = NULL;
26 BackingStoreCache* small_cache = NULL;
28 // Threshold is based on a single large-monitor-width toolstrip.
29 // (32bpp, 32 pixels high, 1920 pixels wide)
30 // TODO(aa): The extension system no longer supports toolstrips, but we think
31 // this might be helping for other examples of small HTML views in Chrome.
32 // Maybe this cache should be redesigned to simply prefer smaller objects to
33 // larger ones, rather than having a fixed threshold.
34 // For more background, see: crbug.com/100506.
35 const size_t kSmallThreshold = 4 * 32 * 1920;
37 // Pick a large monitor size to use as a multiplier. This is multiplied by the
38 // max number of large backing stores (usually tabs) to pick a ceiling on the
39 // max memory to use.
40 // TODO(erikkay) Perhaps we should actually use monitor size? That way we
41 // could make an assertion like "worse case, there are two tabs in the cache".
42 // However, the small_cache might mess up these calculations a bit.
43 // TODO(erikkay) 32bpp assumption isn't great.
44 const size_t kMemoryMultiplier = 4 * 1920 * 1200; // ~9MB
46 // The maximum number of large BackingStoreCache objects (tabs) to use.
47 // Use a minimum of 2, and add one for each 256MB of physical memory you have.
48 // Cap at 5, the thinking being that even if you have a gigantic amount of
49 // RAM, there's a limit to how much caching helps beyond a certain number
50 // of tabs. If users *really* want unlimited stores, allow it via the
51 // --disable-backing-store-limit flag.
52 static size_t MaxNumberOfBackingStores() {
53 static bool unlimited = false;
54 const CommandLine& command = *CommandLine::ForCurrentProcess();
55 unlimited = command.HasSwitch(switches::kDisableBackingStoreLimit);
58 if (unlimited) {
59 // 100 isn't truly unlimited, but given that backing stores count against
60 // GDI memory, it's well past any reasonable number. Many systems will
61 // begin to fail in strange ways well before they hit 100 stores.
62 return 100;
63 } else {
64 return std::min(5, 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256));
68 // The maximum about of memory to use for all BackingStoreCache object combined.
69 static size_t MaxBackingStoreMemory() {
70 // Compute in terms of the number of large monitor's worth of backing-store.
71 return MaxNumberOfBackingStores() * kMemoryMultiplier;
74 // Expires the given |backing_store| from |cache|.
75 void ExpireBackingStoreAt(BackingStoreCache* cache,
76 BackingStoreCache::iterator backing_store) {
77 cache->Erase(backing_store);
80 size_t ExpireLastBackingStore(BackingStoreCache* cache) {
81 if (cache->size() < 1)
82 return 0;
84 // Crazy C++ alert: rbegin.base() is a forward iterator pointing to end(),
85 // so we need to do -- to move one back to the actual last item.
86 BackingStoreCache::iterator entry = --cache->rbegin().base();
87 size_t entry_size = entry->second->MemorySize();
88 ExpireBackingStoreAt(cache, entry);
89 return entry_size;
92 void CreateCacheSpace(size_t size) {
93 // Given a request for |size|, first free from the large cache (until there's
94 // only one item left) and then do the same from the small cache if we still
95 // don't have enough.
96 while (size > 0 && (large_cache->size() > 1 || small_cache->size() > 1)) {
97 BackingStoreCache* cache =
98 (large_cache->size() > 1) ? large_cache : small_cache;
99 while (size > 0 && cache->size() > 1) {
100 size_t entry_size = ExpireLastBackingStore(cache);
101 if (size > entry_size)
102 size -= entry_size;
103 else
104 size = 0;
107 DCHECK(size == 0);
110 // Creates the backing store for the host based on the dimensions passed in.
111 // Removes the existing backing store if there is one.
112 BackingStore* CreateBackingStore(RenderWidgetHost* host,
113 const gfx::Size& backing_store_size) {
114 // Remove any existing backing store in case we're replacing it.
115 BackingStoreManager::RemoveBackingStore(host);
117 if (!large_cache) {
118 large_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT);
119 small_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT);
122 // TODO(erikkay) 32bpp is not always accurate
123 size_t new_mem = backing_store_size.GetArea() * 4;
124 size_t current_mem = BackingStoreManager::MemorySize();
125 size_t max_mem = MaxBackingStoreMemory();
126 DCHECK(new_mem < max_mem);
127 if (current_mem + new_mem > max_mem) {
128 // Need to remove old backing stores to make room for the new one. We
129 // don't want to do this when the backing store is being replace by a new
130 // one for the same WebContents, but this case won't get called then: we'll
131 // have removed the old one in the RemoveBackingStore above, and the cache
132 // won't be over-sized.
133 CreateCacheSpace((current_mem + new_mem) - max_mem);
135 DCHECK((BackingStoreManager::MemorySize() + new_mem) <= max_mem);
137 BackingStoreCache* cache;
138 if (new_mem > kSmallThreshold) {
139 // Limit the number of large backing stores (tabs) to the memory tier number
140 // (between 2-5). While we allow a larger amount of memory for people who
141 // have large windows, this means that those who use small browser windows
142 // won't ever cache more than 5 tabs, so they pay a smaller memory cost.
143 if (large_cache->size() >= MaxNumberOfBackingStores())
144 ExpireLastBackingStore(large_cache);
145 cache = large_cache;
146 } else {
147 cache = small_cache;
149 BackingStore* backing_store = RenderWidgetHostImpl::From(
150 host)->AllocBackingStore(backing_store_size);
151 if (backing_store)
152 cache->Put(host, backing_store);
153 return backing_store;
156 int ComputeTotalArea(const std::vector<gfx::Rect>& rects) {
157 // We assume that the given rects are non-overlapping, which is a property of
158 // the paint rects generated by the PaintAggregator.
159 #ifndef NDEBUG
160 for (size_t i = 0; i < rects.size(); ++i) {
161 for (size_t j = 0; j < rects.size(); ++j) {
162 if (i != j)
163 DCHECK(!rects[i].Intersects(rects[j]));
166 #endif
167 int area = 0;
168 for (size_t i = 0; i < rects.size(); ++i)
169 area += rects[i].size().GetArea();
170 return area;
173 } // namespace
175 // BackingStoreManager ---------------------------------------------------------
177 // static
178 BackingStore* BackingStoreManager::GetBackingStore(
179 RenderWidgetHost* host,
180 const gfx::Size& desired_size) {
181 BackingStore* backing_store = Lookup(host);
182 if (backing_store) {
183 // If we already have a backing store, then make sure it is the correct
184 // size.
185 if (backing_store->size() == desired_size)
186 return backing_store;
187 backing_store = NULL;
190 return backing_store;
193 // static
194 void BackingStoreManager::PrepareBackingStore(
195 RenderWidgetHost* host,
196 const gfx::Size& backing_store_size,
197 TransportDIB::Id bitmap,
198 const gfx::Rect& bitmap_rect,
199 const std::vector<gfx::Rect>& copy_rects,
200 float scale_factor,
201 const base::Closure& completion_callback,
202 bool* needs_full_paint,
203 bool* scheduled_completion_callback) {
204 BackingStore* backing_store = GetBackingStore(host, backing_store_size);
205 if (!backing_store) {
206 // We need to get Webkit to generate a new paint here, as we
207 // don't have a previous snapshot.
208 if (bitmap_rect.size() != backing_store_size ||
209 bitmap_rect.x() != 0 || bitmap_rect.y() != 0 ||
210 ComputeTotalArea(copy_rects) != backing_store_size.GetArea() ||
211 !(backing_store = CreateBackingStore(host, backing_store_size))) {
212 DCHECK(needs_full_paint != NULL);
213 *needs_full_paint = true;
214 *scheduled_completion_callback = false;
215 // Makes no sense to paint the transport dib if we are going
216 // to request a full paint.
217 return;
221 backing_store->PaintToBackingStore(host->GetProcess(), bitmap,
222 bitmap_rect, copy_rects, scale_factor,
223 completion_callback,
224 scheduled_completion_callback);
227 // static
228 BackingStore* BackingStoreManager::Lookup(RenderWidgetHost* host) {
229 if (large_cache) {
230 BackingStoreCache::iterator it = large_cache->Get(host);
231 if (it != large_cache->end())
232 return it->second;
234 // This moves host to the front of the MRU.
235 it = small_cache->Get(host);
236 if (it != small_cache->end())
237 return it->second;
239 return NULL;
242 // static
243 void BackingStoreManager::RemoveBackingStore(RenderWidgetHost* host) {
244 if (!large_cache)
245 return;
247 BackingStoreCache* cache = large_cache;
248 BackingStoreCache::iterator it = cache->Peek(host);
249 if (it == cache->end()) {
250 cache = small_cache;
251 it = cache->Peek(host);
252 if (it == cache->end())
253 return;
255 cache->Erase(it);
258 // static
259 void BackingStoreManager::RemoveAllBackingStores() {
260 if (large_cache) {
261 large_cache->Clear();
262 small_cache->Clear();
266 // static
267 size_t BackingStoreManager::MemorySize() {
268 if (!large_cache)
269 return 0;
271 size_t mem = 0;
272 BackingStoreCache::iterator it;
273 for (it = large_cache->begin(); it != large_cache->end(); ++it)
274 mem += it->second->MemorySize();
276 for (it = small_cache->begin(); it != small_cache->end(); ++it)
277 mem += it->second->MemorySize();
279 return mem;
282 } // namespace content