Provide a generic JavaScript message router implementation (issue #1183).

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@1574 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2014-01-27 23:31:03 +00:00
parent 78bfefee5e
commit 0df95ca529
33 changed files with 6023 additions and 597 deletions

View File

@ -400,6 +400,7 @@
'tests/cefclient/client_switches.cpp',
'tests/cefclient/client_switches.h',
'tests/cefclient/res/osr_test.html',
'tests/unittests/browser_info_map_unittest.cc',
'tests/unittests/command_line_unittest.cc',
'tests/unittests/cookie_unittest.cc',
'tests/unittests/dialog_unittest.cc',
@ -409,6 +410,7 @@
'tests/unittests/geolocation_unittest.cc',
'tests/unittests/jsdialog_unittest.cc',
'tests/unittests/life_span_unittest.cc',
'tests/unittests/message_router_unittest.cc',
'tests/unittests/navigation_unittest.cc',
'tests/unittests/os_rendering_unittest.cc',
'tests/unittests/process_message_unittest.cc',
@ -416,6 +418,8 @@
'tests/unittests/request_handler_unittest.cc',
'tests/unittests/request_unittest.cc',
'tests/cefclient/resource_util.h',
'tests/unittests/routing_test_handler.cc',
'tests/unittests/routing_test_handler.h',
'tests/unittests/run_all_unittests.cc',
'tests/unittests/scheme_handler_unittest.cc',
'tests/unittests/stream_unittest.cc',
@ -1524,10 +1528,13 @@
'tests/unittests/client_app_delegates.cc',
'tests/unittests/cookie_unittest.cc',
'tests/unittests/dom_unittest.cc',
'tests/unittests/message_router_unittest.cc',
'tests/unittests/navigation_unittest.cc',
'tests/unittests/process_message_unittest.cc',
'tests/unittests/request_handler_unittest.cc',
'tests/unittests/request_unittest.cc',
'tests/unittests/routing_test_handler.cc',
'tests/unittests/routing_test_handler.h',
'tests/unittests/scheme_handler_unittest.cc',
'tests/unittests/urlrequest_unittest.cc',
'tests/unittests/test_handler.cc',

View File

@ -36,6 +36,7 @@
],
'includes_wrapper': [
'include/wrapper/cef_byte_read_handler.h',
'include/wrapper/cef_message_router.h',
'include/wrapper/cef_stream_resource_handler.h',
'include/wrapper/cef_xml_object.h',
'include/wrapper/cef_zip_archive.h',
@ -67,13 +68,16 @@
],
'libcef_dll_wrapper_sources_common': [
'libcef_dll/cef_logging.h',
'libcef_dll/cef_macros.h',
'libcef_dll/cpptoc/base_cpptoc.h',
'libcef_dll/cpptoc/cpptoc.h',
'libcef_dll/ctocpp/base_ctocpp.h',
'libcef_dll/ctocpp/ctocpp.h',
'libcef_dll/transfer_util.cpp',
'libcef_dll/transfer_util.h',
'libcef_dll/wrapper/cef_browser_info_map.h',
'libcef_dll/wrapper/cef_byte_read_handler.cc',
'libcef_dll/wrapper/cef_message_router.cc',
'libcef_dll/wrapper/cef_stream_resource_handler.cc',
'libcef_dll/wrapper/cef_xml_object.cc',
'libcef_dll/wrapper/cef_zip_archive.cc',

View File

@ -106,6 +106,18 @@ typedef uint32 cef_color_t;
(static_cast<unsigned>(g) << 8) | \
(static_cast<unsigned>(b) << 0))
// Return an int64 value with the specified low and high int32 component values.
#define CefInt64Set(int32_low, int32_high) \
static_cast<int64>((static_cast<uint32>(int32_low)) | \
(static_cast<int64>(static_cast<int32>(int32_high))) << 32)
// Return the low int32 value from an int64 value.
#define CefInt64GetLow(int64_val) static_cast<int32>(int64_val)
// Return the high int32 value from an int64 value.
#define CefInt64GetHigh(int64_val) \
static_cast<int32>((static_cast<int64>(int64_val) >> 32) & 0xFFFFFFFFL)
#ifdef __cplusplus
extern "C" {
#endif

View File

@ -0,0 +1,417 @@
// Copyright (c) 2014 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_MESSAGE_ROUTER_H_
#define CEF_INCLUDE_WRAPPER_CEF_MESSAGE_ROUTER_H_
#pragma once
#include "include/cef_base.h"
#include "include/cef_browser.h"
#include "include/cef_process_message.h"
#include "include/cef_v8.h"
// The below classes implement support for routing aynchronous messages between
// JavaScript running in the renderer process and C++ running in the browser
// process. An application interacts with the router by passing it data from
// standard CEF C++ callbacks (OnBeforeBrowse, OnProcessMessageRecieved,
// OnContextCreated, etc). The renderer-side router supports generic JavaScript
// callback registration and execution while the browser-side router supports
// application-specific logic via one or more application-provided Handler
// instances.
//
// The renderer-side router implementation exposes a query function and a cancel
// function via the JavaScript 'window' object:
//
// // Create and send a new query.
// var request_id = window.cefQuery({
// request: 'my_request',
// persistent: false,
// onSuccess: function(response) {},
// onFailure: function(error_code, error_message) {}
// });
//
// // Optionally cancel the query.
// window.cefQueryCancel(request_id);
//
// When |window.cefQuery| is executed the request is sent asynchronously to one
// or more C++ Handler objects registered in the browser process. Each C++
// Handler can choose to either handle or ignore the query in the
// Handler::OnQuery callback. If a Handler chooses to handle the query then it
// should execute Callback::Success when a response is available or
// Callback::Failure if an error occurs. This will result in asynchronous
// execution of the associated JavaScript callback in the renderer process. Any
// queries unhandled by C++ code in the browser process will be automatically
// canceled and the associated JavaScript onFailure callback will be executed
// with an error code of -1.
//
// Queries can be either persistent or non-persistent. If the query is
// persistent than the callbacks will remain registered until one of the
// following conditions are met:
//
// A. The query is canceled in JavaScript using the |window.cefQueryCancel|
// function.
// B. The query is canceled in C++ code using the Callback::Failure function.
// C. The context associated with the query is released due to browser
// destruction, navigation or renderer process termination.
//
// If the query is non-persistent then the registration will be removed after
// the JavaScript callback is executed a single time. If a query is canceled for
// a reason other than Callback::Failure being executed then the associated
// Handler's OnQueryCanceled method will be called.
//
// Some possible usage patterns include:
//
// One-time Request. Use a non-persistent query to send a JavaScript request.
// The Handler evaluates the request and returns the response. The query is
// then discarded.
//
// Broadcast. Use a persistent query to register as a JavaScript broadcast
// receiver. The Handler keeps track of all registered Callbacks and executes
// them sequentially to deliver the broadcast message.
//
// Subscription. Use a persistent query to register as a JavaScript subscription
// receiver. The Handler initiates the subscription feed on the first request
// and delivers responses to all registered subscribers as they become
// available. The Handler cancels the subscription feed when there are no
// longer any registered JavaScript receivers.
//
// Message routing occurs on a per-browser and per-context basis. Consequently,
// additional application logic can be applied by restricting which browser or
// context instances are passed into the router. If you choose to use this
// approach do so cautiously. In order for the router to function correctly any
// browser or context instance passed into a single router callback must then
// be passed into all router callbacks.
//
// There is generally no need to have multiple renderer-side routers unless you
// wish to have multiple bindings with different JavaScript function names. It
// can be useful to have multiple browser-side routers with different client-
// provided Handler instances when implementing different behaviors on a per-
// browser basis.
//
// This implementation places no formatting restrictions on payload content.
// An application may choose to exchange anything from simple formatted
// strings to serialized XML or JSON data.
//
//
// EXAMPLE USAGE
//
// 1. Define the router configuration. You can optionally specify settings
// like the JavaScript function names. The configuration must be the same in
// both the browser and renderer processes. If using multiple routers in the
// same application make sure to specify unique function names for each
// router configuration.
//
// // Example config object showing the default values.
// CefMessageRouterConfig config;
// config.js_query_function = "cefQuery";
// config.js_cancel_function = "cefQueryCancel";
//
// 2. Create an instance of CefMessageRouterBrowserSide in the browser process.
// You might choose to make it a member of your CefClient implementation,
// for example.
//
// browser_side_router_ = CefMessageRouterBrowserSide::Create(config);
//
// 3. Register one or more Handlers. The Handler instances must either outlive
// the router or be removed from the router before they're deleted.
//
// browser_side_router_->AddHandler(my_handler);
//
// 4. Call all required CefMessageRouterBrowserSide methods from other callbacks
// in your CefClient implementation (OnBeforeClose, etc). See the
// CefMessageRouterBrowserSide class documentation for the complete list of
// methods.
//
// 5. Create an instance of CefMessageRouterRendererSide in the renderer process.
// You might choose to make it a member of your CefApp implementation, for
// example.
//
// renderer_side_router_ = CefMessageRouterRendererSide::Create(config);
//
// 6. Call all required CefMessageRouterRendererSide methods from other
// callbacks in your CefRenderProcessHandler implementation
// (OnContextCreated, etc). See the CefMessageRouterRendererSide class
// documentation for the complete list of methods.
//
// 7. Execute the query function from JavaScript code.
//
// window.cefQuery({request: 'my_request',
// persistent: false,
// onSuccess: function(response) { print(response); },
// onFailure: function(error_code, error_message) {} });
//
// 8. Handle the query in your Handler::OnQuery implementation and execute the
// appropriate callback either immediately or asynchronously.
//
// void MyHandler::OnQuery(int64 query_id,
// CefRefPtr<CefBrowser> browser,
// CefRefPtr<CefFrame> frame,
// const CefString& request,
// bool persistent,
// CefRefPtr<Callback> callback) {
// if (request == "my_request") {
// callback->Continue("my_response");
// return true;
// }
// return false; // Not handled.
// }
//
// 9. Notice that the onSuccess callback is executed in JavaScript.
///
// Used to configure the query router. The same values must be passed to both
// CefMessageRouterBrowserSide and CefMessageRouterRendererSide. If using multiple
// router pairs make sure to choose values that do not conflict.
///
struct CefMessageRouterConfig {
CefMessageRouterConfig();
// Name of the JavaScript function that will be added to the 'window' object
// for sending a query. The default value is "cefQuery".
CefString js_query_function;
// Name of the JavaScript function that will be added to the 'window' object
// for canceling a pending query. The default value is "cefQueryCancel".
CefString js_cancel_function;
};
///
// Implements the browser side of query routing. The methods of this class may
// be called on any browser process thread unless otherwise indicated.
///
class CefMessageRouterBrowserSide : public CefBase {
public:
///
// Callback associated with a single pending asynchronous query. Execute the
// Success or Failure method to send an asynchronous response to the
// associated JavaScript handler. It is a runtime error to destroy a Callback
// object associated with an uncanceled query without first executing one of
// the callback methods. The methods of this class may be called on any
// browser process thread.
///
class Callback : public CefBase {
public:
///
// Notify the associated JavaScript onSuccess callback that the query has
// completed successfully with the specified |response|.
///
virtual void Success(const CefString& response) =0;
///
// Notify the associated JavaScript onFailure callback that the query has
// failed with the specified |error_code| and |error_message|.
///
virtual void Failure(int error_code, const CefString& error_message) =0;
};
///
// Implement this interface to handle queries. All methods will be executed on
// the browser process UI thread.
///
class Handler {
public:
typedef CefMessageRouterBrowserSide::Callback Callback;
///
// Executed when a new query is received. |query_id| uniquely identifies the
// query for the life span of the router. Return true to handle the query
// or false to propagate the query to other registered handlers, if any. If
// no handlers return true from this method then the query will be
// automatically canceled with an error code of -1 delivered to the
// JavaScript onFailure callback. If this method returns true then a
// Callback method must be executed either in this method or asynchronously
// to complete the query.
///
virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) {
return false;
}
///
// Executed when a query has been canceled either explicitly using the
// JavaScript cancel function or implicitly due to browser destruction,
// navigation or renderer process termination. It will only be called for
// the single handler that returned true from OnQuery for the same
// |query_id|. No references to the associated Callback object should be
// kept after this method is called, nor should any Callback methods be
// executed.
///
virtual void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id) {}
virtual ~Handler() {}
};
///
// Create a new router with the specified configuration.
///
static CefRefPtr<CefMessageRouterBrowserSide> Create(
const CefMessageRouterConfig& config);
///
// Add a new query handler. If |first| is true it will be added as the first
// handler, otherwise it will be added as the last handler. Returns true if
// the handler is added successfully or false if the handler has already been
// added. Must be called on the browser process UI thread. The Handler object
// must either outlive the router or be removed before deletion.
///
virtual bool AddHandler(Handler* handler, bool first) =0;
///
// Remove an existing query handler. Any pending queries associated with the
// handler will be canceled. Handler::OnQueryCanceled will be called and the
// associated JavaScript onFailure callback will be executed with an error
// code of -1. Returns true if the handler is removed successfully or false
// if the handler is not found. Must be called on the browser process UI
// thread.
///
virtual bool RemoveHandler(Handler* handler) =0;
///
// Cancel all pending queries associated with either |browser| or |handler|.
// If both |browser| and |handler| are NULL all pending queries will be
// canceled. Handler::OnQueryCanceled will be called and the associated
// JavaScript onFailure callback will be executed in all cases with an error
// code of -1.
///
virtual void CancelPending(CefRefPtr<CefBrowser> browser,
Handler* handler) =0;
///
// Returns the number of queries currently pending for the specified |browser|
// and/or |handler|. Either or both values may be empty. Must be called on the
// browser process UI thread.
///
virtual int GetPendingCount(CefRefPtr<CefBrowser> browser,
Handler* handler) =0;
// The below methods should be called from other CEF handlers. They must be
// called exactly as documented for the router to function correctly.
///
// Call from CefLifeSpanHandler::OnBeforeClose. Any pending queries associated
// with |browser| will be canceled and Handler::OnQueryCanceled will be called.
// No JavaScript callbacks will be executed since this indicates destruction
// of the browser.
///
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) =0;
///
// Call from CefRequestHandler::OnRenderProcessTerminated. Any pending queries
// associated with |browser| will be canceled and Handler::OnQueryCanceled
// will be called. No JavaScript callbacks will be executed since this
// indicates destruction of the context.
///
virtual void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser) =0;
///
// Call from CefRequestHandler::OnBeforeBrowse only if the navigation is
// allowed to proceed. If |frame| is the main frame then any pending queries
// associated with |browser| will be canceled and Handler::OnQueryCanceled
// will be called. No JavaScript callbacks will be executed since this
// indicates destruction of the context.
///
virtual void OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame) =0;
///
// Call from CefClient::OnProcessMessageReceived. Returns true if the message
// is handled by this router or false otherwise.
///
virtual bool OnProcessMessageReceived(
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) =0;
};
///
// Implements the renderer side of query routing. The methods of this class must
// be called on the render process main thread.
///
class CefMessageRouterRendererSide : public CefBase {
public:
virtual ~CefMessageRouterRendererSide() {}
///
// Create a new router with the specified configuration.
///
static CefRefPtr<CefMessageRouterRendererSide> Create(
const CefMessageRouterConfig& config);
///
// Returns the number of queries currently pending for the specified |browser|
// and/or |context|. Either or both values may be empty.
///
virtual int GetPendingCount(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefV8Context> context) =0;
// The below methods should be called from other CEF handlers. They must be
// called exactly as documented for the router to function correctly.
///
// Call from CefRenderProcessHandler::OnContextCreated. Registers the
// JavaScripts functions with the new context.
///
virtual void OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) =0;
///
// Call from CefRenderProcessHandler::OnContextReleased. Any pending queries
// associated with the released context will be canceled and
// Handler::OnQueryCanceled will be called in the browser process.
///
virtual void OnContextReleased(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) =0;
///
// Call from CefRenderProcessHandler::OnProcessMessageReceived. Returns true
// if the message is handled by this router or false otherwise.
///
virtual bool OnProcessMessageReceived(
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) =0;
};
#endif // CEF_INCLUDE_WRAPPER_CEF_MESSAGE_ROUTER_H_

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights
// Copyright (c) 2014 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.
@ -6,17 +6,28 @@
#define CEF_LIBCEF_DLL_CEF_LOGGING_H_
#pragma once
#include "include/cef_task.h"
#ifdef BUILDING_CEF_SHARED
#include "base/logging.h"
#else
#else // !BUILDING_CEF_SHARED
#include <assert.h> // NOLINT(build/include_order)
#ifndef NDEBUG
#define DCHECK(condition) assert(condition)
#else
#define DCHECK(condition) ((void)0)
#endif
#define DCHECK_EQ(val1, val2) DCHECK(val1 == val2)
#define DCHECK_NE(val1, val2) DCHECK(val1 != val2)
#define DCHECK_LE(val1, val2) DCHECK(val1 <= val2)
#define DCHECK_LT(val1, val2) DCHECK(val1 < val2)
#define DCHECK_GE(val1, val2) DCHECK(val1 >= val2)
#define DCHECK_GT(val1, val2) DCHECK(val1 > val2)
#endif
#endif // !BUILDING_CEF_SHARED
#define CEF_REQUIRE_UI_THREAD() DCHECK(CefCurrentlyOn(TID_UI));
#define CEF_REQUIRE_RENDERER_THREAD() DCHECK(CefCurrentlyOn(TID_RENDERER));
#endif // CEF_LIBCEF_DLL_CEF_LOGGING_H_

