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; |
| 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 { | |
42 public: | 66 public: |
43 explicit RequestViewerHandle( | 67 RequestViewerHandle(content::WebContents* web_contents, |
44 content::WebContents* web_contents, | 68 const std::string& expected_scheme, |
45 const std::string& expected_scheme, | 69 const std::string& expected_request_path, |
46 const std::string& expected_request_path, | 70 scoped_ptr<ContentDataCallback> callback, |
47 const content::URLDataSource::GotDataCallback& callback, | 71 DistilledPagePrefs* distilled_page_prefs); |
48 DistilledPagePrefs* distilled_page_prefs); | |
49 ~RequestViewerHandle() override; | 72 ~RequestViewerHandle() override; |
50 | 73 |
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: | 74 // content::WebContentsObserver implementation: |
62 void DidNavigateMainFrame( | 75 void DidNavigateMainFrame( |
63 const content::LoadCommittedDetails& details, | 76 const content::LoadCommittedDetails& details, |
64 const content::FrameNavigateParams& params) override; | 77 const content::FrameNavigateParams& params) override; |
65 void RenderProcessGone(base::TerminationStatus status) override; | 78 void RenderProcessGone(base::TerminationStatus status) override; |
66 void WebContentsDestroyed() override; | 79 void WebContentsDestroyed() override; |
67 void DidFinishLoad(content::RenderFrameHost* render_frame_host, | 80 void DidFinishLoad(content::RenderFrameHost* render_frame_host, |
68 const GURL& validated_url) override; | 81 const GURL& validated_url) override; |
69 | 82 |
70 private: | 83 private: |
71 // Sends JavaScript to the attached Viewer, buffering data if the viewer isn't | 84 // Sends JavaScript to the attached Viewer, buffering data if the viewer isn't |
72 // ready. | 85 // ready. |
73 void SendJavaScript(const std::string& buffer); | 86 void SendJavaScript(const std::string& buffer) override; |
74 | 87 |
75 // Cancels the current view request. Once called, no updates will be | 88 // Cancels the current view request. Once called, no updates will be |
76 // propagated to the view, and the request to DomDistillerService will be | 89 // propagated to the view, and the request to DomDistillerService will be |
77 // cancelled. | 90 // cancelled. |
78 void Cancel(); | 91 void Cancel(); |
79 | 92 |
80 // DistilledPagePrefs::Observer implementation: | |
81 void OnChangeFontFamily( | |
82 DistilledPagePrefs::FontFamily new_font_family) override; | |
83 void OnChangeTheme(DistilledPagePrefs::Theme new_theme) override; | |
84 | |
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; | 93 // The scheme hosting the current view request; |
90 std::string expected_scheme_; | 94 std::string expected_scheme_; |
91 | 95 |
92 // The query path for the current view request. | 96 // The query path for the current view request. |
93 std::string expected_request_path_; | 97 std::string expected_request_path_; |
94 | 98 |
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 | 99 // Whether the page is sufficiently initialized to handle updates from the |
106 // distiller. | 100 // distiller. |
107 bool waiting_for_page_ready_; | 101 bool waiting_for_page_ready_; |
108 | 102 |
109 // Temporary store of pending JavaScript if the page isn't ready to receive | 103 // Temporary store of pending JavaScript if the page isn't ready to receive |
110 // data from distillation. | 104 // data from distillation. |
111 std::string buffer_; | 105 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 }; | 106 }; |
116 | 107 |
117 DomDistillerViewerSource::RequestViewerHandle::RequestViewerHandle( | 108 DomDistillerViewerSource::RequestViewerHandle::RequestViewerHandle( |
118 content::WebContents* web_contents, | 109 content::WebContents* web_contents, |
119 const std::string& expected_scheme, | 110 const std::string& expected_scheme, |
120 const std::string& expected_request_path, | 111 const std::string& expected_request_path, |
121 const content::URLDataSource::GotDataCallback& callback, | 112 scoped_ptr<ContentDataCallback> callback, |
122 DistilledPagePrefs* distilled_page_prefs) | 113 DistilledPagePrefs* distilled_page_prefs) |
123 : expected_scheme_(expected_scheme), | 114 : DomDistillerRequestViewBase(callback.Pass(), distilled_page_prefs), |
| 115 expected_scheme_(expected_scheme), |
124 expected_request_path_(expected_request_path), | 116 expected_request_path_(expected_request_path), |
125 callback_(callback), | 117 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); | 118 content::WebContentsObserver::Observe(web_contents); |
131 distilled_page_prefs_->AddObserver(this); | 119 distilled_page_prefs_->AddObserver(this); |
132 } | 120 } |
133 | 121 |
134 DomDistillerViewerSource::RequestViewerHandle::~RequestViewerHandle() { | 122 DomDistillerViewerSource::RequestViewerHandle::~RequestViewerHandle() { |
135 distilled_page_prefs_->RemoveObserver(this); | 123 distilled_page_prefs_->RemoveObserver(this); |
136 } | 124 } |
137 | 125 |
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( | 126 void DomDistillerViewerSource::RequestViewerHandle::SendJavaScript( |
147 const std::string& buffer) { | 127 const std::string& buffer) { |
148 if (waiting_for_page_ready_) { | 128 if (waiting_for_page_ready_) { |
149 buffer_ += buffer; | 129 buffer_ += buffer; |
150 } else { | 130 } else { |
151 if (web_contents()) { | 131 if (web_contents()) { |
152 web_contents()->GetMainFrame()->ExecuteJavaScript( | 132 web_contents()->GetMainFrame()->ExecuteJavaScript( |
153 base::UTF8ToUTF16(buffer)); | 133 base::UTF8ToUTF16(buffer)); |
154 } | 134 } |
155 } | 135 } |
(...skipping 27 matching lines...) Expand all Loading... |
183 content::WebContentsObserver::Observe(NULL); | 163 content::WebContentsObserver::Observe(NULL); |
184 | 164 |
185 // Schedule the Viewer for deletion. Ensures distillation is cancelled, and | 165 // Schedule the Viewer for deletion. Ensures distillation is cancelled, and |
186 // any pending data stored in |buffer_| is released. | 166 // any pending data stored in |buffer_| is released. |
187 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); | 167 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); |
188 } | 168 } |
189 | 169 |
190 void DomDistillerViewerSource::RequestViewerHandle::DidFinishLoad( | 170 void DomDistillerViewerSource::RequestViewerHandle::DidFinishLoad( |
191 content::RenderFrameHost* render_frame_host, | 171 content::RenderFrameHost* render_frame_host, |
192 const GURL& validated_url) { | 172 const GURL& validated_url) { |
193 if (is_error_page_) { | 173 if (IsErrorPage()) { |
194 waiting_for_page_ready_ = false; | 174 waiting_for_page_ready_ = false; |
195 SendJavaScript(viewer::GetErrorPageJs()); | 175 SendJavaScript(viewer::GetErrorPageJs()); |
196 SendJavaScript(viewer::GetShowFeedbackFormJs()); | 176 SendJavaScript(viewer::GetShowFeedbackFormJs()); |
197 Cancel(); // This will cause the object to clean itself up. | 177 Cancel(); // This will cause the object to clean itself up. |
198 return; | 178 return; |
199 } | 179 } |
200 | 180 |
201 if (render_frame_host->GetParent()) { | 181 if (render_frame_host->GetParent()) { |
202 return; | 182 return; |
203 } | 183 } |
204 waiting_for_page_ready_ = false; | 184 waiting_for_page_ready_ = false; |
205 if (buffer_.empty()) { | 185 if (buffer_.empty()) { |
206 return; | 186 return; |
207 } | 187 } |
208 web_contents()->GetMainFrame()->ExecuteJavaScript(base::UTF8ToUTF16(buffer_)); | 188 web_contents()->GetMainFrame()->ExecuteJavaScript(base::UTF8ToUTF16(buffer_)); |
209 buffer_.clear(); | 189 buffer_.clear(); |
210 } | 190 } |
211 | 191 |
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( | |
273 DistilledPagePrefs::Theme new_theme) { | |
274 SendJavaScript(viewer::GetDistilledPageThemeJs(new_theme)); | |
275 } | |
276 | |
277 void DomDistillerViewerSource::RequestViewerHandle::OnChangeFontFamily( | |
278 DistilledPagePrefs::FontFamily new_font) { | |
279 SendJavaScript(viewer::GetDistilledPageFontFamilyJs(new_font)); | |
280 } | |
281 | |
282 DomDistillerViewerSource::DomDistillerViewerSource( | 192 DomDistillerViewerSource::DomDistillerViewerSource( |
283 DomDistillerServiceInterface* dom_distiller_service, | 193 DomDistillerServiceInterface* dom_distiller_service, |
284 const std::string& scheme, | 194 const std::string& scheme, |
285 scoped_ptr<ExternalFeedbackReporter> external_reporter) | 195 scoped_ptr<ExternalFeedbackReporter> external_reporter) |
286 : scheme_(scheme), | 196 : scheme_(scheme), |
287 dom_distiller_service_(dom_distiller_service), | 197 dom_distiller_service_(dom_distiller_service), |
288 external_feedback_reporter_(external_reporter.Pass()) { | 198 external_feedback_reporter_(external_reporter.Pass()) { |
289 } | 199 } |
290 | 200 |
291 DomDistillerViewerSource::~DomDistillerViewerSource() { | 201 DomDistillerViewerSource::~DomDistillerViewerSource() { |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
331 return; | 241 return; |
332 } else if (kFeedbackGood == path) { | 242 } else if (kFeedbackGood == path) { |
333 FeedbackReporter::ReportQuality(true); | 243 FeedbackReporter::ReportQuality(true); |
334 return; | 244 return; |
335 } | 245 } |
336 content::WebContents* web_contents = | 246 content::WebContents* web_contents = |
337 content::WebContents::FromRenderFrameHost(render_frame_host); | 247 content::WebContents::FromRenderFrameHost(render_frame_host); |
338 DCHECK(web_contents); | 248 DCHECK(web_contents); |
339 // An empty |path| is invalid, but guard against it. If not empty, assume | 249 // An empty |path| is invalid, but guard against it. If not empty, assume |
340 // |path| starts with '?', which is stripped away. | 250 // |path| starts with '?', which is stripped away. |
| 251 scoped_ptr<ContentDataCallback> data_callback( |
| 252 new ContentDataCallback(callback)); |
341 const std::string path_after_query_separator = | 253 const std::string path_after_query_separator = |
342 path.size() > 0 ? path.substr(1) : ""; | 254 path.size() > 0 ? path.substr(1) : ""; |
343 RequestViewerHandle* request_viewer_handle = new RequestViewerHandle( | 255 RequestViewerHandle* request_viewer_handle = new RequestViewerHandle( |
344 web_contents, scheme_, path_after_query_separator, callback, | 256 web_contents, scheme_, path_after_query_separator, data_callback.Pass(), |
345 dom_distiller_service_->GetDistilledPagePrefs()); | 257 dom_distiller_service_->GetDistilledPagePrefs()); |
346 scoped_ptr<ViewerHandle> viewer_handle = viewer::CreateViewRequest( | 258 scoped_ptr<ViewerHandle> viewer_handle = viewer::CreateViewRequest( |
347 dom_distiller_service_, path, request_viewer_handle, | 259 dom_distiller_service_, path, request_viewer_handle, |
348 web_contents->GetContainerBounds().size()); | 260 web_contents->GetContainerBounds().size()); |
349 | 261 |
350 if (viewer_handle) { | 262 if (viewer_handle) { |
351 // The service returned a |ViewerHandle| and guarantees it will call | 263 // The service returned a |ViewerHandle| and guarantees it will call |
352 // the |RequestViewerHandle|, so passing ownership to it, to ensure the | 264 // the |RequestViewerHandle|, so passing ownership to it, to ensure the |
353 // request is not cancelled. The |RequestViewerHandle| will delete itself | 265 // request is not cancelled. The |RequestViewerHandle| will delete itself |
354 // after receiving the callback. | 266 // after receiving the callback. |
355 request_viewer_handle->TakeViewerHandle(viewer_handle.Pass()); | 267 request_viewer_handle->TakeViewerHandle(viewer_handle.Pass()); |
356 } else { | 268 } else { |
357 request_viewer_handle->flagAsErrorPage(); | 269 request_viewer_handle->FlagAsErrorPage(); |
358 } | 270 } |
359 }; | 271 }; |
360 | 272 |
361 std::string DomDistillerViewerSource::GetMimeType( | 273 std::string DomDistillerViewerSource::GetMimeType( |
362 const std::string& path) const { | 274 const std::string& path) const { |
363 if (kViewerCssPath == path) { | 275 if (kViewerCssPath == path) { |
364 return "text/css"; | 276 return "text/css"; |
365 } | 277 } |
366 if (kViewerJsPath == path) { | 278 if (kViewerJsPath == path) { |
367 return "text/javascript"; | 279 return "text/javascript"; |
(...skipping 15 matching lines...) Expand all Loading... |
383 std::string DomDistillerViewerSource::GetContentSecurityPolicyObjectSrc() | 295 std::string DomDistillerViewerSource::GetContentSecurityPolicyObjectSrc() |
384 const { | 296 const { |
385 return "object-src 'none'; style-src 'self' https://fonts.googleapis.com;"; | 297 return "object-src 'none'; style-src 'self' https://fonts.googleapis.com;"; |
386 } | 298 } |
387 | 299 |
388 std::string DomDistillerViewerSource::GetContentSecurityPolicyFrameSrc() const { | 300 std::string DomDistillerViewerSource::GetContentSecurityPolicyFrameSrc() const { |
389 return "frame-src *;"; | 301 return "frame-src *;"; |
390 } | 302 } |
391 | 303 |
392 } // namespace dom_distiller | 304 } // namespace dom_distiller |
OLD | NEW |