WebKit 中CSS的实现

本文深入探讨了CSS在WebKit和Chromium中的解析、内部表示及应用过程,包括CSS选择器、样式规则的匹配算法、RenderStyle的生成与应用、以及CSS3的变形、变换和动画等功能的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转:http://blog.youkuaiyun.com/milado_nju/article/details/7685517

# CSS在WebKit和Chromium中的实现

## 概述

前面章节介绍了CSS的三种基本要素,大概可以分成选择器,各种基本样式和CSS3引入的变形、变换和动画等。本章在此基础上,着重介绍CSS是如何在WebKit和Chromium得到支持的。首先介绍的是CSS解析器,而后分别阐述上面三种基本要素如何在WebKit和Chromium中实现的。

接前面章节,这里仍然以之前的CSS例子为基础来介绍本章的内容。为方便起见,依旧包含该例子,如下图所示。


## CSS解析器

CSS的词法其实并不复杂,其分析工作由bison完成,具体实现可以在文件CSSGrammar.y中找到,详细过程这里不在赘述。其主要工作是解析词法并调用CSSParser的回调函数来生成结果,例如上例中的第4行到第17行,第4行首先是识别选择器,后面是各个属性的识别,最后,这些全部构成了基本的StyleRule类型(关于类型会在后面介绍),所以调用CSSParser的createStyleRule函数,该函数将选择器和属性列表的中间表示最后处理生成其内部表示保存在一个规则StyleRule中。对于其他的类型,作类似的处理。

解析器对上层的接口是CSSParser,所有任务均有其处理。那么上面例子中有哪些场景会需要创建CSSParser的实例呢?显而易见地有两处地方,第一个是第4行到第17行,当DOM建立好之后在创建RenderObject的时候会调用CSSParser;第二个是第29行,在该段JavaScript代码被执行时,JavaScript引擎会间接调用CSSParser为元素的属性style解析。但其实还有一个地方会使用到CSSParser。

实际上WebKit(其他渲染引擎应该也类似吧K)会为每个网页设置一个缺省的样式,这决定了你所没有设置的元素及其它们的属性缺省值和将要显示的效果。通常来讲,不同的WebKit的移植(port)会有不同的缺省样式。

解析后的结果应该是一系列的样式规则,下面让我们来看看这些规则在WebKit中的内部表示。

## 样式的内部表示

被解析后的CSS样式其实就是一组样式规则,每一个规则包含一组选择器和一组样式属性。如前面例子中第4行到第17行所示。这些数据在WebKit中都有相应的内部表示,为了便于理解,下图给出了CSS的表示和内部结果表示的对应图。


值得一提的是选择器部分,上图是一个选择器列表,这在现实中是比较常见的。举个例子, a[class=abc]其实是两个选择器,第一个是’a’,它一个标签选择器;第二个是’[class=abc]’,它是一个属性选择器。

这里例子中的样式规则是一种基本的样式类型。CSS的标准包含了多种规则类型:

Style:这个是基本类型,一般大多数规则属于这个类型;

Import:是WebKit中为方便引入的,其对应的是一个导入CSS文件的Style元素

Media: 对应于CSS标准中的@media类型

Fontface: CSS3新引入的自定义字体的规则类型

Page: 对于CSS标准中的@page类型

Keyframes: WebKit的@-webkit-key-frames类型,可以用来指定特定帧的样式属性信息

Region:对CSS标准正在进行中的Regions的支持,这方便了开发者对页面进行分区域来排版。详见参考文献2.

下图给出了这些类型在WebKit中的定义及其关系。


这里有必要解释一下StyleRuleImport类,这个一个伪类型,CSS中并没有该类型的定义,只是WebKit处理CSS文件方便引入的。该类需要两个类来辅助完成,一个是StyleSheetContents类,就是CSS文件中各个规则的处理和内部表示,这些规则被称为子规则,构成一个列表;另外一个是CSSStyleSheet,该类是个包装类,代表CSS文件,包含一个StyleSheetContents对象。

## 选择器(CSSSelector类)

CSS选择器的实现并不复杂,其实现由类CSSSelector来完成。CSSSelector的作用是储存从解析器生成的结果信息以被匹配使用。这里匹配指的是当需要为每个DOM中的节点计算样式适合,WebKit需要根据当前的节点信息来从规则列表中找到能够符合调节的规则,并把规则中的属性列表提取出来生成节点的样式信息。

在CSS介绍那一章中,我们知道有多达42种选择器类型,例如。在WebKit的CSSSelector类中,它们被称为伪类型(PseudoType),其含义是表示CSS标准中定义的类型,而主要用于匹配算法的类型定义为Match,它包含Id, Class, Extract, Set, List, Hyphen,PseudoClass,PseudoElement, Contain, Begin, End, PagePseudoClass。这些类型是通过抽象标准中的类型而得来,例如:first-of-type,:last-of-type,:only-of-typed等等都属于PseudoClass。具体的匹配算法参考代码(文件CSSSelector.cpp),这里不在赘述。

