首页 > Web开发 > 详细

QT分析之WebKit

时间:2016-03-15 12:14:07      阅读:378      评论:0      收藏:0      [点我收藏+]

该文章整理自 网易博客 http://blog.163.com/net_worm/blog/static/12770241920101831312381/

转载请注明出处

 

WebKit是QT4新整合的第三方构件。按照惯例动手分析之前,先了解大概

WebKit由三个模块组成:JavaScriptCore、WebCore 和 WebKit

WebKit作为了整个项目的名称。其目录结构:(未校准)

WebCore:

  ¨Page与外框相关的内容(Frame,Page,History,Focus,Window)

  ¨Loader加载资源及Cache

  ¨HTML-DOM HTML内容及解析

  ¨DOM- DOM CORE内容

  ¨XML- XML内容及解析

  ¨Render-排版功能

  ¨CSS-DOM CSS内容

  ¨Binding-DOM与JavascriptCore绑定的功能

  ¨Editing-所有与编辑相关的功能

  JavascriptCore-javascript引擎

  ¨API-基本javascript功能

  ¨Binding与其它功能绑定的功能,如:DOM,C,JNI

  ¨DerviedSource自动产生的代码

  ¨ForwordHeads头文件,无实际意义

  ¨PCRE-Perl-Compatible Regular Expressions 

  ¨KJS-Javascript Kernel

  ¨WTF-KDE的C++模板库

  Unicode unicode 库

  Tools tools库

  CURL-url 客户端传输库

  PlatForm- 与平台相关的功能,如图形图像,字体,Unicode, IO,输入法等.

在QT自带的例子中,有WebKit相关的例子。我选中previewer作为分析的项目。

 
previewer是QT自带的例子,运行之后的样子:
技术分享 
 
我是通过输入URL,进行跟踪分析的。下面是断点保存的调用堆栈,暂存资料
 1      QtWebKitd4.dll!WebCore::MainResourceLoader::loadNow(WebCore::ResourceRequest & r={...})  行458    C++
 2      QtWebKitd4.dll!WebCore::MainResourceLoader::load(const WebCore::ResourceRequest & r={...}, const WebCore::SubstituteData & substituteData={...})  行494 + 0x12 字节    C++
 3      QtWebKitd4.dll!WebCore::DocumentLoader::startLoadingMainResource(unsigned long identifier=0x00000004)  行807 + 0x32 字节    C++
 4      QtWebKitd4.dll!WebCore::FrameLoader::continueLoadAfterWillSubmitForm(WebCore::PolicyAction __formal=PolicyUse)  行3274 + 0x16 字节    C++
 5      QtWebKitd4.dll!WebCore::FrameLoader::continueLoadAfterNavigationPolicy(const WebCore::ResourceRequest & __formal={...}, WTF::PassRefPtr<WebCore::FormState> formState={...}, bool shouldContinue=true)  行3968    C++
 6      QtWebKitd4.dll!WebCore::FrameLoader::callContinueLoadAfterNavigationPolicy(void * argument=0x01d424e8, const WebCore::ResourceRequest & request={...}, WTF::PassRefPtr<WebCore::FormState> formState={...}, bool shouldContinue=true)  行3906    C++
 7      QtWebKitd4.dll!WebCore::PolicyCheck::call(bool shouldContinue=true)  行4963 + 0x3b 字节    C++
 8      QtWebKitd4.dll!WebCore::FrameLoader::continueAfterNavigationPolicy(WebCore::PolicyAction policy=PolicyUse)  行3899    C++
 9      QtWebKitd4.dll!WebCore::FrameLoaderClientQt::slotCallPolicyFunction(int action=0x00000000)  行194    C++
