CEF 中的 JavaScript
- CEF 利用 V8 JS 引擎来实现 JS。
- 浏览器中的每一个 frame 都有自己的 JS 上下文,JS 只能在该上下文中执行。
- JS 只能在渲染进程中的 TID_RENDERER 线程中执行。
- 有关 JS 回调的接口都包含在 CefRenderProcessHandler 中,因此我们要实现这个接口来对 JS 进行扩展。这个接口一般由 CefApp 实现。
执行 JavaScript
可以通过 CefFrame::ExecuteJavaScript() 来执行 JS。该函数可以脱离 JS 上下文,在浏览器进程和渲染进程中都可以执行。例如可以在浏览器创建成功后调用该函数执行一段 JS 脚本:
class MyClient : public CefClient, public CefLifeSpanHandler
{
...
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) override
{
...
auto frame = browser->GetMainFrame();
frame->ExecuteJavaScript("alert('Hello, JS!');", frame->GetURL(), 0);
}
...
};
绑定值到Window对象
可以在 CefRenderProcessHandler::OnContextCreated() 中绑定一些值给 JS 的 window 对象。下面举几个例子。
绑定值
class MyApp : ... , public CefRenderProcessHandler
{
...
virtual void OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) override
{
CEF_REQUIRE_RENDERER_THREAD();
// 获取 window 对象
CefRefPtr<CefV8Value> object = context->GetGlobal();
// 床架一个 JS 字符串
CefRefPtr<CefV8Value> str = CefV8Value::CreateString("Hello, JS!");
// 将创建的字符串绑定到 window 的 greet 属性
object->SetValue("greet", str, V8_PROPERTY_ATTRIBUTE_NONE);
}
};
在 JS 中:
<script language="JavaScript">
alert(window.greet);
</script>
绑定函数
如果要绑定函数,则需要实现 CefV8Handler 来作为函数的处理器:
class MyV8Handler : public CefV8Handler
{
...
virtual bool Execute(const CefString& name, CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval,
CefString& exception) override
{
CEF_REQUIRE_RENDERER_THREAD();
if (name == "add") // 处理函数 add
{
auto num1 = arguments[0]->GetIntValue(); // 参数1
auto num2 = arguments[1]->GetIntValue(); // 参数2
auto sum = num1 + num2;
retval = CefV8Value::CreateInt(sum);
return true;
}
// Function does not exist.
return false;
}
...
};
class MyApp : ..., public CefRenderProcessHandler
{
...
virtual void OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) override
{
CEF_REQUIRE_RENDERER_THREAD();
CefRefPtr<CefV8Value> object = context->GetGlobal();
// 创建函数处理器
CefRefPtr<CefV8Handler> handler = new MyV8Handler();
// 创建函数
CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("add", handler);
// 将函数绑定为 window 的 add 属性
object->SetValue("add", func, V8_PROPERTY_ATTRIBUTE_NONE);
}
};
在 JS 中:
<script language="JavaScript">
alert(window.add(1,2));
</script>
扩展JS
可以在 CefRenderProcessHandler::OnWebKitInitialized() 中将 JS 脚本注入到 V8 中。
扩展值
class MyApp : ..., public CefRenderProcessHandler
{
...
virtual void OnWebKitInitialized() override
{
CEF_REQUIRE_RENDERER_THREAD();
// Define the extension contents.
std::string extensionCode =
"var test;"
"if (!test)"
" test = {};"
"(function() {"
" test.myval = 'Hello JS!';"
"})();";
// Register the extension.
CefRegisterExtension("v8/test", extensionCode, NULL);
}
};
在 JS 中:
<script language="JavaScript">
alert(test.myval);
</script>
扩展函数
如果要扩展函数,则需要实现 CefV8Handler 来作为函数的处理器:
class MyV8Handler : public CefV8Handler
{
...
virtual bool Execute(const CefString& name, CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval,
CefString& exception) override
{
CEF_REQUIRE_RENDERER_THREAD();
if (name == "add")
{
auto num1 = arguments[0]->GetIntValue();
auto num2 = arguments[1]->GetIntValue();
auto sum = num1 + num2;
std::ostringstream oss;
oss << num1 << " + " << num2 << " = " << sum << std::endl;
OutputDebugStringA(oss.str().c_str());
retval = CefV8Value::CreateInt(sum);
return true;
}
// Function does not exist.
return false;
}
...
};
class MyApp : ..., public CefRenderProcessHandler
{
...
virtual void OnWebKitInitialized() override
{
CEF_REQUIRE_RENDERER_THREAD();
// Define the extension contents.
std::string extensionCode =
"var test;"
"if (!test)"
" test = {};"
"(function() {"
" test.add = function(num1, num2) {"
" native function add(num1, num2);"
" return add(num1, num2);"
" };"
"})();";
// Create an instance of my CefV8Handler object.
CefRefPtr<CefV8Handler> handler = new MyV8Handler();
// Register the extension.
CefRegisterExtension("v8/test", extensionCode, handler);
}
...
};
在 JS 中:
<script language="JavaScript">
alert(test.add(1,2));
</script>