OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 "content/browser/web_contents/interstitial_page_impl.h" | |
6 | |
7 #include <vector> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/compiler_specific.h" | |
11 #include "base/message_loop/message_loop.h" | |
12 #include "base/strings/string_util.h" | |
13 #include "base/strings/utf_string_conversions.h" | |
14 #include "base/threading/thread.h" | |
15 #include "content/browser/dom_storage/dom_storage_context_wrapper.h" | |
16 #include "content/browser/dom_storage/session_storage_namespace_impl.h" | |
17 #include "content/browser/loader/resource_dispatcher_host_impl.h" | |
18 #include "content/browser/renderer_host/render_process_host_impl.h" | |
19 #include "content/browser/renderer_host/render_view_host_factory.h" | |
20 #include "content/browser/renderer_host/render_view_host_impl.h" | |
21 #include "content/browser/site_instance_impl.h" | |
22 #include "content/browser/web_contents/navigation_controller_impl.h" | |
23 #include "content/browser/web_contents/navigation_entry_impl.h" | |
24 #include "content/browser/web_contents/web_contents_impl.h" | |
25 #include "content/common/view_messages.h" | |
26 #include "content/port/browser/render_view_host_delegate_view.h" | |
27 #include "content/port/browser/render_widget_host_view_port.h" | |
28 #include "content/port/browser/web_contents_view_port.h" | |
29 #include "content/public/browser/browser_context.h" | |
30 #include "content/public/browser/browser_thread.h" | |
31 #include "content/public/browser/content_browser_client.h" | |
32 #include "content/public/browser/dom_operation_notification_details.h" | |
33 #include "content/public/browser/interstitial_page_delegate.h" | |
34 #include "content/public/browser/invalidate_type.h" | |
35 #include "content/public/browser/notification_service.h" | |
36 #include "content/public/browser/notification_source.h" | |
37 #include "content/public/browser/storage_partition.h" | |
38 #include "content/public/browser/web_contents_delegate.h" | |
39 #include "content/public/common/bindings_policy.h" | |
40 #include "content/public/common/page_transition_types.h" | |
41 #include "net/base/escape.h" | |
42 #include "net/url_request/url_request_context_getter.h" | |
43 | |
44 using WebKit::WebDragOperation; | |
45 using WebKit::WebDragOperationsMask; | |
46 | |
47 namespace content { | |
48 namespace { | |
49 | |
50 void ResourceRequestHelper(ResourceDispatcherHostImpl* rdh, | |
51 int process_id, | |
52 int render_view_host_id, | |
53 ResourceRequestAction action) { | |
54 switch (action) { | |
55 case BLOCK: | |
56 rdh->BlockRequestsForRoute(process_id, render_view_host_id); | |
57 break; | |
58 case RESUME: | |
59 rdh->ResumeBlockedRequestsForRoute(process_id, render_view_host_id); | |
60 break; | |
61 case CANCEL: | |
62 rdh->CancelBlockedRequestsForRoute(process_id, render_view_host_id); | |
63 break; | |
64 default: | |
65 NOTREACHED(); | |
66 } | |
67 } | |
68 | |
69 } // namespace | |
70 | |
71 class InterstitialPageImpl::InterstitialPageRVHDelegateView | |
72 : public RenderViewHostDelegateView { | |
73 public: | |
74 explicit InterstitialPageRVHDelegateView(InterstitialPageImpl* page); | |
75 | |
76 // RenderViewHostDelegateView implementation: | |
77 virtual void ShowPopupMenu(const gfx::Rect& bounds, | |
78 int item_height, | |
79 double item_font_size, | |
80 int selected_item, | |
81 const std::vector<MenuItem>& items, | |
82 bool right_aligned, | |
83 bool allow_multiple_selection) OVERRIDE; | |
84 virtual void StartDragging(const DropData& drop_data, | |
85 WebDragOperationsMask operations_allowed, | |
86 const gfx::ImageSkia& image, | |
87 const gfx::Vector2d& image_offset, | |
88 const DragEventSourceInfo& event_info) OVERRIDE; | |
89 virtual void UpdateDragCursor(WebDragOperation operation) OVERRIDE; | |
90 virtual void GotFocus() OVERRIDE; | |
91 virtual void TakeFocus(bool reverse) OVERRIDE; | |
92 virtual void OnFindReply(int request_id, | |
93 int number_of_matches, | |
94 const gfx::Rect& selection_rect, | |
95 int active_match_ordinal, | |
96 bool final_update); | |
97 | |
98 private: | |
99 InterstitialPageImpl* interstitial_page_; | |
100 | |
101 DISALLOW_COPY_AND_ASSIGN(InterstitialPageRVHDelegateView); | |
102 }; | |
103 | |
104 | |
105 // We keep a map of the various blocking pages shown as the UI tests need to | |
106 // be able to retrieve them. | |
107 typedef std::map<WebContents*, InterstitialPageImpl*> InterstitialPageMap; | |
108 static InterstitialPageMap* g_web_contents_to_interstitial_page; | |
109 | |
110 // Initializes g_web_contents_to_interstitial_page in a thread-safe manner. | |
111 // Should be called before accessing g_web_contents_to_interstitial_page. | |
112 static void InitInterstitialPageMap() { | |
113 if (!g_web_contents_to_interstitial_page) | |
114 g_web_contents_to_interstitial_page = new InterstitialPageMap; | |
115 } | |
116 | |
117 InterstitialPage* InterstitialPage::Create(WebContents* web_contents, | |
118 bool new_navigation, | |
119 const GURL& url, | |
120 InterstitialPageDelegate* delegate) { | |
121 return new InterstitialPageImpl( | |
122 web_contents, | |
123 static_cast<RenderWidgetHostDelegate*>( | |
124 static_cast<WebContentsImpl*>(web_contents)), | |
125 new_navigation, url, delegate); | |
126 } | |
127 | |
128 InterstitialPage* InterstitialPage::GetInterstitialPage( | |
129 WebContents* web_contents) { | |
130 InitInterstitialPageMap(); | |
131 InterstitialPageMap::const_iterator iter = | |
132 g_web_contents_to_interstitial_page->find(web_contents); | |
133 if (iter == g_web_contents_to_interstitial_page->end()) | |
134 return NULL; | |
135 | |
136 return iter->second; | |
137 } | |
138 | |
139 InterstitialPageImpl::InterstitialPageImpl( | |
140 WebContents* web_contents, | |
141 RenderWidgetHostDelegate* render_widget_host_delegate, | |
142 bool new_navigation, | |
143 const GURL& url, | |
144 InterstitialPageDelegate* delegate) | |
145 : WebContentsObserver(web_contents), | |
146 web_contents_(web_contents), | |
147 controller_(static_cast<NavigationControllerImpl*>( | |
148 &web_contents->GetController())), | |
149 render_widget_host_delegate_(render_widget_host_delegate), | |
150 url_(url), | |
151 new_navigation_(new_navigation), | |
152 should_discard_pending_nav_entry_(new_navigation), | |
153 reload_on_dont_proceed_(false), | |
154 enabled_(true), | |
155 action_taken_(NO_ACTION), | |
156 render_view_host_(NULL), | |
157 original_child_id_(web_contents->GetRenderProcessHost()->GetID()), | |
158 original_rvh_id_(web_contents->GetRenderViewHost()->GetRoutingID()), | |
159 should_revert_web_contents_title_(false), | |
160 web_contents_was_loading_(false), | |
161 resource_dispatcher_host_notified_(false), | |
162 rvh_delegate_view_(new InterstitialPageRVHDelegateView(this)), | |
163 create_view_(true), | |
164 delegate_(delegate), | |
165 weak_ptr_factory_(this) { | |
166 InitInterstitialPageMap(); | |
167 // It would be inconsistent to create an interstitial with no new navigation | |
168 // (which is the case when the interstitial was triggered by a sub-resource on | |
169 // a page) when we have a pending entry (in the process of loading a new top | |
170 // frame). | |
171 DCHECK(new_navigation || !web_contents->GetController().GetPendingEntry()); | |
172 } | |
173 | |
174 InterstitialPageImpl::~InterstitialPageImpl() { | |
175 } | |
176 | |
177 void InterstitialPageImpl::Show() { | |
178 if (!enabled()) | |
179 return; | |
180 | |
181 // If an interstitial is already showing or about to be shown, close it before | |
182 // showing the new one. | |
183 // Be careful not to take an action on the old interstitial more than once. | |
184 InterstitialPageMap::const_iterator iter = | |
185 g_web_contents_to_interstitial_page->find(web_contents_); | |
186 if (iter != g_web_contents_to_interstitial_page->end()) { | |
187 InterstitialPageImpl* interstitial = iter->second; | |
188 if (interstitial->action_taken_ != NO_ACTION) { | |
189 interstitial->Hide(); | |
190 } else { | |
191 // If we are currently showing an interstitial page for which we created | |
192 // a transient entry and a new interstitial is shown as the result of a | |
193 // new browser initiated navigation, then that transient entry has already | |
194 // been discarded and a new pending navigation entry created. | |
195 // So we should not discard that new pending navigation entry. | |
196 // See http://crbug.com/9791 | |
197 if (new_navigation_ && interstitial->new_navigation_) | |
198 interstitial->should_discard_pending_nav_entry_= false; | |
199 interstitial->DontProceed(); | |
200 } | |
201 } | |
202 | |
203 // Block the resource requests for the render view host while it is hidden. | |
204 TakeActionOnResourceDispatcher(BLOCK); | |
205 // We need to be notified when the RenderViewHost is destroyed so we can | |
206 // cancel the blocked requests. We cannot do that on | |
207 // NOTIFY_WEB_CONTENTS_DESTROYED as at that point the RenderViewHost has | |
208 // already been destroyed. | |
209 notification_registrar_.Add( | |
210 this, NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED, | |
211 Source<RenderWidgetHost>(controller_->delegate()->GetRenderViewHost())); | |
212 | |
213 // Update the g_web_contents_to_interstitial_page map. | |
214 iter = g_web_contents_to_interstitial_page->find(web_contents_); | |
215 DCHECK(iter == g_web_contents_to_interstitial_page->end()); | |
216 (*g_web_contents_to_interstitial_page)[web_contents_] = this; | |
217 | |
218 if (new_navigation_) { | |
219 NavigationEntryImpl* entry = new NavigationEntryImpl; | |
220 entry->SetURL(url_); | |
221 entry->SetVirtualURL(url_); | |
222 entry->set_page_type(PAGE_TYPE_INTERSTITIAL); | |
223 | |
224 // Give delegates a chance to set some states on the navigation entry. | |
225 delegate_->OverrideEntry(entry); | |
226 | |
227 controller_->SetTransientEntry(entry); | |
228 } | |
229 | |
230 DCHECK(!render_view_host_); | |
231 render_view_host_ = static_cast<RenderViewHostImpl*>(CreateRenderViewHost()); | |
232 render_view_host_->AttachToFrameTree(); | |
233 CreateWebContentsView(); | |
234 | |
235 std::string data_url = "data:text/html;charset=utf-8," + | |
236 net::EscapePath(delegate_->GetHTMLContents()); | |
237 render_view_host_->NavigateToURL(GURL(data_url)); | |
238 | |
239 notification_registrar_.Add(this, NOTIFICATION_NAV_ENTRY_PENDING, | |
240 Source<NavigationController>(controller_)); | |
241 notification_registrar_.Add( | |
242 this, NOTIFICATION_DOM_OPERATION_RESPONSE, | |
243 Source<RenderViewHost>(render_view_host_)); | |
244 } | |
245 | |
246 void InterstitialPageImpl::Hide() { | |
247 // We may have already been hidden, and are just waiting to be deleted. | |
248 // We can't check for enabled() here, because some callers have already | |
249 // called Disable. | |
250 if (!render_view_host_) | |
251 return; | |
252 | |
253 Disable(); | |
254 | |
255 RenderWidgetHostView* old_view = | |
256 controller_->delegate()->GetRenderViewHost()->GetView(); | |
257 if (controller_->delegate()->GetInterstitialPage() == this && | |
258 old_view && | |
259 !old_view->IsShowing() && | |
260 !controller_->delegate()->IsHidden()) { | |
261 // Show the original RVH since we're going away. Note it might not exist if | |
262 // the renderer crashed while the interstitial was showing. | |
263 // Note that it is important that we don't call Show() if the view is | |
264 // already showing. That would result in bad things (unparented HWND on | |
265 // Windows for example) happening. | |
266 old_view->Show(); | |
267 } | |
268 | |
269 // If the focus was on the interstitial, let's keep it to the page. | |
270 // (Note that in unit-tests the RVH may not have a view). | |
271 if (render_view_host_->GetView() && | |
272 render_view_host_->GetView()->HasFocus() && | |
273 controller_->delegate()->GetRenderViewHost()->GetView()) { | |
274 RenderWidgetHostViewPort::FromRWHV( | |
275 controller_->delegate()->GetRenderViewHost()->GetView())->Focus(); | |
276 } | |
277 | |
278 // Shutdown the RVH asynchronously, as we may have been called from a RVH | |
279 // delegate method, and we can't delete the RVH out from under itself. | |
280 base::MessageLoop::current()->PostNonNestableTask( | |
281 FROM_HERE, | |
282 base::Bind(&InterstitialPageImpl::Shutdown, | |
283 weak_ptr_factory_.GetWeakPtr(), | |
284 render_view_host_)); | |
285 render_view_host_ = NULL; | |
286 frame_tree_.SwapMainFrame(NULL); | |
287 controller_->delegate()->DetachInterstitialPage(); | |
288 // Let's revert to the original title if necessary. | |
289 NavigationEntry* entry = controller_->GetVisibleEntry(); | |
290 if (!new_navigation_ && should_revert_web_contents_title_) { | |
291 entry->SetTitle(original_web_contents_title_); | |
292 controller_->delegate()->NotifyNavigationStateChanged( | |
293 INVALIDATE_TYPE_TITLE); | |
294 } | |
295 | |
296 InterstitialPageMap::iterator iter = | |
297 g_web_contents_to_interstitial_page->find(web_contents_); | |
298 DCHECK(iter != g_web_contents_to_interstitial_page->end()); | |
299 if (iter != g_web_contents_to_interstitial_page->end()) | |
300 g_web_contents_to_interstitial_page->erase(iter); | |
301 | |
302 // Clear the WebContents pointer, because it may now be deleted. | |
303 // This signifies that we are in the process of shutting down. | |
304 web_contents_ = NULL; | |
305 } | |
306 | |
307 void InterstitialPageImpl::Observe( | |
308 int type, | |
309 const NotificationSource& source, | |
310 const NotificationDetails& details) { | |
311 switch (type) { | |
312 case NOTIFICATION_NAV_ENTRY_PENDING: | |
313 // We are navigating away from the interstitial (the user has typed a URL | |
314 // in the location bar or clicked a bookmark). Make sure clicking on the | |
315 // interstitial will have no effect. Also cancel any blocked requests | |
316 // on the ResourceDispatcherHost. Note that when we get this notification | |
317 // the RenderViewHost has not yet navigated so we'll unblock the | |
318 // RenderViewHost before the resource request for the new page we are | |
319 // navigating arrives in the ResourceDispatcherHost. This ensures that | |
320 // request won't be blocked if the same RenderViewHost was used for the | |
321 // new navigation. | |
322 Disable(); | |
323 TakeActionOnResourceDispatcher(CANCEL); | |
324 break; | |
325 case NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED: | |
326 if (action_taken_ == NO_ACTION) { | |
327 // The RenderViewHost is being destroyed (as part of the tab being | |
328 // closed); make sure we clear the blocked requests. | |
329 RenderViewHost* rvh = static_cast<RenderViewHost*>( | |
330 static_cast<RenderViewHostImpl*>( | |
331 RenderWidgetHostImpl::From( | |
332 Source<RenderWidgetHost>(source).ptr()))); | |
333 DCHECK(rvh->GetProcess()->GetID() == original_child_id_ && | |
334 rvh->GetRoutingID() == original_rvh_id_); | |
335 TakeActionOnResourceDispatcher(CANCEL); | |
336 } | |
337 break; | |
338 case NOTIFICATION_DOM_OPERATION_RESPONSE: | |
339 if (enabled()) { | |
340 Details<DomOperationNotificationDetails> dom_op_details( | |
341 details); | |
342 delegate_->CommandReceived(dom_op_details->json); | |
343 } | |
344 break; | |
345 default: | |
346 NOTREACHED(); | |
347 } | |
348 } | |
349 | |
350 void InterstitialPageImpl::NavigationEntryCommitted( | |
351 const LoadCommittedDetails& load_details) { | |
352 OnNavigatingAwayOrTabClosing(); | |
353 } | |
354 | |
355 void InterstitialPageImpl::WebContentsDestroyed(WebContents* web_contents) { | |
356 OnNavigatingAwayOrTabClosing(); | |
357 } | |
358 | |
359 RenderViewHostDelegateView* InterstitialPageImpl::GetDelegateView() { | |
360 return rvh_delegate_view_.get(); | |
361 } | |
362 | |
363 const GURL& InterstitialPageImpl::GetURL() const { | |
364 return url_; | |
365 } | |
366 | |
367 void InterstitialPageImpl::RenderViewTerminated( | |
368 RenderViewHost* render_view_host, | |
369 base::TerminationStatus status, | |
370 int error_code) { | |
371 // Our renderer died. This should not happen in normal cases. | |
372 // If we haven't already started shutdown, just dismiss the interstitial. | |
373 // We cannot check for enabled() here, because we may have called Disable | |
374 // without calling Hide. | |
375 if (render_view_host_) | |
376 DontProceed(); | |
377 } | |
378 | |
379 void InterstitialPageImpl::DidNavigate( | |
380 RenderViewHost* render_view_host, | |
381 const ViewHostMsg_FrameNavigate_Params& params) { | |
382 // A fast user could have navigated away from the page that triggered the | |
383 // interstitial while the interstitial was loading, that would have disabled | |
384 // us. In that case we can dismiss ourselves. | |
385 if (!enabled()) { | |
386 DontProceed(); | |
387 return; | |
388 } | |
389 if (PageTransitionCoreTypeIs(params.transition, | |
390 PAGE_TRANSITION_AUTO_SUBFRAME)) { | |
391 // No need to handle navigate message from iframe in the interstitial page. | |
392 return; | |
393 } | |
394 | |
395 // The RenderViewHost has loaded its contents, we can show it now. | |
396 if (!controller_->delegate()->IsHidden()) | |
397 render_view_host_->GetView()->Show(); | |
398 controller_->delegate()->AttachInterstitialPage(this); | |
399 | |
400 RenderWidgetHostView* rwh_view = | |
401 controller_->delegate()->GetRenderViewHost()->GetView(); | |
402 | |
403 // The RenderViewHost may already have crashed before we even get here. | |
404 if (rwh_view) { | |
405 // If the page has focus, focus the interstitial. | |
406 if (rwh_view->HasFocus()) | |
407 Focus(); | |
408 | |
409 // Hide the original RVH since we're showing the interstitial instead. | |
410 rwh_view->Hide(); | |
411 } | |
412 | |
413 // Notify the tab we are not loading so the throbber is stopped. It also | |
414 // causes a WebContentsObserver::DidStopLoading callback that the | |
415 // AutomationProvider (used by the UI tests) expects to consider a navigation | |
416 // as complete. Without this, navigating in a UI test to a URL that triggers | |
417 // an interstitial would hang. | |
418 web_contents_was_loading_ = controller_->delegate()->IsLoading(); | |
419 controller_->delegate()->SetIsLoading( | |
420 controller_->delegate()->GetRenderViewHost(), false, NULL); | |
421 } | |
422 | |
423 void InterstitialPageImpl::UpdateTitle( | |
424 RenderViewHost* render_view_host, | |
425 int32 page_id, | |
426 const string16& title, | |
427 base::i18n::TextDirection title_direction) { | |
428 if (!enabled()) | |
429 return; | |
430 | |
431 DCHECK(render_view_host == render_view_host_); | |
432 NavigationEntry* entry = controller_->GetVisibleEntry(); | |
433 if (!entry) { | |
434 // Crash reports from the field indicate this can be NULL. | |
435 // This is unexpected as InterstitialPages constructed with the | |
436 // new_navigation flag set to true create a transient navigation entry | |
437 // (that is returned as the active entry). And the only case so far of | |
438 // interstitial created with that flag set to false is with the | |
439 // SafeBrowsingBlockingPage, when the resource triggering the interstitial | |
440 // is a sub-resource, meaning the main page has already been loaded and a | |
441 // navigation entry should have been created. | |
442 NOTREACHED(); | |
443 return; | |
444 } | |
445 | |
446 // If this interstitial is shown on an existing navigation entry, we'll need | |
447 // to remember its title so we can revert to it when hidden. | |
448 if (!new_navigation_ && !should_revert_web_contents_title_) { | |
449 original_web_contents_title_ = entry->GetTitle(); | |
450 should_revert_web_contents_title_ = true; | |
451 } | |
452 // TODO(evan): make use of title_direction. | |
453 // http://code.google.com/p/chromium/issues/detail?id=27094 | |
454 entry->SetTitle(title); | |
455 controller_->delegate()->NotifyNavigationStateChanged(INVALIDATE_TYPE_TITLE); | |
456 } | |
457 | |
458 RendererPreferences InterstitialPageImpl::GetRendererPrefs( | |
459 BrowserContext* browser_context) const { | |
460 delegate_->OverrideRendererPrefs(&renderer_preferences_); | |
461 return renderer_preferences_; | |
462 } | |
463 | |
464 WebPreferences InterstitialPageImpl::GetWebkitPrefs() { | |
465 if (!enabled()) | |
466 return WebPreferences(); | |
467 | |
468 return render_view_host_->GetWebkitPrefs(url_); | |
469 } | |
470 | |
471 void InterstitialPageImpl::RenderWidgetDeleted( | |
472 RenderWidgetHostImpl* render_widget_host) { | |
473 delete this; | |
474 } | |
475 | |
476 bool InterstitialPageImpl::PreHandleKeyboardEvent( | |
477 const NativeWebKeyboardEvent& event, | |
478 bool* is_keyboard_shortcut) { | |
479 if (!enabled()) | |
480 return false; | |
481 return render_widget_host_delegate_->PreHandleKeyboardEvent( | |
482 event, is_keyboard_shortcut); | |
483 } | |
484 | |
485 void InterstitialPageImpl::HandleKeyboardEvent( | |
486 const NativeWebKeyboardEvent& event) { | |
487 if (enabled()) | |
488 render_widget_host_delegate_->HandleKeyboardEvent(event); | |
489 } | |
490 | |
491 #if defined(OS_WIN) && defined(USE_AURA) | |
492 gfx::NativeViewAccessible | |
493 InterstitialPageImpl::GetParentNativeViewAccessible() { | |
494 return render_widget_host_delegate_->GetParentNativeViewAccessible(); | |
495 } | |
496 #endif | |
497 | |
498 WebContents* InterstitialPageImpl::web_contents() const { | |
499 return web_contents_; | |
500 } | |
501 | |
502 RenderViewHost* InterstitialPageImpl::CreateRenderViewHost() { | |
503 if (!enabled()) | |
504 return NULL; | |
505 | |
506 // Interstitial pages don't want to share the session storage so we mint a | |
507 // new one. | |
508 BrowserContext* browser_context = web_contents()->GetBrowserContext(); | |
509 scoped_refptr<SiteInstance> site_instance = | |
510 SiteInstance::Create(browser_context); | |
511 DOMStorageContextWrapper* dom_storage_context = | |
512 static_cast<DOMStorageContextWrapper*>( | |
513 BrowserContext::GetStoragePartition( | |
514 browser_context, site_instance.get())->GetDOMStorageContext()); | |
515 session_storage_namespace_ = | |
516 new SessionStorageNamespaceImpl(dom_storage_context); | |
517 | |
518 return RenderViewHostFactory::Create(site_instance.get(), | |
519 this, | |
520 this, | |
521 MSG_ROUTING_NONE, | |
522 MSG_ROUTING_NONE, | |
523 false, | |
524 false); | |
525 } | |
526 | |
527 WebContentsView* InterstitialPageImpl::CreateWebContentsView() { | |
528 if (!enabled() || !create_view_) | |
529 return NULL; | |
530 WebContentsView* web_contents_view = web_contents()->GetView(); | |
531 WebContentsViewPort* web_contents_view_port = | |
532 static_cast<WebContentsViewPort*>(web_contents_view); | |
533 RenderWidgetHostView* view = | |
534 web_contents_view_port->CreateViewForWidget(render_view_host_); | |
535 render_view_host_->SetView(view); | |
536 render_view_host_->AllowBindings(BINDINGS_POLICY_DOM_AUTOMATION); | |
537 | |
538 int32 max_page_id = web_contents()-> | |
539 GetMaxPageIDForSiteInstance(render_view_host_->GetSiteInstance()); | |
540 render_view_host_->CreateRenderView(string16(), | |
541 MSG_ROUTING_NONE, | |
542 max_page_id); | |
543 controller_->delegate()->RenderViewForInterstitialPageCreated( | |
544 render_view_host_); | |
545 view->SetSize(web_contents_view->GetContainerSize()); | |
546 // Don't show the interstitial until we have navigated to it. | |
547 view->Hide(); | |
548 return web_contents_view; | |
549 } | |
550 | |
551 void InterstitialPageImpl::Proceed() { | |
552 // Don't repeat this if we are already shutting down. We cannot check for | |
553 // enabled() here, because we may have called Disable without calling Hide. | |
554 if (!render_view_host_) | |
555 return; | |
556 | |
557 if (action_taken_ != NO_ACTION) { | |
558 NOTREACHED(); | |
559 return; | |
560 } | |
561 Disable(); | |
562 action_taken_ = PROCEED_ACTION; | |
563 | |
564 // Resumes the throbber, if applicable. | |
565 if (web_contents_was_loading_) | |
566 controller_->delegate()->SetIsLoading( | |
567 controller_->delegate()->GetRenderViewHost(), true, NULL); | |
568 | |
569 // If this is a new navigation, the old page is going away, so we cancel any | |
570 // blocked requests for it. If it is not a new navigation, then it means the | |
571 // interstitial was shown as a result of a resource loading in the page. | |
572 // Since the user wants to proceed, we'll let any blocked request go through. | |
573 if (new_navigation_) | |
574 TakeActionOnResourceDispatcher(CANCEL); | |
575 else | |
576 TakeActionOnResourceDispatcher(RESUME); | |
577 | |
578 // No need to hide if we are a new navigation, we'll get hidden when the | |
579 // navigation is committed. | |
580 if (!new_navigation_) { | |
581 Hide(); | |
582 delegate_->OnProceed(); | |
583 return; | |
584 } | |
585 | |
586 delegate_->OnProceed(); | |
587 } | |
588 | |
589 void InterstitialPageImpl::DontProceed() { | |
590 // Don't repeat this if we are already shutting down. We cannot check for | |
591 // enabled() here, because we may have called Disable without calling Hide. | |
592 if (!render_view_host_) | |
593 return; | |
594 DCHECK(action_taken_ != DONT_PROCEED_ACTION); | |
595 | |
596 Disable(); | |
597 action_taken_ = DONT_PROCEED_ACTION; | |
598 | |
599 // If this is a new navigation, we are returning to the original page, so we | |
600 // resume blocked requests for it. If it is not a new navigation, then it | |
601 // means the interstitial was shown as a result of a resource loading in the | |
602 // page and we won't return to the original page, so we cancel blocked | |
603 // requests in that case. | |
604 if (new_navigation_) | |
605 TakeActionOnResourceDispatcher(RESUME); | |
606 else | |
607 TakeActionOnResourceDispatcher(CANCEL); | |
608 | |
609 if (should_discard_pending_nav_entry_) { | |
610 // Since no navigation happens we have to discard the transient entry | |
611 // explicitely. Note that by calling DiscardNonCommittedEntries() we also | |
612 // discard the pending entry, which is what we want, since the navigation is | |
613 // cancelled. | |
614 controller_->DiscardNonCommittedEntries(); | |
615 } | |
616 | |
617 if (reload_on_dont_proceed_) | |
618 controller_->Reload(true); | |
619 | |
620 Hide(); | |
621 delegate_->OnDontProceed(); | |
622 } | |
623 | |
624 void InterstitialPageImpl::CancelForNavigation() { | |
625 // The user is trying to navigate away. We should unblock the renderer and | |
626 // disable the interstitial, but keep it visible until the navigation | |
627 // completes. | |
628 Disable(); | |
629 // If this interstitial was shown for a new navigation, allow any navigations | |
630 // on the original page to resume (e.g., subresource requests, XHRs, etc). | |
631 // Otherwise, cancel the pending, possibly dangerous navigations. | |
632 if (new_navigation_) | |
633 TakeActionOnResourceDispatcher(RESUME); | |
634 else | |
635 TakeActionOnResourceDispatcher(CANCEL); | |
636 } | |
637 | |
638 void InterstitialPageImpl::SetSize(const gfx::Size& size) { | |
639 if (!enabled()) | |
640 return; | |
641 #if !defined(OS_MACOSX) | |
642 // When a tab is closed, we might be resized after our view was NULLed | |
643 // (typically if there was an info-bar). | |
644 if (render_view_host_->GetView()) | |
645 render_view_host_->GetView()->SetSize(size); | |
646 #else | |
647 // TODO(port): Does Mac need to SetSize? | |
648 NOTIMPLEMENTED(); | |
649 #endif | |
650 } | |
651 | |
652 void InterstitialPageImpl::Focus() { | |
653 // Focus the native window. | |
654 if (!enabled()) | |
655 return; | |
656 RenderWidgetHostViewPort::FromRWHV(render_view_host_->GetView())->Focus(); | |
657 } | |
658 | |
659 void InterstitialPageImpl::FocusThroughTabTraversal(bool reverse) { | |
660 if (!enabled()) | |
661 return; | |
662 render_view_host_->SetInitialFocus(reverse); | |
663 } | |
664 | |
665 RenderWidgetHostView* InterstitialPageImpl::GetView() { | |
666 return render_view_host_->GetView(); | |
667 } | |
668 | |
669 RenderViewHost* InterstitialPageImpl::GetRenderViewHostForTesting() const { | |
670 return render_view_host_; | |
671 } | |
672 | |
673 #if defined(OS_ANDROID) | |
674 RenderViewHost* InterstitialPageImpl::GetRenderViewHost() const { | |
675 return render_view_host_; | |
676 } | |
677 #endif | |
678 | |
679 InterstitialPageDelegate* InterstitialPageImpl::GetDelegateForTesting() { | |
680 return delegate_.get(); | |
681 } | |
682 | |
683 void InterstitialPageImpl::DontCreateViewForTesting() { | |
684 create_view_ = false; | |
685 } | |
686 | |
687 gfx::Rect InterstitialPageImpl::GetRootWindowResizerRect() const { | |
688 return gfx::Rect(); | |
689 } | |
690 | |
691 void InterstitialPageImpl::CreateNewWindow( | |
692 int route_id, | |
693 int main_frame_route_id, | |
694 const ViewHostMsg_CreateWindow_Params& params, | |
695 SessionStorageNamespace* session_storage_namespace) { | |
696 NOTREACHED() << "InterstitialPage does not support showing popups yet."; | |
697 } | |
698 | |
699 void InterstitialPageImpl::CreateNewWidget(int route_id, | |
700 WebKit::WebPopupType popup_type) { | |
701 NOTREACHED() << "InterstitialPage does not support showing drop-downs yet."; | |
702 } | |
703 | |
704 void InterstitialPageImpl::CreateNewFullscreenWidget(int route_id) { | |
705 NOTREACHED() | |
706 << "InterstitialPage does not support showing full screen popups."; | |
707 } | |
708 | |
709 void InterstitialPageImpl::ShowCreatedWindow(int route_id, | |
710 WindowOpenDisposition disposition, | |
711 const gfx::Rect& initial_pos, | |
712 bool user_gesture) { | |
713 NOTREACHED() << "InterstitialPage does not support showing popups yet."; | |
714 } | |
715 | |
716 void InterstitialPageImpl::ShowCreatedWidget(int route_id, | |
717 const gfx::Rect& initial_pos) { | |
718 NOTREACHED() << "InterstitialPage does not support showing drop-downs yet."; | |
719 } | |
720 | |
721 void InterstitialPageImpl::ShowCreatedFullscreenWidget(int route_id) { | |
722 NOTREACHED() | |
723 << "InterstitialPage does not support showing full screen popups."; | |
724 } | |
725 | |
726 SessionStorageNamespace* InterstitialPageImpl::GetSessionStorageNamespace( | |
727 SiteInstance* instance) { | |
728 return session_storage_namespace_.get(); | |
729 } | |
730 | |
731 FrameTree* InterstitialPageImpl::GetFrameTree() { | |
732 return &frame_tree_; | |
733 } | |
734 | |
735 void InterstitialPageImpl::Disable() { | |
736 enabled_ = false; | |
737 } | |
738 | |
739 void InterstitialPageImpl::Shutdown(RenderViewHostImpl* render_view_host) { | |
740 render_view_host->Shutdown(); | |
741 // We are deleted now. | |
742 } | |
743 | |
744 void InterstitialPageImpl::OnNavigatingAwayOrTabClosing() { | |
745 if (action_taken_ == NO_ACTION) { | |
746 // We are navigating away from the interstitial or closing a tab with an | |
747 // interstitial. Default to DontProceed(). We don't just call Hide as | |
748 // subclasses will almost certainly override DontProceed to do some work | |
749 // (ex: close pending connections). | |
750 DontProceed(); | |
751 } else { | |
752 // User decided to proceed and either the navigation was committed or | |
753 // the tab was closed before that. | |
754 Hide(); | |
755 } | |
756 } | |
757 | |
758 void InterstitialPageImpl::TakeActionOnResourceDispatcher( | |
759 ResourceRequestAction action) { | |
760 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)) << | |
761 "TakeActionOnResourceDispatcher should be called on the main thread."; | |
762 | |
763 if (action == CANCEL || action == RESUME) { | |
764 if (resource_dispatcher_host_notified_) | |
765 return; | |
766 resource_dispatcher_host_notified_ = true; | |
767 } | |
768 | |
769 // The tab might not have a render_view_host if it was closed (in which case, | |
770 // we have taken care of the blocked requests when processing | |
771 // NOTIFY_RENDER_WIDGET_HOST_DESTROYED. | |
772 // Also we need to test there is a ResourceDispatcherHostImpl, as when unit- | |
773 // tests we don't have one. | |
774 RenderViewHostImpl* rvh = RenderViewHostImpl::FromID(original_child_id_, | |
775 original_rvh_id_); | |
776 if (!rvh || !ResourceDispatcherHostImpl::Get()) | |
777 return; | |
778 | |
779 BrowserThread::PostTask( | |
780 BrowserThread::IO, | |
781 FROM_HERE, | |
782 base::Bind( | |
783 &ResourceRequestHelper, | |
784 ResourceDispatcherHostImpl::Get(), | |
785 original_child_id_, | |
786 original_rvh_id_, | |
787 action)); | |
788 } | |
789 | |
790 InterstitialPageImpl::InterstitialPageRVHDelegateView:: | |
791 InterstitialPageRVHDelegateView(InterstitialPageImpl* page) | |
792 : interstitial_page_(page) { | |
793 } | |
794 | |
795 void InterstitialPageImpl::InterstitialPageRVHDelegateView::ShowPopupMenu( | |
796 const gfx::Rect& bounds, | |
797 int item_height, | |
798 double item_font_size, | |
799 int selected_item, | |
800 const std::vector<MenuItem>& items, | |
801 bool right_aligned, | |
802 bool allow_multiple_selection) { | |
803 NOTREACHED() << "InterstitialPage does not support showing popup menus."; | |
804 } | |
805 | |
806 void InterstitialPageImpl::InterstitialPageRVHDelegateView::StartDragging( | |
807 const DropData& drop_data, | |
808 WebDragOperationsMask allowed_operations, | |
809 const gfx::ImageSkia& image, | |
810 const gfx::Vector2d& image_offset, | |
811 const DragEventSourceInfo& event_info) { | |
812 NOTREACHED() << "InterstitialPage does not support dragging yet."; | |
813 } | |
814 | |
815 void InterstitialPageImpl::InterstitialPageRVHDelegateView::UpdateDragCursor( | |
816 WebDragOperation) { | |
817 NOTREACHED() << "InterstitialPage does not support dragging yet."; | |
818 } | |
819 | |
820 void InterstitialPageImpl::InterstitialPageRVHDelegateView::GotFocus() { | |
821 WebContents* web_contents = interstitial_page_->web_contents(); | |
822 if (web_contents && web_contents->GetDelegate()) | |
823 web_contents->GetDelegate()->WebContentsFocused(web_contents); | |
824 } | |
825 | |
826 void InterstitialPageImpl::InterstitialPageRVHDelegateView::TakeFocus( | |
827 bool reverse) { | |
828 if (!interstitial_page_->web_contents()) | |
829 return; | |
830 WebContentsImpl* web_contents = | |
831 static_cast<WebContentsImpl*>(interstitial_page_->web_contents()); | |
832 if (!web_contents->GetDelegateView()) | |
833 return; | |
834 | |
835 web_contents->GetDelegateView()->TakeFocus(reverse); | |
836 } | |
837 | |
838 void InterstitialPageImpl::InterstitialPageRVHDelegateView::OnFindReply( | |
839 int request_id, int number_of_matches, const gfx::Rect& selection_rect, | |
840 int active_match_ordinal, bool final_update) { | |
841 } | |
842 | |
843 } // namespace content | |
OLD | NEW |