Add abhijeet.k@samsung.com to AUTHORS list.
[chromium-blink-merge.git] / components / dom_distiller / core / viewer.cc
blobc2e05f641d6a703f1f5c4e0751337c8651232fdd
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.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& title,
108 const std::string& textDirection,
109 const std::string& loading_indicator_class,
110 const std::string& original_url,
111 const DistilledPagePrefs::Theme theme,
112 const DistilledPagePrefs::FontFamily font_family,
113 const std::string& htmlContent) {
114 base::StringPiece html_template =
115 ResourceBundle::GetSharedInstance().GetRawDataResource(
116 IDR_DOM_DISTILLER_VIEWER_HTML);
117 // TODO(mdjones): Many or all of these substitutions can be placed on the
118 // page via JavaScript.
119 std::vector<std::string> substitutions;
120 substitutions.push_back(title); // $1
122 std::ostringstream css;
123 std::ostringstream script;
124 #if defined(OS_IOS)
125 // On iOS the content is inlined as there is no API to detect those requests
126 // and return the local data once a page is loaded.
127 css << "<style>" << viewer::GetCss() << "</style>";
128 script << "<script>\n" << viewer::GetJavaScript() << "\n</script>";
129 #else
130 css << "<link rel=\"stylesheet\" href=\"/" << kViewerCssPath << "\">";
131 script << "<script src=\"" << kViewerJsPath << "\"></script>";
132 #endif // defined(OS_IOS)
133 substitutions.push_back(css.str()); // $2
134 substitutions.push_back(script.str()); // $3
136 substitutions.push_back(GetThemeCssClass(theme) + " " +
137 GetFontCssClass(font_family)); // $4
138 substitutions.push_back(loading_indicator_class); // $5
139 substitutions.push_back(original_url); // $6
140 substitutions.push_back(
141 l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_VIEWER_VIEW_ORIGINAL)); // $7
142 substitutions.push_back(textDirection); // $8
143 substitutions.push_back(htmlContent); // $9
145 return ReplaceStringPlaceholders(html_template, substitutions, NULL);
148 } // namespace
150 namespace viewer {
152 const std::string GetShowFeedbackFormJs() {
153 base::StringValue question_val(
154 l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_QUALITY_QUESTION));
155 base::StringValue no_val(
156 l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_QUALITY_ANSWER_NO));
157 base::StringValue yes_val(
158 l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_QUALITY_ANSWER_YES));
160 std::string question;
161 std::string yes;
162 std::string no;
164 base::JSONWriter::Write(&question_val, &question);
165 base::JSONWriter::Write(&yes_val, &yes);
166 base::JSONWriter::Write(&no_val, &no);
168 return "showFeedbackForm(" + question + ", " + yes + ", " + no + ");";
171 const std::string GetUnsafeIncrementalDistilledPageJs(
172 const DistilledPageProto* page_proto,
173 const bool is_last_page) {
174 std::string output;
175 base::StringValue value(page_proto->html());
176 base::JSONWriter::Write(&value, &output);
177 EnsureNonEmptyContent(&output);
178 std::string page_update("addToPage(");
179 page_update += output + ");";
180 return page_update + GetToggleLoadingIndicatorJs(
181 is_last_page);
185 const std::string GetErrorPageJs() {
186 base::StringValue value(l10n_util::GetStringUTF8(
187 IDS_DOM_DISTILLER_VIEWER_FAILED_TO_FIND_ARTICLE_CONTENT));
188 std::string output;
189 base::JSONWriter::Write(&value, &output);
190 std::string page_update("addToPage(");
191 page_update += output + ");";
192 return page_update;
195 const std::string GetToggleLoadingIndicatorJs(const bool is_last_page) {
196 if (is_last_page)
197 return "showLoadingIndicator(true);";
198 else
199 return "showLoadingIndicator(false);";
202 const std::string GetUnsafeArticleTemplateHtml(
203 const DistilledPageProto* page_proto,
204 const DistilledPagePrefs::Theme theme,
205 const DistilledPagePrefs::FontFamily font_family) {
206 DCHECK(page_proto);
208 std::string title = net::EscapeForHTML(page_proto->title());
210 EnsureNonEmptyTitle(&title);
212 std::string text_direction = page_proto->text_direction();
213 std::string original_url = page_proto->url();
215 return ReplaceHtmlTemplateValues(title, text_direction, "hidden",
216 original_url, theme, font_family, "");
219 const std::string GetUnsafeArticleContentJs(
220 const DistilledArticleProto* article_proto) {
221 DCHECK(article_proto);
222 if (article_proto->pages_size() == 0 || !article_proto->pages(0).has_html()) {
223 return "";
226 std::ostringstream unsafe_output_stream;
227 for (int page_num = 0; page_num < article_proto->pages_size(); ++page_num) {
228 unsafe_output_stream << article_proto->pages(page_num).html();
231 std::string output;
232 base::StringValue value(unsafe_output_stream.str());
233 base::JSONWriter::Write(&value, &output);
234 EnsureNonEmptyContent(&output);
235 std::string page_update("addToPage(");
236 page_update += output + ");";
237 return page_update + GetToggleLoadingIndicatorJs(true);
240 const std::string GetErrorPageHtml(
241 const DistilledPagePrefs::Theme theme,
242 const DistilledPagePrefs::FontFamily font_family) {
243 std::string title = l10n_util::GetStringUTF8(
244 IDS_DOM_DISTILLER_VIEWER_FAILED_TO_FIND_ARTICLE_TITLE);
245 return ReplaceHtmlTemplateValues(title, "auto", "hidden", "", theme,
246 font_family, "");
249 const std::string GetUnsafeArticleHtml(
250 const DistilledArticleProto* article_proto,
251 const DistilledPagePrefs::Theme theme,
252 const DistilledPagePrefs::FontFamily font_family) {
253 DCHECK(article_proto);
254 std::string title;
255 std::string unsafe_article_html;
256 std::string text_direction = "";
257 if (article_proto->has_title() && article_proto->pages_size() > 0 &&
258 article_proto->pages(0).has_html()) {
259 title = net::EscapeForHTML(article_proto->title());
260 std::ostringstream unsafe_output_stream;
261 for (int page_num = 0; page_num < article_proto->pages_size(); ++page_num) {
262 unsafe_output_stream << article_proto->pages(page_num).html();
264 unsafe_article_html = unsafe_output_stream.str();
265 text_direction = article_proto->pages(0).text_direction();
268 EnsureNonEmptyTitle(&title);
269 EnsureNonEmptyContent(&unsafe_article_html);
271 std::string original_url;
272 if (article_proto->pages_size() > 0 && article_proto->pages(0).has_url()) {
273 original_url = article_proto->pages(0).url();
276 return ReplaceHtmlTemplateValues(
277 title, text_direction, "hidden", original_url, theme, font_family,
278 unsafe_article_html);
281 const std::string GetCss() {
282 return ResourceBundle::GetSharedInstance().GetRawDataResource(
283 IDR_DISTILLER_CSS).as_string();
286 const std::string GetJavaScript() {
287 return ResourceBundle::GetSharedInstance()
288 .GetRawDataResource(IDR_DOM_DISTILLER_VIEWER_JS)
289 .as_string();
292 scoped_ptr<ViewerHandle> CreateViewRequest(
293 DomDistillerServiceInterface* dom_distiller_service,
294 const std::string& path,
295 ViewRequestDelegate* view_request_delegate,
296 const gfx::Size& render_view_size) {
297 std::string entry_id =
298 url_utils::GetValueForKeyInUrlPathQuery(path, kEntryIdKey);
299 bool has_valid_entry_id = !entry_id.empty();
300 entry_id = StringToUpperASCII(entry_id);
302 std::string requested_url_str =
303 url_utils::GetValueForKeyInUrlPathQuery(path, kUrlKey);
304 GURL requested_url(requested_url_str);
305 bool has_valid_url = url_utils::IsUrlDistillable(requested_url);
307 if (has_valid_entry_id && has_valid_url) {
308 // It is invalid to specify a query param for both |kEntryIdKey| and
309 // |kUrlKey|.
310 return scoped_ptr<ViewerHandle>();
313 if (has_valid_entry_id) {
314 return dom_distiller_service->ViewEntry(
315 view_request_delegate,
316 dom_distiller_service->CreateDefaultDistillerPage(render_view_size),
317 entry_id).Pass();
318 } else if (has_valid_url) {
319 return dom_distiller_service->ViewUrl(
320 view_request_delegate,
321 dom_distiller_service->CreateDefaultDistillerPage(render_view_size),
322 requested_url).Pass();
325 // It is invalid to not specify a query param for |kEntryIdKey| or |kUrlKey|.
326 return scoped_ptr<ViewerHandle>();
329 const std::string GetDistilledPageThemeJs(DistilledPagePrefs::Theme theme) {
330 return "useTheme('" + GetJsTheme(theme) + "');";
333 const std::string GetDistilledPageFontFamilyJs(
334 DistilledPagePrefs::FontFamily font_family) {
335 return "useFontFamily('" + GetJsFontFamily(font_family) + "');";
338 } // namespace viewer
340 } // namespace dom_distiller