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)两种。两者