作者归档:alex

在本地服务器上建立chromium镜像

Android 5.0发布了好长时间,一直都没有来得及研究。前两周终于将android 5.0源码下载了下来,大致看了一下代码。我所关注的并非Material Design,而是Android 5.0的web引擎。从Android 4.4开始,采用了chromium/blink内核,Android 5.0自然也不例外,只是chromium的版本升级到了37.0.2062.116。想到我的chromium webview项目,仍然采用的是34.0.1847,因此打算也将chromium版本进行一次升级。

因为可恶的GFW,下载代码及其不顺利,虽然有VPN,但是对于几十G的chromium下载量,下载速度太慢,而且git不支持断点续传。在折腾了两三个星期之后,决定在公司的服务器上建立chromium的镜像。不知道从哪个版本开始,gclient开始全面转向git。由于git的天然的分布式特点,这为完全镜像chromium源码库提供了便利。

chromium源码库是由chromium本身加上一堆第三方开源库组成,极其庞大。为此我写了一个脚本,放到了github上,有兴趣的可以访问chromium_mirror_script项目,其中chromium_git_list.txt为所有的源码库列表,来自https://chromium.googlesource.com/,随着时间的推移,这个列表也可能发生变化。

有了内部的chromium镜像后,我们可以修改.DEPS.git文件,将其中的git_url值替换为本地git地址,这样通过gclient下载,就是从本地镜像服务器上获取代码了。

我的macos编程之旅

自从购入iMac后,这台水果机一直就是我的专属机。女人天生就对电脑这种科技产品心存畏惧,老婆一看这台电脑不是熟悉的Windows系统,就摸都不想摸。直到有一天,老婆拿了一个拷贝有儿子照片的U盘,要用这台iMac浏览一下照片,因为这台iMac的屏幕够大,显示效果也好。于是我就让位让她操作一下。我指点了一下如何插入U盘,如何打开U盘里的内容,剩下的就交给老婆大人操作了。带着Windows下的操作方式,老婆双击打开了一张图片。嗯,不错,打开了第一张照片,效果相当清晰细腻,色彩鲜艳,不禁赞叹了一句:苹果电脑还是不错的。然后…下一张,怎么浏览下一张图片?按右箭头没有用,界面上也没有浏览下一张的按钮。(注:默认是系统自带的“预览”程序)

我解释,苹果电脑要这么用:首先将要照片导入iPhoto,然后通过iPhoto浏览。老婆睁大眼睛,为什么要导入,我仅仅想浏览一下照片而已。我意识到,和她解释苹果软件的设计理念、照片管理的理论是徒劳的,于是我又告诉她一个新方法:选择所有照片,然后从右键菜单选择“打开”。老婆还是瞪着我:为什么要先选择所有照片?看到照片,第一反应就是双击打开它。我哑口无言…至此,老婆对苹果电脑的一点好感荡然无存。

我有点不服气,mac下的软件那么多,总有一款适合Mac新手,于是到mac appstore上去找图形图像类软件。本来苹果电脑就是以优秀的图形处理能力见长,所以各种图像处理软件琳琅满目,但很遗憾没找到一款功能足够简单,满足我的需求的软件。我在windows下常用的“光影看图”也没出Mac版。作为一个多年的程序员,当然不满足于这样一种结果。既然找不到这样的软件,就自己动手,风衣足食,好歹自己也是写程序维生的。某位国外大神级人物曾经有一个惊人的论调:每半年就要学习一门新的语言。对此我很惭愧,已经有多年围着C++/Java打转,没有去尝试学一门新的语言了。正好以此为契机,开始掌握一门新的技能吧。

Mac OS下开发,Objective-C是当仁不让的No.1,当然今年的兵器普上出现了新面孔Swift。考虑到我在C/C++圈子摸爬滚打了这么多年,估计用Objective C会更称手一些吧。而且Objective C发展了二十多年,一直由苹果独立支撑,竟然在编程语言兵器普上排在前几位,自然有其过人之处。好吧,就选它了。学习一门新的语言,一本经典的入门书籍那是必须的,经过大牛的推荐,我选择了<<Objective-C Programming – The Big Nerd Ranch Guide>>。

