1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXTENSIONS_BROWSER_API_MESSAGING_MESSAGE_SERVICE_H_
#define EXTENSIONS_BROWSER_API_MESSAGING_MESSAGE_SERVICE_H_
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/compiler_specific.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "extensions/browser/api/messaging/message_port.h"
#include "extensions/browser/api/messaging/native_message_host.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/lazy_context_id.h"
#include "extensions/browser/lazy_context_task_queue.h"
#include "extensions/common/api/messaging/message.h"
#include "extensions/common/api/messaging/port_id.h"
#include "extensions/common/extension_id.h"
class GURL;
namespace content {
class BrowserContext;
}
namespace extensions {
namespace mojom {
enum class ChannelType;
}
class ChannelEndpoint;
class Extension;
class ExtensionHost;
class MessagingDelegate;
struct MessagingEndpoint;
struct PortContext;
// This class manages message and event passing between renderer processes.
// It maintains a list of processes that are listening to events and a set of
// open channels.
//
// Messaging works this way:
// - An extension-owned script context (like a background page or a content
// script) adds an event listener to the "onConnect" event.
// - Another context calls "runtime.connect()" to open a channel to the
// extension process, or an extension context calls "tabs.connect(tabId)" to
// open a channel to the content scripts for the given tab. The EMS notifies
// the target process/tab, which then calls the onConnect event in every
// context owned by the connecting extension in that process/tab.
// - Once the channel is established, either side can call postMessage to send
// a message to the opposite side of the channel, which may have multiple
// listeners.
//
// Terminology:
// channel: connection between two ports
// port: One sender or receiver tied to one or more RenderFrameHost instances.
class MessageService : public BrowserContextKeyedAPI,
public MessagePort::ChannelDelegate {
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
// example).
struct MessageChannel;
explicit MessageService(content::BrowserContext* context);
MessageService(const MessageService&) = delete;
MessageService& operator=(const MessageService&) = delete;
~MessageService() override;
// BrowserContextKeyedAPI implementation.
static BrowserContextKeyedAPIFactory<MessageService>* GetFactoryInstance();
// MessagePort::ChannelDelegate implementation.
void CloseChannel(const PortId& port_id,
const std::string& error_message) override;
void ClosePort(const PortId& port_id,
int process_id,
const PortContext& port_context,
bool force_close) override;
void PostMessage(const PortId& port_id, const Message& message) override;
void NotifyResponsePending(const PortId& port_id) override;
// Convenience method to get the MessageService for a browser context.
static MessageService* Get(content::BrowserContext* context);
// Given an extension's ID, opens a channel between the given renderer "port"
// and every listening context owned by that extension. `channel_name` is
// an optional identifier for use by extension developers. `opener_port` is an
// optional pre-opened port that should be attached to the opened channel.
void OpenChannelToExtension(const ChannelEndpoint& source,
const PortId& source_port_id,
const MessagingEndpoint& source_endpoint,
std::unique_ptr<MessagePort> opener_port,
const std::string& target_extension_id,
const GURL& source_url,
mojom::ChannelType channel_type,
const std::string& channel_name);
using ExternalConnectionInfo = mojom::ExternalConnectionInfo;
void OpenChannelToExtension(
const ChannelEndpoint& source,
const PortId& source_port_id,
const ExternalConnectionInfo& info,
mojom::ChannelType channel_type,
const std::string& channel_name,
mojo::PendingAssociatedRemote<extensions::mojom::MessagePort> port,
mojo::PendingAssociatedReceiver<extensions::mojom::MessagePortHost>
port_host);
void OpenChannelToNativeApp(
const ChannelEndpoint& source,
const PortId& source_port_id,
const std::string& native_app_name,
mojo::PendingAssociatedRemote<extensions::mojom::MessagePort> port,
mojo::PendingAssociatedReceiver<extensions::mojom::MessagePortHost>
port_host);
void OpenChannelToTab(
const ChannelEndpoint& source,
const PortId& source_port_id,
int tab_id,
int frame_id,
const std::string& document_id,
mojom::ChannelType channel_type,
const std::string& channel_name,
mojo::PendingAssociatedRemote<extensions::mojom::MessagePort> port,
mojo::PendingAssociatedReceiver<extensions::mojom::MessagePortHost>
port_host);
// Marks the given port as opened by `port_context` in the render process
// with id `process_id`.
void OpenPort(content::RenderProcessHost* process,
const PortId& port_id,
const PortContext& port_context);
// Closes the given port in the given `port_context`. If this was the last
// context or if `force_close` is true, then the other side is closed as well.
void ClosePort(content::RenderProcessHost* process,
const PortId& port_id,
const PortContext& port_context,
bool force_close);
// Notifies the port that one of the receivers of a message indicated that
// they plan to respond to the message later.
void NotifyResponsePending(content::RenderProcessHost* process,
const PortId& port_id,
const PortContext& port_context);
// Returns the number of open channels for test.
size_t GetChannelCountForTest() { return channels_.size(); }
base::WeakPtr<MessagePort::ChannelDelegate> GetChannelDelegate() {
return weak_factory_.GetWeakPtr();
}
private:
friend class MockMessageService;
friend class BrowserContextKeyedAPIFactory<MessageService>;
struct OpenChannelParams;
// Same as `OpenChannelToExtension`, but opens a channel to the tab with the
// given ID. Messages are restricted to that tab, so if there are multiple
// tabs in that process, only the targeted tab will receive messages.
void OpenChannelToTabImpl(
const ChannelEndpoint& source,
const PortId& source_port_id,
int tab_id,
int frame_id,
const std::string& document_id,
const ExtensionId& extension_id,
mojom::ChannelType channel_type,
const std::string& channel_name,
mojo::PendingAssociatedRemote<extensions::mojom::MessagePort> port,
mojo::PendingAssociatedReceiver<extensions::mojom::MessagePortHost>
port_host);
// Creates a MessagePort for the tab with the given `web_contents`.
// Returns nullptr if the tab is not available.
std::unique_ptr<MessagePort> CreateReceiverForTab(
const ExtensionId& extension_id,
const PortId& receiver_port_id,
content::WebContents* receiver_contents,
int receiver_frame_id,
const std::string& receiver_document_id);
void OpenChannelToNativeAppImpl(
const ChannelEndpoint& source,
const PortId& source_port_id,
const std::string& native_app_name,
mojo::PendingAssociatedRemote<extensions::mojom::MessagePort> port,
mojo::PendingAssociatedReceiver<extensions::mojom::MessagePortHost>
port_host);
void OpenPortImpl(const PortId& port_id,
int process_id,
const PortContext& port_context);
// A map of channel ID to its channel object.
using MessageChannelMap =
std::map<ChannelId, std::unique_ptr<MessageChannel>>;
using PendingMessage = std::pair<PortId, Message>;
using PendingMessagesQueue = std::vector<PendingMessage>;
// A set of channel IDs waiting to complete opening, and any pending messages
// queued to be sent on those channels.
using PendingChannelMap = std::map<ChannelId, PendingMessagesQueue>;
// A map of channel ID to information about the extension that is waiting
// for that channel to open. Used for lazy background pages or Service
// Workers.
using PendingLazyContextChannelMap = std::map<ChannelId, LazyContextId>;
// Common implementation for opening a channel configured by `params`.
//
// `target_extension` will be non-null if |params->target_extension_id| is
// non-empty, that is, if the target is an extension, it must exist.
//
// `did_enqueue` will be true if the channel opening was delayed while
// waiting for an event page to start, false otherwise.
void OpenChannelImpl(content::BrowserContext* browser_context,
std::unique_ptr<OpenChannelParams> params,
const Extension* target_extension,
bool did_enqueue);
void ClosePortImpl(const PortId& port_id,
int process_id,
int routing_id,
int worker_thread_id,
bool force_close,
const std::string& error_message);
void CloseChannelImpl(MessageChannelMap::iterator channel_iter,
const PortId& port_id,
const std::string& error_message,
bool notify_other_port);
// Have MessageService take ownership of `channel`, and remove any pending
// channels with the same id.
void AddChannel(std::unique_ptr<MessageChannel> channel,
const PortId& receiver_port_id);
// If the channel is being opened from an incognito tab the user must allow
// the connection.
void OnOpenChannelAllowed(std::unique_ptr<OpenChannelParams> params,
bool allowed);
// Enqueues a message on a pending channel.
void EnqueuePendingMessage(const PortId& port_id,
const ChannelId& channel_id,
const Message& message);
// Enqueues a message on a channel pending on a lazy background page load.
void EnqueuePendingMessageForLazyBackgroundLoad(const PortId& port_id,
const ChannelId& channel_id,
const Message& message);
// Immediately sends a message to the given port.
void DispatchMessage(const PortId& port_id,
MessageChannel* channel,
const Message& message);
// Potentially registers a pending task with lazy context task queue
// to open a channel. Returns true if a task was queued.
// Takes ownership of `params` if true is returned.
bool MaybeAddPendingLazyContextOpenChannelTask(
content::BrowserContext* context,
const Extension* extension,
std::unique_ptr<OpenChannelParams>* params,
const PendingMessagesQueue& pending_messages);
// Callbacks for background task queue tasks. The queue passes in an
// ExtensionHost to its task callbacks, though some of our callbacks don't
// use that argument.
void PendingLazyContextOpenChannel(
std::unique_ptr<OpenChannelParams> params,
const base::UnguessableToken& open_channel_wakeup_context_tracking_id,
std::unique_ptr<LazyContextTaskQueue::ContextInfo> context_info);
void PendingLazyContextClosePort(
const PortId& port_id,
int process_id,
int routing_id,
int worker_thread_id,
bool force_close,
const std::string& error_message,
std::unique_ptr<LazyContextTaskQueue::ContextInfo> context_info) {
if (context_info) {
ClosePortImpl(port_id, process_id, routing_id, worker_thread_id,
force_close, error_message);
}
}
void PendingLazyContextPostMessage(
const PortId& port_id,
const Message& message,
std::unique_ptr<LazyContextTaskQueue::ContextInfo> context_info) {
if (context_info) {
PostMessage(port_id, message);
}
}
void DispatchPendingMessages(const PendingMessagesQueue& queue,
const ChannelId& channel_id);
// BrowserContextKeyedAPI implementation.
static const char* service_name() { return "MessageService"; }
static const bool kServiceRedirectedInIncognito = true;
static const bool kServiceIsCreatedWithBrowserContext = false;
static const bool kServiceIsNULLWhileTesting = true;
const raw_ptr<content::BrowserContext> context_;
// Delegate for embedder-specific messaging, e.g. for Chrome tabs.
// Owned by the ExtensionsAPIClient and guaranteed to outlive `this`.
raw_ptr<MessagingDelegate> messaging_delegate_;
MessageChannelMap channels_;
// A set of channel IDs waiting for user permission to cross the border
// between an incognito page and an app or extension, and any pending messages
// queued to be sent on those channels.
PendingChannelMap pending_incognito_channels_;
PendingLazyContextChannelMap pending_lazy_context_channels_;
base::WeakPtrFactory<MessageService> weak_factory_{this};
};
} // namespace extensions
#endif // EXTENSIONS_BROWSER_API_MESSAGING_MESSAGE_SERVICE_H_
|