42
libcef_dll/cef_macros.h Normal file
View File

@ -0,0 +1,42 @@
// Copyright (c) 2014 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.
#ifndef CEF_LIBCEF_DLL_CEF_MACROS_H_
#define CEF_LIBCEF_DLL_CEF_MACROS_H_
#pragma once
#ifdef BUILDING_CEF_SHARED
#include "base/macros.h"
#else // !BUILDING_CEF_SHARED
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
#endif // !BUILDING_CEF_SHARED
#endif // CEF_LIBCEF_DLL_CEF_MACROS_H_
// Copyright (c) 2014 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.
#ifndef CEF_LIBCEF_DLL_CEF_MACROS_H_
#define CEF_LIBCEF_DLL_CEF_MACROS_H_
#pragma once
#ifdef BUILDING_CEF_SHARED
#include "base/macros.h"
#else // !BUILDING_CEF_SHARED
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
#endif // !BUILDING_CEF_SHARED
#endif // CEF_LIBCEF_DLL_CEF_MACROS_H_

View File

@ -0,0 +1,264 @@
// Copyright (c) 2014 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.
#ifndef CEF_LIBCEF_DLL_WRAPPER_CEF_BROWSER_INFO_MAP_H_
#define CEF_LIBCEF_DLL_WRAPPER_CEF_BROWSER_INFO_MAP_H_
#pragma once
#include <map>
#include "libcef_dll/cef_logging.h"
#include "libcef_dll/cef_macros.h"
// Default traits for CefBrowserInfoMap. Override to provide different object
// destruction behavior.
template<typename ObjectType>
struct DefaultCefBrowserInfoMapTraits {
static void Destruct(ObjectType info) {
delete info;
}
};
// Maps an arbitrary IdType to an arbitrary ObjectType on a per-browser basis.
template <typename IdType,
typename ObjectType,
typename Traits = DefaultCefBrowserInfoMapTraits<ObjectType> >
class CefBrowserInfoMap {
public:
// Implement this interface to visit and optionally delete objects in the map.
class Visitor {
public:
typedef IdType InfoIdType;
typedef ObjectType InfoObjectType;
// Called once for each info object. Set |remove| to true to remove the
// object from the map. It is safe to destruct removed objects in this
// callback. Return true to continue iterating or false to stop iterating.
virtual bool OnNextInfo(int browser_id,
InfoIdType info_id,
InfoObjectType info,
bool* remove) =0;
protected:
virtual ~Visitor() {}
};
CefBrowserInfoMap() {}
~CefBrowserInfoMap() {
clear();
}
// Add an object associated with the specified ID values.
void Add(int browser_id, IdType info_id, ObjectType info) {
InfoMap* info_map = NULL;
typename BrowserInfoMap::const_iterator it_browser =
browser_info_map_.find(browser_id);
if (it_browser == browser_info_map_.end()) {
// No InfoMap exists for the browser ID so create it.
info_map = new InfoMap;
browser_info_map_.insert(std::make_pair(browser_id, info_map));
} else {
info_map = it_browser->second;
// The specified ID should not already exist in the map.
DCHECK_EQ(info_map->find(info_id), info_map->end());
}
info_map->insert(std::make_pair(info_id, info));
}
// Find the object with the specified ID values. |visitor| can optionally be
// used to evaluate or remove the object at the same time. If the object is
// removed using the Visitor the caller is responsible for destroying it.
ObjectType Find(int browser_id, IdType info_id, Visitor* vistor) {
if (browser_info_map_.empty())
return ObjectType();
typename BrowserInfoMap::iterator it_browser =
browser_info_map_.find(browser_id);
if (it_browser == browser_info_map_.end())
return ObjectType();
InfoMap* info_map = it_browser->second;
typename InfoMap::iterator it_info = info_map->find(info_id);
if (it_info == info_map->end())
return ObjectType();
ObjectType info = it_info->second;
bool remove = false;
if (vistor)
vistor->OnNextInfo(browser_id, it_info->first, info, &remove);
if (remove) {
info_map->erase(it_info);
if (info_map->empty()) {
// No more entries in the InfoMap so remove it.
browser_info_map_.erase(it_browser);
delete info_map;
}
}
return info;
}
// Find all objects. If any objects are removed using the Visitor the caller
// is responsible for destroying them.
void FindAll(Visitor* visitor) {
DCHECK(visitor);
if (browser_info_map_.empty())
return;
bool remove, keepgoing;
typename BrowserInfoMap::iterator it_browser = browser_info_map_.begin();
while (it_browser != browser_info_map_.end()) {
InfoMap* info_map = it_browser->second;
typename InfoMap::iterator it_info = info_map->begin();
while (it_info != info_map->end()) {
remove = false;
keepgoing = visitor->OnNextInfo(it_browser->first, it_info->first,
it_info->second, &remove);
if (remove)
info_map->erase(it_info++);
else
++it_info;
if (!keepgoing)
break;
}
if (info_map->empty()) {
// No more entries in the InfoMap so remove it.
browser_info_map_.erase(it_browser++);
delete info_map;
} else {
++it_browser;
}
if (!keepgoing)
break;
}
}
// Find all objects associated with the specified browser. If any objects are
// removed using the Visitor the caller is responsible for destroying them.
void FindAll(int browser_id, Visitor* visitor) {
DCHECK(visitor);
if (browser_info_map_.empty())
return;
typename BrowserInfoMap::iterator it_browser =
browser_info_map_.find(browser_id);
if (it_browser == browser_info_map_.end())
return;
InfoMap* info_map = it_browser->second;
bool remove, keepgoing;
typename InfoMap::iterator it_info = info_map->begin();
while (it_info != info_map->end()) {
remove = false;
keepgoing = visitor->OnNextInfo(browser_id, it_info->first,
it_info->second, &remove);
if (remove)
info_map->erase(it_info++);
else
++it_info;
if (!keepgoing)
break;
}
if (info_map->empty()) {
// No more entries in the InfoMap so remove it.
browser_info_map_.erase(it_browser);
delete info_map;
}
}
// Returns true if the map is empty.
bool empty() const { return browser_info_map_.empty(); }
// Returns the number of objects in the map.
size_t size() const {
if (browser_info_map_.empty())
return 0;
size_t size = 0;
typename BrowserInfoMap::const_iterator it_browser =
browser_info_map_.begin();
for (; it_browser != browser_info_map_.end(); ++it_browser)
size += it_browser->second->size();
return size;
}
// Returns the number of objects in the map that are associated with the
// specified browser.
size_t size(int browser_id) const {
if (browser_info_map_.empty())
return 0;
typename BrowserInfoMap::const_iterator it_browser =
browser_info_map_.find(browser_id);
if (it_browser != browser_info_map_.end())
return it_browser->second->size();
return 0;
}
// Remove all objects from the map. The objects will be destructed.
void clear() {
if (browser_info_map_.empty())
return;
typename BrowserInfoMap::const_iterator it_browser =
browser_info_map_.begin();
for (; it_browser != browser_info_map_.end(); ++it_browser) {
InfoMap* info_map = it_browser->second;
typename InfoMap::const_iterator it_info = info_map->begin();
for (; it_info != info_map->end(); ++it_info)
Traits::Destruct(it_info->second);
delete info_map;
}
browser_info_map_.clear();
}
// Remove all objects from the map that are associated with the specified
// browser. The objects will be destructed.
void clear(int browser_id) {
if (browser_info_map_.empty())
return;
typename BrowserInfoMap::iterator it_browser =
browser_info_map_.find(browser_id);
if (it_browser == browser_info_map_.end())
return;
InfoMap* info_map = it_browser->second;
typename InfoMap::const_iterator it_info = info_map->begin();
for (; it_info != info_map->end(); ++it_info)
Traits::Destruct(it_info->second);
browser_info_map_.erase(it_browser);
delete info_map;
}
private:
// Map IdType to ObjectType instance.
typedef std::map<IdType, ObjectType> InfoMap;
// Map browser ID to InfoMap instance.
typedef std::map<int, InfoMap*> BrowserInfoMap;
BrowserInfoMap browser_info_map_;
DISALLOW_COPY_AND_ASSIGN(CefBrowserInfoMap);
};
#endif // CEF_LIBCEF_DLL_WRAPPER_CEF_BROWSER_INFO_MAP_H_

File diff suppressed because it is too large Load Diff

View File

