]> git.sesse.net Git - casparcg/blobdiff - modules/html/html.cpp
#346 #441 Added remote debugging support in html producer.
[casparcg] / modules / html / html.cpp
index a6ac8c854796492595c310f7034b58c3c3c469e8..dc7365b4a4a05b62456b0bb50f693a2475ffb360 100644 (file)
@@ -26,6 +26,7 @@
 
 #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")
@@ -62,71 +70,6 @@ void caspar_log(
        }
 }
 
-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_;
@@ -158,26 +101,13 @@ public:
 
 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,
@@ -187,34 +117,45 @@ public:
                                "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(
@@ -223,14 +164,12 @@ public:
                        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())
@@ -244,7 +183,7 @@ public:
 
        void OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) override
        {
-               contexts_per_handlers_.clear();
+               contexts_.clear();
        }
 
        bool OnProcessMessageReceived(
@@ -254,9 +193,11 @@ public:
        {
                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;
@@ -272,7 +213,11 @@ public:
 
 bool intercept_command_line(int argc, char** argv)
 {
+#ifdef _WIN32
+       CefMainArgs main_args;
+#else
        CefMainArgs main_args(argc, argv);
+#endif
 
        if (CefExecuteProcess(main_args, CefRefPtr<CefApp>(new renderer_application), nullptr) >= 0)
                return true;
@@ -282,7 +227,7 @@ bool intercept_command_line(int argc, char** argv)
 
 void init(core::module_dependencies dependencies)
 {
-       core::register_producer_factory(html::create_producer);
+       dependencies.producer_registry->register_producer_factory(L"HTML Producer", html::create_producer, html::describe_producer);
        
        CefMainArgs main_args;
        g_cef_executor.reset(new executor(L"cef"));
@@ -290,6 +235,7 @@ void init(core::module_dependencies dependencies)
        {
                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);
        });
@@ -308,12 +254,29 @@ void init(core::module_dependencies dependencies)
                        {
                                return spl::make_shared<html_cg_proxy>(producer);
                        },
-                       [](const spl::shared_ptr<core::frame_factory>& ff, const core::video_format_desc& f, const std::wstring& filename)
+                       [](const core::frame_producer_dependencies& dependencies, const std::wstring& filename)
                        {
-                               return html::create_producer(ff, f, { filename });
+                               return html::create_producer(dependencies, { filename });
                        },
                        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()
@@ -326,6 +289,7 @@ void uninit()
        {
                CefShutdown();
        });
+       g_cef_executor.reset();
 }
 
 class cef_task : public CefTask
@@ -341,13 +305,13 @@ public:
 
        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 (...)
                {
@@ -383,7 +347,7 @@ std::future<void> begin_invoke(const std::function<void()>& func)
        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"));
 }