| // Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights |
| // reserved. Use of this source code is governed by a BSD-style license that |
| // can be found in the LICENSE file. |
| |
| #include "include/wrapper/cef_resource_manager.h" |
| |
| #include <algorithm> |
| #include <vector> |
| |
| #include "include/base/cef_macros.h" |
| #include "include/base/cef_weak_ptr.h" |
| #include "include/cef_parser.h" |
| #include "include/wrapper/cef_stream_resource_handler.h" |
| #include "include/wrapper/cef_zip_archive.h" |
| |
| namespace { |
| |
| #if defined(OS_WIN) |
| #define PATH_SEP '\\' |
| #else |
| #define PATH_SEP '/' |
| #endif |
| |
| // Returns |url| without the query or fragment components, if any. |
| std::string GetUrlWithoutQueryOrFragment(const std::string& url) { |
| // Find the first instance of '?' or '#'. |
| const size_t pos = std::min(url.find('?'), url.find('#')); |
| if (pos != std::string::npos) |
| return url.substr(0, pos); |
| |
| return url; |
| } |
| |
| // Determine the mime type based on the |url| file extension. |
| std::string GetMimeType(const std::string& url) { |
| std::string mime_type; |
| const std::string& url_without_query = GetUrlWithoutQueryOrFragment(url); |
| size_t sep = url_without_query.find_last_of("."); |
| if (sep != std::string::npos) { |
| mime_type = CefGetMimeType(url_without_query.substr(sep + 1)); |
| if (!mime_type.empty()) |
| return mime_type; |
| } |
| return "text/html"; |
| } |
| |
| // Default no-op filter. |
| std::string GetFilteredUrl(const std::string& url) { |
| return url; |
| } |
| |
| // Provider of fixed contents. |
| class ContentProvider : public CefResourceManager::Provider { |
| public: |
| ContentProvider(const std::string& url, |
| const std::string& content, |
| const std::string& mime_type) |
| : url_(url), content_(content), mime_type_(mime_type) { |
| DCHECK(!url.empty()); |
| DCHECK(!content.empty()); |
| } |
| |
| bool OnRequest(scoped_refptr<CefResourceManager::Request> request) OVERRIDE { |
| CEF_REQUIRE_IO_THREAD(); |
| |
| const std::string& url = request->url(); |
| if (url != url_) { |
| // Not handled by this provider. |
| return false; |
| } |
| |
| CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData( |
| static_cast<void*>(const_cast<char*>(content_.data())), |
| content_.length()); |
| |
| // Determine the mime type a single time if it isn't already set. |
| if (mime_type_.empty()) |
| mime_type_ = request->mime_type_resolver().Run(url); |
| |
| request->Continue(new CefStreamResourceHandler(mime_type_, stream)); |
| return true; |
| } |
| |
| private: |
| std::string url_; |
| std::string content_; |
| std::string mime_type_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ContentProvider); |
| }; |
| |
| // Provider of contents loaded from a directory on the file system. |
| class DirectoryProvider : public CefResourceManager::Provider { |
| public: |
| DirectoryProvider(const std::string& url_path, |
| const std::string& directory_path) |
| : url_path_(url_path), directory_path_(directory_path) { |
| DCHECK(!url_path_.empty()); |
| DCHECK(!directory_path_.empty()); |
| |
| // Normalize the path values. |
| if (url_path_[url_path_.size() - 1] != '/') |
| url_path_ += '/'; |
| if (directory_path_[directory_path_.size() - 1] != PATH_SEP) |
| directory_path_ += PATH_SEP; |
| } |
| |
| bool OnRequest(scoped_refptr<CefResourceManager::Request> request) OVERRIDE { |
| CEF_REQUIRE_IO_THREAD(); |
| |
| const std::string& url = request->url(); |
| if (url.find(url_path_) != 0U) { |
| return false; |
| } |
| |
| const std::string& file_path = GetFilePath(url); |
| |
| // Open |file_path| on the FILE thread. |
| CefPostTask(TID_FILE, base::Bind(&DirectoryProvider::OpenOnFileThread, |
| file_path, request)); |
| |
| return true; |
| } |
| |
| private: |
| std::string GetFilePath(const std::string& url) { |
| std::string path_part = url.substr(url_path_.length()); |
| #if defined(OS_WIN) |
| std::replace(path_part.begin(), path_part.end(), '/', '\\'); |
| #endif |
| return directory_path_ + path_part; |
| } |
| |
| static void OpenOnFileThread( |
| const std::string& file_path, |
| scoped_refptr<CefResourceManager::Request> request) { |
| CEF_REQUIRE_FILE_THREAD(); |
| |
| CefRefPtr<CefStreamReader> stream = |
| CefStreamReader::CreateForFile(file_path); |
| |
| // Continue loading on the IO thread. |
| CefPostTask(TID_IO, base::Bind(&DirectoryProvider::ContinueOpenOnIOThread, |
| request, stream)); |
| } |
| |
| static void ContinueOpenOnIOThread( |
| scoped_refptr<CefResourceManager::Request> request, |
| CefRefPtr<CefStreamReader> stream) { |
| CEF_REQUIRE_IO_THREAD(); |
| |
| CefRefPtr<CefStreamResourceHandler> handler; |
| if (stream.get()) { |
| handler = new CefStreamResourceHandler( |
| request->mime_type_resolver().Run(request->url()), stream); |
| } |
| request->Continue(handler); |
| } |
| |
| std::string url_path_; |
| std::string directory_path_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DirectoryProvider); |
| }; |
| |
| // Provider of contents loaded from an archive file. |
| class ArchiveProvider : public CefResourceManager::Provider { |
| public: |
| ArchiveProvider(const std::string& url_path, |
| const std::string& archive_path, |
| const std::string& password) |
| : url_path_(url_path), |
| archive_path_(archive_path), |
| password_(password), |
| archive_load_started_(false), |
| archive_load_ended_(false), |
| ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { |
| DCHECK(!url_path_.empty()); |
| DCHECK(!archive_path_.empty()); |
| |
| // Normalize the path values. |
| if (url_path_[url_path_.size() - 1] != '/') |
| url_path_ += '/'; |
| } |
| |
| bool OnRequest(scoped_refptr<CefResourceManager::Request> request) OVERRIDE { |
| CEF_REQUIRE_IO_THREAD(); |
| |
| const std::string& url = request->url(); |
| if (url.find(url_path_) != 0U) { |
| // Not handled by this provider. |
| return false; |
| } |
| |
| if (!archive_load_started_) { |
| // Initiate archive loading and queue the pending request. |
| archive_load_started_ = true; |
| pending_requests_.push_back(request); |
| |
| // Load the archive file on the FILE thread. |
| CefPostTask(TID_FILE, base::Bind(&ArchiveProvider::LoadOnFileThread, |
| weak_ptr_factory_.GetWeakPtr(), |
| archive_path_, password_)); |
| return true; |
| } |
| |
| if (archive_load_started_ && !archive_load_ended_) { |
| // The archive load has already started. Queue the pending request. |
| pending_requests_.push_back(request); |
| return true; |
| } |
| |
| // Archive loading is done. |
| return ContinueRequest(request); |
| } |
| |
| private: |
| static void LoadOnFileThread(base::WeakPtr<ArchiveProvider> ptr, |
| const std::string& archive_path, |
| const std::string& password) { |
| CEF_REQUIRE_FILE_THREAD(); |
| |
| CefRefPtr<CefZipArchive> archive; |
| |
| CefRefPtr<CefStreamReader> stream = |
| CefStreamReader::CreateForFile(archive_path); |
| if (stream.get()) { |
| archive = new CefZipArchive; |
| if (archive->Load(stream, password, true) == 0) { |
| DLOG(WARNING) << "Empty archive file: " << archive_path; |
| archive = nullptr; |
| } |
| } else { |
| DLOG(WARNING) << "Failed to load archive file: " << archive_path; |
| } |
| |
| CefPostTask(TID_IO, |
| base::Bind(&ArchiveProvider::ContinueOnIOThread, ptr, archive)); |
| } |
| |
| void ContinueOnIOThread(CefRefPtr<CefZipArchive> archive) { |
| CEF_REQUIRE_IO_THREAD(); |
| |
| archive_load_ended_ = true; |
| archive_ = archive; |
| |
| if (!pending_requests_.empty()) { |
| // Continue all pending requests. |
| PendingRequests::const_iterator it = pending_requests_.begin(); |
| for (; it != pending_requests_.end(); ++it) |
| ContinueRequest(*it); |
| pending_requests_.clear(); |
| } |
| } |
| |
| bool ContinueRequest(scoped_refptr<CefResourceManager::Request> request) { |
| CefRefPtr<CefResourceHandler> handler; |
| |
| // |archive_| will be NULL if the archive file failed to load or was empty. |
| if (archive_.get()) { |
| const std::string& url = request->url(); |
| const std::string& relative_path = url.substr(url_path_.length()); |
| CefRefPtr<CefZipArchive::File> file = archive_->GetFile(relative_path); |
| if (file.get()) { |
| handler = new CefStreamResourceHandler( |
| request->mime_type_resolver().Run(url), file->GetStreamReader()); |
| } |
| } |
| |
| if (!handler.get()) |
| return false; |
| |
| request->Continue(handler); |
| return true; |
| } |
| |
| std::string url_path_; |
| std::string archive_path_; |
| std::string password_; |
| |
| bool archive_load_started_; |
| bool archive_load_ended_; |
| CefRefPtr<CefZipArchive> archive_; |
| |
| // List of requests that are pending while the archive is being loaded. |
| typedef std::vector<scoped_refptr<CefResourceManager::Request>> |
| PendingRequests; |
| PendingRequests pending_requests_; |
| |
| // Must be the last member. |
| base::WeakPtrFactory<ArchiveProvider> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ArchiveProvider); |
| }; |
| |
| } // namespace |
| |
| // CefResourceManager::ProviderEntry implementation. |
| |
| struct CefResourceManager::ProviderEntry { |
| ProviderEntry(Provider* provider, int order, const std::string& identifier) |
| : provider_(provider), |
| order_(order), |
| identifier_(identifier), |
| deletion_pending_(false) {} |
| |
| scoped_ptr<Provider> provider_; |
| int order_; |
| std::string identifier_; |
| |
| // List of pending requests currently associated with this provider. |
| RequestList pending_requests_; |
| |
| // True if deletion of this provider is pending. |
| bool deletion_pending_; |
| }; |
| |
| // CefResourceManager::RequestState implementation. |
| |
| CefResourceManager::RequestState::~RequestState() { |
| // Always execute the callback. |
| if (callback_.get()) |
| callback_->Continue(true); |
| } |
| |
| // CefResourceManager::Request implementation. |
| |
| void CefResourceManager::Request::Continue( |
| CefRefPtr<CefResourceHandler> handler) { |
| if (!CefCurrentlyOn(TID_IO)) { |
| CefPostTask(TID_IO, base::Bind(&CefResourceManager::Request::Continue, this, |
| handler)); |
| return; |
| } |
| |
| if (!state_.get()) |
| return; |
| |
| // Disassociate |state_| immediately so that Provider::OnRequestCanceled is |
| // not called unexpectedly if Provider::OnRequest calls this method and then |
| // calls CefResourceManager::Remove*. |
| CefPostTask(TID_IO, |
| base::Bind(&CefResourceManager::Request::ContinueOnIOThread, |
| base::Passed(&state_), handler)); |
| } |
| |
| void CefResourceManager::Request::Stop() { |
| if (!CefCurrentlyOn(TID_IO)) { |
| CefPostTask(TID_IO, base::Bind(&CefResourceManager::Request::Stop, this)); |
| return; |
| } |
| |
| if (!state_.get()) |
| return; |
| |
| // Disassociate |state_| immediately so that Provider::OnRequestCanceled is |
| // not called unexpectedly if Provider::OnRequest calls this method and then |
| // calls CefResourceManager::Remove*. |
| CefPostTask(TID_IO, base::Bind(&CefResourceManager::Request::StopOnIOThread, |
| base::Passed(&state_))); |
| } |
| |
| CefResourceManager::Request::Request(scoped_ptr<RequestState> state) |
| : state_(state.Pass()), params_(state_->params_) { |
| CEF_REQUIRE_IO_THREAD(); |
| |
| ProviderEntry* entry = *(state_->current_entry_pos_); |
| // Should not be on a deleted entry. |
| DCHECK(!entry->deletion_pending_); |
| |
| // Add this request to the entry's pending request list. |
| entry->pending_requests_.push_back(this); |
| state_->current_request_pos_ = --entry->pending_requests_.end(); |
| } |
| |
| // Detaches and returns |state_| if the provider indicates that it will not |
| // handle the request. Note that |state_| may already be NULL if OnRequest |
| // executes a callback before returning, in which case execution will continue |
| // asynchronously in any case. |
| scoped_ptr<CefResourceManager::RequestState> |
| CefResourceManager::Request::SendRequest() { |
| CEF_REQUIRE_IO_THREAD(); |
| Provider* provider = (*state_->current_entry_pos_)->provider_.get(); |
| if (!provider->OnRequest(this)) |
| return state_.Pass(); |
| return scoped_ptr<RequestState>(); |
| } |
| |
| bool CefResourceManager::Request::HasState() { |
| CEF_REQUIRE_IO_THREAD(); |
| return (state_.get() != nullptr); |
| } |
| |
| // static |
| void CefResourceManager::Request::ContinueOnIOThread( |
| scoped_ptr<RequestState> state, |
| CefRefPtr<CefResourceHandler> handler) { |
| CEF_REQUIRE_IO_THREAD(); |
| // The manager may already have been deleted. |
| base::WeakPtr<CefResourceManager> manager = state->manager_; |
| if (manager) |
| manager->ContinueRequest(state.Pass(), handler); |
| } |
| |
| // static |
| void CefResourceManager::Request::StopOnIOThread( |
| scoped_ptr<RequestState> state) { |
| CEF_REQUIRE_IO_THREAD(); |
| // The manager may already have been deleted. |
| base::WeakPtr<CefResourceManager> manager = state->manager_; |
| if (manager) |
| manager->StopRequest(state.Pass()); |
| } |
| |
| // CefResourceManager implementation. |
| |
| CefResourceManager::CefResourceManager() |
| : url_filter_(base::Bind(GetFilteredUrl)), |
| mime_type_resolver_(base::Bind(GetMimeType)) {} |
| |
| CefResourceManager::~CefResourceManager() { |
| CEF_REQUIRE_IO_THREAD(); |
| RemoveAllProviders(); |
| |
| // Delete all entryies now. Requests may still be pending but they will not |
| // call back into this manager due to the use of WeakPtr. |
| if (!providers_.empty()) { |
| ProviderEntryList::iterator it = providers_.begin(); |
| for (; it != providers_.end(); ++it) |
| delete *it; |
| providers_.clear(); |
| } |
| } |
| |
| void CefResourceManager::AddContentProvider(const std::string& url, |
| const std::string& content, |
| const std::string& mime_type, |
| int order, |
| const std::string& identifier) { |
| AddProvider(new ContentProvider(url, content, mime_type), order, identifier); |
| } |
| |
| void CefResourceManager::AddDirectoryProvider(const std::string& url_path, |
| const std::string& directory_path, |
| int order, |
| const std::string& identifier) { |
| AddProvider(new DirectoryProvider(url_path, directory_path), order, |
| identifier); |
| } |
| |
| void CefResourceManager::AddArchiveProvider(const std::string& url_path, |
| const std::string& archive_path, |
| const std::string& password, |
| int order, |
| const std::string& identifier) { |
| AddProvider(new ArchiveProvider(url_path, archive_path, password), order, |
| identifier); |
| } |
| |
| void CefResourceManager::AddProvider(Provider* provider, |
| int order, |
| const std::string& identifier) { |
| DCHECK(provider); |
| if (!provider) |
| return; |
| |
| if (!CefCurrentlyOn(TID_IO)) { |
| CefPostTask(TID_IO, base::Bind(&CefResourceManager::AddProvider, this, |
| provider, order, identifier)); |
| return; |
| } |
| |
| scoped_ptr<ProviderEntry> new_entry( |
| new ProviderEntry(provider, order, identifier)); |
| |
| if (providers_.empty()) { |
| providers_.push_back(new_entry.release()); |
| return; |
| } |
| |
| // Insert before the first entry with a higher |order| value. |
| ProviderEntryList::iterator it = providers_.begin(); |
| for (; it != providers_.end(); ++it) { |
| if ((*it)->order_ > order) |
| break; |
| } |
| |
| providers_.insert(it, new_entry.release()); |
| } |
| |
| void CefResourceManager::RemoveProviders(const std::string& identifier) { |
| if (!CefCurrentlyOn(TID_IO)) { |
| CefPostTask(TID_IO, base::Bind(&CefResourceManager::RemoveProviders, this, |
| identifier)); |
| return; |
| } |
| |
| if (providers_.empty()) |
| return; |
| |
| ProviderEntryList::iterator it = providers_.begin(); |
| while (it != providers_.end()) { |
| if ((*it)->identifier_ == identifier) |
| DeleteProvider(it, false); |
| else |
| ++it; |
| } |
| } |
| |
| void CefResourceManager::RemoveAllProviders() { |
| if (!CefCurrentlyOn(TID_IO)) { |
| CefPostTask(TID_IO, |
| base::Bind(&CefResourceManager::RemoveAllProviders, this)); |
| return; |
| } |
| |
| if (providers_.empty()) |
| return; |
| |
| ProviderEntryList::iterator it = providers_.begin(); |
| while (it != providers_.end()) |
| DeleteProvider(it, true); |
| } |
| |
| void CefResourceManager::SetMimeTypeResolver(const MimeTypeResolver& resolver) { |
| if (!CefCurrentlyOn(TID_IO)) { |
| CefPostTask(TID_IO, base::Bind(&CefResourceManager::SetMimeTypeResolver, |
| this, resolver)); |
| return; |
| } |
| |
| if (!resolver.is_null()) |
| mime_type_resolver_ = resolver; |
| else |
| mime_type_resolver_ = base::Bind(GetMimeType); |
| } |
| |
| void CefResourceManager::SetUrlFilter(const UrlFilter& filter) { |
| if (!CefCurrentlyOn(TID_IO)) { |
| CefPostTask(TID_IO, |
| base::Bind(&CefResourceManager::SetUrlFilter, this, filter)); |
| return; |
| } |
| |
| if (!filter.is_null()) |
| url_filter_ = filter; |
| else |
| url_filter_ = base::Bind(GetFilteredUrl); |
| } |
| |
| cef_return_value_t CefResourceManager::OnBeforeResourceLoad( |
| CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| CefRefPtr<CefRequest> request, |
| CefRefPtr<CefRequestCallback> callback) { |
| CEF_REQUIRE_IO_THREAD(); |
| |
| // Find the first provider that is not pending deletion. |
| ProviderEntryList::iterator current_entry_pos = providers_.begin(); |
| GetNextValidProvider(current_entry_pos); |
| |
| if (current_entry_pos == providers_.end()) { |
| // No providers so continue the request immediately. |
| return RV_CONTINUE; |
| } |
| |
| scoped_ptr<RequestState> state(new RequestState); |
| |
| if (!weak_ptr_factory_.get()) { |
| // WeakPtrFactory instances need to be created and destroyed on the same |
| // thread. This object performs most of its work on the IO thread and will |
| // be destroyed on the IO thread so, now that we're on the IO thread, |
| // properly initialize the WeakPtrFactory. |
| weak_ptr_factory_.reset(new base::WeakPtrFactory<CefResourceManager>(this)); |
| } |
| |
| state->manager_ = weak_ptr_factory_->GetWeakPtr(); |
| state->callback_ = callback; |
| |
| state->params_.url_ = |
| GetUrlWithoutQueryOrFragment(url_filter_.Run(request->GetURL())); |
| state->params_.browser_ = browser; |
| state->params_.frame_ = frame; |
| state->params_.request_ = request; |
| state->params_.url_filter_ = url_filter_; |
| state->params_.mime_type_resolver_ = mime_type_resolver_; |
| |
| state->current_entry_pos_ = current_entry_pos; |
| |
| // If the request is potentially handled we need to continue asynchronously. |
| return SendRequest(state.Pass()) ? RV_CONTINUE_ASYNC : RV_CONTINUE; |
| } |
| |
| CefRefPtr<CefResourceHandler> CefResourceManager::GetResourceHandler( |
| CefRefPtr<CefBrowser> browser, |
| CefRefPtr<CefFrame> frame, |
| CefRefPtr<CefRequest> request) { |
| CEF_REQUIRE_IO_THREAD(); |
| |
| if (pending_handlers_.empty()) |
| return nullptr; |
| |
| CefRefPtr<CefResourceHandler> handler; |
| |
| PendingHandlersMap::iterator it = |
| pending_handlers_.find(request->GetIdentifier()); |
| if (it != pending_handlers_.end()) { |
| handler = it->second; |
| pending_handlers_.erase(it); |
| } |
| |
| return handler; |
| } |
| |
| // Send the request to providers in order until one potentially handles it or we |
| // run out of providers. Returns true if the request is potentially handled. |
| bool CefResourceManager::SendRequest(scoped_ptr<RequestState> state) { |
| bool potentially_handled = false; |
| |
| do { |
| // Should not be on the last provider entry. |
| DCHECK(state->current_entry_pos_ != providers_.end()); |
| scoped_refptr<Request> request = new Request(state.Pass()); |
| |
| // Give the provider an opportunity to handle the request. |
| state = request->SendRequest(); |
| if (state.get()) { |
| // The provider will not handle the request. Move to the next provider if |
| // any. |
| if (!IncrementProvider(state.get())) |
| StopRequest(state.Pass()); |
| } else { |
| potentially_handled = true; |
| } |
| } while (state.get()); |
| |
| return potentially_handled; |
| } |
| |
| void CefResourceManager::ContinueRequest( |
| scoped_ptr<RequestState> state, |
| CefRefPtr<CefResourceHandler> handler) { |
| CEF_REQUIRE_IO_THREAD(); |
| |
| if (handler.get()) { |
| // The request has been handled. Associate the request ID with the handler. |
| pending_handlers_.insert( |
| std::make_pair(state->params_.request_->GetIdentifier(), handler)); |
| StopRequest(state.Pass()); |
| } else { |
| // Move to the next provider if any. |
| if (IncrementProvider(state.get())) |
| SendRequest(state.Pass()); |
| else |
| StopRequest(state.Pass()); |
| } |
| } |
| |
| void CefResourceManager::StopRequest(scoped_ptr<RequestState> state) { |
| CEF_REQUIRE_IO_THREAD(); |
| |
| // Detach from the current provider. |
| DetachRequestFromProvider(state.get()); |
| |
| // Delete the state object and execute the callback. |
| state.reset(); |
| } |
| |
| // Move state to the next provider if any and return true if there are more |
| // providers. |
| bool CefResourceManager::IncrementProvider(RequestState* state) { |
| // Identify the next provider. |
| ProviderEntryList::iterator next_entry_pos = state->current_entry_pos_; |
| GetNextValidProvider(++next_entry_pos); |
| |
| // Detach from the current provider. |
| DetachRequestFromProvider(state); |
| |
| if (next_entry_pos != providers_.end()) { |
| // Update the state to reference the new provider entry. |
| state->current_entry_pos_ = next_entry_pos; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // The new provider, if any, should be determined before calling this method. |
| void CefResourceManager::DetachRequestFromProvider(RequestState* state) { |
| if (state->current_entry_pos_ != providers_.end()) { |
| // Remove the association from the current provider entry. |
| ProviderEntryList::iterator current_entry_pos = state->current_entry_pos_; |
| ProviderEntry* current_entry = *(current_entry_pos); |
| current_entry->pending_requests_.erase(state->current_request_pos_); |
| |
| if (current_entry->deletion_pending_ && |
| current_entry->pending_requests_.empty()) { |
| // Delete the current provider entry now. |
| providers_.erase(current_entry_pos); |
| delete current_entry; |
| } |
| |
| // Set to the end for error checking purposes. |
| state->current_entry_pos_ = providers_.end(); |
| } |
| } |
| |
| // Move to the next provider that is not pending deletion. |
| void CefResourceManager::GetNextValidProvider( |
| ProviderEntryList::iterator& iterator) { |
| while (iterator != providers_.end() && (*iterator)->deletion_pending_) { |
| ++iterator; |
| } |
| } |
| |
| void CefResourceManager::DeleteProvider(ProviderEntryList::iterator& iterator, |
| bool stop) { |
| CEF_REQUIRE_IO_THREAD(); |
| |
| ProviderEntry* current_entry = *(iterator); |
| |
| if (current_entry->deletion_pending_) |
| return; |
| |
| if (!current_entry->pending_requests_.empty()) { |
| // Don't delete the provider entry until all pending requests have cleared. |
| current_entry->deletion_pending_ = true; |
| |
| // Continue pending requests immediately. |
| RequestList::iterator it = current_entry->pending_requests_.begin(); |
| for (; it != current_entry->pending_requests_.end(); ++it) { |
| const scoped_refptr<Request>& request = *it; |
| if (request->HasState()) { |
| if (stop) |
| request->Stop(); |
| else |
| request->Continue(nullptr); |
| current_entry->provider_->OnRequestCanceled(request); |
| } |
| } |
| |
| ++iterator; |
| } else { |
| // Delete the provider entry now. |
| iterator = providers_.erase(iterator); |
| delete current_entry; |
| } |
| } |