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
content / public / browser / web_ui_browser_interface_broker_registry.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_WEB_UI_BROWSER_INTERFACE_BROKER_REGISTRY_H_
#define CONTENT_PUBLIC_BROWSER_WEB_UI_BROWSER_INTERFACE_BROKER_REGISTRY_H_
#include <map>
#include "base/memory/raw_ptr.h"
#include "content/common/content_export.h"
#include "content/public/browser/per_web_ui_browser_interface_broker.h"
#include "content/public/browser/web_ui_controller.h"
namespace content {
// A lightweight class to help interface registration. Shouldn't be used outside
// of registration process.
template <typename ControllerType>
class InterfaceRegistrationHelper {
public:
explicit InterfaceRegistrationHelper(
std::vector<BinderInitializer>* binder_initializers)
: binder_initializers_(binder_initializers) {}
~InterfaceRegistrationHelper() = default;
template <typename Interface>
InterfaceRegistrationHelper<ControllerType>& Add() {
// Insert a function that populates an interface broker instance's
// binder_map.
binder_initializers_->push_back(
base::BindRepeating([](WebUIBinderMap* binder_map) {
binder_map->Add<Interface>(base::BindRepeating(
[](WebUIController* controller,
mojo::PendingReceiver<Interface> receiver) {
auto* concrete_controller = controller->GetAs<ControllerType>();
DCHECK(concrete_controller)
<< "The requesting WebUIController is of a different type.";
concrete_controller->BindInterface(std::move(receiver));
}));
}));
return *this;
}
private:
raw_ptr<std::vector<BinderInitializer>> binder_initializers_;
};
// Maintains a mapping from WebUIController::Type to a list of interfaces
// exposed to MojoJS, and provides methods to set up an interface broker that
// only brokers the registered interfaces for the WebUIController.
//
// To register interfaces for WebUI, use the following code:
//
// registry.ForWebUI<ControllerType>
// .Add<Interface1>()
// .Add<Interface2>();
//
// Background:
//
// Renderer exposed Mojo interfaces in general use a mojo::BinderMap where
// *all* interface binders are registered. When the renderer requests an
// interface, we look for the interface binder in that map and run it.
//
// At a high level, WebUI interfaces work slightly different. Rather than
// using the general mojo::BinderMap that has all renderer-exposed
// interfaces, each WebUI has its own mojo::BinderMap that contains only the
// interfaces exposed to the WebUI. When a WebUI's JS requests an interface,
// it uses that mojo::BinderMap and not the general one.
//
// The implementation of this is done through
// WebUIBrowserInterfaceBrokerRegistry which works as follows:
//
// 1. When we register interfaces for a WebUI, we create a
// a vector of "binder initializers" and add it to a map i.e.
// (WebUI type -> vector<BinderInitializer>). These binder initializers
// are repeating callbacks that wrap a call to BinderMap::Add() with an
// interface binder. Interface binders themselves are repeating callbacks
// that bind Mojo interfaces. Ideally, we would store the binders directly
// and pass them to the BinderMap in step 2., but BinderMap::Add() requires
// a template argument, so we need the binder initializer wrapper.
// 2. When a WebUI starts loading, we check the binder initialializers map to
// see if the WebUI is in the map, and if it is, we create a
// PerWebUIBrowserInterfaceBroker, which subclasses BrowserInterfaceBroker.
// PerWebUIBrowserInterfaceBroker owns a mojo::BinderMap and runs the
// binder initializers for the WebUI, registering all the interface binders
// for the WebUI in the mojo::BinderMap.
// 3. The PerWebUIBrowserInterfaceBroker is then stored in the
// WebUIController and a `BrowserInterfaceBroker` remote endpoint is sent
// to the renderer.
// 4. Through `BrowserInterfaceBroker::GetInterface()` the JS can request
// other remote endpoints.
class CONTENT_EXPORT WebUIBrowserInterfaceBrokerRegistry {
public:
WebUIBrowserInterfaceBrokerRegistry();
~WebUIBrowserInterfaceBrokerRegistry();
WebUIBrowserInterfaceBrokerRegistry(
const WebUIBrowserInterfaceBrokerRegistry&) = delete;
WebUIBrowserInterfaceBrokerRegistry& operator=(
const WebUIBrowserInterfaceBrokerRegistry&) = delete;
template <typename ControllerType>
InterfaceRegistrationHelper<ControllerType> ForWebUI() {
// WebUIController::GetType() requires a instantiated WebUIController
// (because it's a virtual method and can't be static). Here we only have
// type information, so we need to figure out the type from the controller's
// class declaration.
WebUIController::Type type = &ControllerType::kWebUIControllerType;
DCHECK(binder_initializers_.count(type) == 0)
<< "Interfaces for a WebUI should be registered together.";
return InterfaceRegistrationHelper<ControllerType>(
&binder_initializers_[type]);
}
// Creates an unbounded interface broker for |controller|. Caller should call
// Bind() method on the returned broker with a PendingReceiver that receives
// MojoJS.bindInterface requests from the renderer. Returns nullptr if
// controller doesn't have registered interface broker initializers.
std::unique_ptr<PerWebUIBrowserInterfaceBroker> CreateInterfaceBroker(
WebUIController& controller);
// Add interface |binder| to all WebUIs registered here.
//
// This method should only be used in tests. This method should be called
// after ContentBrowserClient::RegisterWebUIInterfaceBrokers and before WebUIs
// being created.
template <typename Interface>
void AddBinderForTesting(
base::RepeatingCallback<void(WebUIController*,
mojo::PendingReceiver<Interface>)> binder) {
for (auto& it : binder_initializers_) {
it.second.push_back(base::BindRepeating(
[](decltype(binder) binder, WebUIBinderMap* binder_map) {
binder_map->Add<Interface>(binder);
},
binder));
}
}
private:
std::map<WebUIController::Type, std::vector<BinderInitializer>>
binder_initializers_;
};
} // namespace content
#endif // CONTENT_PUBLIC_BROWSER_WEB_UI_BROWSER_INTERFACE_BROKER_REGISTRY_H_