日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【-】WebKit Layout (布局)

發布時間:2024/4/15 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【-】WebKit Layout (布局) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?WebKit 在渲染頁面之前,需要確定各個元素的位置、大小,而這個過程就是layout(布局)。下面,我們對layout的主要過程進行一番說明。


一、FrameView::layout方法
FrameView作為與View相關的類,其主要涉及與顯示相關的內容,而其中對頁面元素的布局至關重要,這也是瀏覽器的核心處理部分。

我們都知道瀏覽器從Web服務器獲得數據后,經解析會構建DOM樹、Render樹,然后進行布局處理,進而為渲染頁面作好準備,其中的布局處理往往由FrameView::layout方法發起,讓我們來具體看看其實現,一窺其主要實現過程。


void FrameView::layout(bool allowSubtree)
{
????if (m_midLayout)
??????return;

????// Always ensure ourstyleinfo is up-to-date. This can happen in situations where the layout beats any sort ofstyle recalc update that needs to occur.

????// 進行CSSStyleSelector的更新處理,因為一旦CSS發生變化,布局的結果也可能 發生相關變化,所以在開始布局之前,需要檢查CSS是否發生變化,如果有則需要作相應調整,進而可能影響Render樹等。

????if (m_frame->needsReapplyStyles())
???????m_frame->reapplyStyles();
????else if (document->childNeedsStyleRecalc())
???????document->recalcStyle();

????bool subtree = m_layoutRoot;
????RenderObject* root = subtree ? m_layoutRoot : document->renderer();
????if (!root){
????????// FIXME: Do we need to set m_size here?
????????m_layoutSchedulingEnabled = true;
????????return;
????}

????//布局的處理可能相互嵌套,這與發起布局處理的時機相關。
????//設置滾動條相關
????if (m_canHaveScrollbars){
????????hMode = ScrollbarAuto;
????????vMode = ScrollbarAuto;
????} else {
????????hMode = ScrollbarAlwaysOff;
????????vMode = ScrollbarAlwaysOff;
????}

????if (!subtree){
????????// Now set our scrollbar statefor the layout.

????????if (body->hasTagName(framesetTag)&& !m_frame->settings()->frameFlatteningEnabled()){
????????????//frameset 而且設置了frameFlatteningEnabled,則關閉滾動條
????????????????body->renderer()->setChildNeedsLayout(true);
????????????????vMode = ScrollbarAlwaysOff;
????????????????hMode = ScrollbarAlwaysOff;
????????????} else if (body->hasTagName(bodyTag)){
????????????????// 設置滾動條
????????????????applyOverflowToViewport(o, hMode, vMode);
????????????}
????}

????//root往往為RenderView對象
????RenderLayer* layer = root->enclosingLayer();

????m_midLayout = true;
????beginDeferredRepaints();
????root->layout();
????endDeferredRepaints();
????m_midLayout = false;

????m_layoutSchedulingEnabled = true;

????if (!subtree && !static_cast(root)->printing())
????????adjustViewSize();

????// Now update the positions of all layers.
????//對當前Render樹布局完后,設置RenderLayer樹的布局信息,其中m_doFullRepaint描述是否需要發起渲染處理。

???beginDeferredRepaints();
???layer->updateLayerPositions(...);
???endDeferredRepaints();

????// 設置快速blit
????setCanBlitOnScroll(!useSlowRepaints());

????//因為在布局的過程中,可能進一步獲得網頁數據,則需要繼續布局處理。
????if (!m_postLayoutTasksTimer.isActive()){
????????// Calls resumeScheduledEvents()
????????performPostLayoutTasks();

????????if (!m_postLayoutTasksTimer.isActive()&& needsLayout()){
????????????// Post-layout widget updates or an event handler made us need layout again. Lay out again, but this timedefer widget updates and event dispatch until after we return.
????????????m_postLayoutTasksTimer.startOneShot(0);
????????????pauseScheduledEvents();
????????????layout();
????????}
????} else {
????????resumeScheduledEvents();
????}
}

?

FrameView::layout方法,簡單的說來就是發起對Render樹中的每一個節點按照從父節點到子節點的方式進行x、y、width、height計算,當每一個樹節點的位置及大小確定之后就可以進行后面的渲染。

FrameView::layout往往會調用Render樹根的layout方法即RenderView::layout。

二、RenderView::layout方法

void RenderView::layout()
{
???? if (printing())
???????? m_minPrefWidth = m_maxPrefWidth = m_width;

???? // Use calcWidth/Height to get the newwidth/height, since this will take the fullpage zoom factor into account.
???? bool relayoutChildren = !printing()&&(!m_frameView || m_width != viewWidth()|| m_height != viewHeight());
???? if (relayoutChildren) {

??????? setChildNeedsLayout(true, false);
??????? for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
??????????? if (child->style()->height().isPercent() || child->style()->minHeight().isPercent() || child->style()->maxHeight().isPercent())
??????????????? child->setChildNeedsLayout(true, false);
??????? }

???? }

???? ASSERT(!m_layoutState);
???? LayoutState state;
???? // FIXME: May be better to pusha clip and avoid issuing offscreen repaints.
???? state.m_clipped = false;
???? m_layoutState = &state;

???? if (needsLayout())
???????? RenderBlock::layout();//類繼承的好處,直接調用父類的layout

??? // Reset overflow and then replace it with docWidth and docHeight.
??? m_overflow.clear();
??? addLayoutOverflow(IntRect(0, 0, docWidth(), docHeight()));

???? ASSERT(m_layoutStateDisableCount == 0);
???? ASSERT(m_layoutState == &state);
???? m_layoutState = 0;
???? setNeedsLayout(false);
}

void RenderBlock::layout()
{
???? // Update our first letterinfo now.
???? updateFirstLetter();
???? // Table cells call layoutBlock directly, so don't add any logic here. Put code into layoutBlock().
???? layoutBlock(false);

???? // It'
s safe to check for control clip here, since controls can never betable cells. If we have a lightweight clip, there can never be any overflow from children.
??? if (hasControlClip() && m_overflow)
??????? clearLayoutOverflow();
}



三、RenderBlock::layoutBlock方法

layoutBlock 會分成兩個分支:layoutInlineChildrenlayoutBlockChildren

void RenderBlock::layoutBlock(bool relayoutChildren)
{
??? ...
??? calcWidth();//先計算寬度
??? calcColumnWidth();
???

??? m_overflow.clear();???

??? if(oldWidth != width()|| oldColumnWidth != desiredColumnWidth())
??????? relayoutChildren = true;
??? clearFloats();

??? int previousHeight = height();
??? setHeight(0);

???? //這就是在布局基本概念中提到的Block-level元素的子節點要么是Block-level元素要么為Inline-level元素。
???? if (childrenInline())
???????? layoutInlineChildren(relayoutChildren, repaintTop, repaintBottom);
???? else
???????? layoutBlockChildren(relayoutChildren, maxFloatBottom);

???? // Expand our intrinsicheight to encompass floats.
???? int toAdd = borderBottom()+ paddingBottom()+ horizontalScrollbarHeight();
???? if (floatBottom()>(m_height - toAdd)&&(isInlineBlockOrInlineTable()|| isFloatingOrPositioned()|| hasOverflowClip()||
(parent()&& parent()->isFlexibleBox()|| m_hasColumns)))
???? setHeight(floatBottom()+ toAdd);

???? // Now lay out our columns within this intrinsicheight, since they can slightly affect the intrinsicheight as we adjust for clean column breaks.
???? int singleColumnBottom = layoutColumns();

???? // Calculate our newheight.//布局完子節點后確定父節點高度
???? int oldHeight = height();
???? calcHeight();

???? if (previousHeight != height())
???????? relayoutChildren = true;


???? //另外布局屬性為Fixed和absolute的元素
???? layoutPositionedObjects(relayoutChildren || isRoot());

???? // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if we overflow or not.
??? updateScrollInfoAfterLayout();

??? // Repaint with our new bounds if they are different from our old bounds.
??? bool didFullRepaint = repainter.repaintAfterLayout();
??? if (!didFullRepaint && repaintTop != repaintBottom && (style()->visibility() == VISIBLE || enclosingLayer()->hasVisibleContent())) {
??????? // 設置repaintRect

?????? // Make sure the rect is still non-empty after intersecting for overflow above
??????? if (!repaintRect.isEmpty()) {
??????????? repaintRectangle(repaintRect); // We need to do a partial repaint of our content.
??????????? if (hasReflection())
??????????????? repaintRectangle(reflectedRect(repaintRect));
??????? }
??? }
??? setNeedsLayout(false);
}



