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/external_process_importer_client.h"
6
7#include "base/bind.h"
8#include "base/strings/string_number_conversions.h"
9#include "chrome/browser/browser_process.h"
10#include "chrome/browser/importer/external_process_importer_host.h"
11#include "chrome/browser/importer/in_process_importer_bridge.h"
12#include "chrome/common/importer/firefox_importer_utils.h"
13#include "chrome/common/importer/imported_bookmark_entry.h"
14#include "chrome/common/importer/profile_import_process_messages.h"
15#include "content/public/browser/browser_thread.h"
16#include "content/public/browser/utility_process_host.h"
17#include "grit/generated_resources.h"
18#include "ui/base/l10n/l10n_util.h"
19
20using content::BrowserThread;
21using content::UtilityProcessHost;
22
23ExternalProcessImporterClient::ExternalProcessImporterClient(
24    ExternalProcessImporterHost* importer_host,
25    const importer::SourceProfile& source_profile,
26    uint16 items,
27    InProcessImporterBridge* bridge)
28    : total_bookmarks_count_(0),
29      total_history_rows_count_(0),
30      total_favicons_count_(0),
31      process_importer_host_(importer_host),
32      source_profile_(source_profile),
33      items_(items),
34      bridge_(bridge),
35      cancelled_(false) {
36  process_importer_host_->NotifyImportStarted();
37}
38
39void ExternalProcessImporterClient::Start() {
40  AddRef();  // balanced in Cleanup.
41  BrowserThread::ID thread_id;
42  CHECK(BrowserThread::GetCurrentThreadIdentifier(&thread_id));
43  BrowserThread::PostTask(
44      BrowserThread::IO, FROM_HERE,
45      base::Bind(&ExternalProcessImporterClient::StartProcessOnIOThread,
46                 this,
47                 thread_id));
48}
49
50void ExternalProcessImporterClient::Cancel() {
51  if (cancelled_)
52    return;
53
54  cancelled_ = true;
55  BrowserThread::PostTask(
56      BrowserThread::IO, FROM_HERE,
57      base::Bind(
58          &ExternalProcessImporterClient::CancelImportProcessOnIOThread,
59          this));
60  Release();
61}
62
63void ExternalProcessImporterClient::OnProcessCrashed(int exit_code) {
64  DLOG(ERROR) << __FUNCTION__;
65  if (cancelled_)
66    return;
67
68  process_importer_host_->Cancel();
69}
70
71bool ExternalProcessImporterClient::OnMessageReceived(
72    const IPC::Message& message) {
73  bool handled = true;
74  IPC_BEGIN_MESSAGE_MAP(ExternalProcessImporterClient, message)
75    // Notification messages about the state of the import process.
76    IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_Import_Started,
77                        OnImportStart)
78    IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_Import_Finished,
79                        OnImportFinished)
80    IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_ImportItem_Started,
81                        OnImportItemStart)
82    IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_ImportItem_Finished,
83                        OnImportItemFinished)
84    // Data messages containing items to be written to the user profile.
85    IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyHistoryImportStart,
86                        OnHistoryImportStart)
87    IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyHistoryImportGroup,
88                        OnHistoryImportGroup)
89    IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyHomePageImportReady,
90                        OnHomePageImportReady)
91    IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyBookmarksImportStart,
92                        OnBookmarksImportStart)
93    IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyBookmarksImportGroup,
94                        OnBookmarksImportGroup)
95    IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyFaviconsImportStart,
96                        OnFaviconsImportStart)
97    IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyFaviconsImportGroup,
98                        OnFaviconsImportGroup)
99    IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyPasswordFormReady,
100                        OnPasswordFormImportReady)
101    IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyKeywordsReady,
102                        OnKeywordsImportReady)
103    IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyFirefoxSearchEngData,
104                        OnFirefoxSearchEngineDataReceived)
105#if defined(OS_WIN)
106    IPC_MESSAGE_HANDLER(ProfileImportProcessHostMsg_NotifyIE7PasswordInfo,
107                        OnIE7PasswordReceived)
108#endif
109    IPC_MESSAGE_UNHANDLED(handled = false)
110  IPC_END_MESSAGE_MAP()
111  return handled;
112}
113
114void ExternalProcessImporterClient::OnImportStart() {
115  if (cancelled_)
116    return;
117
118  bridge_->NotifyStarted();
119}
120
121void ExternalProcessImporterClient::OnImportFinished(
122    bool succeeded, const std::string& error_msg) {
123  if (cancelled_)
124    return;
125
126  if (!succeeded)
127    LOG(WARNING) << "Import failed.  Error: " << error_msg;
128  Cleanup();
129}
130
131void ExternalProcessImporterClient::OnImportItemStart(int item_data) {
132  if (cancelled_)
133    return;
134
135  bridge_->NotifyItemStarted(static_cast<importer::ImportItem>(item_data));
136}
137
138void ExternalProcessImporterClient::OnImportItemFinished(int item_data) {
139  if (cancelled_)
140    return;
141
142  importer::ImportItem import_item =
143      static_cast<importer::ImportItem>(item_data);
144  bridge_->NotifyItemEnded(import_item);
145  BrowserThread::PostTask(
146      BrowserThread::IO, FROM_HERE,
147      base::Bind(&ExternalProcessImporterClient::NotifyItemFinishedOnIOThread,
148                 this,
149                 import_item));
150}
151
152void ExternalProcessImporterClient::OnHistoryImportStart(
153    size_t total_history_rows_count) {
154  if (cancelled_)
155    return;
156
157  total_history_rows_count_ = total_history_rows_count;
158  history_rows_.reserve(total_history_rows_count);
159}
160
161void ExternalProcessImporterClient::OnHistoryImportGroup(
162    const std::vector<ImporterURLRow>& history_rows_group,
163    int visit_source) {
164  if (cancelled_)
165    return;
166
167  history_rows_.insert(history_rows_.end(), history_rows_group.begin(),
168                       history_rows_group.end());
169  if (history_rows_.size() == total_history_rows_count_)
170    bridge_->SetHistoryItems(history_rows_,
171                             static_cast<importer::VisitSource>(visit_source));
172}
173
174void ExternalProcessImporterClient::OnHomePageImportReady(
175    const GURL& home_page) {
176  if (cancelled_)
177    return;
178
179  bridge_->AddHomePage(home_page);
180}
181
182void ExternalProcessImporterClient::OnBookmarksImportStart(
183    const string16& first_folder_name,
184    size_t total_bookmarks_count) {
185  if (cancelled_)
186    return;
187
188  bookmarks_first_folder_name_ = first_folder_name;
189  total_bookmarks_count_ = total_bookmarks_count;
190  bookmarks_.reserve(total_bookmarks_count);
191}
192
193void ExternalProcessImporterClient::OnBookmarksImportGroup(
194    const std::vector<ImportedBookmarkEntry>& bookmarks_group) {
195  if (cancelled_)
196    return;
197
198  // Collect sets of bookmarks from importer process until we have reached
199  // total_bookmarks_count_:
200  bookmarks_.insert(bookmarks_.end(), bookmarks_group.begin(),
201                    bookmarks_group.end());
202  if (bookmarks_.size() == total_bookmarks_count_)
203    bridge_->AddBookmarks(bookmarks_, bookmarks_first_folder_name_);
204}
205
206void ExternalProcessImporterClient::OnFaviconsImportStart(
207    size_t total_favicons_count) {
208  if (cancelled_)
209    return;
210
211  total_favicons_count_ = total_favicons_count;
212  favicons_.reserve(total_favicons_count);
213}
214
215void ExternalProcessImporterClient::OnFaviconsImportGroup(
216    const std::vector<ImportedFaviconUsage>& favicons_group) {
217  if (cancelled_)
218    return;
219
220  favicons_.insert(favicons_.end(), favicons_group.begin(),
221                    favicons_group.end());
222  if (favicons_.size() == total_favicons_count_)
223    bridge_->SetFavicons(favicons_);
224}
225
226void ExternalProcessImporterClient::OnPasswordFormImportReady(
227    const content::PasswordForm& form) {
228  if (cancelled_)
229    return;
230
231  bridge_->SetPasswordForm(form);
232}
233
234void ExternalProcessImporterClient::OnKeywordsImportReady(
235    const std::vector<importer::URLKeywordInfo>& url_keywords,
236    bool unique_on_host_and_path) {
237  if (cancelled_)
238    return;
239  bridge_->SetKeywords(url_keywords, unique_on_host_and_path);
240}
241
242void ExternalProcessImporterClient::OnFirefoxSearchEngineDataReceived(
243    const std::vector<std::string> search_engine_data) {
244  if (cancelled_)
245    return;
246  bridge_->SetFirefoxSearchEnginesXMLData(search_engine_data);
247}
248
249#if defined(OS_WIN)
250void ExternalProcessImporterClient::OnIE7PasswordReceived(
251    const importer::ImporterIE7PasswordInfo& importer_password_info) {
252  if (cancelled_)
253    return;
254  bridge_->AddIE7PasswordInfo(importer_password_info);
255}
256#endif
257
258ExternalProcessImporterClient::~ExternalProcessImporterClient() {}
259
260void ExternalProcessImporterClient::Cleanup() {
261  if (cancelled_)
262    return;
263
264  if (process_importer_host_)
265    process_importer_host_->NotifyImportEnded();
266  Release();
267}
268
269void ExternalProcessImporterClient::CancelImportProcessOnIOThread() {
270  if (utility_process_host_.get())
271    utility_process_host_->Send(new ProfileImportProcessMsg_CancelImport());
272}
273
274void ExternalProcessImporterClient::NotifyItemFinishedOnIOThread(
275    importer::ImportItem import_item) {
276  utility_process_host_->Send(
277      new ProfileImportProcessMsg_ReportImportItemFinished(import_item));
278}
279
280void ExternalProcessImporterClient::StartProcessOnIOThread(
281    BrowserThread::ID thread_id) {
282  utility_process_host_ = UtilityProcessHost::Create(
283      this, BrowserThread::GetMessageLoopProxyForThread(thread_id).get())
284      ->AsWeakPtr();
285  utility_process_host_->DisableSandbox();
286
287#if defined(OS_MACOSX)
288  base::EnvironmentVector env;
289  std::string dylib_path = GetFirefoxDylibPath().value();
290  if (!dylib_path.empty())
291    env.push_back(std::make_pair("DYLD_FALLBACK_LIBRARY_PATH", dylib_path));
292  utility_process_host_->SetEnv(env);
293#endif
294
295  // Dictionary of all localized strings that could be needed by the importer
296  // in the external process.
297  DictionaryValue localized_strings;
298  localized_strings.SetString(
299      base::IntToString(IDS_BOOKMARK_GROUP),
300      l10n_util::GetStringUTF8(IDS_BOOKMARK_GROUP));
301  localized_strings.SetString(
302      base::IntToString(IDS_BOOKMARK_GROUP_FROM_FIREFOX),
303      l10n_util::GetStringUTF8(IDS_BOOKMARK_GROUP_FROM_FIREFOX));
304  localized_strings.SetString(
305      base::IntToString(IDS_BOOKMARK_GROUP_FROM_SAFARI),
306      l10n_util::GetStringUTF8(IDS_BOOKMARK_GROUP_FROM_SAFARI));
307  localized_strings.SetString(
308      base::IntToString(IDS_IMPORT_FROM_FIREFOX),
309      l10n_util::GetStringUTF8(IDS_IMPORT_FROM_FIREFOX));
310  localized_strings.SetString(
311      base::IntToString(IDS_IMPORT_FROM_ICEWEASEL),
312      l10n_util::GetStringUTF8(IDS_IMPORT_FROM_ICEWEASEL));
313  localized_strings.SetString(
314      base::IntToString(IDS_IMPORT_FROM_SAFARI),
315      l10n_util::GetStringUTF8(IDS_IMPORT_FROM_SAFARI));
316  localized_strings.SetString(
317      base::IntToString(IDS_BOOKMARK_BAR_FOLDER_NAME),
318      l10n_util::GetStringUTF8(IDS_BOOKMARK_BAR_FOLDER_NAME));
319
320  utility_process_host_->Send(new ProfileImportProcessMsg_StartImport(
321      source_profile_, items_, localized_strings));
322}
323