From 648b7b243f5f2e534cfbb3cfa724e7636e3a2c00 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Fri, 11 Nov 2016 18:22:53 -0500 Subject: [PATCH] Add CefThread interface (issue #1632) --- BUILD.gn | 4 + cef_paths.gypi | 6 + include/capi/cef_thread_capi.h | 115 ++++++ include/cef_thread.h | 117 ++++++ include/internal/cef_thread_internal.h | 4 + include/internal/cef_types.h | 67 ++++ libcef/common/thread_impl.cc | 148 ++++++++ libcef/common/thread_impl.h | 41 +++ libcef_dll/cpptoc/thread_cpptoc.cc | 124 +++++++ libcef_dll/cpptoc/thread_cpptoc.h | 34 ++ libcef_dll/ctocpp/thread_ctocpp.cc | 112 ++++++ libcef_dll/ctocpp/thread_ctocpp.h | 40 +++ libcef_dll/libcef_dll.cc | 2 + libcef_dll/wrapper/libcef_dll_wrapper.cc | 2 + libcef_dll/wrapper_types.h | 1 + tests/unittests/client_app_delegates.cc | 5 + tests/unittests/run_all_unittests.cc | 70 ++-- tests/unittests/thread_unittest.cc | 440 +++++++++++++++++++++++ tools/cef_parser.py | 2 +- tools/make_capi_header.py | 25 +- 20 files changed, 1318 insertions(+), 41 deletions(-) create mode 100644 include/capi/cef_thread_capi.h create mode 100644 include/cef_thread.h create mode 100644 libcef/common/thread_impl.cc create mode 100644 libcef/common/thread_impl.h create mode 100644 libcef_dll/cpptoc/thread_cpptoc.cc create mode 100644 libcef_dll/cpptoc/thread_cpptoc.h create mode 100644 libcef_dll/ctocpp/thread_ctocpp.cc create mode 100644 libcef_dll/ctocpp/thread_ctocpp.h create mode 100644 tests/unittests/thread_unittest.cc diff --git a/BUILD.gn b/BUILD.gn index d258a62b4..fa87642ee 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -463,6 +463,8 @@ static_library("libcef_static") { "libcef/common/task_runner_impl.cc", "libcef/common/task_runner_impl.h", "libcef/common/test/translator_test_impl.cc", + "libcef/common/thread_impl.cc", + "libcef/common/thread_impl.h", "libcef/common/time_impl.cc", "libcef/common/time_util.h", "libcef/common/tracker.cc", @@ -1405,6 +1407,7 @@ cef_unittests_sources = [ "tests/unittests/test_util.h", "tests/unittests/thread_helper.cc", "tests/unittests/thread_helper.h", + "tests/unittests/thread_unittest.cc", "tests/unittests/tracing_unittest.cc", "tests/unittests/translator_unittest.cc", "tests/unittests/urlrequest_unittest.cc", @@ -1710,6 +1713,7 @@ if (is_mac) { "tests/unittests/test_util.h", "tests/unittests/thread_helper.cc", "tests/unittests/thread_helper.h", + "tests/unittests/thread_unittest.cc", "tests/unittests/tracing_unittest.cc", "tests/unittests/v8_unittest.cc", ] diff --git a/cef_paths.gypi b/cef_paths.gypi index ed19176ae..3fe8480be 100644 --- a/cef_paths.gypi +++ b/cef_paths.gypi @@ -65,6 +65,7 @@ 'include/cef_stream.h', 'include/cef_string_visitor.h', 'include/cef_task.h', + 'include/cef_thread.h', 'include/cef_trace.h', 'include/cef_urlrequest.h', 'include/cef_v8.h', @@ -149,6 +150,7 @@ 'include/capi/cef_stream_capi.h', 'include/capi/cef_string_visitor_capi.h', 'include/capi/cef_task_capi.h', + 'include/capi/cef_thread_capi.h', 'include/capi/cef_trace_capi.h', 'include/capi/cef_urlrequest_capi.h', 'include/capi/cef_v8_capi.h', @@ -378,6 +380,8 @@ 'libcef_dll/cpptoc/views/textfield_cpptoc.h', 'libcef_dll/ctocpp/views/textfield_delegate_ctocpp.cc', 'libcef_dll/ctocpp/views/textfield_delegate_ctocpp.h', + 'libcef_dll/cpptoc/thread_cpptoc.cc', + 'libcef_dll/cpptoc/thread_cpptoc.h', 'libcef_dll/cpptoc/test/translator_test_cpptoc.cc', 'libcef_dll/cpptoc/test/translator_test_cpptoc.h', 'libcef_dll/ctocpp/test/translator_test_handler_ctocpp.cc', @@ -636,6 +640,8 @@ 'libcef_dll/ctocpp/views/textfield_ctocpp.h', 'libcef_dll/cpptoc/views/textfield_delegate_cpptoc.cc', 'libcef_dll/cpptoc/views/textfield_delegate_cpptoc.h', + 'libcef_dll/ctocpp/thread_ctocpp.cc', + 'libcef_dll/ctocpp/thread_ctocpp.h', 'libcef_dll/ctocpp/test/translator_test_ctocpp.cc', 'libcef_dll/ctocpp/test/translator_test_ctocpp.h', 'libcef_dll/cpptoc/test/translator_test_handler_cpptoc.cc', diff --git a/include/capi/cef_thread_capi.h b/include/capi/cef_thread_capi.h new file mode 100644 index 000000000..75f1bd8ba --- /dev/null +++ b/include/capi/cef_thread_capi.h @@ -0,0 +1,115 @@ +// Copyright (c) 2016 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. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool and should not edited +// by hand. See the translator.README.txt file in the tools directory for +// more information. +// + +#ifndef CEF_INCLUDE_CAPI_CEF_THREAD_CAPI_H_ +#define CEF_INCLUDE_CAPI_CEF_THREAD_CAPI_H_ +#pragma once + +#include "include/capi/cef_task_capi.h" +#include "include/internal/cef_thread_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/// +// A simple thread abstraction that establishes a message loop on a new thread. +// The consumer uses cef_task_tRunner to execute code on the thread's message +// loop. The thread is terminated when the cef_thread_t object is destroyed or +// stop() is called. All pending tasks queued on the thread's message loop will +// run to completion before the thread is terminated. cef_thread_create() can be +// called on any valid CEF thread in either the browser or render process. This +// structure should only be used for tasks that require a dedicated thread. In +// most cases you can post tasks to an existing CEF thread instead of creating a +// new one; see cef_task.h for details. +/// +typedef struct _cef_thread_t { + /// + // Base structure. + /// + cef_base_t base; + + /// + // Returns the cef_task_tRunner that will execute code on this thread's + // message loop. This function is safe to call from any thread. + /// + struct _cef_task_runner_t* (CEF_CALLBACK *get_task_runner)( + struct _cef_thread_t* self); + + /// + // Returns the platform thread ID. It will return the same value after stop() + // is called. This function is safe to call from any thread. + /// + cef_platform_thread_id_t (CEF_CALLBACK *get_platform_thread_id)( + struct _cef_thread_t* self); + + /// + // Stop and join the thread. This function must be called from the same thread + // that called cef_thread_create(). Do not call this function if + // cef_thread_create() was called with a |stoppable| value of false (0). + /// + void (CEF_CALLBACK *stop)(struct _cef_thread_t* self); + + /// + // Returns true (1) if the thread is currently running. This function must be + // called from the same thread that called cef_thread_create(). + /// + int (CEF_CALLBACK *is_running)(struct _cef_thread_t* self); +} cef_thread_t; + + +/// +// Create and start a new thread. This function does not block waiting for the +// thread to run initialization. |display_name| is the name that will be used to +// identify the thread. |priority| is the thread execution priority. +// |message_loop_type| indicates the set of asynchronous events that the thread +// can process. If |stoppable| is true (1) the thread will stopped and joined on +// destruction or when stop() is called; otherwise, the the thread cannot be +// stopped and will be leaked on shutdown. On Windows the |com_init_mode| value +// specifies how COM will be initialized for the thread. If |com_init_mode| is +// set to COM_INIT_MODE_STA then |message_loop_type| must be set to ML_TYPE_UI. +/// +CEF_EXPORT cef_thread_t* cef_thread_create(const cef_string_t* display_name, + cef_thread_priority_t priority, cef_message_loop_type_t message_loop_type, + int stoppable, cef_com_init_mode_t com_init_mode); + + +#ifdef __cplusplus +} +#endif + +#endif // CEF_INCLUDE_CAPI_CEF_THREAD_CAPI_H_ diff --git a/include/cef_thread.h b/include/cef_thread.h new file mode 100644 index 000000000..a0e78c493 --- /dev/null +++ b/include/cef_thread.h @@ -0,0 +1,117 @@ +// Copyright (c) 2016 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 must follow a specific format in order to +// support the CEF translator tool. See the translator.README.txt file in the +// tools directory for more information. +// + +#ifndef CEF_INCLUDE_CEF_THREAD_H_ +#define CEF_INCLUDE_CEF_THREAD_H_ +#pragma once + +#include "include/cef_task.h" +#include "include/internal/cef_thread_internal.h" + +/// +// A simple thread abstraction that establishes a message loop on a new thread. +// The consumer uses CefTaskRunner to execute code on the thread's message loop. +// The thread is terminated when the CefThread object is destroyed or Stop() is +// called. All pending tasks queued on the thread's message loop will run to +// completion before the thread is terminated. CreateThread() can be called on +// any valid CEF thread in either the browser or render process. This class +// should only be used for tasks that require a dedicated thread. In most cases +// you can post tasks to an existing CEF thread instead of creating a new one; +// see cef_task.h for details. +/// +/*--cef(source=library)--*/ +class CefThread : public CefBase { + public: + /// + // Create and start a new thread. This method does not block waiting for the + // thread to run initialization. |display_name| is the name that will be used + // to identify the thread. |priority| is the thread execution priority. + // |message_loop_type| indicates the set of asynchronous events that the + // thread can process. If |stoppable| is true the thread will stopped and + // joined on destruction or when Stop() is called; otherwise, the the thread + // cannot be stopped and will be leaked on shutdown. On Windows the + // |com_init_mode| value specifies how COM will be initialized for the thread. + // If |com_init_mode| is set to COM_INIT_MODE_STA then |message_loop_type| + // must be set to ML_TYPE_UI. + /// + /*--cef(optional_param=display_name)--*/ + static CefRefPtr CreateThread( + const CefString& display_name, + cef_thread_priority_t priority, + cef_message_loop_type_t message_loop_type, + bool stoppable, + cef_com_init_mode_t com_init_mode); + + /// + // Create and start a new thread with default/recommended values. + // |display_name| is the name that will be used to identify the thread. + /// + static CefRefPtr CreateThread(const CefString& display_name) { + return CreateThread(display_name, TP_NORMAL, ML_TYPE_DEFAULT, true, + COM_INIT_MODE_NONE); + } + + /// + // Returns the CefTaskRunner that will execute code on this thread's message + // loop. This method is safe to call from any thread. + /// + /*--cef()--*/ + virtual CefRefPtr GetTaskRunner() =0; + + /// + // Returns the platform thread ID. It will return the same value after Stop() + // is called. This method is safe to call from any thread. + /// + /*--cef(default_retval=kInvalidPlatformThreadId)--*/ + virtual cef_platform_thread_id_t GetPlatformThreadId() =0; + + /// + // Stop and join the thread. This method must be called from the same thread + // that called CreateThread(). Do not call this method if CreateThread() was + // called with a |stoppable| value of false. + /// + /*--cef()--*/ + virtual void Stop() =0; + + /// + // Returns true if the thread is currently running. This method must be called + // from the same thread that called CreateThread(). + /// + /*--cef()--*/ + virtual bool IsRunning() =0; +}; + +#endif // CEF_INCLUDE_CEF_THREAD_H_ diff --git a/include/internal/cef_thread_internal.h b/include/internal/cef_thread_internal.h index eee2b2ae4..006ff15d3 100644 --- a/include/internal/cef_thread_internal.h +++ b/include/internal/cef_thread_internal.h @@ -46,8 +46,10 @@ extern "C" { #if defined(OS_WIN) typedef DWORD cef_platform_thread_id_t; +#define kInvalidPlatformThreadId 0U #elif defined(OS_POSIX) typedef pid_t cef_platform_thread_id_t; +#define kInvalidPlatformThreadId 0 #endif /// @@ -57,8 +59,10 @@ CEF_EXPORT cef_platform_thread_id_t cef_get_current_platform_thread_id(); #if defined(OS_WIN) typedef DWORD cef_platform_thread_handle_t; +#define kInvalidPlatformThreadHandle 0U #elif defined(OS_POSIX) typedef pthread_t cef_platform_thread_handle_t; +#define kInvalidPlatformThreadHandle 0 #endif /// diff --git a/include/internal/cef_types.h b/include/internal/cef_types.h index 1309c5592..73dccdeef 100644 --- a/include/internal/cef_types.h +++ b/include/internal/cef_types.h @@ -1396,6 +1396,73 @@ typedef enum { TID_RENDERER, } cef_thread_id_t; +/// +// Thread priority values listed in increasing order of importance. +/// +typedef enum { + /// + // Suitable for threads that shouldn't disrupt high priority work. + /// + TP_BACKGROUND, + + /// + // Default priority level. + /// + TP_NORMAL, + + /// + // Suitable for threads which generate data for the display (at ~60Hz). + /// + TP_DISPLAY, + + /// + // Suitable for low-latency, glitch-resistant audio. + /// + TP_REALTIME_AUDIO, +} cef_thread_priority_t; + +/// +// Message loop types. Indicates the set of asynchronous events that a message +// loop can process. +/// +typedef enum { + /// + // Supports tasks and timers. + /// + ML_TYPE_DEFAULT, + + /// + // Supports tasks, timers and native UI events (e.g. Windows messages). + /// + ML_TYPE_UI, + + /// + // Supports tasks, timers and asynchronous IO events. + /// + ML_TYPE_IO, +} cef_message_loop_type_t; + +/// +// Windows COM initialization mode. Specifies how COM will be initialized for a +// new thread. +/// +typedef enum { + /// + // No COM initialization. + /// + COM_INIT_MODE_NONE, + + /// + // Initialize COM using single-threaded apartments. + /// + COM_INIT_MODE_STA, + + /// + // Initialize COM using multi-threaded apartments. + /// + COM_INIT_MODE_MTA, +} cef_com_init_mode_t; + /// // Supported value types. /// diff --git a/libcef/common/thread_impl.cc b/libcef/common/thread_impl.cc new file mode 100644 index 000000000..19a5ee41a --- /dev/null +++ b/libcef/common/thread_impl.cc @@ -0,0 +1,148 @@ +// Copyright 2016 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 "libcef/common/thread_impl.h" + +#include "libcef/common/task_runner_impl.h" + +#include "base/bind.h" +#include "base/threading/thread_restrictions.h" + +namespace { + +void StopAndDestroy(base::Thread* thread) { + // Calling PlatformThread::Join() on the UI thread is otherwise disallowed. + base::ThreadRestrictions::ScopedAllowIO scoped_allow_io; + + // Deleting |thread| will implicitly stop and join it. + delete thread; +} + +} // namespace + +// static +CefRefPtr CefThread::CreateThread( + const CefString& display_name, + cef_thread_priority_t priority, + cef_message_loop_type_t message_loop_type, + bool stoppable, + cef_com_init_mode_t com_init_mode) { + if (!base::MessageLoop::current()) { + NOTREACHED() << "called on invalid thread"; + return nullptr; + } + + CefRefPtr thread_impl = new CefThreadImpl(); + if (!thread_impl->Create(display_name, priority, message_loop_type, stoppable, + com_init_mode)) { + return nullptr; + } + return thread_impl; +} + +CefThreadImpl::CefThreadImpl() + : thread_id_(kInvalidPlatformThreadId) { +} + +CefThreadImpl::~CefThreadImpl() { + if (thread_.get()) { + if (!owner_task_runner_->RunsTasksOnCurrentThread()) { + // Delete |thread_| on the correct thread. + owner_task_runner_->PostTask(FROM_HERE, + base::Bind(StopAndDestroy, base::Unretained(thread_.release()))); + } else { + StopAndDestroy(thread_.release()); + } + } +} + +bool CefThreadImpl::Create(const CefString& display_name, + cef_thread_priority_t priority, + cef_message_loop_type_t message_loop_type, + bool stoppable, + cef_com_init_mode_t com_init_mode) { + owner_task_runner_ = CefTaskRunnerImpl::GetCurrentTaskRunner(); + DCHECK(owner_task_runner_); + if (!owner_task_runner_) + return false; + + thread_.reset(new base::Thread(display_name)); + + base::Thread::Options options; + + switch (priority) { + case TP_BACKGROUND: + options.priority = base::ThreadPriority::BACKGROUND; + break; + case TP_DISPLAY: + options.priority = base::ThreadPriority::DISPLAY; + break; + case TP_REALTIME_AUDIO: + options.priority = base::ThreadPriority::REALTIME_AUDIO; + break; + default: + break; + } + + switch (message_loop_type) { + case ML_TYPE_UI: + options.message_loop_type = base::MessageLoop::TYPE_UI; + break; + case ML_TYPE_IO: + options.message_loop_type = base::MessageLoop::TYPE_IO; + break; + default: + break; + } + + options.joinable = stoppable; + +#if defined(OS_WIN) + if (com_init_mode != COM_INIT_MODE_NONE) { + if (com_init_mode == COM_INIT_MODE_STA) + options.message_loop_type = base::MessageLoop::TYPE_UI; + thread_->init_com_with_mta(com_init_mode == COM_INIT_MODE_MTA); + } +#endif + + if (!thread_->StartWithOptions(options)) { + thread_.reset(); + return false; + } + + thread_task_runner_ = new CefTaskRunnerImpl(thread_->task_runner()); + thread_id_ = thread_->GetThreadId(); + return true; +} + +CefRefPtr CefThreadImpl::GetTaskRunner() { + return thread_task_runner_; +} + +cef_platform_thread_id_t CefThreadImpl::GetPlatformThreadId() { + return thread_id_; +} + +void CefThreadImpl::Stop() { + if (!owner_task_runner_) + return; + if (!owner_task_runner_->RunsTasksOnCurrentThread()) { + NOTREACHED() << "called on invalid thread"; + return; + } + + if (thread_) + StopAndDestroy(thread_.release()); +} + +bool CefThreadImpl::IsRunning() { + if (!owner_task_runner_) + return false; + if (!owner_task_runner_->RunsTasksOnCurrentThread()) { + NOTREACHED() << "called on invalid thread"; + return false; + } + + return thread_ && thread_->IsRunning(); +} diff --git a/libcef/common/thread_impl.h b/libcef/common/thread_impl.h new file mode 100644 index 000000000..d0c8d726c --- /dev/null +++ b/libcef/common/thread_impl.h @@ -0,0 +1,41 @@ +// Copyright 2016 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_COMMON_THREAD_IMPL_H_ +#define CEF_LIBCEF_COMMON_THREAD_IMPL_H_ +#pragma once + +#include "include/cef_thread.h" +#include "base/threading/thread.h" + +class CefThreadImpl : public CefThread { + public: + CefThreadImpl(); + ~CefThreadImpl(); + + bool Create(const CefString& display_name, + cef_thread_priority_t priority, + cef_message_loop_type_t message_loop_type, + bool stoppable, + cef_com_init_mode_t com_init_mode); + + // CefThread methods: + CefRefPtr GetTaskRunner() override; + cef_platform_thread_id_t GetPlatformThreadId() override; + void Stop() override; + bool IsRunning() override; + + private: + std::unique_ptr thread_; + cef_platform_thread_id_t thread_id_; + CefRefPtr thread_task_runner_; + + // TaskRunner for the owner thread. + scoped_refptr owner_task_runner_; + + IMPLEMENT_REFCOUNTING(CefThreadImpl); + DISALLOW_COPY_AND_ASSIGN(CefThreadImpl); +}; + +#endif // CEF_LIBCEF_COMMON_THREAD_IMPL_H_ diff --git a/libcef_dll/cpptoc/thread_cpptoc.cc b/libcef_dll/cpptoc/thread_cpptoc.cc new file mode 100644 index 000000000..0a7df5a9f --- /dev/null +++ b/libcef_dll/cpptoc/thread_cpptoc.cc @@ -0,0 +1,124 @@ +// Copyright (c) 2016 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. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// + +#include "libcef_dll/cpptoc/task_runner_cpptoc.h" +#include "libcef_dll/cpptoc/thread_cpptoc.h" + + +// GLOBAL FUNCTIONS - Body may be edited by hand. + +CEF_EXPORT cef_thread_t* cef_thread_create(const cef_string_t* display_name, + cef_thread_priority_t priority, cef_message_loop_type_t message_loop_type, + int stoppable, cef_com_init_mode_t com_init_mode) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Unverified params: display_name + + // Execute + CefRefPtr _retval = CefThread::CreateThread( + CefString(display_name), + priority, + message_loop_type, + stoppable?true:false, + com_init_mode); + + // Return type: refptr_same + return CefThreadCppToC::Wrap(_retval); +} + + +namespace { + +// MEMBER FUNCTIONS - Body may be edited by hand. + +cef_task_runner_t* CEF_CALLBACK thread_get_task_runner( + struct _cef_thread_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return NULL; + + // Execute + CefRefPtr _retval = CefThreadCppToC::Get(self)->GetTaskRunner( + ); + + // Return type: refptr_same + return CefTaskRunnerCppToC::Wrap(_retval); +} + +cef_platform_thread_id_t CEF_CALLBACK thread_get_platform_thread_id( + struct _cef_thread_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return kInvalidPlatformThreadId; + + // Execute + cef_platform_thread_id_t _retval = CefThreadCppToC::Get( + self)->GetPlatformThreadId(); + + // Return type: simple + return _retval; +} + +void CEF_CALLBACK thread_stop(struct _cef_thread_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + + // Execute + CefThreadCppToC::Get(self)->Stop(); +} + +int CEF_CALLBACK thread_is_running(struct _cef_thread_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + + // Execute + bool _retval = CefThreadCppToC::Get(self)->IsRunning(); + + // Return type: bool + return _retval; +} + +} // namespace + + +// CONSTRUCTOR - Do not edit by hand. + +CefThreadCppToC::CefThreadCppToC() { + GetStruct()->get_task_runner = thread_get_task_runner; + GetStruct()->get_platform_thread_id = thread_get_platform_thread_id; + GetStruct()->stop = thread_stop; + GetStruct()->is_running = thread_is_running; +} + +template<> CefRefPtr CefCppToC::UnwrapDerived(CefWrapperType type, cef_thread_t* s) { + NOTREACHED() << "Unexpected class type: " << type; + return NULL; +} + +#if DCHECK_IS_ON() +template<> base::AtomicRefCount CefCppToC::DebugObjCt = 0; +#endif + +template<> CefWrapperType CefCppToC::kWrapperType = WT_THREAD; diff --git a/libcef_dll/cpptoc/thread_cpptoc.h b/libcef_dll/cpptoc/thread_cpptoc.h new file mode 100644 index 000000000..7d3754c71 --- /dev/null +++ b/libcef_dll/cpptoc/thread_cpptoc.h @@ -0,0 +1,34 @@ +// Copyright (c) 2016 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. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// + +#ifndef CEF_LIBCEF_DLL_CPPTOC_THREAD_CPPTOC_H_ +#define CEF_LIBCEF_DLL_CPPTOC_THREAD_CPPTOC_H_ +#pragma once + +#ifndef BUILDING_CEF_SHARED +#pragma message("Warning: "__FILE__" may be accessed DLL-side only") +#else // BUILDING_CEF_SHARED + +#include "include/cef_thread.h" +#include "include/capi/cef_thread_capi.h" +#include "libcef_dll/cpptoc/cpptoc.h" + +// Wrap a C++ class with a C structure. +// This class may be instantiated and accessed DLL-side only. +class CefThreadCppToC + : public CefCppToC { + public: + CefThreadCppToC(); +}; + +#endif // BUILDING_CEF_SHARED +#endif // CEF_LIBCEF_DLL_CPPTOC_THREAD_CPPTOC_H_ diff --git a/libcef_dll/ctocpp/thread_ctocpp.cc b/libcef_dll/ctocpp/thread_ctocpp.cc new file mode 100644 index 000000000..6711ac2d3 --- /dev/null +++ b/libcef_dll/ctocpp/thread_ctocpp.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2016 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. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// + +#include "libcef_dll/ctocpp/task_runner_ctocpp.h" +#include "libcef_dll/ctocpp/thread_ctocpp.h" + + +// STATIC METHODS - Body may be edited by hand. + +CefRefPtr CefThread::CreateThread(const CefString& display_name, + cef_thread_priority_t priority, cef_message_loop_type_t message_loop_type, + bool stoppable, cef_com_init_mode_t com_init_mode) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Unverified params: display_name + + // Execute + cef_thread_t* _retval = cef_thread_create( + display_name.GetStruct(), + priority, + message_loop_type, + stoppable, + com_init_mode); + + // Return type: refptr_same + return CefThreadCToCpp::Wrap(_retval); +} + + +// VIRTUAL METHODS - Body may be edited by hand. + +CefRefPtr CefThreadCToCpp::GetTaskRunner() { + cef_thread_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, get_task_runner)) + return NULL; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + cef_task_runner_t* _retval = _struct->get_task_runner(_struct); + + // Return type: refptr_same + return CefTaskRunnerCToCpp::Wrap(_retval); +} + +cef_platform_thread_id_t CefThreadCToCpp::GetPlatformThreadId() { + cef_thread_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, get_platform_thread_id)) + return kInvalidPlatformThreadId; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + cef_platform_thread_id_t _retval = _struct->get_platform_thread_id(_struct); + + // Return type: simple + return _retval; +} + +void CefThreadCToCpp::Stop() { + cef_thread_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, stop)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + _struct->stop(_struct); +} + +bool CefThreadCToCpp::IsRunning() { + cef_thread_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, is_running)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + int _retval = _struct->is_running(_struct); + + // Return type: bool + return _retval?true:false; +} + + +// CONSTRUCTOR - Do not edit by hand. + +CefThreadCToCpp::CefThreadCToCpp() { +} + +template<> cef_thread_t* CefCToCpp::UnwrapDerived(CefWrapperType type, CefThread* c) { + NOTREACHED() << "Unexpected class type: " << type; + return NULL; +} + +#if DCHECK_IS_ON() +template<> base::AtomicRefCount CefCToCpp::DebugObjCt = 0; +#endif + +template<> CefWrapperType CefCToCpp::kWrapperType = WT_THREAD; diff --git a/libcef_dll/ctocpp/thread_ctocpp.h b/libcef_dll/ctocpp/thread_ctocpp.h new file mode 100644 index 000000000..21ce8c8c5 --- /dev/null +++ b/libcef_dll/ctocpp/thread_ctocpp.h @@ -0,0 +1,40 @@ +// Copyright (c) 2016 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. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// + +#ifndef CEF_LIBCEF_DLL_CTOCPP_THREAD_CTOCPP_H_ +#define CEF_LIBCEF_DLL_CTOCPP_THREAD_CTOCPP_H_ +#pragma once + +#ifndef USING_CEF_SHARED +#pragma message("Warning: "__FILE__" may be accessed wrapper-side only") +#else // USING_CEF_SHARED + +#include "include/cef_thread.h" +#include "include/capi/cef_thread_capi.h" +#include "libcef_dll/ctocpp/ctocpp.h" + +// Wrap a C structure with a C++ class. +// This class may be instantiated and accessed wrapper-side only. +class CefThreadCToCpp + : public CefCToCpp { + public: + CefThreadCToCpp(); + + // CefThread methods. + CefRefPtr GetTaskRunner() OVERRIDE; + cef_platform_thread_id_t GetPlatformThreadId() OVERRIDE; + void Stop() OVERRIDE; + bool IsRunning() OVERRIDE; +}; + +#endif // USING_CEF_SHARED +#endif // CEF_LIBCEF_DLL_CTOCPP_THREAD_CTOCPP_H_ diff --git a/libcef_dll/libcef_dll.cc b/libcef_dll/libcef_dll.cc index 496c9b067..45ec3ac7d 100644 --- a/libcef_dll/libcef_dll.cc +++ b/libcef_dll/libcef_dll.cc @@ -80,6 +80,7 @@ #include "libcef_dll/cpptoc/stream_writer_cpptoc.h" #include "libcef_dll/cpptoc/task_runner_cpptoc.h" #include "libcef_dll/cpptoc/views/textfield_cpptoc.h" +#include "libcef_dll/cpptoc/thread_cpptoc.h" #include "libcef_dll/cpptoc/urlrequest_cpptoc.h" #include "libcef_dll/cpptoc/v8context_cpptoc.h" #include "libcef_dll/cpptoc/v8exception_cpptoc.h" @@ -321,6 +322,7 @@ CEF_EXPORT void cef_shutdown() { DCHECK(base::AtomicRefCountIsZero(&CefTaskRunnerCppToC::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero(&CefTextfieldCppToC::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero(&CefTextfieldDelegateCToCpp::DebugObjCt)); + DCHECK(base::AtomicRefCountIsZero(&CefThreadCppToC::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero(&CefURLRequestClientCToCpp::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero(&CefURLRequestCppToC::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero(&CefV8AccessorCToCpp::DebugObjCt)); diff --git a/libcef_dll/wrapper/libcef_dll_wrapper.cc b/libcef_dll/wrapper/libcef_dll_wrapper.cc index 3a5d30fdf..ac5f6fc88 100644 --- a/libcef_dll/wrapper/libcef_dll_wrapper.cc +++ b/libcef_dll/wrapper/libcef_dll_wrapper.cc @@ -134,6 +134,7 @@ #include "libcef_dll/ctocpp/stream_writer_ctocpp.h" #include "libcef_dll/ctocpp/task_runner_ctocpp.h" #include "libcef_dll/ctocpp/views/textfield_ctocpp.h" +#include "libcef_dll/ctocpp/thread_ctocpp.h" #include "libcef_dll/ctocpp/urlrequest_ctocpp.h" #include "libcef_dll/ctocpp/v8context_ctocpp.h" #include "libcef_dll/ctocpp/v8exception_ctocpp.h" @@ -313,6 +314,7 @@ CEF_GLOBAL void CefShutdown() { DCHECK(base::AtomicRefCountIsZero(&CefTaskRunnerCToCpp::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero(&CefTextfieldCToCpp::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero(&CefTextfieldDelegateCppToC::DebugObjCt)); + DCHECK(base::AtomicRefCountIsZero(&CefThreadCToCpp::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero(&CefURLRequestCToCpp::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero(&CefURLRequestClientCppToC::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero(&CefV8AccessorCppToC::DebugObjCt)); diff --git a/libcef_dll/wrapper_types.h b/libcef_dll/wrapper_types.h index ac7de8e77..354f58b25 100644 --- a/libcef_dll/wrapper_types.h +++ b/libcef_dll/wrapper_types.h @@ -115,6 +115,7 @@ enum CefWrapperType { WT_TASK_RUNNER, WT_TEXTFIELD, WT_TEXTFIELD_DELEGATE, + WT_THREAD, WT_TRANSLATOR_TEST, WT_TRANSLATOR_TEST_HANDLER, WT_TRANSLATOR_TEST_HANDLER_CHILD, diff --git a/tests/unittests/client_app_delegates.cc b/tests/unittests/client_app_delegates.cc index edb2fd3e2..b094a1836 100644 --- a/tests/unittests/client_app_delegates.cc +++ b/tests/unittests/client_app_delegates.cc @@ -81,6 +81,11 @@ void CreateRenderDelegates(ClientAppRenderer::DelegateSet& delegates) { ClientAppRenderer::DelegateSet& delegates); CreateRoutingTestHandlerDelegate(delegates); + // Bring in the thread tests. + extern void CreateThreadRendererTests( + ClientAppRenderer::DelegateSet& delegates); + CreateThreadRendererTests(delegates); + // Bring in the URLRequest tests. extern void CreateURLRequestRendererTests( ClientAppRenderer::DelegateSet& delegates); diff --git a/tests/unittests/run_all_unittests.cc b/tests/unittests/run_all_unittests.cc index 99140adc4..0f4cf8553 100644 --- a/tests/unittests/run_all_unittests.cc +++ b/tests/unittests/run_all_unittests.cc @@ -11,11 +11,10 @@ #undef Bool #endif -#include "base/threading/thread.h" - #include "include/base/cef_bind.h" #include "include/cef_app.h" #include "include/cef_task.h" +#include "include/cef_thread.h" #include "include/wrapper/cef_helpers.h" #include "include/wrapper/cef_closure_task.h" #include "tests/cefclient/browser/client_app_browser.h" @@ -32,6 +31,12 @@ namespace { +// Used to track state when running tests on a separate thread. +struct TestState { + CefTestSuite* test_suite_; + int retval_; +}; + void QuitMessageLoop() { client::MainMessageLoop* message_loop = client::MainMessageLoop::Get(); if (message_loop) @@ -40,38 +45,28 @@ void QuitMessageLoop() { CefQuitMessageLoop(); } -// Thread used to run the test suite. -class CefTestThread : public base::Thread { - public: - explicit CefTestThread(CefTestSuite* test_suite) - : base::Thread("test_thread"), - test_suite_(test_suite) { - } +// Called on the test thread. +void RunTestsOnTestThread(TestState* test_state) { + CHECK(test_state); - void RunTests() { - // Run the test suite. - retval_ = test_suite_->Run(); + // Run the test suite. + test_state->retval_ = test_state->test_suite_->Run(); - // Wait for all browsers to exit. - while (TestHandler::HasBrowser()) - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); + // Wait for all browsers to exit. + while (TestHandler::HasBrowser()) + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); - // Quit the CEF message loop. - CefPostTask(TID_UI, base::Bind(&QuitMessageLoop)); - } - - int retval() { return retval_; } - - protected: - CefTestSuite* test_suite_; - int retval_; -}; + // Quit the CEF message loop. + CefPostTask(TID_UI, base::Bind(&QuitMessageLoop)); +} // Called on the UI thread. -void RunTests(CefTestThread* thread) { +void ContinueOnUIThread(TestState* test_state, + CefRefPtr test_task_runner) { // Run the test suite on the test thread. - thread->message_loop()->task_runner()->PostTask(FROM_HERE, - base::Bind(&CefTestThread::RunTests, base::Unretained(thread))); + test_task_runner->PostTask( + CefCreateClosureTask(base::Bind(&RunTestsOnTestThread, + base::Unretained(test_state)))); } #if defined(OS_LINUX) @@ -189,24 +184,29 @@ int main(int argc, char* argv[]) { // Run the test suite on the main thread. retval = test_suite.Run(); } else { - // Create the test thread. - std::unique_ptr thread; - thread.reset(new CefTestThread(&test_suite)); - if (!thread->Start()) + TestState test_state = {0}; + test_state.test_suite_ = &test_suite; + + // Create and start the test thread. + CefRefPtr thread = CefThread::CreateThread("test_thread"); + if (!thread) return 1; // Start the tests from the UI thread so that any pending UI tasks get a // chance to execute first. - CefPostTask(TID_UI, base::Bind(&RunTests, thread.get())); + CefPostTask(TID_UI, + base::Bind(&ContinueOnUIThread, base::Unretained(&test_state), + thread->GetTaskRunner())); // Run the CEF message loop. message_loop->Run(); // The test suite has completed. - retval = thread->retval(); + retval = test_state.retval_; // Terminate the test thread. - thread.reset(); + thread->Stop(); + thread = nullptr; } // Shut down CEF. diff --git a/tests/unittests/thread_unittest.cc b/tests/unittests/thread_unittest.cc new file mode 100644 index 000000000..77909516e --- /dev/null +++ b/tests/unittests/thread_unittest.cc @@ -0,0 +1,440 @@ +// Copyright 2016 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#include "include/base/cef_bind.h" +#include "include/cef_task.h" +#include "include/cef_thread.h" +#include "include/wrapper/cef_closure_task.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "tests/cefclient/browser/client_app_browser.h" +#include "tests/cefclient/renderer/client_app_renderer.h" +#include "tests/unittests/test_handler.h" + +using client::ClientAppBrowser; +using client::ClientAppRenderer; + +namespace { + +// Base class for creating and testing threads. +class ThreadTest : public base::RefCountedThreadSafe { + public: + ThreadTest() {} + virtual ~ThreadTest() {} + + // Create the test thread. Should only be called one time. + void CreateTestThread() { + EXPECT_TRUE(!thread_.get()); + + owner_task_runner_ = CefTaskRunner::GetForCurrentThread(); + EXPECT_TRUE(owner_task_runner_.get()); + EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread()); + + thread_ = CefThread::CreateThread("test_thread"); + EXPECT_TRUE(thread_.get()); + EXPECT_TRUE(thread_->IsRunning()); + + thread_id_ = thread_->GetPlatformThreadId(); + EXPECT_NE(thread_id_, kInvalidPlatformThreadId); + + thread_task_runner_ = thread_->GetTaskRunner(); + EXPECT_TRUE(thread_task_runner_.get()); + + AssertOwnerThread(); + } + + // Destroy the test thread. Should only be called one time. + void DestroyTestThread() { + EXPECT_TRUE(thread_.get()); + AssertOwnerThread(); + + EXPECT_TRUE(thread_->IsRunning()); + thread_->Stop(); + EXPECT_FALSE(thread_->IsRunning()); + + AssertOwnerThread(); + + thread_ = nullptr; + } + + // Execute |test_task| on the test thread. After execution |callback| will be + // posted to |callback_task_runner|. + void PostOnTestThreadAndCallback( + const base::Closure& test_task, + CefRefPtr callback_task_runner, + const base::Closure& callback) { + EXPECT_TRUE(thread_.get()); + thread_task_runner_->PostTask(CefCreateClosureTask( + base::Bind(&ThreadTest::ExecuteOnTestThread, this, test_task, + callback_task_runner, callback))); + } + + CefRefPtr owner_task_runner() const { + return owner_task_runner_; + } + CefRefPtr thread_task_runner() const { + return thread_task_runner_; + } + + // Assert that we're running on the owner thread. + void AssertOwnerThread() { + EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread()); + EXPECT_FALSE(thread_task_runner_->BelongsToCurrentThread()); + EXPECT_TRUE(thread_task_runner_->IsSame(thread_->GetTaskRunner())); + EXPECT_EQ(thread_id_, thread_->GetPlatformThreadId()); + } + + // Assert that we're running on the test thread. + void AssertTestThread() { + EXPECT_FALSE(owner_task_runner_->BelongsToCurrentThread()); + EXPECT_TRUE(thread_task_runner_->BelongsToCurrentThread()); + EXPECT_TRUE(thread_task_runner_->IsSame(thread_->GetTaskRunner())); + EXPECT_EQ(thread_id_, thread_->GetPlatformThreadId()); + } + + private: + // Helper for PostOnTestThreadAndCallback(). + void ExecuteOnTestThread(const base::Closure& test_task, + CefRefPtr callback_task_runner, + const base::Closure& callback) { + AssertTestThread(); + + test_task.Run(); + + callback_task_runner->PostTask(CefCreateClosureTask(callback)); + } + + CefRefPtr owner_task_runner_; + + CefRefPtr thread_; + cef_platform_thread_id_t thread_id_; + CefRefPtr thread_task_runner_; + + DISALLOW_COPY_AND_ASSIGN(ThreadTest); +}; + +} // namespace + +// Test thread creation and destruction without any task execution. +TEST(ThreadTest, Create) { + scoped_refptr thread_test = new ThreadTest(); + thread_test->CreateTestThread(); + thread_test->DestroyTestThread(); + thread_test = nullptr; +} + + +namespace { + +// Simple implementation of ThreadTest that creates a thread, executes tasks +// on the thread, then destroys the thread after all tasks have completed. +class SimpleThreadTest : public ThreadTest { + public: + SimpleThreadTest(size_t expected_task_count, + const base::Closure& task_callback, + const base::Closure& done_callback) + : expected_task_count_(expected_task_count), + task_callback_(task_callback), + done_callback_(done_callback), + got_task_count_(0U), + got_done_count_(0U) {} + + void RunTest() { + // Create the test thread. + CreateTestThread(); + + for (size_t i = 0U; i < expected_task_count_; ++i) { + // Execute Task() on the test thread and then call Done() on this thread. + PostOnTestThreadAndCallback( + base::Bind(&SimpleThreadTest::Task, this), owner_task_runner(), + base::Bind(&SimpleThreadTest::Done, this)); + } + } + + void DestroyTest() { + EXPECT_EQ(expected_task_count_, got_task_count_); + EXPECT_EQ(expected_task_count_, got_done_count_); + + // Destroy the test thread. + DestroyTestThread(); + } + + private: + void Task() { + AssertTestThread(); + got_task_count_++; + if (!task_callback_.is_null()) + task_callback_.Run(); + } + + void Done() { + AssertOwnerThread(); + if (++got_done_count_ == expected_task_count_ && !done_callback_.is_null()) + done_callback_.Run(); + } + + const size_t expected_task_count_; + base::Closure task_callback_; + base::Closure done_callback_; + + size_t got_task_count_; + size_t got_done_count_; + + DISALLOW_COPY_AND_ASSIGN(SimpleThreadTest); +}; + + +// Test creation/execution of threads in the browser process. + +const char kBrowserThreadTestHtml[] = "http://test.com/browserthread.html"; + +// Browser side. +class BrowserThreadTestHandler : public TestHandler { + public: + explicit BrowserThreadTestHandler(CefThreadId owner_thread_id) + : owner_thread_id_(owner_thread_id) {} + + void RunTest() override { + AddResource(kBrowserThreadTestHtml, "Test", + "text/html"); + + CreateBrowser(kBrowserThreadTestHtml); + + // Time out the test after a reasonable period of time. + SetTestTimeout(); + } + + void RunThreadTestOnOwnerThread() { + if (!CefCurrentlyOn(owner_thread_id_)) { + // Run the test on the desired owner thread. + CefPostTask(owner_thread_id_, + base::Bind(&BrowserThreadTestHandler::RunThreadTestOnOwnerThread, + this)); + return; + } + + EXPECT_FALSE(thread_test_.get()); + thread_test_ = new SimpleThreadTest( + 3, base::Closure(), + base::Bind(&BrowserThreadTestHandler::DoneOnOwnerThread, this)); + thread_test_->RunTest(); + } + + void DoneOnOwnerThread() { + // Let the call stack unwind before destroying |thread_test_|. + CefPostTask(owner_thread_id_, + base::Bind(&BrowserThreadTestHandler::DestroyTestOnOwnerThread, this)); + } + + void DestroyTestOnOwnerThread() { + EXPECT_TRUE(CefCurrentlyOn(owner_thread_id_)); + + EXPECT_TRUE(thread_test_.get()); + if (thread_test_) { + thread_test_->DestroyTest(); + thread_test_ = nullptr; + } + + got_test_done_.yes(); + + // Call DestroyTest() on the UI thread. + CefPostTask(TID_UI, + base::Bind(&BrowserThreadTestHandler::DestroyTest, this)); + } + + void OnLoadingStateChange(CefRefPtr browser, + bool isLoading, + bool canGoBack, + bool canGoForward) override { + if (!isLoading) + RunThreadTestOnOwnerThread(); + } + + private: + void DestroyTest() override { + EXPECT_FALSE(thread_test_.get()); + EXPECT_TRUE(got_test_done_); + + TestHandler::DestroyTest(); + } + + const CefThreadId owner_thread_id_; + + scoped_refptr thread_test_; + TrackCallback got_test_done_; + + IMPLEMENT_REFCOUNTING(BrowserThreadTestHandler); + DISALLOW_COPY_AND_ASSIGN(BrowserThreadTestHandler); +}; + +} // namespace + +// Test creation of new threads from the browser UI thread. +TEST(ThreadTest, CreateFromBrowserUIThread) { + CefRefPtr handler = + new BrowserThreadTestHandler(TID_UI); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} + +// Test creation of new threads from the browser IO thread. +TEST(ThreadTest, CreateFromBrowserIOThread) { + CefRefPtr handler = + new BrowserThreadTestHandler(TID_IO); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} + +// Test creation of new threads from the browser FILE thread. +TEST(ThreadTest, CreateFromBrowserFILEThread) { + CefRefPtr handler = + new BrowserThreadTestHandler(TID_FILE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} + + +namespace { + +// Test creation/execution of threads in the render process. + +const char kRenderThreadTestHtml[] = "http://test.com/renderthread.html"; +const char kRenderThreadTestMsg[] = "ThreadTest.RenderThreadTest"; + +// Browser side. +class RenderThreadTestHandler : public TestHandler { + public: + RenderThreadTestHandler() {} + + void RunTest() override { + AddResource(kRenderThreadTestHtml, "Test", + "text/html"); + + CreateBrowser(kRenderThreadTestHtml); + + // Time out the test after a reasonable period of time. + SetTestTimeout(); + } + + void OnLoadingStateChange(CefRefPtr browser, + bool isLoading, + bool canGoBack, + bool canGoForward) override { + if (!isLoading) { + // Return the test in the render process. + CefRefPtr msg = + CefProcessMessage::Create(kRenderThreadTestMsg); + EXPECT_TRUE(browser->SendProcessMessage(PID_RENDERER, msg)); + } + } + + bool OnProcessMessageReceived( + CefRefPtr browser, + CefProcessId source_process, + CefRefPtr message) override { + EXPECT_TRUE(browser.get()); + EXPECT_EQ(PID_RENDERER, source_process); + EXPECT_TRUE(message.get()); + EXPECT_TRUE(message->IsReadOnly()); + + const std::string& message_name = message->GetName(); + EXPECT_STREQ(kRenderThreadTestMsg, message_name.c_str()); + + got_message_.yes(); + + if (message->GetArgumentList()->GetBool(0)) + got_success_.yes(); + + // Test is complete. + DestroyTest(); + + return true; + } + + protected: + void DestroyTest() override { + EXPECT_TRUE(got_message_); + EXPECT_TRUE(got_success_); + + TestHandler::DestroyTest(); + } + + TrackCallback got_message_; + TrackCallback got_success_; + + IMPLEMENT_REFCOUNTING(RenderThreadTestHandler); + DISALLOW_COPY_AND_ASSIGN(RenderThreadTestHandler); +}; + +// Renderer side. +class RenderThreadRendererTest : public ClientAppRenderer::Delegate { + public: + RenderThreadRendererTest() {} + + bool OnProcessMessageReceived( + CefRefPtr app, + CefRefPtr browser, + CefProcessId source_process, + CefRefPtr message) override { + if (message->GetName().ToString() == kRenderThreadTestMsg) { + browser_ = browser; + EXPECT_FALSE(thread_test_.get()); + thread_test_ = new SimpleThreadTest( + 3, base::Closure(), + base::Bind(&RenderThreadRendererTest::Done, this)); + thread_test_->RunTest(); + return true; + } + + // Message not handled. + return false; + } + + private: + void Done() { + // Let the call stack unwind before destroying |thread_test_|. + CefPostTask(TID_RENDERER, + base::Bind(&RenderThreadRendererTest::DestroyTest, this)); + } + + void DestroyTest() { + EXPECT_TRUE(thread_test_.get()); + if (thread_test_) { + thread_test_->DestroyTest(); + thread_test_ = nullptr; + } + + // Check if the test has failed. + bool result = !TestFailed(); + + // Return the result to the browser process. + CefRefPtr return_msg = + CefProcessMessage::Create(kRenderThreadTestMsg); + EXPECT_TRUE(return_msg->GetArgumentList()->SetBool(0, result)); + EXPECT_TRUE(browser_->SendProcessMessage(PID_BROWSER, return_msg)); + + browser_ = nullptr; + } + + CefRefPtr browser_; + scoped_refptr thread_test_; + + IMPLEMENT_REFCOUNTING(RenderThreadRendererTest); + DISALLOW_COPY_AND_ASSIGN(RenderThreadRendererTest); +}; + +} // namespace + +TEST(ThreadTest, CreateFromRenderThread) { + CefRefPtr handler = new RenderThreadTestHandler(); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} + + +// Entry point for creating request handler renderer test objects. +// Called from client_app_delegates.cc. +void CreateThreadRendererTests( + ClientAppRenderer::DelegateSet& delegates) { + delegates.insert(new RenderThreadRendererTest); +} diff --git a/tools/cef_parser.py b/tools/cef_parser.py index 6495401a1..f8f0111f5 100644 --- a/tools/cef_parser.py +++ b/tools/cef_parser.py @@ -694,7 +694,7 @@ class obj_header: return obj_analysis([self], value, named) def get_defined_structs(self): - """ Return a list of names already defined structure names. """ + """ Return a list of already defined structure names. """ return ['cef_print_info_t', 'cef_window_info_t', 'cef_base_t'] def get_capi_translations(self): diff --git a/tools/make_capi_header.py b/tools/make_capi_header.py index 83542971a..b8b022f2c 100644 --- a/tools/make_capi_header.py +++ b/tools/make_capi_header.py @@ -90,24 +90,39 @@ def make_capi_header(header, filename): classes = header.get_classes(filename) # identify all includes and forward declarations - all_includes = set([]) + translated_includes = set([]) + internal_includes = set([]) all_declares = set([]) for cls in classes: includes = cls.get_includes() for include in includes: - all_includes.add(include) + if include.startswith('base/'): + # base/ headers are C++. They should not be included by + # translated CEF API headers. + raise Exception('Disallowed include of %s.h from %s' % (include, filename)) + elif include.startswith('internal/'): + # internal/ headers may be C or C++. Include them as-is. + internal_includes.add(include) + else: + translated_includes.add(include) declares = cls.get_forward_declares() for declare in declares: all_declares.add(header.get_class(declare).get_capi_name()) - # output includes - if len(all_includes) > 0: - sorted_includes = sorted(all_includes) + # output translated includes + if len(translated_includes) > 0: + sorted_includes = sorted(translated_includes) for include in sorted_includes: result += '#include "include/capi/' + include + '_capi.h"\n' else: result += '#include "include/capi/cef_base_capi.h"\n' + # output internal includes + if len(internal_includes) > 0: + sorted_includes = sorted(internal_includes) + for include in sorted_includes: + result += '#include "include/' + include + '.h"\n' + result += \ """ #ifdef __cplusplus