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