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_picasa_album_table_reader.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
10#include "chrome/common/chrome_utility_messages.h"
11#include "chrome/common/extensions/chrome_utility_extensions_messages.h"
12#include "content/public/browser/browser_thread.h"
13#include "content/public/browser/child_process_data.h"
14
15using content::BrowserThread;
16
17namespace picasa {
18
19SafePicasaAlbumTableReader::SafePicasaAlbumTableReader(
20    AlbumTableFiles album_table_files)
21    : album_table_files_(album_table_files.Pass()),
22      parser_state_(INITIAL_STATE) {
23  // TODO(tommycli): Add DCHECK to make sure |album_table_files| are all
24  // opened read-only once security adds ability to check PlatformFiles.
25  DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
26}
27
28void SafePicasaAlbumTableReader::Start(const ParserCallback& callback) {
29  DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
30  DCHECK(!callback.is_null());
31
32  callback_ = callback;
33
34  // Don't bother spawning process if any of the files are invalid.
35  if (!album_table_files_.indicator_file.IsValid() ||
36      !album_table_files_.category_file.IsValid() ||
37      !album_table_files_.date_file.IsValid() ||
38      !album_table_files_.filename_file.IsValid() ||
39      !album_table_files_.name_file.IsValid() ||
40      !album_table_files_.token_file.IsValid() ||
41      !album_table_files_.uid_file.IsValid()) {
42    MediaFileSystemBackend::MediaTaskRunner()->PostTask(
43        FROM_HERE,
44        base::Bind(callback_,
45                   false /* parse_success */,
46                   std::vector<AlbumInfo>(),
47                   std::vector<AlbumInfo>()));
48    return;
49  }
50
51  BrowserThread::PostTask(
52      BrowserThread::IO,
53      FROM_HERE,
54      base::Bind(&SafePicasaAlbumTableReader::StartWorkOnIOThread, this));
55}
56
57SafePicasaAlbumTableReader::~SafePicasaAlbumTableReader() {
58}
59
60void SafePicasaAlbumTableReader::StartWorkOnIOThread() {
61  DCHECK_CURRENTLY_ON(BrowserThread::IO);
62  DCHECK_EQ(INITIAL_STATE, parser_state_);
63
64  utility_process_host_ = content::UtilityProcessHost::Create(
65      this,
66      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get())
67      ->AsWeakPtr();
68  // Wait for the startup notification before sending the main IPC to the
69  // utility process, so that we can dup the file handle.
70  utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
71  parser_state_ = PINGED_UTILITY_PROCESS_STATE;
72}
73
74void SafePicasaAlbumTableReader::OnProcessStarted() {
75  DCHECK_CURRENTLY_ON(BrowserThread::IO);
76  if (parser_state_ != PINGED_UTILITY_PROCESS_STATE)
77    return;
78
79  if (utility_process_host_->GetData().handle == base::kNullProcessHandle) {
80    DLOG(ERROR) << "Child process handle is null";
81  }
82  AlbumTableFilesForTransit files_for_transit;
83  files_for_transit.indicator_file = IPC::TakeFileHandleForProcess(
84      album_table_files_.indicator_file.Pass(),
85      utility_process_host_->GetData().handle);
86  files_for_transit.category_file = IPC::TakeFileHandleForProcess(
87      album_table_files_.category_file.Pass(),
88      utility_process_host_->GetData().handle);
89  files_for_transit.date_file = IPC::TakeFileHandleForProcess(
90      album_table_files_.date_file.Pass(),
91      utility_process_host_->GetData().handle);
92  files_for_transit.filename_file = IPC::TakeFileHandleForProcess(
93      album_table_files_.filename_file.Pass(),
94      utility_process_host_->GetData().handle);
95  files_for_transit.name_file = IPC::TakeFileHandleForProcess(
96      album_table_files_.name_file.Pass(),
97      utility_process_host_->GetData().handle);
98  files_for_transit.token_file = IPC::TakeFileHandleForProcess(
99      album_table_files_.token_file.Pass(),
100      utility_process_host_->GetData().handle);
101  files_for_transit.uid_file = IPC::TakeFileHandleForProcess(
102      album_table_files_.uid_file.Pass(),
103      utility_process_host_->GetData().handle);
104  utility_process_host_->Send(new ChromeUtilityMsg_ParsePicasaPMPDatabase(
105      files_for_transit));
106  parser_state_ = STARTED_PARSING_STATE;
107}
108
109void SafePicasaAlbumTableReader::OnParsePicasaPMPDatabaseFinished(
110    bool parse_success,
111    const std::vector<AlbumInfo>& albums,
112    const std::vector<AlbumInfo>& folders) {
113  DCHECK_CURRENTLY_ON(BrowserThread::IO);
114  DCHECK(!callback_.is_null());
115  if (parser_state_ != STARTED_PARSING_STATE)
116    return;
117
118  MediaFileSystemBackend::MediaTaskRunner()->PostTask(
119      FROM_HERE, base::Bind(callback_, parse_success, albums, folders));
120  parser_state_ = FINISHED_PARSING_STATE;
121}
122
123void SafePicasaAlbumTableReader::OnProcessCrashed(int exit_code) {
124  DLOG(ERROR) << "SafePicasaAlbumTableReader::OnProcessCrashed()";
125  OnParsePicasaPMPDatabaseFinished(
126      false, std::vector<AlbumInfo>(), std::vector<AlbumInfo>());
127}
128
129bool SafePicasaAlbumTableReader::OnMessageReceived(
130    const IPC::Message& message) {
131  bool handled = true;
132  IPC_BEGIN_MESSAGE_MAP(SafePicasaAlbumTableReader, message)
133    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted,
134                        OnProcessStarted)
135    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParsePicasaPMPDatabase_Finished,
136                        OnParsePicasaPMPDatabaseFinished)
137    IPC_MESSAGE_UNHANDLED(handled = false)
138  IPC_END_MESSAGE_MAP()
139  return handled;
140}
141
142}  // namespace picasa
143