Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Chromium擴展(Extension)的頁面(Page)加載過程分析

Chromium擴展(Extension)的頁面(Page)加載過程分析

編輯:關於Android編程

Chromium的Extension Page其實就是網頁,因此它們的加載過程與普通網頁相同。常見的Extension Page有Background Page和Popup Page。其中,Background Page在浏覽器窗口初始化完成後自動加載,之後運行在後台中。Popup Page在用戶點擊地址欄右邊的按鈕時加載,並且顯示在彈窗中。本文接下來就分析Extension Page的加載過程。

Extension Page是由Browser進程發加載在Extension Process中的,如圖1所示:

\

圖1 Extension的Background Page和Popup Page的加載示意圖

Extension Process實際上就是Render Process。Chromium的Content層向外提供了一個WebContents類,通過調用這個類的靜態成員函數Create就可以在一個Extension Process加載一個指定的Extension Page。

Background Page是一個特殊的網頁,它的內容是空的,不過包含有一個background.js。這個background.js是在Extension的清單文件中指定的。Popup Page則與普通網頁是一樣的,它既可以包含有UI元素,也可以包含JavaScript腳本。

接下來,我們就結合源代碼,先分析Background Page的加載過程,再分析Popup Page的加載過程。

Chromium的chrome模塊會創建一個ChromeNotificationObserver對象,用來監聽每一個新打開的浏覽器窗口的NOTIFICATION_BROWSER_WINDOW_READY事件。這時候上述ChromeNotificationObserver對象的成員函數OnBrowserWindowReady會被調用,如下所示:

 

void ChromeNotificationObserver::OnBrowserWindowReady(Browser* browser) {
  Profile* profile = browser->profile();
  ......

  extensions::ProcessManager* manager =
      ExtensionSystem::Get(profile)->process_manager();
  ......

  manager->OnBrowserWindowReady();

  ......
}
這個函數定義在文件external/chromium_org/chrome/browser/extensions/chrome_notification_observer.cc中。

 

參數browser指向的是一個Browser對象。這個Browser對象描述的就是一個新打開的浏覽器窗口,ChromeNotificationObserver類的成員函數OnBrowserWindowReady首先調用它的成員函數profile獲得浏覽器在啟動過程中創建的Profile,然後再根據這個Profile獲得一個ProcessManager對象。有了這個ProcessManager對象之後,就可以調用它的成員函數OnBrowserWindowReady,用來通知它有一個新的浏覽器窗口打開了。浏覽器啟動時創建Profile的過程,以及根據Profile創建ProcessManager對象的過程。

ProcessManager類的成員函數OnBrowserWindowReady在調用的過程中,就會為當前加載的Extension創建Background Page,如下所示:

 

void ProcessManager::OnBrowserWindowReady() {
  ......

  CreateBackgroundHostsForProfileStartup();
}
這個函數定義在文件external/chromium_org/extensions/browser/process_manager.cc中。

 

ProcessManager類的成員函數OnBrowserWindowReady調用另外一個成員函數CreateBackgroundHostsForProfileStartup為當前加載的Extension創建Background Page,如下所示:

 

void ProcessManager::CreateBackgroundHostsForProfileStartup() {
  ......

  const ExtensionSet& enabled_extensions =
      ExtensionRegistry::Get(GetBrowserContext())->enabled_extensions();
  for (ExtensionSet::const_iterator extension = enabled_extensions.begin();
       extension != enabled_extensions.end();
       ++extension) {
    CreateBackgroundHostForExtensionLoad(this, extension->get());

    ......
  }

  ......
}
這個函數定義在文件external/chromium_org/extensions/browser/process_manager.cc中。

 

Chromium的Browser進程在啟動的時候,會將那些狀態設置為Enabled的Extension保存在一個Extension Registry的Enabled List中。ProcessManager類的成員函數CreateBackgroundHostsForProfileStartup主要就是遍歷這個Enabled List中的每一個Extension,並且調用函數CreateBackgroundHostForExtensionLoad檢查它們是否指定了Background Page。如果指定了,那麼就會進行加載。

函數CreateBackgroundHostForExtensionLoad的實現如下所示:

 

