electron chromium 内部事件发送到外围 通知到js

参考网页加载完成的事件,实现chromium内部对外的js发送事件。

FrameHostMsg_DidFinishLoad

带反馈的可参考:FrameMsg_BeforeUnload ,反馈事件:FrameHostMsg_BeforeUnload_ACK,FrameHostMsg_RunBeforeUnloadConfirm

基于electron 7:

从js addEventListener事件截获,发往iframe。

首先新添加消息。在D:\dev\electron7\src\content\common\frame_messages.h 定义消息。

IPC_MESSAGE_ROUTED1(FrameMsg_Notify_addEventListener, bool /* is_reload */)

 

1,事件截获:d:\dev\electron7\src\third_party\blink\renderer\core\dom\events\event_target.cc

bool EventTarget::AddEventListenerInternal(
    const AtomicString& event_type,
    EventListener* listener,
    const AddEventListenerOptionsResolved* options) {
  if (!listener)
    return false;

  if (event_type == event_type_names::kTouchcancel ||
      event_type == event_type_names::kTouchend ||
      event_type == event_type_names::kTouchmove ||
      event_type == event_type_names::kTouchstart) {
    if (const LocalDOMWindow* executing_window = ExecutingWindow()) {
      if (const Document* document = executing_window->document()) {
        document->CountUse(options->passive()
                               ? WebFeature::kPassiveTouchEventListener
                               : WebFeature::kNonPassiveTouchEventListener);
      }
    }
  }

  V8DOMActivityLogger* activity_logger =
      V8DOMActivityLogger::CurrentActivityLoggerIfIsolatedWorld();
  if (activity_logger) {
    Vector argv;
    argv.push_back(ToNode() ? ToNode()->nodeName() : InterfaceName());
    argv.push_back(event_type);
    activity_logger->LogEvent("blinkAddEventListener", argv.size(),
                              argv.data());
  }

#ifndef CONFIG_NO_NOTIFY_ADD_EVENT_LISTENER_DISPATCH  // zhibin:patch_to_content
                                                      // start call
  {
    LocalDOMWindow* executing_window = ExecutingWindow();
    LocalFrame* frame = executing_window->GetFrame();
    Node* node = ToNode();

//  String node_name = node ? node->nodeName() : InterfaceName();    

    if (node && node->IsElementNode()) {
      Element* ele = static_cast(node);
      AtomicString old_id = ele->IdForStyleResolution();
      frame->Client()->DispatchDidNotifyEventAdded(
          old_id.GetString().Utf8(), event_type.GetString().Utf8());
    }
    /*  don't send built-in addEventListener  
    else{
      frame->Client()->DispatchDidNotifyEventAdded(
        node_name.Utf8(),
        event_type.GetString().Utf8());
    }
    */  
  }
#endif
  RegisteredEventListener registered_listener;
  bool added = EnsureEventTargetData().event_listener_map.Add(
      event_type, listener, options, &registered_listener);
  if (added) {
    AddedEventListener(event_type, registered_listener);
    if (IsA(listener) &&
        IsInstrumentedForAsyncStack(event_type)) {
      probe::AsyncTaskScheduled(GetExecutionContext(), event_type,
                                listener->async_task_id());
    }
  }
  return added;

 

 

2,发往外围client:

d:\dev\electron7\src\third_party\blink\renderer\core\frame\local_frame_client.h

#ifndef CONFIG_NO_NOTIFY_ADD_EVENT_LISTENER_DISPATCH  // zhibin:patch_to_content
                                                      // ipc
  virtual void DispatchDidNotifyEventAdded(const std::string& node_name,
                                           const std::string& event_type) {}
  #endif

d:\dev\electron7\src\third_party\blink\renderer\core\exported\local_frame_client_impl.h

#ifndef CONFIG_NO_NOTIFY_ADD_EVENT_LISTENER_DISPATCH  // zhibin:patch_to_content
                                                      // ipc 
  void DispatchDidNotifyEventAdded(const std::string& node_name,
                                   const std::string& event_type) override;
 #endif

d:\dev\electron7\src\third_party\blink\renderer\core\exported\local_frame_client_impl.cc

#ifndef CONFIG_NO_NOTIFY_ADD_EVENT_LISTENER_DISPATCH  // zhibin:patch_to_content
void LocalFrameClientImpl::DispatchDidNotifyEventAdded(
    const std::string& node_name,
    const std::string& event_type) {
  web_frame_->DidNotifyEvent(node_name,event_type);
}
#endif

3,传到web层接口:

d:\dev\electron7\src\third_party\blink\public\web\web_local_frame_client.h

#ifndef CONFIG_NO_NOTIFY_ADD_EVENT_LISTENER_DISPATCH  // zhibin:patch_to_content
                                                      // ipc voidparam
  virtual void DidNotifyEventAdded(const std::string& node_name,
                                   const std::string& event_type) {} 

  #endif

实现:d:\dev\electron7\src\third_party\blink\renderer\core\frame\web_local_frame_impl.h

#ifndef CONFIG_NO_NOTIFY_ADD_EVENT_LISTENER_DISPATCH  // zhibin:patch_to_content
  void DidNotifyEvent(const std::string& node_name,
                      const std::string& event_type);
  #endif

d:\dev\electron7\src\third_party\blink\renderer\core\frame\web_local_frame_impl.cc

#ifndef CONFIG_NO_NOTIFY_ADD_EVENT_LISTENER_DISPATCH  // zhibin:patch_to_content
void WebLocalFrameImpl::DidNotifyEvent(const std::string& node_name,
                                       const std::string& event_type) {
  if (!Client())
    return;

//  if (WebPluginContainerImpl* plugin = GetFrame()->GetWebPluginContainer()) //zhi:todo
//    plugin->DidFinishLoading();

  Client()->DidNotifyEventAdded(node_name,event_type);//voidparam not client's interface
}
#endif

4,最后到达frame的实现层,将ipc 消息发送出去:

d:\dev\electron7\src\content\renderer\render_frame_impl.h

d:\dev\electron7\src\content\renderer\render_frame_impl.cc

#ifndef CONFIG_NO_NOTIFY_ADD_EVENT_LISTENER_DISPATCH  // zhibin:patch_to_content
                                                      // voidParam
  void DidNotifyEventAdded(const std::string& node_name,
                           const std::string& event_type) override;
  #endif
#ifndef CONFIG_NO_NOTIFY_ADD_EVENT_LISTENER_DISPATCH  // zhibin:patch_to_content send notification
void RenderFrameImpl::DidNotifyEventAdded(const std::string& node_name,
                                          const std::string& event_type) {
  TRACE_EVENT1("navigation,benchmark,rail", "RenderFrameImpl::DidAddEventListenerCalled",
               "id", routing_id_);
  if (!frame_->Parent()) {
    TRACE_EVENT_INSTANT0("WebCore,benchmark,rail", "DidAddEventListenerCalled",
                         TRACE_EVENT_SCOPE_PROCESS);
  }
  /*
  for (auto& observer : observers_)
    observer.DidFinishLoad();//:todo
*/
 // WebDocumentLoader* document_loader = frame_->GetDocumentLoader();
  Send(new FrameHostMsg_DidAddEventListenerCalled(routing_id_, node_name,
                                                  event_type));
}

 


5,content外层接收事件

D:\dev\electron7\src\content\browser\web_contents\web_contents_impl.h 添加事件处理函数:

  #if 1//zhibin:patch ipc
  void OnAddEventListenerCalled(RenderFrameHost* render_frame_host,
                       const GURL& url) override;
 #endif

 

D:\dev\electron7\src\content\browser\web_contents\web_contents_impl.cc

#if 1//zhibin:patch ipc
    IPC_MESSAGE_HANDLER(FrameMsg_Notify_addEventListener, OnAddEventListenerCalled)
#endif   


#if 1//zhibin:patch ipc
void WebContentsImpl::OnAddEventListenerCalled(RenderFrameHostImpl* source,
                                               const GURL& url) {
 

  if (!source->GetParent()) {
    size_t frame_count = source->frame_tree_node()->GetFrameTreeSize();
    UMA_HISTOGRAM_COUNTS_1000("Navigation.MainFrame.FrameCount", frame_count);
  }

  for (auto& observer : observers_) observer.DidAddEventListenerCalled(source, validated_url); } #endif

 

 6,调用ovserver通知出去

D:\dev\electron7\src\content\public\browser\web_contents_observer.h 回调接口,需要被继承实现。

  virtual void DidFinishLoad(RenderFrameHost* render_frame_host,
                             const GURL& validated_url) {}

#if 1
virtual void DidAddEventListenerCalled(RenderFrameHost* render_frame_host,
const GURL& validated_url) {}

#endif

 

 7,外围electron实现:

D:\dev\electron7\src\electron\shell\browser\api\atom_api_web_contents.cc

#if 1//zhibin:patch ipc

void WebContents::DidAddEventListenerCalled(
    content::RenderFrameHost* render_frame_host,
                                const GURL& validated_url) {
  bool is_main_frame = !render_frame_host->GetParent();
  int frame_process_id = render_frame_host->GetProcess()->GetID();
  int frame_routing_id = render_frame_host->GetRoutingID(); Emit("did-frame-finish-load", is_main_frame, frame_process_id, frame_routing_id); Emit("event-demo"); } #endif

 D:\dev\electron7\src\electron\shell\browser\api\atom_api_web_contents.h

  #if 1//zhibin:patch ipc
  void DidAddEventListenerCalled(content::RenderFrameHost* render_frame_host,
                     const GURL& validated_url) override;
  #endif

  

 nodejs测试示例:

package.json

{
  "name": "eventdemo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "event"
  ],
  "author": "zb",
  "license": "ISC"
}

index.html

DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World!title>
    
    <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
    
  head>
  <body>
    <h1><a href=index.html> myself1a>h1>
     <h1><a href=http://www.baidu.com>baidua>h1>
    We are using node <script>document.write(process.versions.node)script>,
    Chrome <script>document.write(process.versions.chrome)script>,
    and Electron <script>document.write(process.versions.electron)script>.
  body>
  <button id="btn">submit buttonbutton>
    <script>
       alert("I am a alert.");
   
        document.getElementById("btn").addEventListener("click",function(){
            console.log("button clicked.")
        },false);
       
    script>
html>

index.js

const { dialog, app, BrowserWindow } = require('electron')

function createWindow () {   
  // 创建浏览器窗口
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  })

  // 并且为你的应用加载index.html
  win.loadFile('index.html')

  // 打开开发者工具
  //win.webContents.openDevTools();
  win.webContents.on('will-navigate', (e, url,) => {
  console.log("===will-navigate===");
   });
   
   win.webContents.on('did-finish-load', function() {  
      console.log("===============load page successfully");  
      // win.webContents.executeJavaScript('alert(" load finish alert ");');
   });
    
  /*        
   win.webContents.on('event-demo', function() {  
      console.log("==== event-demo=========================");  
   });
   */   
   
  //addEventListener事件通知 
  win.webContents.on('event-demo',  function(event,element_id, event_name){
      console.log("========================= tistar-addEventListener-event begin =========================");    
    console.log(event);
       console.log(element_id);
       console.log(event_name);
       var str='var bbb=document.getElementById("'+'btn'+'");alert(bbb.innerHTML)';
       console.log(str);   
       win.webContents.executeJavaScript(str);
       console.log("========================= tistar-addEventListener-event end =========================");    
  }); 
  
  //屏蔽弹出框事件 
  win.webContents.on('tistar-dialog-event', function(event,dialog_type, message) { 
    console.log("========= tistar-dialog-event begin =========");
      //console.log(event);
      console.log(dialog_type);
      console.log(message);
      console.log("========= tistar-dialog-event end =========="); 
  });

}

// Electron会在初始化完成并且准备好创建浏览器窗口时调用这个方法
// 部分 API 在 ready 事件触发后才能使用。
app.whenReady().then(createWindow)

//当所有窗口都被关闭后退出
app.on('window-all-closed', () => {
  // 在 macOS 上,除非用户用 Cmd + Q 确定地退出,
  // 否则绝大部分应用及其菜单栏会保持激活。
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  // 在macOS上,当单击dock图标并且没有其他窗口打开时,
  // 通常在应用程序中重新创建一个窗口。
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow()
  }
})

// 您可以把应用程序其他的流程写在在此文件中
// 代码 也可以拆分成几个文件,然后用 require 导入。

 


 

参考:https://www.jianshu.com/p/2a2424bdc057

https://www.cnblogs.com/danxi/p/7766147.html

chromium源码阅读--进程间通信(IPC)

 

     第一篇就有提到Chromium是目前默认是采用多进程架构,当然,chromium有singe-process的版本。

     多进程与多线程的区别,确实有很多可以讲的,我的另一篇博客也讲了一些 (Linux 进程,线程),这里是从浏览器的角度来说,如果是多线程,如果一个线程崩溃,影响了整个浏览器的使用,因为在现在的网页标准更新了很多个版本,会有不同标准的页面在网络上,极大可能出现解析,渲染,插件等问题,那么对于用户来说,体验就会差很多了,浏览一个页面出问题,就要重启浏览器。而多进程则可以避免此问题,render进程崩溃只会影响当前的tab。

    嗯,上面说了那么多,就是为了说,多进程之间就需要进程通信来协作,而chromium的进程间通信是非常繁杂的,如何处理这个是我们需要了解的关键。

   那么本质的问题就是:

         1、发那些消息(Message Type)

         2、消息通道是怎么建立的 (Message Channel)

         3、发送者和接收者(Sender,Listener)

OK,咱一个个来。

 

一、 Message Type 

     主要分为2类:“routed” 和 “control”。

     1、routed消息

         主要是用来给某个RenderViewHost对象发送消息的。不过,任何类都可以通过GetNextRoutingID 和 AddRoute 注册,就能接收routed消息。

     2、control消息

          control消息有创建pipe的类处理,当然这些类也可以接收routed消息。比如,请求资源或修改剪贴板不是特定于视图的,所以是控制消息。

     3、消息的声明

1 IPC_MESSAGE_ROUTED2(FrameHostMsg_MyMessage, GURL, int)

      这个宏用来声明routed消息,这里声明了一个从render进程发送到browser进程的消息,并有一个GURL参数,一个int参数

1 IPC_MESSAGE_CONTROL0(FrameMsg_MyMessage)

     这个宏用来声明control消息,这里声明了一个从browser进程发送到render进程的消息,没有参数。

     这里还有几个默认的约定:

          (1)这些宏后面的数字表明有几个参数,最多5个参数,即: IPC_MESSAGE_ROUTED0~IPC_MESSAGE_ROUTED5 或者 IPC_MESSAGE_CONTROL0~IPC_MESSAGE_CONTROL5

          (2)消息名称表明消息的接受者,FrameHostMsg,带Host后缀的类,表示在browser进程接收处理的消息,FrameMsg,则表示在render进程处理的消息,如果是Plugin进程,也会带有Plugin字样。

 

二、Message Channel

    chromium的使用mojo IPC,并且在官网提供了性能对比 (Times in microseconds)

 

Windows Z840

Linux Z620

MacBook Pro 15" 2016

IPC

36.9

69.5

52.5

Mojo cross-process

28.2

48

34.9

 

这里是官网关于mojo的一些介绍,https://chromium.googlesource.com/chromium/src/+/master/mojo/README.md#System-Overview

从unittest看channel的创建:

 

1 void IPCChannelMojoTestBase::CreateChannel(IPC::Listener* listener) {
2   channel_ =
3       IPC::ChannelMojo::Create(TakeHandle(), IPC::Channel::MODE_SERVER,
4                                listener, base::ThreadTaskRunnerHandle::Get());
5 }

 在IPC::ChannelMojo::Create里看到需要 IPC::ChannelMojo的构造,

复制代码
 1 ChannelMojo::ChannelMojo(
 2     mojo::ScopedMessagePipeHandle handle,
 3     Mode mode,
 4     Listener* listener,
 5     const scoped_refptr& ipc_task_runner)
 6     : task_runner_(ipc_task_runner),
 7       pipe_(handle.get()),
 8       listener_(listener),
 9       weak_factory_(this) {
10   weak_ptr_ = weak_factory_.GetWeakPtr();
11   bootstrap_ = MojoBootstrap::Create(std::move(handle), mode, ipc_task_runner);
12 }
复制代码

 在MojoBootstrapImpl里完成sender和listener的绑定:

复制代码
 1 class MojoBootstrapImpl : public MojoBootstrap {
 2  public:
 3   MojoBootstrapImpl(
 4       mojo::ScopedMessagePipeHandle handle,
 5       const scoped_refptr controller)
 6       : controller_(controller),
 7         associated_group_(controller),
 8         handle_(std::move(handle)) {}
 9 
10   ~MojoBootstrapImpl() override {
11     controller_->ShutDown();
12   }
13 
14  private:
15   void Connect(mojom::ChannelAssociatedPtr* sender,
16                mojom::ChannelAssociatedRequest* receiver) override {
17     controller_->Bind(std::move(handle_));
18     controller_->CreateChannelEndpoints(sender, receiver);
19   }
20 
21  。。。
22 }
复制代码

 上面的mojo  Channel的创建过程,linux提供的IPC比如:pipe,unix socket,share memory都不是线程安全的,mojo封装了底层IPC细节并提供了线程安全保障,并且看上面的性能对比,mojo性能更好,这也是chromium逐渐转用mojo的主要因素吧。

 OK,上面介绍了mojo,接下来我们会发现,在进程里都是使用IPC::ChannelProxy这个类来代理完成Channel的各种工作。

 这里我们只需看一个例子就能理解了,比如在browser进程的RenderProcessHost类里声明了GetChannel接口:

