prerender_contents.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2011 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/prerender/prerender_contents.h"
6
7#include "base/metrics/histogram.h"
8#include "base/utf_string_conversions.h"
9#include "chrome/browser/background_contents_service.h"
10#include "chrome/browser/browsing_instance.h"
11#include "chrome/browser/prerender/prerender_manager.h"
12#include "chrome/browser/profiles/profile.h"
13#include "chrome/browser/renderer_host/render_view_host.h"
14#include "chrome/browser/renderer_host/site_instance.h"
15#include "chrome/browser/renderer_preferences_util.h"
16#include "chrome/browser/ui/login/login_prompt.h"
17#include "chrome/common/extensions/extension_constants.h"
18#include "chrome/common/notification_service.h"
19#include "chrome/common/url_constants.h"
20#include "chrome/common/view_types.h"
21#include "chrome/common/render_messages.h"
22#include "chrome/common/render_messages_params.h"
23#include "ui/gfx/rect.h"
24
25class PrerenderContentsFactoryImpl : public PrerenderContents::Factory {
26 public:
27  virtual PrerenderContents* CreatePrerenderContents(
28      PrerenderManager* prerender_manager, Profile* profile, const GURL& url,
29      const std::vector<GURL>& alias_urls) {
30    return new PrerenderContents(prerender_manager, profile, url, alias_urls);
31  }
32};
33
34PrerenderContents::PrerenderContents(PrerenderManager* prerender_manager,
35                                     Profile* profile,
36                                     const GURL& url,
37                                     const std::vector<GURL>& alias_urls)
38    : prerender_manager_(prerender_manager),
39      render_view_host_(NULL),
40      prerender_url_(url),
41      profile_(profile),
42      page_id_(0),
43      has_stopped_loading_(false),
44      final_status_(FINAL_STATUS_MAX) {
45  DCHECK(prerender_manager != NULL);
46  AddAliasURL(prerender_url_);
47  for (std::vector<GURL>::const_iterator it = alias_urls.begin();
48       it != alias_urls.end();
49       ++it) {
50    AddAliasURL(*it);
51  }
52}
53
54// static
55PrerenderContents::Factory* PrerenderContents::CreateFactory() {
56  return new PrerenderContentsFactoryImpl();
57}
58
59void PrerenderContents::StartPrerendering() {
60  DCHECK(profile_ != NULL);
61  SiteInstance* site_instance = SiteInstance::CreateSiteInstance(profile_);
62  render_view_host_ = new RenderViewHost(site_instance, this, MSG_ROUTING_NONE,
63                                         NULL);
64  render_view_host_->AllowScriptToClose(true);
65
66  // Close ourselves when the application is shutting down.
67  registrar_.Add(this, NotificationType::APP_TERMINATING,
68                 NotificationService::AllSources());
69
70  // Register for our parent profile to shutdown, so we can shut ourselves down
71  // as well (should only be called for OTR profiles, as we should receive
72  // APP_TERMINATING before non-OTR profiles are destroyed).
73  // TODO(tburkard): figure out if this is needed.
74  registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
75                 Source<Profile>(profile_));
76  render_view_host_->CreateRenderView(string16());
77
78  // Register to cancel if Authentication is required.
79  registrar_.Add(this, NotificationType::AUTH_NEEDED,
80                 NotificationService::AllSources());
81
82  registrar_.Add(this, NotificationType::AUTH_CANCELLED,
83                 NotificationService::AllSources());
84
85  DCHECK(load_start_time_.is_null());
86  load_start_time_ = base::TimeTicks::Now();
87
88  ViewMsg_Navigate_Params params;
89  params.url = prerender_url_;
90  params.transition = PageTransition::LINK;
91  params.navigation_type = ViewMsg_Navigate_Params::PRERENDER;
92  render_view_host_->Navigate(params);
93}
94
95void PrerenderContents::set_final_status(FinalStatus final_status) {
96  DCHECK(final_status >= FINAL_STATUS_USED && final_status < FINAL_STATUS_MAX);
97  DCHECK_EQ(FINAL_STATUS_MAX, final_status_);
98  final_status_ = final_status;
99}
100
101PrerenderContents::FinalStatus PrerenderContents::final_status() const {
102  return final_status_;
103}
104
105PrerenderContents::~PrerenderContents() {
106  DCHECK(final_status_ != FINAL_STATUS_MAX);
107  UMA_HISTOGRAM_ENUMERATION("Prerender.FinalStatus",
108                            final_status_,
109                            FINAL_STATUS_MAX);
110
111  if (!render_view_host_)   // Will be null for unit tests.
112    return;
113
114  render_view_host_->Shutdown();  // deletes render_view_host
115}
116
117RenderViewHostDelegate::View* PrerenderContents::GetViewDelegate() {
118  return this;
119}
120
121const GURL& PrerenderContents::GetURL() const {
122  return url_;
123}
124
125ViewType::Type PrerenderContents::GetRenderViewType() const {
126  return ViewType::BACKGROUND_CONTENTS;
127}
128
129int PrerenderContents::GetBrowserWindowID() const {
130  return extension_misc::kUnknownWindowId;
131}
132
133void PrerenderContents::DidNavigate(
134    RenderViewHost* render_view_host,
135    const ViewHostMsg_FrameNavigate_Params& params) {
136  // We only care when the outer frame changes.
137  if (!PageTransition::IsMainFrame(params.transition))
138    return;
139
140  // Store the navigation params.
141  ViewHostMsg_FrameNavigate_Params* p = new ViewHostMsg_FrameNavigate_Params();
142  *p = params;
143  navigate_params_.reset(p);
144
145  url_ = params.url;
146
147  AddAliasURL(url_);
148}
149
150void PrerenderContents::UpdateTitle(RenderViewHost* render_view_host,
151                                    int32 page_id,
152                                    const std::wstring& title) {
153  if (title.empty()) {
154    return;
155  }
156
157  title_ = WideToUTF16Hack(title);
158  page_id_ = page_id;
159}
160
161void PrerenderContents::RunJavaScriptMessage(
162    const std::wstring& message,
163    const std::wstring& default_prompt,
164    const GURL& frame_url,
165    const int flags,
166    IPC::Message* reply_msg,
167    bool* did_suppress_message) {
168  // Always suppress JavaScript messages if they're triggered by a page being
169  // prerendered.
170  *did_suppress_message = true;
171  // We still want to show the user the message when they navigate to this
172  // page, so cancel this prerender.
173  Destroy(FINAL_STATUS_JAVASCRIPT_ALERT);
174}
175
176bool PrerenderContents::PreHandleKeyboardEvent(
177    const NativeWebKeyboardEvent& event,
178    bool* is_keyboard_shortcut) {
179  return false;
180}
181
182void PrerenderContents::Observe(NotificationType type,
183                                const NotificationSource& source,
184                                const NotificationDetails& details) {
185  switch (type.value) {
186    case NotificationType::PROFILE_DESTROYED:
187      Destroy(FINAL_STATUS_PROFILE_DESTROYED);
188      return;
189    case NotificationType::APP_TERMINATING:
190      Destroy(FINAL_STATUS_APP_TERMINATING);
191      return;
192
193    case NotificationType::AUTH_NEEDED:
194    case NotificationType::AUTH_CANCELLED: {
195      // Prerendered pages have a NULL controller and the login handler should
196      // be referencing us as the render view host delegate.
197      NavigationController* controller =
198          Source<NavigationController>(source).ptr();
199      LoginNotificationDetails* details_ptr =
200          Details<LoginNotificationDetails>(details).ptr();
201      LoginHandler* handler = details_ptr->handler();
202      DCHECK(handler != NULL);
203      RenderViewHostDelegate* delegate = handler->GetRenderViewHostDelegate();
204      if (controller == NULL && delegate == this)
205        Destroy(FINAL_STATUS_AUTH_NEEDED);
206      break;
207    }
208
209    default:
210      NOTREACHED() << "Unexpected notification sent.";
211      break;
212  }
213}
214
215void PrerenderContents::OnMessageBoxClosed(IPC::Message* reply_msg,
216                                           bool success,
217                                           const std::wstring& prompt) {
218  render_view_host_->JavaScriptMessageBoxClosed(reply_msg, success, prompt);
219}
220
221gfx::NativeWindow PrerenderContents::GetMessageBoxRootWindow() {
222  NOTIMPLEMENTED();
223  return NULL;
224}
225
226TabContents* PrerenderContents::AsTabContents() {
227  return NULL;
228}
229
230ExtensionHost* PrerenderContents::AsExtensionHost() {
231  return NULL;
232}
233
234void PrerenderContents::UpdateInspectorSetting(const std::string& key,
235                                               const std::string& value) {
236  RenderViewHostDelegateHelper::UpdateInspectorSetting(profile_, key, value);
237}
238
239void PrerenderContents::ClearInspectorSettings() {
240  RenderViewHostDelegateHelper::ClearInspectorSettings(profile_);
241}
242
243void PrerenderContents::Close(RenderViewHost* render_view_host) {
244  Destroy(FINAL_STATUS_CLOSED);
245}
246
247RendererPreferences PrerenderContents::GetRendererPrefs(
248    Profile* profile) const {
249  RendererPreferences preferences;
250  renderer_preferences_util::UpdateFromSystemSettings(&preferences, profile);
251  return preferences;
252}
253
254WebPreferences PrerenderContents::GetWebkitPrefs() {
255  return RenderViewHostDelegateHelper::GetWebkitPrefs(profile_,
256                                                      false);  // is_dom_ui
257}
258
259void PrerenderContents::ProcessWebUIMessage(
260    const ViewHostMsg_DomMessage_Params& params) {
261  render_view_host_->BlockExtensionRequest(params.request_id);
262}
263
264void PrerenderContents::CreateNewWindow(
265    int route_id,
266    const ViewHostMsg_CreateWindow_Params& params) {
267  // Since we don't want to permit child windows that would have a
268  // window.opener property, terminate prerendering.
269  Destroy(FINAL_STATUS_CREATE_NEW_WINDOW);
270}
271
272void PrerenderContents::CreateNewWidget(int route_id,
273                                        WebKit::WebPopupType popup_type) {
274  NOTREACHED();
275}
276
277void PrerenderContents::CreateNewFullscreenWidget(int route_id) {
278  NOTREACHED();
279}
280
281void PrerenderContents::ShowCreatedWindow(int route_id,
282                                          WindowOpenDisposition disposition,
283                                          const gfx::Rect& initial_pos,
284                                          bool user_gesture) {
285  // TODO(tburkard): need to figure out what the correct behavior here is
286  NOTIMPLEMENTED();
287}
288
289void PrerenderContents::ShowCreatedWidget(int route_id,
290                                          const gfx::Rect& initial_pos) {
291  NOTIMPLEMENTED();
292}
293
294void PrerenderContents::ShowCreatedFullscreenWidget(int route_id) {
295  NOTIMPLEMENTED();
296}
297
298bool PrerenderContents::OnMessageReceived(const IPC::Message& message) {
299  bool handled = true;
300  bool message_is_ok = true;
301  IPC_BEGIN_MESSAGE_MAP_EX(PrerenderContents, message, message_is_ok)
302    IPC_MESSAGE_HANDLER(ViewHostMsg_DidStartProvisionalLoadForFrame,
303                        OnDidStartProvisionalLoadForFrame)
304    IPC_MESSAGE_HANDLER(ViewHostMsg_DidRedirectProvisionalLoad,
305                        OnDidRedirectProvisionalLoad)
306    IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateFavIconURL, OnUpdateFavIconURL)
307    IPC_MESSAGE_UNHANDLED(handled = false)
308  IPC_END_MESSAGE_MAP_EX()
309
310  return handled;
311}
312
313void PrerenderContents::OnDidStartProvisionalLoadForFrame(int64 frame_id,
314                                                          bool is_main_frame,
315                                                          const GURL& url) {
316  if (is_main_frame)
317    AddAliasURL(url);
318}
319
320void PrerenderContents::OnDidRedirectProvisionalLoad(int32 page_id,
321                                                     const GURL& source_url,
322                                                     const GURL& target_url) {
323  AddAliasURL(target_url);
324}
325
326void PrerenderContents::OnUpdateFavIconURL(int32 page_id,
327                                           const GURL& icon_url) {
328  icon_url_ = icon_url;
329}
330
331void PrerenderContents::AddAliasURL(const GURL& url) {
332  alias_urls_.push_back(url);
333}
334
335bool PrerenderContents::MatchesURL(const GURL& url) const {
336  return std::find(alias_urls_.begin(), alias_urls_.end(), url)
337      != alias_urls_.end();
338}
339
340void PrerenderContents::DidStopLoading() {
341  has_stopped_loading_ = true;
342}
343
344void PrerenderContents::Destroy(FinalStatus final_status) {
345  prerender_manager_->RemoveEntry(this);
346  set_final_status(final_status);
347  delete this;
348}
349