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