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// Objects that handle file operations for saving files, on the file thread.
6//
7// The SaveFileManager owns a set of SaveFile objects, each of which connects
8// with a SaveItem object which belongs to one SavePackage and runs on the file
9// thread for saving data in order to avoid disk activity on either network IO
10// thread or the UI thread. It coordinates the notifications from the network
11// and UI.
12//
13// The SaveFileManager itself is a singleton object owned by the
14// ResourceDispatcherHost.
15//
16// The data sent to SaveFileManager have 2 sources, one is from
17// ResourceDispatcherHost, run in network IO thread, the all sub-resources
18// and save-only-HTML pages will be got from network IO. The second is from
19// render process, those html pages which are serialized from DOM will be
20// composed in render process and encoded to its original encoding, then sent
21// to UI loop in browser process, then UI loop will dispatch the data to
22// SaveFileManager on the file thread. SaveFileManager will directly
23// call SaveFile's method to persist data.
24//
25// A typical saving job operation involves multiple threads:
26//
27// Updating an in progress save file
28// io_thread
29//      |----> data from net   ---->|
30//                                  |
31//                                  |
32//      |----> data from    ---->|  |
33//      |      render process    |  |
34// ui_thread                     |  |
35//                      file_thread (writes to disk)
36//                              |----> stats ---->|
37//                                              ui_thread (feedback for user)
38//
39//
40// Cancel operations perform the inverse order when triggered by a user action:
41// ui_thread (user click)
42//    |----> cancel command ---->|
43//    |           |      file_thread (close file)
44//    |           |---------------------> cancel command ---->|
45//    |                                               io_thread (stops net IO
46// ui_thread (user close tab)                                    for saving)
47//    |----> cancel command ---->|
48//                            Render process(stop serializing DOM and sending
49//                                           data)
50//
51//
52// The SaveFileManager tracks saving requests, mapping from a save ID
53// (unique integer created in the IO thread) to the SavePackage for the
54// tab where the saving job was initiated. In the event of a tab closure
55// during saving, the SavePackage will notice the SaveFileManage to
56// cancel all SaveFile job.
57
58#ifndef CHROME_BROWSER_DOWNLOAD_SAVE_FILE_MANAGER_H__
59#define CHROME_BROWSER_DOWNLOAD_SAVE_FILE_MANAGER_H__
60#pragma once
61
62#include <string>
63
64#include "base/basictypes.h"
65#include "base/hash_tables.h"
66#include "base/memory/ref_counted.h"
67#include "chrome/browser/download/save_types.h"
68
69namespace net {
70class IOBuffer;
71}
72class FilePath;
73class GURL;
74class SaveFile;
75class SavePackage;
76class ResourceDispatcherHost;
77class Task;
78
79namespace net {
80class URLRequestContextGetter;
81}
82
83class SaveFileManager
84    : public base::RefCountedThreadSafe<SaveFileManager> {
85 public:
86  explicit SaveFileManager(ResourceDispatcherHost* rdh);
87
88  // Lifetime management.
89  void Shutdown();
90
91  // Called on the IO thread
92  int GetNextId();
93
94  // Save the specified URL. Called on the UI thread and forwarded to the
95  // ResourceDispatcherHost on the IO thread.
96  void SaveURL(const GURL& url,
97               const GURL& referrer,
98               int render_process_host_id,
99               int render_view_id,
100               SaveFileCreateInfo::SaveFileSource save_source,
101               const FilePath& file_full_path,
102               net::URLRequestContextGetter* request_context_getter,
103               SavePackage* save_package);
104
105  // Notifications sent from the IO thread and run on the file thread:
106  void StartSave(SaveFileCreateInfo* info);
107  void UpdateSaveProgress(int save_id, net::IOBuffer* data, int size);
108  void SaveFinished(int save_id,
109                    const GURL& save_url,
110                    int render_process_id,
111                    bool is_success);
112
113  // Notifications sent from the UI thread and run on the file thread.
114  // Cancel a SaveFile instance which has specified save id.
115  void CancelSave(int save_id);
116
117  // Called on the UI thread to remove a save package from SaveFileManager's
118  // tracking map.
119  void RemoveSaveFile(int save_id, const GURL& save_url,
120                      SavePackage* package);
121
122#if !defined(OS_MACOSX)
123  // Handler for shell operations sent from the UI to the file thread. Mac OS X
124  // requires opening downloads on the UI thread, so it does not use this
125  // method.
126  void OnShowSavedFileInShell(const FilePath full_path);
127#endif
128
129  // Helper function for deleting specified file.
130  void DeleteDirectoryOrFile(const FilePath& full_path, bool is_dir);
131
132  // Runs on file thread to save a file by copying from file system when
133  // original url is using file scheme.
134  void SaveLocalFile(const GURL& original_file_url,
135                     int save_id,
136                     int render_process_id);
137
138  // Renames all the successfully saved files.
139  // |final_names| points to a vector which contains pairs of save ids and
140  // final names of successfully saved files.
141  void RenameAllFiles(
142      const FinalNameList& final_names,
143      const FilePath& resource_dir,
144      int render_process_id,
145      int render_view_id,
146      int save_package_id);
147
148  // When the user cancels the saving, we need to remove all remaining saved
149  // files of this page saving job from save_file_map_.
150  void RemoveSavedFileFromFileMap(const SaveIDList & save_ids);
151
152 private:
153  friend class base::RefCountedThreadSafe<SaveFileManager>;
154
155  ~SaveFileManager();
156
157  // A cleanup helper that runs on the file thread.
158  void OnShutdown();
159
160  // Called only on UI thread to get the SavePackage for a tab's profile.
161  static SavePackage* GetSavePackageFromRenderIds(int render_process_id,
162                                                  int review_view_id);
163
164  // Register a starting request. Associate the save URL with a
165  // SavePackage for further matching.
166  void RegisterStartingRequest(const GURL& save_url,
167                               SavePackage* save_package);
168  // Unregister a start request according save URL, disassociate
169  // the save URL and SavePackage.
170  SavePackage* UnregisterStartingRequest(const GURL& save_url,
171                                         int tab_id);
172
173  // Look up the SavePackage according to save id.
174  SavePackage* LookupPackage(int save_id);
175
176  // Called only on the file thread.
177  // Look up one in-progress saving item according to save id.
178  SaveFile* LookupSaveFile(int save_id);
179
180  // Help function for sending notification of canceling specific request.
181  void SendCancelRequest(int save_id);
182
183  // Notifications sent from the file thread and run on the UI thread.
184
185  // Lookup the SaveManager for this TabContents' saving profile and inform it
186  // the saving job has been started.
187  void OnStartSave(const SaveFileCreateInfo* info);
188  // Update the SavePackage with the current state of a started saving job.
189  // If the SavePackage for this saving job is gone, cancel the request.
190  void OnUpdateSaveProgress(int save_id,
191                            int64 bytes_so_far,
192                            bool write_success);
193  // Update the SavePackage with the finish state, and remove the request
194  // tracking entries.
195  void OnSaveFinished(int save_id, int64 bytes_so_far, bool is_success);
196  // For those requests that do not have valid save id, use
197  // map:(url, SavePackage) to find the request and remove it.
198  void OnErrorFinished(const GURL& save_url, int tab_id);
199  // Notifies SavePackage that the whole page saving job is finished.
200  void OnFinishSavePageJob(int render_process_id,
201                           int render_view_id,
202                           int save_package_id);
203
204  // Notifications sent from the UI thread and run on the file thread.
205
206  // Deletes a specified file on the file thread.
207  void OnDeleteDirectoryOrFile(const FilePath& full_path, bool is_dir);
208
209  // Notifications sent from the UI thread and run on the IO thread
210
211  // Initiates a request for URL to be saved.
212  void OnSaveURL(const GURL& url,
213                 const GURL& referrer,
214                 int render_process_host_id,
215                 int render_view_id,
216                 net::URLRequestContextGetter* request_context_getter);
217  // Handler for a notification sent to the IO thread for generating save id.
218  void OnRequireSaveJobFromOtherSource(SaveFileCreateInfo* info);
219  // Call ResourceDispatcherHost's CancelRequest method to execute cancel
220  // action in the IO thread.
221  void ExecuteCancelSaveRequest(int render_process_id, int request_id);
222
223  // Unique ID for the next SaveFile object.
224  int next_id_;
225
226  // A map of all saving jobs by using save id.
227  typedef base::hash_map<int, SaveFile*> SaveFileMap;
228  SaveFileMap save_file_map_;
229
230  ResourceDispatcherHost* resource_dispatcher_host_;
231
232  // Tracks which SavePackage to send data to, called only on UI thread.
233  // SavePackageMap maps save IDs to their SavePackage.
234  typedef base::hash_map<int, SavePackage*> SavePackageMap;
235  SavePackageMap packages_;
236
237  // There is a gap between after calling SaveURL() and before calling
238  // StartSave(). In this gap, each request does not have save id for tracking.
239  // But sometimes users might want to stop saving job or ResourceDispatcherHost
240  // calls SaveFinished with save id -1 for network error. We name the requests
241  // as starting requests. For tracking those starting requests, we need to
242  // have some data structure.
243  // First we use a hashmap to map the request URL to SavePackage, then we
244  // use a hashmap to map the tab id (we actually use render_process_id) to the
245  // hashmap since it is possible to save same URL in different tab at
246  // same time.
247  typedef base::hash_map<std::string, SavePackage*> StartingRequestsMap;
248  typedef base::hash_map<int, StartingRequestsMap> TabToStartingRequestsMap;
249  TabToStartingRequestsMap tab_starting_requests_;
250
251  DISALLOW_COPY_AND_ASSIGN(SaveFileManager);
252};
253
254#endif  // CHROME_BROWSER_DOWNLOAD_SAVE_FILE_MANAGER_H__
255