Vectorize website settings icons in omnibox
[chromium-blink-merge.git] / components / dom_distiller / content / browser / dom_distiller_viewer_source.cc
blob275666bb8fd4a8ea678665875e30e39e9d6a4913
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/browser/dom_distiller_viewer_source.h"
7 #include <sstream>
8 #include <string>
9 #include <vector>
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/metrics/user_metrics.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "components/dom_distiller/content/browser/distiller_javascript_utils.h"
17 #include "components/dom_distiller/content/browser/external_feedback_reporter.h"
18 #include "components/dom_distiller/core/distilled_page_prefs.h"
19 #include "components/dom_distiller/core/dom_distiller_request_view_base.h"
20 #include "components/dom_distiller/core/dom_distiller_service.h"
21 #include "components/dom_distiller/core/experiments.h"
22 #include "components/dom_distiller/core/feedback_reporter.h"
23 #include "components/dom_distiller/core/task_tracker.h"
24 #include "components/dom_distiller/core/url_constants.h"
25 #include "components/dom_distiller/core/url_utils.h"
26 #include "components/dom_distiller/core/viewer.h"
27 #include "content/public/browser/navigation_details.h"
28 #include "content/public/browser/navigation_entry.h"
29 #include "content/public/browser/render_frame_host.h"
30 #include "content/public/browser/render_view_host.h"
31 #include "content/public/browser/user_metrics.h"
32 #include "content/public/browser/web_contents.h"
33 #include "content/public/browser/web_contents_observer.h"
34 #include "grit/components_strings.h"
35 #include "net/base/url_util.h"
36 #include "net/url_request/url_request.h"
37 #include "ui/base/l10n/l10n_util.h"
39 namespace dom_distiller {
41 // Handles receiving data asynchronously for a specific entry, and passing
42 // it along to the data callback for the data source. Lifetime matches that of
43 // the current main frame's page in the Viewer instance.
44 class DomDistillerViewerSource::RequestViewerHandle
45 : public DomDistillerRequestViewBase,
46 public content::WebContentsObserver {
47 public:
48 RequestViewerHandle(content::WebContents* web_contents,
49 const std::string& expected_scheme,
50 const std::string& expected_request_path,
51 DistilledPagePrefs* distilled_page_prefs);
52 ~RequestViewerHandle() override;
54 // content::WebContentsObserver implementation:
55 void DidNavigateMainFrame(
56 const content::LoadCommittedDetails& details,
57 const content::FrameNavigateParams& params) override;
58 void RenderProcessGone(base::TerminationStatus status) override;
59 void WebContentsDestroyed() override;
60 void DidFinishLoad(content::RenderFrameHost* render_frame_host,
61 const GURL& validated_url) override;
63 private:
64 // Sends JavaScript to the attached Viewer, buffering data if the viewer isn't
65 // ready.
66 void SendJavaScript(const std::string& buffer) override;
68 // Cancels the current view request. Once called, no updates will be
69 // propagated to the view, and the request to DomDistillerService will be
70 // cancelled.
71 void Cancel();
73 // The scheme hosting the current view request;
74 std::string expected_scheme_;
76 // The query path for the current view request.
77 std::string expected_request_path_;
79 // Whether the page is sufficiently initialized to handle updates from the
80 // distiller.
81 bool waiting_for_page_ready_;
83 // Temporary store of pending JavaScript if the page isn't ready to receive
84 // data from distillation.
85 std::string buffer_;
88 DomDistillerViewerSource::RequestViewerHandle::RequestViewerHandle(
89 content::WebContents* web_contents,
90 const std::string& expected_scheme,
91 const std::string& expected_request_path,
92 DistilledPagePrefs* distilled_page_prefs)
93 : DomDistillerRequestViewBase(distilled_page_prefs),
94 expected_scheme_(expected_scheme),
95 expected_request_path_(expected_request_path),
96 waiting_for_page_ready_(true) {
97 content::WebContentsObserver::Observe(web_contents);
98 distilled_page_prefs_->AddObserver(this);
101 DomDistillerViewerSource::RequestViewerHandle::~RequestViewerHandle() {
102 distilled_page_prefs_->RemoveObserver(this);
105 void DomDistillerViewerSource::RequestViewerHandle::SendJavaScript(
106 const std::string& buffer) {
107 if (waiting_for_page_ready_) {
108 buffer_ += buffer;
109 } else {
110 if (web_contents()) {
111 RunIsolatedJavaScript(web_contents()->GetMainFrame(), buffer);
116 void DomDistillerViewerSource::RequestViewerHandle::DidNavigateMainFrame(
117 const content::LoadCommittedDetails& details,
118 const content::FrameNavigateParams& params) {
119 const GURL& navigation = details.entry->GetURL();
120 if (details.is_in_page || (
121 navigation.SchemeIs(expected_scheme_.c_str()) &&
122 expected_request_path_ == navigation.query())) {
123 // In-page navigations, as well as the main view request can be ignored.
124 return;
127 Cancel();
130 void DomDistillerViewerSource::RequestViewerHandle::RenderProcessGone(
131 base::TerminationStatus status) {
132 Cancel();
135 void DomDistillerViewerSource::RequestViewerHandle::WebContentsDestroyed() {
136 Cancel();
139 void DomDistillerViewerSource::RequestViewerHandle::Cancel() {
140 // No need to listen for notifications.
141 content::WebContentsObserver::Observe(NULL);
143 // Schedule the Viewer for deletion. Ensures distillation is cancelled, and
144 // any pending data stored in |buffer_| is released.
145 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
148 void DomDistillerViewerSource::RequestViewerHandle::DidFinishLoad(
149 content::RenderFrameHost* render_frame_host,
150 const GURL& validated_url) {
151 if (render_frame_host->GetParent()) {
152 return;
154 waiting_for_page_ready_ = false;
155 if (!buffer_.empty()) {
156 RunIsolatedJavaScript(web_contents()->GetMainFrame(), buffer_);
157 buffer_.clear();
159 if (IsErrorPage()) {
160 Cancel(); // This will cause the object to clean itself up.
164 DomDistillerViewerSource::DomDistillerViewerSource(
165 DomDistillerServiceInterface* dom_distiller_service,
166 const std::string& scheme,
167 scoped_ptr<ExternalFeedbackReporter> external_reporter)
168 : scheme_(scheme),
169 dom_distiller_service_(dom_distiller_service),
170 external_feedback_reporter_(external_reporter.Pass()) {
173 DomDistillerViewerSource::~DomDistillerViewerSource() {
176 std::string DomDistillerViewerSource::GetSource() const {
177 return scheme_ + "://";
180 void DomDistillerViewerSource::StartDataRequest(
181 const std::string& path,
182 int render_process_id,
183 int render_frame_id,
184 const content::URLDataSource::GotDataCallback& callback) {
185 content::RenderFrameHost* render_frame_host =
186 content::RenderFrameHost::FromID(render_process_id, render_frame_id);
187 if (!render_frame_host) return;
188 content::RenderViewHost* render_view_host =
189 render_frame_host->GetRenderViewHost();
190 DCHECK(render_view_host);
191 CHECK_EQ(0, render_view_host->GetEnabledBindings());
193 if (kViewerCssPath == path) {
194 std::string css = viewer::GetCss();
195 callback.Run(base::RefCountedString::TakeString(&css));
196 return;
197 } else if (kViewerViewOriginalPath == path) {
198 content::RecordAction(base::UserMetricsAction("DomDistiller_ViewOriginal"));
199 callback.Run(NULL);
200 return;
201 } else if (kFeedbackBad == path) {
202 FeedbackReporter::ReportQuality(false);
203 callback.Run(NULL);
204 if (!external_feedback_reporter_)
205 return;
206 content::WebContents* contents =
207 content::WebContents::FromRenderFrameHost(render_frame_host);
208 external_feedback_reporter_->ReportExternalFeedback(
209 contents, contents->GetURL(), false);
210 return;
211 } else if (kFeedbackGood == path) {
212 FeedbackReporter::ReportQuality(true);
213 callback.Run(NULL);
214 return;
216 content::WebContents* web_contents =
217 content::WebContents::FromRenderFrameHost(render_frame_host);
218 DCHECK(web_contents);
219 // An empty |path| is invalid, but guard against it. If not empty, assume
220 // |path| starts with '?', which is stripped away.
221 const std::string path_after_query_separator =
222 path.size() > 0 ? path.substr(1) : "";
223 RequestViewerHandle* request_viewer_handle =
224 new RequestViewerHandle(web_contents, scheme_, path_after_query_separator,
225 dom_distiller_service_->GetDistilledPagePrefs());
226 scoped_ptr<ViewerHandle> viewer_handle = viewer::CreateViewRequest(
227 dom_distiller_service_, path, request_viewer_handle,
228 web_contents->GetContainerBounds().size());
230 GURL current_url(url_utils::GetValueForKeyInUrlPathQuery(path, kUrlKey));
231 std::string unsafe_page_html = viewer::GetUnsafeArticleTemplateHtml(
232 url_utils::GetOriginalUrlFromDistillerUrl(current_url).spec(),
233 dom_distiller_service_->GetDistilledPagePrefs()->GetTheme(),
234 dom_distiller_service_->GetDistilledPagePrefs()->GetFontFamily());
236 if (viewer_handle) {
237 // The service returned a |ViewerHandle| and guarantees it will call
238 // the |RequestViewerHandle|, so passing ownership to it, to ensure the
239 // request is not cancelled. The |RequestViewerHandle| will delete itself
240 // after receiving the callback.
241 request_viewer_handle->TakeViewerHandle(viewer_handle.Pass());
242 } else {
243 request_viewer_handle->FlagAsErrorPage();
246 // Place template on the page.
247 callback.Run(base::RefCountedString::TakeString(&unsafe_page_html));
250 std::string DomDistillerViewerSource::GetMimeType(
251 const std::string& path) const {
252 if (kViewerCssPath == path) {
253 return "text/css";
255 if (kViewerJsPath == path) {
256 return "text/javascript";
258 return "text/html";
261 bool DomDistillerViewerSource::ShouldServiceRequest(
262 const net::URLRequest* request) const {
263 return request->url().SchemeIs(scheme_.c_str());
266 // TODO(nyquist): Start tracking requests using this method.
267 void DomDistillerViewerSource::WillServiceRequest(
268 const net::URLRequest* request,
269 std::string* path) const {
272 std::string DomDistillerViewerSource::GetContentSecurityPolicyObjectSrc()
273 const {
274 return "object-src 'none'; style-src 'self' https://fonts.googleapis.com;";
277 std::string DomDistillerViewerSource::GetContentSecurityPolicyFrameSrc() const {
278 return "frame-src *;";
281 } // namespace dom_distiller