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

Side by Side Diff: chrome/browser/net/resolve_proxy_msg_helper_unittest.cc

Issue 160619: Remove dependency on SingleThreadedProxyResolver from resolve_proxy_msg_helper_unittest (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Address willchan's comments + sync Created 11 years, 4 months 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
« no previous file with comments | « no previous file | net/net.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 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 "chrome/browser/net/resolve_proxy_msg_helper.h" 5 #include "chrome/browser/net/resolve_proxy_msg_helper.h"
6 6
7 #include "base/waitable_event.h" 7 #include "base/waitable_event.h"
8 #include "net/base/net_errors.h" 8 #include "net/base/net_errors.h"
9 #include "net/proxy/mock_proxy_resolver.h"
9 #include "net/proxy/proxy_config_service.h" 10 #include "net/proxy/proxy_config_service.h"
10 #include "net/proxy/proxy_resolver.h"
11 #include "net/proxy/single_threaded_proxy_resolver.h"
12 #include "testing/gtest/include/gtest/gtest.h" 11 #include "testing/gtest/include/gtest/gtest.h"
13 12
14 // This ProxyConfigService always returns "http://pac" as the PAC url to use. 13 // This ProxyConfigService always returns "http://pac" as the PAC url to use.
15 class MockProxyConfigService: public net::ProxyConfigService { 14 class MockProxyConfigService : public net::ProxyConfigService {
16 public: 15 public:
17 virtual int GetProxyConfig(net::ProxyConfig* results) { 16 virtual int GetProxyConfig(net::ProxyConfig* results) {
18 results->pac_url = GURL("http://pac"); 17 results->pac_url = GURL("http://pac");
19 return net::OK; 18 return net::OK;
20 } 19 }
21 }; 20 };
22 21
23 // This PAC resolver always returns the hostname of the query URL as the 22 class MyDelegate : public ResolveProxyMsgHelper::Delegate {
24 // proxy to use. The Block() method will make GetProxyForURL() hang until
25 // Unblock() is called.
26 class SyncMockProxyResolver : public net::ProxyResolver {
27 public: 23 public:
28 SyncMockProxyResolver() : ProxyResolver(false /*expects_pac_bytes*/), 24 struct PendingResult {
29 event_(false, false), 25 PendingResult(IPC::Message* msg,
30 is_blocked_(false) { 26 int error_code,
27 const std::string& proxy_list)
28 : msg(msg), error_code(error_code), proxy_list(proxy_list) {
29 }
30
31 IPC::Message* msg;
32 int error_code;
33 std::string proxy_list;
34 };
35
36 // ResolveProxyMsgHelper::Delegate implementation:
37 virtual void OnResolveProxyCompleted(IPC::Message* reply_msg,
38 int error_code,
39 const std::string& proxy_list) {
40 DCHECK(!pending_result_.get());
41 pending_result_.reset(new PendingResult(reply_msg, error_code, proxy_list));
31 } 42 }
32 43
33 virtual int GetProxyForURL(const GURL& query_url, 44 const PendingResult* pending_result() const { return pending_result_.get(); }
34 net::ProxyInfo* results,
35 net::CompletionCallback* callback,
36 RequestHandle* request) {
37 if (is_blocked_)
38 event_.Wait();
39 results->UseNamedProxy(query_url.host());
40 return net::OK;
41 }
42 45
43 virtual void CancelRequest(RequestHandle request) { 46 void clear_pending_result() {
44 NOTREACHED(); 47 pending_result_.reset();
45 }
46
47 virtual int SetPacScript(const GURL& /*pac_url*/,
48 const std::string& /*bytes*/,
49 net::CompletionCallback* /*callback*/) {
50 return net::OK;
51 }
52
53 void Block() {
54 is_blocked_ = true;
55 event_.Reset();
56 }
57
58 void Unblock() {
59 is_blocked_ = false;
60 event_.Signal();
61 } 48 }
62 49
63 private: 50 private:
64 base::WaitableEvent event_; 51 scoped_ptr<PendingResult> pending_result_;
65 bool is_blocked_;
66 }; 52 };
67 53
68 class MockProxyResolver : public net::SingleThreadedProxyResolver {
69 public:
70 MockProxyResolver()
71 : net::SingleThreadedProxyResolver(new SyncMockProxyResolver) {
72 x = reinterpret_cast<SyncMockProxyResolver*>(resolver());
73 }
74
75 // TODO(eroman): cleanup.
76 SyncMockProxyResolver* x;
77 };
78
79 // This struct holds the values that were passed to
80 // Delegate::OnResolveProxyCompleted(). The caller should use WaitUntilDone()
81 // to block until the result has been populated.
82 struct ResultFuture {
83 public:
84 ResultFuture()
85 : reply_msg(NULL),
86 error_code(0),
87 started_(false, false),
88 completed_(false, false) {
89 }
90
91 // Wait until the request has completed. In other words we have invoked:
92 // ResolveProxyMsgHelper::Delegate::OnResolveProxyCompleted.
93 void WaitUntilDone() {
94 completed_.Wait();
95 }
96
97 // Wait until the request has been sent to ResolveProxyMsgHelper.
98 void WaitUntilStarted() {
99 started_.Wait();
100 }
101
102 bool TimedWaitUntilDone(const base::TimeDelta& max_time) {
103 return completed_.TimedWait(max_time);
104 }
105
106 // These fields are only valid after returning from WaitUntilDone().
107 IPC::Message* reply_msg;
108 int error_code;
109 std::string proxy_list;
110
111 private:
112 friend class AsyncRequestRunner;
113 base::WaitableEvent started_;
114 base::WaitableEvent completed_;
115 };
116
117 // This class lives on the io thread. It starts async requests using the
118 // class under test (ResolveProxyMsgHelper), and signals the result future on
119 // completion.
120 class AsyncRequestRunner : public ResolveProxyMsgHelper::Delegate {
121 public:
122 AsyncRequestRunner(net::ProxyService* proxy_service) {
123 resolve_proxy_msg_helper_.reset(
124 new ResolveProxyMsgHelper(this, proxy_service));
125 }
126
127 void Start(ResultFuture* future, const GURL& url, IPC::Message* reply_msg) {
128 futures_.push_back(future);
129 resolve_proxy_msg_helper_->Start(url, reply_msg);
130
131 // Notify of request start.
132 future->started_.Signal();
133 }
134
135 virtual void OnResolveProxyCompleted(IPC::Message* reply_msg,
136 int error_code,
137 const std::string& proxy_list) {
138 // Update the result future for this request (top of queue), and signal it.
139 ResultFuture* future = futures_.front();
140 futures_.pop_front();
141
142 future->reply_msg = reply_msg;
143 future->error_code = error_code;
144 future->proxy_list = proxy_list;
145
146 // Notify of request completion.
147 future->completed_.Signal();
148 }
149
150 private:
151 std::deque<ResultFuture*> futures_;
152 scoped_ptr<ResolveProxyMsgHelper> resolve_proxy_msg_helper_;
153 };
154
155 // Helper class to start async requests on an io thread, and return a
156 // result future. The caller then uses ResultFuture::WaitUntilDone() to
157 // get at the results. It "bridges" the originating thread with the helper
158 // io thread.
159 class RunnerBridge {
160 public:
161 RunnerBridge() : io_thread_("io_thread"), done_(false, false) {
162 // Start an io thread where we will run the async requests.
163 base::Thread::Options options;
164 options.message_loop_type = MessageLoop::TYPE_IO;
165 io_thread_.StartWithOptions(options);
166
167 // Construct the state that lives on io thread.
168 io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
169 this, &RunnerBridge::DoConstruct));
170 done_.Wait();
171 }
172
173 // Start an async request on the io thread.
174 ResultFuture* Start(const GURL& url, IPC::Message* reply_msg) {
175 ResultFuture* future = new ResultFuture();
176
177 io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
178 async_runner_, &AsyncRequestRunner::Start, future, url, reply_msg));
179
180 return future;
181 }
182
183 void DestroyAsyncRunner() {
184 io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
185 this, &RunnerBridge::DoDestroyAsyncRunner));
186 done_.Wait();
187 }
188
189 ~RunnerBridge() {
190 io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
191 this, &RunnerBridge::DoDestroy));
192 done_.Wait();
193 }
194
195 MockProxyResolver* proxy_resolver() {
196 return proxy_resolver_;
197 }
198
199 // Called from io thread.
200 void DoConstruct() {
201 proxy_resolver_ = new MockProxyResolver();
202 proxy_service_ = new net::ProxyService(new MockProxyConfigService(),
203 proxy_resolver_);
204 async_runner_ = new AsyncRequestRunner(proxy_service_);
205 done_.Signal();
206 }
207
208 // Called from io thread.
209 void DoDestroy() {
210 delete async_runner_;
211 delete proxy_service_;
212 done_.Signal();
213 }
214
215 // Called from io thread.
216 void DoDestroyAsyncRunner() {
217 delete async_runner_;
218 async_runner_ = NULL;
219 done_.Signal();
220 }
221
222 private:
223 base::Thread io_thread_;
224 base::WaitableEvent done_;
225
226 net::ProxyService* proxy_service_;
227 MockProxyResolver* proxy_resolver_; // Owned by proxy_service_.
228
229 AsyncRequestRunner* async_runner_;
230 };
231
232 // Avoid the need to have an AddRef / Release
233 template<>
234 void RunnableMethodTraits<RunnerBridge>::RetainCallee(RunnerBridge*) {}
235 template<>
236 void RunnableMethodTraits<RunnerBridge>::ReleaseCallee(RunnerBridge*) {}
237
238 template<>
239 void RunnableMethodTraits<AsyncRequestRunner>::RetainCallee(
240 AsyncRequestRunner*) {}
241 template<>
242 void RunnableMethodTraits<AsyncRequestRunner>::ReleaseCallee(
243 AsyncRequestRunner*) {}
244
245
246 // Issue three sequential requests -- each should succeed. 54 // Issue three sequential requests -- each should succeed.
247 TEST(ResolveProxyMsgHelperTest, Sequential) { 55 TEST(ResolveProxyMsgHelperTest, Sequential) {
248 RunnerBridge runner; 56 net::MockAsyncProxyResolver* resolver = new net::MockAsyncProxyResolver;
57 net::ProxyService service(new MockProxyConfigService, resolver);
58
59 MyDelegate delegate;
60 ResolveProxyMsgHelper helper(&delegate, &service);
249 61
250 GURL url1("http://www.google1.com/"); 62 GURL url1("http://www.google1.com/");
251 GURL url2("http://www.google2.com/"); 63 GURL url2("http://www.google2.com/");
252 GURL url3("http://www.google3.com/"); 64 GURL url3("http://www.google3.com/");
253 65
254 scoped_ptr<IPC::Message> msg1(new IPC::Message()); 66 scoped_ptr<IPC::Message> msg1(new IPC::Message());
255 scoped_ptr<IPC::Message> msg2(new IPC::Message()); 67 scoped_ptr<IPC::Message> msg2(new IPC::Message());
256 scoped_ptr<IPC::Message> msg3(new IPC::Message()); 68 scoped_ptr<IPC::Message> msg3(new IPC::Message());
257 69
258 // Execute each request sequentially (so there are never 2 requests 70 // Execute each request sequentially (so there are never 2 requests
259 // outstanding at the same time). 71 // outstanding at the same time).
260 72
261 scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1.get())); 73 helper.Start(url1, msg1.get());
262 result1->WaitUntilDone();
263 74
264 scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2.get())); 75 // Finish ProxyService's initialization.
265 result2->WaitUntilDone(); 76 resolver->pending_set_pac_script_request()->CompleteNow(net::OK);
266 77
267 scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3.get())); 78 ASSERT_EQ(1u, resolver->pending_requests().size());
268 result3->WaitUntilDone(); 79 EXPECT_EQ(url1, resolver->pending_requests()[0]->url());
80 resolver->pending_requests()[0]->results()->UseNamedProxy("result1:80");
81 resolver->pending_requests()[0]->CompleteNow(net::OK);
269 82
270 // Check that each request gave the expected result. 83 // Check result.
84 EXPECT_EQ(msg1.get(), delegate.pending_result()->msg);
85 EXPECT_EQ(net::OK, delegate.pending_result()->error_code);
86 EXPECT_EQ("PROXY result1:80", delegate.pending_result()->proxy_list);
87 delegate.clear_pending_result();
271 88
272 EXPECT_EQ(msg1.get(), result1->reply_msg); 89 helper.Start(url2, msg2.get());
273 EXPECT_EQ(net::OK, result1->error_code);
274 EXPECT_EQ("PROXY www.google1.com:80", result1->proxy_list);
275 90
276 EXPECT_EQ(msg2.get(), result2->reply_msg); 91 ASSERT_EQ(1u, resolver->pending_requests().size());
277 EXPECT_EQ(net::OK, result2->error_code); 92 EXPECT_EQ(url2, resolver->pending_requests()[0]->url());
278 EXPECT_EQ("PROXY www.google2.com:80", result2->proxy_list); 93 resolver->pending_requests()[0]->results()->UseNamedProxy("result2:80");
94 resolver->pending_requests()[0]->CompleteNow(net::OK);
279 95
280 EXPECT_EQ(msg3.get(), result3->reply_msg); 96 // Check result.
281 EXPECT_EQ(net::OK, result3->error_code); 97 EXPECT_EQ(msg2.get(), delegate.pending_result()->msg);
282 EXPECT_EQ("PROXY www.google3.com:80", result3->proxy_list); 98 EXPECT_EQ(net::OK, delegate.pending_result()->error_code);
99 EXPECT_EQ("PROXY result2:80", delegate.pending_result()->proxy_list);
100 delegate.clear_pending_result();
101
102 helper.Start(url3, msg3.get());
103
104 ASSERT_EQ(1u, resolver->pending_requests().size());
105 EXPECT_EQ(url3, resolver->pending_requests()[0]->url());
106 resolver->pending_requests()[0]->results()->UseNamedProxy("result3:80");
107 resolver->pending_requests()[0]->CompleteNow(net::OK);
108
109 // Check result.
110 EXPECT_EQ(msg3.get(), delegate.pending_result()->msg);
111 EXPECT_EQ(net::OK, delegate.pending_result()->error_code);
112 EXPECT_EQ("PROXY result3:80", delegate.pending_result()->proxy_list);
113 delegate.clear_pending_result();
283 } 114 }
284 115
285 // Issue a request while one is already in progress -- should be queued. 116 // Issue a request while one is already in progress -- should be queued.
286 TEST(ResolveProxyMsgHelperTest, QueueRequests) { 117 TEST(ResolveProxyMsgHelperTest, QueueRequests) {
287 RunnerBridge runner; 118 net::MockAsyncProxyResolver* resolver = new net::MockAsyncProxyResolver;
119 net::ProxyService service(new MockProxyConfigService, resolver);
120
121 MyDelegate delegate;
122 ResolveProxyMsgHelper helper(&delegate, &service);
288 123
289 GURL url1("http://www.google1.com/"); 124 GURL url1("http://www.google1.com/");
290 GURL url2("http://www.google2.com/"); 125 GURL url2("http://www.google2.com/");
291 GURL url3("http://www.google3.com/"); 126 GURL url3("http://www.google3.com/");
292 127
293 scoped_ptr<IPC::Message> msg1(new IPC::Message()); 128 scoped_ptr<IPC::Message> msg1(new IPC::Message());
294 scoped_ptr<IPC::Message> msg2(new IPC::Message()); 129 scoped_ptr<IPC::Message> msg2(new IPC::Message());
295 scoped_ptr<IPC::Message> msg3(new IPC::Message()); 130 scoped_ptr<IPC::Message> msg3(new IPC::Message());
296 131
297 // Make the proxy resolver hang on the next request. 132 // Start three requests. Since the proxy resolver is async, all the
298 runner.proxy_resolver()->x->Block(); 133 // requests will be pending.
299 134
300 // Start three requests. Since the proxy resolver is hung, the second two 135 helper.Start(url1, msg1.get());
301 // will be pending.
302 136
303 scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1.get())); 137 // Finish ProxyService's initialization.
304 scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2.get())); 138 resolver->pending_set_pac_script_request()->CompleteNow(net::OK);
305 scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3.get()));
306 139
307 // Wait for the final request to have been scheduled. Otherwise we may rush 140 helper.Start(url2, msg2.get());
308 // to calling Unblock() without actually having blocked anything. 141 helper.Start(url3, msg3.get());
309 result3->WaitUntilStarted();
310 142
311 // Unblock the proxy service so requests 1-3 can complete. 143 // ResolveProxyHelper only keeps 1 request outstanding in ProxyService
312 runner.proxy_resolver()->x->Unblock(); 144 // at a time.
145 ASSERT_EQ(1u, resolver->pending_requests().size());
146 EXPECT_EQ(url1, resolver->pending_requests()[0]->url());
313 147
314 // Wait for all the requests to finish (they run in FIFO order). 148 resolver->pending_requests()[0]->results()->UseNamedProxy("result1:80");
315 result3->WaitUntilDone(); 149 resolver->pending_requests()[0]->CompleteNow(net::OK);
316 150
317 // Check that each call invoked the callback with the right parameters. 151 // Check result.
152 EXPECT_EQ(msg1.get(), delegate.pending_result()->msg);
153 EXPECT_EQ(net::OK, delegate.pending_result()->error_code);
154 EXPECT_EQ("PROXY result1:80", delegate.pending_result()->proxy_list);
155 delegate.clear_pending_result();
318 156
319 EXPECT_EQ(msg1.get(), result1->reply_msg); 157 ASSERT_EQ(1u, resolver->pending_requests().size());
320 EXPECT_EQ(net::OK, result1->error_code); 158 EXPECT_EQ(url2, resolver->pending_requests()[0]->url());
321 EXPECT_EQ("PROXY www.google1.com:80", result1->proxy_list);
322 159
323 EXPECT_EQ(msg2.get(), result2->reply_msg); 160 resolver->pending_requests()[0]->results()->UseNamedProxy("result2:80");
324 EXPECT_EQ(net::OK, result2->error_code); 161 resolver->pending_requests()[0]->CompleteNow(net::OK);
325 EXPECT_EQ("PROXY www.google2.com:80", result2->proxy_list);
326 162
327 EXPECT_EQ(msg3.get(), result3->reply_msg); 163 // Check result.
328 EXPECT_EQ(net::OK, result3->error_code); 164 EXPECT_EQ(msg2.get(), delegate.pending_result()->msg);
329 EXPECT_EQ("PROXY www.google3.com:80", result3->proxy_list); 165 EXPECT_EQ(net::OK, delegate.pending_result()->error_code);
166 EXPECT_EQ("PROXY result2:80", delegate.pending_result()->proxy_list);
167 delegate.clear_pending_result();
168
169 ASSERT_EQ(1u, resolver->pending_requests().size());
170 EXPECT_EQ(url3, resolver->pending_requests()[0]->url());
171
172 resolver->pending_requests()[0]->results()->UseNamedProxy("result3:80");
173 resolver->pending_requests()[0]->CompleteNow(net::OK);
174
175 // Check result.
176 EXPECT_EQ(msg3.get(), delegate.pending_result()->msg);
177 EXPECT_EQ(net::OK, delegate.pending_result()->error_code);
178 EXPECT_EQ("PROXY result3:80", delegate.pending_result()->proxy_list);
179 delegate.clear_pending_result();
330 } 180 }
331 181
332 // Delete the helper while a request is in progress, and others are pending. 182 // Delete the helper while a request is in progress, and others are pending.
333 TEST(ResolveProxyMsgHelperTest, CancelPendingRequests) { 183 TEST(ResolveProxyMsgHelperTest, CancelPendingRequests) {
334 RunnerBridge runner; 184 net::MockAsyncProxyResolver* resolver = new net::MockAsyncProxyResolver;
185 net::ProxyService service(new MockProxyConfigService, resolver);
186
187 MyDelegate delegate;
188 scoped_ptr<ResolveProxyMsgHelper> helper(
189 new ResolveProxyMsgHelper(&delegate, &service));
335 190
336 GURL url1("http://www.google1.com/"); 191 GURL url1("http://www.google1.com/");
337 GURL url2("http://www.google2.com/"); 192 GURL url2("http://www.google2.com/");
338 GURL url3("http://www.google3.com/"); 193 GURL url3("http://www.google3.com/");
339 194
340 // NOTE: these are not scoped ptr, since they will be deleted by the 195 // NOTE: these are not scoped ptr, since they will be deleted by the
341 // request's cancellation. 196 // request's cancellation.
342 IPC::Message* msg1 = new IPC::Message(); 197 IPC::Message* msg1 = new IPC::Message();
343 IPC::Message* msg2 = new IPC::Message(); 198 IPC::Message* msg2 = new IPC::Message();
344 IPC::Message* msg3 = new IPC::Message(); 199 IPC::Message* msg3 = new IPC::Message();
345 200
346 // Make the next request block. 201 // Start three requests. Since the proxy resolver is async, all the
347 runner.proxy_resolver()->x->Block(); 202 // requests will be pending.
348 203
349 // Start three requests; since the first one blocked, the other two should 204 helper->Start(url1, msg1);
350 // be pending.
351 205
352 scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1)); 206 // Finish ProxyService's initialization.
353 scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2)); 207 resolver->pending_set_pac_script_request()->CompleteNow(net::OK);
354 scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3));
355 208
356 result3->WaitUntilStarted(); 209 helper->Start(url2, msg2);
210 helper->Start(url3, msg3);
211
212 // ResolveProxyHelper only keeps 1 request outstanding in ProxyService
213 // at a time.
214 ASSERT_EQ(1u, resolver->pending_requests().size());
215 EXPECT_EQ(url1, resolver->pending_requests()[0]->url());
357 216
358 // Delete the underlying ResolveProxyMsgHelper -- this should cancel all 217 // Delete the underlying ResolveProxyMsgHelper -- this should cancel all
359 // the requests which are outstanding. 218 // the requests which are outstanding.
360 runner.DestroyAsyncRunner(); 219 helper.reset();
361 220
362 // Unblocking the proxy resolver means the three requests can complete -- 221 // The pending requests sent to the proxy resolver should have been cancelled.
363 // however they should not try to notify the delegate since we have already
364 // deleted the helper.
365 runner.proxy_resolver()->x->Unblock();
366 222
367 // Check that none of the requests were sent to the delegate. 223 EXPECT_EQ(0u, resolver->pending_requests().size());
368 EXPECT_FALSE( 224
369 result1->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2))); 225 EXPECT_EQ(NULL, delegate.pending_result());
370 EXPECT_FALSE(
371 result2->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2)));
372 EXPECT_FALSE(
373 result3->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2)));
374 226
375 // It should also be the case that msg1, msg2, msg3 were deleted by the 227 // It should also be the case that msg1, msg2, msg3 were deleted by the
376 // cancellation. (Else will show up as a leak in Purify). 228 // cancellation. (Else will show up as a leak in Purify/Valgrind).
377 } 229 }
OLDNEW
« no previous file with comments | « no previous file | net/net.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698