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/logging.h" | |
| 11 #include "base/memory/ref_counted_memory.h" | 12 #include "base/memory/ref_counted_memory.h" |
| 12 #include "base/memory/scoped_ptr.h" | 13 #include "base/memory/scoped_ptr.h" |
| 13 #include "base/message_loop/message_loop.h" | 14 #include "base/message_loop/message_loop.h" |
| 14 #include "base/metrics/user_metrics.h" | 15 #include "base/metrics/user_metrics.h" |
| 15 #include "base/strings/utf_string_conversions.h" | 16 #include "base/strings/utf_string_conversions.h" |
| 16 #include "components/dom_distiller/core/distilled_page_prefs.h" | 17 #include "components/dom_distiller/core/distilled_page_prefs.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/task_tracker.h" | 19 #include "components/dom_distiller/core/task_tracker.h" |
| 19 #include "components/dom_distiller/core/url_constants.h" | 20 #include "components/dom_distiller/core/url_constants.h" |
| 20 #include "components/dom_distiller/core/viewer.h" | 21 #include "components/dom_distiller/core/viewer.h" |
| 21 #include "content/public/browser/navigation_details.h" | 22 #include "content/public/browser/navigation_details.h" |
| 22 #include "content/public/browser/navigation_entry.h" | 23 #include "content/public/browser/navigation_entry.h" |
| 23 #include "content/public/browser/render_frame_host.h" | 24 #include "content/public/browser/render_frame_host.h" |
| 24 #include "content/public/browser/render_view_host.h" | 25 #include "content/public/browser/render_view_host.h" |
| 25 #include "content/public/browser/user_metrics.h" | 26 #include "content/public/browser/user_metrics.h" |
| 26 #include "content/public/browser/web_contents.h" | 27 #include "content/public/browser/web_contents.h" |
| 27 #include "content/public/browser/web_contents_observer.h" | 28 #include "content/public/browser/web_contents_observer.h" |
| 29 #include "content/public/browser/web_contents_user_data.h" | |
| 28 #include "net/base/url_util.h" | 30 #include "net/base/url_util.h" |
| 29 #include "net/url_request/url_request.h" | 31 #include "net/url_request/url_request.h" |
| 30 | 32 |
| 33 namespace { | |
| 34 | |
| 35 // Handles sending conent to pages that are in an error state (i.e. the URL | |
| 36 // was bad or could not be loaded). | |
| 37 class WebContentsErrorObserver | |
| 38 : public content::WebContentsObserver, | |
| 39 public content::WebContentsUserData<WebContentsErrorObserver> { | |
| 40 public: | |
| 41 // Once the page has finished loading, send an error message. | |
| 42 void DocumentOnLoadCompletedInMainFrame() override { | |
| 43 web_contents()->GetMainFrame()->ExecuteJavaScript( | |
| 44 base::UTF8ToUTF16(dom_distiller::viewer::GetErrorPageJs())); | |
| 45 } | |
| 46 | |
| 47 private: | |
| 48 friend class content::WebContentsUserData<WebContentsErrorObserver>; | |
| 49 explicit WebContentsErrorObserver(content::WebContents* web_contents) | |
| 50 : WebContentsObserver(web_contents) {} | |
| 51 | |
| 52 ~WebContentsErrorObserver() override { | |
| 53 content::WebContentsObserver::Observe(NULL); | |
| 54 } | |
| 55 | |
| 56 DISALLOW_COPY_AND_ASSIGN(WebContentsErrorObserver); | |
| 57 }; | |
| 58 | |
| 59 // Handles sending the first page of content after the template has been | |
| 60 // loaded successfully. | |
| 61 class WebContentsTemplateObserver | |
| 62 : public content::WebContentsObserver, | |
| 63 public content::WebContentsUserData<WebContentsTemplateObserver> { | |
| 64 public: | |
| 65 explicit WebContentsTemplateObserver(content::WebContents* web_contents) { | |
| 66 content::WebContentsObserver::Observe(web_contents); | |
| 67 } | |
| 68 | |
| 69 // Once the page has finished loading, send the page(s). | |
| 70 void DocumentOnLoadCompletedInMainFrame() override { | |
| 71 web_contents()->GetMainFrame()->ExecuteJavaScript( | |
| 72 base::UTF8ToUTF16(js_page_content_)); | |
| 73 // First page has been sent, no need to keep observing. | |
| 74 content::WebContentsObserver::Observe(NULL); | |
| 75 } | |
| 76 | |
| 77 // Set the content that should be loaded immediately after the template. | |
| 78 void setInitialContent(const std::string &content) { | |
| 79 js_page_content_ = content; | |
| 80 } | |
| 81 | |
| 82 private: | |
| 83 friend class content::WebContentsUserData<WebContentsErrorObserver>; | |
| 84 | |
| 85 ~WebContentsTemplateObserver() override { | |
| 86 content::WebContentsObserver::Observe(NULL); | |
| 87 } | |
| 88 | |
| 89 // The JavaScript that puts the initial content in the page template. | |
| 90 std::string js_page_content_; | |
| 91 | |
| 92 DISALLOW_COPY_AND_ASSIGN(WebContentsTemplateObserver); | |
| 93 }; | |
| 94 | |
| 95 | |
| 96 } // namespace | |
| 97 | |
| 98 DEFINE_WEB_CONTENTS_USER_DATA_KEY(WebContentsErrorObserver); | |
| 99 | |
| 31 namespace dom_distiller { | 100 namespace dom_distiller { |
| 32 | 101 |
| 33 // Handles receiving data asynchronously for a specific entry, and passing | 102 // Handles receiving data asynchronously for a specific entry, and passing |
| 34 // it along to the data callback for the data source. Lifetime matches that of | 103 // it along to the data callback for the data source. Lifetime matches that of |
| 35 // the current main frame's page in the Viewer instance. | 104 // the current main frame's page in the Viewer instance. |
| 36 class DomDistillerViewerSource::RequestViewerHandle | 105 class DomDistillerViewerSource::RequestViewerHandle |
| 37 : public ViewRequestDelegate, | 106 : public ViewRequestDelegate, |
| 38 public content::WebContentsObserver, | 107 public content::WebContentsObserver, |
| 39 public DistilledPagePrefs::Observer { | 108 public DistilledPagePrefs::Observer { |
| 40 public: | 109 public: |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 97 // Interface for accessing preferences for distilled pages. | 166 // Interface for accessing preferences for distilled pages. |
| 98 DistilledPagePrefs* distilled_page_prefs_; | 167 DistilledPagePrefs* distilled_page_prefs_; |
| 99 | 168 |
| 100 // Whether the page is sufficiently initialized to handle updates from the | 169 // Whether the page is sufficiently initialized to handle updates from the |
| 101 // distiller. | 170 // distiller. |
| 102 bool waiting_for_page_ready_; | 171 bool waiting_for_page_ready_; |
| 103 | 172 |
| 104 // Temporary store of pending JavaScript if the page isn't ready to receive | 173 // Temporary store of pending JavaScript if the page isn't ready to receive |
| 105 // data from distillation. | 174 // data from distillation. |
| 106 std::string buffer_; | 175 std::string buffer_; |
| 176 | |
| 177 // WebContentsObserver for detecting the load completion of the page template. | |
| 178 WebContentsTemplateObserver* template_observer_; | |
| 107 }; | 179 }; |
| 108 | 180 |
| 109 DomDistillerViewerSource::RequestViewerHandle::RequestViewerHandle( | 181 DomDistillerViewerSource::RequestViewerHandle::RequestViewerHandle( |
| 110 content::WebContents* web_contents, | 182 content::WebContents* web_contents, |
| 111 const std::string& expected_scheme, | 183 const std::string& expected_scheme, |
| 112 const std::string& expected_request_path, | 184 const std::string& expected_request_path, |
| 113 const content::URLDataSource::GotDataCallback& callback, | 185 const content::URLDataSource::GotDataCallback& callback, |
| 114 DistilledPagePrefs* distilled_page_prefs) | 186 DistilledPagePrefs* distilled_page_prefs) |
| 115 : expected_scheme_(expected_scheme), | 187 : expected_scheme_(expected_scheme), |
| 116 expected_request_path_(expected_request_path), | 188 expected_request_path_(expected_request_path), |
| 117 callback_(callback), | 189 callback_(callback), |
| 118 page_count_(0), | 190 page_count_(0), |
| 119 distilled_page_prefs_(distilled_page_prefs), | 191 distilled_page_prefs_(distilled_page_prefs), |
| 120 waiting_for_page_ready_(true) { | 192 waiting_for_page_ready_(true) { |
| 121 content::WebContentsObserver::Observe(web_contents); | 193 content::WebContentsObserver::Observe(web_contents); |
| 122 distilled_page_prefs_->AddObserver(this); | 194 distilled_page_prefs_->AddObserver(this); |
| 195 template_observer_ = new WebContentsTemplateObserver(web_contents); | |
| 123 } | 196 } |
| 124 | 197 |
| 125 DomDistillerViewerSource::RequestViewerHandle::~RequestViewerHandle() { | 198 DomDistillerViewerSource::RequestViewerHandle::~RequestViewerHandle() { |
| 126 distilled_page_prefs_->RemoveObserver(this); | 199 distilled_page_prefs_->RemoveObserver(this); |
| 127 } | 200 } |
| 128 | 201 |
| 129 void DomDistillerViewerSource::RequestViewerHandle::SendJavaScript( | 202 void DomDistillerViewerSource::RequestViewerHandle::SendJavaScript( |
| 130 const std::string& buffer) { | 203 const std::string& buffer) { |
| 131 if (waiting_for_page_ready_) { | 204 if (waiting_for_page_ready_) { |
| 132 buffer_ += buffer; | 205 buffer_ += buffer; |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 143 const content::FrameNavigateParams& params) { | 216 const content::FrameNavigateParams& params) { |
| 144 const GURL& navigation = details.entry->GetURL(); | 217 const GURL& navigation = details.entry->GetURL(); |
| 145 if (details.is_in_page || ( | 218 if (details.is_in_page || ( |
| 146 navigation.SchemeIs(expected_scheme_.c_str()) && | 219 navigation.SchemeIs(expected_scheme_.c_str()) && |
| 147 expected_request_path_ == navigation.query())) { | 220 expected_request_path_ == navigation.query())) { |
| 148 // In-page navigations, as well as the main view request can be ignored. | 221 // In-page navigations, as well as the main view request can be ignored. |
| 149 return; | 222 return; |
| 150 } | 223 } |
| 151 | 224 |
| 152 Cancel(); | 225 Cancel(); |
| 153 | |
| 154 } | 226 } |
| 155 | 227 |
| 156 void DomDistillerViewerSource::RequestViewerHandle::RenderProcessGone( | 228 void DomDistillerViewerSource::RequestViewerHandle::RenderProcessGone( |
| 157 base::TerminationStatus status) { | 229 base::TerminationStatus status) { |
| 158 Cancel(); | 230 Cancel(); |
| 159 } | 231 } |
| 160 | 232 |
| 161 void DomDistillerViewerSource::RequestViewerHandle::WebContentsDestroyed() { | 233 void DomDistillerViewerSource::RequestViewerHandle::WebContentsDestroyed() { |
| 162 Cancel(); | 234 Cancel(); |
| 163 } | 235 } |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 181 if (buffer_.empty()) { | 253 if (buffer_.empty()) { |
| 182 return; | 254 return; |
| 183 } | 255 } |
| 184 web_contents()->GetMainFrame()->ExecuteJavaScript(base::UTF8ToUTF16(buffer_)); | 256 web_contents()->GetMainFrame()->ExecuteJavaScript(base::UTF8ToUTF16(buffer_)); |
| 185 buffer_.clear(); | 257 buffer_.clear(); |
| 186 } | 258 } |
| 187 | 259 |
| 188 void DomDistillerViewerSource::RequestViewerHandle::OnArticleReady( | 260 void DomDistillerViewerSource::RequestViewerHandle::OnArticleReady( |
| 189 const DistilledArticleProto* article_proto) { | 261 const DistilledArticleProto* article_proto) { |
| 190 if (page_count_ == 0) { | 262 if (page_count_ == 0) { |
| 191 // This is a single-page article. | 263 template_observer_->setInitialContent( |
| 192 std::string unsafe_page_html = | 264 viewer::GetUnsafeArticleContentJs(article_proto)); |
| 193 viewer::GetUnsafeArticleHtml( | 265 |
| 194 article_proto, | 266 std::string unsafe_page_html = viewer::GetUnsafeArticleTemplateHtml( |
| 195 distilled_page_prefs_->GetTheme(), | 267 &article_proto->pages(0), |
| 196 distilled_page_prefs_->GetFontFamily()); | 268 distilled_page_prefs_->GetTheme(), |
| 269 distilled_page_prefs_->GetFontFamily()); | |
| 197 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); | 270 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); |
| 271 | |
| 272 // Send the actual article content to the page. | |
| 198 } else if (page_count_ == article_proto->pages_size()) { | 273 } else if (page_count_ == article_proto->pages_size()) { |
| 199 // We may still be showing the "Loading" indicator. | 274 // We may still be showing the "Loading" indicator. |
| 200 SendJavaScript(viewer::GetToggleLoadingIndicatorJs(true)); | 275 SendJavaScript(viewer::GetToggleLoadingIndicatorJs(true)); |
| 201 } else { | 276 } else { |
| 202 // It's possible that we didn't get some incremental updates from the | 277 // It's possible that we didn't get some incremental updates from the |
| 203 // distiller. Ensure all remaining pages are flushed to the viewer. | 278 // distiller. Ensure all remaining pages are flushed to the viewer. |
| 204 for (;page_count_ < article_proto->pages_size(); page_count_++) { | 279 for (;page_count_ < article_proto->pages_size(); page_count_++) { |
| 205 const DistilledPageProto& page = article_proto->pages(page_count_); | 280 const DistilledPageProto& page = article_proto->pages(page_count_); |
| 206 SendJavaScript( | 281 SendJavaScript( |
| 207 viewer::GetUnsafeIncrementalDistilledPageJs( | 282 viewer::GetUnsafeIncrementalDistilledPageJs( |
| 208 &page, | 283 &page, |
| 209 page_count_ == article_proto->pages_size())); | 284 page_count_ == article_proto->pages_size())); |
| 210 } | 285 } |
| 211 } | 286 } |
| 212 // No need to hold on to the ViewerHandle now that distillation is complete. | 287 // No need to hold on to the ViewerHandle now that distillation is complete. |
| 213 viewer_handle_.reset(); | 288 viewer_handle_.reset(); |
| 214 } | 289 } |
| 215 | 290 |
| 216 void DomDistillerViewerSource::RequestViewerHandle::OnArticleUpdated( | 291 void DomDistillerViewerSource::RequestViewerHandle::OnArticleUpdated( |
| 217 ArticleDistillationUpdate article_update) { | 292 ArticleDistillationUpdate article_update) { |
| 218 for (;page_count_ < static_cast<int>(article_update.GetPagesSize()); | 293 for (;page_count_ < static_cast<int>(article_update.GetPagesSize()); |
| 219 page_count_++) { | 294 page_count_++) { |
| 220 const DistilledPageProto& page = | 295 const DistilledPageProto& page = |
| 221 article_update.GetDistilledPage(page_count_); | 296 article_update.GetDistilledPage(page_count_); |
| 222 if (page_count_ == 0) { | 297 if (page_count_ == 0) { |
| 298 template_observer_->setInitialContent( | |
| 299 viewer::GetUnsafeIncrementalDistilledPageJs(&page, false)); | |
| 300 | |
| 223 // This is the first page, so send Viewer page scaffolding too. | 301 // This is the first page, so send Viewer page scaffolding too. |
| 224 std::string unsafe_page_html = viewer::GetUnsafePartialArticleHtml( | 302 std::string unsafe_page_html = viewer::GetUnsafeArticleTemplateHtml( |
| 225 &page, | 303 &page, |
| 226 distilled_page_prefs_->GetTheme(), | 304 distilled_page_prefs_->GetTheme(), |
| 227 distilled_page_prefs_->GetFontFamily()); | 305 distilled_page_prefs_->GetFontFamily()); |
| 228 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); | 306 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); |
| 229 } else { | 307 } else { |
| 230 SendJavaScript( | 308 // Send the page content to the client. |
| 231 viewer::GetUnsafeIncrementalDistilledPageJs(&page, false)); | 309 SendJavaScript(viewer::GetUnsafeIncrementalDistilledPageJs(&page, false)); |
|
cjhopman
2015/03/24 01:45:36
Isn't it possible now that the second page has it'
mdjones
2015/03/24 18:42:52
Done/See general comment.
| |
| 232 } | 310 } |
| 233 } | 311 } |
| 234 } | 312 } |
| 235 | 313 |
| 236 void DomDistillerViewerSource::RequestViewerHandle::TakeViewerHandle( | 314 void DomDistillerViewerSource::RequestViewerHandle::TakeViewerHandle( |
| 237 scoped_ptr<ViewerHandle> viewer_handle) { | 315 scoped_ptr<ViewerHandle> viewer_handle) { |
| 238 viewer_handle_ = viewer_handle.Pass(); | 316 viewer_handle_ = viewer_handle.Pass(); |
| 239 } | 317 } |
| 240 | 318 |
| 241 void DomDistillerViewerSource::RequestViewerHandle::OnChangeTheme( | 319 void DomDistillerViewerSource::RequestViewerHandle::OnChangeTheme( |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 307 // The service returned a |ViewerHandle| and guarantees it will call | 385 // The service returned a |ViewerHandle| and guarantees it will call |
| 308 // the |RequestViewerHandle|, so passing ownership to it, to ensure the | 386 // the |RequestViewerHandle|, so passing ownership to it, to ensure the |
| 309 // request is not cancelled. The |RequestViewerHandle| will delete itself | 387 // request is not cancelled. The |RequestViewerHandle| will delete itself |
| 310 // after receiving the callback. | 388 // after receiving the callback. |
| 311 request_viewer_handle->TakeViewerHandle(viewer_handle.Pass()); | 389 request_viewer_handle->TakeViewerHandle(viewer_handle.Pass()); |
| 312 } else { | 390 } else { |
| 313 // The service did not return a |ViewerHandle|, which means the | 391 // The service did not return a |ViewerHandle|, which means the |
| 314 // |RequestViewerHandle| will never be called, so clean up now. | 392 // |RequestViewerHandle| will never be called, so clean up now. |
| 315 delete request_viewer_handle; | 393 delete request_viewer_handle; |
| 316 | 394 |
| 395 WebContentsErrorObserver::CreateForWebContents(web_contents); | |
| 396 | |
| 317 std::string error_page_html = viewer::GetErrorPageHtml( | 397 std::string error_page_html = viewer::GetErrorPageHtml( |
| 318 dom_distiller_service_->GetDistilledPagePrefs()->GetTheme(), | 398 dom_distiller_service_->GetDistilledPagePrefs()->GetTheme(), |
| 319 dom_distiller_service_->GetDistilledPagePrefs()->GetFontFamily()); | 399 dom_distiller_service_->GetDistilledPagePrefs()->GetFontFamily()); |
| 320 callback.Run(base::RefCountedString::TakeString(&error_page_html)); | 400 callback.Run(base::RefCountedString::TakeString(&error_page_html)); |
| 321 } | 401 } |
| 322 }; | 402 }; |
| 323 | 403 |
| 324 std::string DomDistillerViewerSource::GetMimeType( | 404 std::string DomDistillerViewerSource::GetMimeType( |
| 325 const std::string& path) const { | 405 const std::string& path) const { |
| 326 if (kViewerCssPath == path) { | 406 if (kViewerCssPath == path) { |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 342 const net::URLRequest* request, | 422 const net::URLRequest* request, |
| 343 std::string* path) const { | 423 std::string* path) const { |
| 344 } | 424 } |
| 345 | 425 |
| 346 std::string DomDistillerViewerSource::GetContentSecurityPolicyObjectSrc() | 426 std::string DomDistillerViewerSource::GetContentSecurityPolicyObjectSrc() |
| 347 const { | 427 const { |
| 348 return "object-src 'none'; style-src 'self' https://fonts.googleapis.com;"; | 428 return "object-src 'none'; style-src 'self' https://fonts.googleapis.com;"; |
| 349 } | 429 } |
| 350 | 430 |
| 351 } // namespace dom_distiller | 431 } // namespace dom_distiller |
| OLD | NEW |