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 "chrome/browser/android/offline_pages/prerendering_offliner.h" | 5 #include "chrome/browser/android/offline_pages/prerendering_offliner.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/metrics/histogram_macros.h" | 8 #include "base/metrics/histogram_macros.h" |
| 9 #include "base/sys_info.h" | 9 #include "base/sys_info.h" |
| 10 #include "chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h" | 10 #include "chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h" |
| 11 #include "chrome/browser/android/offline_pages/offliner_helper.h" | 11 #include "chrome/browser/android/offline_pages/offliner_helper.h" |
| 12 #include "chrome/browser/profiles/profile.h" | 12 #include "chrome/browser/profiles/profile.h" |
| 13 #include "components/offline_pages/core/background/offliner_policy.h" | 13 #include "components/offline_pages/core/background/offliner_policy.h" |
| 14 #include "components/offline_pages/core/background/save_page_request.h" | 14 #include "components/offline_pages/core/background/save_page_request.h" |
| 15 #include "components/offline_pages/core/client_namespace_constants.h" | 15 #include "components/offline_pages/core/client_namespace_constants.h" |
| 16 #include "components/offline_pages/core/downloads/download_ui_adapter.h" | 16 #include "components/offline_pages/core/downloads/download_ui_adapter.h" |
| 17 #include "components/offline_pages/core/offline_page_model.h" | 17 #include "components/offline_pages/core/offline_page_model.h" |
| 18 #include "content/public/browser/browser_context.h" | 18 #include "content/public/browser/browser_context.h" |
| 19 #include "content/public/browser/browser_thread.h" | |
| 19 #include "content/public/browser/web_contents.h" | 20 #include "content/public/browser/web_contents.h" |
| 20 | 21 |
| 21 namespace offline_pages { | 22 namespace offline_pages { |
| 22 namespace { | 23 namespace { |
| 23 const char kDownloadUIAdapterKey[] = "download-ui-adapter"; | 24 const char kDownloadUIAdapterKey[] = "download-ui-adapter"; |
| 25 const char kTextSignal[] = "Content-Type: text/signal-data"; | |
| 26 const char kNewline[] = "\n"; | |
| 24 } | 27 } |
| 25 | 28 |
| 26 PrerenderingOffliner::PrerenderingOffliner( | 29 PrerenderingOffliner::PrerenderingOffliner( |
| 27 content::BrowserContext* browser_context, | 30 content::BrowserContext* browser_context, |
| 28 const OfflinerPolicy* policy, | 31 const OfflinerPolicy* policy, |
| 29 OfflinePageModel* offline_page_model) | 32 OfflinePageModel* offline_page_model) |
| 30 : browser_context_(browser_context), | 33 : browser_context_(browser_context), |
| 31 policy_(policy), | 34 policy_(policy), |
| 32 offline_page_model_(offline_page_model), | 35 offline_page_model_(offline_page_model), |
| 33 pending_request_(nullptr), | 36 pending_request_(nullptr), |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 97 // Clear pending request and app listener then run completion callback. | 100 // Clear pending request and app listener then run completion callback. |
| 98 pending_request_.reset(nullptr); | 101 pending_request_.reset(nullptr); |
| 99 app_listener_.reset(nullptr); | 102 app_listener_.reset(nullptr); |
| 100 completion_callback_.Run(request, load_status); | 103 completion_callback_.Run(request, load_status); |
| 101 } | 104 } |
| 102 } | 105 } |
| 103 | 106 |
| 104 void PrerenderingOffliner::OnSavePageDone( | 107 void PrerenderingOffliner::OnSavePageDone( |
| 105 const SavePageRequest& request, | 108 const SavePageRequest& request, |
| 106 SavePageResult save_result, | 109 SavePageResult save_result, |
| 107 int64_t offline_id) { | 110 int64_t offline_id, |
| 111 const base::FilePath& saved_filepath) { | |
| 108 // Check if request is still pending receiving a callback. | 112 // Check if request is still pending receiving a callback. |
| 109 if (!pending_request_) | 113 if (!pending_request_) |
| 110 return; | 114 return; |
| 111 | 115 |
| 112 // Also check that this completed request is same as the pending one | 116 // Also check that this completed request is same as the pending one |
| 113 // (since SavePage request is not cancel-able currently and could be old). | 117 // (since SavePage request is not cancel-able currently and could be old). |
| 114 if (request.request_id() != pending_request_->request_id()) { | 118 if (request.request_id() != pending_request_->request_id()) { |
| 115 DVLOG(1) << "Ignoring save callback for old request"; | 119 DVLOG(1) << "Ignoring save callback for old request"; |
| 116 return; | 120 return; |
| 117 } | 121 } |
| 118 | 122 |
| 119 // Clear pending request and app listener here and then inform loader we | 123 // Clear pending request and app listener here and then inform loader we |
| 120 // are done with WebContents. | 124 // are done with WebContents. |
| 121 pending_request_.reset(nullptr); | 125 pending_request_.reset(nullptr); |
| 122 app_listener_.reset(nullptr); | 126 app_listener_.reset(nullptr); |
| 123 GetOrCreateLoader()->StopLoading(); | 127 GetOrCreateLoader()->StopLoading(); |
| 124 | 128 |
| 125 // Determine status and run the completion callback. | 129 // Determine status. |
| 126 Offliner::RequestStatus save_status; | 130 Offliner::RequestStatus save_status; |
| 127 if (save_result == SavePageResult::SUCCESS) { | 131 if (save_result == SavePageResult::SUCCESS) { |
| 128 save_status = RequestStatus::SAVED; | 132 save_status = RequestStatus::SAVED; |
| 129 } else { | 133 } else { |
| 130 // TODO(dougarnett): Consider reflecting some recommendation to retry the | 134 // TODO(dougarnett): Consider reflecting some recommendation to retry the |
| 131 // request based on specific save error cases. | 135 // request based on specific save error cases. |
| 132 save_status = RequestStatus::SAVE_FAILED; | 136 save_status = RequestStatus::SAVE_FAILED; |
| 133 } | 137 } |
| 138 | |
| 139 // Append signal data to the savevd file. | |
|
fgorski
2017/03/08 18:56:42
saved
| |
| 140 base::FilePath filepath_copy(saved_filepath); | |
|
fgorski
2017/03/08 18:56:42
Is there a reason for copying the path here?
| |
| 141 AppendSignalDataOnFileThread(filepath_copy); | |
| 142 // TODO: Do we need to update the file size in the OfflinePageModel? | |
| 143 // TODO: Should we wait for the file IO to be done before we return? | |
| 144 | |
| 145 // Run the completion callback (in the RequestCoordinator). | |
| 134 completion_callback_.Run(request, save_status); | 146 completion_callback_.Run(request, save_status); |
| 135 } | 147 } |
| 136 | 148 |
| 137 bool PrerenderingOffliner::LoadAndSave(const SavePageRequest& request, | 149 bool PrerenderingOffliner::LoadAndSave(const SavePageRequest& request, |
| 138 const CompletionCallback& callback) { | 150 const CompletionCallback& callback) { |
| 139 DCHECK(!pending_request_.get()); | 151 DCHECK(!pending_request_.get()); |
| 140 | 152 |
| 141 if (pending_request_) { | 153 if (pending_request_) { |
| 142 DVLOG(1) << "Already have pending request"; | 154 DVLOG(1) << "Already have pending request"; |
| 143 return false; | 155 return false; |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 273 application_state == | 285 application_state == |
| 274 base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) { | 286 base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) { |
| 275 DVLOG(1) << "App became active, canceling current offlining request"; | 287 DVLOG(1) << "App became active, canceling current offlining request"; |
| 276 SavePageRequest* request = pending_request_.get(); | 288 SavePageRequest* request = pending_request_.get(); |
| 277 Cancel(); | 289 Cancel(); |
| 278 completion_callback_.Run(*request, | 290 completion_callback_.Run(*request, |
| 279 Offliner::RequestStatus::FOREGROUND_CANCELED); | 291 Offliner::RequestStatus::FOREGROUND_CANCELED); |
| 280 } | 292 } |
| 281 } | 293 } |
| 282 | 294 |
| 295 void PrerenderingOffliner::AppendSignalDataOnFileThread( | |
|
fgorski
2017/03/08 18:56:42
I'd reverse the names of methods.
| |
| 296 const base::FilePath& file_path) { | |
|
fgorski
2017/03/08 18:56:43
Add appropriate check for thread here.
| |
| 297 base::FilePath file_path_copy(file_path); | |
| 298 content::BrowserThread::PostTask( | |
| 299 content::BrowserThread::FILE, FROM_HERE, | |
| 300 base::Bind(&PrerenderingOffliner::AppendSignalData, | |
| 301 weak_ptr_factory_.GetWeakPtr(), file_path)); | |
|
fgorski
2017/03/08 18:56:42
weak pointers are not thread-safe
If you want a c
| |
| 302 } | |
| 303 | |
| 304 // TODO: Write unit tests for these new functions, fix unit tests that will | |
| 305 // now try to write to the MHTML file. | |
| 306 void PrerenderingOffliner::AppendSignalData(const base::FilePath& file_path) { | |
|
fgorski
2017/03/08 18:56:42
Can we make it into a function (to avoid temptatio
| |
| 307 DVLOG(0) << "@@@@@@ FilePath " << file_path.value() << " " << __func__; | |
| 308 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
| 309 | |
| 310 std::vector<std::string> signals = loader_->GetSignalData(); | |
| 311 if (signals.size() == 0) | |
|
fgorski
2017/03/08 18:56:42
question 1: What is the point of switching threads
| |
| 312 return; | |
| 313 | |
| 314 // Use the FilePath to create a FILE object, opened for read and write. File | |
| 315 // will be closed when the File object goes out of scope. | |
| 316 base::File saved_file = | |
| 317 base::File(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ | | |
| 318 base::File::FLAG_WRITE); | |
| 319 DVLOG(0) << "@@@@@@ opened file OK"; | |
|
fgorski
2017/03/08 18:56:42
how do you know if opened fine? Isn't there a vali
| |
| 320 | |
| 321 // Get the MHTML boundary string. | |
| 322 std::string boundary = GetBoundaryStringFromFile(saved_file); | |
|
fgorski
2017/03/08 18:56:42
you don't need this until 344
| |
| 323 DVLOG(0) << "@@@@@@ saw a MHTML boundary of " << boundary; | |
| 324 | |
| 325 // Chop off the last two hypens from the boundary string. We do this by | |
|
fgorski
2017/03/08 18:56:42
hyphens?
| |
| 326 // backing up the current position to just before them, and overwriting. | |
| 327 int64_t position = PositionBeforeTwoDashes(saved_file); | |
| 328 saved_file.Seek(base::File::FROM_BEGIN, position); | |
| 329 // TODO: \n, or \r\f, or something else? | |
|
fgorski
2017/03/08 18:56:42
would std::endl work?
| |
| 330 saved_file.WriteAtCurrentPos(kNewline, sizeof(kNewline)); | |
| 331 | |
| 332 // Add a mime type of text/SignalData. | |
| 333 saved_file.WriteAtCurrentPos(kTextSignal, sizeof(kTextSignal)); | |
| 334 saved_file.WriteAtCurrentPos(kNewline, sizeof(kNewline)); | |
| 335 | |
| 336 // Add signals that the loader collected. | |
| 337 for (std::string signal : signals) { | |
| 338 saved_file.WriteAtCurrentPos(signal.c_str(), signal.length()); | |
| 339 saved_file.WriteAtCurrentPos(kNewline, sizeof(kNewline)); | |
| 340 } | |
| 341 | |
| 342 // End with a boundary string followed by a terminating double dash. | |
| 343 saved_file.WriteAtCurrentPos("--", 2); | |
| 344 saved_file.WriteAtCurrentPos(boundary.c_str(), boundary.length()); | |
| 345 saved_file.WriteAtCurrentPos("--", 2); | |
| 346 saved_file.WriteAtCurrentPos(kNewline, sizeof(kNewline)); | |
| 347 | |
| 348 // Exiting will let the file object go out of scope, which closes the file. | |
| 349 DVLOG(0) << "@@@@@@ Done writing MHTML file."; | |
| 350 } | |
| 351 | |
| 352 // Returns an empty string if none can be found. | |
| 353 // TODO: What is the best way? Read backwards from the file? Read boundary= | |
| 354 // from file? | |
| 355 std::string PrerenderingOffliner::GetBoundaryStringFromFile( | |
|
fgorski
2017/03/08 18:56:42
Could this be a function in implementation file?
| |
| 356 base::File& saved_file) { | |
| 357 // We've chosen to get the last boundary so we don't have to read the whole | |
| 358 // file and parse it looking for the boundary="" string | |
| 359 // Seek to just before the two dashes. | |
| 360 int64_t position = PositionBeforeTwoDashes(saved_file); | |
| 361 if (position == 0) | |
| 362 return std::string(); | |
| 363 char found_character = '\0'; | |
| 364 | |
| 365 saved_file.Read(--position, &found_character, 1); | |
| 366 | |
| 367 // Everything between preceeding dashes and end of dashes is boundary, build | |
| 368 // it up as we go along (backwards). | |
| 369 std::string boundary; | |
| 370 boundary = boundary + found_character; | |
| 371 bool one_dash_seen = false; | |
| 372 bool two_dashes_seen = false; | |
| 373 // We have reached the start of the boundary when we see two consecutive | |
| 374 // dashes. | |
| 375 while (!two_dashes_seen && position > 0) { | |
| 376 saved_file.Read(--position, &found_character, 1); | |
| 377 boundary = found_character + boundary; | |
|
fgorski
2017/03/08 18:56:42
Would it make sense to establish position first an
| |
| 378 | |
| 379 // TODO: Verify a boundary may not contain dash dash. If it may, I need to | |
| 380 // get the boundary by parsing instead of reading backwards. Alternately, I | |
| 381 // could pass the boudary down from where we set it. Is this safe if we | |
| 382 // know that *our* boundaries won't contain dash dash? | |
| 383 if (one_dash_seen && found_character == '-') | |
|
fgorski
2017/03/08 18:56:42
int seen_dashes_count = 0;
while (seen_dashes_cou
| |
| 384 two_dashes_seen = true; | |
| 385 else if (found_character == '-') | |
| 386 one_dash_seen = true; | |
| 387 else | |
| 388 one_dash_seen = false; | |
| 389 } | |
| 390 if (!two_dashes_seen) | |
| 391 return std::string(); | |
| 392 | |
| 393 // Our string now has two dashes at the front that we don't want. | |
| 394 return boundary.substr(2); | |
| 395 } | |
| 396 | |
| 397 // TODO: Find and use the appropriate utility function instead, there should | |
| 398 // be one somewhere in Chromium. | |
| 399 bool PrerenderingOffliner::IsWhitespace(char next) { | |
| 400 if (next == '\f' || next == '\r' || next == '\t' || next == ' ') | |
| 401 return true; | |
| 402 | |
| 403 return false; | |
| 404 } | |
| 405 | |
| 406 // returns 0 for failure. | |
| 407 int64_t PrerenderingOffliner::PositionBeforeTwoDashes(base::File& saved_file) { | |
|
fgorski
2017/03/08 18:56:42
if you add end position to signature, you could ni
| |
| 408 // We've chosen to get the last boundary so we don't have to read the whole | |
| 409 // file and parse it looking for the boundary="" string | |
| 410 // Seek to end | |
| 411 int64_t position = saved_file.Seek(base::File::FROM_END, 0); | |
| 412 | |
| 413 // move backwards past any whitespace then last two dashes find preceeding | |
| 414 // dashes. We are using UTF-8. Dash cannot be a lead or trail byte, so we | |
| 415 // should be safe. | |
| 416 char found_character = '\0'; | |
| 417 saved_file.Read(--position, &found_character, 1); | |
| 418 | |
| 419 // Eat any whitespace at the end of the file. | |
| 420 while (IsWhitespace(found_character)) | |
| 421 saved_file.Read(--position, &found_character, 1); | |
| 422 | |
| 423 // Eat two dashes | |
| 424 if (found_character != '-') | |
| 425 return 0; | |
| 426 saved_file.Read(--position, &found_character, 1); | |
| 427 if (found_character != '-') | |
| 428 return 0; | |
| 429 | |
| 430 return --position; | |
| 431 } | |
| 432 | |
| 283 } // namespace offline_pages | 433 } // namespace offline_pages |
| OLD | NEW |