forked from Pissandshittium/pissandshittium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrenderer.cc
187 lines (164 loc) · 7.89 KB
/
renderer.cc
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
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/task/sequence_manager/task_queue.h"
#include "codelabs/mojo_examples/mojo_impls.h"
#include "codelabs/mojo_examples/mojom/interface.mojom.h"
#include "codelabs/mojo_examples/process_bootstrapper.h"
#include "ipc/ipc_channel_mojo.h"
#include "ipc/ipc_channel_proxy.h"
#include "ipc/ipc_sync_channel.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"
static ObjectAImpl g_object_a;
static ObjectBImpl g_object_b;
class CustomTaskQueue : public base::RefCounted<CustomTaskQueue> {
public:
CustomTaskQueue(base::sequence_manager::SequenceManager& sequence_manager,
const base::sequence_manager::TaskQueue::Spec& spec)
: task_queue_(sequence_manager.CreateTaskQueue(spec)),
voter_(task_queue_->CreateQueueEnabledVoter()) {}
void FreezeTaskQueue() { voter_->SetVoteToEnable(false); }
void UnfreezeTaskQueue() {
LOG(INFO) << "Unfreezing the task queue that `ObjectAImpl` is bound to.";
voter_->SetVoteToEnable(true);
}
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner() const {
return task_queue_->task_runner();
}
private:
~CustomTaskQueue() = default;
friend class base::RefCounted<CustomTaskQueue>;
base::sequence_manager::TaskQueue::Handle task_queue_;
// Used to enable/disable the underlying `TaskQueueImpl`.
std::unique_ptr<base::sequence_manager::TaskQueue::QueueEnabledVoter> voter_;
};
class RendererIPCListener : public IPC::Listener {
public:
RendererIPCListener(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> initially_frozen_task_runner)
: initially_frozen_task_runner_(initially_frozen_task_runner) {
// The sequence of events we'll need to perform are the following:
// 1.) Create the ChannelProxy (specifically a SyncChannel) for the
// receiving end of the IPC communication.
// 2.) Accept the incoming mojo invitation. From the invitation, we
// extract a message pipe that we will feed directly into the
// `IPC::ChannelProxy` to initialize it. This bootstraps the
// bidirectional IPC channel between browser <=> renderer.
// 1.) Create a new IPC::ChannelProxy.
channel_proxy_ = IPC::SyncChannel::Create(
this, io_task_runner, base::SingleThreadTaskRunner::GetCurrentDefault(),
&shutdown_event_);
// 2.) Accept the mojo invitation.
mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept(
mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
*base::CommandLine::ForCurrentProcess()));
mojo::ScopedMessagePipeHandle ipc_bootstrap_pipe =
invitation.ExtractMessagePipe("ipc_bootstrap_pipe");
// Get ready to receive the invitation from the browser process, which bears
// a message pipe represented by `ipc_bootstrap_pipe`.
channel_proxy_->Init(
IPC::ChannelMojo::CreateClientFactory(
std::move(ipc_bootstrap_pipe), /*ipc_task_runner=*/io_task_runner,
/*proxy_task_runner=*/
base::SingleThreadTaskRunner::GetCurrentDefault()),
/*create_pipe_now=*/true);
}
private:
// IPC::Listener implementation.
bool OnMessageReceived(const IPC::Message& msg) override {
LOG(WARNING) << "The renderer received a message";
return true;
}
void OnAssociatedInterfaceRequest(
const std::string& interface_name,
mojo::ScopedInterfaceEndpointHandle handle) override {
std::string tmp_name = interface_name;
LOG(WARNING) << "The renderer received an associated interface request for "
<< tmp_name.c_str();
if (interface_name == "codelabs.mojom.ObjectA") {
// Amazingly enough, if you comment out all of this code, which causes the
// `ObjectA` interface to not get bound and therefore the `DoA()` message
// to never be delivered, the `DoB()` message still gets delivered and
// invoked on `ObjectB`. This is because channel-associated interface
// messages are dispatched very differently than non-channel-associated
// ones, because we can't block at all.
mojo::PendingAssociatedReceiver<codelabs::mojom::ObjectA> pending_a(
std::move(handle));
g_object_a.BindToFrozenTaskRunner(
std::move(pending_a), std::move(initially_frozen_task_runner_));
} else if (interface_name == "codelabs.mojom.ObjectB") {
mojo::PendingAssociatedReceiver<codelabs::mojom::ObjectB> pending_b(
std::move(handle));
g_object_b.Bind(std::move(pending_b));
}
}
std::unique_ptr<IPC::SyncChannel> channel_proxy_;
scoped_refptr<base::SingleThreadTaskRunner> initially_frozen_tq_;
base::WaitableEvent shutdown_event_{
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED};
scoped_refptr<base::SingleThreadTaskRunner> initially_frozen_task_runner_;
};
int main(int argc, char** argv) {
base::AtExitManager exit_manager;
base::CommandLine::Init(argc, argv);
LOG(INFO) << "Renderer: "
<< base::CommandLine::ForCurrentProcess()->GetCommandLineString();
ProcessBootstrapper bootstrapper;
// See the documentation above the corresponding "browser.cc".
bootstrapper.InitMainThread(base::MessagePumpType::DEFAULT);
bootstrapper.InitMojo(/*as_browser_process=*/false);
// This is the task queue that `ObjectAImpl`'s receiver will be bound to. We
// freeze it to demonstrate that channel-associated interfaces bound to frozen
// queues *still* have their messages delivered.
scoped_refptr<CustomTaskQueue> initially_frozen_tq =
base::MakeRefCounted<CustomTaskQueue>(
*bootstrapper.sequence_manager.get(),
base::sequence_manager::TaskQueue::Spec(
base::sequence_manager::QueueName::TEST_TQ));
initially_frozen_tq->FreezeTaskQueue();
// The rest of the magic happens in this object.
std::unique_ptr<RendererIPCListener> renderer_ipc_listener =
std::make_unique<RendererIPCListener>(
/*io_task_runner=*/bootstrapper.io_task_runner,
initially_frozen_tq->task_runner());
// Post a task for 3 seconds from now that will unfreeze the TaskRunner that
// the `codelabs::mojom::ObjectA` implementation is bound to. This would
// normally block all messages from going to their corresponding
// implementations (i.e., messages bound for ObjectA would be blocked, and
// necessarily subsequent messages bound for ObjectB would *also* be blocked),
// however since the associated interfaces here are specifically
// *channel*-associated, we do not support blocking messages, so they're all
// delivered immediately.
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(
[](scoped_refptr<CustomTaskQueue> initially_frozen_tq) {
LOG(INFO) << "Unfreezing frozen TaskRunner";
initially_frozen_tq->UnfreezeTaskQueue();
},
initially_frozen_tq),
base::Seconds(3));
// This task is posted first, but will not run until the task runner is
// unfrozen in ~3 seconds.
initially_frozen_tq->task_runner()->PostTask(
FROM_HERE, base::BindOnce([]() {
LOG(WARNING) << "Renderer: This is the first task posted to the frozen "
"TaskRunner. It shouldn't run within the first 2 "
"seconds of the program";
}));
base::RunLoop run_loop;
run_loop.Run();
return 0;
}