首页 > cef > CEF-在程序中使用V8 JS(JavaScriptIntegration翻译)
2017十月10

CEF-在程序中使用V8 JS(JavaScriptIntegration翻译)

[隐藏]

https://bitbucket.org/chromiumembedded/cef/wiki/JavaScriptIntegration.md

该Wiki页面解释了如何在客户端应用程序中使用V8 JavaScript集成。

  

1.介绍

Chromium和CEF使用V8 JavaScript Engine进行内部JavaScript(JS)实现。浏览器中的每个框架都有自己的JS上下文,为该框架中执行的JS代码提供了范围和安全性(有关详细信息,请参阅“使用上下文”部分)。CEF公开了大量的JS特性,用于在客户端应用程序中集成。

  

CEF3 Blink(WebKit)它的JS执行运行在独立的渲染器(render)进程中,渲染器进程中的主线程被标识为TID_RENDERER,并且所有V8执行必须发生在此线程上。 与JS执行相关的回调通过CefRenderProcessHandler接口公开。当初始化新的渲染器进程时,该接口通过CefApp::GetRenderProcessHandler()检索。

  

在浏览器和渲染器进程之间进行通信的JS API应该使用异步回调设计。有关详细信息,请参阅GeneralUsage wiki页面的“异步JavaScript绑定”部分。

 

2.ExecuteJavaScript

从客户端应用程序执行JS的最简单的方法是使用CefFrame::ExecuteJavaScript()函数。 此函数在浏览器进程和渲染器进程中都可获得,可以安全地从JS上下文中使用。

CefRefPtr<CefBrowser> browser = ...;
CefRefPtr<CefFrame> frame = browser->GetMainFrame();
frame->ExecuteJavaScript("alert('ExecuteJavaScript works!');",
    frame->GetURL(), 0);

上面例子会引起在浏览器主框架执行alert('ExecuteJavaScript works!');

   

ExecuteJavaScript()函数可用于与frame的JS上下文中的函数和变量进行交互。为了将值从JS返回到客户端应用程序,请考虑使用窗口绑定或扩展。

  

3.窗口绑定

窗口绑定允许客户端应用程序将值附加到frame的窗口对象。 窗口绑定在CefRenderProcessHandler::OnContextCreated()中实现。

void MyRenderProcessHandler::OnContextCreated(
    CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame,
    CefRefPtr<CefV8Context> context) {
  // Retrieve the context's window object.
  CefRefPtr<CefV8Value> object = context->GetGlobal();

  // Create a new V8 string value. See the "Basic JS Types" section below.
  CefRefPtr<CefV8Value> str = CefV8Value::CreateString("My Value!");

  // Add the string to the window object as "window.myval". See the "JS Objects" section below.
  object->SetValue("myval", str, V8_PROPERTY_ATTRIBUTE_NONE);}

frame中的JavaScript可以与窗口绑定交互。

<script language="JavaScript">
alert(window.myval); // Shows an alert box with "My Value!"
</script>

  

每次重新加载frame时,都会重新加载窗口绑定,这使得客户端应用程序有机会在必要时更改绑定。例如,可以通过修改绑定到该frame的窗口对象的值来向客户端应用程序中的不同的frame提供对不同特征的访问。

 

4.扩展

扩展就像窗口绑定,但它们被加载到每个frame的上下文中,而且一旦加载就不能被修改。当扩展名加载时, 如果DOM不存在,并且在扩展加载期间尝试访问DOM将导致崩溃。 扩展名使用CefRegisterExtension()函数注册,该函数应该从CefRenderProcessHandler ::OnWebKitInitialized()中调用。

void MyRenderProcessHandler::OnWebKitInitialized() {
  // Define the extension contents.
  std::string extensionCode =
    "var test;"
    "if (!test)"
    "  test = {};"
    "(function() {"
    "  test.myval = 'My Value!';"
    "})();";

  // Register the extension.
  CefRegisterExtension("v8/test", extensionCode, NULL);
}

由extensionCode表示的字符串可以是任何有效的JS代码。frame中的JS可以与扩展代码进行交互。

<script language="JavaScript">
alert(test.myval); 
// Shows an alert box with "My Value!"</script>

 

5.基本JS类型

CEF支持创建基本的JS数据类型,包括undefined,null,bool,int,double,date和string。 这些类型是使用CefV8Value::Create*()静态方法创建的。 例如,要创建一个新的JS字符串值,请使用CreateString()方法。

 

可以随时创建不与特定上下文相关联的基本值类型,要测试值类型请使用Is*()函数

CefRefPtr<CefV8Value> val = ...;
if (val.IsString()) {
  // The value is a string.
}

