Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(932)

Unified Diff: content/browser/download/mhtml_generation_manager.cc

Issue 2842653002: [Offline Pages] Generate MHTML header in the browser process. (Closed)
Patch Set: Update format string for gcc error. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 001d30d59f469fbfc9696f80015393a5d031b473..d6e9d7ca29c3575265eeb739de4ac5a85a11fe07 100644
--- a/content/browser/download/mhtml_generation_manager.cc
+++ b/content/browser/download/mhtml_generation_manager.cc
@@ -18,6 +18,7 @@
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversion_utils.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "content/browser/bad_message.h"
@@ -38,6 +39,58 @@ namespace {
const char kContentLocation[] = "Content-Location: ";
const char kContentType[] = "Content-Type: ";
int kInvalidFileSize = -1;
+
+// This is used for compatibility with IE.
+bool IsASCIIPrintable(base::char16 c) {
+ return c >= ' ' && c <= '~';
+}
+
+constexpr int DAY_OF_WEEK_COUNT = 7;
+const char weekdays[DAY_OF_WEEK_COUNT][4] = {"Sun", "Mon", "Tue", "Wed",
+ "Thu", "Fri", "Sat"};
+
+constexpr int MONTH_COUNT = 12;
+const char months[MONTH_COUNT][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+const char* GetDayOfWeek(int day_of_week) {
+ DCHECK(day_of_week >= 0);
+ DCHECK(day_of_week < DAY_OF_WEEK_COUNT);
+ return weekdays[day_of_week];
+}
+
+const char* GetMonthOfYear(int month_of_year) {
+ DCHECK(month_of_year >= 0);
+ DCHECK(month_of_year < MONTH_COUNT);
+ return months[month_of_year];
+}
+
+// See http://tools.ietf.org/html/rfc2822#section-3.3 for more information.
+std::string MakeRFC2822DateString(base::Time time) {
+ base::Time::Exploded utc_exploded;
+ time.UTCExplode(&utc_exploded);
+ return base::StringPrintf(
+ "Date: %3.3s, %.2u %3.3s %u %.2u:%.2u:%.2u GMT",
+ GetDayOfWeek(utc_exploded.day_of_week), utc_exploded.day_of_month,
+ // Month of year is 1-based.
+ GetMonthOfYear(utc_exploded.month - 1), utc_exploded.year,
+ utc_exploded.hour, utc_exploded.minute, utc_exploded.second);
+}
+
+static std::string ReplaceNonPrintableCharacters(const base::string16& text) {
+ base::string16 quoted_text;
+ base::string16 question_mark = base::ASCIIToUTF16("?");
+
+ for (size_t i = 0; i < text.length(); ++i) {
+ base::char16 c = text[i];
+ if (IsASCIIPrintable(c))
+ quoted_text.append(1, c);
+ else
+ quoted_text.append(question_mark);
+ }
+ return base::UTF16ToUTF8(quoted_text);
+}
+
} // namespace
namespace content {
@@ -53,6 +106,7 @@ class MHTMLGenerationManager::Job : public RenderProcessHostObserver {
~Job() override;
int id() const { return job_id_; }
+
void set_browser_file(base::File file) { browser_file_ = std::move(file); }
base::TimeTicks creation_time() const { return creation_time_; }
@@ -82,6 +136,9 @@ class MHTMLGenerationManager::Job : public RenderProcessHostObserver {
return !waiting_for_response_from_renderer && no_more_requests_to_send;
}
+ // Generates the MHTML header.
+ std::string CreateHeader();
+
// Write the MHTML footer and close the file on the file thread and respond
// back on the UI thread with the updated status and file size (which will be
// negative in case of errors).
@@ -167,6 +224,14 @@ class MHTMLGenerationManager::Job : public RenderProcessHostObserver {
// MIME multipart boundary to use in the MHTML doc.
const std::string mhtml_boundary_marker_;
+ // Title of the main page (extracted from the WebContents) for use in the
+ // header.
+ const base::string16 title_;
+
+ // Mime type of the main page (extracted from the WebContents) for use in the
+ // header.
+ const std::string mime_type_;
+
// Digests of URIs of already generated MHTML parts.
std::set<std::string> digests_of_already_serialized_uris_;
std::string salt_;
@@ -198,6 +263,8 @@ MHTMLGenerationManager::Job::Job(int job_id,
params_(params),
frame_tree_node_id_of_busy_frame_(FrameTreeNode::kFrameTreeNodeInvalidId),
mhtml_boundary_marker_(net::GenerateMimeMultipartBoundary()),
+ title_(web_contents->GetTitle()),
+ mime_type_(web_contents->GetContentsMimeType()),
salt_(base::GenerateGUID()),
callback_(callback),
is_finished_(false),
@@ -505,6 +572,24 @@ bool MHTMLGenerationManager::Job::WriteExtraDataParts(
serialized_extra_data_parts.size()) >= 0);
}
+std::string MHTMLGenerationManager::Job::CreateHeader() {
+ DCHECK(!mhtml_boundary_marker_.empty());
+ DCHECK(!mime_type_.empty());
+
+ std::vector<std::string> headers = {
+ "From: <Saved by Blink>",
+ base::StringPrintf("Subject: %s",
+ ReplaceNonPrintableCharacters(title_).c_str()),
+ MakeRFC2822DateString(base::Time::Now()),
+ "Mime-Version: 1.0",
+ "Content-Type: multipart/related;",
+ base::StringPrintf("\ttype=\"%s\";", mime_type_.c_str()),
+ base::StringPrintf("\tboundary=\"%s\";", mhtml_boundary_marker_.c_str()),
+ "\r\n"};
+
+ return base::JoinString(headers, "\r\n");
+}
+
// static
bool MHTMLGenerationManager::Job::WriteFooter(const std::string& boundary,
base::File& file) {
@@ -550,7 +635,8 @@ void MHTMLGenerationManager::SaveMHTML(WebContents* web_contents,
BrowserThread::PostTaskAndReplyWithResult(
BrowserThread::FILE, FROM_HERE,
- base::Bind(&MHTMLGenerationManager::CreateFile, params.file_path),
+ base::Bind(&MHTMLGenerationManager::CreateFileAndWriteHeader,
+ params.file_path, job->CreateHeader()),
base::Bind(&MHTMLGenerationManager::OnFileAvailable,
base::Unretained(this), // Safe b/c |this| is a singleton.
job->id()));
@@ -595,7 +681,10 @@ void MHTMLGenerationManager::OnSerializeAsMHTMLResponse(
}
// static
-base::File MHTMLGenerationManager::CreateFile(const base::FilePath& file_path) {
+MHTMLGenerationManager::FileCreationResult
+MHTMLGenerationManager::CreateFileAndWriteHeader(
+ const base::FilePath& file_path,
+ const std::string& header) {
DCHECK_CURRENTLY_ON(BrowserThread::FILE);
// SECURITY NOTE: A file descriptor to the file created below will be passed
@@ -605,29 +694,34 @@ base::File MHTMLGenerationManager::CreateFile(const base::FilePath& file_path) {
// would allow reading content generated by other renderers / other web
// principals).
uint32_t file_flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
-
- base::File browser_file(file_path, file_flags);
- if (!browser_file.IsValid()) {
- LOG(ERROR) << "Failed to create file to save MHTML at: " <<
- file_path.value();
+ FileCreationResult result;
+ result.file = base::File(file_path, file_flags);
+ result.header_size = -1;
carlosk 2017/04/25 01:28:08 Set default to -1 so not to have to re-set it here
+ if (!result.file.IsValid()) {
+ LOG(ERROR) << "Failed to create file to save MHTML at: "
+ << file_path.value();
+ return result;
}
- return browser_file;
+
+ result.header_size =
+ result.file.WriteAtCurrentPos(header.data(), header.size());
+ return result;
carlosk 2017/04/25 01:28:07 Could you differentiate here between an error crea
}
void MHTMLGenerationManager::OnFileAvailable(int job_id,
- base::File browser_file) {
+ FileCreationResult result) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
Job* job = FindJob(job_id);
DCHECK(job);
- if (!browser_file.IsValid()) {
+ if (!result.file.IsValid() || result.header_size < 0) {
LOG(ERROR) << "Failed to create file";
JobFinished(job, MhtmlSaveStatus::FILE_CREATION_ERROR);
return;
}
- job->set_browser_file(std::move(browser_file));
+ job->set_browser_file(std::move(result.file));
MhtmlSaveStatus save_status = job->SendToNextRenderFrame();
if (save_status != MhtmlSaveStatus::SUCCESS) {

Powered by Google App Engine
This is Rietveld 408576698