10      QtWebKitd4.dll!WebCore::FrameLoaderClientQt::dispatchDecidePolicyForNavigationAction(void (WebCore::PolicyAction)* function=0x10018f0c, const WebCore::NavigationAction & action={...}, const WebCore::ResourceRequest & request={...}, WTF::PassRefPtr<WebCore::FormState> __formal={...})  行938    C++
11      QtWebKitd4.dll!WebCore::FrameLoader::checkNavigationPolicy(const WebCore::ResourceRequest & request={...}, WebCore::DocumentLoader * loader=0x00f63ff8, WTF::PassRefPtr<WebCore::FormState> formState={...}, void (void *, const WebCore::ResourceRequest &, WTF::PassRefPtr<WebCore::FormState>, bool)* function=0x1004e661, void * argument=0x01d424e8)  行3868    C++
12      QtWebKitd4.dll!WebCore::FrameLoader::loadWithDocumentLoader(WebCore::DocumentLoader * loader=0x00f63ff8, WebCore::FrameLoadType type=FrameLoadTypeRedirectWithLockedHistory, WTF::PassRefPtr<WebCore::FormState> prpFormState={...})  行2291    C++
13      QtWebKitd4.dll!WebCore::FrameLoader::loadWithNavigationAction(const WebCore::ResourceRequest & request={...}, const WebCore::NavigationAction & action={...}, WebCore::FrameLoadType type=FrameLoadTypeRedirectWithLockedHistory, WTF::PassRefPtr<WebCore::FormState> formState={...})  行2226    C++
14      QtWebKitd4.dll!WebCore::FrameLoader::loadURL(const WebCore::KURL & newURL={...}, const WebCore::String & referrer={...}, const WebCore::String & frameName={...}, WebCore::FrameLoadType newLoadType=FrameLoadTypeRedirectWithLockedHistory, WebCore::Event * event=0x00000000, WTF::PassRefPtr<WebCore::FormState> prpFormState={...})  行2174    C++
15      QtWebKitd4.dll!WebCore::FrameLoaderClientQt::createFrame(const WebCore::KURL & url={...}, const WebCore::String & name={...}, WebCore::HTMLFrameOwnerElement * ownerElement=0x00f681a0, const WebCore::String & referrer={...}, bool allowsScrolling=false, int marginWidth=0xffffffff, int marginHeight=0xffffffff)  行981 + 0x70 字节    C++
16      QtWebKitd4.dll!WebCore::FrameLoader::loadSubframe(WebCore::HTMLFrameOwnerElement * ownerElement=0x00f681a0, const WebCore::KURL & url={...}, const WebCore::String & name={...}, const WebCore::String & referrer={...})  行472 + 0x74 字节    C++
17      QtWebKitd4.dll!WebCore::FrameLoader::requestFrame(WebCore::HTMLFrameOwnerElement * ownerElement=0x00f681a0, const WebCore::String & urlString={...}, const WebCore::AtomicString & frameName={...})  行442 + 0x29 字节    C++
18      QtWebKitd4.dll!WebCore::HTMLFrameElementBase::openURL()  行105    C++
19      QtWebKitd4.dll!WebCore::HTMLFrameElementBase::setNameAndOpenURL()  行161    C++
20      QtWebKitd4.dll!WebCore::HTMLFrameElementBase::setNameAndOpenURLCallback(WebCore::Node * n=0x00f681a0)  行166    C++
21      QtWebKitd4.dll!WebCore::ContainerNode::dispatchPostAttachCallbacks()  行572 + 0x7 字节    C++
22      QtWebKitd4.dll!WebCore::ContainerNode::attach()  行587    C++
23      QtWebKitd4.dll!WebCore::Element::attach()  行648    C++
24      QtWebKitd4.dll!WebCore::HTMLFrameElementBase::attach()  行194    C++
25      QtWebKitd4.dll!WebCore::HTMLFrameElement::attach()  行67    C++
26      QtWebKitd4.dll!WebCore::HTMLParser::insertNode(WebCore::Node * n=0x00f681a0, bool flat=false)  行351    C++
27      QtWebKitd4.dll!WebCore::HTMLParser::parseToken(WebCore::Token * t=0x00f65fd0)  行256 + 0x19 字节    C++
28 >    QtWebKitd4.dll!WebCore::HTMLTokenizer::processToken()  行1902 + 0x20 字节    C++
29      QtWebKitd4.dll!WebCore::HTMLTokenizer::parseTag(WebCore::SegmentedString & src={...}, WebCore::HTMLTokenizer::State state={...})  行1484 + 0x12 字节    C++
30      QtWebKitd4.dll!WebCore::HTMLTokenizer::write(const WebCore::SegmentedString & str={...}, bool appendData=true)  行1730 + 0x23 字节    C++
31      QtWebKitd4.dll!WebCore::FrameLoader::write(const char * str=0x01d3f5c0, int len=0x000001df, bool flush=false)  行1039 + 0x23 字节    C++
32      QtWebKitd4.dll!WebCore::FrameLoader::addData(const char * bytes=0x01d3f5c0, int length=0x000001df)  行1891    C++
33      QtWebKitd4.dll!WebCore::FrameLoaderClientQt::committedLoad(WebCore::DocumentLoader * loader=0x00f881e0, const char * data=0x01d3f5c0, int length=0x000001df)  行680    C++
34      QtWebKitd4.dll!WebCore::FrameLoader::committedLoad(WebCore::DocumentLoader * loader=0x00f881e0, const char * data=0x01d3f5c0, int length=0x000001df)  行3513    C++
35      QtWebKitd4.dll!WebCore::DocumentLoader::commitLoad(const char * data=0x01d3f5c0, int length=0x000001df)  行356    C++
36      QtWebKitd4.dll!WebCore::DocumentLoader::receivedData(const char * data=0x01d3f5c0, int length=0x000001df)  行368    C++
37      QtWebKitd4.dll!WebCore::FrameLoader::receivedData(const char * data=0x01d3f5c0, int length=0x000001df)  行2342    C++
38      QtWebKitd4.dll!WebCore::MainResourceLoader::addData(const char * data=0x01d3f5c0, int length=0x000001df, bool allAtOnce=false)  行147    C++
39      QtWebKitd4.dll!WebCore::ResourceLoader::didReceiveData(const char * data=0x01d3f5c0, int length=0x000001df, __int64 lengthReceived=0x00000000000001df, bool allAtOnce=false)  行267    C++
40      QtWebKitd4.dll!WebCore::MainResourceLoader::didReceiveData(const char * data=0x01d3f5c0, int length=0x000001df, __int64 lengthReceived=0x00000000000001df, bool allAtOnce=false)  行342    C++
41      QtWebKitd4.dll!WebCore::ResourceLoader::didReceiveData(WebCore::ResourceHandle * __formal=0x00fb9aa0, const char * data=0x01d3f5c0, int length=0x000001df, int lengthReceived=0x000001df)  行418    C++
42      QtWebKitd4.dll!WebCore::QNetworkReplyHandler::forwardData()  行341    C++
43      QtWebKitd4.dll!WebCore::QNetworkReplyHandler::qt_metacall(QMetaObject::Call _c=InvokeMetaMethod, int _id=0x00000002, void * * _a=0x00fba378)  行74    C++
44      QtCored4.dll!QMetaCallEvent::placeMetaCall(QObject * object=0x00f810d0)  行478    C++
45      QtCored4.dll!QObject::event(QEvent * e=0x01d3ee18)  行1102 + 0x14 字节    C++
46      QtGuid4.dll!QApplicationPrivate::notify_helper(QObject * receiver=0x00f810d0, QEvent * e=0x01d3ee18)  行4065 + 0x11 字节    C++
47      QtGuid4.dll!QApplication::notify(QObject * receiver=0x00f810d0, QEvent * e=0x01d3ee18)  行3605 + 0x10 字节    C++
48      QtCored4.dll!QCoreApplication::notifyInternal(QObject * receiver=0x00f810d0, QEvent * event=0x01d3ee18)  行610 + 0x15 字节    C++
49      QtCored4.dll!QCoreApplication::sendEvent(QObject * receiver=0x00f810d0, QEvent * event=0x01d3ee18)  行213 + 0x39 字节    C++
50      QtCored4.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver=0x00000000, int event_type=0x00000000, QThreadData * data=0x00e78f60)  行1247 + 0xd 字节    C++
51      QtCored4.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...})  行679 + 0x10 字节    C++
52      QtGuid4.dll!QGuiEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...})  行1182 + 0x15 字节    C++
53      QtCored4.dll!QEventLoop::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...})  行150    C++
54      QtCored4.dll!QEventLoop::exec(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...})  行201 + 0x2d 字节    C++
55      QtCored4.dll!QCoreApplication::exec()  行888 + 0x15 字节    C++
56      QtGuid4.dll!QApplication::exec()  行3526    C++
57      previewer.exe!main(int argc=0x00000001, char * * argv=0x00e78e20)  行51 + 0x6 字节    C++
58      previewer.exe!WinMain(HINSTANCE__ * instance=0x00400000, HINSTANCE__ * prevInstance=0x00000000, char * __formal=0x001520d9, int cmdShow=0x00000001)  行137 + 0x12 字节    C++
59      previewer.exe!__tmainCRTStartup()  行574 + 0x35 字节    C
60      previewer.exe!WinMainCRTStartup()  行399    C
61      kernel32.dll!7c82f23b() 

