精华内容
下载资源
问答
  • 浏览器线程与js引擎的单线程

    千次阅读 2018-04-10 14:53:43
    1. 浏览器的线程与进程 (1) 进程与线程 进程 学术上说,进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。我们这里将进程...

    1. 浏览器的线程与进程

    (1) 进程与线程

    进程

    学术上说,进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。我们这里将进程比喻为工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。

    线程

    在早期的操作系统中并没有线程的概念,进程是能拥有资源和独立运行的最小单位,也是程序执行的最小单位。任务调度采用的是时间片轮转的抢占式调度方式,而进程是任务调度的最小单位,每个进程有各自独立的一块内存,使得各个进程之间内存地址相互隔离。后来,随着计算机的发展,对CPU的要求越来越高,进程之间的切换开销较大,已经无法满足越来越复杂的程序的要求了。于是就发明了线程,线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元。这里把线程比喻一个车间的工人,即一个车间可以允许由多个工人协同完成一个任务。

    进程和线程的区别和关系

    进程是操作系统分配资源的最小单位,线程是程序执行的最小单位。
    一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
    进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号)。
    调度和切换:线程上下文切换比进程上下文切换要快得多。

    多进程和多线程

    多进程:多进程指的是在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态。多进程带来的好处是明显的,比如你可以听歌的同时,打开编辑器敲代码,编辑器和听歌软件的进程之间丝毫不会相互干扰。
    多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

    (2) 浏览器的进程与线程

    首先打开浏览器,然后打开shift + Esc打开chrome的任务管理器

    此时只有三个进程:
    浏览器进程(Browser进程): 浏览器的主进程(负责协调、主控),只有一个。作用有
        负责浏览器界面显示,与用户交互。如前进,后退等
        负责各个页面的管理,创建和销毁其他进程
        将Renderer进程得到的内存中的Bitmap,绘制到用户界面上
        网络资源的管理,下载等
    GPU进程:用于3D绘制等(可禁止掉,而且这个与页面渲染过程的Composite Layers 有关系,后面性能优化相关文章学习到再来研究一下GPU)
    浏览器渲染进程(Renderer进程,内部是多线程的)每一个标签页的打开都会创建一个浏览器渲染进程(浏览器内核)。默认每个Tab页面一个进程,互不影响。主要作用为页面渲染,脚本执行,事件处理等

    2. 浏览器为什么要多进程?

    在浏览器刚被设计出来的时候,那时的网页非常的简单,每个网页的资源占有率是非常低的,因此一个进程处理多个网页时可行的。然后在今天,大量网页变得日益复杂。把所有网页都放进一个进程的浏览器面临在健壮性,响应速度,安全性方面的挑战。因为如果浏览器中的一个tab网页崩溃的话,将会导致其他被打开的网页应用。另外相对于线程,进程之间是不共享资源和地址空间的,所以不会存在太多的安全问题,而由于多个线程共享着相同的地址空间和资源,所以会存在线程之间有可能会恶意修改或者获取非授权数据等复杂的安全问题。

    3. Browser进程与Render进程,GPU进程之间的如何合作?

    在How browser work为文章中看到过这样一幅图:




    这里的Browser engine我想对应的就是Browser进程,Rendering engine对应的就是Render进程。
    针对与用户打开一个标签页,可以看到首先控制的还是Browser进程。然后我们再看一下chromium多线程模型:



    基本工作方式如下
    Browser进程收到用户的请求,首先由UI线程处理,而且将相应的任务转给IO线程,他随机将该任务传递给Render进程;
    Render进程的IO线程经过简单解释后交给渲染线程,渲染线程接收请求,加载网页并渲染网页,这其中可能需要Browser进程获取资源和需要GPU进程来帮助渲染,最后Render进程将结果由IO线程传递给Browser进程;
    Browser进程接收到结果并将结果绘制出来;

    4. 览器渲染Render进程(浏览器内核)有哪些线程?

    GUI渲染线程

    负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
    当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
    注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。

    JS引擎线程

    也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)
    JS引擎线程负责解析Javascript脚本,运行代码。
    JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
    同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。

    事件触发线程

    归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
    当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
    当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
    注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)

    定时触发器线程

    传说中的setInterval与setTimeout所在线程
    浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
    因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
    注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。

    异步http请求线程

    在XMLHttpRequest在连接后是通过浏览器新开一个线程请求
    将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。

    5. JS引擎线程的相关介绍

    (1) 为什么JavaScript是单线程?

    首先在明确一个概念,JS引擎线程生存在Render进程(浏览器渲染进程)。其实从前面的进程,线程之间的介绍已经明白,线程之间资源共享,相互影响。假设javascript的运行存在两个线程,彼此操作了同一个资源,这样会造成同步问题,修改到底以谁为标准。
    所以,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

    (2) WebWorker会造成js多线程吗?

    首先举一个例子,可以查看web worker
    //index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>webWorker</title>
    </head>
    <body>
      <div>
        <a href='./webworker/index.html'>web worker</a>
      </div>
    </body>
    </html>
    //worker.js
    onmessage = function (count) {
      console.log("web worker start")
    
        var i = 0;
    
        while(i < count.data) {
          i ++;
        }
    
        postMessage("web worker finish");
    };
    然后打开performence面板查看:

    图中蓝色框是浏览器下载完wokder.js文件。紧接着我们我们可以看到红色框DedicatedWorker Thread,在workder thread的时间内去执行worker.js,然后将计算好的结果返回给main thread,最后执行到蓝色框中去。

    这样看起来好像是创建了一个新的线程。如果我没有使用web worker的情况下,DedicatedWorker Thread根本就不存在。
    这好像看起来并没有什么说服性。我们再看一个例子,在本地查看这个例子的时候,具体的数字是我调的,是想达到下面图示的效果。

    //index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>webWorker</title>
    </head>
    <body>
        <script>
            var worker = new Worker("worker.js");
            worker.postMessage(123456);
    
    
            worker.onmessage = function (e) {
                console.log(e.data)
            };
    
            setTimeout(function() {
                var i = 0;
    
                while(i < 9234156) {
                    i ++;
                }
            }, 180);
        </script>
    </body>
    </html>
    //worker.js
    onmessage = function (count) {
      console.log("web worker start")
    
        var i = 0;
    
        while(i < count.data) {
          i ++;
        }
    
        postMessage("web worker finish");
    };
    
    

    如图所示,setTimeout的代码执行的时候,worker.js的代码也在执行。这里我得到的结果是webworker可以在js引擎执行代码的时候去执行另外的代码。

    这里我们需要在明确开始的问题了,是造成js执行的多线程还是js引擎的多线程。这里是两个概念。上面的图示中main的栏目中是浏览器渲染Render进程(本人猜测),因为在这个过程中我们可以看到js代码的执行,也有GUI渲染线程进行html代码的解析。现在我看到的是DedicatedWorker Thread是与览器渲染Render进程同一级的(当然这些都是performence展现出来给我们看的)。

    我在MDN上看到一句话:Worker接口会生成真正的操作系统级别的线程。所以这里的webworker不是一个新的js引擎线程。而是操作系统级别的线程。线程的执行不会影响到原有的js引擎的执行,也不会影响到浏览器渲染Render进程。至于其内部实现,本人就望尘莫及了。但是,人家webworker确实实现了js代码执行的多线程(当然这些都是本人的基于看到的结果猜测的,没有找到实际的论证资料,如果有知道的可以告知,谢谢了)。

    所以我目前得到的结论是: webworker是可以造成js代码的多线程执行,但不是js引擎多线程的执行。webwoker的生命周期是由js引擎线程控制的,因为webweoker提供了一系列的api供我们操作。

    然后我们再说一下webweoker中的一些不能操作的内容:也是出于安全考虑,如果不太小心,那么并发(concurrency)会对你的代码产生有趣的影响。然而,对于 web worker 来说,与其他线程的通信点会被很小心的控制,这意味着你很难引起并发问题。所以webworker也自己做了限制(下面的内容是在网上找到的,因为我没有这么使用过webworker):

    1、不能访问DOM和BOM对象的,Location和navigator的只读访问,并且navigator封装成了WorkerNavigator对象,更改部分属性。无法读取本地文件系统

    2、子线程和父级线程的通讯是通过值拷贝,子线程对通信内容的修改,不会影响到主线程。在通讯过程中值过大也会影响到性能(解决这个问题可以用transferable objects)

    3、并非真的多线程,多线程是因为浏览器的功能

    4、兼容性

    5 因为线程是通过importScripts引入外部的js,并且直接执行,其实是不安全的,很容易被外部注入一些恶意代码

    6、条数限制,大多浏览器能创建webworker线程的条数是有限制的,虽然可以手动去拓展,但是如果不设置的话,基本上都在20条以内,每条线程大概5M左右,需要手动关掉一些不用的线程才能够创建新的线程(相关解决方案)

    7、js存在真的线程的东西,比如SharedArrayBuffer

    (3) js代码的执行(Event Loop)与其他线程之间的合作

    JavaScript 引擎并不是独立运行的,它运行在宿主环境中,对多数开发者来说通常就是Web 浏览器。提供了一种机制来处理程序中多个块(这里的块可以理解成多个回掉函数)的执行,且执行每块时调用JavaScript 引擎,这种机制被称为事件循环。换句话说,JavaScript 引擎本身并没有时间的概念,只是一个按需执行JavaScript 任意代码片段的环境。“事件”(JavaScript 代码执行)调度总是由包含它的环境进行。这个调度是由事件触发线程调度的。

    举例来说,如果你的JavaScript 程序发出一个Ajax 请求,从服务器获取一些数据,那你就在一个函数(通常称为回调函数)中设置好响应代码,然后JavaScript 引擎会通知宿主环境(事件触发线程):“嘿,现在我要暂停执行,你一旦完成网络请求,拿到了数据,就请调用这个函数。”然后浏览器就会设置侦听来自网络的响应,拿到要给你的数据之后,就会把回调函数插入到事件循环,以此实现对这个回调的调度执行。

    关于具体的事件循环的内容可以访问JavaScript之异步 - 基本概念

    请看下图对于一个页面的请求以及js的执行过程中,上面的进程/线程之间的合作。

    由于图片比较大,详情可以访问(https://www.processon.com/view/link/593fc61fe4b0848d3ecf1d56)。

    6. Promise的出现

    其实关于Promise的内容我之前也有看过,具体内容可以查看文章现在这里我只是将Promise基于任务队列的内容用代码的形式展示出来。
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>webWorker</title>
    </head>
    <body>
        <script>
          setTimeout(function() {
            console.log('setTimeout run');
          }, 0);
    
            new Promise(function(resolve, reject) {
            resolve();
            })
            .then(function(){  
            console.log('promise run');
        } );  
        </script>
    </body>
    </html>
    这里的输出结果相信大家猜测的出来:
    promise run
    setTimeout run

    这里的原因不在多说,因为首先js引擎要先执行主线程js的代码(会先执行完,因为一个js的加载,从上往下会执行完成之后,js引擎才会有时间去从事件循环队列中拿出代码块执行),至于setTimeout,间隔时间尽管为0ms,其实真正执行的时候是4ms。而且回掉函数是放在事件循环队列里的。那么Promise呢?好比我们现在执行的是js主线程,执行完成之后,js引擎不会立即去事件循环队列里取代码块执行,而是说当前主线程还有一点事情没有做完,那就是promise,在之前的文章中也谈过。二者事件的粒度不同,promsie是事件循环队列之上的任务队列。


    参考:
    https://blog.csdn.net/Steward2011/article/details/51319298
    https://segmentfault.com/a/1190000012925872
    http://www.imweb.io/topic/58e3bfa845e5c13468f567d5
    http://www.ruanyifeng.com/blog/2014/10/event-loop.html#comment-text
    https://segmentfault.com/a/1190000009313491

    展开全文
  • 浏览器劫持

    千次阅读 2009-03-05 15:09:00
    为什么说“浏览器劫持”可以说是合法的呢?因为大部分浏览器劫持的发起者,都是通过一种被称为“BHO”(Browser Helper Object,浏览器辅助对象)的技术手段植入系统的。BHO是微软早在1999年推出的作为浏览器对第三...

    为什么说“浏览器劫持”可以说是合法的呢?因为大部分浏览器劫持的发起者,都是通过一种被称为“BHO”(Browser Helper Object,浏览器辅助对象)的技术手段植入系统的。

    BHO是微软早在1999年推出的作为浏览器对第三方程序员开放交互接口的业界标准,它是一种可以让程序员使用简单代码进入浏览器领域的“交互接口”(INTERACTIVED Interface)。通过BHO接口,第三方程序员可以自己编写代码获取浏览器的一些行为(Action)和事件通知(Event),如“后退”、“前进”、“当前页面”等,甚至可以获取浏览器的各个组件信息,像菜单、工具栏、坐标等。由于BHO的交互特性,程序员还可以使用代码去控制浏览器的行为,比如常见的修改替换浏览器工具栏、在浏览器界面上添加自己的程序按钮等操作,而这些操作都被视为“合法”的,这就是一切罪恶根源的开始。

    BHO的出现帮助程序员更好的打造个性化浏览器或者为自己的程序实现了方便简洁的交互功能,可以说,如果没有BHO接口的诞生,我们今天就不能用一些工具实现个性化IE的功能了。从某一方面来看,BHO的确是各种缤纷网络互动功能的幕后功臣,但是一切事物都是有两面性的,这个恒古不变的真理同样对BHO有效,于是就有了今天让安全界头痛的“浏览器劫持”的攻击手段诞生。

    看看前面我提到的BHO接口特性,你想到了什么?BHO可以获知和实现浏览器的大部分事件和功能,也就是说,它可以利用少量的代码控制浏览器行为。程序员可以设计出一个BHO按钮以实现用户点击时通知浏览器跳转到某个页面完成交互功能,当然就可以进一步写出控制浏览器跳转到他想让用户去的页面,这就是最初的“浏览器劫持”的成因:BHO劫持。

    在描述BHO劫持之前,我们先要对BHO接口的启动做个简单介绍:符合BHO接口标准的程序代码被写为DLL动态链接库形式在注册表里注册为COM对象,还要在BHO接口的注册表入口处进行组件注册,以后每次IE启动时都会通过这里描述的注册信息调用加载这个DLL文件,而这个DLL文件就因此成为IE的一个模块(BHO组件),与IE共享一个运行周期,直到IE被关闭。

    IE启动时,会加载任何BHO组件,这些组件直接进入IE领域,而IE则成为它们的父进程和载体,从此IE的每一个事件都会通过IUnknown接口传递到BHO用以提供交互的IObjectWithSite接口里,这是BHO实现与IE交互的入口函数。

    BHO接收到IE接口传递来的参数后开始判断IE正在做什么,理论上BHO可以获取IE的大部分事件,然后根据程序员编写的代码,BHO持有对特定事件做出反应的决定权,例如一个可以实现“中文网址”的BHO,就是通过GetSite方法获取到IE当前打开的站点URL(或通过IURLSearchHook接口来获知),如果BHO发现获取到的URL和内置的判断条件匹配,该BHO就会启用SetSite方法强制IE跳转到程序员设定的页面去,这个过程就是利用about:blank篡改主页的“浏览器劫持”方法之一,它的实现原理其实很简单,程序员编写一个恶意BHO组件,当它获取到IE窗口的当前站点为“about:blank”时就强制IE内部跳转到指定的广告页面,于是闹出了不久之前沸沸扬扬的“IE空白页劫持事件”。

    了解了这种类似恶作剧的作案手段,要解决它就容易了,只要找到并删除这个隐藏在系统里的BHO程序即可。

    除了这类“广告软件”性质的BHO,还有一种利用IURLSearchHook接口实现的另一类更隐蔽的BHO,这种BHO从某些方面来说大概不算BHO,因为它并不是响应IUnknown,而是等待IE创建IURLSearchHook来启动。IURLSearchHook被浏览器用来转换一个未知的URL协议地址,当浏览器企图去打开一个未知协议的URL地址时,浏览器首先尝试从这个地址得到当前的协议,如果不成功,浏览器将寻找系统里所有注册为“URL Search Hook”(资源搜索钩子,USH)的对象并把这个IE不能理解的地址发送过去,如果某个USH对象“认识”这个地址,它就返回一个特定的标识告诉IE它知道怎么打开这个地址,然后IE就根据约定的方法调用它,最终打开这个地址。其实USH对象并不陌生,我们一些偷懒的用户就经常为了省事而不输入“http://”,但是IE最终还是能认出并打开某个地址,就是USH的功劳,但是这一点又被恶意程序员拿来磨刀了,通过创建自己的USH对象,恶意程序员能够命令IE在找不到一些网站时自动跳转到事先设置的站点里,如果这个站点带毒或者挂马,用户就完了。

    这类BHO的解决方法和前面一样,只是它比较隐蔽,除非用户经常偷懒,否则可能直到系统崩溃也不会知道自己已经感染了这种东西。也许你会说,只要用户的输入永远不会让IE无法识别,这种渗透不就白费了?但是事实不容乐观,我们无法得知BHO作者还会不会通过其他方法拦截IE,说不定每隔一段时间就让IE弹出一个广告呢?

    上面说了这么多BHO和IE合作搞破坏的事例,可能会给读者造成一种“BHO必须在IE传递数据后才能行动”的误解,然而事实并非如此,浏览器自身也是一个标准的可执行程序,而BHO只是借用这个程序进程启动的DLL,它并非API那种要用的时候就让你过来忙活,忙活完了就一脚踹开的奴隶形态DLL,前面说过了,BHO是一种在浏览器加载时一同启动的例程,它相当于一种自身运行逻辑不太明确的子进程(里面都是对IE事件的响应和操作代码),这个特性就造成了BHO DLL和API DLL本质的区别,BHO并不需要所有事件都必须依赖这个大家伙,它可以有自己决定的权利,只要适当的修改,就能用BHO实现类似DLL木马的功能,当然,这并不是说我们就能在IE眼皮下公然的肆无忌弹干坏事的,由于BHO自身是作为IE子进程启动的,它就必须受到一些限制,例如程序员不能在里面自己创建网络连接,这样会导致IE报错崩溃并供出你写的DLL,害怕BHO成为另一种后门的用户可以松口气了,要在BHO里实现Winsock大概只能在IE休息的时候才可以,但是会有哪个用户开着个开空IE什么事情都不做呢?

    但这并不是说BHO就一定能无害了,虽然用它不能做到远程控制,但是别忘记,BHO能看到IE的所有东西,也就能任意的访问用户文件和注册表,在这个条件成立的前提下,入侵者可以编写代码查找用户隐私,然后在适当时候通过SetSite提交出去——谁叫现在webmail这么流行呢?这就是为什么许多厂商发布诸如“中文网址”、“网络搜索”、“IE定制”、“IE监视”这些功能的BHO的同时都保证“不搜集用户隐私”的原因,只要你想要,BHO就能得到一切。

    有些人也许会想,既然BHO是微软浏览器的权利,那我不用IE了,我用Opera、Firefox不行?对于这点固然无可厚非,但是你用不用Windows?用不用共享软件?如果你用Windows,那么,你仍然可能处于被BHO接触到的世界,因为Windows本身就是和IE紧密结合的,这就把“IE进程”的范围给扩大了,细心的用户大概会发现,IE里能直接访问“我的电脑”,“我的电脑”窗口也能迅速变成IE,因为它们实质都是依赖于IE内核的,正因为这个原因,BHO可以在你打开一个文件夹时跟着偷偷启动。同时,现在的网络正处于一种“共享软件捆绑战略”大肆实施的时代,你再小心也不能避免某些共享软件固定捆绑了BHO的行为,安装后你才会发现文件夹上又多了个什么“助手”、“搜索”了。要想彻底逃开BHO的围困,大概只能放弃使用Windows了。

    Hook,你钩住浏览器了

    正如《侏》里的这句话一样,入侵者也在不断寻找他们的新出路,虽然上面我说了这么多BHO的负面事例,但是真正的危机并不是只有BHO的,在一些使用BHO行不通的场合里,入侵者开始投掷他们的钩子。

    什么是钩子?让我们先看看它的官方定义:

     

    钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。

    钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。

    可能上面的官方定义对一部分读者理解有点困难,其实,钩子就像是一切程序的“先知”,一个实现了钩子的程序自身虽然也是普通程序,但是它总能在别的程序得到数据之前就已经知道了一切,这是为什么呢?对Windows系统有一定了解的读者应该知道,Windows系统是一个通过“信息处理机制”运作的系统,在这个系统里传递的数据都是通过“消息”(Message)的形式发送的,各个消息都遵循了官方的约定,否则就不能让系统产生回应。而且这个传递步骤是颠倒的,例如我们关闭了某个程序,我们可能会认为是程序自己关闭后通知系统的,其实不然,当用户点击关闭按钮的时候,Windows就会把一个叫做WM_CLOSE的消息传递给这个程序,程序接收到消息后就执行卸载自身例程的操作。理解了这点,就能知道钩子的原理了,所谓钩子程序,就是利用了系统提供的Hook API,让自己比每一个程序都提前接收到系统消息,然后做出处理,如果一个钩子拦截了系统给某个程序的WM_CLOSE消息,那么这个程序就会因为接收不到关闭消息而无法关闭自身。除了消息以外,钩子还可以拦截API,像我们都熟悉的屏幕翻译软件就是Hook了一些文本输出函数如TextOutA而达到了目的。

    技术让编程人员可以轻松获取其他程序的一些有用数据或传递相关数据,像现在常见的一些游戏外挂,它们就是利用Hook技术钩住了游戏窗体,然后就可以识别游戏里面的行为和模拟发送按键鼠标消息,最终实现电脑自己玩游戏的功能。把这个技术应用到浏览器上面,就成了另一种控制浏览器行为的方法。

    钩子有两种,本地钩子(Local Hook)和全局钩子(Global Hook),本地钩子只在本进程里起作用,故不属于讨论范围;全局钩子代码必须以DLL形式编写,以便在钩子生效时被其它进程所加载调用,因此我们看到的大部分Hook程序都是DLL形式的。

    其实之前提到的BHO也可以视为一种针对IE的钩子,它钩的是IE的事件,这就是IE与BHO交互的起点,但是对于再复杂一点的操作,例如判断IE下载的是GIF图片还是JPEG图片,BHO无能为力,因为它仅仅知道IE的事件为DownloadBegin和DownloadComplete,对于具体内容,IE本身是不会告诉它的,否则IE岂不是要忙死了?至少我也没见过哪个领导还需要向秘书汇报中午吃了鸡肉还是鸭肉的吧,BHO可不是IE的老婆,或者说IE没有气管炎。

    所以,为了得到IE的更多数据,程序员开始钩IE了。与BHO不同,钩子不需要被动的等待IE事件,它直接和IE形成上司对下属的关系,这次轮到IE要做什么都得经过它批准了。Hook形式的控制不需要DLL文件必须与IE的注册表入口产生组件关系,它可以是一个独立的DLL,通过Rundll32.exe或自带的Loader EXE启动,而且由于它属于Hook形式, 在钩子有效的情况下会被系统自动插入其他程序的进程中,是不是有点像DLL木马呢?

    IE钩子程序载入进程后便能获知所有的消息类型、API和内容,一旦发现某个符合要求的消息,如IE执行了某个事件,或者用户输入了特定内容,钩子的处理代码就开始工作了,它先拦截系统发送给IE的消息,然后分析消息内容,根据不同消息内容作出修改后再发给IE,就完成了一次Hook篡改过程。用著名的3721实名搜索做例子,一些人会以为它是采用了BHO或者IURLSearchHook完成中文域名的识别跳转的,其实它是用了能够第一个得到Windows消息的Hook技术,这样一来就可以避免被其他的竞争对手抢先解析域名了:3721的主程序就是一个Hook DLL,它监视IE地址栏的消息,一旦用户输入的是中文,它便在其他BHO类插件工作之前拦截了这个消息,并调用自身代码完成中文域名到英文URL的转换工作,然后返回(也可能与自己的BHO DLL配合)一个让IE跳转到英文URL的消息,完成域名的翻译任务。

    IE钩子能帮助程序员用少量代码完成更多的IE交互工作,但是一旦这个钩子被用于犯罪,其后果也是严重的,恶意程序员可以写一个拦截IE输入的键盘钩子,达到窃取密码的作用,这样无论你是用HTTP明文协议还是SecurityHTTP加密协议都不能逃避密码被盗的下场了,因为它抓的是你在IE里的输入,后面的数据传输已经不重要了。

    Winsock LSP

    全称为“Windows Socket Layered Service Provider”(分层服务提供商),这是Winsock 2.0才有的功能,它需要Winsock支持服务提供商接口(Service Provider Interface,SPI)才能实现,SPI是一种不能独立工作的技术,它依赖于系统商已经存在的基本协议提供商,如TCP/IP协议等,在这些协议上派分出的子协议即为“分层协议”,如SSL等,它们必须通过一定的接口函数调用,LSP就是这些协议的接口。

    通过LSP,我们可以比分析基本协议更简单的得到我们想要的数据内容,如直接得到系统上运行的浏览器当前正在进行传输的地址和内容,不管这个浏览器是IE,还是Opera或Firefox,因为LSP是直接从Winsock获取信息的,即使不用微软生产的汽车,至少你这辆汽车一直是在微软建造的公路上跑的吧。

    LSP用在正途上可以方便程序员们编写监视系统网络通讯情况的Sniffer,可是现在常见的LSP都被用于浏览器劫持,使用户又多了个噩梦。

    亡羊补牢,还是居安思危?

    也许大部分家庭用户都是在经历过一次入侵或中毒事件后才知道安全防范的重要性的,能亡羊补牢当然是好事,但是如果能对自己的要求提高一点,做到未雨绸缪岂不是更好?我们总是依赖于别人的技术,依赖于模式化的杀毒手段,但那些始终都是别人的东西,控制权不能掌握在自己手上,这并不是很好的事情,也许,该是暂时放弃游戏挂级、搜集明星电影,好好研读一下安全方面和系统原理书籍的时候了,否则在这个不安全的网络中,我们随时可能会迷失自己。

    可能有人会想,小金又在发感慨了。也许是的,因为清除“浏览器劫持”一般都需要手工进行,虽然现在已经有了多个检测浏览器劫持的工具如HijackThis、Browser Hijack Recover等软件面世,但是如果你抱着和以往使用杀毒工具那样“一开扫描就安枕无忧”想法的话,你会发现自己真的会迷失了,由于BHO的特殊性(别忘记,它是合法的),这些工具只会把系统的进程、BHO项目、启动项、LSP等需要有一定技术基础方能理解的东西显示给你,然后由你自己决定IE的明天,如果你不曾重视过安全技术,那么就会觉得这些工具如同另一种折磨你的病毒了。

    展开全文
  • android 浏览器

    万次阅读 2012-06-15 13:45:44
    Android 4.0 Browser增加了表单自动填充功能,比较了一下UC、QQ、Opera、海豚浏览器等,都没有提供此项功能。问了很多人,也没人用过,所以就上网收集了相关资料。 先看看维基百科关于autofill词条的解释吧: ...

    Android 4.0 Browser增加了表单自动填充功能,比较了一下UC、QQ、Opera、海豚浏览器等,都没有提供此项功能。问了很多人,也没人用过,所以就上网收集了相关资料。

    先看看维基百科关于autofill词条的解释吧:

    Autofill is a function in some computer applications or programs, typically those containing forms, which fills in a field automatically.

    Most of the time, such as in Internet Explorer and Google Toolbar, the entries depend on the form field’s name, so as to not enter street names in a last name field or vice-versa. For this use, RFC 3106 proposed names for such form fields, but in the HTML 5 specification this RFC is no longer referenced, thus leaving the selection of names up to each browser’s implementation.

    Chrome浏览器对于自动填充的说明:

    您是否厌倦了反复填写信息相同的网络表单?您可以使用自动填充功能,只需点击一下便可完成表单填写。

    如果需要详细了解自动填充的来龙去脉,可以阅读RFC 3106。简单说,自动填充就是为了提升用户体验整出来的东西,有了它,你就不用每次注册帐号时无聊的填写姓名、地址、城市等资料了。chrome浏览器做的更绝,信用卡卡号都可以自动填充。

    Android浏览器的自动填充条目有:Full name、Company name、Address、Zip code、Country、Phone、Email等。Browser中有一个AutoFillProfileDatabase类来处理其存放,具体说就是保存在autofill.db数据库中。而WebKit的WebSettings.AutoFillProfile类就是用来表示这些字段信息的。整个处理逻辑也不复杂。

    一切看起来很美妙,但要真正用起来还有许多障碍需要克服:

    1、因为涉及到隐私,需要在浏览器的设置中开启Form Auto-fill选项;

    2、用户需要比较勤快,事先要将上面的个人信息录入;

    3、需要网站的支持,这些信息如何和表单上的字段对应,这就是RFC 3106所定义的,表单控件的命名需要遵守规范。RFC 3106给出了一个例子:

    <HTML>
    <HEAD>
    <title> eCom Fields Example </title>
    </HEAD>
    <BODY>
     <FORM action="http://ecom.example.com" method="POST">
    Please enter card information:
    <p>Your name on the card   <INPUT type="text" name="Ecom_Payment_Card_Name" SIZE=40>
    <br>The card number
      <INPUT type="text" name="Ecom_Payment_Card_Number" SIZE=19>
    <br>Expiration date (MM YY)
      <INPUT type="text" name="Ecom_Payment_Card_ExpDate_Month" SIZE=2>
      <INPUT type="text" name="Ecom_Payment_Card_ExpDate_Year" SIZE=4>
      <INPUT type="hidden" name="Ecom_Payment_Card_Protocol">
      <INPUT type="hidden" name="Ecom_SchemaVersion"           value="http://www.ecml.org/version/1.1">
    <br>   <INPUT type="submit" value="submit"> <INPUT type="reset">
     </FORM>
    </BODY>  </HTML>

    非常遗憾的是,国内的网站,包括京东、亚马逊中国、当当、淘宝等均不支持。

    4、用户敢用么?某些网站可能会试图捕获隐藏字段或难以发现的字段中的信息,因此,请勿在您不信任的网站上使用自动填充功能。

    5、某些网站会阻止浏览器保存您输入的内容,因此,无法在这些网站上填写表单。

    总结:

    1、自动填充功能基本上是无用的,至少在国内的环境下。

    分类: Android浏览器 标签: android, webkit

    WebKit for Android分析:表单控件

    2012年4月14日 alex 没有评论

    所谓表单控件,指的是html中的radio, checkbox, text, button, select等。在进行WebKit移植时,需要解决两件事:渲染和事件处理。下面就分析WebKit for Android渲染和事件处理。

    一、表单控件的绘制

    表单控件的渲染并不难,不外乎就是绘图和贴图。但在WebKit中,为了达到和平台一致的视感(look & feel), 定义了一个RenderTheme,包含了paintButton, paintCheckbox, paintTextField等方法,具体实现则由各平台的移植层实现。Android平台的RenderTheme类实现为RenderThemeAndroid,具体的控件绘制类位于Source/WebKit/android/下,类关系图如下:

    html_control_rendering

    类图中并没有TextField和TextArea的绘制类,这是因为文本框只需使用线条绘制边框及绘制文字即可,Webkit有默认的实现,Android移植中没有重新实现。

    RenderSkinNinePatch类是一个实用类,用于处理图片拉伸问题。RenderSkinCombo的实现实际上是贴一个背景图片,让人感觉是一个ComboBox/ListBox,但是ComboBox/ListBox的宽高不是固定的,这样在贴图时避免不了需要对图片进行拉伸。RenderSkinNinePatch则是将图片划分为9个部分,避免拉伸带来失真。

    二、表单控件的事件处理

    表单控件还需要响应用户的事件,比如点击button, 选择radio button,输入文本等等。对于radio button/checkbox而言,处理比较简单,只需更新一下状态数据和图片,而ComboBox/TextField/TextArea的处理要复杂一些,因为涉及到输入。在android webkit实现中,ComboBox和文本框是调用系统控件实现的。

    – 待续

    分类: Android浏览器 标签: android, webkit

    GDB调试webkit技巧一则

    2012年3月29日 alex 2 条评论

    关于如何使用gdb+gdbserver调试android webkit,请参考这篇文章。按照这种方法,每次启动gdb,都要设置调试符号查找路径,要连着输入好几个gdb命令,对于频繁调试webkit来说,很是麻烦。本文介绍的就是一种偷懒的方法。

    好在GDB支持启动文件,可以将gdb命令放在一个GDB启动文件中,然后每次启动GDB时会自动加载它们。GDB的启动文件默认为.gdbinit,也可以在调用GDB时指定启动文件,例如:

    gdb -command=mygdbinit x

    表示要调试可执行程序x,首先从文件mygdbinit中读取命令。本文选用第二种方法,不想将项目特有的指令影响其它程序的gdb调试。很快就可以写一个启动文件和脚本来减少命令的输入:

    # filename: mygdbinit

    set solib-absolute-prefix /home/developer/android/out/target/product/generic/symbols/   

    set solib-search-path /home/developer/android/out/target/product/generic/symbols/system/lib
    target remote :5039

    #!/bin/bash

    ./out/host/linux-x86/bin/adb forward tcp:5039 tcp:5039
    ./prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin/arm-eabi-gdb -command=./mygdbinit out/target/product/generic/symbols/system/bin/app_process

    这个脚本在我的机器上工作得很好,但是在其他小组成员上有问题,原因在于每个人的android源码存放的位置并不相同,在mygdbinit中定义了绝对路径并不合适,简单的解决方案是每个人都把上面两个文件稍微修改一下,有没有更好的解决方法呢?

    我想到的一个解决方案是定义一个环境变量$ANDROID_SOURCE,指向android源码根目录,问题也随之而来,gdb命令中并不能直接使用系统环境变量。借助于万能的互联网,最终还是找到解决方法,最终脚本如下:

    # filename: mygdbinit

    define loadsymbols
        shell echo set solib-absolute-prefix $ANDROID_SOURCE/out/target/product/generic/symbols/ >/tmp/tmp.webkitsymbolspath
        shell echo set solib-search-path $ANDROID_SOURCE/out/target/product/generic/symbols/system/lib >>/tmp/tmp.webkitsymbolspath
        source /tmp/tmp.webkitsymbolspath
        shell rm /tmp/tmp.webkitsymbolspath
    end

    loadsymbols
    target remote :5039

    #!/bin/bash
    if [ -d $ANDROID_SOURCE ] ; then
        echo "ANDROID_SOURCE: $ANDROID_SOURCE"
    else
        echo "you must define ANDROID_SOURCE environment variable to point your android source"
        exit 1
    fi

    $ANDROID_SOURCE/out/host/linux-x86/bin/adb forward tcp:5039 tcp:5039
    $ANDROID_SOURCE/prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin/arm-eabi-gdb -command=$ANDROID_SOURCE/webkit_gdbinit $ANDROID_SOURCE/out/target/product/generic/symbols/system/bin/app_process

    另外在$ANDROID_SOURCE/external/webkit/Tools/gdb/下还有实用的python脚本,可用于gdb调试,打印输出webkit数据类型。

    补充:gdb + ddd是一个不错的选择,可以一边调试,一边查看源代码,断点定义也比较直观,虽然比不上Visual Studio这种IDE,但调试还是可以轻松许多。

    在研究了chromium for android后,发现其中有几个脚本非常有用,稍加修改后用于android webkit调试:

    1、命令行上启动Browser (adb_run_browser)

    #!/bin/bash

    if [ $# –gt 0 ] ; then

        INTENT_ARGS=”-d $1”  # e.g. a URL

    fi

    adb shell am start \

      -a android.intent.action.VIEW \

      -n com.android.browser/.BrowserActivity \

      $INTENT_ARGS

    2、命令行上kill掉Browser (adb_kill_browser)

    #!/bin/bash

    BROWSER_PID_LINES=$(adb shell ps | grep ‘ com.android.browser’)

    VAL=$(echo “$BROWSER_PID_LINES” | wc –l)

    if [ $VAL –lt 1 ] ; then

      echo “Not running Browser.”

    else

      BROWSER_PID=$(echo $BROWSER_PID_LINES | awk ‘{print $2}’)

      if [ “$BROWSER_PID” != "" ] ; then

        set –x

        adb shell kill $BROWSER_PID

        set -

      else

        echo “Browser does not appear to be running.”

      fi

    fi

    分类: Android浏览器 标签: android, gdb, webkit

    Android中的硬件加速

    2012年2月17日 alex 没有评论

    本文的主要内容来自SDK文章的"Hardware Acceleration”.

    从Android 3.0开始,Android的2D渲染管线可以更好的支持硬件加速。硬件加速使用GPU进行View上的绘制操作。

    硬件加速可以在一下四个级别开启或关闭:

    • Application
    • Activity
    • Window
    • View

    Application级别

    往您的应用程序AndroidManifest.xml文件为application标签添加如下的属性即可为整个应用程序开启硬件加速:

    <application android:hardwareAccelerated="true" ...>

    Activity级别

    您还可以控制每个activity是否开启硬件加速,只需在activity元素中添加android:hardwareAccelerated属性即可办到。比如下面的例子,在application级别开启硬件加速,但在某个activity上关闭硬件加速。

    <application android:hardwareAccelerated="true">    <activity ... />    <activity android:hardwareAccelerated="false" /></application>

    Window级别

    如果您需要更小粒度的控制,可以使用如下代码开启某个window的硬件加速:

    getWindow().setFlags(    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);

    注:目前还不能在window级别关闭硬件加速。

    View级别

    您可以在运行时用以下的代码关闭单个view的硬件加速:

    myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

    注:您不能在view级别开启硬件加速

    为什么需要这么多级别的控制?

    很明显,硬件加速能够带来性能提升,android为什么要弄出这么多级别的控制,而不是默认就是全部硬件加速呢?原因是并非所有的2D绘图操作支持硬件加速,如果您的程序中使用了自定义视图或者绘图调用,程序可能会工作不正常。如果您的程序中只是用了标准的视图和Drawable,放心大胆的开启硬件加速吧!具体是哪些绘图操作不支持硬件加速呢?以下是已知不支持硬件加速的绘图操作:

    Android的绘制模型

    开启硬件加速后,Android框架将采用新的绘制模型。基于软件的绘制模型和基于硬件的绘制模型有和不同呢?

    基于软件的绘制模型

    在软件绘制模型下,视图按照如下两个步骤绘制:

    1. Invalidate the hierarchy(注:hierarchy怎么翻译?)

    2. Draw the hierarchy

    应用程序调用invalidate()更新UI的某一部分,失效(invalidation)消息将会在整个视图层中传递,计算每个需要重绘的区域(即脏区域)。然后Android系统将会重绘所有和脏区域有交集的view。很明显,这种绘图模式存在缺点:

    1. 每个绘制操作中会执行不必要的代码。比如如果应用程序调用invalidate()重绘button,而button又位于另一个view之上,即使该view没有变化,也会进行重绘。

    2. 可能会掩盖一些应用程序的bug。因为android系统会重绘与脏区域有交集的view,所以view的内容可能会在没有调用invalidate()的情况下重绘。这可能会导致一个view依赖于其它view的失效才得到正确的行为。

    基于硬件的绘制模型

    Android系统仍然使用invalidate()和draw()来绘制view,但在处理绘制上有所不同。Android系统记录绘制命令到显示列表,而不是立即执行绘制命令。另一个优化就是Android系统只需记录和更新标记为脏(通过invalidate())的view。新的绘制模型包含三个步骤:

    1. Invalidate the hierarchy

    2. 记录和更新显示列表

    3. 绘制显示列表

    分类: Android浏览器 标签: android

    WebKit的JavaScript对象扩展

    2012年1月12日 alex 7 条评论

    本文的内容主要参考网上收集的资料,不过在Android 4.0 webkit上做扩展时,碰到一些问题,觉得有必要记录下来。

    所谓扩展JavaScript对象,就是增加一个JS对象,但它并没有定义在标准的JS对象集合中。如果网页中包含了扩展的JS对象,使用普通的浏览器就会报JS错误。

    下面以添加HelloObject对象为例说明具体步骤,该对象具有description属性:

    1. 添加HelloObject.h, HelloObject.cpp, HelloObject.idl文件,简单起见,将这三个文件放到Source/WebCore/page目录下。

    #ifndef HelloObject_h
    #define HelloObject_h

    #include <wtf/PassRefPtr.h>
    #include <wtf/RefCounted.h>

    #include "PlatformString.h"

    namespace WebCore {

        class HelloObject : public RefCounted<HelloObject> {
        public:
            static PassRefPtr<HelloObject> create() { return adoptRef(new HelloObject()); }

            String description() const;
        private:
            HelloObject();
        };

    } // namespace WebCore

    #endif // HelloObject_h

    HelloObject.h

    #include "config.h"
    #include "HelloObject.h"

    namespace WebCore {

    HelloObject::HelloObject()
    {
    }

    String HelloObject::description() const
    {
        return "Hello Object";
    }

    } // namespace WebCore

    HelloObject.cpp

    module window {

        interface [OmitConstructor] HelloObject {
            readonly attribute DOMString description;
        };

    }

    HelloObject.idl

    2. 修改Source/WebCore/page/下的DOMWindow.h文件,添加如下声明:

    class HelloObject;

    public:

        HelloObject* helloObject() const;

        HelloObject* optionalHelloObject() const { return m_helloObject.get(); }

    private:

        mutable RefPtr<HelloObject> m_helloObject;

    3. 修改Source/WebCore/page/下的DOMWindow.cpp文件,添加接口实现:

    HelloObject* DOMWindow::helloObject() const
    {
        if (!m_helloObject)
            m_helloObject = HelloObject::create();
        return m_helloObject.get();
    }

    在DOMWindow::clear()函数中添加一行语句:

    m_helloObject = 0;

    4. 修改DOMWindow.idl文件,添加:

    attribute [Replaceable] HelloObject helloObject;

    5. 接下来需要修改编译系统,让android编译系统编译新增的文件:

    首先修改Source/WebCore/Android.mk,增加page/HelloObject.cpp到LOCAL_SRC_FILES变量,其次需要修改Source/WebCore/Android.derived.v8bindings.mk,增加$(intermediates)/bindings/V8HelloObject.h到GEN变量。(注:这个是必须的,否则就不会根据HelloObject.idl生成V8HelloObject.h文件,在编译时会出错,这也是折腾了半天得出的成果)

    至此,工作基本上完成,待webkit重新编译后,可以用如下的网页进行验证:

    <html>
    <body>
    <script type="text/javascript">
    document.write("<p> This is from HelloObject: ");
    document.write(helloObject.description + "</p>");
    </script>
    </body>
    </html>

     

    [参考资料]

    1. webkit的js对象扩展(一)——binding方式创建自定义对象(单实例)

    2. android 上 webkit js 本地扩展之全局本地对象实现步骤

    Webkit for Android分析

    2012年1月9日 alex 5 条评论

    本文是在他人文章上针对android 4.0做了一些调整和补充,所有权归原作者。原文作者信息:

    WebSite: http://www.jjos.org/
    作者: 姜江 linuxemacs@gmail.com
    QQ: 457283

    网上有许多webkit的分析文章,其中针对android porting的一篇文章WebKit – WebKit For Android,写的非常好,分析得非常深入。不过这篇文章针对的Android版本比较老(具体版本无从考究),因此本文将在这篇文章的基础上,加入android 4.0 webkit porting的一些内容。

    一、Android WebKit简介

    Webkit是一个开源的浏览器排版和渲染引擎,包含WebCore和JavascriptCore。WebKit有众多的实现(Qt、Gtk, windows, chromium, android, etc)。Android 4.0平台的Web引擎框架采用了WebKit中的WebCore,javascript引擎则是采用google的V8引擎。Android 4.0的webkit采用了和chromium 12.0.742.130中webkit相同的codebase,webkit版本为534.30。

    二、Android WebKit模块框架

    Android平台的WebKit上层由Java语言封装,并且作为API提供给Android应用开发者,而底层使用WebKit核心库(WebCore)进行网页排版。WebKit模块分为两个部分: Java层和C层(webkit库)。Java层和C层通过JNI相互调用,如图1所示:

    AndroidWebKitArchitecture

    图1 Android WebKit模块框架

    在webkit其它平台的移植中,webkit层就是封装WebCore,为上层应用提供接口的。Android的平台具有一定的特殊性,需要提供Java API接口,应用程序框架也是基于Java的,所以在Android的移植中,webkit层实际上被拆成两部分,Java部分和C++部分,它们之间通过JNI接口进行通讯。JNI是一种双向通讯机制,Java代码可以调用C/C++代码,C/C++代码也可以调用Java代码。

    通常,WebCore中回调Java的代码都位于WebKit(Android Implementation)层,但有一个例外,就是Source/WebCore/platform/android/GeolocationServiceBridge.cpp,该文件也包含回调到Java的代码。

    2.1 Java层框架

    2.1.1 Java层源码说明

    Java层的代码位于frameworks/base/core/java/android/webkit目录下。各文件的简单说明如下:

    AccessibilityInjector.java为WebView注入Accessibility
    BrowserFrame.java对WebCore中Frame对象的Java层封装,用于创建WebCore中定义的Frame,以及为该Frame对象提供Java层回调方法
    ByteArrayBuilder.java辅助对象,用于byte块链表的处理
    CacheLoader.javaandroid 4.0 WebKit中不再使用
    CacheManager.javaCache管理对象,负责Java层Cache对象管理
    CallbackProxy.java该对象是用于处理WebCore与UI线程消息的代理类。当有Web事件产生时WebCore线程会调用该回调代理类,代理类会通过消息的方式通知UI线程,并且调用设置的客户对象的回调函数。
    CertTool.java证书工具
    ClientCertRequestHandler.java处理客户端证书请求
    ConsoleMessage.java来自WebCore的Javascript控制台消息
    ContentLoader.javaandroid 4.0 WebKit中不再使用
    CookieManager.java根据RFC2109规范,管理cookies
    CookieSyncManager.javaCookies同步管理对象,该对象负责同步RAM和Flash之间的Cookies数据。实际的物理数据操作在基类WebSyncManager中完成。
    DataLoader.javaandroid 4.0 WebKit中不再使用
    DateSorter.java日期排序
    DebugFlags.java定义调试标志
    DeviceMotionAndroidOrientationManager.java用于实现DeviceMotion和DeviceOrientation
    DeviceMotionService.java实现SensorEventListener接口,处理动作
    DeviceOrientationService.java实现SensorEventListener接口,处理方向变化
    DownloadLister.java下载侦听器接口
    FileLoader.javaandroid 4.0 WebKit中不再使用
    FindActionModeCallback.java
    FrameLoader.javaFrame载入器,用于载入网页Frame数据
    GeolocationPermission.java用于管理浏览器UI的位置信息权限
    GeolocationService.java实现java侧的GeolocationServiceAndroid
    HTML5Audio.javaHTML5 audio支持类
    HTML5VideoFullScreen.java全屏视频视图,仅提供给浏览器使用
    HTML5VideoInline.java内嵌视频视图,仅提供给浏览器使用
    HTML5VideoView.java视频视图,仅提供给浏览器使用
    HTML5VideoViewProxy.javaHTML5视频视图代理类
    HttpAuthHandler.javaHTTP认证请求,需要用户处理
    HttpAuthHandlerImpl.javaHttpAuthHandler实现,仅用于Android Java HTTP stack
    JniUtil.java供JNI使用的实用类,用于获取cache目录等C代码无法直接获取的信息,以及读取资源包中的文件等
    JsPromptResult.javaJs结果提示对象,用于向用户提示Javascript运行结果。
    JsResult.javaJs结果对象,用于用户交互
    JWebCoreJavaBridge.java用Java与WebCore库中Timer和Cookies对象交互的桥接代码。
    KeyStoreHandler.javahttps相关处理
    L10nUtils.java字符串国际化,在使用chrome http stack时用到
    LoadListener.java载入器侦听器,用于处理载入器侦听消息。android 4.0 WebKit中不再使用
    MimeTypeMap.javaMIME类型映射
    MockGeolocation.java模拟地理位置信息
    Network.java该对象封装网络连接逻辑,为调用者提供更为高级的网络连接接口。
    OverScrollGlow.java用于实现OverScroller效果
    PerfChecker.java性能测试
    Plugin.java插件处理相关
    PluginData.java插件处理相关
    PluginFullScreenHolder.java插件处理相关
    PluginList.java插件处理相关
    PluginManager.java插件处理相关
    PluginStub.java插件处理相关
    SearchBox.java定义搜索对话框接口
    SearchBoxImpl.java搜索对话框接口实现
    SelectActionModeCallback.java
    SslCertLookupTable.javahttps相关处理
    SslClientCertLookupTable.javahttps相关处理
    SslErrorHandler.javahttps相关处理
    SslErrorHandlerImpl.javahttps相关处理
    StreamLoader.javaandroid 4.0 WebKit中不再使用
    UrlInterceptHandler.java用于google gears,已废弃
    UrlInterceptRegistry.java用于google gears,已废弃
    URLUtil.javaURL处理实用类
    ValueCallback.java回调接口,用于异步返回数据值
    ViewManager.java子视图管理类,主要用于管理插件视图
    ViewStateSerializer.javaWebView视图序列化和反序列化
    WebBackForwardList.java该对象包含WebView对象中显示的历史数据。
    WebBackForwardListClient.java浏览历史处理的客户接口类,所有需要接收浏览历史改变的类都需要实现该接口。
    WebChromeClient.javaChrome客户基类,Chrome客户对象在浏览器文档标题、进度条、图标改变时候会得到通知。
    WebHistoryItem.java该对象用于保存一条网页历史数据
    WebIconDatabase.java图标数据库管理对象,所有的WebView均请求相同的图标数据库对象
    WebResourceResponse.java封装某个资源的响应信息
    WebSettings.javaWebView的管理设置数据,该对象数据是通过JNI接口从底层获取。
    WebStorage.java处理webstorage数据库
    WebSyncManager.java数据同步对象,用于RAM数据和FLASH数据的同步操作。
    WebTextView.java在html文本输入控件激活时,显示系统原生编辑组件
    WebView.javaWeb视图对象,用于基本的网页数据载入、显示等UI操作。
    WebViewClient.javaWeb视图客户对象,在Web视图中有事件产生时,该对象可以获得通知。
    WebViewCore.java该对象对WebCore库进行了封装,将UI线程中的数据请求发送给WebCore处理,并且通过CallbackProxy的方式,通过消息通知UI线程数据处理的结果。
    WebViewDatabase.java该对象使用SQLiteDatabase为WebCore模块提供数据存取操作。
    WebViewFragment.java实现WebView嵌入到Fragment中
    WebViewWorker.java实现html5 workers,在UI线程和webkit线程开启单独的线程
    ZoomControlBase.java缩放控件接口
    ZoomControlEmbedded.java内置缩放控件
    ZoomControlExternal.java扩展缩放控件,已废弃
    ZoomManager.java维护WebView的缩放状态
    2.1.2 Java层主要类关系图

    WebKit Java层包含79个Java文件,主要的类关系图如下:

    Java layer class diagram

    1)WebView

    WebView类是WebKit模块Java层的视图类,所有需要使用Web浏览功能的Android应用程序都要创建该视图对象显示和处理请求的网络资源。目前,WebKit模块支持HTTP、HTTPS、FTP以及javascript请求。WebView作为应用程序的UI接口,为用户提供了一系列的网页浏览、用户交互接口,客户程序通过这些接口访问WebKit核心代码。

    2)WebViewDatabase

    WebViewDatabase是WebKit模块中针对SQLiteDatabase对象的封装,用于存储和获取运行时浏览器保存的缓冲数据、历史访问数据、浏览器配置数据等。该对象是一个单实例对象,通过getInstance方法获取WebViewDatabase的实例。WebViewDatabase是WebKit模块中的内部对象,仅供WebKit框架内部使用。

    3)WebViewCore

    WebViewCore类是Java层与C层WebKit核心库的交互类,客户程序调用WebView的网页浏览相关操作会转发给BrowserFrame对象。当WebKit核心库完成实际的数据分析和处理后会回调WebViweCore中定义的一系列JNI接口,这些接口会通过CallbackProxy将相关事件通知相应的UI对象。

    4)CallbackProxy

    CallbackProxy是一个代理类,用于UI线程和WebCore线程交互。该类定义了一系列与用户相关的通知方法,当WebCore完成相应的数据处理,则会调用CallbackProxy类中对应的方法,这些方法通过消息方式间接调用相应处理对象的处理方法。

    5)BrowserFrame

    BrowserFrame类负责URL资源的载入、访问历史的维护、数据缓存等操作,该类会通过JNI接口直接与WebKit C层库交互。

    6)JWebCoreJavaBridge

    该类为Java层WebKit代码提供与C层WebKit核心部分的Timer和Cookies操作相关的方法。

    7)WebSettings

    该对象描述了WEB浏览器访问相关的用户配置信息。

    8)DownloadListener

    下载侦听接口,如果客户代码实现该接口,则在下载开始、失败、挂起、完成等情况下,DownloadManagerCore对象会调用客户代码中实现的DwonloadListener方法。

    9)WebBackForwardList

    WebBackForwarList对象维护着用户访问历史记录,该类为客户程序提供操作访问浏览器历史数据的相关方法。

    10)WebViewClient

    WebViewClient类定义了一系列事件方法,如果Android应用程序设置了WebViewClient派生对象,则在页面载入、资源载入、页面访问错误等情况发生时,该派生对象的相应方法会被调用。

    11)WebBackForwardListClient

    WebBackForwardListClient对象定义了对访问历史操作时可能产生的事件接口,当用户实现了该接口,则在操作访问历史时(访问历史移除、访问历史清空等)用户会得到通知。

    12)WebChromeClient

    WebChromeClient类定义了与浏览窗口修饰相关的事件。例如接收到Title、接收到Icon、进度变化时,WebChromeClient的相应方法会被调用。

    2.1.3 流载入器(已废弃)

    在Android 4.0之前的版本,数据载入都是在Java层实现的,从4.0开始,Android webkit引入了chromium的部分代码,输入载入走的是C++代码。不过原有的Java代码仍然保留,可以在编译webkit时用USE_CHROME_NETWORK_STACK宏进行切换。

    2.2 C层框架

    2.2.1 C类与Java类的关系

    WebKit类一般被拆成两个,Java类和C++类。比如在Java API部分,有一个WebView类,在C++部分,也有一个WebView类。WebViewCore, WebSettings等等也是同样的。

    需要注意的是,JNI是C语言接口,所以Java类并不能直接调用C++代码,需要在C++代码中export出C语言接口。所以代码中使用了一个技巧,在Java类中定义一个int成员变量(实际上是一个指针),指向对应的C++类,如下图所示:

    AndroidNativeClass

    1.BrowserFrame

    与BrowserFrame Java类相对应的C++类为WebFrame(文件名为WebCoreFrameBridge.cpp),该类为Dalvik虚拟机回调BrowserFrame类中定义的本地方法进行了封装。与BrowserFrame中回调函数(Java层)相对应的C层结构定义如下:

    struct WebFrame::JavaBrowserFrame
    {
        jweak mObj;
        jweak mHistoryList; // WebBackForwardList object
        jmethodID mStartLoadingResource;
        jmethodID mMaybeSavePassword;
        jmethodID mShouldInterceptRequest;
        jmethodID mLoadStarted;
        jmethodID mTransitionToCommitted;
        jmethodID mLoadFinished;
        jmethodID mReportError;
        jmethodID mSetTitle;
        jmethodID mWindowObjectCleared;
        jmethodID mSetProgress;
        jmethodID mDidReceiveIcon;
        jmethodID mDidReceiveTouchIconUrl;
        jmethodID mUpdateVisitedHistory;
        jmethodID mHandleUrl;
        jmethodID mCreateWindow;
        jmethodID mCloseWindow;
        jmethodID mDecidePolicyForFormResubmission;
        jmethodID mRequestFocus;
        jmethodID mGetRawResFilename;
        jmethodID mDensity;
        jmethodID mGetFileSize;
        jmethodID mGetFile;
        jmethodID mDidReceiveAuthenticationChallenge;
        jmethodID mReportSslCertError;
        jmethodID mRequestClientCert;
        jmethodID mDownloadStart;
        jmethodID mDidReceiveData;
        jmethodID mDidFinishLoading;
        jmethodID mSetCertificate;
        jmethodID mShouldSaveFormData;
        jmethodID mSaveFormData;
        jmethodID mAutoLogin;
        AutoJObject frame(JNIEnv* env) {
            return getRealObject(env, mObj);
        }
        AutoJObject history(JNIEnv* env) {
             return getRealObject(env, mHistoryList);
        }
    };

    该结构作为WebFrame(C层)的一个成员变量(mJavaFrame),在WebFrame构造函数中,用BrowserFrame(Java层)类的回调方法的method ID初始化JavaBrowserFrame结构的各个域。初始后,当WebCore(C层)在剖析网页数据时,有Frame相关的资源改变,比如WEB页面的主题变化,则会通过mJavaFrame结构,调用指定BrowserFrame对象的相应方法,通知Java层处理。

    2.JWebCoreJavaBridge

    与该对象相对应的C层对象为JavaBridge,JavaBridge对象继承了TimerClient, CookieClient, KeyGenerateorClient, FileSystemClient类,主要负责WebCore中的定时器和Cookie管理。与Java层JWebCoreJavaBridge类中方法method ID相关的是JavaBridege中几个成员变量,在构造JavaBridge对象时,会初始化这些成员变量,之后有Timer或者Cookies事件产生,WebCore会通过这些ID值,回调对应JWebCoreJavaBridge的相应方法。

    3.LoadListener

    与该对象相关的C层对象为WebCoreResourceLoader,与LoaderListener中回调函数(Java层)相对应的C层结构是struct resourceloader_t,该结构保存了LoadListener对象ID、CancelMethod ID以及DownloadFiledMethod ID等值。当有Cancel或者Download事件产生,WebCore会回调LoadListener类中的CancelMethod或者DownloadFileMethod。

    4.WebViewCore

    与WebViewCore相关的C类是WebViewCorel,定义了两个数据结构,一个是WebViewCoreFields,对应于Java层WebViewCore对象的成员变量,另一个是WebViewCore::JavaGlue,对应于Java层WebViewCore对象的成员方法。定义如下:

    // Field ids for WebViewCore
    struct WebViewCoreFields {
        jfieldID m_nativeClass;
        jfieldID m_viewportWidth;
        jfieldID m_viewportHeight;
        jfieldID m_viewportInitialScale;
        jfieldID m_viewportMinimumScale;
        jfieldID m_viewportMaximumScale;
        jfieldID m_viewportUserScalable;
        jfieldID m_viewportDensityDpi;
        jfieldID m_webView;
        jfieldID m_drawIsPaused;
        jfieldID m_lowMemoryUsageMb;
        jfieldID m_highMemoryUsageMb;
        jfieldID m_highUsageDeltaMb;
    } gWebViewCoreFields;

    // —————————————————————————-

    struct WebViewCore::JavaGlue {
        jweak m_obj;
        jmethodID m_scrollTo;
        jmethodID m_contentDraw;
        jmethodID m_layersDraw;
        jmethodID m_requestListBox;
        jmethodID m_openFileChooser;
        jmethodID m_requestSingleListBox;
        jmethodID m_jsAlert;
        jmethodID m_jsConfirm;
        jmethodID m_jsPrompt;
        jmethodID m_jsUnload;
        jmethodID m_jsInterrupt;
        jmethodID m_didFirstLayout;
        jmethodID m_updateViewport;
        jmethodID m_sendNotifyProgressFinished;
        jmethodID m_sendViewInvalidate;
        jmethodID m_updateTextfield;
        jmethodID m_updateTextSelection;
        jmethodID m_clearTextEntry;
        jmethodID m_restoreScale;
        jmethodID m_needTouchEvents;
        jmethodID m_requestKeyboard;
        jmethodID m_requestKeyboardWithSelection;
        jmethodID m_exceededDatabaseQuota;
        jmethodID m_reachedMaxAppCacheSize;
        jmethodID m_populateVisitedLinks;
        jmethodID m_geolocationPermissionsShowPrompt;
        jmethodID m_geolocationPermissionsHidePrompt;
        jmethodID m_getDeviceMotionService;
        jmethodID m_getDeviceOrientationService;
        jmethodID m_addMessageToConsole;
        jmethodID m_formDidBlur;
        jmethodID m_getPluginClass;
        jmethodID m_showFullScreenPlugin;
        jmethodID m_hideFullScreenPlugin;
        jmethodID m_createSurface;
        jmethodID m_addSurface;
        jmethodID m_updateSurface;
        jmethodID m_destroySurface;
        jmethodID m_getContext;
        jmethodID m_keepScreenOn;
        jmethodID m_sendFindAgain;
        jmethodID m_showRect;
        jmethodID m_centerFitRect;
        jmethodID m_setScrollbarModes;
        jmethodID m_setInstallableWebApp;
        jmethodID m_enterFullscreenForVideoLayer;
        jmethodID m_setWebTextViewAutoFillable;
        jmethodID m_selectAt;
        AutoJObject object(JNIEnv* env) {
            // We hold a weak reference to the Java WebViewCore to avoid memeory
            // leaks due to circular references when WebView.destroy() is not
            // called manually. The WebView and hence the WebViewCore could become
            // weakly reachable at any time, after which the GC could null our weak
            // reference, so we have to check the return value of this method at
            // every use. Note that our weak reference will be nulled before the
            // WebViewCore is finalized.
            return getRealObject(env, m_obj);
        }
    };

    WebViewCore类有个JavaGlue对象作为成员变量,在构建WebViewCore对象时,用WebViewCore(Java层)中的方法ID值初始化该成员变量。并且会将构建的WebViewCore对象指针复制给WebViewCore(Java层)的mNativeClass,这样将WebViewCore(Java层)和WebViewCore(C层)关联起来。

    5.WebSettings

    与WebSettings相关的C层结构是struct FieldIds(文件名WebSettings.cpp),该结构保存了WebSettings类中定义的属性ID以及方法ID,在构建FieldIds对象时,会设置这些方法和属性的ID值。

    6.WebView

    与WebView相关的C层类是WebView,该类中的m_javaGlue中保存着WebView(Java层)中定义的属性和方法ID,在WebView(C层)构造方法中初始化,并且将构造的WebView对象(C层)的指针,赋值给WebView类(Java层)的mNativeClass变量,这样WebView(Java层)和WebView对象(C层)建立了关系。

    三、基本流程分析

    3.1 webkit初始化

    Android提供了WebView类,该类提供客户化浏览显示的功能。如果客户需要加入浏览器的支持,可像使用其它视图类一样加入应用程序,显示给用户。当客户代码中第一次生成WebView对象时,会初始化WebKit库(包括Java层和C层两个部分),之后用户可以操作WebView对象完成网络或者本地资源的访问。

    WebView对象的生成主要涉及4个类CallbackProxy、WebViewCore、WebViewDatabase以及BrowserFrame。其中CallbackProxy对象为WebKit模块中UI线程和WebKit类库提供交互功能,WebViewCore是WebKit的核心层,负责与C层交互以及WebKit模块C层类库初始化,WebViewDatabase为WebKit模块运行时缓存、cookie等数据存储提供支持,BrowserFrame用于创建WebCore中的Frame,并为Frame提供Java层回调方法。WebKit模块初始化流程如下:

    实例化WebView

    • 创建CallbackProxy对象
    • 创建WebViewCore对象
      1. 调用System.loadLibrary载入webcore相关类库(C层)
      2. 如果是第一次初始化WebViewCore对象,创建WebCoreTherad线程
      3. 创建EventHub对象,处理WebViewCore事件
      4. 获取WebIconDatabase对象实例
      5. 向WebCoreThread发送初始化消息
        • 创建BrowserFrame对象
        • 向WebView发送WEBCORE_INTIALIZED_MSG_ID消息,通知初始化完成
    • 获取WebViewDatabase实例
    • 调用init初始化WebView
    • 收到WEBCORE_INITIALIZED_MSG_ID消息后,调用nativeCreate
    3.1.1 JNI native方法注册

    在创建WebViewCore时进行,调用System.loadLibrary方法载入webcore相关类库,该过程由Dalvik虚拟机完成,它会从动态链接库目录中寻找libWebCore.so类库,载入到内存中,并且调用WebKit初始化模块的JNI_OnLoad方法(代码见WebCoreJniOnLoad.cpp)。WebKit模块的JNI_OnLoad方法中完成了如下初始化操作:

    1. 初始化JavaBridge[registerJavaBridge]

    获取JWebCoreJavaBridge类的mNativeBridge成员变量的fieldID,以及注册JWebCoreJavaBridge类中的native方法

    2. 初始化JniUtil[registerJniUtil]

    注册JniUtil类中的native方法

    3. 初始化WebFrame[registerWebFrame]

    获取BrowserFrame类的mNativeFrame成员变量的ID,以及注册BrowserFrame类中的native方法

    4. 初始化WebCoreResourceLoader[registerResourceLoader]

    获取LoadListener类的mNativeLoader成员的ID,以及注册LoadListener类中的native方法

    5. 初始化WebViewCore[registerWebViewCore]

    获取WebViewCore类的java成员的ID,以及注册WebViewCore类中的native方法

    6. 初始化WebHistory[registerWebHistory]

    获取WebHistoryItem类的java成员的ID,以及注册WebBackForwardList和WebHistoryItem类中的native方法

    7. 初始化WebIconDatabase[registerWebIconDatabase]

    注册WebIconDatabase类中的native方法

    8. 初始化WebSettings[registerWebSettings]

    获取WebSettings类的java成员的ID,以及注册native方法

    9. 初始化WebStorage[registerWebStorage]

    注册WebStorage类的native方法

    10. 初始化WebView[registerWebView]

    获取WebView类的mNativeClass成员的ID,以及注册native方法

    11. 初始化ViewStateSerializer[registerViewStateSerializer]

    注册ViewStateSerializer类的native方法

    12. 初始化GeolocationPermissions[registerGeolocationPermissions]

    注册GeolocationPermissions类的native方法

    13. 初始化MockGeolocation[registerMochGeolocation]

    注册MockGeolocation类的native方法

    14. 初始化HTML5Audio[registerMediaPlayerAudio]

    注册HTML5 Audio类的native方法

    15. 初始化HTML5Video[registerMediaPlayerVideo]

    注册HTML5VideoViewProxy类的native方法

    16. 初始化DeviceMotionAndOrientationManager[registerDeviceMotionAndOrientationManager]

    注册DeviceMotionAndOrientationManager类的native方法

    17. 初始化CookieManager[registerCookieManager]

    注册CookieManager类的native方法

    18. 初始化CacheManager[registerCacheManager]

    注册CacheManager类的native方法

    3.1.2  UI线程和webcore线程

    webcore线程在第一次创建WebViewCore对象时创建, 且只创建一次,该线程负责处理WebCore初始化事件。WebViewCore构造函数会被阻塞,直到WebCoreThread初始化完成。在WebViewCore对象构造最后一步,发送INITIALIZE消息给WebCoreThread,执行webcore相关的初始化(WebViewCore::initialize)。在WebViewCore::initialize方法中,会创建BrowserFrame对象,并且向WebView对象发送WEBCORE_INITIALIZED_MSG_ID消息。WebView收到消息后,会执行nativeCreate方法,创建c层的WebView对象。

    3.1.3 初始化过程序列图

    Initialize Sequence

    3.2 loadData

    loadData用于加载"data:”形式的url,通过该方法,可以将文件内容读入到字符串,然后通过loadData进行加载,是最简单的一种数据加载方法。比如:

    webview.loadData(“<html><body>hello</body></html>”, "text/html”, "utf-8”);

    3.2.1 loadData序列图

    LoadData Sequence

    (未完待续)

    chromium build脚本中开始出现android了

    2011年10月13日 alex 没有评论

    chromium的更新真是够疯狂的,一不小心开发版本就到16了。今天在看chromium源码时,无意中看到有关android的代码了,看来chromium android版本离我们不远了。

    首先是build下多了install-build-deps-android.sh脚本,这个脚本是用来安装android SDK r13和android ndk r6b。难道不考虑android3.2以下版本?

    build下还有一个all_android.gyp文件,里面只有一个target: android_builder_tests.

    build下android文件夹只有一个文件envsetup.sh,在编译android版本前必须执行该脚本设置环境变量。

    看了看源码树,只有base和net目录下有少量的android平台代码。

    从现存的代码来看,似乎android移植才开了个头,不过最近有媒体爆料说即将发布的android 4.0中即将包含chromium浏览器。莫非chromium也会学习android,只进行有限的开放。

    android chromium要来了?

    2011年9月16日 alex 5 条评论

    之前做android浏览器的时候,还考虑过将chromium移植到android。不过浏览了一下chromium的源码后就放弃了,chromium代码库太庞大了,不是几个人能搞定的。这两天网上满天飞的消息,google开始着手将chrome移植到android上了。不过究其消息来源,并非google官方声明,而是google开发团队的一位大牛Andrei Popescu在webkit的mail list写了下面的话:

    We plan to start by setting up a webkit.org build bot that will compile Chromium’s DRT for Android using the Android NDK, SDK and toolchain. We anticipate a reasonably small set of changes to the Chromium port to achieve this. We’re fully committed to maintaining this new flavor of the Chromium port of WebKit and having a build bot up and running as soon as possible will make this an easier task. At the same time, we will be removing the existing incomplete Android port. This includes the Android-specific code in WebCore/platform/android, as well as any code guarded by the PLATFORM(ANDROID) macro.

    简单说,就是Google的Android团队要对于Android里的浏览器进行改造,将这个开源的移动浏览器跟另外一个同样开源的桌面浏览器进行整合,移除Android浏览器独有的特殊代码,取而代之Chromium的代码。

    以google的实力,决定将chromium移到android上,肯定是办得到的。现在的问题是,会在android的哪个版本中出现?毫无疑问,这个时候决定整合浏览器,一定是冲着android平板而来(对于智能手机,现有的android浏览器可以应付得了),况且移植工作量也不小,所以估计要在android 4.0之后才可以看得到。

    我比较关心的是,如果android中有了chromium浏览器,我们做浏览器的是否会失业呢?

    分类: Android浏览器 标签: android, chromium

    webkit android移植(1):开篇

    2011年8月17日 alex 4 条评论

    看到这个标题,您可能会觉得奇怪,android浏览器本来就是基于webkit的,而且android还提供了一个非常方便的WebView及相关类。系统的浏览器当然稳定而且强大,但在某些情况下,可能还是不够,比如android浏览器不支持wml,也不支持html5 video。其实webkit本身是支持的,只不过android没有打开编译开关。另外一个原因,就是android中使用的webkit内核比较老,对于html5的支持比较差。因此萌发了自己编译libwebcore,作为独立的应用进行开发。这样做的一个缺点就是和android平台相关了,没有办法做到2.1/2.2/2.3通用。

    首先说说开发环境,webkit选择android4.0版本包含的webkit。构建系统选择的是android的源码构建系统。这样选择也是为了选择一个稳定的版本,不在build system上花费太多的时间。开发机环境为ubuntu 11.10,64位系统。

    阅读webkit android的代码,我们可以知道,实际上浏览器的代码包括两部分:一部分是java代码,是对webkit的一个封装,提供方便使用的WebView及相关类;一部分是c++代码,也就是webkit代码(包括WebCore和JavaScriptCore,在android 2.2之后,google用V8取代了JavaScriptCore),编译成so,供Java侧代码使用。

    分析webkit的代码接口,主要需要做如下工作:

    1. WTF库的移植

    2. WebCore platform代码的移植

    3. WebCore核心代码的移植

    4. WebCore Support代码的实现

    5. WebKit JNI

    6. WebKit Java封装

    分类: Android浏览器 标签: android, NDK, webkit

    使用内部(com.android.internal)和隐藏(@hide)API[第4部分,定制ADT]

    2011年7月6日 alex 6 条评论

    本文翻译自http://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-4-customizing-adt/

    在前面的文章中,我讲述了如何创建定制的original-android.jar和创建定制的android平台以使用original-android.jar。这仅仅能够使用隐藏API,还为内部API留有一个障碍: ADT。ADT定义了一个规则禁止使用来自com.android.internal的类。

    image_thumb30_thumb

    有几种方法可以越过该限制规则:

    1)完整的ADT源代码可以下载,可以移除或者修改代码,编译然后安装新的定制版本ADT。不好的地方是您必须配置一台64位linux系统,下载源码,编译之。这会花费一些时间,当新版本的ADT出来后,您又需要重新来过。

    2)另外一种方式是修改ADT的字节码,只需替换"com/android/inter/**”字符串为其它的字符串,比如"com/android/internax”。

    第二种方法可通过脚本自动化完成,也不需要访问源码,在windows下也能工作,这也是我在本文中说明第二种方式的原因。

    修改ADT字节码

    进入到您的eclipse的plugins文件夹,找出名为com.android.ide.eclipse.adt_*.jar的文件。做一个备份(以防修改错了),另外复制一份改文件到一个单独的"experimental”文件夹,在那里进行字节码修改。

    修改*.jar为*.zip,解压文件到一个单独的文件夹,下面就是我所得到的:

    image11

    现在进入到com/android/ide/eclipse/adt/internal/project子目录,找出AndroidClasspathContainerInitializer.class文件。

    image12

    该文件包含了字符串"com/android/internal/**”,下一步就是替换该字符串为其它的字符串,如"com/android/internax/**”。改变字符串的长度可能没什么问题,但最好只替换一个字母,保持长度相同。

    我是用notepad++进行替换的,因为它支持非打印字符,而且在编辑打印字符时不会修改非打印字符。

    image13

    修改完后,保存文件,zip压缩文件夹,文件名和原始版本一样。以我的为例:com.android.ide.eclipse.adt_8.0.1.v201012062107-82219.zip,然后重命名为*.jar。

    注意:请确保您正确的压缩了文件,可以比较一下修改的zip和原始的zip的内部目录结构。

    现在删除eclipse plugins文件夹下的原始ADT*.jar文件,复制经过修改的版本,重启eclipse。

    如果没有问题,则会如下图所示:

    image14

    步骤总结:

    1. 停止eclipse
    2. 从eclipse的plugins文件夹取得adt插件的jar文件。
    3. 重命名.jar为.zip,然后解压到一个单独的目录。
    4. 找到com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.class
    5. 将字符串"com/android/internal/**”替换为"com/android/internax/**”
    6. zip压缩所有文件
    7. 重命名.zip为.jar
    8. 用修改版本替换eclipse plugins文件夹下的原始adt jar文件
    9. 启动eclipse。

    展开全文
  • 早在我刚刚学习JavaScript的时候,我就被灌输了这样的思想 ...后来我知道了浏览器仅仅有js一个线程,它与其他线程共同组成了 —– 浏览器UI线程早就想写一篇这样的文章,只是感觉理解的还不够,怕我写的对大

    tip:有问题或者需要大厂内推的+我脉脉哦:丛培森 ٩( ‘ω’ )و

    早在我刚刚学习JavaScript的时候,我就被“灌输”了这样的思想
    JavaScript是单线程的
    可是在我不断的学习过程中
    学到了定时器、ajax的异步加载
    一度让我对这句话产生怀疑
    既然JavaScript是单线程的,为什么它还存在异步加载?
    后来我知道了浏览器中不仅仅有js一个线程,它与其他线程共同组成了 ----- 浏览器UI多线程

    早就想写一篇这样的文章,只是感觉理解的还不够,怕我写的对大家产生误导
    看完网上大家写的各种博文,更是一脸懵逼
    不过最后还是鼓起勇气决定来谈一下╮(╯_╰)╭
    那么现在抛开JavaScript线程的问题不谈,我们先来看看什么是浏览器UI线程
    #浏览器UI多线程#
    先来上一张图

    浏览器UI多线程

    一般情况下浏览器至少有三个常驻线程

    • GUI渲染线程(渲染页面)
    • JS引擎线程(处理脚本)
    • 事件触发线程(控制交互)

    包括浏览器有时候会开辟新的线程比如用完就扔的Http请求线程(Ajax)等等其他线程
    它们共同构成了浏览器的UI线程
    这些线程在UI线程的控制下井然有序的工作着
    关于这个常驻线程网上观点不一致,各个浏览器实现可能也不一样,这里就不深考究了

    虽然我把js引擎线程放在了右下角,但是它是浏览器的主线程
    而且它和GUI渲染线程是水火不容的,不能够同时工作
    道理很简单,因为它们都要影响DOM,如果js线程想要某个DOM的样式,渲染引擎必须停止工作
    #js单线程#
    为什么JavaScript是单线程
    单线程就是同一时间只能干一件事
    那么JavaScript多线程不好吗,那效率多高啊
    不好
    js设计出来就是为了与用户交互,处理DOM
    如果JavaScript多线程了,那就必须处理多线程同步的问题(依稀记得曾经被C++线程同步支配的恐怖)
    假如js是多线程,同一时间一个线程想要修改DOM,另一个线程想要删除DOM
    问题就变得复杂许多,浏览器不知道听谁的,如果引入“锁”的机制,那就麻烦死了(那我就不学前端了( ̄_, ̄ ))
    这样一个脚本语言根本没有必要搞得那么复杂,所以JavaScript诞生起就是单线程执行

    虽然H5提出了Web Worker,但是它不能够操作DOM,还是要委托给大哥js主线程解决
    这些子线程完全受主线程老大控制的(而且受很多限制),实际上并没有改变JavaScript单线程的本质
    #执行栈#
    我们先来看看什么是执行栈
    栈是先进后出(FILO)的数据结构
    执行栈中存放正在执行的任务,每一个任务叫做“帧”
    举个例子

    function foo(c){
    	var a = 1;
    	bar(200);
    }
    function bar(d){
    	var b = 2;
    }
    foo(100);
    

    我们来看看执行栈发生了怎样的变化

    • 最开始,代码没有执行的时候,执行栈为空栈
    • foo函数执行时,创建了一帧,这帧中包含了形参、局部变量(预编译过程),然后把这一帧压入栈中
    • 然后执行foo函数内代码,执行bar函数
    • 创建新帧,同样有形参、局部变量,压入栈中
    • bar函数执行完毕,弹出栈
    • foo函数执行完毕,弹出栈
    • 执行栈为空

    执行栈其实相当于js主线程(同步顺序执行)
    #任务队列#
    队列是先入先出(FIFO)的数据结构
    js线程中还存在着一个任务队列
    任务队列包含了一系列待处理的任务
    单线程就意味着所有任务需要一个接一个的执行,如果一个任务执行的时间太长,那后面的任务就不得不等着
    就好比护士阿姨给排队的小朋友打针,如果最前面的小朋友一直滚针,那就一直扎,后面的小朋友就得等着(这比喻好像不恰当)
    可是如果最前面的小朋友晕针昏倒了
    那么护士阿姨不可能坐那里了等到他醒来,应该先给后面的小朋友扎针
    也就是相当于把那位小朋友“挂起”(异步)

    所以,任务可以分为两种

    • 同步任务
    • 异步任务

    同步任务就是正在主线程执行栈中执行的任务(在屋子内打针的小朋友)
    而异步任务是在任务队列等候处理的任务(在屋子外等候打针的小朋友)
    一旦执行栈中没有任务了,它就会从执行队列中获取任务执行
    #事件与回调#
    任务队列是一个事件的队列,IO设备(输入/输出设备)每完成一项任务,就会在任务队列中添加事件处理
    用户触发了事件,也同样会将回调添加到任务队列中去
    主线程执行异步任务,便是执行回调函数(事件处理函数)
    只要执行栈一空,排在执行队列前面的处理函数便会被优先读取执行
    不过主线程会检查时间,某些事件需要到了规定时间才能进入主线程处理(定时器事件)
    #事件循环#
    主线程从执行队列不断地获取任务,这个过程是循环不断地,叫做“Event Loop”事件循环
    同步任务总是会在异步任务之前执行
    只有当前的脚本执行完,才能够去拿任务队列中的任务执行
    前面也说到了,任务队列中的事件可以是定时器事件
    定时器分为两种 setTimeout() 和 setInterval()
    前者是定时执行一次,后者定时重复执行
    第一个参数为执行的回调函数,第二个参数为间隔时间(ms)
    来看这样一个例子

    setTimeout(function(){
        console.log('timer');
    }, 1000);
    console.log(1);
    console.log(2);
    console.log(3);
    

    这个没什么问题,浏览器打印的是 1 2 3 timer
    但是这样呢

    setTimeout(function(){
        console.log('timer');
    }, 0);//0延时
    console.log(1);
    console.log(2);
    console.log(3);
    

    浏览器打印依然打印的是 1 2 3 timer
    也许有同学知道,旧版浏览器,setTimeout定时至少是10ms(即便你设置了0ms),
    H5新规范是定时至少4ms,改变DOM也是至少16ms
    我们可以暂且认为是这个原因
    那么我再改动一下代码

    setTimeout(function(){
        console.log('timer');
    }, 0);
    var a = +new Date();
    for(var i = 0; i < 1e5; i++){
        console.log(1);
    }
    var b = +new Date();
    console.log(b - a);
    

    这回够刺激了吧,输出10w次,我浏览器都假死了(心疼我chrome)
    不仅如此,我还打印了循环所用时间
    来看看控制台

    输出了10w个1,用了将近7s
    timer依然在最后打印
    这就证明了我前面说的话: 同步任务总是会在异步任务之前执行
    只有我执行栈空了,才会去你任务队列中取任务执行
    #实例#
    最后我举一个例子加深一下理解

    demo.onclick = function(){
        console.log('click');
    }
    function foo(a){
        var b = 1;
        bar(200);
    }
    function bar(c){
        var d = 2;
        click//伪代码 此时触发了click事件(这里我假装程序运行到这里手动点击了demo)
        setTimeout(function(){
            console.log('timer');
        }, 0);
    }
    foo(100);
    

    怕大家蒙我就不写Ajax了
    Ajax如果处理结束后(通过Http请求线程),也会将回调函数放在任务队列中
    还有一点click那一行伪代码我最开始是想用demo.click()模拟触发事件
    后来在测试过程中,发现它好像跟真实触发事件不太一样
    它应该是不通过触发事件线程,而是存在于执行栈中,就相当于单纯地执行click回调函数
    不过这只是我自己的想法有待考证,不过这不是重点,重点是我们理解这个过程,请大家不要吐槽我╰( ̄▽ ̄)╭

    下面看看执行这段代码时发生了什么(主要说栈和队列的问题,不会赘述预编译过程)

    • 主线程开始执行,产生了栈、堆、队列
    • demo节点绑定了事件click,交给事件触发线程异步监听
    • 执行foo函数(之前同样有预编译过程),创建了帧包括foo函数的形参、局部变量压入执行栈中
    • foo函数内执行bar函数,创建帧包括bar函数的形参、局部变量压入执行栈中
    • 触发了click事件,事件触发线程将回调事件处理函数放到js线程的任务队列中
    • 触发了定时器事件,事件触发线程立即(4ms)将回调处理函数放到js线程的任务队列中
    • bar函数执行完毕,弹出栈
    • foo函数执行完毕,弹出栈
    • 此时执行栈为空
    • 执行栈向任务队列中获取一个任务执行:click回调函数,输出‘click’
    • 执行栈向任务队列中获取一个任务执行:定时器回调函数,输出‘timer’
    • 执行结束

    这里从任务队列里不断取任务的过程就是Event Loop

    Event Loop

    有一些我的理解,如果发现不对或者有疑问的地方,call我
    相信大家看了这个例子应该会·对js底层运行机制有一个大概的了解

    主页传送门

    展开全文
  • 基于IE内核的标签浏览器开发过程

    千次阅读 热门讨论 2014-03-23 00:16:40
    By Fanxiushu 2014 转载或引用请注明原作者 在Windows平台做IE内核浏览器,可以非常简单,拖拖控件就能形成一个简单的
  • 【综合篇】浏览器的工作原理:浏览器幕后揭秘

    千次阅读 多人点赞 2020-01-21 18:18:13
    web(给达达前端加星标,提升前端技能)了解浏览器是如何工作的,能够让你站在更高的角度去理解前端浏览器的发展历程的三大路线,第一是应用程序web化,第二是web应用移动化,第三是web操...
  • 浏览器工作原理详解

    万次阅读 多人点赞 2017-09-14 17:07:32
    她在查阅了所有公开发布的关于浏览器内部机制的数据,并花了很时间来研读网络浏览器的源代码。她写道: 在 IE 占据 90%市场份额的年代,我们除了把浏览器当成一个“黑箱”,什么也做了。但是现在,开放源...
  • 但很时候,无论出于数据分析或产品需求,我们需要从某些网站,提取出我们感兴趣、有价值的内容,但是纵然是进化到21世纪的人类,依然只有两只手,一双眼,可能去每一个网页去点去看,然后再复制粘贴。...
  • 1. 客户端与浏览器

    千次阅读 2012-12-02 10:39:24
    一方面觉得感兴趣的人不会很,这些理解和思考只有对Notes背后的技术好奇的人才会感兴趣。即使是开发人员,知道或者没有考虑过这些也可以设计出良好的程序。另一方面是对自己的这些思考的水平要求高,总想着在...
  • 浏览器解码与xss

    千次阅读 热门讨论 2018-06-06 10:09:02
    一个软件,但是由于它的一些特性,经常会出现很的问题,这让开发人员很是头疼,所以今天我们就站在安全的角度(解码)来深度剖析一下浏览器的工作原理。 也是对这么久以来查询的资料的一个总结。 浏览器组成 ...
  • 浏览器如何工作

    千次阅读 2012-02-02 09:32:47
    浏览器可以被认为是使用最广泛的软件,本文将介绍浏览器的工 作原理,我们将看到,从你在地址栏输入google.com到你看到google主页过程中都发生了什么...今天,有五种主流浏览器——IE、Firefox、Safari、Chrome及Op
  • 在过去的几年中,她查阅了所有公开发布的关于浏览器内部机制的数据(请参见资源),并花了很时间来研读网络浏览器的源代码。她写道: 在 IE 占据 90% 市场份额的年代,我们除了把浏览器当成一个“黑箱”,什么也...
  • 浏览器与cdn缓存

    万次阅读 2016-05-24 15:41:29
    关于是否缓存,是浏览器缓存还是CDN缓存,缓存保留长时间,由cache-control控制 请求: 响应: 特别说明:  Cache-control: public 表示缓存的版本可以被代理服务器或者其他中间服务器识别(浏览器,cdn都能...
  • 浏览器重发请求问题

    千次阅读 2020-06-18 21:53:00
    而 POST 请求用于更改(增、删、改)服务器上的资源,会产生一定的副作用。 所以,这样看起来,浏览器是不是就不会因为网络原因啥的自动重发 POST 请求吧?实际上是这样么? 起因 最近在对接地图的一个数据录入...
  • 我们知道浏览器中的JavaScript是单线程执行的,如果遇到重量级的计算任务时,一般需要运行很长时间,这样就会造成UI任务来不及响应,带来不好的用户体验。那么如何在JavaScript中使用线程来解决这类问题呢?HTML5...
  • 网站用户行为数据收集和分析方法

    千次阅读 2016-10-25 23:07:15
    综合介绍了目前国内外对于用户行为数据收集和分析方法所进行的研究, 各种方法的特点, 并介绍一些利用相应方法所开发出的工具实例, 使得建设的网站更加符合用户的需要, 以保障用户与网站之间沟通的顺畅。 随着In ...
  • 浏览器中GET和POST请求的区别

    万次阅读 多人点赞 2016-09-10 03:08:44
    一般我们在浏览器输入一个网址访问网站都是GET请求;在使用FORM表单提交数据中,可以通过设置Method指定提交方式为GET或者POST提交方式,默认为GET提交方式 HTTP定义了与服务器交互的不同方法,其中最基本的四种:...
  • 用户输入网址(假设是个html页面,并且是第一次访问),浏览器向服务器发出请求,服务器返回html文件;  2. 浏览器开始载入html代码,发现标签内有一个标签引用外部CSS文件;  3. 浏览器又发出CSS文件的请求,...
  • 浏览器访问网页流程

    千次阅读 2018-03-05 11:23:58
    换句话说,一张网页,要经历怎样的过程,才能抵达用户面前?下面来从一些细节上面尝试一下探寻里面的秘密。前言:键盘与硬件中断说到输入URL,当然是从手敲键盘开始。对于键盘,生活中用到的最常见的键盘有两种:...
  • 候鸟防关联浏览器原理

    千次阅读 2021-04-20 23:51:55
    最近 google fi 出问题,收到了不少朋友咨询关于 I P 方面,有没有替代的解决...因为市场上自养号测评系统太多了,尤其是 PC 端的自养号操作,形形色色(虽然本质都离开测评大系统五大维度),很多朋友正在使用的测
  • 浏览器渲染原理及流程

    千次阅读 2019-08-29 20:20:41
    大多数设备的刷新频率是60Hz,也就说是浏览器对每一帧画面的渲染工作要在16ms内完成,超出这个时间,页面的渲染就会出现卡顿现象,影响用户体验。前端的用户体验给了前端直观的印象,因此对B/S架构的开发人员来说,...
  • 前端必经之路:浏览器底层工作原理

    千次阅读 多人点赞 2019-06-27 16:48:45
    不过,大多数人并知道,在我们打开浏览器并浏览一个网页时,浏览器底层具体进行了什么工作。今天我将带领大家,逐步剖析我们在使用浏览器访问网页的过程中,浏览器究竟为我们做了些什么。 在进入正文之前,先给...
  • 浏览器相关(持续更新)

    千次阅读 2021-02-21 02:00:23
    用户界面、浏览器引擎、呈现引擎、网络、用户界面后端、JavaScript解释器、数据存储。 2、本地存储 1、cookie简单介绍 cookie一般被问简单的介绍,然后cookie的设置、读取以及是否自动携带的问题。通常在页面中会...
  • 浏览器与CDN缓存

    千次阅读 2017-08-04 10:17:43
    关于是否缓存,是浏览器缓存还是CDN缓存,缓存保留长时间,由cache-control控制 请求: 响应: 特别说明:  Cache-control: public 表示缓存的版本可以被代理服务器或者其他中间服务器识别...
  • 浏览器内部工作原理

    千次阅读 2016-09-14 13:48:02
    一、介绍  浏览器可以被认为是使用最广泛的软件,本文将介绍浏览器的工作原理,我们将看到,从你在地址栏输入google.com到你看到... 本文将基于一些开源浏览器的例子——Firefox、Chrome及Safari,Safari是部分开源的
  • 各大浏览器兼容性问题

    千次阅读 2015-09-09 12:10:07
    浏览器内核简介   Trident IE浏览器(GreenBrowser绿色浏览器, 遨游浏览器....都是IE) Geckos FireFox Presto Opera Webkit ...
  • 浏览器的工作原理:新式网络浏览器幕后揭秘 前端工程  裕波 2014-07-07 7230 访问  5  分享  微信分享 本文转载自html5rocks,考虑到有时候html5rocks方便访问,所以转载过来。 ...
  • Web浏览器中的JavaScript

    千次阅读 2011-11-29 19:28:35
    第二部分包括第13章到第23章的内容,描述了Web浏览器中实现的JavaScript。在这些章节中引入了大量可脚本化的对象,这些对象用于表示Web浏览器和HTML及XML文档的内容。  第13章,Web浏览器中的JavaScript  第14章...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 105,628
精华内容 42,251
关键字:

不产生太多用户数据的浏览器