chrome_content_utility_client.cc revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
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/utility/chrome_content_utility_client.h" 6 7#include "base/base64.h" 8#include "base/bind.h" 9#include "base/command_line.h" 10#include "base/json/json_reader.h" 11#include "base/memory/ref_counted.h" 12#include "base/memory/scoped_ptr.h" 13#include "chrome/common/chrome_utility_messages.h" 14#include "chrome/common/extensions/chrome_manifest_handlers.h" 15#include "chrome/common/extensions/extension.h" 16#include "chrome/common/extensions/extension_l10n_util.h" 17#include "chrome/common/extensions/manifest.h" 18#include "chrome/common/extensions/permissions/chrome_api_permissions.h" 19#include "chrome/common/extensions/update_manifest.h" 20#include "chrome/common/safe_browsing/zip_analyzer.h" 21#include "chrome/utility/extensions/unpacker.h" 22#include "chrome/utility/profile_import_handler.h" 23#include "chrome/utility/web_resource_unpacker.h" 24#include "content/public/child/image_decoder_utils.h" 25#include "content/public/utility/utility_thread.h" 26#include "printing/page_range.h" 27#include "third_party/skia/include/core/SkBitmap.h" 28#include "third_party/zlib/google/zip.h" 29#include "ui/base/ui_base_switches.h" 30#include "ui/gfx/codec/jpeg_codec.h" 31#include "ui/gfx/rect.h" 32#include "ui/gfx/size.h" 33 34#if defined(OS_WIN) 35#include "base/file_util.h" 36#include "base/path_service.h" 37#include "base/win/iat_patch_function.h" 38#include "base/win/scoped_handle.h" 39#include "chrome/common/chrome_paths.h" 40#include "chrome/utility/itunes_pref_parser_win.h" 41#include "printing/emf_win.h" 42#include "ui/gfx/gdi_util.h" 43#endif // defined(OS_WIN) 44 45#if defined(OS_WIN) || defined(OS_MACOSX) 46#include "chrome/common/media_galleries/picasa_types.h" 47#include "chrome/utility/itunes_library_parser.h" 48#include "chrome/utility/media_galleries/picasa_album_table_reader.h" 49#endif // defined(OS_WIN) || defined(OS_MACOSX) 50 51#if defined(ENABLE_PRINTING) 52#include "chrome/common/child_process_logging.h" 53#include "printing/backend/print_backend.h" 54#endif 55 56#if defined(ENABLE_MDNS) 57#include "chrome/utility/local_discovery/service_discovery_message_handler.h" 58#endif // ENABLE_MDNS 59 60namespace chrome { 61 62namespace { 63 64bool Send(IPC::Message* message) { 65 return content::UtilityThread::Get()->Send(message); 66} 67 68void ReleaseProcessIfNeeded() { 69 content::UtilityThread::Get()->ReleaseProcessIfNeeded(); 70} 71 72} // namespace 73 74ChromeContentUtilityClient::ChromeContentUtilityClient() { 75#if !defined(OS_ANDROID) 76 handlers_.push_back(new ProfileImportHandler()); 77#endif // OS_ANDROID 78 79#if defined(ENABLE_MDNS) 80 handlers_.push_back(new local_discovery::ServiceDiscoveryMessageHandler()); 81#endif // ENABLE_MDNS 82} 83 84ChromeContentUtilityClient::~ChromeContentUtilityClient() { 85} 86 87void ChromeContentUtilityClient::UtilityThreadStarted() { 88#if defined(OS_WIN) 89 // Load the pdf plugin before the sandbox is turned on. This is for Windows 90 // only because we need this DLL only on Windows. 91 base::FilePath pdf; 92 if (PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf) && 93 base::PathExists(pdf)) { 94 bool rv = !!LoadLibrary(pdf.value().c_str()); 95 DCHECK(rv) << "Couldn't load PDF plugin"; 96 } 97#endif 98 99 CommandLine* command_line = CommandLine::ForCurrentProcess(); 100 std::string lang = command_line->GetSwitchValueASCII(switches::kLang); 101 if (!lang.empty()) 102 extension_l10n_util::SetProcessLocale(lang); 103} 104 105bool ChromeContentUtilityClient::OnMessageReceived( 106 const IPC::Message& message) { 107 bool handled = true; 108 IPC_BEGIN_MESSAGE_MAP(ChromeContentUtilityClient, message) 109 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_UnpackExtension, OnUnpackExtension) 110 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_UnpackWebResource, 111 OnUnpackWebResource) 112 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ParseUpdateManifest, 113 OnParseUpdateManifest) 114 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_DecodeImage, OnDecodeImage) 115 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_DecodeImageBase64, OnDecodeImageBase64) 116 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RenderPDFPagesToMetafile, 117 OnRenderPDFPagesToMetafile) 118 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RobustJPEGDecodeImage, 119 OnRobustJPEGDecodeImage) 120 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ParseJSON, OnParseJSON) 121 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_GetPrinterCapsAndDefaults, 122 OnGetPrinterCapsAndDefaults) 123 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_StartupPing, OnStartupPing) 124 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_AnalyzeZipFileForDownloadProtection, 125 OnAnalyzeZipFileForDownloadProtection) 126 127#if defined(OS_CHROMEOS) 128 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_CreateZipFile, OnCreateZipFile) 129#endif // defined(OS_CHROMEOS) 130 131#if defined(OS_WIN) 132 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ParseITunesPrefXml, 133 OnParseITunesPrefXml) 134#endif // defined(OS_WIN) 135 136#if defined(OS_WIN) || defined(OS_MACOSX) 137 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ParseITunesLibraryXmlFile, 138 OnParseITunesLibraryXmlFile) 139 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ParsePicasaPMPDatabase, 140 OnParsePicasaPMPDatabase) 141#endif // defined(OS_WIN) || defined(OS_MACOSX) 142 143 IPC_MESSAGE_UNHANDLED(handled = false) 144 IPC_END_MESSAGE_MAP() 145 146 for (Handlers::iterator it = handlers_.begin(); 147 !handled && it != handlers_.end(); ++it) { 148 handled = (*it)->OnMessageReceived(message); 149 } 150 151 return handled; 152} 153 154void ChromeContentUtilityClient::PreSandboxStartup() { 155#if defined(ENABLE_MDNS) 156 local_discovery::ServiceDiscoveryMessageHandler::PreSandboxStartup(); 157#endif // ENABLE_MDNS 158} 159 160void ChromeContentUtilityClient::OnUnpackExtension( 161 const base::FilePath& extension_path, 162 const std::string& extension_id, 163 int location, 164 int creation_flags) { 165 CHECK_GT(location, extensions::Manifest::INVALID_LOCATION); 166 CHECK_LT(location, extensions::Manifest::NUM_LOCATIONS); 167 extensions::PermissionsInfo::GetInstance()->InitializeWithDelegate( 168 extensions::ChromeAPIPermissions()); 169 extensions::RegisterChromeManifestHandlers(); 170 extensions::Unpacker unpacker( 171 extension_path, 172 extension_id, 173 static_cast<extensions::Manifest::Location>(location), 174 creation_flags); 175 if (unpacker.Run() && unpacker.DumpImagesToFile() && 176 unpacker.DumpMessageCatalogsToFile()) { 177 Send(new ChromeUtilityHostMsg_UnpackExtension_Succeeded( 178 *unpacker.parsed_manifest())); 179 } else { 180 Send(new ChromeUtilityHostMsg_UnpackExtension_Failed( 181 unpacker.error_message())); 182 } 183 184 ReleaseProcessIfNeeded(); 185} 186 187void ChromeContentUtilityClient::OnUnpackWebResource( 188 const std::string& resource_data) { 189 // Parse json data. 190 // TODO(mrc): Add the possibility of a template that controls parsing, and 191 // the ability to download and verify images. 192 WebResourceUnpacker unpacker(resource_data); 193 if (unpacker.Run()) { 194 Send(new ChromeUtilityHostMsg_UnpackWebResource_Succeeded( 195 *unpacker.parsed_json())); 196 } else { 197 Send(new ChromeUtilityHostMsg_UnpackWebResource_Failed( 198 unpacker.error_message())); 199 } 200 201 ReleaseProcessIfNeeded(); 202} 203 204void ChromeContentUtilityClient::OnParseUpdateManifest(const std::string& xml) { 205 UpdateManifest manifest; 206 if (!manifest.Parse(xml)) { 207 Send(new ChromeUtilityHostMsg_ParseUpdateManifest_Failed( 208 manifest.errors())); 209 } else { 210 Send(new ChromeUtilityHostMsg_ParseUpdateManifest_Succeeded( 211 manifest.results())); 212 } 213 ReleaseProcessIfNeeded(); 214} 215 216void ChromeContentUtilityClient::OnDecodeImage( 217 const std::vector<unsigned char>& encoded_data) { 218 const SkBitmap& decoded_image = content::DecodeImage(&encoded_data[0], 219 gfx::Size(), 220 encoded_data.size()); 221 if (decoded_image.empty()) { 222 Send(new ChromeUtilityHostMsg_DecodeImage_Failed()); 223 } else { 224 Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(decoded_image)); 225 } 226 ReleaseProcessIfNeeded(); 227} 228 229void ChromeContentUtilityClient::OnDecodeImageBase64( 230 const std::string& encoded_string) { 231 std::string decoded_string; 232 233 if (!base::Base64Decode(encoded_string, &decoded_string)) { 234 Send(new ChromeUtilityHostMsg_DecodeImage_Failed()); 235 return; 236 } 237 238 std::vector<unsigned char> decoded_vector(decoded_string.size()); 239 for (size_t i = 0; i < decoded_string.size(); ++i) { 240 decoded_vector[i] = static_cast<unsigned char>(decoded_string[i]); 241 } 242 243 OnDecodeImage(decoded_vector); 244} 245 246#if defined(OS_CHROMEOS) 247void ChromeContentUtilityClient::OnCreateZipFile( 248 const base::FilePath& src_dir, 249 const std::vector<base::FilePath>& src_relative_paths, 250 const base::FileDescriptor& dest_fd) { 251 bool succeeded = true; 252 253 // Check sanity of source relative paths. Reject if path is absolute or 254 // contains any attempt to reference a parent directory ("../" tricks). 255 for (std::vector<base::FilePath>::const_iterator iter = 256 src_relative_paths.begin(); iter != src_relative_paths.end(); 257 ++iter) { 258 if (iter->IsAbsolute() || iter->ReferencesParent()) { 259 succeeded = false; 260 break; 261 } 262 } 263 264 if (succeeded) 265 succeeded = zip::ZipFiles(src_dir, src_relative_paths, dest_fd.fd); 266 267 if (succeeded) 268 Send(new ChromeUtilityHostMsg_CreateZipFile_Succeeded()); 269 else 270 Send(new ChromeUtilityHostMsg_CreateZipFile_Failed()); 271 ReleaseProcessIfNeeded(); 272} 273#endif // defined(OS_CHROMEOS) 274 275void ChromeContentUtilityClient::OnRenderPDFPagesToMetafile( 276 base::PlatformFile pdf_file, 277 const base::FilePath& metafile_path, 278 const printing::PdfRenderSettings& pdf_render_settings, 279 const std::vector<printing::PageRange>& page_ranges) { 280 bool succeeded = false; 281#if defined(OS_WIN) 282 int highest_rendered_page_number = 0; 283 double scale_factor = 1.0; 284 succeeded = RenderPDFToWinMetafile(pdf_file, 285 metafile_path, 286 pdf_render_settings.area(), 287 pdf_render_settings.dpi(), 288 pdf_render_settings.autorotate(), 289 page_ranges, 290 &highest_rendered_page_number, 291 &scale_factor); 292 if (succeeded) { 293 Send(new ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Succeeded( 294 highest_rendered_page_number, scale_factor)); 295 } 296#endif // defined(OS_WIN) 297 if (!succeeded) { 298 Send(new ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed()); 299 } 300 ReleaseProcessIfNeeded(); 301} 302 303#if defined(OS_WIN) 304// Exported by pdf.dll 305typedef bool (*RenderPDFPageToDCProc)( 306 const unsigned char* pdf_buffer, int buffer_size, int page_number, HDC dc, 307 int dpi_x, int dpi_y, int bounds_origin_x, int bounds_origin_y, 308 int bounds_width, int bounds_height, bool fit_to_bounds, 309 bool stretch_to_bounds, bool keep_aspect_ratio, bool center_in_bounds, 310 bool autorotate); 311 312typedef bool (*GetPDFDocInfoProc)(const unsigned char* pdf_buffer, 313 int buffer_size, int* page_count, 314 double* max_page_width); 315 316// The 2 below IAT patch functions are almost identical to the code in 317// render_process_impl.cc. This is needed to work around specific Windows APIs 318// used by the Chrome PDF plugin that will fail in the sandbox. 319static base::win::IATPatchFunction g_iat_patch_createdca; 320HDC WINAPI UtilityProcess_CreateDCAPatch(LPCSTR driver_name, 321 LPCSTR device_name, 322 LPCSTR output, 323 const DEVMODEA* init_data) { 324 if (driver_name && 325 (std::string("DISPLAY") == std::string(driver_name))) 326 // CreateDC fails behind the sandbox, but not CreateCompatibleDC. 327 return CreateCompatibleDC(NULL); 328 329 NOTREACHED(); 330 return CreateDCA(driver_name, device_name, output, init_data); 331} 332 333static base::win::IATPatchFunction g_iat_patch_get_font_data; 334DWORD WINAPI UtilityProcess_GetFontDataPatch( 335 HDC hdc, DWORD table, DWORD offset, LPVOID buffer, DWORD length) { 336 int rv = GetFontData(hdc, table, offset, buffer, length); 337 if (rv == GDI_ERROR && hdc) { 338 HFONT font = static_cast<HFONT>(GetCurrentObject(hdc, OBJ_FONT)); 339 340 LOGFONT logfont; 341 if (GetObject(font, sizeof(LOGFONT), &logfont)) { 342 std::vector<char> font_data; 343 content::UtilityThread::Get()->PreCacheFont(logfont); 344 rv = GetFontData(hdc, table, offset, buffer, length); 345 content::UtilityThread::Get()->ReleaseCachedFonts(); 346 } 347 } 348 return rv; 349} 350 351bool ChromeContentUtilityClient::RenderPDFToWinMetafile( 352 base::PlatformFile pdf_file, 353 const base::FilePath& metafile_path, 354 const gfx::Rect& render_area, 355 int render_dpi, 356 bool autorotate, 357 const std::vector<printing::PageRange>& page_ranges, 358 int* highest_rendered_page_number, 359 double* scale_factor) { 360 *highest_rendered_page_number = -1; 361 *scale_factor = 1.0; 362 base::win::ScopedHandle file(pdf_file); 363 base::FilePath pdf_module_path; 364 PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_module_path); 365 HMODULE pdf_module = GetModuleHandle(pdf_module_path.value().c_str()); 366 if (!pdf_module) 367 return false; 368 369 RenderPDFPageToDCProc render_proc = 370 reinterpret_cast<RenderPDFPageToDCProc>( 371 GetProcAddress(pdf_module, "RenderPDFPageToDC")); 372 if (!render_proc) 373 return false; 374 375 GetPDFDocInfoProc get_info_proc = reinterpret_cast<GetPDFDocInfoProc>( 376 GetProcAddress(pdf_module, "GetPDFDocInfo")); 377 if (!get_info_proc) 378 return false; 379 380 // Patch the IAT for handling specific APIs known to fail in the sandbox. 381 if (!g_iat_patch_createdca.is_patched()) 382 g_iat_patch_createdca.Patch(pdf_module_path.value().c_str(), 383 "gdi32.dll", "CreateDCA", 384 UtilityProcess_CreateDCAPatch); 385 386 if (!g_iat_patch_get_font_data.is_patched()) 387 g_iat_patch_get_font_data.Patch(pdf_module_path.value().c_str(), 388 "gdi32.dll", "GetFontData", 389 UtilityProcess_GetFontDataPatch); 390 391 // TODO(sanjeevr): Add a method to the PDF DLL that takes in a file handle 392 // and a page range array. That way we don't need to read the entire PDF into 393 // memory. 394 DWORD length = ::GetFileSize(file, NULL); 395 if (length == INVALID_FILE_SIZE) 396 return false; 397 398 std::vector<uint8> buffer; 399 buffer.resize(length); 400 DWORD bytes_read = 0; 401 if (!ReadFile(pdf_file, &buffer.front(), length, &bytes_read, NULL) || 402 (bytes_read != length)) 403 return false; 404 405 int total_page_count = 0; 406 if (!get_info_proc(&buffer.front(), buffer.size(), &total_page_count, NULL)) 407 return false; 408 409 printing::Emf metafile; 410 metafile.InitToFile(metafile_path); 411 // We need to scale down DC to fit an entire page into DC available area. 412 // Current metafile is based on screen DC and have current screen size. 413 // Writing outside of those boundaries will result in the cut-off output. 414 // On metafiles (this is the case here), scaling down will still record 415 // original coordinates and we'll be able to print in full resolution. 416 // Before playback we'll need to counter the scaling up that will happen 417 // in the service (print_system_win.cc). 418 *scale_factor = gfx::CalculatePageScale(metafile.context(), 419 render_area.right(), 420 render_area.bottom()); 421 gfx::ScaleDC(metafile.context(), *scale_factor); 422 423 bool ret = false; 424 std::vector<printing::PageRange>::const_iterator iter; 425 for (iter = page_ranges.begin(); iter != page_ranges.end(); ++iter) { 426 for (int page_number = iter->from; page_number <= iter->to; ++page_number) { 427 if (page_number >= total_page_count) 428 break; 429 // The underlying metafile is of type Emf and ignores the arguments passed 430 // to StartPage. 431 metafile.StartPage(gfx::Size(), gfx::Rect(), 1); 432 if (render_proc(&buffer.front(), buffer.size(), page_number, 433 metafile.context(), render_dpi, render_dpi, 434 render_area.x(), render_area.y(), render_area.width(), 435 render_area.height(), true, false, true, true, 436 autorotate)) 437 if (*highest_rendered_page_number < page_number) 438 *highest_rendered_page_number = page_number; 439 ret = true; 440 metafile.FinishPage(); 441 } 442 } 443 metafile.FinishDocument(); 444 return ret; 445} 446#endif // defined(OS_WIN) 447 448void ChromeContentUtilityClient::OnRobustJPEGDecodeImage( 449 const std::vector<unsigned char>& encoded_data) { 450 // Our robust jpeg decoding is using IJG libjpeg. 451 if (gfx::JPEGCodec::JpegLibraryVariant() == gfx::JPEGCodec::IJG_LIBJPEG) { 452 scoped_ptr<SkBitmap> decoded_image(gfx::JPEGCodec::Decode( 453 &encoded_data[0], encoded_data.size())); 454 if (!decoded_image.get() || decoded_image->empty()) { 455 Send(new ChromeUtilityHostMsg_DecodeImage_Failed()); 456 } else { 457 Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(*decoded_image)); 458 } 459 } else { 460 Send(new ChromeUtilityHostMsg_DecodeImage_Failed()); 461 } 462 ReleaseProcessIfNeeded(); 463} 464 465void ChromeContentUtilityClient::OnParseJSON(const std::string& json) { 466 int error_code; 467 std::string error; 468 base::Value* value = base::JSONReader::ReadAndReturnError( 469 json, base::JSON_PARSE_RFC, &error_code, &error); 470 if (value) { 471 base::ListValue wrapper; 472 wrapper.Append(value); 473 Send(new ChromeUtilityHostMsg_ParseJSON_Succeeded(wrapper)); 474 } else { 475 Send(new ChromeUtilityHostMsg_ParseJSON_Failed(error)); 476 } 477 ReleaseProcessIfNeeded(); 478} 479 480void ChromeContentUtilityClient::OnGetPrinterCapsAndDefaults( 481 const std::string& printer_name) { 482#if defined(ENABLE_PRINTING) 483 scoped_refptr<printing::PrintBackend> print_backend = 484 printing::PrintBackend::CreateInstance(NULL); 485 printing::PrinterCapsAndDefaults printer_info; 486 487 child_process_logging::ScopedPrinterInfoSetter prn_info( 488 print_backend->GetPrinterDriverInfo(printer_name)); 489 490 if (print_backend->GetPrinterCapsAndDefaults(printer_name, &printer_info)) { 491 Send(new ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded( 492 printer_name, printer_info)); 493 } else // NOLINT 494#endif 495 { 496 Send(new ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed( 497 printer_name)); 498 } 499 ReleaseProcessIfNeeded(); 500} 501 502void ChromeContentUtilityClient::OnStartupPing() { 503 Send(new ChromeUtilityHostMsg_ProcessStarted); 504 // Don't release the process, we assume further messages are on the way. 505} 506 507void ChromeContentUtilityClient::OnAnalyzeZipFileForDownloadProtection( 508 IPC::PlatformFileForTransit zip_file) { 509 safe_browsing::zip_analyzer::Results results; 510 safe_browsing::zip_analyzer::AnalyzeZipFile( 511 IPC::PlatformFileForTransitToPlatformFile(zip_file), &results); 512 Send(new ChromeUtilityHostMsg_AnalyzeZipFileForDownloadProtection_Finished( 513 results)); 514 ReleaseProcessIfNeeded(); 515} 516 517#if defined(OS_WIN) 518void ChromeContentUtilityClient::OnParseITunesPrefXml( 519 const std::string& itunes_xml_data) { 520 base::FilePath library_path( 521 itunes::FindLibraryLocationInPrefXml(itunes_xml_data)); 522 Send(new ChromeUtilityHostMsg_GotITunesDirectory(library_path)); 523 ReleaseProcessIfNeeded(); 524} 525#endif // defined(OS_WIN) 526 527#if defined(OS_WIN) || defined(OS_MACOSX) 528void ChromeContentUtilityClient::OnParseITunesLibraryXmlFile( 529 IPC::PlatformFileForTransit itunes_library_file) { 530 itunes::ITunesLibraryParser parser; 531 base::PlatformFile file = 532 IPC::PlatformFileForTransitToPlatformFile(itunes_library_file); 533 bool result = parser.Parse( 534 itunes::ITunesLibraryParser::ReadITunesLibraryXmlFile(file)); 535 Send(new ChromeUtilityHostMsg_GotITunesLibrary(result, parser.library())); 536 ReleaseProcessIfNeeded(); 537} 538 539void ChromeContentUtilityClient::OnParsePicasaPMPDatabase( 540 const picasa::AlbumTableFilesForTransit& album_table_files) { 541 picasa::AlbumTableFiles files; 542 files.indicator_file = IPC::PlatformFileForTransitToPlatformFile( 543 album_table_files.indicator_file); 544 files.category_file = IPC::PlatformFileForTransitToPlatformFile( 545 album_table_files.category_file); 546 files.date_file = IPC::PlatformFileForTransitToPlatformFile( 547 album_table_files.date_file); 548 files.filename_file = IPC::PlatformFileForTransitToPlatformFile( 549 album_table_files.filename_file); 550 files.name_file = IPC::PlatformFileForTransitToPlatformFile( 551 album_table_files.name_file); 552 files.token_file = IPC::PlatformFileForTransitToPlatformFile( 553 album_table_files.token_file); 554 files.uid_file = IPC::PlatformFileForTransitToPlatformFile( 555 album_table_files.uid_file); 556 557 picasa::PicasaAlbumTableReader reader(files); 558 bool parse_success = reader.Init(); 559 Send(new ChromeUtilityHostMsg_ParsePicasaPMPDatabase_Finished( 560 parse_success, 561 reader.albums(), 562 reader.folders())); 563 ReleaseProcessIfNeeded(); 564} 565#endif // defined(OS_WIN) || defined(OS_MACOSX) 566 567} // namespace chrome 568