您的位置:首页 > 移动开发 > Android开发

Chromium扩展(Extension)的页面(Page)加载过程分析

2016-09-19 01:00 375 查看
       Chromium的Extension Page其实就是网页,因此它们的加载过程与普通网页相同。常见的Extension Page有Background Page和Popup Page。其中,Background Page在浏览器窗口初始化完成后自动加载,之后运行在后台中。Popup Page在用户点击地址栏右边的按钮时加载,并且显示在弹窗中。本文接下来就分析Extension Page的加载过程。老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!
《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!

       Extension Page是加载在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对象的过程,可以参考前面Chromium扩展(Extension)加载过程分析一文。

        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扩展(Extension)加载过程分析一文提到,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、Hosted App和Packaged App的更详细描述,可以参考Chrome应用基础一文。
       我们假设参数extension描述的是一个Extension,并且是一个安装在本地的Extension。这时候它在Manifest文件实际上只是为Backgropund Page指定了Background Script,如我们在前面Chromium扩展(Extension)机制简要介绍和学习计划一文所示的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<Extension> {
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时Chromium Extension Protocol Handler会返回[extension_id]/_generated_background_page.html的内容给WebKit处理。这个文件的内容是动态生成的。以前面的Page action example为例,Chromium Extension Protocol Handler为它生成的_generated_background_page.html的内容如下所示:
<!DOCTYPE html>
<body>
<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> ActionInfo::Load(const Extension* extension,
const base::DictionaryValue* dict,
base::string16* error) {
scoped_ptr<ActionInfo> 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加载过程。我们在前面Chromium扩展(Extension)机制简要介绍和学习计划一文提到,Extension由Page和Content Script组成。在接下来一篇文章中,我们将继续分析Extension的Content Script的加载过程。这样我们就更加完整地理解Extension的实现原理了。敬请关注!更多的信息也可以关注老罗的新浪微博:http://weibo.com/shengyangluo
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息