static void CreateBackgroundHostForExtensionLoad(
    ProcessManager* manager, const Extension* extension) {
  DVLOG(1) << "CreateBackgroundHostForExtensionLoad";
  if (BackgroundInfo::HasPersistentBackgroundPage(extension))
    manager->CreateBackgroundHost(extension,
                                  BackgroundInfo::GetBackgroundURL(extension));
}
這個函數定義在文件external/chromium_org/extensions/browser/process_manager.cc中。

 

函數CreateBackgroundHostForExtensionLoad首先檢查參數extension描述的Extension是否指定了類型為persitent的Background Page。如果指定了,那麼就會調用參數manager指向的一個ProcessManager對象的成員函數CreateBackgroundHost對它進行加載。對於非persitent的Background Page,它們只會在特定事件發生時,才會被加載。本文主要以類型為persitent的Background Page為例,說明它們的加載過程。非persitent的Background Page的加載過程,也是類似的。

函數CreateBackgroundHostForExtensionLoad在調用ProcessManager類的成員函數CreateBackgroundHost加載一個Background Page之前,首先要獲得這個Background Page的URL。這個URL是通過調用BackgroundInfo類的靜態成員函數GetBackgroundURL獲得的,如下所示:

 

GURL BackgroundInfo::GetBackgroundURL(const Extension* extension) {
  const BackgroundInfo& info = GetBackgroundInfo(extension);
  if (info.background_scripts_.empty())
    return info.background_url_;
  return extension->GetResourceURL(kGeneratedBackgroundPageFilename);
}
這個函數定義在文件external/chromium_org/extensions/common/manifest_handlers/background_info.cc中。

 

Chromium將其平台上的程序分為擴展(Extension)和應用(App)兩種。兩者具有相同的文件結構,在Chromium中都是通過一個Extension類描述,但是後者比前者具有更嚴格的權限限制。應用又分為Hosted App(托管應用)和Packaged App(打包應用)兩種。Hosted App只提供一個圖標和Manifest文件,並且在Manifest文件中聲明了它的Popup Page和Background Page等URL。Packaged App則將Popup Page和Background Page文件打包在一起安裝在本地。關於Extension、Packaged App和Packaged App的更詳細描述。

我們假設參數extension描述的是一個Extension,並且是一個安裝在本地的Extension。這時候它在Manifest文件實際上只是為Backgropund Page指定了Background Script,如我們在前面一文所示的Page action example:

 

{  
  ......  
  
  "background": {  
    "scripts": ["background.js"]  
  },  

  ......
}
BackgroundInfo類的靜態成員函數GetBackgroundURL首先會獲得參數extension描述的Extension的Background Page信息。這些信息保存在一個BackgroundInfo對象中。當這個BackgroundInfo對象的成員變量background_scripts_的值不等空時,它的值描述的就是一個Background Page的Background Script,同時也表明參數extension描述的不是一個Hosted App。在這種情況下,我們就不能直接獲得Background Page的URL,而是要通過調用參數extension指向的Extension對象的成員函數GetResourceURL獲得。

 

Extension類的成員函數GetResourceURL的實現如下所示:

 

class Extension : public base::RefCountedThreadSafe {
 public:
  ......

  GURL GetResourceURL(const std::string& relative_path) const {
    return GetResourceURL(url(), relative_path);
  }

  ......
};
這個函數定義在文件external/chromium_org/extensions/common/extension.h中。

 

從前面的調用過程可以知道,參數relative_path的值為kGeneratedBackgroundPageFilename。Extension類的成員函數GetResourceURL首先調用另外一個成員函數url獲得當前正在處理的Extension的URL。這個URL的形式為:chrome-extension://[extension_id]/。其中,[extension_id]為當前正在處理的Extension的ID。

最後,Extension類的成員函數GetResourceURL將參數relative_path的值kGeneratedBackgroundPageFilename添加在前面獲得的URL的後面,從而得到當前正在處理的Extension的Background Page的URL。這是通過調用Extension類的靜態成員函數GetResourceURL實現的,如下所示:

 

