Chromium扩展(Extension)的ContentScript加载过程分析

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

Chromium扩展(Extension)的ContentScript加载过程分析Chromium的Extension由Page和ContentScript组成。Page有UI和JS,它们加载在自己的ExtensionProcess中渲染和执行。ContentScript只有JS,这些JS是注入在宿主网页中执行的。ContentScript可以访问宿主网页的DOMTree,从而可以增强宿主网页的功能。本文接下来分析ContentScript注入到宿主网页执行的过程。[plain]viewplaincopy在CODE上查看代码片派生到我的代码片{......content_scripts:[{matches:[],js:[content.js],run_at:document_start,all_frames:true}]}这个清单文件仅对URL为“”的网页感兴趣。当这个网页在Chromium中加载的时候,Chromium就会往里面注入脚本content.js。注入过程如图1所示:首先,在前面文章中提到,Browser进程在加载Extension之前,会创建一个UserScriptMaster对象。此后每当加载一个Extension,这个UserScriptMaster对象的成员函数OnExtensionLoaded都会被调用,用来收集当前正在加载的Extension的ContentScript。此后,每当Browser进程启动一个Render进程时,代表该Render进程的一个RenderProcessHostImpl对象的成员函数OnProcessLaunched都会被调用,用来通知Browser进程新的Render进程已经启动起来的。这时候这个RenderProcessHostImpl对象会到上述UserScriptMaster对象中获取当前收集到的所有ContentScript。这些ContentScript接下来会通过一个类型为ExtensionMsg_UpdateUserScript的IPC消息传递给新启动的Render进程。新启动的Render进程通过一个Dispatcher对象接收这个IPC消息,并且会将它传递过来的ContentScript保存在一个UserScriptSlave对象中。接下来,每当Render进程加载一个网页时,都会在三个时机检查是否需要在该网页中注入ContentScript。从前面Chromium扩展(Extension)机制简要介绍和学习计划一文可以知道,这三个时机分别为document_start、document_end和document_idle,分别表示网页的Document对象开始创建、结束创建以及空闲时。接下来我们以document_start这个时机为例,说明ContentScript注入到宿主网页的过程。网页的Document对象是在WebKit中创建的。WebKit为网页创建了Document对象之后,会调用Content层的一个RenderFrameImpl对象的成员函数didCreateDocumentElement,用来通知后者,它描述的网页的Document对象已经创建好了。这时候这个RenderFrameImpl对象将会调用前面提到的UserScriptSlave对象的成员函数InjectScripts,用来通知后者,现在可以将ContentScript注入当前正在加载的网页中去执行。前面提到的UserScriptSlave对象会调用另外一个WebLocalFrameImpl对象的成员函数executeScriptInIsolatedWorld,用来注入符合条件的ContentScript到当前正在加载的网页中去,并且在JS引擎的一个IsolatedWorld中执行。ContentScript在IsolatedWorld中执行,意味着它不可以访问在宿主网页中定义的JavaScript,包括不能调用在宿主网页中定义的JavaScript函数,以及访问宿主网页中定义的变量。以上就是ContentScript注入到宿主网页中执行的大概流程。接下来我们结合源代码进行详细的分析,以便对这个注入流程有更深刻的认识。我们首先分析UserScriptMaster类收集ContentScript的过程。这要从UserScriptMaster类的构造函数说起,如下所示:[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片UserScriptMaster::UserScriptMaster(Profile*profile):......,extension_registry_observer_(this){extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));registrar_.Add(this,chrome::NOTIFICATION_EXTENSIONS_READY,content::Sourceprofile(profile_));registrar_.Add(this,content::NOTIFICATION_RENDERER_PROCESS_CREATED,content::NotificationService::AllBrowserContextsAndSources());}这个函数定义在文件external/chromium_org/chrome/browser/extensions/user_script_master.cc中。UserScriptMaster类的成员变量extension_registry_observer_描述的是一个ExtensionRegistryObserver对象。这个ExtensionRegistryObserver对象接下来会注册到与参数profile描述的一个Profile对象关联的一个ExtensionRegistry对象中去,也就是注入到与当前使用的Profile关联的一个ExtensionRegistry对象中去。这个ExtensionRegistry对象的创建过程可以参考前面Chromium扩展(Extension)加载过程分析一文。与此同时,UserScriptMaster类的构造函数还会通过成员变量registrar_描述的一个NotificationRegistrar对象监控chrome::NOTIFICATION_EXTENSIONS_READY和content::NOTIFICATION_RENDERER_PROCESS_CREATED事件。其中,事件chrome::NOTIFICATION_EXTENSIONS_READY用来通知Chromium的ExtensionService已经启动了,而事件content::NOTIFICATION_RENDERER_PROCESS_CREATED用来通知有一个新的Render进程启动起来。如前面所述,当新的Render进程启动起来的时候,UserScriptMaster类会将当前加载的Extension定义的ContentScript传递给它处理。这个过程我们在后面会进行详细分析。从前面Chromium扩展(Extension)加载过程分析一文还可以知道,接下来加载的每一个Extension,都会保存在上述ExtensionRegistry对象内部的一个EnabledList中,并且都会调用注册在上述ExtensionRegistry对象中的每一个ExtensionRegistryObserver对象的成员函数OnExtensionLoaded,通知它们有一个新的Extension被加载。当UserScriptMaster类的成员变量extension_registry_observer_描述的ExtensionRegistryObserver对象的成员函数OnExtensionLoaded被调用时,它又会调用UserScriptMaster类的成员函数OnExtensionLoaded,以便UserScriptMaster类可以收集新加载的Extension定义的ContentScript,如下所示:[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片voidUserScriptMaster::OnExtensionLoaded(content::BrowserContext*browser_context,constExtension*extension){//Addanycontentscriptsinsidetheextension.extensions_info_[extension-id()]=ExtensionSet::ExtensionPathAndDefaultLocale(extension-path(),LocaleInfo::GetDefaultLocale(extension));......constUserScriptList&scripts=ContentScriptsInfo::GetContentScripts(extension);for(UserScriptList::const_iteratoriter=scripts.begin();iter!=scripts.end();++iter){user_scripts_.push_back(*iter);.....}if(extensions_service_ready_){changed_extensions_.insert(extension-id());if(script_reloader_.get()){pending_load_=true;}else{StartLoad();}}}这个函数定义在文件external/chromium_org/chrome/browser/extensions/user_script_master.cc中。参数extension描述的就是当前正在加载的Extension。UserScriptMaster类的成员函数OnExtensionLoaded首先会调用ExtensionSet类的静态成员函数ExtensionPathAndDefaultLocale将该Extension的Path和Locale信息封装在一个ExtensionPathAndDefaultLocale对象中,并且以该Extension的ID为键值,将上述ExtensionPathAndDefaultLocale对象保存在成员变量extensions_info_描述的一个std::map中。UserScriptMaster类的成员函数OnExtensionLoaded接下来将当前正在加载的Extension定义的所有ContentScript保存在成员变量user_scripts_描述的一个std::vector中。UserScriptMaster类有一个类型为bool的成员变量extensions_service_ready_。当它的值等于true的时候,表示Chromium的ExtensionService已经启动起来了。这时候extensions_service_ready_就会将当前正在加载的Extension的ID插入到UserScriptMaster类的成员变量changed_extensions_描述的一个std::set中去,表示有一个新的Extension需要处理。这里说的处理,就是将新加载的Extension定义的ContentScript的内容读取出来,并且保存在一个共享内存中。将Extension定义的ContentScript的内容读取出来,并且保存在一个共享内存中,是通过UserScriptMaster类的成员变量scrip

1 / 23
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功