diff options
author | rob <rob@robwu.nl> | 2016-01-06 13:22:09 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-01-06 21:22:58 +0000 |
commit | 3e2a0730e0dfb9c4dcbce45eea275270db900e90 (patch) | |
tree | bb5daf2237e4791771a94c2341d8764a7fe7d7b7 /chrome/browser/extensions/api/messaging | |
parent | 8bdc571018faac493cb358ea1f8b7314b9178aa4 (diff) | |
download | chromium_src-3e2a0730e0dfb9c4dcbce45eea275270db900e90.zip chromium_src-3e2a0730e0dfb9c4dcbce45eea275270db900e90.tar.gz chromium_src-3e2a0730e0dfb9c4dcbce45eea275270db900e90.tar.bz2 |
Use FrameTreeNode ID as frameId in extension APIs
Use FrameTreeNode IDs instead of RenderFrame routing IDs as "frame id"
in the extension APIs (webNavigation, webRequest, extension messaging)
because FTN IDs are globally unique, and RFH IDs are not.
This extends the public content API with:
- RenderFrameHost::FromFrameTreeNodeId
- RenderFrameHost::GetFrameTreeNodeId
- WebContents::FindFrameByFrameTreeNodeId
The extension APIs are modified as follows:
- webRequest:
* Frame IDs may be unavailable after frame removal (crbug.com/572930).
* Blocking webRequest handlers may be slower than before due to an
extra IO->UI->IO hop to determine the frameId (but this happens only
once per frame load).
- webNavigation:
* processId is no longer required in chrome.webNavigation.getFrame,
but marked as optional (deprecated) to make sure that the API is
backwards-compatible.
* frameId is constant across navigations.
- Extension messaging (chrome.runtime.connect and friends):
* Small change for extension developers in the following scenario:
1. Open port to tab.
2. Accept the port by listening to onConnect *in a child frame*.
3. Unload the document in the frame.
Old behavior: onDisconnect was not triggered in the background until
the tab was reloaded or closed.
New behavior: onDisconnect is called.
* Extension messaging works correctly in out-of-process frames.
* Move port lifetime management from renderer to browser.
* Tie port lifetime to frames instead of processes.
* Only notify frames in renderers if they have accepted the port.
* Remove obsolete work-around for crbug.com/520303.
* Unify open/close/postMessage logic in ChromeExtensionMessageFilter
(crbug.com/394383#c7)
- The extension frameId logic is no longer scattered over several files,
but resides in a single class (ExtensionApiFrameIdMap).
- IDs are now guaranteed to be -1 or higher (before this, -2 was
occasionally seen).
Depends on https://codereview.chromium.org/1413853005/
BUG=432875
TEST=browser_tests --gtest_filter=\
ExtensionWebRequestApiTest.*:\
WebNavigationApiTest.*:\
ExtensionApiTest.Messaging*:\
ExternallyConnectableMessagingTest.*:\
ExternallyConnectableMessagingWithTlsChannelIdTest.*
Review URL: https://codereview.chromium.org/1413543005
Cr-Commit-Position: refs/heads/master@{#367914}
Diffstat (limited to 'chrome/browser/extensions/api/messaging')
6 files changed, 414 insertions, 238 deletions
diff --git a/chrome/browser/extensions/api/messaging/extension_message_port.cc b/chrome/browser/extensions/api/messaging/extension_message_port.cc index 1e4f87f..a22568f9 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 diff --git a/chrome/browser/extensions/api/messaging/extension_message_port.h b/chrome/browser/extensions/api/messaging/extension_message_port.h index 79b8003..f950f64 100644 --- a/chrome/browser/extensions/api/messaging/extension_message_port.h +++ b/chrome/browser/extensions/api/messaging/extension_message_port.h @@ -5,46 +5,101 @@ #ifndef CHROME_BROWSER_EXTENSIONS_API_MESSAGING_EXTENSION_MESSAGE_PORT_H_ #define CHROME_BROWSER_EXTENSIONS_API_MESSAGING_EXTENSION_MESSAGE_PORT_H_ +#include "base/macros.h" #include "chrome/browser/extensions/api/messaging/message_service.h" class GURL; namespace content { +class BrowserContext; +class RenderFrameHost; class RenderProcessHost; } // namespace content +namespace IPC { +class Message; +} // namespace IPC + namespace extensions { // A port that manages communication with an extension. +// The port's lifetime will end when either all receivers close the port, or +// when the opener / receiver explicitly closes the channel. class ExtensionMessagePort : public MessageService::MessagePort { public: - ExtensionMessagePort(content::RenderProcessHost* process, - int routing_id, - const std::string& extension_id); - void DispatchOnConnect(int dest_port_id, - const std::string& channel_name, + // Create a port that is tied to frame(s) in a single tab. + ExtensionMessagePort(base::WeakPtr<MessageService> message_service, + int port_id, + const std::string& extension_id, + content::RenderFrameHost* rfh, + bool include_child_frames); + // Create a port that is tied to all frames of an extension, possibly spanning + // multiple tabs, including the invisible background page, popups, etc. + ExtensionMessagePort(base::WeakPtr<MessageService> message_service, + int port_id, + const std::string& extension_id, + content::RenderProcessHost* extension_process); + ~ExtensionMessagePort() override; + + bool IsValidPort() override; + + // MessageService::MessagePort: + void DispatchOnConnect(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, const std::string& target_extension_id, const GURL& source_url, const std::string& tls_channel_id) override; - void DispatchOnDisconnect(int source_port_id, - const std::string& error_message) override; - void DispatchOnMessage(const Message& message, int target_port_id) override; + void DispatchOnDisconnect(const std::string& error_message) override; + void DispatchOnMessage(const Message& message) override; void IncrementLazyKeepaliveCount() override; void DecrementLazyKeepaliveCount() override; - content::RenderProcessHost* GetRenderProcessHost() override; + void OpenPort(int process_id, int routing_id) override; + void ClosePort(int process_id, int routing_id) override; private: - content::RenderProcessHost* process_; - int routing_id_; + class FrameTracker; + + // Registers a frame as a receiver / sender. + void RegisterFrame(content::RenderFrameHost* rfh); + + // Unregisters a frame as a receiver / sender. When there are no registered + // frames any more, the port closes via CloseChannel(). + void UnregisterFrame(content::RenderFrameHost* rfh); + + // Immediately close the port and its associated channel. + void CloseChannel(); + + // Send a IPC message to the renderer for all registered frames. + void SendToPort(scoped_ptr<IPC::Message> msg); + + base::WeakPtr<MessageService> weak_message_service_; + + int port_id_; std::string extension_id_; - void* background_host_ptr_; // used in IncrementLazyKeepaliveCount + content::BrowserContext* browser_context_; + // Only for receivers in an extension process. + content::RenderProcessHost* extension_process_; + + // When the port is used as a sender, this set contains only one element. + // If used as a receiver, it may contain any number of frames. + // This set is populated before the first message is sent to the destination, + // and shrinks over time when the port is rejected by the recipient frame, or + // when the frame is removed or unloaded. + std::set<content::RenderFrameHost*> frames_; + + // Whether the renderer acknowledged creation of the port. This is used to + // distinguish abnormal port closure (e.g. no receivers) from explicit port + // closure (e.g. by the port.disconnect() JavaScript method in the renderer). + bool did_create_port_; + + ExtensionHost* background_host_ptr_; // used in IncrementLazyKeepaliveCount + scoped_ptr<FrameTracker> frame_tracker_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionMessagePort); }; } // namespace extensions diff --git a/chrome/browser/extensions/api/messaging/message_service.cc b/chrome/browser/extensions/api/messaging/message_service.cc index 743ba0c..e936b7d 100644 --- a/chrome/browser/extensions/api/messaging/message_service.cc +++ b/chrome/browser/extensions/api/messaging/message_service.cc @@ -18,7 +18,6 @@ #include "base/prefs/pref_service.h" #include "base/stl_util.h" #include "build/build_config.h" -#include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/api/messaging/extension_message_port.h" #include "chrome/browser/extensions/api/messaging/incognito_connectability.h" #include "chrome/browser/extensions/api/messaging/native_message_port.h" @@ -30,7 +29,6 @@ #include "chrome/browser/tab_contents/tab_util.h" #include "components/guest_view/common/guest_view_constants.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/notification_service.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" @@ -40,6 +38,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/common/child_process_host.h" #include "extensions/browser/event_router.h" +#include "extensions/browser/extension_api_frame_id_map.h" #include "extensions/browser/extension_host.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" @@ -134,10 +133,9 @@ struct MessageService::MessageChannel { struct MessageService::OpenChannelParams { int source_process_id; + int source_routing_id; scoped_ptr<base::DictionaryValue> source_tab; int source_frame_id; - int target_tab_id; - int target_frame_id; scoped_ptr<MessagePort> receiver; int receiver_port_id; std::string source_extension_id; @@ -150,10 +148,9 @@ struct MessageService::OpenChannelParams { // Takes ownership of receiver. OpenChannelParams(int source_process_id, + int source_routing_id, scoped_ptr<base::DictionaryValue> source_tab, int source_frame_id, - int target_tab_id, - int target_frame_id, MessagePort* receiver, int receiver_port_id, const std::string& source_extension_id, @@ -163,9 +160,8 @@ struct MessageService::OpenChannelParams { bool include_tls_channel_id, bool include_guest_process_info) : source_process_id(source_process_id), + source_routing_id(source_routing_id), source_frame_id(source_frame_id), - target_tab_id(target_tab_id), - target_frame_id(target_frame_id), receiver(receiver), receiver_port_id(receiver_port_id), source_extension_id(source_extension_id), @@ -197,11 +193,6 @@ static content::RenderProcessHost* GetExtensionProcess( } // namespace -content::RenderProcessHost* - MessageService::MessagePort::GetRenderProcessHost() { - return NULL; -} - // static void MessageService::AllocatePortIdPair(int* port1, int* port2) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -229,11 +220,6 @@ MessageService::MessageService(BrowserContext* context) LazyBackgroundTaskQueue::Get(context)), weak_factory_(this) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - - registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, - content::NotificationService::AllBrowserContextsAndSources()); - registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, - content::NotificationService::AllBrowserContextsAndSources()); } MessageService::~MessageService() { @@ -266,11 +252,11 @@ void MessageService::OpenChannelToExtension( bool include_tls_channel_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - content::RenderProcessHost* source = - content::RenderProcessHost::FromID(source_process_id); + content::RenderFrameHost* source = + content::RenderFrameHost::FromID(source_process_id, source_routing_id); if (!source) return; - BrowserContext* context = source->GetBrowserContext(); + BrowserContext* context = source->GetProcess()->GetBrowserContext(); ExtensionRegistry* registry = ExtensionRegistry::Get(context); const Extension* target_extension = @@ -341,9 +327,8 @@ void MessageService::OpenChannelToExtension( content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(source_process_id, source_routing_id); - // Main frame's frameId is 0. if (rfh) - source_frame_id = !rfh->GetParent() ? 0 : source_routing_id; + source_frame_id = ExtensionApiFrameIdMap::GetFrameId(rfh); } else { // Check to see if it was a WebView making the request. // Sending messages from WebViews to extensions breaks webview isolation, @@ -352,20 +337,13 @@ void MessageService::OpenChannelToExtension( if (is_web_view && extensions::Manifest::IsComponentLocation( target_extension->location())) { include_guest_process_info = true; - auto* rfh = content::RenderFrameHost::FromID(source_process_id, - source_routing_id); - // Include |source_frame_id| so that we can retrieve the guest's frame - // routing id in OpenChannelImpl. - if (rfh) - source_frame_id = source_routing_id; } } scoped_ptr<OpenChannelParams> params(new OpenChannelParams( - source_process_id, std::move(source_tab), source_frame_id, -1, - -1, // no target_tab_id/target_frame_id for connections to extensions - nullptr, receiver_port_id, source_extension_id, target_extension_id, - source_url, channel_name, include_tls_channel_id, + source_process_id, source_routing_id, std::move(source_tab), + source_frame_id, nullptr, receiver_port_id, source_extension_id, + target_extension_id, source_url, channel_name, include_tls_channel_id, include_guest_process_info)); pending_incognito_channels_[GET_CHANNEL_ID(params->receiver_port_id)] = @@ -423,13 +401,14 @@ void MessageService::OpenChannelToNativeApp( const std::string& native_app_name) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - content::RenderProcessHost* source = - content::RenderProcessHost::FromID(source_process_id); + content::RenderFrameHost* source = + content::RenderFrameHost::FromID(source_process_id, source_routing_id); if (!source) return; #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) - Profile* profile = Profile::FromBrowserContext(source->GetBrowserContext()); + Profile* profile = + Profile::FromBrowserContext(source->GetProcess()->GetBrowserContext()); ExtensionService* extension_service = ExtensionSystem::Get(profile)->extension_service(); bool has_permission = false; @@ -457,14 +436,13 @@ void MessageService::OpenChannelToNativeApp( } scoped_ptr<MessageChannel> channel(new MessageChannel()); - channel->opener.reset(new ExtensionMessagePort(source, MSG_ROUTING_CONTROL, - source_extension_id)); + channel->opener.reset( + new ExtensionMessagePort(weak_factory_.GetWeakPtr(), + GET_OPPOSITE_PORT_ID(receiver_port_id), + source_extension_id, source, false)); // Get handle of the native view and pass it to the native messaging host. - content::RenderFrameHost* render_frame_host = - content::RenderFrameHost::FromID(source_process_id, source_routing_id); - gfx::NativeView native_view = - render_frame_host ? render_frame_host->GetNativeView() : nullptr; + gfx::NativeView native_view = source ? source->GetNativeView() : nullptr; std::string error = kReceivingEndDoesntExistError; scoped_ptr<NativeMessageHost> native_host = NativeMessageHost::Create( @@ -496,18 +474,21 @@ void MessageService::OpenChannelToNativeApp( } void MessageService::OpenChannelToTab(int source_process_id, + int source_routing_id, int receiver_port_id, int tab_id, int frame_id, const std::string& extension_id, const std::string& channel_name) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK_GE(frame_id, -1); - content::RenderProcessHost* source = - content::RenderProcessHost::FromID(source_process_id); + content::RenderFrameHost* source = + content::RenderFrameHost::FromID(source_process_id, source_routing_id); if (!source) return; - Profile* profile = Profile::FromBrowserContext(source->GetBrowserContext()); + Profile* profile = + Profile::FromBrowserContext(source->GetProcess()->GetBrowserContext()); WebContents* contents = NULL; scoped_ptr<MessagePort> receiver; @@ -520,30 +501,21 @@ void MessageService::OpenChannelToTab(int source_process_id, return; } - int receiver_routing_id; - if (frame_id > 0) { - // Positive frame ID is child frame. - int receiver_process_id = contents->GetRenderProcessHost()->GetID(); - if (!content::RenderFrameHost::FromID(receiver_process_id, frame_id)) { - // Frame does not exist. - DispatchOnDisconnect( - source, receiver_port_id, kReceivingEndDoesntExistError); - return; - } - receiver_routing_id = frame_id; - } else if (frame_id == 0) { - // Frame ID 0 is main frame. - receiver_routing_id = contents->GetMainFrame()->GetRoutingID(); - } else { - DCHECK_EQ(-1, frame_id); - // If the frame ID is not set (i.e. -1), then the channel has to be opened - // in every frame. - // TODO(robwu): Update logic so that frames that are not hosted in the main - // frame's process can also receive the port. - receiver_routing_id = MSG_ROUTING_CONTROL; + // Frame ID -1 is every frame in the tab. + bool include_child_frames = frame_id == -1; + content::RenderFrameHost* receiver_rfh = + include_child_frames + ? contents->GetMainFrame() + : ExtensionApiFrameIdMap::GetRenderFrameHostById(contents, frame_id); + if (!receiver_rfh) { + DispatchOnDisconnect( + source, receiver_port_id, kReceivingEndDoesntExistError); + return; } - receiver.reset(new ExtensionMessagePort(contents->GetRenderProcessHost(), - receiver_routing_id, extension_id)); + receiver.reset( + new ExtensionMessagePort(weak_factory_.GetWeakPtr(), + receiver_port_id, extension_id, receiver_rfh, + include_child_frames)); const Extension* extension = nullptr; if (!extension_id.empty()) { @@ -556,11 +528,11 @@ void MessageService::OpenChannelToTab(int source_process_id, scoped_ptr<OpenChannelParams> params(new OpenChannelParams( source_process_id, + source_routing_id, scoped_ptr<base::DictionaryValue>(), // Source tab doesn't make sense // for opening to tabs. -1, // If there is no tab, then there is no frame either. - tab_id, frame_id, receiver.release(), receiver_port_id, extension_id, - extension_id, + receiver.release(), receiver_port_id, extension_id, extension_id, GURL(), // Source URL doesn't make sense for opening to tabs. channel_name, false, // Connections to tabs don't get TLS channel IDs. @@ -576,46 +548,48 @@ void MessageService::OpenChannelImpl(BrowserContext* browser_context, DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(target_extension != nullptr, !params->target_extension_id.empty()); - content::RenderProcessHost* source = - content::RenderProcessHost::FromID(params->source_process_id); + content::RenderFrameHost* source = + content::RenderFrameHost::FromID(params->source_process_id, + params->source_routing_id); if (!source) return; // Closed while in flight. - if (!params->receiver || !params->receiver->GetRenderProcessHost()) { + if (!params->receiver || !params->receiver->IsValidPort()) { DispatchOnDisconnect(source, params->receiver_port_id, kReceivingEndDoesntExistError); return; } MessageChannel* channel(new MessageChannel); - channel->opener.reset(new ExtensionMessagePort(source, MSG_ROUTING_CONTROL, - params->source_extension_id)); + channel->opener.reset( + new ExtensionMessagePort(weak_factory_.GetWeakPtr(), + GET_OPPOSITE_PORT_ID(params->receiver_port_id), + params->source_extension_id, source, false)); channel->receiver.reset(params->receiver.release()); AddChannel(channel, params->receiver_port_id); + // TODO(robwu): Could |guest_process_id| and |guest_render_frame_routing_id| + // be removed? In the past extension message routing was process-based, but + // now that extensions are routed from a specific RFH, the special casing for + // guest views seems no longer necessary, because the ExtensionMessagePort can + // simply obtain the source process & frame ID directly from the RFH. int guest_process_id = content::ChildProcessHost::kInvalidUniqueID; int guest_render_frame_routing_id = MSG_ROUTING_NONE; if (params->include_guest_process_info) { guest_process_id = params->source_process_id; - guest_render_frame_routing_id = params->source_frame_id; - auto* guest_rfh = content::RenderFrameHost::FromID( - guest_process_id, guest_render_frame_routing_id); - // Reset the |source_frame_id| parameter. - params->source_frame_id = -1; + guest_render_frame_routing_id = params->source_routing_id; - DCHECK(guest_rfh == nullptr || - WebViewGuest::FromWebContents( - WebContents::FromRenderFrameHost(guest_rfh)) != nullptr); + DCHECK(WebViewGuest::FromWebContents( + WebContents::FromRenderFrameHost(source))); } // Send the connect event to the receiver. Give it the opener's port ID (the // opener has the opposite port ID). channel->receiver->DispatchOnConnect( - params->receiver_port_id, params->channel_name, - std::move(params->source_tab), params->source_frame_id, - params->target_tab_id, params->target_frame_id, guest_process_id, - guest_render_frame_routing_id, params->source_extension_id, - params->target_extension_id, params->source_url, params->tls_channel_id); + params->channel_name, std::move(params->source_tab), + params->source_frame_id, guest_process_id, guest_render_frame_routing_id, + params->source_extension_id, params->target_extension_id, + params->source_url, params->tls_channel_id); // Report the event to the event router, if the target is an extension. // @@ -662,10 +636,36 @@ void MessageService::AddChannel(MessageChannel* channel, int receiver_port_id) { pending_lazy_background_page_channels_.erase(channel_id); } +void MessageService::OpenPort(int port_id, int process_id, int routing_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK(!IS_OPENER_PORT_ID(port_id)); + + int channel_id = GET_CHANNEL_ID(port_id); + MessageChannelMap::iterator it = channels_.find(channel_id); + if (it == channels_.end()) + return; + + it->second->receiver->OpenPort(process_id, routing_id); +} + +void MessageService::ClosePort( + int port_id, int process_id, int routing_id, bool force_close) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + ClosePortImpl(port_id, process_id, routing_id, force_close, std::string()); +} + void MessageService::CloseChannel(int port_id, const std::string& error_message) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + ClosePortImpl(port_id, content::ChildProcessHost::kInvalidUniqueID, + MSG_ROUTING_NONE, true, error_message); +} +void MessageService::ClosePortImpl(int port_id, + int process_id, + int routing_id, + bool force_close, + const std::string& error_message) { // Note: The channel might be gone already, if the other side closed first. int channel_id = GET_CHANNEL_ID(port_id); MessageChannelMap::iterator it = channels_.find(channel_id); @@ -675,12 +675,23 @@ void MessageService::CloseChannel(int port_id, if (pending != pending_lazy_background_page_channels_.end()) { lazy_background_task_queue_->AddPendingTask( pending->second.first, pending->second.second, - base::Bind(&MessageService::PendingLazyBackgroundPageCloseChannel, - weak_factory_.GetWeakPtr(), port_id, error_message)); + base::Bind(&MessageService::PendingLazyBackgroundPageClosePort, + weak_factory_.GetWeakPtr(), port_id, process_id, + routing_id, force_close, error_message)); } return; } - CloseChannelImpl(it, port_id, error_message, true); + + // The difference between closing a channel and port is that closing a port + // does not necessarily have to destroy the channel if there are multiple + // receivers, whereas closing a channel always forces all ports to be closed. + if (force_close) { + CloseChannelImpl(it, port_id, error_message, true); + } else if (IS_OPENER_PORT_ID(port_id)) { + it->second->opener->ClosePort(process_id, routing_id); + } else { + it->second->receiver->ClosePort(process_id, routing_id); + } } void MessageService::CloseChannelImpl( @@ -696,8 +707,7 @@ void MessageService::CloseChannelImpl( if (notify_other_port) { MessagePort* port = IS_OPENER_PORT_ID(closing_port_id) ? channel->receiver.get() : channel->opener.get(); - port->DispatchOnDisconnect(GET_OPPOSITE_PORT_ID(closing_port_id), - error_message); + port->DispatchOnDisconnect(error_message); } // Balance the IncrementLazyKeepaliveCount() in OpenChannelImpl. @@ -723,53 +733,6 @@ void MessageService::PostMessage(int source_port_id, const Message& message) { DispatchMessage(source_port_id, iter->second, message); } -void MessageService::Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - switch (type) { - case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: - case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { - content::RenderProcessHost* renderer = - content::Source<content::RenderProcessHost>(source).ptr(); - OnProcessClosed(renderer); - break; - } - default: - NOTREACHED(); - return; - } -} - -void MessageService::OnProcessClosed(content::RenderProcessHost* process) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - // Close any channels that share this renderer. We notify the opposite - // port that its pair has closed. - for (MessageChannelMap::iterator it = channels_.begin(); - it != channels_.end(); ) { - MessageChannelMap::iterator current = it++; - - content::RenderProcessHost* opener_process = - current->second->opener->GetRenderProcessHost(); - content::RenderProcessHost* receiver_process = - current->second->receiver->GetRenderProcessHost(); - - // Only notify the other side if it has a different porocess host. - bool notify_other_port = opener_process && receiver_process && - opener_process != receiver_process; - - if (opener_process == process) { - CloseChannelImpl(current, GET_CHANNEL_OPENER_ID(current->first), - std::string(), notify_other_port); - } else if (receiver_process == process) { - CloseChannelImpl(current, GET_CHANNEL_RECEIVERS_ID(current->first), - std::string(), notify_other_port); - } - } -} - void MessageService::EnqueuePendingMessage(int source_port_id, int channel_id, const Message& message) { @@ -827,7 +790,7 @@ void MessageService::DispatchMessage(int source_port_id, MessagePort* port = IS_OPENER_PORT_ID(dest_port_id) ? channel->opener.get() : channel->receiver.get(); - port->DispatchOnMessage(message, dest_port_id); + port->DispatchOnMessage(message); } bool MessageService::MaybeAddPendingLazyBackgroundPageOpenChannelTask( @@ -882,8 +845,9 @@ void MessageService::OnOpenChannelAllowed(scoped_ptr<OpenChannelParams> params, pending_incognito_channels_.erase(pending_for_incognito); // Re-lookup the source process since it may no longer be valid. - content::RenderProcessHost* source = - content::RenderProcessHost::FromID(params->source_process_id); + content::RenderFrameHost* source = + content::RenderFrameHost::FromID(params->source_process_id, + params->source_routing_id); if (!source) { return; } @@ -894,14 +858,20 @@ void MessageService::OnOpenChannelAllowed(scoped_ptr<OpenChannelParams> params, return; } - BrowserContext* context = source->GetBrowserContext(); + BrowserContext* context = source->GetProcess()->GetBrowserContext(); // Note: we use the source's profile here. If the source is an incognito // process, we will use the incognito EPM to find the right extension process, // which depends on whether the extension uses spanning or split mode. - params->receiver.reset(new ExtensionMessagePort( - GetExtensionProcess(context, params->target_extension_id), - MSG_ROUTING_CONTROL, params->target_extension_id)); + if (content::RenderProcessHost* extension_process = + GetExtensionProcess(context, params->target_extension_id)) { + params->receiver.reset( + new ExtensionMessagePort( + weak_factory_.GetWeakPtr(), params->receiver_port_id, + params->target_extension_id, extension_process)); + } else { + params->receiver.reset(); + } // If the target requests the TLS channel id, begin the lookup for it. // The target might also be a lazy background page, checked next, but the @@ -957,13 +927,14 @@ void MessageService::GotChannelID(scoped_ptr<OpenChannelParams> params, pending_tls_channel_id_channels_.erase(pending_for_tls_channel_id); // Re-lookup the source process since it may no longer be valid. - content::RenderProcessHost* source = - content::RenderProcessHost::FromID(params->source_process_id); + content::RenderFrameHost* source = + content::RenderFrameHost::FromID(params->source_process_id, + params->source_routing_id); if (!source) { return; } - BrowserContext* context = source->GetBrowserContext(); + BrowserContext* context = source->GetProcess()->GetBrowserContext(); ExtensionRegistry* registry = ExtensionRegistry::Get(context); const Extension* target_extension = registry->enabled_extensions().GetByID(params->target_extension_id); @@ -990,20 +961,22 @@ void MessageService::PendingLazyBackgroundPageOpenChannel( if (!host) return; // TODO(mpcomplete): notify source of disconnect? - params->receiver.reset(new ExtensionMessagePort(host->render_process_host(), - MSG_ROUTING_CONTROL, - params->target_extension_id)); + params->receiver.reset( + new ExtensionMessagePort( + weak_factory_.GetWeakPtr(), params->receiver_port_id, + params->target_extension_id, host->render_process_host())); OpenChannelImpl(host->browser_context(), std::move(params), host->extension(), true /* did_enqueue */); } -void MessageService::DispatchOnDisconnect(content::RenderProcessHost* source, +void MessageService::DispatchOnDisconnect(content::RenderFrameHost* source, int port_id, const std::string& error_message) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - ExtensionMessagePort port(source, MSG_ROUTING_CONTROL, ""); - port.DispatchOnDisconnect(GET_OPPOSITE_PORT_ID(port_id), error_message); + ExtensionMessagePort port(weak_factory_.GetWeakPtr(), + GET_OPPOSITE_PORT_ID(port_id), "", source, false); + port.DispatchOnDisconnect(error_message); } void MessageService::DispatchPendingMessages(const PendingMessagesQueue& queue, diff --git a/chrome/browser/extensions/api/messaging/message_service.h b/chrome/browser/extensions/api/messaging/message_service.h index d170b97..08a3f96 100644 --- a/chrome/browser/extensions/api/messaging/message_service.h +++ b/chrome/browser/extensions/api/messaging/message_service.h @@ -16,8 +16,6 @@ #include "base/memory/weak_ptr.h" #include "base/values.h" #include "chrome/browser/extensions/api/messaging/message_property_provider.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_registrar.h" #include "extensions/browser/api/messaging/native_message_host.h" #include "extensions/browser/browser_context_keyed_api_factory.h" #include "extensions/common/api/messaging/message.h" @@ -27,8 +25,6 @@ class Profile; namespace content { class BrowserContext; -class RenderProcessHost; -class WebContents; } namespace extensions { @@ -54,11 +50,8 @@ class LazyBackgroundTaskQueue; // // Terminology: // channel: connection between two ports -// port: an IPC::Message::Process interface and an optional routing_id (in the -// case that the port is a tab). The Process is usually either a -// RenderProcessHost or a RenderViewHost. -class MessageService : public BrowserContextKeyedAPI, - public content::NotificationObserver { +// port: One sender or receiver tied to one or more RenderFrameHost instances. +class MessageService : public BrowserContextKeyedAPI { public: // A messaging channel. Note that the opening port can be the same as the // receiver, if an extension background page wants to talk to its tab (for @@ -69,13 +62,15 @@ class MessageService : public BrowserContextKeyedAPI, class MessagePort { public: virtual ~MessagePort() {} + + // Called right before a port is connected to a channel. If false, the port + // is not used and the channel is closed. + virtual bool IsValidPort() = 0; + // Notify the port that the channel has been opened. - virtual void DispatchOnConnect(int dest_port_id, - const std::string& channel_name, + virtual void DispatchOnConnect(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, @@ -85,21 +80,22 @@ class MessageService : public BrowserContextKeyedAPI, // Notify the port that the channel has been closed. If |error_message| is // non-empty, it indicates an error occurred while opening the connection. - virtual void DispatchOnDisconnect(int source_port_id, - const std::string& error_message) {} + virtual void DispatchOnDisconnect(const std::string& error_message) {} // Dispatch a message to this end of the communication. - virtual void DispatchOnMessage(const Message& message, - int target_port_id) = 0; + virtual void DispatchOnMessage(const Message& message) = 0; + + // Mark the port as opened by the specific frame. + virtual void OpenPort(int process_id, int routing_id) {} - // MessagPorts that target extensions will need to adjust their keepalive + // Close the port for the given frame. + virtual void ClosePort(int process_id, int routing_id) {} + + // MessagePorts that target extensions will need to adjust their keepalive // counts for their lazy background page. virtual void IncrementLazyKeepaliveCount() {} virtual void DecrementLazyKeepaliveCount() {} - // Get the RenderProcessHost (if any) associated with the port. - virtual content::RenderProcessHost* GetRenderProcessHost(); - protected: MessagePort() {} @@ -145,6 +141,7 @@ class MessageService : public BrowserContextKeyedAPI, // are restricted to that tab, so if there are multiple tabs in that process, // only the targeted tab will receive messages. void OpenChannelToTab(int source_process_id, + int source_routing_id, int receiver_port_id, int tab_id, int frame_id, @@ -158,6 +155,14 @@ class MessageService : public BrowserContextKeyedAPI, const std::string& source_extension_id, const std::string& native_app_name); + // Mark the given port as opened by the frame identified by + // (process_id, routing_id). + void OpenPort(int port_id, int process_id, int routing_id); + + // Closes the given port in the given frame. If this was the last frame or if + // |force_close| is true, then the other side is closed as well. + void ClosePort(int port_id, int process_id, int routing_id, bool force_close); + // Closes the message channel associated with the given port, and notifies // the other side. void CloseChannel(int port_id, const std::string& error_message); @@ -200,6 +205,12 @@ class MessageService : public BrowserContextKeyedAPI, const Extension* target_extension, bool did_enqueue); + void ClosePortImpl(int port_id, + int process_id, + int routing_id, + bool force_close, + const std::string& error_message); + void CloseChannelImpl(MessageChannelMap::iterator channel_iter, int port_id, const std::string& error_message, @@ -209,14 +220,6 @@ class MessageService : public BrowserContextKeyedAPI, // channels with the same id. void AddChannel(MessageChannel* channel, int receiver_port_id); - // content::NotificationObserver interface. - void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) override; - - // A process that might be in our list of channels has closed. - void OnProcessClosed(content::RenderProcessHost* process); - // If the channel is being opened from an incognito tab the user must allow // the connection. void OnOpenChannelAllowed(scoped_ptr<OpenChannelParams> params, bool allowed); @@ -252,11 +255,15 @@ class MessageService : public BrowserContextKeyedAPI, scoped_ptr<OpenChannelParams> params, int source_process_id, extensions::ExtensionHost* host); - void PendingLazyBackgroundPageCloseChannel(int port_id, - const std::string& error_message, - extensions::ExtensionHost* host) { + void PendingLazyBackgroundPageClosePort(int port_id, + int process_id, + int routing_id, + bool force_close, + const std::string& error_message, + extensions::ExtensionHost* host) { if (host) - CloseChannel(port_id, error_message); + ClosePortImpl(port_id, process_id, routing_id, force_close, + error_message); } void PendingLazyBackgroundPagePostMessage(int port_id, const Message& message, @@ -267,7 +274,7 @@ class MessageService : public BrowserContextKeyedAPI, // Immediate dispatches a disconnect to |source| for |port_id|. Sets source's // runtime.lastMessage to |error_message|, if any. - void DispatchOnDisconnect(content::RenderProcessHost* source, + void DispatchOnDisconnect(content::RenderFrameHost* source, int port_id, const std::string& error_message); @@ -282,7 +289,6 @@ class MessageService : public BrowserContextKeyedAPI, static const bool kServiceIsCreatedWithBrowserContext = false; static const bool kServiceIsNULLWhileTesting = true; - content::NotificationRegistrar registrar_; MessageChannelMap channels_; // A set of channel IDs waiting for TLS channel IDs to complete opening, and // any pending messages queued to be sent on those channels. This and the diff --git a/chrome/browser/extensions/api/messaging/native_message_port.cc b/chrome/browser/extensions/api/messaging/native_message_port.cc index 51e8478..dc97cf8 100644 --- a/chrome/browser/extensions/api/messaging/native_message_port.cc +++ b/chrome/browser/extensions/api/messaging/native_message_port.cc @@ -102,9 +102,16 @@ NativeMessagePort::~NativeMessagePort() { host_task_runner_->DeleteSoon(FROM_HERE, core_.release()); } -void NativeMessagePort::DispatchOnMessage( - const Message& message, - int target_port_id) { +bool NativeMessagePort::IsValidPort() { + // The native message port is immediately connected after construction, so it + // is not possible to invalidate the port between construction and connection. + // The return value doesn't matter since native messaging follows a code path + // where IsValidPort() is never called. + NOTREACHED(); + return true; +} + +void NativeMessagePort::DispatchOnMessage(const Message& message) { DCHECK(thread_checker_.CalledOnValidThread()); core_->OnMessageFromChrome(message.data); } diff --git a/chrome/browser/extensions/api/messaging/native_message_port.h b/chrome/browser/extensions/api/messaging/native_message_port.h index 438b317..a19485f 100644 --- a/chrome/browser/extensions/api/messaging/native_message_port.h +++ b/chrome/browser/extensions/api/messaging/native_message_port.h @@ -21,7 +21,8 @@ class NativeMessagePort : public MessageService::MessagePort { ~NativeMessagePort() override; // MessageService::MessagePort implementation. - void DispatchOnMessage(const Message& message, int target_port_id) override; + bool IsValidPort() override; + void DispatchOnMessage(const Message& message) override; private: class Core; |