| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "chrome/browser/importer/firefox_importer_unittest_utils.h" | |
| 6 | |
| 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.h" | |
| 13 #include "base/test/test_timeouts.h" | |
| 14 #include "chrome/browser/importer/firefox_importer_utils.h" | |
| 15 #include "ipc/ipc_channel.h" | |
| 16 #include "ipc/ipc_descriptors.h" | |
| 17 #include "ipc/ipc_listener.h" | |
| 18 #include "ipc/ipc_message.h" | |
| 19 #include "ipc/ipc_multiprocess_test.h" | |
| 20 #include "ipc/ipc_switches.h" | |
| 21 #include "testing/multiprocess_func_list.h" | |
| 22 | |
| 23 #define IPC_MESSAGE_IMPL | |
| 24 #include "chrome/browser/importer/firefox_importer_unittest_messages_internal.h" | |
| 25 | |
| 26 namespace { | |
| 27 | |
| 28 // Name of IPC Channel to use for Server<-> Child Communications. | |
| 29 const char kTestChannelID[] = "T1"; | |
| 30 | |
| 31 // Launch the child process: | |
| 32 // |nss_path| - path to the NSS directory holding the decryption libraries. | |
| 33 // |channel| - IPC Channel to use for communication. | |
| 34 // |handle| - On return, the process handle to use to communicate with the | |
| 35 // child. | |
| 36 bool LaunchNSSDecrypterChildProcess(const base::FilePath& nss_path, | |
| 37 IPC::Channel* channel, base::ProcessHandle* handle) { | |
| 38 CommandLine cl(*CommandLine::ForCurrentProcess()); | |
| 39 cl.AppendSwitchASCII(switches::kTestChildProcess, "NSSDecrypterChildProcess"); | |
| 40 | |
| 41 // Set env variable needed for FF encryption libs to load. | |
| 42 // See "chrome/browser/importer/nss_decryptor_mac.mm" for an explanation of | |
| 43 // why we need this. | |
| 44 base::EnvironmentVector env; | |
| 45 std::pair<std::string, std::string> dyld_override; | |
| 46 dyld_override.first = "DYLD_FALLBACK_LIBRARY_PATH"; | |
| 47 dyld_override.second = nss_path.value(); | |
| 48 env.push_back(dyld_override); | |
| 49 | |
| 50 int ipcfd = channel->TakeClientFileDescriptor(); | |
| 51 if (ipcfd == -1) | |
| 52 return false; | |
| 53 | |
| 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, kPrimaryIPCChannel + 3)); | |
| 57 | |
| 58 bool debug_on_start = CommandLine::ForCurrentProcess()->HasSwitch( | |
| 59 switches::kDebugChildren); | |
| 60 base::LaunchOptions options; | |
| 61 options.environ = &env; | |
| 62 options.fds_to_remap = &fds_to_map; | |
| 63 options.wait = debug_on_start; | |
| 64 return base::LaunchProcess(cl.argv(), options, handle); | |
| 65 } | |
| 66 | |
| 67 } // namespace | |
| 68 | |
| 69 //----------------------- Server -------------------- | |
| 70 | |
| 71 // Class to communicate on the server side of the IPC Channel. | |
| 72 // Method calls are sent over IPC and replies are read back into class | |
| 73 // variables. | |
| 74 // This class needs to be called on a single thread. | |
| 75 class FFDecryptorServerChannelListener : public IPC::Listener { | |
| 76 public: | |
| 77 FFDecryptorServerChannelListener() | |
| 78 : got_result(false), sender_(NULL) {} | |
| 79 | |
| 80 void SetSender(IPC::Sender* sender) { | |
| 81 sender_ = sender; | |
| 82 } | |
| 83 | |
| 84 void OnInitDecryptorResponse(bool result) { | |
| 85 DCHECK(!got_result); | |
| 86 result_bool = result; | |
| 87 got_result = true; | |
| 88 base::MessageLoop::current()->Quit(); | |
| 89 } | |
| 90 | |
| 91 void OnDecryptedTextResonse(const string16& decrypted_text) { | |
| 92 DCHECK(!got_result); | |
| 93 result_string = decrypted_text; | |
| 94 got_result = true; | |
| 95 base::MessageLoop::current()->Quit(); | |
| 96 } | |
| 97 | |
| 98 void QuitClient() { | |
| 99 if (sender_) | |
| 100 sender_->Send(new Msg_Decryptor_Quit()); | |
| 101 } | |
| 102 | |
| 103 virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE { | |
| 104 bool handled = true; | |
| 105 IPC_BEGIN_MESSAGE_MAP(FFDecryptorServerChannelListener, msg) | |
| 106 IPC_MESSAGE_HANDLER(Msg_Decryptor_InitReturnCode, OnInitDecryptorResponse) | |
| 107 IPC_MESSAGE_HANDLER(Msg_Decryptor_Response, OnDecryptedTextResonse) | |
| 108 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 109 IPC_END_MESSAGE_MAP() | |
| 110 return handled; | |
| 111 } | |
| 112 | |
| 113 // If an error occured, just kill the message Loop. | |
| 114 virtual void OnChannelError() OVERRIDE { | |
| 115 got_result = false; | |
| 116 base::MessageLoop::current()->Quit(); | |
| 117 } | |
| 118 | |
| 119 // Results of IPC calls. | |
| 120 string16 result_string; | |
| 121 bool result_bool; | |
| 122 // True if IPC call succeeded and data in above variables is valid. | |
| 123 bool got_result; | |
| 124 | |
| 125 private: | |
| 126 IPC::Sender* sender_; // weak | |
| 127 }; | |
| 128 | |
| 129 FFUnitTestDecryptorProxy::FFUnitTestDecryptorProxy() | |
| 130 : child_process_(0) { | |
| 131 } | |
| 132 | |
| 133 bool FFUnitTestDecryptorProxy::Setup(const base::FilePath& nss_path) { | |
| 134 // Create a new message loop and spawn the child process. | |
| 135 message_loop_.reset(new base::MessageLoopForIO()); | |
| 136 | |
| 137 listener_.reset(new FFDecryptorServerChannelListener()); | |
| 138 channel_.reset(new IPC::Channel(kTestChannelID, | |
| 139 IPC::Channel::MODE_SERVER, | |
| 140 listener_.get())); | |
| 141 CHECK(channel_->Connect()); | |
| 142 listener_->SetSender(channel_.get()); | |
| 143 | |
| 144 // Spawn child and set up sync IPC connection. | |
| 145 bool ret = LaunchNSSDecrypterChildProcess(nss_path, | |
| 146 channel_.get(), | |
| 147 &child_process_); | |
| 148 return ret && (child_process_ != 0); | |
| 149 } | |
| 150 | |
| 151 FFUnitTestDecryptorProxy::~FFUnitTestDecryptorProxy() { | |
| 152 listener_->QuitClient(); | |
| 153 channel_->Close(); | |
| 154 | |
| 155 if (child_process_) { | |
| 156 base::WaitForSingleProcess(child_process_, base::TimeDelta::FromSeconds(5)); | |
| 157 base::CloseProcessHandle(child_process_); | |
| 158 } | |
| 159 } | |
| 160 | |
| 161 // A message_loop task that quits the message loop when invoked, setting cancel | |
| 162 // causes the task to do nothing when invoked. | |
| 163 class CancellableQuitMsgLoop : public base::RefCounted<CancellableQuitMsgLoop> { | |
| 164 public: | |
| 165 CancellableQuitMsgLoop() : cancelled_(false) {} | |
| 166 void QuitNow() { | |
| 167 if (!cancelled_) | |
| 168 base::MessageLoop::current()->Quit(); | |
| 169 } | |
| 170 bool cancelled_; | |
| 171 | |
| 172 private: | |
| 173 friend class base::RefCounted<CancellableQuitMsgLoop>; | |
| 174 ~CancellableQuitMsgLoop() {} | |
| 175 }; | |
| 176 | |
| 177 // Spin until either a client response arrives or a timeout occurs. | |
| 178 bool FFUnitTestDecryptorProxy::WaitForClientResponse() { | |
| 179 // What we're trying to do here is to wait for an RPC message to go over the | |
| 180 // wire and the client to reply. If the client does not reply by a given | |
| 181 // timeout we kill the message loop. | |
| 182 // The way we do this is to post a CancellableQuitMsgLoop for 3 seconds in | |
| 183 // the future and cancel it if an RPC message comes back earlier. | |
| 184 // This relies on the IPC listener class to quit the message loop itself when | |
| 185 // a message comes in. | |
| 186 scoped_refptr<CancellableQuitMsgLoop> quit_task( | |
| 187 new CancellableQuitMsgLoop()); | |
| 188 base::MessageLoop::current()->PostDelayedTask( | |
| 189 FROM_HERE, | |
| 190 base::Bind(&CancellableQuitMsgLoop::QuitNow, quit_task.get()), | |
| 191 TestTimeouts::action_max_timeout()); | |
| 192 | |
| 193 message_loop_->Run(); | |
| 194 bool ret = !quit_task->cancelled_; | |
| 195 quit_task->cancelled_ = false; | |
| 196 return ret; | |
| 197 } | |
| 198 | |
| 199 bool FFUnitTestDecryptorProxy::DecryptorInit(const base::FilePath& dll_path, | |
| 200 const base::FilePath& db_path) { | |
| 201 channel_->Send(new Msg_Decryptor_Init(dll_path, db_path)); | |
| 202 bool ok = WaitForClientResponse(); | |
| 203 if (ok && listener_->got_result) { | |
| 204 listener_->got_result = false; | |
| 205 return listener_->result_bool; | |
| 206 } | |
| 207 return false; | |
| 208 } | |
| 209 | |
| 210 string16 FFUnitTestDecryptorProxy::Decrypt(const std::string& crypt) { | |
| 211 channel_->Send(new Msg_Decrypt(crypt)); | |
| 212 bool ok = WaitForClientResponse(); | |
| 213 if (ok && listener_->got_result) { | |
| 214 listener_->got_result = false; | |
| 215 return listener_->result_string; | |
| 216 } | |
| 217 return string16(); | |
| 218 } | |
| 219 | |
| 220 //---------------------------- Child Process ----------------------- | |
| 221 | |
| 222 // Class to listen on the client side of the ipc channel, it calls through | |
| 223 // to the NSSDecryptor and sends back a reply. | |
| 224 class FFDecryptorClientChannelListener : public IPC::Listener { | |
| 225 public: | |
| 226 FFDecryptorClientChannelListener() | |
| 227 : sender_(NULL) {} | |
| 228 | |
| 229 void SetSender(IPC::Sender* sender) { | |
| 230 sender_ = sender; | |
| 231 } | |
| 232 | |
| 233 void OnDecryptor_Init(base::FilePath dll_path, base::FilePath db_path) { | |
| 234 bool ret = decryptor_.Init(dll_path, db_path); | |
| 235 sender_->Send(new Msg_Decryptor_InitReturnCode(ret)); | |
| 236 } | |
| 237 | |
| 238 void OnDecrypt(std::string crypt) { | |
| 239 string16 unencrypted_str = decryptor_.Decrypt(crypt); | |
| 240 sender_->Send(new Msg_Decryptor_Response(unencrypted_str)); | |
| 241 } | |
| 242 | |
| 243 void OnQuitRequest() { | |
| 244 base::MessageLoop::current()->Quit(); | |
| 245 } | |
| 246 | |
| 247 virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE { | |
| 248 bool handled = true; | |
| 249 IPC_BEGIN_MESSAGE_MAP(FFDecryptorClientChannelListener, msg) | |
| 250 IPC_MESSAGE_HANDLER(Msg_Decryptor_Init, OnDecryptor_Init) | |
| 251 IPC_MESSAGE_HANDLER(Msg_Decrypt, OnDecrypt) | |
| 252 IPC_MESSAGE_HANDLER(Msg_Decryptor_Quit, OnQuitRequest) | |
| 253 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 254 IPC_END_MESSAGE_MAP() | |
| 255 return handled; | |
| 256 } | |
| 257 | |
| 258 virtual void OnChannelError() OVERRIDE { | |
| 259 base::MessageLoop::current()->Quit(); | |
| 260 } | |
| 261 | |
| 262 private: | |
| 263 NSSDecryptor decryptor_; | |
| 264 IPC::Sender* sender_; | |
| 265 }; | |
| 266 | |
| 267 // Entry function in child process. | |
| 268 MULTIPROCESS_IPC_TEST_MAIN(NSSDecrypterChildProcess) { | |
| 269 base::MessageLoopForIO main_message_loop; | |
| 270 FFDecryptorClientChannelListener listener; | |
| 271 | |
| 272 IPC::Channel channel(kTestChannelID, IPC::Channel::MODE_CLIENT, &listener); | |
| 273 CHECK(channel.Connect()); | |
| 274 listener.SetSender(&channel); | |
| 275 | |
| 276 // run message loop | |
| 277 base::MessageLoop::current()->Run(); | |
| 278 | |
| 279 return 0; | |
| 280 } | |
| OLD | NEW |