#include <common/executor.h>
#include <common/future.h>
+#include <common/env.h>
#include <core/producer/cg_proxy.h>
#include <boost/thread/future.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/log/trivial.hpp>
+#include <boost/property_tree/ptree.hpp>
+#include <map>
+
+#pragma warning(push)
+#pragma warning(disable: 4458)
#include <cef_app.h>
+#include <cef_version.h>
+#pragma warning(pop)
#pragma comment(lib, "libcef.lib")
#pragma comment(lib, "libcef_dll_wrapper.lib")
}
}
-class animation_handler : public CefV8Handler
-{
-private:
- std::vector<CefRefPtr<CefV8Value>> callbacks_;
- boost::timer since_start_timer_;
-public:
- CefRefPtr<CefBrowser> browser;
- std::function<CefRefPtr<CefV8Context>()> get_context;
-
- bool Execute(
- const CefString& name,
- CefRefPtr<CefV8Value> object,
- const CefV8ValueList& arguments,
- CefRefPtr<CefV8Value>& retval,
- CefString& exception) override
- {
- if (!CefCurrentlyOn(TID_RENDERER))
- return false;
-
- if (arguments.size() < 1 || !arguments.at(0)->IsFunction())
- {
- return false;
- }
-
- callbacks_.push_back(arguments.at(0));
-
- if (browser)
- browser->SendProcessMessage(PID_BROWSER, CefProcessMessage::Create(
- ANIMATION_FRAME_REQUESTED_MESSAGE_NAME));
-
- return true;
- }
-
- void tick()
- {
- if (!get_context)
- return;
-
- auto context = get_context();
-
- if (!context)
- return;
-
- if (!CefCurrentlyOn(TID_RENDERER))
- return;
-
- std::vector<CefRefPtr<CefV8Value>> callbacks;
- callbacks_.swap(callbacks);
-
- CefV8ValueList callback_args;
- CefTime timestamp;
- timestamp.Now();
- callback_args.push_back(CefV8Value::CreateDouble(
- since_start_timer_.elapsed() * 1000.0));
-
- for (auto callback : callbacks)
- {
- callback->ExecuteFunctionWithContext(
- context, callback, callback_args);
- }
- }
-
- IMPLEMENT_REFCOUNTING(animation_handler);
-};
-
class remove_handler : public CefV8Handler
{
CefRefPtr<CefBrowser> browser_;
class renderer_application : public CefApp, CefRenderProcessHandler
{
- std::vector<std::pair<CefRefPtr<animation_handler>, CefRefPtr<CefV8Context>>> contexts_per_handlers_;
- //std::map<CefRefPtr<animation_handler>, CefRefPtr<CefV8Context>> contexts_per_handlers_;
+ std::vector<CefRefPtr<CefV8Context>> contexts_;
public:
CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() override
{
return this;
}
- CefRefPtr<CefV8Context> get_context(
- const CefRefPtr<animation_handler>& handler)
- {
- for (auto& ctx : contexts_per_handlers_)
- {
- if (ctx.first == handler)
- return ctx.second;
- }
-
- return nullptr;
- }
-
void OnContextCreated(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
"context for frame "
+ boost::lexical_cast<std::string>(frame->GetIdentifier())
+ " created");
-
- CefRefPtr<animation_handler> handler = new animation_handler;
- contexts_per_handlers_.push_back(std::make_pair(handler, context));
- auto handler_ptr = handler.get();
-
- handler->browser = browser;
- handler->get_context = [this, handler_ptr]
- {
- return get_context(handler_ptr);
- };
+ contexts_.push_back(context);
auto window = context->GetGlobal();
- auto function = CefV8Value::CreateFunction(
- "requestAnimationFrame",
- handler.get());
- window->SetValue(
- "requestAnimationFrame",
- function,
- V8_PROPERTY_ATTRIBUTE_NONE);
-
- function = CefV8Value::CreateFunction(
- "remove",
- new remove_handler(browser));
window->SetValue(
"remove",
- function,
+ CefV8Value::CreateFunction(
+ "remove",
+ new remove_handler(browser)),
V8_PROPERTY_ATTRIBUTE_NONE);
+
+ CefRefPtr<CefV8Value> ret;
+ CefRefPtr<CefV8Exception> exception;
+ bool injected = context->Eval(R"(
+ var requestedAnimationFrames = {};
+ var currentAnimationFrameId = 0;
+
+ window.requestAnimationFrame = function(callback) {
+ requestedAnimationFrames[++currentAnimationFrameId] = callback;
+ return currentAnimationFrameId;
+ }
+
+ window.cancelAnimationFrame = function(animationFrameId) {
+ delete requestedAnimationFrames[animationFrameId];
+ }
+
+ function tickAnimations() {
+ var requestedFrames = requestedAnimationFrames;
+ var timestamp = performance.now();
+ requestedAnimationFrames = {};
+
+ for (var animationFrameId in requestedFrames)
+ if (requestedFrames.hasOwnProperty(animationFrameId))
+ requestedFrames[animationFrameId](timestamp);
+ }
+ )", ret, exception);
+
+ if (!injected)
+ caspar_log(browser, boost::log::trivial::error, "Could not inject javascript animation code.");
}
void OnContextReleased(
CefRefPtr<CefV8Context> context)
{
auto removed = boost::remove_if(
- contexts_per_handlers_, [&](const std::pair<
- CefRefPtr<animation_handler>,
- CefRefPtr<CefV8Context>>& c)
- {
- return c.second->IsSame(context);
- });
+ contexts_, [&](const CefRefPtr<CefV8Context>& c)
+ {
+ return c->IsSame(context);
+ });
- if (removed != contexts_per_handlers_.end())
+ if (removed != contexts_.end())
caspar_log(browser, boost::log::trivial::trace,
"context for frame "
+ boost::lexical_cast<std::string>(frame->GetIdentifier())
void OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) override
{
- contexts_per_handlers_.clear();
+ contexts_.clear();
}
bool OnProcessMessageReceived(
{
if (message->GetName().ToString() == TICK_MESSAGE_NAME)
{
- for (auto& handler : contexts_per_handlers_)
+ for (auto& context : contexts_)
{
- handler.first->tick();
+ CefRefPtr<CefV8Value> ret;
+ CefRefPtr<CefV8Exception> exception;
+ context->Eval("tickAnimations()", ret, exception);
}
return true;
{
CefSettings settings;
settings.no_sandbox = true;
+ settings.remote_debugging_port = env::properties().get(L"configuration.html.remote-debugging-port", 0);
//settings.windowless_rendering_enabled = true;
CefInitialize(main_args, settings, nullptr, nullptr);
});
},
false
);
+
+ auto cef_version_major = boost::lexical_cast<std::wstring>(cef_version_info(0));
+ auto cef_revision = boost::lexical_cast<std::wstring>(cef_version_info(1));
+ auto chrome_major = boost::lexical_cast<std::wstring>(cef_version_info(2));
+ auto chrome_minor = boost::lexical_cast<std::wstring>(cef_version_info(3));
+ auto chrome_build = boost::lexical_cast<std::wstring>(cef_version_info(4));
+ auto chrome_patch = boost::lexical_cast<std::wstring>(cef_version_info(5));
+
+ dependencies.system_info_provider_repo->register_version_provider(L"cef", [=]
+ {
+ return cef_version_major + L"." + chrome_build + L"." + cef_revision;
+ });
+ dependencies.system_info_provider_repo->register_system_info_provider([=](boost::property_tree::wptree& info)
+ {
+ info.add(L"system.cef.version", cef_version_major + L"." + chrome_build + L"." + cef_revision);
+ info.add(L"system.cef.chromeversion", chrome_major + L"." + chrome_minor + L"." + chrome_build + L"." + chrome_patch);
+ });
}
void uninit()
void Execute() override
{
- CASPAR_LOG(trace) << "[cef_task] executing task";
+ CASPAR_LOG_CALL(trace) << "[cef_task] executing task";
try
{
function_();
promise_.set_value();
- CASPAR_LOG(trace) << "[cef_task] task succeeded";
+ CASPAR_LOG_CALL(trace) << "[cef_task] task succeeded";
}
catch (...)
{
if (CefPostTask(TID_UI, task.get()))
return task->future();
else
- BOOST_THROW_EXCEPTION(caspar_exception()
+ CASPAR_THROW_EXCEPTION(caspar_exception()
<< msg_info("[cef_executor] Could not post task"));
}