Chromium网页URL载入过程分析
Chromium在Browser进程中为网页创建了一个Frame Tree之后,会将网页的URL发送给Render进程进行载入。Render进程接收到网页URL载入请求之后,会做一些必要的初始化工作,然后请求Browser进程下载网页的内容。Browser进程一边下载网页内容。一边又通过共享内存将网页内容传递给Render进程解析。也就是创建DOM Tree。本文接下来就分析网页URL的载入过程。
老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!
《Android系统源码情景分析》一书正在进击的程序猿网(http://0xcc0xcd.com)中连载。点击进入!
Render进程之所以要请求Browser进程下载网页的内容,是由于Render进程没有网络訪问权限。
出于安全考虑。Chromium将Render进程启动在一个受限环境中,使得Render进程没有网络訪问权限。那为什么不是Browser进程主动下载好网页内容再交给Render进程解析呢?
这是由于Render进程是通过WebKit载入网页URL的,WebKit不关心自己所在的进程是否有网络訪问权限,它通过特定的接口訪问网络。
这个特定接口由WebKit的使用者。也就是Render进程中的Content模块实现。
Content模块在实现这个接口的时候,会通过IPC请求Browser进程下载网络的内容。这样的设计方式使得WebKit能够灵活地使用:既能够在有网络訪问权限的进程中使用,也能够在没有网络訪问权限的进程中使用。而且使用方式是统一的。
从前面Chromium Frame Tree创建过程分析一文能够知道,Browser进程中为要载入的网页创建了一个Frame Tree之后,会向Render进程发送一个类型为FrameMsg_Navigate的IPC消息。Render进程接收到这个IPC消息之后。处理流程如图1所看到的:
图1 网页URL载入过程
Render进程运行了一些初始化工作之后,就向Browser进程发送一个类型为ResourceHostMsg_RequestResource的IPC消息。Browser进程收到这个IPC消息之后。就会通过HTTP协议请求Webserver将网页的内容返回来。
请求得到响应后。Browser进程就会创建一块共享内存,而且通过一个类型为ResourceMsg_SetDataBuffer的IPC消息将这块共享内存传递给Render进程的。
以后每当下载到新的网页内容,Browser进程就会将它们写入到前面创建的共享内存中去,而且发送Render进程发送一个类型为ResourceMsg_DataReceived的IPC消息。Render进程接收到这个IPC消息之后,就会从共享内存中读出Browser进程写入的内容。而且进行解析。也就是创建一个DOM Tree。这个过程一直持续到网页内容下载完毕为止。
接下来,我们就从Render进程接收类型为FrameMsg_Navigate的IPC消息開始分析网页URL的载入过程。
Render进程是通过RenderFrameImpl类的成员函数OnMessageReceived接收类型为FrameMsg_Navigate的IPC消息的。例如以下所看到的:
bool RenderFrameImpl::OnMessageReceived(const IPC::Message& msg) {
...... bool handled = true;
IPC_BEGIN_MESSAGE_MAP(RenderFrameImpl, msg)
IPC_MESSAGE_HANDLER(FrameMsg_Navigate, OnNavigate)
......
IPC_END_MESSAGE_MAP() return handled;
}
这个函数定义在文件external/chromium_org/content/renderer/render_frame_impl.cc中。
RenderFrameImpl类的成员函数OnMessageReceived将类型为FrameMsg_Navigate的IPC消息分发给另外一个成员函数OnNavigate处理,后者的实现例如以下所看到的:
void RenderFrameImpl::OnNavigate(const FrameMsg_Navigate_Params& params) {
...... bool is_reload = RenderViewImpl::IsReload(params);
...... WebFrame* frame = frame_;
...... if (is_reload) {
......
} else if (params.page_state.IsValid()) {
......
} else if (!params.base_url_for_data_url.is_empty()) {
......
} else {
// Navigate to the given URL.
WebURLRequest request(params.url);
...... frame->loadRequest(request); ......
} ......
}
这个函数定义在文件external/chromium_org/content/renderer/render_frame_impl.cc中。
从前面Chromium Frame Tree创建过程分析一文能够知道,RenderFrameImpl类的成员变量frame_指向的是一个WebLocalFrameImpl对象。假设当前正在处理的RenderFrameImpl对象还没有载入过URL,而且当前要载入的URL不为空,RenderFrameImpl类的成员函数OnNavigate会调用成员变量frame_指向的WebLocalFrameImpl对象的成员函数loadRequest载入指定的URL。
WebLocalFrameImpl类的成员函数loadRequest的实现例如以下所看到的:
void WebLocalFrameImpl::loadRequest(const WebURLRequest& request)
{
......
const ResourceRequest& resourceRequest = request.toResourceRequest(); if (resourceRequest.url().protocolIs("javascript")) {
loadJavaScriptURL(resourceRequest.url());
return;
} frame()->loader().load(FrameLoadRequest(0, resourceRequest));
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp中。
假设參数request描写叙述的URL指定的协议是"javascript",那么表示要载入的是一段JavaScript。这时候WebLocalFrameImpl类的成员函数loadRequest会调用另外一个成员函数loadJavaScriptURL载入这段JavaScript。
在其他情况下,WebLocalFrameImpl类的成员函数loadRequest首先调用成员函数frame获得成员变量m_frame描写叙述的一个LocalFrame对象,接着又调用这个LocalFrame对象的成员函数loader获得其成员变量m_loader描写叙述的一个FrameLoader对象。有了这个FrameLoader对象之后。就调用它的成员函数load载入參数request描写叙述的URL。
WebLocalFrameImpl类的成员变量m_frame描写叙述的LocalFrame对象和LocalFrame类的成员变量m_loader描写叙述的FrameLoader对象的创建过程,能够參考前面Chromium Frame Tree创建过程分析一文。接下来我们继续分析FrameLoader类的成员函数load的实现,例如以下所看到的:
void FrameLoader::load(const FrameLoadRequest& passedRequest)
{
...... FrameLoadRequest request(passedRequest);
...... FrameLoadType newLoadType = determineFrameLoadType(request);
NavigationAction action(request.resourceRequest(), newLoadType, request.formState(), request.triggeringEvent());
...... loadWithNavigationAction(action, newLoadType, request.formState(), request.substituteData(), request.clientRedirect()); ......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/FrameLoader.cpp中。
FrameLoader类的成员函数load主要是调用另外一个成员函数loadWithNavigationAction载入參数passedRequest描写叙述的URL。
FrameLoader类的成员函数loadWithNavigationAction的实现例如以下所看到的:
void FrameLoader::loadWithNavigationAction(const NavigationAction& action, FrameLoadType type, PassRefPtrWillBeRawPtr<FormState> formState, const SubstituteData& substituteData, ClientRedirectPolicy clientRedirect, const AtomicString& overrideEncoding)
{
...... const ResourceRequest& request = action.resourceRequest();
...... m_policyDocumentLoader = client()->createDocumentLoader(m_frame, request, substituteData.isValid() ? substituteData : defaultSubstituteDataForURL(request.url()));
...... m_provisionalDocumentLoader = m_policyDocumentLoader.release();
...... m_provisionalDocumentLoader->startLoadingMainResource();
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/FrameLoader.cpp中。
FrameLoader类的成员函数loadWithNavigationAction首先调用成员函数client获得一个FrameLoaderClientImpl对象。接着再调用这个FrameLoaderClientImpl对象的成员函数createDocumentLoader为參数action描写叙述的URL创建了一个WebDataSourceImpl对象,而且保存在成员变量m_policyDocumentLoader中。
关于FrameLoader类的成员函数client和FrameLoaderClientImpl类的成员函数createDocumentLoader的实现。能够參考前面Chromium Frame Tree创建过程分析一文。
FrameLoader类的成员函数loadWithNavigationAction接下来又将成员变量m_policyDocumentLoader描写叙述的WebDataSourceImpl对象转移到另外一个成员变量m_provisionalDocumentLoader中。最后调用这个WebDataSourceImpl对象的成员函数startLoadingMainResource载入參数action描写叙述的URL。
WebDataSourceImpl类的成员函数startLoadingMainResource是从父类DocumentLoader继承下来的,它的实现例如以下所看到的:
void DocumentLoader::startLoadingMainResource()
{
...... FetchRequest cachedResourceRequest(request, FetchInitiatorTypeNames::document, mainResourceLoadOptions);
m_mainResource = m_fetcher->fetchMainResource(cachedResourceRequest, m_substituteData);
...... m_mainResource->addClient(this); ......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/DocumentLoader.cpp中。
从前面Chromium Frame Tree创建过程分析一文能够知道,DocumentLoader类的成员变量m_fetcher描写叙述的是一个ResourceFetcher对象,DocumentLoader类的成员函数startLoadingMainResource调用这个ResourceFetcher对象的成员函数fetchMainResource请求载入本地变量cachedResourceRequest描写叙述的资源。这个资源描写叙述的即为上一步指定要载入的URL。
ResourceFetcher类的成员函数fetchMainResource运行结束后。会返回一个RawResource对象。这个RawResource对象保存在WebDataSourceImpl类的成员变量m_mainResource中。这个RawResource对象描写叙述的是一个异步载入的资源。DocumentLoader类的成员startLoadingMainResource调用它的成员函数addClient将当前正在处理的DocumentLoader对象加入到它的内部去,用来获得异步载入的资源数据,也就是本地变量cachedResourceRequest描写叙述的URL相应的网页内容。
RawResource类的成员函数addClient是从父类Resource继承下来的,它的实现例如以下所看到的:
void Resource::addClient(ResourceClient* client)
{
if (addClientToSet(client))
didAddClient(client);
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/Resource.cpp中。
Resource类的成员函数addClient调用另外一个成员函数addClientToSet将參数client描写叙述的一个DocumentLoader对象保存在内部,例如以下所看到的:
bool Resource::addClientToSet(ResourceClient* client)
{
...... m_clients.add(client);
return true;
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/Resource.cpp中。
Resource类的成员函数addClientToSet将參数client描写叙述的一个DocumentLoader保存在成员变量m_clients描写叙述的一个Hash Set中,以便当前正在处理的Resource对象描写叙述的网页内容从Webserver下载回来的时候。能够交给它处理。
接下来我们继续分析WebDataSourceImpl类的成员函数startLoadingMainResource调用成员变量m_fetcher描写叙述的ResourceFetcher对象的成员函数fetchMainResource载入本地变量cachedResourceRequest描写叙述的URL的过程,例如以下所看到的:
ResourcePtr<RawResource> ResourceFetcher::fetchMainResource(FetchRequest& request, const SubstituteData& substituteData)
{
......
return toRawResource(requestResource(Resource::MainResource, request));
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp中。
ResourceFetcher类的成员函数fetchMainResource调用另外一个成员函数requestResource载入參数request描写叙述的URL。
ResourceFetcher类的成员函数requestResource会返回一个RawResource对象给调用者,即ResourceFetcher类的成员函数fetchMainResource。
后者又会将这个RawResource对象返回给它的调用者。
ResourceFetcher类的成员函数requestResource的实现例如以下所看到的:
ResourcePtr<Resource> ResourceFetcher::requestResource(Resource::Type type, FetchRequest& request)
{
...... KURL url = request.resourceRequest().url();
...... const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer(), request.options());
switch (policy) {
......
case Load:
resource = createResourceForLoading(type, request, request.charset());
break;
.....
} ...... if (resourceNeedsLoad(resource.get(), request, policy)) {
...... if (!m_documentLoader || !m_documentLoader->scheduleArchiveLoad(resource.get(), request.resourceRequest()))
resource->load(this, request.options()); ......
} ...... return resource;
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp中。
ResourceFetcher类的成员函数requestResource首先调用成员函数createResourceForLoading为參数request描写叙述的URL创建一个RawResource对象。例如以下所看到的:
ResourcePtr<Resource> ResourceFetcher::createResourceForLoading(Resource::Type type, FetchRequest& request, const String& charset)
{
...... ResourcePtr<Resource> resource = createResource(type, request.resourceRequest(), charset); ......
return resource;
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp中。
ResourceFetcher类的成员函数createResourceForLoading调用函数createResource依据參数type和request创建一个RawResource对象,例如以下所看到的:
static Resource* createResource(Resource::Type type, const ResourceRequest& request, const String& charset)
{
switch (type) {
......
case Resource::MainResource:
case Resource::Raw:
case Resource::TextTrack:
case Resource::Media:
return new RawResource(request, type);
......
} ......
return 0;
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp中。
从前面的调用过程能够知道,參数type的值等于Resource::MainResource,因此函数createResource创建的是一个RawResource对象。
回到ResourceFetcher类的成员函数requestResource中。它调用成员函数createResourceForLoading为參数request描写叙述的URL创建了一个RawResource对象之后。接下来又调用成员函数resourceNeedsLoad推断该URL是否须要进行载入。假设须要进行载入,那么ResourceFetcher类的成员函数requestResource又会调用成员变量m_documentLoader描写叙述的一个DocumentLoader对象的成员函数scheduleArchiveLoad推断要载入的URL描写叙述的是否是一个存档文件。假设不是,那么就会调用前面创建的RawResource对象的成员函数load从Webserver下载相应的网页内容。
我们假设request描写叙述的URL须要进行载入,而且不是一个存档文件。因此接下来我们继续分析RawResource类的成员函数load的实现。RawResource类的成员函数load是从父类Resource继承下来的。它的实现例如以下所看到的:
void Resource::load(ResourceFetcher* fetcher, const ResourceLoaderOptions& options)
{
...... ResourceRequest request(m_resourceRequest);
...... m_loader = ResourceLoader::create(fetcher, this, request, options);
m_loader->start();
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/Resource.cpp中。
Resource类的成员变量m_resourceRequest描写叙述的是要载入的URL,Resource类的成员函数load首先调用ResourceLoader类的静态成员函数create为其创建一个ResourceLoader对象,例如以下所看到的:
PassRefPtr<ResourceLoader> ResourceLoader::create(ResourceLoaderHost* host, Resource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options)
{
RefPtr<ResourceLoader> loader(adoptRef(new ResourceLoader(host, resource, options)));
loader->init(request);
return loader.release();
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceLoader.cpp中。
从这里能够看到,ResourceLoader类的静态成员函数create创建的是一个ResourceLoader对象。这个ResourceLoader对象经过初始化之后,会返回给调用者。
回到Resource类的成员函数load中,它为要载入的URL创建了一个ResourceLoader对象之后,会调用这个ResourceLoader对象的成员函数start開始载入要载入的URL,例如以下所看到的:
void ResourceLoader::start()
{
...... m_loader = adoptPtr(blink::Platform::current()->createURLLoader());
......
blink::WrappedResourceRequest wrappedRequest(m_request);
m_loader->loadAsynchronously(wrappedRequest, this);
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceLoader.cpp中。
ResourceLoader类的成员函数start首先调用由Chromium的Content模块实现的一个blink::Platform接口的成员函数createURLLoader创建一个WebURLLoaderImpl对象,接着再调用这个WebURLLoaderImpl对象的成员函数loadAsynchronously对象成员变量m_request描写叙述的URL进行异步载入。
Chromium的Content模块的BlinkPlatformImpl类实现了blink::Platform接口,它的成员函数createURLLoader的实现例如以下所看到的:
WebURLLoader* BlinkPlatformImpl::createURLLoader() {
return new WebURLLoaderImpl;
}
这个函数定义在文件external/chromium_org/content/child/blink_platform_impl.cc中。
从这里能够看到。BlinkPlatformImpl类的成员函数createURLLoader创建的是一个WebURLLoaderImpl对象。这个WebURLLoaderImpl对象会返回给调用者。
接下来我们继续分析WebURLLoaderImpl类的成员函数loadAsynchronously异步载入一个URL的过程。例如以下所看到的:
void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest& request,
WebURLLoaderClient* client) {
...... context_->set_client(client);
context_->Start(request, NULL);
}
这个函数定义在文件external/chromium_org/content/child/web_url_loader_impl.cc中。
从前面的调用过程能够知道。參数client描写叙述的是一个ResourceLoader对象。
这个ResourceLoader对象会保存在WebURLLoaderImpl类的成员变量content_描写叙述的一个WebURLLoaderImpl::Context对象的内部。这是通过调用WebURLLoaderImpl::Context类的成员函数set_client实现的。例如以下所看到的:
class WebURLLoaderImpl::Context : public base::RefCounted<Context>,
public RequestPeer {
public:
...... void set_client(WebURLLoaderClient* client) { client_ = client; } private:
...... WebURLLoaderClient* client_; ......
};
这个函数定义在文件external/chromium_org/content/child/web_url_loader_impl.cc中。
WebURLLoaderImpl::Context类的成员函数set_client将參数client描写叙述的ResourceLoader对象保存在成员变量client_中。
回到WebURLLoaderImpl类的成员函数loadAsynchronously中,它接下来会继续调用成员变量content_描写叙述的一个WebURLLoaderImpl::Context对象的成员函数Start载入參数request描写叙述的URL,例如以下所看到的:
void WebURLLoaderImpl::Context::Start(const WebURLRequest& request,
SyncLoadResponse* sync_load_response) {
...... GURL url = request.url();
...... RequestInfo request_info;
......
request_info.url = url;
......
bridge_.reset(ChildThread::current()->resource_dispatcher()->CreateBridge(
request_info)); ...... if (bridge_->Start(this)) {
AddRef(); // Balanced in OnCompletedRequest
} else {
bridge_.reset();
}
}
这个函数定义在文件external/chromium_org/content/child/web_url_loader_impl.cc中。
WebURLLoaderImpl::Context类的成员函数Start首先调用当前Render进程的一个ChildThread单例的成员函数resource_dispatcher获得一个ResourceDispatcher对象。例如以下所看到的:
class CONTENT_EXPORT ChildThread
: public IPC::Listener,
public IPC::Sender,
public NON_EXPORTED_BASE(mojo::ServiceProvider) {
public:
...... ResourceDispatcher* resource_dispatcher() const {
return resource_dispatcher_.get();
} ...... private:
...... // Handles resource loads for this process.
scoped_ptr<ResourceDispatcher> resource_dispatcher_; ......
};
这个函数定义在文件external/chromium_org/content/child/child_thread.h中。
ChildThread类的成员函数resource_dispatcher返回的是成员变量resource_dispatcher_描写叙述的一个ResourceDispatcher对象。
回到WebURLLoaderImpl::Context类的成员函数Start中,它获得了一个ResourceDispatcher对象之后,接着调用这个ResourceDispatcher对象的成员函数CreateBridge创建一个IPCResourceLoaderBridge对象,例如以下所看到的:
ResourceLoaderBridge* ResourceDispatcher::CreateBridge(
const RequestInfo& request_info) {
return new IPCResourceLoaderBridge(this, request_info);
}
这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。
从这里能够看到,ResourceDispatcher类的成员函数CreateBridge创建的是一个IPCResourceLoaderBridge对象,而且会将这个IPCResourceLoaderBridge对象返回给调用者。
回到WebURLLoaderImpl::Context类的成员函数Start中,它获得了一个IPCResourceLoaderBridge对象之后。接着调用这个IPCResourceLoaderBridge对象的成员函数Start载入參数request描写叙述的URL。例如以下所看到的:
bool IPCResourceLoaderBridge::Start(RequestPeer* peer) {
...... // generate the request ID, and append it to the message
request_id_ = dispatcher_->AddPendingRequest(peer,
request_.resource_type,
request_.origin_pid,
frame_origin_,
request_.url,
request_.download_to_file); return dispatcher_->message_sender()->Send(
new ResourceHostMsg_RequestResource(routing_id_, request_id_, request_));
}
这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。
IPCResourceLoaderBridge类的成员变量dispatcher_描写叙述的是一个ResourceDispatcher对象,IPCResourceLoaderBridge类的成员函数Start首先调用这个ResourceDispatcher对象的成员函数AddPendingRequest将參数peer描写叙述的一个WebURLLoaderImpl::Context对象保存在内部。例如以下所看到的:
int ResourceDispatcher::AddPendingRequest(RequestPeer* callback,
ResourceType::Type resource_type,
int origin_pid,
const GURL& frame_origin,
const GURL& request_url,
bool download_to_file) {
// Compute a unique request_id for this renderer process.
int id = MakeRequestID();
pending_requests_[id] = PendingRequestInfo(callback,
resource_type,
origin_pid,
frame_origin,
request_url,
download_to_file);
return id;
}
这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。
ResourceDispatcher类的成员函数AddPendingRequest首先调用成员函数MakeRequestID生成一个Request ID,接着将參数callback描写叙述的一个WebURLLoaderImpl::Context对象封装在一个PendingRequestInfo对象中,而且以上述Request ID为键值。将这个PendingRequestInfo对象保存在成员变量pending_requests_描写叙述的一个Hash Map中。
回到IPCResourceLoaderBridge类的成员函数Start中,它接下来调用成员变量dispatcher_描写叙述的ResourceDispatcher对象的成员函数message_sender获得一个IPC::Sender对象,而且通过这个IPC::Sender对象向Browser进程发送一个类型为ResourceHostMsg_RequestResource的IPC消息。用来请求Browser进程下载成员变量request_描写叙述的URL相应的网页的内容。
在Browser进程中。类型为ResourceHostMsg_RequestResource的IPC消息是由ResourceDispatcherHostImpl类的成员函数OnMessageReceived进行接收的,例如以下所看到的:
bool ResourceDispatcherHostImpl::OnMessageReceived(
const IPC::Message& message,
ResourceMessageFilter* filter) {
......
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ResourceDispatcherHostImpl, message)
IPC_MESSAGE_HANDLER(ResourceHostMsg_RequestResource, OnRequestResource)
......
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP() ......
}
这个函数定义在文件external/chromium_org/content/browser/loader/resource_dispatcher_host_impl.cc中。
ResourceDispatcherHostImpl类的成员函数OnMessageReceived将类型为ResourceHostMsg_RequestResource的IPC消息分发给另外一个成员函数OnRequestResource处理,后者的实现例如以下所看到的:
void ResourceDispatcherHostImpl::OnRequestResource(
int routing_id,
int request_id,
const ResourceHostMsg_Request& request_data) {
BeginRequest(request_id, request_data, NULL, routing_id);
}
这个函数定义在文件external/chromium_org/content/browser/loader/resource_dispatcher_host_impl.cc中。
ResourceDispatcherHostImpl类的成员函数OnRequestResource调用另外一个成员函数BeginRequest開始下载參数request_data描写叙述的URL相应的网页内容,后者的实现例如以下所看到的:
void ResourceDispatcherHostImpl::BeginRequest(
int request_id,
const ResourceHostMsg_Request& request_data,
IPC::Message* sync_result, // only valid for sync
int route_id) {
...... // Construct the request.
net::CookieStore* cookie_store =
GetContentClient()->browser()->OverrideCookieStoreForRenderProcess(
child_id);
scoped_ptr<net::URLRequest> new_request;
new_request = request_context->CreateRequest(
request_data.url, request_data.priority, NULL, cookie_store);
...... scoped_ptr<ResourceHandler> handler(
CreateResourceHandler(
new_request.get(),
request_data, sync_result, route_id, process_type, child_id,
resource_context)); if (handler)
BeginRequestInternal(new_request.Pass(), handler.Pass());
}
这个函数定义在文件external/chromium_org/content/browser/loader/resource_dispatcher_host_impl.cc中。
ResourceDispatcherHostImpl类的成员函数BeginRequest首先从參数request_data取出要下载网页内容的URL。接着又将该URL封装在一个URLRequest对象中。
ResourceDispatcherHostImpl类的成员函数BeginRequest接下来又调用另外一个成员函数CreateResourceHandler创建了一个AsyncResourceHandler对象。这个AsyncResourceHandler对象用来异步接收和处理从Webserver下载回来的网页内容。
ResourceDispatcherHostImpl类的成员函数CreateResourceHandler的实现例如以下所看到的:
scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::CreateResourceHandler(
net::URLRequest* request,
const ResourceHostMsg_Request& request_data,
IPC::Message* sync_result,
int route_id,
int process_type,
int child_id,
ResourceContext* resource_context) {
// Construct the IPC resource handler.
scoped_ptr<ResourceHandler> handler;
if (sync_result) {
...... handler.reset(new SyncResourceHandler(request, sync_result, this));
} else {
handler.reset(new AsyncResourceHandler(request, this)); // The RedirectToFileResourceHandler depends on being next in the chain.
if (request_data.download_to_file) {
handler.reset(
new RedirectToFileResourceHandler(handler.Pass(), request));
}
} ...... // Install a CrossSiteResourceHandler for all main frame requests. This will
// let us check whether a transfer is required and pause for the unload
// handler either if so or if a cross-process navigation is already under way.
bool is_swappable_navigation =
request_data.resource_type == ResourceType::MAIN_FRAME;
// If we are using --site-per-process, install it for subframes as well.
if (!is_swappable_navigation &&
CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) {
is_swappable_navigation =
request_data.resource_type == ResourceType::SUB_FRAME;
}
if (is_swappable_navigation && process_type == PROCESS_TYPE_RENDERER)
handler.reset(new CrossSiteResourceHandler(handler.Pass(), request)); // Insert a buffered event handler before the actual one.
handler.reset(
new BufferedResourceHandler(handler.Pass(), this, request)); ...... handler.reset(
new ThrottlingResourceHandler(handler.Pass(), request, throttles.Pass())); return handler.Pass();
}
这个函数定义在文件external/chromium_org/content/browser/loader/resource_dispatcher_host_impl.cc中。
从前面的调用过程能够知道。參数sync_result的值等于NULL。因此ResourceDispatcherHostImpl类的成员函数CreateResourceHandler首先创建了一个AsyncResourceHandler对象,保存在本地变量handler中,表示要通过异步方式下载參数request描写叙述的URL。
接下来ResourceDispatcherHostImpl类的成员函数CreateResourceHandler又会依据情况创建其他的Handler对象。这些Handler对象会依次连接在一起。当中,后面创建的Handler对象位于前面创建的Handler对象的前面。
下载回来的网页内容将依次被这些Handler对象处理。
这意味着下载回来的网页内容最后会被最先创建的AsyncResourceHandler对象进行处理。
为了简单起见,后面我们仅仅分析这个AsyncResourceHandler对象处理下载回来的网页内容的过程,也就是假设ResourceDispatcherHostImpl类的成员函数CreateResourceHandler返回给调用者的是一个AsyncResourceHandler对象。
回到ResourceDispatcherHostImpl类的成员函数BeginRequest中,它最后调用另外一个成员函数BeginRequestInternal下载本地变量new_request描写叙述的URL相应的网页内容,例如以下所看到的:
void ResourceDispatcherHostImpl::BeginRequestInternal(
scoped_ptr<net::URLRequest> request,
scoped_ptr<ResourceHandler> handler) {
...... ResourceRequestInfoImpl* info =
ResourceRequestInfoImpl::ForRequest(request.get());
...... linked_ptr<ResourceLoader> loader(
new ResourceLoader(request.Pass(), handler.Pass(), this)); ..... StartLoading(info, loader);
}
这个函数定义在文件external/chromium_org/content/browser/loader/resource_dispatcher_host_impl.cc中。
ResourceDispatcherHostImpl类的成员函数BeginRequestInternal将參数request描写叙述的URL和參数handler描写叙述的AsyncResourceHandler对象封装在一个ResourceLoader对象后。调用另外一个成员函数StartLoading開始载入參数request描写叙述的URL。
ResourceDispatcherHostImpl类的成员函数StartLoading的实现例如以下所看到的:
void ResourceDispatcherHostImpl::StartLoading(
ResourceRequestInfoImpl* info,
const linked_ptr<ResourceLoader>& loader) {
...... loader->StartRequest();
}
这个函数定义在文件external/chromium_org/content/browser/loader/resource_dispatcher_host_impl.cc中。
ResourceDispatcherHostImpl类的成员函数StartLoading主要是调用參数loader描写叙述的ResourceLoader对象的成员函数StartRequest開始载入其内部封装的URL。
ResourceLoader类的成员函数StartRequest的实现例如以下所看到的:
void ResourceLoader::StartRequest() {
...... // Give the handler a chance to delay the URLRequest from being started.
bool defer_start = false;
if (!handler_->OnWillStart(request_->url(), &defer_start)) {
Cancel();
return;
} if (defer_start) {
deferred_stage_ = DEFERRED_START;
} else {
StartRequestInternal();
}
}
这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。
ResourceLoader类的成员变量handler_描写叙述的便是前面我们假设ResourceDispatcherHostImpl类的成员函数CreateResourceHandler返回的AsyncResourceHandler对象。ResourceLoader类的成员函数StartRequest调用这个AsyncResourceHandler对象的成员函数OnWillStart询问是要取消、延迟、还是立即下载当前正在处理的ResourceLoader对象封装的URL相应的网页内容。
我们假设是第三种情况,这时候ResourceLoader类的成员函数StartRequest就会立即调用另外一个成员函数StartRequestInternal下载当前正在处理的ResourceLoader对象封装的URL相应的网页内容。
ResourceLoader类的成员函数StartRequestInternal的实现例如以下所看到的:
void ResourceLoader::StartRequestInternal() {
...... request_->Start(); ......
}
这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。
ResourceLoader类的成员变量request_描写叙述的是前面在ResourceDispatcherHostImpl类的成员函数BeginRequest中创建的一个URLRequest对象。这个URLRequest对象封装了要下载的URL。ResourceLoader类的成员函数StartRequestInternal通过调用这个URLRequest对象的成员函数Start就能够启动下载网页的过程了。
URLRequest类是Chromium在Net模块中提供的一个类,用来运行具体的网络操作,也就是依据约定的协议请求Webserver返回指定URL相应的网页的内容。这个过程我们留给读者自行分析。
Webserver响应了请求之后。Chromium的Net模块会调用ResourceLoader类的成员函数OnResponseStarted,它的实现例如以下所看到的:
void ResourceLoader::OnResponseStarted(net::URLRequest* unused) {
...... if (request_->status().is_success()) {
StartReading(false); // Read the first chunk.
} ......
}
这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。
ResourceLoader类的成员函数OnResponseStarted检查Webserver的响应是否成功。比如Webserver是否依据HTTP协议返回了200响应。假设成功的话。那么接下来就会调用另外一个成员函数StartReading读出第一块数据。
ResourceLoader类的成员函数StartReading的实现例如以下所看到的:
void ResourceLoader::StartReading(bool is_continuation) {
int bytes_read = 0;
ReadMore(&bytes_read); ...... if (!is_continuation || bytes_read <= 0) {
OnReadCompleted(request_.get(), bytes_read);
} else {
// Else, trigger OnReadCompleted asynchronously to avoid starving the IO
// thread in case the URLRequest can provide data synchronously.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&ResourceLoader::OnReadCompleted,
weak_ptr_factory_.GetWeakPtr(),
request_.get(),
bytes_read));
}
}
这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。
ResourceLoader类的成员函数StartReading调用成员函数ReadMore读取Webserver返回来的数据。读出来的数据大小保存在本地变量bytes_read中。
ResourceLoader类的成员函数ReadMore的实现例如以下所看到的:
void ResourceLoader::ReadMore(int* bytes_read) {
...... scoped_refptr<net::IOBuffer> buf;
int buf_size;
if (!handler_->OnWillRead(&buf, &buf_size, -1)) {
Cancel();
return;
} ...... request_->Read(buf.get(), buf_size, bytes_read); ......
}
这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。
ResourceLoader类的成员函数ReadMore首先调用成员变量handler_描写叙述的一个AsyncResourceHandler对象的成员函数OnWillRead获取一个Buffer。这个Buffer用来保存从Webserver返回来的数据。这些数据能够通过调用ResourceLoader类的成员变量reqeust_描写叙述的一个URLRequest对象的成员函数Read获得。
AsyncResourceHandler对象的成员函数OnWillRead的实现例如以下所看到的:
bool AsyncResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) {
...... if (!EnsureResourceBufferIsInitialized())
return false; ......
char* memory = buffer_->Allocate(&allocation_size_);
..... *buf = new DependentIOBuffer(buffer_.get(), memory);
*buf_size = allocation_size_; ...... return true;
}
这个函数定义在文件external/chromium_org/content/browser/loader/async_resource_handler.cc中。
AsyncResourceHandler对象的成员函数OnWillRead首先调用成员函数EnsureResourceBufferIsInitialized确保成员变量buffer_指向了一块共享内存,然后再从这块共享内存中分配一块大小等于成员变量allocation_size_的值的缓冲区,用来返回给调用者保存从Webserver返回来的数据。
AsyncResourceHandler类的成员函数EnsureResourceBufferIsInitialized的实现例如以下所看到的:
bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() {
if (buffer_.get() && buffer_->IsInitialized())
return true; ...... buffer_ = new ResourceBuffer();
return buffer_->Initialize(kBufferSize,
kMinAllocationSize,
kMaxAllocationSize);
}
这个函数定义在文件external/chromium_org/content/browser/loader/async_resource_handler.cc中。
AsyncResourceHandler类的成员函数EnsureResourceBufferIsInitialized首先检查成员变量buffer_是否指向了一个ResourceBuffer对象,而且这个ResourceBuffer对象描写叙述的共享内存是否已经创建。
假设AsyncResourceHandler类的成员变量buffer_还没有指向一个ResourceBuffer对象,或者指向了一个ResourceBuffer对象,可是这个ResourceBuffer对象描写叙述的共享内存还没有创建。那么AsyncResourceHandler类的成员函数EnsureResourceBufferIsInitialized就会创建一个ResourceBuffer对象保存在成员变量buffer_中。而且调用这个ResourceBuffer对象的成员函数Initialize创建一块大小为kBufferSize的共享内存。
这块共享内存每次能够分配出来的缓冲区最小值为kMinAllocationSize,最大值为kMaxAllocationSize。
在Android平台上,调用ResourceBuffer类的成员函数Initialize创建的共享内存实际上是匿名共享内存。匿名共享内存能够通过Binder机制在两个进程之间进行共享。
这一点能够參考前面Android系统匿名共享内存Ashmem(Anonymous Shared Memory)在进程间共享的原理分析一文。这样Browser进程就能够通过这块匿名共享内存将下载回来的网页内容传递给Render进程处理。
这一步运行完毕后,回到ResourceLoader类的成员函数StartReading中,假设没有读出数据(表明数据已经下载完毕),或者參数is_continuation的值等于false(表示读出来的是第一个数据块),那么ResourceLoader类的成员函数StartReading就会调用成员函数OnReadCompleted立即进行下一步处理。其余情况下。为了避免当前(网络)线程被堵塞,ResourceLoader类的成员函数StartReading并不会立即调用成员函数OnReadCompleted处理读出来的数据,而是延后一个消息处理。也就是等ResourceLoader类的成员函数StartReading返回到Chromium的Net模块之后再作处理。
接下来我们继续分析ResourceLoader类的成员函数OnReadCompleted的实现。例如以下所看到的:
void ResourceLoader::OnReadCompleted(net::URLRequest* unused, int bytes_read) {
...... CompleteRead(bytes_read); ...... if (bytes_read > 0) {
StartReading(true); // Read the next chunk.
} else {
// URLRequest reported an EOF. Call ResponseCompleted.
DCHECK_EQ(0, bytes_read);
ResponseCompleted();
}
}
这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。
ResourceLoader类的成员函数OnReadCompleted首先调用成员函数CompleteRead处理当前读出来的数据,数据的大小由參数bytes_read描写叙述。假设当前读出来的数据的大小大于0。那么就表示数据还没读完,这时候就须要调用前面分析的成员函数StartReading继续进行读取。
注意,这时候传递成员函数StartReading的參数为true,表示不是第一次读取Webserver返回来的数据。
还有一方面,假设当前读出来的数据的大小小于等于0,那么就说明Webserver已经把全部的数据都返回来了,这时候ResourceLoader类的成员函数OnReadCompleted就调用另外一个成员函数ResponseCompleted结束读取数据。
接下来我们继续分析ResourceLoader类的成员函数CompleteRead的实现,以便了解Browser进程将下载回来的网页内容返回给Render进程处理的过程。例如以下所看到的:
void ResourceLoader::CompleteRead(int bytes_read) {
...... bool defer = false;
if (!handler_->OnReadCompleted(bytes_read, &defer)) {
Cancel();
} ......
}
这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。
ResourceLoader类的成员函数CompleteRead将读取出来的数据交给成员变量handler_描写叙述的一个AsyncResourceHandler对象处理。这是通过调用它的成员函数OnReadCompleted实现的。
AsyncResourceHandler类的成员函数OnReadCompleted的实现例如以下所看到的:
bool AsyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
...... if (!sent_first_data_msg_) {
base::SharedMemoryHandle handle;
int size;
if (!buffer_->ShareToProcess(filter->PeerHandle(), &handle, &size))
return false;
filter->Send(new ResourceMsg_SetDataBuffer(
GetRequestID(), handle, size, filter->peer_pid()));
sent_first_data_msg_ = true;
} int data_offset = buffer_->GetLastAllocationOffset(); int64_t current_transfer_size = request()->GetTotalReceivedBytes();
int encoded_data_length = current_transfer_size - reported_transfer_size_;
reported_transfer_size_ = current_transfer_size; filter->Send(new ResourceMsg_DataReceived(
GetRequestID(), data_offset, bytes_read, encoded_data_length)); ......
}
这个函数定义在文件external/chromium_org/content/browser/loader/async_resource_handler.cc。
当AsyncResourceHandler类的成员变量sent_first_data_msg_的值等于false的时候。表示当前正在处理的AsyncResourceHandler对象还没有向Render进程返回过从Webserver下载回来的网页内容。
这时候AsyncResourceHandler类的成员函数OnReadCompleted首先要向Render进程发送一个类型为ResourceMsg_SetDataBuffer的IPC消息。
这个IPC消息会将AsyncResourceHandler类的成员变量buffer_描写叙述的共享内存传递给Render进程,以便Render进程接下来能够通过这块共享内存读取从Webserver下载回来的网页内容。
最后,AsyncResourceHandler类的成员函数OnReadCompleted再向Render进程发送一个类型为ResourceMsg_DataReceived的IPC消息。
这个IPC消息告诉Render进程从前面所描写叙述的共享内存的什么位置開始读取多少数据。有了这些数据之后,Render进程就能够构建网页的DOM Tree了。
接下来我们就继续分析Render进程接收和处理类型为ResourceMsg_SetDataBuffer和ResourceMsg_DataReceived的IPC消息的过程。
Render进程是通过ResourceDispatcher类的成员函数DispatchMessage接收类型为ResourceMsg_SetDataBuffer和ResourceMsg_DataReceived的IPC消息的,例如以下所看到的:
void ResourceDispatcher::DispatchMessage(const IPC::Message& message) {
IPC_BEGIN_MESSAGE_MAP(ResourceDispatcher, message)
......
IPC_MESSAGE_HANDLER(ResourceMsg_SetDataBuffer, OnSetDataBuffer)
IPC_MESSAGE_HANDLER(ResourceMsg_DataReceived, OnReceivedData)
......
IPC_END_MESSAGE_MAP()
}
这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。
从这里能够看到,ResourceDispatcher类的成员函数DispatchMessage把类型为ResourceMsg_SetDataBuffer的IPC消息分发给成员函数OnSetDataBuffer处理。把类型为ResourceMsg_DataReceived的IPC消息分发给成员函数OnReceivedData处理。
ResourceDispatcher类的成员函数OnSetDataBuffer的实现例如以下所看到的:
void ResourceDispatcher::OnSetDataBuffer(int request_id,
base::SharedMemoryHandle shm_handle,
int shm_size,
base::ProcessId renderer_pid) {
......
PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
...... request_info->buffer.reset(
new base::SharedMemory(shm_handle, true)); // read only bool ok = request_info->buffer->Map(shm_size);
...... request_info->buffer_size = shm_size;
}
这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。
从前面的分析能够知道,Render进程在请求Browser进程下载指定URL相应的网页内容之前,会创建一个PendingRequestInfo对象。这个PendingRequestInfo对象以一个Request ID为键值保存在ResourceDispatcher类的内部。这个Request ID即为參数request_id描写叙述的Request ID。
因此。ResourceDispatcher类的成员函数OnSetDataBuffer能够通过參数request_id获得一个PendingRequestInfo对象。有了这个PendingRequestInfo对象之后。ResourceDispatcher类的成员函数OnSetDataBuffer就依据參数shm_handle描写叙述的句柄创建一个ShareMemory对象,保存在它的成员变量buffer中。
ResourceDispatcher类的成员函数OnSetDataBuffer最后调用上述ShareMemory对象的成员函数Map就可以将Browser进程传递过来的共享内存映射到当前进程的地址空间来,这样以后就能够直接从这块共享内存读出Browser进程下载回来的网页内容。
ResourceDispatcher类的成员函数OnReceivedData的实现例如以下所看到的:
void ResourceDispatcher::OnReceivedData(int request_id,
int data_offset,
int data_length,
int encoded_data_length) {
......
PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
......
if (request_info && data_length > 0) {
......
linked_ptr<base::SharedMemory> retain_buffer(request_info->buffer);
...... const char* data_start = static_cast<char*>(request_info->buffer->memory());
......
const char* data_ptr = data_start + data_offset;
...... // Check whether this response data is compliant with our cross-site
// document blocking policy. We only do this for the first packet.
std::string alternative_data;
if (request_info->site_isolation_metadata.get()) {
request_info->blocked_response =
SiteIsolationPolicy::ShouldBlockResponse(
request_info->site_isolation_metadata, data_ptr, data_length,
&alternative_data);
request_info->site_isolation_metadata.reset(); // When the response is blocked we may have any alternative data to
// send to the renderer. When |alternative_data| is zero-sized, we do not
// call peer's callback.
if (request_info->blocked_response && !alternative_data.empty()) {
data_ptr = alternative_data.data();
data_length = alternative_data.size();
encoded_data_length = alternative_data.size();
}
} if (!request_info->blocked_response || !alternative_data.empty()) {
if (request_info->threaded_data_provider) {
request_info->threaded_data_provider->OnReceivedDataOnForegroundThread(
data_ptr, data_length, encoded_data_length);
// A threaded data provider will take care of its own ACKing, as the
// data may be processed later on another thread.
send_ack = false;
} else {
request_info->peer->OnReceivedData(
data_ptr, data_length, encoded_data_length);
}
} ......
} ......
}
这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。
ResourceDispatcher类的成员函数OnReceivedData首先获得參数request_id相应的一个PendingRequestInfo对象。保存在本地变量request_info中。有了这个PendingRequestInfo对象之后,就能够依据參数data_offset和data_length从它的成员变量buffer描写叙述的共享内存中获得Browser进程下载回来的网页内容。
假设这是一个跨站(cross-site)请求下载回来的内容。ResourceDispatcher类的成员函数OnReceivedData会调用SiteIsolationPolicy类的静态成员函数ShouldBlockResponse依据Cross-Site Document Blocking Policy决定是否须要阻止下载回来的内容在当前Render进程中载入。关于Chromium的Cross-Site Document Blocking Policy,能够參考Site Isolation和Blocking Cross-Site Documents for Site Isolation这两篇文章。
假设SiteIsolationPolicy类的静态成员函数ShouldBlockResponse表明要阻止下载回来的内容在当前Render进程中载入。那么本地变量request_info指向的PendingRequestInfo对象的成员变量blocked_response的值就会等于true。这时候假设SiteIsolationPolicy类的静态成员函数ShouldBlockResponse还返回了Alternative Data,那么这个Alternative Data就会替换下载回来的网页内容交给WebKit处理。
假设SiteIsolationPolicy类的静态成员函数ShouldBlockResponse没有阻止下载回来的内容在当前Render进程中载入,或者阻止的同一时候也提供了Alternative Data,那么ResourceDispatcher类的成员函数OnReceivedData接下来继续推断本地变量request_info指向的PendingRequestInfo对象的成员变量threaded_data_provider是否指向了一个ThreadedDataProvider对象。假设指向了一个ThreadedDataProvider对象,那么ResourceDispatcher类的成员函数OnReceivedData会将下载回来的网页内容交给这个ThreadedDataProvider对象的成员函数OnReceivedDataOnForegroundThread处理。否则的话,下载回来的网页内容将会交给本地变量request_info指向的PendingRequestInfo对象的成员变量peer描写叙述的一个WebURLLoaderImpl::Context对象的成员函数OnReceivedData处理。
WebKit在请求Chromium的Content模块下载指定URL相应的网页内容时,能够指定将下载回来的网页内容交给一个后台线程进行接收和解析,这时候本地变量request_info指向的PendingRequestInfo对象的成员变量threaded_data_provider就会指向一个ThreadedDataProvider对象。这个ThreadedDataProvider对象就会将下载回来的网页内容交给一个后台线程接收和解析。我们不考虑这样的情况。因此接下来我们继续分析WebURLLoaderImpl::Context类的成员函数OnReceivedData的实现,例如以下所看到的:
void WebURLLoaderImpl::Context::OnReceivedData(const char* data,
int data_length,
int encoded_data_length) {
...... if (ftp_listing_delegate_) {
// The FTP listing delegate will make the appropriate calls to
// client_->didReceiveData and client_->didReceiveResponse.
ftp_listing_delegate_->OnReceivedData(data, data_length);
} else if (multipart_delegate_) {
// The multipart delegate will make the appropriate calls to
// client_->didReceiveData and client_->didReceiveResponse.
multipart_delegate_->OnReceivedData(data, data_length, encoded_data_length);
} else {
client_->didReceiveData(loader_, data, data_length, encoded_data_length);
}
}
这个函数定义在文件external/chromium_org/content/child/web_url_loader_impl.cc中。
当从Webserver返回来的网页内容的MIME类型为“text/vnd.chromium.ftp-dir”时,WebURLLoaderImpl::Context类的成员变量ftp_listing_delegate_指向一个FtpDirectoryListingResponseDelegate对象。
这时候从Webserver返回来的网页内容是一些FTP文件夹,上述FtpDirectoryListingResponseDelegate对象对这些网页内容进行一些排版处理后,再交给WebKit处理,也就是ResourceLoader类的成员变量client_描写叙述的一个ResourceLoader对象处理。
当从Webserver返回来的网页内容的MIME类型为“multipart/x-mixed-replace”时,WebURLLoaderImpl::Context类的成员变量multipart_delegate_指向一个MultipartResponseDelegate对象。
这时候从Webserver返回来的网页内容包括若干个数据块,每个数据块都有单独的MIME类型。而且它们之间通过一个Boundary String。上述MultipartResponseDelegate对象依据Boundary String解析出每一数据块之后,再交给WebKit处理,也就是ResourceLoader类的成员变量client_描写叙述的一个ResourceLoader对象处理。
在其余情况下,WebURLLoaderImpl::Context类的成员函数OnReceivedData直接把Webserver返回来的网页内容交给WebKit处理,也就是调用ResourceLoader类的成员变量client_描写叙述的一个ResourceLoader对象的成员函数didReceiveData进行处理。
至此,我们就分析完毕Chromium下载指定URL相应的网页内容的过程了。
下载回来的网页内容将由WebKit进行处理,也就是由ResourceLoader类的成员函数didReceiveData进行处理。
这个处理过程即为网页内容的解析过程,解析后就会得到一棵DOM Tree。有了DOM Tree之后,接下来就能够对下载回来的网页内容进行渲染了。在接下来的一篇文章中,我们再具体分析WebKit依据网页内容生成DOM Tree的过程,敬请关注!
很多其他的信息也能够关注老罗的新浪微博:http://weibo.com/shengyangluo。
Chromium网页URL载入过程分析的更多相关文章
- Chromium网页Frame Tree创建过程分析
Chromium在加载一个网页之前,需要在Browser进程创建一个Frame Tree.Browser进程为网页创建了Frame Tree之后,再请求Render进程加载其内容.Frame ...
- Chromium网页Layer Tree创建过程分析
在Chromium中.WebKit会创建一个Graphics Layer Tree描写叙述网页.Graphics Layer Tree是和网页渲染相关的一个Tree. 网页渲染终于由Chromium的 ...
- Chromium网页Graphics Layer Tree创建过程分析
在前面一文中.我们分析了网页Render Layer Tree的创建过程.在创建Render Layer的同一时候,WebKit还会为其创建Graphics Layer.这些Graphics Laye ...
- Chromium网页输入事件捕捉和手势检測过程分析
连续的输入事件可能会产生一定的手势操作.比如滑动手势和捏合手势. 在Chromium中,网页的输入事件是在Browser进程中捕捉的.Browser进程捕获输入事件之后,会进行手势操作检測.检測出来的 ...
- JavaScript如何获取网页url中的参数
我们可以自定义一个公共函数来实现网页url中的参数获取,返回的是一个数组 GetUrlRequest: function () { var url = decodeURI(location.searc ...
- 获取网页URL地址及参数等的两种方法(js和C#)
转:获取网页URL地址及参数等的两种方法(js和C#) 一 js 先看一个示例 用javascript获取url网址信息 <script type="text/javascript&q ...
- 爬虫_网页url设计
为什么需要网页URL设计? 每个url不同的结构代表着不同的网页模块和信息的展现形式,为了方便维护与管理 网页url怎么设计? 分层: 主域名,子域名 一般形式为: 主域名: www.job.com ...
- 可操纵网页URL地址的js插件-url.js
url.js是一款能够很有用方便的操纵网页URL地址的js插件.通过url.js你能够设置和获取当前URL的參数,也能够对当前URL的參数进行更新.删除操作.还能够将当前URL的參数显示为json字符 ...
- LAMP环境下,通过网页url获取gb2312编码中文命名的下载资源方法
最近有个功能, 要求获取中文命名的.zip压缩文件,我准备直接采用网页url填写压缩文件地址的方式获取下载资源, 但问题是 我们的linux系统和php编程环境都是采用的zh_GB2312编码, 而浏 ...
随机推荐
- python 内存中写入文件(read读取不到文件解决)
from io import StringIO a = StringIO.StringIO('title') a.write('content1\n') a.write('content2') a.s ...
- 交换工资 SQL
- 10款jQuery/CSS3动画应用 超有用
http://www.html5tricks.com/10-jquery-css3-animation.html
- android抓取各种log的方法
1.logcat (四类log buffer是main,radio.system.events) adb wait-for-device logcat adb logcat -v time > ...
- golang sync.Mutex
//go func 和主线程之间的关系是并行和竞争关系 package main import ( "fmt" "sync" "time" ...
- powerdesigner 连接mysql提示“connection test failed”
powerdesigner 连接mysql提示“connection test failed”,该如何解决: 1.把64位的jdk换成32位的jdk(VM只支持32的jre) 2.系统变量: CL ...
- C语言速度优化之指针赋值与if推断
近期在写的一个项目须要优化处理速度,我写了一下程序来測试指针赋值与指针推断的速度比較.结果让我大吃一惊. #include <stdio.h> #include <stdlib.h& ...
- c3p0的经常使用配置方式
1:第一种方式很easy c3p0.driverClass=com.mysql.jdbc.Driver c3p0.jdbcUrl=jdbc:mysql://localhost:3308/databas ...
- Bitmap-把方形图片处理为圆形
这个是直接在网上转载的,自己验证可靠 转载自http://my.oschina.net/zhouz/blog/213164 直接贴上代码 import android.graphics.Bitmap; ...
- 动态规划例子:Maximal Square
Given a 2D binary matrix filled with 0's and 1's, find the largest square containing all 1's and ret ...