下面就开始我的mac os编程之旅吧!

开始使用markdown写日志

受《像极客一样写博客》这篇文章的影响,我也打算以后在github上写日志了。在wordpress上写日志差不多两年了,一直没有找到linux下好用的客户端。现在写文章一直都是用的Windows Live Writer,用起来不错,可惜只能在Windows下使用,而我因为工作原因,大部分时间都是在Linux上写代码。自从接触了markdown后,觉得是一个不错的选择,至少写博客足够了。使用github pages,写文章就像写代码,也不用担心文章丢失,提交记录随时可查。图片我托管到七牛云存储。

欢迎光临我的新空间:mogoweb.github.io. 同时祈祷github不要被墙。这个空间还会继续保留,两边的文章也会同步。

Chromium网络加载速度研究(1)

本文主要内容翻译自Google的文档Data Compression Proxy,如果您觉得文章写的不明白,请参看原文。

对于一款浏览器而言,速度无疑是非常重要的,其中加载速度更是重中之重。UC浏览器很早就采用了云端加速技术,所以在网页加载速度方面一直很有优势。现在越来越多的浏览器,如Opera、QQ浏览器都采用了服务器端加速技术。Chrome移动版从V33开始正式支持数据压缩代理。下面就分析一下Chrome for Android所采用的数据压缩技术。

数据压缩代理

最新的Chrome for Android支持数据压缩代理,通过Google部署的代理服务器优化网站内容,可以极大的减少数据流量。有数据表明,采用这一特性可以减少web页面大小的50%。要启用这一功能,进入Chrome浏览器的菜单"设置->带宽管理->减少数据流量消耗",然后打开即可。

实现原理

最核心的优化,减少数据大小,是由Google服务器实现的。当开启了"数据压缩代理"特性时,Chrome在手机和运行于Google数据中心的服务器之间建立连接,所有非加密的HTTP请求都通过该连接中转。

Data Compression Proxy

代理服务器收到手机发起的请求后,向目标网站发起请求,对每个回应进行优化,再回给手机。内容优化是通过Google的开源库PageSpeed实现的,该库针对Chrome移动浏览器进行了特别的微调。网页的渲染、JavaScript执行,是由手机上的Chrome浏览器完成的。

  • HTTPS连接不通过数据压缩代理
  • 使用数据压缩代理不需要Google账号
  • 匿名窗口中的浏览不经过数据压缩代理

如果您对压缩代理节省的带宽表示怀疑,可以进入到浏览器设置中查看浏览器接收数据的原始大小和优化大小的对比图。如下图所示,带宽节省增长很快。

bandwidth usage

让我们近距离观察一下压缩代理进行了那些特别的优化。

SPDY

从手机到代理服务器会尽力采用SPDY, 这是一个针对Web优化的协议,得到了Chrome/Firefox和Opera的支持,也是即将到来的HTTP/2.0标准的基础。如果SPDY连接无法创建,则会建立一个普通的HTTP/1.1代理连接。

http-https

使用SPDY协议,代理服务器可以在一个TCP连接上同时复用多个请求和回应,这样做有很大的好处:它可以分摊多个请求的TCP握手开销。去掉每个请求的TCP慢速启动阶段,提高吞吐量,还能在数据流上智能的定义请求和回应优先级。事实上,研究表明仅仅使用SPDY可以减少移动网络下23%的页面加载时间,这还没有将内容优化算在内。

此外,使用数据压缩代理还有许多其它的好处:

  • DNS延迟绑定:DNS查找由代理服务器执行,而不是由手机,这样查找过程可以快很多。
  • 更少的网络活动和更快的加载速度意味着移动射频活动期更短(减少电量消耗)
内容优化

悲剧的是,大多数网站并没有对移动终端进行优化,导致内容加载低效和渲染慢。PageSpeed的经验表明,许多内容的优化可以自动完成,就如同数据压缩代理所做的。

图片转码: 平均下来,每个页面60%的传输数据是图片。为此,代理服务器特别对此进行优化,将所有的图片都转码为WebP格式。WebP比当前流行的格式,如JPEG和PNG,数据量更小。代理支持最新的WebP无损格式,并根据设备分辨率和像素密度对每个图像进行优化。综合各种方法,图像大小可以减少80%。

