Home | History | Annotate | Download | only in download
      1 // Copyright (c) 2011 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/download/save_package.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/file_path.h"
     10 #include "base/file_util.h"
     11 #include "base/i18n/file_util_icu.h"
     12 #include "base/logging.h"
     13 #include "base/message_loop.h"
     14 #include "base/stl_util-inl.h"
     15 #include "base/string_piece.h"
     16 #include "base/string_split.h"
     17 #include "base/sys_string_conversions.h"
     18 #include "base/task.h"
     19 #include "base/threading/thread.h"
     20 #include "base/utf_string_conversions.h"
     21 #include "chrome/browser/browser_process.h"
     22 #include "chrome/browser/download/download_item.h"
     23 #include "chrome/browser/download/download_item_model.h"
     24 #include "chrome/browser/download/download_manager.h"
     25 #include "chrome/browser/download/download_prefs.h"
     26 #include "chrome/browser/download/download_shelf.h"
     27 #include "chrome/browser/download/download_util.h"
     28 #include "chrome/browser/download/save_file.h"
     29 #include "chrome/browser/download/save_file_manager.h"
     30 #include "chrome/browser/download/save_item.h"
     31 #include "chrome/browser/net/url_fixer_upper.h"
     32 #include "chrome/browser/platform_util.h"
     33 #include "chrome/browser/prefs/pref_member.h"
     34 #include "chrome/browser/prefs/pref_service.h"
     35 #include "chrome/browser/profiles/profile.h"
     36 #include "chrome/browser/tab_contents/tab_util.h"
     37 #include "chrome/common/chrome_paths.h"
     38 #include "chrome/common/pref_names.h"
     39 #include "chrome/common/render_messages.h"
     40 #include "chrome/common/url_constants.h"
     41 #include "content/browser/browser_thread.h"
     42 #include "content/browser/renderer_host/render_process_host.h"
     43 #include "content/browser/renderer_host/render_view_host.h"
     44 #include "content/browser/renderer_host/render_view_host_delegate.h"
     45 #include "content/browser/renderer_host/resource_dispatcher_host.h"
     46 #include "content/browser/tab_contents/tab_contents.h"
     47 #include "content/common/notification_service.h"
     48 #include "content/common/notification_type.h"
     49 #include "grit/generated_resources.h"
     50 #include "net/base/io_buffer.h"
     51 #include "net/base/mime_util.h"
     52 #include "net/base/net_util.h"
     53 #include "net/url_request/url_request_context.h"
     54 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPageSerializerClient.h"
     55 #include "ui/base/l10n/l10n_util.h"
     56 #include "net/url_request/url_request_context_getter.h"
     57 
     58 using base::Time;
     59 using WebKit::WebPageSerializerClient;
     60 
     61 namespace {
     62 
     63 // A counter for uniquely identifying each save package.
     64 int g_save_package_id = 0;
     65 
     66 // Default name which will be used when we can not get proper name from
     67 // resource URL.
     68 const char kDefaultSaveName[] = "saved_resource";
     69 
     70 const FilePath::CharType kDefaultHtmlExtension[] =
     71 #if defined(OS_WIN)
     72     FILE_PATH_LITERAL("htm");
     73 #else
     74     FILE_PATH_LITERAL("html");
     75 #endif
     76 
     77 // Maximum number of file ordinal number. I think it's big enough for resolving
     78 // name-conflict files which has same base file name.
     79 const int32 kMaxFileOrdinalNumber = 9999;
     80 
     81 // Maximum length for file path. Since Windows have MAX_PATH limitation for
     82 // file path, we need to make sure length of file path of every saved file
     83 // is less than MAX_PATH
     84 #if defined(OS_WIN)
     85 const uint32 kMaxFilePathLength = MAX_PATH - 1;
     86 #elif defined(OS_POSIX)
     87 const uint32 kMaxFilePathLength = PATH_MAX - 1;
     88 #endif
     89 
     90 // Maximum length for file ordinal number part. Since we only support the
     91 // maximum 9999 for ordinal number, which means maximum file ordinal number part
     92 // should be "(9998)", so the value is 6.
     93 const uint32 kMaxFileOrdinalNumberPartLength = 6;
     94 
     95 // If false, we don't prompt the user as to where to save the file.  This
     96 // exists only for testing.
     97 bool g_should_prompt_for_filename = true;
     98 
     99 // Indexes used for specifying which element in the extensions dropdown
    100 // the user chooses when picking a save type.
    101 const int kSelectFileHtmlOnlyIndex = 1;
    102 const int kSelectFileCompleteIndex = 2;
    103 
    104 // Used for mapping between SavePackageType constants and the indexes above.
    105 const SavePackage::SavePackageType kIndexToSaveType[] = {
    106   SavePackage::SAVE_TYPE_UNKNOWN,
    107   SavePackage::SAVE_AS_ONLY_HTML,
    108   SavePackage::SAVE_AS_COMPLETE_HTML,
    109 };
    110 
    111 // Used for mapping between the IDS_ string identifiers and the indexes above.
    112 const int kIndexToIDS[] = {
    113   0, IDS_SAVE_PAGE_DESC_HTML_ONLY, IDS_SAVE_PAGE_DESC_COMPLETE,
    114 };
    115 
    116 int SavePackageTypeToIndex(SavePackage::SavePackageType type) {
    117   for (size_t i = 0; i < arraysize(kIndexToSaveType); ++i) {
    118     if (kIndexToSaveType[i] == type)
    119       return i;
    120   }
    121   NOTREACHED();
    122   return -1;
    123 }
    124 
    125 // Strip current ordinal number, if any. Should only be used on pure
    126 // file names, i.e. those stripped of their extensions.
    127 // TODO(estade): improve this to not choke on alternate encodings.
    128 FilePath::StringType StripOrdinalNumber(
    129     const FilePath::StringType& pure_file_name) {
    130   FilePath::StringType::size_type r_paren_index =
    131       pure_file_name.rfind(FILE_PATH_LITERAL(')'));
    132   FilePath::StringType::size_type l_paren_index =
    133       pure_file_name.rfind(FILE_PATH_LITERAL('('));
    134   if (l_paren_index >= r_paren_index)
    135     return pure_file_name;
    136 
    137   for (FilePath::StringType::size_type i = l_paren_index + 1;
    138        i != r_paren_index; ++i) {
    139     if (!IsAsciiDigit(pure_file_name[i]))
    140       return pure_file_name;
    141   }
    142 
    143   return pure_file_name.substr(0, l_paren_index);
    144 }
    145 
    146 // Check whether we can save page as complete-HTML for the contents which
    147 // have specified a MIME type. Now only contents which have the MIME type
    148 // "text/html" can be saved as complete-HTML.
    149 bool CanSaveAsComplete(const std::string& contents_mime_type) {
    150   return contents_mime_type == "text/html" ||
    151          contents_mime_type == "application/xhtml+xml";
    152 }
    153 
    154 }  // namespace
    155 
    156 SavePackage::SavePackage(TabContents* tab_contents,
    157                          SavePackageType save_type,
    158                          const FilePath& file_full_path,
    159                          const FilePath& directory_full_path)
    160     : TabContentsObserver(tab_contents),
    161       file_manager_(NULL),
    162       download_(NULL),
    163       page_url_(GetUrlToBeSaved()),
    164       saved_main_file_path_(file_full_path),
    165       saved_main_directory_path_(directory_full_path),
    166       title_(tab_contents->GetTitle()),
    167       finished_(false),
    168       user_canceled_(false),
    169       disk_error_occurred_(false),
    170       save_type_(save_type),
    171       all_save_items_count_(0),
    172       wait_state_(INITIALIZE),
    173       tab_id_(tab_contents->GetRenderProcessHost()->id()),
    174       unique_id_(g_save_package_id++),
    175       ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
    176   DCHECK(page_url_.is_valid());
    177   DCHECK(save_type_ == SAVE_AS_ONLY_HTML ||
    178          save_type_ == SAVE_AS_COMPLETE_HTML);
    179   DCHECK(!saved_main_file_path_.empty() &&
    180          saved_main_file_path_.value().length() <= kMaxFilePathLength);
    181   DCHECK(!saved_main_directory_path_.empty() &&
    182          saved_main_directory_path_.value().length() < kMaxFilePathLength);
    183   InternalInit();
    184 }
    185 
    186 SavePackage::SavePackage(TabContents* tab_contents)
    187     : TabContentsObserver(tab_contents),
    188       file_manager_(NULL),
    189       download_(NULL),
    190       page_url_(GetUrlToBeSaved()),
    191       title_(tab_contents->GetTitle()),
    192       finished_(false),
    193       user_canceled_(false),
    194       disk_error_occurred_(false),
    195       save_type_(SAVE_TYPE_UNKNOWN),
    196       all_save_items_count_(0),
    197       wait_state_(INITIALIZE),
    198       tab_id_(tab_contents->GetRenderProcessHost()->id()),
    199       unique_id_(g_save_package_id++),
    200       ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
    201   DCHECK(page_url_.is_valid());
    202   InternalInit();
    203 }
    204 
    205 // This is for testing use. Set |finished_| as true because we don't want
    206 // method Cancel to be be called in destructor in test mode.
    207 // We also don't call InternalInit().
    208 SavePackage::SavePackage(TabContents* tab_contents,
    209                          const FilePath& file_full_path,
    210                          const FilePath& directory_full_path)
    211     : TabContentsObserver(tab_contents),
    212       file_manager_(NULL),
    213       download_(NULL),
    214       saved_main_file_path_(file_full_path),
    215       saved_main_directory_path_(directory_full_path),
    216       finished_(true),
    217       user_canceled_(false),
    218       disk_error_occurred_(false),
    219       save_type_(SAVE_TYPE_UNKNOWN),
    220       all_save_items_count_(0),
    221       wait_state_(INITIALIZE),
    222       tab_id_(0),
    223       unique_id_(g_save_package_id++),
    224       ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
    225 }
    226 
    227 SavePackage::~SavePackage() {
    228   // Stop receiving saving job's updates
    229   if (!finished_ && !canceled()) {
    230     // Unexpected quit.
    231     Cancel(true);
    232   }
    233 
    234   DCHECK(all_save_items_count_ == (waiting_item_queue_.size() +
    235                                    completed_count() +
    236                                    in_process_count()));
    237   // Free all SaveItems.
    238   while (!waiting_item_queue_.empty()) {
    239     // We still have some items which are waiting for start to save.
    240     SaveItem* save_item = waiting_item_queue_.front();
    241     waiting_item_queue_.pop();
    242     delete save_item;
    243   }
    244 
    245   STLDeleteValues(&saved_success_items_);
    246   STLDeleteValues(&in_progress_items_);
    247   STLDeleteValues(&saved_failed_items_);
    248 
    249   // The DownloadItem is owned by DownloadManager.
    250   download_ = NULL;
    251 
    252   file_manager_ = NULL;
    253 
    254   // If there's an outstanding save dialog, make sure it doesn't call us back
    255   // now that we're gone.
    256   if (select_file_dialog_.get())
    257     select_file_dialog_->ListenerDestroyed();
    258 }
    259 
    260 // Retrieves the URL to be saved from tab_contents_ variable.
    261 GURL SavePackage::GetUrlToBeSaved() {
    262   // Instead of using tab_contents_.GetURL here, we use url()
    263   // (which is the "real" url of the page)
    264   // from the NavigationEntry because it reflects its' origin
    265   // rather than the displayed one (returned by GetURL) which may be
    266   // different (like having "view-source:" on the front).
    267   NavigationEntry* active_entry =
    268       tab_contents()->controller().GetActiveEntry();
    269   return active_entry->url();
    270 }
    271 
    272 // Cancel all in progress request, might be called by user or internal error.
    273 void SavePackage::Cancel(bool user_action) {
    274   if (!canceled()) {
    275     if (user_action)
    276       user_canceled_ = true;
    277     else
    278       disk_error_occurred_ = true;
    279     Stop();
    280   }
    281 }
    282 
    283 // Init() can be called directly, or indirectly via GetSaveInfo(). In both
    284 // cases, we need file_manager_ to be initialized, so we do this first.
    285 void SavePackage::InternalInit() {
    286   ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host();
    287   if (!rdh) {
    288     NOTREACHED();
    289     return;
    290   }
    291 
    292   file_manager_ = rdh->save_file_manager();
    293   if (!file_manager_) {
    294     NOTREACHED();
    295     return;
    296   }
    297 }
    298 
    299 // Initialize the SavePackage.
    300 bool SavePackage::Init() {
    301   // Set proper running state.
    302   if (wait_state_ != INITIALIZE)
    303     return false;
    304 
    305   wait_state_ = START_PROCESS;
    306 
    307   // Initialize the request context and resource dispatcher.
    308   Profile* profile = tab_contents()->profile();
    309   if (!profile) {
    310     NOTREACHED();
    311     return false;
    312   }
    313 
    314   request_context_getter_ = profile->GetRequestContext();
    315 
    316   // Create the fake DownloadItem and display the view.
    317   DownloadManager* download_manager =
    318       tab_contents()->profile()->GetDownloadManager();
    319   download_ = new DownloadItem(download_manager,
    320                                saved_main_file_path_,
    321                                page_url_,
    322                                profile->IsOffTheRecord());
    323 
    324   // Transfer the ownership to the download manager. We need the DownloadItem
    325   // to be alive as long as the Profile is alive.
    326   download_manager->SavePageAsDownloadStarted(download_);
    327 
    328   tab_contents()->OnStartDownload(download_);
    329 
    330   // Check save type and process the save page job.
    331   if (save_type_ == SAVE_AS_COMPLETE_HTML) {
    332     // Get directory
    333     DCHECK(!saved_main_directory_path_.empty());
    334     GetAllSavableResourceLinksForCurrentPage();
    335   } else {
    336     wait_state_ = NET_FILES;
    337     SaveFileCreateInfo::SaveFileSource save_source = page_url_.SchemeIsFile() ?
    338         SaveFileCreateInfo::SAVE_FILE_FROM_FILE :
    339         SaveFileCreateInfo::SAVE_FILE_FROM_NET;
    340     SaveItem* save_item = new SaveItem(page_url_,
    341                                        GURL(),
    342                                        this,
    343                                        save_source);
    344     // Add this item to waiting list.
    345     waiting_item_queue_.push(save_item);
    346     all_save_items_count_ = 1;
    347     download_->set_total_bytes(1);
    348 
    349     DoSavingProcess();
    350   }
    351 
    352   return true;
    353 }
    354 
    355 // On POSIX, the length of |pure_file_name| + |file_name_ext| is further
    356 // restricted by NAME_MAX. The maximum allowed path looks like:
    357 // '/path/to/save_dir' + '/' + NAME_MAX.
    358 uint32 SavePackage::GetMaxPathLengthForDirectory(const FilePath& base_dir) {
    359 #if defined(OS_POSIX)
    360   return std::min(kMaxFilePathLength,
    361                   static_cast<uint32>(base_dir.value().length()) +
    362                   NAME_MAX + 1);
    363 #else
    364   return kMaxFilePathLength;
    365 #endif
    366 }
    367 
    368 // File name is considered being consist of pure file name, dot and file
    369 // extension name. File name might has no dot and file extension, or has
    370 // multiple dot inside file name. The dot, which separates the pure file
    371 // name and file extension name, is last dot in the whole file name.
    372 // This function is for making sure the length of specified file path is not
    373 // great than the specified maximum length of file path and getting safe pure
    374 // file name part if the input pure file name is too long.
    375 // The parameter |dir_path| specifies directory part of the specified
    376 // file path. The parameter |file_name_ext| specifies file extension
    377 // name part of the specified file path (including start dot). The parameter
    378 // |max_file_path_len| specifies maximum length of the specified file path.
    379 // The parameter |pure_file_name| input pure file name part of the specified
    380 // file path. If the length of specified file path is great than
    381 // |max_file_path_len|, the |pure_file_name| will output new pure file name
    382 // part for making sure the length of specified file path is less than
    383 // specified maximum length of file path. Return false if the function can
    384 // not get a safe pure file name, otherwise it returns true.
    385 bool SavePackage::GetSafePureFileName(const FilePath& dir_path,
    386                                       const FilePath::StringType& file_name_ext,
    387                                       uint32 max_file_path_len,
    388                                       FilePath::StringType* pure_file_name) {
    389   DCHECK(!pure_file_name->empty());
    390   int available_length = static_cast<int>(max_file_path_len -
    391                                           dir_path.value().length() -
    392                                           file_name_ext.length());
    393   // Need an extra space for the separator.
    394   if (!file_util::EndsWithSeparator(dir_path))
    395     --available_length;
    396 
    397   // Plenty of room.
    398   if (static_cast<int>(pure_file_name->length()) <= available_length)
    399     return true;
    400 
    401   // Limited room. Truncate |pure_file_name| to fit.
    402   if (available_length > 0) {
    403     *pure_file_name = pure_file_name->substr(0, available_length);
    404     return true;
    405   }
    406 
    407   // Not enough room to even use a shortened |pure_file_name|.
    408   pure_file_name->clear();
    409   return false;
    410 }
    411 
    412 // Generate name for saving resource.
    413 bool SavePackage::GenerateFileName(const std::string& disposition,
    414                                    const GURL& url,
    415                                    bool need_html_ext,
    416                                    FilePath::StringType* generated_name) {
    417   // TODO(jungshik): Figure out the referrer charset when having one
    418   // makes sense and pass it to GetSuggestedFilename.
    419   string16 suggested_name =
    420       net::GetSuggestedFilename(url, disposition, "",
    421                                 ASCIIToUTF16(kDefaultSaveName));
    422 
    423   // TODO(evan): this code is totally wrong -- we should just generate
    424   // Unicode filenames and do all this encoding switching at the end.
    425   // However, I'm just shuffling wrong code around, at least not adding
    426   // to it.
    427 #if defined(OS_WIN)
    428   FilePath file_path = FilePath(suggested_name);
    429 #else
    430   FilePath file_path = FilePath(
    431       base::SysWideToNativeMB(UTF16ToWide(suggested_name)));
    432 #endif
    433 
    434   DCHECK(!file_path.empty());
    435   FilePath::StringType pure_file_name =
    436       file_path.RemoveExtension().BaseName().value();
    437   FilePath::StringType file_name_ext = file_path.Extension();
    438 
    439   // If it is HTML resource, use ".htm{l,}" as its extension.
    440   if (need_html_ext) {
    441     file_name_ext = FILE_PATH_LITERAL(".");
    442     file_name_ext.append(kDefaultHtmlExtension);
    443   }
    444 
    445   // Need to make sure the suggested file name is not too long.
    446   uint32 max_path = GetMaxPathLengthForDirectory(saved_main_directory_path_);
    447 
    448   // Get safe pure file name.
    449   if (!GetSafePureFileName(saved_main_directory_path_, file_name_ext,
    450                            max_path, &pure_file_name))
    451     return false;
    452 
    453   FilePath::StringType file_name = pure_file_name + file_name_ext;
    454 
    455   // Check whether we already have same name.
    456   if (file_name_set_.find(file_name) == file_name_set_.end()) {
    457     file_name_set_.insert(file_name);
    458   } else {
    459     // Found same name, increase the ordinal number for the file name.
    460     FilePath::StringType base_file_name = StripOrdinalNumber(pure_file_name);
    461 
    462     // We need to make sure the length of base file name plus maximum ordinal
    463     // number path will be less than or equal to kMaxFilePathLength.
    464     if (!GetSafePureFileName(saved_main_directory_path_, file_name_ext,
    465         max_path - kMaxFileOrdinalNumberPartLength, &base_file_name))
    466       return false;
    467 
    468     // Prepare the new ordinal number.
    469     uint32 ordinal_number;
    470     FileNameCountMap::iterator it = file_name_count_map_.find(base_file_name);
    471     if (it == file_name_count_map_.end()) {
    472       // First base-name-conflict resolving, use 1 as initial ordinal number.
    473       file_name_count_map_[base_file_name] = 1;
    474       ordinal_number = 1;
    475     } else {
    476       // We have met same base-name conflict, use latest ordinal number.
    477       ordinal_number = it->second;
    478     }
    479 
    480     if (ordinal_number > (kMaxFileOrdinalNumber - 1)) {
    481       // Use a random file from temporary file.
    482       FilePath temp_file;
    483       file_util::CreateTemporaryFile(&temp_file);
    484       file_name = temp_file.RemoveExtension().BaseName().value();
    485       // Get safe pure file name.
    486       if (!GetSafePureFileName(saved_main_directory_path_,
    487                                FilePath::StringType(),
    488                                max_path, &file_name))
    489         return false;
    490     } else {
    491       for (int i = ordinal_number; i < kMaxFileOrdinalNumber; ++i) {
    492         FilePath::StringType new_name = base_file_name +
    493             StringPrintf(FILE_PATH_LITERAL("(%d)"), i) + file_name_ext;
    494         if (file_name_set_.find(new_name) == file_name_set_.end()) {
    495           // Resolved name conflict.
    496           file_name = new_name;
    497           file_name_count_map_[base_file_name] = ++i;
    498           break;
    499         }
    500       }
    501     }
    502 
    503     file_name_set_.insert(file_name);
    504   }
    505 
    506   DCHECK(!file_name.empty());
    507   generated_name->assign(file_name);
    508 
    509   return true;
    510 }
    511 
    512 // We have received a message from SaveFileManager about a new saving job. We
    513 // create a SaveItem and store it in our in_progress list.
    514 void SavePackage::StartSave(const SaveFileCreateInfo* info) {
    515   DCHECK(info && !info->url.is_empty());
    516 
    517   SaveUrlItemMap::iterator it = in_progress_items_.find(info->url.spec());
    518   if (it == in_progress_items_.end()) {
    519     // If not found, we must have cancel action.
    520     DCHECK(canceled());
    521     return;
    522   }
    523   SaveItem* save_item = it->second;
    524 
    525   DCHECK(!saved_main_file_path_.empty());
    526 
    527   save_item->SetSaveId(info->save_id);
    528   save_item->SetTotalBytes(info->total_bytes);
    529 
    530   // Determine the proper path for a saving job, by choosing either the default
    531   // save directory, or prompting the user.
    532   DCHECK(!save_item->has_final_name());
    533   if (info->url != page_url_) {
    534     FilePath::StringType generated_name;
    535     // For HTML resource file, make sure it will have .htm as extension name,
    536     // otherwise, when you open the saved page in Chrome again, download
    537     // file manager will treat it as downloadable resource, and download it
    538     // instead of opening it as HTML.
    539     bool need_html_ext =
    540         info->save_source == SaveFileCreateInfo::SAVE_FILE_FROM_DOM;
    541     if (!GenerateFileName(info->content_disposition,
    542                           GURL(info->url),
    543                           need_html_ext,
    544                           &generated_name)) {
    545       // We can not generate file name for this SaveItem, so we cancel the
    546       // saving page job if the save source is from serialized DOM data.
    547       // Otherwise, it means this SaveItem is sub-resource type, we treat it
    548       // as an error happened on saving. We can ignore this type error for
    549       // sub-resource links which will be resolved as absolute links instead
    550       // of local links in final saved contents.
    551       if (info->save_source == SaveFileCreateInfo::SAVE_FILE_FROM_DOM)
    552         Cancel(true);
    553       else
    554         SaveFinished(save_item->save_id(), 0, false);
    555       return;
    556     }
    557 
    558     // When saving page as only-HTML, we only have a SaveItem whose url
    559     // must be page_url_.
    560     DCHECK(save_type_ == SAVE_AS_COMPLETE_HTML);
    561     DCHECK(!saved_main_directory_path_.empty());
    562 
    563     // Now we get final name retrieved from GenerateFileName, we will use it
    564     // rename the SaveItem.
    565     FilePath final_name = saved_main_directory_path_.Append(generated_name);
    566     save_item->Rename(final_name);
    567   } else {
    568     // It is the main HTML file, use the name chosen by the user.
    569     save_item->Rename(saved_main_file_path_);
    570   }
    571 
    572   // If the save source is from file system, inform SaveFileManager to copy
    573   // corresponding file to the file path which this SaveItem specifies.
    574   if (info->save_source == SaveFileCreateInfo::SAVE_FILE_FROM_FILE) {
    575     BrowserThread::PostTask(
    576         BrowserThread::FILE, FROM_HERE,
    577         NewRunnableMethod(file_manager_,
    578                           &SaveFileManager::SaveLocalFile,
    579                           save_item->url(),
    580                           save_item->save_id(),
    581                           tab_id()));
    582     return;
    583   }
    584 
    585   // Check whether we begin to require serialized HTML data.
    586   if (save_type_ == SAVE_AS_COMPLETE_HTML && wait_state_ == HTML_DATA) {
    587     // Inform backend to serialize the all frames' DOM and send serialized
    588     // HTML data back.
    589     GetSerializedHtmlDataForCurrentPageWithLocalLinks();
    590   }
    591 }
    592 
    593 // Look up SaveItem by save id from in progress map.
    594 SaveItem* SavePackage::LookupItemInProcessBySaveId(int32 save_id) {
    595   if (in_process_count()) {
    596     for (SaveUrlItemMap::iterator it = in_progress_items_.begin();
    597         it != in_progress_items_.end(); ++it) {
    598       SaveItem* save_item = it->second;
    599       DCHECK(save_item->state() == SaveItem::IN_PROGRESS);
    600       if (save_item->save_id() == save_id)
    601         return save_item;
    602     }
    603   }
    604   return NULL;
    605 }
    606 
    607 // Remove SaveItem from in progress map and put it to saved map.
    608 void SavePackage::PutInProgressItemToSavedMap(SaveItem* save_item) {
    609   SaveUrlItemMap::iterator it = in_progress_items_.find(
    610       save_item->url().spec());
    611   DCHECK(it != in_progress_items_.end());
    612   DCHECK(save_item == it->second);
    613   in_progress_items_.erase(it);
    614 
    615   if (save_item->success()) {
    616     // Add it to saved_success_items_.
    617     DCHECK(saved_success_items_.find(save_item->save_id()) ==
    618            saved_success_items_.end());
    619     saved_success_items_[save_item->save_id()] = save_item;
    620   } else {
    621     // Add it to saved_failed_items_.
    622     DCHECK(saved_failed_items_.find(save_item->url().spec()) ==
    623            saved_failed_items_.end());
    624     saved_failed_items_[save_item->url().spec()] = save_item;
    625   }
    626 }
    627 
    628 // Called for updating saving state.
    629 bool SavePackage::UpdateSaveProgress(int32 save_id,
    630                                      int64 size,
    631                                      bool write_success) {
    632   // Because we might have canceled this saving job before,
    633   // so we might not find corresponding SaveItem.
    634   SaveItem* save_item = LookupItemInProcessBySaveId(save_id);
    635   if (!save_item)
    636     return false;
    637 
    638   save_item->Update(size);
    639 
    640   // If we got disk error, cancel whole save page job.
    641   if (!write_success) {
    642     // Cancel job with reason of disk error.
    643     Cancel(false);
    644   }
    645   return true;
    646 }
    647 
    648 // Stop all page saving jobs that are in progress and instruct the file thread
    649 // to delete all saved  files.
    650 void SavePackage::Stop() {
    651   // If we haven't moved out of the initial state, there's nothing to cancel and
    652   // there won't be valid pointers for file_manager_ or download_.
    653   if (wait_state_ == INITIALIZE)
    654     return;
    655 
    656   // When stopping, if it still has some items in in_progress, cancel them.
    657   DCHECK(canceled());
    658   if (in_process_count()) {
    659     SaveUrlItemMap::iterator it = in_progress_items_.begin();
    660     for (; it != in_progress_items_.end(); ++it) {
    661       SaveItem* save_item = it->second;
    662       DCHECK(save_item->state() == SaveItem::IN_PROGRESS);
    663       save_item->Cancel();
    664     }
    665     // Remove all in progress item to saved map. For failed items, they will
    666     // be put into saved_failed_items_, for successful item, they will be put
    667     // into saved_success_items_.
    668     while (in_process_count())
    669       PutInProgressItemToSavedMap(in_progress_items_.begin()->second);
    670   }
    671 
    672   // This vector contains the save ids of the save files which SaveFileManager
    673   // needs to remove from its save_file_map_.
    674   SaveIDList save_ids;
    675   for (SavedItemMap::iterator it = saved_success_items_.begin();
    676       it != saved_success_items_.end(); ++it)
    677     save_ids.push_back(it->first);
    678   for (SaveUrlItemMap::iterator it = saved_failed_items_.begin();
    679       it != saved_failed_items_.end(); ++it)
    680     save_ids.push_back(it->second->save_id());
    681 
    682   BrowserThread::PostTask(
    683       BrowserThread::FILE, FROM_HERE,
    684       NewRunnableMethod(file_manager_,
    685                         &SaveFileManager::RemoveSavedFileFromFileMap,
    686                         save_ids));
    687 
    688   finished_ = true;
    689   wait_state_ = FAILED;
    690 
    691   // Inform the DownloadItem we have canceled whole save page job.
    692   download_->Cancel(false);
    693 }
    694 
    695 void SavePackage::CheckFinish() {
    696   if (in_process_count() || finished_)
    697     return;
    698 
    699   FilePath dir = (save_type_ == SAVE_AS_COMPLETE_HTML &&
    700                   saved_success_items_.size() > 1) ?
    701                   saved_main_directory_path_ : FilePath();
    702 
    703   // This vector contains the final names of all the successfully saved files
    704   // along with their save ids. It will be passed to SaveFileManager to do the
    705   // renaming job.
    706   FinalNameList final_names;
    707   for (SavedItemMap::iterator it = saved_success_items_.begin();
    708       it != saved_success_items_.end(); ++it)
    709     final_names.push_back(std::make_pair(it->first,
    710                                          it->second->full_path()));
    711 
    712   BrowserThread::PostTask(
    713       BrowserThread::FILE, FROM_HERE,
    714       NewRunnableMethod(file_manager_,
    715                         &SaveFileManager::RenameAllFiles,
    716                         final_names,
    717                         dir,
    718                         tab_contents()->GetRenderProcessHost()->id(),
    719                         tab_contents()->render_view_host()->routing_id(),
    720                         id()));
    721 }
    722 
    723 // Successfully finished all items of this SavePackage.
    724 void SavePackage::Finish() {
    725   // User may cancel the job when we're moving files to the final directory.
    726   if (canceled())
    727     return;
    728 
    729   wait_state_ = SUCCESSFUL;
    730   finished_ = true;
    731 
    732   // This vector contains the save ids of the save files which SaveFileManager
    733   // needs to remove from its save_file_map_.
    734   SaveIDList save_ids;
    735   for (SaveUrlItemMap::iterator it = saved_failed_items_.begin();
    736        it != saved_failed_items_.end(); ++it)
    737     save_ids.push_back(it->second->save_id());
    738 
    739   BrowserThread::PostTask(
    740       BrowserThread::FILE, FROM_HERE,
    741       NewRunnableMethod(file_manager_,
    742                         &SaveFileManager::RemoveSavedFileFromFileMap,
    743                         save_ids));
    744 
    745   download_->OnAllDataSaved(all_save_items_count_);
    746   download_->MarkAsComplete();
    747 
    748   NotificationService::current()->Notify(
    749       NotificationType::SAVE_PACKAGE_SUCCESSFULLY_FINISHED,
    750       Source<SavePackage>(this),
    751       Details<GURL>(&page_url_));
    752 }
    753 
    754 // Called for updating end state.
    755 void SavePackage::SaveFinished(int32 save_id, int64 size, bool is_success) {
    756   // Because we might have canceled this saving job before,
    757   // so we might not find corresponding SaveItem. Just ignore it.
    758   SaveItem* save_item = LookupItemInProcessBySaveId(save_id);
    759   if (!save_item)
    760     return;
    761 
    762   // Let SaveItem set end state.
    763   save_item->Finish(size, is_success);
    764   // Remove the associated save id and SavePackage.
    765   file_manager_->RemoveSaveFile(save_id, save_item->url(), this);
    766 
    767   PutInProgressItemToSavedMap(save_item);
    768 
    769   // Inform the DownloadItem to update UI.
    770   // We use the received bytes as number of saved files.
    771   download_->Update(completed_count());
    772 
    773   if (save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM &&
    774       save_item->url() == page_url_ && !save_item->received_bytes()) {
    775     // If size of main HTML page is 0, treat it as disk error.
    776     Cancel(false);
    777     return;
    778   }
    779 
    780   if (canceled()) {
    781     DCHECK(finished_);
    782     return;
    783   }
    784 
    785   // Continue processing the save page job.
    786   DoSavingProcess();
    787 
    788   // Check whether we can successfully finish whole job.
    789   CheckFinish();
    790 }
    791 
    792 // Sometimes, the net io will only call SaveFileManager::SaveFinished with
    793 // save id -1 when it encounters error. Since in this case, save id will be
    794 // -1, so we can only use URL to find which SaveItem is associated with
    795 // this error.
    796 // Saving an item failed. If it's a sub-resource, ignore it. If the error comes
    797 // from serializing HTML data, then cancel saving page.
    798 void SavePackage::SaveFailed(const GURL& save_url) {
    799   SaveUrlItemMap::iterator it = in_progress_items_.find(save_url.spec());
    800   if (it == in_progress_items_.end()) {
    801     NOTREACHED();  // Should not exist!
    802     return;
    803   }
    804   SaveItem* save_item = it->second;
    805 
    806   save_item->Finish(0, false);
    807 
    808   PutInProgressItemToSavedMap(save_item);
    809 
    810   // Inform the DownloadItem to update UI.
    811   // We use the received bytes as number of saved files.
    812   download_->Update(completed_count());
    813 
    814   if (save_type_ == SAVE_AS_ONLY_HTML ||
    815       save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM) {
    816     // We got error when saving page. Treat it as disk error.
    817     Cancel(true);
    818   }
    819 
    820   if (canceled()) {
    821     DCHECK(finished_);
    822     return;
    823   }
    824 
    825   // Continue processing the save page job.
    826   DoSavingProcess();
    827 
    828   CheckFinish();
    829 }
    830 
    831 void SavePackage::SaveCanceled(SaveItem* save_item) {
    832   // Call the RemoveSaveFile in UI thread.
    833   file_manager_->RemoveSaveFile(save_item->save_id(),
    834                                 save_item->url(),
    835                                 this);
    836   if (save_item->save_id() != -1)
    837     BrowserThread::PostTask(
    838         BrowserThread::FILE, FROM_HERE,
    839         NewRunnableMethod(file_manager_,
    840                           &SaveFileManager::CancelSave,
    841                           save_item->save_id()));
    842 }
    843 
    844 // Initiate a saving job of a specific URL. We send the request to
    845 // SaveFileManager, which will dispatch it to different approach according to
    846 // the save source. Parameter process_all_remaining_items indicates whether
    847 // we need to save all remaining items.
    848 void SavePackage::SaveNextFile(bool process_all_remaining_items) {
    849   DCHECK(tab_contents());
    850   DCHECK(waiting_item_queue_.size());
    851 
    852   do {
    853     // Pop SaveItem from waiting list.
    854     SaveItem* save_item = waiting_item_queue_.front();
    855     waiting_item_queue_.pop();
    856 
    857     // Add the item to in_progress_items_.
    858     SaveUrlItemMap::iterator it = in_progress_items_.find(
    859         save_item->url().spec());
    860     DCHECK(it == in_progress_items_.end());
    861     in_progress_items_[save_item->url().spec()] = save_item;
    862     save_item->Start();
    863     file_manager_->SaveURL(save_item->url(),
    864                            save_item->referrer(),
    865                            tab_contents()->GetRenderProcessHost()->id(),
    866                            routing_id(),
    867                            save_item->save_source(),
    868                            save_item->full_path(),
    869                            request_context_getter_.get(),
    870                            this);
    871   } while (process_all_remaining_items && waiting_item_queue_.size());
    872 }
    873 
    874 
    875 // Open download page in windows explorer on file thread, to avoid blocking the
    876 // user interface.
    877 void SavePackage::ShowDownloadInShell() {
    878   DCHECK(file_manager_);
    879   DCHECK(finished_ && !canceled() && !saved_main_file_path_.empty());
    880   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    881 #if defined(OS_MACOSX)
    882   // Mac OS X requires opening downloads on the UI thread.
    883   platform_util::ShowItemInFolder(saved_main_file_path_);
    884 #else
    885   BrowserThread::PostTask(
    886       BrowserThread::FILE, FROM_HERE,
    887       NewRunnableMethod(file_manager_,
    888                         &SaveFileManager::OnShowSavedFileInShell,
    889                         saved_main_file_path_));
    890 #endif
    891 }
    892 
    893 // Calculate the percentage of whole save page job.
    894 int SavePackage::PercentComplete() {
    895   if (!all_save_items_count_)
    896     return 0;
    897   else if (!in_process_count())
    898     return 100;
    899   else
    900     return completed_count() / all_save_items_count_;
    901 }
    902 
    903 // Continue processing the save page job after one SaveItem has been
    904 // finished.
    905 void SavePackage::DoSavingProcess() {
    906   if (save_type_ == SAVE_AS_COMPLETE_HTML) {
    907     // We guarantee that images and JavaScripts must be downloaded first.
    908     // So when finishing all those sub-resources, we will know which
    909     // sub-resource's link can be replaced with local file path, which
    910     // sub-resource's link need to be replaced with absolute URL which
    911     // point to its internet address because it got error when saving its data.
    912     SaveItem* save_item = NULL;
    913     // Start a new SaveItem job if we still have job in waiting queue.
    914     if (waiting_item_queue_.size()) {
    915       DCHECK(wait_state_ == NET_FILES);
    916       save_item = waiting_item_queue_.front();
    917       if (save_item->save_source() != SaveFileCreateInfo::SAVE_FILE_FROM_DOM) {
    918         SaveNextFile(false);
    919       } else if (!in_process_count()) {
    920         // If there is no in-process SaveItem, it means all sub-resources
    921         // have been processed. Now we need to start serializing HTML DOM
    922         // for the current page to get the generated HTML data.
    923         wait_state_ = HTML_DATA;
    924         // All non-HTML resources have been finished, start all remaining
    925         // HTML files.
    926         SaveNextFile(true);
    927       }
    928     } else if (in_process_count()) {
    929       // Continue asking for HTML data.
    930       DCHECK(wait_state_ == HTML_DATA);
    931     }
    932   } else {
    933     // Save as HTML only.
    934     DCHECK(wait_state_ == NET_FILES);
    935     DCHECK(save_type_ == SAVE_AS_ONLY_HTML);
    936     if (waiting_item_queue_.size()) {
    937       DCHECK(all_save_items_count_ == waiting_item_queue_.size());
    938       SaveNextFile(false);
    939     }
    940   }
    941 }
    942 
    943 bool SavePackage::OnMessageReceived(const IPC::Message& message) {
    944   bool handled = true;
    945   IPC_BEGIN_MESSAGE_MAP(SavePackage, message)
    946     IPC_MESSAGE_HANDLER(ViewHostMsg_SendCurrentPageAllSavableResourceLinks,
    947                         OnReceivedSavableResourceLinksForCurrentPage)
    948     IPC_MESSAGE_HANDLER(ViewHostMsg_SendSerializedHtmlData,
    949                         OnReceivedSerializedHtmlData)
    950     IPC_MESSAGE_UNHANDLED(handled = false)
    951   IPC_END_MESSAGE_MAP()
    952   return handled;
    953 }
    954 
    955 // After finishing all SaveItems which need to get data from net.
    956 // We collect all URLs which have local storage and send the
    957 // map:(originalURL:currentLocalPath) to render process (backend).
    958 // Then render process will serialize DOM and send data to us.
    959 void SavePackage::GetSerializedHtmlDataForCurrentPageWithLocalLinks() {
    960   if (wait_state_ != HTML_DATA)
    961     return;
    962   std::vector<GURL> saved_links;
    963   std::vector<FilePath> saved_file_paths;
    964   int successful_started_items_count = 0;
    965 
    966   // Collect all saved items which have local storage.
    967   // First collect the status of all the resource files and check whether they
    968   // have created local files although they have not been completely saved.
    969   // If yes, the file can be saved. Otherwise, there is a disk error, so we
    970   // need to cancel the page saving job.
    971   for (SaveUrlItemMap::iterator it = in_progress_items_.begin();
    972        it != in_progress_items_.end(); ++it) {
    973     DCHECK(it->second->save_source() ==
    974            SaveFileCreateInfo::SAVE_FILE_FROM_DOM);
    975     if (it->second->has_final_name())
    976       successful_started_items_count++;
    977     saved_links.push_back(it->second->url());
    978     saved_file_paths.push_back(it->second->file_name());
    979   }
    980 
    981   // If not all file of HTML resource have been started, then wait.
    982   if (successful_started_items_count != in_process_count())
    983     return;
    984 
    985   // Collect all saved success items.
    986   for (SavedItemMap::iterator it = saved_success_items_.begin();
    987        it != saved_success_items_.end(); ++it) {
    988     DCHECK(it->second->has_final_name());
    989     saved_links.push_back(it->second->url());
    990     saved_file_paths.push_back(it->second->file_name());
    991   }
    992 
    993   // Get the relative directory name.
    994   FilePath relative_dir_name = saved_main_directory_path_.BaseName();
    995 
    996   tab_contents()->render_view_host()->
    997       GetSerializedHtmlDataForCurrentPageWithLocalLinks(
    998       saved_links, saved_file_paths, relative_dir_name);
    999 }
   1000 
   1001 // Process the serialized HTML content data of a specified web page
   1002 // retrieved from render process.
   1003 void SavePackage::OnReceivedSerializedHtmlData(const GURL& frame_url,
   1004                                                const std::string& data,
   1005                                                int32 status) {
   1006   WebPageSerializerClient::PageSerializationStatus flag =
   1007       static_cast<WebPageSerializerClient::PageSerializationStatus>(status);
   1008   // Check current state.
   1009   if (wait_state_ != HTML_DATA)
   1010     return;
   1011 
   1012   int id = tab_id();
   1013   // If the all frames are finished saving, we need to close the
   1014   // remaining SaveItems.
   1015   if (flag == WebPageSerializerClient::AllFramesAreFinished) {
   1016     for (SaveUrlItemMap::iterator it = in_progress_items_.begin();
   1017          it != in_progress_items_.end(); ++it) {
   1018       VLOG(20) << " " << __FUNCTION__ << "()"
   1019                << " save_id = " << it->second->save_id()
   1020                << " url = \"" << it->second->url().spec() << "\"";
   1021       BrowserThread::PostTask(
   1022           BrowserThread::FILE, FROM_HERE,
   1023           NewRunnableMethod(file_manager_,
   1024                             &SaveFileManager::SaveFinished,
   1025                             it->second->save_id(),
   1026                             it->second->url(),
   1027                             id,
   1028                             true));
   1029     }
   1030     return;
   1031   }
   1032 
   1033   SaveUrlItemMap::iterator it = in_progress_items_.find(frame_url.spec());
   1034   if (it == in_progress_items_.end())
   1035     return;
   1036   SaveItem* save_item = it->second;
   1037   DCHECK(save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM);
   1038 
   1039   if (!data.empty()) {
   1040     // Prepare buffer for saving HTML data.
   1041     scoped_refptr<net::IOBuffer> new_data(new net::IOBuffer(data.size()));
   1042     memcpy(new_data->data(), data.data(), data.size());
   1043 
   1044     // Call write file functionality in file thread.
   1045     BrowserThread::PostTask(
   1046         BrowserThread::FILE, FROM_HERE,
   1047         NewRunnableMethod(file_manager_,
   1048                           &SaveFileManager::UpdateSaveProgress,
   1049                           save_item->save_id(),
   1050                           new_data,
   1051                           static_cast<int>(data.size())));
   1052   }
   1053 
   1054   // Current frame is completed saving, call finish in file thread.
   1055   if (flag == WebPageSerializerClient::CurrentFrameIsFinished) {
   1056     VLOG(20) << " " << __FUNCTION__ << "()"
   1057              << " save_id = " << save_item->save_id()
   1058              << " url = \"" << save_item->url().spec() << "\"";
   1059     BrowserThread::PostTask(
   1060         BrowserThread::FILE, FROM_HERE,
   1061         NewRunnableMethod(file_manager_,
   1062                           &SaveFileManager::SaveFinished,
   1063                           save_item->save_id(),
   1064                           save_item->url(),
   1065                           id,
   1066                           true));
   1067   }
   1068 }
   1069 
   1070 // Ask for all savable resource links from backend, include main frame and
   1071 // sub-frame.
   1072 void SavePackage::GetAllSavableResourceLinksForCurrentPage() {
   1073   if (wait_state_ != START_PROCESS)
   1074     return;
   1075 
   1076   wait_state_ = RESOURCES_LIST;
   1077   tab_contents()->render_view_host()->
   1078       GetAllSavableResourceLinksForCurrentPage(page_url_);
   1079 }
   1080 
   1081 // Give backend the lists which contain all resource links that have local
   1082 // storage, after which, render process will serialize DOM for generating
   1083 // HTML data.
   1084 void SavePackage::OnReceivedSavableResourceLinksForCurrentPage(
   1085     const std::vector<GURL>& resources_list,
   1086     const std::vector<GURL>& referrers_list,
   1087     const std::vector<GURL>& frames_list) {
   1088   if (wait_state_ != RESOURCES_LIST)
   1089     return;
   1090 
   1091   DCHECK(resources_list.size() == referrers_list.size());
   1092   all_save_items_count_ = static_cast<int>(resources_list.size()) +
   1093                            static_cast<int>(frames_list.size());
   1094 
   1095   // We use total bytes as the total number of files we want to save.
   1096   download_->set_total_bytes(all_save_items_count_);
   1097 
   1098   if (all_save_items_count_) {
   1099     // Put all sub-resources to wait list.
   1100     for (int i = 0; i < static_cast<int>(resources_list.size()); ++i) {
   1101       const GURL& u = resources_list[i];
   1102       DCHECK(u.is_valid());
   1103       SaveFileCreateInfo::SaveFileSource save_source = u.SchemeIsFile() ?
   1104           SaveFileCreateInfo::SAVE_FILE_FROM_FILE :
   1105           SaveFileCreateInfo::SAVE_FILE_FROM_NET;
   1106       SaveItem* save_item = new SaveItem(u, referrers_list[i],
   1107                                          this, save_source);
   1108       waiting_item_queue_.push(save_item);
   1109     }
   1110     // Put all HTML resources to wait list.
   1111     for (int i = 0; i < static_cast<int>(frames_list.size()); ++i) {
   1112       const GURL& u = frames_list[i];
   1113       DCHECK(u.is_valid());
   1114       SaveItem* save_item = new SaveItem(u, GURL(),
   1115           this, SaveFileCreateInfo::SAVE_FILE_FROM_DOM);
   1116       waiting_item_queue_.push(save_item);
   1117     }
   1118     wait_state_ = NET_FILES;
   1119     DoSavingProcess();
   1120   } else {
   1121     // No resource files need to be saved, treat it as user cancel.
   1122     Cancel(true);
   1123   }
   1124 }
   1125 
   1126 void SavePackage::SetShouldPromptUser(bool should_prompt) {
   1127   g_should_prompt_for_filename = should_prompt;
   1128 }
   1129 
   1130 FilePath SavePackage::GetSuggestedNameForSaveAs(
   1131     bool can_save_as_complete,
   1132     const std::string& contents_mime_type) {
   1133   FilePath name_with_proper_ext =
   1134       FilePath::FromWStringHack(UTF16ToWideHack(title_));
   1135 
   1136   // If the page's title matches its URL, use the URL. Try to use the last path
   1137   // component or if there is none, the domain as the file name.
   1138   // Normally we want to base the filename on the page title, or if it doesn't
   1139   // exist, on the URL. It's not easy to tell if the page has no title, because
   1140   // if the page has no title, TabContents::GetTitle() will return the page's
   1141   // URL (adjusted for display purposes). Therefore, we convert the "title"
   1142   // back to a URL, and if it matches the original page URL, we know the page
   1143   // had no title (or had a title equal to its URL, which is fine to treat
   1144   // similarly).
   1145   GURL fixed_up_title_url =
   1146       URLFixerUpper::FixupURL(UTF16ToUTF8(title_), std::string());
   1147 
   1148   if (page_url_ == fixed_up_title_url) {
   1149     std::string url_path;
   1150     std::vector<std::string> url_parts;
   1151     base::SplitString(page_url_.path(), '/', &url_parts);
   1152     if (!url_parts.empty()) {
   1153       for (int i = static_cast<int>(url_parts.size()) - 1; i >= 0; --i) {
   1154         url_path = url_parts[i];
   1155         if (!url_path.empty())
   1156           break;
   1157       }
   1158     }
   1159     if (url_path.empty())
   1160       url_path = page_url_.host();
   1161     name_with_proper_ext = FilePath::FromWStringHack(UTF8ToWide(url_path));
   1162   }
   1163 
   1164   // Ask user for getting final saving name.
   1165   name_with_proper_ext = EnsureMimeExtension(name_with_proper_ext,
   1166                                              contents_mime_type);
   1167   // Adjust extension for complete types.
   1168   if (can_save_as_complete)
   1169     name_with_proper_ext = EnsureHtmlExtension(name_with_proper_ext);
   1170 
   1171   FilePath::StringType file_name = name_with_proper_ext.value();
   1172   file_util::ReplaceIllegalCharactersInPath(&file_name, ' ');
   1173   return FilePath(file_name);
   1174 }
   1175 
   1176 FilePath SavePackage::EnsureHtmlExtension(const FilePath& name) {
   1177   // If the file name doesn't have an extension suitable for HTML files,
   1178   // append one.
   1179   FilePath::StringType ext = name.Extension();
   1180   if (!ext.empty())
   1181     ext.erase(ext.begin());  // Erase preceding '.'.
   1182   std::string mime_type;
   1183   if (!net::GetMimeTypeFromExtension(ext, &mime_type) ||
   1184       !CanSaveAsComplete(mime_type)) {
   1185     return FilePath(name.value() + FILE_PATH_LITERAL(".") +
   1186                     kDefaultHtmlExtension);
   1187   }
   1188   return name;
   1189 }
   1190 
   1191 FilePath SavePackage::EnsureMimeExtension(const FilePath& name,
   1192     const std::string& contents_mime_type) {
   1193   // Start extension at 1 to skip over period if non-empty.
   1194   FilePath::StringType ext = name.Extension().length() ?
   1195       name.Extension().substr(1) : name.Extension();
   1196   FilePath::StringType suggested_extension =
   1197       ExtensionForMimeType(contents_mime_type);
   1198   std::string mime_type;
   1199   if (!suggested_extension.empty() &&
   1200       (!net::GetMimeTypeFromExtension(ext, &mime_type) ||
   1201       !IsSavableContents(mime_type))) {
   1202     // Extension is absent or needs to be updated.
   1203     return FilePath(name.value() + FILE_PATH_LITERAL(".") +
   1204                     suggested_extension);
   1205   }
   1206   return name;
   1207 }
   1208 
   1209 const FilePath::CharType* SavePackage::ExtensionForMimeType(
   1210     const std::string& contents_mime_type) {
   1211   static const struct {
   1212     const FilePath::CharType *mime_type;
   1213     const FilePath::CharType *suggested_extension;
   1214   } extensions[] = {
   1215     { FILE_PATH_LITERAL("text/html"), kDefaultHtmlExtension },
   1216     { FILE_PATH_LITERAL("text/xml"), FILE_PATH_LITERAL("xml") },
   1217     { FILE_PATH_LITERAL("application/xhtml+xml"), FILE_PATH_LITERAL("xhtml") },
   1218     { FILE_PATH_LITERAL("text/plain"), FILE_PATH_LITERAL("txt") },
   1219     { FILE_PATH_LITERAL("text/css"), FILE_PATH_LITERAL("css") },
   1220   };
   1221 #if defined(OS_POSIX)
   1222   FilePath::StringType mime_type(contents_mime_type);
   1223 #elif defined(OS_WIN)
   1224   FilePath::StringType mime_type(UTF8ToWide(contents_mime_type));
   1225 #endif  // OS_WIN
   1226   for (uint32 i = 0; i < ARRAYSIZE_UNSAFE(extensions); ++i) {
   1227     if (mime_type == extensions[i].mime_type)
   1228       return extensions[i].suggested_extension;
   1229   }
   1230   return FILE_PATH_LITERAL("");
   1231 }
   1232 
   1233 
   1234 
   1235 // static.
   1236 // Check whether the preference has the preferred directory for saving file. If
   1237 // not, initialize it with default directory.
   1238 FilePath SavePackage::GetSaveDirPreference(PrefService* prefs) {
   1239   DCHECK(prefs);
   1240 
   1241   if (!prefs->FindPreference(prefs::kSaveFileDefaultDirectory)) {
   1242     DCHECK(prefs->FindPreference(prefs::kDownloadDefaultDirectory));
   1243     FilePath default_save_path = prefs->GetFilePath(
   1244         prefs::kDownloadDefaultDirectory);
   1245     prefs->RegisterFilePathPref(prefs::kSaveFileDefaultDirectory,
   1246                                 default_save_path);
   1247   }
   1248 
   1249   // Get the directory from preference.
   1250   FilePath save_file_path = prefs->GetFilePath(
   1251       prefs::kSaveFileDefaultDirectory);
   1252   DCHECK(!save_file_path.empty());
   1253 
   1254   return save_file_path;
   1255 }
   1256 
   1257 void SavePackage::GetSaveInfo() {
   1258   // Can't use tab_contents_ in the file thread, so get the data that we need
   1259   // before calling to it.
   1260   PrefService* prefs = tab_contents()->profile()->GetPrefs();
   1261   FilePath website_save_dir = GetSaveDirPreference(prefs);
   1262   FilePath download_save_dir = prefs->GetFilePath(
   1263       prefs::kDownloadDefaultDirectory);
   1264   std::string mime_type = tab_contents()->contents_mime_type();
   1265 
   1266   BrowserThread::PostTask(
   1267       BrowserThread::FILE, FROM_HERE,
   1268       NewRunnableMethod(this, &SavePackage::CreateDirectoryOnFileThread,
   1269           website_save_dir, download_save_dir, mime_type));
   1270 }
   1271 
   1272 void SavePackage::CreateDirectoryOnFileThread(
   1273     const FilePath& website_save_dir,
   1274     const FilePath& download_save_dir,
   1275     const std::string& mime_type) {
   1276   FilePath save_dir;
   1277   // If the default html/websites save folder doesn't exist...
   1278   if (!file_util::DirectoryExists(website_save_dir)) {
   1279     // If the default download dir doesn't exist, create it.
   1280     if (!file_util::DirectoryExists(download_save_dir))
   1281       file_util::CreateDirectory(download_save_dir);
   1282     save_dir = download_save_dir;
   1283   } else {
   1284     // If it does exist, use the default save dir param.
   1285     save_dir = website_save_dir;
   1286   }
   1287 
   1288   bool can_save_as_complete = CanSaveAsComplete(mime_type);
   1289   FilePath suggested_filename = GetSuggestedNameForSaveAs(can_save_as_complete,
   1290                                                           mime_type);
   1291   FilePath::StringType pure_file_name =
   1292       suggested_filename.RemoveExtension().BaseName().value();
   1293   FilePath::StringType file_name_ext = suggested_filename.Extension();
   1294 
   1295   // Need to make sure the suggested file name is not too long.
   1296   uint32 max_path = GetMaxPathLengthForDirectory(save_dir);
   1297 
   1298   if (GetSafePureFileName(save_dir, file_name_ext, max_path, &pure_file_name)) {
   1299     save_dir = save_dir.Append(pure_file_name + file_name_ext);
   1300   } else {
   1301     // Cannot create a shorter filename. This will cause the save as operation
   1302     // to fail unless the user pick a shorter name. Continuing even though it
   1303     // will fail because returning means no save as popup for the user, which
   1304     // is even more confusing. This case should be rare though.
   1305     save_dir = save_dir.Append(suggested_filename);
   1306   }
   1307 
   1308   BrowserThread::PostTask(
   1309       BrowserThread::UI, FROM_HERE,
   1310       NewRunnableMethod(this, &SavePackage::ContinueGetSaveInfo, save_dir,
   1311                         can_save_as_complete));
   1312 }
   1313 
   1314 void SavePackage::ContinueGetSaveInfo(const FilePath& suggested_path,
   1315                                       bool can_save_as_complete) {
   1316   // The TabContents which owns this SavePackage may have disappeared during
   1317   // the UI->FILE->UI thread hop of
   1318   // GetSaveInfo->CreateDirectoryOnFileThread->ContinueGetSaveInfo.
   1319   if (!tab_contents())
   1320     return;
   1321   DownloadPrefs* download_prefs =
   1322       tab_contents()->profile()->GetDownloadManager()->download_prefs();
   1323   int file_type_index =
   1324       SavePackageTypeToIndex(
   1325           static_cast<SavePackageType>(download_prefs->save_file_type()));
   1326 
   1327   SelectFileDialog::FileTypeInfo file_type_info;
   1328   FilePath::StringType default_extension;
   1329 
   1330   // If the contents can not be saved as complete-HTML, do not show the
   1331   // file filters.
   1332   if (can_save_as_complete) {
   1333     bool add_extra_extension = false;
   1334     FilePath::StringType extra_extension;
   1335     if (!suggested_path.Extension().empty() &&
   1336         suggested_path.Extension().compare(FILE_PATH_LITERAL("htm")) &&
   1337         suggested_path.Extension().compare(FILE_PATH_LITERAL("html"))) {
   1338       add_extra_extension = true;
   1339       extra_extension = suggested_path.Extension().substr(1);
   1340     }
   1341 
   1342     file_type_info.extensions.resize(2);
   1343     file_type_info.extensions[kSelectFileHtmlOnlyIndex - 1].push_back(
   1344         FILE_PATH_LITERAL("htm"));
   1345     file_type_info.extensions[kSelectFileHtmlOnlyIndex - 1].push_back(
   1346         FILE_PATH_LITERAL("html"));
   1347 
   1348     if (add_extra_extension) {
   1349       file_type_info.extensions[kSelectFileHtmlOnlyIndex - 1].push_back(
   1350           extra_extension);
   1351     }
   1352 
   1353     file_type_info.extension_description_overrides.push_back(
   1354         l10n_util::GetStringUTF16(kIndexToIDS[kSelectFileCompleteIndex - 1]));
   1355     file_type_info.extensions[kSelectFileCompleteIndex - 1].push_back(
   1356         FILE_PATH_LITERAL("htm"));
   1357     file_type_info.extensions[kSelectFileCompleteIndex - 1].push_back(
   1358         FILE_PATH_LITERAL("html"));
   1359 
   1360     if (add_extra_extension) {
   1361       file_type_info.extensions[kSelectFileCompleteIndex - 1].push_back(
   1362           extra_extension);
   1363     }
   1364 
   1365     file_type_info.extension_description_overrides.push_back(
   1366         l10n_util::GetStringUTF16(kIndexToIDS[kSelectFileCompleteIndex]));
   1367     file_type_info.include_all_files = false;
   1368     default_extension = kDefaultHtmlExtension;
   1369   } else {
   1370     file_type_info.extensions.resize(1);
   1371     file_type_info.extensions[kSelectFileHtmlOnlyIndex - 1].push_back(
   1372         suggested_path.Extension());
   1373 
   1374     if (!file_type_info.extensions[kSelectFileHtmlOnlyIndex - 1][0].empty()) {
   1375       // Drop the .
   1376       file_type_info.extensions[kSelectFileHtmlOnlyIndex - 1][0].erase(0, 1);
   1377     }
   1378 
   1379     file_type_info.include_all_files = true;
   1380     file_type_index = 1;
   1381   }
   1382 
   1383   if (g_should_prompt_for_filename) {
   1384     if (!select_file_dialog_.get())
   1385       select_file_dialog_ = SelectFileDialog::Create(this);
   1386     select_file_dialog_->SelectFile(SelectFileDialog::SELECT_SAVEAS_FILE,
   1387                                     string16(),
   1388                                     suggested_path,
   1389                                     &file_type_info,
   1390                                     file_type_index,
   1391                                     default_extension,
   1392                                     tab_contents(),
   1393                                     platform_util::GetTopLevel(
   1394                                         tab_contents()->GetNativeView()),
   1395                                     NULL);
   1396   } else {
   1397     // Just use 'suggested_path' instead of opening the dialog prompt.
   1398     ContinueSave(suggested_path, file_type_index);
   1399   }
   1400 }
   1401 
   1402 // Called after the save file dialog box returns.
   1403 void SavePackage::ContinueSave(const FilePath& final_name,
   1404                                int index) {
   1405   // Ensure the filename is safe.
   1406   saved_main_file_path_ = final_name;
   1407   download_util::GenerateSafeFileName(tab_contents()->contents_mime_type(),
   1408                                       &saved_main_file_path_);
   1409 
   1410   // The option index is not zero-based.
   1411   DCHECK(index >= kSelectFileHtmlOnlyIndex &&
   1412          index <= kSelectFileCompleteIndex);
   1413 
   1414   saved_main_directory_path_ = saved_main_file_path_.DirName();
   1415 
   1416   PrefService* prefs = tab_contents()->profile()->GetPrefs();
   1417   StringPrefMember save_file_path;
   1418   save_file_path.Init(prefs::kSaveFileDefaultDirectory, prefs, NULL);
   1419 #if defined(OS_POSIX)
   1420   std::string path_string = saved_main_directory_path_.value();
   1421 #elif defined(OS_WIN)
   1422   std::string path_string = WideToUTF8(saved_main_directory_path_.value());
   1423 #endif
   1424   // If user change the default saving directory, we will remember it just
   1425   // like IE and FireFox.
   1426   if (!tab_contents()->profile()->IsOffTheRecord() &&
   1427       save_file_path.GetValue() != path_string) {
   1428     save_file_path.SetValue(path_string);
   1429   }
   1430 
   1431   save_type_ = kIndexToSaveType[index];
   1432 
   1433   prefs->SetInteger(prefs::kSaveFileType, save_type_);
   1434 
   1435   if (save_type_ == SavePackage::SAVE_AS_COMPLETE_HTML) {
   1436     // Make new directory for saving complete file.
   1437     saved_main_directory_path_ = saved_main_directory_path_.Append(
   1438         saved_main_file_path_.RemoveExtension().BaseName().value() +
   1439         FILE_PATH_LITERAL("_files"));
   1440   }
   1441 
   1442   Init();
   1443 }
   1444 
   1445 // Static
   1446 bool SavePackage::IsSavableURL(const GURL& url) {
   1447   for (int i = 0; chrome::kSavableSchemes[i] != NULL; ++i) {
   1448     if (url.SchemeIs(chrome::kSavableSchemes[i])) {
   1449       return true;
   1450     }
   1451   }
   1452   return false;
   1453 }
   1454 
   1455 // Static
   1456 bool SavePackage::IsSavableContents(const std::string& contents_mime_type) {
   1457   // WebKit creates Document object when MIME type is application/xhtml+xml,
   1458   // so we also support this MIME type.
   1459   return contents_mime_type == "text/html" ||
   1460          contents_mime_type == "text/xml" ||
   1461          contents_mime_type == "application/xhtml+xml" ||
   1462          contents_mime_type == "text/plain" ||
   1463          contents_mime_type == "text/css" ||
   1464          net::IsSupportedJavascriptMimeType(contents_mime_type.c_str());
   1465 }
   1466 
   1467 // SelectFileDialog::Listener interface.
   1468 void SavePackage::FileSelected(const FilePath& path,
   1469                                int index, void* params) {
   1470   ContinueSave(path, index);
   1471 }
   1472 
   1473 void SavePackage::FileSelectionCanceled(void* params) {
   1474 }
   1475