使用Get*Value()函数来得到值:

CefString strVal = val.GetStringValue();

  

6.JS数组

使用接受长度参数的CefV8Value::CreateArray()静态方法创建数组。 数组只能在上下文中创建和使用(有关详细信息,请参阅“使用上下文”部分)。

// Create an array that can contain two values.
CefRefPtr<CefV8Value> arr = CefV8Value::CreateArray(2);

使用SetValue()将值加入数组,第一个参数为Index

// Add two values to the array.
arr->SetValue(0, CefV8Value::CreateString("My First String!"));
arr->SetValue(1, CefV8Value::CreateString("My Second String!"));

IsArray()函数测试CefV8Value是否为数组,GetArrayLength()获取数组长度,将Index做为第一个参数传入,使用GetValue()来获取值

  

7.JS对象

使用CefV8Value::CreateObject()静态方法创建对象,有个可选参数CefV8Accessor,对象只能在上下文中创建和使用(有关详细信息,请参阅“使用上下文”部分)。

CefRefPtr<CefV8Value> obj = CefV8Value::CreateObject(NULL);

使用SetValue()将值赋给一个对象,第一个参数为Key字符串

obj->SetValue("myval", CefV8Value::CreateString("My String!"));

 

8.带访问器的JS对象

对象可以可选性的关联CefV8Accessor,它提供了用于获取和设置值的本地实现

CefRefPtr<CefV8Accessor> accessor = …;
CefRefPtr<CefV8Value> obj = CefV8Value::CreateObject(accessor);

必须由客户端应用程序提供CefV8Accessor接口的实现。

class MyV8Accessor : public CefV8Accessor {
public:
  MyV8Accessor() {}

  virtual bool Get(const CefString& name,
                   const CefRefPtr<CefV8Value> object,
                   CefRefPtr<CefV8Value>& retval,
                   CefString& exception) OVERRIDE {
    if (name == "myval") {
      // Return the value.
      retval = CefV8Value::CreateString(myval_);
      return true;
    }

    // Value does not exist.
    return false;
  }

  virtual bool Set(const CefString& name,
                   const CefRefPtr<CefV8Value> object,
                   const CefRefPtr<CefV8Value> value,
                   CefString& exception) OVERRIDE {
    if (name == "myval") {
      if (value.IsString()) {
        // Store the value.
        myval_ = value.GetStringValue();
      } else {
        // Throw an exception.
        exception = "Invalid value type";
      }
      return true;
    }

    // Value does not exist.
    return false;
  }

  // Variable used for storing the value.
  CefString myval_;

  // Provide the reference counting implementation for this class.
  IMPLEMENT_REFCOUNTING(MyV8Accessor);
};

为了通过访问器来传值,SetValue()函数方式必须设置AccessControl和PropertyAttribute参数

obj->SetValue("myval", V8_ACCESS_CONTROL_DEFAULT, 
    V8_PROPERTY_ATTRIBUTE_NONE);

 

9.JS函数

CEF支持使用本机实现创建JS函数。函数使用CefV8Value::CreateFunction()静态方法创建,接受名称和CefV8Handler参数,函数只能在上下文中创建和使用(有关详细信息,请参阅“使用上下文”部分)。

CefRefPtr<CefV8Handler> handler = …;
CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("myfunc", handler);

必须由客户端应用程序提供CefV8Handler接口的实现。

class MyV8Handler : public CefV8Handler {
public:
  MyV8Handler() {}

  virtual bool Execute(const CefString& name,
                       CefRefPtr<CefV8Value> object,
                       const CefV8ValueList& arguments,
                       CefRefPtr<CefV8Value>& retval,
                       CefString& exception) OVERRIDE {
    if (name == "myfunc") {
      // Return my string value.
      retval = CefV8Value::CreateString("My Value!");
      return true;
    }

    // Function does not exist.
    return false;
  }

  // Provide the reference counting implementation for this class.
  IMPLEMENT_REFCOUNTING(MyV8Handler);
};

  

10.函数和窗口绑定

函数可用于创建复杂的窗口绑定。

void MyRenderProcessHandler::OnContextCreated(
    CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame,
    CefRefPtr<CefV8Context> context) {
  // Retrieve the context's window object.
  CefRefPtr<CefV8Value> object = context->GetGlobal();

  // Create an instance of my CefV8Handler object.
  CefRefPtr<CefV8Handler> handler = new MyV8Handler();

  // Create the "myfunc" function.
  CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("myfunc", handler);

  // Add the "myfunc" function to the "window" object.
  object->SetValue("myfunc", func, V8_PROPERTY_ATTRIBUTE_NONE);
}
<script language="JavaScript">
alert(window.myfunc()); // Shows an alert box with "My Value!"
</script>

 

