chromium IME

最近负责改进浏览器的输入法,现在改进基本完成,做一个总结,这一篇先总结一下chromium的事件传递部分,同时也介绍一下chromium中的IME模块。

chromium中有几个处理事件的组件:

    1.PlatformEventSource:chromium事件源,负责将Platform事件接入到浏览器中,维护一个 PlatformEventDispatcher 列表,并通过遍历这个列表来下发事件,是一个单例类;

class EVENTS_EXPORT PlatformEventSource {
public:
  // 添加 PlatformEventDispatcher 到 dispatcher list 中
  void AddPlatformEventDispatcher(PlatformEventDispatcher* dispatcher);
  
protected:
  PlatformEventSource();  

  // 遍历 dispatcher list 让每一个 dispatcher 分别分发事件,核心方法
  virtual uint32_t DispatchEvent(PlatformEvent platform_event);
private:
  // chromium 中定义的 ObserserList 模板,不是一个普通的STL模板,它支持通知在通知期间加入的观察者
  typedef base::ObserverList<PlatformEventDispatcher> PlatformEventDispatcherList;
  PlatformEventDispatcherList dispatchers_;
  static PlatformEventSource *instance_;
};

//核心方法的实现
uint32_t PlatformEventSource::DispatchEvent(PlatformEvent platform_event) {
  ...
  if ((action & POST_DISPATCH_PERFORM_DEFAULT) && dispatchers_.might_have_observers()) {
    base::ObserverList<PlatformEventDispatcher>::Iterator iter(&dispatchers_);
    while (PlatformEventDispatcher* dispatcher = iter.GetNext()) {
      if (dispatcher->CanDispatchEvent(platform_event))
         action = dispatcher->DispatchEvent(platform_event);
      // 事件被处理就停止分发,在此通过返回值确定
      if (action & POST_DISPATCH_STOP_PROPAGATION)
         break;
  }
  ...
}

    2.PlatformEventDispatcher:chromium中的事件分发者,要回去chromium中的事件,就是继承这个类,并且将注册到PlatformEventSource的dispatcher列表中;

class EVENTS_EXPORT PlatformDispatcher{
public:
  ...
  // 判断是否分发这个事件
  virtual bool CanDispatchEvent(const PlatformEvent& event) = 0;
  // 传递事件,返回值表示事件是否继续传递,停止,返回POST_DISPATCH_STOP_PROPAGATION
  // 继续传递:POST_DISPATCH_PERFORM_DEFAULT 或者 0都可以
  virtual uint32_t DispatchEvent(const PlatformEvent& event) = 0;
  ...
};

    3.BrowserWindow:浏览器的主窗体,也就是UI部分,使用chromium的Aura UI实现,Aura UI封装了win、mac以及gtk等的实现,具有良好跨平台的能力。由于UI也需要接收事件,所以也会继承 PlatformEventDispatcher,实现以上两个方法就可以得到传入 chromium 的事件 ,不属于UI部分处理的事件,将会传递到后续的dispatcher;


    4.WindowTreeHost:PlatformEventDispatcher的子类,拥有分发事件的能力,主要负责将事件传递到IME或者render进程(页面);将事件发送到chromium的EventHandler,然后会将事件转换成WebKit事件,发送到render,由WebKit处理。这部分后续单独总结。


    InputMethod:声明了输入相关的接口,包括显示键盘,处理输入,以及事件转发等。

    InputMethodBase:实现 InputMethod 中平台独立性的接口。

    InputMethodMinimal:InputMethodBase的子类,chromium中在使用Aura UI时默认生成的IME组件;不同平台上的IME继承自InputMethodBase,实现平台相关的事件转换。

    TextInputClient:声明输入框的逻辑接口。

默认事件处理流程:

1、来自系统的事件,首先被送到PlatformEventSource;

2、PlatformEventSource  将分发事件,遍历 PlatformEventDispatcher 列表。

3、首先调用 dispatcher 的 CanDispatchEvent 方法,若该dispatcher 支持当前事件,则继续调用的 DispatcherEvent() 方法,进一步处理事件,否则跳过当前dispatcher;

4、Aura UI 封装了平台相关的UI组件,网页实际就是显示在一个封装的 Navtie widget 中,而其中就有个 WindowTreeHost;

5、WindowTreeHost创建时将自身注册到 PlatformSourceEvent 的 dispatcher 列表中,所以当事件来到时,先经过WindowTreeHost;

6、WindowTreeHost首先将事件传送到 IME 模块中,若当前输入状态,则 IME 处理事件,否则,将事件通过WIndowTreeHost(IME的delegate)重新发送到 事件处理器,

      若处于输入状态,则通过事件获取输入的字符,然后显示到输入的地方(默认情况下,只支持USB键盘的事件处理),然后事件停止,不在继续发送和处理。

以上就是一个默认的处理输入的流程。


实际场景中,目前是针对电视机的基于chromium的浏览器,除了USB键盘,还支持了对遥控器和USB鼠标事件的响应。

所以实际流程是这样的:

1、使用Aura UI实现了一个browser界面、以及界面内部的OSK,将browser界面继承自 PlatformEventDispatcher,同时注册到 PlatformEventSource 的 dispatcher列表。

2、当事件达到时,先有browser界面处理,若处理,则事件停止传送,否则,将通过WindowTreeHost 将事件分发到IME,然后是页面;

3、问题一,OSK属于browser界面,运行在browser端的UI线程,当使用OSK向页面输入时,需要通过开启一组额外的IPC,使处理逻辑变得复杂

4、问题二,当使用USB键盘输入时,事件直接通过IME组件,然后输入内容发送到页面,这容易出现输入内容不同步的问题;

5、问题三, 当使用遥控器和USB鼠标时,事件同样先有browser界面中的OSK处理,然后通过IPC传给页面。


所以需要进行改进,将OSK以及输入事件的处理同意起来,解决输入的同步问题。


解决思路:

1、将IME模块独立出来,让其最先接收事件,若是输入事件,直接处理,否则将事件继续往下发送;

2、OSK的显示仍然有browser界面管理,但是其输入的操作合并到IME中,让输入事件统一处理,输入结果统一设置,解决输入不同步的问题。


解决方案:

1、创建一个 ImeEventHandler 类(一下称 handler),继承自PlatformEventDispatcher,第一个注册到 PlatformEventSource 的dispatcher 列表中;

2、OSK保持事件处理能力,但是事件来源是handler,而不是browser UI;

3、USB键盘的输入也一样,事件来源于handler,不再是WindowTreeHost。


新的处理流程:

1、创建ImeEventHandler,注册为 PatformEventSource 的第一个dispatcher。

2、当事件到来,根据事件的类型,分别发送的响应的处理函数;

3、若是USB Keyboard 输入事件,直接从Event中获取要输入的字符,若是遥控器或者USB 鼠标类型,传到OSK处理事件,将输入结果返回到handler,统一输入。

4、不是输入事件,仍然抛到 browser UI 或者是 WindowTreeHost 去处理。


此处用 ImeEventHandler 来实现IME的工作流程,将输入事件的传递与其他UI和页面分离开来,有handler来完成主要的输入功能。

说明一下,在使用遥控器和USB鼠标时,产生的事件并没有要输入的字符,而是在遥控器和USB鼠标触发点击按钮之后,将事件传送到OSK,通过事件的位置来判断点击了哪一个按键,然后产生响应的结果。


以上是一个思路和方案的总结,若有不当之处,请指正。

在后面,还将为OSK添加一个外接的输入法框架,可以接入多种语言的输入引擎,同时该输入法框架运行在browser之外,与browser通过IPC进行通信。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值