[下面的框架可能不正确和/或缺失,没有为 kernel32.dll 加载符号]   

 

分三个阶段对QWebView进行分析:初始化(获取数据)、HTML解析、页面显示。从QT自带的文档中可以知道

1 QWebView -> QWebPage => QWebFrame(一个QWebPage含多个QWebFrame)

在界面中选择了Open URL,输入URL之后,调用的是:void MainWindow::openUrl()

 1 void MainWindow::openUrl()
 2 {
 3     bool ok;
 4     QString url = QInputDialog::getText(this, tr("Enter a URL"),
 5                   tr("URL:"), QLineEdit::Normal, "http://", &ok);
 6 
 7     if (ok && !url.isEmpty()) {
 8         centralWidget->webView->setUrl(url);
 9     }
10 }

调用的是QWebView::setUrl()

1 void QWebView::setUrl(const QUrl &url)
2 {
3     page()->mainFrame()->setUrl(url);
4 }

其中page()是获取QWebPage指针,QWebPage::mainFrame()获取的是QWebFrame指针

所以调用的是:QWebFrame::setUrl()

1 void QWebFrame::setUrl(const QUrl &url)
2 {
3     d->frame->loader()->begin(ensureAbsoluteUrl(url));
4     d->frame->loader()->end();
5     load(ensureAbsoluteUrl(url));
6 }

ensureAbsoluteUrl()函数作用是,确保URL是绝对URL(完整URL)。所谓相对URL是指没有输入http://或者https://等前缀的web地址。先看第一句的调用。其中隐含了从QUrl到KURL的变换。

 1 void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin)
 2 {
 3     // We need to take a reference to the security origin because |clear|
 4     // might destroy the document that owns it.
 5     RefPtr<SecurityOrigin> forcedSecurityOrigin = origin;
 6 
 7     bool resetScripting = !(m_isDisplayingInitialEmptyDocument && m_frame->document() && m_frame->document()->securityOrigin()->isSecureTransitionTo(url));
 8     clear(resetScripting, resetScripting);      // 清除上一次的数据,为本次装载准备
 9     if (resetScripting)   
10         m_frame->script()->updatePlatformScriptObjects();    // 在Windows平台下,这是空函数
11     if (dispatch)
12         dispatchWindowObjectAvailable();
13 
14     m_needsClear = true;
15     m_isComplete = false;
16     m_didCallImplicitClose = false;
17     m_isLoadingMainResource = true;
18     m_isDisplayingInitialEmptyDocument = m_creatingInitialEmptyDocument;
19 
20     KURL ref(url);
21     ref.setUser(String());
22     ref.setPass(String());
23     ref.setRef(String());
24     m_outgoingReferrer = ref.string();
25     m_URL = url;
26 
27     RefPtr<Document> document;
28     
29     if (!m_isDisplayingInitialEmptyDocument && m_client->shouldUsePluginDocument(m_responseMIMEType))
30         document = PluginDocument::create(m_frame);
31     else
32         document = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode());    // 创建DOM文件,m_responseMIMEType不同实体不同。
33 
34 // 如果是"text/html"创建HTMLDocument实体;"application/xhtml+xml"创建Document实体
35 
36 // 如果是"application/x-ftp-directory"则是FTPDirectoryDocument实体
37 
38 // text/vnd.wap.wml 对应 WMLDocument 实体(无线)
39 
40 // "application/pdf" /"text/plain" 对应 PluginDocument实体
41 
42 // 如果是MediaPlayer::supportsType(type),创建的是MediaDocument实体
43 
44 // "image/svg+xml" 对应 SVGDocument实体
45     m_frame->setDocument(document);
46 
47     document->setURL(m_URL);
48     if (m_decoder)
49         document->setDecoder(m_decoder.get());
50     if (forcedSecurityOrigin)
51         document->setSecurityOrigin(forcedSecurityOrigin.get());
52 
53     m_frame->domWindow()->setURL(document->url());
54     m_frame->domWindow()->setSecurityOrigin(document->securityOrigin());
55 
56     updatePolicyBaseURL();   // 更新排布策略的基础URL
57 
58     Settings* settings = document->settings();
59     document->docLoader()->setAutoLoadImages(settings && settings->loadsImagesAutomatically());
60 
61     if (m_documentLoader) {
62         String dnsPrefetchControl = m_documentLoader->response().httpHeaderField("X-DNS-Prefetch-Control");
63         if (!dnsPrefetchControl.isEmpty())
64             document->parseDNSPrefetchControlHeader(dnsPrefetchControl);
65     }
66 
67 #if FRAME_LOADS_USER_STYLESHEET
68     KURL userStyleSheet = settings ? settings->userStyleSheetLocation() : KURL();
69     if (!userStyleSheet.isEmpty())
70         m_frame->setUserStyleSheetLocation(userStyleSheet);
71 #endif
72 
73     restoreDocumentState();
74 
75     document->implicitOpen();
76 
77     if (m_frame->view())
78         m_frame->view()->setContentsSize(IntSize());
79 
80 #if USE(LOW_BANDWIDTH_DISPLAY)
81     // Low bandwidth display is a first pass display without external resources
82     // used to give an instant visual feedback. We currently only enable it for
83     // HTML documents in the top frame.
84     if (document->isHTMLDocument() && !m_frame->tree()->parent() && m_useLowBandwidthDisplay) {
85         m_pendingSourceInLowBandwidthDisplay = String();
86         m_finishedParsingDuringLowBandwidthDisplay = false;
87         m_needToSwitchOutLowBandwidthDisplay = false;
88         document->setLowBandwidthDisplay(true);
89     }
90 #endif
91 }

