1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4//
5// 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// ResourceDispatcherHostImpl.
15//
16// The data sent to SaveFileManager have 2 sources, one is from
17// ResourceDispatcherHostImpl, 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 contents)                               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 (unique
53// integer created in the IO thread) to the SavePackage for the contents where
54// the saving job was initiated. In the event of a contents closure during
55// saving, the SavePackage will notify the SaveFileManage to cancel all SaveFile
56// jobs.
57
58#ifndef CONTENT_BROWSER_DOWNLOAD_SAVE_FILE_MANAGER_H_
59#define CONTENT_BROWSER_DOWNLOAD_SAVE_FILE_MANAGER_H_
60
61#include <string>
62
63#include "base/basictypes.h"
64#include "base/containers/hash_tables.h"
65#include "base/memory/ref_counted.h"
66#include "content/browser/download/save_types.h"
67#include "content/common/content_export.h"
68
69class GURL;
70
71namespace base {
72class FilePath;
73}
74
75namespace net {
76class IOBuffer;
77}
78
79namespace content {
80class ResourceContext;
81class SaveFile;
82class SavePackage;
83struct Referrer;
84
85class SaveFileManager : public base::RefCountedThreadSafe<SaveFileManager> {
86 public:
87  SaveFileManager();
88
89  // Lifetime management.
90  CONTENT_EXPORT void Shutdown();
91
92  // Called on the IO thread. This generates unique IDs for
93  // SaveFileResourceHandler objects (there's one per file in a SavePackage).
94  // Note that this is different from the SavePackage's id.
95  int GetNextId();
96
97  // Save the specified URL. Called on the UI thread and forwarded to the
98  // ResourceDispatcherHostImpl on the IO thread.
99  void SaveURL(const GURL& url,
100               const Referrer& referrer,
101               int render_process_host_id,
102               int render_view_id,
103               SaveFileCreateInfo::SaveFileSource save_source,
104               const base::FilePath& file_full_path,
105               ResourceContext* context,
106               SavePackage* save_package);
107
108  // Notifications sent from the IO thread and run on the file thread:
109  void StartSave(SaveFileCreateInfo* info);
110  void UpdateSaveProgress(int save_id, net::IOBuffer* data, int size);
111  void SaveFinished(int save_id,
112                    const GURL& save_url,
113                    int render_process_id,
114                    bool is_success);
115
116  // Notifications sent from the UI thread and run on the file thread.
117  // Cancel a SaveFile instance which has specified save id.
118  void CancelSave(int save_id);
119
120  // Called on the UI thread to remove a save package from SaveFileManager's
121  // tracking map.
122  void RemoveSaveFile(int save_id, const GURL& save_url,
123                      SavePackage* package);
124
125  // Helper function for deleting specified file.
126  void DeleteDirectoryOrFile(const base::FilePath& full_path, bool is_dir);
127
128  // Runs on file thread to save a file by copying from file system when
129  // original url is using file scheme.
130  void SaveLocalFile(const GURL& original_file_url,
131                     int save_id,
132                     int render_process_id);
133
134  // Renames all the successfully saved files.
135  // |final_names| points to a vector which contains pairs of save ids and
136  // final names of successfully saved files.
137  void RenameAllFiles(
138      const FinalNameList& final_names,
139      const base::FilePath& resource_dir,
140      int render_process_id,
141      int render_view_id,
142      int save_package_id);
143
144  // When the user cancels the saving, we need to remove all remaining saved
145  // files of this page saving job from save_file_map_.
146  void RemoveSavedFileFromFileMap(const SaveIDList & save_ids);
147
148 private:
149  friend class base::RefCountedThreadSafe<SaveFileManager>;
150
151  ~SaveFileManager();
152
153  // A cleanup helper that runs on the file thread.
154  void OnShutdown();
155
156  // Called only on UI thread to get the SavePackage for a contents's browser
157  // context.
158  static SavePackage* GetSavePackageFromRenderIds(int render_process_id,
159                                                  int review_view_id);
160
161  // Register a starting request. Associate the save URL with a
162  // SavePackage for further matching.
163  void RegisterStartingRequest(const GURL& save_url,
164                               SavePackage* save_package);
165  // Unregister a start request according save URL, disassociate
166  // the save URL and SavePackage.
167  SavePackage* UnregisterStartingRequest(const GURL& save_url,
168                                         int contents_id);
169
170  // Look up the SavePackage according to save id.
171  SavePackage* LookupPackage(int save_id);
172
173  // Called only on the file thread.
174  // Look up one in-progress saving item according to save id.
175  SaveFile* LookupSaveFile(int save_id);
176
177  // Help function for sending notification of canceling specific request.
178  void SendCancelRequest(int save_id);
179
180  // Notifications sent from the file thread and run on the UI thread.
181
182  // Lookup the SaveManager for this WebContents' saving browser context and
183  // inform it the saving job has been started.
184  void OnStartSave(const SaveFileCreateInfo* info);
185  // Update the SavePackage with the current state of a started saving job.
186  // If the SavePackage for this saving job is gone, cancel the request.
187  void OnUpdateSaveProgress(int save_id,
188                            int64 bytes_so_far,
189                            bool write_success);
190  // Update the SavePackage with the finish state, and remove the request
191  // tracking entries.
192  void OnSaveFinished(int save_id, int64 bytes_so_far, bool is_success);
193  // For those requests that do not have valid save id, use
194  // map:(url, SavePackage) to find the request and remove it.
195  void OnErrorFinished(const GURL& save_url, int contents_id);
196  // Notifies SavePackage that the whole page saving job is finished.
197  void OnFinishSavePageJob(int render_process_id,
198                           int render_view_id,
199                           int save_package_id);
200
201  // Notifications sent from the UI thread and run on the file thread.
202
203  // Deletes a specified file on the file thread.
204  void OnDeleteDirectoryOrFile(const base::FilePath& full_path, bool is_dir);
205
206  // Notifications sent from the UI thread and run on the IO thread
207
208  // Initiates a request for URL to be saved.
209  void OnSaveURL(const GURL& url,
210                 const Referrer& referrer,
211                 int render_process_host_id,
212                 int render_view_id,
213                 ResourceContext* context);
214  // Handler for a notification sent to the IO thread for generating save id.
215  void OnRequireSaveJobFromOtherSource(SaveFileCreateInfo* info);
216  // Call ResourceDispatcherHostImpl's CancelRequest method to execute cancel
217  // action in the IO thread.
218  void ExecuteCancelSaveRequest(int render_process_id, int request_id);
219
220  // Unique ID for the next SaveFile object.
221  int next_id_;
222
223  // A map of all saving jobs by using save id.
224  typedef base::hash_map<int, SaveFile*> SaveFileMap;
225  SaveFileMap save_file_map_;
226
227  // Tracks which SavePackage to send data to, called only on UI thread.
228  // SavePackageMap maps save IDs to their SavePackage.
229  typedef base::hash_map<int, SavePackage*> SavePackageMap;
230  SavePackageMap packages_;
231
232  // There is a gap between after calling SaveURL() and before calling
233  // StartSave(). In this gap, each request does not have save id for tracking.
234  // But sometimes users might want to stop saving job or ResourceDispatcherHost
235  // calls SaveFinished with save id -1 for network error. We name the requests
236  // as starting requests. For tracking those starting requests, we need to
237  // have some data structure.
238  // First we use a hashmap to map the request URL to SavePackage, then we use a
239  // hashmap to map the contents id (we actually use render_process_id) to the
240  // hashmap since it is possible to save the same URL in different contents at
241  // same time.
242  typedef base::hash_map<std::string, SavePackage*> StartingRequestsMap;
243  typedef base::hash_map<int, StartingRequestsMap>
244      ContentsToStartingRequestsMap;
245  ContentsToStartingRequestsMap contents_starting_requests_;
246
247  DISALLOW_COPY_AND_ASSIGN(SaveFileManager);
248};
249
250}  // namespace content
251
252#endif  // CONTENT_BROWSER_DOWNLOAD_SAVE_FILE_MANAGER_H_
253