| 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";
|
| }
|
|
|
|
|