1// Copyright 2013 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/media_galleries/fileapi/safe_iapps_library_parser.h"
6
7#include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
8#include "chrome/common/chrome_utility_messages.h"
9#include "chrome/common/extensions/chrome_utility_extensions_messages.h"
10#include "content/public/browser/browser_thread.h"
11#include "content/public/browser/child_process_data.h"
12#include "ipc/ipc_platform_file.h"
13
14using content::BrowserThread;
15using content::UtilityProcessHost;
16
17namespace iapps {
18
19SafeIAppsLibraryParser::SafeIAppsLibraryParser()
20    : parser_state_(INITIAL_STATE) {}
21
22void SafeIAppsLibraryParser::ParseIPhotoLibrary(
23    const base::FilePath& library_file,
24    const IPhotoParserCallback& callback) {
25  library_file_path_ = library_file;
26  iphoto_callback_ = callback;
27  Start();
28}
29
30void SafeIAppsLibraryParser::ParseITunesLibrary(
31    const base::FilePath& library_file,
32    const ITunesParserCallback& callback) {
33  library_file_path_ = library_file;
34  itunes_callback_ = callback;
35  Start();
36}
37
38void SafeIAppsLibraryParser::Start() {
39  DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
40
41  // |library_file_| will be closed on the IO thread once it has been handed
42  // off to the child process.
43  library_file_.Initialize(library_file_path_,
44                           base::File::FLAG_OPEN | base::File::FLAG_READ);
45  if (!library_file_.IsValid()) {
46    VLOG(1) << "Could not open iApps library XML file: "
47            << library_file_path_.value();
48    BrowserThread::PostTask(
49        BrowserThread::IO, FROM_HERE,
50        base::Bind(&SafeIAppsLibraryParser::OnOpenLibraryFileFailed, this));
51    return;
52  }
53
54  BrowserThread::PostTask(
55      BrowserThread::IO, FROM_HERE,
56      base::Bind(&SafeIAppsLibraryParser::StartProcessOnIOThread, this));
57}
58
59SafeIAppsLibraryParser::~SafeIAppsLibraryParser() {}
60
61void SafeIAppsLibraryParser::StartProcessOnIOThread() {
62  DCHECK_CURRENTLY_ON(BrowserThread::IO);
63  DCHECK_EQ(INITIAL_STATE, parser_state_);
64
65  scoped_refptr<base::MessageLoopProxy> message_loop_proxy =
66      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
67  utility_process_host_ =
68      UtilityProcessHost::Create(this, message_loop_proxy.get())->AsWeakPtr();
69  // Wait for the startup notification before sending the main IPC to the
70  // utility process, so that we can dup the file handle.
71  utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
72  parser_state_ = PINGED_UTILITY_PROCESS_STATE;
73}
74
75void SafeIAppsLibraryParser::OnUtilityProcessStarted() {
76  DCHECK_CURRENTLY_ON(BrowserThread::IO);
77  if (parser_state_ != PINGED_UTILITY_PROCESS_STATE)
78    return;
79
80  if (utility_process_host_->GetData().handle == base::kNullProcessHandle) {
81    DLOG(ERROR) << "Child process handle is null";
82    OnError();
83    return;
84  }
85
86  if (!itunes_callback_.is_null()) {
87    utility_process_host_->Send(
88        new ChromeUtilityMsg_ParseITunesLibraryXmlFile(
89            IPC::TakeFileHandleForProcess(
90                library_file_.Pass(),
91                utility_process_host_->GetData().handle)));
92  } else if (!iphoto_callback_.is_null()) {
93#if defined(OS_MACOSX)
94    utility_process_host_->Send(
95        new ChromeUtilityMsg_ParseIPhotoLibraryXmlFile(
96            IPC::TakeFileHandleForProcess(
97                library_file_.Pass(),
98                utility_process_host_->GetData().handle)));
99#endif
100  }
101
102  parser_state_ = STARTED_PARSING_STATE;
103}
104
105#if defined(OS_MACOSX)
106void SafeIAppsLibraryParser::OnGotIPhotoLibrary(
107    bool result, const iphoto::parser::Library& library) {
108  DCHECK_CURRENTLY_ON(BrowserThread::IO);
109  DCHECK(!iphoto_callback_.is_null());
110
111  if (parser_state_ != STARTED_PARSING_STATE)
112    return;
113
114  MediaFileSystemBackend::MediaTaskRunner()->PostTask(
115      FROM_HERE,
116      base::Bind(iphoto_callback_, result, library));
117  parser_state_ = FINISHED_PARSING_STATE;
118}
119#endif
120
121void SafeIAppsLibraryParser::OnGotITunesLibrary(
122    bool result, const itunes::parser::Library& library) {
123  DCHECK_CURRENTLY_ON(BrowserThread::IO);
124  DCHECK(!itunes_callback_.is_null());
125
126  if (parser_state_ != STARTED_PARSING_STATE)
127    return;
128
129  MediaFileSystemBackend::MediaTaskRunner()->PostTask(
130      FROM_HERE,
131      base::Bind(itunes_callback_, result, library));
132  parser_state_ = FINISHED_PARSING_STATE;
133}
134
135void SafeIAppsLibraryParser::OnOpenLibraryFileFailed() {
136  DCHECK_CURRENTLY_ON(BrowserThread::IO);
137  OnError();
138}
139
140void SafeIAppsLibraryParser::OnProcessCrashed(int exit_code) {
141  OnError();
142}
143
144void SafeIAppsLibraryParser::OnError() {
145  parser_state_ = FINISHED_PARSING_STATE;
146  if (!itunes_callback_.is_null())
147    OnGotITunesLibrary(false /* failed */, itunes::parser::Library());
148
149#if defined(OS_MACOSX)
150  if (!iphoto_callback_.is_null())
151    OnGotIPhotoLibrary(false /* failed */, iphoto::parser::Library());
152#endif
153}
154
155bool SafeIAppsLibraryParser::OnMessageReceived(
156    const IPC::Message& message) {
157  bool handled = true;
158  IPC_BEGIN_MESSAGE_MAP(SafeIAppsLibraryParser, message)
159    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted,
160                        OnUtilityProcessStarted)
161#if defined(OS_MACOSX)
162    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GotIPhotoLibrary,
163                        OnGotIPhotoLibrary)
164#endif
165    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GotITunesLibrary,
166                        OnGotITunesLibrary)
167    IPC_MESSAGE_UNHANDLED(handled = false)
168  IPC_END_MESSAGE_MAP()
169  return handled;
170}
171
172}  // namespace iapps
173