// static
GURL Extension::GetResourceURL(const GURL& extension_url,
                               const std::string& relative_path) {
  ......

  std::string path = relative_path;

  // If the relative path starts with "/", it is "absolute" relative to the
  // extension base directory, but extension_url is already specified to refer
  // to that base directory, so strip the leading "/" if present.
  if (relative_path.size() > 0 && relative_path[0] == '/')
    path = relative_path.substr(1);

  GURL ret_val = GURL(extension_url.spec() + path);
  ......

  return ret_val;
}
這個函數定義在文件external/chromium_org/extensions/common/extension.cc中。

 

通過上面的分析,我們就可以知道,一個Extension如果指定了Background Page,那麼這個Background Page的URL就為chrome-extension://[extension_id]/kGeneratedBackgroundPageFilename。其中,kGeneratedBackgroundPageFilename是一個常量,它的定義如下所示:

 

const char kGeneratedBackgroundPageFilename[] =
    "_generated_background_page.html";
這個常量定義在文件external/chromium_org/extensions/common/constants.cc中。

 

這意味著,一個Extension的Background Page的URL為:chrome-extension://[extension_id]/_generated_background_page.html。Chromium的extension模塊會注冊一個Chromium Extension Protocol Handler,用來處理chrome-extension協議。也就是說,當我們在浏覽器的地址欄輸入上述URL時ChromiumExtension Protocol Handler會返回[extension_id]/_generated_background_page.html的內容給WebKit處理。這個文件的內容是動態生成的。以前面的Page action example為例,Chromium Extension Protocol Handler為它生成的_generated_background_page.html的內容如下所示:

 


<script src="background.js"></script>獲得了要加載的Background Page的URL之後,回到前面分析的函數CreateBackgroundHostForExtensionLoad中,接下來它就會調用ProcessManager類的成員函數CreateBackgroundHost加載Background Page,如下所示:

 

 