看其中document->implicitOpen()的代码:

 1 void Document::implicitOpen()
 2 {
 3     cancelParsing();
 4 
 5     clear();
 6     m_tokenizer = createTokenizer();
 7     setParsing(true);
 8 }
 9 
10 Tokenizer *HTMLDocument::createTokenizer()
11 {
12     bool reportErrors = false;
13     if (frame())
14         if (Page* page = frame()->page())
15             reportErrors = page->inspectorController()->windowVisible();
16 
17     return new HTMLTokenizer(this, reportErrors);
18 }

新创建的HTMLTokenizer对象,就是HTML的解析器。

回到QWebFrame::setUrl()的第二句:d->frame->loader()->end();

只是把上次未完的解析停止:

 1 void FrameLoader::endIfNotLoadingMainResource()
 2 {
 3     if (m_isLoadingMainResource || !m_frame->page())
 4         return;
 5 
 6     // http://bugs.webkit.org/show_bug.cgi?id=10854
 7     // The frame‘s last ref may be removed and it can be deleted by checkCompleted(), 
 8     // so we‘ll add a protective refcount
 9     RefPtr<Frame> protector(m_frame);
10 
11     // make sure nothing‘s left in there
12     if (m_frame->document()) {
13         write(0, 0, true);
14         m_frame->document()->finishParsing();
15    } else
16         // WebKit partially uses WebCore when loading non-HTML docs.  In these cases doc==nil, but
17         // WebCore is enough involved that we need to checkCompleted() in order for m_bComplete to
18         // become true.  An example is when a subframe is a pure text doc, and that subframe is the
19         // last one to complete.
20         checkCompleted();
21 }

再来看QWebFrame::setUrl()的第三句:load(ensureAbsoluteUrl(url));

1 void QWebFrame::load(const QUrl &url)
2 {
3    load(QNetworkRequest(ensureAbsoluteUrl(url)));
4 }

新建一个QNetworkRequest对象,然后调用

1 void load(const QNetworkRequest &request,
2               QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
3               const QByteArray &body = QByteArray());

看其代码:

 1 void QWebFrame::load(const QNetworkRequest &req,
 2                      QNetworkAccessManager::Operation operation,
 3                      const QByteArray &body)
 4 {
 5     if (d->parentFrame())
 6         d->page->d->insideOpenCall = true;
 7 
 8     QUrl url = ensureAbsoluteUrl(req.url());
 9 
10     WebCore::ResourceRequest request(url);
11 
12     switch (operation) {
13         case QNetworkAccessManager::HeadOperation:
14             request.setHTTPMethod("HEAD");
15             break;
16         case QNetworkAccessManager::GetOperation:
17             request.setHTTPMethod("GET");
18             break;
19         case QNetworkAccessManager::PutOperation:
20             request.setHTTPMethod("PUT");
21             break;
22         case QNetworkAccessManager::PostOperation:
23             request.setHTTPMethod("POST");
24             break;
25         case QNetworkAccessManager::UnknownOperation:
26             // eh?
27             break;
28     }
29 
30     QList<QByteArray> httpHeaders = req.rawHeaderList();
31     for (int i = 0; i < httpHeaders.size(); ++i) {
32         const QByteArray &headerName = httpHeaders.at(i);
33         request.addHTTPHeaderField(QString::fromLatin1(headerName), QString::fromLatin1(req.rawHeader(headerName)));
34     }
35 
36     if (!body.isEmpty())
37         request.setHTTPBody(WebCore::FormData::create(body.constData(), body.size()));
38 
39     d->frame->loader()->load(request);
40 
41     if (d->parentFrame())
42         d->page->d->insideOpenCall = false;
43 }

看关键的FrameLoader::load()

 1 void FrameLoader::load(const ResourceRequest& request)
 2 {
 3     load(request, SubstituteData());
 4 }
 5 
 6 void FrameLoader::load(const ResourceRequest& request, const SubstituteData& substituteData)
 7 {
 8     if (m_inStopAllLoaders)
 9         return;
10         
11     // FIXME: is this the right place to reset loadType? Perhaps this should be done after loading is finished or aborted.
12     m_loadType = FrameLoadTypeStandard;
13     load(m_client->createDocumentLoader(request, substituteData).get());
14 }

 

上面m_client对应的是FrameLoaderClientQt实体,m_client->createDocumentLoader()创建的是DocumentLoader对象。进一步看FrameLoader::load(DocumentLoader *)的代码:

 1 void FrameLoader::load(DocumentLoader* newDocumentLoader)
 2 {
 3     ResourceRequest& r = newDocumentLoader->request();
 4     addExtraFieldsToMainResourceRequest(r);
 5     FrameLoadType type;
 6 
 7     if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->originalRequest().url())) {
 8         r.setCachePolicy(ReloadIgnoringCacheData);
 9         type = FrameLoadTypeSame;
10     } else
11         type = FrameLoadTypeStandard;
12 
13     if (m_documentLoader)
14         newDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding());
15     
16     // When we loading alternate content for an unreachable URL that we‘re
17     // visiting in the history list, we treat it as a reload so the history list 
18     // is appropriately maintained.
19     //
20     // FIXME: This seems like a dangerous overloading of the meaning of "FrameLoadTypeReload" ...
21     // shouldn‘t a more explicit type of reload be defined, that means roughly 
22     // "load without affecting history" ? 
23     if (shouldReloadToHandleUnreachableURL(newDocumentLoader)) {
24         ASSERT(type == FrameLoadTypeStandard);
25         type = FrameLoadTypeReload;
26     }
27 
28     loadWithDocumentLoader(newDocumentLoader, type, 0);
29 }