内容压缩: 代理智能的压缩和缩减HTML、JavaScript和CSS资源,通过移除不必要的空格、注释以及其它与页面渲染无关的元数据,再加上对所有资源启用gzip压缩,可以极大的节约带宽。

安全浏览: 代理实现了移动Chrome浏览器上的安全浏览,当您访问恶意或者钓鱼网站时会告之浏览器,浏览器会显示一个警告页面。恶意网站列表由代理持续更新。

该采用云加速技术吗?

通过前面的分析,采用数据压缩代理似乎是个不错的方案,但是从图1可以看出,所有的数据都需要通过数据压缩代理中转,这就要求:

  • 拥有强大的服务器,快速的进行数据压缩
  • 拥有强大的数据中心,否则瓶颈会存在于数据压缩代理
  • 拥有强大的分布式计算能力,浏览器终端需要能够就近连接数据压缩代理

在中国,Chrome for Android如果开启数据压缩代理功能,速度反而更慢,其原因就在于Google在中国并没有部署服务器。对于个人开发者和中小企业,一来无法部署强大的数据中心,二来也无法使用Google的服务,所以服务器端加速方案不具备可行性。

基于chromium的产品开发的源码管理策略

chromium这个开源项目的特点之一就是代码量庞大,使用了众多的第三方开源库,整个版本库差不多10G了。特点之二就是更新频繁,
去年chrome的版本还是v16,现在chrome已经更新到v32了。所以有时基于chromium开发浏览器非常痛苦,一不小心就落后别人一大截。源码管理是项目启动之初就需要考虑的,在这方面我们就走了弯路,下面就分析一下源码管理策略,至于采用何种策略就需要根据项目需求和环境而定。

Android 4.4 WebView实现分析

随着Android4.4的发布,Android WebView改成由Chromium驱动了。浏览Android4.4源代码,可以看到之前版本中的external/WebKit目录被移除掉了,取而代之的是chromium_org。也就是说chromium已经完全取代了之前的WebKit for Android。虽然chromium完全取代了以前的WebKit for Android,但Android WebView的API接口并没有变,与老的版本完全兼容。这样带来的好处是基于WebView构建的APP,无需做任何修改,就能享受chromium内核的高效与强大。下面就Android 4.4中WebView的实现进行分析。

先来看看frameworks/base/core/java/android/webkit下的文件,里面有我们熟悉的WebView、WebSettings、WebViewClient等类,其中最主要的就是WebView了,相关的类关系如下:

wps_clip_image-510

    从图中可以看出,设计者抽象出了一个WebViewProvider接口,WebView本身并不做事,所有的处理就是交给WebViewProvider。而WebViewProvider只是一个接口,由实现者决定采用哪种引擎。从Android4.1开始,就采用了这种设计架构,开始为chromium核心的WebView做准备。在Android 4.4之前的版本,由WebViewClassic实现WebViewProvider接口。在Android4.4中,则是WebViewChromium实现WebViewProvider接口。WebViewFactory也采用了相似的结构,决定了如何实例化WebViewFactoryProvider,WebViewFactoryProvider有一个关键的接口createWebView,创建具体的WebViewProvider。(注:AOSP master源码基于WebKit的实现仍然保留,但在Android4.4中已经移除,由于采用了灵活的架构,所以在两种核心之间非常容易切换)

目前的代码结构如下图所示,在ContentAPI之上,Chromium的WebView实现封装了一个新的类AwContents,该类主要基于ContentViewCore类的实现,不同的是,AwContents需要基于一个原来存在于“chrome/”目录下的模块(图中的BrowserComponents),但是AwContents不应该依赖该目录,所以,将chrome中的一些所谓的浏览器模块化是Chromium的一个方向。目前,一些模块以及从chrome中抽取出来了,参见“components/”。

wps_clip_image-31344

AwContents提供的不是WebView的API,所以,需要一层桥接部分,将AwContents桥接到WebView,这就是图中的桥接模块,该模块位于Android源代码中的frameworks/webview/chromium/java/com/android/webview/chromium/目录下,WebViewChromium和WebViewChromiumFactory类作为WebView的具体实现,依赖于Chromium项目的AwContents模块,整体模块图如下:

