1 // Copyright 2013 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 "components/dom_distiller/content/distiller_page_web_contents.h" 6 7 #include "base/callback.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "base/strings/utf_string_conversions.h" 10 #include "components/dom_distiller/content/web_contents_main_frame_observer.h" 11 #include "components/dom_distiller/core/distiller_page.h" 12 #include "components/dom_distiller/core/dom_distiller_service.h" 13 #include "content/public/browser/browser_context.h" 14 #include "content/public/browser/navigation_controller.h" 15 #include "content/public/browser/render_frame_host.h" 16 #include "content/public/browser/render_view_host.h" 17 #include "content/public/browser/web_contents.h" 18 #include "content/public/browser/web_contents_observer.h" 19 #include "url/gurl.h" 20 21 namespace dom_distiller { 22 23 SourcePageHandleWebContents::SourcePageHandleWebContents( 24 scoped_ptr<content::WebContents> web_contents) 25 : web_contents_(web_contents.Pass()) { 26 DCHECK(web_contents_); 27 } 28 29 SourcePageHandleWebContents::~SourcePageHandleWebContents() { 30 } 31 32 scoped_ptr<content::WebContents> SourcePageHandleWebContents::GetWebContents() { 33 return web_contents_.Pass(); 34 } 35 36 scoped_ptr<DistillerPage> DistillerPageWebContentsFactory::CreateDistillerPage() 37 const { 38 DCHECK(browser_context_); 39 return scoped_ptr<DistillerPage>(new DistillerPageWebContents( 40 browser_context_, scoped_ptr<SourcePageHandleWebContents>())); 41 } 42 43 scoped_ptr<DistillerPage> 44 DistillerPageWebContentsFactory::CreateDistillerPageWithHandle( 45 scoped_ptr<SourcePageHandle> handle) const { 46 DCHECK(browser_context_); 47 scoped_ptr<SourcePageHandleWebContents> web_contents_handle = 48 scoped_ptr<SourcePageHandleWebContents>( 49 static_cast<SourcePageHandleWebContents*>(handle.release())); 50 return scoped_ptr<DistillerPage>(new DistillerPageWebContents( 51 browser_context_, web_contents_handle.Pass())); 52 } 53 54 DistillerPageWebContents::DistillerPageWebContents( 55 content::BrowserContext* browser_context, 56 scoped_ptr<SourcePageHandleWebContents> optional_web_contents_handle) 57 : state_(IDLE), browser_context_(browser_context) { 58 if (optional_web_contents_handle) { 59 web_contents_ = optional_web_contents_handle->GetWebContents().Pass(); 60 } 61 } 62 63 DistillerPageWebContents::~DistillerPageWebContents() { 64 } 65 66 void DistillerPageWebContents::DistillPageImpl(const GURL& url, 67 const std::string& script) { 68 DCHECK(browser_context_); 69 DCHECK(state_ == IDLE); 70 state_ = LOADING_PAGE; 71 script_ = script; 72 73 if (web_contents_ && web_contents_->GetLastCommittedURL() == url) { 74 WebContentsMainFrameObserver* main_frame_observer = 75 WebContentsMainFrameObserver::FromWebContents(web_contents_.get()); 76 if (main_frame_observer && main_frame_observer->is_initialized()) { 77 if (main_frame_observer->is_document_loaded_in_main_frame()) { 78 // Main frame has already loaded for the current WebContents, so execute 79 // JavaScript immediately. 80 ExecuteJavaScript(); 81 } else { 82 // Main frame document has not loaded yet, so wait until it has before 83 // executing JavaScript. It will trigger after DocumentLoadedInFrame is 84 // called for the main frame. 85 content::WebContentsObserver::Observe(web_contents_.get()); 86 } 87 } else { 88 // The WebContentsMainFrameObserver has not been correctly initialized, 89 // so fall back to creating a new WebContents. 90 CreateNewWebContents(url); 91 } 92 } else { 93 CreateNewWebContents(url); 94 } 95 } 96 97 void DistillerPageWebContents::CreateNewWebContents(const GURL& url) { 98 // Create new WebContents to use for distilling the content. 99 content::WebContents::CreateParams create_params(browser_context_); 100 create_params.initially_hidden = true; 101 web_contents_.reset(content::WebContents::Create(create_params)); 102 DCHECK(web_contents_.get()); 103 104 // Start observing WebContents and load the requested URL. 105 content::WebContentsObserver::Observe(web_contents_.get()); 106 content::NavigationController::LoadURLParams params(url); 107 web_contents_->GetController().LoadURLWithParams(params); 108 } 109 110 void DistillerPageWebContents::DocumentLoadedInFrame( 111 int64 frame_id, 112 RenderViewHost* render_view_host) { 113 if (frame_id == web_contents_->GetMainFrame()->GetRoutingID()) { 114 ExecuteJavaScript(); 115 } 116 } 117 118 void DistillerPageWebContents::DidFailLoad( 119 int64 frame_id, 120 const GURL& validated_url, 121 bool is_main_frame, 122 int error_code, 123 const base::string16& error_description, 124 RenderViewHost* render_view_host) { 125 if (is_main_frame) { 126 content::WebContentsObserver::Observe(NULL); 127 DCHECK(state_ == LOADING_PAGE || state_ == EXECUTING_JAVASCRIPT); 128 state_ = PAGELOAD_FAILED; 129 scoped_ptr<base::Value> empty(base::Value::CreateNullValue()); 130 OnWebContentsDistillationDone(GURL(), empty.get()); 131 } 132 } 133 134 void DistillerPageWebContents::ExecuteJavaScript() { 135 content::RenderFrameHost* frame = web_contents_->GetMainFrame(); 136 DCHECK(frame); 137 DCHECK_EQ(LOADING_PAGE, state_); 138 state_ = EXECUTING_JAVASCRIPT; 139 content::WebContentsObserver::Observe(NULL); 140 web_contents_->Stop(); 141 DVLOG(1) << "Beginning distillation"; 142 frame->ExecuteJavaScript( 143 base::UTF8ToUTF16(script_), 144 base::Bind(&DistillerPageWebContents::OnWebContentsDistillationDone, 145 base::Unretained(this), 146 web_contents_->GetLastCommittedURL())); 147 } 148 149 void DistillerPageWebContents::OnWebContentsDistillationDone( 150 const GURL& page_url, 151 const base::Value* value) { 152 DCHECK(state_ == PAGELOAD_FAILED || state_ == EXECUTING_JAVASCRIPT); 153 state_ = IDLE; 154 DistillerPage::OnDistillationDone(page_url, value); 155 } 156 157 } // namespace dom_distiller 158