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/strings/utf_string_conversions.h" | 14 #include "base/strings/utf_string_conversions.h" |
| 15 #include "components/dom_distiller/core/distilled_page_prefs.h" | |
| 16 #include "components/dom_distiller/core/dom_distiller_service.h" | |
| 15 #include "components/dom_distiller/core/task_tracker.h" | 17 #include "components/dom_distiller/core/task_tracker.h" |
| 16 #include "components/dom_distiller/core/url_constants.h" | 18 #include "components/dom_distiller/core/url_constants.h" |
| 17 #include "components/dom_distiller/core/viewer.h" | 19 #include "components/dom_distiller/core/viewer.h" |
| 18 #include "content/public/browser/navigation_details.h" | 20 #include "content/public/browser/navigation_details.h" |
| 19 #include "content/public/browser/navigation_entry.h" | 21 #include "content/public/browser/navigation_entry.h" |
| 20 #include "content/public/browser/render_frame_host.h" | 22 #include "content/public/browser/render_frame_host.h" |
| 21 #include "content/public/browser/render_view_host.h" | 23 #include "content/public/browser/render_view_host.h" |
| 22 #include "content/public/browser/web_contents.h" | 24 #include "content/public/browser/web_contents.h" |
| 23 #include "content/public/browser/web_contents_observer.h" | 25 #include "content/public/browser/web_contents_observer.h" |
| 24 #include "net/base/url_util.h" | 26 #include "net/base/url_util.h" |
| 25 #include "net/url_request/url_request.h" | 27 #include "net/url_request/url_request.h" |
| 26 | 28 |
| 27 namespace dom_distiller { | 29 namespace dom_distiller { |
| 28 | 30 |
| 29 // Handles receiving data asynchronously for a specific entry, and passing | 31 // Handles receiving data asynchronously for a specific entry, and passing |
| 30 // it along to the data callback for the data source. Lifetime matches that of | 32 // it along to the data callback for the data source. Lifetime matches that of |
| 31 // the current main frame's page in the Viewer instance. | 33 // the current main frame's page in the Viewer instance. |
| 32 class DomDistillerViewerSource::RequestViewerHandle | 34 class DomDistillerViewerSource::RequestViewerHandle |
| 33 : public ViewRequestDelegate, | 35 : public ViewRequestDelegate, |
| 34 public content::WebContentsObserver { | 36 public content::WebContentsObserver, |
| 37 public DistilledPagePrefs::Observer { | |
| 35 public: | 38 public: |
| 36 explicit RequestViewerHandle( | 39 explicit RequestViewerHandle( |
| 37 content::WebContents* web_contents, | 40 content::WebContents* web_contents, |
| 38 const std::string& expected_scheme, | 41 const std::string& expected_scheme, |
| 39 const std::string& expected_request_path, | 42 const std::string& expected_request_path, |
| 40 const content::URLDataSource::GotDataCallback& callback); | 43 const content::URLDataSource::GotDataCallback& callback, |
| 44 DistilledPagePrefs* distilled_page_prefs); | |
| 41 virtual ~RequestViewerHandle(); | 45 virtual ~RequestViewerHandle(); |
| 42 | 46 |
| 43 // ViewRequestDelegate implementation. | 47 // ViewRequestDelegate implementation: |
| 44 virtual void OnArticleReady( | 48 virtual void OnArticleReady( |
| 45 const DistilledArticleProto* article_proto) OVERRIDE; | 49 const DistilledArticleProto* article_proto) OVERRIDE; |
| 46 | 50 |
| 47 virtual void OnArticleUpdated( | 51 virtual void OnArticleUpdated( |
| 48 ArticleDistillationUpdate article_update) OVERRIDE; | 52 ArticleDistillationUpdate article_update) OVERRIDE; |
| 49 | 53 |
| 50 void TakeViewerHandle(scoped_ptr<ViewerHandle> viewer_handle); | 54 void TakeViewerHandle(scoped_ptr<ViewerHandle> viewer_handle); |
| 51 | 55 |
| 52 // WebContentsObserver: | 56 // content::WebContentsObserver implementation: |
| 53 virtual void DidNavigateMainFrame( | 57 virtual void DidNavigateMainFrame( |
| 54 const content::LoadCommittedDetails& details, | 58 const content::LoadCommittedDetails& details, |
| 55 const content::FrameNavigateParams& params) OVERRIDE; | 59 const content::FrameNavigateParams& params) OVERRIDE; |
| 56 virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE; | 60 virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE; |
| 57 virtual void WebContentsDestroyed() OVERRIDE; | 61 virtual void WebContentsDestroyed() OVERRIDE; |
| 58 virtual void DidFinishLoad( | 62 virtual void DidFinishLoad( |
| 59 int64 frame_id, | 63 int64 frame_id, |
| 60 const GURL& validated_url, | 64 const GURL& validated_url, |
| 61 bool is_main_frame, | 65 bool is_main_frame, |
| 62 content::RenderViewHost* render_view_host) OVERRIDE; | 66 content::RenderViewHost* render_view_host) OVERRIDE; |
| 63 | 67 |
| 64 private: | 68 private: |
| 65 // Sends JavaScript to the attached Viewer, buffering data if the viewer isn't | 69 // Sends JavaScript to the attached Viewer, buffering data if the viewer isn't |
| 66 // ready. | 70 // ready. |
| 67 void SendJavaScript(const std::string& buffer); | 71 void SendJavaScript(const std::string& buffer); |
| 68 | 72 |
| 69 // Cancels the current view request. Once called, no updates will be | 73 // Cancels the current view request. Once called, no updates will be |
| 70 // propagated to the view, and the request to DomDistillerService will be | 74 // propagated to the view, and the request to DomDistillerService will be |
| 71 // cancelled. | 75 // cancelled. |
| 72 void Cancel(); | 76 void Cancel(); |
| 73 | 77 |
| 78 // DistilledPagePrefs::Observer implementation: | |
| 79 virtual void OnChangeTheme(DistilledPagePrefs::Theme new_theme) OVERRIDE; | |
| 80 | |
| 74 // The handle to the view request towards the DomDistillerService. It | 81 // The handle to the view request towards the DomDistillerService. It |
| 75 // needs to be kept around to ensure the distillation request finishes. | 82 // needs to be kept around to ensure the distillation request finishes. |
| 76 scoped_ptr<ViewerHandle> viewer_handle_; | 83 scoped_ptr<ViewerHandle> viewer_handle_; |
| 77 | 84 |
| 78 // WebContents associated with the Viewer's render process. | 85 // WebContents associated with the Viewer's render process. |
| 79 content::WebContents* web_contents_; | 86 content::WebContents* web_contents_; |
| 80 | 87 |
| 81 // The scheme hosting the current view request; | 88 // The scheme hosting the current view request; |
| 82 std::string expected_scheme_; | 89 std::string expected_scheme_; |
| 83 | 90 |
| 84 // The query path for the current view request. | 91 // The query path for the current view request. |
| 85 std::string expected_request_path_; | 92 std::string expected_request_path_; |
| 86 | 93 |
| 87 // Holds the callback to where the data retrieved is sent back. | 94 // Holds the callback to where the data retrieved is sent back. |
| 88 content::URLDataSource::GotDataCallback callback_; | 95 content::URLDataSource::GotDataCallback callback_; |
| 89 | 96 |
| 90 // Number of pages of the distilled article content that have been rendered by | 97 // Number of pages of the distilled article content that have been rendered by |
| 91 // the viewer. | 98 // the viewer. |
| 92 int page_count_; | 99 int page_count_; |
| 93 | 100 |
| 94 // Whether the page is sufficiently initialized to handle updates from the | 101 // Whether the page is sufficiently initialized to handle updates from the |
| 95 // distiller. | 102 // distiller. |
| 96 bool waiting_for_page_ready_; | 103 bool waiting_for_page_ready_; |
| 97 | 104 |
| 98 // Temporary store of pending JavaScript if the page isn't ready to receive | 105 // Temporary store of pending JavaScript if the page isn't ready to receive |
| 99 // data from distillation. | 106 // data from distillation. |
| 100 std::string buffer_; | 107 std::string buffer_; |
| 108 | |
| 109 // Interface for accessing preferences for distilled pages. | |
| 110 DistilledPagePrefs* distilled_page_prefs_; | |
| 101 }; | 111 }; |
| 102 | 112 |
| 103 DomDistillerViewerSource::RequestViewerHandle::RequestViewerHandle( | 113 DomDistillerViewerSource::RequestViewerHandle::RequestViewerHandle( |
| 104 content::WebContents* web_contents, | 114 content::WebContents* web_contents, |
| 105 const std::string& expected_scheme, | 115 const std::string& expected_scheme, |
| 106 const std::string& expected_request_path, | 116 const std::string& expected_request_path, |
| 107 const content::URLDataSource::GotDataCallback& callback) | 117 const content::URLDataSource::GotDataCallback& callback, |
| 118 DistilledPagePrefs* distilled_page_prefs) | |
| 108 : web_contents_(web_contents), | 119 : web_contents_(web_contents), |
| 109 expected_scheme_(expected_scheme), | 120 expected_scheme_(expected_scheme), |
| 110 expected_request_path_(expected_request_path), | 121 expected_request_path_(expected_request_path), |
| 111 callback_(callback), | 122 callback_(callback), |
| 112 page_count_(0), | 123 page_count_(0), |
| 113 waiting_for_page_ready_(true) { | 124 waiting_for_page_ready_(true), |
| 125 distilled_page_prefs_(distilled_page_prefs) { | |
| 114 content::WebContentsObserver::Observe(web_contents_); | 126 content::WebContentsObserver::Observe(web_contents_); |
| 127 distilled_page_prefs_->AddObserver(this); | |
| 115 } | 128 } |
| 116 | 129 |
| 117 DomDistillerViewerSource::RequestViewerHandle::~RequestViewerHandle() { | 130 DomDistillerViewerSource::RequestViewerHandle::~RequestViewerHandle() { |
| 118 // Balanced with constructor although can be a no-op if frame navigated away. | 131 // Balanced with constructor although can be a no-op if frame navigated away. |
| 119 content::WebContentsObserver::Observe(NULL); | 132 content::WebContentsObserver::Observe(NULL); |
| 133 distilled_page_prefs_->RemoveObserver(this); | |
| 120 } | 134 } |
| 121 | 135 |
| 122 void DomDistillerViewerSource::RequestViewerHandle::SendJavaScript( | 136 void DomDistillerViewerSource::RequestViewerHandle::SendJavaScript( |
| 123 const std::string& buffer) { | 137 const std::string& buffer) { |
| 124 if (waiting_for_page_ready_) { | 138 if (waiting_for_page_ready_) { |
| 125 buffer_ += buffer; | 139 buffer_ += buffer; |
| 126 } else { | 140 } else { |
| 127 if (web_contents_) { | 141 if (web_contents_) { |
| 128 web_contents_->GetMainFrame()->ExecuteJavaScript( | 142 web_contents_->GetMainFrame()->ExecuteJavaScript( |
| 129 base::UTF8ToUTF16(buffer)); | 143 base::UTF8ToUTF16(buffer)); |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 183 web_contents_->GetMainFrame()->ExecuteJavaScript( | 197 web_contents_->GetMainFrame()->ExecuteJavaScript( |
| 184 base::UTF8ToUTF16(buffer_)); | 198 base::UTF8ToUTF16(buffer_)); |
| 185 } | 199 } |
| 186 buffer_.clear(); | 200 buffer_.clear(); |
| 187 } | 201 } |
| 188 | 202 |
| 189 void DomDistillerViewerSource::RequestViewerHandle::OnArticleReady( | 203 void DomDistillerViewerSource::RequestViewerHandle::OnArticleReady( |
| 190 const DistilledArticleProto* article_proto) { | 204 const DistilledArticleProto* article_proto) { |
| 191 if (page_count_ == 0) { | 205 if (page_count_ == 0) { |
| 192 // This is a single-page article. | 206 // This is a single-page article. |
| 193 std::string unsafe_page_html = viewer::GetUnsafeArticleHtml(article_proto); | 207 std::string unsafe_page_html = viewer::GetUnsafeArticleHtml( |
| 208 article_proto, distilled_page_prefs_->GetTheme()); | |
| 194 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); | 209 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); |
| 195 } else if (page_count_ == article_proto->pages_size()) { | 210 } else if (page_count_ == article_proto->pages_size()) { |
| 196 // We may still be showing the "Loading" indicator. | 211 // We may still be showing the "Loading" indicator. |
| 197 SendJavaScript(viewer::GetToggleLoadingIndicatorJs(true)); | 212 SendJavaScript(viewer::GetToggleLoadingIndicatorJs(true)); |
| 198 } else { | 213 } else { |
| 199 // It's possible that we didn't get some incremental updates from the | 214 // It's possible that we didn't get some incremental updates from the |
| 200 // distiller. Ensure all remaining pages are flushed to the viewer. | 215 // distiller. Ensure all remaining pages are flushed to the viewer. |
| 201 for (;page_count_ < article_proto->pages_size(); page_count_++) { | 216 for (;page_count_ < article_proto->pages_size(); page_count_++) { |
| 202 const DistilledPageProto& page = article_proto->pages(page_count_); | 217 const DistilledPageProto& page = article_proto->pages(page_count_); |
| 203 SendJavaScript( | 218 SendJavaScript( |
| 204 viewer::GetUnsafeIncrementalDistilledPageJs( | 219 viewer::GetUnsafeIncrementalDistilledPageJs( |
| 205 &page, | 220 &page, |
| 206 page_count_ == article_proto->pages_size())); | 221 page_count_ == article_proto->pages_size())); |
| 207 } | 222 } |
| 208 } | 223 } |
| 209 // No need to hold on to the ViewerHandle now that distillation is complete. | 224 // No need to hold on to the ViewerHandle now that distillation is complete. |
| 210 viewer_handle_.reset(); | 225 viewer_handle_.reset(); |
| 211 } | 226 } |
| 212 | 227 |
| 213 void DomDistillerViewerSource::RequestViewerHandle::OnArticleUpdated( | 228 void DomDistillerViewerSource::RequestViewerHandle::OnArticleUpdated( |
| 214 ArticleDistillationUpdate article_update) { | 229 ArticleDistillationUpdate article_update) { |
| 215 for (;page_count_ < static_cast<int>(article_update.GetPagesSize()); | 230 for (;page_count_ < static_cast<int>(article_update.GetPagesSize()); |
| 216 page_count_++) { | 231 page_count_++) { |
| 217 const DistilledPageProto& page = | 232 const DistilledPageProto& page = |
| 218 article_update.GetDistilledPage(page_count_); | 233 article_update.GetDistilledPage(page_count_); |
| 219 if (page_count_ == 0) { | 234 if (page_count_ == 0) { |
| 220 // This is the first page, so send Viewer page scaffolding too. | 235 // This is the first page, so send Viewer page scaffolding too. |
| 221 std::string unsafe_page_html = viewer::GetUnsafePartialArticleHtml(&page); | 236 std::string unsafe_page_html = viewer::GetUnsafePartialArticleHtml( |
| 237 &page, distilled_page_prefs_->GetTheme()); | |
| 222 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); | 238 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); |
| 223 } else { | 239 } else { |
| 224 SendJavaScript( | 240 SendJavaScript( |
| 225 viewer::GetUnsafeIncrementalDistilledPageJs(&page, false)); | 241 viewer::GetUnsafeIncrementalDistilledPageJs(&page, false)); |
| 226 } | 242 } |
| 227 } | 243 } |
| 228 } | 244 } |
| 229 | 245 |
| 230 void DomDistillerViewerSource::RequestViewerHandle::TakeViewerHandle( | 246 void DomDistillerViewerSource::RequestViewerHandle::TakeViewerHandle( |
| 231 scoped_ptr<ViewerHandle> viewer_handle) { | 247 scoped_ptr<ViewerHandle> viewer_handle) { |
| 232 viewer_handle_ = viewer_handle.Pass(); | 248 viewer_handle_ = viewer_handle.Pass(); |
| 233 } | 249 } |
| 234 | 250 |
| 251 void DomDistillerViewerSource::RequestViewerHandle::OnChangeTheme( | |
|
nyquist
2014/07/09 20:38:32
It should be possible to test this in //chrome/bro
smaslo
2014/07/10 17:01:06
Done.
| |
| 252 DistilledPagePrefs::Theme new_theme) { | |
| 253 SendJavaScript(viewer::GetDistilledPageThemeJs(new_theme)); | |
| 254 } | |
| 255 | |
| 235 DomDistillerViewerSource::DomDistillerViewerSource( | 256 DomDistillerViewerSource::DomDistillerViewerSource( |
| 236 DomDistillerServiceInterface* dom_distiller_service, | 257 DomDistillerServiceInterface* dom_distiller_service, |
| 237 const std::string& scheme) | 258 const std::string& scheme) |
| 238 : scheme_(scheme), dom_distiller_service_(dom_distiller_service) { | 259 : scheme_(scheme), dom_distiller_service_(dom_distiller_service) { |
| 239 } | 260 } |
| 240 | 261 |
| 241 DomDistillerViewerSource::~DomDistillerViewerSource() { | 262 DomDistillerViewerSource::~DomDistillerViewerSource() { |
| 242 } | 263 } |
| 243 | 264 |
| 244 std::string DomDistillerViewerSource::GetSource() const { | 265 std::string DomDistillerViewerSource::GetSource() const { |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 271 content::WebContents* web_contents = | 292 content::WebContents* web_contents = |
| 272 content::WebContents::FromRenderFrameHost( | 293 content::WebContents::FromRenderFrameHost( |
| 273 content::RenderFrameHost::FromID(render_process_id, | 294 content::RenderFrameHost::FromID(render_process_id, |
| 274 render_frame_id)); | 295 render_frame_id)); |
| 275 DCHECK(web_contents); | 296 DCHECK(web_contents); |
| 276 // An empty |path| is invalid, but guard against it. If not empty, assume | 297 // An empty |path| is invalid, but guard against it. If not empty, assume |
| 277 // |path| starts with '?', which is stripped away. | 298 // |path| starts with '?', which is stripped away. |
| 278 const std::string path_after_query_separator = | 299 const std::string path_after_query_separator = |
| 279 path.size() > 0 ? path.substr(1) : ""; | 300 path.size() > 0 ? path.substr(1) : ""; |
| 280 RequestViewerHandle* request_viewer_handle = new RequestViewerHandle( | 301 RequestViewerHandle* request_viewer_handle = new RequestViewerHandle( |
| 281 web_contents, scheme_, path_after_query_separator, callback); | 302 web_contents, scheme_, path_after_query_separator, callback, |
| 303 dom_distiller_service_->GetDistilledPagePrefs()); | |
| 282 scoped_ptr<ViewerHandle> viewer_handle = viewer::CreateViewRequest( | 304 scoped_ptr<ViewerHandle> viewer_handle = viewer::CreateViewRequest( |
| 283 dom_distiller_service_, path, request_viewer_handle); | 305 dom_distiller_service_, path, request_viewer_handle); |
| 284 | 306 |
| 285 if (viewer_handle) { | 307 if (viewer_handle) { |
| 286 // The service returned a |ViewerHandle| and guarantees it will call | 308 // The service returned a |ViewerHandle| and guarantees it will call |
| 287 // the |RequestViewerHandle|, so passing ownership to it, to ensure the | 309 // the |RequestViewerHandle|, so passing ownership to it, to ensure the |
| 288 // request is not cancelled. The |RequestViewerHandle| will delete itself | 310 // request is not cancelled. The |RequestViewerHandle| will delete itself |
| 289 // after receiving the callback. | 311 // after receiving the callback. |
| 290 request_viewer_handle->TakeViewerHandle(viewer_handle.Pass()); | 312 request_viewer_handle->TakeViewerHandle(viewer_handle.Pass()); |
| 291 } else { | 313 } else { |
| 292 // The service did not return a |ViewerHandle|, which means the | 314 // The service did not return a |ViewerHandle|, which means the |
| 293 // |RequestViewerHandle| will never be called, so clean up now. | 315 // |RequestViewerHandle| will never be called, so clean up now. |
| 294 delete request_viewer_handle; | 316 delete request_viewer_handle; |
| 295 | 317 |
| 296 std::string error_page_html = viewer::GetErrorPageHtml(); | 318 std::string error_page_html = viewer::GetErrorPageHtml( |
| 319 dom_distiller_service_->GetDistilledPagePrefs()->GetTheme()); | |
| 297 callback.Run(base::RefCountedString::TakeString(&error_page_html)); | 320 callback.Run(base::RefCountedString::TakeString(&error_page_html)); |
| 298 } | 321 } |
| 299 }; | 322 }; |
| 300 | 323 |
| 301 std::string DomDistillerViewerSource::GetMimeType( | 324 std::string DomDistillerViewerSource::GetMimeType( |
| 302 const std::string& path) const { | 325 const std::string& path) const { |
| 303 if (kViewerCssPath == path) { | 326 if (kViewerCssPath == path) { |
| 304 return "text/css"; | 327 return "text/css"; |
| 305 } | 328 } |
| 306 if (kViewerJsPath == path) { | 329 if (kViewerJsPath == path) { |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 319 const net::URLRequest* request, | 342 const net::URLRequest* request, |
| 320 std::string* path) const { | 343 std::string* path) const { |
| 321 } | 344 } |
| 322 | 345 |
| 323 std::string DomDistillerViewerSource::GetContentSecurityPolicyObjectSrc() | 346 std::string DomDistillerViewerSource::GetContentSecurityPolicyObjectSrc() |
| 324 const { | 347 const { |
| 325 return "object-src 'none'; style-src 'self';"; | 348 return "object-src 'none'; style-src 'self';"; |
| 326 } | 349 } |
| 327 | 350 |
| 328 } // namespace dom_distiller | 351 } // namespace dom_distiller |
| OLD | NEW |