• 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