首页 > 其他分享 >WebKit Inside: CSS 样式表的匹配时机

WebKit Inside: CSS 样式表的匹配时机

时间:2023-10-05 17:12:57浏览次数:40  
标签:... 匹配 外部 代码 注释 样式表 WebKit Inside

WebKit Inside: CSS 的解析 介绍了 CSS 样式表的解析过程,这篇文章继续介绍 CSS 的匹配时机。

无外部样式表

内部样式表和行内样式表本身就在 HTML 里面,解析 HTML 标签构建 DOM 树时内部样式表和行内样式就会被解析完毕。因此如果 HTML 里面只有内部样式表和行内样式,那么当 DOM 树构建完毕之后,就可以进行样式表的匹配了。

假设 HTML 里面的行内样式在 <div>标签,那么 CSS 匹配样式时机如下图所示:

image

如果 HTML 里面除了内部样式表或者行内样式,还有外部样式表,那么情形比较复杂。

由于引入外部样式表的 <link>标签可以位于 <head>标签中,也可以位于<body>标签中,这两种情形下,匹配时机不一样。

外部样式表位于 head

如果 HTML 里面有外部样式表和内部样式表,HTML 代码如下:

<html>
	<head>
		<meta charset='utf-8' />
		<title>EasyHTML</title>
		<style text="text/css">
		/* 内部样式表 */
		div {
			background-color: red;
		}
		</style>
		<!-- 外部样式表-->
		<link rel="stylesheet" href="cs.css" />
		
	</head>
	<body>
		<div>kkk</div>
	</body>
</html>

外部样式表 CSS 文件如下:

div {
	background-color: blue;
	font-size: 20px;
}

如果在 DOM 树构建完成之前,外部样式表就已经下载回来并且解析,那么,当 DOM 树构建完成之后,就可以直接进行样式表的匹配。

但是如果在 DOM 树构建完成之后,外部样式表还没有下载回来,那么即使内部样式表已经解析完成了,也不会进行任何样式表的匹配。调用堆栈如下图所示:

image

在函数 TreeResolver::resolveElement中,此时第一行 if里面 m_didSeePendingStyleSheet为真,因此不会进行任何样式的匹配。

由于没有进行样式匹配,无法构建渲染树,当然也不会布局和绘制,在外部样式表的下载过程中,页面是空白的。因此 CSS 的下载虽然不阻塞 DOM 树的构建,但是阻塞渲染。

变量m_didSeePendingStyleSheet在函数TreeResolver::resovle里面设置,如果位于 <head>标签里面的外部样式表还未下载成功,这个变量就是 true。设置好 m_didSeePendingStyleSheet变量,函数 TreeResolver::resove 最终会调用到TreeResolver::resolveElement里面。

TreeResolver::resolve相关代码如下所示:

std::unique_ptr<Update> TreeResolver::resolve()
{
    ...
    // 1. 设置 m_didSeePendingStyleSheet 变量
    m_didSeePendingStylesheet = m_document.styleScope().hasPendingSheetsBeforeBody();
    ...
    // 2. TreeResolver::resolveElement 函数由下面这个函数调用进去
    resolveComposedTree();
    ...
    return WTFMove(m_update);
}

上面代码注释 1 处设置m_didSeePendingStyleSheet

代码注释 2 处,函数 TreeResolver::resolveComposedTree会调用到TreeResolver::resolveElement

当外部样式表下载完毕,仍会回调到函数TreeResolver::resove,调用堆栈如下:
image

由于此时变量m_didSeePendingStyleSheet设置为false,样式表可以正常进行匹配。

image

外部样式表位于 body

把上面 HTML 里面的外部样式表挪到<body>标签,其他不变:

<html>
	<head>
		<meta charset='utf-8' />
		<title>EasyHTML</title>
		<style text="text/css">
		/* 内部样式表 */
		div {
			background-color: red;
		}
		</style>
	</head>
	<body>
		<!-- 外部样式表-->
		<link rel="stylesheet" href="cs.css" />
		<div>kkk</div>
	</body>
</html>

这种情形下的匹配时机会发生变化。

如果位于<body>标签的外部样式标在 DOM 树构建完成之前下载完成,那么匹配时机和上面位于<head>标签的外部样式表一样,也就是 DOM 树构建完成就进行匹配。

如果 DOM 树构建完成之后,位于<body>标签的外部样式表还未下载成功,此时由于内部样式表已经解析完成,WebKit 会对现有已解析样式表进行匹配,匹配完成之后会构建渲染树,相关代码如下:

void Document::resolveStyle(ResolveStyleType type)
{
    ...
    Style::TreeResolver resolver(*this, WTFMove(m_pendingRenderTreeUpdate));
    // 1. 进行 CSS 样式表匹配
    auto styleUpdate = resolver.resolve();
    ...
    if (styleUpdate) {
        // 2. 样式表匹配完成,这里会进行渲染树构建
        updateRenderTree(WTFMove(styleUpdate));
        frameView.styleAndRenderTreeDidChange();
    }
    ...
    if (m_renderView->needsLayout())
        // 3. 渲染树构建完毕,这里会发起布局
        frameView.layoutContext().scheduleLayout();
    ...
}