wps_clip_image-26071

AwContents基于Content之上,专门针对Android WebView需求而进行的一个封装,这个封装只针对Android平台。

WebView同样也是基于ContentAPI(web contents, ContentViewCore等),在这点上来说,它同Content Shell和Chromium浏览器没有大的不同,区别在于它们对很多Delegate类的实现不同,这是ContentAPI用于让使用者参与内部逻辑和实现的过程。具体来说,它主要有以下两个方面的不同:

1.   渲染机制

因为WebView提供的是一个View控件,那么View控件的容器可能接受储存在CPU中的结构(如bitmap),也可能是储存在GPU内存中的结构(如surface),所以它需要提供两种不同的输出结果。

Chromium引入了一种新的合成器UberCompositor++,该合成器支持输出到GPU和CPU内存两种方式。对于Compositor的结果输出到给定View的GPU内存这种方式,关键点在于实现AwContents.InternalAccessDelegate接口的requestDrawGL方法。与requestDrawGL实现有关的代码不多,只涉及到DrawGLFunctor.java和GraphicsUtils.java,以及与之关联的native实现draw_gl_functor.cpp, graphic_buffer_impl.cpp和graphics_utils.cpp,naitve代码位于frameworks/webview/chromium/plat_support目录下。这部分代码不多,但牵涉到了Android源码,并不是只使用了Android SDK和NDK的API。

2.   进程

目前WebView只支持单进程方式,未来不排除支持多进程方式。单进程意味了没有办法使用Android的isolated UID机制,因此,某种程度上来讲,安全性降低了,而且页面的渲染崩溃会导致使用WebView的应用程序崩溃。

Android 4.4中的chromium

Android 4.4代码树中的chromium源码位于external/chromium_org,而不是external/chromium, external/chromium是一个老版本的chromium,主要提供chromium net网络栈。external/chromium_org下的chromium版本为r227252,对应的Chromium发行版本为30.0.1599.101, Blink版本为r159105。

AOSP源码中的默认浏览器基于chromium核心,但Browser代码和之前的版本并没有差别,这也看出WebView API的兼容性相当好。而google官方则宣布以后android系统中不再提供浏览器,只有Chrome for Android。

Android4.4中的chromium移植分析

从Android4.4的chromium_org git库log可以看到,从chromium源码获取分支后,只cherry-pick了两个提交,而且cherry-pick的代码也是来自chromium项目,信息如下:

commit 3311b27cb6fdbe7af035a7d769944a17328c6227

Author: Ben Murdoch <[email protected]>

Date:   Mon Oct 21 12:33:52 2013 +0100

    Cherry pick  [Android] MediaPlayerBridge should pass Bridge to getAllowedOperations

    Cherry pick of https://codereview.chromium.org/32213003/

    Bug: 11311417

    Original description:

    [Android] MediaPlayerBridge should pass Bridge to getAllowedOperations

    Right now the Java side is expecting a MediaPlayer object to be passed

    to the MediaPlayerBridge.getAllowedOperations function, but native

    is actually passing it a MediaPlayerBridge. Update the code to be

    consistent - Java now expects the MediaPlayerBrdige and uses it to

    extract a MediaPlayer.

    Change-Id: I5ac618c833c6c3ff015d43405a9a4d507fce9ba5

commit 41a8683eef4139670e32f284a160dc3702d9a5c2

Author: Ben Murdoch <[email protected]>

Date:   Mon Oct 21 11:04:45 2013 +0100

    Cherry pick android: fix base::Time::FromLocalExploded() crash.

    Cherry pick of https://codereview.chromium.org/27472003/

    Bug: 11313033

    Original description:

    android: fix base::Time::FromLocalExploded() crash.

…skipping…

    Record Chromium merge at DEPS revision 30.0.1599.101

    This commit was generated by merge_to_master.py.

    Change-Id: Ie9e9e034f3e5d9a3fe3edf87595c7be467d3136b

结合前面的分析,可以得出如下结论:

1)AwContents都是基于Android SDK/NDK开发,并没有使用Android Source里面的未公开API和库。

