对浏览器加载资源有很多不确定性,例如:
- css/font的资源的优化级会比img高,资源的优化级是怎么确定的呢?
- 资源优先级又是如何影响加载的先后顺序的?
- 有几种情况可能会导致资源被阻止加载?
通过源码可以找到答案。此次源码解读基于Chromium 64(10月28日更新的源码)。
下面通过加载资源的步骤,依次说明。
1. 开始加载
通过以下命令打开Chromium,同时打开一个网页:
chromium --renderer-startup-dialog https://www.baidu.com
Chrome会在DocumentLoader.cpp里面通过以下代码去加载:
main_resource_ =
RawResource::FetchMainResource(fetch_params, Fetcher(), substitute_data_);
页面资源属于MainRescouce,Chrome把Rescource归为以下几种:
enum Type : uint8_t {
kMainResource,
kImage,
kCSSStyleSheet,
kScript,
kFont,
kRaw,
kSVGDocument,
kXSLStyleSheet,
kLinkPrefetch,
kTextTrack,
kImportResource,
kMedia, // Audio or video file requested by a HTML5 media element
kManifest,
kMock // Only for testing
};
除了常见的image/css/js/font之外,我们发现还有像textTrack的资源,这个是什么东西呢?这个是video的字幕,使用webvtt格式:
<video controls poster="/images/sample.gif">
<source src="sample.mp4" type="video/mp4">
<track kind="captions" src="sampleCaptions.vtt" srclang="en">
</video>
还有动态请求ajax属于Raw类型。因为ajax可以请求多种资源。
MainResource包括location即导航输入地址得到的页面、使用frame/iframe嵌套的、通过超链接点击的页面以及表单提交这几种。
接着交给稍底层的ResourceFecher去加载,所有资源都是通过它加载:
fetcher->RequestResource(
params, RawResourceFactory(Resource::kMainResource), substitute_data)
在这个里面会先对请求做预处理。
2. 预处理请求
每发个请求会生成一个ResourceRequest对象,这个对象包含了http请求的所有信息:

包括url、http header、http body等,还有请求的优先级信息等:

然后会根据页面的加载策略对这个请求做一些预处理,如下代码:
PrepareRequestResult result = PrepareRequest(params, factory, substitute_data,
identifier, blocked_reason);
if (result == kAbort)
return nullptr;
if (result == kBlock)
return ResourceForBlockedRequest(params, factory, blocked_reason);
prepareRequest会做两件事情,一件是检查请求是否合法,第二件是把请求做些修改。如果检查合法性返回kAbort或者kBlock,说明资源被废弃了或者被阻止了,就不去加载了。
被block的原因可能有以下几种:
enum class ResourceRequestBlockedReason {
kCSP, // CSP内容安全策略检查
kMixedContent, // mixed content
kOrigin, // secure origin
kInspector, // devtools的检查器
kSubresourceFilter,
kOther,
kNone
};
源码里面会在这个函数做合法性检查:
blocked_reason = Context().CanRequest(/*参数省略*/);
if (blocked_reason != ResourceRequestBlockedReason::kNone) {
return kBlock;
}
CanRequest函数会相应地检查以下内容:
(1)CSP(Content Security Policy)内容安全策略检查
CSP是减少XSS攻击一个策略。如果我们只允许加载自己域的图片的话,可以加上下面这个meta标签:
<meta http-equiv="Content-Security-Policy" content="img-src 'self';">
或者是后端设置这个http响应头。
self表示本域,如果加载其它域的图片浏览器将会报错:

所以这个可以防止一些XSS注入的跨域请求。
源码里面会检查该请求是否符合CSP的设定要求:
const ContentSecurityPolicy* csp = GetContentSecurityPolicy();
if (csp && !csp->AllowRequest(
request_context, url, options.content_security_policy_nonce,
options.integrity_metadata, options.parser_disposition,
redirect_status, reporting_policy, check_header_type)) {
return ResourceRequestBlockedReason::kCSP;
}
如果有CSP并且AllowRequest没有通过的话就会返回堵塞的原因。具体的检查过程是根据不同的资源类型去获取该类资源资源的CSP设定进行比较。
接着会根据CSP的要求改变请求:
ModifyRequestForCSP(request);
主要是升级http为https。
(2)upgrade-insecure-requests
如果设定了以下CSP规则:
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
那么会将网页的http请求强制升级为https,这是通过改变request对象实现的:
url.SetProtocol("https");
if (url.Port() == 80)
url.SetPort(443);
resource_request.SetURL(url);
包括改变url的协议和端口号。
(3)Mixed Content混合内容block
在https的网站请求http的内容就是Mixed Content,例如加载一个http的JS脚本,这种请求通常会被浏览器堵塞掉,因为http是没有加密的,容易受到中间人的攻击,如修改JS的内容,从而控制整个https的页面,而图片之类的资源即使内容被修改可能只是展示出问题,所以默认没有block掉。源码里面会检查Mixed Content的内容:
if (ShouldBlockFetchByMixedContentCheck(request_context, frame_type,
resource_request.GetRedirectStatus(),
url, reporting_policy))
return ResourceRequestBlockedReason::kMixedContent;
在源码里面,以下4种资源是optionally-blockable(被动混合内容):
// "Optionally-blockable" mixed content
case WebURLRequest::kRequestContextAudio:
case WebURLRequest::kRequestContextFavicon:
case WebURLRequest::kRequestContextImage:
case WebURLRequest::kRequestContextVideo:
return WebMixedContentContextType::kOptionallyBlockable;
什么叫被动混合内容呢?W3C文档是这么说的:那些不会打破页面重要部分,风险比较低的,但是使用频率又比较高的Mixed Content内容。
而剩下的其它所有资源几乎都是blockable的,包括JS/CSS/Iframe/XMLHttpRequest等:

我们注意到img srcset里的资源也是默认会被阻止的,即下面的img会被block:
<img srcset="http://fedren.com/test-1x.png 1x, http://fedren.com/test-2x.png 2x" alt>
但是使用src的不会被block:
<img src="http://fedren.com/images/sell/icon-home.png" alt>
如下图所示:

这就是optionally-blockable和blocakable资源的区分。
对于被动混合内容,如果设置strick mode:
<meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">
那么即使是optionally的也会被block掉:
case WebMixedContentContextType::kOptionallyBlockable:
allowed = !strict_mode;
if (allowed) {
content_settings_client->PassiveInsecureContentFound(url);
client->DidDisplayInsecureContent();
}
break;
上面代码,如果strick_mode是true,allowed就是false,被动混合内容就会被阻止。
而对于主动混合内容,如果用户设置允许加载:

那么也是可以加载的:
case WebMixedContentContextType::kBlockable: {
// Strictly block subresources that are mixed with respect to their
// subframes, unless all insecure content is allowed. This is to avoid the
// following situation: https://a.com embeds https://b.com, which loads a
// script over insecure HTTP. The user opts to allow the insecure content,
// thinking that they are allowing an insecure script to run on
// https://a.com and not realizing that they are in fact allowing an
// insecure script on https://b.com.
bool should_ask_embedder =
!strict_mode && settings &&
(!settings->GetStrictlyBlockBlockableMixedContent() ||
settings->GetAllowRunningOfInsecureContent());
allowed = should_ask_embedder &&
content_settings_client->AllowRunningInsecureContent(
settings && settings->GetAllowRunningOfInsecureContent(),
security_origin, url);
break;
代码倒数第4行会去判断当前的client即当前页面的设置是否允许加载blockable的资源。另外源码注释还提到了一种特殊的情况,就是http://a.com的页面包含了http://b.com的页面,http://b.com允许加载blockable的资源,http://a.com在非strick mode的时候页面是允许加载的,但是如果http://a.com是strick mode,那么将不允许加载。
并且如果页面设置了strick mode,用户设置的允许blockable资源加载的设置将会失效:
// If we're in strict mode, we'll automagically fail everything, and
// intentionally skip the client checks in order to prevent degrading the
// site's security UI.
bool strict_mode =
mixed_frame->GetSecurityContext()->GetInsecureRequestPolicy() &
kBlockAllMixedContent ||
settings->GetStrictMixedContentChecking();
(4)Origin Block
这个主要是svg使用use的获取svg资源的时候必须不能跨域,如下以下资源将会被阻塞:
<svg>
<use href=
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除