上面代码注释 1 处进行 CSS 样式表匹配。

代码注释 2 处现有已解析样式表匹配完毕,会进行渲染树的构建。

代码注释 3 处,如果条件允许,会进行布局计算。

但是很遗憾,如果位于<body>标签的外部样式表没有下载完成,因此不满足布局条件,代码运行不到上面代码注释 3 处,调用堆栈如下:

image

虽然有了渲染树,但是由于没有布局,也就不会进行绘制,在外部样式表下载过程中,页面同样是白色的。CSS 样式表下载依然阻塞渲染

下面看一下上图判断是否可以布局的代码,代码如下:

bool Document::shouldScheduleLayout() const
{
    ...
    // 1. 因为 isVisuallyNonEmpty 方法返回了 false,导致了布局条件不满足
    if (view() && !view()->isVisuallyNonEmpty())
        return false;
   ...
    return true;
}

上面代码注释 1 处由于方法LocalFrameView::isVisuallyNonEmpty返回了false,导致布局条件不满足。

方法LocalFrameView::isVisuallyNonEmpty代码如下:

bool isVisuallyNonEmpty() const { return m_contentQualifiesAsVisuallyNonEmpty; }

这个方法返回了变量m_contentQualifiesAsVisuallyNonEmpty的值,这个变量被设置为true的方法为LocalFrameView::checkAndDispatchDidReachVisuallyNonEmptyState,代码如下:

void LocalFrameView::checkAndDispatchDidReachVisuallyNonEmptyState()
{
    // 1. qualifiesAsVisuallyNonEmpty 回调函数
    auto qualifiesAsVisuallyNonEmpty = [&] {
        ...
        // 2. isMoreContentExpected 回调函数
        auto isMoreContentExpected = [&]() {
            ...
            auto& resourceLoader = documentLoader->cachedResourceLoader();
            // 3. 如果外部样式表已经下载成功,页面没有其他请求,这里返回 false,说明没有其他内容需要加载了
            if (!resourceLoader.requestCount())
                return false;

            // 4. 如果页面还有其他请求,代码运行到这里
            auto& resources = resourceLoader.allCachedResources();
            for (auto& resource : resources) {
                ...
                if (resource.value->type() == CachedResource::Type::CSSStyleSheet || resource.value->type() == CachedResource::Type::FontResource)
                    // 5. 如果正在加载的请求里面有样式表类型后者字体资源,那么这里返回 true,说明还需要等待这些资源加载
                    return true;
            }
            return false;
        };

        // Finished parsing the main document and we still don't yet have enough content. Check if we might be getting some more.
        if (finishedParsingMainDocument)
            // 6. 调用 isMoreContentExpected 回调函数
            return !isMoreContentExpected();

        return false;
    };

    if (m_contentQualifiesAsVisuallyNonEmpty)
        return;

    // 7. 调用 qualifiesAsVisuallyNonEmpty 回调函数
    if (!qualifiesAsVisuallyNonEmpty())
        return;

    // 8. 这里设置 m_contentQualifiesAsVisuallyNonEmpty 为 true
    m_contentQualifiesAsVisuallyNonEmpty = true;
    ...
}

上面代码注释 1 处定义了qualifiesAsVisuallyNonEmpty回调函数。

代码注释 2 定义了isMoreContentExpected回调函数。

代码注释 7 处调用了回调函数qualifiesAsVisuallyNonEmpty

qualifiesAsVisuallyNonEmpty回调函数里面,调用了回调函数isMoreContentExpected,如代码注释 6 所示。

回调函数isMoreContentExpected里面会判断当前是否还有其他请求,如果代码注释 3 所示。如果没有其他请求了,isMoreContentExpected 函数返回 false,表明没有其他内容要加载了。因此,此时代码会运行到代码注释 8 处,将变量m_contentQualifiesAsVisuallyNonEmpty设置为true

如果页面还有其他资源的请求,比如外部样式表还在请求,那么回调函数isMoreContentExpected会运行到代码注释 5 处。这里会判断请求资源类型是否是样式表或者字体资源,如果是这两种资源之一,这里返回 true。这样,代码会运行到注释 7 处,直接返回而不设置变量m_contentQualifiesAsVisuallyNonEmpty

因此,如果位于<body>标签的外部样式表还在下载,那么就会在上面代码注释 7 返回,所以不会进行布局。

如果外部样式表下载成功并解析之后,会调用Document::resolveStyle方法,这个方法会进行样式表的匹配,渲染树的构建,布局的调用,代码如下:

void Document::resolveStyle(ResolveStyleType type)
{
        ...
        Style::TreeResolver resolver(*this, WTFMove(m_pendingRenderTreeUpdate));
        // 1. 样式表匹配
        auto styleUpdate = resolver.resolve();
        ...
        if (styleUpdate) {
            // 2. 构建渲染树
            updateRenderTree(WTFMove(styleUpdate));
            // 3. 设置 m_contentQualifiesAsVisuallyNonEmpty = true 的方法在这里调用
            frameView.styleAndRenderTreeDidChange();
        }
        ...
        if (m_renderView->needsLayout())
            // 4. 调用布局方法
            frameView.layoutContext().scheduleLayout();
        ...
}

上面代码注释 1 处进行样式表匹配。

代码注释 2 进行渲染树构建。

代码注释 3 这个方法内部会调用LocalFrameView::checkAndDispatchDidReachVisuallyNonEmptyState方法设置变量m_contentQualifiesAsVisuallyNonEmpty。由于外部样式表已经下载成功,此时变量m_contentQualifiesAsVisuallyNonEmpty就会被设置成true

由于上面的设置,后续代码注释 4 处的布局方法调用就可以成功了。

这种情形下匹配时机如下图所示:
image

标签:...,匹配,外部,代码,注释,样式表,WebKit,Inside
From: https://www.cnblogs.com/chaoguo1234/p/17743557.html

相关文章

  • WebKit Inside: CSS 样式表的解析
    CSS全称为层叠样式表(Cascading Style Sheet),用来定义HTML文件最终显示的外观。为了理解CSS的加载与解析,需要对CSS样式表的组成,尤其是CSSSelector有所了解,相关部分可以参看这里。HTML文件里面引入CSS样式表有3种方式:1外部样式表2内部样式表3行内样式不......
  • WebKit Inside: CSS 样式表解码字符集
    CSS样式表引入有3种方式:外部样式表、内部样式表、行内样式,不同的引入方式,解码样式表的字符集原理不一样。外部样式表外部样式表由link标签引入,当WebKit解析到link标签时就会构造CachedCSSStyleSheet对象。这个对象持有CachedResourceRequest对象和TextResourceDec......
  • Datainside数据分析,基于大数据分析学生成绩综合评价
    Datainside是一种基于大数据分析的学生成绩综合评价方法,通过对海量学生成绩数据进行深度挖掘和分析,为学生的学习表现提供全面、客观的评价。以下是对Datainside数据分析学生成绩综合评价的详细描述:1.数据收集:Datainside首先需要收集大量学生成绩数据,包括不同科目、不同年级、不......
  • 【matplotlib基础】--样式表
    Matplotlib库由于诞生的比较早,所以其默认的显示样式很难符合现在的审美,这也是它经常为人诟病的地方。不过,经过版本更迭之后,现在Matplotlib已经内置了很多样式表,通过使用不同的样式表,可以整体改变绘制图形的风格,不用再调整一个个显示参数。1.样式表的使用1.1.所有内置样式......
  • CSS gradient渐变之webkit核心浏览器下的使用
    [color=red][b]一、关于渐变[/b][/color]渐变是一种应用于平面的视觉效果,可以从一种颜色逐渐地转变成另外一种颜色,故可以创建类似于彩虹的效果渐变可以应用在任何可以使用图片的地方。例如,您可以指定一个这么一个渐变:顶部的颜色是红色,中间的是蓝色,底部为黄色来作为div的背景色。渐......
  • 数据驱动决策,Datainside引领可视化报告新时代
     数据驱动决策已经成为当今社会中的重要趋势,而Datainside作为一家数据可视化报告平台,正引领着可视化报告的新时代。下面是关于Datainside平台的详细描述。数据集成与连接Datainside平台提供强大的数据集成和连接功能。它可以与各类数据源进行无缝连接,包括企业内部的数据仓库......
  • Qt-QWidget对样式表设置边框无效的解决方法
    相关资料:https://www.ngui.cc/el/1647992.html?action=onClick   1、现象在对QWidget使用样式表时无效QWidget#MyWgt{border:1pxsolidgray;}2、原因原因是QWidget只支持background、background-clip和background-origin属性。官方说明3、解决方法3.1使用QFrame代替QW......
  • Android官方资料--Inside OTA Packages
    InsideOTAPackagesINTHISDOCUMENTEdifysyntaxBuilt-infunctionsThesystembuildstheupdaterbinaryfrombootable/recovery/updater andusesitinanOTApackage.Thepackageitselfisa.zipfile(ota_update.zip,incremental_ota_update.zip)that......
  • 解决vue项目build的时候报错Warning: Accessing non-existent property ‘cat‘ of mo
    *正在执行任务:npmrunbuild>[email protected]>nodebuild/build.js-buildingforproduction...(node:8992)Warning:Accessingnon-existentproperty'cat'ofmoduleexportsinsidecirculardependency(Use`node--trace-warnings...`t......
  • Webkit 实现页面滚动条美化
    当页面或者某个容器布局内容超出过后,就会有滚动条,但默认的有点丑,经常需要自己来美化一下,这里做个笔记吧./*美化全局的滚动条*/::-webkit-scrollbar{width:4px;height:6px;}::-webkit-scrollbar-corner{display:block;}::-webkit-scrollbar-thumb{......