File: message_service.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (333 lines) | stat: -rw-r--r-- 13,775 bytes parent folder | download | duplicates (5)
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_