• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4 
5 #include "include/wrapper/cef_resource_manager.h"
6 
7 #include <algorithm>
8 #include <vector>
9 
10 #include "include/base/cef_macros.h"
11 #include "include/base/cef_weak_ptr.h"
12 #include "include/cef_parser.h"
13 #include "include/wrapper/cef_stream_resource_handler.h"
14 #include "include/wrapper/cef_zip_archive.h"
15 
16 namespace {
17 
18 #if defined(OS_WIN)
19 #define PATH_SEP '\\'
20 #else
21 #define PATH_SEP '/'
22 #endif
23 
24 // Returns |url| without the query or fragment components, if any.
GetUrlWithoutQueryOrFragment(const std::string & url)25 std::string GetUrlWithoutQueryOrFragment(const std::string& url) {
26   // Find the first instance of '?' or '#'.
27   const size_t pos = std::min(url.find('?'), url.find('#'));
28   if (pos != std::string::npos)
29     return url.substr(0, pos);
30 
31   return url;
32 }
33 
34 // Determine the mime type based on the |url| file extension.
GetMimeType(const std::string & url)35 std::string GetMimeType(const std::string& url) {
36   std::string mime_type;
37   const std::string& url_without_query = GetUrlWithoutQueryOrFragment(url);
38   size_t sep = url_without_query.find_last_of(".");
39   if (sep != std::string::npos) {
40     mime_type = CefGetMimeType(url_without_query.substr(sep + 1));
41     if (!mime_type.empty())
42       return mime_type;
43   }
44   return "text/html";
45 }
46 
47 // Default no-op filter.
GetFilteredUrl(const std::string & url)48 std::string GetFilteredUrl(const std::string& url) {
49   return url;
50 }
51 
52 // Provider of fixed contents.
53 class ContentProvider : public CefResourceManager::Provider {
54  public:
ContentProvider(const std::string & url,const std::string & content,const std::string & mime_type)55   ContentProvider(const std::string& url,
56                   const std::string& content,
57                   const std::string& mime_type)
58       : url_(url), content_(content), mime_type_(mime_type) {
59     DCHECK(!url.empty());
60     DCHECK(!content.empty());
61   }
62 
OnRequest(scoped_refptr<CefResourceManager::Request> request)63   bool OnRequest(scoped_refptr<CefResourceManager::Request> request) OVERRIDE {
64     CEF_REQUIRE_IO_THREAD();
65 
66     const std::string& url = request->url();
67     if (url != url_) {
68       // Not handled by this provider.
69       return false;
70     }
71 
72     CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
73         static_cast<void*>(const_cast<char*>(content_.data())),
74         content_.length());
75 
76     // Determine the mime type a single time if it isn't already set.
77     if (mime_type_.empty())
78       mime_type_ = request->mime_type_resolver().Run(url);
79 
80     request->Continue(new CefStreamResourceHandler(mime_type_, stream));
81     return true;
82   }
83 
84  private:
85   std::string url_;
86   std::string content_;
87   std::string mime_type_;
88 
89   DISALLOW_COPY_AND_ASSIGN(ContentProvider);
90 };
91 
92 // Provider of contents loaded from a directory on the file system.
93 class DirectoryProvider : public CefResourceManager::Provider {
94  public:
DirectoryProvider(const std::string & url_path,const std::string & directory_path)95   DirectoryProvider(const std::string& url_path,
96                     const std::string& directory_path)
97       : url_path_(url_path), directory_path_(directory_path) {
98     DCHECK(!url_path_.empty());
99     DCHECK(!directory_path_.empty());
100 
101     // Normalize the path values.
102     if (url_path_[url_path_.size() - 1] != '/')
103       url_path_ += '/';
104     if (directory_path_[directory_path_.size() - 1] != PATH_SEP)
105       directory_path_ += PATH_SEP;
106   }
107 
OnRequest(scoped_refptr<CefResourceManager::Request> request)108   bool OnRequest(scoped_refptr<CefResourceManager::Request> request) OVERRIDE {
109     CEF_REQUIRE_IO_THREAD();
110 
111     const std::string& url = request->url();
112     if (url.find(url_path_) != 0U) {
113       return false;
114     }
115 
116     const std::string& file_path = GetFilePath(url);
117 
118     // Open |file_path| on the FILE thread.
119     CefPostTask(TID_FILE, base::Bind(&DirectoryProvider::OpenOnFileThread,
120                                      file_path, request));
121 
122     return true;
123   }
124 
125  private:
GetFilePath(const std::string & url)126   std::string GetFilePath(const std::string& url) {
127     std::string path_part = url.substr(url_path_.length());
128 #if defined(OS_WIN)
129     std::replace(path_part.begin(), path_part.end(), '/', '\\');
130 #endif
131     return directory_path_ + path_part;
132   }
133 
OpenOnFileThread(const std::string & file_path,scoped_refptr<CefResourceManager::Request> request)134   static void OpenOnFileThread(
135       const std::string& file_path,
136       scoped_refptr<CefResourceManager::Request> request) {
137     CEF_REQUIRE_FILE_THREAD();
138 
139     CefRefPtr<CefStreamReader> stream =
140         CefStreamReader::CreateForFile(file_path);
141 
142     // Continue loading on the IO thread.
143     CefPostTask(TID_IO, base::Bind(&DirectoryProvider::ContinueOpenOnIOThread,
144                                    request, stream));
145   }
146 
ContinueOpenOnIOThread(scoped_refptr<CefResourceManager::Request> request,CefRefPtr<CefStreamReader> stream)147   static void ContinueOpenOnIOThread(
148       scoped_refptr<CefResourceManager::Request> request,
149       CefRefPtr<CefStreamReader> stream) {
150     CEF_REQUIRE_IO_THREAD();
151 
152     CefRefPtr<CefStreamResourceHandler> handler;
153     if (stream.get()) {
154       handler = new CefStreamResourceHandler(
155           request->mime_type_resolver().Run(request->url()), stream);
156     }
157     request->Continue(handler);
158   }
159 
160   std::string url_path_;
161   std::string directory_path_;
162 
163   DISALLOW_COPY_AND_ASSIGN(DirectoryProvider);
164 };
165 
166 // Provider of contents loaded from an archive file.
167 class ArchiveProvider : public CefResourceManager::Provider {
168  public:
ArchiveProvider(const std::string & url_path,const std::string & archive_path,const std::string & password)169   ArchiveProvider(const std::string& url_path,
170                   const std::string& archive_path,
171                   const std::string& password)
172       : url_path_(url_path),
173         archive_path_(archive_path),
174         password_(password),
175         archive_load_started_(false),
176         archive_load_ended_(false),
177         ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
178     DCHECK(!url_path_.empty());
179     DCHECK(!archive_path_.empty());
180 
181     // Normalize the path values.
182     if (url_path_[url_path_.size() - 1] != '/')
183       url_path_ += '/';
184   }
185 
OnRequest(scoped_refptr<CefResourceManager::Request> request)186   bool OnRequest(scoped_refptr<CefResourceManager::Request> request) OVERRIDE {
187     CEF_REQUIRE_IO_THREAD();
188 
189     const std::string& url = request->url();
190     if (url.find(url_path_) != 0U) {
191       // Not handled by this provider.
192       return false;
193     }
194 
195     if (!archive_load_started_) {
196       // Initiate archive loading and queue the pending request.
197       archive_load_started_ = true;
198       pending_requests_.push_back(request);
199 
200       // Load the archive file on the FILE thread.
201       CefPostTask(TID_FILE, base::Bind(&ArchiveProvider::LoadOnFileThread,
202                                        weak_ptr_factory_.GetWeakPtr(),
203                                        archive_path_, password_));
204       return true;
205     }
206 
207     if (archive_load_started_ && !archive_load_ended_) {
208       // The archive load has already started. Queue the pending request.
209       pending_requests_.push_back(request);
210       return true;
211     }
212 
213     // Archive loading is done.
214     return ContinueRequest(request);
215   }
216 
217  private:
LoadOnFileThread(base::WeakPtr<ArchiveProvider> ptr,const std::string & archive_path,const std::string & password)218   static void LoadOnFileThread(base::WeakPtr<ArchiveProvider> ptr,
219                                const std::string& archive_path,
220                                const std::string& password) {
221     CEF_REQUIRE_FILE_THREAD();
222 
223     CefRefPtr<CefZipArchive> archive;
224 
225     CefRefPtr<CefStreamReader> stream =
226         CefStreamReader::CreateForFile(archive_path);
227     if (stream.get()) {
228       archive = new CefZipArchive;
229       if (archive->Load(stream, password, true) == 0) {
230         DLOG(WARNING) << "Empty archive file: " << archive_path;
231         archive = nullptr;
232       }
233     } else {
234       DLOG(WARNING) << "Failed to load archive file: " << archive_path;
235     }
236 
237     CefPostTask(TID_IO,
238                 base::Bind(&ArchiveProvider::ContinueOnIOThread, ptr, archive));
239   }
240 
ContinueOnIOThread(CefRefPtr<CefZipArchive> archive)241   void ContinueOnIOThread(CefRefPtr<CefZipArchive> archive) {
242     CEF_REQUIRE_IO_THREAD();
243 
244     archive_load_ended_ = true;
245     archive_ = archive;
246 
247     if (!pending_requests_.empty()) {
248       // Continue all pending requests.
249       PendingRequests::const_iterator it = pending_requests_.begin();
250       for (; it != pending_requests_.end(); ++it)
251         ContinueRequest(*it);
252       pending_requests_.clear();
253     }
254   }
255 
ContinueRequest(scoped_refptr<CefResourceManager::Request> request)256   bool ContinueRequest(scoped_refptr<CefResourceManager::Request> request) {
257     CefRefPtr<CefResourceHandler> handler;
258 
259     // |archive_| will be NULL if the archive file failed to load or was empty.
260     if (archive_.get()) {
261       const std::string& url = request->url();
262       const std::string& relative_path = url.substr(url_path_.length());
263       CefRefPtr<CefZipArchive::File> file = archive_->GetFile(relative_path);
264       if (file.get()) {
265         handler = new CefStreamResourceHandler(
266             request->mime_type_resolver().Run(url), file->GetStreamReader());
267       }
268     }
269 
270     if (!handler.get())
271       return false;
272 
273     request->Continue(handler);
274     return true;
275   }
276 
277   std::string url_path_;
278   std::string archive_path_;
279   std::string password_;
280 
281   bool archive_load_started_;
282   bool archive_load_ended_;
283   CefRefPtr<CefZipArchive> archive_;
284 
285   // List of requests that are pending while the archive is being loaded.
286   typedef std::vector<scoped_refptr<CefResourceManager::Request>>
287       PendingRequests;
288   PendingRequests pending_requests_;
289 
290   // Must be the last member.
291   base::WeakPtrFactory<ArchiveProvider> weak_ptr_factory_;
292 
293   DISALLOW_COPY_AND_ASSIGN(ArchiveProvider);
294 };
295 
296 }  // namespace
297 
298 // CefResourceManager::ProviderEntry implementation.
299 
300 struct CefResourceManager::ProviderEntry {
ProviderEntryCefResourceManager::ProviderEntry301   ProviderEntry(Provider* provider, int order, const std::string& identifier)
302       : provider_(provider),
303         order_(order),
304         identifier_(identifier),
305         deletion_pending_(false) {}
306 
307   scoped_ptr<Provider> provider_;
308   int order_;
309   std::string identifier_;
310 
311   // List of pending requests currently associated with this provider.
312   RequestList pending_requests_;
313 
314   // True if deletion of this provider is pending.
315   bool deletion_pending_;
316 };
317 
318 // CefResourceManager::RequestState implementation.
319 
~RequestState()320 CefResourceManager::RequestState::~RequestState() {
321   // Always execute the callback.
322   if (callback_.get())
323     callback_->Continue(true);
324 }
325 
326 // CefResourceManager::Request implementation.
327 
Continue(CefRefPtr<CefResourceHandler> handler)328 void CefResourceManager::Request::Continue(
329     CefRefPtr<CefResourceHandler> handler) {
330   if (!CefCurrentlyOn(TID_IO)) {
331     CefPostTask(TID_IO, base::Bind(&CefResourceManager::Request::Continue, this,
332                                    handler));
333     return;
334   }
335 
336   if (!state_.get())
337     return;
338 
339   // Disassociate |state_| immediately so that Provider::OnRequestCanceled is
340   // not called unexpectedly if Provider::OnRequest calls this method and then
341   // calls CefResourceManager::Remove*.
342   CefPostTask(TID_IO,
343               base::Bind(&CefResourceManager::Request::ContinueOnIOThread,
344                          base::Passed(&state_), handler));
345 }
346 
Stop()347 void CefResourceManager::Request::Stop() {
348   if (!CefCurrentlyOn(TID_IO)) {
349     CefPostTask(TID_IO, base::Bind(&CefResourceManager::Request::Stop, this));
350     return;
351   }
352 
353   if (!state_.get())
354     return;
355 
356   // Disassociate |state_| immediately so that Provider::OnRequestCanceled is
357   // not called unexpectedly if Provider::OnRequest calls this method and then
358   // calls CefResourceManager::Remove*.
359   CefPostTask(TID_IO, base::Bind(&CefResourceManager::Request::StopOnIOThread,
360                                  base::Passed(&state_)));
361 }
362 
Request(scoped_ptr<RequestState> state)363 CefResourceManager::Request::Request(scoped_ptr<RequestState> state)
364     : state_(state.Pass()), params_(state_->params_) {
365   CEF_REQUIRE_IO_THREAD();
366 
367   ProviderEntry* entry = *(state_->current_entry_pos_);
368   // Should not be on a deleted entry.
369   DCHECK(!entry->deletion_pending_);
370 
371   // Add this request to the entry's pending request list.
372   entry->pending_requests_.push_back(this);
373   state_->current_request_pos_ = --entry->pending_requests_.end();
374 }
375 
376 // Detaches and returns |state_| if the provider indicates that it will not
377 // handle the request. Note that |state_| may already be NULL if OnRequest
378 // executes a callback before returning, in which case execution will continue
379 // asynchronously in any case.
380 scoped_ptr<CefResourceManager::RequestState>
SendRequest()381 CefResourceManager::Request::SendRequest() {
382   CEF_REQUIRE_IO_THREAD();
383   Provider* provider = (*state_->current_entry_pos_)->provider_.get();
384   if (!provider->OnRequest(this))
385     return state_.Pass();
386   return scoped_ptr<RequestState>();
387 }
388 
HasState()389 bool CefResourceManager::Request::HasState() {
390   CEF_REQUIRE_IO_THREAD();
391   return (state_.get() != nullptr);
392 }
393 
394 // static
ContinueOnIOThread(scoped_ptr<RequestState> state,CefRefPtr<CefResourceHandler> handler)395 void CefResourceManager::Request::ContinueOnIOThread(
396     scoped_ptr<RequestState> state,
397     CefRefPtr<CefResourceHandler> handler) {
398   CEF_REQUIRE_IO_THREAD();
399   // The manager may already have been deleted.
400   base::WeakPtr<CefResourceManager> manager = state->manager_;
401   if (manager)
402     manager->ContinueRequest(state.Pass(), handler);
403 }
404 
405 // static
StopOnIOThread(scoped_ptr<RequestState> state)406 void CefResourceManager::Request::StopOnIOThread(
407     scoped_ptr<RequestState> state) {
408   CEF_REQUIRE_IO_THREAD();
409   // The manager may already have been deleted.
410   base::WeakPtr<CefResourceManager> manager = state->manager_;
411   if (manager)
412     manager->StopRequest(state.Pass());
413 }
414 
415 // CefResourceManager implementation.
416 
CefResourceManager()417 CefResourceManager::CefResourceManager()
418     : url_filter_(base::Bind(GetFilteredUrl)),
419       mime_type_resolver_(base::Bind(GetMimeType)) {}
420 
~CefResourceManager()421 CefResourceManager::~CefResourceManager() {
422   CEF_REQUIRE_IO_THREAD();
423   RemoveAllProviders();
424 
425   // Delete all entryies now. Requests may still be pending but they will not
426   // call back into this manager due to the use of WeakPtr.
427   if (!providers_.empty()) {
428     ProviderEntryList::iterator it = providers_.begin();
429     for (; it != providers_.end(); ++it)
430       delete *it;
431     providers_.clear();
432   }
433 }
434 
AddContentProvider(const std::string & url,const std::string & content,const std::string & mime_type,int order,const std::string & identifier)435 void CefResourceManager::AddContentProvider(const std::string& url,
436                                             const std::string& content,
437                                             const std::string& mime_type,
438                                             int order,
439                                             const std::string& identifier) {
440   AddProvider(new ContentProvider(url, content, mime_type), order, identifier);
441 }
442 
AddDirectoryProvider(const std::string & url_path,const std::string & directory_path,int order,const std::string & identifier)443 void CefResourceManager::AddDirectoryProvider(const std::string& url_path,
444                                               const std::string& directory_path,
445                                               int order,
446                                               const std::string& identifier) {
447   AddProvider(new DirectoryProvider(url_path, directory_path), order,
448               identifier);
449 }
450 
AddArchiveProvider(const std::string & url_path,const std::string & archive_path,const std::string & password,int order,const std::string & identifier)451 void CefResourceManager::AddArchiveProvider(const std::string& url_path,
452                                             const std::string& archive_path,
453                                             const std::string& password,
454                                             int order,
455                                             const std::string& identifier) {
456   AddProvider(new ArchiveProvider(url_path, archive_path, password), order,
457               identifier);
458 }
459 
AddProvider(Provider * provider,int order,const std::string & identifier)460 void CefResourceManager::AddProvider(Provider* provider,
461                                      int order,
462                                      const std::string& identifier) {
463   DCHECK(provider);
464   if (!provider)
465     return;
466 
467   if (!CefCurrentlyOn(TID_IO)) {
468     CefPostTask(TID_IO, base::Bind(&CefResourceManager::AddProvider, this,
469                                    provider, order, identifier));
470     return;
471   }
472 
473   scoped_ptr<ProviderEntry> new_entry(
474       new ProviderEntry(provider, order, identifier));
475 
476   if (providers_.empty()) {
477     providers_.push_back(new_entry.release());
478     return;
479   }
480 
481   // Insert before the first entry with a higher |order| value.
482   ProviderEntryList::iterator it = providers_.begin();
483   for (; it != providers_.end(); ++it) {
484     if ((*it)->order_ > order)
485       break;
486   }
487 
488   providers_.insert(it, new_entry.release());
489 }
490 
RemoveProviders(const std::string & identifier)491 void CefResourceManager::RemoveProviders(const std::string& identifier) {
492   if (!CefCurrentlyOn(TID_IO)) {
493     CefPostTask(TID_IO, base::Bind(&CefResourceManager::RemoveProviders, this,
494                                    identifier));
495     return;
496   }
497 
498   if (providers_.empty())
499     return;
500 
501   ProviderEntryList::iterator it = providers_.begin();
502   while (it != providers_.end()) {
503     if ((*it)->identifier_ == identifier)
504       DeleteProvider(it, false);
505     else
506       ++it;
507   }
508 }
509 
RemoveAllProviders()510 void CefResourceManager::RemoveAllProviders() {
511   if (!CefCurrentlyOn(TID_IO)) {
512     CefPostTask(TID_IO,
513                 base::Bind(&CefResourceManager::RemoveAllProviders, this));
514     return;
515   }
516 
517   if (providers_.empty())
518     return;
519 
520   ProviderEntryList::iterator it = providers_.begin();
521   while (it != providers_.end())
522     DeleteProvider(it, true);
523 }
524 
SetMimeTypeResolver(const MimeTypeResolver & resolver)525 void CefResourceManager::SetMimeTypeResolver(const MimeTypeResolver& resolver) {
526   if (!CefCurrentlyOn(TID_IO)) {
527     CefPostTask(TID_IO, base::Bind(&CefResourceManager::SetMimeTypeResolver,
528                                    this, resolver));
529     return;
530   }
531 
532   if (!resolver.is_null())
533     mime_type_resolver_ = resolver;
534   else
535     mime_type_resolver_ = base::Bind(GetMimeType);
536 }
537 
SetUrlFilter(const UrlFilter & filter)538 void CefResourceManager::SetUrlFilter(const UrlFilter& filter) {
539   if (!CefCurrentlyOn(TID_IO)) {
540     CefPostTask(TID_IO,
541                 base::Bind(&CefResourceManager::SetUrlFilter, this, filter));
542     return;
543   }
544 
545   if (!filter.is_null())
546     url_filter_ = filter;
547   else
548     url_filter_ = base::Bind(GetFilteredUrl);
549 }
550 
OnBeforeResourceLoad(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefRequest> request,CefRefPtr<CefRequestCallback> callback)551 cef_return_value_t CefResourceManager::OnBeforeResourceLoad(
552     CefRefPtr<CefBrowser> browser,
553     CefRefPtr<CefFrame> frame,
554     CefRefPtr<CefRequest> request,
555     CefRefPtr<CefRequestCallback> callback) {
556   CEF_REQUIRE_IO_THREAD();
557 
558   // Find the first provider that is not pending deletion.
559   ProviderEntryList::iterator current_entry_pos = providers_.begin();
560   GetNextValidProvider(current_entry_pos);
561 
562   if (current_entry_pos == providers_.end()) {
563     // No providers so continue the request immediately.
564     return RV_CONTINUE;
565   }
566 
567   scoped_ptr<RequestState> state(new RequestState);
568 
569   if (!weak_ptr_factory_.get()) {
570     // WeakPtrFactory instances need to be created and destroyed on the same
571     // thread. This object performs most of its work on the IO thread and will
572     // be destroyed on the IO thread so, now that we're on the IO thread,
573     // properly initialize the WeakPtrFactory.
574     weak_ptr_factory_.reset(new base::WeakPtrFactory<CefResourceManager>(this));
575   }
576 
577   state->manager_ = weak_ptr_factory_->GetWeakPtr();
578   state->callback_ = callback;
579 
580   state->params_.url_ =
581       GetUrlWithoutQueryOrFragment(url_filter_.Run(request->GetURL()));
582   state->params_.browser_ = browser;
583   state->params_.frame_ = frame;
584   state->params_.request_ = request;
585   state->params_.url_filter_ = url_filter_;
586   state->params_.mime_type_resolver_ = mime_type_resolver_;
587 
588   state->current_entry_pos_ = current_entry_pos;
589 
590   // If the request is potentially handled we need to continue asynchronously.
591   return SendRequest(state.Pass()) ? RV_CONTINUE_ASYNC : RV_CONTINUE;
592 }
593 
GetResourceHandler(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefRequest> request)594 CefRefPtr<CefResourceHandler> CefResourceManager::GetResourceHandler(
595     CefRefPtr<CefBrowser> browser,
596     CefRefPtr<CefFrame> frame,
597     CefRefPtr<CefRequest> request) {
598   CEF_REQUIRE_IO_THREAD();
599 
600   if (pending_handlers_.empty())
601     return nullptr;
602 
603   CefRefPtr<CefResourceHandler> handler;
604 
605   PendingHandlersMap::iterator it =
606       pending_handlers_.find(request->GetIdentifier());
607   if (it != pending_handlers_.end()) {
608     handler = it->second;
609     pending_handlers_.erase(it);
610   }
611 
612   return handler;
613 }
614 
615 // Send the request to providers in order until one potentially handles it or we
616 // run out of providers. Returns true if the request is potentially handled.
SendRequest(scoped_ptr<RequestState> state)617 bool CefResourceManager::SendRequest(scoped_ptr<RequestState> state) {
618   bool potentially_handled = false;
619 
620   do {
621     // Should not be on the last provider entry.
622     DCHECK(state->current_entry_pos_ != providers_.end());
623     scoped_refptr<Request> request = new Request(state.Pass());
624 
625     // Give the provider an opportunity to handle the request.
626     state = request->SendRequest();
627     if (state.get()) {
628       // The provider will not handle the request. Move to the next provider if
629       // any.
630       if (!IncrementProvider(state.get()))
631         StopRequest(state.Pass());
632     } else {
633       potentially_handled = true;
634     }
635   } while (state.get());
636 
637   return potentially_handled;
638 }
639 
ContinueRequest(scoped_ptr<RequestState> state,CefRefPtr<CefResourceHandler> handler)640 void CefResourceManager::ContinueRequest(
641     scoped_ptr<RequestState> state,
642     CefRefPtr<CefResourceHandler> handler) {
643   CEF_REQUIRE_IO_THREAD();
644 
645   if (handler.get()) {
646     // The request has been handled. Associate the request ID with the handler.
647     pending_handlers_.insert(
648         std::make_pair(state->params_.request_->GetIdentifier(), handler));
649     StopRequest(state.Pass());
650   } else {
651     // Move to the next provider if any.
652     if (IncrementProvider(state.get()))
653       SendRequest(state.Pass());
654     else
655       StopRequest(state.Pass());
656   }
657 }
658 
StopRequest(scoped_ptr<RequestState> state)659 void CefResourceManager::StopRequest(scoped_ptr<RequestState> state) {
660   CEF_REQUIRE_IO_THREAD();
661 
662   // Detach from the current provider.
663   DetachRequestFromProvider(state.get());
664 
665   // Delete the state object and execute the callback.
666   state.reset();
667 }
668 
669 // Move state to the next provider if any and return true if there are more
670 // providers.
IncrementProvider(RequestState * state)671 bool CefResourceManager::IncrementProvider(RequestState* state) {
672   // Identify the next provider.
673   ProviderEntryList::iterator next_entry_pos = state->current_entry_pos_;
674   GetNextValidProvider(++next_entry_pos);
675 
676   // Detach from the current provider.
677   DetachRequestFromProvider(state);
678 
679   if (next_entry_pos != providers_.end()) {
680     // Update the state to reference the new provider entry.
681     state->current_entry_pos_ = next_entry_pos;
682     return true;
683   }
684 
685   return false;
686 }
687 
688 // The new provider, if any, should be determined before calling this method.
DetachRequestFromProvider(RequestState * state)689 void CefResourceManager::DetachRequestFromProvider(RequestState* state) {
690   if (state->current_entry_pos_ != providers_.end()) {
691     // Remove the association from the current provider entry.
692     ProviderEntryList::iterator current_entry_pos = state->current_entry_pos_;
693     ProviderEntry* current_entry = *(current_entry_pos);
694     current_entry->pending_requests_.erase(state->current_request_pos_);
695 
696     if (current_entry->deletion_pending_ &&
697         current_entry->pending_requests_.empty()) {
698       // Delete the current provider entry now.
699       providers_.erase(current_entry_pos);
700       delete current_entry;
701     }
702 
703     // Set to the end for error checking purposes.
704     state->current_entry_pos_ = providers_.end();
705   }
706 }
707 
708 // Move to the next provider that is not pending deletion.
GetNextValidProvider(ProviderEntryList::iterator & iterator)709 void CefResourceManager::GetNextValidProvider(
710     ProviderEntryList::iterator& iterator) {
711   while (iterator != providers_.end() && (*iterator)->deletion_pending_) {
712     ++iterator;
713   }
714 }
715 
DeleteProvider(ProviderEntryList::iterator & iterator,bool stop)716 void CefResourceManager::DeleteProvider(ProviderEntryList::iterator& iterator,
717                                         bool stop) {
718   CEF_REQUIRE_IO_THREAD();
719 
720   ProviderEntry* current_entry = *(iterator);
721 
722   if (current_entry->deletion_pending_)
723     return;
724 
725   if (!current_entry->pending_requests_.empty()) {
726     // Don't delete the provider entry until all pending requests have cleared.
727     current_entry->deletion_pending_ = true;
728 
729     // Continue pending requests immediately.
730     RequestList::iterator it = current_entry->pending_requests_.begin();
731     for (; it != current_entry->pending_requests_.end(); ++it) {
732       const scoped_refptr<Request>& request = *it;
733       if (request->HasState()) {
734         if (stop)
735           request->Stop();
736         else
737           request->Continue(nullptr);
738         current_entry->provider_->OnRequestCanceled(request);
739       }
740     }
741 
742     ++iterator;
743   } else {
744     // Delete the provider entry now.
745     iterator = providers_.erase(iterator);
746     delete current_entry;
747   }
748 }
749