@ -13,54 +13,43 @@ namespace binding_test {
namespace {
const char* kMessageName = "binding_test";
const char kTestUrl[] = "http://tests/binding";
const char kTestMessageName[] = "BindingTest";
// Handle messages in the browser process.
class ProcessMessageDelegate : public ClientHandler::ProcessMessageDelegate {
class Handler : public CefMessageRouterBrowserSide::Handler {
public:
ProcessMessageDelegate() {
}
Handler() {}
// From ClientHandler::ProcessMessageDelegate.
virtual bool OnProcessMessageReceived(
CefRefPtr<ClientHandler> handler,
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) OVERRIDE {
std::string message_name = message->GetName();
if (message_name == kMessageName) {
// Handle the message.
std::string result;
CefRefPtr<CefListValue> args = message->GetArgumentList();
if (args->GetSize() > 0 && args->GetType(0) == VTYPE_STRING) {
// Our result is a reverse of the original message.
result = args->GetString(0);
std::reverse(result.begin(), result.end());
} else {
result = "Invalid request";
}
// Send the result back to the render process.
CefRefPtr<CefProcessMessage> response =
CefProcessMessage::Create(kMessageName);
response->GetArgumentList()->SetString(0, result);
browser->SendProcessMessage(PID_RENDERER, response);
// Called due to cefQuery execution in binding.html.
virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) OVERRIDE {
// Only handle messages from the test URL.
const std::string& url = frame->GetURL();
if (url.find(kTestUrl) != 0)
return false;
const std::string& message_name = request;
if (message_name.find(kTestMessageName) == 0) {
// Reverse the string and return.
std::string result = message_name.substr(sizeof(kTestMessageName));
std::reverse(result.begin(), result.end());
callback->Success(result);
return true;
}
return false;
}
IMPLEMENT_REFCOUNTING(ProcessMessageDelegate);
};
} // namespace
void CreateProcessMessageDelegates(
ClientHandler::ProcessMessageDelegateSet& delegates) {
delegates.insert(new ProcessMessageDelegate);
void CreateMessageHandlers(ClientHandler::MessageHandlerSet& handlers) {
handlers.insert(new Handler());
}
} // namespace binding_test

View File

@ -10,9 +10,8 @@
namespace binding_test {
// Delegate creation. Called from ClientHandler.
void CreateProcessMessageDelegates(
ClientHandler::ProcessMessageDelegateSet& delegates);
// Handler creation. Called from ClientHandler.
void CreateMessageHandlers(ClientHandler::MessageHandlerSet& handlers);
} // namespace binding_test

View File

@ -14,201 +14,21 @@
#include "include/cef_v8.h"
#include "util.h" // NOLINT(build/include)
namespace {
// Forward declarations.
void SetList(CefRefPtr<CefV8Value> source, CefRefPtr<CefListValue> target);
void SetList(CefRefPtr<CefListValue> source, CefRefPtr<CefV8Value> target);
// Transfer a V8 value to a List index.
void SetListValue(CefRefPtr<CefListValue> list, int index,
CefRefPtr<CefV8Value> value) {
if (value->IsArray()) {
CefRefPtr<CefListValue> new_list = CefListValue::Create();
SetList(value, new_list);
list->SetList(index, new_list);
} else if (value->IsString()) {
list->SetString(index, value->GetStringValue());
} else if (value->IsBool()) {
list->SetBool(index, value->GetBoolValue());
} else if (value->IsInt()) {
list->SetInt(index, value->GetIntValue());
} else if (value->IsDouble()) {
list->SetDouble(index, value->GetDoubleValue());
}
}
// Transfer a V8 array to a List.
void SetList(CefRefPtr<CefV8Value> source, CefRefPtr<CefListValue> target) {
ASSERT(source->IsArray());
int arg_length = source->GetArrayLength();
if (arg_length == 0)
return;
// Start with null types in all spaces.
target->SetSize(arg_length);
for (int i = 0; i < arg_length; ++i)
SetListValue(target, i, source->GetValue(i));
}
// Transfer a List value to a V8 array index.
void SetListValue(CefRefPtr<CefV8Value> list, int index,
CefRefPtr<CefListValue> value) {
CefRefPtr<CefV8Value> new_value;
CefValueType type = value->GetType(index);
switch (type) {
case VTYPE_LIST: {
CefRefPtr<CefListValue> list = value->GetList(index);
new_value = CefV8Value::CreateArray(static_cast<int>(list->GetSize()));
SetList(list, new_value);
} break;
case VTYPE_BOOL:
new_value = CefV8Value::CreateBool(value->GetBool(index));
break;
case VTYPE_DOUBLE:
new_value = CefV8Value::CreateDouble(value->GetDouble(index));
break;
case VTYPE_INT:
new_value = CefV8Value::CreateInt(value->GetInt(index));
break;
case VTYPE_STRING:
new_value = CefV8Value::CreateString(value->GetString(index));
break;
default:
break;
}
if (new_value.get()) {
list->SetValue(index, new_value);
} else {
list->SetValue(index, CefV8Value::CreateNull());
}
}
// Transfer a List to a V8 array.
void SetList(CefRefPtr<CefListValue> source, CefRefPtr<CefV8Value> target) {
ASSERT(target->IsArray());
int arg_length = static_cast<int>(source->GetSize());
if (arg_length == 0)
return;
for (int i = 0; i < arg_length; ++i)
SetListValue(target, i, source);
}
// Handles the native implementation for the client_app extension.
class ClientAppExtensionHandler : public CefV8Handler {
public:
explicit ClientAppExtensionHandler(CefRefPtr<ClientApp> client_app)
: client_app_(client_app) {
}
virtual bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) {
bool handled = false;
if (name == "sendMessage") {
// Send a message to the browser process.
if ((arguments.size() == 1 || arguments.size() == 2) &&
arguments[0]->IsString()) {
CefRefPtr<CefBrowser> browser =
CefV8Context::GetCurrentContext()->GetBrowser();
ASSERT(browser.get());
CefString name = arguments[0]->GetStringValue();
if (!name.empty()) {
CefRefPtr<CefProcessMessage> message =
CefProcessMessage::Create(name);
// Translate the arguments, if any.
if (arguments.size() == 2 && arguments[1]->IsArray())
SetList(arguments[1], message->GetArgumentList());
browser->SendProcessMessage(PID_BROWSER, message);
handled = true;
}
}
} else if (name == "setMessageCallback") {
// Set a message callback.
if (arguments.size() == 2 && arguments[0]->IsString() &&
arguments[1]->IsFunction()) {
std::string name = arguments[0]->GetStringValue();
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
int browser_id = context->GetBrowser()->GetIdentifier();
client_app_->SetMessageCallback(name, browser_id, context,
arguments[1]);
handled = true;
}
} else if (name == "removeMessageCallback") {
// Remove a message callback.
if (arguments.size() == 1 && arguments[0]->IsString()) {
std::string name = arguments[0]->GetStringValue();
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
int browser_id = context->GetBrowser()->GetIdentifier();
bool removed = client_app_->RemoveMessageCallback(name, browser_id);
retval = CefV8Value::CreateBool(removed);
handled = true;
}
}
if (!handled)
exception = "Invalid method arguments";
return true;
}
private:
CefRefPtr<ClientApp> client_app_;
IMPLEMENT_REFCOUNTING(ClientAppExtensionHandler);
};
} // namespace
ClientApp::ClientApp() {
CreateBrowserDelegates(browser_delegates_);
CreateRenderDelegates(render_delegates_);
}
void ClientApp::OnRegisterCustomSchemes(
CefRefPtr<CefSchemeRegistrar> registrar) {
// Default schemes that support cookies.
cookieable_schemes_.push_back("http");
cookieable_schemes_.push_back("https");
}
void ClientApp::SetMessageCallback(const std::string& message_name,
int browser_id,
CefRefPtr<CefV8Context> context,
CefRefPtr<CefV8Value> function) {
ASSERT(CefCurrentlyOn(TID_RENDERER));
callback_map_.insert(
std::make_pair(std::make_pair(message_name, browser_id),
std::make_pair(context, function)));
}
bool ClientApp::RemoveMessageCallback(const std::string& message_name,
int browser_id) {
ASSERT(CefCurrentlyOn(TID_RENDERER));
CallbackMap::iterator it =
callback_map_.find(std::make_pair(message_name, browser_id));
if (it != callback_map_.end()) {
callback_map_.erase(it);
return true;
}
return false;
RegisterCustomSchemes(registrar, cookieable_schemes_);
}
void ClientApp::OnContextInitialized() {
CreateBrowserDelegates(browser_delegates_);
// Register cookieable schemes with the global cookie manager.
CefRefPtr<CefCookieManager> manager = CefCookieManager::GetGlobalManager();
ASSERT(manager.get());
@ -234,34 +54,14 @@ void ClientApp::OnRenderProcessThreadCreated(
}
void ClientApp::OnRenderThreadCreated(CefRefPtr<CefListValue> extra_info) {
CreateRenderDelegates(render_delegates_);
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end(); ++it)
(*it)->OnRenderThreadCreated(this, extra_info);
}
void ClientApp::OnWebKitInitialized() {
// Register the client_app extension.
std::string app_code =
"var app;"
"if (!app)"
" app = {};"
"(function() {"
" app.sendMessage = function(name, arguments) {"
" native function sendMessage();"
" return sendMessage(name, arguments);"
" };"
" app.setMessageCallback = function(name, callback) {"
" native function setMessageCallback();"
" return setMessageCallback(name, callback);"
" };"
" app.removeMessageCallback = function(name) {"
" native function removeMessageCallback();"
" return removeMessageCallback(name);"
" };"
"})();";
CefRegisterExtension("v8/app", app_code,
new ClientAppExtensionHandler(this));
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end(); ++it)
(*it)->OnWebKitInitialized(this);
@ -281,7 +81,6 @@ void ClientApp::OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) {
CefRefPtr<CefLoadHandler> ClientApp::GetLoadHandler() {
CefRefPtr<CefLoadHandler> load_handler;
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end() && !load_handler.get(); ++it)
load_handler = (*it)->GetLoadHandler(this);
@ -319,18 +118,6 @@ void ClientApp::OnContextReleased(CefRefPtr<CefBrowser> browser,
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end(); ++it)
(*it)->OnContextReleased(this, browser, frame, context);
// Remove any JavaScript callbacks registered for the context that has been
// released.
if (!callback_map_.empty()) {
CallbackMap::iterator it = callback_map_.begin();
for (; it != callback_map_.end();) {
if (it->second.first->IsSame(context))
callback_map_.erase(it++);
else
++it;
}
}
}
void ClientApp::OnUncaughtException(CefRefPtr<CefBrowser> browser,
@ -367,47 +154,5 @@ bool ClientApp::OnProcessMessageReceived(
message);
}
if (handled)
return true;
// Execute the registered JavaScript callback if any.
if (!callback_map_.empty()) {
CefString message_name = message->GetName();
CallbackMap::const_iterator it = callback_map_.find(
std::make_pair(message_name.ToString(),
browser->GetIdentifier()));
if (it != callback_map_.end()) {
// Keep a local reference to the objects. The callback may remove itself
// from the callback map.
CefRefPtr<CefV8Context> context = it->second.first;
CefRefPtr<CefV8Value> callback = it->second.second;
// Enter the context.
context->Enter();
CefV8ValueList arguments;
// First argument is the message name.
arguments.push_back(CefV8Value::CreateString(message_name));
// Second argument is the list of message arguments.
CefRefPtr<CefListValue> list = message->GetArgumentList();
CefRefPtr<CefV8Value> args =
CefV8Value::CreateArray(static_cast<int>(list->GetSize()));
SetList(list, args);
arguments.push_back(args);
// Execute the callback.
CefRefPtr<CefV8Value> retval = callback->ExecuteFunction(NULL, arguments);
if (retval.get()) {
if (retval->IsBool())
handled = retval->GetBoolValue();
}
// Exit the context.
context->Exit();
}
}
return handled;
}

View File

@ -103,22 +103,6 @@ class ClientApp : public CefApp,
ClientApp();
// Set a JavaScript callback for the specified |message_name| and |browser_id|
// combination. Will automatically be removed when the associated context is
// released. Callbacks can also be set in JavaScript using the
// app.setMessageCallback function.
void SetMessageCallback(const std::string& message_name,
int browser_id,
CefRefPtr<CefV8Context> context,
CefRefPtr<CefV8Value> function);
// Removes the JavaScript callback for the specified |message_name| and
// |browser_id| combination. Returns true if a callback was removed. Callbacks
// can also be removed in JavaScript using the app.removeMessageCallback
// function.
bool RemoveMessageCallback(const std::string& message_name,
int browser_id);
private:
// Creates all of the BrowserDelegate objects. Implemented in
// client_app_delegates.
@ -134,9 +118,7 @@ class ClientApp : public CefApp,
// CefApp methods.
virtual void OnRegisterCustomSchemes(
CefRefPtr<CefSchemeRegistrar> registrar) OVERRIDE {
RegisterCustomSchemes(registrar, cookieable_schemes_);
}
CefRefPtr<CefSchemeRegistrar> registrar) OVERRIDE;
virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler()
OVERRIDE { return this; }
virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler()
@ -181,19 +163,14 @@ class ClientApp : public CefApp,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) OVERRIDE;
// Map of message callbacks.
typedef std::map<std::pair<std::string, int>,
std::pair<CefRefPtr<CefV8Context>, CefRefPtr<CefV8Value> > >
CallbackMap;
CallbackMap callback_map_;
// Set of supported BrowserDelegates.
// Set of supported BrowserDelegates. Only used in the browser process.
BrowserDelegateSet browser_delegates_;
// Set of supported RenderDelegates.
// Set of supported RenderDelegates. Only used in the renderer process.
RenderDelegateSet render_delegates_;
// Schemes that will be registered with the global cookie manager.
// Schemes that will be registered with the global cookie manager. Used in
// both the browser and renderer process.
std::vector<CefString> cookieable_schemes_;
IMPLEMENT_REFCOUNTING(ClientApp);

