Add CefResourceManager class for mapping requests to data providers (issue #1640).
This commit is contained in:
parent
d02f03a71a
commit
b6e5310bce
2
cef.gyp
2
cef.gyp
|
@ -392,6 +392,7 @@
|
|||
'<(DEPTH)/testing/gtest.gyp:gtest',
|
||||
'<(DEPTH)/third_party/icu/icu.gyp:icui18n',
|
||||
'<(DEPTH)/third_party/icu/icu.gyp:icuuc',
|
||||
'<(DEPTH)/third_party/zlib/google/zip.gyp:zip',
|
||||
'<(DEPTH)/ui/base/ui_base.gyp:ui_base',
|
||||
'libcef_dll_wrapper',
|
||||
],
|
||||
|
@ -429,6 +430,7 @@
|
|||
'tests/unittests/request_context_unittest.cc',
|
||||
'tests/unittests/request_handler_unittest.cc',
|
||||
'tests/unittests/request_unittest.cc',
|
||||
'tests/unittests/resource_manager_unittest.cc',
|
||||
'tests/unittests/routing_test_handler.cc',
|
||||
'tests/unittests/routing_test_handler.h',
|
||||
'tests/unittests/run_all_unittests.cc',
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
'include/wrapper/cef_closure_task.h',
|
||||
'include/wrapper/cef_helpers.h',
|
||||
'include/wrapper/cef_message_router.h',
|
||||
'include/wrapper/cef_resource_manager.h',
|
||||
'include/wrapper/cef_stream_resource_handler.h',
|
||||
'include/wrapper/cef_xml_object.h',
|
||||
'include/wrapper/cef_zip_archive.h',
|
||||
|
@ -130,6 +131,7 @@
|
|||
'libcef_dll/wrapper/cef_byte_read_handler.cc',
|
||||
'libcef_dll/wrapper/cef_closure_task.cc',
|
||||
'libcef_dll/wrapper/cef_message_router.cc',
|
||||
'libcef_dll/wrapper/cef_resource_manager.cc',
|
||||
'libcef_dll/wrapper/cef_stream_resource_handler.cc',
|
||||
'libcef_dll/wrapper/cef_xml_object.cc',
|
||||
'libcef_dll/wrapper/cef_zip_archive.cc',
|
||||
|
|
|
@ -0,0 +1,372 @@
|
|||
// Copyright (c) 2015 Marshall A. Greenblatt. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the name Chromium Embedded
|
||||
// Framework nor the names of its contributors may be used to endorse
|
||||
// or promote products derived from this software without specific prior
|
||||
// written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// ---------------------------------------------------------------------------
|
||||
//
|
||||
// The contents of this file are only available to applications that link
|
||||
// against the libcef_dll_wrapper target.
|
||||
//
|
||||
|
||||
#ifndef CEF_INCLUDE_WRAPPER_CEF_RESOURCE_MANAGER_H_
|
||||
#define CEF_INCLUDE_WRAPPER_CEF_RESOURCE_MANAGER_H_
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "include/base/cef_macros.h"
|
||||
#include "include/base/cef_ref_counted.h"
|
||||
#include "include/base/cef_weak_ptr.h"
|
||||
#include "include/cef_request_handler.h"
|
||||
#include "include/wrapper/cef_closure_task.h"
|
||||
#include "include/wrapper/cef_helpers.h"
|
||||
|
||||
///
|
||||
// Class for managing multiple resource providers. For each resource request
|
||||
// providers will be called in order and have the option to (a) handle the
|
||||
// request by returning a CefResourceHandler, (b) pass the request to the next
|
||||
// provider in order, or (c) stop handling the request. See comments on the
|
||||
// Request object for additional usage information. The methods of this class
|
||||
// may be called on any browser process thread unless otherwise indicated.
|
||||
///
|
||||
class CefResourceManager :
|
||||
public base::RefCountedThreadSafe<CefResourceManager, CefDeleteOnIOThread> {
|
||||
public:
|
||||
///
|
||||
// Provides an opportunity to modify |url| before it is passed to a provider.
|
||||
// For example, the implementation could rewrite |url| to include a default
|
||||
// file extension. |url| will be fully qualified and may contain query or
|
||||
// fragment components.
|
||||
///
|
||||
typedef base::Callback<std::string(
|
||||
const std::string& /*url*/)> UrlFilter;
|
||||
|
||||
///
|
||||
// Used to resolve mime types for URLs, usually based on the file extension.
|
||||
// |url| will be fully qualified and may contain query or fragment components.
|
||||
///
|
||||
typedef base::Callback<std::string(
|
||||
const std::string& /*url*/)> MimeTypeResolver;
|
||||
|
||||
private:
|
||||
// Values that stay with a request as it moves between providers.
|
||||
struct RequestParams {
|
||||
std::string url_;
|
||||
CefRefPtr<CefBrowser> browser_;
|
||||
CefRefPtr<CefFrame> frame_;
|
||||
CefRefPtr<CefRequest> request_;
|
||||
UrlFilter url_filter_;
|
||||
MimeTypeResolver mime_type_resolver_;
|
||||
};
|
||||
|
||||
// Values that are associated with the pending request only.
|
||||
struct RequestState;
|
||||
|
||||
public:
|
||||
///
|
||||
// Object representing a request. Each request object is used for a single
|
||||
// call to Provider::OnRequest and will become detached (meaning the callbacks
|
||||
// will no longer trigger) after Request::Continue or Request::Stop is called.
|
||||
// A request passed to Provider::OnRequestCanceled will already have been
|
||||
// detached. The methods of this class may be called on any browser process
|
||||
// thread.
|
||||
///
|
||||
class Request : public base::RefCountedThreadSafe<Request> {
|
||||
public:
|
||||
///
|
||||
// Returns the URL associated with this request. The returned value will be
|
||||
// fully qualified but will not contain query or fragment components. It
|
||||
// will already have been passed through the URL filter.
|
||||
///
|
||||
std::string url() const { return params_.url_; }
|
||||
|
||||
///
|
||||
// Returns the CefBrowser associated with this request.
|
||||
///
|
||||
CefRefPtr<CefBrowser> browser() const { return params_.browser_; }
|
||||
|
||||
///
|
||||
// Returns the CefFrame associated with this request.
|
||||
///
|
||||
CefRefPtr<CefFrame> frame() const { return params_.frame_; }
|
||||
|
||||
///
|
||||
// Returns the CefRequest associated with this request.
|
||||
///
|
||||
CefRefPtr<CefRequest> request() const { return params_.request_; }
|
||||
|
||||
///
|
||||
// Returns the current URL filter.
|
||||
///
|
||||
const CefResourceManager::UrlFilter& url_filter() const {
|
||||
return params_.url_filter_;
|
||||
}
|
||||
|
||||
///
|
||||
// Returns the current mime type resolver.
|
||||
///
|
||||
const CefResourceManager::MimeTypeResolver& mime_type_resolver() const {
|
||||
return params_.mime_type_resolver_;
|
||||
}
|
||||
|
||||
///
|
||||
// Continue handling the request. If |handler| is non-NULL then no
|
||||
// additional providers will be called and the |handler| value will be
|
||||
// returned via CefResourceManager::GetResourceHandler. If |handler| is NULL
|
||||
// then the next provider in order, if any, will be called. If there are no
|
||||
// additional providers then NULL will be returned via CefResourceManager::
|
||||
// GetResourceHandler.
|
||||
///
|
||||
void Continue(CefRefPtr<CefResourceHandler> handler);
|
||||
|
||||
///
|
||||
// Stop handling the request. No additional providers will be called and
|
||||
// NULL will be returned via CefResourceManager::GetResourceHandler.
|
||||
///
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
// Only allow deletion via scoped_refptr.
|
||||
friend class base::RefCountedThreadSafe<Request>;
|
||||
|
||||
friend class CefResourceManager;
|
||||
|
||||
// The below methods are called on the browser process IO thread.
|
||||
|
||||
explicit Request(scoped_ptr<RequestState> state);
|
||||
|
||||
scoped_ptr<RequestState> SendRequest();
|
||||
bool HasState();
|
||||
|
||||
static void ContinueOnIOThread(scoped_ptr<RequestState> state,
|
||||
CefRefPtr<CefResourceHandler> handler);
|
||||
static void StopOnIOThread(scoped_ptr<RequestState> state);
|
||||
|
||||
// Will be non-NULL while the request is pending. Only accessed on the
|
||||
// browser process IO thread.
|
||||
scoped_ptr<RequestState> state_;
|
||||
|
||||
// Params that stay with this request object. Safe to access on any thread.
|
||||
RequestParams params_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Request);
|
||||
};
|
||||
|
||||
typedef std::list<scoped_refptr<Request> > RequestList;
|
||||
|
||||
|
||||
///
|
||||
// Interface implemented by resource providers. A provider may be created on
|
||||
// any thread but the methods will be called on, and the object will be
|
||||
// destroyed on, the browser process IO thread.
|
||||
///
|
||||
class Provider {
|
||||
public:
|
||||
///
|
||||
// Called to handle a request. If the provider knows immediately that it
|
||||
// will not handle the request return false. Otherwise, return true and call
|
||||
// Request::Continue or Request::Stop either in this method or
|
||||
// asynchronously to indicate completion. See comments on Request for
|
||||
// additional usage information.
|
||||
///
|
||||
virtual bool OnRequest(scoped_refptr<Request> request) =0;
|
||||
|
||||
///
|
||||
// Called when a request has been canceled. It is still safe to dereference
|
||||
// |request| but any calls to Request::Continue or Request::Stop will be
|
||||
// ignored.
|
||||
///
|
||||
virtual void OnRequestCanceled(scoped_refptr<Request> request) {}
|
||||
|
||||
virtual ~Provider() {}
|
||||
};
|
||||
|
||||
CefResourceManager();
|
||||
|
||||
///
|
||||
// Add a provider that maps requests for |url| to |content|. |url| should be
|
||||
// fully qualified but not include a query or fragment component. If
|
||||
// |mime_type| is empty the MimeTypeResolver will be used. See comments on
|
||||
// AddProvider for usage of the |order| and |identifier| parameters.
|
||||
///
|
||||
void AddContentProvider(const std::string& url,
|
||||
const std::string& content,
|
||||
const std::string& mime_type,
|
||||
int order,
|
||||
const std::string& identifier);
|
||||
|
||||
///
|
||||
// Add a provider that maps requests that start with |url_path| to files under
|
||||
// |directory_path|. |url_path| should include an origin and optional path
|
||||
// component only. Files will be loaded when a matching URL is requested.
|
||||
// See comments on AddProvider for usage of the |order| and |identifier|
|
||||
// parameters.
|
||||
///
|
||||
void AddDirectoryProvider(const std::string& url_path,
|
||||
const std::string& directory_path,
|
||||
int order,
|
||||
const std::string& identifier);
|
||||
|
||||
///
|
||||
// Add a provider that maps requests that start with |url_path| to files
|
||||
// stored in the archive file at |archive_path|. |url_path| should include an
|
||||
// origin and optional path component only. The archive file will be loaded
|
||||
// when a matching URL is requested for the first time. See comments on
|
||||
// AddProvider for usage of the |order| and |identifier| parameters.
|
||||
///
|
||||
void AddArchiveProvider(const std::string& url_path,
|
||||
const std::string& archive_path,
|
||||
const std::string& password,
|
||||
int order,
|
||||
const std::string& identifier);
|
||||
|
||||
///
|
||||
// Add a provider. This object takes ownership of |provider|. Providers will
|
||||
// be called in ascending order based on the |order| value. Multiple providers
|
||||
// sharing the same |order| value will be called in the order that they were
|
||||
// added. The |identifier| value, which does not need to be unique, can be
|
||||
// used to remove the provider at a later time.
|
||||
///
|
||||
void AddProvider(Provider* provider,
|
||||
int order,
|
||||
const std::string& identifier);
|
||||
|
||||
///
|
||||
// Remove all providers with the specified |identifier| value. If any removed
|
||||
// providers have pending requests the Provider::OnRequestCancel method will
|
||||
// be called. The removed providers may be deleted immediately or at a later
|
||||
// time.
|
||||
///
|
||||
void RemoveProviders(const std::string& identifier);
|
||||
|
||||
///
|
||||
// Remove all providers. If any removed providers have pending requests the
|
||||
// Provider::OnRequestCancel method will be called. The removed providers may
|
||||
// be deleted immediately or at a later time.
|
||||
///
|
||||
void RemoveAllProviders();
|
||||
|
||||
///
|
||||
// Set the url filter. If not set the default no-op filter will be used.
|
||||
// Changes to this value will not affect currently pending requests.
|
||||
///
|
||||
void SetUrlFilter(const UrlFilter& filter);
|
||||
|
||||
///
|
||||
// Set the mime type resolver. If not set the default resolver will be used.
|
||||
// Changes to this value will not affect currently pending requests.
|
||||
///
|
||||
void SetMimeTypeResolver(const MimeTypeResolver& resolver);
|
||||
|
||||
|
||||
// The below methods should be called from other CEF handlers. They must be
|
||||
// called exactly as documented for the manager to function correctly.
|
||||
|
||||
///
|
||||
// Called from CefRequestHandler::OnBeforeResourceLoad on the browser process
|
||||
// IO thread.
|
||||
///
|
||||
cef_return_value_t OnBeforeResourceLoad(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
CefRefPtr<CefRequest> request,
|
||||
CefRefPtr<CefRequestCallback> callback);
|
||||
|
||||
///
|
||||
// Called from CefRequestHandler::GetResourceHandler on the browser process
|
||||
// IO thread.
|
||||
///
|
||||
CefRefPtr<CefResourceHandler> GetResourceHandler(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
CefRefPtr<CefRequest> request);
|
||||
|
||||
private:
|
||||
// Only allow deletion via scoped_refptr.
|
||||
friend struct CefDeleteOnThread<TID_IO>;
|
||||
friend class base::RefCountedThreadSafe<CefResourceManager,
|
||||
CefDeleteOnIOThread>;
|
||||
|
||||
~CefResourceManager();
|
||||
|
||||
// Provider and associated information.
|
||||
struct ProviderEntry;
|
||||
typedef std::list<ProviderEntry*> ProviderEntryList;
|
||||
|
||||
// Values associated with the pending request only. Ownership will be passed
|
||||
// between requests and the resource manager as request handling proceeds.
|
||||
struct RequestState {
|
||||
~RequestState();
|
||||
|
||||
base::WeakPtr<CefResourceManager> manager_;
|
||||
|
||||
// Callback to execute once request handling is complete.
|
||||
CefRefPtr<CefRequestCallback> callback_;
|
||||
|
||||
// Position of the currently associated ProviderEntry in the |providers_|
|
||||
// list.
|
||||
ProviderEntryList::iterator current_entry_pos_;
|
||||
|
||||
// Position of this request object in the currently associated
|
||||
// ProviderEntry's |pending_requests_| list.
|
||||
RequestList::iterator current_request_pos_;
|
||||
|
||||
// Params that will be copied to each request object.
|
||||
RequestParams params_;
|
||||
};
|
||||
|
||||
// Methods that manage request state between requests. Called on the browser
|
||||
// process IO thread.
|
||||
bool SendRequest(scoped_ptr<RequestState> state);
|
||||
void ContinueRequest(scoped_ptr<RequestState> state,
|
||||
CefRefPtr<CefResourceHandler> handler);
|
||||
void StopRequest(scoped_ptr<RequestState> state);
|
||||
bool IncrementProvider(RequestState* state);
|
||||
void DetachRequestFromProvider(RequestState* state);
|
||||
void GetNextValidProvider(ProviderEntryList::iterator& iterator);
|
||||
void DeleteProvider(ProviderEntryList::iterator& iterator, bool stop);
|
||||
|
||||
// The below members are only accessed on the browser process IO thread.
|
||||
|
||||
// List of providers including additional associated information.
|
||||
ProviderEntryList providers_;
|
||||
|
||||
// Map of response ID to pending CefResourceHandler object.
|
||||
typedef std::map<uint64, CefRefPtr<CefResourceHandler> > PendingHandlersMap;
|
||||
PendingHandlersMap pending_handlers_;
|
||||
|
||||
UrlFilter url_filter_;
|
||||
MimeTypeResolver mime_type_resolver_;
|
||||
|
||||
// Must be the last member.
|
||||
base::WeakPtrFactory<CefResourceManager> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CefResourceManager);
|
||||
};
|
||||
|
||||
#endif // CEF_INCLUDE_WRAPPER_CEF_RESOURCE_MANAGER_H_
|
|
@ -0,0 +1,767 @@
|
|||
// 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_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) {
|
||||
size_t query_pos = url.find('?');
|
||||
if (query_pos != std::string::npos)
|
||||
return url.substr(0, query_pos);
|
||||
|
||||
size_t fragment_pos = url.find('#');
|
||||
if (fragment_pos != std::string::npos)
|
||||
return url.substr(0, fragment_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),
|
||||
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)),
|
||||
weak_ptr_factory_(this) {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -121,6 +121,9 @@ ClientHandler::ClientHandler(Delegate* delegate,
|
|||
dialog_handler_ = new ClientDialogHandlerGtk();
|
||||
#endif
|
||||
|
||||
resource_manager_ = new CefResourceManager();
|
||||
test_runner::SetupResourceManager(resource_manager_);
|
||||
|
||||
// Read command line settings.
|
||||
CefRefPtr<CefCommandLine> command_line =
|
||||
CefCommandLine::GetGlobalCommandLine();
|
||||
|
@ -461,13 +464,24 @@ bool ClientHandler::OnOpenURLFromTab(
|
|||
return false;
|
||||
}
|
||||
|
||||
cef_return_value_t ClientHandler::OnBeforeResourceLoad(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
CefRefPtr<CefRequest> request,
|
||||
CefRefPtr<CefRequestCallback> callback) {
|
||||
CEF_REQUIRE_IO_THREAD();
|
||||
|
||||
return resource_manager_->OnBeforeResourceLoad(browser, frame, request,
|
||||
callback);
|
||||
}
|
||||
|
||||
CefRefPtr<CefResourceHandler> ClientHandler::GetResourceHandler(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
CefRefPtr<CefRequest> request) {
|
||||
CEF_REQUIRE_IO_THREAD();
|
||||
|
||||
return test_runner::GetResourceHandler(browser, frame, request);
|
||||
return resource_manager_->GetResourceHandler(browser, frame, request);
|
||||
}
|
||||
|
||||
bool ClientHandler::OnQuotaRequest(CefRefPtr<CefBrowser> browser,
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "include/cef_client.h"
|
||||
#include "include/wrapper/cef_helpers.h"
|
||||
#include "include/wrapper/cef_message_router.h"
|
||||
#include "include/wrapper/cef_resource_manager.h"
|
||||
#include "cefclient/browser/client_types.h"
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
|
@ -212,6 +213,11 @@ class ClientHandler : public CefClient,
|
|||
const CefString& target_url,
|
||||
CefRequestHandler::WindowOpenDisposition target_disposition,
|
||||
bool user_gesture) OVERRIDE;
|
||||
cef_return_value_t OnBeforeResourceLoad(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
CefRefPtr<CefRequest> request,
|
||||
CefRefPtr<CefRequestCallback> callback) OVERRIDE;
|
||||
CefRefPtr<CefResourceHandler> GetResourceHandler(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
|
@ -299,6 +305,9 @@ class ClientHandler : public CefClient,
|
|||
// in client_renderer.cc.
|
||||
CefRefPtr<CefMessageRouterBrowserSide> message_router_;
|
||||
|
||||
// Manages the registration and delivery of resources.
|
||||
CefRefPtr<CefResourceManager> resource_manager_;
|
||||
|
||||
// MAIN THREAD MEMBERS
|
||||
// The following members will only be accessed on the main thread. This will
|
||||
// be the same as the CEF UI thread except when using multi-threaded message
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
#include <string>
|
||||
#include "include/cef_stream.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "include/wrapper/cef_resource_manager.h"
|
||||
#endif
|
||||
|
||||
namespace client {
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
|
@ -22,6 +26,12 @@ bool LoadBinaryResource(const char* resource_name, std::string& resource_data);
|
|||
// Retrieve a resource as a steam reader.
|
||||
CefRefPtr<CefStreamReader> GetBinaryResourceReader(const char* resource_name);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Create a new provider for loading binary resources.
|
||||
CefResourceManager::Provider* CreateBinaryResourceProvider(
|
||||
const std::string& url_path);
|
||||
#endif
|
||||
|
||||
} // namespace client
|
||||
|
||||
#endif // CEF_TESTS_CEFCLIENT_BROWSER_RESOURCE_UTIL_H_
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "include/base/cef_logging.h"
|
||||
#include "include/cef_stream.h"
|
||||
#include "include/wrapper/cef_byte_read_handler.h"
|
||||
#include "include/wrapper/cef_stream_resource_handler.h"
|
||||
#include "cefclient/browser/resource.h"
|
||||
|
||||
namespace client {
|
||||
|
@ -58,6 +59,46 @@ int GetResourceId(const char* resource_name) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Provider of binary resources.
|
||||
class BinaryResourceProvider : public CefResourceManager::Provider {
|
||||
public:
|
||||
explicit BinaryResourceProvider(const std::string& url_path)
|
||||
: url_path_(url_path) {
|
||||
DCHECK(!url_path.empty());
|
||||
}
|
||||
|
||||
bool OnRequest(scoped_refptr<CefResourceManager::Request> request) OVERRIDE {
|
||||
CEF_REQUIRE_IO_THREAD();
|
||||
|
||||
const std::string& url = request->url();
|
||||
if (url.find(url_path_) != 0L) {
|
||||
// Not handled by this provider.
|
||||
return false;
|
||||
}
|
||||
|
||||
CefRefPtr<CefResourceHandler> handler;
|
||||
|
||||
const std::string& relative_path = url.substr(url_path_.length());
|
||||
if (!relative_path.empty()) {
|
||||
CefRefPtr<CefStreamReader> stream =
|
||||
GetBinaryResourceReader(relative_path.data());
|
||||
if (stream.get()) {
|
||||
handler = new CefStreamResourceHandler(
|
||||
request->mime_type_resolver().Run(url),
|
||||
stream);
|
||||
}
|
||||
}
|
||||
|
||||
request->Continue(handler);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string url_path_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BinaryResourceProvider);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool LoadBinaryResource(const char* resource_name, std::string& resource_data) {
|
||||
|
@ -94,4 +135,9 @@ CefRefPtr<CefStreamReader> GetBinaryResourceReader(const char* resource_name) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
CefResourceManager::Provider* CreateBinaryResourceProvider(
|
||||
const std::string& url_path) {
|
||||
return new BinaryResourceProvider(url_path);
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
|
|
|
@ -266,45 +266,75 @@ void RunOtherTests(CefRefPtr<CefBrowser> browser) {
|
|||
browser->GetMainFrame()->LoadURL("http://tests/other_tests");
|
||||
}
|
||||
|
||||
// Retrieve the file name and mime type based on the specified url.
|
||||
bool ParseTestUrl(const std::string& url,
|
||||
std::string* file_name,
|
||||
std::string* mime_type) {
|
||||
// Retrieve the path component.
|
||||
CefURLParts parts;
|
||||
CefParseURL(url, parts);
|
||||
std::string file = CefString(&parts.path);
|
||||
if (file.size() < 2)
|
||||
return false;
|
||||
|
||||
// Remove the leading slash.
|
||||
file = file.substr(1);
|
||||
|
||||
// Verify that the file name is valid.
|
||||
for(size_t i = 0; i < file.size(); ++i) {
|
||||
const char c = file[i];
|
||||
if (!isalpha(c) && !isdigit(c) && c != '_' && c != '.')
|
||||
return false;
|
||||
// Provider that dumps the request contents.
|
||||
class RequestDumpResourceProvider : public CefResourceManager::Provider {
|
||||
public:
|
||||
explicit RequestDumpResourceProvider(const std::string& url)
|
||||
: url_(url) {
|
||||
DCHECK(!url.empty());
|
||||
}
|
||||
|
||||
// Determine the mime type based on the file extension, if any.
|
||||
size_t pos = file.rfind(".");
|
||||
if (pos != std::string::npos) {
|
||||
std::string ext = file.substr(pos + 1);
|
||||
if (ext == "html")
|
||||
*mime_type = "text/html";
|
||||
else if (ext == "png")
|
||||
*mime_type = "image/png";
|
||||
else
|
||||
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;
|
||||
}
|
||||
|
||||
const std::string& dump = DumpRequestContents(request->request());
|
||||
std::string str = "<html><body bgcolor=\"white\"><pre>" + dump +
|
||||
"</pre></body></html>";
|
||||
CefRefPtr<CefStreamReader> stream =
|
||||
CefStreamReader::CreateForData(
|
||||
static_cast<void*>(const_cast<char*>(str.c_str())),
|
||||
str.size());
|
||||
DCHECK(stream.get());
|
||||
request->Continue(new CefStreamResourceHandler("text/html", stream));
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string url_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(RequestDumpResourceProvider);
|
||||
};
|
||||
|
||||
// Add a file extension to |url| if none is currently specified.
|
||||
std::string RequestUrlFilter(const std::string& url) {
|
||||
if (url.find(kTestOrigin) != 0U) {
|
||||
// Don't filter anything outside of the test origin.
|
||||
return url;
|
||||
}
|
||||
|
||||
// Identify where the query or fragment component, if any, begins.
|
||||
size_t suffix_pos = url.find('?');
|
||||
if (suffix_pos == std::string::npos)
|
||||
suffix_pos = url.find('#');
|
||||
|
||||
std::string url_base, url_suffix;
|
||||
if (suffix_pos == std::string::npos) {
|
||||
url_base = url;
|
||||
} else {
|
||||
// Default to an html extension if none is specified.
|
||||
*mime_type = "text/html";
|
||||
file += ".html";
|
||||
url_base = url.substr(0, suffix_pos);
|
||||
url_suffix = url.substr(suffix_pos);
|
||||
}
|
||||
|
||||
*file_name = file;
|
||||
return true;
|
||||
// Identify the last path component.
|
||||
size_t path_pos = url_base.rfind('/');
|
||||
if (path_pos == std::string::npos)
|
||||
return url;
|
||||
|
||||
const std::string& path_component = url_base.substr(path_pos);
|
||||
|
||||
// Identify if a file extension is currently specified.
|
||||
size_t ext_pos = path_component.rfind(".");
|
||||
if (ext_pos != std::string::npos)
|
||||
return url;
|
||||
|
||||
// Rebuild the URL with a file extension.
|
||||
return url_base + ".html" + url_suffix;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -480,37 +510,30 @@ std::string GetErrorString(cef_errorcode_t code) {
|
|||
}
|
||||
}
|
||||
|
||||
CefRefPtr<CefResourceHandler> GetResourceHandler(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
CefRefPtr<CefRequest> request) {
|
||||
std::string url = request->GetURL();
|
||||
if (url.find(kTestOrigin) == 0) {
|
||||
// Handle URLs in the test origin.
|
||||
std::string file_name, mime_type;
|
||||
if (ParseTestUrl(url, &file_name, &mime_type)) {
|
||||
if (file_name == "request.html") {
|
||||
// Show the request contents.
|
||||
const std::string& dump = DumpRequestContents(request);
|
||||
std::string str = "<html><body bgcolor=\"white\"><pre>" + dump +
|
||||
"</pre></body></html>";
|
||||
CefRefPtr<CefStreamReader> stream =
|
||||
CefStreamReader::CreateForData(
|
||||
static_cast<void*>(const_cast<char*>(str.c_str())),
|
||||
str.size());
|
||||
DCHECK(stream.get());
|
||||
return new CefStreamResourceHandler("text/html", stream);
|
||||
} else {
|
||||
// Load the resource from file.
|
||||
CefRefPtr<CefStreamReader> stream =
|
||||
GetBinaryResourceReader(file_name.c_str());
|
||||
if (stream.get())
|
||||
return new CefStreamResourceHandler(mime_type, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
void SetupResourceManager(CefRefPtr<CefResourceManager> resource_manager) {
|
||||
const std::string& test_origin = kTestOrigin;
|
||||
|
||||
return NULL;
|
||||
// Add the URL filter.
|
||||
resource_manager->SetUrlFilter(base::Bind(RequestUrlFilter));
|
||||
|
||||
// Add provider for resource dumps.
|
||||
resource_manager->AddProvider(
|
||||
new RequestDumpResourceProvider(test_origin + "request.html"),
|
||||
0, std::string());
|
||||
|
||||
// Add provider for bundled resource files.
|
||||
#if defined(OS_WIN)
|
||||
// Read resources from the binary.
|
||||
resource_manager->AddProvider(CreateBinaryResourceProvider(test_origin),
|
||||
100, std::string());
|
||||
#elif defined(OS_POSIX)
|
||||
// Read resources from a directory on disk.
|
||||
std::string resource_dir;
|
||||
if (GetResourceDir(resource_dir)) {
|
||||
resource_manager->AddDirectoryProvider(test_origin, resource_dir,
|
||||
100, std::string());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Alert(CefRefPtr<CefBrowser> browser, const std::string& message) {
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
#include <string>
|
||||
|
||||
#include "include/cef_browser.h"
|
||||
#include "include/cef_resource_handler.h"
|
||||
#include "include/cef_request.h"
|
||||
#include "include/wrapper/cef_message_router.h"
|
||||
#include "include/wrapper/cef_resource_manager.h"
|
||||
|
||||
namespace client {
|
||||
namespace test_runner {
|
||||
|
@ -30,11 +30,8 @@ std::string GetDataURI(const std::string& data,
|
|||
// Returns the string representation of the specified error code.
|
||||
std::string GetErrorString(cef_errorcode_t code);
|
||||
|
||||
// Get test resources.
|
||||
CefRefPtr<CefResourceHandler> GetResourceHandler(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
CefRefPtr<CefRequest> request);
|
||||
// Set up the resource manager for tests.
|
||||
void SetupResourceManager(CefRefPtr<CefResourceManager> resource_manager);
|
||||
|
||||
// Show a JS alert message.
|
||||
void Alert(CefRefPtr<CefBrowser> browser, const std::string& message);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -274,9 +274,10 @@ void TestHandler::DestroyTest() {
|
|||
ui_thread_helper_.reset(NULL);
|
||||
}
|
||||
|
||||
void TestHandler::OnTestTimeout(int timeout_ms) {
|
||||
void TestHandler::OnTestTimeout(int timeout_ms, bool treat_as_error) {
|
||||
EXPECT_UI_THREAD();
|
||||
EXPECT_TRUE(false) << "Test timed out after " << timeout_ms << "ms";
|
||||
if (treat_as_error)
|
||||
EXPECT_TRUE(false) << "Test timed out after " << timeout_ms << "ms";
|
||||
DestroyTest();
|
||||
}
|
||||
|
||||
|
@ -322,21 +323,24 @@ void TestHandler::ClearResources() {
|
|||
resource_map_.clear();
|
||||
}
|
||||
|
||||
void TestHandler::SetTestTimeout(int timeout_ms) {
|
||||
void TestHandler::SetTestTimeout(int timeout_ms, bool treat_as_error) {
|
||||
if (!CefCurrentlyOn(TID_UI)) {
|
||||
CefPostTask(TID_UI, base::Bind(&TestHandler::SetTestTimeout, this,
|
||||
timeout_ms));
|
||||
timeout_ms, treat_as_error));
|
||||
return;
|
||||
}
|
||||
|
||||
if (CefCommandLine::GetGlobalCommandLine()->HasSwitch("disable-test-timeout"))
|
||||
if (treat_as_error &&
|
||||
CefCommandLine::GetGlobalCommandLine()->HasSwitch(
|
||||
"disable-test-timeout")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use a weak reference to |this| via UIThreadHelper so that the TestHandler
|
||||
// can be destroyed before the timeout expires.
|
||||
GetUIThreadHelper()->PostDelayedTask(
|
||||
base::Bind(&TestHandler::OnTestTimeout, base::Unretained(this),
|
||||
timeout_ms),
|
||||
timeout_ms, treat_as_error),
|
||||
timeout_ms);
|
||||
}
|
||||
|
||||
|
|
|
@ -221,7 +221,7 @@ class TestHandler : public CefClient,
|
|||
|
||||
// Called on the UI thread if the test times out as a result of calling
|
||||
// SetTestTimeout(). Calls DestroyTest() by default.
|
||||
virtual void OnTestTimeout(int timeout_ms);
|
||||
virtual void OnTestTimeout(int timeout_ms, bool treat_as_error);
|
||||
|
||||
// Called from CreateBrowser() to optionally set per-browser settings.
|
||||
virtual void PopulateBrowserSettings(CefBrowserSettings* settings) {}
|
||||
|
@ -242,7 +242,7 @@ class TestHandler : public CefClient,
|
|||
}
|
||||
|
||||
// Call OnTestTimeout() after the specified amount of time.
|
||||
void SetTestTimeout(int timeout_ms = 5000);
|
||||
void SetTestTimeout(int timeout_ms = 5000, bool treat_as_error = true);
|
||||
|
||||
// Signal that the test is complete. This will be called automatically when
|
||||
// all existing non-popup browsers are closed if
|
||||
|
|
Loading…
Reference in New Issue