2)Android 4.4中使用的chromium,直接取自chromium开源项目,虽然它使用了自己的git库:https://android.googlesource.com/platform/external/chromium_org

3)Android4.4中的chromium并没有针对android平台做一些特殊修改,和chromium开源项目的代码一致

4)与Android source密切相关的代码位于ChromiumWebView。如前面所述,在实现reqeustDrawGL时,使用到了Frameworks 未公开的API,native部分代码也用到了Android source中的代码。所涉及的类和Android Source如下:

1.HardwareCanvas

2.ViewRootImpl

3.DrawGlInfo.h

4.GraphicBuffer.h

5.GraphicsJNI.h

5)整个chromium移植代码中,与android source密切相关的代码极少,绝大部分都是基于SDK和NDK而开发。

调试ContentShell启动过程的方法

本文探讨的如何调试ContentShell for Android的启动过程,主要是C++部分代码的流程,这对于理解ContentShell的启动流程、render进程是如何创建并启动的有很大的帮助。

理解Contenthell启动流程的最好方法是使用调试器逐步跟踪,不过chromium在很多地方采用了异步方式,单步跟踪相当困难,比较好的方法是在关键代码处打上断点,查看调用栈。由于ContentShell程序一启动,就会执行启动流程,启动主进程和render进程,并加载网页,这时再用gdb attach到进程,往往我们希望跟踪的代码已经执行过去了。一种比较土的方法就是在程序入口处加上sleep语句,在程序暂停时,执行gdb attach到进程,然后在关键代码处设上断点。这种方法有点不好的地方就是sleep的时间不好定,定短了,gdb还没有attach上去,程序已经执行过了。设长了,每次等的时间就长了。

其实chromium开发者已经考虑到这种需求,所以加入了一个命令行参数:wait-for-java-debugger。查看代码,可以发现在ContentShellActivity.java中就有一个方法waitForDebuggerIfNeeded(),其作用就是等待java调试器attach之后才继续往下执行。所以本文介绍的方法就是jdb/gdb组合来调试ContentShell启动过程。

  1. 修改content/shell/android/shell_apk/AndroidManifest.xml,在application节点加入android:debuggable=”true”,然后编译ContentShell,安装到连接的android设备。
  2. 从这里下载jdb_content_shell脚本,放到build/android目录下。
  3. 执行如下命令增加命令行参数:
    adb_content_shell_command_line --wait-for-java-debugger
  4. 启动android设备上的ContentShell。应该可以看到界面只是一个空白界面,程序没有接着往下执行。
  5. 执行adb_gdb_content_shell命令启动gdb调试,设置断点。
  6. 执行jdb_content_shell命令,可以观察到程序在继续往后执行。

[译]Android WebView

原文地址:https://mogoweb.net/38-2/#wiki/android-webview

1. 架构&组件

  • AOSP中的Android WebView将基于AwContents,其位于Content API和Content View之上。
  • 与Content API不同,它还依赖于从最初的chrome browser分离出来的组件,组件位于components/下,包含auto_login_parser,navigation_interception,visitedlink_browser,visitedlink_renderer和web_contents_delegate_android,这些都是用于页面。
  • 无渲染进程(进程内渲染),无沙盒机制(sandbox机制)
  • 图形架构和Content Shell有很大的不同,且正在开发中,将支持软件和硬件输出。

2. API

  • API类似于EFL WebView,不会暴露内部的browser/renderer/gpu进程。
  • 能够加载URL,接收加载过程中的事件和回调。
  • 可通过addJavaScriptInterface扩展JavaScript API,允许JavaScript调用Java代码,反之这不支持。
  • 提供了若干API接收页面状态改变通知和页面回调。

3. 消息循环和事件处理

  • 整合利用Android消息处理
  • native代码设置定时器到Java侧的消息循环

4. 图形

  • 渲染使用chromium合成器,开启了硬件加速
  • 合成器输出支持软件和硬件Surface
  • 硬件加速的架构和content shell不同,和ChromeOS类似。现在还处在早期开发阶段,使用了UberCompositor,支持软件输出