View File

@ -98,7 +98,15 @@ ClientHandler::ClientHandler()
m_StopHwnd(NULL),
m_ReloadHwnd(NULL),
m_bFocusOnEditableField(false) {
CreateProcessMessageDelegates(process_message_delegates_);
// Create the browser-side router for query handling.
CefMessageRouterConfig config;
message_router_ = CefMessageRouterBrowserSide::Create(config);
// Register handlers with the router.
CreateMessageHandlers(message_handler_set_);
MessageHandlerSet::const_iterator it = message_handler_set_.begin();
for (; it != message_handler_set_.end(); ++it)
message_router_->AddHandler(*(it), false);
// Read command line settings.
CefRefPtr<CefCommandLine> command_line =
@ -120,6 +128,11 @@ bool ClientHandler::OnProcessMessageReceived(
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) {
if (message_router_->OnProcessMessageReceived(browser, source_process,
message)) {
return true;
}
// Check for messages from the client renderer.
std::string message_name = message->GetName();
if (message_name == client_renderer::kFocusedNodeChangedMessage) {
@ -131,16 +144,7 @@ bool ClientHandler::OnProcessMessageReceived(
return true;
}
bool handled = false;
// Execute delegate callbacks.
ProcessMessageDelegateSet::iterator it = process_message_delegates_.begin();
for (; it != process_message_delegates_.end() && !handled; ++it) {
handled = (*it)->OnProcessMessageReceived(this, browser, source_process,
message);
}
return handled;
return false;
}
void ClientHandler::OnBeforeContextMenu(
@ -343,6 +347,8 @@ bool ClientHandler::DoClose(CefRefPtr<CefBrowser> browser) {
void ClientHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
REQUIRE_UI_THREAD();
message_router_->OnBeforeClose(browser);
if (m_BrowserId == browser->GetIdentifier()) {
// Free the browser pointer so that the browser can be destroyed
m_Browser = NULL;
@ -363,7 +369,17 @@ void ClientHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
}
if (--m_BrowserCount == 0) {
// All browser windows have closed. Quit the application message loop.
// All browser windows have closed.
// Remove and delete message router handlers.
MessageHandlerSet::const_iterator it = message_handler_set_.begin();
for (; it != message_handler_set_.end(); ++it) {
message_router_->RemoveHandler(*(it));
delete *(it);
}
message_handler_set_.clear();
message_router_ = NULL;
// Quit the application message loop.
AppQuitMessageLoop();
}
}
@ -412,10 +428,18 @@ void ClientHandler::OnLoadError(CefRefPtr<CefBrowser> browser,
frame->LoadString(ss.str(), failedUrl);
}
bool ClientHandler::OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool is_redirect) {
message_router_->OnBeforeBrowse(browser, frame);
return false;
}
CefRefPtr<CefResourceHandler> ClientHandler::GetResourceHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) {
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.
@ -469,6 +493,8 @@ void ClientHandler::OnProtocolExecution(CefRefPtr<CefBrowser> browser,
void ClientHandler::OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
TerminationStatus status) {
message_router_->OnRenderProcessTerminated(browser);
// Load the startup URL if that's not the website that we terminated on.
CefRefPtr<CefFrame> frame = browser->GetMainFrame();
std::string url = frame->GetURL();
@ -693,16 +719,15 @@ bool ClientHandler::Save(const std::string& path, const std::string& data) {
}
// static
void ClientHandler::CreateProcessMessageDelegates(
ProcessMessageDelegateSet& delegates) {
// Create the binding test delegates.
binding_test::CreateProcessMessageDelegates(delegates);
void ClientHandler::CreateMessageHandlers(MessageHandlerSet& handlers) {
// Create the dialog test handlers.
dialog_test::CreateMessageHandlers(handlers);
// Create the dialog test delegates.
dialog_test::CreateProcessMessageDelegates(delegates);
// Create the binding test handlers.
binding_test::CreateMessageHandlers(handlers);
// Create the window test delegates.
window_test::CreateProcessMessageDelegates(delegates);
// Create the window test handlers.
window_test::CreateMessageHandlers(handlers);
}
void ClientHandler::BuildTestMenu(CefRefPtr<CefMenuModel> model) {

View File

@ -11,6 +11,7 @@
#include <set>
#include <string>
#include "include/cef_client.h"
#include "include/wrapper/cef_message_router.h"
#include "cefclient/util.h"
@ -32,32 +33,14 @@ class ClientHandler : public CefClient,
public CefRenderHandler,
public CefRequestHandler {
public:
// Interface for process message delegates. Do not perform work in the
// RenderDelegate constructor.
class ProcessMessageDelegate : public virtual CefBase {
public:
// Called when a process message is received. Return true if the message was
// handled and should not be passed on to other handlers.
// ProcessMessageDelegates should check for unique message names to avoid
// interfering with each other.
virtual bool OnProcessMessageReceived(
CefRefPtr<ClientHandler> handler,
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) {
return false;
}
};
typedef std::set<CefRefPtr<ProcessMessageDelegate> >
ProcessMessageDelegateSet;
// Interface implemented to handle off-screen rendering.
class RenderHandler : public CefRenderHandler {
public:
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) =0;
};
typedef std::set<CefMessageRouterBrowserSide::Handler*> MessageHandlerSet;
ClientHandler();
virtual ~ClientHandler();
@ -174,6 +157,10 @@ class ClientHandler : public CefClient,
const CefString& failedUrl) OVERRIDE;
// CefRequestHandler methods
virtual bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool is_redirect) OVERRIDE;
virtual CefRefPtr<CefResourceHandler> GetResourceHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
@ -264,9 +251,9 @@ class ClientHandler : public CefClient,
void SetLoading(bool isLoading);
void SetNavState(bool canGoBack, bool canGoForward);
// Create all of ProcessMessageDelegate objects.
static void CreateProcessMessageDelegates(
ProcessMessageDelegateSet& delegates);
// Create all CefMessageRouterBrowserSide::Handler objects. They will be
// deleted when the ClientHandler is destroyed.
static void CreateMessageHandlers(MessageHandlerSet& handlers);
// Test context menu creation.
void BuildTestMenu(CefRefPtr<CefMenuModel> model);
@ -317,15 +304,19 @@ class ClientHandler : public CefClient,
// True if an editable field currently has focus.
bool m_bFocusOnEditableField;
// Registered delegates.
ProcessMessageDelegateSet process_message_delegates_;
// The startup URL.
std::string m_StartupURL;
// True if mouse cursor change is disabled.
bool m_bMouseCursorChangeDisabled;
// Handles the browser side of query routing. The renderer side is handled
// in client_renderer.cpp.
CefRefPtr<CefMessageRouterBrowserSide> message_router_;
// Set of Handlers registered with the message router.
MessageHandlerSet message_handler_set_;
// Number of currently existing browser windows. The application will exit
// when the number of windows reaches 0.
static int m_BrowserCount;

View File

@ -8,6 +8,7 @@
#include <string>
#include "include/cef_dom.h"
#include "include/wrapper/cef_message_router.h"
#include "cefclient/util.h"
namespace client_renderer {
@ -22,6 +23,26 @@ class ClientRenderDelegate : public ClientApp::RenderDelegate {
: last_node_is_editable_(false) {
}
virtual void OnWebKitInitialized(CefRefPtr<ClientApp> app) OVERRIDE {
// Create the renderer-side router for query handling.
CefMessageRouterConfig config;
message_router_ = CefMessageRouterRendererSide::Create(config);
}
virtual void OnContextCreated(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) OVERRIDE {
message_router_->OnContextCreated(browser, frame, context);
}
virtual void OnContextReleased(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) OVERRIDE {
message_router_->OnContextReleased(browser, frame, context);
}
virtual void OnFocusedNodeChanged(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
@ -37,9 +58,21 @@ class ClientRenderDelegate : public ClientApp::RenderDelegate {
}
}
virtual bool OnProcessMessageReceived(
CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) OVERRIDE {
return message_router_->OnProcessMessageReceived(
browser, source_process, message);
}
private:
bool last_node_is_editable_;
// Handles the renderer side of query routing.
CefRefPtr<CefMessageRouterRendererSide> message_router_;
IMPLEMENT_REFCOUNTING(ClientRenderDelegate);
};

View File

@ -11,54 +11,54 @@ namespace dialog_test {
namespace {
const char* kTestUrl = "http://tests/dialogs";
const char* kFileOpenMessageName = "DialogTest.FileOpen";
const char* kFileOpenMultipleMessageName = "DialogTest.FileOpenMultiple";
const char* kFileSaveMessageName = "DialogTest.FileSave";
const char kTestUrl[] = "http://tests/dialogs";
const char kFileOpenMessageName[] = "DialogTest.FileOpen";
const char kFileOpenMultipleMessageName[] = "DialogTest.FileOpenMultiple";
const char kFileSaveMessageName[] = "DialogTest.FileSave";
class RunFileDialogCallback : public CefRunFileDialogCallback {
// Callback executed when the file dialog is dismissed.
class DialogCallback : public CefRunFileDialogCallback {
public:
explicit RunFileDialogCallback(const std::string& message_name)
: message_name_(message_name) {
explicit DialogCallback(
CefRefPtr<CefMessageRouterBrowserSide::Callback> router_callback)
: router_callback_(router_callback) {
}
virtual void OnFileDialogDismissed(
CefRefPtr<CefBrowserHost> browser_host,
const std::vector<CefString>& file_paths) OVERRIDE {
// Send a message back to the render process with the same name and the list
// of file paths.
CefRefPtr<CefProcessMessage> message =
CefProcessMessage::Create(message_name_);
CefRefPtr<CefListValue> args = message->GetArgumentList();
CefRefPtr<CefListValue> val = CefListValue::Create();
for (int i = 0; i < static_cast<int>(file_paths.size()); ++i)
val->SetString(i, file_paths[i]);
args->SetList(0, val);
// Send a message back to the render process with the list of file paths.
std::string response;
for (int i = 0; i < static_cast<int>(file_paths.size()); ++i) {
if (!response.empty())
response += "|"; // Use a delimiter disallowed in file paths.
response += file_paths[i];
}
// This will result in a call to the callback registered via JavaScript in
// dialogs.html.
browser_host->GetBrowser()->SendProcessMessage(PID_RENDERER, message);
router_callback_->Success(response);
router_callback_ = NULL;
}
private:
std::string message_name_;
CefRefPtr<CefMessageRouterBrowserSide::Callback> router_callback_;
IMPLEMENT_REFCOUNTING(RunFileDialogCallback);
IMPLEMENT_REFCOUNTING(DialogCallback);
};
// Handle messages in the browser process.
class ProcessMessageDelegate : public ClientHandler::ProcessMessageDelegate {
class Handler : public CefMessageRouterBrowserSide::Handler {
public:
ProcessMessageDelegate() {
}
Handler() {}
// From ClientHandler::ProcessMessageDelegate.
virtual bool OnProcessMessageReceived(
CefRefPtr<ClientHandler> handler,
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) OVERRIDE {
std::string url = browser->GetMainFrame()->GetURL();
// Called due to cefQuery execution in dialogs.html.
virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) OVERRIDE {
// Only handle messages from the test URL.
const std::string& url = frame->GetURL();
if (url.find(kTestUrl) != 0)
return false;
@ -68,35 +68,31 @@ class ProcessMessageDelegate : public ClientHandler::ProcessMessageDelegate {
file_types.push_back(".log");
file_types.push_back(".patch");
std::string message_name = message->GetName();
CefRefPtr<DialogCallback> dialog_callback(new DialogCallback(callback));
const std::string& message_name = request;
if (message_name == kFileOpenMessageName) {
browser->GetHost()->RunFileDialog(FILE_DIALOG_OPEN, "My Open Dialog",
"test.txt", file_types, new RunFileDialogCallback(message_name));
return true;
"test.txt", file_types, dialog_callback.get());
} else if (message_name == kFileOpenMultipleMessageName) {
browser->GetHost()->RunFileDialog(FILE_DIALOG_OPEN_MULTIPLE,
"My Open Multiple Dialog", CefString(), file_types,
new RunFileDialogCallback(message_name));
return true;
dialog_callback.get());
} else if (message_name == kFileSaveMessageName) {
browser->GetHost()->RunFileDialog(FILE_DIALOG_SAVE, "My Save Dialog",
"test.txt", file_types, new RunFileDialogCallback(message_name));
return true;
"test.txt", file_types, dialog_callback.get());
} else {
ASSERT(false); // Not reached.
}
return false;
return true;
}
IMPLEMENT_REFCOUNTING(ProcessMessageDelegate);
};
} // namespace
void CreateProcessMessageDelegates(
ClientHandler::ProcessMessageDelegateSet& delegates) {
delegates.insert(new ProcessMessageDelegate);
void CreateMessageHandlers(ClientHandler::MessageHandlerSet& handlers) {
handlers.insert(new Handler());
}
} // namespace dialog_test

View File

@ -10,9 +10,8 @@
namespace dialog_test {
// Delegate creation. Called from ClientHandler.
void CreateProcessMessageDelegates(
ClientHandler::ProcessMessageDelegateSet& delegates);
/// Handler creation. Called from ClientHandler.
void CreateMessageHandlers(ClientHandler::MessageHandlerSet& handlers);
} // namespace dialog_test

View File

@ -3,15 +3,16 @@
<title>Binding Test</title>
<script language="JavaScript">
// Register the callback for messages from the browser process.
app.setMessageCallback('binding_test', function(name, args) {
document.getElementById('result').value = "Response: "+args[0];
});
// Send a message to the browser process.
// Send a query to the browser process.
function sendMessage() {
var msg = document.getElementById("message").value;
app.sendMessage('binding_test', [msg]);
// Results in a call to the OnQuery method in binding_test.cpp
window.cefQuery({
request: 'BindingTest:' + document.getElementById("message").value,
onSuccess: function(response) {
document.getElementById('result').value = 'Response: '+response;
},
onFailure: function(error_code, error_message) {}
});
}
</script>

View File

@ -35,14 +35,14 @@ function show_file_dialog(element, test) {
var message = 'DialogTest.' + test;
var target = document.getElementById(element);
// Register for the callback from OnFileDialogDismissed in dialog_test.cpp.
app.setMessageCallback(message, function(msg, paths) {
target.innerText = paths.join();
app.removeMessageCallback(message);
// Results in a call to the OnQuery method in dialog_test.cpp
window.cefQuery({
request: message,
onSuccess: function(response) {
target.innerText = response;
},
onFailure: function(error_code, error_message) {}
});
// This will result in a call to OnProcessMessageReceived in dialog_test.cpp.
app.sendMessage(message);
}
window.addEventListener('load', setup, false);

View File

@ -8,10 +8,6 @@
#LI11select {width: 75px;}
</style>
<script>
function sendBrowserMessage(paramString) {
app.sendMessage("osrtest", [paramString]);
}
function getElement(id) { return document.getElementById(id); }
function makeH1Red() { getElement('LI00').style.color='red'; }
function makeH1Black() { getElement('LI00').style.color='black'; }
@ -22,12 +18,13 @@
}
function onEventTest(event) {
var param = event.type;
var param = 'osr' + event.type;
if (event.type == "click")
param += event.button;
sendBrowserMessage(param);
// Results in a call to the OnQuery method in os_rendering_unittest.cc.
window.testQuery({request: param});
}
</script>

View File

@ -3,8 +3,12 @@
<title>Window Test</title>
<script>
function send_message(test, params) {
// This will result in a call to OnProcessMessageReceived in window_test.cpp.
app.sendMessage('WindowTest.' + test, params);
var message = 'WindowTest.' + test;
if (typeof params != 'undefined')
message += ':' + params;
// Results in a call to the OnQuery method in window_test.cpp.
window.cefQuery({'request' : message});
}
function minimize() {
@ -28,7 +32,7 @@ function position() {
if (isNaN(x) || isNaN(y) || isNaN(width) || isNaN(height))
alert('Please specify a valid numeric value.');
else
send_message('Position', [x, y, width, height]);
send_message('Position', x + ',' + y + ',' + width + ',' + height);
}
</script>
</head>

View File

@ -5,7 +5,9 @@
#include "cefclient/window_test.h"
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
#include "include/wrapper/cef_stream_resource_handler.h"
@ -13,56 +15,66 @@ namespace window_test {
namespace {
const char* kMessagePositionName = "WindowTest.Position";
const char* kMessageMinimizeName = "WindowTest.Minimize";
const char* kMessageMaximizeName = "WindowTest.Maximize";
const char* kMessageRestoreName = "WindowTest.Restore";
const char kTestUrl[] = "http://tests/window";
const char kMessagePositionName[] = "WindowTest.Position";
const char kMessageMinimizeName[] = "WindowTest.Minimize";
const char kMessageMaximizeName[] = "WindowTest.Maximize";
const char kMessageRestoreName[] = "WindowTest.Restore";
// Handle messages in the browser process.
class ProcessMessageDelegate : public ClientHandler::ProcessMessageDelegate {
class Handler : public CefMessageRouterBrowserSide::Handler {
public:
ProcessMessageDelegate() {
}
Handler() {}
// From ClientHandler::ProcessMessageDelegate.
virtual bool OnProcessMessageReceived(
CefRefPtr<ClientHandler> handler,
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) OVERRIDE {
std::string message_name = message->GetName();
if (message_name == kMessagePositionName) {
CefRefPtr<CefListValue> args = message->GetArgumentList();
if (args->GetSize() >= 4) {
int x = args->GetInt(0);
int y = args->GetInt(1);
int width = args->GetInt(2);
int height = args->GetInt(3);
SetPos(browser->GetHost()->GetWindowHandle(), x, y, width, height);
// Called due to cefBroadcast execution in window.html.
virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) OVERRIDE {
// Only handle messages from the test URL.
const std::string& url = frame->GetURL();
if (url.find(kTestUrl) != 0)
return false;
const std::string& message_name = request;
if (message_name.find(kMessagePositionName) == 0) {
// Parse the comma-delimited list of integer values.
std::vector<int> vec;
const std::string& vals =
message_name.substr(sizeof(kMessagePositionName));
std::stringstream ss(vals);
int i;
while (ss >> i) {
vec.push_back(i);
if (ss.peek() == ',')
ss.ignore();
}
if (vec.size() == 4) {
SetPos(browser->GetHost()->GetWindowHandle(),
vec[0], vec[1], vec[2], vec[3]);
}
return true;
} else if (message_name == kMessageMinimizeName) {
Minimize(browser->GetHost()->GetWindowHandle());
return true;
} else if (message_name == kMessageMaximizeName) {
Maximize(browser->GetHost()->GetWindowHandle());
return true;
} else if (message_name == kMessageRestoreName) {
Restore(browser->GetHost()->GetWindowHandle());
return true;
} else {
ASSERT(false); // Not reached.
}
return false;
callback->Success("");
return true;
}
IMPLEMENT_REFCOUNTING(ProcessMessageDelegate);
};
} // namespace
void CreateProcessMessageDelegates(
ClientHandler::ProcessMessageDelegateSet& delegates) {
delegates.insert(new ProcessMessageDelegate);
void CreateMessageHandlers(ClientHandler::MessageHandlerSet& handlers) {
handlers.insert(new Handler());
}
void ModifyBounds(const CefRect& display, CefRect& window) {

View File

@ -10,9 +10,8 @@
namespace window_test {
// Delegate creation. Called from ClientHandler.
void CreateProcessMessageDelegates(
ClientHandler::ProcessMessageDelegateSet& delegates);
/// Handler creation. Called from ClientHandler.
void CreateMessageHandlers(ClientHandler::MessageHandlerSet& handlers);
// Fit |window| inside |display|. Coordinates are relative to the upper-left
// corner of the display.

View File

@ -0,0 +1,718 @@
// Copyright (c) 2014 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 <list>
#include "testing/gtest/include/gtest/gtest.h"
#include "libcef_dll/wrapper/cef_browser_info_map.h"
namespace {
struct MyObject {
MyObject(int val = 0) : member(val) {}
int member;
};
int g_destruct_ct = 0;
struct MyObjectTraits {
static void Destruct(MyObject info) {
g_destruct_ct++;
}
};
typedef CefBrowserInfoMap<int, MyObject, MyObjectTraits> MyObjectMap;
class MyVisitor : public MyObjectMap::Visitor {
public:
MyVisitor(bool remove = false,
int remove_browser_id = 0,
InfoIdType remove_info_id = 0)
: remove_(remove),
remove_browser_id_(remove_browser_id),
remove_info_id_(remove_info_id) {}
virtual bool OnNextInfo(int browser_id,
InfoIdType info_id,
InfoObjectType info,
bool* remove) OVERRIDE {
Info info_rec;
info_rec.browser_id = browser_id;
info_rec.info_id = info_id;
info_rec.info = info;
info_list_.push_back(info_rec);
// Based on test configuration remove no objects, all objects, or only the
// specified object.
*remove = remove_ || (browser_id == remove_browser_id_ &&
info_id == remove_info_id_);
return true;
}
// Returns true if the specified info was passed to OnNextInfo. Removes the
// record if found.
bool Exists(int browser_id,
InfoIdType info_id,
InfoObjectType info) {
InfoList::iterator it = info_list_.begin();
for (; it != info_list_.end(); ++it) {
const Info& found_info = *it;
if (browser_id == found_info.browser_id &&
info_id == found_info.info_id &&
info.member == found_info.info.member) {
info_list_.erase(it);
return true;
}
}
return false;
}
size_t info_size() const { return info_list_.size(); }
private:
bool remove_;
int remove_browser_id_;
InfoIdType remove_info_id_;
struct Info {
int browser_id;
InfoIdType info_id;
InfoObjectType info;
};
// Track calls to OnNextInfo.
typedef std::list<Info> InfoList;
InfoList info_list_;
};
} // namespace
TEST(BrowserInfoMapTest, AddSingleBrowser) {
MyObjectMap map;
const int kBrowserId = 1;
g_destruct_ct = 0;
EXPECT_EQ(0U, map.size());
EXPECT_EQ(0U, map.size(kBrowserId));
MyObject obj1(1);
map.Add(kBrowserId, 1, obj1);
EXPECT_EQ(1U, map.size());
EXPECT_EQ(1U, map.size(kBrowserId));
MyObject obj2(2);
map.Add(kBrowserId, 2, obj2);
EXPECT_EQ(2U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId));
MyObject obj3(3);
map.Add(kBrowserId, 3, obj3);
EXPECT_EQ(3U, map.size());
EXPECT_EQ(3U, map.size(kBrowserId));
EXPECT_EQ(0, g_destruct_ct);
map.clear(kBrowserId);
EXPECT_EQ(0U, map.size());
EXPECT_EQ(0U, map.size(kBrowserId));
EXPECT_EQ(3, g_destruct_ct);
}
TEST(BrowserInfoMapTest, AddMultipleBrowsers) {
MyObjectMap map;
const int kBrowserId1 = 1;
const int kBrowserId2 = 2;
g_destruct_ct = 0;
EXPECT_EQ(0U, map.size());
EXPECT_EQ(0U, map.size(kBrowserId1));
EXPECT_EQ(0U, map.size(kBrowserId2));
MyObject obj1(1);
map.Add(kBrowserId1, 1, obj1);
EXPECT_EQ(1U, map.size());
EXPECT_EQ(1U, map.size(kBrowserId1));
EXPECT_EQ(0U, map.size(kBrowserId2));
MyObject obj2(2);
map.Add(kBrowserId1, 2, obj2);
EXPECT_EQ(2U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId1));
EXPECT_EQ(0U, map.size(kBrowserId2));
MyObject obj3(3);
map.Add(kBrowserId2, 3, obj3);
EXPECT_EQ(3U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId1));
EXPECT_EQ(1U, map.size(kBrowserId2));
MyObject obj4(4);
map.Add(kBrowserId2, 4, obj4);
EXPECT_EQ(4U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId1));
EXPECT_EQ(2U, map.size(kBrowserId2));
EXPECT_EQ(0, g_destruct_ct);
map.clear(kBrowserId1);
EXPECT_EQ(2U, map.size());
EXPECT_EQ(0U, map.size(kBrowserId1));
EXPECT_EQ(2U, map.size(kBrowserId2));
EXPECT_EQ(2, g_destruct_ct);
map.clear(kBrowserId2);
EXPECT_EQ(0U, map.size());
EXPECT_EQ(0U, map.size(kBrowserId1));
EXPECT_EQ(0U, map.size(kBrowserId2));
EXPECT_EQ(4, g_destruct_ct);
}
TEST(BrowserInfoMapTest, FindSingleBrowser) {
MyObjectMap map;
const int kBrowserId = 1;
g_destruct_ct = 0;
// obj1 not added yet.
MyObject nf1 = map.Find(kBrowserId, 1, NULL);
EXPECT_EQ(0, nf1.member);
MyObject obj1(1);
map.Add(kBrowserId, 1, obj1);
// obj1 should exist.
MyObject f1 = map.Find(kBrowserId, 1, NULL);
EXPECT_EQ(obj1.member, f1.member);
// obj2 not added yet.
MyObject nf2 = map.Find(kBrowserId, 2, NULL);
EXPECT_EQ(0, nf2.member);
MyObject obj2(2);
map.Add(kBrowserId, 2, obj2);
// obj2 should exist.
MyObject f2 = map.Find(kBrowserId, 2, NULL);
EXPECT_EQ(obj2.member, f2.member);
// find obj1 again.
MyObject f1b = map.Find(kBrowserId, 1, NULL);
EXPECT_EQ(obj1.member, f1b.member);
// find obj2 again.
MyObject f2b = map.Find(kBrowserId, 2, NULL);
EXPECT_EQ(obj2.member, f2b.member);
// doesn't exist.
MyObject nf3 = map.Find(kBrowserId, 3, NULL);
EXPECT_EQ(0, nf3.member);
MyObject nf4 = map.Find(10, 1, NULL);
EXPECT_EQ(0, nf4.member);
MyObject nf5 = map.Find(10, 3, NULL);
EXPECT_EQ(0, nf5.member);
EXPECT_EQ(0, g_destruct_ct);
map.clear();
EXPECT_EQ(2, g_destruct_ct);
}
TEST(BrowserInfoMapTest, FindMultipleBrowsers) {
MyObjectMap map;
const int kBrowserId1 = 1;
const int kBrowserId2 = 2;
g_destruct_ct = 0;
// obj1 not added yet.
MyObject nf1 = map.Find(kBrowserId1, 1, NULL);
EXPECT_EQ(0, nf1.member);
MyObject obj1(1);
map.Add(kBrowserId1, 1, obj1);
// obj1 should exist.
MyObject f1 = map.Find(kBrowserId1, 1, NULL);
EXPECT_EQ(obj1.member, f1.member);
// obj2 not added yet.
MyObject nf2 = map.Find(kBrowserId1, 2, NULL);
EXPECT_EQ(0, nf2.member);
MyObject obj2(2);
map.Add(kBrowserId1, 2, obj2);
// obj2 should exist.
MyObject f2 = map.Find(kBrowserId1, 2, NULL);
EXPECT_EQ(obj2.member, f2.member);
// obj3 not added yet.
MyObject nf3 = map.Find(kBrowserId2, 3, NULL);
EXPECT_EQ(0, nf3.member);
MyObject obj3(3);
map.Add(kBrowserId2, 3, obj3);
// obj3 should exist.
MyObject f3 = map.Find(kBrowserId2, 3, NULL);
EXPECT_EQ(obj3.member, f3.member);
// obj4 not added yet.
MyObject nf4 = map.Find(kBrowserId2, 4, NULL);
EXPECT_EQ(0, nf4.member);
MyObject obj4(4);
map.Add(kBrowserId2, 4, obj4);
// obj4 should exist.
MyObject f4 = map.Find(kBrowserId2, 4, NULL);
EXPECT_EQ(obj4.member, f4.member);
// obj1-3 should exist.
MyObject f1b = map.Find(kBrowserId1, 1, NULL);
EXPECT_EQ(obj1.member, f1b.member);
MyObject f2b = map.Find(kBrowserId1, 2, NULL);
EXPECT_EQ(obj2.member, f2b.member);
MyObject f3b = map.Find(kBrowserId2, 3, NULL);
EXPECT_EQ(obj3.member, f3b.member);
// wrong browser
MyObject nf5 = map.Find(kBrowserId1, 4, NULL);
EXPECT_EQ(0, nf5.member);
MyObject nf6 = map.Find(kBrowserId2, 1, NULL);
EXPECT_EQ(0, nf6.member);
// deosn't exist
MyObject nf7 = map.Find(kBrowserId2, 5, NULL);
EXPECT_EQ(0, nf7.member);
MyObject nf8 = map.Find(8, 1, NULL);
EXPECT_EQ(0, nf8.member);
MyObject nf9 = map.Find(8, 10, NULL);
EXPECT_EQ(0, nf9.member);
EXPECT_EQ(0, g_destruct_ct);
map.clear();
EXPECT_EQ(4, g_destruct_ct);
}
TEST(BrowserInfoMapTest, Find) {
MyObjectMap map;
const int kBrowserId1 = 1;
const int kBrowserId2 = 2;
g_destruct_ct = 0;
MyObject obj1(1);
map.Add(kBrowserId1, 1, obj1);
MyObject obj2(2);
map.Add(kBrowserId1, 2, obj2);
MyObject obj3(3);
map.Add(kBrowserId2, 3, obj3);
MyObject obj4(4);
map.Add(kBrowserId2, 4, obj4);
EXPECT_EQ(4U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId1));
EXPECT_EQ(2U, map.size(kBrowserId2));
// should only visit the single object
MyVisitor visitor;
map.Find(kBrowserId2, 4, &visitor);
EXPECT_EQ(1U, visitor.info_size());
EXPECT_TRUE(visitor.Exists(kBrowserId2, 4, obj4));
EXPECT_EQ(0U, visitor.info_size());
EXPECT_EQ(4U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId1));
EXPECT_EQ(2U, map.size(kBrowserId2));
EXPECT_EQ(0, g_destruct_ct);
map.clear();
EXPECT_EQ(4, g_destruct_ct);
}
TEST(BrowserInfoMapTest, FindAndRemove) {
MyObjectMap map;
const int kBrowserId1 = 1;
const int kBrowserId2 = 2;
g_destruct_ct = 0;
MyObject obj1(1);
map.Add(kBrowserId1, 1, obj1);
MyObject obj2(2);
map.Add(kBrowserId1, 2, obj2);
MyObject obj3(3);
map.Add(kBrowserId2, 3, obj3);
MyObject obj4(4);
map.Add(kBrowserId2, 4, obj4);
EXPECT_EQ(4U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId1));
EXPECT_EQ(2U, map.size(kBrowserId2));
// should only visit and remove the single object.
MyVisitor visitor(true);
map.Find(kBrowserId1, 2, &visitor);
EXPECT_EQ(1U, visitor.info_size());
EXPECT_TRUE(visitor.Exists(kBrowserId1, 2, obj2));
EXPECT_EQ(0U, visitor.info_size());
EXPECT_EQ(3U, map.size());
EXPECT_EQ(1U, map.size(kBrowserId1));
EXPECT_EQ(2U, map.size(kBrowserId2));
// visited object shouldn't exist
MyObject nf2 = map.Find(kBrowserId1, 2, NULL);
EXPECT_EQ(0, nf2.member);
// other objects should exist
MyObject nf1 = map.Find(kBrowserId1, 1, NULL);
EXPECT_EQ(obj1.member, nf1.member);
MyObject nf3 = map.Find(kBrowserId2, 3, NULL);
EXPECT_EQ(obj3.member, nf3.member);
MyObject nf4 = map.Find(kBrowserId2, 4, NULL);
EXPECT_EQ(obj4.member, nf4.member);
EXPECT_EQ(0, g_destruct_ct);
map.clear();
// should destruct the remaining 3 objects
EXPECT_EQ(3, g_destruct_ct);
}
TEST(BrowserInfoMapTest, FindAll) {
MyObjectMap map;
const int kBrowserId1 = 1;
const int kBrowserId2 = 2;
g_destruct_ct = 0;
MyObject obj1(1);
map.Add(kBrowserId1, 1, obj1);
MyObject obj2(2);
map.Add(kBrowserId1, 2, obj2);
MyObject obj3(3);
map.Add(kBrowserId2, 3, obj3);
MyObject obj4(4);
map.Add(kBrowserId2, 4, obj4);
EXPECT_EQ(4U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId1));
EXPECT_EQ(2U, map.size(kBrowserId2));
// should visit all objects
MyVisitor visitor;
map.FindAll(&visitor);
EXPECT_EQ(4U, visitor.info_size());
EXPECT_TRUE(visitor.Exists(kBrowserId1, 1, obj1));
EXPECT_TRUE(visitor.Exists(kBrowserId1, 2, obj2));
EXPECT_TRUE(visitor.Exists(kBrowserId2, 3, obj3));
EXPECT_TRUE(visitor.Exists(kBrowserId2, 4, obj4));
// should be no unexpected visits
EXPECT_EQ(0U, visitor.info_size());
EXPECT_EQ(4U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId1));
EXPECT_EQ(2U, map.size(kBrowserId2));
EXPECT_EQ(0, g_destruct_ct);
map.clear();
EXPECT_EQ(4, g_destruct_ct);
}
TEST(BrowserInfoMapTest, FindAllByBrowser) {
MyObjectMap map;
const int kBrowserId1 = 1;
const int kBrowserId2 = 2;
g_destruct_ct = 0;
MyObject obj1(1);
map.Add(kBrowserId1, 1, obj1);
MyObject obj2(2);
map.Add(kBrowserId1, 2, obj2);
MyObject obj3(3);
map.Add(kBrowserId2, 3, obj3);
MyObject obj4(4);
map.Add(kBrowserId2, 4, obj4);
EXPECT_EQ(4U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId1));
EXPECT_EQ(2U, map.size(kBrowserId2));
// should only visit browser2 objects
MyVisitor visitor;
map.FindAll(kBrowserId2, &visitor);
EXPECT_EQ(2U, visitor.info_size());
EXPECT_TRUE(visitor.Exists(kBrowserId2, 3, obj3));
EXPECT_TRUE(visitor.Exists(kBrowserId2, 4, obj4));
// should be no unexpected visits
EXPECT_EQ(0U, visitor.info_size());
EXPECT_EQ(4U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId1));
EXPECT_EQ(2U, map.size(kBrowserId2));
EXPECT_EQ(0, g_destruct_ct);
map.clear();
EXPECT_EQ(4, g_destruct_ct);
}
TEST(BrowserInfoMapTest, FindAllAndRemoveAll) {
MyObjectMap map;
const int kBrowserId1 = 1;
const int kBrowserId2 = 2;
g_destruct_ct = 0;
MyObject obj1(1);
map.Add(kBrowserId1, 1, obj1);
MyObject obj2(2);
map.Add(kBrowserId1, 2, obj2);
MyObject obj3(3);
map.Add(kBrowserId2, 3, obj3);
MyObject obj4(4);
map.Add(kBrowserId2, 4, obj4);
EXPECT_EQ(4U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId1));
EXPECT_EQ(2U, map.size(kBrowserId2));
// should visit all objects
MyVisitor visitor(true);
map.FindAll(&visitor);
EXPECT_EQ(4U, visitor.info_size());
EXPECT_TRUE(visitor.Exists(kBrowserId1, 1, obj1));
EXPECT_TRUE(visitor.Exists(kBrowserId1, 2, obj2));
EXPECT_TRUE(visitor.Exists(kBrowserId2, 3, obj3));
EXPECT_TRUE(visitor.Exists(kBrowserId2, 4, obj4));
// should be no unexpected visits
EXPECT_EQ(0U, visitor.info_size());
EXPECT_EQ(0U, map.size());
EXPECT_EQ(0U, map.size(kBrowserId1));
EXPECT_EQ(0U, map.size(kBrowserId2));
EXPECT_EQ(0, g_destruct_ct);
}
TEST(BrowserInfoMapTest, FindAllAndRemoveOne) {
MyObjectMap map;
const int kBrowserId1 = 1;
const int kBrowserId2 = 2;
g_destruct_ct = 0;
MyObject obj1(1);
map.Add(kBrowserId1, 1, obj1);
MyObject obj2(2);
map.Add(kBrowserId1, 2, obj2);
MyObject obj3(3);
map.Add(kBrowserId2, 3, obj3);
MyObject obj4(4);
map.Add(kBrowserId2, 4, obj4);
EXPECT_EQ(4U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId1));
EXPECT_EQ(2U, map.size(kBrowserId2));
// should visit all objects and remove one
MyVisitor visitor(false, kBrowserId2, 3);
map.FindAll(&visitor);
EXPECT_EQ(4U, visitor.info_size());
EXPECT_TRUE(visitor.Exists(kBrowserId1, 1, obj1));
EXPECT_TRUE(visitor.Exists(kBrowserId1, 2, obj2));
EXPECT_TRUE(visitor.Exists(kBrowserId2, 3, obj3));
EXPECT_TRUE(visitor.Exists(kBrowserId2, 4, obj4));
// should be no unexpected visits
EXPECT_EQ(0U, visitor.info_size());
EXPECT_EQ(3U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId1));
EXPECT_EQ(1U, map.size(kBrowserId2));
// removed object shouldn't exist
MyObject nf3 = map.Find(kBrowserId2, 3, NULL);
EXPECT_EQ(0, nf3.member);
// other objects should exist
MyObject f1 = map.Find(kBrowserId1, 1, NULL);
EXPECT_EQ(obj1.member, f1.member);
MyObject f2 = map.Find(kBrowserId1, 2, NULL);
EXPECT_EQ(obj2.member, f2.member);
MyObject f4 = map.Find(kBrowserId2, 4, NULL);
EXPECT_EQ(obj4.member, f4.member);
EXPECT_EQ(0, g_destruct_ct);
map.clear();
// should destruct the remaining 3 objects
EXPECT_EQ(3, g_destruct_ct);
}
TEST(BrowserInfoMapTest, FindAllAndRemoveAllByBrowser) {
MyObjectMap map;
const int kBrowserId1 = 1;
const int kBrowserId2 = 2;
g_destruct_ct = 0;
MyObject obj1(1);
map.Add(kBrowserId1, 1, obj1);
MyObject obj2(2);
map.Add(kBrowserId1, 2, obj2);
MyObject obj3(3);
map.Add(kBrowserId2, 3, obj3);
MyObject obj4(4);
map.Add(kBrowserId2, 4, obj4);
EXPECT_EQ(4U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId1));
EXPECT_EQ(2U, map.size(kBrowserId2));
// should only visit browser1 objects
MyVisitor visitor(true);
map.FindAll(kBrowserId1, &visitor);
EXPECT_EQ(2U, visitor.info_size());
EXPECT_TRUE(visitor.Exists(kBrowserId1, 1, obj1));
EXPECT_TRUE(visitor.Exists(kBrowserId1, 2, obj2));
// should be no unexpected visits
EXPECT_EQ(0U, visitor.info_size());
EXPECT_EQ(2U, map.size());
EXPECT_EQ(0U, map.size(kBrowserId1));
EXPECT_EQ(2U, map.size(kBrowserId2));
// browser1 objects shouldn't exist
MyObject nf1 = map.Find(kBrowserId1, 1, NULL);
EXPECT_EQ(0, nf1.member);
MyObject nf2 = map.Find(kBrowserId1, 2, NULL);
EXPECT_EQ(0, nf2.member);
// browser 2 objects should exist
MyObject f3 = map.Find(kBrowserId2, 3, NULL);
EXPECT_EQ(obj3.member, f3.member);
MyObject f4 = map.Find(kBrowserId2, 4, NULL);
EXPECT_EQ(obj4.member, f4.member);
EXPECT_EQ(0, g_destruct_ct);
map.clear();
// should destruct the remaining 2 objects
EXPECT_EQ(2, g_destruct_ct);
}
TEST(BrowserInfoMapTest, FindAllAndRemoveOneByBrowser) {
MyObjectMap map;
const int kBrowserId1 = 1;
const int kBrowserId2 = 2;
g_destruct_ct = 0;
MyObject obj1(1);
map.Add(kBrowserId1, 1, obj1);
MyObject obj2(2);
map.Add(kBrowserId1, 2, obj2);
MyObject obj3(3);
map.Add(kBrowserId2, 3, obj3);
MyObject obj4(4);
map.Add(kBrowserId2, 4, obj4);
EXPECT_EQ(4U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId1));
EXPECT_EQ(2U, map.size(kBrowserId2));
// should visit browser2 objects and remove one
MyVisitor visitor(false, kBrowserId2, 4);
map.FindAll(kBrowserId2, &visitor);
EXPECT_EQ(2U, visitor.info_size());
EXPECT_TRUE(visitor.Exists(kBrowserId2, 3, obj3));
EXPECT_TRUE(visitor.Exists(kBrowserId2, 4, obj4));
// should be no unexpected visits
EXPECT_EQ(0U, visitor.info_size());
EXPECT_EQ(3U, map.size());
EXPECT_EQ(2U, map.size(kBrowserId1));
EXPECT_EQ(1U, map.size(kBrowserId2));
// removed object shouldn't exist
MyObject nf4 = map.Find(kBrowserId2, 4, NULL);
EXPECT_EQ(0, nf4.member);
// other objects should exist
MyObject f1 = map.Find(kBrowserId1, 1, NULL);
EXPECT_EQ(obj1.member, f1.member);
MyObject f2 = map.Find(kBrowserId1, 2, NULL);
EXPECT_EQ(obj2.member, f2.member);
MyObject f3 = map.Find(kBrowserId2, 3, NULL);
EXPECT_EQ(obj3.member, f3.member);
EXPECT_EQ(0, g_destruct_ct);
map.clear();
// should destruct the remaining 3 objects
EXPECT_EQ(3, g_destruct_ct);
}
namespace {
class MyHeapObject {
public:
MyHeapObject(int* destroy_ct) : destroy_ct_(destroy_ct) {}
~MyHeapObject() {
(*destroy_ct_)++;
}
private:
int* destroy_ct_;
};
} // namespace
TEST(BrowserInfoMapTest, DefaultTraits) {
CefBrowserInfoMap<int, MyHeapObject*> map;
int destroy_ct = 0;
map.Add(1, 1, new MyHeapObject(&destroy_ct));
map.Add(1, 2, new MyHeapObject(&destroy_ct));
map.Add(2, 1, new MyHeapObject(&destroy_ct));
map.Add(2, 2, new MyHeapObject(&destroy_ct));
map.Add(3, 1, new MyHeapObject(&destroy_ct));
map.Add(3, 2, new MyHeapObject(&destroy_ct));
EXPECT_EQ(6U, map.size());
map.clear(1);
EXPECT_EQ(4U, map.size());
EXPECT_EQ(2, destroy_ct);
map.clear();
EXPECT_EQ(0U, map.size());
EXPECT_EQ(6, destroy_ct);
}

