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