看FrameLoader::loadWithDocumentLoader()的代码:

 1 void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState)
 2 {
 3     ASSERT(m_client->hasWebView());
 4 
 5     // Unfortunately the view must be non-nil, this is ultimately due
 6     // to parser requiring a FrameView.  We should fix this dependency.
 7 
 8     ASSERT(m_frame->view());
 9 
10     m_policyLoadType = type;
11     RefPtr<FormState> formState = prpFormState;
12     bool isFormSubmission = formState;
13 
14     const KURL& newURL = loader->request().url();
15 
16     if (shouldScrollToAnchor(isFormSubmission, m_policyLoadType, newURL)) {
17         RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader;
18         NavigationAction action(newURL, m_policyLoadType, isFormSubmission);
19 
20         oldDocumentLoader->setTriggeringAction(action);
21         stopPolicyCheck();
22         checkNavigationPolicy(loader->request(), oldDocumentLoader.get(), formState,
23             callContinueFragmentScrollAfterNavigationPolicy, this);
24     } else {
25         if (Frame* parent = m_frame->tree()->parent())
26             loader->setOverrideEncoding(parent->loader()->documentLoader()->overrideEncoding());
27 
28         stopPolicyCheck();
29         setPolicyDocumentLoader(loader);
30 
31         checkNavigationPolicy(loader->request(), loader, formState,
32             callContinueLoadAfterNavigationPolicy, this);
33     }
34 }

上面调用checkNavigationPolicy()是关键,看其实现:

 1 void FrameLoader::checkNavigationPolicy(const ResourceRequest& request, DocumentLoader* loader,
 2     PassRefPtr<FormState> formState, NavigationPolicyDecisionFunction function, void* argument)
 3 {
 4     NavigationAction action = loader->triggeringAction();
 5     if (action.isEmpty()) {
 6         action = NavigationAction(request.url(), NavigationTypeOther);
 7         loader->setTriggeringAction(action);
 8     }
 9         
10     // Don‘t ask more than once for the same request or if we are loading an empty URL.
11     // This avoids confusion on the part of the client.
12     if (equalIgnoringHeaderFields(request, loader->lastCheckedRequest()) || (!request.isNull() && request.url().isEmpty())) {
13         function(argument, request, 0, true);
14         loader->setLastCheckedRequest(request);
15         return;
16     }
17     
18     // We are always willing to show alternate content for unreachable URLs;
19     // treat it like a reload so it maintains the right state for b/f list.
20     if (loader->substituteData().isValid() && !loader->substituteData().failingURL().isEmpty()) {
21         if (isBackForwardLoadType(m_policyLoadType))
22             m_policyLoadType = FrameLoadTypeReload;
23         function(argument, request, 0, true);
24         return;
25     }
26     
27     loader->setLastCheckedRequest(request);
28 
29     m_policyCheck.set(request, formState.get(), function, argument);
30 
31     m_delegateIsDecidingNavigationPolicy = true;
32     m_client->dispatchDecidePolicyForNavigationAction(&FrameLoader::continueAfterNavigationPolicy,
33         action, request, formState);
34     m_delegateIsDecidingNavigationPolicy = false;
35 }

其中m_client是FrameLoaderClientQt实体指针

 1 void FrameLoaderClientQt::dispatchDecidePolicyForNavigationAction(FramePolicyFunction function, const WebCore::NavigationAction& action, const WebCore::ResourceRequest& request, PassRefPtr<WebCore::FormState>)
 2 {
 3     Q_ASSERT(!m_policyFunction);
 4     Q_ASSERT(m_webFrame);
 5     m_policyFunction = function;
 6 #if QT_VERSION < 0x040400
 7     QWebNetworkRequest r(request);
 8 #else
 9     QNetworkRequest r(request.toNetworkRequest());
10 #endif
11     QWebPage*page = m_webFrame->page();
12 
13     if (!page->d->acceptNavigationRequest(m_webFrame, r, QWebPage::NavigationType(action.type()))) {
14         if (action.type() == NavigationTypeFormSubmitted || action.type() == NavigationTypeFormResubmitted)
15             m_frame->loader()->resetMultipleFormSubmissionProtection();
16 
17         if (action.type() == NavigationTypeLinkClicked && r.url().hasFragment()) {
18             ResourceRequest emptyRequest;
19             m_frame->loader()->activeDocumentLoader()->setLastCheckedRequest(emptyRequest);
20         }
21 
22         slotCallPolicyFunction(PolicyIgnore);
23         return;
24     }
25     slotCallPolicyFunction(PolicyUse);
26 }
27 void FrameLoaderClientQt::slotCallPolicyFunction(int action)
28 {
29     if (!m_frame || !m_policyFunction)
30         return;
31     FramePolicyFunction function = m_policyFunction;
32     m_policyFunction = 0;
33     (m_frame->loader()->*function)(WebCore::PolicyAction(action));
34 }

用函数指针回调,FrameLoader::continueAfterNavigationPolicy(PolicyAction policy),参数为PolicyUse

 

 1 void FrameLoader::continueAfterNavigationPolicy(PolicyAction policy)
 2 {
 3     PolicyCheck check = m_policyCheck;
 4     m_policyCheck.clear();
 5 
 6     bool shouldContinue = policy == PolicyUse;
 7     
 8     switch (policy) {
 9         case PolicyIgnore:
10             check.clearRequest();
11             break;
12         case PolicyDownload:
13             m_client->startDownload(check.request());
14             check.clearRequest();
15             break;
16         case PolicyUse: {
17             ResourceRequest request(check.request());
18             
19             if (!m_client->canHandleRequest(request)) {
20                 handleUnimplementablePolicy(m_client->cannotShowURLError(check.request()));
21                 check.clearRequest();
22                 shouldContinue = false;
23             }
24             break;
25         }
26     }
27 
28     check.call(shouldContinue);
29 }

 

上面调用的是PolicyCheck::call(),参数为true

1 void PolicyCheck::call(bool shouldContinue)
2 {
3     if (m_navigationFunction)
4         m_navigationFunction(m_argument, m_request, m_formState.get(), shouldContinue);
5     if (m_newWindowFunction)
6         m_newWindowFunction(m_argument, m_request, m_formState.get(), m_frameName, shouldContinue);
7     ASSERT(!m_contentFunction);
8 }

