继续上一次来分析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的实现。