<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[向东博客 专注WEB应用 构架之美 --- 构架之美，在于尽态极妍 | 应用之美，在于药到病除]]></title> 
<link>https://jackxiang.com/index.php</link> 
<description><![CDATA[赢在IT，Playin' with IT,Focus on Killer Application,Marketing Meets Technology.]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[向东博客 专注WEB应用 构架之美 --- 构架之美，在于尽态极妍 | 应用之美，在于药到病除]]></copyright>
<item>
<link>https://jackxiang.com/post//</link>
<title><![CDATA[嵌入式浏览器开发]]></title> 
<author>jack &lt;xdy108@126.com&gt;</author>
<category><![CDATA[WEB2.0]]></category>
<pubDate>Tue, 11 Aug 2009 03:13:49 +0000</pubDate> 
<guid>https://jackxiang.com/post//</guid> 
<description>
<![CDATA[ 
	<p>来源：<a href="http://blog.csdn.net/wushuan10141/archive/2008/07/31/2747790.aspx">http://blog.csdn.net/wushuan10141/archive/2008/07/31/2747790.aspx</a><br />摘&nbsp; 要</p><p></p><p>&nbsp;&nbsp;&nbsp; 本文主要论述了嵌入式浏览器开发所涉及到的技术，对HTML词法分析、浏览器JavaScript支持、linux和gtk编程等做了详细的分析和介绍，并对Netbit浏览器的开发进行了介绍，分析了主要的模块的功能和组成，并给出了主要算法。</p><p></p><p>还对典型的嵌入式浏览器进行了分析，包括Gzilla,Mozilla,Thunder和zen,总结了浏览器设计的技术要点和开发策略。</p><p></p><p>另外，在论文开头，介绍了目前嵌入式浏览器产品的发展现状，嵌入式浏览器的组成和各部份功能说明。</p><p></p><p>&nbsp;&nbsp;&nbsp; HTML词法分析是着重讨论的技术问题，给出了详尽的数据结构，程序流程。</p><p></p><p>&nbsp;&nbsp;&nbsp; Netbit浏览器的开发，是本论文的重点，分模块进行了分析，提出了需要改进的地方，并给出了改进措施。</p><p></p><p></p><p></p><p>关键词：嵌入式浏览器，HTML词法分析，Javascript，linux，gtk</p><p></p><p>ABSTRACT</p><p></p><p>This paper is focused on the technology of embedded browser.Html scanner, JavaScript support for browser, linux and gtk programming are deeply discussed.and introduce the development of Netbit browser, analyse the main module of Netbit, the algorithm is presented. </p><p></p><p>what&rsquo;s more, other typical embedded browsers are analysed, include Gzilla,Mozilla,Thunder and zen，and summarize the important technology and the policy of browser development</p><p></p><p>In addition,at the head of the paper,introduce the present status of embedded browser product,the general form of embedded browser and description of the main parts.</p><p></p><p>HTML scanner is a important technological problem, the data structure and the flow of the program are presented.</p><p></p><p>The development of Netbit Browser is mainly discussed,and analysed by module,give the opinion to improve it.</p><p></p><p></p><p></p><p>KEY WORDS:</p><p>embedded browser,HTML token,Javascript,linux,gtk</p><p></p><p>目 录</p><p></p><p>引言&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;5</p><p>第一章 嵌入式浏览器设计概述 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip; 6</p><p>1.1目前嵌入式浏览器产品的发展现状 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip; 6</p><p>1.2嵌入式浏览器的组成和各部份功能说明 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip; 8</p><p>1.3开发难度分析 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip; 9</p><p>1.4开发的前期策划 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip; 10</p><p></p><p>第二章 HTML词法分析器的设计及其应用 &hellip;&hellip;&hellip;&hellip;&hellip; 12</p><p>2.1 Bit Token的组成及其功能 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;12</p><p>2.2 数据结构 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;12</p><p>2.3 算法 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;13</p><p>2.4词法分析结果示意&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;15</p><p>2.4 HTML词法分析的应用 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;15</p><p></p><p>第三章 浏览器JavaScript支持的实现 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;18</p><p>3.1基本的JavaScript 开发环境 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;18</p><p>3.2 JavaScript Engine &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;18</p><p>3.3 JavaScript与浏览器接合 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip; 19</p><p>3.4 浏览器消息响应 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip; 21</p><p></p><p>第四章 Linux程序开发技术及 GTK图形程序开发&hellip;&hellip; 23</p><p>4.1 Linux下的程序开发环境 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;23</p><p>4.2 GTK图形程序开发 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;29</p><p></p><p>第五章 Netbit浏览器开发与分析 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip; 31</p><p>5.1 Netbit 浏览器简介 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip; 31</p><p>5.2 Netbit Browser浏览器各部份的功能 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip; 31</p><p>5.3 界面模块&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;32</p><p>5.4 控制模块&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;34</p><p>5.5词法分析模块&hellip; &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;34</p><p>5.6使用PIXMAP的画图模块&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;35 </p><p>5.7 文本文件的显示模块&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;36</p><p>5.8 HTML文件的显示模块 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip; 37</p><p>5.9 Netbit实际应用效果及比较 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip; 40</p><p>5.10 Netbit Browser的缺点分析及改进办法 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;44</p><p>5.11 Netbit Browser未来的展望 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;48</p><p></p><p>第六章&nbsp; 其它浏览器分析&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip; 50</p><p>6.1&nbsp; Dillo(Gzilla)浏览器分析 &hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip; 50</p><p>6.2 Thunder浏览器分析&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;55</p><p>6.3 Mozilla浏览器分析&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;56</p><p>6.4 ZEN浏览器分析&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;57</p><p>6.5 浏览器分析工作的总结&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;58</p><p></p><p>致&nbsp; 谢&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip; 60</p><p>参考文献表&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;61</p><p></p><p>引&nbsp; 言</p><p></p><p>随着计算机、网络技术的迅猛发展，新型的高科技含量的电子产品层出不穷，遍及家电、生产、军事等各个领域。嵌入式系统因其体积小，速度快而受到重视。在掌上电脑、PDA、手机、导航设备、信息家电领域嵌入式系统被广泛采用，这也是近期产品研发的热点。嵌入式浏览器作为重要的嵌入式系统应用软件，是不可或缺的。但目前，嵌入式浏览器产品的市场被少数国外大公司垄断，众多的小型嵌入式浏览器也尚处于发展期，而国内的自主技术的嵌入式浏览器更屈指可数，刚刚进入实用阶段。迫切需要更多的人来关注嵌入式浏览器技术，开发中国自主知识产权的浏览器产品。</p><p></p><p>本文阐述了嵌入式浏览器开发的关键技术，并详细说明Netbit Browser的实际开发过程，技术细节。并以网站的方式公布了其源代码，为国内嵌入式浏览器的发展做了有益的尝试。</p><p></p><p></p><p>第一章 嵌入式浏览器设计概述</p><p></p><p>1.1目前嵌入式浏览器产品的发展现状</p><p></p><p>1.1.1桌面浏览器简介及其与嵌入式浏览器的区别：</p><p>常见的桌面浏览器有微软的Internet Explorer，网景的Netscape，开放源码的Mozilla，以及opera，mosaic，Hotjava，字符模式的lynx等。其中IE，Opera已有嵌入式版本，以上的大多数浏览器均有跨多平台的版本。这些桌面型浏览器功能参差不齐，但大多能实现基本的浏览功能。其中IE与Netscape对HTML页面的显示效果均堪称完美（对于XML，WML等标准的网页浏览，本文不予讨论）。</p><p></p><p>与完美的浏览效果，功能丰富，支持多种媒体格式和视音频插件等优点形成对立的是，这些浏览器庞大的体积和对机器配置的高要求。这也是桌面浏览器和嵌入式浏览器最大的区别。另外，桌面型浏览器通常是基于通用的操作系统，用于桌面电脑如PC机、MAC机等。而嵌入式浏览器通常基于专用系统。</p><p></p><p>需要指出的是，我们在网上常看到的一些所谓的优秀浏览器，如Netcaptor，魔装网神，腾讯，飓风，Fantasia，金山WPS office的浏览器等等，均为使用IE内核的浏览器，使用了多页面多线程的方式，降低了系统资源的耗费，方便了浏览，但软件本身没有多少技术可言，可以用Delphi，VB等工具轻易开发出来（如果不要求完善的功能，只需简单到拖一个IE控件过去就行了），这也体现了微软的COM+及ActiveX技术的强大。</p><p></p><p>1.1.2常见嵌入式浏览器</p><p>(1)Internet Explorer for WinCE，是IE的Windows CE版，这也意味着用户为了使用它，必须选用基于WinCE平台的产品，随着微软势力在掌上终端领域的蔓延，WinCE版的IE必将大行其道。</p><p></p><p>(2)Access Netfront，Access是日本的浏览器大厂，占据了大部份的日本浏览器市场，其产品广泛应用于手持设备、机顶盒、游戏机等产品。其新推出的Netfront Linux版本可以免费下载，网址是：<a href="http://www.access.co.jp/product/develop">http://www.access.co.jp/product/develop</a>。经试用，发现Netfront浏览效果非常好，对中文的支持和表格的显示效果，甚至超过Netscape 4.6 for Linux。而且支持键盘操作。可以使用箭头键来选择网址进行跳转。但显然为了简化设计，Netfront使用了一定的权宜手段。比如它不支持使用&lt;font&gt;标记来设置字体，也不支持样式表，所有字体的大小是固定的，但一般情况不易发现，不过由于Linux版本不是商业性版本，无法就此推断其他版本的特性。Netfront Linux版大小为1.9M，如再加以简化（比如去掉一些图片按钮）可以做的更小。</p><p></p><p>(3)Gzilla，现名Dillo，是基于Linux，使用GTK作为GUI平台开发的浏览器，其开发目的也包含嵌入式的应用。是开放源码项目。我们在设计Netbit Browser时主要参考了该浏览器的界面设计。该浏览器的缺点是不支持表格，布局过于简单，不支持中文，浏览效果比较差，优点是网络功能完善，支持cache缓冲，能够支持多种图片格式。网址是：<a href="http://www.gzilla.com/">http://www.gzilla.com</a>。</p><p></p><p>(4)Viewml，使用FLTK作为GUI平台开发的浏览器，主要面向嵌入式的应用。是开放源码项目。使用C++面向对象的设计。因为使用FLTK，所以可以在嵌入式的Micro Window平台运行，该浏览器支持简单的表格，界面还没有做（只有一个主窗口），不支持中文，由于FLTK的BUG比较多，我们在实际编译时经常出错，即使编译通过，也常常是还没运行就core dump了，但曾经由某人编译成功，基本可用，网址是：<a href="http://www.gzilla.com/">http://www.gzilla.com</a>。</p><p></p><p>(5)ZEN，一个并不知名的嵌入式浏览器，但却十分优秀，开放源码项目，不仅支持表格，支持多种图像格式，而且在设计上很有特色，将涉及到GUI平台的部分独立出来，用户可以指定不同的GUI平台运行，内置了对字符，SVGA和GTK的支持，开发这只需按照相应规范编写该软件的涉及GUI的部份，就可以将其移植到其它GUI平台，而无须对整个软件进行修改。缺点是网络功能比较弱，不支持多线程，另外其必须等到所有图片下载完毕才开始布局，若某文件传输失败则会导致死锁，需用户中断。网址是：<a href="http://www.nocrew.org/software/zen/">http://www.nocrew.org/software/zen/</a>。</p><p></p><p>以上主要介绍了开放源码的嵌入式浏览器，因为其对本项目具有实际的参考价值。</p><p></p><p>1.1.3&nbsp; 嵌入式浏览器的应用领域</p><p>嵌入式浏览器主要应用于手持终端和信息家电，例如掌上电脑、3G手机、网络机顶盒、具有网络功能的其它家电，如可上网的电冰箱等，另外，在军事等领域也有一定的应用需求。</p><p></p><p>1.2嵌入式浏览器的组成和各部份功能说明</p><p>下面简介主要的组成部份，其设计原理后面将进行专门论述。</p><p>1.2.1 词法分析</p><p>主要是针对HTML进行词法分析，该词法分析器实际是通用的，还可用于XML、WML的词法分析，只需替换元素名称即可。</p><p></p><p>HTML词法分析是浏览器设计的基础环节之一，也是整个设计过程中重要的前端工作，其数据结构的拟定与接下来的语法分析和布局算法密切相关，词法分析的效率与准确性、容错性也关系到整个浏览器设计的质量。</p><p></p><p>1.2.2语法分析</p><p>在大型的浏览器中，语法分析通常是独立的模块，但在嵌入式浏览器中，语法分析通常是与布局混合在一起的，即一边进行语法分析，一边布局，不设立相应的数据结构来存储语法分析的结果。语法分析对布局的算法有直接的指导意义。</p><p></p><p>1.2.3布局</p><p>本文中的布局应理解为界面输出前的预演和处理位置等信息的算法，而不是具体的画图和界面输出。布局算法的好坏直接关系到界面的输出效果，但布局算法也是要根据GUI所能实现的输出效果来设计的，所以，GUI往往成为系统的瓶颈，如果GUI提供的支持有限，即使有再好的布局算法，也是纸上谈兵，无用武之地。</p><p></p><p>1.2.4&nbsp; GUI及软件界面</p><p>GUI是指图形用户界面，浏览器无疑是一个图形函数库的大需求者，选择好的GUI环境实属不易，为了能够实现专有的功能，浏览器开发者往往还要对基础的GUI库进行包装、改写，甚至写自己的控件。所有这些，是需要有专业的GUI库开发经验的专职人员来完成。</p><p></p><p>GUI的功劳第一在于软件的操作界面，如窗口、菜单、工具条、滚动条等。第二在于主浏览窗体中的图形元素的输出（通常以自画控件的方式）。前者通常直接使用GUI库中的已有控件，编程难度很小，后者主要利用基本的点线函数，编程难度比较大。</p><p></p><p>1.2.5&nbsp; JavaScript支持</p><p>实际上JavaScript的编译器是开放源码的，而且全世界通用，包括IE、Mozilla、Netscape等浏览器都使用相同的JavaScript的编译器，但为何其对JavaScript的支持不尽相同呢，这是因为JavaScript在浏览器里主要是用于操纵界面元素和与浏览器内部命令进行结合。这实际上是程序员努力的结果，而不是JavaScript编译器的功劳。这其中的机制将在后面专门介绍。</p><p></p><p>1.2.6&nbsp; IO</p><p>主要是指通过HTTP协议或文件系统来读取网页内容，网络部份超出了本论文的讨论范围，不予详述，后面将介绍与网络部份的接口。</p><p></p><p>以上主要按工作的性质进行分类，介绍了浏览器设计的主要几个部份，但与实际开发工作的人员划分不同，实际工作要考虑人员的实际能力和经验，结合要实现的目标，对各部份的投入也有轻重缓急之分，比如，如果没有词法分析的先行实现，语法分析与布局都是纸上谈兵。GUI平台不确定，布局工作也无法展开。</p><p></p><p>1.3开发难度分析</p><p>我们也可以看到，要设计效果较好的浏览器产品，决不是三五个人、两三条枪就能办到的，参与工作的人员不仅需要有相关经验，还需要有一定的投入，一方面对程序员艰苦的工作给予鼓励，另一方面也可能需要购买一些成熟的开发包，在基础的开发平台、测试平台上也要有一定的投资，总的来说，开发自主技术的浏览器产品是有一定的风险的，这也是国内目前相关产品为数不多的原因。国外大公司在相关领域的科研与实践已很成熟，在大量投入之后，其产品已为广大用户所接受，国内的企业若从零做起，也很难追赶。因而买断技术，加以改进也可以成为快速成长的一条道路。</p><p></p><p>1.4开发的前期策划</p><p>以下的工作均是建立在大量的调查基础上的。是浏览器开发前期准备工作所解决的问题。</p><p></p><p>1.4.1&nbsp; 平台的选取</p><p>目前的商业化嵌入式操作系统功能完善，但使用费用高昂，使用Linux内核的嵌入式操作系统不仅功能强大，便于开发移植，而且费用低廉，基于Linux的嵌入式系统已日渐成熟，也有相应的窗口系统可以使用，我们使用PC上的Linux来进行浏览器开发，是再好不过的了，软件从PC移植到嵌入式设备只需重新编译一下就可以了（前提是使用的函数库、开发包同时具有PC和嵌入式的版本）。</p><p></p><p>1.4.2&nbsp; GUI的选取</p><p>GUI的选取是颇费脑筋的问题，一方面要考虑嵌入式系统是否有对应的版本，一方面要考虑该GUI的功能是否足够实用。</p><p></p><p>目前常见的基于Linux的GUI开发包主要有GTK、FLTK、QT、KDE等，其中FLTK有Micro Window的版本，是比较好的选择，但使用FLTK开发的软件还比较少，对于初学者进入的难度更大，而且目前BUG较多，而GTK比较成熟，使用GTK的软件比比皆是，资料也很丰富，我们的参考对象Gzilla也使用GTK，也就是说如果使用GTK，将有很好的原型进行参考，这使得GTK对于目前毫无GUI开发经验的浏览器开发组而言，充满了巨大的诱惑。考虑到GUI是可以成为独立模块的，如浏览器ZEN的设计，我们决定使用GTK，待时机成熟可换用其它平台，也并非难事。</p><p></p><p>在前期，我们也对FLTK加以了研究，还尝试了以FLTK为基础构建GUI中间层的办法，但由于工作量巨大，初步估算需2万行程序，而不得不暂缓该计划。建立GUI中间层在理论上是可行的，这样，在必要时可以釜底抽薪，替换掉基础的GUI平台，应用软件完全不需修改，但中间层与基础层的接口要重写，其工作量也很大，未必是核算的买卖。</p><p></p><p>1.4.3&nbsp; 确定要实现的基本功能</p><p>鉴于对浏览器开发难度的充分考虑，以及现有人员的水平，拟定实现以下功能，以及需要考虑但暂不予实现的功能。</p><p></p><p>需要实现的包括：</p><p>(1) 界面：包括窗口，菜单，输入框，工具条，滚动条等的支持。</p><p>(2) 词法分析：必须实现实用的HTML词法分析，支持HTML4.0全部元素。</p><p>(3) 实现简单网页的布局：实现对简单网页的查看。</p><p>(4) 支持基本IO，支持采用线程的网络传输。</p><p></p><p>需要考虑的功能：</p><p>(1) JavaScript支持</p><p>(2) 汉字支持</p><p>(3) 图片格式支持</p><p>(4) 表单支持</p><p>(5) 页面元素的消息响应</p><p></p><p>1.4.4&nbsp; 人员分工</p><p>由于情况的变动，造成了人员比较紧张，在前期准备工作中，人力充沛，使得收集的资料比较完备，打下了较好的基础。在后期简化了目标，虽然人员减少，但也能够实现主要的工作。考虑到网络是比较独立的部份，把它分出去由专人负责。</p><p></p><p></p><p>第二章 HTML词法分析器的设计及其应用</p><p></p><p>HTML词法分析是浏览器设计的基础环节之一，也是整个设计过程中重要的前端工作，其数据结构的拟定与接下来的语法分析和布局算法密切相关，词法分析的效率与准确性、容错性也关系到整个浏览器设计的质量。</p><p></p><p>下面将介绍一个HTML词法分析器&mdash;&mdash;Bit Token的设计思路。</p><p></p><p>Bit Token是Netbit Browser的HTML词法分析器，使用标准C编程，Netbit Browser是基于Linux/Gtk的浏览器，开放源码项目，网址是<a href="http://netbit_browser.myetang.com/">http://netbit_browser.myetang.com</a>。</p><p></p><p>2.1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bit Token的组成及其功能</p><p>Bit Token作为Netbit Browser的词法分析部份，负责对接收的HTML代码进行词法分析，主要的目的是提取网页中元素的名称及其属性，并以恰当的形式（即按一定的数据结构）加以保存，也就是完成了将数据流离散化、结构化的过程。</p><p></p><p>主要由以下几个部分组成：</p><p>1、初始化：完成对数据结构的初始化，主要是分配内存，变量赋初值。</p><p>2、主体的数据流分析：逐字符的进行判断，确定数据的归属类型。</p><p>3、元素的分析：提取元素的名称、属性和值域。</p><p>4、释放：主要是对内存的释放。</p><p>2.2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 数据结构</p><p>typedef struct BitTokenContext</p><p>&#123;char * strBuffer;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //当前正在处理的HTML代码</p><p>int&nbsp;&nbsp;&nbsp; bufferLength;</p><p>int&nbsp;&nbsp;&nbsp; curPosition;</p><p>char * global_strBuffer;&nbsp;&nbsp;&nbsp; //全局HTML代码</p><p>int&nbsp;&nbsp;&nbsp; global_bufferLength;</p><p>int&nbsp;&nbsp;&nbsp; global_curPosition;</p><p>BitTokenList *tokenList;&nbsp;&nbsp;&nbsp; //元素节点链表</p><p>BitTokenList *tokenList_tail;</p><p>BitPTagList pTagList;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //元素名称表，指向静态数据</p><p>&#125;BitTokenContext,*BitPTokenContext;</p><p></p><p>BitTokenContext是用于存储当前待分析网页全局属性的数据结构，其中TokenList是核心的元素节点链表。词法分析的目的就是生成这样一个链表。下面给出该链表的数据结构，是很简单的双向链表。</p><p></p><p>typedef struct TokenList</p><p>&#123; BitToken *token;&nbsp; //元素节点</p><p>&nbsp;struct TokenList *priou;</p><p>&nbsp;struct TokenList *next;</p><p>&#125;BitTokenList,*BitPTokenList;</p><p></p><p>以下是元素节点的数据结构：</p><p></p><p>typedef struct BitToken</p><p>&#123;int type;&nbsp; //节点类型，如定义的HTML_BODY，HTML_TXT等。</p><p>char *pData; //如果是HTML_TXT型元素，则为其内容，否则为空</p><p>BOOL end;&nbsp;&nbsp;&nbsp; //是否是结束元素，如&lt;/body&gt;</p><p>BitTokenAttrList *attrList; //元素属性链表，因为可能有多个属性，所以使用链表存储</p><p>BitTokenAttrList *attrList_tail;</p><p>&#125;BitToken,*BitPToken;</p><p></p><p>请注意，以上出现tail标记的指针变量，如BitTokenList * tokenList_tail等，其作用是用于保存链表结尾节点指针，便于在释放内存时，直接找到链尾，提高了算法的效率。</p><p></p><p>2.3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 算法</p><p>2.3.1 基本算法：</p><p></p><p>首先介绍基本的算法：</p><p>(1) 从存储网页的字符串中，顺序读入一个字符</p><p></p><p>(2) 如果遇到 &lt; ，认为遇到TAG（元素），处理该元素，使用函数Token_ConsumTag()，处理完毕后，指针移到该元素尾。</p><p>&nbsp;&nbsp;&nbsp; </p><p>(3) 如果遇到回车、空格，则跳过。</p><p></p><p>(4) 如果遇到 &gt; ，则跳过(不应该出现此情况，为了容错)。</p><p>&nbsp;&nbsp;&nbsp; </p><p>(5) 如果非以上情况，则认为遇到文字，处理这段文字，使用函数Token_Consum_PlainText()。处理完毕，指针指向下一个元素首。</p><p></p><p>(6) 循环以上操作，直到该网页分析完毕。</p><p>&nbsp;&nbsp;&nbsp; </p><p>由此看来，主算法十分简单而清晰，主要是Token_ConsumTag()和Token_Consum_PlainText()这两个函数起关键作用，由于其中涉及到许多细节问题，此处不予详述。</p><p></p><p>2.3.2 算法效率与改进：</p><p>采用以上的基本算法，是可用的，但当网页比较大的时候，比如600K，该算法的效率成倍下降，这主要是由于要处理的字符串太大，在内存中完成查找、替换、复制、移动等操作，响应时间明显下降。对此的改进办法就是分段进行词法分析，不仅极大的提高了效率（在某些情况下约提高30倍），也有利于浏览器整体设计，因为当网页较大时，若等待全部内容传输完毕，再一次性完成词法分析和布局，用户会感到等待时间过长，一般现在成熟的浏览器都采用边传输，边分析，边显示。</p><p></p><p>分段进行词法分析的算法复杂度明显增加，比如，当每段定为1024字节，在第1024字节处，可能正好将一个完整元素截断，按常规分析方法会造成错误。解决的办法是，采用回溯，确认要分析的部份至少包含1个完整元素。</p><p></p><p>具体做法是：判断1024字节处是否为元素结束字符 &lsquo;&gt;&rsquo;，如果不是，则判断前一个字节，直到找到元素结束字符为止，这样可保证至少包含一个元素。</p><p></p><p>采用分段进行词法分析，实际每次分析的代码会不足1024字节，余下的部份汇入到下一段的分析过程即可，直到所有内容被分析完毕。</p><p></p><p>2.4词法分析的结果</p><p>下面是一段很简单的HTML代码。</p><p>&lt;html&gt;</p><p>&lt;img src=&ldquo;go.gif&rdquo; width=200 height=100&gt;</p><p>&lt;a HREF=&quot;<a href="http://www.263.net/">http://www.263.net</a>&quot;&gt;首都在线&lt;/a&gt;</p><p>&lt;/html&gt;</p><p>分析后，数据存储结构如下</p><p>：</p><p>&lt;img&gt;<br />&nbsp;<br />&lt;a&gt;<br />&nbsp;<br />text<br />&nbsp;<br />&lt;/a&gt;<br />&nbsp;<br />src<br />&nbsp;<br />go.gif<br />&nbsp;<br />width<br />&nbsp;<br />200<br />&nbsp;<br />height<br />&nbsp;<br />100<br />&nbsp;<br />href<br />&nbsp;<br /><a href="http://www.263.net/">http://www.263.net</a><br />&nbsp;<br />data<br />&nbsp;<br />首都在线<br />&nbsp;</p><p></p><p>可以看到，词法分析的结果是一个元素节点链表，每个节点的属性也形成了一个链表，元素节点是有先后顺序的，元素属性的先后顺序是无所谓的。</p><p></p><p>词法分析将网页的文本数据流以清晰的结构表现出来，这样，在后面的应用中就可以很容易的遍历各节点，并轻松地获得各元素节点的属性。</p><p>2.5 HTML词法分析的应用</p><p>2.5.1 应用举例：</p><p>HTML词法分析程序通常应用于浏览器设计、网页制作软件设计等领域，本人以一个使用VC开发的软件&ldquo;HTML智能分析&rdquo;来举例说明，下载网址：</p><p><a href="http://netbit_browser.myetang.com/introduce.html">http://netbit_browser.myetang.com/introduce.html</a>。</p><p></p><p>&ldquo;HTML智能分析&rdquo;同样使用Bit Token词法分析器，&ldquo;HTML智能分析&rdquo;是一个网页信息提取、处理软件。</p><p></p><p>具有以下主要功能：</p><p>1、智能提取网页中的文字信息，智能排版，并可在进行编辑后保存。</p><p>2、统计网页的有关信息。</p><p>3、根据用户设置的版式，将分析和编辑的结果，自动生成新的网页。</p><p></p><p>用户可使用该软件来将HTML转为TXT格式，其对HTML中文字内容的提取准确、快速、不含冗余信息，版式工整清晰，保持本来面貌。</p><p></p><p>其主要设计思路是，在Bit Token词法分析器的基础上，结合浏览器布局的基本算法，对影响到TXT版面效果的元素进行处理。</p><p></p><p>比如&lt;PRE&gt;标记，代表所包含的内容浏览器应不予分析，按TXT格式输出，而如表格&lt;TR&gt;等元素则意味着需要换行。而在HTML中，在无&lt;PRE&gt;这种特殊情况时，回车都是忽略不记的。这就造成了矛盾。使用常规的简单算法进行HTML到TXT的转换无法解决这些问题。造成转换后的版式&ldquo;失真&ldquo;。而&ldquo;HTML智能分析&rdquo;却能很好的解决。</p><p></p><p>由于&ldquo;HTML智能分析&rdquo;使用了底层的词法分析技术，还可以很容易的过滤掉&lt;SCRIPT&gt;与&lt;STYLE&gt;(样式表)。并可以对网页中的元素进行统计和语法校验。</p><p></p><p>以下是该程序的片断：</p><p>&nbsp;&nbsp;&nbsp; pTtokenList=global_cx-&gt;tokenList; //取首节点</p><p>&nbsp;&nbsp;&nbsp; while(pTtokenList!=NULL) //循环直至处理完所有节点</p><p>&nbsp;&nbsp;&nbsp; &#123;</p><p>&nbsp;&nbsp;&nbsp;&nbsp; switch(pTtokenList-&gt;token-&gt;type)</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;//根据节点类型，做不同的处理</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case HTML_TITLE: &hellip;&hellip;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case HTML_TEXT:&nbsp; &hellip;&hellip;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; default:&nbsp;&nbsp; &hellip;&hellip;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125; //switch</p><p>&nbsp;&nbsp;&nbsp; pTtokenList=pTtokenList-&gt;next; //取下一个节点</p><p>&nbsp;&nbsp;&nbsp; &#125; //while</p><p></p><p>这段程序实际上就是一个简单的语法分析和布局的过程。</p><p></p><p>2.5.2&nbsp; Bit Token在应用中存在的问题及修改意见</p><p>由于HTML的标记多是成对出现的，并且存在&lt;SCRIPT&gt;这样的特殊元素，其内容为Javascript程序，函数的字符串参数等可能包含其它的元素标记。例如语句：Alert(&ldquo;&lt;font&gt; is a tag&rdquo;);</p><p></p><p>因此，在词法分析时要对&lt;SCRIPT&gt;标记进行特殊处理，遇到&lt;SCRIPT&gt;就应逐字符读入后面的内容，直到遇到下一个&lt;/SCRIPT&gt;标记。目前的Bit Token由于开发时间所限，未对其加以特殊处理，存在一些问题，但由于浏览器对Javascript的支持是较复杂的工作，目前的Netbit Browser尚不予实现，因而没有导致明显问题，而&ldquo;HTML智能分析&rdquo;这个软件只是需要对Javascript进行删除操作，也不会造成影响。尽管如此，对&lt;SCRIPT&gt;的特殊处理还是有待完善，尽管这同时也会带来一些问题，需要进行大量的测试，来保证新加入代码的稳定性。</p><p></p><p>正如前面所述，HTML词法分析是浏览器设计的基础环节之一，但并非最重要和最具难度的环节，若想开发出效果较好的浏览器产品，还要在布局和GUI设计上多下功夫。</p><p></p><p></p><p>第三章 浏览器JavaScript支持的实现</p><p></p><p>本部份主要针对Mozilla和Netscape浏览器源代码的JavaScript部份进行了分析，阐述了浏览器Javascript实现的机制。</p><p>3.1基本的JavaScript 开发环境</p><p>JavaScript Reference与JavaScript API:</p><p>JavaScript Reference是Mozilla所使用JavaScript开发环境，是使用ANSI C的独立的开发包，据Mozilla文档介绍，该开发包涉及到超过160家公司的版权。而且被广泛使用，实际已成为了进行JavaScript应用开发的标准平台。</p><p></p><p>JavaScript Reference可以用于建立包含JavaScript runtime的Library或 DLL。既可以编译成小的 &quot;shell&quot; 程序（像早期的BASIC），又连接Library后生成交互式的JavaScript解释器，也可以用来解释.js 文件。由于使用了ANSI C编程，可以用VC、GCC等编译器在不同平台下编译。</p><p></p><p>生成的&quot;shell&quot; 程序，对比浏览器对JavaScript的支持，相同之处是使用相同的包含JavaScript runtime的Library 或 DLL，我们把这部份相同的Library或 DLL称为JavaScript API，我们实际开发JavaScript应用，也是在JavaScript API基础上工作，而不用过多考虑其内部的实现。关于JavaScript API，参见JavaScript API详解。JavaScript API实际就是Javscript解释器的对外接口函数库。</p><p></p><p>3.2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JavaScript Engine</p><p>JavaScript Engine是浏览器开发者为了利用JavaScript API来实现实际应用而设立的中间层，用于初始化JavaScript环境，提供对JavaScript解释、执行的接口。浏览器主体程序的设计者可以通过JavaScript Engine，方便的实现各种应用，毕竟JavaScript API太基础了，直接使用不太方便。</p><p></p><p>下面介绍JavaScript Engine的主要功能和实现方法。这也包含了利用JavaScript API进行应用的基本思路。</p><p></p><p>(1) 初始化：</p><p>内存分配：rt=JS_Init(10000L);</p><p>初始化cx：cx = JS_NewContext(rt, STACK_CHUNK_SIZE);</p><p>初始化globalObj：globalObj = JS_NewObject(cx, &amp;globalClass, 0, 0);</p><p>定义标准类：JS_InitStandardClasses(cx, globalObj);</p><p>定义系统函数：JS_DefineFunctions(cx, globalObj, g_functions);</p><p>定义报错函数：JS_SetErrorReporter(cx,JS_ErrorReporter);</p><p>注册其它类：</p><p>RegisterClassPoint&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (cx,globalObj);</p><p>RegisterClassSize&nbsp;&nbsp;&nbsp; (cx,globalObj);</p><p>RegisterClassRect&nbsp;&nbsp;&nbsp; (cx,globalObj);</p><p>RegisterClassPolygon (cx,globalObj);</p><p>RegisterClassColorKey&nbsp;&nbsp;&nbsp; (cx,globalObj);</p><p>RegisterClassTDTimer (cx,globalObj);</p><p>初始化定时器：TDTimerListInit();</p><p></p><p>(2) 提供对JavaScript解释、执行的接口函数：</p><p>TD_EvaluateScript(JSContext *cx,JSObject *obj, const char *bytes, uintN length,const char *filename, uintN lineno,jsval *rval)</p><p></p><p>3.3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JavaScript与浏览器接合</p><p>基本概念：JavaScript操作HTML元素的常见方式</p><p>例：</p><p>&lt;html&gt;&lt;head&gt;</p><p>&lt;script&gt;&lt;!--</p><p>function ChangeImage(index)</p><p>&#123;image0.src=&quot;a&quot;+index+&quot;.gif&quot;;&#125;</p><p>--&gt;&lt;/script&gt;</p><p>&lt;/head&gt;</p><p>&lt;a onmouseover=&quot;ChangeImage(0);&quot;&gt;军人&lt;/a&gt;&lt;br&gt;</p><p>&lt;a onmouseover=&quot;ChangeImage(1);&quot;&gt;眼睛&lt;/a&gt;&lt;br&gt;</p><p>&lt;img id=&quot;image0&quot; src=&quot;a0.gif&quot;&gt;&lt;/img&gt;</p><p>&lt;/html&gt;</p><p>当鼠标移到文字上时，触发事件mouseover，调用ChangeImage()函数，使得图像源(SRC)发生变化，重新调入新图片。</p><p></p><p>由此产生两个关键问题：</p><p>1.&nbsp;&nbsp; javascript如何获取HTML元素的名称和属性。</p><p>2.&nbsp;&nbsp; javascript如何改变HTML元素的属性，并操作WIDGET重画。</p><p></p><p>下面分别阐述这两个问题：</p><p>首先介绍涉及到的浏览器流程：</p><p>PARSE<br />&nbsp;<br />Tokenize<br />&nbsp;<br />BuildModel<br />&nbsp;<br />BuildPres<br />&nbsp;<br />ProcessElement<br />&nbsp;<br />WidgetPaint<br />&nbsp;<br />&nbsp;</p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p>问题1解决：HTML元素作为Javascript对象进行注册。</p><p>注册过程在BuildModel中进行。BuildModel的首要任务是将Token后的结点按包含关系展成一棵树。其次就是要将某些结点注册为JavaScript对象。</p><p></p><p>注册的过程是：</p><p>定义新对象：JSObject *proto;</p><p></p><p>初始化该对象：</p><p>TD_JSXMLElementClassInit(JS_GetGlobalContext(),</p><p>&nbsp;(void **)&amp;proto))</p><p></p><p>使用JS_DefineObject或JS_NewObject定义对象属性：</p><p>根据是否定义了该元素的名称区别对待：</p><p>if(TD_XMLContentIsNamedItem(aElement,&amp;aName))</p><p>&#123;&nbsp;&nbsp; parent = js_GetGlobalObject();</p><p>*aReturn=JS_DefineObject(JS_GetGlobalContext(),js_GetGlobalObject(),aName-&gt;mStr,&amp;ElementClass,proto,JSPROP_ENUMERATE);</p><p>&#125;</p><p>else</p><p>&#123;&nbsp;&nbsp; parent=aElement-&gt;parent-&gt;mScriptObject;</p><p>*aReturn = JS_NewObject(JS_GetGlobalContext(), &amp;ElementClass, proto, parent);</p><p>&#125;</p><p>将对象加入：</p><p>JS_SetPrivate(JS_GetGlobalContext(), (JSObject *)*aReturn, aElement);这样，在编译时，HTML元素的标识就能被Javascript编译器识别，否则会报错变量未定义。</p><p></p><p>问题2解决：利用注册给对象的函数实现操作符的功能化。</p><p>具体可理解为：当image0.src=&rdquo;1.gif&rdquo;被执行时，相当于为对象设置或改变属性，此时SetElementProperty函数被调用（该函数在注册该对象时由JSXMLElementClassInit捆绑给该对象，其内容由用户自己定义），SetElementProperty通过函数指针调用函数TD_JSXMLSetAtrByID，改变结点树上结点属性，并重新生成该节点对应的widget，重画界面。</p><p></p><p>问题3：如何建立Javascript对象与结点树上结点的对应？</p><p>解决: Javascript对象与结点树是同时生成的，它们的共同性质是结点具有相同属性，Javascript对象根据ID属性查找树，找到要操作的对应结点。</p><p></p><p>3.4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 浏览器消息响应</p><p>在主消息循环中调用TDWidgetProcessMsg，处理与widget有关消息。</p><p>首先：取得当前焦点所在的widget</p><p>pWidget=TDWidgetGetAtPoint(pThis-&gt;baseDoc.base.mWidget,pt,&amp;index);</p><p>处理该widget对该消息的响应。</p><p></p><p>最后一般为调用JavaScript执行，实现实际响应。</p><p>TDVOID&nbsp; TDWidgetDoAction(TDPWidgetAction pAnchor)</p><p>&#123;</p><p>&nbsp;&nbsp;&nbsp; jsval jval;</p><p>&nbsp;&nbsp;&nbsp; if(pAnchor)</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TD_EvaluateScript(JS_GetGlobalContext(),js_GetGlobalObject(),pAnchor-&gt;mAction.mStr,pAnchor-&gt;mAction.mLength,TDNULL,0,&amp;jval);</p><p>&#125;</p><p>其中pAnchor-&gt;mAction.mStr即为界面对象(widget)对应的JavaScript源码，解释执行的结果就是调用为该对象注册的函数来重画该widget，从而实现动态效果。</p><p></p><p>第四章&nbsp; Linux程序开发技术及 GTK图形程序开发</p><p>4.1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Linux下的程序开发环境</p><p>4.1.1基本知识</p><p>(1) 标准 (ANSI C, POSIX, SVID, XPG, ...)</p><p>ANSI C：这一标准是 ANSI（美国国家标准局）于 1989 年制定的 C 语言标准。 后来被 ISO（国际标准化组织）接受为标准，因此也称为 ISO C。</p><p><br />POSIX：该标准最初由 IEEE 开发的标准族，部分已经被 ISO 接受为国际标准。</p><p><br />SVID：System V 的接口描述。System V 接口描述（SVID）是描述 AT&amp;T Unix System V 操作 系统的文档，是对 POSIX 标准的扩展超集。</p><p><br />XPG：X/Open 可移植性指南。X/Open 可移植性指南（由 X/Open Company, Ltd.出版）， 是比 POSIX 更为一般的标准。</p><p><br />(2) 函数库和系统调用</p><p>&nbsp;glibc：众所周知，C 语言并没有为常见的操作，例如输入/输出、内存管理，字符串操作等提供内置的支持。相反，这些功能一般由标准的&ldquo;函数库&rdquo;来提供。GNU 的 C 函数库，即 glibc，是 Linux 上最重要的函数库，它定义了 ISO C 标准指定的所有的库函数，以及由 POSIX 或其他 UNIX 操作系统变种指定的附加特色，还包括有与 GNU 系统相关的扩展。目前，流行的 Linux 系统使用 glibc 2.0 以上的版本。glibc 基于如下标准：ISO C: C 编程语言的国际标准，即 ANSI C，POSIX，Berkeley Unix，SVID，XPG。</p><p></p><p>其他重要函数库：除 glibc 之外，流行的 Linux 发行版中还包含有一些其他的函数库，这些函数库具有重要地位，例如：GNU Libtool，CrackLib，LibGTop。图形文件操作函数库包括： libungif、libtiff、libpng、Imlib, libjpeg 等，可分别用来操作 GIF、TIFF、PNG、JPEG 以及其他一些格式图形文件。</p><p><br />(3) 系统调用</p><p>系统调用是操作系统提供给外部程序的接口。在 C 语言中，操作系统的系统调用通常通过函数调用的形式完成，在 Linux 系统中，系统调用函数定义在 glibc 中。</p><p><br />谈到系统调用时，需要注意如下几点：系统调用函数通常在成功时返回 0 值，不成功时返回非零值。errno 中包含有错误代码。系统调用是一个非常耗时 的过程。</p><p>(4) C 语言编程风格</p><p>&nbsp;&nbsp;&nbsp; 良好的编程风格可以在许多方面帮助开发人员，可以增加代码的可读性，并帮助你理清头绪。编程风格最能体现一个程序员的综合素质。</p><p><br />在 Linux 中，我们经常看到的是定义非常简单的函数接口和变量名称。C 语言最初来自 UNIX 操作系统，与 UNIX 的设计原则一样，C 语言被广泛认可和使用的一个重要原因是它的灵活性以及简洁性。在利用 C 语言编写程序时，应当符合其简洁的设计原则，而不应当使用非常复杂的变量命名方法。Linus 为 Linux 内核定义的 C 语言编码风格要点如下：</p><p><br />缩进时，使用长度为 8 个字符宽的 Tab 键。如果程序的缩进超过 3 级，则应考虑重新设计程序。</p><p><br />大括号的位置。除函数的定义体外，应当将左大括号放在行尾，而将右大括号放在行首。函数的定义体应将左右大括号放在行首。如下所示：</p><p>int function(int x, int y)</p><p>&#123;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (x == y) &#123;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;</p><p>&nbsp;else &#123;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...</p><p>&nbsp;&nbsp;&nbsp;&nbsp; &#125;</p><p>&nbsp;&nbsp;&nbsp;&nbsp; return 0;</p><p>&#125;</p><p>应采用简洁的命名方法。对变量名，不赞成使用大小写混写的形式，但鼓励使用描述性的名称；尽可能不使用全局变量；不采用匈牙利命名法表示变量的类型；采用短小精悍的名称表示局部变量；保持函数短小，从而避免使用过多的局部变量。保持函数短小精悍。不应过分强调注释的作用，应尽量采用好的编码风格而不是添加过多的注释。</p><p><br />(5) 库和头文件的保存位置</p><p>函数库：</p><p>/lib：系统必备共享库</p><p>/usr/lib：标准共享库和静态库</p><p>/usr/i486-linux-libc5/lib：libc5 兼容性函数库</p><p>/usr/X11R6/lib：X11R6 的函数库</p><p>/usr/local/lib：本地函数库</p><p><br />头文件：</p><p>/usr/include：系统头文件</p><p>/usr/local/include：本地头文件</p><p></p><p>4.1.2&nbsp;&nbsp; Linux 上的编译器和调试器</p><p>(1)&nbsp;&nbsp;&nbsp; Linux 上的 C/C++ 编译器和调试器</p><p><br />运行 gcc/egcs：Linux 中最重要的软件开发工具是 GCC。GCC 是 GNU 的 C 和 C++ 编译器。实际上，GCC 能够编译三种语言：C、C++ 和 Object C（C 语言的一种面向对象扩展）。利用 gcc 命令可同时编译并连接 C 和 C++ 源程序。</p><p><br />例如利用如下的命令可编译生成可执行文件，并执行程序：</p><p>$ gcc -o factorial main.c factorial.c</p><p>$ ./factorial 5</p><p><br />GCC 可同时用来编译 C 程序和 C++ 程序。一般来说，C 编译器通过源文件的后缀名来判断是 C 程序还是 C++ 程序。在 Linux 中，C 源文件的后缀名为 .c，而 C++ 源文件的后缀名为 .C 或 .cpp。</p><p><br />但是，gcc 命令只能编译 C++ 源文件，而不能自动和 C++ 程序使用的库连接。因此，通常使用 g++ 命令来完成 C++ 程序的编译和连接，该程序会自动调用 gcc 实现编译。</p><p><br />例如可以如下调用 g++ 命令编译、连接并生成可执行文件：</p><p>$ g++ -o hello hello.C</p><p>$ ./hello</p><p><br />(2) gcc/egcs 的主要选项</p><p>-ansi&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 只支持 ANSI 标准的 C 语法。这一选项将禁止 GNU C 的某些特色，</p><p>-c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 只编译并生成目标文件。</p><p>-DMACRO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 以字符串&ldquo;1&rdquo;定义 MACRO 宏。</p><p>-DMACRO=DEFN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 以字符串&ldquo;DEFN&rdquo;定义 MACRO 宏。</p><p>-E&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 只运行 C 预编译器。</p><p>-g&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 生成调试信息。GNU 调试器可利用该信息。</p><p>-IDIRECTORY&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 指定额外的头文件搜索路径DIRECTORY。</p><p>-LDIRECTORY&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 指定额外的函数库搜索路径DIRECTORY。</p><p>-lLIBRARY&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 连接时搜索指定的函数库LIBRARY。</p><p>-m486&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 针对 486 进行代码优化。</p><p>-o FILE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 生成指定的输出文件。用在生成可执行文件时。</p><p>-O0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不进行优化处理。</p><p>-O 或 -O1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 优化生成代码。</p><p>-O2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 进一步优化。</p><p>-O3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 比 -O2 更进一步优化，包括 inline 函数。</p><p>-shared&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 生成共享目标文件。通常用在建立共享库时。</p><p>-static&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 禁止使用共享连接。</p><p>-UMACRO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取消对 MACRO 宏的定义。</p><p>-w&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不生成任何警告信息。</p><p>-Wall&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 生成所有警告信息。</p><p><br />(3)gdb</p><p>&nbsp;&nbsp;&nbsp; GNU 的调试器称为 gdb，该程序是一个交互式工具，工作在字符模式。在 X Window 系统中，有一个 gdb 的前端图形工具，称为 xxgdb。gdb 是功能强大的调试程序，可完成如下的调试:设置断点；监视程序变量的值；程序的单步执行；修改变量的值。</p><p><br />在可以使用 gdb 调试程序之前，必须使用 -g 选项编译源文件。可在 makefile 中如下定义 CFLAGS 变量：CFLAGS = -g，运行 gdb 调试程序时通常使用如下的命令：gdb progname</p><p><br />(4) 创建和使用静态库</p><p>创建一个静态库是相当简单的。通常使用 ar 程序把一些目标文件（.o）组合在一起，成为一个单独的库，然后运行 ranlib，以给库加入一些索引信息。</p><p><br />(5) 创建和使用共享库</p><p>特殊的编译和连接选项-D_REENTRANT使得预处理器符号 _REENTRANT 被定义，这个符号激活一些宏特性。-fPIC选项产生位置独立的代码。由于库是在运行的时候被调入，因此这个选项是必需的，-shared选项告诉编译器产生共享库代码。</p><p>&nbsp;</p><p>4.1.3&nbsp;&nbsp; GNU make 和 makefile</p><p>(1) GNU make简介</p><p><br />在大型的开发项目中，通常有几十到上百个的源文件，如果每次均手工键入 gcc 命令进行编译的话，则会非常不方便。因此，人们通常利用 make 工具来自动完成编译工作。这些工作包括：如果仅修改了某几个源文件，则只重新编译这几个源文件；如果某个头文件被修改了，则重新编译所有包含该头文件的源文件。利用这种自动编译可大大简化开发工作，避免不必要的重新编译。</p><p><br />实际上，make 工具通过一个称为 makefile 的文件来完成并自动维护编译工作。makefile 需要按照某种语法进行编写，其中说明了如何编译各个源文件并连接生成可执行文件，并定义了源文件之间的依赖关系。</p><p><br />当修改了其中某个源文件时，如果其他源文件依赖于该文件，则也要重新编译所有依赖该文件的源文件。</p><p><br />默认情况下，GNU make 工具在当前工作目录中按如下顺序搜索 makefile: GNUmakefile,makefile,Makefile。在 UNIX 系统中，习惯使用 Makefile 作为 makfile 文件。</p><p><br />(2) makefile 基本结构</p><p>makefile 中一般包含如下内容：</p><p>* 需要由 make 工具创建的项目，通常是目标文件和可执行文件。</p><p>* 要创建的项目依赖于哪些文件。</p><p>* 创建每个项目时需要运行的命令。</p><p></p><p>例如，假设你现在有一个 C++ 源文件 test.C，该源文件包含有自定义的头文件 test.h，则目标文件 test.o 明确依赖于两个源文件：test.C 和 test.h。另外，你可能只希望利用 g++ 命令来生成 test.o 目标文件。</p><p><br />这时，就可以利用如下的 makefile 来定义 test.o 的创建规则：</p><p>test.o: test.C test.h</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g++ -c -g test.C</p><p><br />一个 makefile 文件中可定义多个目标，利用 make target 命令可指定要编译的目标，如果不指定目标，则使用第一个目标。通常，makefile 中定义有 clean 目标，可用来清除编译过程中的中间文件，例如：</p><p>clean:</p><p>&nbsp;&nbsp;&nbsp; rm -f *.o</p><p></p><p>运行 make clean 时，将执行 rm -f *.o 命令，最终删除所有编译过程中产生的所有中间文件。</p><p><br />(3) makefile 变量</p><p>GNU 的 make 工具除提供有建立目标的基本功能之外，还有许多便于表达依赖性关系以及建立目标的命令的特色。其中之一就是变量或宏的定义能力。如果你要以相同的编译选项同时编译十几个 C 源文件，而为每个目标的编译指定冗长的编译选项的话，将是非常乏味的。但利用简单的变量定义，可避免这种乏味的工作，例如：</p><p>CC = gcc</p><p>CCFLAGS = -D_DEBUG -g -m486</p><p>test.o: test.c test.h</p><p>&nbsp;&nbsp;&nbsp; $(CC) -c $(CCFLAGS) test.c</p><p>&nbsp;&nbsp;&nbsp; 在上面的例子中，CC 和 CCFLAGS 就是 make 的变量。GNU make 通常称之为变量，而其他 UNIX 的 make工具称之为宏，实际是同一个东西。在 makefile 中引用变量的值时，只需变量名之前添加 $ 符号。</p><p><br />(4) 运行 make</p><p>GNU make 命令还有一些其他选项：</p><p>-C DIR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在读取 makefile 之前改变到指定的目录 DIR。</p><p>-f FILE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 以指定的 FILE 文件作为 makefile。</p><p>-h&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 显示所有的 make 选项。</p><p>-i&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 忽略所有的命令执行错误。</p><p>-I DIR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当包含其他 makefile 文件时，可利用该选项指定搜索目录。</p><p>-n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 只打印要执行的命令，但不执行这些命令。</p><p>-p&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 显示 make 变量数据库和隐含规则。</p><p>-s&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在执行命令时不显示命令。</p><p>-w&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在处理 makefile 之前和之后，显示工作目录。</p><p>-W FILE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 假定文件 FILE 已经被修改。</p><p>4.2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GTK图形程序开发</p><p>4.2.1 简介<br />GTK (GIMP Toolkit) 起源於开发用来做为GIMP (General Image Manipulation Program)的一套工具. GTK建立在GDK (GIMP Drawing Kit)的上层, 基本上是将Xlib功能包装起来. 它被称为GIMP toolkit是因为它是为了开发GIMP而写的, 但现在被许多免费软体计划所使用。<br />4.2.2 一个简单的GTK程序</p><p>#include &lt;gtk/gtk.h&gt;</p><p>int main( int argc,char *argv[] )</p><p>&#123; GtkWidget *window;</p><p>&nbsp; gtk_init (&amp;argc, &amp;argv);</p><p>&nbsp; window = gtk_window_new (GTK_WINDOW_TOPLEVEL);</p><p>&nbsp; gtk_widget_show (window);</p><p>&nbsp; gtk_main ();</p><p>&nbsp; return(0);</p><p>&#125;</p><p>编译方法：gcc －Wall -g base.c -o base `gtk-config --cflags --libs`</p><p>该程序是一个最简单的窗口程序。</p><p></p><p>4.2.3 GTK的层次结构</p><p>GIMP<br />&nbsp;<br />GTK+<br />&nbsp;<br />GDK<br />&nbsp;<br />GLIB<br />&nbsp;<br />XLIB<br />&nbsp;</p><p></p><p>4.2.4 GTK的事件与信号处理</p><p>GTK是一个事件驱动的工具集，一个GTK应用通常在gtk_main上休眠直到一个事件发生，这时控制被传递给合适的函数。</p><p>&nbsp;&nbsp; 信号处理函数：</p><p>gint gtk_signal_connect( GtkObject, gchar,GtkSignalFunc, gpointer);</p><p>void gtk_signal_disconnect( GtkObject, gint);</p><p>void gtk_signal_handler_block( GtkObject, guint);</p><p>void gtk_signal_handler_unblock( GtkObject, guint);</p><p>void gtk_signal_emit( GtkObject, guint, ... );</p><p>事件与信号不是一回事</p><p></p><p>4.2.5 GTK的控件</p><p>使用一个控件的步骤</p><p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gtk_*_new 初始化一个控件 </p><p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 绑定控件与信号及事件 </p><p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把它包装到一个包容器中</p><p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 告诉GTK显示这个控件（gtk_widget_show） </p><p>控件体系类似于MFC的类库，子类具有父类的特征，在使用时要使用类型强制转换。</p><p></p><p>第五章 Netbit浏览器开发与分析</p><p>5.1 Netbit 浏览器简介</p><p>Netbit Browser 是基于Linux平台的浏览器，使用了gtk作为gui开发工具。项目的目的是要建立一个小型的、快捷的web浏览器，并便于移植到嵌入式系统中。该项目是开放源码项目，由<a href="mailto:sogo456@263.net">sogo456@263.net</a>负责维护，网址是：<a href="http://netbit_browser.myetang.com/">http://netbit_browser.myetang.com</a>。</p><p></p><p>项目目前已完成 0.0.1 demo版，实现了基本界面，html4.0词法分析、支持本地文本文件查看，英文网页浏览（使用菜单open file），支持简单http连接，支持URL访问，如输入<a href="http://www.gtk.org/download/top.htm">http://www.gtk.org/download/top.htm</a>等，支持字体颜色、字号等基本的元素。</p><p></p><p>Netbit Browser在界面上主要承袭了Gzilla的风格，在技术上主要涉及了词法、语法分析，文档布局，PIXMAP画图，文件IO操作，简单HTTP访问等技术。</p><p>界面模块<br />&nbsp;<br />控制模块<br />&nbsp;</p><p></p><p>PIXMAP画图模块<br />&nbsp;</p><p>5.2 Netbit Browser浏览器各部份的功能</p><p>IO模块<br />&nbsp;<br />词法分析模块<br />&nbsp;<br />文本文件显示模块<br />&nbsp;<br />HTML文件显示模块<br />&nbsp;</p><p></p><p>(1)主程序：用于初始化。</p><p>(2)界面模块：窗口、菜单、工具条、滚动条等的初始化。</p><p>(3)控制模块：负责命令的响应，消息的处理，是软件控制的中枢。</p><p>(4)IO模块：包括http和文件操作。</p><p>(5)词法分析模块：HTML的词法分析。</p><p>(6)文本文件的显示模块：对Plain text显示的处理，也包含相应的布局算法。</p><p>(7)HTML文件的显示模块：在Netbit browser中使用了语法分析与布局同时进行的方式，由此模块来驱动HTML文件的显示。</p><p>(8)使用PIXMAP的画图模块：是实际输出采用的方法，被6、7模块调用。</p><p></p><p>以下将就主要的较复杂的模块进行说明。</p><p></p><p>5.3 界面模块</p><p>(1) interface.c，interface.h：用于界面的初始化和定制。</p><p>主要的函数说明：</p><p>void a_Interface_init(void);界面初始化的主函数</p><p>void a_Interface_status(BrowserWindow *bw, const char *format, ... );</p><p>设置状态栏内容。</p><p>void a_Interface_openfile_dialog(BrowserWindow *bw);文件打开对话框</p><p>void a_Interface_set_Page_title(BrowserWindow *bw, char *title);</p><p>设置窗口标题。</p><p>void a_Interface_entry_open_url(GtkWidget *widget, BrowserWindow *bw);</p><p>打开网址输入栏输入的网址。</p><p>BrowserWindow *a_Interface_new_browser_window(gint width, gint height);</p><p>新建浏览窗口，是很重要的函数，在其中创建了所有的界面控件，并对按钮等进行了消息绑定。</p><p></p><p>(2) menu.c，menu.h</p><p>主要函数：GtkWidget *a_Menu_mainbar_new (BrowserWindow *bw);</p><p>定义了主菜单项，并进行了消息绑定</p><p></p><p>(3) browser.h</p><p>定义了重要的窗口结构如下：</p><p>struct _BrowserWindow</p><p>&#123;</p><p>&nbsp;&nbsp; /* 主窗口的widgets */</p><p>&nbsp;&nbsp; GtkWidget *main_window;</p><p>&nbsp;&nbsp; GtkWidget *back_button;</p><p>&nbsp;&nbsp; GtkWidget *forw_button;</p><p>&nbsp;&nbsp; GtkWidget *stop_button;</p><p>&nbsp;&nbsp; GtkWidget *location;</p><p>&nbsp;&nbsp; GtkWidget *location_button;</p><p>&nbsp;&nbsp; GtkWidget *status;</p><p></p><p>&nbsp;&nbsp; /* 键盘控制表*/</p><p>&nbsp;&nbsp; GtkAccelGroup *accel_group;</p><p></p><p>&nbsp;&nbsp; /* 工具条按钮 */</p><p>&nbsp;&nbsp; GtkWidget *back_menuitem;</p><p>&nbsp;&nbsp; GtkWidget *forw_menuitem;</p><p>&nbsp;&nbsp; GtkWidget *stop_menuitem;</p><p></p><p>&nbsp;&nbsp; /* 主文档 widget. (用于绘制HTML或其它) */</p><p>&nbsp;&nbsp; GtkWidget *layout;</p><p></p><p>&nbsp;&nbsp; /* 当前光标类型 */</p><p>&nbsp;&nbsp; GdkCursorType CursorType;</p><p></p><p>&nbsp;&nbsp; /* 对话框widgets*/</p><p>&nbsp;&nbsp; GtkWidget *open_dialog_window;</p><p>&nbsp;&nbsp; GtkWidget *open_dialog_entry;</p><p>&nbsp;&nbsp; GtkWidget *openfile_dialog_window;</p><p>&nbsp;&nbsp; GtkWidget *quit_dialog_window;</p><p></p><p>&nbsp;&nbsp; /* 指向保存词法分析结果的数据结构 */</p><p>&nbsp;&nbsp; BitTokenContext *global_cx;</p><p></p><p>&nbsp;&nbsp; /* 文件类型：html or plain text*/</p><p>&nbsp;&nbsp; gint file_type;</p><p>&#125;;</p><p></p><p>5.4 控制模块</p><p>(1)command.c，command.h</p><p>主要的函数说明：</p><p>void a_Commands_openfile_callback (GtkWidget *widget, gpointer client_data);打开文件的对话框</p><p>void a_Commands_openurl_callback (GtkWidget *widget, gpointer client_data);打开URL</p><p>void a_Commands_close_callback(GtkWidget * widget, gpointer client_data);关闭窗口</p><p>void a_Commands_exit_callback (GtkWidget *widget, gpointer client_data);退出程序</p><p>void a_Commands_viewsource_callback (GtkWidget *widget, gpointer client_data);查看HTML源码</p><p>void a_Commands_reload_callback (GtkWidget *widget, gpointer client_data);刷新当前网页</p><p>void a_Commands_home_callback (GtkWidget *widget, gpointer client_data);显示主页</p><p>void a_Commands_helphome_callback (GtkWidget *widget, gpointer client_data); 显示帮助</p><p></p><p>(2)nav.h，nav.c：是命令对应的与网页操作有关的具体实施</p><p></p><p>主要的函数说明：</p><p>void a_Nav_push(BrowserWindow *bw, const char*);按URL打开一个网址或文件，具有对不完整URL的兼容性。</p><p>void a_Nav_reload(BrowserWindow *bw);刷新当前网页</p><p>void a_Nav_open_splash(BrowserWindow *bw,char *str);打开起始页（内置页面）</p><p></p><p>5.5词法分析模块</p><p>词法分析的原理和算法在前面已有详述。</p><p>(1)&nbsp;&nbsp;&nbsp; BitToken.c，BitToken.h</p><p>主要的函数说明：</p><p>BitTokenContext * Bit_NewContext(); 创建新的全局结构</p><p>int Bit_Tokenize(BitTokenContext *global_cx); 局部词法分析</p><p>void Bit_BeginToken(BitTokenContext *global_cx); 全局词法分析</p><p>int Bit_DestroyToken(BitTokenContext *global_cx); 释放内存</p><p>char *Token_ReadUntil(BitTokenContext *global_cx,char *sUntil);重要的字符处理函数，读取到指定字符后结束</p><p>char *Token_GetAttribute(BitTokenContext *global_cx); 取元素属性</p><p>void Token_ConvertIfNeed(char * aString); 转义字串的处理</p><p>int Token_ConsumTag(BitTokenContext *global_cx);处理元素</p><p>int Token_Consum_PlainText(BitTokenContext *global_cx); 处理文本</p><p>void Bit_ShowTokenResult(BitTokenContext *global_cx);显示分析结果</p><p>void Bit_SaveTokenResult(BitTokenContext *global_cx,char * filename); 保存分析结果</p><p></p><p>(2)&nbsp;&nbsp;&nbsp; BitHtmlDtd.h，BitHtmlDtd.c</p><p>用于存储HTML4.0元素的名称和属性。</p><p></p><p>(3)&nbsp;&nbsp;&nbsp; BitTokenList.h，BitTokenList.c</p><p>元素链表相关</p><p>(4)&nbsp;&nbsp;&nbsp; BitTokenAttrList.h，BitTokenAttrList.c</p><p>元素属性链表相关</p><p>(5)&nbsp;&nbsp;&nbsp; BitStr.h，BitStr.c</p><p>字符串处理函数</p><p></p><p>5.6使用PIXMAP的画图模块</p><p>因本部份是HTML文件的显示模块、文本文件的显示模块的基础，所以先予说明。</p><p></p><p>paint.c，paint.h</p><p>主要的函数说明：</p><p>gint pixmap_new(GtkWidget *widget,int width,int height);</p><p>在此函数中使用</p><p>pixmap&nbsp; = gdk_pixmap_new(widget-&gt;window,width+30,height+30,-1);来新建一个pixmap。</p><p></p><p>gint expose_event (GtkWidget *widget, GdkEventExpose *event); 在expose消息到来时，即若界面被破坏需重画时，使用</p><p>gdk_draw_pixmap(widget-&gt;window,widget-&gt;style-&gt;fg_gc[GTK_WIDGET_STATE(widget)],pixmap,event-&gt;area.x,event-&gt;area.y,event-&gt;area.x,ent-&gt;area.y, event-&gt;area.width, event-&gt;area.height);来重画。</p><p></p><p>gint pixmap_repaint(GtkWidget *widget);用于提供手动重画。</p><p>gint Browser_Paint(BrowserWindow *bw);layout的主函数，用来根据文件类型来调用HTML文件的显示模块或文本文件的显示，同时初始化滚动条。</p><p></p><p>5.7 文本文件的显示模块</p><p>plain.c，plain.h</p><p>char *Plain_handle_tabs(const char *str)将TAB转为空格。</p><p>void a_Plain_write(GtkLayout *display,char *Buf1, gint BufSize)主要函数</p><p>下面介绍一下文本显示的算法。</p><p>指定默认字体</p><p>font=gdk_font_load(&quot;-adobe-helvetica-medium-r-normal--14-*-*-*-*-*-iso8859-1&quot;);</p><p>通过预布局来计算页面的长度：</p><p>while(i&lt;BufSize)&nbsp; </p><p>&nbsp; &#123; j=0;</p><p>&nbsp;&nbsp;&nbsp; while(line_size&lt;SCREEN_WIDTH-20 &amp;&amp; Buf[i]!='&#92;n')</p><p>&nbsp;&nbsp;&nbsp; &#123;str[j]=Buf[i];</p><p>&nbsp;&nbsp;&nbsp;&nbsp; line_size+=gdk_char_width(font,str[j]);</p><p>&nbsp;&nbsp;&nbsp;&nbsp; j++;</p><p>&nbsp;&nbsp;&nbsp;&nbsp; i++;</p><p>&nbsp;&nbsp;&nbsp; &#125;</p><p>&nbsp;&nbsp;&nbsp; str[j]='&#92;0';&nbsp;&nbsp;&nbsp; </p><p>&nbsp;&nbsp;&nbsp; if(Buf[i]=='&#92;n')i++;</p><p>&nbsp;&nbsp;&nbsp; x=X_START;</p><p>&nbsp;&nbsp;&nbsp; line_size=x;</p><p>&nbsp;&nbsp;&nbsp; y+=16;</p><p>&nbsp; &#125;</p><p>创建PIXMAP</p><p>&nbsp; pixmap_new(drawing_area,SCREEN_WIDTH,y);</p><p>&nbsp; gc = gdk_gc_new(drawing_area-&gt;window);</p><p>进行真实的画图。</p><p>pixmap_repaint(drawing_area);</p><p>输出到PIXMAP并显示</p><p></p><p>5.8 HTML文件的显示模块</p><p>这部份是整个浏览器最重要的部份之一，综合了语法分析与HTML的布局、输出，其算法的好坏直接关系到网页的显示效果。</p><p></p><p>主要流程：</p><p>&nbsp; while(pTtokenList!=NULL) </p><p>&nbsp; &#123;&hellip;&hellip;&hellip;&hellip;</p><p>&nbsp;&nbsp; switch(pTtokenList-&gt;token-&gt;type)</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case HTML_TITLE:</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip;&hellip;&hellip;&hellip;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case HTML_TEXT:</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip;&hellip;&hellip;&hellip;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;</p><p>&nbsp;&nbsp;&nbsp; &hellip;&hellip;&hellip;&hellip;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip;&hellip;&hellip;&hellip;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; default:</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip;&hellip;&hellip;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125; //switch</p><p>&nbsp;&nbsp; pTtokenList=pTtokenList-&gt;next; </p><p>&nbsp; &#125; //while</p><p></p><p>可以看到，这部份与语词分析结合的十分紧密，利用词法分析的结果，遍历各元素节点，取出其元素属性，根据一定的布局算法来进行布局。</p><p></p><p>例如：当遇到title元素时，就使用gtk函数来设定窗口标题为指定标题</p><p>gtk_window_set_title(GTK_WINDOW(bw-&gt;main_window),pTtokenList-&gt;token-&gt;pData);</p><p>其中pTtokenList-&gt;token-&gt;pData即为词法分析分析出的标题内容。</p><p></p><p>由于程序结构十分简单清晰，大部份元素的处理都简单易懂，参考源程序即可，下面主要针对&lt;font&gt;和相关标记对字体的设置阐述其算法。</p><p></p><p>由于&lt;font&gt;标记允许嵌套，所以使用了栈来对font元素进行管理，例如以下的HTML代码：</p><p>&lt;font size=4 color=#0000FF&gt;</p><p>This program is not &lt;b&gt;free software&lt;/b&gt;; you can redistribute it and/or </p><p>modify it under the terms of the &lt;font size=5 color=FF0000&gt;GNU General </p><p>Public License &lt;/font&gt;as published by the Free Software Foundation; </p><p>either version 2 of the License, or (at your option) any later version.</p><p>&lt;/font&gt;</p><p></p><p>显示的效果应为GNU General Public License的字号为5，颜色为FF0000，即红色；free software应为粗体，受首尾两个呼应的font标记约束，其它字字号均为4，颜色为0000FF，由于free software只被&lt;b&gt;&lt;/b&gt;这一对加粗符号约束，所以其颜色应受首尾的font标记的约束，即应为0000FF。</p><p></p><p>这种嵌套的约束方式带来了HTML元素管理的混乱，也容易产生冗余的HTML代码，但既然标准是这么定的，也只能想办法加以解决，固然现在随着样式表的广泛采用，font已面临寿终正寝，但仍然大量存在，特别在对字体的颜色的设置，使用font标记很方便。</p><p></p><p>栈式管理的主要算法详解：</p><p>void html_open_font(GtkWidget *widget,char * style_str,char *color_str,char *size_str,int html_element,int insert_to_list)；</p><p></p><p>该函数用于指定当前的字体属性，其参数包括style,color,size,以及改变字体属性的元素的名称，int insert_to_list用于标记此字体属性是否入栈，通常是入栈的。</p><p></p><p>这样，在出现font或相关元素的首标记时，我们将词法分析的结果提取出来，即将其元素属性提取出来，作为参数传递给html_open_font函数，该函数将这些属性进行组合，设置成为当前字体属性，并入栈保存；在出现font或相关元素的尾标记时，出于保险（因为存在交错包含关系的元素），首先检验栈顶元素与正在处理的元素尾标记是否匹配（名称相同），如相同则出栈，并将栈内下一字体属性设为系统的当前字体属性。</p><p></p><p>出栈函数为void html_close_font(GtkWidget *widget,int html_element)</p><p>需要注意的是由于并不是所有的font元素都指定所有的属性，可能只指定其中的一个或一部份属性，因此在入栈时必须做这样的处理，即首先获取当前的字体属性，根据哪些属性发生了变化来组合新的字体属性，然后入栈。</p><p></p><p>所使用的栈的结构很简单，如下。</p><p>typedef struct _font_list&#123;</p><p>int html_element;</p><p>char color_str[15];</p><p>char size_str[15];</p><p>char style_str[15];</p><p>&#125;font_list;</p><p>此为font_list的类型定义，描述了字体属性的结构</p><p></p><p>font_list font_opening[50]; 定义一个数组作为栈的存储形式</p><p>int current_font=0; 定义一个整型变量，作为栈顶指针</p><p></p><p>如此，一个简单的数组就发挥了巨大的作用，配以一点点算法，就带来了丰富多彩的界面效果。</p><p></p><p>5.9 Netbit实际应用效果及比较</p><p>下图为Netbit browser运行时的界面，所打开的页面源代码如下：</p><p>&lt;html&gt;</p><p>&lt;body&gt;</p><p>&lt;h1&gt;</p><p>&lt;font color=#FF00FF&gt;&lt;b&gt;Netbit Browser Version 0.0.1 Demo&lt;/b&gt;&lt;/font&gt;&lt;/h1&gt;</p><p>&lt;hr&gt;</p><p>&lt;h4&gt;License&lt;/h4&gt;</p><p>&lt;p&gt;</p><p>&lt;font size=4 color=#0000FF&gt;</p><p>This program is not &lt;b&gt;free software&lt;/b&gt;; you can redistribute it and/or </p><p>modify it under the terms of the &lt;font size=5 color=FF0000&gt;GNU General </p><p>Public License &lt;/font&gt;as published by the Free Software Foundation; </p><p>either version 2 of the License, or (at your option) any later version.</p><p>&lt;/font&gt;</p><p>&lt;hr&gt;</p><p>&lt;h3&gt;Design based on GTK, by sogo and ce!&lt;/h3&gt;</p><p>&lt;/body&gt;</p><p>&lt;/html&gt;</p><br /><p>以下为主菜单</p><br /><p>以下为工具条</p><br /><p>输入网页的URL，即可进行访问。</p><br /><p>以下为打开文件对话框</p><br /><p>以下为查看HTML源码对话框</p><br /><p>下面对比Netbit Browser，看看其它浏览器查看此网页的效果。</p><p>以下为KDE浏览该网页的效果</p><br /><p></p><p></p><p>以下为GZILLA浏览该网页的效果，GZILLA对字体颜色的处理比较差，只有黑色的字体。对字号的支持也不好。</p><br /><p>以下为Netscape显示该网页的效果，Netscape默认背景色是灰色。</p><br /><p>IE查看该网页的效果，字体不同是由于IE设置的默认字体不同。</p><p><br />可以看到，在对简单英文网页的支持效果上看，Netbit Browser，已接近于成熟浏览器的水平，甚至优于一些小型的嵌入式浏览器如GZILLA，Netfront，但在复杂页面的显示上还有较大的差距。</p><p></p><p>可以得出的结论是，Netbit Browser 0.0.1 Demo 版已经具有了一定的实用价值，但要对其进行完善，工作量还很巨大。</p><p></p><p>对比Netscape，IE的漫长的开发历史和巨大的资金投入，Netbit Browser的未来依然生死未卜。</p><p>&nbsp;</p><p>5.10 Netbit Browser的缺点分析及改进办法</p><p>基础的GUI设计上存在缺陷</p><p>Netbit Browser目前的页面输出实际上还采用了简单的画图机制，无法在主窗体内放置如按钮、编辑框、单选框等控件，也无法处理页面元素的消息响应，（Netbit Browser 0.0.1-013版已进行了改进），而使用GTK作为开发平台是完全可以实现这些要求的，GZILLA就是最好的实例，它通过对现有控件的组合，开发了自己的文档视图控件，实现了上述功能。但之所以目前没有采用先进的文档视控件，是因为要实现这样的有较强实用性的自画文档视图控件，是需要很大的工作量的，仅GZILLA为实现其核心的DW文档视图控件，就动用了超过7000行的代码，比Netbit Browser目前的总代码量还大。而Netscape由于考虑到支持多个GUI平台，还需要一个抽象的中间层文档视图控件，这个中间层也在万行以上。</p><p></p><p>改进措施</p><p>固然，能容纳百川，一触即发的文档视控件的开发是很繁重的工作，但原理却并不复杂。下面加以阐述。</p><p></p><p>大多数的GUI平台都提供了方便用户进行控件组合的机制，例如有的控件能包含其它的控件，通常称之为container(容器)，以GTK为例说明其原理。</p><p></p><p>GTK控件是以流行的控件组件的观念来设计的。 不过, 依然是以C来写的。 比起用C++来说, 这可以大大改善可移植性及稳定性。 但同时, 这也意味著widget 作者需要小心许多实际操作上的问题。 所有同一类别的控件的一般声明 (例如所有的按钮控件)是放在 class structure。 只有一个这样的结构。 在这个结构中储存类别信号的声明。 要支撑这样的继承, 第一栏的资料结构必须是其父类别的资料结构。例如GtkButton的类别的声明看起来像这样: </p><p>struct _GtkButtonClass&#123;&nbsp; GtkContainerClass parent_class;&nbsp; void (* pressed)&nbsp; (GtkButton *button);&nbsp; void (* released) (GtkButton *button);&nbsp; void (* clicked)&nbsp; (GtkButton *button);&nbsp; void (* enter)&nbsp;&nbsp;&nbsp; (GtkButton *button);&nbsp; void (* leave)&nbsp;&nbsp;&nbsp; (GtkButton *button);&#125;;当一个按钮被看成是个container(容器)时(例如, 当它被缩放时), 其类别结构可被传到GtkContainerClass, 而其相关的栏位被用来处理信号。</p><p>具体而言，比如我们使用一个基础的layout控件来作为我们自画的文档视图控件的基础控件，layout = gtk_layout_new(NULL, NULL);</p><p></p><p>接下来我们就可以使用gtk_layout_put函数将其它的允许被包含的控件放进去，就是这么简单，那难度在哪呢？其实，对于网页显示而言，能放进去多少个按钮、编辑框、单选框并不是最主要的，这很容易实现，只要采用了类似layout这样的基础控件，我们原则上可以组合出来很多种效果。我们迫切关心的是那些需要用画图方法来实现的页面元素，如文字、图片、表格、直线是如何产生的。下面加以阐述。</p><p></p><p>实际上，无论在什么情况下，我们要作画都需要合适的画布，要在可以作画的控件上才可以施展拳脚，drawing_area正是这样的控件，如此，我们只要将画画在drawing_area上，然后再使用gtk_layout_put函数将drawing_area放置到layout控件上，不就万事大吉了？不错，但只对了一半，原来drawing_area本身并没有实现自我重画的机制，当最小化窗口或打开对话框时，原有的界面就被破坏，只有进行重画才能恢复原貌，重画又是怎样实现的呢？原来，我们在将drawing_area放置于layout之前，是做了手脚的，使用以下函数</p><p>gtk_object_set_data(GTK_OBJECT(drawing_area), &quot;layout&quot;, partp);</p><p>来将画在drawing_area上的信息对应的数据封装给了drawing_area控件。同时使用函数</p><p>gtk_signal_connect(GTK_OBJECT(drawing_area), &quot;expose_event&quot;,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (GtkSignalFunc)render_line_event, NULL);</p><p>来将expose_event这个重画消息，捆绑给了drawing_area控件。render_line_event即是用于重画的函数，到了需要重画的时候，该函数取出封装给drawing_area的数据进行重画。</p><p></p><p>如此这般费神，终于可以让线条、文字、表格、图片得见天日，完美的展现在众人面前（当然，所有的画与重画的函数都要自己写好了才行）。不要高兴太早，超级链接都不可点击，GIF动画也不会动，界面还是死的，原来忘了画龙点睛，怎么办？加消息。</p><p></p><p>加消息的步骤通常如下，以文字的超级链接为例。</p><p>首先新建一个消息盒子，event_box = gtk_event_box_new();</p><p>如果着急的话先把盒子放到layout上，当然用gtk_layout_put函数。若是最后再放上去，效果是同样的。</p><p>将drawing_area装到盒子里。</p><p>gtk_container_add(GTK_CONTAINER(event_box),drawing_area);</p><p></p><p>下面指定消息：</p><p>if(partp-&gt;parent &amp;&amp; partp-&gt;parent-&gt;type == LAYOUT_PART_LINK) &#123;</p><p>&nbsp;&nbsp;&nbsp; gtk_signal_connect(GTK_OBJECT(event_box), &quot;enter_notify_event&quot;,</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (GtkSignalFunc)activate_text_link, drawing_area);</p><p>&nbsp;&nbsp;&nbsp; gtk_signal_connect(GTK_OBJECT(event_box), &quot;leave_notify_event&quot;,</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (GtkSignalFunc)activate_text_link, drawing_area);</p><p>&nbsp;&nbsp;&nbsp; gtk_signal_connect(GTK_OBJECT(event_box), &quot;button_press_event&quot;,</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (GtkSignalFunc)select_text_link, drawing_area);</p><p>&nbsp;&nbsp;&nbsp; gtk_signal_connect(GTK_OBJECT(event_box), &quot;button_release_event&quot;,</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (GtkSignalFunc)select_text_link, drawing_area);</p><p>&nbsp;&nbsp;&nbsp; gtk_widget_add_events(event_box, </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GDK_ENTER_NOTIFY_MASK &#124; </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GDK_LEAVE_NOTIFY_MASK &#124;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GDK_BUTTON_PRESS_MASK &#124;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GDK_BUTTON_RELEASE_MASK);</p><p>gtk_widget_realize(event_box);</p><p></p><p>如此这般，超级链接终于可点了（同样，消息引发的函数都得老老实实写好了）。可以实现移到上面就自动变色，点击就跳转到其它网页。</p><p></p><p>别忘了，指定一个漂亮的小手，这样点超级链接的时候更有情调。</p><p>gdk_window_set_cursor(event_box-&gt;window, gdk_cursor_new(GDK_HAND2));</p><p></p><p>&nbsp;&nbsp;&nbsp; 综上所述，如果每一个消息对应的函数都能按部就班的完成，超级自画文档视图控件的完成也就指日可待了。</p><p></p><p>若只支持简单的字、线、图，程序大致可以控制在千行以内，当然这不包括文字布局算法、图像解码算法的部份。除此之外，我们还要拟定比较完善的数据结构来存储我们画在drawing_area上的信息，通常这些信息是语法分析的结果，也可以是词法分析的结果，在自画过程中融入语法分析的过程。</p><p></p><p>5.11 Netbit Browser未来的展望</p><p>&nbsp;&nbsp;&nbsp; Netbit Browser作为一个毕业设计课题，还是接近圆满的。通过Netbit Browser的开发，对常见的浏览器开发技术有了深入了解和实践机会，但若作为实际的软件开发项目来讲，还差的很远，为了使其能继续得以生存，决定将其变为开放源码项目，遵循GPL协议开发，现已成为中国Linux论坛（<a href="http://www.linuxforum.net/">http://www.linuxforum.net</a>）的MyLinux计划的一个子项目。</p><p>&nbsp;&nbsp;&nbsp; </p><p>&nbsp;&nbsp;&nbsp; 下面主要讨论一下Netbit Browser在技术上亟待解决的问题。</p><p>(1)超级链接的支持，实际上在对技术的了解的基础上。如前面的详细分析可见，该部份的实现已没有障碍，应该尽快实现，根本上是个编程序的毅力问题。</p><p></p><p>(2)图片的显示, 图片显示因为有很多开放的开发包或源程序，只是显示的话难度不大，但要考虑到多个图片的同时传输、JPG的逐步显示、GIF的动态显示就愈显复杂，仍需要对图像算法、网络传输、线程、消息等技术多加研究。</p><p></p><p>(3)TABLE等复杂的元素的布局，这实际是一个众多浏览器共同的问题，目前除了IE以外，其它浏览器都还存在较大的问题，如果能实现类似Netfront的效果，就比较实用了。开放源码的ZEN浏览器的table布局算法也很值得借鉴。</p><p></p><p>市场展望：Netbit Browser使用标准C编程，可以移植到Windows平台。也可以利用相关技术实现有市场价值的应用，如&ldquo;HTML智能分析&rdquo;这个软件，就是利用了Netbit Browser的词法分析技术进行了有益的尝试。</p><p></p><p>第六章&nbsp; 其它浏览器分析</p><p>6.1&nbsp; Dillo(Gzilla)浏览器分析</p><p>Gzilla的最新版本改名为Dillo，Netbit Browser的界面设计主要借鉴了Dillo，其模块划分也受了Dillo很大的影响，Dillo浏览器是学习GTK程序开发的优秀范例，下面就其主要的模块加以说明。</p><p></p><p>6.1.1主函数流程分析</p><p>主要是对main函数进行分析。</p><p>gtk_true();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p><p>gtk_init(&amp;argc, &amp;argv); /*　gtk初始化的一般方式　*/</p><p>a_Prefs_init();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;&nbsp; 初始化　preference ，preference包括: http_proxy, no_proxy, home, link_color, bg_color, text_color, allow_white_bg, force_my_colors。　函数a_Prefs_init()调用Pref_load()分析文件dillorc，并初始化全局变量。*/</p><p>a_Dns_init();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*　初始化DNS模块。　此模块具体工作过程请参阅下文*/</p><p>a_Url_init();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*　该函数首先初始化全局变量http_proxy, 和 no_proxy； */</p><p>a_Mime_init();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*从网络（a_Http_get）或本地(a_File_get)取得文件后，不同的文件需要不同的方法打开。该函数定义了如下几种打开文件的方法：&nbsp;&nbsp;&nbsp; */</p><p>a_Dicache_init();&nbsp;&nbsp; /* Dicache用于图形文件的处理。*/</p><p>a_Interface_init(); /*　初始化几个有关界面的全局变量　*/</p><p>a_Dw_image_init();&nbsp; /*　图形信息的初始化　（没有分析）*/</p><p>bw = a_Interface_new_browser_window();&nbsp; /*　生成一个browser window(bw)，初始化用户图形界面。这些代码是基于gtk开发的。主要的回调函数都是在这里定义*/</p><p>a_Bookmarks_init();&nbsp;&nbsp;&nbsp;&nbsp; /*　初始化书签功能。这是比较独立的一个模块　*/</p><p>&hellip;&hellip;&hellip;&hellip;&hellip;</p><p>gtk_main();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*　gtk消息循环　*/</p><p>/*　以下为内存释放　*/</p><p>a_Cache_freeall();</p><p>a_Dicache_freeall();</p><p>a_Http_freeall();</p><p>a_Dns_freeall();</p><p>a_Prefs_freeall();</p><p></p><p>6.1.2文件的取得</p><p></p><p>当用户发出一个Url 请求时，Dillo 首先要取得Url所指向的文件；然后根据文件类型，选择相应的解释器。一个Url所指向的文件可能在本地，也可能在网络上。这节将主要介绍Dillo如何从网上取得文件。</p><p></p><p>模块interface定义了Dillo的图形用户界面。当用户发出一个Url请求时（例如open 一个网页），相应的回调函数将调用函数a_Nav_push()。模块Nav中的函数主要是维护每一个bw中的Url 堆栈(bw 是Browser Window的简写，是浏览器窗口的数据结构)。函数a_Nav_push()的主要功能是将当前的请求记录到bw 中的expecting域： </p><p>bw-&gt;nav_expect.url = g_strdup(url);</p><p>bw-&gt;nav_expect.title = NULL;</p><p>bw-&gt;nav_expecting = TRUE;</p><p></p><p>随后，调用函数Nav_open_url() 。该函数首先判断请求是否就在当前页中（如anchor 就在当前页中），如果是，则直接跳到当前页中指定位置；如果否，调用函数a_Cache_open_url() 并修改用户界面（状态条和一些buttons）。</p><p>模块Cache 是Dillo中的关键模块。模块HTTP 和模块File负责取得文件，模块Cache负责将取得的各种文件送到不同的解释器；同时模块Cache负责管理缓冲区，缓冲区中存在的文件不需要再通过网络下载。函数a_Cache_open_url() 首先搜索缓冲区，判断请求的文件数据是否已经存在；如果存在，调用Cache_process_queue()处理Cache中的数据；否则，调用a_Url_open ()从网上取文件。</p><p></p><p>函数a_Url_open()的主要功能是调用合适的opener，该函数一般调用Url_open()。Url_open()将调用合适的method：a_File_get()或者a_Http_get()。这里只讨论a_Http_get()。</p><p></p><p>函数a_Http_get()主要是创建一个http连接，发出DNS请求。它首先根据全局变量HTTP_Proxy和 No_Proxy，修改用户Url；然后创建非阻塞socket；最后通过调用a_DNS_lookup()发出DNS请求。</p><p></p><p>Dillo中的DNS请求将由多个线程完成。域名解析完成后，回调函数Http_dns_callback()将被调用。它首先发送Http请求(send query)；然后接收回答(receive answer)。发送、接收操作都是通过调用a_IO_submit()实现的。函数a_IO_submit()通过gtk的支持，实现了阻塞IO操作。当数据被接收到，回调函数a_Cache_callback()将被调用。</p><p></p><p>函数a_Cache_callback()主要功能是处理接收到的数据，判断文件是否下载完毕。接收到的数据将由函数Cache_process_queue()处理。</p><p></p><p>6.1.3&nbsp; 选择合适的解释器</p><p>函数Cache_process_queue()的主要功能是为下载的文件选择一个合适的解释器。</p><p></p><p>(1)该函数首先判断协议头(header)（根据Http协议，body前为header）是否下载完毕，如果没有下载完，直接返回。</p><p>(2)随后，调用函数a_Web_dispatch_by_type()选择解释器。</p><p>(3)调用此解释器。</p><p>(4)如果文件下载完毕，通知解释器，并处理相应的Cache队列。</p><p></p><p>函数a_Web_dispatch_by_type()首先调用a_Mime_set_viewer()，该函数根据文件类型选择相应的viewer(即：主函数中a_Mime_init()初始化的几种打开方式: a_Gif_image() a_Jpeg_image() 和a_Html_text())。对于Html文件，a_Html_text()将会被调用。a_Html_text()主要生成一个DilloHtml结构和一个DwPage结构。</p><p></p><p>DilloHtml中有几个比较重要的结构：</p><p>Dw&nbsp;&nbsp;&nbsp;&nbsp; 指向DwPage结构，DwPage是Dillo自定义的文档视图。</p><p>堆栈&nbsp;&nbsp; 维护一个堆栈，记录当前正在处理的tag，用于语法分析。</p><p>Bw&nbsp;&nbsp;&nbsp;&nbsp; 记录当前的窗口。</p><p></p><p>然后，函数a_Web_dispatch_by_type()调用a_Dw_gtk_scroller_set_dw(), 为DwPage（dw）设置边界，为相应的窗口（bw）设置滚动条，并将页面（dw ）嵌入到窗口中（bw）。此函数里的操作是基于gtk的。</p><p></p><p>函数a_Html_text()将会指定Html文件的解释器为Html_Callback().</p><p></p><p>6.1.4&nbsp; Html 文件的显示</p><p>widget<br />&nbsp;<br />word<br />&nbsp;<br />word<br />&nbsp;<br />word<br />&nbsp;<br />Line<br />&nbsp;<br />Line<br />&nbsp;<br />Line<br />&nbsp;<br />Page<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;</p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p>一般Html文件的显示需要经过词法分析、语法分析和布局(layout)，显示等几步。在Html显示方面，Dillo处理的比较简单。它在语法分析的同时，进行布局。</p><p></p><p>这里有两个概念：word 和line。一个word相当于一个Html文件中的一个tag ，若干个word形成一个line，而若干line形成一个page。在语法分析的过程中，发现一个tag，则生成一个word( 可能还会生成一个widget，如img)，同时进行布局。当一行布满时，一个新的line将会生成。这种处理方式简单，但是具有较大的局限性。Dillo 中不支持Table 标记就是这种局限性的一种体现。</p><p></p><p>Html_write( )</p><p>函数Html_callback()通过调用Html_write()实现Html文件的分析、布局。Html_write()是对已经下载完毕、还没有处理的一段数据的处理。</p><p></p><p>Html_write()的主要过程是</p><p>(1)调用a_Dw_page_update_begin()。</p><p>(2)处理缓冲区中的数据</p><p>当前字符是空格，而且不在Tag &ldquo; pre&rdquo;中处理空格</p><p>当前字符是&ldquo;&lt;&rdquo;，</p><p>&nbsp;&nbsp;&nbsp; 如果是注释，则跳过注释</p><p>&nbsp;&nbsp;&nbsp; 如果是一个Tag，则处理此Tag. (Html_process_tag())</p><p>&nbsp;&nbsp;&nbsp; 如果是word 则处理此word (Html_process_word()).</p><p>(3)将当前字符的位置，记录到对应DilloHtml结构中Start_Ofs (相对于整个文件头，未处理数据的起始地址，下一次调用此函数时使用)。</p><p>(4)处理状态条，显示浏览器当前的工作状态。</p><p>(5)a_Dw_page_update_end( )。对DwPage结构（页结构）中，word、line、widget等的改变，都应该放在这两个函数：a_Dw_page_update_begin()　和a_Dw_page_update_end()之间。在第二个函数调用之后，画面将会更新。</p><p></p><p>Tag处理一例: Html_tag_open_a()</p><p></p><p>此函数用于处理Tag（ anchor）的开始标签。</p><p>(1)将此tag压入对应DilloHtml中的堆栈，</p><p>(2)取得属性 &rdquo;href&rdquo;，创建一个attr，调用函数a_Dw_page_add_attr(page, &amp;attr)，将其加入相应的page.</p><p>(3)取得属性&rdquo;name&rdquo;，调用函数a_Dw_page_add_anchor()。此函数的主要功能：</p><p>创建一个word,记录word的属性是anchor，</p><p>以name为索引将此anchor插入当前page的anchor Hash表。</p><p>设置它在页面中的位置。 </p><p></p><p>结尾标签由函数Html_tag_close_default()处理。它的主要功能是将此标签从DilloHtml栈中弹出。</p><p></p><p>Html_process_word（）</p><p>此函数处理网页中，所要显示的文本。此函数首先根据当前DilloHtml堆栈中的ParseMode对文本进行处理。ParseMode有如下几种：STASH、VERBATIM、 PRE。</p><p></p><p>处理完后，调用函数a_Dw_page_add_text()，将这些文本插入页中。</p><p>此函数的主要流程为：</p><p>(1)设置字体</p><p>(2)计算文本宽度</p><p>(3)调用Dw_page_new_word()，在此页中创建一个新的word，记录此word的属性为Text.</p><p></p><p>6.1.5 Dillo分析总结</p><p></p><p>Dillo是运行在Linux平台上基于gtk的图形网络浏览器。具有规模小的优点。但是Html 页面的显示质量较差，不支持JavaScript、Java。基于Dillo的嵌入式浏览器改造需要考虑如下几个方面：</p><p></p><p>(1)从整体结构来看，从取得文件到文件处理，大量使用回调函数，代码的可读性差，结构不清晰。建议改成消息机制，这样既增强了各模块的独立性，同时提高了并发性。</p><p>(2)Html文件的显示模块需要改写。Dillo不能支持比较常用的标签Table，而这种缺陷又是因为设计结构本身的缺陷，所以该模块需要做较大的改写。</p><p></p><p>(3)Dillo对于gtk的依赖较强，由于gtk不适合于嵌入式系统，需要改成基于fltk或embedded QT等嵌入式ToolKits，因此改造的工作量较大。</p><p></p><p>6.2 Thunder浏览器分析</p><p>Thunder浏览器是一个XML浏览器，用于制作多媒体演示软件，支持多媒体格式包括文字、图像、音频、视频。采用标准C编程，在WINDOWS下使用VC编译，软件规模较大，源程序达到2.5M。</p><p>主要模块功能介绍：</p><p>JavaScript: JavaScript API函数库</p><p>JsEngine: 浏览器JavaScript支持引擎</p><p>TStream:流式文件处理</p><p>TXml:XML的词法语法分析与布局</p><p>Widget:widget控件库</p><p>TDevice:图形、声音等的驱动函数库</p><p>TKernel:核心系统函数，包括内存分配、消息解析宏以及接口类型定义等</p><p></p><p>Thunder浏览器值得称道的是对JavaScript支持的效果良好，通过JavaScript来达到对图片的操作，实现了较好的动态效果。是学习JavaScript支持的良好范例。</p><p></p><p>另外，Thunder浏览器的词法分析也比较完善，Neibit Browser的设计也参考了Thunder的设计，并借鉴了部份字符串处理函数。</p><p></p><p>Thunder浏览器的布局算法十分简单，因为其支持的XML是自己定义的，在图片等元素的属性里不仅定义了其应该出现的绝对位置，还定义了图片的长度，宽度，使得布局工作易如反掌，没有算法可借鉴。</p><p></p><p>6.3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Mozilla浏览器分析</p><p>6.3.1&nbsp; COM</p><p>COM是由Microsoft提出的组件标准，它不仅定义了程序之间进行交互的标准，并且也提供了组件程序运行所需的环境。COM所定义的模块之间的接口标准是二进制可执行代码级标准，因此模块之间独立性更强，具有语言无关性。　在Mozilla中，COM机制利用C++语言实现，几乎所有的对象都是COM对象，若干COM对象又组合成COM模块。COM对象在初始化时，都要注册到COM运行环境（COM库）中；当其它模块需要调用该对象的方法时，先要通过COM运行环境，以该COM对象的全球唯一ID为参数，创建该COM对象，取得对象接口指针；最后通过该对象接口指针调用方法。</p><p></p><p>COM机制使各个模块之间独立性非常高，为裁减提供了便利；但是，为了实现这套机制，Mozilla（Linux环境下）必须要实现相应的运行环境（COM库），所有的COM对象都要继承实现一些COM标准接口，这些都增加了代码量，使得单一模块（对象）代码规模增大。</p><p></p><p>6.3.2&nbsp; XML</p><p></p><p>Mozilla对于XML提供了强大的支持，甚至可以说整个软件包是对XML理论的一种实现。Mozilla 的用户界面主要是由XML文本描述（在Mozilla中称为XUL: XML-base User Interface Language）；所描述界面中的控件，被解释转换成DOM(Document Object Model)对象；与控件相关的操作大部分由JavaScript实现。DOM 和RDF是Mozilla软件包中比较大的核心模块，同时两者也是XML的一部分。DOM 是程序访问和维护HTML 和XML文档的API；RDF ( Resource Description Framework) 提供了对整个资源（包括本地资源）的描述体系；在Mozilla中，JavaScript与C++代码的互操作，也需要这两个模块的支持。可以说，XML在整个软件包中起到了纲举目张的作用。</p><p></p><p>对XML的支持增强了系统的功能，但是客观上也增加了系统的复杂性，增大了系统规模，为裁减造成了困难（最早曾尝试裁减Mozilla）。</p><p></p><p>6.3.3&nbsp; Layout</p><p>在对HTML脚本语法分析结束后，要对页面进行布局。在整个浏览器中，Layout属于关键技术之一。Mozilla提供了强大的布局功能，在系统自带的测试用例中，有非常复杂的页面（如：CSS styles, Deeply Nested Tables, Frames, DHTML等），MozillaV14可以对这些页面进行出色的布局；而RedHat Linux6.2自带的Netscape4.7x在显示这些页面时，错误比较多，甚至不能显示。</p><p></p><p>强大的功能是以规模巨大的代码量为代价的，MozillaV14中layout模块达到了3.8M，这是整个裁减过程中最难处理的模块之一。</p><p></p><p>6.4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ZEN浏览器分析</p><p>正如第一章简介中指出的，ZEN是一个不知名的优秀开放源码浏览器，拥有可换界面的优异特性，用户可改换GUI平台，设定自己的界面风格，极符合嵌入式的要求。</p><p></p><p>主要模块说明：</p><p>parser：词法语法分析。</p><p>layouter：布局。</p><p>ui：不同的GUI界面和控件库，包括GTK、SVGA、字符模式，也可自定义。</p><p>image：图像处理。</p><p>protocol：协议，包括file和http。</p><p></p><p>下面给出其语法分析的数据结构示意：</p><p></p><p>page</p><p>&nbsp;&#124;</p><p>X- text &lt;-&gt; link &lt;-&gt; text &lt;-&gt; table -X</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#124;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#124;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text -X&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; table_row -X</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#124;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; table_cell &lt;-&gt; table_cell -X</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#124;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#124; </p><p>text -X&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; image &ndash;X</p><p></p><p>可见，节点之间不仅有先后次序，还有父子关系，正确的语法分析和良好的布局算法，使得ZEN可以支持表格等复杂元素，格式基本正确。</p><p></p><p>关于使用GTK来支持超级链接的消息响应，第五章已有详细阐述，其参考对象就是ZEN浏览器。</p><p></p><p>让我们记住作者Konfucius的座右铭吧：</p><p>&quot;The reward of studying, lies in the studies themselves.&quot;</p><p></p><p>6.5 浏览器分析工作的总结</p><p>浏览器分析工作是一项十分耗费精力的工作，由于浏览器的源代码大多比较庞大，模块的独立性也较差，往往环环相扣，上下相关，涉及的技术也十分繁杂，分析起来十分费力，进展也比较缓慢，大约占了整个开发工作的1/3时间。但这项工作一定要踏识的完成，要多借鉴成熟的技术，这样在自己的开发工作中才可以事半功倍，做到心中有数也就能够按部就班。</p><p></p><p></p><p>致&nbsp; 谢</p><p></p><p>在本课题的研究设计的过程中，得到了许多老师和同学的帮助，借此机会向他们表示诚挚的谢意。</p><p></p><p>首先要感谢我的导师张丽芬老师。在整个课题过程中，张老师对我热心指导、严格要求，在选题、系统总体设计与技术方案上，给予我宝贵的建议，帮助我建立了正确的设计思想，保证了课题的研究和开发工作的顺利完成。我从她那里学到的不仅是学术方面的知识，还有优良品格和严谨的治学态度。</p><p></p><p>感谢.....等同学在前期的分析和准备中所做的大量工作，为课题的开发建立了良好的基础。</p><p></p><p>感谢李洋同学送来了宝贵的GDK、GTK的详细资料。</p><p></p><p>最后感谢..同学的密切配合，和给予我的热情帮助和鼓励。</p><p></p><p>本课题所涉及的相关技术包含了一个广泛的技术集合，半年多的研究与设计工作只是管中窥豹。未来，随着技术的不断发展，相信后人会有更出色的表现。也希望我的工作能给国内的浏览器开发工作者带来一定的参考价值。</p><p><br />本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/wushuan10141/archive/2008/07/31/2747790.aspx">http://blog.csdn.net/wushuan10141/archive/2008/07/31/2747790.aspx</a></p>
]]>
</description>
</item><item>
<link>https://jackxiang.com/post//#blogcomment</link>
<title><![CDATA[[评论] 嵌入式浏览器开发]]></title> 
<author> &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> 
<guid>https://jackxiang.com/post//#blogcomment</guid> 
<description>
<![CDATA[ 
	
]]>
</description>
</item>
</channel>
</rss>