Index: chrome/browser/extensions/api/messaging/extension_message_port.cc |
diff --git a/chrome/browser/extensions/api/messaging/extension_message_port.cc b/chrome/browser/extensions/api/messaging/extension_message_port.cc |
index 1e4f87f901d6234975f9d3a6dccfcf139172fd3b..a22568f956a5f380d8481f1aefd13e875d3783de 100644 |
--- a/chrome/browser/extensions/api/messaging/extension_message_port.cc |
+++ b/chrome/browser/extensions/api/messaging/extension_message_port.cc |
@@ -4,31 +4,121 @@ |
#include "chrome/browser/extensions/api/messaging/extension_message_port.h" |
+#include "base/scoped_observer.h" |
#include "chrome/browser/profiles/profile.h" |
+#include "content/public/browser/navigation_details.h" |
+#include "content/public/browser/render_frame_host.h" |
#include "content/public/browser/render_process_host.h" |
+#include "content/public/browser/web_contents.h" |
+#include "content/public/browser/web_contents_observer.h" |
#include "extensions/browser/extension_host.h" |
#include "extensions/browser/process_manager.h" |
+#include "extensions/browser/process_manager_observer.h" |
#include "extensions/common/extension_messages.h" |
#include "extensions/common/manifest_handlers/background_info.h" |
namespace extensions { |
-ExtensionMessagePort::ExtensionMessagePort(content::RenderProcessHost* process, |
- int routing_id, |
- const std::string& extension_id) |
- : process_(process), |
- routing_id_(routing_id), |
- extension_id_(extension_id), |
- background_host_ptr_(NULL) { |
+const char kReceivingEndDoesntExistError[] = |
+ "Could not establish connection. Receiving end does not exist."; |
+ |
+// Helper class to detect when frames are destroyed. |
+class ExtensionMessagePort::FrameTracker : public content::WebContentsObserver, |
+ public ProcessManagerObserver { |
+ public: |
+ explicit FrameTracker(ExtensionMessagePort* port) |
+ : pm_observer_(this), port_(port) {} |
+ ~FrameTracker() override {} |
+ |
+ void TrackExtensionProcessFrames() { |
+ pm_observer_.Add(ProcessManager::Get(port_->browser_context_)); |
+ } |
+ |
+ void TrackTabFrames(content::WebContents* tab) { |
+ Observe(tab); |
+ } |
+ |
+ private: |
+ // content::WebContentsObserver overrides: |
+ void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) |
+ override { |
+ port_->UnregisterFrame(render_frame_host); |
+ } |
+ |
+ void DidNavigateAnyFrame(content::RenderFrameHost* render_frame_host, |
+ const content::LoadCommittedDetails& details, |
+ const content::FrameNavigateParams&) override { |
+ if (!details.is_in_page) |
+ port_->UnregisterFrame(render_frame_host); |
+ } |
+ |
+ // extensions::ProcessManagerObserver overrides: |
+ void OnExtensionFrameUnregistered( |
+ const std::string& extension_id, |
+ content::RenderFrameHost* render_frame_host) override { |
+ if (extension_id == port_->extension_id_) |
+ port_->UnregisterFrame(render_frame_host); |
+ } |
+ |
+ ScopedObserver<ProcessManager, ProcessManagerObserver> pm_observer_; |
+ ExtensionMessagePort* port_; // Owns this FrameTracker. |
+ |
+ DISALLOW_COPY_AND_ASSIGN(FrameTracker); |
+}; |
+ |
+ExtensionMessagePort::ExtensionMessagePort( |
+ base::WeakPtr<MessageService> message_service, |
+ int port_id, |
+ const std::string& extension_id, |
+ content::RenderProcessHost* extension_process) |
+ : weak_message_service_(message_service), |
+ port_id_(port_id), |
+ extension_id_(extension_id), |
+ browser_context_(extension_process->GetBrowserContext()), |
+ extension_process_(extension_process), |
+ frames_(ProcessManager::Get(browser_context_)-> |
+ GetRenderFrameHostsForExtension(extension_id)), |
+ did_create_port_(false), |
+ background_host_ptr_(nullptr), |
+ frame_tracker_(new FrameTracker(this)) { |
+ frame_tracker_->TrackExtensionProcessFrames(); |
+} |
+ |
+ExtensionMessagePort::ExtensionMessagePort( |
+ base::WeakPtr<MessageService> message_service, |
+ int port_id, |
+ const std::string& extension_id, |
+ content::RenderFrameHost* rfh, |
+ bool include_child_frames) |
+ : weak_message_service_(message_service), |
+ port_id_(port_id), |
+ extension_id_(extension_id), |
+ browser_context_(rfh->GetProcess()->GetBrowserContext()), |
+ extension_process_(nullptr), |
+ did_create_port_(false), |
+ background_host_ptr_(nullptr), |
+ frame_tracker_(new FrameTracker(this)) { |
+ content::WebContents* tab = content::WebContents::FromRenderFrameHost(rfh); |
+ DCHECK(tab); |
+ frame_tracker_->TrackTabFrames(tab); |
+ if (include_child_frames) { |
+ tab->ForEachFrame(base::Bind(&ExtensionMessagePort::RegisterFrame, |
+ base::Unretained(this))); |
+ } else { |
+ RegisterFrame(rfh); |
+ } |
+} |
+ |
+ExtensionMessagePort::~ExtensionMessagePort() {} |
+ |
+bool ExtensionMessagePort::IsValidPort() { |
+ return !frames_.empty(); |
} |
void ExtensionMessagePort::DispatchOnConnect( |
- int dest_port_id, |
const std::string& channel_name, |
scoped_ptr<base::DictionaryValue> source_tab, |
int source_frame_id, |
- int target_tab_id, |
- int target_frame_id, |
int guest_process_id, |
int guest_render_frame_routing_id, |
const std::string& source_extension_id, |
@@ -44,32 +134,26 @@ void ExtensionMessagePort::DispatchOnConnect( |
info.target_id = target_extension_id; |
info.source_id = source_extension_id; |
info.source_url = source_url; |
- info.target_tab_id = target_tab_id; |
- info.target_frame_id = target_frame_id; |
info.guest_process_id = guest_process_id; |
info.guest_render_frame_routing_id = guest_render_frame_routing_id; |
- process_->Send(new ExtensionMsg_DispatchOnConnect( |
- routing_id_, dest_port_id, channel_name, source, info, tls_channel_id)); |
+ SendToPort(make_scoped_ptr(new ExtensionMsg_DispatchOnConnect( |
+ MSG_ROUTING_NONE, port_id_, channel_name, source, info, tls_channel_id))); |
} |
void ExtensionMessagePort::DispatchOnDisconnect( |
- int source_port_id, |
const std::string& error_message) { |
- process_->Send(new ExtensionMsg_DispatchOnDisconnect( |
- routing_id_, source_port_id, error_message)); |
+ SendToPort(make_scoped_ptr(new ExtensionMsg_DispatchOnDisconnect( |
+ MSG_ROUTING_NONE, port_id_, error_message))); |
} |
-void ExtensionMessagePort::DispatchOnMessage(const Message& message, |
- int target_port_id) { |
- process_->Send(new ExtensionMsg_DeliverMessage( |
- routing_id_, target_port_id, message)); |
+void ExtensionMessagePort::DispatchOnMessage(const Message& message) { |
+ SendToPort(make_scoped_ptr(new ExtensionMsg_DeliverMessage( |
+ MSG_ROUTING_NONE, port_id_, message))); |
} |
void ExtensionMessagePort::IncrementLazyKeepaliveCount() { |
- Profile* profile = |
- Profile::FromBrowserContext(process_->GetBrowserContext()); |
- extensions::ProcessManager* pm = ProcessManager::Get(profile); |
+ ProcessManager* pm = ProcessManager::Get(browser_context_); |
ExtensionHost* host = pm->GetBackgroundHostForExtension(extension_id_); |
if (host && BackgroundInfo::HasLazyBackgroundPage(host->extension())) |
pm->IncrementLazyKeepaliveCount(host->extension()); |
@@ -80,16 +164,66 @@ void ExtensionMessagePort::IncrementLazyKeepaliveCount() { |
} |
void ExtensionMessagePort::DecrementLazyKeepaliveCount() { |
- Profile* profile = |
- Profile::FromBrowserContext(process_->GetBrowserContext()); |
- extensions::ProcessManager* pm = ProcessManager::Get(profile); |
+ ProcessManager* pm = ProcessManager::Get(browser_context_); |
ExtensionHost* host = pm->GetBackgroundHostForExtension(extension_id_); |
if (host && host == background_host_ptr_) |
pm->DecrementLazyKeepaliveCount(host->extension()); |
} |
-content::RenderProcessHost* ExtensionMessagePort::GetRenderProcessHost() { |
- return process_; |
+void ExtensionMessagePort::OpenPort(int process_id, int routing_id) { |
+ DCHECK(routing_id != MSG_ROUTING_NONE || extension_process_); |
+ |
+ did_create_port_ = true; |
+} |
+ |
+void ExtensionMessagePort::ClosePort(int process_id, int routing_id) { |
+ if (routing_id == MSG_ROUTING_NONE) { |
+ // The only non-frame-specific message is the response to an unhandled |
+ // onConnect event in the extension process. |
+ DCHECK(extension_process_); |
+ frames_.clear(); |
+ CloseChannel(); |
+ return; |
+ } |
+ |
+ content::RenderFrameHost* rfh = |
+ content::RenderFrameHost::FromID(process_id, routing_id); |
+ if (rfh) |
+ UnregisterFrame(rfh); |
+} |
+ |
+void ExtensionMessagePort::CloseChannel() { |
+ std::string error_message = did_create_port_ ? std::string() : |
+ kReceivingEndDoesntExistError; |
+ if (weak_message_service_) |
+ weak_message_service_->CloseChannel(port_id_, error_message); |
+} |
+ |
+void ExtensionMessagePort::RegisterFrame(content::RenderFrameHost* rfh) { |
+ frames_.insert(rfh); |
+} |
+ |
+void ExtensionMessagePort::UnregisterFrame(content::RenderFrameHost* rfh) { |
+ if (frames_.erase(rfh) != 0 && frames_.empty()) |
+ CloseChannel(); |
+} |
+ |
+void ExtensionMessagePort::SendToPort(scoped_ptr<IPC::Message> msg) { |
+ DCHECK_GT(frames_.size(), 0UL); |
+ if (extension_process_) { |
+ // All extension frames reside in the same process, so we can just send a |
+ // single IPC message to the extension process as an optimization. |
+ // The frame tracking is then only used to make sure that the port gets |
+ // closed when all frames have closed / reloaded. |
+ msg->set_routing_id(MSG_ROUTING_CONTROL); |
+ extension_process_->Send(msg.release()); |
+ return; |
+ } |
+ for (content::RenderFrameHost* rfh : frames_) { |
+ IPC::Message* msg_copy = new IPC::Message(*msg.get()); |
+ msg_copy->set_routing_id(rfh->GetRoutingID()); |
+ rfh->Send(msg_copy); |
+ } |
} |
} // namespace extensions |