Index: chrome/browser/android/offline_pages/prerendering_offliner.cc |
diff --git a/chrome/browser/android/offline_pages/prerendering_offliner.cc b/chrome/browser/android/offline_pages/prerendering_offliner.cc |
index e7ee1f979c2c3d4737f8d9404f6b5a77c4a483ce..e69ea23e6a7234e649557a82a8c26a874536f266 100644 |
--- a/chrome/browser/android/offline_pages/prerendering_offliner.cc |
+++ b/chrome/browser/android/offline_pages/prerendering_offliner.cc |
@@ -16,11 +16,14 @@ |
#include "components/offline_pages/core/downloads/download_ui_adapter.h" |
#include "components/offline_pages/core/offline_page_model.h" |
#include "content/public/browser/browser_context.h" |
+#include "content/public/browser/browser_thread.h" |
#include "content/public/browser/web_contents.h" |
namespace offline_pages { |
namespace { |
const char kDownloadUIAdapterKey[] = "download-ui-adapter"; |
+const char kTextSignal[] = "Content-Type: text/signal-data"; |
+const char kNewline[] = "\n"; |
} |
PrerenderingOffliner::PrerenderingOffliner( |
@@ -104,7 +107,8 @@ void PrerenderingOffliner::OnLoadPageDone( |
void PrerenderingOffliner::OnSavePageDone( |
const SavePageRequest& request, |
SavePageResult save_result, |
- int64_t offline_id) { |
+ int64_t offline_id, |
+ const base::FilePath& saved_filepath) { |
// Check if request is still pending receiving a callback. |
if (!pending_request_) |
return; |
@@ -122,7 +126,7 @@ void PrerenderingOffliner::OnSavePageDone( |
app_listener_.reset(nullptr); |
GetOrCreateLoader()->StopLoading(); |
- // Determine status and run the completion callback. |
+ // Determine status. |
Offliner::RequestStatus save_status; |
if (save_result == SavePageResult::SUCCESS) { |
save_status = RequestStatus::SAVED; |
@@ -131,6 +135,14 @@ void PrerenderingOffliner::OnSavePageDone( |
// request based on specific save error cases. |
save_status = RequestStatus::SAVE_FAILED; |
} |
+ |
+ // Append signal data to the savevd file. |
fgorski
2017/03/08 18:56:42
saved
|
+ base::FilePath filepath_copy(saved_filepath); |
fgorski
2017/03/08 18:56:42
Is there a reason for copying the path here?
|
+ AppendSignalDataOnFileThread(filepath_copy); |
+ // TODO: Do we need to update the file size in the OfflinePageModel? |
+ // TODO: Should we wait for the file IO to be done before we return? |
+ |
+ // Run the completion callback (in the RequestCoordinator). |
completion_callback_.Run(request, save_status); |
} |
@@ -280,4 +292,142 @@ void PrerenderingOffliner::OnApplicationStateChange( |
} |
} |
+void PrerenderingOffliner::AppendSignalDataOnFileThread( |
fgorski
2017/03/08 18:56:42
I'd reverse the names of methods.
|
+ const base::FilePath& file_path) { |
fgorski
2017/03/08 18:56:43
Add appropriate check for thread here.
|
+ base::FilePath file_path_copy(file_path); |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::FILE, FROM_HERE, |
+ base::Bind(&PrerenderingOffliner::AppendSignalData, |
+ weak_ptr_factory_.GetWeakPtr(), file_path)); |
fgorski
2017/03/08 18:56:42
weak pointers are not thread-safe
If you want a c
|
+} |
+ |
+// TODO: Write unit tests for these new functions, fix unit tests that will |
+// now try to write to the MHTML file. |
+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
|
+ DVLOG(0) << "@@@@@@ FilePath " << file_path.value() << " " << __func__; |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
+ |
+ std::vector<std::string> signals = loader_->GetSignalData(); |
+ if (signals.size() == 0) |
fgorski
2017/03/08 18:56:42
question 1: What is the point of switching threads
|
+ return; |
+ |
+ // Use the FilePath to create a FILE object, opened for read and write. File |
+ // will be closed when the File object goes out of scope. |
+ base::File saved_file = |
+ base::File(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ | |
+ base::File::FLAG_WRITE); |
+ DVLOG(0) << "@@@@@@ opened file OK"; |
fgorski
2017/03/08 18:56:42
how do you know if opened fine? Isn't there a vali
|
+ |
+ // Get the MHTML boundary string. |
+ std::string boundary = GetBoundaryStringFromFile(saved_file); |
fgorski
2017/03/08 18:56:42
you don't need this until 344
|
+ DVLOG(0) << "@@@@@@ saw a MHTML boundary of " << boundary; |
+ |
+ // Chop off the last two hypens from the boundary string. We do this by |
fgorski
2017/03/08 18:56:42
hyphens?
|
+ // backing up the current position to just before them, and overwriting. |
+ int64_t position = PositionBeforeTwoDashes(saved_file); |
+ saved_file.Seek(base::File::FROM_BEGIN, position); |
+ // TODO: \n, or \r\f, or something else? |
fgorski
2017/03/08 18:56:42
would std::endl work?
|
+ saved_file.WriteAtCurrentPos(kNewline, sizeof(kNewline)); |
+ |
+ // Add a mime type of text/SignalData. |
+ saved_file.WriteAtCurrentPos(kTextSignal, sizeof(kTextSignal)); |
+ saved_file.WriteAtCurrentPos(kNewline, sizeof(kNewline)); |
+ |
+ // Add signals that the loader collected. |
+ for (std::string signal : signals) { |
+ saved_file.WriteAtCurrentPos(signal.c_str(), signal.length()); |
+ saved_file.WriteAtCurrentPos(kNewline, sizeof(kNewline)); |
+ } |
+ |
+ // End with a boundary string followed by a terminating double dash. |
+ saved_file.WriteAtCurrentPos("--", 2); |
+ saved_file.WriteAtCurrentPos(boundary.c_str(), boundary.length()); |
+ saved_file.WriteAtCurrentPos("--", 2); |
+ saved_file.WriteAtCurrentPos(kNewline, sizeof(kNewline)); |
+ |
+ // Exiting will let the file object go out of scope, which closes the file. |
+ DVLOG(0) << "@@@@@@ Done writing MHTML file."; |
+} |
+ |
+// Returns an empty string if none can be found. |
+// TODO: What is the best way? Read backwards from the file? Read boundary= |
+// from file? |
+std::string PrerenderingOffliner::GetBoundaryStringFromFile( |
fgorski
2017/03/08 18:56:42
Could this be a function in implementation file?
|
+ base::File& saved_file) { |
+ // We've chosen to get the last boundary so we don't have to read the whole |
+ // file and parse it looking for the boundary="" string |
+ // Seek to just before the two dashes. |
+ int64_t position = PositionBeforeTwoDashes(saved_file); |
+ if (position == 0) |
+ return std::string(); |
+ char found_character = '\0'; |
+ |
+ saved_file.Read(--position, &found_character, 1); |
+ |
+ // Everything between preceeding dashes and end of dashes is boundary, build |
+ // it up as we go along (backwards). |
+ std::string boundary; |
+ boundary = boundary + found_character; |
+ bool one_dash_seen = false; |
+ bool two_dashes_seen = false; |
+ // We have reached the start of the boundary when we see two consecutive |
+ // dashes. |
+ while (!two_dashes_seen && position > 0) { |
+ saved_file.Read(--position, &found_character, 1); |
+ boundary = found_character + boundary; |
fgorski
2017/03/08 18:56:42
Would it make sense to establish position first an
|
+ |
+ // TODO: Verify a boundary may not contain dash dash. If it may, I need to |
+ // get the boundary by parsing instead of reading backwards. Alternately, I |
+ // could pass the boudary down from where we set it. Is this safe if we |
+ // know that *our* boundaries won't contain dash dash? |
+ if (one_dash_seen && found_character == '-') |
fgorski
2017/03/08 18:56:42
int seen_dashes_count = 0;
while (seen_dashes_cou
|
+ two_dashes_seen = true; |
+ else if (found_character == '-') |
+ one_dash_seen = true; |
+ else |
+ one_dash_seen = false; |
+ } |
+ if (!two_dashes_seen) |
+ return std::string(); |
+ |
+ // Our string now has two dashes at the front that we don't want. |
+ return boundary.substr(2); |
+} |
+ |
+// TODO: Find and use the appropriate utility function instead, there should |
+// be one somewhere in Chromium. |
+bool PrerenderingOffliner::IsWhitespace(char next) { |
+ if (next == '\f' || next == '\r' || next == '\t' || next == ' ') |
+ return true; |
+ |
+ return false; |
+} |
+ |
+// returns 0 for failure. |
+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
|
+ // We've chosen to get the last boundary so we don't have to read the whole |
+ // file and parse it looking for the boundary="" string |
+ // Seek to end |
+ int64_t position = saved_file.Seek(base::File::FROM_END, 0); |
+ |
+ // move backwards past any whitespace then last two dashes find preceeding |
+ // dashes. We are using UTF-8. Dash cannot be a lead or trail byte, so we |
+ // should be safe. |
+ char found_character = '\0'; |
+ saved_file.Read(--position, &found_character, 1); |
+ |
+ // Eat any whitespace at the end of the file. |
+ while (IsWhitespace(found_character)) |
+ saved_file.Read(--position, &found_character, 1); |
+ |
+ // Eat two dashes |
+ if (found_character != '-') |
+ return 0; |
+ saved_file.Read(--position, &found_character, 1); |
+ if (found_character != '-') |
+ return 0; |
+ |
+ return --position; |
+} |
+ |
} // namespace offline_pages |