mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			749 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			749 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// 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 = NULL;
 | 
						|
      }
 | 
						|
    } 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() != NULL);
 | 
						|
}
 | 
						|
 | 
						|
// 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 NULL;
 | 
						|
 | 
						|
  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(NULL);
 | 
						|
        current_entry->provider_->OnRequestCanceled(request);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    ++iterator;
 | 
						|
  } else {
 | 
						|
    // Delete the provider entry now.
 | 
						|
    iterator = providers_.erase(iterator);
 | 
						|
    delete current_entry;
 | 
						|
  }
 | 
						|
}
 |