Chromium Code Reviews| 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 |