m_navigationFunction又是一个函数指针,指向的是FrameLoader::callContinueLoadAfterNavigationPolicy()

 1 void FrameLoader::callContinueLoadAfterNavigationPolicy(void* argument,
 2     const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue)
 3 {
 4     FrameLoader* loader = static_cast<FrameLoader*>(argument);
 5     loader->continueLoadAfterNavigationPolicy(request, formState, shouldContinue);
 6 }
 7 
 8 void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState> formState, bool shouldContinue)
 9 {
10     // If we loaded an alternate page to replace an unreachableURL, we‘ll get in here with a
11     // nil policyDataSource because loading the alternate page will have passed
12     // through this method already, nested; otherwise, policyDataSource should still be set.
13     ASSERT(m_policyDocumentLoader || !m_provisionalDocumentLoader->unreachableURL().isEmpty());
14 
15     bool isTargetItem = m_provisionalHistoryItem ? m_provisionalHistoryItem->isTargetItem() : false;
16 
17     // Two reasons we can‘t continue:
18     //    1) Navigation policy delegate said we can‘t so request is nil. A primary case of this 
19     //       is the user responding Cancel to the form repost nag sheet.
20     //    2) User responded Cancel to an alert popped up by the before unload event handler.
21     // The "before unload" event handler runs only for the main frame.
22     bool canContinue = shouldContinue && (!isLoadingMainFrame() || m_frame->shouldClose());
23 
24     if (!canContinue) {
25         // If we were waiting for a quick redirect, but the policy delegate decided to ignore it, then we 
26         // need to report that the client redirect was cancelled.
27         if (m_quickRedirectComing)
28             clientRedirectCancelledOrFinished(false);
29 
30         setPolicyDocumentLoader(0);
31 
32         // If the navigation request came from the back/forward menu, and we punt on it, we have the 
33         // problem that we have optimistically moved the b/f cursor already, so move it back.  For sanity, 
34         // we only do this when punting a navigation for the target frame or top-level frame.  
35         if ((isTargetItem || isLoadingMainFrame()) && isBackForwardLoadType(m_policyLoadType))
36             if (Page* page = m_frame->page()) {
37                 Frame* mainFrame = page->mainFrame();
38                 if (HistoryItem* resetItem = mainFrame->loader()->m_currentHistoryItem.get()) {
39                     page->backForwardList()->goToItem(resetItem);
40                     Settings* settings = m_frame->settings();
41                     page->setGlobalHistoryItem((!settings || settings->privateBrowsingEnabled()) ? 0 : resetItem);
42                 }
43             }
44         return;
45     }
46 
47     FrameLoadType type = m_policyLoadType;
48     stopAllLoaders();
49     
50     // <rdar://problem/6250856> - In certain circumstances on pages with multiple frames, stopAllLoaders()
51     // might detach the current FrameLoader, in which case we should bail on this newly defunct load. 
52     if (!m_frame->page())
53         return;
54         
55     setProvisionalDocumentLoader(m_policyDocumentLoader.get());
56     m_loadType = type;
57     setState(FrameStateProvisional);
58 
59     setPolicyDocumentLoader(0);
60 
61     if (isBackForwardLoadType(type) && loadProvisionalItemFromCachedPage())
62         return;
63 
64     if (formState)
65         m_client->dispatchWillSubmitForm(&FrameLoader::continueLoadAfterWillSubmitForm, formState);
66     else
67         continueLoadAfterWillSubmitForm();
68 }
69 
70 void FrameLoader::continueLoadAfterWillSubmitForm(PolicyAction)
71 {
72     if (!m_provisionalDocumentLoader)
73         return;
74 
75     // DocumentLoader calls back to our prepareForLoadStart
76     m_provisionalDocumentLoader->prepareForLoadStart();
77     
78     // The load might be cancelled inside of prepareForLoadStart(), nulling out the m_provisionalDocumentLoader, 
79     // so we need to null check it again.
80     if (!m_provisionalDocumentLoader)
81         return;
82     // 先看活动的DocumentLoader能否装载
83     DocumentLoader* activeDocLoader = activeDocumentLoader();
84     if (activeDocLoader && activeDocLoader->isLoadingMainResource())
85         return;
86     // 看Cache中能否装载
87     m_provisionalDocumentLoader->setLoadingFromCachedPage(false);
88 
89     unsigned long identifier = 0;
90 
91     if (Page* page = m_frame->page()) {
92         identifier = page->progress()->createUniqueIdentifier();
93         dispatchAssignIdentifierToInitialRequest(identifier, m_provisionalDocumentLoader.get(), m_provisionalDocumentLoader->originalRequest());
94     }
95 
96     if (!m_provisionalDocumentLoader->startLoadingMainResource(identifier))
97         m_provisionalDocumentLoader->updateLoading();
98 }

上面的装载过程,如果是第一次并且只有m_provisionalDocumentLoader的话,只会执行最后一中装载。

 1 bool DocumentLoader::startLoadingMainResource(unsigned long identifier)
 2 {
 3     ASSERT(!m_mainResourceLoader);
 4     m_mainResourceLoader = MainResourceLoader::create(m_frame);
 5     m_mainResourceLoader->setIdentifier(identifier);
 6 
 7     // FIXME: Is there any way the extra fields could have not been added by now?
 8     // If not, it would be great to remove this line of code.
 9     frameLoader()->addExtraFieldsToMainResourceRequest(m_request);
10 
11     if (!m_mainResourceLoader->load(m_request, m_substituteData)) {
12         // FIXME: If this should really be caught, we should just ASSERT this doesn‘t happen;
13         // should it be caught by other parts of WebKit or other parts of the app?
14         LOG_ERROR("could not create WebResourceHandle for URL %s -- should be caught by policy handler level", m_request.url().string().ascii().data());
15         m_mainResourceLoader = 0;
16         return false;
17     }
18 
19     return true;
20 }

创建MainResourceLoader对象,并调用load()

 1 bool MainResourceLoader::load(const ResourceRequest& r, const SubstituteData& substituteData)
 2 {
 3     ASSERT(!m_handle);
 4 
 5     m_substituteData = substituteData;
 6 
 7 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
 8     // Check if this request should be loaded from the application cache
 9     if (!m_substituteData.isValid() && frameLoader()->frame()->settings() && frameLoader()->frame()->settings()->offlineWebApplicationCacheEnabled()) {
10         ASSERT(!m_applicationCache);
11 
12         m_applicationCache = ApplicationCacheGroup::cacheForMainRequest(r, m_documentLoader.get());
13 
14         if (m_applicationCache) {
15             // Get the resource from the application cache. By definition, cacheForMainRequest() returns a cache that contains the resource.
16             ApplicationCacheResource* resource = m_applicationCache->resourceForRequest(r);
17             m_substituteData = SubstituteData(resource->data(), 
18                                               resource->response().mimeType(),
19                                               resource->response().textEncodingName(), KURL());
20         }
21     }
22 #endif
23 
24     ResourceRequest request(r);
25     bool defer = defersLoading();
26     if (defer) {
27         bool shouldLoadEmpty = shouldLoadAsEmptyDocument(r.url());
28         if (shouldLoadEmpty)
29             defer = false;
30     }
31     if (!defer) {
32         if (loadNow(request)) {
33             // Started as an empty document, but was redirected to something non-empty.
34             ASSERT(defersLoading());
35             defer = true;
36         }
37     }
38     if (defer)
39         m_initialRequest = request;
40 
41     return true;
42 }

