Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / utility / importer / firefox_importer_unittest_utils_mac.cc
blob6b2114970760f27db1f54b90b6ca343562401a89
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/utility/importer/firefox_importer_unittest_utils.h"
7 #include "base/base_switches.h"
8 #include "base/bind.h"
9 #include "base/command_line.h"
10 #include "base/file_util.h"
11 #include "base/files/file_path.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/posix/global_descriptors.h"
14 #include "base/process/kill.h"
15 #include "base/process/launch.h"
16 #include "base/test/test_timeouts.h"
17 #include "chrome/common/importer/firefox_importer_utils.h"
18 #include "ipc/ipc_channel.h"
19 #include "ipc/ipc_descriptors.h"
20 #include "ipc/ipc_listener.h"
21 #include "ipc/ipc_message.h"
22 #include "ipc/ipc_multiprocess_test.h"
23 #include "ipc/ipc_switches.h"
24 #include "testing/multiprocess_func_list.h"
26 #define IPC_MESSAGE_IMPL
27 #include "chrome/utility/importer/firefox_importer_unittest_messages_internal.h"
29 namespace {
31 // Name of IPC Channel to use for Server<-> Child Communications.
32 const char kTestChannelID[] = "T1";
34 // Launch the child process:
35 // |nss_path| - path to the NSS directory holding the decryption libraries.
36 // |channel| - IPC Channel to use for communication.
37 // |handle| - On return, the process handle to use to communicate with the
38 // child.
39 bool LaunchNSSDecrypterChildProcess(const base::FilePath& nss_path,
40 IPC::Channel* channel, base::ProcessHandle* handle) {
41 CommandLine cl(*CommandLine::ForCurrentProcess());
42 cl.AppendSwitchASCII(switches::kTestChildProcess, "NSSDecrypterChildProcess");
44 // Set env variable needed for FF encryption libs to load.
45 // See "chrome/utility/importer/nss_decryptor_mac.mm" for an explanation of
46 // why we need this.
47 base::LaunchOptions options;
48 options.environ["DYLD_FALLBACK_LIBRARY_PATH"] = nss_path.value();
50 int ipcfd = channel->TakeClientFileDescriptor();
51 if (ipcfd == -1)
52 return false;
54 file_util::ScopedFD client_file_descriptor_closer(&ipcfd);
55 base::FileHandleMappingVector fds_to_map;
56 fds_to_map.push_back(std::pair<int,int>(ipcfd,
57 kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor));
59 bool debug_on_start = CommandLine::ForCurrentProcess()->HasSwitch(
60 switches::kDebugChildren);
61 options.fds_to_remap = &fds_to_map;
62 options.wait = debug_on_start;
63 return base::LaunchProcess(cl.argv(), options, handle);
66 } // namespace
68 //----------------------- Server --------------------
70 // Class to communicate on the server side of the IPC Channel.
71 // Method calls are sent over IPC and replies are read back into class
72 // variables.
73 // This class needs to be called on a single thread.
74 class FFDecryptorServerChannelListener : public IPC::Listener {
75 public:
76 FFDecryptorServerChannelListener()
77 : got_result(false), sender_(NULL) {}
79 void SetSender(IPC::Sender* sender) {
80 sender_ = sender;
83 void OnInitDecryptorResponse(bool result) {
84 DCHECK(!got_result);
85 result_bool = result;
86 got_result = true;
87 base::MessageLoop::current()->Quit();
90 void OnDecryptedTextResonse(const base::string16& decrypted_text) {
91 DCHECK(!got_result);
92 result_string = decrypted_text;
93 got_result = true;
94 base::MessageLoop::current()->Quit();
97 void QuitClient() {
98 if (sender_)
99 sender_->Send(new Msg_Decryptor_Quit());
102 virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE {
103 bool handled = true;
104 IPC_BEGIN_MESSAGE_MAP(FFDecryptorServerChannelListener, msg)
105 IPC_MESSAGE_HANDLER(Msg_Decryptor_InitReturnCode, OnInitDecryptorResponse)
106 IPC_MESSAGE_HANDLER(Msg_Decryptor_Response, OnDecryptedTextResonse)
107 IPC_MESSAGE_UNHANDLED(handled = false)
108 IPC_END_MESSAGE_MAP()
109 return handled;
112 // If an error occured, just kill the message Loop.
113 virtual void OnChannelError() OVERRIDE {
114 got_result = false;
115 base::MessageLoop::current()->Quit();
118 // Results of IPC calls.
119 base::string16 result_string;
120 bool result_bool;
121 // True if IPC call succeeded and data in above variables is valid.
122 bool got_result;
124 private:
125 IPC::Sender* sender_; // weak
128 FFUnitTestDecryptorProxy::FFUnitTestDecryptorProxy()
129 : child_process_(0) {
132 bool FFUnitTestDecryptorProxy::Setup(const base::FilePath& nss_path) {
133 // Create a new message loop and spawn the child process.
134 message_loop_.reset(new base::MessageLoopForIO());
136 listener_.reset(new FFDecryptorServerChannelListener());
137 channel_.reset(new IPC::Channel(kTestChannelID,
138 IPC::Channel::MODE_SERVER,
139 listener_.get()));
140 CHECK(channel_->Connect());
141 listener_->SetSender(channel_.get());
143 // Spawn child and set up sync IPC connection.
144 bool ret = LaunchNSSDecrypterChildProcess(nss_path,
145 channel_.get(),
146 &child_process_);
147 return ret && (child_process_ != 0);
150 FFUnitTestDecryptorProxy::~FFUnitTestDecryptorProxy() {
151 listener_->QuitClient();
152 channel_->Close();
154 if (child_process_) {
155 base::WaitForSingleProcess(child_process_, base::TimeDelta::FromSeconds(5));
156 base::CloseProcessHandle(child_process_);
160 // A message_loop task that quits the message loop when invoked, setting cancel
161 // causes the task to do nothing when invoked.
162 class CancellableQuitMsgLoop : public base::RefCounted<CancellableQuitMsgLoop> {
163 public:
164 CancellableQuitMsgLoop() : cancelled_(false) {}
165 void QuitNow() {
166 if (!cancelled_)
167 base::MessageLoop::current()->Quit();
169 bool cancelled_;
171 private:
172 friend class base::RefCounted<CancellableQuitMsgLoop>;
173 ~CancellableQuitMsgLoop() {}
176 // Spin until either a client response arrives or a timeout occurs.
177 bool FFUnitTestDecryptorProxy::WaitForClientResponse() {
178 // What we're trying to do here is to wait for an RPC message to go over the
179 // wire and the client to reply. If the client does not reply by a given
180 // timeout we kill the message loop.
181 // The way we do this is to post a CancellableQuitMsgLoop for 3 seconds in
182 // the future and cancel it if an RPC message comes back earlier.
183 // This relies on the IPC listener class to quit the message loop itself when
184 // a message comes in.
185 scoped_refptr<CancellableQuitMsgLoop> quit_task(
186 new CancellableQuitMsgLoop());
187 base::MessageLoop::current()->PostDelayedTask(
188 FROM_HERE,
189 base::Bind(&CancellableQuitMsgLoop::QuitNow, quit_task.get()),
190 TestTimeouts::action_max_timeout());
192 message_loop_->Run();
193 bool ret = !quit_task->cancelled_;
194 quit_task->cancelled_ = false;
195 return ret;
198 bool FFUnitTestDecryptorProxy::DecryptorInit(const base::FilePath& dll_path,
199 const base::FilePath& db_path) {
200 channel_->Send(new Msg_Decryptor_Init(dll_path, db_path));
201 bool ok = WaitForClientResponse();
202 if (ok && listener_->got_result) {
203 listener_->got_result = false;
204 return listener_->result_bool;
206 return false;
209 base::string16 FFUnitTestDecryptorProxy::Decrypt(const std::string& crypt) {
210 channel_->Send(new Msg_Decrypt(crypt));
211 bool ok = WaitForClientResponse();
212 if (ok && listener_->got_result) {
213 listener_->got_result = false;
214 return listener_->result_string;
216 return base::string16();
219 //---------------------------- Child Process -----------------------
221 // Class to listen on the client side of the ipc channel, it calls through
222 // to the NSSDecryptor and sends back a reply.
223 class FFDecryptorClientChannelListener : public IPC::Listener {
224 public:
225 FFDecryptorClientChannelListener()
226 : sender_(NULL) {}
228 void SetSender(IPC::Sender* sender) {
229 sender_ = sender;
232 void OnDecryptor_Init(base::FilePath dll_path, base::FilePath db_path) {
233 bool ret = decryptor_.Init(dll_path, db_path);
234 sender_->Send(new Msg_Decryptor_InitReturnCode(ret));
237 void OnDecrypt(std::string crypt) {
238 base::string16 unencrypted_str = decryptor_.Decrypt(crypt);
239 sender_->Send(new Msg_Decryptor_Response(unencrypted_str));
242 void OnQuitRequest() {
243 base::MessageLoop::current()->Quit();
246 virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE {
247 bool handled = true;
248 IPC_BEGIN_MESSAGE_MAP(FFDecryptorClientChannelListener, msg)
249 IPC_MESSAGE_HANDLER(Msg_Decryptor_Init, OnDecryptor_Init)
250 IPC_MESSAGE_HANDLER(Msg_Decrypt, OnDecrypt)
251 IPC_MESSAGE_HANDLER(Msg_Decryptor_Quit, OnQuitRequest)
252 IPC_MESSAGE_UNHANDLED(handled = false)
253 IPC_END_MESSAGE_MAP()
254 return handled;
257 virtual void OnChannelError() OVERRIDE {
258 base::MessageLoop::current()->Quit();
261 private:
262 NSSDecryptor decryptor_;
263 IPC::Sender* sender_;
266 // Entry function in child process.
267 MULTIPROCESS_IPC_TEST_MAIN(NSSDecrypterChildProcess) {
268 base::MessageLoopForIO main_message_loop;
269 FFDecryptorClientChannelListener listener;
271 IPC::Channel channel(kTestChannelID, IPC::Channel::MODE_CLIENT, &listener);
272 CHECK(channel.Connect());
273 listener.SetSender(&channel);
275 // run message loop
276 base::MessageLoop::current()->Run();
278 return 0;