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