Expand SecurityStyleChanged interfaces to include explanations
[chromium-blink-merge.git] / components / dom_distiller / core / viewer.cc
blob2e131618e1faa4107cf96bc45142602443a416c6
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/dom_distiller/core/viewer.h"
7 #include <string>
8 #include <vector>
10 #include "base/json/json_writer.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/strings/string_util.h"
14 #include "components/dom_distiller/core/distilled_page_prefs.h"
15 #include "components/dom_distiller/core/dom_distiller_service.h"
16 #include "components/dom_distiller/core/proto/distilled_article.pb.h"
17 #include "components/dom_distiller/core/proto/distilled_page.pb.h"
18 #include "components/dom_distiller/core/task_tracker.h"
19 #include "components/dom_distiller/core/url_constants.h"
20 #include "components/dom_distiller/core/url_utils.h"
21 #include "grit/components_resources.h"
22 #include "grit/components_strings.h"
23 #include "net/base/escape.h"
24 #include "net/url_request/url_request.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/base/resource/resource_bundle.h"
27 #include "url/gurl.h"
29 namespace dom_distiller {
31 namespace {
33 // JS Themes. Must agree with useTheme() in dom_distiller_viewer.js.
34 const char kDarkJsTheme[] = "dark";
35 const char kLightJsTheme[] = "light";
36 const char kSepiaJsTheme[] = "sepia";
38 // CSS Theme classes. Must agree with classes in distilledpage.css.
39 const char kDarkCssClass[] = "dark";
40 const char kLightCssClass[] = "light";
41 const char kSepiaCssClass[] = "sepia";
43 // JS FontFamilies. Must agree with useFontFamily() in dom_distiller_viewer.js.
44 const char kSerifJsFontFamily[] = "serif";
45 const char kSansSerifJsFontFamily[] = "sans-serif";
46 const char kMonospaceJsFontFamily[] = "monospace";
48 // CSS FontFamily classes. Must agree with classes in distilledpage.css.
49 const char kSerifCssClass[] = "serif";
50 const char kSansSerifCssClass[] = "sans-serif";
51 const char kMonospaceCssClass[] = "monospace";
53 // Maps themes to JS themes.
54 const std::string GetJsTheme(DistilledPagePrefs::Theme theme) {
55 if (theme == DistilledPagePrefs::DARK) {
56 return kDarkJsTheme;
57 } else if (theme == DistilledPagePrefs::SEPIA) {
58 return kSepiaJsTheme;
60 return kLightJsTheme;
63 // Maps themes to CSS classes.
64 const std::string GetThemeCssClass(DistilledPagePrefs::Theme theme) {
65 if (theme == DistilledPagePrefs::DARK) {
66 return kDarkCssClass;
67 } else if (theme == DistilledPagePrefs::SEPIA) {
68 return kSepiaCssClass;
70 return kLightCssClass;
73 // Maps font families to JS font families.
74 const std::string GetJsFontFamily(DistilledPagePrefs::FontFamily font_family) {
75 if (font_family == DistilledPagePrefs::SERIF) {
76 return kSerifJsFontFamily;
77 } else if (font_family == DistilledPagePrefs::MONOSPACE) {
78 return kMonospaceJsFontFamily;
80 return kSansSerifJsFontFamily;
83 // Maps fontFamilies to CSS fontFamily classes.
84 const std::string GetFontCssClass(DistilledPagePrefs::FontFamily font_family) {
85 if (font_family == DistilledPagePrefs::SERIF) {
86 return kSerifCssClass;
87 } else if (font_family == DistilledPagePrefs::MONOSPACE) {
88 return kMonospaceCssClass;
90 return kSansSerifCssClass;
93 void EnsureNonEmptyTitle(std::string* title) {
94 if (title->empty())
95 *title = l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_VIEWER_NO_DATA_TITLE);
98 void EnsureNonEmptyContent(std::string* content) {
99 UMA_HISTOGRAM_BOOLEAN("DomDistiller.PageHasDistilledData", !content->empty());
100 if (content->empty()) {
101 *content = l10n_util::GetStringUTF8(
102 IDS_DOM_DISTILLER_VIEWER_NO_DATA_CONTENT);
106 std::string ReplaceHtmlTemplateValues(
107 const std::string& original_url,
108 const DistilledPagePrefs::Theme theme,
109 const DistilledPagePrefs::FontFamily font_family) {
110 base::StringPiece html_template =
111 ResourceBundle::GetSharedInstance().GetRawDataResource(
112 IDR_DOM_DISTILLER_VIEWER_HTML);
113 std::vector<std::string> substitutions;
115 std::ostringstream css;
116 std::ostringstream script;
117 #if defined(OS_IOS)
118 // On iOS the content is inlined as there is no API to detect those requests
119 // and return the local data once a page is loaded.
120 css << "<style>" << viewer::GetCss() << viewer::GetIOSCss() << "</style>";
121 script << "<script>\n" << viewer::GetJavaScript() << "\n</script>";
122 #else
123 css << "<link rel=\"stylesheet\" href=\"/" << kViewerCssPath << "\">";
124 script << "<script src=\"" << kViewerJsPath << "\"></script>";
125 #endif // defined(OS_IOS)
127 substitutions.push_back(
128 l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_VIEWER_LOADING_TITLE)); // $1
130 substitutions.push_back(css.str()); // $2
131 substitutions.push_back(GetThemeCssClass(theme) + " " +
132 GetFontCssClass(font_family)); // $3
134 substitutions.push_back(
135 l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_VIEWER_NO_DATA_TITLE)); // $4
136 substitutions.push_back(
137 l10n_util::GetStringUTF8(
138 IDS_DOM_DISTILLER_JAVASCRIPT_DISABLED_CONTENT)); // $5
140 substitutions.push_back(original_url); // $6
141 substitutions.push_back(
142 l10n_util::GetStringUTF8(
143 IDS_DOM_DISTILLER_VIEWER_CLOSE_READER_VIEW)); // $7
145 substitutions.push_back(script.str()); // $8
147 return ReplaceStringPlaceholders(html_template, substitutions, NULL);
150 } // namespace
152 namespace viewer {
154 const std::string GetShowFeedbackFormJs() {
155 base::StringValue question_val(
156 l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_QUALITY_QUESTION));
157 base::StringValue no_val(
158 l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_QUALITY_ANSWER_NO));
159 base::StringValue yes_val(
160 l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_QUALITY_ANSWER_YES));
162 std::string question;
163 std::string yes;
164 std::string no;
166 base::JSONWriter::Write(question_val, &question);
167 base::JSONWriter::Write(yes_val, &yes);
168 base::JSONWriter::Write(no_val, &no);
170 return "showFeedbackForm(" + question + ", " + yes + ", " + no + ");";
173 const std::string GetUnsafeIncrementalDistilledPageJs(
174 const DistilledPageProto* page_proto,
175 const bool is_last_page) {
176 std::string output(page_proto->html());
177 EnsureNonEmptyContent(&output);
178 base::StringValue value(output);
179 base::JSONWriter::Write(value, &output);
180 std::string page_update("addToPage(");
181 page_update += output + ");";
182 return page_update + GetToggleLoadingIndicatorJs(
183 is_last_page);
187 const std::string GetErrorPageJs() {
188 base::StringValue value(l10n_util::GetStringUTF8(
189 IDS_DOM_DISTILLER_VIEWER_FAILED_TO_FIND_ARTICLE_CONTENT));
190 std::string output;
191 base::JSONWriter::Write(value, &output);
192 std::string page_update("addToPage(");
193 page_update += output + ");";
194 return page_update;
197 const std::string GetSetTitleJs(std::string title) {
198 EnsureNonEmptyTitle(&title);
199 base::StringValue value(title);
200 std::string output;
201 base::JSONWriter::Write(value, &output);
202 return "setTitle(" + output + ");";
205 const std::string GetSetTextDirectionJs(const std::string& direction) {
206 base::StringValue value(direction);
207 std::string output;
208 base::JSONWriter::Write(value, &output);
209 return "setTextDirection(" + output + ");";
212 const std::string GetToggleLoadingIndicatorJs(const bool is_last_page) {
213 if (is_last_page)
214 return "showLoadingIndicator(true);";
215 else
216 return "showLoadingIndicator(false);";
219 const std::string GetUnsafeArticleTemplateHtml(
220 const std::string original_url,
221 const DistilledPagePrefs::Theme theme,
222 const DistilledPagePrefs::FontFamily font_family) {
223 return ReplaceHtmlTemplateValues(original_url, theme, font_family);
226 const std::string GetUnsafeArticleContentJs(
227 const DistilledArticleProto* article_proto) {
228 DCHECK(article_proto);
229 std::ostringstream unsafe_output_stream;
230 if (article_proto->pages_size() > 0 && article_proto->pages(0).has_html()) {
231 for (int page_num = 0; page_num < article_proto->pages_size(); ++page_num) {
232 unsafe_output_stream << article_proto->pages(page_num).html();
236 std::string output(unsafe_output_stream.str());
237 EnsureNonEmptyContent(&output);
238 base::JSONWriter::Write(base::StringValue(output), &output);
239 std::string page_update("addToPage(");
240 page_update += output + ");";
241 return page_update + GetToggleLoadingIndicatorJs(true);
244 const std::string GetCss() {
245 return ResourceBundle::GetSharedInstance().GetRawDataResource(
246 IDR_DISTILLER_CSS).as_string();
249 const std::string GetIOSCss() {
250 return ResourceBundle::GetSharedInstance().GetRawDataResource(
251 IDR_DISTILLER_IOS_CSS).as_string();
254 const std::string GetJavaScript() {
255 return ResourceBundle::GetSharedInstance()
256 .GetRawDataResource(IDR_DOM_DISTILLER_VIEWER_JS)
257 .as_string();
260 scoped_ptr<ViewerHandle> CreateViewRequest(
261 DomDistillerServiceInterface* dom_distiller_service,
262 const std::string& path,
263 ViewRequestDelegate* view_request_delegate,
264 const gfx::Size& render_view_size) {
265 std::string entry_id =
266 url_utils::GetValueForKeyInUrlPathQuery(path, kEntryIdKey);
267 bool has_valid_entry_id = !entry_id.empty();
268 entry_id = base::StringToUpperASCII(entry_id);
270 std::string requested_url_str =
271 url_utils::GetValueForKeyInUrlPathQuery(path, kUrlKey);
272 GURL requested_url(requested_url_str);
273 bool has_valid_url = url_utils::IsUrlDistillable(requested_url);
275 if (has_valid_entry_id && has_valid_url) {
276 // It is invalid to specify a query param for both |kEntryIdKey| and
277 // |kUrlKey|.
278 return scoped_ptr<ViewerHandle>();
281 if (has_valid_entry_id) {
282 return dom_distiller_service->ViewEntry(
283 view_request_delegate,
284 dom_distiller_service->CreateDefaultDistillerPage(render_view_size),
285 entry_id).Pass();
286 } else if (has_valid_url) {
287 return dom_distiller_service->ViewUrl(
288 view_request_delegate,
289 dom_distiller_service->CreateDefaultDistillerPage(render_view_size),
290 requested_url).Pass();
293 // It is invalid to not specify a query param for |kEntryIdKey| or |kUrlKey|.
294 return scoped_ptr<ViewerHandle>();
297 const std::string GetDistilledPageThemeJs(DistilledPagePrefs::Theme theme) {
298 return "useTheme('" + GetJsTheme(theme) + "');";
301 const std::string GetDistilledPageFontFamilyJs(
302 DistilledPagePrefs::FontFamily font_family) {
303 return "useFontFamily('" + GetJsFontFamily(font_family) + "');";
306 } // namespace viewer
308 } // namespace dom_distiller