Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(95)

Side by Side Diff: ppapi/proxy/nacl_message_scanner.cc

Issue 53123002: Rename HandleConverter to NaClMessageScanner. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remove SerializedVar fix for a future CL, add TODO. Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ppapi/proxy/handle_converter.h" 5 #include "ppapi/proxy/nacl_message_scanner.h"
6 6
7 #include <vector> 7 #include <vector>
8 #include "base/bind.h" 8 #include "base/bind.h"
9 #include "ipc/ipc_message.h" 9 #include "ipc/ipc_message.h"
10 #include "ipc/ipc_message_macros.h" 10 #include "ipc/ipc_message_macros.h"
11 #include "ppapi/proxy/ppapi_messages.h" 11 #include "ppapi/proxy/ppapi_messages.h"
12 #include "ppapi/proxy/resource_message_params.h" 12 #include "ppapi/proxy/resource_message_params.h"
13 #include "ppapi/proxy/serialized_handle.h" 13 #include "ppapi/proxy/serialized_handle.h"
14 #include "ppapi/proxy/serialized_var.h" 14 #include "ppapi/proxy/serialized_var.h"
15 15
16 class NaClDescImcShm; 16 class NaClDescImcShm;
17 17
18 namespace IPC { 18 namespace IPC {
19 class Message; 19 class Message;
20 } 20 }
21 21
22 namespace { 22 namespace {
23 23
24 typedef std::vector<ppapi::proxy::SerializedHandle> Handles;
25
26 struct ScanningResults {
27 ScanningResults() : handle_index(0) {}
28
29 // Vector to hold handles found in the message.
30 Handles handles;
31 // Current handle index in the rewritten message.
32 int handle_index;
Mark Seaborn 2013/11/02 00:57:44 Can you expand on the comment to explain how this
bbudge 2013/11/02 01:18:50 Done.
33 // The rewritten message. This may be NULL, so all ScanParam overloads should
34 // check for NULL before writing to it. In some cases, a ScanParam overload
35 // may set this to NULL when it can determine that there are no parameters
36 // that need conversion. (See the ResourceMessageReplyParams overload).
Mark Seaborn 2013/11/02 00:57:44 Nit: put '.' inside ()s
bbudge 2013/11/02 01:18:50 I removed this part of the comment since without t
37 scoped_ptr<IPC::Message> new_msg;
38 };
39
24 void WriteHandle(int handle_index, 40 void WriteHandle(int handle_index,
25 const ppapi::proxy::SerializedHandle& handle, 41 const ppapi::proxy::SerializedHandle& handle,
26 IPC::Message* message) { 42 IPC::Message* msg) {
27 ppapi::proxy::SerializedHandle::WriteHeader(handle.header(), message); 43 ppapi::proxy::SerializedHandle::WriteHeader(handle.header(), msg);
28 44
29 // Now write the handle itself in POSIX style. 45 // Now write the handle itself in POSIX style.
30 message->WriteBool(true); // valid == true 46 msg->WriteBool(true); // valid == true
31 message->WriteInt(handle_index); 47 msg->WriteInt(handle_index);
32 } 48 }
33 49
34 typedef std::vector<ppapi::proxy::SerializedHandle> Handles; 50 // Define overloads for each kind of message parameter that requires special
51 // handling. See ScanTuple for how these get used.
35 52
36 // We define overloads for catching SerializedHandles so that we can share 53 // Overload to match SerializedHandle.
37 // them correctly to the untrusted side. 54 void ScanParam(const ppapi::proxy::SerializedHandle& handle,
38 // See ConvertHandlesImpl for how these get used. 55 ScanningResults* results) {
39 void ConvertHandlesInParam(const ppapi::proxy::SerializedHandle& handle, 56 results->handles.push_back(handle);
40 Handles* handles, 57 if (results->new_msg)
41 IPC::Message* msg, 58 WriteHandle(results->handle_index++, handle, results->new_msg.get());
42 int* handle_index) {
43 handles->push_back(handle);
44 if (msg)
45 WriteHandle((*handle_index)++, handle, msg);
46 } 59 }
47 60
48 void HandleWriter(int* handle_index, 61 void HandleWriter(int* handle_index,
49 IPC::Message* m, 62 IPC::Message* m,
50 const ppapi::proxy::SerializedHandle& handle) { 63 const ppapi::proxy::SerializedHandle& handle) {
51 WriteHandle((*handle_index)++, handle, m); 64 WriteHandle((*handle_index)++, handle, m);
52 } 65 }
53 66
54 void ConvertHandlesInParam(const ppapi::proxy::SerializedVar& var, 67 // Overload to match SerializedVar, which can contain handles.
55 Handles* handles, 68 void ScanParam(const ppapi::proxy::SerializedVar& var,
56 IPC::Message* msg, 69 ScanningResults* results) {
57 int* handle_index) {
58 std::vector<ppapi::proxy::SerializedHandle*> var_handles = var.GetHandles(); 70 std::vector<ppapi::proxy::SerializedHandle*> var_handles = var.GetHandles();
71 // TODO(bbudge) Remove this early out, since a subsequent SerializedVar may
72 // contain handles, and we will have dropped this one's data.
59 if (var_handles.empty()) 73 if (var_handles.empty())
60 return; 74 return;
61 75
62 for (size_t i = 0; i < var_handles.size(); ++i) 76 for (size_t i = 0; i < var_handles.size(); ++i)
63 handles->push_back(*var_handles[i]); 77 results->handles.push_back(*var_handles[i]);
64 if (msg) 78 if (results->new_msg)
65 var.WriteDataToMessage(msg, base::Bind(&HandleWriter, handle_index)); 79 var.WriteDataToMessage(results->new_msg.get(),
80 base::Bind(&HandleWriter, &results->handle_index));
66 } 81 }
67 82
68 // For PpapiMsg_ResourceReply and the reply to PpapiHostMsg_ResourceSyncCall, 83 // For PpapiMsg_ResourceReply and the reply to PpapiHostMsg_ResourceSyncCall,
69 // the handles are carried inside the ResourceMessageReplyParams. 84 // the handles are carried inside the ResourceMessageReplyParams.
70 // NOTE: We only translate handles from host->NaCl. The only kind of 85 // NOTE: We only intercept handles from host->NaCl. The only kind of
71 // ResourceMessageParams that travels this direction is 86 // ResourceMessageParams that travels this direction is
72 // ResourceMessageReplyParams, so that's the only one we need to handle. 87 // ResourceMessageReplyParams, so that's the only one we need to handle.
73 void ConvertHandlesInParam( 88 void ScanParam(const ppapi::proxy::ResourceMessageReplyParams& params,
74 const ppapi::proxy::ResourceMessageReplyParams& params, 89 ScanningResults* results) {
75 Handles* handles, 90 // If the resource reply params don't contain handles, NULL the new message
76 IPC::Message* msg, 91 // pointer to cancel further rewriting.
77 int* handle_index) { 92 // NOTE: This works because only handles currently force rewriting, and we
78 // First, if we need to rewrite the message parameters, write everything 93 // know at this point that the message has none.
79 // before the handles (there's nothing after the handles). 94 if (params.handles().empty()) {
80 if (msg) { 95 results->new_msg.reset(NULL);
81 params.WriteReplyHeader(msg); 96 return;
Mark Seaborn 2013/11/02 00:57:44 Is this another early-out that you added in a more
bbudge 2013/11/02 01:18:50 This is the legitimate early out. I'll add it back
97 }
98
99 // If we need to rewrite the message, write everything before the handles
100 // (there's nothing after the handles).
101 if (results->new_msg) {
102 params.WriteReplyHeader(results->new_msg.get());
82 // IPC writes the vector length as an int before the contents of the 103 // IPC writes the vector length as an int before the contents of the
83 // vector. 104 // vector.
84 msg->WriteInt(static_cast<int>(params.handles().size())); 105 results->new_msg->WriteInt(static_cast<int>(params.handles().size()));
85 } 106 }
86 for (Handles::const_iterator iter = params.handles().begin(); 107 for (Handles::const_iterator iter = params.handles().begin();
87 iter != params.handles().end(); 108 iter != params.handles().end();
88 ++iter) { 109 ++iter) {
89 // ConvertHandle will write each handle to |msg|, if necessary. 110 // ScanParam will write each handle to the new message, if necessary.
90 ConvertHandlesInParam(*iter, handles, msg, handle_index); 111 ScanParam(*iter, results);
91 } 112 }
92 // Tell ResourceMessageReplyParams that we have taken the handles, so it 113 // Tell ResourceMessageReplyParams that we have taken the handles, so it
93 // shouldn't close them. The NaCl runtime will take ownership of them. 114 // shouldn't close them. The NaCl runtime will take ownership of them.
94 params.ConsumeHandles(); 115 params.ConsumeHandles();
95 } 116 }
96 117
97 // This overload is to catch all types other than SerializedHandle or 118 // Overload to match all other types. If we need to rewrite the message,
98 // ResourceMessageReplyParams. On Windows, |msg| will be a valid pointer, and we 119 // write the parameter.
99 // must write |param| to it.
100 template <class T> 120 template <class T>
101 void ConvertHandlesInParam(const T& param, 121 void ScanParam(const T& param, ScanningResults* results) {
102 Handles* /* handles */, 122 if (results->new_msg)
103 IPC::Message* msg, 123 IPC::WriteParam(results->new_msg.get(), param);
104 int* /* handle_index */) {
105 // It's not a handle, so just write to the output message, if necessary.
106 if (msg)
107 IPC::WriteParam(msg, param);
108 } 124 }
109 125
110 // These just break apart the given tuple and run ConvertHandle over each param. 126 // These just break apart the given tuple and run ScanParam over each param.
111 // The idea is to extract any handles in the tuple, while writing all data to 127 // The idea is to scan elements in the tuple which require special handling,
112 // msg (if msg is valid). The msg will only be valid on Windows, where we need 128 // and write them into the |results| struct.
113 // to re-write all of the message parameters, writing the handles in POSIX style
114 // for NaCl.
115 template <class A> 129 template <class A>
116 void ConvertHandlesImpl(const Tuple1<A>& t1, Handles* handles, 130 void ScanTuple(const Tuple1<A>& t1, ScanningResults* results) {
117 IPC::Message* msg) { 131 ScanParam(t1.a, results);
118 int handle_index = 0;
119 ConvertHandlesInParam(t1.a, handles, msg, &handle_index);
120 } 132 }
121 template <class A, class B> 133 template <class A, class B>
122 void ConvertHandlesImpl(const Tuple2<A, B>& t1, Handles* handles, 134 void ScanTuple(const Tuple2<A, B>& t1, ScanningResults* results) {
123 IPC::Message* msg) { 135 ScanParam(t1.a, results);
124 int handle_index = 0; 136 ScanParam(t1.b, results);
125 ConvertHandlesInParam(t1.a, handles, msg, &handle_index);
126 ConvertHandlesInParam(t1.b, handles, msg, &handle_index);
127 } 137 }
128 template <class A, class B, class C> 138 template <class A, class B, class C>
129 void ConvertHandlesImpl(const Tuple3<A, B, C>& t1, Handles* handles, 139 void ScanTuple(const Tuple3<A, B, C>& t1, ScanningResults* results) {
130 IPC::Message* msg) { 140 ScanParam(t1.a, results);
131 int handle_index = 0; 141 ScanParam(t1.b, results);
132 ConvertHandlesInParam(t1.a, handles, msg, &handle_index); 142 ScanParam(t1.c, results);
133 ConvertHandlesInParam(t1.b, handles, msg, &handle_index);
134 ConvertHandlesInParam(t1.c, handles, msg, &handle_index);
135 } 143 }
136 template <class A, class B, class C, class D> 144 template <class A, class B, class C, class D>
137 void ConvertHandlesImpl(const Tuple4<A, B, C, D>& t1, Handles* handles, 145 void ScanTuple(const Tuple4<A, B, C, D>& t1, ScanningResults* results) {
138 IPC::Message* msg) { 146 ScanParam(t1.a, results);
139 int handle_index = 0; 147 ScanParam(t1.b, results);
140 ConvertHandlesInParam(t1.a, handles, msg, &handle_index); 148 ScanParam(t1.c, results);
141 ConvertHandlesInParam(t1.b, handles, msg, &handle_index); 149 ScanParam(t1.d, results);
142 ConvertHandlesInParam(t1.c, handles, msg, &handle_index);
143 ConvertHandlesInParam(t1.d, handles, msg, &handle_index);
144 } 150 }
145 151
146 template <class MessageType> 152 template <class MessageType>
147 class HandleConverterImpl { 153 class MessageScannerImpl {
148 public: 154 public:
149 explicit HandleConverterImpl(const IPC::Message* msg) 155 explicit MessageScannerImpl(const IPC::Message* msg)
150 : msg_(static_cast<const MessageType*>(msg)) { 156 : msg_(static_cast<const MessageType*>(msg)) {
151 } 157 }
152 bool ConvertMessage(Handles* handles, IPC::Message* out_msg) { 158 bool ScanMessage(ScanningResults* results) {
153 typename TupleTypes<typename MessageType::Schema::Param>::ValueTuple params; 159 typename TupleTypes<typename MessageType::Schema::Param>::ValueTuple params;
154 if (!MessageType::Read(msg_, &params)) 160 if (!MessageType::Read(msg_, &params))
155 return false; 161 return false;
156 ConvertHandlesImpl(params, handles, out_msg); 162 ScanTuple(params, results);
157 return true; 163 return true;
158 } 164 }
159 165
160 bool ConvertReply(Handles* handles, IPC::SyncMessage* out_msg) { 166 bool ScanReply(ScanningResults* results) {
161 typename TupleTypes<typename MessageType::Schema::ReplyParam>::ValueTuple 167 typename TupleTypes<typename MessageType::Schema::ReplyParam>::ValueTuple
162 params; 168 params;
163 if (!MessageType::ReadReplyParam(msg_, &params)) 169 if (!MessageType::ReadReplyParam(msg_, &params))
164 return false; 170 return false;
165 // If we need to rewrite the message (i.e., on Windows), we need to make 171 // If we need to rewrite the message, write the message id first.
166 // sure we write the message id first. 172 if (results->new_msg) {
167 if (out_msg) { 173 results->new_msg->set_reply();
168 out_msg->set_reply();
169 int id = IPC::SyncMessage::GetMessageId(*msg_); 174 int id = IPC::SyncMessage::GetMessageId(*msg_);
170 out_msg->WriteInt(id); 175 results->new_msg->WriteInt(id);
171 } 176 }
172 ConvertHandlesImpl(params, handles, out_msg); 177 ScanTuple(params, results);
173 return true; 178 return true;
174 } 179 }
175 // TODO(dmichael): Add ConvertSyncMessage for outgoing sync messages, if we 180 // TODO(dmichael): Add ScanSyncMessage for outgoing sync messages, if we ever
176 // ever pass handles in one of those. 181 // need to scan those.
177 182
178 private: 183 private:
179 const MessageType* msg_; 184 const MessageType* msg_;
180 }; 185 };
181 186
182 } // namespace 187 } // namespace
183 188
184 #define CASE_FOR_MESSAGE(MESSAGE_TYPE) \ 189 #define CASE_FOR_MESSAGE(MESSAGE_TYPE) \
185 case MESSAGE_TYPE::ID: { \ 190 case MESSAGE_TYPE::ID: { \
186 HandleConverterImpl<MESSAGE_TYPE> extractor(&msg); \ 191 MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \
187 if (!extractor.ConvertMessage(handles, new_msg_ptr->get())) \ 192 if (rewrite_msg) \
193 results.new_msg.reset( \
194 new IPC::Message(msg.routing_id(), msg.type())); \
195 if (!scanner.ScanMessage(&results)) \
188 return false; \ 196 return false; \
189 break; \ 197 break; \
190 } 198 }
191 #define CASE_FOR_REPLY(MESSAGE_TYPE) \ 199 #define CASE_FOR_REPLY(MESSAGE_TYPE) \
192 case MESSAGE_TYPE::ID: { \ 200 case MESSAGE_TYPE::ID: { \
193 HandleConverterImpl<MESSAGE_TYPE> extractor(&msg); \ 201 MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \
194 if (!extractor.ConvertReply( \ 202 if (rewrite_msg) \
195 handles, \ 203 results.new_msg.reset( \
196 static_cast<IPC::SyncMessage*>(new_msg_ptr->get()))) \ 204 new IPC::Message(msg.routing_id(), msg.type())); \
205 if (!scanner.ScanReply(&results)) \
197 return false; \ 206 return false; \
198 break; \ 207 break; \
199 } 208 }
200 209
201 namespace ppapi { 210 namespace ppapi {
202 namespace proxy { 211 namespace proxy {
203 212
204 class SerializedHandle; 213 class SerializedHandle;
205 214
206 HandleConverter::HandleConverter() { 215 NaClMessageScanner::NaClMessageScanner() {
207 } 216 }
208 217
209 bool HandleConverter::ConvertNativeHandlesToPosix( 218 // Windows IPC differs from POSIX in that native handles are serialized in the
219 // message body, rather than passed in a separate FileDescriptorSet. Therefore,
220 // on Windows, any message containing handles must be rewritten in the POSIX
221 // format before we can send it to the NaCl plugin.
222 //
223 // On POSIX and Windows we have to rewrite PpapiMsg_CreateNaClChannel messages.
224 // These contain a handle with an invalid (place holder) descriptor. We need to
225 // locate this handle so it can be replaced with a valid one when the channel is
226 // created.
227 bool NaClMessageScanner::ScanMessage(
210 const IPC::Message& msg, 228 const IPC::Message& msg,
211 std::vector<SerializedHandle>* handles, 229 std::vector<SerializedHandle>* handles,
212 scoped_ptr<IPC::Message>* new_msg_ptr) { 230 scoped_ptr<IPC::Message>* new_msg_ptr) {
213 DCHECK(handles); 231 DCHECK(handles);
232 DCHECK(handles->empty());
214 DCHECK(new_msg_ptr); 233 DCHECK(new_msg_ptr);
215 DCHECK(!new_msg_ptr->get()); 234 DCHECK(!new_msg_ptr->get());
216 235
217 // In Windows, we need to re-write the contents of the message. This is 236 bool rewrite_msg =
218 // because in Windows IPC code, native HANDLE values are serialized in the
219 // body of the message.
220 //
221 // In POSIX, we only serialize an index in to a FileDescriptorSet, and the
222 // actual file descriptors are sent out-of-band. So on Windows, to make a
223 // message that's compatible with Windows, we need to write a new message that
224 // has simple indices in the message body instead of the HANDLEs.
225 //
226 // NOTE: This means on Windows, new_msg_ptr's serialized contents are not
227 // compatible with Windows IPC deserialization code; it is intended to be
228 // passed to NaCl.
229 #if defined(OS_WIN) 237 #if defined(OS_WIN)
230 new_msg_ptr->reset(new IPC::Message(msg.routing_id(), msg.type())); 238 true;
231 #else 239 #else
232 // Even on POSIX, we have to rewrite messages to create channels, because 240 (msg.type() == PpapiMsg_CreateNaClChannel::ID);
233 // these contain a handle with an invalid (place holder) descriptor. The
234 // message sending code sees this and doesn't pass the descriptor over
235 // correctly.
236 if (msg.type() == PpapiMsg_CreateNaClChannel::ID)
237 new_msg_ptr->reset(new IPC::Message(msg.routing_id(), msg.type()));
238 #endif 241 #endif
239 242
243 // We can't always tell from the message ID if rewriting is needed. Therefore,
244 // scan any message types that might contain a handle. If we later determine
245 // that there are no handles, we can cancel the rewriting by clearing the
246 // results.new_msg pointer.
247 ScanningResults results;
240 switch (msg.type()) { 248 switch (msg.type()) {
241 CASE_FOR_MESSAGE(PpapiMsg_CreateNaClChannel) 249 CASE_FOR_MESSAGE(PpapiMsg_CreateNaClChannel)
242 CASE_FOR_MESSAGE(PpapiMsg_PPBAudio_NotifyAudioStreamCreated) 250 CASE_FOR_MESSAGE(PpapiMsg_PPBAudio_NotifyAudioStreamCreated)
243 CASE_FOR_MESSAGE(PpapiMsg_PPPMessaging_HandleMessage) 251 CASE_FOR_MESSAGE(PpapiMsg_PPPMessaging_HandleMessage)
244 CASE_FOR_MESSAGE(PpapiPluginMsg_ResourceReply) 252 CASE_FOR_MESSAGE(PpapiPluginMsg_ResourceReply)
245 case IPC_REPLY_ID: { 253 case IPC_REPLY_ID: {
246 int id = IPC::SyncMessage::GetMessageId(msg); 254 int id = IPC::SyncMessage::GetMessageId(msg);
247 PendingSyncMsgMap::iterator iter(pending_sync_msgs_.find(id)); 255 PendingSyncMsgMap::iterator iter(pending_sync_msgs_.find(id));
248 if (iter == pending_sync_msgs_.end()) { 256 if (iter == pending_sync_msgs_.end()) {
249 NOTREACHED(); 257 NOTREACHED();
250 return false; 258 return false;
251 } 259 }
252 uint32_t type = iter->second; 260 uint32_t type = iter->second;
253 pending_sync_msgs_.erase(iter); 261 pending_sync_msgs_.erase(iter);
254 switch (type) { 262 switch (type) {
255 CASE_FOR_REPLY(PpapiHostMsg_PPBGraphics3D_GetTransferBuffer) 263 CASE_FOR_REPLY(PpapiHostMsg_PPBGraphics3D_GetTransferBuffer)
256 CASE_FOR_REPLY(PpapiHostMsg_PPBImageData_CreateSimple) 264 CASE_FOR_REPLY(PpapiHostMsg_PPBImageData_CreateSimple)
257 CASE_FOR_REPLY(PpapiHostMsg_ResourceSyncCall) 265 CASE_FOR_REPLY(PpapiHostMsg_ResourceSyncCall)
258 CASE_FOR_REPLY(PpapiHostMsg_SharedMemory_CreateSharedMemory) 266 CASE_FOR_REPLY(PpapiHostMsg_SharedMemory_CreateSharedMemory)
259 default: 267 default:
260 // Do nothing for messages we don't know. 268 // Do nothing for messages we don't know.
261 break; 269 break;
262 } 270 }
263 break; 271 break;
264 } 272 }
265 default: 273 default:
266 // Do nothing for messages we don't know. 274 // Do nothing for messages we don't know.
267 break; 275 break;
268 } 276 }
277
278 // Only messages containing handles need to be rewritten. If no handles are
279 // found, don't return the rewritten message either. This must be changed if
280 // we ever add new param types that also require rewriting.
281 if (!results.handles.empty()) {
Mark Seaborn 2013/11/02 00:57:44 Wasn't this change also part of the changes to the
bbudge 2013/11/02 01:18:50 This corresponds to the check in NaClIPCAdapter th
282 handles->swap(results.handles);
283 *new_msg_ptr = results.new_msg.Pass();
284 }
269 return true; 285 return true;
270 } 286 }
271 287
272 void HandleConverter::RegisterSyncMessageForReply(const IPC::Message& msg) { 288 void NaClMessageScanner::RegisterSyncMessageForReply(const IPC::Message& msg) {
273 DCHECK(msg.is_sync()); 289 DCHECK(msg.is_sync());
274 290
275 int msg_id = IPC::SyncMessage::GetMessageId(msg); 291 int msg_id = IPC::SyncMessage::GetMessageId(msg);
276 DCHECK(pending_sync_msgs_.find(msg_id) == pending_sync_msgs_.end()); 292 DCHECK(pending_sync_msgs_.find(msg_id) == pending_sync_msgs_.end());
277 293
278 pending_sync_msgs_[msg_id] = msg.type(); 294 pending_sync_msgs_[msg_id] = msg.type();
279 } 295 }
280 296
281 } // namespace proxy 297 } // namespace proxy
282 } // namespace ppapi 298 } // namespace ppapi
OLDNEW
« components/nacl/loader/nacl_ipc_adapter.cc ('K') | « ppapi/proxy/nacl_message_scanner.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698