分享

webkit内核分析之 Frame

 wusiqi111 2019-04-28

参考地址:http://blog.csdn.net/dlmu2001/article/details/6164873

1.    描述

Frame类是WebCore内核同应用之间联系的一个重要的类。它有点像设计模式中的Façade,将内核的各个不同的零配件组装在了一起,但又不是Façade,因为用户很多时候还是要直接去操作里面的组件。除了设计上的考虑,Frame还有语法上的意义,它对应于Page里面的帧。

看一下类定义:

复制代码

  1  class Frame : public RefCounted<Frame>, public TiledBackingStoreClient {  2     public:  3         static PassRefPtr<Frame> create(Page*, HTMLFrameOwnerElement*, FrameLoaderClient*);  4   5         void init();  6         void setView(PassRefPtr<FrameView>);  7         void createView(const IntSize&, const Color&, bool, const IntSize&, bool,  8             ScrollbarMode = ScrollbarAuto, bool horizontalLock = false,  9             ScrollbarMode = ScrollbarAuto, bool verticalLock = false); 10  11         ~Frame(); 12  13         void addDestructionObserver(FrameDestructionObserver*); 14         void removeDestructionObserver(FrameDestructionObserver*); 15  16         void detachFromPage(); 17         void pageDestroyed(); 18         void disconnectOwnerElement(); 19  20         Page* page() const; 21         HTMLFrameOwnerElement* ownerElement() const; 22  23         Document* document() const; 24         FrameView* view() const; 25  26         Editor* editor() const; 27         EventHandler* eventHandler() const; 28         FrameLoader* loader() const; 29         NavigationScheduler* navigationScheduler() const; 30         SelectionController* selection() const; 31         FrameTree* tree() const; 32         AnimationController* animation() const; 33         ScriptController* script(); 34          35         RenderView* contentRenderer() const; // Root of the render tree for the document contained in this frame. 36         RenderPart* ownerRenderer() const; // Renderer for the element that contains this frame. 37  38         void transferChildFrameToNewDocument(); 39  40     // ======== All public functions below this point are candidates to move out of Frame into another class. ======== 41  42         bool isDisconnected() const; 43         void setIsDisconnected(bool); 44         bool excludeFromTextSearch() const; 45         void setExcludeFromTextSearch(bool); 46  47         void injectUserScripts(UserScriptInjectionTime); 48          49         String layerTreeAsText(bool showDebugInfo = false) const; 50  51         // Unlike most accessors in this class, domWindow() always creates a new DOMWindow if m_domWindow is null. 52         // Callers that don't need a new DOMWindow to be created should use existingDOMWindow(). 53         DOMWindow* domWindow() const; 54         DOMWindow* existingDOMWindow() { return m_domWindow.get(); } 55         void setDOMWindow(DOMWindow*); 56         void clearFormerDOMWindow(DOMWindow*); 57         void clearDOMWindow(); 58  59         static Frame* frameForWidget(const Widget*); 60  61         Settings* settings() const; // can be NULL 62  63         enum AdjustViewSizeOrNot { DoNotAdjustViewSize, AdjustViewSize }; 64         void setPrinting(bool printing, const FloatSize& pageSize, float maximumShrinkRatio, AdjustViewSizeOrNot); 65  66         bool inViewSourceMode() const; 67         void setInViewSourceMode(bool = true); 68  69         void keepAlive(); // Used to keep the frame alive when running a script that might destroy it. 70         static void cancelAllKeepAlive(); 71  72         void setDocument(PassRefPtr<Document>); 73  74         void setPageZoomFactor(float factor); 75         float pageZoomFactor() const { return m_pageZoomFactor; } 76         void setTextZoomFactor(float factor); 77         float textZoomFactor() const { return m_textZoomFactor; } 78         void setPageAndTextZoomFactors(float pageZoomFactor, float textZoomFactor); 79  80         void scalePage(float scale, const IntPoint& origin); 81         float pageScaleFactor() const { return m_pageScaleFactor; } 82  83 #if ENABLE(ORIENTATION_EVENTS) 84         // Orientation is the interface orientation in degrees. Some examples are: 85         //  0 is straight up; -90 is when the device is rotated 90 clockwise; 86         //  90 is when rotated counter clockwise. 87         void sendOrientationChangeEvent(int orientation); 88         int orientation() const { return m_orientation; } 89 #endif 90  91         void clearTimers(); 92         static void clearTimers(FrameView*, Document*); 93  94         String documentTypeString() const; 95  96         String displayStringModifiedByEncoding(const String&) const; 97  98         DragImageRef nodeImage(Node*); 99         DragImageRef dragImageForSelection();100 101         VisiblePosition visiblePositionForPoint(const IntPoint& framePoint);102         Document* documentAtPoint(const IntPoint& windowPoint);103         PassRefPtr<Range> rangeForPoint(const IntPoint& framePoint);104 105         String searchForLabelsAboveCell(Regular*, HTMLTableCellElement*, size_t* resultDistanceFromStartOfCell);106         String searchForLabelsBeforeElement(const Vector<String>& labels, Element*, size_t* resultDistance, bool* resultIsInCellAbove);107         String matchLabelsAgainstElement(const Vector<String>& labels, Element*);108 109         Color getDocumentBackgroundColor() const;110         111 #if PLATFORM(MAC)112         NSString* searchForLabelsBeforeElement(NSArray* labels, Element*, size_t* resultDistance, bool* resultIsInCellAbove);113         NSString* matchLabelsAgainstElement(NSArray* labels, Element*);114 115         NSImage* selectionImage(bool forceBlackText = false) const;116         NSImage* snapshotDragImage(Node*, NSRect* imageRect, NSRect* elementRect) const;117         NSImage* imageFromRect(NSRect) const;118 #endif119 120 #if ENABLE(MEDIA_STREAM)121         MediaStreamFrameController* mediaStreamFrameController() const { return m_mediaStreamFrameController.get(); }122 #endif123 124     // ========125 126     private:127         Frame(Page*, HTMLFrameOwnerElement*, FrameLoaderClient*);128 129         void injectUserScriptsForWorld(DOMWrapperWorld*, const UserScriptVector&, UserScriptInjectionTime);130         void lifeSupportTimerFired(Timer<Frame>*);131 132 #if USE(ACCELERATED_COMPOSITING)133         void updateContentsScale(float);134 #endif135 136         HashSet<FrameDestructionObserver*> m_destructionObservers;137 138         Page* m_page;
139         mutable FrameTree m_treeNode;
140         mutable FrameLoader m_loader;
141         mutable NavigationScheduler m_navigationScheduler;
142 
143         mutable RefPtr<DOMWindow> m_domWindow;
144         HashSet<DOMWindow*> m_liveFormerWindows;
145 
146         HTMLFrameOwnerElement* m_ownerElement;
147         RefPtr<FrameView> m_view;
148         RefPtr<Document> m_doc;
149 
150         ScriptController m_script;
151 
152         mutable Editor m_editor;
153         mutable SelectionController m_selectionController;
154         mutable EventHandler m_eventHandler;
155         mutable AnimationController m_animationController;
156 
157         Timer<Frame> m_lifeSupportTimer;
158 
159         float m_pageZoomFactor;
160         float m_textZoomFactor;
161 
162         float m_pageScaleFactor;
163 
164 #if ENABLE(ORIENTATION_EVENTS)
165         int m_orientation;
166 #endif
167 
168         bool m_inViewSourceMode;
169         bool m_isDisconnected;
170         bool m_excludeFromTextSearch;171 172 #if ENABLE(TILED_BACKING_STORE)173     // FIXME: The tiled backing store belongs in FrameView, not Frame.174 175     public:176         TiledBackingStore* tiledBackingStore() const { return m_tiledBackingStore.get(); }177         void setTiledBackingStoreEnabled(bool);178 179     private:180         // TiledBackingStoreClient interface181         virtual void tiledBackingStorePaintBegin();182         virtual void tiledBackingStorePaint(GraphicsContext*, const IntRect&);183         virtual void tiledBackingStorePaintEnd(const Vector<IntRect>& paintedArea);184         virtual IntRect tiledBackingStoreContentsRect();185         virtual IntRect tiledBackingStoreVisibleRect();186         virtual Color tiledBackingStoreBackgroundColor() const;187 188         OwnPtr<TiledBackingStore> m_tiledBackingStore;189 #endif190 191 #if ENABLE(MEDIA_STREAM)192         OwnPtr<MediaStreamFrameController> m_mediaStreamFrameController;193 #endif194     }

复制代码

2.    类结构

1,FrameTree对象用来协助管理父帧和子帧的关系,常见的比如 main frame之中有 iframe元素,就会调用 FrameLoaderClientQt::createFrame来产生子帧,产生的子帧会通过appendChild添加到主帧的树状结构中。Frame通过FrameTree对象,可以方便的访问它的父帧,子帧,兄弟帧。

复制代码

 1 Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer) 2 { 3     bool allowsScrolling = true; 4     int marginWidth = -1; 5     int marginHeight = -1; 6     if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) { 7         HTMLFrameElementBase* o = static_cast<HTMLFrameElementBase*>(ownerElement); 8         allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff; 9         marginWidth = o->marginWidth();10         marginHeight = o->marginHeight();11     }12 13     if (!ownerElement->document()->securityOrigin()->canDisplay(url)) {14         FrameLoader::reportLocalLoadFailed(m_frame, url.string());15         return 0;16     }17 18     if (!ownerElement->document()->contentSecurityPolicy()->allowChildFrameFromSource(url))19         return 0;20 21     bool hideReferrer = SecurityOrigin::shouldHideReferrer(url, referrer);22     RefPtr<Frame> frame = m_frame->loader()->client()->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer, allowsScrolling, marginWidth, marginHeight);23 24     if (!frame)  {25         m_frame->loader()->checkCallImplicitClose();26         return 0;27     }28     29     // All new frames will have m_isComplete set to true at this point due to synchronously loading30     // an empty document in FrameLoader::init(). But many frames will now be starting an31     // asynchronous load of url, so we set m_isComplete to false and then check if the load is32     // actually completed below. (Note that we set m_isComplete to false even for synchronous33     // loads, so that checkCompleted() below won't bail early.)34     // FIXME: Can we remove this entirely? m_isComplete normally gets set to false when a load is committed.35     frame->loader()->started();36    37     RenderObject* renderer = ownerElement->renderer();38     FrameView* view = frame->view();39     if (renderer && renderer->isWidget() && view)40         toRenderWidget(renderer)->setWidget(view);41     42     m_frame->loader()->checkCallImplicitClose();43     44     // Some loads are performed synchronously (e.g., about:blank and loads45     // cancelled by returning a null ResourceRequest from requestFromDelegate).46     // In these cases, the synchronous load would have finished47     // before we could connect the signals, so make sure to send the 
48     // completed() signal for the child by hand and mark the load as being49     // complete.50     // FIXME: In this case the Frame will have finished loading before 
51     // it's being added to the child list. It would be a good idea to52     // create the child first, then invoke the loader separately.53     if (frame->loader()->state() == FrameStateComplete && !frame->loader()->policyDocumentLoader())54         frame->loader()->checkCompleted();55 56     return frame.get();57 }

复制代码

然后就是创建看createFrame函数

