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 "ui/gfx/screen.h" 20#include "url/gurl.h" 21 22namespace dom_distiller { 23 24SourcePageHandleWebContents::SourcePageHandleWebContents( 25 scoped_ptr<content::WebContents> web_contents) 26 : web_contents_(web_contents.Pass()) { 27 DCHECK(web_contents_); 28} 29 30SourcePageHandleWebContents::~SourcePageHandleWebContents() { 31} 32 33scoped_ptr<content::WebContents> SourcePageHandleWebContents::GetWebContents() { 34 return web_contents_.Pass(); 35} 36 37scoped_ptr<DistillerPage> DistillerPageWebContentsFactory::CreateDistillerPage( 38 const gfx::Size& render_view_size) const { 39 DCHECK(browser_context_); 40 return scoped_ptr<DistillerPage>(new DistillerPageWebContents( 41 browser_context_, render_view_size, 42 scoped_ptr<SourcePageHandleWebContents>())); 43} 44 45scoped_ptr<DistillerPage> 46DistillerPageWebContentsFactory::CreateDistillerPageWithHandle( 47 scoped_ptr<SourcePageHandle> handle) const { 48 DCHECK(browser_context_); 49 scoped_ptr<SourcePageHandleWebContents> web_contents_handle = 50 scoped_ptr<SourcePageHandleWebContents>( 51 static_cast<SourcePageHandleWebContents*>(handle.release())); 52 return scoped_ptr<DistillerPage>(new DistillerPageWebContents( 53 browser_context_, gfx::Size(), web_contents_handle.Pass())); 54} 55 56DistillerPageWebContents::DistillerPageWebContents( 57 content::BrowserContext* browser_context, 58 const gfx::Size& render_view_size, 59 scoped_ptr<SourcePageHandleWebContents> optional_web_contents_handle) 60 : state_(IDLE), browser_context_(browser_context), 61 render_view_size_(render_view_size) { 62 if (optional_web_contents_handle) { 63 web_contents_ = optional_web_contents_handle->GetWebContents().Pass(); 64 if (render_view_size.IsEmpty()) 65 render_view_size_ = web_contents_->GetContainerBounds().size(); 66 } 67} 68 69DistillerPageWebContents::~DistillerPageWebContents() { 70 if (web_contents_) 71 web_contents_->SetDelegate(NULL); 72} 73 74void DistillerPageWebContents::DistillPageImpl(const GURL& url, 75 const std::string& script) { 76 DCHECK(browser_context_); 77 DCHECK(state_ == IDLE); 78 state_ = LOADING_PAGE; 79 script_ = script; 80 81 if (web_contents_ && web_contents_->GetLastCommittedURL() == url) { 82 WebContentsMainFrameObserver* main_frame_observer = 83 WebContentsMainFrameObserver::FromWebContents(web_contents_.get()); 84 if (main_frame_observer && main_frame_observer->is_initialized()) { 85 if (main_frame_observer->is_document_loaded_in_main_frame()) { 86 // Main frame has already loaded for the current WebContents, so execute 87 // JavaScript immediately. 88 ExecuteJavaScript(); 89 } else { 90 // Main frame document has not loaded yet, so wait until it has before 91 // executing JavaScript. It will trigger after DocumentLoadedInFrame is 92 // called for the main frame. 93 content::WebContentsObserver::Observe(web_contents_.get()); 94 } 95 } else { 96 // The WebContentsMainFrameObserver has not been correctly initialized, 97 // so fall back to creating a new WebContents. 98 CreateNewWebContents(url); 99 } 100 } else { 101 CreateNewWebContents(url); 102 } 103} 104 105void DistillerPageWebContents::CreateNewWebContents(const GURL& url) { 106 // Create new WebContents to use for distilling the content. 107 content::WebContents::CreateParams create_params(browser_context_); 108 create_params.initially_hidden = true; 109 web_contents_.reset(content::WebContents::Create(create_params)); 110 DCHECK(web_contents_.get()); 111 112 web_contents_->SetDelegate(this); 113 114 // Start observing WebContents and load the requested URL. 115 content::WebContentsObserver::Observe(web_contents_.get()); 116 content::NavigationController::LoadURLParams params(url); 117 web_contents_->GetController().LoadURLWithParams(params); 118} 119 120gfx::Size DistillerPageWebContents::GetSizeForNewRenderView( 121 content::WebContents* web_contents) const { 122 gfx::Size size(render_view_size_); 123 if (size.IsEmpty()) 124 size = web_contents->GetContainerBounds().size(); 125 // If size is still empty, set it to fullscreen so that document.offsetWidth 126 // in the executed domdistiller.js won't be 0. 127 if (size.IsEmpty()) { 128 DVLOG(1) << "Using fullscreen as default RenderView size"; 129 size = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().size(); 130 } 131 return size; 132} 133 134void DistillerPageWebContents::DocumentLoadedInFrame( 135 content::RenderFrameHost* render_frame_host) { 136 if (render_frame_host == web_contents_->GetMainFrame()) { 137 ExecuteJavaScript(); 138 } 139} 140 141void DistillerPageWebContents::DidFailLoad( 142 content::RenderFrameHost* render_frame_host, 143 const GURL& validated_url, 144 int error_code, 145 const base::string16& error_description) { 146 if (!render_frame_host->GetParent()) { 147 content::WebContentsObserver::Observe(NULL); 148 DCHECK(state_ == LOADING_PAGE || state_ == EXECUTING_JAVASCRIPT); 149 state_ = PAGELOAD_FAILED; 150 scoped_ptr<base::Value> empty(base::Value::CreateNullValue()); 151 OnWebContentsDistillationDone(GURL(), empty.get()); 152 } 153} 154 155void DistillerPageWebContents::ExecuteJavaScript() { 156 content::RenderFrameHost* frame = web_contents_->GetMainFrame(); 157 DCHECK(frame); 158 DCHECK_EQ(LOADING_PAGE, state_); 159 state_ = EXECUTING_JAVASCRIPT; 160 content::WebContentsObserver::Observe(NULL); 161 web_contents_->Stop(); 162 DVLOG(1) << "Beginning distillation"; 163 frame->ExecuteJavaScript( 164 base::UTF8ToUTF16(script_), 165 base::Bind(&DistillerPageWebContents::OnWebContentsDistillationDone, 166 base::Unretained(this), 167 web_contents_->GetLastCommittedURL())); 168} 169 170void DistillerPageWebContents::OnWebContentsDistillationDone( 171 const GURL& page_url, 172 const base::Value* value) { 173 DCHECK(state_ == PAGELOAD_FAILED || state_ == EXECUTING_JAVASCRIPT); 174 state_ = IDLE; 175 DistillerPage::OnDistillationDone(page_url, value); 176} 177 178} // namespace dom_distiller 179