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