Switch TestFrameNavigationObserver to DidCommitProvisionalLoadForFrame.
[chromium-blink-merge.git] / components / dom_distiller / content / dom_distiller_viewer_source.cc
blobf1ed1a8932e8c4bba3a7ebb1c13d6338ed3de2e3
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"
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/core/distilled_page_prefs.h"
17 #include "components/dom_distiller/core/dom_distiller_request_view_base.h"
18 #include "components/dom_distiller/core/dom_distiller_service.h"
19 #include "components/dom_distiller/core/external_feedback_reporter.h"
20 #include "components/dom_distiller/core/feedback_reporter.h"
21 #include "components/dom_distiller/core/task_tracker.h"
22 #include "components/dom_distiller/core/url_constants.h"
23 #include "components/dom_distiller/core/viewer.h"
24 #include "content/public/browser/navigation_details.h"
25 #include "content/public/browser/navigation_entry.h"
26 #include "content/public/browser/render_frame_host.h"
27 #include "content/public/browser/render_view_host.h"
28 #include "content/public/browser/user_metrics.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/browser/web_contents_observer.h"
31 #include "net/base/url_util.h"
32 #include "net/url_request/url_request.h"
34 namespace dom_distiller {
36 namespace {
38 class ContentDataCallback : public DistillerDataCallback {
39 public:
40 ContentDataCallback(const content::URLDataSource::GotDataCallback& callback);
41 // Runs the callback.
42 void RunCallback(std::string& data) override;
44 private:
45 // The callback that actually gets run.
46 content::URLDataSource::GotDataCallback callback_;
49 ContentDataCallback::ContentDataCallback(
50 const content::URLDataSource::GotDataCallback& callback) {
51 callback_ = callback;
54 void ContentDataCallback::RunCallback(std::string& data) {
55 callback_.Run(base::RefCountedString::TakeString(&data));
58 } // namespace
60 // Handles receiving data asynchronously for a specific entry, and passing
61 // it along to the data callback for the data source. Lifetime matches that of
62 // the current main frame's page in the Viewer instance.
63 class DomDistillerViewerSource::RequestViewerHandle
64 : public DomDistillerRequestViewBase,
65 public content::WebContentsObserver {
66 public:
67 RequestViewerHandle(content::WebContents* web_contents,
68 const std::string& expected_scheme,
69 const std::string& expected_request_path,
70 scoped_ptr<ContentDataCallback> callback,
71 DistilledPagePrefs* distilled_page_prefs);
72 ~RequestViewerHandle() override;
74 // content::WebContentsObserver implementation:
75 void DidNavigateMainFrame(
76 const content::LoadCommittedDetails& details,
77 const content::FrameNavigateParams& params) override;
78 void RenderProcessGone(base::TerminationStatus status) override;
79 void WebContentsDestroyed() override;
80 void DidFinishLoad(content::RenderFrameHost* render_frame_host,
81 const GURL& validated_url) override;
83 private:
84 // Sends JavaScript to the attached Viewer, buffering data if the viewer isn't
85 // ready.
86 void SendJavaScript(const std::string& buffer) override;
88 // Cancels the current view request. Once called, no updates will be
89 // propagated to the view, and the request to DomDistillerService will be
90 // cancelled.
91 void Cancel();
93 // The scheme hosting the current view request;
94 std::string expected_scheme_;
96 // The query path for the current view request.
97 std::string expected_request_path_;
99 // Whether the page is sufficiently initialized to handle updates from the
100 // distiller.
101 bool waiting_for_page_ready_;
103 // Temporary store of pending JavaScript if the page isn't ready to receive
104 // data from distillation.
105 std::string buffer_;
108 DomDistillerViewerSource::RequestViewerHandle::RequestViewerHandle(
109 content::WebContents* web_contents,
110 const std::string& expected_scheme,
111 const std::string& expected_request_path,
112 scoped_ptr<ContentDataCallback> callback,
113 DistilledPagePrefs* distilled_page_prefs)
114 : DomDistillerRequestViewBase(callback.Pass(), distilled_page_prefs),
115 expected_scheme_(expected_scheme),
116 expected_request_path_(expected_request_path),
117 waiting_for_page_ready_(true) {
118 content::WebContentsObserver::Observe(web_contents);
119 distilled_page_prefs_->AddObserver(this);
122 DomDistillerViewerSource::RequestViewerHandle::~RequestViewerHandle() {
123 distilled_page_prefs_->RemoveObserver(this);
126 void DomDistillerViewerSource::RequestViewerHandle::SendJavaScript(
127 const std::string& buffer) {
128 if (waiting_for_page_ready_) {
129 buffer_ += buffer;
130 } else {
131 if (web_contents()) {
132 web_contents()->GetMainFrame()->ExecuteJavaScript(
133 base::UTF8ToUTF16(buffer));
138 void DomDistillerViewerSource::RequestViewerHandle::DidNavigateMainFrame(
139 const content::LoadCommittedDetails& details,
140 const content::FrameNavigateParams& params) {
141 const GURL& navigation = details.entry->GetURL();
142 if (details.is_in_page || (
143 navigation.SchemeIs(expected_scheme_.c_str()) &&
144 expected_request_path_ == navigation.query())) {
145 // In-page navigations, as well as the main view request can be ignored.
146 return;
149 Cancel();
152 void DomDistillerViewerSource::RequestViewerHandle::RenderProcessGone(
153 base::TerminationStatus status) {
154 Cancel();
157 void DomDistillerViewerSource::RequestViewerHandle::WebContentsDestroyed() {
158 Cancel();
161 void DomDistillerViewerSource::RequestViewerHandle::Cancel() {
162 // No need to listen for notifications.
163 content::WebContentsObserver::Observe(NULL);
165 // Schedule the Viewer for deletion. Ensures distillation is cancelled, and
166 // any pending data stored in |buffer_| is released.
167 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
170 void DomDistillerViewerSource::RequestViewerHandle::DidFinishLoad(
171 content::RenderFrameHost* render_frame_host,
172 const GURL& validated_url) {
173 if (IsErrorPage()) {
174 waiting_for_page_ready_ = false;
175 SendJavaScript(viewer::GetErrorPageJs());
176 SendJavaScript(viewer::GetShowFeedbackFormJs());
177 Cancel(); // This will cause the object to clean itself up.
178 return;
181 if (render_frame_host->GetParent()) {
182 return;
184 waiting_for_page_ready_ = false;
185 if (buffer_.empty()) {
186 return;
188 web_contents()->GetMainFrame()->ExecuteJavaScript(base::UTF8ToUTF16(buffer_));
189 buffer_.clear();
192 DomDistillerViewerSource::DomDistillerViewerSource(
193 DomDistillerServiceInterface* dom_distiller_service,
194 const std::string& scheme,
195 scoped_ptr<ExternalFeedbackReporter> external_reporter)
196 : scheme_(scheme),
197 dom_distiller_service_(dom_distiller_service),
198 external_feedback_reporter_(external_reporter.Pass()) {
201 DomDistillerViewerSource::~DomDistillerViewerSource() {
204 std::string DomDistillerViewerSource::GetSource() const {
205 return scheme_ + "://";
208 void DomDistillerViewerSource::StartDataRequest(
209 const std::string& path,
210 int render_process_id,
211 int render_frame_id,
212 const content::URLDataSource::GotDataCallback& callback) {
213 content::RenderFrameHost* render_frame_host =
214 content::RenderFrameHost::FromID(render_process_id, render_frame_id);
215 if (!render_frame_host) return;
216 content::RenderViewHost* render_view_host =
217 render_frame_host->GetRenderViewHost();
218 DCHECK(render_view_host);
219 CHECK_EQ(0, render_view_host->GetEnabledBindings());
221 if (kViewerCssPath == path) {
222 std::string css = viewer::GetCss();
223 callback.Run(base::RefCountedString::TakeString(&css));
224 return;
225 } else if (kViewerJsPath == path) {
226 std::string js = viewer::GetJavaScript();
227 callback.Run(base::RefCountedString::TakeString(&js));
228 return;
229 } else if (kViewerViewOriginalPath == path) {
230 content::RecordAction(base::UserMetricsAction("DomDistiller_ViewOriginal"));
231 callback.Run(NULL);
232 return;
233 } else if (kFeedbackBad == path) {
234 FeedbackReporter::ReportQuality(false);
235 callback.Run(NULL);
236 if (!external_feedback_reporter_)
237 return;
238 content::WebContents* contents =
239 content::WebContents::FromRenderFrameHost(render_frame_host);
240 external_feedback_reporter_->ReportExternalFeedback(
241 contents, contents->GetURL(), false);
242 return;
243 } else if (kFeedbackGood == path) {
244 FeedbackReporter::ReportQuality(true);
245 callback.Run(NULL);
246 return;
248 content::WebContents* web_contents =
249 content::WebContents::FromRenderFrameHost(render_frame_host);
250 DCHECK(web_contents);
251 // An empty |path| is invalid, but guard against it. If not empty, assume
252 // |path| starts with '?', which is stripped away.
253 scoped_ptr<ContentDataCallback> data_callback(
254 new ContentDataCallback(callback));
255 const std::string path_after_query_separator =
256 path.size() > 0 ? path.substr(1) : "";
257 RequestViewerHandle* request_viewer_handle = new RequestViewerHandle(
258 web_contents, scheme_, path_after_query_separator, data_callback.Pass(),
259 dom_distiller_service_->GetDistilledPagePrefs());
260 scoped_ptr<ViewerHandle> viewer_handle = viewer::CreateViewRequest(
261 dom_distiller_service_, path, request_viewer_handle,
262 web_contents->GetContainerBounds().size());
264 if (viewer_handle) {
265 // The service returned a |ViewerHandle| and guarantees it will call
266 // the |RequestViewerHandle|, so passing ownership to it, to ensure the
267 // request is not cancelled. The |RequestViewerHandle| will delete itself
268 // after receiving the callback.
269 request_viewer_handle->TakeViewerHandle(viewer_handle.Pass());
270 } else {
271 request_viewer_handle->FlagAsErrorPage();
275 std::string DomDistillerViewerSource::GetMimeType(
276 const std::string& path) const {
277 if (kViewerCssPath == path) {
278 return "text/css";
280 if (kViewerJsPath == path) {
281 return "text/javascript";
283 return "text/html";
286 bool DomDistillerViewerSource::ShouldServiceRequest(
287 const net::URLRequest* request) const {
288 return request->url().SchemeIs(scheme_.c_str());
291 // TODO(nyquist): Start tracking requests using this method.
292 void DomDistillerViewerSource::WillServiceRequest(
293 const net::URLRequest* request,
294 std::string* path) const {
297 std::string DomDistillerViewerSource::GetContentSecurityPolicyObjectSrc()
298 const {
299 return "object-src 'none'; style-src 'self' https://fonts.googleapis.com;";
302 std::string DomDistillerViewerSource::GetContentSecurityPolicyFrameSrc() const {
303 return "frame-src *;";
306 } // namespace dom_distiller