Chromium擴(kuò)展(Extension)通信機(jī)制分析_第1頁(yè)
Chromium擴(kuò)展(Extension)通信機(jī)制分析_第2頁(yè)
Chromium擴(kuò)展(Extension)通信機(jī)制分析_第3頁(yè)
Chromium擴(kuò)展(Extension)通信機(jī)制分析_第4頁(yè)
Chromium擴(kuò)展(Extension)通信機(jī)制分析_第5頁(yè)
已閱讀5頁(yè),還剩17頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

Chromium擴(kuò)展(Extension)通信機(jī)制分析Chromium的Extension由Page和ContentScript組成。如果將Extension看作是一個(gè)App,那么Page和ContentScript就是Extension的Module。既然是Module,就避免不了需要相互通信。也正是由于相互通信,使得它們形成一個(gè)完整的App。本文接下來(lái)就分析Extension的Page之間以及Page與ContentScript之間的通信機(jī)制。Chromium的Extension模塊提供了接口,讓Extension的Page與Page之間,以及Page與ContentScript之間,可以方便地通信,如圖1所示:Extension的Page之間的通信,表現(xiàn)為可以訪問(wèn)各自定義的JS變量和函數(shù)。例如,我們?cè)谇懊鍯hromium擴(kuò)展(Extension)機(jī)制簡(jiǎn)要介紹和學(xué)習(xí)計(jì)劃一文中提到的Pageactionexample,定義了一個(gè)BackgroundPage和一個(gè)PopupPage。其中,BackgroundPage包含了的一個(gè)background.js,它的內(nèi)容如下所示:[javascript]viewplaincopy在CODE上查看代碼片派生到我的代碼片chrome.tabs.onUpdated.addListener(function(tabId,changeInfo,tab){varviews=chrome.extension.getViews({type:"tab"});if(views.length>0){console.log(views[0].whoiam);}else{console.log("Notab");}});varwhoiam="background.html"它定義了一個(gè)變量whoiam,同時(shí)它又會(huì)通過(guò)Extension模塊提供的API接口chrome.extension.getViews,獲得在瀏覽器窗口的Tab中加載的所有ExtensionPage的window對(duì)象。假設(shè)此時(shí)Pageactionexample在Tab中加載了一個(gè)ExtensionPage,并且這個(gè)Page也像BackgroundPage一樣定義了變量whoiam,那么BackgroundPage就可以通過(guò)它的window對(duì)象直接訪問(wèn)它的變量whoiam。API接口chrome.extension.getViews除了可以獲得在Tab中加載的Page的window對(duì)象,還可以獲得以其它方式加載的Page的window對(duì)象。例如,在彈窗口中加載的PopupPage的window對(duì)象,以及在瀏覽器的InfoBar(信息欄)和Notification(通知面板)中加載的Page的window對(duì)象。可以通過(guò)type參數(shù)指定要獲取哪一種類型的Page的window對(duì)象。如果沒(méi)有指定,那么就會(huì)獲得所有類型的Page的window對(duì)象。注意,API接口chrome.extension.getViews獲得的是非BackgroundPage的window對(duì)象。如果需要獲得BackgroundPage的window對(duì)象,可以使用另外一個(gè)API接口chrome.extension.getBackgroundPage。例如,我們?cè)谇懊鍯hromium擴(kuò)展(Extension)機(jī)制簡(jiǎn)要介紹和學(xué)習(xí)計(jì)劃一文中提到的Pageactionexample的PopupPage,包含有一個(gè)popup.js,它的內(nèi)容如下所示:[javascript]viewplaincopy在CODE上查看代碼片派生到我的代碼片document.addEventListener('DOMContentLoaded',function(){getImageUrl(function(imageUrl,width,height){varimageResult=document.getElementById('image-result');imageResult.width=width;imageResult.height=height;imageResult.src=imageUrl;imageResult.hidden=false;console.log(chrome.extension.getBackgroundPage().whoiam);},function(errorMessage){renderStatus('Cannotdisplayimage.'+errorMessage);});});varwhoiam="popup.html"它在PopupPage中顯示圖片時(shí),就可以通過(guò)調(diào)用API接口chrome.extension.getBackgroundPage獲得BackgroundPage的window對(duì)象,然后通過(guò)這個(gè)window對(duì)象訪問(wèn)在BackgroundPage中定義的變量whoiam。從前面的定義可以知道,這個(gè)變量的值等于“background.html”。總結(jié)來(lái)說(shuō),就是Extension的Page之間,可以通過(guò)chrome.extension.getViews和chrome.extension.getBackgroundPage這兩個(gè)API接口獲得對(duì)方的window對(duì)象。有了對(duì)方的window對(duì)象之后,就可以直接進(jìn)行通信了。由于Extension的Page和ContentScript不在同一個(gè)進(jìn)程,它們的通信過(guò)程就會(huì)復(fù)雜一些??傮w來(lái)說(shuō),是通過(guò)消息進(jìn)行通信的。接下來(lái)我們以PopupPage與ContentScript的通信為例,說(shuō)明Extension的Page和ContentScript的通信過(guò)程。在前面Chromium擴(kuò)展(Extension)機(jī)制簡(jiǎn)要介紹和學(xué)習(xí)計(jì)劃一文中提到的Pageactionexample,它的PopupPage可以通過(guò)API接口chrome.tabs.sendRequest向ContentScript發(fā)送請(qǐng)求,如下所示:[javascript]viewplaincopy在CODE上查看代碼片派生到我的代碼片functiontestRequest(){chrome.tabs.getSelected(null,function(tab){chrome.tabs.sendRequest(tab.id,{counter:counter},functionhandler(response){counter=response.counter;document.querySelector('#resultsRequest').innerHTML="<fontcolor='gray'>response:"+counter+"</font>";document.querySelector('#testRequest').innerText="send"+(counter-1)+"totabpage";});});}這個(gè)請(qǐng)求會(huì)被封裝在一個(gè)類型為ExtensionHostMsg_PostMessage的IPC消息,并且發(fā)送給Browser進(jìn)程。Browser進(jìn)程會(huì)找到Pageactionexample的ContentScript的宿主網(wǎng)頁(yè)所在的Render進(jìn)程,并且將請(qǐng)求封裝成另外一個(gè)類型為ExtensionMsg_DeliverMessage的IPC消息發(fā)送給它。Render進(jìn)程收到類型為ExtensionMsg_DeliverMessage的IPC消息后,就會(huì)將封裝在里面的請(qǐng)求提取出來(lái),并且交給ContentScript處理,如下所示:[javascript]viewplaincopy在CODE上查看代碼片派生到我的代碼片chrome.extension.onRequest.addListener(function(request,sender,sendResponse){sendResponse({counter:request.counter+1});});ContentScript需要通過(guò)API接口chrome.extension.onRequest.addListener注冊(cè)一個(gè)函數(shù),用來(lái)接收來(lái)自ExtensionPage的請(qǐng)求。這個(gè)函數(shù)的第三個(gè)參數(shù)是一個(gè)Callback函數(shù)。通過(guò)這個(gè)Callback函數(shù),ContentScript可以向BackgroundPage發(fā)送Response。Extension的ContentScript同樣也可以向Extension的Page發(fā)送請(qǐng)求。不過(guò),它是通過(guò)另外一個(gè)API接口chrome.runtime.sendMessage進(jìn)行發(fā)送的。例如,上述Pageactionexample的ContentScript就是通過(guò)這個(gè)接口向BackgroundPage發(fā)送請(qǐng)求的,如下所示:[javascript]viewplaincopy在CODE上查看代碼片派生到我的代碼片functiontestRequest(){chrome.runtime.sendMessage({counter:counter},function(response){counter=response.counter;document.querySelector('#resultsRequest').innerText="response:"+counter;document.querySelector('#testRequest').innerText="send"+(counter-1)+"tobackgroundpage";});}這個(gè)請(qǐng)求同樣是先通過(guò)一個(gè)類型為ExtensionHostMsg_PostMessage的IPC消息傳遞到Browser進(jìn)程,然后再由Browser進(jìn)程通過(guò)另外一個(gè)類型為ExtensionMsg_DeliverMessage的IPC消息傳遞給Extension進(jìn)程中的BackgroundPage。BackgroundPage需要通過(guò)API接口chrome.runtime.onMessage.addListener注冊(cè)一個(gè)函數(shù),用來(lái)接收來(lái)自ContentScript的請(qǐng)求,如下所示:[javascript]viewplaincopy在CODE上查看代碼片派生到我的代碼片chrome.runtime.onMessage.addListener(function(request,sender,sendResponse){sendResponse({counter:request.counter+1});});這個(gè)函數(shù)的第三個(gè)參數(shù)同樣是一個(gè)Callback函數(shù)。通過(guò)這個(gè)Callback函數(shù),BackgroundPage可以向ContentScript發(fā)送Response??偨Y(jié)來(lái)說(shuō),就是Extension的Page可以通過(guò)API接口chrome.tabs.sendRequest和chrome.extension.onRequest.addListener與ContentScript通信,而ContentScript可以通過(guò)API接口chrome.runtime.sendMessage和chrome.runtime.onMessage.addListener與Page通信。接下來(lái),我們結(jié)合源代碼分析chrome.extension.getViews、chrome.extension.getBackgroundPage和chrome.runtime.sendMessage這三個(gè)API接口的實(shí)現(xiàn)。了解這三個(gè)API接口的實(shí)現(xiàn)之后,我們就會(huì)對(duì)上述的Extension通信機(jī)制,有更深刻的認(rèn)識(shí)。在分析上述三個(gè)API接口之前,我們首先簡(jiǎn)單介紹一下JSBinding。JSBinding類型于Java里面的JNI,用來(lái)在JS與C/C++之間建立橋梁,也就是用來(lái)將一個(gè)JS接口綁定到一個(gè)C/C++函數(shù)中去。Chromium使用的JS引擎是V8。V8引擎在創(chuàng)建完成ScriptContext之后,會(huì)向WebKit發(fā)出通知。WebKit再向Chromium的Content模塊發(fā)出通知。Content模塊又會(huì)向Extension模塊發(fā)出通知。Extension模塊獲得通知后,就會(huì)將chrome.extension.getViews、chrome.extension.getBackgroundPage和chrome.runtime.sendMessage接口綁定到Chromium內(nèi)部定義的函數(shù)中去,從而使得它們可以通過(guò)Chromium的基礎(chǔ)設(shè)施來(lái)實(shí)現(xiàn)Extension的通信機(jī)制。V8引擎的初始化發(fā)生在V8WindowShell類的成員函數(shù)initialize中,它的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片boolV8WindowShell::initialize(){TRACE_EVENT0("v8","V8WindowShell::initialize");createContext();ScriptState::Scopescope(m_scriptState.get());v8::Handle<v8::Context>context=m_scriptState->context();m_frame->loader().client()->didCreateScriptContext(context,m_world->extensionGroup(),m_world->worldId());returntrue;}這個(gè)函數(shù)定義在文件external/chromium_org/third_party/WebKit/Source/bindings/v8/V8WindowShell.cpp中。V8WindowShell類的成員函數(shù)initialize在初始化完成V8引擎之后,會(huì)通過(guò)調(diào)用成員變量m_frame指向的一個(gè)LocalFrame對(duì)象的成員函數(shù)loader獲得一個(gè)FrameLoader對(duì)象。有了這個(gè)FrameLoader對(duì)象之后,再調(diào)用它的成員函數(shù)client就可以獲得一個(gè)FrameLoaderClientImpl對(duì)象。有了這個(gè)FrameLoaderClientImpl對(duì)象,就可以調(diào)用它的成員函數(shù)didCreateScriptContext向WebKit發(fā)出通知,V8引擎的ScriptContext創(chuàng)建好了。FrameLoaderClientImpl類的成員函數(shù)didCreateScriptContext的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidFrameLoaderClientImpl::didCreateScriptContext(v8::Handle<v8::Context>context,intextensionGroup,intworldId){WebViewImpl*webview=m_webFrame->viewImpl();if(m_webFrame->client())m_webFrame->client()->didCreateScriptContext(m_webFrame,context,extensionGroup,worldId);}這個(gè)函數(shù)定義在文件external/chromium_org/third_party/WebKit/Source/web/FrameLoaderClientImpl.cpp中。FrameLoaderClientImpl類的成員變量m_webFrame指向的是一個(gè)WebLocalFrameImpl對(duì)象。FrameLoaderClientImpl類的成員函數(shù)didCreateScriptContext首先調(diào)用這個(gè)WebLocalFrameImpl對(duì)象的成員函數(shù)viewImpl獲得一個(gè)WebViewImpl對(duì)象。有了這個(gè)WebViewImpl對(duì)象之后,再調(diào)用它的成員函數(shù)client可以獲得一個(gè)RenderFrameImpl對(duì)象。這個(gè)RenderFrameImpl對(duì)象實(shí)現(xiàn)了WebViewClient接口,它是從Chromium的Content層設(shè)置進(jìn)來(lái)的,作為WebKit回調(diào)Content的接口。因此,有了這個(gè)RenderFrameImpl對(duì)象之后,F(xiàn)rameLoaderClientImpl類的成員函數(shù)didCreateScriptContext就可以調(diào)用它的成員函數(shù)didCreateScriptContext,用來(lái)通知它V8引擎的ScriptContext創(chuàng)建好了。RenderFrameImpl類的成員函數(shù)didCreateScriptContext的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidRenderFrameImpl::didCreateScriptContext(blink::WebLocalFrame*frame,v8::Handle<v8::Context>context,intextension_group,intworld_id){DCHECK(!frame_||frame_==frame);GetContentClient()->renderer()->DidCreateScriptContext(frame,context,extension_group,world_id);}這個(gè)函數(shù)定義在文件external/chromium_org/content/renderer/render_frame_impl.cc中。我們假設(shè)當(dāng)前基于Chromium實(shí)現(xiàn)的瀏覽器為Chrome。這時(shí)候RenderFrameImpl類的成員函數(shù)didCreateScriptContext調(diào)用函數(shù)GetContentClient獲得的是一個(gè)ChromeContentClient對(duì)象。有了這個(gè)ChromeContentClient對(duì)象之后,調(diào)用它的成員函數(shù)renderer可以獲得一個(gè)ChromeContentRendererClient對(duì)象。有了這個(gè)ChromeContentRendererClient對(duì)象之后,RenderFrameImpl類的成員函數(shù)didCreateScriptContext就可以調(diào)用它的成員函數(shù)DidCreateScriptContext,用來(lái)通知它V8引擎的ScriptContext創(chuàng)建好了。ChromeContentRendererClient類的成員函數(shù)DidCreateScriptContext的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidChromeContentRendererClient::DidCreateScriptContext(WebFrame*frame,v8::Handle<v8::Context>context,intextension_group,intworld_id){extension_dispatcher_->DidCreateScriptContext(frame,context,extension_group,world_id);}這個(gè)函數(shù)定義在文件external/chromium_org/chrome/renderer/chrome_content_renderer_client.cc中。ChromeContentRendererClient類的成員變量extension_dispatcher_指向的是一個(gè)Dispatcher對(duì)象。這個(gè)Dispatcher對(duì)象就是我們?cè)谇懊鍯hromium擴(kuò)展(Extension)的ContentScript加載過(guò)程分析一文中提到的那個(gè)用來(lái)接收Extension相關(guān)的IPC消息的Dispatcher對(duì)象,ChromeContentRendererClient類的成員函數(shù)DidCreateScriptContext調(diào)用所做的事情就是調(diào)用它的成員函數(shù)DidCreateScriptContext,用來(lái)通知它V8引擎的ScriptContext創(chuàng)建好了。Dispatcher類的成員函數(shù)DidCreateScriptContext的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidDispatcher::DidCreateScriptContext(WebFrame*frame,constv8::Handle<v8::Context>&v8_context,intextension_group,intworld_id){std::stringextension_id=GetExtensionID(frame,world_id);constExtension*extension=extensions_.GetByID(extension_id);Feature::Contextcontext_type=ClassifyJavaScriptContext(extension,extension_group,ScriptContext::GetDataSourceURLForFrame(frame),frame->document().securityOrigin());ScriptContext*context=delegate_->CreateScriptContext(v8_context,frame,extension,context_type).release();{scoped_ptr<ModuleSystem>module_system(newModuleSystem(context,&source_map_));context->set_module_system(module_system.Pass());}ModuleSystem*module_system=context->module_system();RegisterNativeHandlers(module_system,context);}這個(gè)函數(shù)定義在文件external/chromium_org/extensions/renderer/dispatcher.cc中。參數(shù)frame描述的是當(dāng)前加載的網(wǎng)頁(yè),另外一個(gè)參數(shù)world_id描述的是V8引擎中的一個(gè)IsolatedWorldID。從前面Chromium擴(kuò)展(Extension)的ContentScript加載過(guò)程分析一文可以知道,IsolatedWorld是用來(lái)執(zhí)行Extension的ContentScript的,并且每一個(gè)Extension在其宿主網(wǎng)頁(yè)中都有一個(gè)唯一的IsolatedWorld。這意味著根據(jù)這個(gè)IsolatedWorldID可以獲得它所對(duì)應(yīng)的Extension。這可以通過(guò)調(diào)用Dispatcher類的成員函數(shù)GetExtensionID獲得。知道了參數(shù)world_id描述的IsolatedWorld對(duì)應(yīng)的Extension之后,Dispatcher類的成員函數(shù)DidCreateScriptContext就可以為這個(gè)Extension創(chuàng)建一個(gè)ScriptContext。這個(gè)ScriptContext實(shí)際上只是對(duì)參數(shù)v8_context描述的V8ScriptContext進(jìn)行封裝。創(chuàng)建上述ScriptContext的目的是創(chuàng)建一個(gè)ModuleSystem。通過(guò)這個(gè)ModuleSystem,可以向參數(shù)world_id描述的IsolatedWorld注冊(cè)NativeHandler。NativeHandler的作用就是創(chuàng)建JSBinding。有了這些JSBinding之后,我們就可以在ContentScript中調(diào)用Extension相關(guān)的API接口了。Dispatcher類的成員函數(shù)DidCreateScriptContext最后是通過(guò)調(diào)用另外一個(gè)成員函數(shù)RegisterNativeHandlers向參數(shù)world_id描述的IsolatedWorld注冊(cè)NativeHandler的,它的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidDispatcher::RegisterNativeHandlers(ModuleSystem*module_system,ScriptContext*context){module_system->RegisterNativeHandler("messaging_natives",scoped_ptr<NativeHandler>(MessagingBindings::Get(this,context)));module_system->RegisterNativeHandler("runtime",scoped_ptr<NativeHandler>(newRuntimeCustomBindings(context)));}這個(gè)函數(shù)定義在文件external/chromium_org/extensions/renderer/dispatcher.cc中。Dispatcher類的成員函數(shù)RegisterNativeHandlers注冊(cè)了一系列的NativeHandler。每一個(gè)NativeHandler都對(duì)應(yīng)有一個(gè)名稱。這個(gè)名稱在JS中稱為Module,可以通過(guò)JS函數(shù)require進(jìn)行引用。這一點(diǎn)后面我們就會(huì)看到它的用法。這里我們只關(guān)注與前面提到的API接口chrome.extension.getViews、chrome.extension.getBackgroundPage和chrome.runtime.sendMessage相關(guān)的兩個(gè)Module:“message_natives”和“runtime”。其中,名稱為“message_natives”的Module使用的NativeHandler是一個(gè)ExtensionImpl對(duì)象。這個(gè)ExtensionImpl對(duì)象是通過(guò)調(diào)用MessagingBindings類的靜態(tài)成員函數(shù)Get創(chuàng)建的,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片ObjectBackedNativeHandler*MessagingBindings::Get(Dispatcher*dispatcher,ScriptContext*context){returnnewExtensionImpl(dispatcher,context);}這個(gè)函數(shù)定義在文件external/chromium_org/extensions/renderer/messaging_bindings.cc中。這個(gè)ExtensionImpl對(duì)象在創(chuàng)建的時(shí)候,就會(huì)為名稱為“message_natives”的Module導(dǎo)出的JS函數(shù)創(chuàng)建Binding,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片classExtensionImpl:publicObjectBackedNativeHandler{public:ExtensionImpl(Dispatcher*dispatcher,ScriptContext*context):ObjectBackedNativeHandler(context),dispatcher_(dispatcher){RouteFunction("PostMessage",base::Bind(&ExtensionImpl::PostMessage,base::Unretained(this)));}};這個(gè)類定義在文件external/chromium_org/extensions/renderer/messaging_bindings.cc中。從這里可以看到,名稱為“message_natives”的Module導(dǎo)出了一個(gè)名稱為“PostMessage”的JS函數(shù)。這個(gè)JS函數(shù)綁定了ExtensionImpl類的成員函數(shù)PostMessage。這意味著以后我們?cè)贘S中調(diào)用了messaging_natives.PostMessage函數(shù)時(shí),ExtensionImpl類的成員函數(shù)PostMessage就會(huì)被調(diào)用?;氐紻ispatcher類的成員函數(shù)RegisterNativeHandlers中,它注冊(cè)的另外一個(gè)名稱為“runtime”的Module使用的NativeHandler是一個(gè)RuntimeCustomBindings對(duì)象。這個(gè)RuntimeCustomBindings對(duì)象在創(chuàng)建的過(guò)程中,就會(huì)為名稱為“runtime”的Module導(dǎo)出的JS函數(shù)創(chuàng)建Binding,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片RuntimeCustomBindings::RuntimeCustomBindings(ScriptContext*context):ObjectBackedNativeHandler(context){RouteFunction("GetExtensionViews",base::Bind(&RuntimeCustomBindings::GetExtensionViews,base::Unretained(this)));}這個(gè)類定義在文件external/chromium_org/extensions/renderer/runtime_custom_bindings.cc中。這里可以看到,名稱為“runtime”的Module導(dǎo)出了一個(gè)名稱為“GetExtensionViews”的JS函數(shù)。這個(gè)JS函數(shù)綁定了RuntimeCustomBindings類的成員函數(shù)GetExtensionViews。這意味著以后我們?cè)贘S中調(diào)用了runtime.GetExtensionViews函數(shù)時(shí),RuntimeCustomBindings類的成員函數(shù)GetExtensionViews就會(huì)被調(diào)用。有了以上JSBinding相關(guān)的背景知識(shí)之后,接下來(lái)我們就開(kāi)始分析Chromium的Extension提供的API接口chrome.extension.getViews、chrome.extension.getBackgroundPage和chrome.runtime.sendMessage的實(shí)現(xiàn)了。我們首先分析chrome.extension.getViews和chrome.extension.getBackgroundPage這兩個(gè)API接口的實(shí)現(xiàn),如下所示:[javascript]viewplaincopy在CODE上查看代碼片派生到我的代碼片varbinding=require('binding').Binding.create('extension');varruntimeNatives=requireNative('runtime');varGetExtensionViews=runtimeNatives.GetExtensionViews;binding.registerCustomHook(function(bindingsAPI,extensionId){varapiFunctions=bindingsAPI.apiFunctions;apiFunctions.setHandleRequest('getViews',function(properties){varwindowId=WINDOW_ID_NONE;vartype='ALL';if(properties){if(properties.type!=null){type=properties.type;}if(properties.windowId!=null){windowId=properties.windowId;}}returnGetExtensionViews(windowId,type);});apiFunctions.setHandleRequest('getBackgroundPage',function(){returnGetExtensionViews(-1,'BACKGROUND')[0]||null;});});這兩個(gè)JS接口定義在文件external/chromium_org/extensions/renderer/resources/extension_custom_bindings.js中。從這里可以看到,chrome.extension.getViews和chrome.extension.getBackgroundPage這兩個(gè)API接口都是通過(guò)調(diào)用名稱為"runtime"的Module導(dǎo)出的函數(shù)GetExtensionViews(即runtime.GetExtensionViews)實(shí)現(xiàn)的。不過(guò),后者在調(diào)用函數(shù)runtime.GetExtensionViews時(shí),兩個(gè)參數(shù)被固定為-1和"BACKGROUND",表示要獲取的是BackgroundPage的window對(duì)象。

