chromium 書簽實現(xiàn)機制
一、概述
?????? 之前有介紹過chromium的界面層的相關(guān)知識,這篇文章則是重點介紹chromium中書簽這一個模塊,不僅有界面層的知識,還會較多的介紹邏輯層的一些內(nèi)容。接下來會詳細(xì)介紹chromium中書簽功能的詳細(xì)實現(xiàn)。主要問題有:
??????????????1、書簽數(shù)據(jù)在硬盤和內(nèi)存中存儲結(jié)構(gòu);
??????????????2、初始化過程;
??????????????3、相關(guān)操作的實現(xiàn)步驟;
??????????????4、線程模型;
??????????????5、書簽同步。
二、書簽結(jié)構(gòu)綜述
??????? Bookmark界面層在..srcchromebrowseruiviewsbookmarks中實現(xiàn)。Bookmark邏輯層在..srcchromebrowserbookmarks 中實現(xiàn),包括讀寫書簽文件,書簽導(dǎo)入導(dǎo)出等。
???????書簽在整個工程中是chrome特色功能,它包括三種類型,書簽欄、其他、和“mobile”。書簽欄管理用戶創(chuàng)建的所有書簽
???????作為chromium拓展中心的一個功能,整體流程圖如圖:
?
??? 圖1 書簽整體流程圖
???????在書簽?zāi)K中,其中主要的兩個模塊是底層數(shù)據(jù)處理和界面顯示,具體實現(xiàn)分別在BookmarkModel和BookmarkBarView中,它們之間采用的是observer設(shè)計模式,BookmarkModel(具體目標(biāo)),BookmarkBarView(具體觀察者)。
三、書簽存儲結(jié)構(gòu)
???????書簽的存儲結(jié)構(gòu)需要從配置文件中結(jié)構(gòu)及內(nèi)存中存儲結(jié)構(gòu)兩方面分析。
???????書簽配置文件中存儲在本地Bookmarks中,以JSON的格式存儲。并在書簽?zāi)K啟動的時候load文件中的書簽數(shù)據(jù)。本地JSON書簽數(shù)據(jù)如下:
???????? "children": [ {
??????????? "date_added": "12994954482846617",
??????????? "id": "7",
??????????? "name": "Google",
??????????? "type": "url",
??????????? "url": "http://www.google.com.hk/"
???????? },
???????在內(nèi)存中每個節(jié)點對應(yīng)一個BookmarkNode。其中包含了favicon, id and type等節(jié)點信息。
???????在BookmarkModel中定義了如下幾個節(jié)點:
???????? BookmarkNode root_;
???????? BookmarkPermanentNode* bookmark_bar_node_;
???????? BookmarkPermanentNode* other_node_;
???????? BookmarkPermanentNode* mobile_node_;
???????root_作為根節(jié)點,是書簽欄中節(jié)點和“其他”文件夾中節(jié)點的父節(jié)點。BookmarkPermanentNode只是BookmarkNode一個簡單的封裝,用來管理書簽欄及“其他”等這種固定的文件夾。
四、書簽初始化及釋放
???????書簽功能的初始化:可以分為兩個部分,數(shù)據(jù)邏輯層和界面層。從上面的主流程圖中我們可以看出,數(shù)據(jù)邏輯層先于界面層初始化,具體初始化流程如下:
???????數(shù)據(jù)邏輯層:
???????初始化流程,拓展中心統(tǒng)一初始化,在ExtensionService::InitEventRouters()中調(diào)用
???????bookmark_event_router_.reset(new BookmarkExtensionEventRouter(
???????????? BookmarkModelFactory::GetForProfile(profile_)));
???????? ??? bookmark_event_router_->Init();
???????最終在BookmarkModelFactory完成BookmarkModel的創(chuàng)建。另外,BookmarkExtensionEventRouter繼承自BookmarkModelObserver,是BookmarkModel的一個具體觀察者。會將bookmark中的修改通知裝換成event傳給extension system
???????邏輯層初始化執(zhí)行順序如下圖:
?
圖2 書簽數(shù)據(jù)邏輯層執(zhí)行
???????為防止阻塞UI線程,書簽文件的加載是在一個后臺進(jìn)程中完成的,chromium啟動的時候,主線程Post出一個文件加載的Task,同時傳入了一個回調(diào)函數(shù)的指針參數(shù),當(dāng)消息中心處理這個Task時會調(diào)用之前傳入的回調(diào),并完成書簽文件的加載接JSON數(shù)據(jù)的解析。注意這個步驟是在File加載線程中完成的。加載解析完成后,F(xiàn)ile加載線程會POST出一個Task,并將操作權(quán)交還給主線程,去完成后需初始化工作。
??????? 書簽文件的解析是在JSONFileValueSerializer中完成的,在這個里面會將JSON文件反序列化。并將其內(nèi)容存入BookmarkLoadDetails中,供接下來的調(diào)用。
???????界面層:
???????BookmarkBarView類是BookmarkModel的具體觀察者,負(fù)責(zé)書簽數(shù)據(jù)的渲染布局等,與書簽界面層相關(guān)的操作主要在這里面實現(xiàn)。
???????BookmarkBarView由BrowserView統(tǒng)一管理,BrowserView不會主動創(chuàng)建BookmarkBarView,而是在之后執(zhí)行需要書簽的操作中,如UpdateUIForContents,若書簽界面層尚未創(chuàng)建,則執(zhí)行創(chuàng)建操作,否則跳過。
???????書簽?zāi)K的釋放:同樣重點完成數(shù)據(jù)邏輯層和界面層的釋放。釋放的過程和創(chuàng)建相反。先delete 管理界面模塊的BookmarkBarView,這部分功能由BrowserView統(tǒng)一管理釋放。之后再進(jìn)一步delete管理邏輯層的模塊BookmarkModel,這部分釋放工作在配置文件管理中心統(tǒng)一釋放。
五、書簽操作執(zhí)行流程
???????書簽界面層與邏輯層的類關(guān)系圖如下。
?
?? 圖3 界面層、邏輯層類關(guān)系圖
?? ?
???????BookmarkModel繼承自PofileDeyedService,因而可以實現(xiàn)在配置文件管理中心統(tǒng)一釋放
???????BookmarkBarView繼承自DetachableToolbarView,是一個與Chrome frame分離的view控件。
???????BookmarkModel與BookmarkBarView均繼承自消息中心NotificationObserver,并會通過registrar_注冊自己需要接受的通知。
???????BookmarkModel類中的實現(xiàn)。它是是observer模式中的具體目標(biāo)及數(shù)據(jù)邏輯層的核心實現(xiàn)。BookmarkModel對象需要通過 BookmarkModelFactory才能創(chuàng)建,而不能直接創(chuàng)建。
???????BookmarkModel類管理者內(nèi)存中的書簽數(shù)據(jù),也向外提供了很多的操作接口,如AddNode、RemoveAndDeleteNode、RemoveNode等。用戶操作書簽時,都會通過調(diào)用這些接口來修改書簽內(nèi)容。
???????在BookmarkModel中通過定義一個BookmarkStorage的對象來操作書簽的磁盤讀寫,上面提到的文件加載就是通過這個對象來執(zhí)行的。另外,當(dāng)書簽數(shù)據(jù)與磁盤同步也在?? 這個里面完成,當(dāng)用戶修改了書簽內(nèi)容時,會調(diào)用其中的ScheduleSave()函數(shù)寫回磁盤。
???????書簽磁盤讀寫的類圖如下:
?
? 圖4 書簽磁盤讀寫類圖
???????其中BookmarkLoadDetails是用來臨時存儲書簽數(shù)據(jù)的對象。
?????? BookmarkBarView類派生自BookmarkModelObserver,是observer模式中一個具體的觀察者。中完成書簽界面展示以及部分用戶操作。
?????? 另外,書簽欄內(nèi)部的子文件夾顯示也存在菜單欄的彈出,包括點擊文件夾及拖動書簽節(jié)點至文件夾都會彈出。BookmarkBarView中定義了兩個菜單對書簽欄的子菜單進(jìn)行管理:
???????? BookmarkMenuController* bookmark_menu_;
???????? BookmarkMenuController* bookmark_drop_menu_;
???????其中bookmark_menu_用于管理用戶點擊BookmarkBarView中的文件夾時彈出的菜單(BookmarkBarView是MenuButtonListener的子類,可以收聽到這個事件)。bookmark_drop_menu_是用戶拖拽一個node到文件夾上時彈出的菜單。
?
圖5 書簽右鍵菜單實現(xiàn)類圖
???????Chromium中書簽最基本的操作有添加、刪除、修改等。同時,用戶操作書簽的方式也有很多,如快捷鍵,主菜單,書簽右鍵菜單等。書簽操作的執(zhí)行順序基本如下。
?
圖6 書簽操作執(zhí)行順序
???????書簽的顯示實現(xiàn)
?????? 在chromium的代碼中,每次調(diào)用網(wǎng)頁時都會調(diào)用void Browser::UpdateBookmarkBarState(BookmarkBarStateChangeReason reason),用以判斷是否需要顯示書簽,書簽有三種狀態(tài),SHOW(顯示與地址欄不分離)、DETACHED(顯示與地址欄分離)、HIDDEN(不顯示)。
[cpp]?view
plaincopy
void?Browser::UpdateBookmarkBarState(BookmarkBarStateChangeReason?reason)?{??
??BookmarkBar::State?state;??
??//?The?bookmark?bar?is?hidden?in?fullscreen?mode,?unless?on?the?new?tab?page.??
??if?(browser_defaults::bookmarks_enabled?&&??
??????profile_->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar)?&&??
??????(!window_?||?!window_->IsFullscreen()))?{??
????state?=?BookmarkBar::SHOW;??
??}?else?{??
????WebContents*?web_contents?=?chrome::GetActiveWebContents(this);??
????BookmarkTabHelper*?bookmark_tab_helper?=??
????????web_contents???BookmarkTabHelper::FromWebContents(web_contents)?:?NULL;??
????if?(bookmark_tab_helper?&&?bookmark_tab_helper->ShouldShowBookmarkBar())??
??????state?=?BookmarkBar::DETACHED;??
????else??
??????state?=?BookmarkBar::HIDDEN;??
??}??
??
??//?Only?allow?the?bookmark?bar?to?be?shown?in?default?mode.??
??if?(!search_model_->mode().is_default())??
????state?=?BookmarkBar::HIDDEN;??
??
??if?(state?==?bookmark_bar_state_)??
????return;??
??
??bookmark_bar_state_?=?state;??
??
??if?(!window_)??
????return;??//?This?is?called?from?the?constructor?when?window_?is?NULL.??
??
??if?(reason?==?BOOKMARK_BAR_STATE_CHANGE_TAB_SWITCH)?{??
????//?Don't?notify?BrowserWindow?on?a?tab?switch?as?at?the?time?this?is?invoked??
????//?BrowserWindow?hasn't?yet?switched?tabs.?The?BrowserWindow?implementations??
????//?end?up?querying?state?once?they?process?the?tab?switch.??
????return;??
??}??
??
??BookmarkBar::AnimateChangeType?animate_type?=??
??????(reason?==?BOOKMARK_BAR_STATE_CHANGE_PREF_CHANGE)????
??????BookmarkBar::ANIMATE_STATE_CHANGE?:??
??????BookmarkBar::DONT_ANIMATE_STATE_CHANGE;??
??window_->BookmarkBarStateChanged(animate_type);??
}??
?????? 在這個函數(shù)中最終會調(diào)用window_->BookmarkBarStateChanged(animate_type),其中會調(diào)用到BrowserView的實現(xiàn)中,再由其中的bookmark_bar_view_來統(tǒng)一接手管理界面的工作。
???????書簽欄中點擊打開網(wǎng)頁
???????當(dāng)從書簽欄中點擊打開網(wǎng)頁時,先會調(diào)用void BookmarkBarView::ButtonPressed函數(shù),并在此函數(shù)中會調(diào)用page_navigator_->OpenURL(params);進(jìn)一步調(diào)用WebContentsImple相應(yīng)的方法來實現(xiàn)。
???????當(dāng)前頁直接添加書簽
???????將當(dāng)前頁添加到書簽則會調(diào)用void BookmarkCurrentPage(Browser* browser)(browser_commands.cc中的全局函數(shù)),再調(diào)用void BrowserView::ShowBookmarkBubble(const GURL& url, bool already_bookmarked)最終是在BookmarkBubbleView中完成。
六、書簽線程模型
???????在整個書簽?zāi)K中,除了文件讀取是在后臺File線程中完成外,其余操作均在主線程中執(zhí)行,線程執(zhí)行方法與整個chromium的線程模型保持一致。
???????為了保證書簽文件在加載是不出現(xiàn)線程安全問題,chromium中設(shè)計了一個BookmarkStorage的中間層來讀寫書簽文件,并在BookmarkStorage中定義了一個BookmarkLoadDetails,通過操作它來加載書簽文件中的詳細(xì)信息。加載完成后回調(diào)到主線程,并把所有權(quán)交還給BookmarkModel。但BookmarkModel中沒有對BookmarkLoadDetails的引用,不能直接操作其中的內(nèi)容,這樣就確保解決了線程可能帶來的問題。
七、登陸后書簽同步
???????用戶登陸成功后,數(shù)據(jù)同步中心會對相關(guān)數(shù)據(jù)進(jìn)行同步,書簽也包括在內(nèi)。在Chromium中會由browser_sync同步中心統(tǒng)一管理用戶同步相關(guān)操作。BookmarkModelAssociator負(fù)責(zé)實現(xiàn)bookmark model 和sync model之間的聯(lián)系算法、為sync node 及bookmark node之間的相互獲取提供方法。同步完成后最終會調(diào)用BookmarkModel的相關(guān)方法來實現(xiàn)書簽的更新。