Chromium Code Reviews| Index: content/browser/download/mhtml_generation_manager.cc |
| diff --git a/content/browser/download/mhtml_generation_manager.cc b/content/browser/download/mhtml_generation_manager.cc |
| index b989af11932a1e2c49ec1451e7d55560874b8f28..343c3e5f7b26d676e15cf0f026a36ca3ec418ef5 100644 |
| --- a/content/browser/download/mhtml_generation_manager.cc |
| +++ b/content/browser/download/mhtml_generation_manager.cc |
| @@ -32,6 +32,13 @@ |
| #include "content/public/common/mhtml_generation_params.h" |
| #include "net/base/mime_util.h" |
| +namespace { |
| +const char kContentLocation[] = "Content-Location: "; |
| +const char kContentType[] = "Content-Type: "; |
| +const int kMHTMLExtraDataKey = 0; |
| + |
|
Dmitry Titov
2017/03/18 00:58:34
extra empty line
Pete Williamson
2017/03/20 18:26:53
Done.
|
| +} // namespace |
| + |
| namespace content { |
| // The class and all of its members live on the UI thread. Only static methods |
| @@ -101,9 +108,18 @@ class MHTMLGenerationManager::Job : public RenderProcessHostObserver { |
| static std::tuple<MhtmlSaveStatus, int64_t> CloseFileOnFileThread( |
| MhtmlSaveStatus save_status, |
| const std::string& boundary, |
| - base::File file); |
| + base::File file, |
| + const std::vector<MHTMLGenerationManager::MHTMLExtraSection>* |
| + extra_sections); |
| void AddFrame(RenderFrameHost* render_frame_host); |
| + // If we have any extra data sections to write out, write them into the file |
| + // while on the file thread. Returns true for success. |
| + static bool WriteExtraDataSectionsOnFileThread( |
| + const std::string& boundary, |
| + base::File& file, |
| + const std::vector<MHTMLExtraSection>* extra_sections); |
| + |
| // Creates a new map with values (content ids) the same as in |
| // |frame_tree_node_to_content_id_| map, but with the keys translated from |
| // frame_tree_node_id into a |site_instance|-specific routing_id. |
| @@ -156,6 +172,9 @@ class MHTMLGenerationManager::Job : public RenderProcessHostObserver { |
| // destroyed by MHTMLGenerationManager::OnFileClosed). |
| bool is_finished_; |
| + // Any extra data sections that should be emitted into the output MHTML. |
| + std::vector<MHTMLExtraSection>* extra_sections_; |
| + |
| // RAII helper for registering this Job as a RenderProcessHost observer. |
| ScopedObserver<RenderProcessHost, MHTMLGenerationManager::Job> |
| observed_renderer_process_host_; |
| @@ -185,6 +204,9 @@ MHTMLGenerationManager::Job::Job(int job_id, |
| DCHECK(!pending_frame_tree_node_ids_.empty()); |
| DCHECK(FrameTreeNode::GloballyFindByID(pending_frame_tree_node_ids_.front()) |
| ->parent() == nullptr); |
| + |
| + // Save off any extra data. |
| + extra_sections_ = MHTMLExtraData::FromWebContents(web_contents); |
| } |
| MHTMLGenerationManager::Job::~Job() { |
| @@ -355,7 +377,7 @@ void MHTMLGenerationManager::Job::CloseFile( |
| &MHTMLGenerationManager::Job::CloseFileOnFileThread, save_status, |
| (save_status == MhtmlSaveStatus::SUCCESS ? mhtml_boundary_marker_ |
| : std::string()), |
| - base::Passed(&browser_file_)), |
| + base::Passed(&browser_file_), extra_sections_), |
| callback); |
| } |
| @@ -401,25 +423,34 @@ MhtmlSaveStatus MHTMLGenerationManager::Job::OnSerializeAsMHTMLResponse( |
| // static |
| std::tuple<MhtmlSaveStatus, int64_t> |
| -MHTMLGenerationManager::Job::CloseFileOnFileThread(MhtmlSaveStatus save_status, |
| - const std::string& boundary, |
| - base::File file) { |
| +MHTMLGenerationManager::Job::CloseFileOnFileThread( |
| + MhtmlSaveStatus save_status, |
| + const std::string& boundary, |
| + base::File file, |
| + const std::vector<MHTMLExtraSection>* extra_sections) { |
| DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| + int64_t file_size = -1; |
| // If no previous error occurred the boundary should have been provided. |
| if (save_status == MhtmlSaveStatus::SUCCESS) { |
| TRACE_EVENT0("page-serialization", |
| "MHTMLGenerationManager::Job MHTML footer writing"); |
| DCHECK(!boundary.empty()); |
| + |
| + // Write the extra data into a section of its own, if we have any. |
| + if (!WriteExtraDataSectionsOnFileThread(boundary, file, extra_sections)) |
| + save_status = MhtmlSaveStatus::FILE_WRITTING_ERROR; |
|
Dmitry Titov
2017/03/18 00:58:34
there was a comment about early return here and yo
Pete Williamson
2017/03/20 18:26:53
Ah, there are now two cases, and only one of them
|
| + |
| std::string footer = base::StringPrintf("--%s--\r\n", boundary.c_str()); |
| DCHECK(base::IsStringASCII(footer)); |
| - if (file.WriteAtCurrentPos(footer.data(), footer.size()) < 0) |
| + if (file.WriteAtCurrentPos(footer.data(), footer.size()) < 0) { |
| save_status = MhtmlSaveStatus::FILE_WRITTING_ERROR; |
| + return std::make_tuple(save_status, file_size); |
| + } |
| } |
| // If the file is still valid try to close it. Only update the status if that |
| // won't hide an earlier error. |
| - int64_t file_size = -1; |
| if (file.IsValid()) { |
| file_size = file.GetLength(); |
| file.Close(); |
| @@ -430,10 +461,70 @@ MHTMLGenerationManager::Job::CloseFileOnFileThread(MhtmlSaveStatus save_status, |
| return std::make_tuple(save_status, file_size); |
| } |
| +// static |
| +bool MHTMLGenerationManager::Job::WriteExtraDataSectionsOnFileThread( |
| + const std::string& boundary, |
| + base::File& file, |
| + const std::vector<MHTMLExtraSection>* extra_sections) { |
| + // Don't write an extra data section if there is none. |
| + if (extra_sections->empty()) |
| + return true; |
| + |
| + std::string extra_sections_serialized; |
| + |
| + // For each extra section, serialize that section and add to our accumulator |
| + // string. |
| + for (auto section : *extra_sections) { |
| + // Write a newline, then a boundary, another newline, then the content |
| + // location, another newline, the content type, another newline, the extra |
| + // data string, and end with a newline. |
| + std::string extra_data_section = base::StringPrintf( |
| + "--%s--\r\n%s%s\r\n%s%s\r\n%s\r\n", boundary.c_str(), kContentLocation, |
| + section.content_location.c_str(), kContentType, |
| + section.content_type.c_str(), section.body.c_str()); |
| + DCHECK(base::IsStringASCII(extra_data_section)); |
| + |
| + extra_sections_serialized += extra_data_section; |
| + } |
| + |
| + // Write the string into the file. Returns false if we failed the write. |
| + return (file.WriteAtCurrentPos(extra_sections_serialized.data(), |
| + extra_sections_serialized.size()) >= 0); |
| +} |
| + |
| MHTMLGenerationManager* MHTMLGenerationManager::GetInstance() { |
| return base::Singleton<MHTMLGenerationManager>::get(); |
| } |
| +MHTMLGenerationManager::MHTMLExtraData::MHTMLExtraData() {} |
| + |
| +MHTMLGenerationManager::MHTMLExtraData::~MHTMLExtraData() {} |
| + |
| +std::vector<MHTMLGenerationManager::MHTMLExtraSection>* |
| +MHTMLGenerationManager::MHTMLExtraData::FromWebContents( |
| + content::WebContents* contents) { |
| + MHTMLExtraData* extra_data = |
| + static_cast<MHTMLExtraData*>(contents->GetUserData(&kMHTMLExtraDataKey)); |
| + return extra_data->sections(); |
| +} |
| + |
| +void MHTMLGenerationManager::MHTMLExtraData::AddToWebContents( |
| + content::WebContents* contents, |
| + MHTMLGenerationManager::MHTMLExtraSection& section) { |
| + MHTMLExtraData* extra_data = |
| + static_cast<MHTMLExtraData*>(contents->GetUserData(&kMHTMLExtraDataKey)); |
| + // If we don't already have any extra data, add it now. |
| + if (extra_data == nullptr) { |
| + extra_data = new MHTMLExtraData(); |
| + // Note that we hold onto extra_data until the end of this function, even |
| + // though we transfer it to the web contents user data. |
| + contents->SetUserData(&kMHTMLExtraDataKey, |
| + std::unique_ptr<MHTMLExtraData>(extra_data)); |
| + } |
| + // Add this section to the list of sections to be saved out. |
| + extra_data->sections()->push_back(section); |
| +} |
| + |
| MHTMLGenerationManager::MHTMLGenerationManager() : next_job_id_(0) {} |
| MHTMLGenerationManager::~MHTMLGenerationManager() { |