| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ios/chrome/browser/reading_list/url_downloader.h" | 5 #include "ios/chrome/browser/reading_list/url_downloader.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/files/file_path.h" | 10 #include "base/files/file_path.h" |
| 11 #include "base/files/file_util.h" | 11 #include "base/files/file_util.h" |
| 12 #include "base/md5.h" | |
| 13 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
| 14 #include "base/path_service.h" | 13 #include "base/path_service.h" |
| 15 #include "ios/chrome/browser/chrome_paths.h" | 14 #include "ios/chrome/browser/chrome_paths.h" |
| 16 #include "ios/chrome/browser/dom_distiller/distiller_viewer.h" | 15 #include "ios/chrome/browser/dom_distiller/distiller_viewer.h" |
| 16 #include "ios/chrome/browser/reading_list/offline_url_utils.h" |
| 17 #include "ios/web/public/web_thread.h" | 17 #include "ios/web/public/web_thread.h" |
| 18 #include "net/base/escape.h" | 18 #include "net/base/escape.h" |
| 19 #include "url/gurl.h" | 19 #include "url/gurl.h" |
| 20 | 20 |
| 21 const char kReadingListOfflineDirectory[] = "Offline"; | |
| 22 | |
| 23 // URLDownloader | 21 // URLDownloader |
| 24 | 22 |
| 25 URLDownloader::URLDownloader( | 23 URLDownloader::URLDownloader( |
| 26 dom_distiller::DomDistillerService* distiller_service, | 24 dom_distiller::DomDistillerService* distiller_service, |
| 27 PrefService* prefs, | 25 PrefService* prefs, |
| 28 base::FilePath chrome_profile_path, | 26 base::FilePath chrome_profile_path, |
| 29 const DownloadCompletion& download_completion, | 27 const DownloadCompletion& download_completion, |
| 30 const SuccessCompletion& delete_completion) | 28 const SuccessCompletion& delete_completion) |
| 31 : distiller_service_(distiller_service), | 29 : distiller_service_(distiller_service), |
| 32 pref_service_(prefs), | 30 pref_service_(prefs), |
| 33 download_completion_(download_completion), | 31 download_completion_(download_completion), |
| 34 delete_completion_(delete_completion), | 32 delete_completion_(delete_completion), |
| 35 working_(false), | 33 working_(false), |
| 36 base_directory_(chrome_profile_path), | 34 base_directory_(chrome_profile_path), |
| 37 task_tracker_() {} | 35 task_tracker_() {} |
| 38 | 36 |
| 39 URLDownloader::~URLDownloader() { | 37 URLDownloader::~URLDownloader() { |
| 40 task_tracker_.TryCancelAll(); | 38 task_tracker_.TryCancelAll(); |
| 41 } | 39 } |
| 42 | 40 |
| 43 void URLDownloader::OfflineURLExists(const GURL& url, | 41 void URLDownloader::OfflineURLExists(const GURL& url, |
| 44 base::Callback<void(bool)> callback) { | 42 base::Callback<void(bool)> callback) { |
| 45 task_tracker_.PostTaskAndReplyWithResult( | 43 task_tracker_.PostTaskAndReplyWithResult( |
| 46 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE).get(), | 44 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE).get(), |
| 47 FROM_HERE, base::Bind(&base::PathExists, OfflinePageAbsolutePath(url)), | 45 FROM_HERE, |
| 46 base::Bind(&base::PathExists, |
| 47 reading_list::OfflinePageAbsolutePath(base_directory_, url)), |
| 48 callback); | 48 callback); |
| 49 } | 49 } |
| 50 | 50 |
| 51 void URLDownloader::RemoveOfflineURL(const GURL& url) { | 51 void URLDownloader::RemoveOfflineURL(const GURL& url) { |
| 52 // Remove all download tasks for this url as it would be pointless work. | 52 // Remove all download tasks for this url as it would be pointless work. |
| 53 tasks_.erase( | 53 tasks_.erase( |
| 54 std::remove(tasks_.begin(), tasks_.end(), std::make_pair(DOWNLOAD, url)), | 54 std::remove(tasks_.begin(), tasks_.end(), std::make_pair(DOWNLOAD, url)), |
| 55 tasks_.end()); | 55 tasks_.end()); |
| 56 tasks_.push_back(std::make_pair(DELETE, url)); | 56 tasks_.push_back(std::make_pair(DELETE, url)); |
| 57 HandleNextTask(); | 57 HandleNextTask(); |
| 58 } | 58 } |
| 59 | 59 |
| 60 void URLDownloader::DownloadOfflineURL(const GURL& url) { | 60 void URLDownloader::DownloadOfflineURL(const GURL& url) { |
| 61 if (std::find(tasks_.begin(), tasks_.end(), std::make_pair(DOWNLOAD, url)) == | 61 if (std::find(tasks_.begin(), tasks_.end(), std::make_pair(DOWNLOAD, url)) == |
| 62 tasks_.end()) { | 62 tasks_.end()) { |
| 63 tasks_.push_back(std::make_pair(DOWNLOAD, url)); | 63 tasks_.push_back(std::make_pair(DOWNLOAD, url)); |
| 64 HandleNextTask(); | 64 HandleNextTask(); |
| 65 } | 65 } |
| 66 } | 66 } |
| 67 | 67 |
| 68 void URLDownloader::DownloadCompletionHandler(const GURL& url, | 68 void URLDownloader::DownloadCompletionHandler(const GURL& url, |
| 69 const std::string& title, | 69 const std::string& title, |
| 70 SuccessState success) { | 70 SuccessState success) { |
| 71 DCHECK(working_); | 71 DCHECK(working_); |
| 72 | 72 |
| 73 auto post_delete = base::Bind( | 73 auto post_delete = base::Bind( |
| 74 [](URLDownloader* _this, const GURL& url, const std::string& title, | 74 [](URLDownloader* _this, const GURL& url, const std::string& title, |
| 75 SuccessState success) { | 75 SuccessState success) { |
| 76 _this->download_completion_.Run(url, success, | 76 _this->download_completion_.Run( |
| 77 _this->OfflinePagePath(url), title); | 77 url, success, reading_list::OfflinePagePath(url), title); |
| 78 _this->distiller_.reset(); | 78 _this->distiller_.reset(); |
| 79 _this->working_ = false; | 79 _this->working_ = false; |
| 80 _this->HandleNextTask(); | 80 _this->HandleNextTask(); |
| 81 }, | 81 }, |
| 82 base::Unretained(this), url, title, success); | 82 base::Unretained(this), url, title, success); |
| 83 | 83 |
| 84 // If downloading failed, clean up any partial download. | 84 // If downloading failed, clean up any partial download. |
| 85 if (success == ERROR_RETRY || success == ERROR_PERMANENT) { | 85 if (success == ERROR_RETRY || success == ERROR_PERMANENT) { |
| 86 task_tracker_.PostTaskAndReply( | 86 task_tracker_.PostTaskAndReply( |
| 87 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE).get(), | 87 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE).get(), |
| 88 FROM_HERE, base::Bind( | 88 FROM_HERE, base::Bind( |
| 89 [](const base::FilePath& offline_directory_path) { | 89 [](const base::FilePath& offline_directory_path) { |
| 90 base::DeleteFile(offline_directory_path, true); | 90 base::DeleteFile(offline_directory_path, true); |
| 91 }, | 91 }, |
| 92 OfflineURLDirectoryAbsolutePath(url)), | 92 reading_list::OfflineURLDirectoryAbsolutePath( |
| 93 base_directory_, url)), |
| 93 post_delete); | 94 post_delete); |
| 94 } else { | 95 } else { |
| 95 post_delete.Run(); | 96 post_delete.Run(); |
| 96 } | 97 } |
| 97 } | 98 } |
| 98 | 99 |
| 99 void URLDownloader::DeleteCompletionHandler(const GURL& url, bool success) { | 100 void URLDownloader::DeleteCompletionHandler(const GURL& url, bool success) { |
| 100 DCHECK(working_); | 101 DCHECK(working_); |
| 101 delete_completion_.Run(url, success); | 102 delete_completion_.Run(url, success); |
| 102 working_ = false; | 103 working_ = false; |
| 103 HandleNextTask(); | 104 HandleNextTask(); |
| 104 } | 105 } |
| 105 | 106 |
| 106 void URLDownloader::HandleNextTask() { | 107 void URLDownloader::HandleNextTask() { |
| 107 if (working_ || tasks_.empty()) { | 108 if (working_ || tasks_.empty()) { |
| 108 return; | 109 return; |
| 109 } | 110 } |
| 110 working_ = true; | 111 working_ = true; |
| 111 | 112 |
| 112 Task task = tasks_.front(); | 113 Task task = tasks_.front(); |
| 113 tasks_.pop_front(); | 114 tasks_.pop_front(); |
| 114 GURL url = task.second; | 115 GURL url = task.second; |
| 115 | 116 |
| 116 if (task.first == DELETE) { | 117 if (task.first == DELETE) { |
| 117 task_tracker_.PostTaskAndReplyWithResult( | 118 task_tracker_.PostTaskAndReplyWithResult( |
| 118 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE).get(), | 119 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE).get(), |
| 119 FROM_HERE, base::Bind(&base::DeleteFile, | 120 FROM_HERE, base::Bind(&base::DeleteFile, |
| 120 OfflineURLDirectoryAbsolutePath(url), true), | 121 reading_list::OfflineURLDirectoryAbsolutePath( |
| 122 base_directory_, url), |
| 123 true), |
| 121 base::Bind(&URLDownloader::DeleteCompletionHandler, | 124 base::Bind(&URLDownloader::DeleteCompletionHandler, |
| 122 base::Unretained(this), url)); | 125 base::Unretained(this), url)); |
| 123 } else if (task.first == DOWNLOAD) { | 126 } else if (task.first == DOWNLOAD) { |
| 124 DCHECK(!distiller_); | 127 DCHECK(!distiller_); |
| 125 OfflineURLExists(url, base::Bind(&URLDownloader::DownloadURL, | 128 OfflineURLExists(url, base::Bind(&URLDownloader::DownloadURL, |
| 126 base::Unretained(this), url)); | 129 base::Unretained(this), url)); |
| 127 } | 130 } |
| 128 } | 131 } |
| 129 | 132 |
| 130 void URLDownloader::DownloadURL(GURL url, bool offline_url_exists) { | 133 void URLDownloader::DownloadURL(GURL url, bool offline_url_exists) { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 166 images, | 169 images, |
| 167 const std::string& html) { | 170 const std::string& html) { |
| 168 if (CreateOfflineURLDirectory(url)) { | 171 if (CreateOfflineURLDirectory(url)) { |
| 169 return SaveHTMLForURL(SaveAndReplaceImagesInHTML(url, html, images), url) | 172 return SaveHTMLForURL(SaveAndReplaceImagesInHTML(url, html, images), url) |
| 170 ? DOWNLOAD_SUCCESS | 173 ? DOWNLOAD_SUCCESS |
| 171 : ERROR_PERMANENT; | 174 : ERROR_PERMANENT; |
| 172 } | 175 } |
| 173 return ERROR_PERMANENT; | 176 return ERROR_PERMANENT; |
| 174 } | 177 } |
| 175 | 178 |
| 176 base::FilePath URLDownloader::OfflineRootDirectoryPath() { | |
| 177 return base_directory_.Append( | |
| 178 FILE_PATH_LITERAL(kReadingListOfflineDirectory)); | |
| 179 } | |
| 180 | |
| 181 std::string URLDownloader::OfflineURLDirectoryID(const GURL& url) { | |
| 182 return base::MD5String(url.spec()); | |
| 183 } | |
| 184 | |
| 185 base::FilePath URLDownloader::OfflinePagePath(const GURL& url) { | |
| 186 base::FilePath directory(OfflineURLDirectoryID(url)); | |
| 187 return directory.Append(FILE_PATH_LITERAL("page.html")); | |
| 188 } | |
| 189 | |
| 190 base::FilePath URLDownloader::OfflineURLDirectoryAbsolutePath(const GURL& url) { | |
| 191 return OfflineRootDirectoryPath().Append(OfflineURLDirectoryID(url)); | |
| 192 } | |
| 193 | |
| 194 base::FilePath URLDownloader::OfflinePageAbsolutePath(const GURL& url) { | |
| 195 return OfflineRootDirectoryPath().Append(OfflinePagePath(url)); | |
| 196 } | |
| 197 | |
| 198 bool URLDownloader::CreateOfflineURLDirectory(const GURL& url) { | 179 bool URLDownloader::CreateOfflineURLDirectory(const GURL& url) { |
| 199 base::FilePath path = OfflineURLDirectoryAbsolutePath(url); | 180 base::FilePath path = |
| 181 reading_list::OfflineURLDirectoryAbsolutePath(base_directory_, url); |
| 200 if (!DirectoryExists(path)) { | 182 if (!DirectoryExists(path)) { |
| 201 return CreateDirectoryAndGetError(path, nil); | 183 return CreateDirectoryAndGetError(path, nil); |
| 202 } | 184 } |
| 203 return true; | 185 return true; |
| 204 } | 186 } |
| 205 | 187 |
| 206 bool URLDownloader::SaveImage(const GURL& url, | 188 bool URLDownloader::SaveImage(const GURL& url, |
| 207 const GURL& image_url, | 189 const GURL& image_url, |
| 208 const std::string& data, | 190 const std::string& data, |
| 209 std::string* image_name) { | 191 std::string* image_name) { |
| 210 std::string image_hash = base::MD5String(image_url.spec()); | 192 std::string image_hash = base::MD5String(image_url.spec()); |
| 211 *image_name = image_hash; | 193 *image_name = image_hash; |
| 212 base::FilePath path = OfflineURLDirectoryAbsolutePath(url).Append(image_hash); | 194 base::FilePath path = |
| 195 reading_list::OfflineURLDirectoryAbsolutePath(base_directory_, url) |
| 196 .Append(image_hash); |
| 213 if (!base::PathExists(path)) { | 197 if (!base::PathExists(path)) { |
| 214 return base::WriteFile(path, data.c_str(), data.length()) > 0; | 198 return base::WriteFile(path, data.c_str(), data.length()) > 0; |
| 215 } | 199 } |
| 216 return true; | 200 return true; |
| 217 } | 201 } |
| 218 | 202 |
| 219 std::string URLDownloader::SaveAndReplaceImagesInHTML( | 203 std::string URLDownloader::SaveAndReplaceImagesInHTML( |
| 220 const GURL& url, | 204 const GURL& url, |
| 221 const std::string& html, | 205 const std::string& html, |
| 222 const std::vector<dom_distiller::DistillerViewerInterface::ImageInfo>& | 206 const std::vector<dom_distiller::DistillerViewerInterface::ImageInfo>& |
| (...skipping 13 matching lines...) Expand all Loading... |
| 236 pos = mutable_html.find(image_url, pos + local_image_name.size()); | 220 pos = mutable_html.find(image_url, pos + local_image_name.size()); |
| 237 } | 221 } |
| 238 } | 222 } |
| 239 return mutable_html; | 223 return mutable_html; |
| 240 } | 224 } |
| 241 | 225 |
| 242 bool URLDownloader::SaveHTMLForURL(std::string html, const GURL& url) { | 226 bool URLDownloader::SaveHTMLForURL(std::string html, const GURL& url) { |
| 243 if (html.empty()) { | 227 if (html.empty()) { |
| 244 return false; | 228 return false; |
| 245 } | 229 } |
| 246 base::FilePath path = OfflinePageAbsolutePath(url); | 230 base::FilePath path = |
| 231 reading_list::OfflinePageAbsolutePath(base_directory_, url); |
| 247 return base::WriteFile(path, html.c_str(), html.length()) > 0; | 232 return base::WriteFile(path, html.c_str(), html.length()) > 0; |
| 248 } | 233 } |
| OLD | NEW |