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