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 the first page of content after the template has been | |
36 // loaded successfully. | |
37 class WebContentsTemplateObserver | |
38 : public content::WebContentsObserver, | |
39 public content::WebContentsUserData<WebContentsTemplateObserver> { | |
40 public: | |
41 explicit WebContentsTemplateObserver(content::WebContents* web_contents) { | |
42 content::WebContentsObserver::Observe(web_contents); | |
43 } | |
44 | |
45 // Once the page has finished loading, send the page(s). | |
46 void DocumentOnLoadCompletedInMainFrame() override { | |
47 web_contents()->GetMainFrame()->ExecuteJavaScript( | |
48 base::UTF8ToUTF16(js_page_content_)); | |
49 // First page has been sent, no need to keep observing. | |
50 content::WebContentsObserver::Observe(NULL); | |
51 } | |
52 | |
53 // Set the content that should be loaded immediately after the template. | |
54 void setInitialContent(const std::string &content) { | |
55 js_page_content_ = content; | |
56 } | |
57 | |
58 private: | |
59 friend class content::WebContentsUserData<WebContentsTemplateObserver>; | |
60 | |
61 ~WebContentsTemplateObserver() override { | |
62 content::WebContentsObserver::Observe(NULL); | |
63 } | |
64 | |
65 // The JavaScript that puts the initial content in the page template. | |
66 std::string js_page_content_; | |
67 | |
68 DISALLOW_COPY_AND_ASSIGN(WebContentsTemplateObserver); | |
69 }; | |
70 | |
71 } // namespace | |
72 | |
31 namespace dom_distiller { | 73 namespace dom_distiller { |
32 | 74 |
33 // Handles receiving data asynchronously for a specific entry, and passing | 75 // 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 | 76 // 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. | 77 // the current main frame's page in the Viewer instance. |
36 class DomDistillerViewerSource::RequestViewerHandle | 78 class DomDistillerViewerSource::RequestViewerHandle |
37 : public ViewRequestDelegate, | 79 : public ViewRequestDelegate, |
38 public content::WebContentsObserver, | 80 public content::WebContentsObserver, |
39 public DistilledPagePrefs::Observer { | 81 public DistilledPagePrefs::Observer { |
40 public: | 82 public: |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
97 // Interface for accessing preferences for distilled pages. | 139 // Interface for accessing preferences for distilled pages. |
98 DistilledPagePrefs* distilled_page_prefs_; | 140 DistilledPagePrefs* distilled_page_prefs_; |
99 | 141 |
100 // Whether the page is sufficiently initialized to handle updates from the | 142 // Whether the page is sufficiently initialized to handle updates from the |
101 // distiller. | 143 // distiller. |
102 bool waiting_for_page_ready_; | 144 bool waiting_for_page_ready_; |
103 | 145 |
104 // Temporary store of pending JavaScript if the page isn't ready to receive | 146 // Temporary store of pending JavaScript if the page isn't ready to receive |
105 // data from distillation. | 147 // data from distillation. |
106 std::string buffer_; | 148 std::string buffer_; |
149 | |
150 // WebContentsObserver for detecting the load completion of the page template. | |
151 WebContentsTemplateObserver* template_observer_; | |
107 }; | 152 }; |
108 | 153 |
109 DomDistillerViewerSource::RequestViewerHandle::RequestViewerHandle( | 154 DomDistillerViewerSource::RequestViewerHandle::RequestViewerHandle( |
110 content::WebContents* web_contents, | 155 content::WebContents* web_contents, |
111 const std::string& expected_scheme, | 156 const std::string& expected_scheme, |
112 const std::string& expected_request_path, | 157 const std::string& expected_request_path, |
113 const content::URLDataSource::GotDataCallback& callback, | 158 const content::URLDataSource::GotDataCallback& callback, |
114 DistilledPagePrefs* distilled_page_prefs) | 159 DistilledPagePrefs* distilled_page_prefs) |
115 : expected_scheme_(expected_scheme), | 160 : expected_scheme_(expected_scheme), |
116 expected_request_path_(expected_request_path), | 161 expected_request_path_(expected_request_path), |
117 callback_(callback), | 162 callback_(callback), |
118 page_count_(0), | 163 page_count_(0), |
119 distilled_page_prefs_(distilled_page_prefs), | 164 distilled_page_prefs_(distilled_page_prefs), |
120 waiting_for_page_ready_(true) { | 165 waiting_for_page_ready_(true) { |
121 content::WebContentsObserver::Observe(web_contents); | 166 content::WebContentsObserver::Observe(web_contents); |
122 distilled_page_prefs_->AddObserver(this); | 167 distilled_page_prefs_->AddObserver(this); |
168 template_observer_ = new WebContentsTemplateObserver(web_contents); | |
123 } | 169 } |
124 | 170 |
125 DomDistillerViewerSource::RequestViewerHandle::~RequestViewerHandle() { | 171 DomDistillerViewerSource::RequestViewerHandle::~RequestViewerHandle() { |
126 distilled_page_prefs_->RemoveObserver(this); | 172 distilled_page_prefs_->RemoveObserver(this); |
127 } | 173 } |
128 | 174 |
129 void DomDistillerViewerSource::RequestViewerHandle::SendJavaScript( | 175 void DomDistillerViewerSource::RequestViewerHandle::SendJavaScript( |
130 const std::string& buffer) { | 176 const std::string& buffer) { |
131 if (waiting_for_page_ready_) { | 177 if (waiting_for_page_ready_) { |
132 buffer_ += buffer; | 178 buffer_ += buffer; |
(...skipping 10 matching lines...) Expand all Loading... | |
143 const content::FrameNavigateParams& params) { | 189 const content::FrameNavigateParams& params) { |
144 const GURL& navigation = details.entry->GetURL(); | 190 const GURL& navigation = details.entry->GetURL(); |
145 if (details.is_in_page || ( | 191 if (details.is_in_page || ( |
146 navigation.SchemeIs(expected_scheme_.c_str()) && | 192 navigation.SchemeIs(expected_scheme_.c_str()) && |
147 expected_request_path_ == navigation.query())) { | 193 expected_request_path_ == navigation.query())) { |
148 // In-page navigations, as well as the main view request can be ignored. | 194 // In-page navigations, as well as the main view request can be ignored. |
149 return; | 195 return; |
150 } | 196 } |
151 | 197 |
152 Cancel(); | 198 Cancel(); |
153 | |
154 } | 199 } |
155 | 200 |
156 void DomDistillerViewerSource::RequestViewerHandle::RenderProcessGone( | 201 void DomDistillerViewerSource::RequestViewerHandle::RenderProcessGone( |
157 base::TerminationStatus status) { | 202 base::TerminationStatus status) { |
158 Cancel(); | 203 Cancel(); |
159 } | 204 } |
160 | 205 |
161 void DomDistillerViewerSource::RequestViewerHandle::WebContentsDestroyed() { | 206 void DomDistillerViewerSource::RequestViewerHandle::WebContentsDestroyed() { |
162 Cancel(); | 207 Cancel(); |
163 } | 208 } |
(...skipping 17 matching lines...) Expand all Loading... | |
181 if (buffer_.empty()) { | 226 if (buffer_.empty()) { |
182 return; | 227 return; |
183 } | 228 } |
184 web_contents()->GetMainFrame()->ExecuteJavaScript(base::UTF8ToUTF16(buffer_)); | 229 web_contents()->GetMainFrame()->ExecuteJavaScript(base::UTF8ToUTF16(buffer_)); |
185 buffer_.clear(); | 230 buffer_.clear(); |
186 } | 231 } |
187 | 232 |
188 void DomDistillerViewerSource::RequestViewerHandle::OnArticleReady( | 233 void DomDistillerViewerSource::RequestViewerHandle::OnArticleReady( |
189 const DistilledArticleProto* article_proto) { | 234 const DistilledArticleProto* article_proto) { |
190 if (page_count_ == 0) { | 235 if (page_count_ == 0) { |
191 // This is a single-page article. | 236 template_observer_->setInitialContent( |
192 std::string unsafe_page_html = | 237 viewer::GetUnsafeArticleContentJs(article_proto)); |
193 viewer::GetUnsafeArticleHtml( | 238 |
194 article_proto, | 239 std::string unsafe_page_html = viewer::GetUnsafeArticleTemplateHtml( |
195 distilled_page_prefs_->GetTheme(), | 240 &article_proto->pages(0), |
196 distilled_page_prefs_->GetFontFamily()); | 241 distilled_page_prefs_->GetTheme(), |
242 distilled_page_prefs_->GetFontFamily()); | |
197 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); | 243 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); |
244 | |
245 // Send the actual article content to the page. | |
198 } else if (page_count_ == article_proto->pages_size()) { | 246 } else if (page_count_ == article_proto->pages_size()) { |
199 // We may still be showing the "Loading" indicator. | 247 // We may still be showing the "Loading" indicator. |
200 SendJavaScript(viewer::GetToggleLoadingIndicatorJs(true)); | 248 SendJavaScript(viewer::GetToggleLoadingIndicatorJs(true)); |
201 } else { | 249 } else { |
202 // It's possible that we didn't get some incremental updates from the | 250 // It's possible that we didn't get some incremental updates from the |
203 // distiller. Ensure all remaining pages are flushed to the viewer. | 251 // distiller. Ensure all remaining pages are flushed to the viewer. |
204 for (;page_count_ < article_proto->pages_size(); page_count_++) { | 252 for (;page_count_ < article_proto->pages_size(); page_count_++) { |
205 const DistilledPageProto& page = article_proto->pages(page_count_); | 253 const DistilledPageProto& page = article_proto->pages(page_count_); |
206 SendJavaScript( | 254 SendJavaScript( |
207 viewer::GetUnsafeIncrementalDistilledPageJs( | 255 viewer::GetUnsafeIncrementalDistilledPageJs( |
208 &page, | 256 &page, |
209 page_count_ == article_proto->pages_size())); | 257 page_count_ == article_proto->pages_size())); |
210 } | 258 } |
211 } | 259 } |
212 // No need to hold on to the ViewerHandle now that distillation is complete. | 260 // No need to hold on to the ViewerHandle now that distillation is complete. |
213 viewer_handle_.reset(); | 261 viewer_handle_.reset(); |
214 } | 262 } |
215 | 263 |
216 void DomDistillerViewerSource::RequestViewerHandle::OnArticleUpdated( | 264 void DomDistillerViewerSource::RequestViewerHandle::OnArticleUpdated( |
217 ArticleDistillationUpdate article_update) { | 265 ArticleDistillationUpdate article_update) { |
218 for (;page_count_ < static_cast<int>(article_update.GetPagesSize()); | 266 for (;page_count_ < static_cast<int>(article_update.GetPagesSize()); |
219 page_count_++) { | 267 page_count_++) { |
220 const DistilledPageProto& page = | 268 const DistilledPageProto& page = |
221 article_update.GetDistilledPage(page_count_); | 269 article_update.GetDistilledPage(page_count_); |
222 if (page_count_ == 0) { | 270 if (page_count_ == 0) { |
271 template_observer_->setInitialContent( | |
272 viewer::GetUnsafeIncrementalDistilledPageJs(&page, false)); | |
273 | |
223 // This is the first page, so send Viewer page scaffolding too. | 274 // This is the first page, so send Viewer page scaffolding too. |
224 std::string unsafe_page_html = viewer::GetUnsafePartialArticleHtml( | 275 std::string unsafe_page_html = viewer::GetUnsafeArticleTemplateHtml( |
225 &page, | 276 &page, |
226 distilled_page_prefs_->GetTheme(), | 277 distilled_page_prefs_->GetTheme(), |
227 distilled_page_prefs_->GetFontFamily()); | 278 distilled_page_prefs_->GetFontFamily()); |
228 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); | 279 callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); |
229 } else { | 280 } else { |
230 SendJavaScript( | 281 // Send the page content to the client. |
231 viewer::GetUnsafeIncrementalDistilledPageJs(&page, false)); | 282 SendJavaScript(viewer::GetUnsafeIncrementalDistilledPageJs(&page, false)); |
232 } | 283 } |
233 } | 284 } |
234 } | 285 } |
235 | 286 |
236 void DomDistillerViewerSource::RequestViewerHandle::TakeViewerHandle( | 287 void DomDistillerViewerSource::RequestViewerHandle::TakeViewerHandle( |
237 scoped_ptr<ViewerHandle> viewer_handle) { | 288 scoped_ptr<ViewerHandle> viewer_handle) { |
238 viewer_handle_ = viewer_handle.Pass(); | 289 viewer_handle_ = viewer_handle.Pass(); |
239 } | 290 } |
240 | 291 |
241 void DomDistillerViewerSource::RequestViewerHandle::OnChangeTheme( | 292 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 | 358 // The service returned a |ViewerHandle| and guarantees it will call |
308 // the |RequestViewerHandle|, so passing ownership to it, to ensure the | 359 // the |RequestViewerHandle|, so passing ownership to it, to ensure the |
309 // request is not cancelled. The |RequestViewerHandle| will delete itself | 360 // request is not cancelled. The |RequestViewerHandle| will delete itself |
310 // after receiving the callback. | 361 // after receiving the callback. |
311 request_viewer_handle->TakeViewerHandle(viewer_handle.Pass()); | 362 request_viewer_handle->TakeViewerHandle(viewer_handle.Pass()); |
312 } else { | 363 } else { |
313 // The service did not return a |ViewerHandle|, which means the | 364 // The service did not return a |ViewerHandle|, which means the |
314 // |RequestViewerHandle| will never be called, so clean up now. | 365 // |RequestViewerHandle| will never be called, so clean up now. |
315 delete request_viewer_handle; | 366 delete request_viewer_handle; |
316 | 367 |
368 WebContentsTemplateObserver* template_observer = | |
cjhopman
2015/03/25 02:30:50
Who owns this? When is it deleted?
mdjones
2015/03/25 21:29:20
The observer now deletes itself once the first pag
| |
369 new WebContentsTemplateObserver(web_contents); | |
370 template_observer->setInitialContent(viewer::GetErrorPageJs()); | |
371 | |
317 std::string error_page_html = viewer::GetErrorPageHtml( | 372 std::string error_page_html = viewer::GetErrorPageHtml( |
318 dom_distiller_service_->GetDistilledPagePrefs()->GetTheme(), | 373 dom_distiller_service_->GetDistilledPagePrefs()->GetTheme(), |
319 dom_distiller_service_->GetDistilledPagePrefs()->GetFontFamily()); | 374 dom_distiller_service_->GetDistilledPagePrefs()->GetFontFamily()); |
320 callback.Run(base::RefCountedString::TakeString(&error_page_html)); | 375 callback.Run(base::RefCountedString::TakeString(&error_page_html)); |
321 } | 376 } |
322 }; | 377 }; |
323 | 378 |
324 std::string DomDistillerViewerSource::GetMimeType( | 379 std::string DomDistillerViewerSource::GetMimeType( |
325 const std::string& path) const { | 380 const std::string& path) const { |
326 if (kViewerCssPath == path) { | 381 if (kViewerCssPath == path) { |
(...skipping 15 matching lines...) Expand all Loading... | |
342 const net::URLRequest* request, | 397 const net::URLRequest* request, |
343 std::string* path) const { | 398 std::string* path) const { |
344 } | 399 } |
345 | 400 |
346 std::string DomDistillerViewerSource::GetContentSecurityPolicyObjectSrc() | 401 std::string DomDistillerViewerSource::GetContentSecurityPolicyObjectSrc() |
347 const { | 402 const { |
348 return "object-src 'none'; style-src 'self' https://fonts.googleapis.com;"; | 403 return "object-src 'none'; style-src 'self' https://fonts.googleapis.com;"; |
349 } | 404 } |
350 | 405 |
351 } // namespace dom_distiller | 406 } // namespace dom_distiller |
OLD | NEW |