11.函数和扩展

函数可用于创建复杂的扩展。 注意使用在使用扩展时需要“native function”前向声明。

void MyRenderProcessHandler::OnWebKitInitialized() {
  // Define the extension contents.
  std::string extensionCode =
    "var test;"
    "if (!test)"
    "  test = {};"
    "(function() {"
    "  test.myfunc = function() {"
    "    native function myfunc();"
    "    return myfunc();"
    "  };"
    "})();";

  // Create an instance of my CefV8Handler object.
  CefRefPtr<CefV8Handler> handler = new MyV8Handler();

  // Register the extension.
  CefRegisterExtension("v8/test", extensionCode, handler);
}
<script language="JavaScript">
alert(test.myfunc()); // Shows an alert box with "My Value!"
</script>

 

12.使用上下文

浏览器的每个frame都有自己的V8上下文,上下文定义了该frame定义的所有变量、对象和函数的范围。如果当前代码位置在调用堆栈里有一个CefV8Handler,CefV8Accessor或OnContextCreated()/OnContextReleased()回调,则V8将位于上下文中,OnContextCreated()和OnContextReleased() 定义与frame关联的V8上下文的完整使用寿命。 使用以下方法时,请注意遵循以下规则:

1.对该上下文的OnContextReleased()的调用,不要执行或使用V8上下文的引用

2.所有V8对象的寿命未指定(直到GC)。将V8对象直接引用到自己的内部实现对象时要小心。在许多情况下,当为上下文调用OnContextReleased()时,使用与V8上下文关联的代理对象可能会更好,并且可以“断开”(允许内部实现对象被释放)。

 

数组,对象和函数只能V8的上下文环境下创建和使用,如果V8不在上下文,则应用程序必须调用Enter()进入上下文件,并通过调用Exit()退出上下文,Enter()和Exit()仅用于以下场景:

1.在现有上下文之外创建V8对象,函数或数组时。 例如,当创建一个JS对象以响应本地菜单回调时。

2.在当前上下文之外的上下文中创建V8对象,函数或数组时。 例如,如果来自frame1的call需要修改frame2的上下文。

   

13.执行函数

本地代码可以使用ExecuteFunction()和ExecuteFunctionWithContext()方法执行JS函数。 只有当V8已经在上下文中时才使用ExecuteFunction()方法,如“使用上下文”部分所述。 ExecuteFunctionWithContext()方法允许应用程序指定要输入的上下文以供执行。

 

14.使用JS回调

当使用本机代码注册JS函数回调时,应用程序应该存储对本机代码中的当前上下文和JS函数的引用。 这可以如下实现:

1.在OnJSBinding()中创建一个“register”函数。

void MyRenderProcessHandler::OnContextCreated(
    CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame,
    CefRefPtr<CefV8Context> context) {
  // Retrieve the context's window object.
  CefRefPtr<CefV8Value> object = context->GetGlobal();

  CefRefPtr<CefV8Handler> handler = new MyV8Handler(this);
  object->SetValue("register",
                   CefV8Value::CreateFunction("register", handler),
                   V8_PROPERTY_ATTRIBUTE_NONE);
}

2.在“register”函数的MyV8Handler::Execute()实现中,保留对上下文和函数的引用。

bool MyV8Handler::Execute(const CefString& name,
                          CefRefPtr<CefV8Value> object,
                          const CefV8ValueList& arguments,
                          CefRefPtr<CefV8Value>& retval,
                          CefString& exception) {
  if (name == "register") {
    if (arguments.size() == 1 && arguments[0]->IsFunction()) {
      callback_func_ = arguments[0];
      callback_context_ = CefV8Context::GetCurrentContext();
      return true;
    }
  }

  return false;
}

3.通过JavaScript注册JS回调。

<script language="JavaScript">
function myFunc() {
  // do something in JS.
}
window.register(myFunc);
</script>

4.稍后再执行JS回调。

CefV8ValueList args;
CefRefPtr<CefV8Value> retval;
CefRefPtr<CefV8Exception> exception;
if (callback_func_->ExecuteFunctionWithContext(callback_context_, NULL, args, retval, exception, false)) {
  if (exception.get()) {
    // Execution threw an exception.
  } else {
    // Execution succeeded.
  }
}

有关使用回调的更多信息,请参阅GeneralUsage wiki页面的“异步JavaScript绑定”部分。

文章作者:hgy413
本文地址:https://hgy413.com/4425.html
版权所有 © 转载时必须以链接形式注明作者和原始出处!

本文目前尚无任何评论.

发表评论