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

Unified Diff: net/proxy/proxy_resolver_mojo_unittest.cc

Issue 917863005: Implementation of ProxyResolver that uses a Mojo service. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@sam-v8-pac-utility-proxy
Patch Set: Address review comments. Created 5 years, 10 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/proxy/proxy_resolver_mojo.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/proxy/proxy_resolver_mojo_unittest.cc
diff --git a/net/proxy/proxy_resolver_mojo_unittest.cc b/net/proxy/proxy_resolver_mojo_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4de66fd70f7daa67455a56d17cd53a2506d1bfb6
--- /dev/null
+++ b/net/proxy/proxy_resolver_mojo_unittest.cc
@@ -0,0 +1,530 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/proxy/proxy_resolver_mojo.h"
+
+#include <list>
+#include <map>
+#include <string>
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "mojo/common/common_type_converters.h"
+#include "net/base/net_log.h"
+#include "net/base/test_completion_callback.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/proxy/mojo_proxy_resolver_factory.h"
+#include "net/proxy/mojo_type_converters.h"
+#include "net/proxy/proxy_info.h"
+#include "net/proxy/proxy_resolver_script_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h"
+#include "third_party/mojo/src/mojo/public/cpp/bindings/error_handler.h"
+#include "url/gurl.h"
+
+namespace net {
+
+namespace {
+
+static const char kScriptData[] = "FooBarBaz";
+
+struct SetPacScriptAction {
+ public:
+ enum Action {
+ COMPLETE,
+ DROP,
+ };
+
+ static SetPacScriptAction ReturnResult(int error) {
+ SetPacScriptAction result;
+ result.error = error;
+ return result;
+ }
+
+ static SetPacScriptAction DropRequest() {
+ SetPacScriptAction result;
+ result.action = DROP;
+ return result;
+ }
+
+ Action action = COMPLETE;
+ int error = OK;
+};
+
+class MockMojoProxyResolver : public interfaces::ProxyResolver {
+ public:
+ explicit MockMojoProxyResolver(
+ mojo::InterfaceRequest<interfaces::ProxyResolver> req)
+ : pac_script_calls_(0), binding_(this, req.Pass()) {}
+ ~MockMojoProxyResolver() override {
+ STLDeleteContainerPairSecondPointers(proxy_results_.begin(),
+ proxy_results_.end());
+ };
+
+ void AddPacScriptAction(SetPacScriptAction action);
+ // Returned script data is UTF8.
+ std::string GetPacScriptData() { return pac_script_data_; }
+ int GetPacScriptCalls() { return pac_script_calls_; }
+
+ void AddProxyResult(const GURL& url,
+ int error,
+ mojo::Array<interfaces::ProxyServerPtr> proxy_servers);
+ void CloseRequestClient(const GURL& url);
+
+ void Disconnect() { binding_.Close(); }
+
+ void WaitForSetPacScript();
+
+ private:
+ struct ProxyResults {
+ explicit ProxyResults(bool close)
+ : close(close), error(OK), proxy_servers() {}
+ ProxyResults(int err, mojo::Array<interfaces::ProxyServerPtr>& s)
+ : close(false), error(err), proxy_servers(s.Pass()) {}
+
+ bool close;
+ int error;
+ mojo::Array<interfaces::ProxyServerPtr> proxy_servers;
+ };
+
+ // Overridden from interfaces::ProxyResolver:
+ void SetPacScript(const mojo::String& data,
+ const mojo::Callback<void(int32_t)>& callback) override;
+ void GetProxyForUrl(
+ const mojo::String& url,
+ interfaces::ProxyResolverRequestClientPtr client) override;
+
+ std::string pac_script_data_;
+ std::list<SetPacScriptAction> pac_script_actions_;
+ int pac_script_calls_;
+ base::Closure pac_script_quit_closure_;
+
+ std::map<GURL, ProxyResults*> proxy_results_;
+
+ mojo::Binding<interfaces::ProxyResolver> binding_;
+};
+
+void MockMojoProxyResolver::AddProxyResult(
+ const GURL& url,
+ int error,
+ mojo::Array<interfaces::ProxyServerPtr> proxy_servers) {
+ proxy_results_[url] = new ProxyResults(error, proxy_servers);
+}
+
+void MockMojoProxyResolver::CloseRequestClient(const GURL& url) {
+ proxy_results_[url] = new ProxyResults(true);
+}
+
+void MockMojoProxyResolver::AddPacScriptAction(SetPacScriptAction action) {
+ pac_script_actions_.push_back(action);
+}
+
+void MockMojoProxyResolver::SetPacScript(
+ const mojo::String& data,
+ const mojo::Callback<void(int32_t)>& callback) {
+ pac_script_data_ = data.To<std::string>();
+ pac_script_calls_++;
+
+ DCHECK(!pac_script_actions_.empty());
+ SetPacScriptAction action = pac_script_actions_.front();
+ pac_script_actions_.pop_front();
+
+ switch (action.action) {
+ case SetPacScriptAction::COMPLETE:
+ callback.Run(action.error);
+ break;
+ case SetPacScriptAction::DROP:
+ binding_.Close();
+ break;
+ }
+
+ if (!pac_script_quit_closure_.is_null())
+ pac_script_quit_closure_.Run();
+ pac_script_quit_closure_.Reset();
+}
+
+void MockMojoProxyResolver::WaitForSetPacScript() {
+ if (pac_script_calls_)
+ return;
+
+ base::RunLoop run_loop;
+ pac_script_quit_closure_ = run_loop.QuitClosure();
+ run_loop.Run();
+}
+
+void MockMojoProxyResolver::GetProxyForUrl(
+ const mojo::String& url,
+ interfaces::ProxyResolverRequestClientPtr client) {
+ ProxyResults* result = proxy_results_[url.To<GURL>()];
+ ASSERT_NE(nullptr, result);
+ if (result->close) {
+ client.reset();
+ } else {
+ client->ReportResult(result->error, result->proxy_servers.Clone());
+ }
+}
+
+class TestMojoProxyResolverFactory : public MojoProxyResolverFactory {
+ public:
+ TestMojoProxyResolverFactory();
+ ~TestMojoProxyResolverFactory();
+
+ // Overridden from MojoProxyResolverFactory:
+ void Create(mojo::InterfaceRequest<interfaces::ProxyResolver> req,
+ interfaces::HostResolverPtr host_resolver) override;
+
+ MockMojoProxyResolver* GetMockResolver() {
Sam McNally 2015/02/17 06:23:09 MockMojoProxyResolver& mock_resolver()
Anand Mistry (off Chromium) 2015/02/18 06:48:52 Done.
+ return mock_proxy_resolver_.get();
+ }
+
+ void AddFuturePacScriptAction(int creation, SetPacScriptAction action);
+
+ int NumCreateCalls() const { return num_create_calls_; }
Sam McNally 2015/02/17 06:23:09 num_create_calls()
Anand Mistry (off Chromium) 2015/02/18 06:48:52 Done.
+ void WaitForCreateCalls(int num);
+
+ private:
+ int num_create_calls_;
+ base::Closure run_loop_quit_closure_;
+ std::map<int, std::list<SetPacScriptAction>> pac_script_actions_;
+
+ scoped_ptr<MockMojoProxyResolver> mock_proxy_resolver_;
+};
+
+TestMojoProxyResolverFactory::TestMojoProxyResolverFactory()
+ : num_create_calls_(0) {
+}
+
+TestMojoProxyResolverFactory::~TestMojoProxyResolverFactory() {
+}
+
+void TestMojoProxyResolverFactory::Create(
+ mojo::InterfaceRequest<interfaces::ProxyResolver> req,
+ interfaces::HostResolverPtr host_resolver) {
+ mock_proxy_resolver_.reset(new MockMojoProxyResolver(req.Pass()));
+
+ for (const auto& action : pac_script_actions_[num_create_calls_])
+ mock_proxy_resolver_->AddPacScriptAction(action);
+ num_create_calls_++;
+
+ if (!run_loop_quit_closure_.is_null())
+ run_loop_quit_closure_.Run();
+ run_loop_quit_closure_.Reset();
+}
+
+void TestMojoProxyResolverFactory::AddFuturePacScriptAction(
+ int creation,
+ SetPacScriptAction action) {
+ pac_script_actions_[creation].push_back(action);
+}
+
+void TestMojoProxyResolverFactory::WaitForCreateCalls(int num) {
+ while (num_create_calls_ < num) {
+ base::RunLoop run_loop;
+ run_loop_quit_closure_ = run_loop.QuitClosure();
+ run_loop.Run();
+ }
+}
+
+class Request {
+ public:
+ Request(ProxyResolverMojo* resolver, const GURL& url);
+
+ int Resolve();
+ void Cancel();
+ int WaitForResult();
+
+ int error() const { return error_; }
+ const ProxyInfo& results() const { return results_; }
+
+ private:
+ // Completion callback for ProxyResolverMojo::Resolve.
+ void OnResolveDone(int error);
+
+ ProxyResolverMojo* resolver_;
+ const GURL url_;
+ ProxyInfo results_;
+ ProxyResolver::RequestHandle handle_;
+ int error_;
+
+ base::RunLoop run_loop_;
Sam McNally 2015/02/17 06:23:09 The comment for RunLoop says it should be created
Anand Mistry (off Chromium) 2015/02/18 06:48:52 Done.
+};
+
+Request::Request(ProxyResolverMojo* resolver, const GURL& url)
+ : resolver_(resolver), url_(url), error_(0) {
+}
+
+int Request::Resolve() {
+ BoundNetLog net_log;
+ error_ = resolver_->GetProxyForURL(
+ url_, &results_,
+ base::Bind(&Request::OnResolveDone, base::Unretained(this)), &handle_,
+ net_log);
+ return error_;
+}
+
+void Request::Cancel() {
+ resolver_->CancelRequest(handle_);
+}
+
+void Request::OnResolveDone(int error) {
+ error_ = error;
+ run_loop_.Quit();
+}
+
+int Request::WaitForResult() {
+ run_loop_.Run();
+ return error_;
+}
+
+} // namespace
+
+class ProxyResolverMojoTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ mock_host_resolver_.rules()->AddRule("example.com", "1.2.3.4");
+
+ proxy_resolver_mojo_.reset(new ProxyResolverMojo(
+ &mojo_proxy_resolver_factory_, &mock_host_resolver_));
+ }
+
+ scoped_ptr<Request> MakeRequest(const GURL& url) {
+ return scoped_ptr<Request>(new Request(proxy_resolver_mojo_.get(), url));
Sam McNally 2015/02/17 06:23:09 make_scoped_ptr
Anand Mistry (off Chromium) 2015/02/18 06:48:52 Done.
+ }
+
+ mojo::Array<interfaces::ProxyServerPtr> ProxyServersFromPacString(
+ const std::string& pac_string) {
+ ProxyInfo proxy_info;
+ proxy_info.UsePacString(pac_string);
+
+ return mojo::Array<interfaces::ProxyServerPtr>::From(
+ proxy_info.proxy_list().GetAll());
+ }
+
+ MockHostResolver mock_host_resolver_;
+ TestMojoProxyResolverFactory mojo_proxy_resolver_factory_;
+ scoped_ptr<ProxyResolverMojo> proxy_resolver_mojo_;
+};
+
+TEST_F(ProxyResolverMojoTest, SetPacScript) {
+ TestCompletionCallback callback;
+ scoped_refptr<ProxyResolverScriptData> pac_script(
+ ProxyResolverScriptData::FromUTF8(kScriptData));
+ mojo_proxy_resolver_factory_.GetMockResolver()->AddPacScriptAction(
+ SetPacScriptAction::ReturnResult(OK));
+ EXPECT_EQ(OK, callback.GetResult(proxy_resolver_mojo_->SetPacScript(
+ pac_script, callback.callback())));
+ EXPECT_EQ(kScriptData,
+ mojo_proxy_resolver_factory_.GetMockResolver()->GetPacScriptData());
+}
+
+TEST_F(ProxyResolverMojoTest, SetPacScript_Empty) {
+ TestCompletionCallback callback;
+ scoped_refptr<ProxyResolverScriptData> pac_script(
+ ProxyResolverScriptData::FromUTF8(""));
+ EXPECT_EQ(ERR_PAC_SCRIPT_FAILED,
+ callback.GetResult(proxy_resolver_mojo_->SetPacScript(
+ pac_script, callback.callback())));
+}
+
+TEST_F(ProxyResolverMojoTest, SetPacScript_Url) {
+ TestCompletionCallback callback;
+ scoped_refptr<ProxyResolverScriptData> pac_script(
+ ProxyResolverScriptData::FromURL(GURL("http://www.example.com")));
+ EXPECT_EQ(ERR_PAC_SCRIPT_FAILED,
+ callback.GetResult(proxy_resolver_mojo_->SetPacScript(
+ pac_script, callback.callback())));
+}
+
+TEST_F(ProxyResolverMojoTest, SetPacScript_Failed) {
+ TestCompletionCallback callback;
+ scoped_refptr<ProxyResolverScriptData> pac_script(
+ ProxyResolverScriptData::FromUTF8(kScriptData));
+ mojo_proxy_resolver_factory_.GetMockResolver()->AddPacScriptAction(
+ SetPacScriptAction::ReturnResult(ERR_PAC_STATUS_NOT_OK));
+ EXPECT_EQ(ERR_PAC_STATUS_NOT_OK,
+ callback.GetResult(proxy_resolver_mojo_->SetPacScript(
+ pac_script, callback.callback())));
+}
+
+TEST_F(ProxyResolverMojoTest, SetPacScript_Disconnected) {
+ scoped_refptr<ProxyResolverScriptData> pac_script(
+ ProxyResolverScriptData::FromUTF8(kScriptData));
+ TestCompletionCallback callback;
+ EXPECT_EQ(ERR_IO_PENDING, proxy_resolver_mojo_->SetPacScript(
+ pac_script, callback.callback()));
+ mojo_proxy_resolver_factory_.GetMockResolver()->Disconnect();
+ EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, callback.GetResult(ERR_IO_PENDING));
+
+ // The service should have been recreated transparently.
+ mojo_proxy_resolver_factory_.WaitForCreateCalls(2);
+}
+
+TEST_F(ProxyResolverMojoTest, SetPacScript_SuccessThenDisconnect) {
+ scoped_refptr<ProxyResolverScriptData> pac_script(
+ ProxyResolverScriptData::FromUTF8(kScriptData));
+ mojo_proxy_resolver_factory_.GetMockResolver()->AddPacScriptAction(
+ SetPacScriptAction::ReturnResult(OK));
+ mojo_proxy_resolver_factory_.GetMockResolver()->AddPacScriptAction(
+ SetPacScriptAction::DropRequest());
+ mojo_proxy_resolver_factory_.AddFuturePacScriptAction(
+ 1, SetPacScriptAction::ReturnResult(OK));
+ {
+ TestCompletionCallback callback;
+ EXPECT_EQ(OK, callback.GetResult(proxy_resolver_mojo_->SetPacScript(
+ pac_script, callback.callback())));
+ EXPECT_EQ(
+ kScriptData,
+ mojo_proxy_resolver_factory_.GetMockResolver()->GetPacScriptData());
+ }
+
+ {
+ TestCompletionCallback callback;
+ EXPECT_EQ(ERR_IO_PENDING, proxy_resolver_mojo_->SetPacScript(
+ pac_script, callback.callback()));
Sam McNally 2015/02/17 06:23:09 This should use a different pac script.
Anand Mistry (off Chromium) 2015/02/18 06:48:52 Done.
+ EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, callback.GetResult(ERR_IO_PENDING));
+ }
+
+ // The service should have been recreated transparently.
+ mojo_proxy_resolver_factory_.WaitForCreateCalls(2);
+
+ // Wait for SetPacScript() to be run.
+ mojo_proxy_resolver_factory_.GetMockResolver()->WaitForSetPacScript();
+ EXPECT_EQ(
+ 1, mojo_proxy_resolver_factory_.GetMockResolver()->GetPacScriptCalls());
+ EXPECT_EQ(kScriptData,
+ mojo_proxy_resolver_factory_.GetMockResolver()->GetPacScriptData());
+}
+
+TEST_F(ProxyResolverMojoTest, SetPacScript_Cancel) {
+ scoped_refptr<ProxyResolverScriptData> pac_script(
+ ProxyResolverScriptData::FromUTF8(kScriptData));
+ TestCompletionCallback callback;
+ mojo_proxy_resolver_factory_.GetMockResolver()->AddPacScriptAction(
+ SetPacScriptAction::ReturnResult(OK));
+ EXPECT_EQ(ERR_IO_PENDING, proxy_resolver_mojo_->SetPacScript(
+ pac_script, callback.callback()));
+ proxy_resolver_mojo_->CancelSetPacScript();
+}
+
+TEST_F(ProxyResolverMojoTest, SetPacScript_CancelAndSetAgain) {
+ scoped_refptr<ProxyResolverScriptData> pac_script(
+ ProxyResolverScriptData::FromUTF8(kScriptData));
+ mojo_proxy_resolver_factory_.GetMockResolver()->AddPacScriptAction(
+ SetPacScriptAction::ReturnResult(ERR_FAILED));
+ mojo_proxy_resolver_factory_.GetMockResolver()->AddPacScriptAction(
+ SetPacScriptAction::ReturnResult(ERR_UNEXPECTED));
+ TestCompletionCallback callback1;
+ EXPECT_EQ(ERR_IO_PENDING, proxy_resolver_mojo_->SetPacScript(
+ pac_script, callback1.callback()));
+ proxy_resolver_mojo_->CancelSetPacScript();
+
+ TestCompletionCallback callback2;
+ EXPECT_EQ(ERR_IO_PENDING, proxy_resolver_mojo_->SetPacScript(
+ pac_script, callback2.callback()));
+ EXPECT_EQ(ERR_UNEXPECTED, callback2.GetResult(ERR_IO_PENDING));
+ EXPECT_EQ(
+ 2, mojo_proxy_resolver_factory_.GetMockResolver()->GetPacScriptCalls());
+}
+
+TEST_F(ProxyResolverMojoTest, GetProxyForURL) {
+ scoped_ptr<Request> request(MakeRequest(GURL("http://www.example.com")));
+ mojo_proxy_resolver_factory_.GetMockResolver()->AddProxyResult(
+ GURL("http://www.example.com"), OK, ProxyServersFromPacString("DIRECT"));
+ EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
+ EXPECT_EQ(OK, request->WaitForResult());
+
+ EXPECT_EQ("DIRECT", request->results().ToPacString());
+}
+
+TEST_F(ProxyResolverMojoTest, GetProxyForURL_MultipleResults) {
+ static const char kPacString[] =
+ "PROXY foo1:80;DIRECT;SOCKS foo2:1234;"
+ "SOCKS5 foo3:1080;HTTPS foo4:443;QUIC foo6:8888";
+ scoped_ptr<Request> request(MakeRequest(GURL("http://www.example.com")));
+ mojo_proxy_resolver_factory_.GetMockResolver()->AddProxyResult(
+ GURL("http://www.example.com"), OK,
+ ProxyServersFromPacString(kPacString));
+ EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
+ EXPECT_EQ(OK, request->WaitForResult());
+
+ EXPECT_EQ(kPacString, request->results().ToPacString());
+}
+
+TEST_F(ProxyResolverMojoTest, GetProxyForURL_Error) {
+ scoped_ptr<Request> request(MakeRequest(GURL("http://www.example.com")));
+ mojo_proxy_resolver_factory_.GetMockResolver()->AddProxyResult(
+ GURL("http://www.example.com"), ERR_UNEXPECTED,
+ mojo::Array<interfaces::ProxyServerPtr>());
+ EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
+ EXPECT_EQ(ERR_UNEXPECTED, request->WaitForResult());
+
+ EXPECT_TRUE(request->results().is_empty());
+}
+
+TEST_F(ProxyResolverMojoTest, GetProxyForURL_Cancel) {
+ scoped_ptr<Request> request(MakeRequest(GURL("http://www.example.com")));
+ mojo_proxy_resolver_factory_.GetMockResolver()->AddProxyResult(
+ GURL("http://www.example.com"), OK, ProxyServersFromPacString("DIRECT"));
+ EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
+ request->Cancel();
+}
+
+TEST_F(ProxyResolverMojoTest, GetProxyForURL_MultipleRequests) {
+ mojo_proxy_resolver_factory_.GetMockResolver()->AddProxyResult(
+ GURL("http://www.example.com"), OK, ProxyServersFromPacString("DIRECT"));
+ mojo_proxy_resolver_factory_.GetMockResolver()->AddProxyResult(
+ GURL("https://www.chromium.org"), OK,
+ ProxyServersFromPacString("HTTPS foo:443"));
+
+ scoped_ptr<Request> request1(MakeRequest(GURL("http://www.example.com")));
+ EXPECT_EQ(ERR_IO_PENDING, request1->Resolve());
+ scoped_ptr<Request> request2(MakeRequest(GURL("https://www.chromium.org")));
+ EXPECT_EQ(ERR_IO_PENDING, request2->Resolve());
+
+ EXPECT_EQ(OK, request1->WaitForResult());
+ EXPECT_EQ(OK, request2->WaitForResult());
+
+ EXPECT_EQ("DIRECT", request1->results().ToPacString());
+ EXPECT_EQ("HTTPS foo:443", request2->results().ToPacString());
+}
+
+TEST_F(ProxyResolverMojoTest, GetProxyForURL_Disconnect) {
+ {
+ scoped_ptr<Request> request(MakeRequest(GURL("http://www.example.com")));
+ mojo_proxy_resolver_factory_.GetMockResolver()->AddProxyResult(
+ GURL("http://www.example.com"), OK,
+ ProxyServersFromPacString("DIRECT"));
+ EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
+ mojo_proxy_resolver_factory_.GetMockResolver()->Disconnect();
+ EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, request->WaitForResult());
+ EXPECT_TRUE(request->results().is_empty());
+ }
+
+ // The service should have been recreated transparently.
+ mojo_proxy_resolver_factory_.WaitForCreateCalls(2);
+
+ {
+ scoped_ptr<Request> request(MakeRequest(GURL("http://www.example.com")));
+ mojo_proxy_resolver_factory_.GetMockResolver()->AddProxyResult(
+ GURL("http://www.example.com"), OK,
+ ProxyServersFromPacString("DIRECT"));
+ EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
+ EXPECT_EQ(OK, request->WaitForResult());
+ EXPECT_EQ("DIRECT", request->results().ToPacString());
+ }
+}
+
+TEST_F(ProxyResolverMojoTest, GetProxyForURL_ClientClosed) {
+ scoped_ptr<Request> request(MakeRequest(GURL("http://www.example.com")));
+ mojo_proxy_resolver_factory_.GetMockResolver()->CloseRequestClient(
+ GURL("http://www.example.com"));
+ EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
+ EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, request->WaitForResult());
+
+ EXPECT_TRUE(request->results().is_empty());
+}
+
+} // namespace net
« no previous file with comments | « net/proxy/proxy_resolver_mojo.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698