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 "components/dom_distiller/core/task_tracker.h" | 15 #include "components/dom_distiller/core/task_tracker.h" |
| 15 #include "components/dom_distiller/core/url_constants.h" | 16 #include "components/dom_distiller/core/url_constants.h" |
| 16 #include "components/dom_distiller/core/viewer.h" | 17 #include "components/dom_distiller/core/viewer.h" |
| 18 #include "content/public/browser/navigation_details.h" | |
| 19 #include "content/public/browser/navigation_entry.h" | |
| 17 #include "content/public/browser/render_frame_host.h" | 20 #include "content/public/browser/render_frame_host.h" |
| 18 #include "content/public/browser/render_view_host.h" | 21 #include "content/public/browser/render_view_host.h" |
| 22 #include "content/public/browser/web_contents.h" | |
| 23 #include "content/public/browser/web_contents_observer.h" | |
| 24 #include "net/base/url_util.h" | |
| 19 #include "net/url_request/url_request.h" | 25 #include "net/url_request/url_request.h" |
| 20 | 26 |
| 21 namespace dom_distiller { | 27 namespace dom_distiller { |
| 22 | 28 |
| 23 // Handles receiving data asynchronously for a specific entry, and passing | 29 // Handles receiving data asynchronously for a specific entry, and passing |
| 24 // it along to the data callback for the data source. | 30 // 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. | |
| 25 class DomDistillerViewerSource::RequestViewerHandle | 32 class DomDistillerViewerSource::RequestViewerHandle |
| 26 : public ViewRequestDelegate { | 33 : public ViewRequestDelegate, |
| 34 public content::WebContentsObserver { | |
| 27 public: | 35 public: |
| 28 explicit RequestViewerHandle( | 36 explicit RequestViewerHandle( |
| 37 content::WebContents* web_contents, | |
| 38 const std::string& expected_scheme, | |
| 39 const std::string& expected_request_path, | |
| 29 const content::URLDataSource::GotDataCallback& callback); | 40 const content::URLDataSource::GotDataCallback& callback); |
| 30 virtual ~RequestViewerHandle(); | 41 virtual ~RequestViewerHandle(); |
| 31 | 42 |
| 32 // ViewRequestDelegate implementation. | 43 // ViewRequestDelegate implementation. |
| 33 virtual void OnArticleReady( | 44 virtual void OnArticleReady( |
| 34 const DistilledArticleProto* article_proto) OVERRIDE; | 45 const DistilledArticleProto* article_proto) OVERRIDE; |
| 35 | 46 |
| 36 virtual void OnArticleUpdated( | 47 virtual void OnArticleUpdated( |
| 37 ArticleDistillationUpdate article_update) OVERRIDE; | 48 ArticleDistillationUpdate article_update) OVERRIDE; |
| 38 | 49 |
| 39 void TakeViewerHandle(scoped_ptr<ViewerHandle> viewer_handle); | 50 void TakeViewerHandle(scoped_ptr<ViewerHandle> viewer_handle); |
| 40 | 51 |
| 52 // WebContentsObserver: | |
| 53 virtual void DidNavigateMainFrame( | |
| 54 const content::LoadCommittedDetails& details, | |
| 55 const content::FrameNavigateParams& params) OVERRIDE; | |
| 56 virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE; | |
| 57 virtual void WebContentsDestroyed() OVERRIDE; | |
| 58 virtual void DidFinishLoad( | |
| 59 int64 frame_id, | |
| 60 const GURL& validated_url, | |
| 61 bool is_main_frame, | |
| 62 content::RenderViewHost* render_view_host) OVERRIDE; | |
| 63 | |
| 41 private: | 64 private: |
| 65 // Sends JavaScript to the attached Viewer, buffering data if the viewer isn't | |
| 66 // ready. | |
| 67 void SendJavaScript(const std::string& buffer); | |
| 68 | |
| 69 // Cancels the current view request. Once called, no updates will be | |
| 70 // propagated to the view, and the request to DomDistillerService will be | |
| 71 // cancelled. | |
| 72 void Cancel(); | |
| 73 | |
| 42 // The handle to the view request towards the DomDistillerService. It | 74 // The handle to the view request towards the DomDistillerService. It |
| 43 // needs to be kept around to ensure the distillation request finishes. | 75 // needs to be kept around to ensure the distillation request finishes. |
| 44 scoped_ptr<ViewerHandle> viewer_handle_; | 76 scoped_ptr<ViewerHandle> viewer_handle_; |
| 45 | 77 |
| 46 // This holds the callback to where the data retrieved is sent back. | 78 // WebContents associated with the Viewer's render process. |
| 79 content::WebContents* web_contents_; | |
| 80 | |
| 81 // The scheme hosting the current view request; | |
| 82 std::string expected_scheme_; | |
| 83 | |
| 84 // The query path for the current view request. | |
| 85 std::string expected_request_path_; | |
| 86 | |
| 87 // Holds the callback to where the data retrieved is sent back. | |
| 47 content::URLDataSource::GotDataCallback callback_; | 88 content::URLDataSource::GotDataCallback callback_; |
| 89 | |
| 90 // Number of pages of the distilled article content that have been rendered by | |
| 91 // the viewer. | |
| 92 int page_count_; | |
| 93 | |
| 94 // Whether the page is sufficiently initialized to handle updates from the | |
| 95 // distiller. | |
| 96 bool waiting_for_page_ready_; | |
| 97 | |
| 98 // Temporary store of pending JavaScript if the page isn't ready to receive | |
| 99 // data from distillation. | |
| 100 std::string buffer_; | |
| 48 }; | 101 }; |
| 49 | 102 |
| 50 DomDistillerViewerSource::RequestViewerHandle::RequestViewerHandle( | 103 DomDistillerViewerSource::RequestViewerHandle::RequestViewerHandle( |
| 104 content::WebContents* web_contents, | |
| 105 const std::string& expected_scheme, | |
| 106 const std::string& expected_request_path, | |
| 51 const content::URLDataSource::GotDataCallback& callback) | 107 const content::URLDataSource::GotDataCallback& callback) |
| 52 : callback_(callback) { | 108 : web_contents_(web_contents), |
| 109 expected_scheme_(expected_scheme), | |
| 110 expected_request_path_(expected_request_path), | |
| 111 callback_(callback), | |
| 112 page_count_(0), | |
| 113 waiting_for_page_ready_(true) { | |
| 114 content::WebContentsObserver::Observe(web_contents_); | |
| 53 } | 115 } |
| 54 | 116 |
| 55 DomDistillerViewerSource::RequestViewerHandle::~RequestViewerHandle() { | 117 DomDistillerViewerSource::RequestViewerHandle::~RequestViewerHandle() { |
| 118 // Balanced with constructor although can be a no-op if frame navigated away. | |
| 119 content::WebContentsObserver::Observe(NULL); | |
| 120 } | |
| 121 | |
| 122 void DomDistillerViewerSource::RequestViewerHandle::SendJavaScript( | |
| 123 const std::string& buffer) { | |
| 124 if (waiting_for_page_ready_) { | |
| 125 buffer_ += buffer; | |
| 126 } else { | |
| 127 if (web_contents_) { | |
| 128 web_contents_->GetMainFrame()->ExecuteJavaScript( | |
| 129 base::UTF8ToUTF16(buffer)); | |
| 130 } | |
| 131 } | |
| 132 } | |
| 133 | |
| 134 void DomDistillerViewerSource::RequestViewerHandle::DidNavigateMainFrame( | |
| 135 const content::LoadCommittedDetails& details, | |
| 136 const content::FrameNavigateParams& params) { | |
| 137 const GURL& navigation = details.entry->GetURL(); | |
| 138 if (details.is_in_page || ( | |
| 139 navigation.SchemeIs(expected_scheme_.c_str()) && | |
| 140 expected_request_path_ == navigation.query())) { | |
| 141 // In-page navigations, as well as the main view request can be ignored. | |
| 142 return; | |
| 143 } | |
| 144 | |
| 145 Cancel(); | |
| 146 | |
| 147 } | |
| 148 | |
| 149 void DomDistillerViewerSource::RequestViewerHandle::RenderProcessGone( | |
| 150 base::TerminationStatus status) { | |
| 151 Cancel(); | |
| 152 } | |
| 153 | |
| 154 void DomDistillerViewerSource::RequestViewerHandle::WebContentsDestroyed() { | |
| 155 Cancel(); | |
| 156 } | |
| 157 | |
| 158 void DomDistillerViewerSource::RequestViewerHandle::Cancel() { | |
| 159 // Ensure we don't send any incremental updates to the Viewer. | |
| 160 web_contents_ = NULL; | |
| 161 | |
| 162 // No need to listen for notifications. | |
| 163 content::WebContentsObserver::Observe(NULL); | |
| 164 | |
| 165 // Schedule the Viewer for deletion. Ensures distillation is cancelled, and | |
| 166 // any pending data stored in |buffer_| is released. | |
| 167 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); | |
| 168 } | |
| 169 | |
| 170 void DomDistillerViewerSource::RequestViewerHandle::DidFinishLoad( | |
| 171 int64 frame_id, | |
| 172 const GURL& validated_url, | |
| 173 bool is_main_frame, | |
| 174 content::RenderViewHost* render_view_host) { | |
| 175 if (!is_main_frame || web_contents_ == NULL) { | |
| 176 return; | |
| 177 } | |
| 178 waiting_for_page_ready_ = false; | |
| 179 if (buffer_.empty()) { | |
| 180 return; | |
| 181 } | |
| 182 if (web_contents_) { | |
| 183 web_contents_->GetMainFrame()->ExecuteJavaScript( | |
| 184 base::UTF8ToUTF16(buffer_)); | |
| 185 } | |
| 186 buffer_.clear(); | |
| 56 } | 187 } |
| 57 | 188 |
| 58 void DomDistillerViewerSource::RequestViewerHandle::OnArticleReady( | 189 void DomDistillerViewerSource::RequestViewerHandle::OnArticleReady( |
| 59 const DistilledArticleProto* article_proto) { | 190 const DistilledArticleProto* article_proto) { |
| 60 std::string unsafe_page_html = viewer::GetUnsafeHtml(article_proto); | 191 if (page_count_ == 0) { |
| 61 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); | 192 // This is a single-page article. |
| 62 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); | 193 std::string unsafe_page_html = viewer::GetUnsafeArticleHtml(article_proto); |
| 194 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); | |
| 195 } else if (page_count_ == article_proto->pages_size()) { | |
| 196 // We may still be showing the "Loading" indicator. | |
| 197 SendJavaScript(viewer::GetToggleLoadingIndicatorJs(true)); | |
| 198 } else { | |
| 199 // It's possible that we didn't get some incremental updates from the | |
| 200 // distiller. Ensure all remaining pages are flushed to the viewer. | |
| 201 for (;page_count_ < article_proto->pages_size(); page_count_++) { | |
| 202 const DistilledPageProto& page = article_proto->pages(page_count_); | |
| 203 SendJavaScript( | |
| 204 viewer::GetUnsafeIncrementalDistilledPageJs( | |
| 205 &page, | |
| 206 page_count_ == article_proto->pages_size())); | |
| 207 } | |
| 208 } | |
| 209 // No need to hold on to the ViewerHandle now that distillation is complete. | |
| 210 viewer_handle_.reset(); | |
| 63 } | 211 } |
| 64 | 212 |
| 65 void DomDistillerViewerSource::RequestViewerHandle::OnArticleUpdated( | 213 void DomDistillerViewerSource::RequestViewerHandle::OnArticleUpdated( |
| 66 ArticleDistillationUpdate article_update) { | 214 ArticleDistillationUpdate article_update) { |
| 67 // TODO(nyquist): Add support for displaying pages incrementally. | 215 for (;page_count_ < static_cast<int>(article_update.GetPagesSize()); |
| 216 page_count_++) { | |
| 217 const DistilledPageProto& page = | |
| 218 article_update.GetDistilledPage(page_count_); | |
| 219 if (page_count_ == 0) { | |
| 220 // This is the first page, so send Viewer page scaffolding too. | |
| 221 std::string unsafe_page_html = viewer::GetUnsafePartialArticleHtml(&page); | |
| 222 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); | |
| 223 } else { | |
| 224 SendJavaScript( | |
| 225 viewer::GetUnsafeIncrementalDistilledPageJs(&page, false)); | |
| 226 } | |
| 227 } | |
| 68 } | 228 } |
| 69 | 229 |
| 70 void DomDistillerViewerSource::RequestViewerHandle::TakeViewerHandle( | 230 void DomDistillerViewerSource::RequestViewerHandle::TakeViewerHandle( |
| 71 scoped_ptr<ViewerHandle> viewer_handle) { | 231 scoped_ptr<ViewerHandle> viewer_handle) { |
| 72 viewer_handle_ = viewer_handle.Pass(); | 232 viewer_handle_ = viewer_handle.Pass(); |
| 73 } | 233 } |
| 74 | 234 |
| 75 DomDistillerViewerSource::DomDistillerViewerSource( | 235 DomDistillerViewerSource::DomDistillerViewerSource( |
| 76 DomDistillerServiceInterface* dom_distiller_service, | 236 DomDistillerServiceInterface* dom_distiller_service, |
| 77 const std::string& scheme) | 237 const std::string& scheme) |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 91 int render_frame_id, | 251 int render_frame_id, |
| 92 const content::URLDataSource::GotDataCallback& callback) { | 252 const content::URLDataSource::GotDataCallback& callback) { |
| 93 content::RenderFrameHost* render_frame_host = | 253 content::RenderFrameHost* render_frame_host = |
| 94 content::RenderFrameHost::FromID(render_process_id, render_frame_id); | 254 content::RenderFrameHost::FromID(render_process_id, render_frame_id); |
| 95 DCHECK(render_frame_host); | 255 DCHECK(render_frame_host); |
| 96 content::RenderViewHost* render_view_host = | 256 content::RenderViewHost* render_view_host = |
| 97 render_frame_host->GetRenderViewHost(); | 257 render_frame_host->GetRenderViewHost(); |
| 98 DCHECK(render_view_host); | 258 DCHECK(render_view_host); |
| 99 CHECK_EQ(0, render_view_host->GetEnabledBindings()); | 259 CHECK_EQ(0, render_view_host->GetEnabledBindings()); |
| 100 | 260 |
| 101 if (kCssPath == path) { | 261 if (kViewerCssPath == path) { |
| 102 std::string css = viewer::GetCss(); | 262 std::string css = viewer::GetCss(); |
| 103 callback.Run(base::RefCountedString::TakeString(&css)); | 263 callback.Run(base::RefCountedString::TakeString(&css)); |
| 104 return; | 264 return; |
| 105 } | 265 } |
| 106 | 266 if (kViewerJsPath == path) { |
| 267 std::string js = viewer::GetJavaScript(); | |
| 268 callback.Run(base::RefCountedString::TakeString(&js)); | |
| 269 return; | |
| 270 } | |
| 271 content::WebContents* web_contents = | |
| 272 content::WebContents::FromRenderFrameHost( | |
| 273 content::RenderFrameHost::FromID(render_process_id, | |
| 274 render_frame_id)); | |
| 275 DCHECK(web_contents); | |
| 107 RequestViewerHandle* request_viewer_handle = | 276 RequestViewerHandle* request_viewer_handle = |
| 108 new RequestViewerHandle(callback); | 277 new RequestViewerHandle(web_contents, scheme_, path.substr(1), callback); |
| 109 scoped_ptr<ViewerHandle> viewer_handle = viewer::CreateViewRequest( | 278 scoped_ptr<ViewerHandle> viewer_handle = viewer::CreateViewRequest( |
| 110 dom_distiller_service_, path, request_viewer_handle); | 279 dom_distiller_service_, path, request_viewer_handle); |
| 111 | 280 |
| 112 if (viewer_handle) { | 281 if (viewer_handle) { |
| 113 // The service returned a |ViewerHandle| and guarantees it will call | 282 // The service returned a |ViewerHandle| and guarantees it will call |
| 114 // the |RequestViewerHandle|, so passing ownership to it, to ensure the | 283 // the |RequestViewerHandle|, so passing ownership to it, to ensure the |
| 115 // request is not cancelled. The |RequestViewerHandle| will delete itself | 284 // request is not cancelled. The |RequestViewerHandle| will delete itself |
| 116 // after receiving the callback. | 285 // after receiving the callback. |
| 117 request_viewer_handle->TakeViewerHandle(viewer_handle.Pass()); | 286 request_viewer_handle->TakeViewerHandle(viewer_handle.Pass()); |
| 118 } else { | 287 } else { |
| 119 // The service did not return a |ViewerHandle|, which means the | 288 // The service did not return a |ViewerHandle|, which means the |
| 120 // |RequestViewerHandle| will never be called, so clean up now. | 289 // |RequestViewerHandle| will never be called, so clean up now. |
| 121 delete request_viewer_handle; | 290 delete request_viewer_handle; |
| 122 | 291 |
| 123 std::string error_page_html = viewer::GetErrorPageHtml(); | 292 std::string error_page_html = viewer::GetErrorPageHtml(); |
| 124 callback.Run(base::RefCountedString::TakeString(&error_page_html)); | 293 callback.Run(base::RefCountedString::TakeString(&error_page_html)); |
| 125 } | 294 } |
| 126 }; | 295 }; |
| 127 | 296 |
| 128 std::string DomDistillerViewerSource::GetMimeType( | 297 std::string DomDistillerViewerSource::GetMimeType( |
| 129 const std::string& path) const { | 298 const std::string& path) const { |
| 130 if (path == kCssPath) | 299 if (kViewerCssPath == path) { |
| 131 return "text/css"; | 300 return "text/css"; |
| 301 } | |
| 302 if (kViewerJsPath == path) { | |
| 303 return "text/javascript"; | |
| 304 } | |
| 132 return "text/html"; | 305 return "text/html"; |
| 133 } | 306 } |
| 134 | 307 |
| 135 bool DomDistillerViewerSource::ShouldServiceRequest( | 308 bool DomDistillerViewerSource::ShouldServiceRequest( |
| 136 const net::URLRequest* request) const { | 309 const net::URLRequest* request) const { |
| 137 return request->url().SchemeIs(scheme_.c_str()); | 310 return request->url().SchemeIs(scheme_.c_str()); |
| 138 } | 311 } |
| 139 | 312 |
| 140 // TODO(nyquist): Start tracking requests using this method. | 313 // TODO(nyquist): Start tracking requests using this method. |
| 141 void DomDistillerViewerSource::WillServiceRequest( | 314 void DomDistillerViewerSource::WillServiceRequest( |
| 142 const net::URLRequest* request, | 315 const net::URLRequest* request, |
| 143 std::string* path) const { | 316 std::string* path) const { |
|
nyquist
2014/05/14 17:54:35
Oh, and for future reference, you could abuse the
| |
| 144 } | 317 } |
| 145 | 318 |
| 146 std::string DomDistillerViewerSource::GetContentSecurityPolicyObjectSrc() | 319 std::string DomDistillerViewerSource::GetContentSecurityPolicyObjectSrc() |
| 147 const { | 320 const { |
| 148 return "object-src 'none'; style-src 'self';"; | 321 return "object-src 'none'; style-src 'self';"; |
| 149 } | 322 } |
| 150 | 323 |
| 151 } // namespace dom_distiller | 324 } // namespace dom_distiller |
| OLD | NEW |