| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 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 <Windows.h> | |
| 6 #include <objbase.h> | |
| 7 | |
| 8 #include "chrome/browser/save_file_manager.h" | |
| 9 | |
| 10 #include "base/file_util.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "base/scoped_ptr.h" | |
| 13 #include "base/string_util.h" | |
| 14 #include "base/task.h" | |
| 15 #include "chrome/browser/browser_process.h" | |
| 16 #include "chrome/browser/resource_dispatcher_host.h" | |
| 17 #include "chrome/browser/save_file.h" | |
| 18 #include "chrome/browser/save_package.h" | |
| 19 #include "chrome/browser/tab_contents.h" | |
| 20 #include "chrome/browser/tab_util.h" | |
| 21 #include "chrome/browser/web_contents.h" | |
| 22 #include "chrome/common/chrome_paths.h" | |
| 23 #include "chrome/common/stl_util-inl.h" | |
| 24 #include "chrome/common/win_util.h" | |
| 25 #include "chrome/common/win_safe_util.h" | |
| 26 #include "googleurl/src/gurl.h" | |
| 27 #include "net/base/net_util.h" | |
| 28 #include "net/url_request/url_request_context.h" | |
| 29 | |
| 30 SaveFileManager::SaveFileManager(MessageLoop* ui_loop, | |
| 31 MessageLoop* io_loop, | |
| 32 ResourceDispatcherHost* rdh) | |
| 33 : next_id_(0), | |
| 34 ui_loop_(ui_loop), | |
| 35 io_loop_(io_loop), | |
| 36 resource_dispatcher_host_(rdh) { | |
| 37 DCHECK(ui_loop_); | |
| 38 // Need to make sure that we are in UI thread because using g_browser_process | |
| 39 // on a non-UI thread can cause crashes during shutdown. | |
| 40 DCHECK(ui_loop_ == MessageLoop::current()); | |
| 41 // Cache the message loop of file thread. | |
| 42 base::Thread* thread = g_browser_process->file_thread(); | |
| 43 if (thread) | |
| 44 file_loop_ = thread->message_loop(); | |
| 45 else | |
| 46 // It could be NULL when it is created in unit test of | |
| 47 // ResourceDispatcherHost. | |
| 48 file_loop_ = NULL; | |
| 49 DCHECK(resource_dispatcher_host_); | |
| 50 } | |
| 51 | |
| 52 SaveFileManager::~SaveFileManager() { | |
| 53 // Check for clean shutdown. | |
| 54 DCHECK(save_file_map_.empty()); | |
| 55 } | |
| 56 | |
| 57 // Called during the browser shutdown process to clean up any state (open files, | |
| 58 // timers) that live on the saving thread (file thread). | |
| 59 void SaveFileManager::Shutdown() { | |
| 60 MessageLoop* loop = GetSaveLoop(); | |
| 61 if (loop) { | |
| 62 loop->PostTask(FROM_HERE, | |
| 63 NewRunnableMethod(this, &SaveFileManager::OnShutdown)); | |
| 64 } | |
| 65 } | |
| 66 | |
| 67 // Stop file thread operations. | |
| 68 void SaveFileManager::OnShutdown() { | |
| 69 DCHECK(MessageLoop::current() == GetSaveLoop()); | |
| 70 STLDeleteValues(&save_file_map_); | |
| 71 } | |
| 72 | |
| 73 SaveFile* SaveFileManager::LookupSaveFile(int save_id) { | |
| 74 SaveFileMap::iterator it = save_file_map_.find(save_id); | |
| 75 return it == save_file_map_.end() ? NULL : it->second; | |
| 76 } | |
| 77 | |
| 78 // Called on the IO thread when | |
| 79 // a) The ResourceDispatcherHost has decided that a request is savable. | |
| 80 // b) The resource does not come from the network, but we still need a | |
| 81 // save ID for for managing the status of the saving operation. So we | |
| 82 // file a request from the file thread to the IO thread to generate a | |
| 83 // unique save ID. | |
| 84 int SaveFileManager::GetNextId() { | |
| 85 DCHECK(MessageLoop::current() == io_loop_); | |
| 86 return next_id_++; | |
| 87 } | |
| 88 | |
| 89 void SaveFileManager::RegisterStartingRequest(const std::wstring& save_url, | |
| 90 SavePackage* save_package) { | |
| 91 // Make sure it runs in the UI thread. | |
| 92 DCHECK(MessageLoop::current() == ui_loop_); | |
| 93 int tab_id = save_package->GetTabId(); | |
| 94 | |
| 95 // Register this starting request. | |
| 96 StartingRequestsMap& starting_requests = tab_starting_requests_[tab_id]; | |
| 97 bool never_present = starting_requests.insert( | |
| 98 StartingRequestsMap::value_type(save_url, save_package)).second; | |
| 99 DCHECK(never_present); | |
| 100 } | |
| 101 | |
| 102 SavePackage* SaveFileManager::UnregisterStartingRequest( | |
| 103 const std::wstring& save_url, int tab_id) { | |
| 104 // Make sure it runs in UI thread. | |
| 105 DCHECK(MessageLoop::current() == ui_loop_); | |
| 106 | |
| 107 TabToStartingRequestsMap::iterator it = tab_starting_requests_.find(tab_id); | |
| 108 if (it != tab_starting_requests_.end()) { | |
| 109 StartingRequestsMap& requests = it->second; | |
| 110 StartingRequestsMap::iterator sit = requests.find(save_url); | |
| 111 if (sit == requests.end()) | |
| 112 return NULL; | |
| 113 | |
| 114 // Found, erase it from starting list and return SavePackage. | |
| 115 SavePackage* save_package = sit->second; | |
| 116 requests.erase(sit); | |
| 117 // If there is no element in requests, remove it | |
| 118 if (requests.empty()) | |
| 119 tab_starting_requests_.erase(it); | |
| 120 return save_package; | |
| 121 } | |
| 122 | |
| 123 return NULL; | |
| 124 } | |
| 125 | |
| 126 void SaveFileManager::RequireSaveJobFromOtherSource(SaveFileCreateInfo* info) { | |
| 127 // This function must be called on the UI thread, because the io_loop_ | |
| 128 // pointer may be junk when we use it on file thread. We can only rely on the | |
| 129 // io_loop_ pointer being valid when we run code on the UI thread (or on | |
| 130 // the IO thread. | |
| 131 DCHECK(MessageLoop::current() == ui_loop_); | |
| 132 DCHECK(info->save_id == -1); | |
| 133 // Since the data will come from render process, so we need to start | |
| 134 // this kind of save job by ourself. | |
| 135 io_loop_->PostTask(FROM_HERE, | |
| 136 NewRunnableMethod(this, | |
| 137 &SaveFileManager::OnRequireSaveJobFromOtherSource, | |
| 138 info)); | |
| 139 } | |
| 140 | |
| 141 // Look up a SavePackage according to a save id. | |
| 142 SavePackage* SaveFileManager::LookupPackage(int save_id) { | |
| 143 DCHECK(MessageLoop::current() == ui_loop_); | |
| 144 SavePackageMap::iterator it = packages_.find(save_id); | |
| 145 if (it != packages_.end()) | |
| 146 return it->second; | |
| 147 return NULL; | |
| 148 } | |
| 149 | |
| 150 // Call from SavePackage for starting a saving job | |
| 151 void SaveFileManager::SaveURL(const std::wstring& url, | |
| 152 const std::wstring& referrer, | |
| 153 int render_process_host_id, | |
| 154 int render_view_id, | |
| 155 SaveFileCreateInfo::SaveFileSource save_source, | |
| 156 const std::wstring& file_full_path, | |
| 157 URLRequestContext* request_context, | |
| 158 SavePackage* save_package) { | |
| 159 DCHECK(MessageLoop::current() == ui_loop_); | |
| 160 if (!io_loop_) { | |
| 161 NOTREACHED(); // Net IO thread must exist. | |
| 162 return; | |
| 163 } | |
| 164 | |
| 165 // Register a saving job. | |
| 166 RegisterStartingRequest(url, save_package); | |
| 167 if (save_source == SaveFileCreateInfo::SAVE_FILE_FROM_NET) { | |
| 168 GURL save_url(url); | |
| 169 DCHECK(save_url.is_valid()); | |
| 170 | |
| 171 io_loop_->PostTask(FROM_HERE, | |
| 172 NewRunnableMethod(this, | |
| 173 &SaveFileManager::OnSaveURL, | |
| 174 save_url, | |
| 175 GURL(referrer), | |
| 176 render_process_host_id, | |
| 177 render_view_id, | |
| 178 request_context)); | |
| 179 } else { | |
| 180 // We manually start the save job. | |
| 181 SaveFileCreateInfo* info = new SaveFileCreateInfo(file_full_path, | |
| 182 url, | |
| 183 save_source, | |
| 184 -1); | |
| 185 info->render_process_id = render_process_host_id; | |
| 186 info->render_view_id = render_view_id; | |
| 187 RequireSaveJobFromOtherSource(info); | |
| 188 } | |
| 189 } | |
| 190 | |
| 191 // Utility function for look up table maintenance, called on the UI thread. | |
| 192 // A manager may have multiple save page job (SavePackage) in progress, | |
| 193 // so we just look up the save id and remove it from the tracking table. | |
| 194 // If the save id is -1, it means we just send a request to save, but the | |
| 195 // saving action has still not happened, need to call UnregisterStartingRequest | |
| 196 // to remove it from the tracking map. | |
| 197 void SaveFileManager::RemoveSaveFile(int save_id, const std::wstring& save_url, | |
| 198 SavePackage* package) { | |
| 199 DCHECK(MessageLoop::current() == ui_loop_ && package); | |
| 200 // A save page job(SavePackage) can only have one manager, | |
| 201 // so remove it if it exists. | |
| 202 if (save_id == -1) { | |
| 203 SavePackage* old_package = UnregisterStartingRequest(save_url, | |
| 204 package->GetTabId()); | |
| 205 DCHECK(old_package == package); | |
| 206 } else { | |
| 207 SavePackageMap::iterator it = packages_.find(save_id); | |
| 208 if (it != packages_.end()) | |
| 209 packages_.erase(it); | |
| 210 } | |
| 211 } | |
| 212 | |
| 213 // Static | |
| 214 // Utility function for converting request IDs to a TabContents. Must be called | |
| 215 // only on the UI thread. | |
| 216 SavePackage* SaveFileManager::GetSavePackageFromRenderIds( | |
| 217 int render_process_id, int render_view_id) { | |
| 218 TabContents* contents = tab_util::GetTabContentsByID(render_process_id, | |
| 219 render_view_id); | |
| 220 if (contents && contents->type() == TAB_CONTENTS_WEB) { | |
| 221 // Convert const pointer of WebContents to pointer of WebContents. | |
| 222 const WebContents* web_contents = contents->AsWebContents(); | |
| 223 if (web_contents) | |
| 224 return web_contents->get_save_package(); | |
| 225 } | |
| 226 | |
| 227 return NULL; | |
| 228 } | |
| 229 | |
| 230 // Utility function for deleting specified file. | |
| 231 void SaveFileManager::DeleteDirectoryOrFile(const std::wstring& full_path, | |
| 232 bool is_dir) { | |
| 233 DCHECK(MessageLoop::current() == ui_loop_); | |
| 234 MessageLoop* loop = GetSaveLoop(); | |
| 235 DCHECK(loop); | |
| 236 loop->PostTask(FROM_HERE, | |
| 237 NewRunnableMethod(this, | |
| 238 &SaveFileManager::OnDeleteDirectoryOrFile, | |
| 239 full_path, | |
| 240 is_dir)); | |
| 241 } | |
| 242 | |
| 243 void SaveFileManager::SendCancelRequest(int save_id) { | |
| 244 // Cancel the request which has specific save id. | |
| 245 DCHECK(save_id > -1); | |
| 246 MessageLoop* loop = GetSaveLoop(); | |
| 247 DCHECK(loop); | |
| 248 loop->PostTask(FROM_HERE, | |
| 249 NewRunnableMethod(this, | |
| 250 &SaveFileManager::CancelSave, | |
| 251 save_id)); | |
| 252 } | |
| 253 | |
| 254 // Notifications sent from the IO thread and run on the file thread: | |
| 255 | |
| 256 // The IO thread created |info|, but the file thread (this method) uses it | |
| 257 // to create a SaveFile which will hold and finally destroy |info|. It will | |
| 258 // then passes |info| to the UI thread for reporting saving status. | |
| 259 void SaveFileManager::StartSave(SaveFileCreateInfo* info) { | |
| 260 DCHECK(MessageLoop::current() == GetSaveLoop()); | |
| 261 DCHECK(info); | |
| 262 SaveFile* save_file = new SaveFile(info); | |
| 263 DCHECK(LookupSaveFile(info->save_id) == NULL); | |
| 264 save_file_map_[info->save_id] = save_file; | |
| 265 info->path = save_file->full_path(); | |
| 266 | |
| 267 ui_loop_->PostTask(FROM_HERE, | |
| 268 NewRunnableMethod(this, | |
| 269 &SaveFileManager::OnStartSave, | |
| 270 info)); | |
| 271 } | |
| 272 | |
| 273 // We do forward an update to the UI thread here, since we do not use timer to | |
| 274 // update the UI. If the user has canceled the saving action (in the UI | |
| 275 // thread). We may receive a few more updates before the IO thread gets the | |
| 276 // cancel message. We just delete the data since the SaveFile has been deleted. | |
| 277 void SaveFileManager::UpdateSaveProgress(int save_id, | |
| 278 char* data, | |
| 279 int data_len) { | |
| 280 DCHECK(MessageLoop::current() == GetSaveLoop()); | |
| 281 SaveFile* save_file = LookupSaveFile(save_id); | |
| 282 if (save_file) { | |
| 283 bool write_success = save_file->AppendDataToFile(data, data_len); | |
| 284 ui_loop_->PostTask(FROM_HERE, | |
| 285 NewRunnableMethod(this, | |
| 286 &SaveFileManager::OnUpdateSaveProgress, | |
| 287 save_file->save_id(), | |
| 288 save_file->bytes_so_far(), | |
| 289 write_success)); | |
| 290 } | |
| 291 delete [] data; | |
| 292 } | |
| 293 | |
| 294 // The IO thread will call this when saving is completed or it got error when | |
| 295 // fetching data. In the former case, we forward the message to OnSaveFinished | |
| 296 // in UI thread. In the latter case, the save ID will be -1, which means the | |
| 297 // saving action did not even start, so we need to call OnErrorFinished in UI | |
| 298 // thread, which will use the save URL to find corresponding request record and | |
| 299 // delete it. | |
| 300 void SaveFileManager::SaveFinished(int save_id, | |
| 301 std::wstring save_url, | |
| 302 int render_process_id, | |
| 303 bool is_success) { | |
| 304 DCHECK(MessageLoop::current() == GetSaveLoop()); | |
| 305 SaveFileMap::iterator it = save_file_map_.find(save_id); | |
| 306 if (it != save_file_map_.end()) { | |
| 307 SaveFile* save_file = it->second; | |
| 308 ui_loop_->PostTask(FROM_HERE, | |
| 309 NewRunnableMethod(this, | |
| 310 &SaveFileManager::OnSaveFinished, | |
| 311 save_id, | |
| 312 save_file->bytes_so_far(), | |
| 313 is_success)); | |
| 314 | |
| 315 save_file->Finish(); | |
| 316 } else if (save_id == -1) { | |
| 317 // Before saving started, we got error. We still call finish process. | |
| 318 DCHECK(!save_url.empty()); | |
| 319 ui_loop_->PostTask(FROM_HERE, | |
| 320 NewRunnableMethod(this, | |
| 321 &SaveFileManager::OnErrorFinished, | |
| 322 save_url, | |
| 323 render_process_id)); | |
| 324 } | |
| 325 } | |
| 326 | |
| 327 // Notifications sent from the file thread and run on the UI thread. | |
| 328 | |
| 329 void SaveFileManager::OnStartSave(const SaveFileCreateInfo* info) { | |
| 330 DCHECK(MessageLoop::current() == ui_loop_); | |
| 331 SavePackage* save_package = | |
| 332 GetSavePackageFromRenderIds(info->render_process_id, | |
| 333 info->render_view_id); | |
| 334 if (!save_package) { | |
| 335 // Cancel this request. | |
| 336 SendCancelRequest(info->save_id); | |
| 337 return; | |
| 338 } | |
| 339 | |
| 340 // Insert started saving job to tracking list. | |
| 341 SavePackageMap::iterator sit = packages_.find(info->save_id); | |
| 342 if (sit == packages_.end()) { | |
| 343 // Find the registered request. If we can not find, it means we have | |
| 344 // canceled the job before. | |
| 345 SavePackage* old_save_package = UnregisterStartingRequest(info->url, | |
| 346 info->render_process_id); | |
| 347 if (!old_save_package) { | |
| 348 // Cancel this request. | |
| 349 SendCancelRequest(info->save_id); | |
| 350 return; | |
| 351 } | |
| 352 DCHECK(old_save_package == save_package); | |
| 353 packages_[info->save_id] = save_package; | |
| 354 } else { | |
| 355 NOTREACHED(); | |
| 356 } | |
| 357 | |
| 358 // Forward this message to SavePackage. | |
| 359 save_package->StartSave(info); | |
| 360 } | |
| 361 | |
| 362 void SaveFileManager::OnUpdateSaveProgress(int save_id, int64 bytes_so_far, | |
| 363 bool write_success) { | |
| 364 DCHECK(MessageLoop::current() == ui_loop_); | |
| 365 SavePackage* package = LookupPackage(save_id); | |
| 366 if (package) | |
| 367 package->UpdateSaveProgress(save_id, bytes_so_far, write_success); | |
| 368 else | |
| 369 SendCancelRequest(save_id); | |
| 370 } | |
| 371 | |
| 372 void SaveFileManager::OnSaveFinished(int save_id, | |
| 373 int64 bytes_so_far, | |
| 374 bool is_success) { | |
| 375 DCHECK(MessageLoop::current() == ui_loop_); | |
| 376 SavePackage* package = LookupPackage(save_id); | |
| 377 if (package) | |
| 378 package->SaveFinished(save_id, bytes_so_far, is_success); | |
| 379 } | |
| 380 | |
| 381 void SaveFileManager::OnErrorFinished(std::wstring save_url, int tab_id) { | |
| 382 DCHECK(MessageLoop::current() == ui_loop_); | |
| 383 SavePackage* save_package = UnregisterStartingRequest(save_url, tab_id); | |
| 384 if (save_package) | |
| 385 save_package->SaveFailed(save_url); | |
| 386 } | |
| 387 | |
| 388 void SaveFileManager::OnCancelSaveRequest(int render_process_id, | |
| 389 int request_id) { | |
| 390 DCHECK(MessageLoop::current() == ui_loop_); | |
| 391 DCHECK(io_loop_); | |
| 392 io_loop_->PostTask(FROM_HERE, | |
| 393 NewRunnableMethod(this, | |
| 394 &SaveFileManager::ExecuteCancelSaveRequest, | |
| 395 render_process_id, | |
| 396 request_id)); | |
| 397 } | |
| 398 | |
| 399 // Notifications sent from the UI thread and run on the IO thread. | |
| 400 | |
| 401 void SaveFileManager::OnSaveURL(const GURL& url, | |
| 402 const GURL& referrer, | |
| 403 int render_process_host_id, | |
| 404 int render_view_id, | |
| 405 URLRequestContext* request_context) { | |
| 406 DCHECK(MessageLoop::current() == io_loop_); | |
| 407 resource_dispatcher_host_->BeginSaveFile(url, | |
| 408 referrer, | |
| 409 render_process_host_id, | |
| 410 render_view_id, | |
| 411 request_context); | |
| 412 } | |
| 413 | |
| 414 void SaveFileManager::OnRequireSaveJobFromOtherSource( | |
| 415 SaveFileCreateInfo* info) { | |
| 416 DCHECK(MessageLoop::current() == io_loop_); | |
| 417 DCHECK(info->save_id == -1); | |
| 418 // Generate a unique save id. | |
| 419 info->save_id = GetNextId(); | |
| 420 // Start real saving action. | |
| 421 MessageLoop* loop = GetSaveLoop(); | |
| 422 DCHECK(loop); | |
| 423 loop->PostTask(FROM_HERE, | |
| 424 NewRunnableMethod(this, | |
| 425 &SaveFileManager::StartSave, | |
| 426 info)); | |
| 427 } | |
| 428 | |
| 429 void SaveFileManager::ExecuteCancelSaveRequest(int render_process_id, | |
| 430 int request_id) { | |
| 431 DCHECK(MessageLoop::current() == io_loop_); | |
| 432 resource_dispatcher_host_->CancelRequest(render_process_id, | |
| 433 request_id, | |
| 434 false); | |
| 435 } | |
| 436 | |
| 437 // Notifications sent from the UI thread and run on the file thread. | |
| 438 | |
| 439 // This method will be sent via a user action, or shutdown on the UI thread, | |
| 440 // and run on the file thread. We don't post a message back for cancels, | |
| 441 // but we do forward the cancel to the IO thread. Since this message has been | |
| 442 // sent from the UI thread, the saving job may have already completed and | |
| 443 // won't exist in our map. | |
| 444 void SaveFileManager::CancelSave(int save_id) { | |
| 445 DCHECK(MessageLoop::current() == GetSaveLoop()); | |
| 446 SaveFileMap::iterator it = save_file_map_.find(save_id); | |
| 447 if (it != save_file_map_.end()) { | |
| 448 SaveFile* save_file = it->second; | |
| 449 | |
| 450 // If the data comes from the net IO thread, then forward the cancel | |
| 451 // message to IO thread. If the data comes from other sources, just | |
| 452 // ignore the cancel message. | |
| 453 if (save_file->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_NET) { | |
| 454 ui_loop_->PostTask(FROM_HERE, | |
| 455 NewRunnableMethod(this, | |
| 456 &SaveFileManager::OnCancelSaveRequest, | |
| 457 save_file->render_process_id(), | |
| 458 save_file->request_id())); | |
| 459 | |
| 460 // UI thread will notify the render process to stop sending data, | |
| 461 // so in here, we need not to do anything, just close the save file. | |
| 462 save_file->Cancel(); | |
| 463 } else { | |
| 464 // If we did not find SaveFile in map, the saving job should either get | |
| 465 // data from other sources or have finished. | |
| 466 DCHECK(save_file->save_source() != | |
| 467 SaveFileCreateInfo::SAVE_FILE_FROM_NET || | |
| 468 !save_file->in_progress()); | |
| 469 } | |
| 470 // Whatever the save file is renamed or not, just delete it. | |
| 471 save_file_map_.erase(it); | |
| 472 delete save_file; | |
| 473 } | |
| 474 } | |
| 475 | |
| 476 // It is possible that SaveItem which has specified save_id has been canceled | |
| 477 // before this function runs. So if we can not find corresponding SaveFile by | |
| 478 // using specified save_id, just return. | |
| 479 void SaveFileManager::SaveLocalFile(const std::wstring& original_file_url, | |
| 480 int save_id, | |
| 481 int render_process_id) { | |
| 482 DCHECK(MessageLoop::current() == GetSaveLoop()); | |
| 483 SaveFile* save_file = LookupSaveFile(save_id); | |
| 484 if (!save_file) | |
| 485 return; | |
| 486 DCHECK(!save_file->path_renamed()); | |
| 487 // If it has finished, just return. | |
| 488 if (!save_file->in_progress()) | |
| 489 return; | |
| 490 | |
| 491 // Close the save file before the copy operation. | |
| 492 save_file->Finish(); | |
| 493 | |
| 494 GURL file_url(original_file_url); | |
| 495 DCHECK(file_url.SchemeIsFile()); | |
| 496 std::wstring file_path; | |
| 497 net::FileURLToFilePath(file_url, &file_path); | |
| 498 // If we can not get valid file path from original URL, treat it as | |
| 499 // disk error. | |
| 500 if (file_path.empty()) | |
| 501 SaveFinished(save_id, original_file_url, render_process_id, false); | |
| 502 | |
| 503 // Copy the local file to the temporary file. It will be renamed to its | |
| 504 // final name later. | |
| 505 bool success = CopyFile(file_path.c_str(), | |
| 506 save_file->full_path().c_str(), FALSE) != 0; | |
| 507 if (!success) | |
| 508 file_util::Delete(save_file->full_path(), false); | |
| 509 SaveFinished(save_id, original_file_url, render_process_id, success); | |
| 510 } | |
| 511 | |
| 512 void SaveFileManager::OnDeleteDirectoryOrFile(const std::wstring& full_path, | |
| 513 bool is_dir) { | |
| 514 DCHECK(MessageLoop::current() == GetSaveLoop()); | |
| 515 DCHECK(!full_path.empty()); | |
| 516 | |
| 517 file_util::Delete(full_path, is_dir); | |
| 518 } | |
| 519 | |
| 520 // Open a saved page package, show it in a Windows Explorer window. | |
| 521 // We run on this thread to avoid blocking the UI with slow Shell operations. | |
| 522 void SaveFileManager::OnShowSavedFileInShell(const std::wstring full_path) { | |
| 523 DCHECK(MessageLoop::current() == GetSaveLoop()); | |
| 524 win_util::ShowItemInFolder(full_path); | |
| 525 } | |
| 526 | |
| 527 void SaveFileManager::RenameAllFiles( | |
| 528 const FinalNameList& final_names, | |
| 529 const std::wstring& resource_dir, | |
| 530 int render_process_id, | |
| 531 int render_view_id) { | |
| 532 DCHECK(MessageLoop::current() == GetSaveLoop()); | |
| 533 | |
| 534 if (!resource_dir.empty() && !file_util::PathExists(resource_dir)) | |
| 535 file_util::CreateDirectory(resource_dir); | |
| 536 | |
| 537 for (FinalNameList::const_iterator i = final_names.begin(); | |
| 538 i != final_names.end(); ++i) { | |
| 539 SaveFileMap::iterator it = save_file_map_.find(i->first); | |
| 540 if (it != save_file_map_.end()) { | |
| 541 SaveFile* save_file = it->second; | |
| 542 DCHECK(!save_file->in_progress()); | |
| 543 save_file->Rename(i->second); | |
| 544 delete save_file; | |
| 545 save_file_map_.erase(it); | |
| 546 } | |
| 547 } | |
| 548 | |
| 549 ui_loop_->PostTask(FROM_HERE, | |
| 550 NewRunnableMethod(this, | |
| 551 &SaveFileManager::OnFinishSavePageJob, | |
| 552 render_process_id, | |
| 553 render_view_id)); | |
| 554 } | |
| 555 | |
| 556 void SaveFileManager::OnFinishSavePageJob(int render_process_id, | |
| 557 int render_view_id) { | |
| 558 DCHECK(MessageLoop::current() == ui_loop_); | |
| 559 | |
| 560 SavePackage* save_package = | |
| 561 GetSavePackageFromRenderIds(render_process_id, render_view_id); | |
| 562 | |
| 563 save_package->Finish(); | |
| 564 } | |
| 565 | |
| 566 void SaveFileManager::RemoveSavedFileFromFileMap( | |
| 567 const SaveIDList& save_ids) { | |
| 568 DCHECK(MessageLoop::current() == GetSaveLoop()); | |
| 569 | |
| 570 for (SaveIDList::const_iterator i = save_ids.begin(); | |
| 571 i != save_ids.end(); ++i) { | |
| 572 SaveFileMap::iterator it = save_file_map_.find(*i); | |
| 573 if (it != save_file_map_.end()) { | |
| 574 SaveFile* save_file = it->second; | |
| 575 DCHECK(!save_file->in_progress()); | |
| 576 DeleteFile(save_file->full_path().c_str()); | |
| 577 delete save_file; | |
| 578 save_file_map_.erase(it); | |
| 579 } | |
| 580 } | |
| 581 } | |
| 582 | |
| OLD | NEW |