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
content / public / browser / render_frame_host_receiver_set.h [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_PUBLIC_BROWSER_RENDER_FRAME_HOST_RECEIVER_SET_H_
#define CONTENT_PUBLIC_BROWSER_RENDER_FRAME_HOST_RECEIVER_SET_H_
#include <map>
#include <vector>
#include "base/compiler_specific.h"
#include "base/memory/raw_ptr.h"
#include "content/common/content_export.h"
#include "content/public/browser/active_url_message_filter.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents_observer.h"
#include "mojo/public/cpp/bindings/associated_receiver_set.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
namespace content {
class WebContents;
// Owns a set of Channel-associated interface receivers with frame context on
// message dispatch.
//
// When messages are dispatched to the implementation, the implementation can
// call GetCurrentTargetFrame() on this object (see below) to determine which
// frame sent the message.
//
// In order to expose the interface to all RenderFrames, a binder must be
// registered for the interface. Typically this is done in
// RegisterAssociatedInterfaceBindersForRenderFrameHost() in a
// ContentBrowserClient subclass. Doing that will expose the interface to all
// remote RenderFrame objects. If the WebContents is destroyed at any point, the
// receivers will automatically reset and will cease to dispatch further
// incoming messages.
//
// Because this object uses Channel-associated interface receivers, all messages
// sent via these interfaces are ordered with respect to legacy Chrome IPC
// messages on the relevant IPC::Channel (i.e. the Channel between the browser
// and whatever render process hosts the sending frame.)
//
// Because this is a templated class, its complete implementation lives in the
// header file.
template <typename Interface>
class CONTENT_EXPORT RenderFrameHostReceiverSet : public WebContentsObserver {
public:
using ImplPointerType = Interface*;
RenderFrameHostReceiverSet(WebContents* web_contents, Interface* impl)
: WebContentsObserver(web_contents), impl_(impl) {}
~RenderFrameHostReceiverSet() override = default;
RenderFrameHostReceiverSet(const RenderFrameHostReceiverSet&) = delete;
RenderFrameHostReceiverSet& operator=(const RenderFrameHostReceiverSet&) =
delete;
void Bind(RenderFrameHost* render_frame_host,
mojo::PendingAssociatedReceiver<Interface> pending_receiver) {
// If the RenderFrameHost does not have a live RenderFrame:
// 1. There is no point in binding receivers, as the renderer should not be
// doing anything with this RenderFrameHost.
// 2. More problematic, `RenderFrameDeleted()` might not be called again
// for `render_frame_host`, potentially leaving dangling pointers to the
// RenderFrameHost (or other related objects) after the RenderFrameHost
// itself is later deleted.
if (!render_frame_host->IsRenderFrameLive()) {
return;
}
// Inject the ActiveUrlMessageFilter to improve crash reporting. This filter
// sets the correct URL crash keys based on the target RFH that is
// processing a message.
mojo::ReceiverId id = receivers_.Add(
impl_, std::move(pending_receiver), render_frame_host,
std::make_unique<internal::ActiveUrlMessageFilter>(render_frame_host));
frame_to_receivers_map_[render_frame_host].push_back(id);
}
// Implementations of `Interface` can call `GetCurrentTargetFrame()` to
// determine which frame sent the message. `GetCurrentTargetFrame()` will
// never return `nullptr`.
//
// Important: this method must only be called while the incoming message is
// being dispatched on the stack.
RETURNS_NONNULL RenderFrameHost* GetCurrentTargetFrame() {
if (current_target_frame_for_testing_)
return current_target_frame_for_testing_;
return receivers_.current_context();
}
// Reports the currently dispatching Message as bad and closes+removes the
// receiver which received the message. Prefer this over the global
// `mojo::ReportBadMessage()` function, since calling this method promptly
// disconnects the receiver, preventing further (potentially bad) messages
// from being processed.
//
// Important: this method must only be called while the incoming message is
// being dispatched on the stack. To report a bad message after asynchronous
// processing (e.g. posting a task that then reports a the bad message), use
// `GetMessageCallback()` and pass the returned callback to the async task
// that needs to report the message as bad.
NOT_TAIL_CALLED void ReportBadMessage(const std::string& message) {
receivers_.ReportBadMessage(message);
}
// Creates a callback which, when run, reports the currently dispatching
// Message as bad and closes+removes the receiver which received the message.
// Prefer this over the global `mojo::GetBadMessageCallback()` function,
// since running the callback promptly disconnects the receiver, preventing
// further (potentially bad) messages from being processed.
//
// Important: like `ReportBadMessage()`, this method must only be called while
// the incoming message is being dispatched on the stack. However, unlike
// `ReportBadMessage()`, the returned callback may be called even if the
// original message is no longer being dispatched on the stack.
//
// Sequence safety: the returned callback must be called on the sequence that
// owns `this` (i.e. the UI thread).
mojo::ReportBadMessageCallback GetBadMessageCallback() {
return receivers_.GetBadMessageCallback();
}
void SetCurrentTargetFrameForTesting(RenderFrameHost* render_frame_host) {
current_target_frame_for_testing_ = render_frame_host;
}
// Allows test code to swap the interface implementation.
//
// Returns the existing interface implementation to the caller.
//
// The caller needs to guarantee that `new_impl` will live longer than
// `this` Receiver. One way to achieve this is to store the returned
// `old_impl` and swap it back in when `new_impl` is getting destroyed.
// Test code should prefer using `mojo::test::ScopedSwapImplForTesting` if
// possible.
[[nodiscard]] ImplPointerType SwapImplForTesting(ImplPointerType new_impl) {
ImplPointerType old_impl = impl_;
impl_ = new_impl;
for (const auto& it : frame_to_receivers_map_) {
const std::vector<mojo::ReceiverId>& receiver_ids = it.second;
for (const mojo::ReceiverId& id : receiver_ids) {
// RenderFrameHostReceiverSet only allows all-or=nothing swaps, so
// all the old impls are expected to be equal to `this`'s old impl_.
CHECK_EQ(old_impl, receivers_.SwapImplForTesting(id, new_impl));
}
}
return old_impl;
}
private:
// content::WebContentsObserver:
void RenderFrameDeleted(RenderFrameHost* render_frame_host) override {
auto it = frame_to_receivers_map_.find(render_frame_host);
if (it == frame_to_receivers_map_.end())
return;
for (auto id : it->second)
receivers_.Remove(id);
frame_to_receivers_map_.erase(it);
}
// Receiver set for each frame in the page. Note, bindings are reused across
// navigations that are same-site since the RenderFrameHost is reused in that
// case.
mojo::AssociatedReceiverSet<Interface, RenderFrameHost*> receivers_;
// Track which RenderFrameHosts are in the |receivers_| set so they can
// be removed them when a RenderFrameHost is removed.
std::map<RenderFrameHost*, std::vector<mojo::ReceiverId>>
frame_to_receivers_map_;
raw_ptr<RenderFrameHost> current_target_frame_for_testing_ = nullptr;
// Must outlive this class.
raw_ptr<Interface> impl_;
};
} // namespace content
#endif // CONTENT_PUBLIC_BROWSER_RENDER_FRAME_HOST_RECEIVER_SET_H_