歡迎來到 黑吧安全網 聚焦網絡安全前沿資訊,精華內容,交流技術心得!

在Microsoft Edge中實現DOM樹

來源:本站整理 作者:佚名 時間:2018-12-13 TAG: 我要投稿

DOM是Web平臺編程模型的基礎,其設計和性能直接影響著瀏覽器管道(Pipeline)的模型,然而,DOM的歷史演化卻遠不是一個簡單的事情。
在過去三年中,微軟的安全專家們早已經開始在Microsoft Edge上對DOM進行了重構,這次重構的主要目標就是要搭建一個更加先進的架構,提供更好的實際操作性能和更加簡潔的操作。在這篇文章中,微軟的安全專家們將引導我們來了解Internet Explorer和Microsoft Edge中DOM的歷史演變過程,以及他們在這幾年對DOM樹先進化演變的影響。現在我們已經能看到新的DOM架構對Windows 10 Creators Update性能大幅提升的幫助:

安全專家們認為真正的DOM架構應該是幾個子系統的相互協調與合作,比如在Microsoft Edge中,就包括JS中的事件綁定,事件捕獲,事件編輯,拼寫檢查,HTML屬性,CSSOM,文本設置和其他所有相關的功能。在這些子系統中,DOM樹正位于中心。

由上圖可以看出,DOM真的是構成Web編程模型的幾個子系統的協調。但這只是DOM非常表面的東西,真正的一些內部細節,還要從DOM的歷史開始說起。
Internet Explorer DOM樹的歷史
如今的網絡開發人員一提起DOM,就通常會想到一棵看起來像這樣結構的樹:

然而,現實操作卻并不是像我們想的這么簡單,比如,Internet Explorer的DOM實現就相當的復雜。
簡單來說,Internet Explorer的DOM就是為了滿足90年代的網頁設計的,當時設計原始數據結構時,Web主要是一個文檔查看器,頂多包含幾個動畫GIF和幾幅圖像。因此,DOM的算法和數據結構更接近于Microsoft Word這樣的文檔查看器。回想早期的網絡,因為JavaScript不允許腳本化網頁,所以我們所了解的DOM樹根本就不存在。當是,由于文本是主要的實現手段,所以DOM的內部設計都是圍繞快速,高效的文本來進行存儲和操作的。WYSIWYG富文本編輯器就是當是的產物,專門用于字符插入和有限的格式化。
以文本為中心的設計
作為以文本為中心的設計結果,DOM的原理結構就是為文本存儲做準備的,這是一個復雜的文本數組系統,可以通過最少或在沒有內存分配的情況下進行高效拆分和連接。存儲功能可以將文本和標簽表示為線性進程,可由全局索引或字符位置(CP)尋址。在給定的CP中插入文本是非常高效的,并且通過高效的“拼接”操作集中復制或粘貼一系列文本。下圖就清楚的表明如何將包含“hello world”的簡單標記加載到文本存儲中,以及如何為每個字符和標簽分配CP。

為了存儲非文本數據,例如,格式化和分組信息,另一組對象的存儲就必須單獨維護,比如,樹位置(TreePos對象)的雙向鏈接列表。 TreePos對象是HTML源標記中的標簽語義,每個邏輯元素由開始和結束TreePos表示。這種線性結構使得在深度優先時,可以很快的遍歷整個DOM樹,幾乎每個DOM都需要搜索API,CSS以及布局算法。之后,安全專家們將TreePos對象擴展到另外兩種“位置”:TreeDataPos(用于指示文本的占位符)和PointerPos(用于指示插入符號,范圍邊界點,如生成的內容節點)。
每個TreePos對象還包括一個CP對象,它作為標簽的全局序數索引(對于像legacy document.all API這樣的東西有用)。從TreePos進入文本存儲時要用到CP,通過比較節點順序,甚至減去CP索引來查找文本的長度。
為了將這些節點整合在一起,TreeNode將會把它們綁定在一起,并建立了JavaScript DOM所期望的“樹”的層次,如下所示。

增加復雜層次
原有的這些CP基礎造成了DOM極其復雜,為了使整個系統能高效的運行,CP必須是最新的。因此,在每次DOM操作之后,例如輸入文本,復制或粘貼,DOM API操作,甚至點擊頁面在DOM中設置插入點都可以更新CP。最初,DOM操作主要由HTML解析器或用戶操作驅動,所以CP始終保持最新的模型是完全合理的。但是隨著JavaScript和DHTML的興起,這些操作變得越來越普遍和頻繁。
為了保持原來的更新速度,DOM添加了新的結構并且伸展樹(SplayTree)也隨之產生,伸展樹是在TreePos對象上添加了一系列重疊的樹連接。首先這些復雜結構的增加提高了DOM的性能,可以用O(log n)速度實現全局CP更新。然而,伸展樹實際上僅針對重復的本地搜索進行優化。
另一個在設計中出現的現象就是前面提到的復制或粘貼的“拼接”操作被擴展到處理所有的樹突變中。核心的拼接功能分三步進行,如下圖所示。

在步驟1中,拼接將通過從操作開始到操作結束遍歷樹形位置來記錄拼接信息。然后創建一個拼接記錄,其中包含此操作的命令指令。
在步驟2中,與該操作相關聯的所有節點,即,TreeNode和TreePos對象會從樹中刪除。要注意的是,在IE DOM樹中,TreeNode / TreePos對象與腳本引用的Element對象不同,以便于重疊標簽,因此刪除它們不是從功能方面考慮的。
在步驟3中,使用拼接記錄來重新創建目標位置中的新對象。例如,為了完成一個appendChild DOM操作,splice創建了一個圍繞節點的范圍(從TreeNode開始到TreePos結尾),將原來位置的編輯范圍經過拼接,創建了新的節點來表示節點及其子節點的新位置。大家可以想象一下,這樣一來雖然創造了很多內存分配,但算法的速度也降低了很多。

[1] [2] [3]  下一頁

【聲明】:黑吧安全網(http://www.gkrbnd.live)登載此文出于傳遞更多信息之目的,并不代表本站贊同其觀點和對其真實性負責,僅適于網絡安全技術愛好者學習研究使用,學習中請遵循國家相關法律法規。如有問題請聯系我們,聯系郵箱[email protected],我們會在最短的時間內進行處理。
  • 最新更新
    • 相關閱讀
      • 本類熱門
        • 最近下載
        福彩原副主任