复制代码

 1 PassRefPtr<Frame> FrameLoaderClientQt::createFrame(const KURL& url, const String& name, HTMLFrameOwnerElement* ownerElement, 2                                         const String& referrer, bool allowsScrolling, int marginWidth, int marginHeight) 3 { 4     if (!m_webFrame) 5         return 0; 6  7     QWebFrameData frameData(m_frame->page(), m_frame, ownerElement, name); 8  9     if (url.isEmpty())10         frameData.url = blankURL();11     else12         frameData.url = url;13 14     frameData.referrer = referrer;15     frameData.allowsScrolling = allowsScrolling;16     frameData.marginWidth = marginWidth;17     frameData.marginHeight = marginHeight;18 19     QPointer<QWebFrame> webFrame = new QWebFrame(m_webFrame, &frameData);20     // The creation of the frame may have run arbitrary JavaScript that removed it from the page already.21     if (!webFrame->d->frame->page()) {22         frameData.frame.release();23         ASSERT(webFrame.isNull());24         return 0;25     }26 27     emit m_webFrame->page()->frameCreated(webFrame);28 29     // FIXME: Set override encoding if we have one.30 31     m_frame->loader()->loadURLIntoChildFrame(frameData.url, frameData.referrer, frameData.frame.get());32 33     // The frame's onload handler may have removed it from the document.34     if (!frameData.frame->tree()->parent())35         return 0;36 37     return frameData.frame.release();38 }

复制代码

1 void FrameTree::appendChild(PassRefPtr<Frame> child)2 {3     ASSERT(child->page() == m_thisFrame->page());4     child->tree()->m_parent = m_thisFrame;5     actuallyAppendChild(child); // Note, on return |child| is null.6 }

2,维护FrameLoader对象用来完成frame的加载,FrameLoader是一个非常重要的类,后续进行进一步的分析。

3,维护NavigationScheduler对象用来管理页面跳转调度(比如重定向,meta refresh等)。

4,DOMWindow用来管理同 DOM 相关的事件、属性和消息。

5,FrameView类用于Frame的排版

6,Frame文档解析后,对于每一个tag或者attr,会有对应的dom节点关联,Document类用来管理这些dom节点。不同的文档类型继承出不同的子类,比如HTML文档对应子类 HTMLDocument,XML文档对应于XMLDocument。

7,ScriptController对象。脚本控制器,用来管理脚本的执行和操作

8,Editor对象用来处理页面的编辑相关工作,比如拷贝,粘贴,输入等,Editor对象,它同Page类的 EditorClient对象紧密合作。 和EditorClient关系就如同 Page和 Frame的关系

9,SelectionController 用来管理 Frame中的选取操作

10,AnimationController 动画控制,控制动画的播放、暂停、继续(同 HTML video标签是否有关系?)

11,EventHandler 事件处理对象,这里的对象主要是同上层应用也即是用户参与的事件, 比如鼠标事件、按键事件(快捷键等)、滚动事件、resize事件等。这是一个浏览器外壳经常需要打交道的类

3.    主要接口

3.1   Create

复制代码

 1 static PassRefPtr<Frame> create(Page*, HTMLFrameOwnerElement*, FrameLoaderClient*); 2  3  4  5 PassRefPtr<Frame> Frame::create(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* client) 6 { 7     RefPtr<Frame> frame = adoptRef(new Frame(page, ownerElement, client)); 8     if (!ownerElement) 9         page->setMainFrame(frame);10     return frame.release();11 }

复制代码

描述:
      调用Frame构造函数,创建出 Frame 对象。 有两个地方会创建 Frame对象: 一个是要加载一个新的页面请求,这时候会创建一个 main frame。 一个是 在加载子帧的时候,通过 FrameLoaderClientQt 的

createFrame接口,创建子帧对应的Frame对象,在第一种情况下 HTMLFrameOwnerElement的参数为NULL, 第二种情况传子帧的父元素。在一个tab页内,main frame会重用

调用系列:

复制代码

 1 QwebPage::setView 2 QwebPage::setViewportSize 3 QwebPage::mainFrame 4 QwebPagePrivate::createMainFrame 5 QwebFrameData::QwebFrameData 6 Frame::create 7  8 FrameLoader::finishedLoading 9 ......10 HTMLDocumentParser::append11 ......12 HTMLTreeBuilder::processToken13 ......14 HTMLElementBase::openURL15 SubFrameLoader::requestFrame16 ......17 FrameLoaderClientQt::creatFrame18 QwebFrameData::QwebFrameData19 Frame::create

复制代码

源码追踪一下(只跟踪源码和颜色标识,不解释):

第一种情况下:

1,

复制代码

 1 void QWebPage::setView(QWidget* view) 2 { 3     if (this->view() == view) 4         return; 5  6     d->view = view; 7     setViewportSize(view ? view->size() : QSize(0, 0)); 8  9     // If we have no client, we install a special client delegating10     // the responsibility to the QWidget. This is the code path11     // handling a.o. the "legacy" QWebView.12     //13     // If such a special delegate already exist, we substitute the view.14 15     if (d->client) {16         if (d->client->isQWidgetClient())17             static_cast<PageClientQWidget*>(d->client.get())->view = view;18         return;19     }20 21     if (view)22         d->client = new PageClientQWidget(view, this);23 }

复制代码

2,

复制代码

 1 void QWebPage::setViewportSize(const QSize &size) const 2 { 3     d->viewportSize = size; 4  5     QWebFrame *frame = mainFrame(); 6     if (frame->d->frame && frame->d->frame->view()) { 7         WebCore::FrameView* view = frame->d->frame->view(); 8         view->resize(size); 9         view->adjustViewSize();10     }11 }

复制代码

3,

1 QWebFrame *QWebPage::mainFrame() const2 {3     d->createMainFrame();4     return d->mainFrame;5 }

4,

复制代码

1 void QWebPagePrivate::createMainFrame()2 {3     if (!mainFrame) {4         QWebFrameData frameData(page);5         mainFrame = new QWebFrame(q, &frameData);6 7         emit q->frameCreated(mainFrame);8     }9 }

复制代码

5,

1     QWebFrameData(WebCore::Page*, WebCore::Frame* parentFrame = 0,2                   WebCore::HTMLFrameOwnerElement* = 0,3                   const WTF::String& frameName = WTF::String());

6,

复制代码

 1 QWebFrameData::QWebFrameData(WebCore::Page* parentPage, WebCore::Frame* parentFrame, 2                              WebCore::HTMLFrameOwnerElement* ownerFrameElement, 3                              const WTF::String& frameName) 4     : name(frameName) 5     , ownerElement(ownerFrameElement) 6     , page(parentPage) 7     , allowsScrolling(true) 8     , marginWidth(0) 9     , marginHeight(0)10 {11     frameLoaderClient = new FrameLoaderClientQt();12     frame = Frame::create(page, ownerElement, frameLoaderClient);13 14     // FIXME: All of the below should probably be moved over into WebCore15     frame->tree()->setName(name);16     if (parentFrame)17         parentFrame->tree()->appendChild(frame);18 }

复制代码

第二种情况下:

1,FrameLoader::finishedLoading()

复制代码

 1 void FrameLoader::finishedLoading() 2 { 3     // Retain because the stop may release the last reference to it. 4     RefPtr<Frame> protect(m_frame); 5  6     RefPtr<DocumentLoader> dl = activeDocumentLoader(); 7     dl->finishedLoading(); 8     if (!dl->mainDocumentError().isNull() || !dl->frameLoader()) 9         return;10     dl->setPrimaryLoadComplete(true);11     m_client->dispatchDidLoadMainResource(dl.get());12     checkLoadComplete();13 }

复制代码

2,DocumentLoader::finishedLoading()

复制代码

1 void DocumentLoader::finishedLoading()2 {3     m_gotFirstByte = true;   
4     commitIfReady();5     if (FrameLoader* loader = frameLoader()) {6         loader->finishedLoadingDocument(this);7         m_writer.end();8     }9 }

复制代码

3,DocumentWriter::end()

1 void DocumentWriter::end()2 {3     m_frame->loader()->didEndDocument();4     endIfNotLoadingMainResource();5 }

4, DocumentWriter::endIfNotLoadingMainResource()

复制代码

 1 void DocumentWriter::endIfNotLoadingMainResource() 2 { 3     if (m_frame->loader()->isLoadingMainResource() || !m_frame->page() || !m_frame->document()) 4         return; 5  6     // http://bugs./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 there12     addData(0, 0, true);13     m_frame->document()->finishParsing();14 }

复制代码

5,DocumentWriter::addData(const char* str, int len, bool flush)

复制代码

1 void DocumentWriter::addData(const char* str, int len, bool flush)2 {3     if (len == -1)4         len = strlen(str);5 6     DocumentParser* parser = m_frame->document()->parser();7     if (parser)8         parser->appendBytes(this, str, len, flush);9 }

复制代码

6,DecodedDataDocumentParser::appendBytes(DocumentWriter* writer , const char* data, int length, bool shouldFlush)

复制代码

 1 void DecodedDataDocumentParser::appendBytes(DocumentWriter* writer , const char* data, int length, bool shouldFlush) 2 { 3     if (!length && !shouldFlush) 4         return; 5  6     TextResourceDecoder* decoder = writer->createDecoderIfNeeded(); 7     String decoded = decoder->decode(data, length); 8     if (shouldFlush) 9         decoded += decoder->flush();10     if (decoded.isEmpty())11         return;12 13     writer->reportDataReceived();14 15     append(decoded);16 }

复制代码

7,HTMLDocumentParser::append(const SegmentedString& source)

复制代码

 1 void HTMLDocumentParser::append(const SegmentedString& source) 2 { 3     if (isStopped()) 4         return; 5  6     // pumpTokenizer can cause this parser to be detached from the Document, 7     // but we need to ensure it isn't deleted yet. 8     RefPtr<HTMLDocumentParser> protect(this); 9 10     if (m_preloadScanner) {11         if (m_input.current().isEmpty() && !isWaitingForScripts()) {12             // We have parsed until the end of the current input and so are now moving ahead of the preload scanner.13             // Clear the scanner so we know to scan starting from the current input point if we block again.14             m_preloadScanner.clear();15         } else {16             m_preloadScanner->appendToEnd(source);17             if (isWaitingForScripts())18                 m_preloadScanner->scan();19         }20     }21 22     m_input.appendToEnd(source);23 24     if (inPumpSession()) {25         // We've gotten data off the network in a nested write.26         // We don't want to consume any more of the input stream now.  Do27         // not worry.  We'll consume this data in a less-nested write().28         return;29     }30 31     pumpTokenizerIfPossible(AllowYield);32 33     endIfDelayed();34 }

复制代码

8,HTMLDocumentParser::pumpTokenizerIfPossible(SynchronousMode mode)

复制代码

 1 void HTMLDocumentParser::pumpTokenizerIfPossible(SynchronousMode mode) 2 { 3     if (isStopped() || m_treeBuilder->isPaused()) 4         return; 5  6     // Once a resume is scheduled, HTMLParserScheduler controls when we next pump. 7     if (isScheduledForResume()) { 8         ASSERT(mode == AllowYield); 9         return;10     }11 12     pumpTokenizer(mode);13 }

复制代码

9,HTMLDocumentParser::pumpTokenizer(SynchronousMode mode)

复制代码

 1 void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode) 2 { 3     ASSERT(!isStopped()); 4     ASSERT(!isScheduledForResume()); 5     // ASSERT that this object is both attached to the Document and protected. 6     ASSERT(refCount() >= 2); 7  8     PumpSession session(m_pumpSessionNestingLevel); 9 10     // We tell the InspectorInstrumentation about every pump, even if we11     // end up pumping nothing.  It can filter out empty pumps itself.12     // FIXME: m_input.current().length() is only accurate if we13     // end up parsing the whole buffer in this pump.  We should pass how14     // much we parsed as part of didWriteHTML instead of willWriteHTML.15     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willWriteHTML(document(), m_input.current().length(), m_tokenizer->lineNumber());16 17     while (canTakeNextToken(mode, session) && !session.needsYield) {18         if (!isParsingFragment())19             m_sourceTracker.start(m_input, m_token);20 21         if (!m_tokenizer->nextToken(m_input.current(), m_token))22             break;23 24         if (!isParsingFragment()) {25             m_sourceTracker.end(m_input, m_token);26 27             // We do not XSS filter innerHTML, which means we (intentionally) fail28             // http/tests/security/xssAuditor/dom-write-innerHTML.html29             m_xssFilter.filterToken(m_token);30         }31 32         m_treeBuilder->constructTreeFromToken(m_token);33         ASSERT(m_token.isUninitialized());34     }35 36     // Ensure we haven't been totally deref'ed after pumping. Any caller of this37     // function should be holding a RefPtr to this to ensure we weren't deleted.38     ASSERT(refCount() >= 1);39 40     if (isStopped())41         return;42 43     if (session.needsYield)44         m_parserScheduler->scheduleForResume();45 46     if (isWaitingForScripts()) {47         ASSERT(m_tokenizer->state() == HTMLTokenizer::DataState);48         if (!m_preloadScanner) {49             m_preloadScanner = adoptPtr(new HTMLPreloadScanner(document()));50             m_preloadScanner->appendToEnd(m_input.current());51         }52         m_preloadScanner->scan();53     }54 55     InspectorInstrumentation::didWriteHTML(cookie, m_tokenizer->lineNumber());56 }

复制代码

10,HTMLTreeBuilder::constructTreeFromToken(HTMLToken& rawToken)

复制代码

 1 void HTMLTreeBuilder::constructTreeFromToken(HTMLToken& rawToken) 2 { 3     AtomicHTMLToken token(rawToken); 4  5     // We clear the rawToken in case constructTreeFromAtomicToken 6     // synchronously re-enters the parser. We don't clear the token immedately 7     // for Character tokens because the AtomicHTMLToken avoids copying the 8     // characters by keeping a pointer to the underlying buffer in the 9     // HTMLToken. Fortuantely, Character tokens can't cause use to re-enter10     // the parser.11     //12     // FIXME: Top clearing the rawToken once we start running the parser off13     // the main thread or once we stop allowing synchronous JavaScript14     // execution from parseMappedAttribute.15     if (rawToken.type() != HTMLToken::Character)16         rawToken.clear();17 18     constructTreeFromAtomicToken(token);19 20     if (!rawToken.isUninitialized()) {21         ASSERT(rawToken.type() == HTMLToken::Character);22         rawToken.clear();23     }24 }

复制代码

11,HTMLTreeBuilder::constructTreeFromAtomicToken(AtomicHTMLToken& token)

复制代码

 1 void HTMLTreeBuilder::constructTreeFromAtomicToken(AtomicHTMLToken& token) 2 { 3     processToken(token); 4  5     // Swallowing U+0000 characters isn't in the HTML5 spec, but turning all 6     // the U+0000 characters into replacement characters has compatibility 7     // problems. 8     m_parser->tokenizer()->setForceNullCharacterReplacement(m_insertionMode == TextMode || m_insertionMode == InForeignContentMode); 9     m_parser->tokenizer()->setShouldAllowCDATA(m_insertionMode == InForeignContentMode && !isInHTMLNamespace(m_tree.currentNode()));10 }

复制代码

12,HTMLTreeBuilder::processToken(AtomicHTMLToken& token)

复制代码

 1 void HTMLTreeBuilder::processToken(AtomicHTMLToken& token) 2 { 3     switch (token.type()) { 4     case HTMLToken::Uninitialized: 5         ASSERT_NOT_REACHED(); 6         break; 7     case HTMLToken::DOCTYPE: 8         processDoctypeToken(token); 9         break;10     case HTMLToken::StartTag:11         processStartTag(token);12         break;13     case HTMLToken::EndTag:14         processEndTag(token);15         break;16     case HTMLToken::Comment:17         processComment(token);18         return;19     case HTMLToken::Character:20         processCharacter(token);21         break;22     case HTMLToken::EndOfFile:23         processEndOfFile(token);24         break;25     }26 }

复制代码

13,HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token)

复制代码

  1 void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token)  2 {  3     ASSERT(token.type() == HTMLToken::StartTag);  4     switch (insertionMode()) {  5     case InitialMode:  6         ASSERT(insertionMode() == InitialMode);  7         defaultForInitial();  8         // Fall through.  9     case BeforeHTMLMode: 10         ASSERT(insertionMode() == BeforeHTMLMode); 11         if (token.name() == htmlTag) { 12             m_tree.insertHTMLHtmlStartTagBeforeHTML(token); 13             setInsertionMode(BeforeHeadMode); 14             return; 15         } 16         defaultForBeforeHTML(); 17         // Fall through. 18     case BeforeHeadMode: 19         ASSERT(insertionMode() == BeforeHeadMode); 20         if (token.name() == htmlTag) { 21             m_tree.insertHTMLHtmlStartTagInBody(token); 22             return; 23         } 24         if (token.name() == headTag) { 25             m_tree.insertHTMLHeadElement(token); 26             setInsertionMode(InHeadMode); 27             return; 28         } 29         defaultForBeforeHead(); 30         // Fall through. 31     case InHeadMode: 32         ASSERT(insertionMode() == InHeadMode); 33         if (processStartTagForInHead(token)) 34             return; 35         defaultForInHead(); 36         // Fall through. 37     case AfterHeadMode: 38         ASSERT(insertionMode() == AfterHeadMode); 39         if (token.name() == htmlTag) { 40             m_tree.insertHTMLHtmlStartTagInBody(token); 41             return; 42         } 43         if (token.name() == bodyTag) { 44             m_framesetOk = false; 45             m_tree.insertHTMLBodyElement(token); 46             setInsertionMode(InBodyMode); 47             return; 48         } 49         if (token.name() == framesetTag) { 50             m_tree.insertHTMLElement(token); 51             setInsertionMode(InFramesetMode); 52             return; 53         } 54         if (token.name() == baseTag 55             || token.name() == basefontTag 56             || token.name() == bgsoundTag 57             || token.name() == linkTag 58             || token.name() == metaTag 59             || token.name() == noframesTag 60             || token.name() == scriptTag 61             || token.name() == styleTag 62             || token.name() == titleTag) { 63             parseError(token); 64             ASSERT(m_tree.head()); 65             m_tree.openElements()->pushHTMLHeadElement(m_tree.head()); 66             processStartTagForInHead(token); 67             m_tree.openElements()->removeHTMLHeadElement(m_tree.head()); 68             return; 69         } 70         if (token.name() == headTag) { 71             parseError(token); 72             return; 73         } 74         defaultForAfterHead(); 75         // Fall through 76     case InBodyMode: 77         ASSERT(insertionMode() == InBodyMode); 78         processStartTagForInBody(token); 79         break; 80     case InTableMode: 81         ASSERT(insertionMode() == InTableMode); 82         processStartTagForInTable(token); 83         break; 84     case InCaptionMode: 85         ASSERT(insertionMode() == InCaptionMode); 86         if (isCaptionColOrColgroupTag(token.name()) 87             || isTableBodyContextTag(token.name()) 88             || isTableCellContextTag(token.name()) 89             || token.name() == trTag) { 90             parseError(token); 91             if (!processCaptionEndTagForInCaption()) { 92                 ASSERT(isParsingFragment()); 93                 return; 94             } 95             reprocessStartTag(token); 96             return; 97         } 98         processStartTagForInBody(token); 99         break;100     case InColumnGroupMode:101         ASSERT(insertionMode() == InColumnGroupMode);102         if (token.name() == htmlTag) {103             m_tree.insertHTMLHtmlStartTagInBody(token);104             return;105         }106         if (token.name() == colTag) {107             m_tree.insertSelfClosingHTMLElement(token);108             return;109         }110         if (!processColgroupEndTagForInColumnGroup()) {111             ASSERT(isParsingFragment());112             return;113         }114         reprocessStartTag(token);115         break;116     case InTableBodyMode:117         ASSERT(insertionMode() == InTableBodyMode);118         if (token.name() == trTag) {119             m_tree.openElements()->popUntilTableBodyScopeMarker(); // How is there ever anything to pop?120             m_tree.insertHTMLElement(token);121             setInsertionMode(InRowMode);122             return;123         }124         if (isTableCellContextTag(token.name())) {125             parseError(token);126             processFakeStartTag(trTag);127             ASSERT(insertionMode() == InRowMode);128             reprocessStartTag(token);129             return;130         }131         if (isCaptionColOrColgroupTag(token.name()) || isTableBodyContextTag(token.name())) {132             // FIXME: This is slow.133             if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) {134                 ASSERT(isParsingFragment());135                 parseError(token);136                 return;137             }138             m_tree.openElements()->popUntilTableBodyScopeMarker();139             ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName()));140             processFakeEndTag(m_tree.currentElement()->tagQName());141             reprocessStartTag(token);142             return;143         }144         processStartTagForInTable(token);145         break;146     case InRowMode:147         ASSERT(insertionMode() == InRowMode);148         if (isTableCellContextTag(token.name())) {149             m_tree.openElements()->popUntilTableRowScopeMarker();150             m_tree.insertHTMLElement(token);151             setInsertionMode(InCellMode);152             m_tree.activeFormattingElements()->appendMarker();153             return;154         }155         if (token.name() == trTag156             || isCaptionColOrColgroupTag(token.name())157             || isTableBodyContextTag(token.name())) {158             if (!processTrEndTagForInRow()) {159                 ASSERT(isParsingFragment());160                 return;161             }162             ASSERT(insertionMode() == InTableBodyMode);163             reprocessStartTag(token);164             return;165         }166         processStartTagForInTable(token);167         break;168     case InCellMode:169         ASSERT(insertionMode() == InCellMode);170         if (isCaptionColOrColgroupTag(token.name())171             || isTableCellContextTag(token.name())172             || token.name() == trTag173             || isTableBodyContextTag(token.name())) {174             // FIXME: This could be more efficient.175             if (!m_tree.openElements()->inTableScope(tdTag) && !m_tree.openElements()->inTableScope(thTag)) {176                 ASSERT(isParsingFragment());177                 parseError(token);178                 return;179             }180             closeTheCell();181             reprocessStartTag(token);182             return;183         }184         processStartTagForInBody(token);185         break;186     case AfterBodyMode:187     case AfterAfterBodyMode:188         ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);189         if (token.name() == htmlTag) {190             m_tree.insertHTMLHtmlStartTagInBody(token);191             return;192         }193         setInsertionMode(InBodyMode);194         reprocessStartTag(token);195         break;196     case InHeadNoscriptMode:197         ASSERT(insertionMode() == InHeadNoscriptMode);198         if (token.name() == htmlTag) {199             m_tree.insertHTMLHtmlStartTagInBody(token);200             return;201         }202         if (token.name() == basefontTag203             || token.name() == bgsoundTag204             || token.name() == linkTag205             || token.name() == metaTag206             || token.name() == noframesTag207             || token.name() == styleTag) {208             bool didProcess = processStartTagForInHead(token);209             ASSERT_UNUSED(didProcess, didProcess);210             return;211         }212         if (token.name() == htmlTag || token.name() == noscriptTag) {213             parseError(token);214             return;215         }216         defaultForInHeadNoscript();217         processToken(token);218         break;219     case InFramesetMode:220         ASSERT(insertionMode() == InFramesetMode);221         if (token.name() == htmlTag) {222             m_tree.insertHTMLHtmlStartTagInBody(token);223             return;224         }225         if (token.name() == framesetTag) {226             m_tree.insertHTMLElement(token);227             return;228         }229         if (token.name() == frameTag) {230             m_tree.insertSelfClosingHTMLElement(token);231             return;232         }233         if (token.name() == noframesTag) {234             processStartTagForInHead(token);235             return;236         }237         parseError(token);238         break;239     case AfterFramesetMode:240     case AfterAfterFramesetMode:241         ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);242         if (token.name() == htmlTag) {243             m_tree.insertHTMLHtmlStartTagInBody(token);244             return;245         }246         if (token.name() == noframesTag) {247             processStartTagForInHead(token);248             return;249         }250         parseError(token);251         break;252     case InSelectInTableMode:253         ASSERT(insertionMode() == InSelectInTableMode);254         if (token.name() == captionTag255             || token.name() == tableTag256             || isTableBodyContextTag(token.name())257             || token.name() == trTag258             || isTableCellContextTag(token.name())) {259             parseError(token);260             AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());261             processEndTag(endSelect);262             reprocessStartTag(token);263             return;264         }265         // Fall through266     case InSelectMode:267         ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);268         if (token.name() == htmlTag) {269             m_tree.insertHTMLHtmlStartTagInBody(token);270             return;271         }272         if (token.name() == optionTag) {273             if (m_tree.currentNode()->hasTagName(optionTag)) {274                 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());275                 processEndTag(endOption);276             }277             m_tree.insertHTMLElement(token);278             return;279         }280         if (token.name() == optgroupTag) {281             if (m_tree.currentNode()->hasTagName(optionTag)) {282                 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());283                 processEndTag(endOption);284             }285             if (m_tree.currentNode()->hasTagName(optgroupTag)) {286                 AtomicHTMLToken endOptgroup(HTMLToken::EndTag, optgroupTag.localName());287                 processEndTag(endOptgroup);288             }289             m_tree.insertHTMLElement(token);290             return;291         }292         if (token.name() == selectTag) {293             parseError(token);294             AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());295             processEndTag(endSelect);296             return;297         }298         if (token.name() == inputTag299             || token.name() == keygenTag300             || token.name() == textareaTag) {301             parseError(token);302             if (!m_tree.openElements()->inSelectScope(selectTag)) {303                 ASSERT(isParsingFragment());304                 return;305             }306             AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());307             processEndTag(endSelect);308             reprocessStartTag(token);309             return;310         }311         if (token.name() == scriptTag) {312             bool didProcess = processStartTagForInHead(token);313             ASSERT_UNUSED(didProcess, didProcess);314             return;315         }316         break;317     case InTableTextMode:318         defaultForInTableText();319         processStartTag(token);320         break;321     case InForeignContentMode: {322         if (shouldProcessForeignContentUsingInBodyInsertionMode(token, m_tree.currentNode())) {323             processForeignContentUsingInBodyModeAndResetMode(token);324             return;325         }326         if (token.name() == bTag327             || token.name() == bigTag328             || token.name() == blockquoteTag329             || token.name() == bodyTag330             || token.name() == brTag331             || token.name() == centerTag332             || token.name() == codeTag333             || token.name() == ddTag334             || token.name() == divTag335             || token.name() == dlTag336             || token.name() == dtTag337             || token.name() == emTag338             || token.name() == embedTag339             || isNumberedHeaderTag(token.name())340             || token.name() == headTag341             || token.name() == hrTag342             || token.name() == iTag343             || token.name() == imgTag344             || token.name() == liTag345             || token.name() == listingTag346             || token.name() == menuTag347             || token.name() == metaTag348             || token.name() == nobrTag349             || token.name() == olTag350             || token.name() == pTag351             || token.name() == preTag352             || token.name() == rubyTag353             || token.name() == sTag354             || token.name() == smallTag355             || token.name() == spanTag356             || token.name() == strongTag357             || token.name() == strikeTag358             || token.name() == subTag359             || token.name() == supTag360             || token.name() == tableTag361             || token.name() == ttTag362             || token.name() == uTag363             || token.name() == ulTag364             || token.name() == varTag365             || (token.name() == fontTag && (token.getAttributeItem(colorAttr) || token.getAttributeItem(faceAttr) || token.getAttributeItem(sizeAttr)))) {366             parseError(token);367             m_tree.openElements()->popUntilForeignContentScopeMarker();368             resetInsertionModeAppropriately();369             reprocessStartTag(token);370             return;371         }372         const AtomicString& currentNamespace = m_tree.currentElement()->namespaceURI();373         if (currentNamespace == MathMLNames::mathmlNamespaceURI)374             adjustMathMLAttributes(token);375         if (currentNamespace == SVGNames::svgNamespaceURI) {376             adjustSVGTagNameCase(token);377             adjustSVGAttributes(token);378         }379         adjustForeignAttributes(token);380         m_tree.insertForeignElement(token, currentNamespace);381         break;382     }383     case TextMode:384         ASSERT_NOT_REACHED();385         break;386     }387 }

复制代码

14,HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken& token)

1 void HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken& token)2 {3     m_openElements.push(attachToCurrent(createHTMLElement(token)));4 }

15,HTMLConstructionSite::attachToCurrent(PassRefPtr<Element> child)

1 PassRefPtr<Element> HTMLConstructionSite::attachToCurrent(PassRefPtr<Element> child)2 {3     return attach(currentNode(), child);4 }

16,HTMLConstructionSite::attach(ContainerNode* rawParent, PassRefPtr<ChildType> prpChild)

复制代码

 1 template<typename ChildType> 2 PassRefPtr<ChildType> HTMLConstructionSite::attach(ContainerNode* rawParent, PassRefPtr<ChildType> prpChild) 3 { 4     RefPtr<ChildType> child = prpChild; 5     RefPtr<ContainerNode> parent = rawParent; 6  7     // FIXME: It's confusing that HTMLConstructionSite::attach does the magic 8     // redirection to the foster parent but HTMLConstructionSite::attachAtSite 9     // doesn't. It feels like we're missing a concept somehow.10     if (shouldFosterParent()) {11         fosterParent(child.get());12         ASSERT(child->attached() || !child->parentNode() || !child->parentNode()->attached());13         return child.release();14     }15 16     parent->parserAddChild(child);17 18     // An event handler (DOM Mutation, beforeload, et al.) could have removed19     // the child, in which case we shouldn't try attaching it.20     if (!child->parentNode())21         return child.release();22 23     if (parent->attached() && !child->attached())24         child->attach();25     return child.release();26 }

复制代码

17,ContainerNode::parserAddChild(PassRefPtr<Node> newChild)

复制代码

 1 void ContainerNode::parserAddChild(PassRefPtr<Node> newChild) 2 { 3     ASSERT(newChild); 4     ASSERT(!newChild->parentNode()); // Use appendChild if you need to handle reparenting (and want DOM mutation events). 5  6 #if ENABLE(INSPECTOR) 7     InspectorInstrumentation::willInsertDOMNode(document(), newChild.get(), this); 8 #endif 9 10     forbidEventDispatch();11     Node* last = m_lastChild;12     // FIXME: This method should take a PassRefPtr.13     appendChildToContainer<Node, ContainerNode>(newChild.get(), this);14     newChild->setTreeScopeRecursively(treeScope());15     16     allowEventDispatch();17 18     // FIXME: Why doesn't this use notifyChildInserted(newChild) instead?19     document()->incDOMTreeVersion();20     if (inDocument())21         newChild->insertedIntoDocument();22     childrenChanged(true, last, 0, 1);23 }

复制代码

18,ContainerNode::insertedIntoDocument()

复制代码

 1 void ContainerNode::insertedIntoDocument() 2 { 3     RefPtr<Node> protect(this); 4  5     Node::insertedIntoDocument(); 6     insertedIntoTree(false); 7  8     for (RefPtr<Node> child = m_firstChild; child; child = child->nextSibling()) { 9         // Guard against mutation during re-parenting.10         if (!inDocument()) // Check for self being removed from document while reparenting.11             break;12         if (child->parentNode() != this) // Check for child being removed from subtree while reparenting.13             break;14         child->insertedIntoDocument();15     }16 }

复制代码

19,HTMLFrameElementBase::insertedIntoDocument()

复制代码

 1 void HTMLFrameElementBase::insertedIntoDocument() 2 { 3     HTMLFrameOwnerElement::insertedIntoDocument(); 4  5     if (m_remainsAliveOnRemovalFromTree) { 6         updateOnReparenting(); 7         setRemainsAliveOnRemovalFromTree(false); 8         return; 9     }10     // DocumentFragments don't kick of any loads.11     if (!document()->frame())12         return;13 14     // Loads may cause synchronous javascript execution (e.g. beforeload or15     // src=javascript), which could try to access the renderer before the normal16     // parser machinery would call lazyAttach() and set us as needing style17     // resolve.  Any code which expects this to be attached will resolve style18     // before using renderer(), so this will make sure we attach in time.19     // FIXME: Normally lazyAttach marks the renderer as attached(), but we don't20     // want to do that here, as as callers expect to call attach() right after21     // this and attach() will ASSERT(!attached())22     ASSERT(!renderer()); // This recalc is unecessary if we already have a renderer.23     lazyAttach(DoNotSetAttached);24     setNameAndOpenURL();25 }

复制代码

20,HTMLFrameElementBase::setNameAndOpenURL()

复制代码

1 void HTMLFrameElementBase::setNameAndOpenURL()2 {3     m_frameName = getAttribute(nameAttr);4     if (m_frameName.isNull())5         m_frameName = getIdAttribute();6     openURL();7 }

复制代码

21,HTMLFrameElementBase::openURL(bool lockHistory, bool lockBackForwardList)

复制代码

 1 void HTMLFrameElementBase::openURL(bool lockHistory, bool lockBackForwardList) 2 { 3     if (!isURLAllowed()) 4         return; 5  6     if (m_URL.isEmpty()) 7         m_URL = blankURL().string(); 8  9     Frame* parentFrame = document()->frame();10     if (!parentFrame)11         return;12 13     parentFrame->loader()->subframeLoader()->requestFrame(this, m_URL, m_frameName, lockHistory, lockBackForwardList);14     if (contentFrame())15         contentFrame()->setInViewSourceMode(viewSourceMode());16 }

复制代码

22,SubframeLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList)

复制代码

 1 bool SubframeLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList) 2 { 3     // Support for <frame src="javascript:string"> 4     KURL scriptURL; 5     KURL url; 6     if (protocolIsJavaScript(urlString)) { 7         scriptURL = completeURL(urlString); // completeURL() encodes the URL. 8         url = blankURL(); 9     } else10         url = completeURL(urlString);11 12     Frame* frame = loadOrRedirectSubframe(ownerElement, url, frameName, lockHistory, lockBackForwardList);13     if (!frame)14         return false;15 16     if (!scriptURL.isEmpty())17         frame->script()->executeIfJavaScriptURL(scriptURL);18 19     return true;20 }

复制代码

23,SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList)

复制代码

1 Frame* SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList)2 {3     Frame* frame = ownerElement->contentFrame();4     if (frame)5         frame->navigationScheduler()->scheduleLocationChange(m_frame->document()->securityOrigin(), url.string(), m_frame->loader()->outgoingReferrer(), lockHistory, lockBackForwardList);6     else7         frame = loadSubframe(ownerElement, url, frameName, m_frame->loader()->outgoingReferrer());8     return frame;9 }

复制代码

24,SubframeLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer)

复制代码

 1 Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer) 2 { 3     bool allowsScrolling = true; 4     int marginWidth = -1; 5     int marginHeight = -1; 6     if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) { 7         HTMLFrameElementBase* o = static_cast<HTMLFrameElementBase*>(ownerElement); 8         allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff; 9         marginWidth = o->marginWidth();10         marginHeight = o->marginHeight();11     }12 13     if (!ownerElement->document()->securityOrigin()->canDisplay(url)) {14         FrameLoader::reportLocalLoadFailed(m_frame, url.string());15         return 0;16     }17 18     if (!ownerElement->document()->contentSecurityPolicy()->allowChildFrameFromSource(url))19         return 0;20 21     bool hideReferrer = SecurityOrigin::shouldHideReferrer(url, referrer);22     RefPtr<Frame> frame = m_frame->loader()->client()->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer, allowsScrolling, marginWidth, marginHeight);23 24     if (!frame)  {25         m_frame->loader()->checkCallImplicitClose();26         return 0;27     }28     29     // All new frames will have m_isComplete set to true at this point due to synchronously loading30     // an empty document in FrameLoader::init(). But many frames will now be starting an31     // asynchronous load of url, so we set m_isComplete to false and then check if the load is32     // actually completed below. (Note that we set m_isComplete to false even for synchronous33     // loads, so that checkCompleted() below won't bail early.)34     // FIXME: Can we remove this entirely? m_isComplete normally gets set to false when a load is committed.35     frame->loader()->started();36    37     RenderObject* renderer = ownerElement->renderer();38     FrameView* view = frame->view();39     if (renderer && renderer->isWidget() && view)40         toRenderWidget(renderer)->setWidget(view);41     42     m_frame->loader()->checkCallImplicitClose();43     44     // Some loads are performed synchronously (e.g., about:blank and loads45     // cancelled by returning a null ResourceRequest from requestFromDelegate).46     // In these cases, the synchronous load would have finished47     // before we could connect the signals, so make sure to send the 
48     // completed() signal for the child by hand and mark the load as being49     // complete.50     // FIXME: In this case the Frame will have finished loading before 
51     // it's being added to the child list. It would be a good idea to52     // create the child first, then invoke the loader separately.53     if (frame->loader()->state() == FrameStateComplete && !frame->loader()->policyDocumentLoader())54         frame->loader()->checkCompleted();55 56     return frame.get();57 }

复制代码

25,FrameLoaderClientQt::createFrame(const KURL& url, const String& name, HTMLFrameOwnerElement* ownerElement, const String& referrer, bool allowsScrolling, int marginWidth, int marginHeight)

复制代码

 1 PassRefPtr<Frame> FrameLoaderClientQt::createFrame(const KURL& url, const String& name, HTMLFrameOwnerElement* ownerElement, 2                                         const String& referrer, bool allowsScrolling, int marginWidth, int marginHeight) 3 { 4     if (!m_webFrame) 5         return 0; 6  7     QWebFrameData frameData(m_frame->page(), m_frame, ownerElement, name); 8  9     if (url.isEmpty())10         frameData.url = blankURL();11     else12         frameData.url = url;13 14     frameData.referrer = referrer;15     frameData.allowsScrolling = allowsScrolling;16     frameData.marginWidth = marginWidth;17     frameData.marginHeight = marginHeight;18 19     QPointer<QWebFrame> webFrame = new QWebFrame(m_webFrame, &frameData);20     // The creation of the frame may have run arbitrary JavaScript that removed it from the page already.21     if (!webFrame->d->frame->page()) {22         frameData.frame.release();23         ASSERT(webFrame.isNull());24         return 0;25     }26 27     emit m_webFrame->page()->frameCreated(webFrame);28 29     // FIXME: Set override encoding if we have one.30 31     m_frame->loader()->loadURLIntoChildFrame(frameData.url, frameData.referrer, frameData.frame.get());32 33     // The frame's onload handler may have removed it from the document.34     if (!frameData.frame->tree()->parent())35         return 0;36 37     return frameData.frame.release();38 }

复制代码

26,QWebFrameData::QWebFrameData(... )

复制代码

 1 QWebFrameData::QWebFrameData(WebCore::Page* parentPage, WebCore::Frame* parentFrame, 2                              WebCore::HTMLFrameOwnerElement* ownerFrameElement, 3                              const WTF::String& frameName) 4     : name(frameName) 5     , ownerElement(ownerFrameElement) 6     , page(parentPage) 7     , allowsScrolling(true) 8     , marginWidth(0) 9     , marginHeight(0)10 {11     frameLoaderClient = new FrameLoaderClientQt();12     frame = Frame::create(page, ownerElement, frameLoaderClient);13 14     // FIXME: All of the below should probably be moved over into WebCore15     frame->tree()->setName(name);16     if (parentFrame)17         parentFrame->tree()->appendChild(frame);18 }

复制代码

OK,代码量太庞大了,至此,代码跟踪完成!!!

3.2  createView

1         void createView(const IntSize&, const Color&, bool, const IntSize&, bool,2             ScrollbarMode = ScrollbarAuto, bool horizontalLock = false,3             ScrollbarMode = ScrollbarAuto, bool verticalLock = false);

描述:
      创建出FrameView对象,以用于之后的排版。应用调用这个函数的时候需要传入同排版有关的一些信息,如初始 视窗大小、背景色、滚动条模式等。创建出FrameView以后,即调用Frame::setView设置成当前的FrameView。

实现:

复制代码

 1 void Frame::setView(PassRefPtr<FrameView> view) 2 { 3     // We the custom scroll bars as early as possible to prevent m_doc->detach() 4     // from messing with the view such that its scroll bars won't be torn down. 5     // FIXME: We should revisit this. 6     if (m_view) 7         m_view->detachCustomScrollbars(); 8  9     // Detach the document now, so any onUnload handlers get run - if10     // we wait until the view is destroyed, then things won't be11     // hooked up enough for some JavaScript calls to work.12     if (!view && m_doc && m_doc->attached() && !m_doc->inPageCache()) {13         // FIXME: We don't call willRemove here. Why is that OK?14         m_doc->detach();15     }16     17     if (m_view)18         m_view->unscheduleRelayout();19     20     eventHandler()->clear();21 22     m_view = view;23 24     // Only one form submission is allowed per view of a part.25     // Since this part may be getting reused as a result of being26     // pulled from the back/forward cache, reset this flag.27     loader()->resetMultipleFormSubmissionProtection();28     29 #if ENABLE(TILED_BACKING_STORE)30     if (m_view && tiledBackingStore())31         m_view->setPaintsEntireContents(true);32 #endif33 }

复制代码

函数调用系列:

1 FrameLoader::commitProvisionalLoad2 FrameLoader::transitionToCommitted3 FrameLoaderClientQt::transitionToCommittedForNewPage4 Frame::createView

跟踪一下代码(同上)
1,QWebView::load(const QUrl &url)

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

2,QWebFrame::load(const QNetworkRequest &req,QNetworkAccessManager::Operation operation,const QByteArray &body)

复制代码

 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::DeleteOperation:26             request.setHTTPMethod("DELETE");27             break;28         case QNetworkAccessManager::CustomOperation:29             request.setHTTPMethod(req.attribute(QNetworkRequest::CustomVerbAttribute).toByteArray().constData());30             break;31         case QNetworkAccessManager::UnknownOperation:32             // eh?33             break;34     }35 36     QVariant cacheLoad = req.attribute(QNetworkRequest::CacheLoadControlAttribute);37     if (cacheLoad.isValid()) {38         bool ok;39         uint cacheLoadValue = cacheLoad.toUInt(&ok);40         if (ok)41             request.setCachePolicy(cacheLoadControlToCachePolicy(cacheLoadValue));42     }43 44     QList<QByteArray> httpHeaders = req.rawHeaderList();45     for (int i = 0; i < httpHeaders.size(); ++i) {46         const QByteArray &headerName = httpHeaders.at(i);47         request.addHTTPHeaderField(QString::fromLatin1(headerName), QString::fromLatin1(req.rawHeader(headerName)));48     }49 50     if (!body.isEmpty())51         request.setHTTPBody(WebCore::FormData::create(body.constData(), body.size()));52 53     d->frame->loader()->load(request, false);54 55     if (d->parentFrame())56         d->page->d->insideOpenCall = false;57 }

复制代码

3, FrameLoader::load(const ResourceRequest& request, bool lockHistory)

1 void FrameLoader::load(const ResourceRequest& request, bool lockHistory)2 {3     load(request, SubstituteData(), lockHistory);4 }

4,FrameLoader::load(const ResourceRequest& request, const SubstituteData& substituteData, bool lockHistory)

复制代码

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

复制代码

5,FrameLoader::load(DocumentLoader* newDocumentLoader)

复制代码

 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     } else11         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're17     // 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         // shouldReloadToHandleUnreachableURL() returns true only when the original load type is back-forward.25         // In this case we should save the document state now. Otherwise the state can be lost because load type is26         // changed and updateForBackForwardNavigation() will not be called when loading is committed.27         history()->saveDocumentAndScrollState();28 29         ASSERT(type == FrameLoadTypeStandard);30         type = FrameLoadTypeReload;31     }32 33     loadWithDocumentLoader(newDocumentLoader, type, 0);34 }

复制代码

6,FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState)

复制代码

 1 void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState) 2 { 3     // Retain because dispatchBeforeLoadEvent may release the last reference to it. 4     RefPtr<Frame> protect(m_frame); 5  6     ASSERT(m_client->hasWebView()); 7  8     // Unfortunately the view must be non-nil, this is ultimately due 9     // to parser requiring a FrameView.  We should fix this dependency.10 11     ASSERT(m_frame->view());12 13     if (m_pageDismissalEventBeingDispatched)14         return;15 16     if (m_frame->document())17         m_previousUrl = m_frame->document()->url();18 19     policyChecker()->setLoadType(type);20     RefPtr<FormState> formState = prpFormState;21     bool isFormSubmission = formState;22 23     const KURL& newURL = loader->request().url();24     const String& httpMethod = loader->request().httpMethod();25 26     if (shouldScrollToAnchor(isFormSubmission,  httpMethod, policyChecker()->loadType(), newURL)) {27         RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader;28         NavigationAction action(newURL, policyChecker()->loadType(), isFormSubmission);29 30         oldDocumentLoader->setTriggeringAction(action);31         policyChecker()->stopCheck();32         policyChecker()->checkNavigationPolicy(loader->request(), oldDocumentLoader.get(), formState,33             callContinueFragmentScrollAfterNavigationPolicy, this);34     } else {35         if (Frame* parent = m_frame->tree()->parent())36             loader->setOverrideEncoding(parent->loader()->documentLoader()->overrideEncoding());37 38         policyChecker()->stopCheck();39         setPolicyDocumentLoader(loader);40         if (loader->triggeringAction().isEmpty())41             loader->setTriggeringAction(NavigationAction(newURL, policyChecker()->loadType(), isFormSubmission));42 43         if (Element* ownerElement = m_frame->ownerElement()) {44             // We skip dispatching the beforeload event if we've already45             // committed a real document load because the event would leak46             // subsequent activity by the frame which the parent frame isn't47             // supposed to learn. For example, if the child frame navigated to48             // a new URL, the parent frame shouldn't learn the URL.49             if (!m_stateMachine.committedFirstRealDocumentLoad()50                 && !ownerElement->dispatchBeforeLoadEvent(loader->request().url().string())) {51                 continueLoadAfterNavigationPolicy(loader->request(), formState, false);52                 return;53             }54         }55 56         policyChecker()->checkNavigationPolicy(loader->request(), loader, formState,57             callContinueLoadAfterNavigationPolicy, this);58     }59 }

复制代码

7,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 }

8,FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState> formState, bool shouldContinue)

复制代码

 1 void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState> formState, bool shouldContinue) 2 { 3     // If we loaded an alternate page to replace an unreachableURL, we'll get in here with a 4     // nil policyDataSource because loading the alternate page will have passed 5     // through this method already, nested; otherwise, policyDataSource should still be set. 6     ASSERT(m_policyDocumentLoader || !m_provisionalDocumentLoader->unreachableURL().isEmpty()); 7  8     bool isTargetItem = history()->provisionalItem() ? history()->provisionalItem()->isTargetItem() : false; 9 10     // Two reasons we can't continue:11     //    1) Navigation policy delegate said we can't so request is nil. A primary case of this 
12     //       is the user responding Cancel to the form repost nag sheet.13     //    2) User responded Cancel to an alert popped up by the before unload event handler.14     bool canContinue = shouldContinue && shouldClose();15 16     if (!canContinue) {17         // If we were waiting for a quick redirect, but the policy delegate decided to ignore it, then we 
18         // need to report that the client redirect was cancelled.19         if (m_quickRedirectComing)20             clientRedirectCancelledOrFinished(false);21 22         setPolicyDocumentLoader(0);23 24         // If the navigation request came from the back/forward menu, and we punt on it, we have the 
25         // problem that we have optimistically moved the b/f cursor already, so move it back.  For sanity, 
26         // we only do this when punting a navigation for the target frame or top-level frame.  27         if ((isTargetItem || isLoadingMainFrame()) && isBackForwardLoadType(policyChecker()->loadType())) {28             if (Page* page = m_frame->page()) {29                 Frame* mainFrame = page->mainFrame();30                 if (HistoryItem* resetItem = mainFrame->loader()->history()->currentItem()) {31                     page->backForward()->setCurrentItem(resetItem);32                     m_frame->loader()->client()->updateGlobalHistoryItemForPage();33                 }34             }35         }36         return;37     }38 39     FrameLoadType type = policyChecker()->loadType();40     // A new navigation is in progress, so don't clear the history's provisional item.41     stopAllLoaders(ShouldNotClearProvisionalItem);42     43     // <rdar://problem/6250856> - In certain circumstances on pages with multiple frames, stopAllLoaders()44     // might detach the current FrameLoader, in which case we should bail on this newly defunct load. 45     if (!m_frame->page())46         return;47 48 #if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC) && ENABLE(INSPECTOR)49     if (Page* page = m_frame->page()) {50         if (page->mainFrame() == m_frame)51             m_frame->page()->inspectorController()->resume();52     }53 #endif54 55     setProvisionalDocumentLoader(m_policyDocumentLoader.get());56     m_loadType = type;57     setState(FrameStateProvisional);58 59     setPolicyDocumentLoader(0);60 61     if (isBackForwardLoadType(type) && history()->provisionalItem()->isInPageCache()) {62         loadProvisionalItemFromCachedPage();63         return;64     }65 66     if (formState)67         m_client->dispatchWillSubmitForm(&PolicyChecker::continueLoadAfterWillSubmitForm, formState);68     else69         continueLoadAfterWillSubmitForm();70 }

复制代码

9,FrameLoader::loadProvisionalItemFromCachedPage()

复制代码

 1 void FrameLoader::loadProvisionalItemFromCachedPage() 2 { 3     DocumentLoader* provisionalLoader = provisionalDocumentLoader(); 4     LOG(PageCache, "WebCorePageCache: Loading provisional DocumentLoader %p with URL '%s' from CachedPage", provisionalDocumentLoader(), provisionalDocumentLoader()->url().string().utf8().data()); 5  6     provisionalLoader->prepareForLoadStart(); 7  8     m_loadingFromCachedPage = true; 9     10     // Should have timing data from previous time(s) the page was shown.11     ASSERT(provisionalLoader->timing()->navigationStart);12     provisionalLoader->resetTiming();13     provisionalLoader->timing()->navigationStart = currentTime();    
14 15     provisionalLoader->setCommitted(true);16     commitProvisionalLoad();17 }

复制代码

10,FrameLoader::commitProvisionalLoad()

复制代码

 1 void FrameLoader::commitProvisionalLoad() 2 { 3     RefPtr<CachedPage> cachedPage = m_loadingFromCachedPage ? pageCache()->get(history()->provisionalItem()) : 0; 4     RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader; 5  6     LOG(PageCache, "WebCoreLoading %s: About to commit provisional load from previous URL '%s' to new URL '%s'", m_frame->tree()->uniqueName().string().utf8().data(), 7         m_frame->document() ? m_frame->document()->url().string().utf8().data() : "", 
 8         pdl ? pdl->url().string().utf8().data() : "<no provisional DocumentLoader>"); 9 10     // Check to see if we need to cache the page we are navigating away from into the back/forward cache.11     // We are doing this here because we know for sure that a new page is about to be loaded.12     HistoryItem* item = history()->currentItem();13     if (!m_frame->tree()->parent() && PageCache::canCache(m_frame->page()) && !item->isInPageCache())14         pageCache()->add(item, m_frame->page());15 16     if (m_loadType != FrameLoadTypeReplace)17         closeOldDataSources();18 19     if (!cachedPage && !m_stateMachine.creatingInitialEmptyDocument())20         m_client->makeRepresentation(pdl.get());21 22     transitionToCommitted(cachedPage);23 24     if (pdl) {25         // Check if the destination page is allowed to access the previous page's timing information.26         RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(pdl->request().url());27         m_documentLoader->timing()->hasSameOriginAsPreviousDocument = securityOrigin->canRequest(m_previousUrl);28     }29 30     // Call clientRedirectCancelledOrFinished() here so that the frame load delegate is notified that the redirect's31     // status has changed, if there was a redirect.  The frame load delegate may have saved some state about32     // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:.  Since we are33     // just about to commit a new page, there cannot possibly be a pending redirect at this point.34     if (m_sentRedirectNotification)35         clientRedirectCancelledOrFinished(false);36     37     if (cachedPage && cachedPage->document()) {38         prepareForCachedPageRestore();39         cachedPage->restore(m_frame->page());40 41         dispatchDidCommitLoad();42 43         // If we have a title let the WebView know about it. 44         StringWithDirection title = m_documentLoader->title();45         if (!title.isNull())46             m_client->dispatchDidReceiveTitle(title);47 48         checkCompleted();49     } else {        
50         KURL url = pdl->substituteData().responseURL();51         if (url.isEmpty())52             url = pdl->url();53         if (url.isEmpty())54             url = pdl->responseURL();55         if (url.isEmpty())56             url = blankURL();57 58         didOpenURL(url);59     }60 61     LOG(Loading, "WebCoreLoading %s: Finished committing provisional load to URL %s", m_frame->tree()->uniqueName().string().utf8().data(),62         m_frame->document() ? m_frame->document()->url().string().utf8().data() : "");63 64     if (m_loadType == FrameLoadTypeStandard && m_documentLoader->isClientRedirect())65         history()->updateForClientRedirect();66 67     if (m_loadingFromCachedPage) {68         m_frame->document()->documentDidBecomeActive();69         70         // Force a layout to update view size and thereby update scrollbars.71         m_frame->view()->forceLayout();72 73         const ResponseVector& responses = m_documentLoader->responses();74         size_t count = responses.size();75         for (size_t i = 0; i < count; i++) {76             const ResourceResponse& response = responses[i];77             // FIXME: If the WebKit client changes or cancels the request, this is not respected.78             ResourceError error;79             unsigned long identifier;80             ResourceRequest request(response.url());81             requestFromDelegate(request, identifier, error);82             // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.83             // However, with today's computers and networking speeds, this won't happen in practice.84             // Could be an issue with a giant local file.85             notifier()->sendRemainingDelegateMessages(m_documentLoader.get(), identifier, response, static_cast<int>(response.expectedContentLength()), 0, error);86         }87         88         pageCache()->remove(history()->currentItem());89 90         m_documentLoader->setPrimaryLoadComplete(true);91 92         // FIXME: Why only this frame and not parent frames?93         checkLoadCompleteForThisFrame();94     }95 }

复制代码

11,FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage)

复制代码

  1 void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage)  2 {  3     ASSERT(m_client->hasWebView());  4     ASSERT(m_state == FrameStateProvisional);  5   6     if (m_state != FrameStateProvisional)  7         return;  8   9     if (m_frame->view()) 10         m_frame->view()->scrollAnimator()->cancelAnimations(); 11  12     m_client->setCopiesOnScroll(); 13     history()->updateForCommit(); 14  15     // The call to closeURL() invokes the unload event handler, which can execute arbitrary 16     // JavaScript. If the script initiates a new load, we need to abandon the current load, 17     // or the two will stomp each other. 18     DocumentLoader* pdl = m_provisionalDocumentLoader.get(); 19     if (m_documentLoader) 20         closeURL(); 21     if (pdl != m_provisionalDocumentLoader) 22         return; 23  24     // Nothing else can interupt this commit - set the Provisional->Committed transition in stone 25     if (m_documentLoader) 26         m_documentLoader->stopLoadingSubresources(); 27     if (m_documentLoader) 28         m_documentLoader->stopLoadingPlugIns(); 29  30     setDocumentLoader(m_provisionalDocumentLoader.get()); 31     setProvisionalDocumentLoader(0); 32     setState(FrameStateCommittedPage); 33  34     // Handle adding the URL to the back/forward list. 35     DocumentLoader* dl = m_documentLoader.get(); 36  37     switch (m_loadType) { 38         case FrameLoadTypeForward: 39         case FrameLoadTypeBack: 40         case FrameLoadTypeIndexedBackForward: 41             if (m_frame->page()) { 42                 // If the first load within a frame is a navigation within a back/forward list that was attached 43                 // without any of the items being loaded then we need to update the history in a similar manner as 44                 // for a standard load with the exception of updating the back/forward list (<rdar://problem/8091103>). 45                 if (!m_stateMachine.committedFirstRealDocumentLoad()) 46                     history()->updateForStandardLoad(HistoryController::UpdateAllExceptBackForwardList); 47  48                 history()->updateForBackForwardNavigation(); 49  50                 // For cached pages, CachedFrame::restore will take care of firing the popstate event with the history item's state object 51                 if (history()->currentItem() && !cachedPage) 52                     m_pendingStateObject = history()->currentItem()->stateObject(); 53  54                 // Create a document view for this document, or used the cached view. 55                 if (cachedPage) { 56                     DocumentLoader* cachedDocumentLoader = cachedPage->documentLoader(); 57                     ASSERT(cachedDocumentLoader); 58                     cachedDocumentLoader->setFrame(m_frame); 59                     m_client->transitionToCommittedFromCachedFrame(cachedPage->cachedMainFrame()); 60  61                 } else 62                     m_client->transitionToCommittedForNewPage(); 63             } 64             break; 65  66         case FrameLoadTypeReload: 67         case FrameLoadTypeReloadFromOrigin: 68         case FrameLoadTypeSame: 69         case FrameLoadTypeReplace: 70             history()->updateForReload(); 71             m_client->transitionToCommittedForNewPage(); 72             break; 73  74         case FrameLoadTypeStandard: 75             history()->updateForStandardLoad(); 76             if (m_frame->view()) 77                 m_frame->view()->setScrollbarsSuppressed(true); 78             m_client->transitionToCommittedForNewPage(); 79             break; 80  81         case FrameLoadTypeRedirectWithLockedBackForwardList: 82             history()->updateForRedirectWithLockedBackForwardList(); 83             m_client->transitionToCommittedForNewPage(); 84             break; 85  86         // FIXME Remove this check when dummy ds is removed (whatever "dummy ds" is). 87         // An exception should be thrown if we're in the FrameLoadTypeUninitialized state. 88         default: 89             ASSERT_NOT_REACHED(); 90     } 91  92     m_documentLoader->writer()->setMIMEType(dl->responseMIMEType()); 93  94     // Tell the client we've committed this URL. 95     ASSERT(m_frame->view()); 96  97     if (m_stateMachine.creatingInitialEmptyDocument()) 98         return; 99 100     if (!m_stateMachine.committedFirstRealDocumentLoad())101         m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocumentPostCommit);102 103     if (!m_client->hasHTMLView())104         receivedFirstData();105 }

复制代码

12,FrameLoaderClientQt::transitionToCommittedForNewPage()

复制代码

 1 void FrameLoaderClientQt::transitionToCommittedForNewPage() 2 { 3     ASSERT(m_frame); 4     ASSERT(m_webFrame); 5  6     QBrush brush = m_webFrame->page()->palette().brush(QPalette::Base); 7     QColor backgroundColor = brush.style() == Qt::SolidPattern ? brush.color() : QColor(); 8  9     QWebPage* page = m_webFrame->page();10     const QSize preferredLayoutSize = page->preferredContentsSize();11 12     ScrollbarMode hScrollbar = (ScrollbarMode) m_webFrame->scrollBarPolicy(Qt::Horizontal);13     ScrollbarMode vScrollbar = (ScrollbarMode) m_webFrame->scrollBarPolicy(Qt::Vertical);14     bool hLock = hScrollbar != ScrollbarAuto;15     bool vLock = vScrollbar != ScrollbarAuto;16 17     IntSize currentVisibleContentSize = m_frame->view() ? m_frame->view()->actualVisibleContentRect().size() : IntSize();18 19     m_frame->createView(m_webFrame->page()->viewportSize(),
20                         backgroundColor, !backgroundColor.alpha(),
21                         preferredLayoutSize.isValid() ? IntSize(preferredLayoutSize) : IntSize(),
22                         preferredLayoutSize.isValid(),
23                         hScrollbar, hLock,
24                         vScrollbar, vLock);25 26     bool isMainFrame = m_frame == m_frame->page()->mainFrame();27     if (isMainFrame && page->d->client) {28         m_frame->view()->setPaintsEntireContents(page->d->client->viewResizesToContentsEnabled());29         m_frame->view()->setDelegatesScrolling(page->d->client->viewResizesToContentsEnabled());30     }31 32     // The HistoryController will update the scroll position later if needed.33     m_frame->view()->setActualVisibleContentRect(IntRect(IntPoint::zero(), currentVisibleContentSize));34 }

复制代码

13,Frame::createView( ... )

14,Frame::setView(PassRefPtr<FrameView> view)

复制代码

 1 void Frame::setView(PassRefPtr<FrameView> view) 2 { 3     // We the custom scroll bars as early as possible to prevent m_doc->detach() 4     // from messing with the view such that its scroll bars won't be torn down. 5     // FIXME: We should revisit this. 6     if (m_view) 7         m_view->detachCustomScrollbars(); 8  9     // Detach the document now, so any onUnload handlers get run - if10     // we wait until the view is destroyed, then things won't be11     // hooked up enough for some JavaScript calls to work.12     if (!view && m_doc && m_doc->attached() && !m_doc->inPageCache()) {13         // FIXME: We don't call willRemove here. Why is that OK?14         m_doc->detach();15     }16     17     if (m_view)18         m_view->unscheduleRelayout();19     20     eventHandler()->clear();21 22     m_view = view;23 24     // Only one form submission is allowed per view of a part.25     // Since this part may be getting reused as a result of being26     // pulled from the back/forward cache, reset this flag.27     loader()->resetMultipleFormSubmissionProtection();28     29 #if ENABLE(TILED_BACKING_STORE)30     if (m_view && tiledBackingStore())31         m_view->setPaintsEntireContents(true);32 #endif33 }

复制代码

OK!!! CreateView代码跟踪到此为止!!

3.3 setDocument

1 void setDocument(PassRefPtr<Document>);

描述:

    置同Frame关联的Document对象(一般是DocumentWriter创建的)。还是两种情况

实现:

复制代码

 1 void Frame::setDocument(PassRefPtr<Document> newDoc) 2 { 3     ASSERT(!newDoc || newDoc->frame()); 4     if (m_doc && m_doc->attached() && !m_doc->inPageCache()) { 5         // FIXME: We don't call willRemove here. Why is that OK? 6         m_doc->detach(); 7     } 8  9     m_doc = newDoc;10     selection()->updateSecureKeyboardEntryIfActive();11 12     if (m_doc && !m_doc->attached())13         m_doc->attach();14 15     // Update the cached 'document' property, which is now stale.16     m_script.updateDocument();17 18     if (m_page)19         m_page->updateViewportArguments();20 }

复制代码

函数调用系列:

复制代码

 1 QWebFrame::QwebFrame 2 QwebFramePrivate::init 3 Frame::init 4 FrameLoader::init 5 DocumentWriter::begin 6 Frame::setDocument 7  8  9 DocumentLoader::receivedData10 DocumentLoader::commitLoad11 FrameLoaderClientQt::committedLoad12 DocumentLoader::commitData13 DocumentWriter::setEncoding14 DocumentWriter::willSetEncoding15 FrameLoader::receivedFirstData16 DocumentWriter::begin17 FrameLoader::clear18 Frame::setDocument

复制代码

代码跟踪(分两种)
情况一:

1,QWebFrame::QWebFrame(QWebFrame *parent, QWebFrameData *frameData)

复制代码

 1 QWebFrame::QWebFrame(QWebFrame *parent, QWebFrameData *frameData) 2     : QObject(parent) 3     , d(new QWebFramePrivate) 4 { 5     d->page = parent->d->page; 6     d->init(this, frameData); 7 #if ENABLE(ORIENTATION_EVENTS) && ENABLE(DEVICE_ORIENTATION) 8     connect(&d->m_orientation, SIGNAL(readingChanged()), this, SLOT(_q_orientationChanged())); 9     d->m_orientation.start();10 #endif11 }

复制代码

2,QWebFramePrivate::init(QWebFrame *qframe, QWebFrameData *frameData)

复制代码

 1 void QWebFramePrivate::init(QWebFrame *qframe, QWebFrameData *frameData) 2 { 3     q = qframe; 4  5     allowsScrolling = frameData->allowsScrolling; 6     marginWidth = frameData->marginWidth; 7     marginHeight = frameData->marginHeight; 8     frame = frameData->frame.get(); 9     frameLoaderClient = frameData->frameLoaderClient;10     frameLoaderClient->setFrame(qframe, frame);11 12     frame->init();13 }

复制代码

3,Frame::init()

1  inline void Frame::init()2 {3     m_loader.init();4 }

4,FrameLoader::init()

复制代码

 1 void FrameLoader::init() 2 { 3     // Propagate sandbox attributes to this Frameloader and its descendants. 4     // This needs to be done early, so that an initial document gets correct sandbox flags in its SecurityOrigin. 5     updateSandboxFlags(); 6  7     // this somewhat odd set of steps is needed to give the frame an initial empty document 8     m_stateMachine.advanceTo(FrameLoaderStateMachine::CreatingInitialEmptyDocument); 9     setPolicyDocumentLoader(m_client->createDocumentLoader(ResourceRequest(KURL(ParsedURLString, "")), SubstituteData()).get());10     setProvisionalDocumentLoader(m_policyDocumentLoader.get());11     setState(FrameStateProvisional);12     m_provisionalDocumentLoader->setResponse(ResourceResponse(KURL(), "text/html", 0, String(), String()));13     m_provisionalDocumentLoader->finishedLoading();14     m_documentLoader->writer()->begin(KURL(), false);15     m_documentLoader->writer()->end();16     m_frame->document()->cancelParsing();17     m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocument);18     m_didCallImplicitClose = true;19 20     m_networkingContext = m_client->createNetworkingContext();21 }

复制代码

5,DocumentWriter::begin(const KURL& urlReference, bool dispatch, SecurityOrigin* origin)

复制代码

 1 void DocumentWriter::begin(const KURL& urlReference, 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     // We grab a local copy of the URL because it's easy for callers to supply 8     // a URL that will be deallocated during the execution of this function. 9     // For example, see <https://bugs./show_bug.cgi?id=66360>.10     KURL url = urlReference;11 12     // Create a new document before clearing the frame, because it may need to13     // inherit an aliased security context.14     RefPtr<Document> document = createDocument(url);15     16     // If the new document is for a Plugin but we're supposed to be sandboxed from Plugins,17     // then replace the document with one whose parser will ignore the incoming data (bug 39323)18     if (document->isPluginDocument() && m_frame->loader()->isSandboxed(SandboxPlugins))19         document = SinkDocument::create(m_frame, url);20 21     // FIXME: Do we need to consult the content security policy here about blocked plug-ins?22 23     bool resetScripting = !(m_frame->loader()->stateMachine()->isDisplayingInitialEmptyDocument() && m_frame->document()->securityOrigin()->isSecureTransitionTo(url));24     m_frame->loader()->clear(resetScripting, resetScripting);25     clear();26     if (resetScripting)27         m_frame->script()->updatePlatformScriptObjects();28 29     m_frame->loader()->setOutgoingReferrer(url);30     m_frame->setDocument(document);31 32     if (m_decoder)33         document->setDecoder(m_decoder.get());34     if (forcedSecurityOrigin)35         document->setSecurityOrigin(forcedSecurityOrigin.get());36 37     m_frame->domWindow()->setURL(document->url());38     m_frame->domWindow()->setSecurityOrigin(document->securityOrigin());39 40     m_frame->loader()->didBeginDocument(dispatch);41 42     document->implicitOpen();43 44     if (m_frame->view() && m_frame->loader()->client()->hasHTMLView())45         m_frame->view()->setContentsSize(IntSize());46 }

复制代码

6,Frame::setDocument(PassRefPtr<Document> newDoc)

情况二:

1,MainResourceLoader::didReceiveData(const char* data, int length, long long encodedDataLength, bool allAtOnce)

复制代码

 1 void MainResourceLoader::didReceiveData(const char* data, int length, long long encodedDataLength, bool allAtOnce) 2 { 3     ASSERT(data); 4     ASSERT(length != 0); 5  6     ASSERT(!m_response.isNull()); 7  8 #if USE(CFNETWORK) || PLATFORM(MAC) 9     // Workaround for <rdar://problem/6060782>10     if (m_response.isNull()) {11         m_response = ResourceResponse(KURL(), "text/html", 0, String(), String());12         if (DocumentLoader* documentLoader = frameLoader()->activeDocumentLoader())13             documentLoader->setResponse(m_response);14     }15 #endif16 17     // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.18     // See <rdar://problem/6304600> for more details.19 #if !USE(CF)20     ASSERT(!defersLoading());21 #endif22  23  #if ENABLE(OFFLINE_WEB_APPLICATIONS)24     documentLoader()->applicationCacheHost()->mainResourceDataReceived(data, length, encodedDataLength, allAtOnce);25 #endif26 27     // The additional processing can do anything including possibly removing the last28     // reference to this object; one example of this is 3266216.29     RefPtr<MainResourceLoader> protect(this);30 31     m_timeOfLastDataReceived = currentTime();32 33     ResourceLoader::didReceiveData(data, length, encodedDataLength, allAtOnce);34 }

复制代码

2,ResourceLoader::didReceiveData(const char* data, int length, long long encodedDataLength, bool allAtOnce)

复制代码

 1 void ResourceLoader::didReceiveData(const char* data, int length, long long encodedDataLength, bool allAtOnce) 2 { 3     // The following assertions are not quite valid here, since a subclass 4     // might override didReceiveData in a way that invalidates them. This 5     // happens with the steps listed in 3266216 6     // ASSERT(con == connection); 7     // ASSERT(!m_reachedTerminalState); 8  9     // Protect this in this delegate method since the additional processing can do10     // anything including possibly derefing this; one example of this is Radar 3266216.11     RefPtr<ResourceLoader> protector(this);12 13     addData(data, length, allAtOnce);14     // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.15     // However, with today's computers and networking speeds, this won't happen in practice.16     // Could be an issue with a giant local file.17     if (m_sendResourceLoadCallbacks && m_frame)18         frameLoader()->notifier()->didReceiveData(this, data, length, static_cast<int>(encodedDataLength));19 }

复制代码

3,MainResourceLoader::addData(const char* data, int length, bool allAtOnce)

1 void MainResourceLoader::addData(const char* data, int length, bool allAtOnce)2 {3     ResourceLoader::addData(data, length, allAtOnce);4     documentLoader()->receivedData(data, length);5 }

4,DocumentLoader::receivedData(const char* data, int length)

1 void DocumentLoader::receivedData(const char* data, int length)2 {    
3     m_gotFirstByte = true;4     if (doesProgressiveLoad(m_response.mimeType()))5         commitLoad(data, length);6 }

5,DocumentLoader::commitLoad(const char* data, int length)

复制代码

 1 void DocumentLoader::commitLoad(const char* data, int length) 2 { 3     // Both unloading the old page and parsing the new page may execute JavaScript which destroys the datasource 4     // by starting a new load, so retain temporarily. 5     RefPtr<Frame> protectFrame(m_frame); 6     RefPtr<DocumentLoader> protectLoader(this); 7  8     commitIfReady(); 9     FrameLoader* frameLoader = DocumentLoader::frameLoader();10     if (!frameLoader)11         return;12 #if ENABLE(WEB_ARCHIVE)13     if (ArchiveFactory::isArchiveMimeType(response().mimeType()))14         return;15 #endif16     frameLoader->client()->committedLoad(this, data, length);17 }

复制代码

6,FrameLoaderClientQt::committedLoad(WebCore::DocumentLoader* loader, const char* data, int length)

复制代码

 1 void FrameLoaderClientQt::committedLoad(WebCore::DocumentLoader* loader, const char* data, int length) 2 { 3     if (!m_pluginView) 4         loader->commitData(data, length); 5      6     // We re-check here as the plugin can have been created. 7     if (m_pluginView && m_pluginView->isPluginView()) { 8         if (!m_hasSentResponseToPlugin) { 9             m_pluginView->didReceiveResponse(loader->response());10             // The function didReceiveResponse sets up a new stream to the plug-in.11             // On a full-page plug-in, a failure in setting up this stream can cause the12             // main document load to be cancelled, setting m_pluginView to null.13             if (!m_pluginView)14                 return;15             m_hasSentResponseToPlugin = true;16         }17         m_pluginView->didReceiveData(data, length);18     }19 }

复制代码

7,DocumentLoader::commitData(const char* bytes, int length)

复制代码

 1 void DocumentLoader::commitData(const char* bytes, int length) 2 { 3     // Set the text encoding.  This is safe to call multiple times. 4     bool userChosen = true; 5     String encoding = overrideEncoding(); 6     if (encoding.isNull()) { 7         userChosen = false; 8         encoding = response().textEncodingName(); 9     }10     m_writer.setEncoding(encoding, userChosen);11     ASSERT(m_frame->document()->parsing());12     m_writer.addData(bytes, length);13 }

复制代码

8,DocumentWriter::setEncoding(const String& name, bool userChosen)

1 void DocumentWriter::setEncoding(const String& name, bool userChosen)2 {3     m_frame->loader()->willSetEncoding();4     m_encoding = name;5     m_encodingWasChosenByUser = userChosen;6 }

9,FrameLoader::willSetEncoding()

1 void FrameLoader::willSetEncoding()2 {3     if (!m_workingURL.isEmpty())4         receivedFirstData();5 }

10,FrameLoader::receivedFirstData()

复制代码

 1 void FrameLoader::receivedFirstData() 2 { 3     activeDocumentLoader()->writer()->begin(m_workingURL, false); 4     activeDocumentLoader()->writer()->setDocumentWasLoadedAsPartOfNavigation(); 5  6     dispatchDidCommitLoad(); 7     dispatchDidClearWindowObjectsInAllWorlds(); 8      9     if (m_documentLoader) {10         StringWithDirection ptitle = m_documentLoader->title();11         // If we have a title let the WebView know about it.12         if (!ptitle.isNull())13             m_client->dispatchDidReceiveTitle(ptitle);14     }15 16     m_workingURL = KURL();17 18     double delay;19     String url;20     if (!m_documentLoader)21         return;22     if (m_frame->inViewSourceMode())23         return;24     if (!parseHTTPRefresh(m_documentLoader->response().httpHeaderField("Refresh"), false, delay, url))25         return;26 27     if (url.isEmpty())28         url = m_frame->document()->url().string();29     else30         url = m_frame->document()->completeURL(url).string();31 32     m_frame->navigationScheduler()->scheduleRedirect(delay, url);33 }

复制代码

11,DocumentWriter::begin(const KURL& urlReference, bool dispatch, SecurityOrigin* origin)

复制代码

 1 void DocumentWriter::begin(const KURL& urlReference, 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     // We grab a local copy of the URL because it's easy for callers to supply 8     // a URL that will be deallocated during the execution of this function. 9     // For example, see <https://bugs./show_bug.cgi?id=66360>.10     KURL url = urlReference;11 12     // Create a new document before clearing the frame, because it may need to13     // inherit an aliased security context.14     RefPtr<Document> document = createDocument(url);15     16     // If the new document is for a Plugin but we're supposed to be sandboxed from Plugins,17     // then replace the document with one whose parser will ignore the incoming data (bug 39323)18     if (document->isPluginDocument() && m_frame->loader()->isSandboxed(SandboxPlugins))19         document = SinkDocument::create(m_frame, url);20 21     // FIXME: Do we need to consult the content security policy here about blocked plug-ins?22 23     bool resetScripting = !(m_frame->loader()->stateMachine()->isDisplayingInitialEmptyDocument() && m_frame->document()->securityOrigin()->isSecureTransitionTo(url));24     m_frame->loader()->clear(resetScripting, resetScripting);25     clear();26     if (resetScripting)27         m_frame->script()->updatePlatformScriptObjects();28 29     m_frame->loader()->setOutgoingReferrer(url);30     m_frame->setDocument(document);31 32     if (m_decoder)33         document->setDecoder(m_decoder.get());34     if (forcedSecurityOrigin)35         document->setSecurityOrigin(forcedSecurityOrigin.get());36 37     m_frame->domWindow()->setURL(document->url());38     m_frame->domWindow()->setSecurityOrigin(document->securityOrigin());39 40     m_frame->loader()->didBeginDocument(dispatch);41 42     document->implicitOpen();43 44     if (m_frame->view() && m_frame->loader()->client()->hasHTMLView())45         m_frame->view()->setContentsSize(IntSize());46 }

复制代码

12,FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects, bool clearFrameView)

复制代码

 1 void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects, bool clearFrameView) 2 { 3     m_frame->editor()->clear(); 4  5     if (!m_needsClear) 6         return; 7     m_needsClear = false; 8      9     if (!m_frame->document()->inPageCache()) {10         m_frame->document()->cancelParsing();11         m_frame->document()->stopActiveDOMObjects();12         if (m_frame->document()->attached()) {13             m_frame->document()->willRemove();14             m_frame->document()->detach();15             16             m_frame->document()->removeFocusedNodeOfSubtree(m_frame->document());17         }18     }19 20     // Do this after detaching the document so that the unload event works.21     if (clearWindowProperties) {22         m_frame->clearDOMWindow();23         m_frame->script()->clearWindowShell(m_frame->document()->inPageCache());24     }25 26     m_frame->selection()->clear();27     m_frame->eventHandler()->clear();28     if (clearFrameView && m_frame->view())29         m_frame->view()->clear();30 31     // Do not drop the document before the ScriptController and view are cleared32     // as some destructors might still try to access the document.33     m_frame->setDocument(0);34 35     m_subframeLoader.clear();36 37     if (clearScriptObjects)38         m_frame->script()->clearScriptObjects();39 40     m_frame->navigationScheduler()->clear();41 42     m_checkTimer.stop();43     m_shouldCallCheckCompleted = false;44     m_shouldCallCheckLoadComplete = false;45 46     if (m_stateMachine.isDisplayingInitialEmptyDocument() && m_stateMachine.committedFirstRealDocumentLoad())47         m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad);48 }

复制代码

13,Frame::setDocument(PassRefPtr<Document> newDoc)

OK! setDocuemnt源码跟踪到此为止!

3.4 init

1 void init();

描述:

     Frame对象初始化,会调用 FrameLoader::init 初始化FrameLoader对象

实现:

1     inline void Frame::init()2     {3         m_loader.init();4     }

调用系列:

1 QWebFrame::QWebFrame2 QwebFramePrivate::init3 Frame::init

代码已经跟踪过,见上面

3.5 setPageAndTextZoomFactors

1 void setPageAndTextZoomFactors(float pageZoomFactor, float textZoomFactor);

 描述:

     设置页面放大因子和文字放大因子。在网页缩放或者改变网页字体大小的时候调用

实现:

复制代码

 1 void Frame::setPageAndTextZoomFactors(float pageZoomFactor, float textZoomFactor) 2 { 3     if (m_pageZoomFactor == pageZoomFactor && m_textZoomFactor == textZoomFactor) 4         return; 5  6     Page* page = this->page(); 7     if (!page) 8         return; 9 10     Document* document = this->document();11     if (!document)12         return;13 14     m_editor.dismissCorrectionPanelAsIgnored();15 16 #if ENABLE(SVG)17     // Respect SVGs zoomAndPan="disabled" property in standalone SVG documents.18     // FIXME: How to handle compound documents + zoomAndPan="disabled"? Needs SVG WG clarification.19     if (document->isSVGDocument()) {20         if (!static_cast<SVGDocument*>(document)->zoomAndPanEnabled())21             return;22         if (document->renderer())23             document->renderer()->setNeedsLayout(true);24     }25 #endif26 27     if (m_pageZoomFactor != pageZoomFactor) {28         if (FrameView* view = this->view()) {29             // Update the scroll position when doing a full page zoom, so the content stays in relatively the same position.30             IntPoint scrollPosition = view->scrollPosition();31             float percentDifference = (pageZoomFactor / m_pageZoomFactor);32             view->setScrollPosition(IntPoint(scrollPosition.x() * percentDifference, scrollPosition.y() * percentDifference));33         }34     }35 36     m_pageZoomFactor = pageZoomFactor;37     m_textZoomFactor = textZoomFactor;38 39     document->recalcStyle(Node::Force);40 41     for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling())42         child->setPageAndTextZoomFactors(m_pageZoomFactor, m_textZoomFactor);43 44     if (FrameView* view = this->view()) {45         if (document->renderer() && document->renderer()->needsLayout() && view->didFirstLayout())46             view->layout();47     }48 }

复制代码

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多