diff --git a/chrome/browser/offline_pages/thumbnail_decoder_impl.h b/chrome/browser/offline_pages/thumbnail_decoder_impl.h index 577fde8e7ffe0f..69c9092d30f7c9 100644 --- a/chrome/browser/offline_pages/thumbnail_decoder_impl.h +++ b/chrome/browser/offline_pages/thumbnail_decoder_impl.h @@ -12,6 +12,10 @@ namespace offline_pages { +// Decodes the downloaded JPEG image, crops it and re-encode it as a PNG +// file to be used as the thumbnail of an offlined suggested article. +// Note: the local decoding in a separate process and local re-encoding as a PNG +// are important security measures as these downloaded images are web content. class ThumbnailDecoderImpl : public ThumbnailDecoder { public: explicit ThumbnailDecoderImpl( diff --git a/chrome/common/available_offline_content.mojom b/chrome/common/available_offline_content.mojom index f6ac36e6385576..b2a97e1c3f4b5d 100644 --- a/chrome/common/available_offline_content.mojom +++ b/chrome/common/available_offline_content.mojom @@ -23,6 +23,9 @@ enum AvailableContentType { }; // A single piece of content that is available offline. +// Note: Some of the content pieces stored in this struct are web content and +// must be properly handled for securing their presentation on the net error +// page. struct AvailableOfflineContent { // Together id and name_space define a unique ID for this item. string id; diff --git a/components/neterror/resources/neterror.css b/components/neterror/resources/neterror.css index 9655508e804d81..b0cfddabad2eae 100644 --- a/components/neterror/resources/neterror.css +++ b/components/neterror/resources/neterror.css @@ -507,6 +507,10 @@ div.offline-content-suggestion { word-break: break-all; } +.no-attribution .offline-content-suggestion-attribution { + display: none; +} + .offline-content-suggestion-freshness:before { content: '-'; display: inline-block; diff --git a/components/neterror/resources/neterror.js b/components/neterror/resources/neterror.js index 86b1b344515117..bbbe8e77cb3fbd 100644 --- a/components/neterror/resources/neterror.js +++ b/components/neterror/resources/neterror.js @@ -174,11 +174,14 @@ function launchDownloadsPage() { // Populates a summary of suggested offline content. function offlineContentSummaryAvailable(summary) { + // Note: See AvailableContentSummaryToValue in + // available_offline_content_helper.cc for the data contained in |summary|. if (!summary || summary.total_items == 0 || !loadTimeData.valueExists('offlineContentSummary')) { return; } - + // TODO(https://crbug.com/852872): Customize presented icons based on the + // types of available offline content. document.getElementById('offline-content-summary').hidden = false; } @@ -190,14 +193,14 @@ function getIconForSuggestedItem(item) { return 'image-video'; case 2: // kAudio return 'image-music-note'; - case 0: // kPrefetchedUnopenedPage + case 0: // kPrefetchedPage case 3: // kOtherPage return 'image-earth'; } return 'image-file'; } -function getSuggestedContentDiv(item) { +function getSuggestedContentDiv(item, index) { // Note: See AvailableContentToValue in available_offline_content_helper.cc // for the data contained in an |item|. var visual = ''; @@ -221,12 +224,12 @@ function getSuggestedContentDiv(item) {
-
- ${item.title} +
-
- ${item.attribution} +
${item.date_modified} @@ -242,20 +245,34 @@ function getSuggestedContentDiv(item) { } // Populates a list of suggested offline content. +// Note: For security reasons all content downloaded from the web is considered +// unsafe and must be securely handled to be presented on the dino page. The +// image content is already safely re-encoded after being downloaded but the +// textual content, like title and attribution, must be properly handled here. function offlineContentAvailable(suggestions) { if (!suggestions || !loadTimeData.valueExists('offlineContentList')) return; var suggestionsHTML = []; - for (var item of suggestions) - suggestionsHTML.push(getSuggestedContentDiv(item)); + for (var index = 0; index < suggestions.length; index++) + suggestionsHTML.push(getSuggestedContentDiv(suggestions[index], index)); + document.getElementById('offline-content-suggestions').innerHTML = suggestionsHTML.join('\n'); - var contentListElement = document.getElementById('offline-content-list') - contentListElement.hidden = false; + // Sets textual web content using |textContent| to make sure it's handled as + // plain text. + for (var index = 0; index < suggestions.length; index++) { + document.getElementById(`offline-content-suggestion-title-${index}`) + .textContent = suggestions[index].title; + document.getElementById(`offline-content-suggestion-attribution-${index}`) + .textContent = suggestions[index].attribution; + } + + var contentListElement = document.getElementById('offline-content-list'); if (document.dir == 'rtl') contentListElement.classList.add('is-rtl'); + contentListElement.hidden = false; } function onDocumentLoad() {