5. WebAPI扩展

  • API addJavaScriptInterface可用于JavaScript API扩展,但支持JavaScript调用Java代码。
  • 相反方向的调用(Java -> JavaScript)不支持
  • addJavaScriptInterface通过NPObject机制(之前用于NPAPI插件)支持进程外访问,虽然在WebView中不再需要。
  • 来自Java侧的事件通知貌似还不支持

6. API细节和涵盖范围

  • WebViewClient用于页面状态改变通知
  • WebChromeClient包含参与渲染逻辑的回调
  • 原生代码/JS交互
  • 页面加载
  • 导航&历史记录
  • 文字搜索
  • 事件处理
  • 轨迹球、触摸、键盘
  • 不支持鼠标事件
  • 设置:编码
  • 页面信息:标题、favicon
  • HTTPS/SSL
  • 页面加载状态、缩放、重定向、URL重载,等等

7. 源码

  • src/android_webview
    • android_webview_java.jar, libwebviewchromium.so和webviewchromium.pak是本模块的编译目标.
  • Google的工程师正在Android开源项目里开发基于Chrome核心的新WebView.
    • 官方 站点:
    • https://android.googlesource.com/platform/frameworks/base.git/+/master/core/java/android/webkit/

    • 镜像:
    • https://mogoweb.net/15-2/

  • 第三方开发者也在基于android_webview模块开发ChromeView
  • Third party developers work in progress to implement ChromeView based on android_webview module.
    • ChromeView有着和Android Webview一样的API.
    • https://github.com/pwnall/chromeview-src

浏览器插件扩展研究

首先说明一下,本文探讨的并非NPAPI和PPAPI这种插件技术,而是一种扩展浏览器功能的方法,并且范围仅限于Android平台。插件技术在软件系统中得到了广泛的应用,程序员熟悉的Eclipse就是因为有着极好的插件扩展机制,几乎可以作为所有的程序设计语言的IDE。一般人熟悉的媒体播放器,也使用到了插件技术,以支持不同的编码和格式。甚至photoshop中的滤镜,也算的上是插件。

百度百科上对插件的定义为:

插件(Plug-in,又称addin、add-in、addon或add-on,又译外挂)是一种遵循一定规范的应用程序接口编写出来的程序。

插件和应用程序的最大区别在于插件必须依赖于应用程序才能发挥自身功能,仅靠插件是无法正常运行的。相反地,应用程序并不需要依赖插件就可以运行。一般而言,插件具有以下特征:

  • 独立于应用程序部署,可以被应用程序动态加载。比如Eclipse的插件,可以直接放到plugins目录,就可以被Eclipse识别并动态加载。视频解码器可以单独安装,媒体播放器可以根据视频格式自动加载。
  • 具有自描述元数据,比如插件名称、icon、版本、描述等,能够以统一的方式被应用程序检索到。
  • 遵循应用程序的应用程序接口,插件一般只针对特定的应用程序而开发。


要实现插件机制,需要解决如下问题:

  • 自注册。要求插件能够独立于应用程序记录某些信息,让应用程序能够找到插件,并且加载。比如Windows下可以通过注册表。
  • 动态加载。要求应用程序能够动态的加载插件,而不是静态绑定。比如Java应用程序可以通过反射机制动态。
  • 插件管理。应用程序一般要求能够安装、卸载、禁用插件,显示插件信息。

Android插件系统设计

Android是采用现代设计思想而设计的移动操作系统,其天然的组件架构为设计插件系统提供了极大的便利。我将按照如下思路设计插件:

  • 插件都设计为Service。通过AIDL定义插件和应用程序之间的应用程序接口,插件实现应用程序定义的接口,应用程序通过Android IPC机制调用接口。插件和应用程序位于不同的进程控件,避免由于插件崩溃而影响到应用程序的运行。
  • 插件可以有UI,也可以是纯实现功能的后台服务。插件编译为单独的apk包,可独立于应用程序部署/卸载。
  • 应用程序通过Android Package Manager发现插件。为了区分应用程序所属的插件,可以定义一个特殊的Intent(比如aexp.intent.action.PICK_PLUGIN),并在插件的AndroidManifest.xml中定义Intent filter。
  • 利用Android的元数据(meta-data)定义插件信息,比如名称、描述、版本等等。
  • 应用程序监听 ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED和ACTION_PACKAGE_REPLACED事件来动态的更新插件列表。
  • 利用Android的Receiver机制,实现应用程序和插件之间的事件交互。

