| Index: chrome/browser/importer/firefox_importer_unittest_utils_mac.cc
|
| diff --git a/chrome/browser/importer/firefox_importer_unittest_utils_mac.cc b/chrome/browser/importer/firefox_importer_unittest_utils_mac.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..d44b3369ad4ae2b5420102a7b75fa99f5f0e306d
|
| --- /dev/null
|
| +++ b/chrome/browser/importer/firefox_importer_unittest_utils_mac.cc
|
| @@ -0,0 +1,266 @@
|
| +// Copyright (c) 2009 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 "chrome/browser/importer/firefox_importer_unittest_utils.h"
|
| +
|
| +#include "base/command_line.h"
|
| +#include "base/debug_on_start.h"
|
| +#include "base/file_path.h"
|
| +#include "base/message_loop.h"
|
| +#include "chrome/browser/importer/firefox_importer_utils.h"
|
| +#include "ipc/ipc_channel.h"
|
| +#include "ipc/ipc_descriptors.h"
|
| +#include "ipc/ipc_message.h"
|
| +#include "ipc/ipc_message_utils.h"
|
| +#include "ipc/ipc_switches.h"
|
| +#include "testing/multiprocess_func_list.h"
|
| +
|
| +// Definition of IPC Messages used for this test.
|
| +#define MESSAGES_INTERNAL_FILE \
|
| + "chrome/browser/importer/firefox_importer_unittest_messages_internal.h"
|
| +#include "ipc/ipc_message_macros.h"
|
| +
|
| +namespace {
|
| +
|
| +// Name of IPC Channel to use for Server<-> Child Communications.
|
| +const char kTestChannelID[] = "T1";
|
| +
|
| +// Launch the child process:
|
| +// |nss_path| - path to the NSS directory holding the decryption libraries.
|
| +// |channel| - IPC Channel to use for communication.
|
| +// |handle| - On return, the process handle to use to communicate with the
|
| +// child.
|
| +bool LaunchNSSDecrypterChildProcess(const std::wstring& nss_path,
|
| + const IPC::Channel& channel, base::ProcessHandle* handle) {
|
| + CommandLine cl(*CommandLine::ForCurrentProcess());
|
| + cl.AppendSwitchWithValue(L"client", L"NSSDecrypterChildProcess");
|
| +
|
| + FilePath ff_dylib_dir = FilePath::FromWStringHack(nss_path);
|
| + // Set env variable needed for FF encryption libs to load.
|
| + // See "chrome/browser/importer/nss_decryptor_mac.mm" for an explanation of
|
| + // why we need this.
|
| + base::environment_vector env;
|
| + std::pair<const char*,const char*> dyld_override;
|
| + dyld_override.first = "DYLD_FALLBACK_LIBRARY_PATH";
|
| + dyld_override.second = ff_dylib_dir.value().c_str();
|
| + env.push_back(dyld_override);
|
| +
|
| + base::file_handle_mapping_vector fds_to_map;
|
| + const int ipcfd = channel.GetClientFileDescriptor();
|
| + if (ipcfd > -1) {
|
| + fds_to_map.push_back(std::pair<int,int>(ipcfd, kPrimaryIPCChannel + 3));
|
| + } else {
|
| + return false;
|
| + }
|
| +
|
| + bool debug_on_start = CommandLine::ForCurrentProcess()->HasSwitch(
|
| + switches::kDebugChildren);
|
| + return base::LaunchApp(cl.argv(), env, fds_to_map, debug_on_start,
|
| + handle);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +//----------------------- Server --------------------
|
| +
|
| +// Class to communicate on the server side of the IPC Channel.
|
| +// Method calls are sent over IPC and replies are read back into class
|
| +// variables.
|
| +// This class needs to be called on a single thread.
|
| +class FFDecryptorServerChannelListener : public IPC::Channel::Listener {
|
| + public:
|
| + FFDecryptorServerChannelListener()
|
| + : got_result(false), sender_(NULL) {}
|
| +
|
| + void SetSender(IPC::Message::Sender* sender) {
|
| + sender_ = sender;
|
| + }
|
| +
|
| + void OnInitDecryptorResponse(bool result) {
|
| + DCHECK(!got_result);
|
| + result_bool = result;
|
| + got_result = true;
|
| + MessageLoop::current()->Quit();
|
| + }
|
| +
|
| + void OnDecryptedTextResonse(std::wstring decrypted_text) {
|
| + DCHECK(!got_result);
|
| + result_string = decrypted_text;
|
| + got_result = true;
|
| + MessageLoop::current()->Quit();
|
| + }
|
| +
|
| + void QuitClient() {
|
| + if (sender_)
|
| + sender_->Send(new Msg_Decryptor_Quit());
|
| + }
|
| +
|
| + virtual void OnMessageReceived(const IPC::Message& msg) {
|
| + IPC_BEGIN_MESSAGE_MAP(FFDecryptorServerChannelListener, msg)
|
| + IPC_MESSAGE_HANDLER(Msg_Decryptor_InitReturnCode, OnInitDecryptorResponse)
|
| + IPC_MESSAGE_HANDLER(Msg_Decryptor_Response, OnDecryptedTextResonse)
|
| + IPC_END_MESSAGE_MAP()
|
| + }
|
| +
|
| + // If an error occured, just kill the message Loop.
|
| + virtual void OnChannelError() {
|
| + got_result = false;
|
| + MessageLoop::current()->Quit();
|
| + }
|
| +
|
| + // Results of IPC calls.
|
| + std::wstring result_string;
|
| + bool result_bool;
|
| + // True if IPC call succeeded and data in above variables is valid.
|
| + bool got_result;
|
| +
|
| + private:
|
| + IPC::Message::Sender* sender_; // weak
|
| +};
|
| +
|
| +FFUnitTestDecryptorProxy::FFUnitTestDecryptorProxy()
|
| + : child_process_(0) {
|
| +}
|
| +
|
| +bool FFUnitTestDecryptorProxy::Setup(std::wstring& nss_path) {
|
| + // Create a new message loop and spawn the child process.
|
| + message_loop_.reset(new MessageLoopForIO());
|
| +
|
| + listener_.reset(new FFDecryptorServerChannelListener());
|
| + channel_.reset(new IPC::Channel(kTestChannelID,
|
| + IPC::Channel::MODE_SERVER,
|
| + listener_.get()));
|
| + channel_->Connect();
|
| + listener_->SetSender(channel_.get());
|
| +
|
| + // Spawn child and set up sync IPC connection.
|
| + bool ret = LaunchNSSDecrypterChildProcess(nss_path,
|
| + *(channel_.get()),
|
| + &child_process_);
|
| + return ret && (child_process_ != 0);
|
| +}
|
| +
|
| +FFUnitTestDecryptorProxy::~FFUnitTestDecryptorProxy() {
|
| + listener_->QuitClient();
|
| + channel_->Close();
|
| +
|
| + if (child_process_) {
|
| + base::WaitForSingleProcess(child_process_, 5000);
|
| + base::CloseProcessHandle(child_process_);
|
| + }
|
| +}
|
| +
|
| +// A message_loop task that quits the message loop when invoked, setting cancel
|
| +// causes the task to do nothing when invoked.
|
| +class CancellableQuitMsgLoop : public base::RefCounted<CancellableQuitMsgLoop> {
|
| + public:
|
| + CancellableQuitMsgLoop() : cancelled_(false) {}
|
| + void QuitNow() {
|
| + if (!cancelled_)
|
| + MessageLoop::current()->Quit();
|
| + }
|
| + bool cancelled_;
|
| +};
|
| +
|
| +// Spin until either a client response arrives or a timeout occurs.
|
| +bool FFUnitTestDecryptorProxy::WaitForClientResponse() {
|
| + const int64 kLoopTimeoutMS = 10 * 1000; // 10 seconds.
|
| +
|
| + // What we're trying to do here is to wait for an RPC message to go over the
|
| + // wire and the client to reply. If the client does not replyy by a given
|
| + // timeout we kill the message loop.
|
| + // The way we do this is to post a CancellableQuitMsgLoop for 3 seconds in
|
| + // the future and cancel it if an RPC message comes back earlier.
|
| + // This relies on the IPC listener class to quit the message loop itself when
|
| + // a message comes in.
|
| + scoped_refptr<CancellableQuitMsgLoop> quit_task =
|
| + new CancellableQuitMsgLoop();
|
| + MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableMethod(
|
| + quit_task.get(), &CancellableQuitMsgLoop::QuitNow), kLoopTimeoutMS);
|
| +
|
| + message_loop_->Run();
|
| + bool ret = !quit_task->cancelled_;
|
| + quit_task->cancelled_ = false;
|
| + return ret;
|
| +}
|
| +
|
| +bool FFUnitTestDecryptorProxy::DecryptorInit(const std::wstring& dll_path,
|
| + const std::wstring& db_path) {
|
| + channel_->Send(new Msg_Decryptor_Init(dll_path, db_path));
|
| + bool ok = WaitForClientResponse();
|
| + if (ok && listener_->got_result) {
|
| + listener_->got_result = false;
|
| + return listener_->result_bool;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +std::wstring FFUnitTestDecryptorProxy::Decrypt(const std::string& crypt) {
|
| + channel_->Send(new Msg_Decrypt(crypt));
|
| + bool ok = WaitForClientResponse();
|
| + if (ok && listener_->got_result) {
|
| + listener_->got_result = false;
|
| + return listener_->result_string;
|
| + }
|
| + return L"";
|
| +}
|
| +
|
| +//---------------------------- Child Process -----------------------
|
| +
|
| +// Class to listen on the client side of the ipc channel, it calls through
|
| +// to the NSSDecryptor and sends back a reply.
|
| +class FFDecryptorClientChannelListener : public IPC::Channel::Listener {
|
| + public:
|
| + FFDecryptorClientChannelListener()
|
| + : sender_(NULL) {}
|
| +
|
| + void SetSender(IPC::Message::Sender* sender) {
|
| + sender_ = sender;
|
| + }
|
| +
|
| + void OnDecryptor_Init(std::wstring dll_path, std::wstring db_path) {
|
| + bool ret = decryptor_.Init(dll_path, db_path);
|
| + sender_->Send(new Msg_Decryptor_InitReturnCode(ret));
|
| + }
|
| +
|
| + void OnDecrypt(std::string crypt) {
|
| + std::wstring unencrypted_str = decryptor_.Decrypt(crypt);
|
| + sender_->Send(new Msg_Decryptor_Response(unencrypted_str));
|
| + }
|
| +
|
| + void OnQuitRequest() {
|
| + MessageLoop::current()->Quit();
|
| + }
|
| +
|
| + virtual void OnMessageReceived(const IPC::Message& msg) {
|
| + IPC_BEGIN_MESSAGE_MAP(FFDecryptorClientChannelListener, msg)
|
| + IPC_MESSAGE_HANDLER(Msg_Decryptor_Init, OnDecryptor_Init)
|
| + IPC_MESSAGE_HANDLER(Msg_Decrypt, OnDecrypt)
|
| + IPC_MESSAGE_HANDLER(Msg_Decryptor_Quit, OnQuitRequest)
|
| + IPC_END_MESSAGE_MAP()
|
| + }
|
| +
|
| + virtual void OnChannelError() {
|
| + MessageLoop::current()->Quit();
|
| + }
|
| +
|
| + private:
|
| + NSSDecryptor decryptor_;
|
| + IPC::Message::Sender* sender_;
|
| +};
|
| +
|
| +// Entry function in child process.
|
| +MULTIPROCESS_TEST_MAIN(NSSDecrypterChildProcess) {
|
| + MessageLoopForIO main_message_loop;
|
| + FFDecryptorClientChannelListener listener;
|
| +
|
| + IPC::Channel channel(kTestChannelID, IPC::Channel::MODE_CLIENT, &listener);
|
| + channel.Connect();
|
| + listener.SetSender(&channel);
|
| +
|
| + // run message loop
|
| + MessageLoop::current()->Run();
|
| +
|
| + return 0;
|
| +}
|
|
|