如何使用 CSS 让你的浏览器卡死

@chokcoco 2019-03-07 12:27:30发表于 chokcoco/iCSS

本文写作中,未完成...

使用 CSS 让你的浏览器卡死,听起来好像很奇怪。这里的卡死包括但不局限于浏览器crash崩溃浏览器无响应内存不足等等浏览器已经失去响应,用户已经卡到无法进行操作的行为。

能造成卡死无响应的,前端而言,通常都是 JavaScript 脚本的问题,死循环、内存泄漏、堆栈溢出等等导致的。

今天,我们就来聊聊不涉及 JS,仅仅是使用 CSS 的情况下,有没有可能让浏览器崩溃或者卡死。当然,使用 CSS 让浏览器卡死不是说页面同时渲染几千万个 div,给它们加上各种耗性能样式,那谁顶得住。

这里说的使用 CSS 让浏览器卡死,应该是局限在只使用少量几个标签,用特定 CSS 代码让 webview 在极短时间内卡死或者崩溃。

CraSSh

第一个方法,额,源自 CraSSh,巧妙的使用 calc() 以及CSS变量 var(--xx)

原理就是通过将一个指数级递增长的对 calc() 以及 var(--xx) 调用的表达式,赋给一个具体的元素样式。现代浏览器在短时间内进行大量的运算,将导致内存不足而使浏览器崩溃。

额,描述很费力,具体看一下代码,我们只有一个简单的 div:

<div></div>

CSS 样式如下:

div {
  --initial-level-0: calc(1vh + 1% + 1px + 1em + 1vw + 1cm);

  --level-1: calc(var(--initial-level-0) + var(--initial-level-0));
  --level-2: calc(var(--level-1) + var(--level-1));
  --level-3: calc(var(--level-2) + var(--level-2));
  --level-4: calc(var(--level-3) + var(--level-3));
  --level-5: calc(var(--level-4) + var(--level-4));
  --level-6: calc(var(--level-5) + var(--level-5));
  --level-7: calc(var(--level-6) + var(--level-6));
  --level-8: calc(var(--level-7) + var(--level-7));
  --level-9: calc(var(--level-8) + var(--level-8));
  --level-10: calc(var(--level-9) + var(--level-9));
  --level-11: calc(var(--level-10) + var(--level-10));
  --level-12: calc(var(--level-11) + var(--level-11));
  --level-13: calc(var(--level-12) + var(--level-12));
  --level-14: calc(var(--level-13) + var(--level-13));
  --level-15: calc(var(--level-14) + var(--level-14));
  --level-16: calc(var(--level-15) + var(--level-15));
  --level-17: calc(var(--level-16) + var(--level-16));
  --level-18: calc(var(--level-17) + var(--level-17));
  --level-19: calc(var(--level-18) + var(--level-18));
  --level-20: calc(var(--level-19) + var(--level-19));
  --level-21: calc(var(--level-20) + var(--level-20));
  --level-22: calc(var(--level-21) + var(--level-21));
  --level-23: calc(var(--level-22) + var(--level-22));
  --level-24: calc(var(--level-23) + var(--level-23));
  --level-25: calc(var(--level-24) + var(--level-24));
  --level-26: calc(var(--level-25) + var(--level-25));
  --level-27: calc(var(--level-26) + var(--level-26));
  --level-28: calc(var(--level-27) + var(--level-27));
  --level-29: calc(var(--level-28) + var(--level-28));
  --level-30: calc(var(--level-29) + var(--level-29));

  --level-final: calc(var(--level-30) + 1px);

    border-width: var(--level-final);                                 
    border-style: solid;
}

可以看到,从 --level-1 --level-30,每次的运算量都是成倍的增长,最终到 --level-final 变量,展开将有 2^30 = 1073741824--initial-level-0 表达式的内容。

并且,每个 --initial-level-0 表达式的内容 -- calc(1vh + 1% + 1px + 1em + 1vw + 1cm),在浏览器解析的时候,也已经足够复杂。

混合在一起,就导致了浏览器的 BOOM,为了能看到效果,我们将上述样式赋给某个元素被 hover 的时候,得到如下效果:

css-crash

你可以点下面的链接 Demo 尝试一下,更详细的原理介绍可以戳原文链接

CodePen Demo -- CraSSh

box-shadow

box-shadow,在众多 CSS 属性中属于耗性能样式。

同时,box-shadow 有个特点,单个元素可以叠加多重阴影。所以即便只有一个 <div> 标签,通过填充 N 重阴影,当 N 足够大时,即可以轻易让浏览器卡死。

当然,为了有趣一点。我们希望填充的多重阴影有意义而不是毫无章法。有一个小技巧很多同学都知道,就是使用阴影去模拟一张图片。

理论上任意一张图片,每一个像素点都可以由一重 1px*1px 的 box-shadow 来表示。

为了完成这个任务, canvas 刚好提供了一个方法 CanvasRenderingContext2D.getImageData 可以获取到图片每一个像素点的 rgba 值,那么图片转为一个完全由 box-shadow 表示的图片是完全可行的。

下面这个小插件可以实现图片向单div标签的转换:

img2Div

我尝试转换了一张 1920*1080 的图片,也就是相当于给单个标签 2073600 重阴影,在等待的过程中 JavaScript 运算已经接近崩溃。转换完成后,图片正确被渲染,但是整个页面卡到无法操作,感兴趣的可以自行尝试下。:)

mix-blend-mod

mix-blend-mod 混合模式,另外一个性能杀手。

CodePen Demo -- CSS WAVE MOVE

filter


最后,新开通的公众号求关注,形式希望是更短的篇幅,质量更高一些的技巧类文章,包括但不局限于 CSS:

image