Wiki

Clone wiki

java-cef / 体验V49中自定义协议

* 何为自定义协议

浏览器嘛,最重要的就是对HTTP协议的解析支持,http开头的即是。

但是也有其他协议浏览器可以进行支持,例如IE浏览器支持ftp协议。还有Chrome浏览器的设置界面chrome://等。

chromium本身就支持二次开发人员扩展自己的自定义协议,示例代码中就加入了两种自定义协议,search://和client://。

示例代码中,输入client://路径会打开一些本地资源文件,我们不介绍这个,大家可以看看。而输入search://接关键字会打开google搜索,我们着重介绍这个自定义协议,因为这里面涉及一个PunyCode/IDN的问题。

* 从search://看自定义协议

示例代码中search://后面的关键字会放到google中搜索,由于大陆访问不了google,我们使用baidu搜索。

类tests.detailed.handler.SearchSchemeHandler中38行处。

#!java

newUrl = "http://www.baidu.com/s?wd=" + newUrl;

1. 说在前面

虽然我没有把注册、拦截自定义协议的详细步骤写出来,但示例代码中体现地很完整。

首先重载org.cef.handler.CefAppHandlerAdapter.onRegisterCustomSchemes函数使用org.cef.callback.CefSchemeRegistrar.addCustomScheme注册协议名称;然后重载org.cef.handler.CefAppHandlerAdapter.onContextInitialized函数为协议注册工厂类;示例代码中从类tests.detailed.handler.AppHandler的39和57行(这个类在上一讲启用Flash中改过)

示例代码中对于工厂类、实例的管理以及对应CefBrowser/CefRequest的对应关系没有深究,但是对处理函数的线程同步等操作,包括协议头和内容分离的思想需要注意。

2. Punycode问题

我们启动调试,在地址栏输入search://java-cef,它的搜索结果完全正确;但是如果输入中文,例如search://浏览器,则会出现意想之外的效果。。。

search.gif

首先,这个问题不是因为编码(中文到unicode之类的),而是因为一种叫做智能域名(IDN)的机制。以search://浏览器为例,我们search://是协议,而浏览器域名;跟http://www.baidu.com中两个部分类似。智能域名的一个作用就有把非拉丁语系的域名转为拉丁语系域名(域名只支持ISO8859-1的ASCII编码)。因此我们的中文就被转换为xn--开头的一串英文了。

验证的方式很简单,你搜索search://浏览器/谷歌谷歌二字不会被转义,因为它不是域名了。

我们使用Java提供的java.net.IDN.toUnicode函数可以从已被转义的字符转回来,再放到百度进行搜索就是对的了。

#!java

  @Override
  public boolean processRequest(CefRequest request,
                                CefCallback callback) {
    // cut away "scheme://"
    String requestUrl = request.getURL();
    String newUrl = requestUrl.substring(scheme.length()+3);
    // cut away a trailing "/" if any
    if (newUrl.indexOf('/') == newUrl.length()-1) {
      newUrl = newUrl.substring(0, newUrl.length()-1);
    }

    try {
        URI tempUri = new URI(requestUrl);
        String asciiDomain = tempUri.getHost();
        String unicodeDomain = IDN.toUnicode(asciiDomain);
        newUrl = newUrl.replace(asciiDomain, unicodeDomain);
    } catch (URISyntaxException e) {
    }

    newUrl = "http://www.baidu.com/s?wd=" + newUrl;

    CefRequest newRequest = CefRequest.create();
    if (newRequest != null) {
      newRequest.setMethod("GET");
      newRequest.setURL(newUrl);
      newRequest.setFirstPartyForCookies(newUrl);
      browser_.loadRequest(newRequest);
    }
    return false;
  }

上文中使用try-catch围绕的代码(用到了URI类和IDN类)即是我新添加的代码。

Updated