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