继续深入看MainResourceLoader::loadNow()

 1 bool MainResourceLoader::loadNow(ResourceRequest& r)
 2 {
 3     bool shouldLoadEmptyBeforeRedirect = shouldLoadAsEmptyDocument(r.url());
 4 
 5     ASSERT(!m_handle);
 6     ASSERT(shouldLoadEmptyBeforeRedirect || !defersLoading());
 7 
 8     // Send this synthetic delegate callback since clients expect it, and
 9     // we no longer send the callback from within NSURLConnection for
10     // initial requests.
11     willSendRequest(r, ResourceResponse());
12 
13     // <rdar://problem/4801066>
14     // willSendRequest() is liable to make the call to frameLoader() return NULL, so we need to check that here
15     if (!frameLoader())
16         return false;
17     
18     const KURL& url = r.url();
19     bool shouldLoadEmpty = shouldLoadAsEmptyDocument(url) && !m_substituteData.isValid();
20 
21     if (shouldLoadEmptyBeforeRedirect && !shouldLoadEmpty && defersLoading())
22         return true;
23 
24     if (m_substituteData.isValid()) 
25         handleDataLoadSoon(r);
26     else if (shouldLoadEmpty || frameLoader()->representationExistsForURLScheme(url.protocol()))
27         handleEmptyLoad(url, !shouldLoadEmpty);
28     else
29         m_handle = ResourceHandle::create(r, this, m_frame.get(), false, true, true);
30 
31     return false;
32 }

主要两个调用:willSendRequest()和ResourceHandle::create(),前面一个估计是发送请求前的相关设定;后一个就是请求发送了。先看前一个:

 1 void MainResourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
 2 {
 3     // Note that there are no asserts here as there are for the other callbacks. This is due to the
 4     // fact that this "callback" is sent when starting every load, and the state of callback
 5     // deferrals plays less of a part in this function in preventing the bad behavior deferring 
 6     // callbacks is meant to prevent.
 7     ASSERT(!newRequest.isNull());
 8     
 9     // The additional processing can do anything including possibly removing the last
10     // reference to this object; one example of this is 3266216.
11     RefPtr<MainResourceLoader> protect(this);
12     
13     // Update cookie policy base URL as URL changes, except for subframes, which use the
14     // URL of the main frame which doesn‘t change when we redirect.
15     if (frameLoader()->isLoadingMainFrame())
16         newRequest.setMainDocumentURL(newRequest.url());
17     
18     // If we‘re fielding a redirect in response to a POST, force a load from origin, since
19     // this is a common site technique to return to a page viewing some data that the POST
20     // just modified.
21     // Also, POST requests always load from origin, but this does not affect subresources.
22     if (newRequest.cachePolicy() == UseProtocolCachePolicy && isPostOrRedirectAfterPost(newRequest, redirectResponse))
23         newRequest.setCachePolicy(ReloadIgnoringCacheData);
24 
25     ResourceLoader::willSendRequest(newRequest, redirectResponse);
26     
27     // Don‘t set this on the first request. It is set when the main load was started.
28     m_documentLoader->setRequest(newRequest);
29 
30     // FIXME: Ideally we‘d stop the I/O until we hear back from the navigation policy delegate
31     // listener. But there‘s no way to do that in practice. So instead we cancel later if the
32     // listener tells us to. In practice that means the navigation policy needs to be decided
33     // synchronously for these redirect cases.
34 
35     ref(); // balanced by deref in continueAfterNavigationPolicy
36     frameLoader()->checkNavigationPolicy(newRequest, callContinueAfterNavigationPolicy, this);
37 }

主要是调用ResourceLoader::willSendRequest()函数:

 1 void ResourceLoader::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
 2 {
 3     // Protect this in this delegate method since the additional processing can do
 4     // anything including possibly derefing this; one example of this is Radar 3266216.
 5     RefPtr<ResourceLoader> protector(this);
 6         
 7     ASSERT(!m_reachedTerminalState);
 8 
 9     if (m_sendResourceLoadCallbacks) {
10         if (!m_identifier) {
11             m_identifier = m_frame->page()->progress()->createUniqueIdentifier();
12             frameLoader()->assignIdentifierToInitialRequest(m_identifier, request);
13         }
14 
15         frameLoader()->willSendRequest(this, request, redirectResponse);
16     }
17     
18     m_request = request;
19 }

进一步调用FrameLoader::willSendRequest()

1 void FrameLoader::willSendRequest(ResourceLoader* loader, ResourceRequest& clientRequest, const ResourceResponse& redirectResponse)
2 {
3     applyUserAgent(clientRequest);
4     dispatchWillSendRequest(loader->documentLoader(), loader->identifier(), clientRequest, redirectResponse);
5 }

更多的调用:

 1 void FrameLoader::dispatchWillSendRequest(DocumentLoader* loader, unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse)
 2 {
 3     StringImpl* oldRequestURL = request.url().string().impl();
 4     m_documentLoader->didTellClientAboutLoad(request.url());
 5 
 6     m_client->dispatchWillSendRequest(loader, identifier, request, redirectResponse);
 7 
 8     // If the URL changed, then we want to put that new URL in the "did tell client" set too.
 9     if (oldRequestURL != request.url().string().impl())
10         m_documentLoader->didTellClientAboutLoad(request.url());
11 
12     if (Page* page = m_frame->page())
13         page->inspectorController()->willSendRequest(loader, identifier, request, redirectResponse);
14 }