從前面的分析可以知道,函數(shù)runtime.GetExtensionViews綁定到了RuntimeCustomBindings類的成員函數(shù)GetExtensionViews。這意味著,當(dāng)我們?cè)贘S中調(diào)用chrome.extension.getViews和chrome.extension.getBackgroundPage這兩個(gè)API接口時(shí),最后會(huì)調(diào)用到C++層的RuntimeCustomBindings類的成員函數(shù)GetExtensionViews。它的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidRuntimeCustomBindings::GetExtensionViews(constv8::FunctionCallbackInfo<v8::Value>&args){if(args.Length()!=2)return;if(!args[0]->IsInt32()||!args[1]->IsString())return;//|browser_window_id|==extension_misc::kUnknownWindowIdmeansgetting//browser_window_id=args[0]->Int32Value();std::stringview_type_string=*v8::String::Utf8Value(args[1]->ToString());StringToUpperASCII(&view_type_string);//|view_type|==VIEW_TYPE_INVALIDmeansgettinganytypeof//views.ViewTypeview_type=VIEW_TYPE_INVALID;if(view_type_string==kViewTypeBackgroundPage){view_type=VIEW_TYPE_EXTENSION_BACKGROUND_PAGE;}elseif(view_type_string==kViewTypeInfobar){view_type=VIEW_TYPE_EXTENSION_INFOBAR;}elseif(view_type_string==kViewTypeTabContents){view_type=VIEW_TYPE_TAB_CONTENTS;}elseif(view_type_string==kViewTypePopup){view_type=VIEW_TYPE_EXTENSION_POPUP;}elseif(view_type_string==kViewTypeExtensionDialog){view_type=VIEW_TYPE_EXTENSION_DIALOG;}elseif(view_type_string==kViewTypeAppWindow){view_type=VIEW_TYPE_APP_WINDOW;}elseif(view_type_string==kViewTypePanel){view_type=VIEW_TYPE_PANEL;}elseif(view_type_string!=kViewTypeAll){return;}std::stringextension_id=context()->GetExtensionID();if(extension_id.empty())return;std::vector<content::RenderView*>views=ExtensionHelper::GetExtensionViews(extension_id,browser_window_id,view_type);v8::Local<v8::Array>v8_views=v8::Array::New(args.GetIsolate());intv8_index=0;for(size_ti=0;i<views.size();++i){v8::Local<v8::Context>context=views[i]->GetWebView()->mainFrame()->mainWorldScriptContext();if(!context.IsEpty()){v8::Local<v8::Value>window=context->Global();DCHECK(!window.IsEmpty());v8_views->Set(v8::Integer::New(args.GetIsolate(),v8_index++),window);}}args.GetReturnValue().Set(v8_views);}這個(gè)函數(shù)定義在文件external/chromium_org/extensions/renderer/runtime_custom_bindings.cc中。RuntimeCustomBindings類的成員函數(shù)GetExtensionViews首先將從JS傳遞過(guò)來(lái)的參數(shù)(PageWindowID和PageType)提取出來(lái),并且獲得當(dāng)前正在調(diào)用的Extension的ID,然后就調(diào)用ExtensionHelper類的靜態(tài)成員函數(shù)GetExtensionViews獲得指定的Page所加載在的RenderView。在Render進(jìn)程中,每一個(gè)網(wǎng)頁(yè)都是加載在一個(gè)RenderView中的。有了RenderView之后,就可以獲得它在WebKit層為網(wǎng)頁(yè)創(chuàng)建的V8ScriptContext。有了V8ScriptContext之后,就可以獲得網(wǎng)頁(yè)的window對(duì)象了。這些window對(duì)象最后會(huì)返回到JS層中去給調(diào)用者。接下來(lái),我們繼續(xù)分析ExtensionHelper類的靜態(tài)成員函數(shù)GetExtensionViews的實(shí)現(xiàn),以便了解ExtensionPage對(duì)應(yīng)的RenderView的獲取過(guò)程,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片std::vector<content::RenderView*>ExtensionHelper::GetExtensionViews(conststd::string&extension_id,intbrowser_window_id,ViewTypeview_type){ViewAccumulatoraccumulator(extension_id,browser_window_id,view_type);content::RenderView::ForEach(&accumulator);returnaccumulator.views();}這個(gè)函數(shù)定義在文件external/chromium_org/extensions/renderer/extension_helper.cc中。ExtensionHelper類的靜態(tài)成員函數(shù)GetExtensionViews通過(guò)調(diào)用RenderView類的靜態(tài)成員函數(shù)ForEach遍歷在當(dāng)前Render進(jìn)程創(chuàng)建的所有RenderView,并且通過(guò)一個(gè)ViewAccumulator對(duì)象挑選出那些符合條件的RenderView返回給調(diào)用者。RenderView類的靜態(tài)成員函數(shù)ForEach的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidRenderView::ForEach(RenderViewVisitor*visitor){ViewMap*views=g_view_map.Pointer();for(ViewMap::iteratorit=views->begin();it!=views->end();++it){if(!visitor->Visit(it->second))return;}}這個(gè)函數(shù)定義在文件external/chromium_org/content/renderer/render_view_impl.cc中。RenderView類的靜態(tài)成員函數(shù)ForEach通過(guò)遍歷全局變量g_view_map描述的一個(gè)Map可以獲得當(dāng)前Render進(jìn)程創(chuàng)建的所有RenderView。這些RenderView將會(huì)進(jìn)一步交給參數(shù)visitor描述的一個(gè)ViewAccumulator對(duì)象進(jìn)行處理。從前面Chromium網(wǎng)頁(yè)FrameTree創(chuàng)建過(guò)程分析一文可以知道,Render進(jìn)程為網(wǎng)頁(yè)創(chuàng)建的RenderView實(shí)際上是一個(gè)RenderViewImpl對(duì)象。每一個(gè)RenderViewImpl對(duì)象在創(chuàng)建完成后,它們的成員函數(shù)Initialize都會(huì)被調(diào)用,用來(lái)執(zhí)行初始化工作。在初始化的過(guò)程,RenderViewImpl對(duì)象就會(huì)將自己保存在上述全局變量g_view_map描述的一個(gè)Map中,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidRenderViewImpl::Initialize(RenderViewImplParams*params){g_view_map.Get().insert(std::make_pair(webview(),this));}這個(gè)函數(shù)定義在文件external/chromium_org/content/renderer/render_view_impl.cc中。因此,前面分析的RenderView類的靜態(tài)成員函數(shù)ForEach,可以通過(guò)遍歷全局變量g_view_map描述的Map獲得當(dāng)前Render進(jìn)程創(chuàng)建的所有RenderView。這樣,我們就分析完成了chrome.extension.getViews和chrome.extension.getBackgroundPage這兩個(gè)API接口的實(shí)現(xiàn)。接下來(lái),我們繼續(xù)分析chrome.runtime.sendMessage這個(gè)API接口的實(shí)現(xiàn),如下所示:[javascript]viewplaincopy在CODE上查看代碼片派生到我的代碼片varmessaging=require('messaging');binding.registerCustomHook(function(binding,id,contextType){apiFunctions.setHandleRequest('sendMessage',function(targetId,message,options,responseCallback){varconnectOptions={name:messaging.kMessageChannel};forEach(options,function(k,v){connectOptions[k]=v;});varport=runtime.connect(targetId||runtime.id,connectOptions);messaging.sendMessageImpl(port,message,responseCallback);});});這個(gè)JS接口定義在文件external/chromium_org/extensions/renderer/resources/runtime_custom_bindings.js中。從這里可以看到,chrome.runtime.sendMessage這個(gè)API接口是通過(guò)調(diào)用名稱為"messaging"的Module導(dǎo)出的函數(shù)sendMessageImpl(即messaging.sendMessageImpl)實(shí)現(xiàn)的。在調(diào)用函數(shù)messaging.sendMessageImpl的時(shí)候,需要指定的一個(gè)Port。在Extension中,所有的消息都是通過(guò)通道進(jìn)行傳輸?shù)摹_@個(gè)通道就稱為Port。我們可以通過(guò)調(diào)用另外一個(gè)API接口runtime.connect獲得一個(gè)連接到目標(biāo)通信對(duì)象的Port。有了這個(gè)Port之后,就可以向目標(biāo)通信對(duì)象發(fā)送消息了。目標(biāo)通信對(duì)象可以通過(guò)參數(shù)targetId描述。如果沒(méi)有指定targetId,則使用默認(rèn)的Port進(jìn)行發(fā)送消息。這個(gè)默認(rèn)的Port由runtime.id描述。函數(shù)messaging.sendMessageImpl的實(shí)現(xiàn)如下所示:[javascript]viewplaincopy在CODE上查看代碼片派生到我的代碼片functionsendMessageImpl(port,request,responseCallback){if(!=kNativeMessageChannel)port.postMessage(request);functionmessageListener(response){try{responseCallback(response);}finally{port.disconnect();}}port.onMessage.addListener(messageListener);};這個(gè)函數(shù)定義在文件external/chromium_org/extensions/renderer/resources/messaging.js中。從前面的調(diào)用過(guò)程可以知道,參數(shù)port描述的Port的名稱被設(shè)置為kMessageChannel,它的值不等于kNativeMessageChannel。在這種情況下,函數(shù)messaging.sendMessageImpl將會(huì)調(diào)用參數(shù)port描述的Port的成員函數(shù)postMessage發(fā)送參數(shù)request描述的消息給目標(biāo)通信對(duì)象,并且它會(huì)將參數(shù)responseCallback描述的一個(gè)Callback封裝在一個(gè)Listener中。當(dāng)目標(biāo)通信對(duì)象處理完成參數(shù)request描述的消息進(jìn)行Reponse時(shí),上述Listener就會(huì)調(diào)用它內(nèi)部封裝的Callback,這樣消息的發(fā)送方就可以得到接收方的回復(fù)了。參數(shù)port描述的Port的成員函數(shù)postMessage的實(shí)現(xiàn)如下所示:[javascript]viewplaincopy在CODE上查看代碼片派生到我的代碼片varmessagingNatives=requireNative('messaging_natives');PortItotype.postMessage=function(msg){messagingNatives.PostMessage(this.portId_,msg);};這個(gè)函數(shù)定義在文件external/chromium_org/extensions/renderer/resources/messaging.js中。從這里可以看到,Port類的成員函數(shù)postMessage是通過(guò)調(diào)用名稱為“messaging_natives”的Module導(dǎo)出的函數(shù)PostMessage(即messaging_natives.PostMessage)實(shí)現(xiàn)的。從前面的分析可以知道,函數(shù)messaging_natives.PostMessage綁定到了ExtensionImpl類的成員函數(shù)PostMessage。這意味中,當(dāng)我們?cè)贘S中調(diào)用chrome.runtime.sendMessage這個(gè)API接口時(shí),最后會(huì)調(diào)用到C++層的ExtensionImpl類的成員函數(shù)PostMessage。它的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片classExtensionImpl:publicObjectBackedNativeHandler{private://Sendsamessagealongthegivenchannel.voidPostMessage(constv8::FunctionCallbackInfo<v8::Value>&args){content::RenderView*renderview=context()->GetRenderView();intport_id=args[0]->Int32Value();renderview->Send(newExtensionHostMsg_PostMessage(renderview->GetRoutingID(),port_id,Message(*v8::String::Utf8Value(args[1]),blink::WebUserGestureIndicator::isProcessingUserGesture())));}};這個(gè)函數(shù)定義在文件external/chromium_org/extensions/renderer/messaging_bindings.cc中。ExtensionImpl類的成員函數(shù)PostMessage首先獲得一個(gè)RenderView。這個(gè)RenderView描述的是消息發(fā)送方所屬的網(wǎng)頁(yè)。獲得這個(gè)RenderView的目的,是為了調(diào)用它的成員函數(shù)Send向Browser進(jìn)程發(fā)送一個(gè)類型為ExtensionHostMsg_PostMessage的IPC消息。這個(gè)IPC消息封裝了JS層所要發(fā)送的消息。Browser進(jìn)程通過(guò)ChromeExtensionWebContentsObserver類的成員函數(shù)OnMessageReceived接收類型為ExtensionHostMsg_PostMessage的IPC消息,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片boolChromeExtensionWebContentsObserver::OnMessageReceived(constIPC::Message&message){boolhandled=true;IPC_BEGIN_MESSAGE_MAP(ChromeExtensionWebContentsObserver,message)IPC_MESSAGE_HANDLER(ExtensionHostMsg_PostMessage,OnPostMessage)IPC_MESSAGE_UNHANDLED(handled=false)IPC_END_MESSAGE_MAP()returnhandled;}這個(gè)函數(shù)定義在文件external/chromium_org/chrome/browser/extensions/chrome_extension_web_contents_observer.cc中。從這里可以看到,ChromeExtensionWebContentsObserver類的成員函數(shù)OnMessageReceived將類型為ExtensionHostMsg_PostMessage的IPC消息分發(fā)給另外一個(gè)成員函數(shù)OnPostMessage處理,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidChromeExtensionWebContentsObserver::OnPostMessage(intport_id,constMessage&message){MessageService*message_service=MessageService::Get(browser_context());if(message_service){message_service->PostMessage(port_id,message);}}這個(gè)函數(shù)定義在文件external/chromium_org/chrome/browser/extensions/chrome_extension_web_contents_observer.cc中。ChromeExtensionWebContentsObserver類的成員函數(shù)OnPostMessage首先通過(guò)MessageService類的靜態(tài)成員函數(shù)Get獲得一個(gè)MessageService對(duì)象。這個(gè)MessageService對(duì)象負(fù)責(zé)管理在當(dāng)前Render進(jìn)程創(chuàng)建的所有Port。因此,有了這個(gè)MessageService對(duì)象之后,就可以調(diào)用它的成員函數(shù)PostMessage將參數(shù)message描述的消息分發(fā)給參數(shù)port_id描述的Port處理,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidMessageService::PostMessage(intsource_port_id,constMessage&message){intchannel_id=GET_CHANNEL_ID(source_port_id);MessageChannelMap::iteratoriter=channels_.find(channel_id);DispatchMessage(source_port_id,iter->second,message);}這個(gè)函數(shù)定義在external/chromium_org/chrome/browser/extensions/api/messaging/message_service.cc中。MessageService類的成員函數(shù)PostMessage首先根據(jù)參數(shù)source_port_id獲得一個(gè)ChannelID。有了這個(gè)ChannelID之后,就可以在成員變量channels_描述的一個(gè)Map中獲得一個(gè)對(duì)應(yīng)的MessageChannel對(duì)象。這個(gè)MessageChannel描述的就是一個(gè)Port。因此,有了這個(gè)MessageChannel對(duì)象之后,MessageService類的成員函數(shù)PostMessage就可以調(diào)用另外一個(gè)成員函數(shù)DispatchMessage將參數(shù)message描述的消息分發(fā)給它處理,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidMessageService::DispatchMessage(intsource_port_id,MessageChannel*channel,constMessage&message){//FigureoutwhichporttheIDdest_port_id=GET_OPPOSITE_PORT_ID(source_port_id);MessagePort*port=IS_OPENER_PORT_ID(dest_port_id)?channel->opener.get():channel->receiver.get();port->DispatchOnMessage(message,dest_port_id);}這個(gè)函數(shù)定義在external/chromium_org/chrome/browser/extensions/api/messaging/message_service.cc中。MessageService類的成員函數(shù)DispatchMessage首先根據(jù)參數(shù)source_port_id獲得目標(biāo)通信對(duì)象用來(lái)接收消息的Port的ID。這個(gè)ID就稱為DestPortID。有了這個(gè)DestPortID之后,就可以通過(guò)參數(shù)channel描述的MessageChannel對(duì)象獲得一個(gè)MessagePort對(duì)象。通過(guò)調(diào)用這個(gè)MessagePort對(duì)象的成員函數(shù)DispatchOnMessage,就可

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論