OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/dom_distiller/content/dom_distiller_viewer_source.h" | 5 #include "components/dom_distiller/content/dom_distiller_viewer_source.h" |
6 | 6 |
7 #include <sstream> | 7 #include <sstream> |
8 #include <string> | 8 #include <string> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "base/memory/ref_counted_memory.h" | 11 #include "base/memory/ref_counted_memory.h" |
12 #include "base/memory/scoped_ptr.h" | 12 #include "base/memory/scoped_ptr.h" |
13 #include "base/message_loop/message_loop.h" | 13 #include "base/message_loop/message_loop.h" |
14 #include "base/metrics/user_metrics.h" | 14 #include "base/metrics/user_metrics.h" |
15 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" |
16 #include "components/dom_distiller/core/distilled_page_prefs.h" | 16 #include "components/dom_distiller/core/distilled_page_prefs.h" |
17 #include "components/dom_distiller/core/dom_distiller_request_view_base.h" | |
17 #include "components/dom_distiller/core/dom_distiller_service.h" | 18 #include "components/dom_distiller/core/dom_distiller_service.h" |
18 #include "components/dom_distiller/core/external_feedback_reporter.h" | 19 #include "components/dom_distiller/core/external_feedback_reporter.h" |
19 #include "components/dom_distiller/core/feedback_reporter.h" | 20 #include "components/dom_distiller/core/feedback_reporter.h" |
20 #include "components/dom_distiller/core/task_tracker.h" | 21 #include "components/dom_distiller/core/task_tracker.h" |
21 #include "components/dom_distiller/core/url_constants.h" | 22 #include "components/dom_distiller/core/url_constants.h" |
22 #include "components/dom_distiller/core/viewer.h" | 23 #include "components/dom_distiller/core/viewer.h" |
23 #include "content/public/browser/navigation_details.h" | 24 #include "content/public/browser/navigation_details.h" |
24 #include "content/public/browser/navigation_entry.h" | 25 #include "content/public/browser/navigation_entry.h" |
25 #include "content/public/browser/render_frame_host.h" | 26 #include "content/public/browser/render_frame_host.h" |
26 #include "content/public/browser/render_view_host.h" | 27 #include "content/public/browser/render_view_host.h" |
27 #include "content/public/browser/user_metrics.h" | 28 #include "content/public/browser/user_metrics.h" |
28 #include "content/public/browser/web_contents.h" | 29 #include "content/public/browser/web_contents.h" |
29 #include "content/public/browser/web_contents_observer.h" | 30 #include "content/public/browser/web_contents_observer.h" |
30 #include "net/base/url_util.h" | 31 #include "net/base/url_util.h" |
31 #include "net/url_request/url_request.h" | 32 #include "net/url_request/url_request.h" |
32 | 33 |
33 namespace dom_distiller { | 34 namespace dom_distiller { |
34 | 35 |
36 namespace { | |
37 | |
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; | |
nyquist
2015/04/24 22:13:09
RunCallback
mdjones
2015/04/25 00:55:03
Done.
| |
43 | |
44 private: | |
45 // The callback that actually gets run. | |
46 content::URLDataSource::GotDataCallback callback_; | |
47 }; | |
48 | |
49 ContentDataCallback::ContentDataCallback( | |
50 const content::URLDataSource::GotDataCallback& callback) { | |
51 callback_ = callback; | |
52 } | |
53 | |
54 void ContentDataCallback::runCallback(std::string& data) { | |
55 callback_.Run(base::RefCountedString::TakeString(&data)); | |
56 } | |
57 | |
58 } // namespace | |
59 | |
35 // Handles receiving data asynchronously for a specific entry, and passing | 60 // Handles receiving data asynchronously for a specific entry, and passing |
36 // it along to the data callback for the data source. Lifetime matches that of | 61 // it along to the data callback for the data source. Lifetime matches that of |
37 // the current main frame's page in the Viewer instance. | 62 // the current main frame's page in the Viewer instance. |
38 class DomDistillerViewerSource::RequestViewerHandle | 63 class DomDistillerViewerSource::RequestViewerHandle |
39 : public ViewRequestDelegate, | 64 : public DomDistillerRequestViewBase, |
40 public content::WebContentsObserver, | 65 public content::WebContentsObserver, |
41 public DistilledPagePrefs::Observer { | 66 public DistilledPagePrefs::Observer { |
42 public: | 67 public: |
43 explicit RequestViewerHandle( | 68 explicit RequestViewerHandle(content::WebContents* web_contents, |
nyquist
2015/04/24 22:13:08
Does this need to be explicit?
mdjones
2015/04/25 00:55:03
Not as far as I can tell.
| |
44 content::WebContents* web_contents, | 69 const std::string& expected_scheme, |
45 const std::string& expected_scheme, | 70 const std::string& expected_request_path, |
46 const std::string& expected_request_path, | 71 scoped_ptr<ContentDataCallback> callback, |
47 const content::URLDataSource::GotDataCallback& callback, | 72 DistilledPagePrefs* distilled_page_prefs); |
48 DistilledPagePrefs* distilled_page_prefs); | |
49 ~RequestViewerHandle() override; | 73 ~RequestViewerHandle() override; |
50 | 74 |
51 // Flag this request as an error and send the error page template. | |
52 void flagAsErrorPage(); | |
53 | |
54 // ViewRequestDelegate implementation: | |
55 void OnArticleReady(const DistilledArticleProto* article_proto) override; | |
56 | |
57 void OnArticleUpdated(ArticleDistillationUpdate article_update) override; | |
58 | |
59 void TakeViewerHandle(scoped_ptr<ViewerHandle> viewer_handle); | |
60 | |
61 // content::WebContentsObserver implementation: | 75 // content::WebContentsObserver implementation: |
62 void DidNavigateMainFrame( | 76 void DidNavigateMainFrame( |
63 const content::LoadCommittedDetails& details, | 77 const content::LoadCommittedDetails& details, |
64 const content::FrameNavigateParams& params) override; | 78 const content::FrameNavigateParams& params) override; |
65 void RenderProcessGone(base::TerminationStatus status) override; | 79 void RenderProcessGone(base::TerminationStatus status) override; |
66 void WebContentsDestroyed() override; | 80 void WebContentsDestroyed() override; |
67 void DidFinishLoad(content::RenderFrameHost* render_frame_host, | 81 void DidFinishLoad(content::RenderFrameHost* render_frame_host, |
68 const GURL& validated_url) override; | 82 const GURL& validated_url) override; |
69 | 83 |
70 private: | 84 private: |
71 // Sends JavaScript to the attached Viewer, buffering data if the viewer isn't | 85 // Sends JavaScript to the attached Viewer, buffering data if the viewer isn't |
72 // ready. | 86 // ready. |
73 void SendJavaScript(const std::string& buffer); | 87 void SendJavaScript(const std::string& buffer) override; |
74 | 88 |
75 // Cancels the current view request. Once called, no updates will be | 89 // Cancels the current view request. Once called, no updates will be |
76 // propagated to the view, and the request to DomDistillerService will be | 90 // propagated to the view, and the request to DomDistillerService will be |
77 // cancelled. | 91 // cancelled. |
78 void Cancel(); | 92 void Cancel(); |
79 | 93 |
80 // DistilledPagePrefs::Observer implementation: | 94 // DistilledPagePrefs::Observer implementation: |
81 void OnChangeFontFamily( | 95 void OnChangeFontFamily( |
nyquist
2015/04/24 22:13:09
Could these observer methods also move up to the b
mdjones
2015/04/25 00:55:03
They can. Originally I left them out because iOS d
| |
82 DistilledPagePrefs::FontFamily new_font_family) override; | 96 DistilledPagePrefs::FontFamily new_font_family) override; |
83 void OnChangeTheme(DistilledPagePrefs::Theme new_theme) override; | 97 void OnChangeTheme(DistilledPagePrefs::Theme new_theme) override; |
84 | 98 |
85 // The handle to the view request towards the DomDistillerService. It | |
86 // needs to be kept around to ensure the distillation request finishes. | |
87 scoped_ptr<ViewerHandle> viewer_handle_; | |
88 | |
89 // The scheme hosting the current view request; | 99 // The scheme hosting the current view request; |
90 std::string expected_scheme_; | 100 std::string expected_scheme_; |
91 | 101 |
92 // The query path for the current view request. | 102 // The query path for the current view request. |
93 std::string expected_request_path_; | 103 std::string expected_request_path_; |
94 | 104 |
95 // Holds the callback to where the data retrieved is sent back. | |
96 content::URLDataSource::GotDataCallback callback_; | |
97 | |
98 // Number of pages of the distilled article content that have been rendered by | |
99 // the viewer. | |
100 int page_count_; | |
101 | |
102 // Interface for accessing preferences for distilled pages. | |
103 DistilledPagePrefs* distilled_page_prefs_; | |
104 | |
105 // Whether the page is sufficiently initialized to handle updates from the | 105 // Whether the page is sufficiently initialized to handle updates from the |
106 // distiller. | 106 // distiller. |
107 bool waiting_for_page_ready_; | 107 bool waiting_for_page_ready_; |
108 | 108 |
109 // Temporary store of pending JavaScript if the page isn't ready to receive | 109 // Temporary store of pending JavaScript if the page isn't ready to receive |
110 // data from distillation. | 110 // data from distillation. |
111 std::string buffer_; | 111 std::string buffer_; |
112 | |
113 // Flag to tell this observer that the web contents are in an error state. | |
114 bool is_error_page_; | |
115 }; | 112 }; |
116 | 113 |
117 DomDistillerViewerSource::RequestViewerHandle::RequestViewerHandle( | 114 DomDistillerViewerSource::RequestViewerHandle::RequestViewerHandle( |
118 content::WebContents* web_contents, | 115 content::WebContents* web_contents, |
119 const std::string& expected_scheme, | 116 const std::string& expected_scheme, |
120 const std::string& expected_request_path, | 117 const std::string& expected_request_path, |
121 const content::URLDataSource::GotDataCallback& callback, | 118 scoped_ptr<ContentDataCallback> callback, |
122 DistilledPagePrefs* distilled_page_prefs) | 119 DistilledPagePrefs* distilled_page_prefs) |
123 : expected_scheme_(expected_scheme), | 120 : DomDistillerRequestViewBase(callback.Pass(), distilled_page_prefs), |
121 expected_scheme_(expected_scheme), | |
124 expected_request_path_(expected_request_path), | 122 expected_request_path_(expected_request_path), |
125 callback_(callback), | 123 waiting_for_page_ready_(true) { |
126 page_count_(0), | |
127 distilled_page_prefs_(distilled_page_prefs), | |
128 waiting_for_page_ready_(true), | |
129 is_error_page_(false) { | |
130 content::WebContentsObserver::Observe(web_contents); | 124 content::WebContentsObserver::Observe(web_contents); |
131 distilled_page_prefs_->AddObserver(this); | 125 distilled_page_prefs_->AddObserver(this); |
132 } | 126 } |
133 | 127 |
134 DomDistillerViewerSource::RequestViewerHandle::~RequestViewerHandle() { | 128 DomDistillerViewerSource::RequestViewerHandle::~RequestViewerHandle() { |
135 distilled_page_prefs_->RemoveObserver(this); | 129 distilled_page_prefs_->RemoveObserver(this); |
136 } | 130 } |
137 | 131 |
138 void DomDistillerViewerSource::RequestViewerHandle::flagAsErrorPage() { | |
139 is_error_page_ = true; | |
140 std::string error_page_html = viewer::GetErrorPageHtml( | |
141 distilled_page_prefs_->GetTheme(), | |
142 distilled_page_prefs_->GetFontFamily()); | |
143 callback_.Run(base::RefCountedString::TakeString(&error_page_html)); | |
144 } | |
145 | |
146 void DomDistillerViewerSource::RequestViewerHandle::SendJavaScript( | 132 void DomDistillerViewerSource::RequestViewerHandle::SendJavaScript( |
147 const std::string& buffer) { | 133 const std::string& buffer) { |
148 if (waiting_for_page_ready_) { | 134 if (waiting_for_page_ready_) { |
149 buffer_ += buffer; | 135 buffer_ += buffer; |
150 } else { | 136 } else { |
151 if (web_contents()) { | 137 if (web_contents()) { |
152 web_contents()->GetMainFrame()->ExecuteJavaScript( | 138 web_contents()->GetMainFrame()->ExecuteJavaScript( |
153 base::UTF8ToUTF16(buffer)); | 139 base::UTF8ToUTF16(buffer)); |
154 } | 140 } |
155 } | 141 } |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
202 return; | 188 return; |
203 } | 189 } |
204 waiting_for_page_ready_ = false; | 190 waiting_for_page_ready_ = false; |
205 if (buffer_.empty()) { | 191 if (buffer_.empty()) { |
206 return; | 192 return; |
207 } | 193 } |
208 web_contents()->GetMainFrame()->ExecuteJavaScript(base::UTF8ToUTF16(buffer_)); | 194 web_contents()->GetMainFrame()->ExecuteJavaScript(base::UTF8ToUTF16(buffer_)); |
209 buffer_.clear(); | 195 buffer_.clear(); |
210 } | 196 } |
211 | 197 |
212 void DomDistillerViewerSource::RequestViewerHandle::OnArticleReady( | |
213 const DistilledArticleProto* article_proto) { | |
214 // TODO(mdjones): Move this logic to super class so it can be used in both | |
215 // android and IOS. http://crbug.com/472797 | |
216 if (page_count_ == 0) { | |
217 std::string unsafe_page_html = viewer::GetUnsafeArticleTemplateHtml( | |
218 &article_proto->pages(0), | |
219 distilled_page_prefs_->GetTheme(), | |
220 distilled_page_prefs_->GetFontFamily()); | |
221 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); | |
222 // Send first page to client. | |
223 SendJavaScript(viewer::GetUnsafeArticleContentJs(article_proto)); | |
224 // If any content was loaded, show the feedback form. | |
225 SendJavaScript(viewer::GetShowFeedbackFormJs()); | |
226 } else if (page_count_ == article_proto->pages_size()) { | |
227 // We may still be showing the "Loading" indicator. | |
228 SendJavaScript(viewer::GetToggleLoadingIndicatorJs(true)); | |
229 } else { | |
230 // It's possible that we didn't get some incremental updates from the | |
231 // distiller. Ensure all remaining pages are flushed to the viewer. | |
232 for (;page_count_ < article_proto->pages_size(); page_count_++) { | |
233 const DistilledPageProto& page = article_proto->pages(page_count_); | |
234 SendJavaScript( | |
235 viewer::GetUnsafeIncrementalDistilledPageJs( | |
236 &page, | |
237 page_count_ == article_proto->pages_size())); | |
238 } | |
239 } | |
240 // No need to hold on to the ViewerHandle now that distillation is complete. | |
241 viewer_handle_.reset(); | |
242 } | |
243 | |
244 void DomDistillerViewerSource::RequestViewerHandle::OnArticleUpdated( | |
245 ArticleDistillationUpdate article_update) { | |
246 for (;page_count_ < static_cast<int>(article_update.GetPagesSize()); | |
247 page_count_++) { | |
248 const DistilledPageProto& page = | |
249 article_update.GetDistilledPage(page_count_); | |
250 // Send the page content to the client. This will execute after the page is | |
251 // ready. | |
252 SendJavaScript(viewer::GetUnsafeIncrementalDistilledPageJs(&page, false)); | |
253 | |
254 if (page_count_ == 0) { | |
255 // This is the first page, so send Viewer page scaffolding too. | |
256 std::string unsafe_page_html = viewer::GetUnsafeArticleTemplateHtml( | |
257 &page, | |
258 distilled_page_prefs_->GetTheme(), | |
259 distilled_page_prefs_->GetFontFamily()); | |
260 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); | |
261 // If any content was loaded, show the feedback form. | |
262 SendJavaScript(viewer::GetShowFeedbackFormJs()); | |
263 } | |
264 } | |
265 } | |
266 | |
267 void DomDistillerViewerSource::RequestViewerHandle::TakeViewerHandle( | |
268 scoped_ptr<ViewerHandle> viewer_handle) { | |
269 viewer_handle_ = viewer_handle.Pass(); | |
270 } | |
271 | |
272 void DomDistillerViewerSource::RequestViewerHandle::OnChangeTheme( | 198 void DomDistillerViewerSource::RequestViewerHandle::OnChangeTheme( |
273 DistilledPagePrefs::Theme new_theme) { | 199 DistilledPagePrefs::Theme new_theme) { |
274 SendJavaScript(viewer::GetDistilledPageThemeJs(new_theme)); | 200 SendJavaScript(viewer::GetDistilledPageThemeJs(new_theme)); |
275 } | 201 } |
276 | 202 |
277 void DomDistillerViewerSource::RequestViewerHandle::OnChangeFontFamily( | 203 void DomDistillerViewerSource::RequestViewerHandle::OnChangeFontFamily( |
278 DistilledPagePrefs::FontFamily new_font) { | 204 DistilledPagePrefs::FontFamily new_font) { |
279 SendJavaScript(viewer::GetDistilledPageFontFamilyJs(new_font)); | 205 SendJavaScript(viewer::GetDistilledPageFontFamilyJs(new_font)); |
280 } | 206 } |
281 | 207 |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
331 return; | 257 return; |
332 } else if (kFeedbackGood == path) { | 258 } else if (kFeedbackGood == path) { |
333 FeedbackReporter::ReportQuality(true); | 259 FeedbackReporter::ReportQuality(true); |
334 return; | 260 return; |
335 } | 261 } |
336 content::WebContents* web_contents = | 262 content::WebContents* web_contents = |
337 content::WebContents::FromRenderFrameHost(render_frame_host); | 263 content::WebContents::FromRenderFrameHost(render_frame_host); |
338 DCHECK(web_contents); | 264 DCHECK(web_contents); |
339 // An empty |path| is invalid, but guard against it. If not empty, assume | 265 // An empty |path| is invalid, but guard against it. If not empty, assume |
340 // |path| starts with '?', which is stripped away. | 266 // |path| starts with '?', which is stripped away. |
267 scoped_ptr<ContentDataCallback> data_callback( | |
268 new ContentDataCallback(callback)); | |
341 const std::string path_after_query_separator = | 269 const std::string path_after_query_separator = |
342 path.size() > 0 ? path.substr(1) : ""; | 270 path.size() > 0 ? path.substr(1) : ""; |
343 RequestViewerHandle* request_viewer_handle = new RequestViewerHandle( | 271 RequestViewerHandle* request_viewer_handle = new RequestViewerHandle( |
344 web_contents, scheme_, path_after_query_separator, callback, | 272 web_contents, scheme_, path_after_query_separator, data_callback.Pass(), |
345 dom_distiller_service_->GetDistilledPagePrefs()); | 273 dom_distiller_service_->GetDistilledPagePrefs()); |
346 scoped_ptr<ViewerHandle> viewer_handle = viewer::CreateViewRequest( | 274 scoped_ptr<ViewerHandle> viewer_handle = viewer::CreateViewRequest( |
347 dom_distiller_service_, path, request_viewer_handle, | 275 dom_distiller_service_, path, request_viewer_handle, |
348 web_contents->GetContainerBounds().size()); | 276 web_contents->GetContainerBounds().size()); |
349 | 277 |
350 if (viewer_handle) { | 278 if (viewer_handle) { |
351 // The service returned a |ViewerHandle| and guarantees it will call | 279 // The service returned a |ViewerHandle| and guarantees it will call |
352 // the |RequestViewerHandle|, so passing ownership to it, to ensure the | 280 // the |RequestViewerHandle|, so passing ownership to it, to ensure the |
353 // request is not cancelled. The |RequestViewerHandle| will delete itself | 281 // request is not cancelled. The |RequestViewerHandle| will delete itself |
354 // after receiving the callback. | 282 // after receiving the callback. |
(...skipping 28 matching lines...) Expand all Loading... | |
383 std::string DomDistillerViewerSource::GetContentSecurityPolicyObjectSrc() | 311 std::string DomDistillerViewerSource::GetContentSecurityPolicyObjectSrc() |
384 const { | 312 const { |
385 return "object-src 'none'; style-src 'self' https://fonts.googleapis.com;"; | 313 return "object-src 'none'; style-src 'self' https://fonts.googleapis.com;"; |
386 } | 314 } |
387 | 315 |
388 std::string DomDistillerViewerSource::GetContentSecurityPolicyFrameSrc() const { | 316 std::string DomDistillerViewerSource::GetContentSecurityPolicyFrameSrc() const { |
389 return "frame-src *;"; | 317 return "frame-src *;"; |
390 } | 318 } |
391 | 319 |
392 } // namespace dom_distiller | 320 } // namespace dom_distiller |
OLD | NEW |