bool ProcessManager::CreateBackgroundHost(const Extension* extension,
                                          const GURL& url) {
  ......

  ExtensionHost* host =
      new ExtensionHost(extension, GetSiteInstanceForURL(url), url,
                        VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
  host->CreateRenderViewSoon();
  ......
  return true;
}
這個函數定義在文件external/chromium_org/extensions/browser/process_manager.cc中。

 

ProcessManager類的成員函數CreateBackgroundHost首先是創建一個ExtensionHost對象。這個ExtensionHost對象類似於Chromium加載一個普通URL時在Browser進程中創建的RenderProcessHost對象。關於RenderProcessHost的詳細描述,可以參考Chromium多進程架構簡要介紹和學習計劃這個系列的文章。

ExtensionHost對象在創建的過程中,會創建一個WebContents對象,如下所示:

 

ExtensionHost::ExtensionHost(const Extension* extension,
                             SiteInstance* site_instance,
                             const GURL& url,
                             ViewType host_type)
    : ......,
      initial_url_(url),
      ...... {
  ......

  host_contents_.reset(WebContents::Create(
      WebContents::CreateParams(browser_context_, site_instance))),
  
  ......
}

這個函數定義在文件external/chromium_org/extensions/browser/extension_host.cc中。

創建出來的WebContents對象保存在ExtensionHost類的成員變量host_contents_中。同時,要加載的Background Page URL將會保存在ExtensionHost類的成員變量initial_url_中。

回到前面分析的ProcessManager類的成員函數CreateBackgroundHost,它創建了一個ExtensionHost對象之後,接下來就會調用這個ExtensionHost對象的成員函數CreateRenderViewSoon加載指定的Background Page,如下所示:

 

void ExtensionHost::CreateRenderViewSoon() {
  if ((render_process_host() && render_process_host()->HasConnection())) {
    // If the process is already started, go ahead and initialize the RenderView
    // synchronously. The process creation is the real meaty part that we want
    // to defer.
    CreateRenderViewNow();
  } else {
    ProcessCreationQueue::GetInstance()->CreateSoon(this);
  }
}
這個函數定義在文件external/chromium_org/extensions/browser/extension_host.cc中。

 

ExtensionHost類的成員函數CreateRenderViewSoon首先判斷用來加載指定的Background Page的Extension Process是否已經創建出來了。如果已經創建,那麼就直接調用另外一個成員函數CreateRenderViewNow同步請求在這個Extension Process中加載指定的Background Page。否則的話,則需要通過調用當前進程中的一個ProcessCreationQueue單例對象的成員函數CreateSoon異步請求加載指定的的Background Page。後一種情況之所以要異步請求,是因為這種情況需要先創建一個Extension Process,然後才能加載指定的Background Page,而創建Extension Process是一個相對耗時的操作。

在異步請求情況下,指定的Background Page同樣也是通過ExtensionHost類的成員函數CreateRenderViewNow進行加載的。因此,接下來我們繼續分析它的實現,如下所示:

 

void ExtensionHost::CreateRenderViewNow() {
  LoadInitialURL();
  ......
}
這個函數定義在文件external/chromium_org/extensions/browser/extension_host.cc中。

 

ExtensionHost類的成員函數CreateRenderViewNow調用另外一個成員函數LoadInitialURL加載指定的Background Page,如下所示:

 

void ExtensionHost::LoadInitialURL() {
  host_contents_->GetController().LoadURL(
      initial_url_, content::Referrer(), content::PAGE_TRANSITION_LINK,
      std::string());
}
這個函數定義在文件external/chromium_org/extensions/browser/extension_host.cc中。

 

從前面的分析可以知道,指定要加載的Background Page的URL保存在ExtensionHost類的成員變量initial_url_中。ExtensionHost類的成員函數LoadInitialURL首先通過調用成員變量host_contents_指向的WebContents對象的成員函數GetController獲得一個NavigationControllerImpl對象。有了這個NavigationControllerImpl對象之後,就可以調用它的成員函數LoadURL在相應的Extension Process中加載指定的Background Page了。這個過程與加載一個普通的URL是一樣的,具體可以參考前面Chromium網頁Frame Tree創建過程分析一文。

這樣,我們就分析完成了Extension的Background Page的加載過程。從分析的過程可以知道,Extension的Background Page本質上是一個保存在本地的網頁,因此它的加載過程與普通的URL並無異。接下來,我們繼續分析Extension的Popup Page的加載過程。

從前面Chromium擴展(Extension)機制簡要介紹和學習計劃一文可以知道,Browser Action和Page Action均可在地址欄右邊放置一個按鈕,並且在點擊該按鈕時,顯示一個Popup Page。兩者加載Popup Page的原理都是一樣的,因此接下來我們以Browser Action為例,分析Popup Page的加載過程。

當一個Extension指定了Browser Action時,Chromium將會為其創建一個BrowserActionButton。這個BrowserActionButton描述的就是Extension在地址欄右邊的按鈕。當用戶點擊這個按鈕的時候,BrowserActionButton類的成員函數ButtonPressed就會被調用,如下所示:

 

void BrowserActionButton::ButtonPressed(views::Button* sender,
                                        const ui::Event& event) {
  delegate_->OnBrowserActionExecuted(this);
}
這個函數定義在文件external/chromium_org/chrome/browser/ui/views/toolbar/browser_action_view.cc中。

 

BrowserActionButton類的成員變量delegate_指向的是一個BrowserActionsContainer對象。這個BrowserActionsContainer對象描述的是包含Browser Action Button的容器,BrowserActionButton類的成員函數ButtonPressed調用它的成員函數OnBrowserActionExecuted,通知它在一個彈出窗口中加載一個Popup Page。

BrowserActionsContainer類的成員函數OnBrowserActionExecuted的實現如下所示:

 

void BrowserActionsContainer::OnBrowserActionExecuted(
    BrowserActionButton* button) {
  ShowPopup(button, ExtensionPopup::SHOW, true);
}
這個函數定義在文件external/chromium_org/chrome/browser/ui/views/toolbar/browser_actions_container.cc中。

 

BrowserActionsContainer類的成員函數OnBrowserActionExecuted調用另外一個成員函數ShowPopup顯示一個Popup Page,如下所示:

 

bool BrowserActionsContainer::ShowPopup(
    BrowserActionButton* button,
    ExtensionPopup::ShowAction show_action,
    bool should_grant) {
  const Extension* extension = button->extension();
  GURL popup_url;
  if (model_->ExecuteBrowserAction(
          extension, browser_, &popup_url, should_grant) !=
      extensions::ExtensionToolbarModel::ACTION_SHOW_POPUP) {
    return false;
  }

  ......

  popup_ = ExtensionPopup::ShowPopup(popup_url, browser_, reference_view,
                                     views::BubbleBorder::TOP_RIGHT,
                                     show_action);
  ......

  return true;
}
這個函數定義在文件external/chromium_org/chrome/browser/ui/views/toolbar/browser_actions_container.cc中。

 

BrowserActionsContainer類的成員函數ShowPopup首先調用成員變量model_指向的一個ExtensionToolbarModel對象的成員函數ExecuteBrowserAction獲得要顯示的Popup Page的URL,如下所示:

 

ExtensionToolbarModel::Action ExtensionToolbarModel::ExecuteBrowserAction(
    const Extension* extension,
    Browser* browser,
    GURL* popup_url_out,
    bool should_grant) {
  ......

  ExtensionAction* browser_action =
      ExtensionActionManager::Get(profile_)->GetBrowserAction(*extension);

  ......

  if (browser_action->HasPopup(tab_id)) {
    if (popup_url_out)
      *popup_url_out = browser_action->GetPopupUrl(tab_id);
    return ACTION_SHOW_POPUP;
  }

  ......

  return ACTION_NONE;
}
這個函數定義在文件external/chromium_org/chrome/browser/extensions/extension_toolbar_model.cc中。

 

參數extension描述的就是要顯示Popup Page的Extension。ExtensionToolbarModel類的成員函數ExecuteBrowserAction首先通過調用ExtensionActionManager類的靜態成員函數Get獲得與當前使用的Profile關聯的一個ExtensionActionManager對象。有了這個ExtensionActionManager對象之後,就可以獲得參數extension描述的Extension在清單文件中配置的Browser Action信息。這些信息封裝在一個ExtensionAction對象中。

獲得了要顯示Popup Page的Extension的Browser Action信息之後,就可以檢查它是否指定了Popup Page。如果指定了,並且輸出參數popup_url_out的值不等於NULL,那麼ExtensionToolbarModel類的成員函數ExecuteBrowserAction就會調用前面獲得的一個ExtensionAction對象的成員函數GetPopupUrl獲得當前要顯示的Popup Page的URL。獲取到的Popup Page URL將會通過輸出參數popup_url_out返回給調用者。

接下來我們繼續分析ExtensionAction類的成員函數GetPopupUrl的實現,以便了解Extension的Popup Page URL的結構,如下所示:

 

GURL ExtensionAction::GetPopupUrl(int tab_id) const {
  return GetValue(&popup_url_, tab_id);
}
這個函數定義在文件external/chromium_org/chrome/browser/extensions/extension_action.cc中。

 

ExtensionAction類的成員函數GetPopupUrl返回的是成員變量popup_url_的值。這個值是在ExtensionAction類的構造函數中設置的,如下所示:

 

ExtensionAction::ExtensionAction(const std::string& extension_id,
                                 extensions::ActionInfo::Type action_type,
                                 const extensions::ActionInfo& manifest_data)
    : extension_id_(extension_id), action_type_(action_type) {
  ......
  SetPopupUrl(kDefaultTabId, manifest_data.default_popup_url);
  ......
}
這個函數定義在文件external/chromium_org/chrome/browser/extensions/extension_action.cc中。

 

參數manifest_data指向的是一個ActionInfo對象。這個ActionInfo對象描述的是Extension在清單文件中配置的Browser Action信息。通過這個ActionInfo對象的成員變量default_popup_url可以獲得為Browser Action指定的Popup Page文件名。以前面Chromium擴展(Extension)機制簡要介紹和學習計劃一文中的Browser action example為例,它為Browser Action指定的Popup Page文件名為“popup.html”,如下所示:

 

{  
  ...... 
  
  "browser_action": {  
    "default_icon": "icon.png",  
    "default_popup": "popup.html"  
  },  
  
  ......
}  
回到ExtensionAction類的構造函數中,它獲得的Popup Page URL將會通過調用成員函數SetPopupUrl保存在成員變量popup_url_中,如下所示:

 

 

void ExtensionAction::SetPopupUrl(int tab_id, const GURL& url) {
  ......
  SetValue(&popup_url_, tab_id, url);
}
這個函數定義在文件external/chromium_org/chrome/browser/extensions/extension_action.cc中。

 

從前面的分析就可以知道,Extension的Popup Page的URL來自於ExtensionAction類的成員變量popup_url_,而後者的值又來自於ActionInfo類的成員變量default_popup_url。因此,接下來我們繼續分析ActionInfo類的成員變量default_popup_url的設置過程,以便了解Extension的Popup Page URL的結構。

ActionInfo類的成員變量default_popup_url是在加載Extension的過程中設置的。一個Extension如果指定了Browser Action,那麼Chromium就會調用ActionInfo類的靜態成員函數Load解析Browser Action的信息,如下所示:

 

scoped_ptr ActionInfo::Load(const Extension* extension,
                                        const base::DictionaryValue* dict,
                                        base::string16* error) {
  scoped_ptr result(new ActionInfo());
  ......

  // Read the action's |popup| (optional).
  const char* popup_key = NULL;
  if (dict->HasKey(keys::kPageActionDefaultPopup))
    popup_key = keys::kPageActionDefaultPopup;

  ......

  if (popup_key) {
    const base::DictionaryValue* popup = NULL;
    std::string url_str;

    if (dict->GetString(popup_key, &url_str)) {
      // On success, |url_str| is set.  Nothing else to do.
    } 
    ......

    if (!url_str.empty()) {
      // An empty string is treated as having no popup.
      result->default_popup_url = Extension::GetResourceURL(extension->url(),
                                                            url_str);
      ......
    } 
    ......
  }

  return result.Pass();
}
這個函數定義在文件external/chromium_org/chrome/common/extensions/api/extension_action/action_info.cc中。

 

ActionInfo類的靜態成員函數Load首先創建一個ActionInfo對象描述當前要解析的Browser Action的信息。

ActionInfo類的靜態成員函數Load接下來檢查當前要解析的Browser Action是否指定了popup屬性。如果指定了,那麼它的值就是一個Popup Page的文件名。這個文件名會被提取出來,保存在變量url_str中。

有了Popup Page的文件名之後,我們還需要知道它所屬的Extension的URL。這可以通過調用參數extension指向的Extension對象的成員函數url獲得。前面我們提到過,一個Extension的URL的形式為:chrome-extension://[extension_id]/。其中,[extension_id]為當前正在處理的Extension的ID。

有了Extension的URL之後,就可以將前面獲得的Popup Page文件名附加在它之後,從而得到一個Popup Page的URL。這是通過調用Extension類的靜態成員函數GetResourceURL實現的。Extension類的靜態成員函數GetResourceURL我們在前面已經分析過,這裡不再復述。

最後,ActionInfo類的靜態成員函數Load會將獲得Popup Page URL保存在前面創建的ActionInfo對象的成員變量default_popup_url中。這樣,我們前面分析的ExtensionAction類的構造函數就可以通過這個成員變量獲得一個Popup Page的URL,並且將它保存在自己的成員變量popup_url_中了。

這一步執行完成之後,回到前面分析的BrowserActionsContainer類的成員函數ShowPopup中,這時候就它獲得了要加載的Popup Page的URL,接下來它又會通過調用ExtensionPopup類的靜態成員函數ShowPopup加載該URL,如下所示:

 

ExtensionPopup* ExtensionPopup::ShowPopup(const GURL& url,
                                          Browser* browser,
                                          views::View* anchor_view,
                                          views::BubbleBorder::Arrow arrow,
                                          ShowAction show_action) {
  extensions::ExtensionViewHost* host =
      extensions::ExtensionViewHostFactory::CreatePopupHost(url, browser);
  ExtensionPopup* popup = new ExtensionPopup(host, anchor_view, arrow,
      show_action);

  ......

  return popup;
}
這個函數定義在文件external/chromium_org/chrome/browser/ui/views/extensions/extension_popup.cc中。

 

ExtensionPopup類的靜態成員函數ShowPopup首先調用ExtensionViewHostFactory類的靜態成員函數CreatePopupHost創建一個ExtensionViewHost對象。有了這個ExtensionViewHost對象之後,就可以將它封裝在一個ExtensionPopup對象中。這個ExtensionPopup對象描述的就是當前要顯示的Popup Page。

ExtensionViewHostFactory類的靜態成員函數CreatePopupHost在創建ExtensionViewHost對象的過程中,就會通過前面提到的WebContents接口加載Popup Page,如下所示:

 

ExtensionViewHost* ExtensionViewHostFactory::CreatePopupHost(const GURL& url,
                                                             Browser* browser) {
  DCHECK(browser);
  return CreateViewHost(
      url, browser->profile(), browser, VIEW_TYPE_EXTENSION_POPUP);
}
這個函數定義在文件external/chromium_org/chrome/browser/extensions/extension_view_host_factory.cc中。

 

ExtensionViewHostFactory類的靜態成員函數CreatePopupHost通過調用函數CreateViewHost創建一個ExtensionViewHost對象,如下所示:

 

ExtensionViewHost* CreateViewHost(const GURL& url,
                                  Profile* profile,
                                  Browser* browser,
                                  extensions::ViewType view_type) {
  ......

  const Extension* extension = GetExtensionForUrl(profile, url);
  ......

  return CreateViewHostForExtension(
      extension, url, profile, browser, view_type);
}

這個函數定義在文件external/chromium_org/chrome/browser/extensions/extension_view_host_factory.cc中。

函數CreateViewHost首先通過調用函數GetExtensionForUrl獲得一個Extension對象。這個Extension對象描述的就是當前要顯示Popup Page的Extension。有了這個Extension對象之後,函數CreateViewHost最後調用另外一個函數CreateViewHostForExtension創建一個ExtensionViewHost對象,如下所示:

 

ExtensionViewHost* CreateViewHostForExtension(const Extension* extension,
                                              const GURL& url,
                                              Profile* profile,
                                              Browser* browser,
                                              ViewType view_type) {
  ......
  ProcessManager* pm =
      ExtensionSystem::Get(profile)->process_manager();
  content::SiteInstance* site_instance = pm->GetSiteInstanceForURL(url);
  ExtensionViewHost* host =
#if defined(OS_MACOSX)
      new ExtensionViewHostMac(extension, site_instance, url, view_type);
#else
      new ExtensionViewHost(extension, site_instance, url, view_type);
#endif
  host->CreateView(browser);
  return host;
}
這個函數定義在文件external/chromium_org/chrome/browser/extensions/extension_view_host_factory.cc中。

 

從這裡可以看到,在非Mac OS X平台上,函數CreateViewHostForExtension會創建一個ExtensionViewHost對象。這個ExtensionViewHost對象的創建過程,也就是ExtensionViewHost的構造函數的實現,如下所示:

 

ExtensionViewHost::ExtensionViewHost(
    const Extension* extension,
    content::SiteInstance* site_instance,
    const GURL& url,
    ViewType host_type)
    : ExtensionHost(extension, site_instance, url, host_type),
      ...... {
  ......
}
這個函數定義在文件external/chromium_org/chrome/browser/extensions/extension_view_host.cc中。

 

ExtensionViewHost類是從ExtensionHost類繼承下來的。因此,ExtensionViewHost的構造函數會調用父類ExtensionHost的構造函數,用來執行初始化工作。前面我們在分析Extension的Background Page的加載過程時,已經分析過ExtensionHost類的構造函數了。它將會創建一個WebContents對象。有了這個WebContents對象之後,以後就可以加載這裡的參數url描述的一個Popup Page了。

這樣,我們就分析完成Extension的Popup Page的加載過程了。從分析的過程就可以看出,Extension的Popup Page與Background Page都具有自己的URL。URL的形式為chrome-extension://[extension_id]/xxx.html。這些URL都是通過Chromium的Content層向外提供的API接口WebContents在一個Extension Process中加載的。

與此同時,普通網頁的URL也是通過Chromium的Content層向外提供的API接口WebContents在一個Render Process中加載的。Extension Process和Render Process的概念是一樣的,都是用來加載、解析和渲染網頁的。從這個角度看,Extension Page的加載、解析和渲染與一般的Web Page並無異。

至此,我們就以Background Page和Popup Page為例,分析了Extension的Page加載過程。Extension由Page和Content Script組成。在接下來一篇文章中,我們將繼續分析Extension的Content Script的加載過程。這樣我們就更加完整地理解Extension的實現原理了。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved