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/content/dom_distiller_viewer_source.h"
11 #include "base/memory/ref_counted_memory.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/string_util.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 "content/public/browser/render_frame_host.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "grit/component_resources.h"
22 #include "grit/component_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"
31 const char kCssPath[] = "readability.css";
33 std::string ReplaceHtmlTemplateValues(std::string title, std::string content) {
34 base::StringPiece html_template =
35 ResourceBundle::GetSharedInstance().GetRawDataResource(
36 IDR_DOM_DISTILLER_VIEWER_HTML);
37 std::vector<std::string> substitutions;
38 substitutions.push_back(title); // $1
39 substitutions.push_back(kCssPath); // $2
40 substitutions.push_back(title); // $3
41 substitutions.push_back(content); // $4
42 return ReplaceStringPlaceholders(html_template, substitutions, NULL);
47 namespace dom_distiller {
49 // Handles receiving data asynchronously for a specific entry, and passing
50 // it along to the data callback for the data source.
51 class RequestViewerHandle : public ViewRequestDelegate {
53 explicit RequestViewerHandle(
54 const content::URLDataSource::GotDataCallback& callback);
55 virtual ~RequestViewerHandle();
57 // ViewRequestDelegate implementation.
58 virtual void OnArticleReady(const DistilledArticleProto* article_proto)
61 void TakeViewerHandle(scoped_ptr<ViewerHandle> viewer_handle);
64 // The handle to the view request towards the DomDistillerService. It
65 // needs to be kept around to ensure the distillation request finishes.
66 scoped_ptr<ViewerHandle> viewer_handle_;
68 // This holds the callback to where the data retrieved is sent back.
69 content::URLDataSource::GotDataCallback callback_;
72 RequestViewerHandle::RequestViewerHandle(
73 const content::URLDataSource::GotDataCallback& callback)
74 : callback_(callback) {}
76 RequestViewerHandle::~RequestViewerHandle() {}
78 void RequestViewerHandle::OnArticleReady(
79 const DistilledArticleProto* article_proto) {
80 DCHECK(article_proto);
82 std::string unsafe_article_html;
83 if (article_proto->has_title() && article_proto->pages_size() > 0 &&
84 article_proto->pages(0).has_html()) {
85 title = net::EscapeForHTML(article_proto->title());
86 // TODO(shashishekhar): Add support for correcting displaying multiple pages
87 // after discussing the right way to display them.
88 std::ostringstream unsafe_output_stream;
89 for (int page_num = 0; page_num < article_proto->pages_size(); ++page_num) {
90 unsafe_output_stream << article_proto->pages(page_num).html();
92 unsafe_article_html = unsafe_output_stream.str();
94 title = l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_VIEWER_NO_DATA_TITLE);
96 l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_VIEWER_NO_DATA_CONTENT);
98 std::string unsafe_page_html =
99 ReplaceHtmlTemplateValues(title, unsafe_article_html);
100 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html));
101 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
104 void RequestViewerHandle::TakeViewerHandle(
105 scoped_ptr<ViewerHandle> viewer_handle) {
106 viewer_handle_ = viewer_handle.Pass();
109 DomDistillerViewerSource::DomDistillerViewerSource(
110 DomDistillerService* dom_distiller_service,
111 const std::string& scheme)
112 : scheme_(scheme), dom_distiller_service_(dom_distiller_service) {}
114 DomDistillerViewerSource::~DomDistillerViewerSource() {}
116 std::string DomDistillerViewerSource::GetSource() const {
117 return scheme_ + "://";
120 void DomDistillerViewerSource::StartDataRequest(
121 const std::string& path,
122 int render_process_id,
124 const content::URLDataSource::GotDataCallback& callback) {
125 content::RenderFrameHost* render_frame_host =
126 content::RenderFrameHost::FromID(render_process_id, render_frame_id);
127 DCHECK(render_frame_host);
128 content::RenderViewHost* render_view_host =
129 render_frame_host->GetRenderViewHost();
130 DCHECK(render_view_host);
131 CHECK_EQ(0, render_view_host->GetEnabledBindings());
133 if (kCssPath == path) {
134 std::string css = ResourceBundle::GetSharedInstance()
135 .GetRawDataResource(IDR_DISTILLER_CSS)
137 callback.Run(base::RefCountedString::TakeString(&css));
141 RequestViewerHandle* request_viewer_handle =
142 new RequestViewerHandle(callback);
143 std::string entry_id = StringToUpperASCII(path);
144 scoped_ptr<ViewerHandle> viewer_handle =
145 dom_distiller_service_->ViewEntry(request_viewer_handle, entry_id);
147 // The service returned a |ViewerHandle| and guarantees it will call
148 // the |RequestViewerHandle|, so passing ownership to it, to ensure the
149 // request is not cancelled. The |RequestViewerHandle| will delete itself
150 // after receiving the callback.
151 request_viewer_handle->TakeViewerHandle(viewer_handle.Pass());
153 // The service did not return a |ViewerHandle|, which means the
154 // |RequestViewerHandle| will never be called, so clean up now.
155 delete request_viewer_handle;
157 std::string title = l10n_util::GetStringUTF8(
158 IDS_DOM_DISTILLER_VIEWER_FAILED_TO_FIND_ARTICLE_TITLE);
159 std::string content = l10n_util::GetStringUTF8(
160 IDS_DOM_DISTILLER_VIEWER_FAILED_TO_FIND_ARTICLE_CONTENT);
161 std::string html = ReplaceHtmlTemplateValues(title, content);
162 callback.Run(base::RefCountedString::TakeString(&html));
166 std::string DomDistillerViewerSource::GetMimeType(const std::string& path)
168 if (path == kCssPath)
173 bool DomDistillerViewerSource::ShouldServiceRequest(
174 const net::URLRequest* request) const {
175 return request->url().SchemeIs(scheme_.c_str());
178 void DomDistillerViewerSource::WillServiceRequest(
179 const net::URLRequest* request,
180 std::string* path) const {
181 if (*path != kCssPath) {
182 // Since the full request is not available to StartDataRequest, replace the
183 // path to contain the data needed.
184 *path = request->url().host();
188 std::string DomDistillerViewerSource::GetContentSecurityPolicyObjectSrc()
190 return "object-src 'none'; style-src 'self'";
193 } // namespace dom_distiller