继续上一次来分析
LoadRequest
的代码,在分析这个函数代码之前,先看看
WebFrame
类的继承层次关系,如下:
class WebFrame : public base::RefCounted<WebFrame> {
WebFrame
是一个接口类,但它先继承引用计数类
RefCounted
,这样对于这个对象多次访问,就可以使用引用计数来判断对象的生命周期了。对于
base::RefCounted<WebFrame>
的语法,其实它是一种模板实现的多态特性,这种方案是最高效的实现方式,比使用虚函数更少占内存,并且运行的速度也更快。它就是解决如下的问题:
void Release() {
if (subtle::RefCountedBase::Release()) {
delete static_cast<T*>(this);
}
}
上面的函数里
static_cast<T*>(this)
,它就是一种多态的实现方法,由于
base::RefCounted
类并没有声明为虚析构函数,如下:
template <class T>
class RefCounted : public subtle::RefCountedBase {
public:
RefCounted() { }
~RefCounted() { }
既然没有把类
RefCounted
声明为虚析构函数,又想在基类里调用派生类的析构函数,只好使用
static_cast
和类型转换了,这是一种比较好的模板使用方法,在
WTL
里就大量使用这种技术。
接着可以看到:
class WebFrameImpl : public WebFrame {
public:
WebFrameImpl();
~WebFrameImpl();
类
WebFrameImpl
是继承接口类
WebFrame
,这里是使用接口与实现分析的设计模式,这样更方便代码灵活地复用。可见设计
Chrome
的设计师和写代码的程序员,都是顶尖的模板高手,大部的思想与
WTL
库的设计是一脉相承。也难怪
Chrome
的浏览器使用
WTL
库来设计界面。
#001
void WebFrameImpl::LoadRequest(WebRequest* request) {
#002
SubstituteData data;
#003
InternalLoadRequest(request, data, false);
#004
}
在
WebFrame
里调用函数
LoadRequest
,实际上是调用实现类
WebFrameImpl
函数
LoadRequest
,而在这个函数又是调用
InternalLoadRequest
来实现的,它的代码如下:
#001
void WebFrameImpl::InternalLoadRequest(const WebRequest* request,
#002
const SubstituteData& data,
#003
bool replace) {
//
转换请求参数。
#004
const WebRequestImpl* request_impl =
#005
static_cast<const WebRequestImpl*>(request);
#006
获取请求的资源。
#007
const ResourceRequest& resource_request =
#008
request_impl->frame_load_request().resourceRequest();
#009
#010
// Special-case javascript URLs.
Do not interrupt the existing load when
#011
// asked to load a javascript URL unless the script generates a result.
#012
// We can't just use FrameLoader::executeIfJavaScriptURL because it doesn't
#013
// handle redirects properly.
获取需要下载网页的地址。
#014
const KURL& kurl = resource_request.url();
处理加载
javascript
的连接情况。
#015
if (!data.isValid() && kurl.protocol() == "javascript") {
#016
// Don't attempt to reload javascript URLs.
#017
if (resource_request.cachePolicy() == ReloadIgnoringCacheData)
#018
return;
#019
#020
// We can't load a javascript: URL if there is no Document!
#021
if (!frame_->document())
#022
return;
#023
#024
// TODO(darin): Is this the best API to use here?
It works and seems good,
#025
// but will it change out from under us?
#026
DeprecatedString script =
#027
KURL::decode_string(kurl.deprecatedString().mid(sizeof("javascript:")-1));
#028
bool succ = false;
加载执行脚本。
#029
WebCore::String value =
#030
frame_->loader()->executeScript(script, &succ, true);
#031
if (succ && !frame_->loader()->isScheduledLocationChangePending()) {
#032
// TODO(darin): We need to figure out how to represent this in session
#033
// history.
Hint: don't re-eval script when the user or script navigates
#034
// back-n-forth (instead store the script result somewhere).
#035
LoadDocumentData(kurl, value, String("text/html"), String());
#036
}
#037
return;
#038
}
#039
停止上一次没有完成的加载情况。
#040
StopLoading();
// make sure existing activity stops
#041
#042
// Keep track of the request temporarily.
This is effectively a way of
#043
// passing the request to callbacks that may need it.
See
#044
// WebFrameLoaderClient::createDocumentLoader.
保存当前的请求连接。
#045
currently_loading_request_ = request;
#046
#047
// If we have a current datasource, save the request info on it immediately.
#048
// This is because WebCore may not actually initiate a load on the toplevel
#049
// frame for some subframe navigations, so we want to update its request.
获取当前数据源,如果已经存在就可以保存它。
#050
WebDataSourceImpl* datasource = GetDataSourceImpl();
#051
if (datasource)
#052
CacheCurrentRequestInfo(datasource);
#053
如果数据有效就可以直接替换就行了。
#054
if (data.isValid()) {
#055
frame_->loader()->load(resource_request, data);
#056
if (replace) {
#057
// Do this to force WebKit to treat the load as replacing the currently
#058
// loaded page.
#059
frame_->loader()->setReplacing();
#060
}
如果是历史网页选择,就判断是否出错的加载处理。
#061
} else if (request_impl->history_item()) {
#062
// Use the history item if we have one, otherwise fall back to standard
#063
// load.
#064
RefPtr<HistoryItem> current_item = frame_->loader()->currentHistoryItem();
#065
#066
// If there is no current_item, which happens when we are navigating in
#067
// session history after a crash, we need to manufacture one otherwise
#068
// WebKit hoarks. This is probably the wrong thing to do, but it seems to
#069
// work.
#070
if (!current_item) {
#071
current_item = new HistoryItem(KURL("about:blank"), "");
#072
frame_->loader()->setCurrentHistoryItem(current_item);
#073
frame_->page()->backForwardList()->setCurrentItem(current_item.get());
#074
#075
// Mark the item as fake, so that we don't attempt to save its state and
#076
// end up with about:blank in the navigation history.
#077
frame_->page()->backForwardList()->setCurrentItemFake(true);
#078
}
#079
#080
frame_->loader()->goToItem(request_impl->history_item().get(),
#081
WebCore::FrameLoadTypeIndexedBackForward);
重新加载网页。
#082
} else if (resource_request.cachePolicy() == ReloadIgnoringCacheData) {
#083
frame_->loader()->reload();
下面开始调用
load
来加载新下载的网页资源。
#084
} else {
#085
frame_->loader()->load(resource_request);
#086
}
#087
#088
currently_loading_request_ = NULL;
#089
}
上面通过几种情况来分别实现了加载
javascript
网页的处理,还有历史选项处理,还有重新加载网页和加载新网页的处理。下一次再来分析加载新网页的函数
frame_->loader()->load
的实现。