囧~~还有下一步吗??
m_client->dispatchWillSendRequest()实际调用的是FrameLoaderClientQt::dispatchWillSendRequest(),目前是一个空函数(仅在dump的时候打印信息)。

 1 void InspectorController::willSendRequest(DocumentLoader*, unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse)
 2 {
 3     if (!enabled())
 4         return;
 5 
 6     InspectorResource* resource = m_resources.get(identifier).get();
 7     if (!resource)
 8         return;
 9 
10     resource->startTime = currentTime();
11 
12     if (!redirectResponse.isNull()) {
13         updateResourceRequest(resource, request);
14         updateResourceResponse(resource, redirectResponse);
15     }
16 
17     if (resource != m_mainResource && windowVisible()) {
18         if (!resource->scriptObject)
19             addScriptResource(resource);
20         else
21             updateScriptResourceRequest(resource);
22 
23         updateScriptResource(resource, resource->startTime, resource->responseReceivedTime, resource->endTime);
24 
25         if (!redirectResponse.isNull())
26             updateScriptResourceResponse(resource);
27     }
28 }

在这里设定了开始时间,猜测是供请求超时判断用的,请求超时的定时器在何处设定有待进一步分析。
看都是一些Resource的更新,感觉意义不大,不再进一步追踪。回到MainResourceLoader::loadNow(),看下一步ResourceHandle::create()

 1 PassRefPtr<ResourceHandle> ResourceHandle::create(const ResourceRequest& request, ResourceHandleClient* client,
 2     Frame* frame, bool defersLoading, bool shouldContentSniff, bool mightDownloadFromHandle)
 3 {
 4     RefPtr<ResourceHandle> newHandle(adoptRef(new ResourceHandle(request, client, defersLoading, shouldContentSniff, mightDownloadFromHandle)));
 5 
 6     if (!request.url().isValid()) {
 7         newHandle->scheduleFailure(InvalidURLFailure);
 8         return newHandle.release();
 9     }
10     // 检查端口号(port)是否合法
11     if (!portAllowed(request)) {
12         newHandle->scheduleFailure(BlockedFailure);
13         return newHandle.release();
14     }
15         
16     if (newHandle->start(frame))
17         return newHandle.release();
18 
19     return 0;
20 }

看关键的ResourceHandle::start调用:

 1 bool ResourceHandle::start(Frame* frame)
 2 {
 3     if (!frame)
 4         return false;
 5 
 6     Page *page = frame->page();
 7     // If we are no longer attached to a Page, this must be an attempted load from an
 8     // onUnload handler, so let‘s just block it.
 9     if (!page)
10         return false;
11 
12     getInternal()->m_frame = static_cast<FrameLoaderClientQt*>(frame->loader()->client())->webFrame();
13 #if QT_VERSION < 0x040400
14     return QWebNetworkManager::self()->add(this, getInternal()->m_frame->page()->d->networkInterface);
15 #else
16     ResourceHandleInternal *d = getInternal();
17     d->m_job = new QNetworkReplyHandler(this, QNetworkReplyHandler::LoadMode(d->m_defersLoading));
18     return true;
19 #endif
20 }

新创建了一个QNetworkReplyHandler对象,QNetworkReplyHandler在构造的时候会调用QNetworkReplyHandler::start()

 1 void QNetworkReplyHandler::start()
 2 {
 3     m_shouldStart = false;
 4 
 5     ResourceHandleInternal* d = m_resourceHandle->getInternal();
 6 
 7     QNetworkAccessManager* manager = d->m_frame->page()->networkAccessManager();
 8 
 9     const QUrl url = m_request.url();
10     const QString scheme = url.scheme();
11     // Post requests on files and data don‘t really make sense, but for
12     // fast/forms/form-post-urlencoded.html and for fast/forms/button-state-restore.html
13     // we still need to retrieve the file/data, which means we map it to a Get instead.
14     if (m_method == QNetworkAccessManager::PostOperation
15         && (!url.toLocalFile().isEmpty() || url.scheme() == QLatin1String("data")))
16         m_method = QNetworkAccessManager::GetOperation;
17 
18     m_startTime = QDateTime::currentDateTime().toTime_t();
19 
20     switch (m_method) {
21         case QNetworkAccessManager::GetOperation:
22             m_reply = manager->get(m_request);
23             break;
24         case QNetworkAccessManager::PostOperation: {
25             FormDataIODevice* postDevice = new FormDataIODevice(d->m_request.httpBody()); 
26             m_reply = manager->post(m_request, postDevice);
27             postDevice->setParent(m_reply);
28             break;
29         }
30         case QNetworkAccessManager::HeadOperation:
31             m_reply = manager->head(m_request);
32             break;
33         case QNetworkAccessManager::PutOperation: {
34             FormDataIODevice* putDevice = new FormDataIODevice(d->m_request.httpBody()); 
35             m_reply = manager->put(m_request, putDevice);
36             putDevice->setParent(m_reply);
37             break;
38         }
39         case QNetworkAccessManager::UnknownOperation: {
40             m_reply = 0;
41             ResourceHandleClient* client = m_resourceHandle->client();
42             if (client) {
43                 ResourceError error(url.host(), 400 /*bad request*/,
44                                     url.toString(),
45                                     QCoreApplication::translate("QWebPage", "Bad HTTP request"));
46                 client->didFail(m_resourceHandle, error);
47             }
48             return;
49         }
50     }
51 
52     m_reply->setParent(this);
53 
54     connect(m_reply, SIGNAL(finished()),
55             this, SLOT(finish()), Qt::QueuedConnection);
56 
57     // For http(s) we know that the headers are complete upon metaDataChanged() emission, so we
58     // can send the response as early as possible
59     if (scheme == QLatin1String("http") || scheme == QLatin1String("https"))
60         connect(m_reply, SIGNAL(metaDataChanged()),
61                 this, SLOT(sendResponseIfNeeded()), Qt::QueuedConnection);
62 
63     connect(m_reply, SIGNAL(readyRead()),
64             this, SLOT(forwardData()), Qt::QueuedConnection);
65 }

看到了熟悉的QNetworkAccessManager、QNetworkReply。跟踪至此,初始化和URL请求发送基本完成。

 

 

QT分析之WebKit

原文:http://www.cnblogs.com/lfsblack/p/5278777.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!