### StyleResolver和RenderStyle

在样式规则解析完成之后,剩下的问题是如何把这样规则应用到具体的元素上。这就涉及到两个主要的类:StyleResolver和RenderStyle。

StyleResolver是管理类,其负责根据样式规则为每一个Document中的元素匹配响应的样式属性,它和Document 节点是一一对应关系,也就是说WebKit为每个Document创建一个StyleResolver对象,为所有该Document中的节点计算样式,并将其结果保存到RenderStyle对象中。

RenderStyle是元素所有样式属性的内部表示。由于其包含了所有样式属性,为了节约空间,WebKit将属性分为两种类型:常用属性和非常用属性。非常用属性会进行分组合并,并且仅在需要时创建,这相对有效地节约了内存。该对象在被StyleResolver创建后由该元素所对应的RenderObject所拥有。

那么StyleResolver是如何为一个DOM元素生成RenderStyle对象的呢?大致地有如下几个主要步骤:

1.      首先创建一个新的RenderStyle对象

2.      从它的父亲那里继承它的一切可以继承的属性

3.      如果是link类元素,设置link属性

4.      而后是样式规则的匹配,从已知规则中找到匹配到的属性

5.      将匹配到的属性应用到RenderStyle对象中

6.      为该DOM元素的RenderStyle做一些修正工作

7.      清理StyleResolver,为下次匹配请求做准备

## RenderStyle 和RenderObject

元素在匹配生成其样式属性值之后,RenderStyle对象被RenderObject所获得,这个触发一个重新绘制的动作,WebKit此时可以根据样式属性值来计算它的布局和显示,这将在下一章作详细介绍。

##JavaScript设置样式

JavaScript有能力设置任何元素的样式值,如例子中第29行所示,其原则是覆盖样式中同一属性的值。大致的过程是,JavaScript引擎调用设置属性值的公共处理函数,然后该函数调用属性值解析函数,在这个例子中则是CSS的JS绑定函数,而后将解析后的信息设置到元素的style属性的样式webkitTransform中,然后设置该元素需要重新计算style和invalidate它的style属性,如下图所示。


在这之后,重新绘制请求被处理时,WebKit先会重新计算布局,而后在渲染相应的区域。

 ## CSS3 变形(transform)、变换(transition)和动画(animation)

在WebKit渲染基础中,我们介绍了DOM数,Render树和RenderLayer树,根据WebKit的设计原则,可以知道上面HTML例子的三种树结构如下图所示。也就是说,元素P的内容会被包含在中间的RenderLayer中,Div元素的内容会被会包含单独的最下面的RenderLayer中,因为Div元素的需要做CSS的3D变形。


我们知道RenderLayer会有一个后端存储空间,Chromium中是一个FrameBuffer,该层会转成一个Texture,那些CSS3的变形,变换和动画效果将作用于该Texture上,而后绘制在网页的framebuffer中。

变形比较简单,WebKit和Chromium内部有一个Matrix来表示平移,旋转,缩放和扭曲等变形,因为OpenGL有直接对变形的支持,所以只需创建响应的OpenGL变换操作即可。

变换和动画的实现大致相同,前面解析的过程跟其他属性没有区别,其结果会保存于Animation对象中。在chromium的实现中,CC(chromium compositor, 前面介绍过)中有两个主要类对其提供支持,一个是CCLayerAnimationController,一个是CCActiveAnimation。前者是控制动画的行为,后者则是包含动画相关参数和状态。这一部分其实很复杂,有兴趣的读者可以自行阅读代码。

## 源文件目录

third_party/WebKit/Source/WebCore/css/

  与CSS解析,内部表示生成等一系列相关的类

third_party/WebKit/Source/WebCore/rendering/style

  渲染所需要的样式的支持类,其依赖于CSS解析器及其结果

## 参考文献

1.      http://www.w3schools.com/css

2.      http://dev.w3.org/csswg/css3-regions/

 

