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