• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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.
4 
5 #include "content/browser/appcache/appcache_service_impl.h"
6 
7 #include <functional>
8 
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/stl_util.h"
15 #include "content/browser/appcache/appcache.h"
16 #include "content/browser/appcache/appcache_backend_impl.h"
17 #include "content/browser/appcache/appcache_entry.h"
18 #include "content/browser/appcache/appcache_executable_handler.h"
19 #include "content/browser/appcache/appcache_histograms.h"
20 #include "content/browser/appcache/appcache_policy.h"
21 #include "content/browser/appcache/appcache_quota_client.h"
22 #include "content/browser/appcache/appcache_response.h"
23 #include "content/browser/appcache/appcache_service_impl.h"
24 #include "content/browser/appcache/appcache_storage_impl.h"
25 #include "net/base/completion_callback.h"
26 #include "net/base/io_buffer.h"
27 #include "storage/browser/quota/special_storage_policy.h"
28 
29 namespace content {
30 
31 namespace {
32 
DeferredCallback(const net::CompletionCallback & callback,int rv)33 void DeferredCallback(const net::CompletionCallback& callback, int rv) {
34   callback.Run(rv);
35 }
36 
37 }  // namespace
38 
AppCacheInfoCollection()39 AppCacheInfoCollection::AppCacheInfoCollection() {}
40 
~AppCacheInfoCollection()41 AppCacheInfoCollection::~AppCacheInfoCollection() {}
42 
43 // AsyncHelper -------
44 
45 class AppCacheServiceImpl::AsyncHelper
46     : public AppCacheStorage::Delegate {
47  public:
AsyncHelper(AppCacheServiceImpl * service,const net::CompletionCallback & callback)48   AsyncHelper(AppCacheServiceImpl* service,
49               const net::CompletionCallback& callback)
50       : service_(service), callback_(callback) {
51     service_->pending_helpers_.insert(this);
52   }
53 
~AsyncHelper()54   virtual ~AsyncHelper() {
55     if (service_)
56       service_->pending_helpers_.erase(this);
57   }
58 
59   virtual void Start() = 0;
60   virtual void Cancel();
61 
62  protected:
CallCallback(int rv)63   void CallCallback(int rv) {
64     if (!callback_.is_null()) {
65       // Defer to guarantee async completion.
66       base::MessageLoop::current()->PostTask(
67           FROM_HERE, base::Bind(&DeferredCallback, callback_, rv));
68     }
69     callback_.Reset();
70   }
71 
72   AppCacheServiceImpl* service_;
73   net::CompletionCallback callback_;
74 };
75 
Cancel()76 void AppCacheServiceImpl::AsyncHelper::Cancel() {
77   if (!callback_.is_null()) {
78     callback_.Run(net::ERR_ABORTED);
79     callback_.Reset();
80   }
81   service_->storage()->CancelDelegateCallbacks(this);
82   service_ = NULL;
83 }
84 
85 // CanHandleOfflineHelper -------
86 
87 class AppCacheServiceImpl::CanHandleOfflineHelper : AsyncHelper {
88  public:
CanHandleOfflineHelper(AppCacheServiceImpl * service,const GURL & url,const GURL & first_party,const net::CompletionCallback & callback)89   CanHandleOfflineHelper(
90       AppCacheServiceImpl* service, const GURL& url,
91       const GURL& first_party, const net::CompletionCallback& callback)
92       : AsyncHelper(service, callback),
93         url_(url),
94         first_party_(first_party) {
95   }
96 
Start()97   virtual void Start() OVERRIDE {
98     AppCachePolicy* policy = service_->appcache_policy();
99     if (policy && !policy->CanLoadAppCache(url_, first_party_)) {
100       CallCallback(net::ERR_FAILED);
101       delete this;
102       return;
103     }
104 
105     service_->storage()->FindResponseForMainRequest(url_, GURL(), this);
106   }
107 
108  private:
109   // AppCacheStorage::Delegate implementation.
110   virtual void OnMainResponseFound(
111       const GURL& url, const AppCacheEntry& entry,
112       const GURL& fallback_url, const AppCacheEntry& fallback_entry,
113       int64 cache_id, int64 group_id, const GURL& mainfest_url) OVERRIDE;
114 
115   GURL url_;
116   GURL first_party_;
117 
118   DISALLOW_COPY_AND_ASSIGN(CanHandleOfflineHelper);
119 };
120 
OnMainResponseFound(const GURL & url,const AppCacheEntry & entry,const GURL & fallback_url,const AppCacheEntry & fallback_entry,int64 cache_id,int64 group_id,const GURL & manifest_url)121 void AppCacheServiceImpl::CanHandleOfflineHelper::OnMainResponseFound(
122       const GURL& url, const AppCacheEntry& entry,
123       const GURL& fallback_url, const AppCacheEntry& fallback_entry,
124       int64 cache_id, int64 group_id, const GURL& manifest_url) {
125   bool can = (entry.has_response_id() || fallback_entry.has_response_id());
126   CallCallback(can ? net::OK : net::ERR_FAILED);
127   delete this;
128 }
129 
130 // DeleteHelper -------
131 
132 class AppCacheServiceImpl::DeleteHelper : public AsyncHelper {
133  public:
DeleteHelper(AppCacheServiceImpl * service,const GURL & manifest_url,const net::CompletionCallback & callback)134   DeleteHelper(
135       AppCacheServiceImpl* service, const GURL& manifest_url,
136       const net::CompletionCallback& callback)
137       : AsyncHelper(service, callback), manifest_url_(manifest_url) {
138   }
139 
Start()140   virtual void Start() OVERRIDE {
141     service_->storage()->LoadOrCreateGroup(manifest_url_, this);
142   }
143 
144  private:
145   // AppCacheStorage::Delegate implementation.
146   virtual void OnGroupLoaded(
147       AppCacheGroup* group, const GURL& manifest_url) OVERRIDE;
148   virtual void OnGroupMadeObsolete(AppCacheGroup* group,
149                                    bool success,
150                                    int response_code) OVERRIDE;
151 
152   GURL manifest_url_;
153   DISALLOW_COPY_AND_ASSIGN(DeleteHelper);
154 };
155 
OnGroupLoaded(AppCacheGroup * group,const GURL & manifest_url)156 void AppCacheServiceImpl::DeleteHelper::OnGroupLoaded(
157       AppCacheGroup* group, const GURL& manifest_url) {
158   if (group) {
159     group->set_being_deleted(true);
160     group->CancelUpdate();
161     service_->storage()->MakeGroupObsolete(group, this, 0);
162   } else {
163     CallCallback(net::ERR_FAILED);
164     delete this;
165   }
166 }
167 
OnGroupMadeObsolete(AppCacheGroup * group,bool success,int response_code)168 void AppCacheServiceImpl::DeleteHelper::OnGroupMadeObsolete(
169     AppCacheGroup* group,
170     bool success,
171     int response_code) {
172   CallCallback(success ? net::OK : net::ERR_FAILED);
173   delete this;
174 }
175 
176 // DeleteOriginHelper -------
177 
178 class AppCacheServiceImpl::DeleteOriginHelper : public AsyncHelper {
179  public:
DeleteOriginHelper(AppCacheServiceImpl * service,const GURL & origin,const net::CompletionCallback & callback)180   DeleteOriginHelper(
181       AppCacheServiceImpl* service, const GURL& origin,
182       const net::CompletionCallback& callback)
183       : AsyncHelper(service, callback), origin_(origin),
184         num_caches_to_delete_(0), successes_(0), failures_(0) {
185   }
186 
Start()187   virtual void Start() OVERRIDE {
188     // We start by listing all caches, continues in OnAllInfo().
189     service_->storage()->GetAllInfo(this);
190   }
191 
192  private:
193   // AppCacheStorage::Delegate implementation.
194   virtual void OnAllInfo(AppCacheInfoCollection* collection) OVERRIDE;
195   virtual void OnGroupLoaded(
196       AppCacheGroup* group, const GURL& manifest_url) OVERRIDE;
197   virtual void OnGroupMadeObsolete(AppCacheGroup* group,
198                                    bool success,
199                                    int response_code) OVERRIDE;
200 
201   void CacheCompleted(bool success);
202 
203   GURL origin_;
204   int num_caches_to_delete_;
205   int successes_;
206   int failures_;
207 
208   DISALLOW_COPY_AND_ASSIGN(DeleteOriginHelper);
209 };
210 
OnAllInfo(AppCacheInfoCollection * collection)211 void AppCacheServiceImpl::DeleteOriginHelper::OnAllInfo(
212     AppCacheInfoCollection* collection) {
213   if (!collection) {
214     // Failed to get a listing.
215     CallCallback(net::ERR_FAILED);
216     delete this;
217     return;
218   }
219 
220   std::map<GURL, AppCacheInfoVector>::iterator found =
221       collection->infos_by_origin.find(origin_);
222   if (found == collection->infos_by_origin.end() || found->second.empty()) {
223     // No caches for this origin.
224     CallCallback(net::OK);
225     delete this;
226     return;
227   }
228 
229   // We have some caches to delete.
230   const AppCacheInfoVector& caches_to_delete = found->second;
231   successes_ = 0;
232   failures_ = 0;
233   num_caches_to_delete_ = static_cast<int>(caches_to_delete.size());
234   for (AppCacheInfoVector::const_iterator iter = caches_to_delete.begin();
235        iter != caches_to_delete.end(); ++iter) {
236     service_->storage()->LoadOrCreateGroup(iter->manifest_url, this);
237   }
238 }
239 
OnGroupLoaded(AppCacheGroup * group,const GURL & manifest_url)240 void AppCacheServiceImpl::DeleteOriginHelper::OnGroupLoaded(
241       AppCacheGroup* group, const GURL& manifest_url) {
242   if (group) {
243     group->set_being_deleted(true);
244     group->CancelUpdate();
245     service_->storage()->MakeGroupObsolete(group, this, 0);
246   } else {
247     CacheCompleted(false);
248   }
249 }
250 
OnGroupMadeObsolete(AppCacheGroup * group,bool success,int response_code)251 void AppCacheServiceImpl::DeleteOriginHelper::OnGroupMadeObsolete(
252     AppCacheGroup* group,
253     bool success,
254     int response_code) {
255   CacheCompleted(success);
256 }
257 
CacheCompleted(bool success)258 void AppCacheServiceImpl::DeleteOriginHelper::CacheCompleted(bool success) {
259   if (success)
260     ++successes_;
261   else
262     ++failures_;
263   if ((successes_ + failures_) < num_caches_to_delete_)
264     return;
265 
266   CallCallback(!failures_ ? net::OK : net::ERR_FAILED);
267   delete this;
268 }
269 
270 
271 // GetInfoHelper -------
272 
273 class AppCacheServiceImpl::GetInfoHelper : AsyncHelper {
274  public:
GetInfoHelper(AppCacheServiceImpl * service,AppCacheInfoCollection * collection,const net::CompletionCallback & callback)275   GetInfoHelper(
276       AppCacheServiceImpl* service, AppCacheInfoCollection* collection,
277       const net::CompletionCallback& callback)
278       : AsyncHelper(service, callback), collection_(collection) {
279   }
280 
Start()281   virtual void Start() OVERRIDE {
282     service_->storage()->GetAllInfo(this);
283   }
284 
285  private:
286   // AppCacheStorage::Delegate implementation.
287   virtual void OnAllInfo(AppCacheInfoCollection* collection) OVERRIDE;
288 
289   scoped_refptr<AppCacheInfoCollection> collection_;
290 
291   DISALLOW_COPY_AND_ASSIGN(GetInfoHelper);
292 };
293 
OnAllInfo(AppCacheInfoCollection * collection)294 void AppCacheServiceImpl::GetInfoHelper::OnAllInfo(
295       AppCacheInfoCollection* collection) {
296   if (collection)
297     collection->infos_by_origin.swap(collection_->infos_by_origin);
298   CallCallback(collection ? net::OK : net::ERR_FAILED);
299   delete this;
300 }
301 
302 // CheckResponseHelper -------
303 
304 class AppCacheServiceImpl::CheckResponseHelper : AsyncHelper {
305  public:
CheckResponseHelper(AppCacheServiceImpl * service,const GURL & manifest_url,int64 cache_id,int64 response_id)306   CheckResponseHelper(
307       AppCacheServiceImpl* service, const GURL& manifest_url, int64 cache_id,
308       int64 response_id)
309       : AsyncHelper(service, net::CompletionCallback()),
310         manifest_url_(manifest_url),
311         cache_id_(cache_id),
312         response_id_(response_id),
313         kIOBufferSize(32 * 1024),
314         expected_total_size_(0),
315         amount_headers_read_(0),
316         amount_data_read_(0) {
317   }
318 
Start()319   virtual void Start() OVERRIDE {
320     service_->storage()->LoadOrCreateGroup(manifest_url_, this);
321   }
322 
Cancel()323   virtual void Cancel() OVERRIDE {
324     AppCacheHistograms::CountCheckResponseResult(
325         AppCacheHistograms::CHECK_CANCELED);
326     response_reader_.reset();
327     AsyncHelper::Cancel();
328   }
329 
330  private:
331   virtual void OnGroupLoaded(AppCacheGroup* group,
332                              const GURL& manifest_url) OVERRIDE;
333   void OnReadInfoComplete(int result);
334   void OnReadDataComplete(int result);
335 
336   // Inputs describing what to check.
337   GURL manifest_url_;
338   int64 cache_id_;
339   int64 response_id_;
340 
341   // Internals used to perform the checks.
342   const int kIOBufferSize;
343   scoped_refptr<AppCache> cache_;
344   scoped_ptr<AppCacheResponseReader> response_reader_;
345   scoped_refptr<HttpResponseInfoIOBuffer> info_buffer_;
346   scoped_refptr<net::IOBuffer> data_buffer_;
347   int64 expected_total_size_;
348   int amount_headers_read_;
349   int amount_data_read_;
350   DISALLOW_COPY_AND_ASSIGN(CheckResponseHelper);
351 };
352 
OnGroupLoaded(AppCacheGroup * group,const GURL & manifest_url)353 void AppCacheServiceImpl::CheckResponseHelper::OnGroupLoaded(
354     AppCacheGroup* group, const GURL& manifest_url) {
355   DCHECK_EQ(manifest_url_, manifest_url);
356   if (!group || !group->newest_complete_cache() || group->is_being_deleted() ||
357       group->is_obsolete()) {
358     AppCacheHistograms::CountCheckResponseResult(
359         AppCacheHistograms::MANIFEST_OUT_OF_DATE);
360     delete this;
361     return;
362   }
363 
364   cache_ = group->newest_complete_cache();
365   const AppCacheEntry* entry = cache_->GetEntryWithResponseId(response_id_);
366   if (!entry) {
367     if (cache_->cache_id() == cache_id_) {
368       AppCacheHistograms::CountCheckResponseResult(
369           AppCacheHistograms::ENTRY_NOT_FOUND);
370       service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
371     } else {
372       AppCacheHistograms::CountCheckResponseResult(
373           AppCacheHistograms::RESPONSE_OUT_OF_DATE);
374     }
375     delete this;
376     return;
377   }
378 
379   // Verify that we can read the response info and data.
380   expected_total_size_ = entry->response_size();
381   response_reader_.reset(service_->storage()->CreateResponseReader(
382       manifest_url_, group->group_id(), response_id_));
383   info_buffer_ = new HttpResponseInfoIOBuffer();
384   response_reader_->ReadInfo(
385       info_buffer_.get(),
386       base::Bind(&CheckResponseHelper::OnReadInfoComplete,
387                  base::Unretained(this)));
388 }
389 
OnReadInfoComplete(int result)390 void AppCacheServiceImpl::CheckResponseHelper::OnReadInfoComplete(int result) {
391   if (result < 0) {
392     AppCacheHistograms::CountCheckResponseResult(
393         AppCacheHistograms::READ_HEADERS_ERROR);
394     service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
395     delete this;
396     return;
397   }
398   amount_headers_read_ = result;
399 
400   // Start reading the data.
401   data_buffer_ = new net::IOBuffer(kIOBufferSize);
402   response_reader_->ReadData(
403       data_buffer_.get(),
404       kIOBufferSize,
405       base::Bind(&CheckResponseHelper::OnReadDataComplete,
406                  base::Unretained(this)));
407 }
408 
OnReadDataComplete(int result)409 void AppCacheServiceImpl::CheckResponseHelper::OnReadDataComplete(int result) {
410   if (result > 0) {
411     // Keep reading until we've read thru everything or failed to read.
412     amount_data_read_ += result;
413     response_reader_->ReadData(
414         data_buffer_.get(),
415         kIOBufferSize,
416         base::Bind(&CheckResponseHelper::OnReadDataComplete,
417                    base::Unretained(this)));
418     return;
419   }
420 
421   AppCacheHistograms::CheckResponseResultType check_result;
422   if (result < 0)
423     check_result = AppCacheHistograms::READ_DATA_ERROR;
424   else if (info_buffer_->response_data_size != amount_data_read_ ||
425            expected_total_size_ != amount_data_read_ + amount_headers_read_)
426     check_result = AppCacheHistograms::UNEXPECTED_DATA_SIZE;
427   else
428     check_result = AppCacheHistograms::RESPONSE_OK;
429   AppCacheHistograms::CountCheckResponseResult(check_result);
430 
431   if (check_result != AppCacheHistograms::RESPONSE_OK)
432     service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
433   delete this;
434 }
435 
436 // AppCacheStorageReference ------
437 
AppCacheStorageReference(scoped_ptr<AppCacheStorage> storage)438 AppCacheStorageReference::AppCacheStorageReference(
439     scoped_ptr<AppCacheStorage> storage)
440     : storage_(storage.Pass()) {}
~AppCacheStorageReference()441 AppCacheStorageReference::~AppCacheStorageReference() {}
442 
443 // AppCacheServiceImpl -------
444 
AppCacheServiceImpl(storage::QuotaManagerProxy * quota_manager_proxy)445 AppCacheServiceImpl::AppCacheServiceImpl(
446     storage::QuotaManagerProxy* quota_manager_proxy)
447     : appcache_policy_(NULL),
448       quota_client_(NULL),
449       handler_factory_(NULL),
450       quota_manager_proxy_(quota_manager_proxy),
451       request_context_(NULL),
452       force_keep_session_state_(false) {
453   if (quota_manager_proxy_.get()) {
454     quota_client_ = new AppCacheQuotaClient(this);
455     quota_manager_proxy_->RegisterClient(quota_client_);
456   }
457 }
458 
~AppCacheServiceImpl()459 AppCacheServiceImpl::~AppCacheServiceImpl() {
460   DCHECK(backends_.empty());
461   std::for_each(pending_helpers_.begin(),
462                 pending_helpers_.end(),
463                 std::mem_fun(&AsyncHelper::Cancel));
464   STLDeleteElements(&pending_helpers_);
465   if (quota_client_)
466     quota_client_->NotifyAppCacheDestroyed();
467 
468   // Destroy storage_ first; ~AppCacheStorageImpl accesses other data members
469   // (special_storage_policy_).
470   storage_.reset();
471 }
472 
Initialize(const base::FilePath & cache_directory,const scoped_refptr<base::SingleThreadTaskRunner> & db_thread,const scoped_refptr<base::SingleThreadTaskRunner> & cache_thread)473 void AppCacheServiceImpl::Initialize(
474     const base::FilePath& cache_directory,
475     const scoped_refptr<base::SingleThreadTaskRunner>& db_thread,
476     const scoped_refptr<base::SingleThreadTaskRunner>& cache_thread) {
477   DCHECK(!storage_.get());
478   cache_directory_ = cache_directory;
479   db_thread_ = db_thread;
480   cache_thread_ = cache_thread;
481   AppCacheStorageImpl* storage = new AppCacheStorageImpl(this);
482   storage->Initialize(cache_directory, db_thread, cache_thread);
483   storage_.reset(storage);
484 }
485 
ScheduleReinitialize()486 void AppCacheServiceImpl::ScheduleReinitialize() {
487   if (reinit_timer_.IsRunning())
488     return;
489 
490   // Reinitialization only happens when corruption has been noticed.
491   // We don't want to thrash the disk but we also don't want to
492   // leave the appcache disabled for an indefinite period of time. Some
493   // users never shutdown the browser.
494 
495   const base::TimeDelta kZeroDelta;
496   const base::TimeDelta kOneHour(base::TimeDelta::FromHours(1));
497   const base::TimeDelta k30Seconds(base::TimeDelta::FromSeconds(30));
498 
499   // If the system managed to stay up for long enough, reset the
500   // delay so a new failure won't incur a long wait to get going again.
501   base::TimeDelta up_time = base::Time::Now() - last_reinit_time_;
502   if (next_reinit_delay_ != kZeroDelta && up_time > kOneHour)
503     next_reinit_delay_ = kZeroDelta;
504 
505   reinit_timer_.Start(FROM_HERE, next_reinit_delay_,
506                       this, &AppCacheServiceImpl::Reinitialize);
507 
508   // Adjust the delay for next time.
509   base::TimeDelta increment = std::max(k30Seconds, next_reinit_delay_);
510   next_reinit_delay_ = std::min(next_reinit_delay_ + increment,  kOneHour);
511 }
512 
Reinitialize()513 void AppCacheServiceImpl::Reinitialize() {
514   AppCacheHistograms::CountReinitAttempt(!last_reinit_time_.is_null());
515   last_reinit_time_ = base::Time::Now();
516 
517   // Inform observers of about this and give them a chance to
518   // defer deletion of the old storage object.
519   scoped_refptr<AppCacheStorageReference>
520       old_storage_ref(new AppCacheStorageReference(storage_.Pass()));
521   FOR_EACH_OBSERVER(Observer, observers_,
522                     OnServiceReinitialized(old_storage_ref.get()));
523 
524   Initialize(cache_directory_, db_thread_, cache_thread_);
525 }
526 
CanHandleMainResourceOffline(const GURL & url,const GURL & first_party,const net::CompletionCallback & callback)527 void AppCacheServiceImpl::CanHandleMainResourceOffline(
528     const GURL& url,
529     const GURL& first_party,
530     const net::CompletionCallback& callback) {
531   CanHandleOfflineHelper* helper =
532       new CanHandleOfflineHelper(this, url, first_party, callback);
533   helper->Start();
534 }
535 
GetAllAppCacheInfo(AppCacheInfoCollection * collection,const net::CompletionCallback & callback)536 void AppCacheServiceImpl::GetAllAppCacheInfo(
537     AppCacheInfoCollection* collection,
538     const net::CompletionCallback& callback) {
539   DCHECK(collection);
540   GetInfoHelper* helper = new GetInfoHelper(this, collection, callback);
541   helper->Start();
542 }
543 
DeleteAppCacheGroup(const GURL & manifest_url,const net::CompletionCallback & callback)544 void AppCacheServiceImpl::DeleteAppCacheGroup(
545     const GURL& manifest_url,
546     const net::CompletionCallback& callback) {
547   DeleteHelper* helper = new DeleteHelper(this, manifest_url, callback);
548   helper->Start();
549 }
550 
DeleteAppCachesForOrigin(const GURL & origin,const net::CompletionCallback & callback)551 void AppCacheServiceImpl::DeleteAppCachesForOrigin(
552     const GURL& origin,  const net::CompletionCallback& callback) {
553   DeleteOriginHelper* helper = new DeleteOriginHelper(this, origin, callback);
554   helper->Start();
555 }
556 
CheckAppCacheResponse(const GURL & manifest_url,int64 cache_id,int64 response_id)557 void AppCacheServiceImpl::CheckAppCacheResponse(const GURL& manifest_url,
558                                             int64 cache_id,
559                                             int64 response_id) {
560   CheckResponseHelper* helper = new CheckResponseHelper(
561       this, manifest_url, cache_id, response_id);
562   helper->Start();
563 }
564 
set_special_storage_policy(storage::SpecialStoragePolicy * policy)565 void AppCacheServiceImpl::set_special_storage_policy(
566     storage::SpecialStoragePolicy* policy) {
567   special_storage_policy_ = policy;
568 }
569 
RegisterBackend(AppCacheBackendImpl * backend_impl)570 void AppCacheServiceImpl::RegisterBackend(
571     AppCacheBackendImpl* backend_impl) {
572   DCHECK(backends_.find(backend_impl->process_id()) == backends_.end());
573   backends_.insert(
574       BackendMap::value_type(backend_impl->process_id(), backend_impl));
575 }
576 
UnregisterBackend(AppCacheBackendImpl * backend_impl)577 void AppCacheServiceImpl::UnregisterBackend(
578     AppCacheBackendImpl* backend_impl) {
579   backends_.erase(backend_impl->process_id());
580 }
581 
582 }  // namespace content
583