Chromium Code Reviews| 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" | 12 #include "base/md5.h" |
| 13 #include "base/memory/ptr_util.h" | 13 #include "base/memory/ptr_util.h" |
| 14 #include "base/path_service.h" | 14 #include "base/path_service.h" |
| 15 #include "ios/chrome/browser/chrome_paths.h" | 15 #include "ios/chrome/browser/chrome_paths.h" |
| 16 #include "ios/chrome/browser/dom_distiller/distiller_viewer.h" | 16 #include "ios/chrome/browser/dom_distiller/distiller_viewer.h" |
| 17 #include "ios/web/public/web_thread.h" | 17 #include "ios/web/public/web_thread.h" |
| 18 #include "url/gurl.h" | 18 #include "url/gurl.h" |
| 19 | 19 |
| 20 namespace { | 20 namespace { |
| 21 char const kOfflineDirectory[] = "Offline"; | 21 char const kOfflineDirectory[] = "Offline"; |
| 22 | |
| 23 // TODO(crbug.com/629771): Handle errors & retrying of failed saves, including | |
| 24 // distillation failure. | |
| 25 | |
| 26 } // namespace | 22 } // namespace |
| 27 | 23 |
| 28 // URLDownloader | 24 // URLDownloader |
| 29 | 25 |
| 30 URLDownloader::URLDownloader( | 26 URLDownloader::URLDownloader( |
| 31 dom_distiller::DomDistillerService* distiller_service, | 27 dom_distiller::DomDistillerService* distiller_service, |
| 32 PrefService* prefs, | 28 PrefService* prefs, |
| 33 base::FilePath chrome_profile_path, | 29 base::FilePath chrome_profile_path, |
| 34 const SuccessCompletion& download_completion, | 30 const DownloadCompletion& download_completion, |
| 35 const SuccessCompletion& delete_completion) | 31 const SuccessCompletion& delete_completion) |
| 36 : distiller_service_(distiller_service), | 32 : distiller_service_(distiller_service), |
| 37 pref_service_(prefs), | 33 pref_service_(prefs), |
| 38 download_completion_(download_completion), | 34 download_completion_(download_completion), |
| 39 delete_completion_(delete_completion), | 35 delete_completion_(delete_completion), |
| 40 working_(false), | 36 working_(false), |
| 41 base_directory_(chrome_profile_path), | 37 base_directory_(chrome_profile_path), |
| 42 task_tracker_() {} | 38 task_tracker_() {} |
| 43 | 39 |
| 44 URLDownloader::~URLDownloader() { | 40 URLDownloader::~URLDownloader() { |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 63 } | 59 } |
| 64 | 60 |
| 65 void URLDownloader::DownloadOfflineURL(const GURL& url) { | 61 void URLDownloader::DownloadOfflineURL(const GURL& url) { |
| 66 if (std::find(tasks_.begin(), tasks_.end(), std::make_pair(DOWNLOAD, url)) == | 62 if (std::find(tasks_.begin(), tasks_.end(), std::make_pair(DOWNLOAD, url)) == |
| 67 tasks_.end()) { | 63 tasks_.end()) { |
| 68 tasks_.push_back(std::make_pair(DOWNLOAD, url)); | 64 tasks_.push_back(std::make_pair(DOWNLOAD, url)); |
| 69 HandleNextTask(); | 65 HandleNextTask(); |
| 70 } | 66 } |
| 71 } | 67 } |
| 72 | 68 |
| 73 void URLDownloader::DownloadCompletionHandler(const GURL& url, bool success) { | 69 void URLDownloader::DownloadCompletionHandler(const GURL& url, |
| 70 std::string title, | |
| 71 SuccessState success) { | |
| 74 DCHECK(working_); | 72 DCHECK(working_); |
| 75 download_completion_.Run(url, success); | 73 |
| 76 distiller_.reset(); | 74 auto postDelete = base::Bind( |
|
sdefresne
2016/09/09 09:58:57
style: variable should name should use name_with_u
lody
2016/09/09 12:17:53
Done.
| |
| 77 working_ = false; | 75 [](URLDownloader* _this, const GURL& url, std::string title, |
| 78 HandleNextTask(); | 76 SuccessState success) { |
| 77 _this->download_completion_.Run( | |
| 78 url, success, | |
| 79 GURL("file://" + _this->OfflineURLPagePath(url).value()), title); | |
|
sdefresne
2016/09/09 09:58:57
nit: maybe use url::kFileScheme instead of "file"
lody
2016/09/09 12:17:53
Done.
| |
| 80 _this->distiller_.reset(); | |
| 81 _this->working_ = false; | |
| 82 _this->HandleNextTask(); | |
| 83 }, | |
| 84 base::Unretained(this), url, title, success); | |
|
noyau (Ping after 24h)
2016/09/09 09:39:58
I'm always worried when I see a base::Unretained.
sdefresne
2016/09/09 09:58:57
+1
lody
2016/09/09 12:17:53
That's what the task_tracker_ is for. Already had
noyau (Ping after 24h)
2016/09/09 12:45:22
Ah, yes, I remember now. Sorry, should have checke
| |
| 85 | |
| 86 // If downloading failed, clean up any partial download. | |
| 87 if (success == ERROR_RETRY || success == ERROR_PERMANENT) { | |
| 88 task_tracker_.PostTaskAndReply( | |
| 89 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE).get(), | |
| 90 FROM_HERE, base::Bind( | |
| 91 [](base::FilePath offlineDirectoryPath) { | |
|
sdefresne
2016/09/09 09:58:57
style: variable should name should use name_with_u
lody
2016/09/09 12:17:53
Done.
| |
| 92 base::DeleteFile(offlineDirectoryPath, true); | |
| 93 }, | |
| 94 OfflineURLDirectoryPath(url)), | |
| 95 postDelete); | |
| 96 } else { | |
| 97 postDelete.Run(); | |
| 98 } | |
| 79 } | 99 } |
| 80 | 100 |
| 81 void URLDownloader::DeleteCompletionHandler(const GURL& url, bool success) { | 101 void URLDownloader::DeleteCompletionHandler(const GURL& url, bool success) { |
| 82 DCHECK(working_); | 102 DCHECK(working_); |
| 83 delete_completion_.Run(url, success); | 103 delete_completion_.Run(url, success); |
| 84 working_ = false; | 104 working_ = false; |
| 85 HandleNextTask(); | 105 HandleNextTask(); |
| 86 } | 106 } |
| 87 | 107 |
| 88 void URLDownloader::HandleNextTask() { | 108 void URLDownloader::HandleNextTask() { |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 104 base::Unretained(this), url)); | 124 base::Unretained(this), url)); |
| 105 } else if (task.first == DOWNLOAD) { | 125 } else if (task.first == DOWNLOAD) { |
| 106 DCHECK(!distiller_); | 126 DCHECK(!distiller_); |
| 107 OfflineURLExists(url, base::Bind(&URLDownloader::DownloadURL, | 127 OfflineURLExists(url, base::Bind(&URLDownloader::DownloadURL, |
| 108 base::Unretained(this), url)); | 128 base::Unretained(this), url)); |
| 109 } | 129 } |
| 110 } | 130 } |
| 111 | 131 |
| 112 void URLDownloader::DownloadURL(GURL url, bool offlineURLExists) { | 132 void URLDownloader::DownloadURL(GURL url, bool offlineURLExists) { |
| 113 if (offlineURLExists) { | 133 if (offlineURLExists) { |
| 114 DownloadCompletionHandler(url, false); | 134 DownloadCompletionHandler(url, "", DOWNLOAD_EXISTS); |
| 115 return; | 135 return; |
| 116 } | 136 } |
| 117 | 137 |
| 118 distiller_.reset(new dom_distiller::DistillerViewer( | 138 distiller_.reset(new dom_distiller::DistillerViewer( |
| 119 distiller_service_, pref_service_, url, | 139 distiller_service_, pref_service_, url, |
| 120 base::Bind(&URLDownloader::DistillerCallback, base::Unretained(this)))); | 140 base::Bind(&URLDownloader::DistillerCallback, base::Unretained(this)))); |
| 121 } | 141 } |
| 122 | 142 |
| 123 void URLDownloader::DistillerCallback( | 143 void URLDownloader::DistillerCallback( |
| 124 const GURL& pageURL, | 144 const GURL& pageURL, |
| 125 const std::string& html, | 145 const std::string& html, |
| 126 const std::vector<dom_distiller::DistillerViewerInterface::ImageInfo>& | 146 const std::vector<dom_distiller::DistillerViewerInterface::ImageInfo>& |
| 127 images) { | 147 images, |
| 148 const std::string& title) { | |
| 128 std::vector<dom_distiller::DistillerViewer::ImageInfo> imagesBlock = images; | 149 std::vector<dom_distiller::DistillerViewer::ImageInfo> imagesBlock = images; |
| 129 std::string blockHTML = html; | 150 std::string blockHTML = html; |
| 151 if (html.empty()) { | |
| 152 // TODO identify retry from permanent errors | |
| 153 DownloadCompletionHandler(pageURL, "", ERROR_PERMANENT); | |
| 154 return; | |
| 155 } | |
| 130 task_tracker_.PostTaskAndReplyWithResult( | 156 task_tracker_.PostTaskAndReplyWithResult( |
| 131 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE).get(), | 157 web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE).get(), |
| 132 FROM_HERE, | 158 FROM_HERE, |
| 133 base::Bind(&URLDownloader::SaveDistilledHTML, base::Unretained(this), | 159 base::Bind(&URLDownloader::SaveDistilledHTML, base::Unretained(this), |
| 134 pageURL, imagesBlock, blockHTML), | 160 pageURL, imagesBlock, blockHTML), |
| 135 base::Bind(&URLDownloader::DownloadCompletionHandler, | 161 base::Bind(&URLDownloader::DownloadCompletionHandler, |
| 136 base::Unretained(this), pageURL)); | 162 base::Unretained(this), pageURL, title)); |
| 137 } | 163 } |
| 138 | 164 |
| 139 bool URLDownloader::SaveDistilledHTML( | 165 URLDownloader::SuccessState URLDownloader::SaveDistilledHTML( |
| 140 const GURL& url, | 166 const GURL& url, |
| 141 std::vector<dom_distiller::DistillerViewerInterface::ImageInfo> images, | 167 std::vector<dom_distiller::DistillerViewerInterface::ImageInfo> images, |
| 142 std::string html) { | 168 std::string html) { |
| 143 if (CreateOfflineURLDirectory(url)) { | 169 if (CreateOfflineURLDirectory(url)) { |
| 144 return SaveHTMLForURL(SaveAndReplaceImagesInHTML(url, html, images), url); | 170 return SaveHTMLForURL(SaveAndReplaceImagesInHTML(url, html, images), url) |
| 171 ? DOWNLOAD_SUCCESS | |
| 172 : ERROR_PERMANENT; | |
| 145 } | 173 } |
| 146 return false; | 174 return ERROR_PERMANENT; |
| 147 } | 175 } |
| 148 | 176 |
| 149 base::FilePath URLDownloader::OfflineDirectoryPath() { | 177 base::FilePath URLDownloader::OfflineDirectoryPath() { |
| 150 return base_directory_.Append(kOfflineDirectory); | 178 return base_directory_.Append(kOfflineDirectory); |
| 151 } | 179 } |
| 152 | 180 |
| 153 base::FilePath URLDownloader::OfflineURLDirectoryPath(const GURL& url) { | 181 base::FilePath URLDownloader::OfflineURLDirectoryPath(const GURL& url) { |
| 154 std::string hash = base::MD5String(url.spec()); | 182 std::string hash = base::MD5String(url.spec()); |
| 155 return OfflineDirectoryPath().Append(hash); | 183 return OfflineDirectoryPath().Append(hash); |
| 156 } | 184 } |
| 157 | 185 |
| 158 base::FilePath URLDownloader::OfflineURLPagePath(const GURL& url) { | 186 base::FilePath URLDownloader::OfflineURLPagePath(const GURL& url) { |
| 159 return OfflineURLDirectoryPath(url).Append("page.html"); | 187 return OfflineURLDirectoryPath(url).Append("page.html"); |
| 160 } | 188 } |
| 161 | 189 |
| 162 bool URLDownloader::CreateOfflineURLDirectory(const GURL& url) { | 190 bool URLDownloader::CreateOfflineURLDirectory(const GURL& url) { |
| 163 base::FilePath path = OfflineURLDirectoryPath(url); | 191 base::FilePath path = OfflineURLDirectoryPath(url); |
| 164 if (!DirectoryExists(path)) { | 192 if (!DirectoryExists(path)) { |
| 165 return CreateDirectoryAndGetError(path, nil); | 193 return CreateDirectoryAndGetError(path, nil); |
| 166 } | 194 } |
| 167 return true; | 195 return true; |
| 168 } | 196 } |
| 169 | 197 |
| 170 std::string URLDownloader::SaveImage(const GURL& url, | 198 bool URLDownloader::SaveImage(const GURL& url, |
| 171 const GURL& imageURL, | 199 const GURL& imageURL, |
| 172 const std::string& data) { | 200 const std::string& data, |
| 173 base::FilePath path = | 201 base::FilePath& path) { |
| 174 OfflineURLDirectoryPath(url).Append(base::MD5String(imageURL.spec())); | 202 path = OfflineURLDirectoryPath(url).Append(base::MD5String(imageURL.spec())); |
| 175 if (!base::PathExists(path)) { | 203 if (!base::PathExists(path)) { |
| 176 base::WriteFile(path, data.c_str(), data.length()); | 204 return base::WriteFile(path, data.c_str(), data.length()) > 0; |
| 177 } | 205 } |
| 178 return path.AsUTF8Unsafe(); | 206 return true; |
| 179 } | 207 } |
| 180 | 208 |
| 181 // TODO(crbug.com/625621) DomDistiller doesn't correctly handle srcset | |
| 182 // attributes in img tags. | |
| 183 std::string URLDownloader::SaveAndReplaceImagesInHTML( | 209 std::string URLDownloader::SaveAndReplaceImagesInHTML( |
| 184 const GURL& url, | 210 const GURL& url, |
| 185 const std::string& html, | 211 const std::string& html, |
| 186 const std::vector<dom_distiller::DistillerViewerInterface::ImageInfo>& | 212 const std::vector<dom_distiller::DistillerViewerInterface::ImageInfo>& |
| 187 images) { | 213 images) { |
| 188 std::string mutableHTML = html; | 214 std::string mutableHTML = html; |
| 189 for (size_t i = 0; i < images.size(); i++) { | 215 for (size_t i = 0; i < images.size(); i++) { |
| 190 const std::string& localImagePath = | 216 base::FilePath localImagePath; |
|
sdefresne
2016/09/09 09:58:58
style: s/localImagePath/local_image_path/
lody
2016/09/09 12:17:53
Done.
| |
| 191 SaveImage(url, images[i].url, images[i].data); | 217 if (!SaveImage(url, images[i].url, images[i].data, localImagePath)) { |
| 218 return ""; | |
|
sdefresne
2016/09/09 09:58:58
Please prefer to use std::string() instead of "".
lody
2016/09/09 12:17:53
Done.
| |
| 219 } | |
| 192 const std::string& imageURL = images[i].url.spec(); | 220 const std::string& imageURL = images[i].url.spec(); |
| 193 size_t imageURLSize = imageURL.size(); | 221 size_t imageURLSize = imageURL.size(); |
| 194 size_t pos = mutableHTML.find(imageURL, 0); | 222 size_t pos = mutableHTML.find(imageURL, 0); |
| 195 while (pos != std::string::npos) { | 223 while (pos != std::string::npos) { |
| 196 mutableHTML.replace(pos, imageURLSize, localImagePath); | 224 mutableHTML.replace(pos, imageURLSize, localImagePath.AsUTF8Unsafe()); |
| 197 pos = mutableHTML.find(imageURL, pos + imageURLSize); | 225 pos = mutableHTML.find(imageURL, pos + imageURLSize); |
| 198 } | 226 } |
| 199 } | 227 } |
| 200 return mutableHTML; | 228 return mutableHTML; |
| 201 } | 229 } |
| 202 | 230 |
| 203 bool URLDownloader::SaveHTMLForURL(std::string html, const GURL& url) { | 231 bool URLDownloader::SaveHTMLForURL(std::string html, const GURL& url) { |
| 232 if (html.empty()) { | |
| 233 return false; | |
| 234 } | |
| 204 base::FilePath path = OfflineURLPagePath(url); | 235 base::FilePath path = OfflineURLPagePath(url); |
| 205 return base::WriteFile(path, html.c_str(), html.length()) < 0; | 236 return base::WriteFile(path, html.c_str(), html.length()) < 0; |
| 206 } | 237 } |
| OLD | NEW |