1 IPC::ChannelProxy* GetChannel() = 0;

根据chromium的套路,你大致就能想到,有一个RenderProcessHostImpl类会来实现这个接口,嗯,果不其然:

复制代码
 1 class CONTENT_EXPORT RenderProcessHostImpl
 2     : public RenderProcessHost,
 3       public ChildProcessLauncher::Client,
 4       public ui::GpuSwitchingObserver,
 5       public mojom::RouteProvider,
 6       public mojom::AssociatedInterfaceProvider,
 7       public mojom::RendererHost {
 8       ...
 9       IPC::ChannelProxy* GetChannel() override;
10       ...
11 }
复制代码

我们可以看到这里会提供一个IPC::ChannelProxy的指针,那么顺着这个,ChannelProxy的创建和初始化就不远了。

复制代码
bool RenderProcessHostImpl::Init() {
   ...
   if (!channel_)
    InitializeChannelProxy();
  
   ...
   CreateMessageFilters();
  RegisterMojoInterfaces();
  ...

}
复制代码

可以看到,上面初始化了Channel并给当前实例创建了MessageFilter和在mojo里注册了消息发送的mojo interface。

mojo会负责将channel两端连通,之后的消息发送就可使用IPC::ChannelProxy来完成了。

 

三、发送者和接收者

    1、发送者

    chromium里定义了IPC::Sender的接口:

复制代码
 1 class Message;
 2 
 3 class IPC_EXPORT Sender {
 4  public:
 5   // Sends the given IPC message.  The implementor takes ownership of the
 6   // given Message regardless of whether or not this method succeeds.  This
 7   // is done to make this method easier to use.  Returns true on success and
 8   // false otherwise.
 9   virtual bool Send(Message* msg) = 0;
10 
11  protected:
12   virtual ~Sender() {}
13 };
复制代码

    上面的使用例子,我们可以看到 IPC::ChannelProxy 是消息的发送者,看类的声明:

1 class IPC_EXPORT ChannelProxy : public Sender {
2 
3 }

2、接收者

    同样chromium也定义Listener。

复制代码
class Message;

// Implemented by consumers of a Channel to receive messages.
class IPC_EXPORT Listener {
 public:
  // Called when a message is received.  Returns true iff the message was
  // handled.
  virtual bool OnMessageReceived(const Message& message) = 0;

  ...
};
复制代码

我们在前面提到的router,是消息接收者,也是消息发送者:

1 class IPC_EXPORT MessageRouter : public Listener, public Sender { 
2     ...
3 }

还有子线程实例也是Listener:

复制代码
1 class CONTENT_EXPORT ChildThreadImpl
2     : public IPC::Listener,
3       virtual public ChildThread,
4       private base::FieldTrialList::Observer,
5       public mojom::RouteProvider,
6       public mojom::AssociatedInterfaceProvider,
7       public mojom::ChildControl {
8     ...
9  }
复制代码

好了,更多例子我也不举了,chromium IPC还有更多的内容,在代码待我们学习,这里暂时总结到这里,后续再补充。

 

   

7.Web IDL绑定

当JavaScript访问node.firstChild时,将调用node.h中的Node :: firstChild()。它是如何工作的?我们来看看node.firstChild是如何工作的。

首先,您需要根据规范定义IDL文件:

// node.idl
interface Node : EventTarget { [...] readonly attribute Node? firstChild; }; 

Web IDL的语法在Web IDL规范中定义。 [...]称为IDL扩展属性。一些IDL扩展属性在Web IDL规范中定义,而其他属性是特定于Blink的IDL扩展属性。除了特定于Blink的IDL扩展属性外,IDL文件应以特定的方式编写(即只需从规范中复制和粘贴)。

其次,您需要为Node定义C ++类并为firstChild实现C ++ getter:

class EventTarget : public ScriptWrappable { // All classes exposed to JavaScript must inherit from ScriptWrappable. ...; }; class Node : public EventTarget { DEFINE_WRAPPERTYPEINFO(); // All classes that have IDL files must have this macro. Node* firstChild() const { return first_child_; } }; 

在一般情况下,就是这样。构建node.idl时,IDL编译器会自动为Node接口和Node.firstChild生成Blink-V8绑定。自动生成的绑定在// src / out / {Debug,Release} / gen / third_party / blink / renderer / bindings / core / v8 / v8_node.h中生成。当JavaScript调用node.firstChild时,V8在v8_node.h中调用V8Node :: firstChildAttributeGetterCallback(),然后它调用您在上面定义的Node :: firstChild()。

8.V8和Blink

8.1 Isolate, Context, World

当您编写涉及V8 API的代码时,了解Isolate,Context和World的概念非常重要。它们分别由代码库中的v8 :: Isolate,v8 :: Context和DOMWrapperWorld表示。

Isolate对应于物理线程。 Isolate : physical thread in Blink = 1 : 1。主线程有自己的隔离。工作线程有自己的隔离。

Context对应于全局对象(在Frame的情况下,它是Frame的窗口对象)。由于每个帧都有自己的窗口对象,因此渲染器进程中有多个上下文。当您调用V8 API时,您必须确保您处于正确的上下文中。否则,v8 :: Isolate :: GetCurrentContext()将返回错误的上下文,在最坏的情况下,它将最终泄漏对象并导致安全问题。

World是支持Chrome扩展程序内容脚本的概念。世界与Web标准中的任何内容都不对应。内容脚本希望与网页共享DOM,但出于安全原因,必须将内容脚本的JavaScript对象与网页的JavaScript堆隔离。 (另外一个内容脚本的JavaScript堆必须与另一个内容脚本的JavaScript堆隔离。)为了实现隔离,主线程为网页创建一个主要世界,为每个内容脚本创建一个隔离的世界。主要世界和孤立的世界可以访问相同的C ++ DOM对象,但它们的JavaScript对象是隔离的。通过为一个C ++ DOM对象创建多个V8包装器来实现这种隔离。即每个世界一个V8包装器。

 
electron chromium 内部事件发送到外围 通知到js_第1张图片
v8_blink_arch.jpg

Context,World和Frame之间有什么关系?

 

想象一下,主线上有N个世界(一个主要世界+(N - 1)个孤立的世界)。然后一个Frame应该有N个窗口对象,每个窗口对象用于一个世界。上下文是对应于窗口对象的概念。这意味着当我们有M帧和N个世界时,我们有M * N上下文(但是上下文是懒洋洋地创建的)。

对于worker,只有一个世界和一个全球对象。因此,只有一个上下文。

同样,当您使用V8 API时,您应该非常小心使用正确的上下文。否则,您最终会在孤立的世界之间泄漏JavaScript对象并导致安全灾难(例如,A.com的扩展可以操纵来自B.com的扩展)。

8.2 V8 API

在//v8/include/v8.h中定义了很多V8 API。由于V8 API是低级的并且难以正确使用,因此platform / bindings /提供了一堆包装V8 API的辅助类。您应该考虑尽可能多地使用帮助程序类。如果您的代码必须大量使用V8 API,那么这些文件应该放在bindings / {core,modules}中。

V8使用句柄指向V8对象。最常见的句柄是v8 :: Local <>,用于指向机器堆栈中的V8对象。在机器堆栈上分配v8 :: HandleScope后,必须使用v8 :: Local <>。不应在机器堆栈外使用v8 :: Local <>:

void function() { v8::HandleScope scope; v8::Local::Object> object = ...; // This is correct. } class SomeObject : public GarbageCollected> { v8::Local::Object> object_; // This is wrong. }; 
8.3 V8 wrappers

每个C ++ DOM对象(例如,Node)具有其对应的V8包装器。准确地说,每个C ++ DOM对象每个世界都有相应的V8包装器。

V8包装器对其对应的C ++ DOM对象具有强引用。但是,C ++ DOM对象只有对V8包装器的弱引用。因此,如果您希望将V8包装器保持活动一段时间,则必须明确地执行此操作。否则,V8包装器将过早收集,V8包装器上的JS属性将丢失。


div = document.getElementbyId("div"); child = div.firstChild; child.foo = "bar"; child = null; gc(); // If we don't do anything, the V8 wrapper of |firstChild| is collected by the GC. assert(div.firstChild.foo === "bar"); //...and this will fail. 

如果我们不做任何事情,那么孩子会被GC收集,因此child.foo会丢失。为了使div.firstChild的V8包装器保持活动状态,我们必须添加一种机制,“只要div所属的DOM树可以从V8到达,就可以使div.firstChild的V8包装器保持活动状态”。



作者:JeffMony
链接:https://www.jianshu.com/p/2a2424bdc057
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(electron chromium 内部事件发送到外围 通知到js)