View File

@ -21,6 +21,16 @@ void ClientApp::CreateBrowserDelegates(BrowserDelegateSet& delegates) {
// static
void ClientApp::CreateRenderDelegates(RenderDelegateSet& delegates) {
// Bring in the routing test handler delegate.
extern void CreateRoutingTestHandlerDelegate(
ClientApp::RenderDelegateSet& delegates);
CreateRoutingTestHandlerDelegate(delegates);
// Bring in the message router tests.
extern void CreateMessageRouterRendererTests(
ClientApp::RenderDelegateSet& delegates);
CreateMessageRouterRendererTests(delegates);
// Bring in the process message tests.
extern void CreateProcessMessageRendererTests(
ClientApp::RenderDelegateSet& delegates);

View File

@ -3,7 +3,7 @@
// can be found in the LICENSE file.
#include "include/cef_runnable.h"
#include "tests/unittests/test_handler.h"
#include "tests/unittests/routing_test_handler.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@ -13,7 +13,7 @@ const char kUnloadDialogText[] = "Are you sure?";
const char kUnloadMsg[] = "LifeSpanTestHandler.Unload";
// Browser side.
class LifeSpanTestHandler : public TestHandler {
class LifeSpanTestHandler : public RoutingTestHandler {
public:
struct Settings {
Settings()
@ -35,9 +35,9 @@ class LifeSpanTestHandler : public TestHandler {
virtual void RunTest() OVERRIDE {
// Add the resources that we will navigate to/from.
std::string page = "<html><script>";
page += "window.onunload = function() { app.sendMessage('" +
std::string(kUnloadMsg) + "'); };";
page += "window.onunload = function() { window.testQuery({request:'" +
std::string(kUnloadMsg) + "'}); };";
if (settings_.add_onunload_handler) {
page += "window.onbeforeunload = function() { return '" +
@ -53,7 +53,7 @@ class LifeSpanTestHandler : public TestHandler {
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE {
got_after_created_.yes();
TestHandler::OnAfterCreated(browser);
RoutingTestHandler::OnAfterCreated(browser);
}
virtual bool DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE {
@ -78,7 +78,7 @@ class LifeSpanTestHandler : public TestHandler {
EXPECT_TRUE(browser->IsSame(GetBrowser()));
}
TestHandler::OnBeforeClose(browser);
RoutingTestHandler::OnBeforeClose(browser);
}
virtual bool OnBeforeUnloadDialog(
@ -116,18 +116,18 @@ class LifeSpanTestHandler : public TestHandler {
browser->GetHost()->CloseBrowser(settings_.force_close);
}
virtual bool OnProcessMessageReceived(
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) OVERRIDE {
const std::string& message_name = message->GetName();
if (message_name == kUnloadMsg) {
virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) OVERRIDE {
if (request.ToString() == kUnloadMsg) {
if (!executing_delay_close_)
got_unload_message_.yes();
return true;
}
return false;
callback->Success("");
return true;
}
TrackCallback got_after_created_;

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
#include "tests/cefclient/client_app.h"
#include "tests/cefclient/resource_util.h"
#include "tests/unittests/test_handler.h"
#include "tests/unittests/routing_test_handler.h"
#include "base/logging.h"
#include "ui/events/keycodes/keyboard_codes.h"
@ -190,7 +190,7 @@ enum OSRTestType {
};
// Used in the browser process.
class OSRTestHandler : public TestHandler,
class OSRTestHandler : public RoutingTestHandler,
public CefRenderHandler,
public CefContextMenuHandler {
public:
@ -224,7 +224,7 @@ class OSRTestHandler : public TestHandler,
EXPECT_TRUE(browser->GetHost()->IsWindowRenderingDisabled());
DestroySucceededTestSoon();
}
TestHandler::OnAfterCreated(browser);
RoutingTestHandler::OnAfterCreated(browser);
}
virtual void OnLoadEnd(CefRefPtr<CefBrowser> browser,
@ -247,37 +247,33 @@ class OSRTestHandler : public TestHandler,
}
}
virtual bool OnProcessMessageReceived(
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) OVERRIDE {
virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) OVERRIDE {
EXPECT_TRUE(browser.get());
EXPECT_EQ(PID_RENDERER, source_process);
EXPECT_TRUE(message.get());
EXPECT_TRUE(message->IsReadOnly());
if (!started())
return false;
const CefString kMessageName = "osrtest";
EXPECT_EQ(kMessageName, message->GetName());
CefString stringParam = message->GetArgumentList()->GetString(0);
const std::string& messageStr = request;
switch(test_type_) {
case OSR_TEST_FOCUS:
EXPECT_EQ(stringParam, std::string("focus"));
EXPECT_STREQ(messageStr.c_str(), "osrfocus");
DestroySucceededTestSoon();
break;
case OSR_TEST_CLICK_LEFT:
EXPECT_EQ(stringParam, std::string("click0"));
EXPECT_STREQ(messageStr.c_str(), "osrclick0");
DestroySucceededTestSoon();
break;
case OSR_TEST_CLICK_MIDDLE:
EXPECT_EQ(stringParam, std::string("click1"));
EXPECT_STREQ(messageStr.c_str(), "osrclick1");
DestroySucceededTestSoon();
break;
case OSR_TEST_MOUSE_MOVE:
EXPECT_EQ(stringParam, std::string("mousemove"));
EXPECT_STREQ(messageStr.c_str(), "osrmousemove");
DestroySucceededTestSoon();
break;
default:
@ -285,6 +281,7 @@ class OSRTestHandler : public TestHandler,
break;
}
callback->Success("");
return true;
}

View File

@ -12,11 +12,8 @@
namespace {
// Unique values for the SendRecv test.
const char* kSendRecvUrlNative =
"http://tests/ProcessMessageTest.SendRecv/Native";
const char* kSendRecvUrlJavaScript =
"http://tests/ProcessMessageTest.SendRecv/JavaScript";
const char* kSendRecvMsg = "ProcessMessageTest.SendRecv";
const char kSendRecvUrl[] = "http://tests/ProcessMessageTest.SendRecv";
const char kSendRecvMsg[] = "ProcessMessageTest.SendRecv";
// Creates a test message.
CefRefPtr<CefProcessMessage> CreateTestMessage() {
@ -55,7 +52,7 @@ class SendRecvRendererTest : public ClientApp::RenderDelegate {
EXPECT_TRUE(message.get());
std::string url = browser->GetMainFrame()->GetURL();
if (url == kSendRecvUrlNative) {
if (url == kSendRecvUrl) {
// Echo the message back to the sender natively.
EXPECT_TRUE(browser->SendProcessMessage(PID_BROWSER, message));
return true;
@ -72,33 +69,14 @@ class SendRecvRendererTest : public ClientApp::RenderDelegate {
// Browser side.
class SendRecvTestHandler : public TestHandler {
public:
explicit SendRecvTestHandler(bool native)
: native_(native) {
SendRecvTestHandler() {
}
virtual void RunTest() OVERRIDE {
message_ = CreateTestMessage();
if (native_) {
// Native test.
AddResource(kSendRecvUrlNative, "<html><body>TEST NATIVE</body></html>",
"text/html");
CreateBrowser(kSendRecvUrlNative);
} else {
// JavaScript test.
std::string content =
"<html><head>\n"
"<script>\n"
"function cb(name, args) {\n"
" app.sendMessage(name, args);\n"
"}\n"
"app.setMessageCallback('"+std::string(kSendRecvMsg)+"', cb);\n"
"</script>\n"
"<body>TEST JAVASCRIPT</body>\n"
"</head></html>";
AddResource(kSendRecvUrlJavaScript, content, "text/html");
CreateBrowser(kSendRecvUrlJavaScript);
}
AddResource(kSendRecvUrl, "<html><body>TEST</body></html>", "text/html");
CreateBrowser(kSendRecvUrl);
}
virtual void OnLoadEnd(CefRefPtr<CefBrowser> browser,
@ -128,30 +106,21 @@ class SendRecvTestHandler : public TestHandler {
return true;
}
bool native_;
CefRefPtr<CefProcessMessage> message_;
TrackCallback got_message_;
};
} // namespace
// Verify native send and recieve
TEST(ProcessMessageTest, SendRecvNative) {
CefRefPtr<SendRecvTestHandler> handler = new SendRecvTestHandler(true);
// Verify send and recieve.
TEST(ProcessMessageTest, SendRecv) {
CefRefPtr<SendRecvTestHandler> handler = new SendRecvTestHandler();
handler->ExecuteTest();
EXPECT_TRUE(handler->got_message_);
}
// Verify JavaScript send and recieve
TEST(ProcessMessageTest, SendRecvJavaScript) {
CefRefPtr<SendRecvTestHandler> handler = new SendRecvTestHandler(false);
handler->ExecuteTest();
EXPECT_TRUE(handler->got_message_);
}
// Verify create
// Verify create.
TEST(ProcessMessageTest, Create) {
CefRefPtr<CefProcessMessage> message =
CefProcessMessage::Create(kSendRecvMsg);
@ -167,7 +136,7 @@ TEST(ProcessMessageTest, Create) {
EXPECT_FALSE(args->IsReadOnly());
}
// Verify copy
// Verify copy.
TEST(ProcessMessageTest, Copy) {
CefRefPtr<CefProcessMessage> message = CreateTestMessage();
CefRefPtr<CefProcessMessage> message2 = message->Copy();

View File

@ -0,0 +1,104 @@
// Copyright (c) 2014 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 "tests/unittests/routing_test_handler.h"
#include "tests/cefclient/client_app.h"
namespace {
void SetRouterConfig(CefMessageRouterConfig& config) {
config.js_query_function = "testQuery";
config.js_cancel_function = "testQueryCancel";
}
// Handle the renderer side of the routing implementation.
class RoutingRenderDelegate : public ClientApp::RenderDelegate {
public:
RoutingRenderDelegate() {}
virtual void OnWebKitInitialized(CefRefPtr<ClientApp> app) OVERRIDE {
// Create the renderer-side router for query handling.
CefMessageRouterConfig config;
SetRouterConfig(config);
message_router_ = CefMessageRouterRendererSide::Create(config);
}
virtual void OnContextCreated(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) OVERRIDE {
message_router_->OnContextCreated(browser, frame, context);
}
virtual void OnContextReleased(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) OVERRIDE {
message_router_->OnContextReleased(browser, frame, context);
}
virtual bool OnProcessMessageReceived(
CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) OVERRIDE {
return message_router_->OnProcessMessageReceived(
browser, source_process, message);
}
private:
CefRefPtr<CefMessageRouterRendererSide> message_router_;
IMPLEMENT_REFCOUNTING(RoutingRenderDelegate);
};
} // namespace
RoutingTestHandler::RoutingTestHandler() {
}
void RoutingTestHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
if (!message_router_) {
// Create the browser-side router for query handling.
CefMessageRouterConfig config;
SetRouterConfig(config);
message_router_ = CefMessageRouterBrowserSide::Create(config);
message_router_->AddHandler(this, false);
}
TestHandler::OnAfterCreated(browser);
}
void RoutingTestHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
message_router_->OnBeforeClose(browser);
TestHandler::OnBeforeClose(browser);
}
void RoutingTestHandler::OnRenderProcessTerminated(
CefRefPtr<CefBrowser> browser,
TerminationStatus status) {
message_router_->OnRenderProcessTerminated(browser);
}
bool RoutingTestHandler::OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool is_redirect) {
message_router_->OnBeforeBrowse(browser, frame);
return false;
}
bool RoutingTestHandler::OnProcessMessageReceived(
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) {
return message_router_->OnProcessMessageReceived(
browser, source_process, message);
}
// Entry point for creating the test delegate.
// Called from client_app_delegates.cc.
void CreateRoutingTestHandlerDelegate(
ClientApp::RenderDelegateSet& delegates) {
delegates.insert(new RoutingRenderDelegate);
}

View File

@ -0,0 +1,44 @@
// Copyright (c) 2014 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.
#ifndef CEF_TESTS_UNITTESTS_ROUTING_TEST_HANDLER_H_
#define CEF_TESTS_UNITTESTS_ROUTING_TEST_HANDLER_H_
#pragma once
#include "include/wrapper/cef_message_router.h"
#include "tests/unittests/test_handler.h"
// Extends TestHandler to provide message routing functionality. The
// RoutingTestHandler implementation must be called from subclass
// overrides unless otherwise indicated.
class RoutingTestHandler :
public TestHandler,
public CefMessageRouterBrowserSide::Handler {
public:
RoutingTestHandler();
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
virtual void OnRenderProcessTerminated(
CefRefPtr<CefBrowser> browser,
TerminationStatus status) OVERRIDE;
// Only call this method if the navigation isn't canceled.
virtual bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool is_redirect) OVERRIDE;
// Returns true if the router handled the navigation.
virtual bool OnProcessMessageReceived(
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) OVERRIDE;
private:
CefRefPtr<CefMessageRouterBrowserSide> message_router_;
};
#endif // CEF_TESTS_UNITTESTS_ROUTING_TEST_HANDLER_H_

View File

@ -3,7 +3,6 @@
// can be found in the LICENSE file.
#include "tests/unittests/test_handler.h"
#include "base/logging.h"
#include "include/cef_command_line.h"
#include "include/cef_runnable.h"
#include "include/cef_stream.h"
@ -47,16 +46,16 @@ void TestHandler::CompletionState::WaitForTests() {
TestHandler::Collection::Collection(CompletionState* completion_state)
: completion_state_(completion_state) {
DCHECK(completion_state_);
EXPECT_TRUE(completion_state_);
}
void TestHandler::Collection::AddTestHandler(TestHandler* test_handler) {
DCHECK_EQ(test_handler->completion_state_, completion_state_);
EXPECT_EQ(test_handler->completion_state_, completion_state_);
handler_list_.push_back(test_handler);
}
void TestHandler::Collection::ExecuteTests() {
DCHECK_GT(handler_list_.size(), 0UL);
EXPECT_GT(handler_list_.size(), 0UL);
TestHandlerList::const_iterator it;
@ -79,7 +78,8 @@ void TestHandler::Collection::ExecuteTests() {
int TestHandler::browser_count_ = 0;
TestHandler::TestHandler(CompletionState* completion_state)
: browser_id_(0) {
: first_browser_id_(0),
signal_completion_when_all_browsers_close_(true) {
if (completion_state) {
completion_state_ = completion_state;
completion_state_owned_ = false;
@ -90,31 +90,48 @@ TestHandler::TestHandler(CompletionState* completion_state)
}
TestHandler::~TestHandler() {
EXPECT_TRUE(browser_map_.empty());
if (completion_state_owned_)
delete completion_state_;
}
void TestHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
EXPECT_UI_THREAD();
browser_count_++;
AutoLock lock_scope(this);
if (!browser->IsPopup()) {
// Keep the main child window, but not popup windows
browser_ = browser;
browser_id_ = browser->GetIdentifier();
// Keep non-popup browsers.
const int browser_id = browser->GetIdentifier();
EXPECT_EQ(browser_map_.find(browser_id), browser_map_.end());
if (browser_map_.empty()) {
first_browser_id_ = browser_id;
first_browser_ = browser;
}
browser_map_.insert(std::make_pair(browser_id, browser));
}
}
void TestHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
{
AutoLock lock_scope(this);
if (browser_id_ == browser->GetIdentifier()) {
// Free the browser pointer so that the browser can be destroyed
browser_ = NULL;
browser_id_ = 0;
EXPECT_UI_THREAD();
if (!browser->IsPopup()) {
// Free the browser pointer so that the browser can be destroyed.
const int browser_id = browser->GetIdentifier();
BrowserMap::iterator it = browser_map_.find(browser_id);
EXPECT_NE(it, browser_map_.end());
browser_map_.erase(it);
if (browser_id == first_browser_id_) {
first_browser_id_ = 0;
first_browser_ = NULL;
}
if (browser_map_.empty() &&
signal_completion_when_all_browsers_close_) {
// Signal that the test is now complete.
completion_state_->TestComplete();
TestComplete();
}
}
@ -125,7 +142,7 @@ CefRefPtr<CefResourceHandler> TestHandler::GetResourceHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) {
AutoLock lock_scope(this);
EXPECT_IO_THREAD();
if (resource_map_.size() > 0) {
CefString url = request->GetURL();
@ -150,8 +167,22 @@ CefRefPtr<CefResourceHandler> TestHandler::GetResourceHandler(
return NULL;
}
CefRefPtr<CefBrowser> TestHandler::GetBrowser() {
return first_browser_;
}
int TestHandler::GetBrowserId() {
return first_browser_id_;
}
void TestHandler::GetAllBrowsers(BrowserMap* map) {
EXPECT_UI_THREAD();
EXPECT_TRUE(map);
*map = browser_map_;
}
void TestHandler::ExecuteTest() {
DCHECK_EQ(completion_state_->total(), 1);
EXPECT_EQ(completion_state_->total(), 1);
// Run the test
RunTest();
@ -166,9 +197,21 @@ void TestHandler::SetupComplete() {
}
void TestHandler::DestroyTest() {
AutoLock lock_scope(this);
if (browser_id_ != 0)
browser_->GetHost()->CloseBrowser(false);
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI, NewCefRunnableMethod(this, &TestHandler::DestroyTest));
return;
}
if (!browser_map_.empty()) {
// Use a copy of the map since the original may be modified while we're
// iterating.
BrowserMap browser_map = browser_map_;
// Tell all non-popup browsers to close.
BrowserMap::const_iterator it = browser_map.begin();
for (; it != browser_map.end(); ++it)
it->second->GetHost()->CloseBrowser(false);
}
}
void TestHandler::CreateBrowser(
@ -187,6 +230,13 @@ void TestHandler::CreateBrowser(
void TestHandler::AddResource(const std::string& url,
const std::string& content,
const std::string& mimeType) {
if (!CefCurrentlyOn(TID_IO)) {
CefPostTask(TID_IO,
NewCefRunnableMethod(this, &TestHandler::AddResource, url, content,
mimeType));
return;
}
// Ignore the query component, if any.
std::string urlStr = url;
size_t idx = urlStr.find('?');
@ -198,9 +248,25 @@ void TestHandler::AddResource(const std::string& url,
}
void TestHandler::ClearResources() {
if (!CefCurrentlyOn(TID_IO)) {
CefPostTask(TID_IO,
NewCefRunnableMethod(this, &TestHandler::ClearResources));
return;
}
resource_map_.clear();
}
void TestHandler::TestComplete() {
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI, NewCefRunnableMethod(this, &TestHandler::TestComplete));
return;
}
EXPECT_TRUE(browser_map_.empty());
completion_state_->TestComplete();
}
// global functions

View File

@ -94,6 +94,8 @@ class TestHandler : public CefClient,
TestHandlerList handler_list_;
};
typedef std::map<int, CefRefPtr<CefBrowser> > BrowserMap;
// The |completion_state| object if specified must outlive this class.
explicit TestHandler(CompletionState* completion_state = NULL);
virtual ~TestHandler();
@ -149,8 +151,13 @@ class TestHandler : public CefClient,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) OVERRIDE;
CefRefPtr<CefBrowser> GetBrowser() { return browser_; }
int GetBrowserId() { return browser_id_; }
// These methods should only be used if at most one non-popup browser exists.
CefRefPtr<CefBrowser> GetBrowser();
int GetBrowserId();
// Copies the map of all the currently existing browsers into |map|. Must be
// called on the UI thread.
void GetAllBrowsers(BrowserMap* map);
// Called by the test function to execute the test. This method blocks until
// the test is complete. Do not reference the object after this method
@ -166,8 +173,11 @@ class TestHandler : public CefClient,
// Collection.
virtual void SetupComplete();
// Destroy the browser window. Once the window is destroyed test completion
// will be signaled.
// Close any existing non-popup browsers. Test completion will be signaled
// once all the browsers have closed if
// |signal_completion_when_all_browsers_close_| is true (default value).
// If no browsers exist then this method will do nothing and
// TestComplete() must be called manually.
virtual void DestroyTest();
void CreateBrowser(const CefString& url,
@ -178,26 +188,44 @@ class TestHandler : public CefClient,
const std::string& mimeType);
void ClearResources();
void SetSignalCompletionWhenAllBrowsersClose(bool val) {
signal_completion_when_all_browsers_close_ = val;
}
bool SignalCompletionWhenAllBrowsersClose() const {
return signal_completion_when_all_browsers_close_;
}
// Signal that the test is complete. This will be called automatically when
// all existing non-popup browsers are closed if
// |signal_completion_when_all_browsers_close_| is true (default value). It
// is an error to call this method before all browsers have closed.
void TestComplete();
private:
// The child browser window
CefRefPtr<CefBrowser> browser_;
// The browser window identifier
int browser_id_;
// Used to notify when the test is complete
// Used to notify when the test is complete. Can be accessed on any thread.
CompletionState* completion_state_;
bool completion_state_owned_;
// Map of resources that can be automatically loaded
// Map browser ID to browser object for non-popup browsers. Only accessed on
// the UI thread.
BrowserMap browser_map_;
// Values for the first created browser. Modified on the UI thread but can be
// accessed on any thread.
int first_browser_id_;
CefRefPtr<CefBrowser> first_browser_;
// Map of resources that can be automatically loaded. Only accessed on the
// IO thread.
typedef std::map<std::string, std::pair<std::string, std::string> >
ResourceMap;
ResourceMap resource_map_;
// If true test completion will be signaled when all browsers have closed.
bool signal_completion_when_all_browsers_close_;
// Include the default reference counting implementation.
IMPLEMENT_REFCOUNTING(TestHandler);
// Include the default locking implementation.
IMPLEMENT_LOCKING(TestHandler);
// Used to track the number of currently existing browser windows.
static int browser_count_;
@ -215,4 +243,8 @@ void WaitForThread(CefRefPtr<CefTaskRunner> task_runner);
// Returns true if the currently running test has failed.
bool TestFailed();
#define EXPECT_UI_THREAD() EXPECT_TRUE(CefCurrentlyOn(TID_UI));
#define EXPECT_IO_THREAD() EXPECT_TRUE(CefCurrentlyOn(TID_IO));
#define EXPECT_RENDERER_THREAD() EXPECT_TRUE(CefCurrentlyOn(TID_RENDERER));
#endif // CEF_TESTS_UNITTESTS_TEST_HANDLER_H_