插件的例子请看参考文档,无法翻墙的同学看这里。

浏览器插件框架设计

Chrome浏览器的extensions技术非常强大,让广大的开发人员使用html/css/js就能扩充Chrome浏览器的功能。Google甚至还打造了chrome web store,上面已经有成千上万个插件,有很多新奇和实用的插件,不得不佩服程序员的创造力。不过Chrome for android尚不支持extensions,在开发Browser,曾考虑过enable chromium的extensions开关,后来有些问题实在无法解决,只能放弃。

为了让插件能够更好的扩充浏览器功能,浏览器也需要开放一些接口和事件给插件,比如:

  • tabs操作,包括新建tab、关闭tab,在tab上加载指定的URL
  • bookmark
  • history

如果想要设计一份完备的浏览器编程API,建议参考Chrome extensions文档,虽然是以js提供的,但非常完备,具有极高的参考价值。

参考文档

1. MY LIFE WITH ANDROID :-) Plugins

2. MY LIFE WITH ANDROID :-) plugins with user interface

浏览器中实现阅读模式

这里对阅读模式的定义是:去除网页上与主题内容无关的图片、导航、广告等等,给用户提供一个清爽的网页。在PC浏览器中,chrome/firefox都有这样的扩展/插件。我个人用的最多的是chrome上的印象笔记.悦读插件,和evernote整合,非常好用。从IOS 6开始,safari浏览器也提供了阅读器功能,用户界面如下:

safari_readmode_1 safari_readmode_2

当网页中主题内容比较明确、且文字内容较多时,地址栏上就会出现“阅读器”按钮,点击“阅读器”按钮,就会提供给用户一个清爽的网页。更绝的是,如果网页内容包含多个分页,“阅读器”模式下还可以自动整合,无需费力的点击下一页了,相当贴心,如下图所示:

safari_readmode_3

如果要在浏览器中实现这一个功能,可以有三种方法:

1、服务器端对网页进行处理

其思路和百度转码相同,就是在服务器端对网页进行处理,UC浏览器能够做到省流量,也是采用了这一技术。不过百度转码主要目的是为网页瘦身,所以处理出来的效果惨不忍睹。国外有一家公司,提供了免费的readability API,其口号是:

Transforms any article … into a calm, humane, readable experience

详情请访问https://www.readability.com。

使用国外免费的API,实现简单,无需架设服务器,但缺点也是明显的,速度不够理想,容易被墙,说不定哪天就不能使用了。

2、修改webkit,对DOM树进行处理

webkit中,有一个步骤就是解析html,形成DOM树,有了这个DOM树就好办了,有什么节点、节点属性、节点样式都一目了然。所以接下来要做的事情就是筛选主题内容,过滤无关紧要的节点。至于采用什么算法进行筛选,还没有找到现成的C++库,网上有一个C#库:NReadability 可供参考,如果对javascript比较熟的话,直接参考readability提供的js脚本也可以。

通过修改webkit实现阅读模式,速度快,更容易和浏览器界面进行整合(比如,safari中,只有文字较多的网页才会出现阅读器),也比较适合对js前端开发不熟悉的C++程序员,缺点是webkit比较复杂,需要对webkit了解深入,而且之后的维护也是个麻烦事情。

3、注入js,对DOM树进行处理

javascript具有操作DOM的能力,也可以应用样式表来修改节点的样式,所以通过注入JS脚本来实现阅读模式是一个不错的选择。chrome/firefox中扩展插件,或者一种称为bookmarklet的小片代码就是这样实现的。

回到chromium for android,ContentView提供了evaluateJavaScript方法为当前ContentView注入javascript,所以实现起来非常容易。

TODO

在safari中,实现了多页拼接的处理。Readability在一篇文章中也声明实现了多页合并的处理,不过我对javascript研究的不够多,还没有弄清是如何实现的。如果有精通web前端开发的同学一起探讨,非常欢迎。