Commit 6c0f52e9 authored by Dan Harrington's avatar Dan Harrington Committed by Commit Bot

Fix broken utf-8 text for content-on-dino-page

See bug for more information about the problem, and solution.

This was manually tested.

Bug: 910458
Change-Id: I663a1354f12a917c478144530311b31e4bd1657f
Reviewed-on: https://chromium-review.googlesource.com/c/1357520Reviewed-by: default avatarCathy Li <chili@chromium.org>
Commit-Queue: Dan H <harringtond@google.com>
Cr-Commit-Position: refs/heads/master@{#613106}
parent 69d34166
......@@ -10,6 +10,7 @@
#include "base/json/json_writer.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/common/available_offline_content.mojom.h"
#include "components/error_page/common/net_error_info.h"
......@@ -22,6 +23,23 @@ namespace {
using chrome::mojom::AvailableOfflineContentPtr;
using chrome::mojom::AvailableContentType;
// Converts a string to base-64 data. This is done for security purposes, to
// avoid potential XSS. Note that when this value is decoded in javascript, we
// want to use the atob() function, but that function only handles latin-1
// characters. Additionally, javascript needs UTF16 strings. So we instead
// encode to UTF16, and then store that data as base64.
std::string ConvertToUTF16Base64(const std::string& text) {
base::string16 text_utf16 = base::UTF8ToUTF16(text);
std::string utf16_bytes;
for (base::char16 c : text_utf16) {
utf16_bytes.push_back(static_cast<char>(c >> 8));
utf16_bytes.push_back(static_cast<char>(c & 0xff));
}
std::string encoded;
base::Base64Encode(utf16_bytes, &encoded);
return encoded;
}
base::Value AvailableContentToValue(const AvailableOfflineContentPtr& content) {
// All pieces of text content downloaded from the web will be base64 encoded
// to lessen security risks when this dictionary is passed as a string to
......@@ -30,13 +48,13 @@ base::Value AvailableContentToValue(const AvailableOfflineContentPtr& content) {
base::Value value(base::Value::Type::DICTIONARY);
value.SetKey("ID", base::Value(content->id));
value.SetKey("name_space", base::Value(content->name_space));
base::Base64Encode(content->title, &base64_encoded);
value.SetKey("title_base64", base::Value(base64_encoded));
base::Base64Encode(content->snippet, &base64_encoded);
value.SetKey("snippet_base64", base::Value(base64_encoded));
value.SetKey("title_base64",
base::Value(ConvertToUTF16Base64(content->title)));
value.SetKey("snippet_base64",
base::Value(ConvertToUTF16Base64(content->snippet)));
value.SetKey("date_modified", base::Value(content->date_modified));
base::Base64Encode(content->attribution, &base64_encoded);
value.SetKey("attribution_base64", base::Value(base64_encoded));
value.SetKey("attribution_base64",
base::Value(ConvertToUTF16Base64(content->attribution)));
value.SetKey("thumbnail_data_uri",
base::Value(content->thumbnail_data_uri.spec()));
value.SetKey("content_type",
......
......@@ -2696,23 +2696,23 @@ const std::string GetExpectedAvailableContentAsJson() {
std::string want_json = R"([
{
"ID": "ID",
"attribution_base64": "YXR0cmlidXRpb24=",
"attribution_base64": "AGEAdAB0AHIAaQBiAHUAdABpAG8Abg==",
"content_type": 0,
"date_modified": "date_modified",
"name_space": "name_space",
"snippet_base64": "c25pcHBldA==",
"snippet_base64": "AHMAbgBpAHAAcABlAHQ=",
"thumbnail_data_uri": "data:image/png;base64,abc",
"title_base64": "dGl0bGU="
"title_base64": "AHQAaQB0AGwAZQ=="
},
{
"ID": "ID2",
"attribution_base64": "YXR0cmlidXRpb24y",
"attribution_base64": "AGEAdAB0AHIAaQBiAHUAdABpAG8AbgAy",
"content_type": 3,
"date_modified": "date_modified2",
"name_space": "name_space2",
"snippet_base64": "c25pcHBldDI=",
"snippet_base64": "AHMAbgBpAHAAcABlAHQAMg==",
"thumbnail_data_uri": "data:image/png;base64,abc",
"title_base64": "dGl0bGUy"
"title_base64": "AHQAaQB0AGwAZQAy"
}
])";
base::ReplaceChars(want_json, base::kWhitespaceASCII, "", &want_json);
......
......@@ -2,6 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Decodes a UTF16 string that is encoded as base64.
function decodeUTF16Base64ToString(encoded_text) {
var data = atob(encoded_text);
var result = '';
for (var i = 0; i < data.length; i += 2) {
result +=
String.fromCharCode(data.charCodeAt(i) * 256 + data.charCodeAt(i + 1));
}
return result;
}
function toggleHelpBox() {
var helpBoxOuter = document.getElementById('details');
helpBoxOuter.classList.toggle(HIDDEN_CLASS);
......@@ -286,9 +297,11 @@ function offlineContentAvailable(isShown, suggestions) {
// plain text.
for (var index = 0; index < suggestions.length; index++) {
document.getElementById(`offline-content-suggestion-title-${index}`)
.textContent = atob(suggestions[index].title_base64);
.textContent =
decodeUTF16Base64ToString(suggestions[index].title_base64);
document.getElementById(`offline-content-suggestion-attribution-${index}`)
.textContent = atob(suggestions[index].attribution_base64);
.textContent =
decodeUTF16Base64ToString(suggestions[index].attribution_base64);
}
var contentListElement = document.getElementById('offline-content-list');
......@@ -324,8 +337,7 @@ function onDocumentLoad() {
var showSavedCopyButtonVisible =
loadTimeData.valueExists('showSavedCopyButton') &&
loadTimeData.getValue('showSavedCopyButton').msg;
var downloadButtonVisible =
loadTimeData.valueExists('downloadButton') &&
var downloadButtonVisible = loadTimeData.valueExists('downloadButton') &&
loadTimeData.getValue('downloadButton').msg;
// If offline content suggestions will be visible, the usual buttons will not
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment