Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(246)

Side by Side Diff: components/dom_distiller/content/dom_distiller_viewer_source.cc

Issue 260073009: [dom_distiller] Add support for incremental viewer. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698