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 3 // found in the LICENSE file. 4 5 #include "chrome/browser/dom_distiller/tab_utils.h" 6 7 #include "base/message_loop/message_loop.h" 8 #include "chrome/browser/dom_distiller/dom_distiller_service_factory.h" 9 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" 10 #include "chrome/browser/ui/tab_contents/core_tab_helper_delegate.h" 11 #include "components/dom_distiller/content/distiller_page_web_contents.h" 12 #include "components/dom_distiller/core/distiller_page.h" 13 #include "components/dom_distiller/core/dom_distiller_service.h" 14 #include "components/dom_distiller/core/task_tracker.h" 15 #include "components/dom_distiller/core/url_constants.h" 16 #include "components/dom_distiller/core/url_utils.h" 17 #include "content/public/browser/browser_context.h" 18 #include "content/public/browser/navigation_controller.h" 19 #include "content/public/browser/web_contents.h" 20 #include "content/public/browser/web_contents_observer.h" 21 22 namespace { 23 24 using dom_distiller::ViewRequestDelegate; 25 using dom_distiller::DistilledArticleProto; 26 using dom_distiller::ArticleDistillationUpdate; 27 using dom_distiller::ViewerHandle; 28 using dom_distiller::SourcePageHandleWebContents; 29 using dom_distiller::DomDistillerService; 30 using dom_distiller::DomDistillerServiceFactory; 31 using dom_distiller::DistillerPage; 32 using dom_distiller::SourcePageHandle; 33 34 // An no-op ViewRequestDelegate which holds a ViewerHandle and deletes itself 35 // after the WebContents navigates or goes away. This class is a band-aid to 36 // keep a TaskTracker around until the distillation starts from the viewer. 37 class SelfDeletingRequestDelegate : public ViewRequestDelegate, 38 public content::WebContentsObserver { 39 public: 40 explicit SelfDeletingRequestDelegate(content::WebContents* web_contents); 41 virtual ~SelfDeletingRequestDelegate(); 42 43 // ViewRequestDelegate implementation. 44 virtual void OnArticleReady( 45 const DistilledArticleProto* article_proto) OVERRIDE; 46 virtual void OnArticleUpdated( 47 ArticleDistillationUpdate article_update) OVERRIDE; 48 49 // content::WebContentsObserver implementation. 50 virtual void DidNavigateMainFrame( 51 const content::LoadCommittedDetails& details, 52 const content::FrameNavigateParams& params) OVERRIDE; 53 virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE; 54 virtual void WebContentsDestroyed() OVERRIDE; 55 56 // Takes ownership of the ViewerHandle to keep distillation alive until |this| 57 // is deleted. 58 void TakeViewerHandle(scoped_ptr<ViewerHandle> viewer_handle); 59 60 private: 61 // The handle to the view request towards the DomDistillerService. It 62 // needs to be kept around to ensure the distillation request finishes. 63 scoped_ptr<ViewerHandle> viewer_handle_; 64 }; 65 66 void SelfDeletingRequestDelegate::DidNavigateMainFrame( 67 const content::LoadCommittedDetails& details, 68 const content::FrameNavigateParams& params) { 69 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 70 } 71 72 void SelfDeletingRequestDelegate::RenderProcessGone( 73 base::TerminationStatus status) { 74 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 75 } 76 77 void SelfDeletingRequestDelegate::WebContentsDestroyed() { 78 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 79 } 80 81 SelfDeletingRequestDelegate::SelfDeletingRequestDelegate( 82 content::WebContents* web_contents) 83 : WebContentsObserver(web_contents) { 84 } 85 86 SelfDeletingRequestDelegate::~SelfDeletingRequestDelegate() { 87 } 88 89 void SelfDeletingRequestDelegate::OnArticleReady( 90 const DistilledArticleProto* article_proto) { 91 } 92 93 void SelfDeletingRequestDelegate::OnArticleUpdated( 94 ArticleDistillationUpdate article_update) { 95 } 96 97 void SelfDeletingRequestDelegate::TakeViewerHandle( 98 scoped_ptr<ViewerHandle> viewer_handle) { 99 viewer_handle_ = viewer_handle.Pass(); 100 } 101 102 // Start loading the viewer URL of the current page in |web_contents|. 103 void StartNavigationToDistillerViewer(content::WebContents* web_contents, 104 const GURL& url) { 105 GURL viewer_url = dom_distiller::url_utils::GetDistillerViewUrlFromUrl( 106 dom_distiller::kDomDistillerScheme, url); 107 content::NavigationController::LoadURLParams params(viewer_url); 108 params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK; 109 web_contents->GetController().LoadURLWithParams(params); 110 } 111 112 void StartDistillation(content::WebContents* web_contents) { 113 // Start distillation using |web_contents|, and ensure ViewerHandle stays 114 // around until the viewer requests distillation. 115 SelfDeletingRequestDelegate* view_request_delegate = 116 new SelfDeletingRequestDelegate(web_contents); 117 scoped_ptr<content::WebContents> old_web_contents_sptr(web_contents); 118 scoped_ptr<SourcePageHandleWebContents> source_page_handle( 119 new SourcePageHandleWebContents(old_web_contents_sptr.Pass())); 120 DomDistillerService* dom_distiller_service = 121 DomDistillerServiceFactory::GetForBrowserContext( 122 web_contents->GetBrowserContext()); 123 scoped_ptr<DistillerPage> distiller_page = 124 dom_distiller_service->CreateDefaultDistillerPageWithHandle( 125 source_page_handle.PassAs<SourcePageHandle>()) 126 .Pass(); 127 128 const GURL& last_committed_url = web_contents->GetLastCommittedURL(); 129 scoped_ptr<ViewerHandle> viewer_handle = dom_distiller_service->ViewUrl( 130 view_request_delegate, distiller_page.Pass(), last_committed_url); 131 view_request_delegate->TakeViewerHandle(viewer_handle.Pass()); 132 } 133 134 } // namespace 135 136 void DistillCurrentPageAndView(content::WebContents* old_web_contents) { 137 DCHECK(old_web_contents); 138 // Create new WebContents. 139 content::WebContents::CreateParams create_params( 140 old_web_contents->GetBrowserContext()); 141 content::WebContents* new_web_contents = 142 content::WebContents::Create(create_params); 143 DCHECK(new_web_contents); 144 145 // Copy all navigation state from the old WebContents to the new one. 146 new_web_contents->GetController().CopyStateFrom( 147 old_web_contents->GetController()); 148 149 // StartNavigationToDistillerViewer must come before swapping the tab contents 150 // to avoid triggering a reload of the page. This reloadmakes it very 151 // difficult to distinguish between the intermediate reload and a user hitting 152 // the back button. 153 StartNavigationToDistillerViewer(new_web_contents, 154 old_web_contents->GetLastCommittedURL()); 155 156 CoreTabHelper::FromWebContents(old_web_contents)->delegate()->SwapTabContents( 157 old_web_contents, new_web_contents, false, false); 158 159 StartDistillation(old_web_contents); 160 } 161