<think>好的,我现在需要处理用户关于BUUCTF-USB题目的询问。首先,我得回忆一下这个题目的具体内容。BUUCTF是一个CTF(Capture The Flag)竞赛平台,里面的题目通常涉及各种安全领域的挑战,比如逆向工程、密码学、取证等。USB相关的题目可能涉及到USB流量分析,这在CTF中是比较常见的取证类题型。 首先,用户可能想知道如何解决这个题目,或者需要了解解题的一般步骤。通常,USB流量分析的题目会提供一个抓包文件,比如pcap或者pcapng格式,其中包含USB传输的数据。解题的关键在于提取这些数据,并分析其中可能的键盘输入、鼠标移动或存储设备的数据传输。 接下来,我需要考虑解题的具体步骤。首先,用户可能需要使用Wireshark之类的工具打开提供的流量文件,然后过滤出USB传输的数据包。对于键盘数据,通常关注的是URB_INTERRUPT in类型的传输,因为键盘输入是通过中断传输实现的。数据部分可能包含按键的键码,需要将这些键码转换为实际的字符。 这里需要注意,USB键码到字符的转换可能需要对照USB HID使用表。例如,键码0x04对应a,0x05对应b,依此类推,但可能会有移位或修饰键的影响。此外,有些题目可能会将数据隐藏在鼠标移动的坐标中,或者存储设备的批量传输中,所以需要根据具体情况调整分析方法。 另外,可能需要使用脚本来自动化提取和处理数据。例如,用Python的scapy库读取pcap文件,过滤出有效的数据段,然后进行转换。用户可能需要编写这样的脚本,或者在互联网上查找现有的工具或脚本进行辅助。 可能遇到的挑战包括数据包的过滤是否正确,键码转换是否有误,或者数据是否经过加密或编码。这时候需要仔细检查每一步的输出,确认是否遗漏了某些关键数据包,或者是否需要调整转换逻辑。 此外,BUUCTF的题目可能有不同的变种,所以需要根据具体的题目文件进行调整。比如,有些题目可能使用不同的传输类型,或者数据被分成多个包,需要重新组合。这时候需要仔细分析数据包的结构,查看是否有长度字段或标识符来帮助重组数据。 最后,总结解题思路:分析流量文件,确定数据传输类型,提取有效数据,转换数据格式,最终得到flag。可能需要结合工具和脚本,逐步排除干扰信息,找到关键数据段。</think>BUUCTF中的USB类题目通常涉及USB流量分析,属于取证题型。以下是解题的逐步指南: --- ### **1. 题目背景** - 题目提供一个包含USB设备通信数据的抓包文件(如`.pcap`)。 - 目标:从流量中提取隐藏的Flag,常见于键盘输入、鼠标轨迹或存储设备传输。 --- ### **2. 解题步骤** #### **步骤1:分析流量类型** - 使用 **Wireshark** 打开文件,过滤USB流量: - 键盘输入:`usb.transfer_type == 0x01`(中断传输)且`usb.endpoint_number == 0x81`。 - 鼠标移动:`usb.endpoint_number == 0x81`,数据长度可能为4字节。 - 存储设备:`usb.transfer_type == 0x02`(批量传输)。 #### **步骤2:提取数据** - **键盘输入**:提取`Leftover Capture Data`字段(例如:`2d0000000000`)。 - 键码转换规则: - 第3字节为实际键码(如`0x04`→a,`0x05`→b,参考[HID键码表](https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf))。 - 若存在`0x02`修饰键(Shift),字符需转换大写或符号。 - **鼠标数据**:提取坐标(如`x=bytes[1], y=bytes[2]`),可能用于绘制轨迹。 #### **步骤3:编写脚本自动化处理** - 使用Python示例提取键盘数据: ```python from scapy.all import * packets = rdpcap('usb.pcap') keystrokes = [] for pkt in packets: if pkt.haslayer('USBTransferData'): data = pkt['USBTransferData'].leftover if len(data) >= 8: # 键盘数据通常为8字节 key = data[2:3].hex() if key != '00': keystrokes.append(key) # 转换键码为字符(需对照HID表) ``` #### **步骤4:处理特殊编码** - 若数据经过加密或编码(如Base64、Hex),需进一步解码。 - 示例:提取的字符串`666c61677b...7d`→Hex解码→`flag{...}`。 --- ### **3. 常见技巧** - **数据重组**:若数据分片,需按`URB ID`或包顺序合并。 - **流量筛选**:排除无效数据(如`000000000000`)。 - **工具辅助**:使用`tshark`命令行快速提取字段: ```bash tshark -r usb.pcap -T fields -e usb.capdata | grep -v "000000000000" ``` --- ### **4. 例题参考** - **BUUCTF-USB** 示例解法: 1. 过滤键盘中断流量,提取键码。 2. 发现键码序列:`0x16 0x07 0x08...`。 3. 对照HID表转换,得到`f l a g { ... }`。 --- ### **5. 总结** - 关键点:确定设备类型→过滤有效数据→转换键码/坐标→解码隐藏信息。 - 扩展学习:了解USB协议细节及更多HID设备的数据格式(如游戏手柄)。 遇到具体题目时,需结合流量特征灵活调整分析策略!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值