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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: components/dom_distiller/content/dom_distiller_viewer_source.cc
diff --git a/components/dom_distiller/content/dom_distiller_viewer_source.cc b/components/dom_distiller/content/dom_distiller_viewer_source.cc
index c3351c2cc5383870384c95f89af27b1d1cd3c977..22ceab6d9f239992c3d837b3a5be8642e0f685d7 100644
--- a/components/dom_distiller/content/dom_distiller_viewer_source.cc
+++ b/components/dom_distiller/content/dom_distiller_viewer_source.cc
@@ -11,21 +11,32 @@
#include "base/memory/ref_counted_memory.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
#include "components/dom_distiller/core/task_tracker.h"
#include "components/dom_distiller/core/url_constants.h"
#include "components/dom_distiller/core/viewer.h"
+#include "content/public/browser/navigation_details.h"
+#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "net/base/url_util.h"
#include "net/url_request/url_request.h"
namespace dom_distiller {
// Handles receiving data asynchronously for a specific entry, and passing
-// it along to the data callback for the data source.
+// it along to the data callback for the data source. Lifetime matches that of
+// the current main frame's page in the Viewer instance.
class DomDistillerViewerSource::RequestViewerHandle
- : public ViewRequestDelegate {
+ : public ViewRequestDelegate,
+ public content::WebContentsObserver {
public:
explicit RequestViewerHandle(
+ content::WebContents* web_contents,
+ const std::string& expected_scheme,
+ const std::string& expected_request_path,
const content::URLDataSource::GotDataCallback& callback);
virtual ~RequestViewerHandle();
@@ -38,33 +49,182 @@ class DomDistillerViewerSource::RequestViewerHandle
void TakeViewerHandle(scoped_ptr<ViewerHandle> viewer_handle);
+ // WebContentsObserver:
+ virtual void DidNavigateMainFrame(
+ const content::LoadCommittedDetails& details,
+ const content::FrameNavigateParams& params) OVERRIDE;
+ virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE;
+ virtual void WebContentsDestroyed() OVERRIDE;
+ virtual void DidFinishLoad(
+ int64 frame_id,
+ const GURL& validated_url,
+ bool is_main_frame,
+ content::RenderViewHost* render_view_host) OVERRIDE;
+
private:
+ // Sends JavaScript to the attached Viewer, buffering data if the viewer isn't
+ // ready.
+ void SendJavaScript(const std::string& buffer);
+
+ // Cancels the current view request. Once called, no updates will be
+ // propagated to the view, and the request to DomDistillerService will be
+ // cancelled.
+ void Cancel();
+
// The handle to the view request towards the DomDistillerService. It
// needs to be kept around to ensure the distillation request finishes.
scoped_ptr<ViewerHandle> viewer_handle_;
- // This holds the callback to where the data retrieved is sent back.
+ // WebContents associated with the Viewer's render process.
+ content::WebContents* web_contents_;
+
+ // The scheme hosting the current view request;
+ std::string expected_scheme_;
+
+ // The query path for the current view request.
+ std::string expected_request_path_;
+
+ // Holds the callback to where the data retrieved is sent back.
content::URLDataSource::GotDataCallback callback_;
+
+ // Number of pages of the distilled article content that have been rendered by
+ // the viewer.
+ int page_count_;
+
+ // Whether the page is sufficiently initialized to handle updates from the
+ // distiller.
+ bool waiting_for_page_ready_;
+
+ // Temporary store of pending JavaScript if the page isn't ready to receive
+ // data from distillation.
+ std::string buffer_;
};
DomDistillerViewerSource::RequestViewerHandle::RequestViewerHandle(
+ content::WebContents* web_contents,
+ const std::string& expected_scheme,
+ const std::string& expected_request_path,
const content::URLDataSource::GotDataCallback& callback)
- : callback_(callback) {
+ : web_contents_(web_contents),
+ expected_scheme_(expected_scheme),
+ expected_request_path_(expected_request_path),
+ callback_(callback),
+ page_count_(0),
+ waiting_for_page_ready_(true) {
+ content::WebContentsObserver::Observe(web_contents_);
}
DomDistillerViewerSource::RequestViewerHandle::~RequestViewerHandle() {
+ // Balanced with constructor although can be a no-op if frame navigated away.
+ content::WebContentsObserver::Observe(NULL);
+}
+
+void DomDistillerViewerSource::RequestViewerHandle::SendJavaScript(
+ const std::string& buffer) {
+ if (waiting_for_page_ready_) {
+ buffer_ += buffer;
+ } else {
+ if (web_contents_) {
+ web_contents_->GetMainFrame()->ExecuteJavaScript(
+ base::UTF8ToUTF16(buffer));
+ }
+ }
+}
+
+void DomDistillerViewerSource::RequestViewerHandle::DidNavigateMainFrame(
+ const content::LoadCommittedDetails& details,
+ const content::FrameNavigateParams& params) {
+ const GURL& navigation = details.entry->GetURL();
+ if (details.is_in_page || (
+ navigation.SchemeIs(expected_scheme_.c_str()) &&
+ expected_request_path_ == navigation.query())) {
+ // In-page navigations, as well as the main view request can be ignored.
+ return;
+ }
+
+ Cancel();
+
+}
+
+void DomDistillerViewerSource::RequestViewerHandle::RenderProcessGone(
+ base::TerminationStatus status) {
+ Cancel();
+}
+
+void DomDistillerViewerSource::RequestViewerHandle::WebContentsDestroyed() {
+ Cancel();
+}
+
+void DomDistillerViewerSource::RequestViewerHandle::Cancel() {
+ // Ensure we don't send any incremental updates to the Viewer.
+ web_contents_ = NULL;
+
+ // No need to listen for notifications.
+ content::WebContentsObserver::Observe(NULL);
+
+ // Schedule the Viewer for deletion. Ensures distillation is cancelled, and
+ // any pending data stored in |buffer_| is released.
+ base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+void DomDistillerViewerSource::RequestViewerHandle::DidFinishLoad(
+ int64 frame_id,
+ const GURL& validated_url,
+ bool is_main_frame,
+ content::RenderViewHost* render_view_host) {
+ if (!is_main_frame || web_contents_ == NULL) {
+ return;
+ }
+ waiting_for_page_ready_ = false;
+ if (buffer_.empty()) {
+ return;
+ }
+ if (web_contents_) {
+ web_contents_->GetMainFrame()->ExecuteJavaScript(
+ base::UTF8ToUTF16(buffer_));
+ }
+ buffer_.clear();
}
void DomDistillerViewerSource::RequestViewerHandle::OnArticleReady(
const DistilledArticleProto* article_proto) {
- std::string unsafe_page_html = viewer::GetUnsafeHtml(article_proto);
- callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html));
- base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+ if (page_count_ == 0) {
+ // This is a single-page article.
+ std::string unsafe_page_html = viewer::GetUnsafeArticleHtml(article_proto);
+ callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html));
+ } else if (page_count_ == article_proto->pages_size()) {
+ // We may still be showing the "Loading" indicator.
+ SendJavaScript(viewer::GetToggleLoadingIndicatorJs(true));
+ } else {
+ // It's possible that we didn't get some incremental updates from the
+ // distiller. Ensure all remaining pages are flushed to the viewer.
+ for (;page_count_ < article_proto->pages_size(); page_count_++) {
+ const DistilledPageProto& page = article_proto->pages(page_count_);
+ SendJavaScript(
+ viewer::GetUnsafeIncrementalDistilledPageJs(
+ &page,
+ page_count_ == article_proto->pages_size()));
+ }
+ }
+ // No need to hold on to the ViewerHandle now that distillation is complete.
+ viewer_handle_.reset();
}
void DomDistillerViewerSource::RequestViewerHandle::OnArticleUpdated(
ArticleDistillationUpdate article_update) {
- // TODO(nyquist): Add support for displaying pages incrementally.
+ for (;page_count_ < static_cast<int>(article_update.GetPagesSize());
+ page_count_++) {
+ const DistilledPageProto& page =
+ article_update.GetDistilledPage(page_count_);
+ if (page_count_ == 0) {
+ // This is the first page, so send Viewer page scaffolding too.
+ std::string unsafe_page_html = viewer::GetUnsafePartialArticleHtml(&page);
+ callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html));
+ } else {
+ SendJavaScript(
+ viewer::GetUnsafeIncrementalDistilledPageJs(&page, false));
+ }
+ }
}
void DomDistillerViewerSource::RequestViewerHandle::TakeViewerHandle(
@@ -98,14 +258,23 @@ void DomDistillerViewerSource::StartDataRequest(
DCHECK(render_view_host);
CHECK_EQ(0, render_view_host->GetEnabledBindings());
- if (kCssPath == path) {
+ if (kViewerCssPath == path) {
std::string css = viewer::GetCss();
callback.Run(base::RefCountedString::TakeString(&css));
return;
}
-
+ if (kViewerJsPath == path) {
+ std::string js = viewer::GetJavaScript();
+ callback.Run(base::RefCountedString::TakeString(&js));
+ return;
+ }
+ content::WebContents* web_contents =
+ content::WebContents::FromRenderFrameHost(
+ content::RenderFrameHost::FromID(render_process_id,
+ render_frame_id));
+ DCHECK(web_contents);
RequestViewerHandle* request_viewer_handle =
- new RequestViewerHandle(callback);
+ new RequestViewerHandle(web_contents, scheme_, path.substr(1), callback);
scoped_ptr<ViewerHandle> viewer_handle = viewer::CreateViewRequest(
dom_distiller_service_, path, request_viewer_handle);
@@ -127,8 +296,12 @@ void DomDistillerViewerSource::StartDataRequest(
std::string DomDistillerViewerSource::GetMimeType(
const std::string& path) const {
- if (path == kCssPath)
+ if (kViewerCssPath == path) {
return "text/css";
+ }
+ if (kViewerJsPath == path) {
+ return "text/javascript";
+ }
return "text/html";
}

Powered by Google App Engine
This is Rietveld 408576698