四、RenderBlock::layoutBlockChildren方法


void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom)
{
int top = borderTop()+ paddingTop();
int bottom = borderBottom()+ paddingBottom()+ horizontalScrollbarHeight();


??? // Fieldsets need to find their legend and position it inside the border of the object.
??? // The legend then gets skipped during normal layout.
??? RenderObject* legend = layoutLegend(relayoutChildren);


//遍歷子節點

RenderBox* next = firstChildBox();
??? while (next) {
??????? RenderBox* child = next;
??????? next = child->nextSiblingBox();

??????? if (legend == child)
??????????? continue; // Skip the legend, since it has already been positioned up in the fieldset's border.
?????? // Make sure we layout children if they need it.
??????? // FIXME: Technically percentage height objects only need a relayout if their percentage isn't going to be turned into
??????? // an auto value.? Add a method to determine this, so that we can avoid the relayout.
??????? if (relayoutChildren || ((child->style()->height().isPercent() || child->style()->minHeight().isPercent() || child->style()->maxHeight().isPercent()) && !isRenderView()))
??????????? child->setChildNeedsLayout(true, false);

??????? // If relayoutChildren is set and we have percentage padding, we also need to invalidate the child's pref widths.
??????? if (relayoutChildren && (child->style()->paddingLeft().isPercent() || child->style()->paddingRight().isPercent()))
??????????? child->setPrefWidthsDirty(true, false);

??????? // Handle the four types of special elements first.? These include positioned content, floating content, compacts and
??????? // run-ins.? When we encounter these four types of objects, we don't actually lay them out as normal flow blocks.
??????? if (handleSpecialChild(child, marginInfo))
??????????? continue;

??????? // Lay out the child.
??????? layoutBlockChild(child, marginInfo, previousFloatBottom, maxFloatBottom);
??? }

??? // Now do the handling of the bottom of the block, adding in our bottom border/padding and
??? // determining the correct collapsed bottom margin information.
??? handleBottomOfBlock(top, bottom, marginInfo);
}


layoutBlockChild 方法就比較復雜了,具體可以自己看代碼。

五、RenderBlock::layoutInlineChildren方法
這個方法相當復雜,其作用就是布局文字、圖像等,對文字行高確定、斷行等處理,同時還包括 文字從左到右或從右到左的布局處理。具體可以參考bidi.cpp中的源碼實現。

六、調用FrameView::layout方法的時機
由于從Web服務器獲取的網頁數據不可能一次性完成,往往需要邊獲取數據,邊布局,然后渲染,這樣才可能獲得良好的用戶感受。

所以一旦獲得主要數據如css數據及body等標簽后,就可以開始布局,布局完后會根據當前條件決定是否將布局的數據渲染出來,或者繼續布局處理后來獲取的數據,這樣也增加了布局處理過程的復雜度。

而調用layout方法的時機也至關重要,因為layout本身就可能需要花費大量的時間如layoutBlockChildren、layoutInlineChildren等處理,其往往與網頁的內容有關,而網頁的內容卻由網頁開發者來確定,對瀏覽器來講是千變萬化的,這就對layout方法的實現及調用時機提出更高的要求,同時確定了其復雜性。

調用layout的時機主要有獲得一定DOM文檔數據后調用Document::updateLayout()、需要重新使用CSS數據時調用Document::recalcStyle()、改變窗口大小后調用Frame::forceLayout()等來實現。。。

七、總結
其實WebKit涉及網頁布局方面的layout方法蠻復雜的,如其他RenderObject子類也會根據自身情況重載實現layout,還有對float、fixed、absolute、inline元素等的處理,但其主要邏輯就象上面所提,這里只是匯總一下主要流程及概念,針對每一個具體標簽或RenderObject的布局實現則需要更深一步的了解,希望大家能對了解WebKit的網頁布局過程有一個清晰而準確的認識。。

總結

以上是生活随笔為你收集整理的【-】WebKit Layout (布局)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。