From b6e09acf8ced26198871626c76bb5a3741cc51f1 Mon Sep 17 00:00:00 2001 From: "brettw@google.com" Date: Tue, 12 Aug 2008 16:11:09 +0000 Subject: Move RenderViewHost management out of WebContents into a new RenderViewHostManager object. The goal for this patch is to change no logic or APIs, just move the code. So there are some not very clean callback functions and no new unit tests for this file (although is is still covered by the same WebContents unit tests). This should make the actual cleanup in a later pass much easier to follow. I changed the ordering of only a few operations (like WebContents shutdown), and checked that this shouldn't matter. I had to change the "source" for several notifications since they are no longer sent from the WebContents. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@716 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/browser/automation/automation_provider.cc | 2 +- chrome/browser/browser.vcproj | 8 + chrome/browser/browser_commands.cc | 4 +- chrome/browser/dom_ui/dom_ui_host.cc | 5 +- chrome/browser/dom_ui/dom_ui_host.h | 3 +- chrome/browser/render_view_host_manager.cc | 956 ++++++++++++++++++ chrome/browser/render_view_host_manager.h | 339 +++++++ chrome/browser/ssl_blocking_page.cc | 4 +- chrome/browser/tab_contents.h | 3 + chrome/browser/tab_contents_container_view.cc | 12 +- chrome/browser/tabs/tab_strip_model.cc | 5 +- chrome/browser/web_contents.cc | 1117 +++------------------- chrome/browser/web_contents.h | 239 ++--- chrome/browser/web_contents_unittest.cc | 155 +-- chrome/browser/web_drop_target.cc | 8 +- chrome/common/notification_types.h | 11 +- 16 files changed, 1645 insertions(+), 1226 deletions(-) create mode 100644 chrome/browser/render_view_host_manager.cc create mode 100644 chrome/browser/render_view_host_manager.h (limited to 'chrome') diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index 5c10d68..ae675bf 100644 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -2011,7 +2011,7 @@ void AutomationProvider::GetPageType(const IPC::Message& message, int handle) { // no navigation entry were created for it we need to ask the WebContents. if (page_type == NavigationEntry::NORMAL_PAGE && tab->active_contents()->AsWebContents() && - tab->active_contents()->AsWebContents()->IsShowingInterstitialPage()) + tab->active_contents()->AsWebContents()->showing_interstitial_page()) page_type = NavigationEntry::INTERSTITIAL_PAGE; Send(new AutomationMsg_GetPageTypeResponse(message.routing_id(), true, diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj index b7c955b..5598daf 100644 --- a/chrome/browser/browser.vcproj +++ b/chrome/browser/browser.vcproj @@ -894,6 +894,14 @@ > + + + + diff --git a/chrome/browser/browser_commands.cc b/chrome/browser/browser_commands.cc index 79e1f06..9492c71 100644 --- a/chrome/browser/browser_commands.cc +++ b/chrome/browser/browser_commands.cc @@ -780,7 +780,7 @@ void Browser::GoBack() { // to the previous page as it is already available in a render view host // of the WebContents. This makes the back more snappy and avoids potential // reloading of POST pages. - if (web_contents && web_contents->IsShowingInterstitialPage()) { + if (web_contents && web_contents->showing_interstitial_page()) { // Let the delegate decide (if any) if it wants to handle the back // navigation (it may have extra things to do). if (web_contents->interstitial_page_delegate() && @@ -830,7 +830,7 @@ void Browser::Reload() { TabContents* current_tab = GetSelectedTabContents(); if (current_tab) { WebContents* web_contents = current_tab->AsWebContents(); - if (web_contents && web_contents->IsShowingInterstitialPage()) { + if (web_contents && web_contents->showing_interstitial_page()) { NavigationEntry* entry = current_tab->controller()->GetActiveEntry(); DCHECK(entry); // Should exist if interstitial is showing. OpenURL(entry->GetURL(), CURRENT_TAB, PageTransition::RELOAD); diff --git a/chrome/browser/dom_ui/dom_ui_host.cc b/chrome/browser/dom_ui/dom_ui_host.cc index b7d685f..f00c5a3 100644 --- a/chrome/browser/dom_ui/dom_ui_host.cc +++ b/chrome/browser/dom_ui/dom_ui_host.cc @@ -54,13 +54,14 @@ DOMUIHost::~DOMUIHost() { STLDeleteContainerPointers(handlers_.begin(), handlers_.end()); } -bool DOMUIHost::CreateRenderView(RenderViewHost* render_view_host) { +bool DOMUIHost::CreateRenderViewForRenderManager( + RenderViewHost* render_view_host) { // Be sure to enable DOM UI bindings on the RenderViewHost before // CreateRenderView is called. Since a cross-site transition may be // involved, this may or may not be the same RenderViewHost that we had when // we were created. render_view_host->AllowDOMUIBindings(); - return WebContents::CreateRenderView(render_view_host); + return WebContents::CreateRenderViewForRenderManager(render_view_host); } void DOMUIHost::AddMessageHandler(DOMMessageHandler* handler) { diff --git a/chrome/browser/dom_ui/dom_ui_host.h b/chrome/browser/dom_ui/dom_ui_host.h index 09d624f..a0a5970 100644 --- a/chrome/browser/dom_ui/dom_ui_host.h +++ b/chrome/browser/dom_ui/dom_ui_host.h @@ -60,7 +60,8 @@ class DOMUIHost : public WebContents { RenderViewHostFactory* render_view_factory); // Initializes the given renderer, after enabling DOM UI bindings on it. - virtual bool CreateRenderView(RenderViewHost* render_view_host); + virtual bool CreateRenderViewForRenderManager( + RenderViewHost* render_view_host); // Add type-specific javascript message handlers. // TODO(timsteele): Any implementation of this method should really be done diff --git a/chrome/browser/render_view_host_manager.cc b/chrome/browser/render_view_host_manager.cc new file mode 100644 index 0000000..b046c8e --- /dev/null +++ b/chrome/browser/render_view_host_manager.cc @@ -0,0 +1,956 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/render_view_host_manager.h" + +#include "base/command_line.h" +#include "base/logging.h" +#include "chrome/browser/interstitial_page_delegate.h" +#include "chrome/browser/navigation_controller.h" +#include "chrome/browser/navigation_entry.h" +#include "chrome/browser/render_widget_host_view.h" +#include "chrome/browser/render_view_host.h" +#include "chrome/browser/render_view_host_delegate.h" +#include "chrome/browser/site_instance.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/notification_service.h" + +// Destroys the given |**render_view_host| and NULLs out |*render_view_host| +// and then NULLs the field. Callers should only pass pointers to the +// pending_render_view_host_, interstitial_render_view_host_, or +// original_render_view_host_ fields of this object. +static void CancelRenderView(RenderViewHost** render_view_host) { + (*render_view_host)->Shutdown(); + (*render_view_host) = NULL; +} + +RenderViewHostManager::RenderViewHostManager( + RenderViewHostFactory* render_view_factory, + RenderViewHostDelegate* render_view_delegate, + Delegate* delegate) + : delegate_(delegate), + renderer_state_(NORMAL), + render_view_factory_(render_view_factory), + render_view_delegate_(render_view_delegate), + render_view_host_(NULL), + original_render_view_host_(NULL), + interstitial_render_view_host_(NULL), + pending_render_view_host_(NULL), + interstitial_delegate_(NULL), + showing_repost_interstitial_(false) { +} + +RenderViewHostManager::~RenderViewHostManager() { + // Shutdown should have been called which should have cleaned these up. + DCHECK(!render_view_host_); + DCHECK(!pending_render_view_host_); + DCHECK(!original_render_view_host_); + DCHECK(!interstitial_render_view_host_); +} + +void RenderViewHostManager::Init(Profile* profile, + SiteInstance* site_instance, + int routing_id, + HANDLE modal_dialog_event) { + // Create a RenderViewHost, once we have an instance. It is important to + // immediately give this SiteInstance to a RenderViewHost so that it is + // ref counted. + if (!site_instance) + site_instance = SiteInstance::CreateSiteInstance(profile); + render_view_host_ = CreateRenderViewHost( + site_instance, routing_id, modal_dialog_event); +} + +void RenderViewHostManager::Shutdown() { + if (showing_interstitial_page()) { + // The tab is closed while the interstitial page is showing, hide and + // destroy it. + HideInterstitialPage(false, false); + } + DCHECK(!interstitial_render_view_host_) << "Should have been deleted by Hide"; + + if (pending_render_view_host_) { + pending_render_view_host_->Shutdown(); + pending_render_view_host_ = NULL; + } + if (original_render_view_host_) { + original_render_view_host_->Shutdown(); + original_render_view_host_ = NULL; + } + + // We should always have a main RenderViewHost. + render_view_host_->Shutdown(); + render_view_host_ = NULL; +} + +RenderViewHost* RenderViewHostManager::Navigate(const NavigationEntry& entry) { + RenderViewHost* dest_render_view_host = UpdateRendererStateNavigate(entry); + if (!dest_render_view_host) + return NULL; // We weren't able to create a pending render view host. + + // If the current render_view_host_ isn't live, we should create it so + // that we don't show a sad tab while the dest_render_view_host fetches + // its first page. (Bug 1145340) + if (dest_render_view_host != render_view_host_ && + !render_view_host_->IsRenderViewLive()) { + delegate_->CreateRenderViewForRenderManager(render_view_host_); + } + + // If the renderer crashed, then try to create a new one to satisfy this + // navigation request. + if (!dest_render_view_host->IsRenderViewLive()) { + if (!delegate_->CreateRenderViewForRenderManager(dest_render_view_host)) + return NULL; + + // Now that we've created a new renderer, be sure to hide it if it isn't + // our primary one. Otherwise, we might crash if we try to call Show() + // on it later. + if (dest_render_view_host != render_view_host_ && + dest_render_view_host->view()) { + dest_render_view_host->view()->Hide(); + } else { + // This is our primary renderer, notify here as we won't be calling + // SwapToRenderView (which does the notify). + RenderViewHostSwitchedDetails details; + details.new_host = render_view_host_; + details.old_host = NULL; + NotificationService::current()->Notify( + NOTIFY_RENDER_VIEW_HOST_CHANGED, + Source( + delegate_->GetControllerForRenderManager()), + Details(&details)); + } + } + + showing_repost_interstitial_ = false; + return dest_render_view_host; +} + +void RenderViewHostManager::Stop() { + render_view_host_->Stop(); + + // If we aren't in the NORMAL renderer state, we should stop the pending + // renderers. This will lead to a DidFailProvisionalLoad, which will + // properly destroy them. + if (renderer_state_ == PENDING) { + pending_render_view_host_->Stop(); + + } else if (renderer_state_ == ENTERING_INTERSTITIAL) { + interstitial_render_view_host_->Stop(); + if (pending_render_view_host_) { + pending_render_view_host_->Stop(); + } + + } else if (renderer_state_ == LEAVING_INTERSTITIAL) { + if (pending_render_view_host_) { + pending_render_view_host_->Stop(); + } + } +} + +void RenderViewHostManager::SetIsLoading(bool is_loading) { + render_view_host_->SetIsLoading(is_loading); + if (pending_render_view_host_) + pending_render_view_host_->SetIsLoading(is_loading); + if (original_render_view_host_) + original_render_view_host_->SetIsLoading(is_loading); +} + +void RenderViewHostManager::DidNavigateMainFrame( + RenderViewHost* render_view_host) { + if (renderer_state_ == NORMAL) { + // We should only hear this from our current renderer. + DCHECK(render_view_host == render_view_host_); + return; + } else if (renderer_state_ == PENDING) { + if (render_view_host == pending_render_view_host_) { + // The pending cross-site navigation completed, so show the renderer. + SwapToRenderView(&pending_render_view_host_, true); + renderer_state_ = NORMAL; + } else if (render_view_host == render_view_host_) { + // A navigation in the original page has taken place. Cancel the pending + // one. + CancelRenderView(&pending_render_view_host_); + renderer_state_ = NORMAL; + } else { + // No one else should be sending us DidNavigate in this state. + DCHECK(false); + return; + } + + } else if (renderer_state_ == ENTERING_INTERSTITIAL) { + if (render_view_host == interstitial_render_view_host_) { + // The interstitial renderer is ready, so show it, and keep the old + // RenderViewHost around. + original_render_view_host_ = render_view_host_; + SwapToRenderView(&interstitial_render_view_host_, false); + renderer_state_ = INTERSTITIAL; + } else if (render_view_host == render_view_host_) { + // We shouldn't get here, because the original render view was the one + // that caused the ShowInterstitial. + // However, until we intercept navigation events from JavaScript, it is + // possible to get here, if another tab tells render_view_host_ to + // navigate. To be safe, we'll cancel the interstitial and show the + // page that caused the DidNavigate. + CancelRenderView(&interstitial_render_view_host_); + if (pending_render_view_host_) + CancelRenderView(&pending_render_view_host_); + renderer_state_ = NORMAL; + } else if (render_view_host == pending_render_view_host_) { + // We shouldn't get here, because the original render view was the one + // that caused the ShowInterstitial. + // However, until we intercept navigation events from JavaScript, it is + // possible to get here, if another tab tells pending_render_view_host_ + // to navigate. To be safe, we'll cancel the interstitial and show the + // page that caused the DidNavigate. + CancelRenderView(&interstitial_render_view_host_); + SwapToRenderView(&pending_render_view_host_, true); + renderer_state_ = NORMAL; + } else { + // No one else should be sending us DidNavigate in this state. + DCHECK(false); + return; + } + + } else if (renderer_state_ == INTERSTITIAL) { + if (render_view_host == original_render_view_host_) { + // We shouldn't get here, because the original render view was the one + // that caused the ShowInterstitial. + // However, until we intercept navigation events from JavaScript, it is + // possible to get here, if another tab tells render_view_host_ to + // navigate. To be safe, we'll cancel the interstitial and show the + // page that caused the DidNavigate. + SwapToRenderView(&original_render_view_host_, true); + if (pending_render_view_host_) + CancelRenderView(&pending_render_view_host_); + renderer_state_ = NORMAL; + } else if (render_view_host == pending_render_view_host_) { + // No one else should be sending us DidNavigate in this state. + // However, until we intercept navigation events from JavaScript, it is + // possible to get here, if another tab tells pending_render_view_host_ + // to navigate. To be safe, we'll cancel the interstitial and show the + // page that caused the DidNavigate. + SwapToRenderView(&pending_render_view_host_, true); + CancelRenderView(&original_render_view_host_); + renderer_state_ = NORMAL; + } else { + // No one else should be sending us DidNavigate in this state. + DCHECK(false); + return; + } + InterstitialPageGone(); + + } else if (renderer_state_ == LEAVING_INTERSTITIAL) { + if (render_view_host == original_render_view_host_) { + // We navigated to something in the original renderer, so show it. + if (pending_render_view_host_) + CancelRenderView(&pending_render_view_host_); + SwapToRenderView(&original_render_view_host_, true); + renderer_state_ = NORMAL; + } else if (render_view_host == pending_render_view_host_) { + // We navigated to something in the pending renderer. + CancelRenderView(&original_render_view_host_); + SwapToRenderView(&pending_render_view_host_, true); + renderer_state_ = NORMAL; + } else { + // No one else should be sending us DidNavigate in this state. + DCHECK(false); + return; + } + InterstitialPageGone(); + + } else { + // No such state. + DCHECK(false); + return; + } +} + +void RenderViewHostManager::OnCrossSiteResponse(int new_render_process_host_id, + int new_request_id) { + // Should only see this while we have a pending renderer, possibly during an + // interstitial. Otherwise, we should ignore. + if (renderer_state_ != PENDING && renderer_state_ != LEAVING_INTERSTITIAL) + return; + DCHECK(pending_render_view_host_); + + // Tell the old renderer to run its onunload handler. When it finishes, it + // will send a ClosePage_ACK to the ResourceDispatcherHost with the given + // IDs (of the pending RVH's request), allowing the pending RVH's response to + // resume. + if (showing_interstitial_page()) { + DCHECK(original_render_view_host_); + original_render_view_host_->ClosePage(new_render_process_host_id, + new_request_id); + } else { + render_view_host_->ClosePage(new_render_process_host_id, + new_request_id); + } + + // ResourceDispatcherHost has told us to run the onunload handler, which + // means it is not a download or unsafe page, and we are going to perform the + // navigation. Thus, we no longer need to remember that the RenderViewHost + // is part of a pending cross-site request. + pending_render_view_host_->SetHasPendingCrossSiteRequest(false); +} + +void RenderViewHostManager::RendererAbortedProvisionalLoad( + RenderViewHost* render_view_host) { + // We used to cancel the pending renderer here for cross-site downloads. + // However, it's not safe to do that because the download logic repeatedly + // looks for this TabContents based on a render view ID. Instead, we just + // leave the pending renderer around until the next navigation event + // (Navigate, DidNavigate, etc), which will clean it up properly. + // TODO(creis): All of this will go away when we move the cross-site logic + // to ResourceDispatcherHost, so that we intercept responses rather than + // navigation events. (That's necessary to support onunload anyway.) Once + // we've made that change, we won't create a pending renderer until we know + // the response is not a download. + + if (renderer_state_ == ENTERING_INTERSTITIAL) { + if ((pending_render_view_host_ && + (pending_render_view_host_ == render_view_host)) || + (!pending_render_view_host_ && + (render_view_host_ == render_view_host))) { + // The abort came from the RenderViewHost that triggered the + // interstitial. (e.g., User clicked stop after ShowInterstitial but + // before the interstitial was visible.) We should go back to NORMAL. + // Note that this is an uncommon case, because we are only in the + // ENTERING_INTERSTITIAL state in the small time window while the + // interstitial's RenderViewHost is being created. + if (pending_render_view_host_) + CancelRenderView(&pending_render_view_host_); + CancelRenderView(&interstitial_render_view_host_); + renderer_state_ = NORMAL; + } + + // We can get here, at least in the following case. + // We show an interstitial, then navigate to a URL that leads to another + // interstitial. Now there's a race. The new interstitial will be + // created and we will go to ENTERING_INTERSTITIAL, but the old one will + // meanwhile destroy itself and fire DidFailProvisionalLoad. That puts + // us here. Should be safe to ignore the DidFailProvisionalLoad, from + // the perspective of the renderer state. + } else if (renderer_state_ == LEAVING_INTERSTITIAL) { + // If we've left the interstitial by seeing a download (or otherwise + // aborting a load), we should get back to the original page, because + // interstitial page doesn't make sense anymore. (For example, we may + // have clicked Proceed on a download URL.) + + // TODO(creis): This causes problems in the old process model when + // visiting a new URL from an interstitial page. + // This is because we receive a DidFailProvisionalLoad from cancelling + // the first request, which is indistinguishable from a + // DidFailProvisionalLoad from the second request (if it is a download). + // We need to find a way to distinguish these cases, because it doesn't + // make sense to keep showing the interstitial after a download. + // if (pending_render_view_host_) + // CancelRenderView(&pending_render_view_host_); + // SwapToRenderView(&original_render_view_host_, true); + // renderer_state_ = NORMAL; + // delegate_->InterstitialPageGoneFromRenderManager(); + } +} + +void RenderViewHostManager::ShouldClosePage(bool proceed) { + // Should only see this while we have a pending renderer. Otherwise, we + // should ignore. + if (!pending_render_view_host_) { + bool proceed_to_fire_unload; + delegate_->BeforeUnloadFiredFromRenderManager(proceed, + &proceed_to_fire_unload); + + if (proceed_to_fire_unload) { + // This is not a cross-site navigation, the tab is being closed. + render_view_host_->FirePageUnload(); + } + return; + } + + DCHECK(renderer_state_ != ENTERING_INTERSTITIAL); + DCHECK(renderer_state_ != INTERSTITIAL); + if (proceed) { + // Ok to unload the current page, so proceed with the cross-site navigate. + pending_render_view_host_->SetNavigationsSuspended(false); + } else { + // Current page says to cancel. + CancelRenderView(&pending_render_view_host_); + renderer_state_ = NORMAL; + } +} + +void RenderViewHostManager::ShowInterstitialPage( + const std::string& html_text, + InterstitialPageDelegate* delegate) { + // Note that it is important that the interstitial page render view host is + // in the same process as the normal render view host for the tab, so they + // use page ids from the same pool. If they came from different processes, + // page ids may collide causing confusion in the controller (existing + // navigation entries in the controller history could get overridden with the + // interstitial entry). + SiteInstance* interstitial_instance = NULL; + + if (renderer_state_ == NORMAL) { + // render_view_host_ will not be deleted before the end of this method, so + // we don't have to worry about this SiteInstance's ref count dropping to + // zero. + interstitial_instance = render_view_host_->site_instance(); + + } else if (renderer_state_ == PENDING) { + // pending_render_view_host_ will not be deleted before the end of this + // method (when we are in this state), so we don't have to worry about this + // SiteInstance's ref count dropping to zero. + interstitial_instance = pending_render_view_host_->site_instance(); + + } else if (renderer_state_ == ENTERING_INTERSTITIAL) { + // We should never get here if we're in the process of showing an + // interstitial. + // However, until we intercept navigation events from JavaScript, it is + // possible to get here, if another tab tells render_view_host_ to + // navigate to a URL that causes an interstitial. To be safe, we'll cancel + // the first interstitial. + CancelRenderView(&interstitial_render_view_host_); + renderer_state_ = NORMAL; + + // We'd like to now show the new interstitial, but if there's a + // pending_render_view_host_, we can't tell if this JavaScript navigation + // occurred in the original or the pending renderer. That means we won't + // know where to proceed, so we can't show the interstitial. This is + // really just meant to avoid a crash until we can intercept JavaScript + // navigation events, so for now we'll kill the interstitial and go back + // to the last known good page. + if (pending_render_view_host_) { + CancelRenderView(&pending_render_view_host_); + return; + } + // Should be safe to show the interstitial for the new page. + // render_view_host_ will not be deleted before the end of this method, so + // we don't have to worry about this SiteInstance's ref count dropping to + // zero. + interstitial_instance = render_view_host_->site_instance(); + + } else if (renderer_state_ == INTERSTITIAL) { + // We should never get here if we're already showing an interstitial. + // However, until we intercept navigation events from JavaScript, it is + // possible to get here, if another tab tells render_view_host_ to + // navigate to a URL that causes an interstitial. To be safe, we'll go + // back to normal first. + if (pending_render_view_host_ != NULL) { + // There was a pending RVH. We don't know which RVH caused this call + // to ShowInterstitial, so we can't really proceed. We'll have to stay + // in the NORMAL state, showing the last good page. This is only a + // temporary fix anyway, to stave off a crash. + HideInterstitialPage(false, false); + return; + } + // Should be safe to show the interstitial for the new page. + // render_view_host_ will not be deleted before the end of this method, so + // we don't have to worry about this SiteInstance's ref count dropping to + // zero. + SwapToRenderView(&original_render_view_host_, true); + interstitial_instance = render_view_host_->site_instance(); + + } else if (renderer_state_ == LEAVING_INTERSTITIAL) { + SwapToRenderView(&original_render_view_host_, true); + interstitial_instance = NULL; + if (pending_render_view_host_) { + // We're now effectively in PENDING. + // pending_render_view_host_ will not be deleted before the end of this + // method, so we don't have to worry about this SiteInstance's ref count + // dropping to zero. + interstitial_instance = pending_render_view_host_->site_instance(); + } else { + // We're now effectively in NORMAL. + // render_view_host_ will not be deleted before the end of this method, + // so we don't have to worry about this SiteInstance's ref count dropping + // to zero. + interstitial_instance = render_view_host_->site_instance(); + } + + } else { + // No such state. + DCHECK(false); + return; + } + + // Create a pending renderer and move to ENTERING_INTERSTITIAL. + interstitial_render_view_host_ = + CreateRenderViewHost(interstitial_instance, MSG_ROUTING_NONE, NULL); + interstitial_delegate_ = delegate; + bool success = delegate_->CreateRenderViewForRenderManager( + interstitial_render_view_host_); + if (!success) { + // TODO(creis): If this fails, should we load the interstitial in + // render_view_host_? We shouldn't just skip the interstitial... + CancelRenderView(&interstitial_render_view_host_); + return; + } + + // Don't show the view yet. + interstitial_render_view_host_->view()->Hide(); + + renderer_state_ = ENTERING_INTERSTITIAL; + + // We allow the DOM bindings as a way to get the page to talk back to us. + interstitial_render_view_host_->AllowDomAutomationBindings(); + + interstitial_render_view_host_->LoadAlternateHTMLString(html_text, false, + GURL::EmptyGURL(), + std::string()); +} + +void RenderViewHostManager::HideInterstitialPage(bool wait_for_navigation, + bool proceed) { + if (renderer_state_ == NORMAL || renderer_state_ == PENDING) { + // Shouldn't get here, since there's no interstitial showing. + DCHECK(false); + return; + + } else if (renderer_state_ == ENTERING_INTERSTITIAL) { + // Unclear if it is possible to get here. (Can you hide the interstitial + // before it is shown?) If so, we should go back to NORMAL. + CancelRenderView(&interstitial_render_view_host_); + if (pending_render_view_host_) + CancelRenderView(&pending_render_view_host_); + renderer_state_ = NORMAL; + return; + } + + DCHECK(showing_interstitial_page()); + DCHECK(render_view_host_ && original_render_view_host_ && + !interstitial_render_view_host_); + + if (renderer_state_ == INTERSTITIAL) { + // Disable the Proceed button on the interstitial, because the destination + // renderer might get replaced. + DisableInterstitialProceed(false); + + } else if (renderer_state_ == LEAVING_INTERSTITIAL) { + // We have already given up the ability to proceed by starting a new + // navigation. If this is a request to proceed, we must ignore it. + // (Hopefully we will have disabled the Proceed button by now, but it's + // possible to get here before that happens.) + if (proceed) + return; + } + + if (wait_for_navigation) { + // We are resuming the loading. We need to set the state to loading again + // as it was set to false when the interstitial stopped loading (so the + // throbber runs). + delegate_->DidStartLoadingFromRenderManager(render_view_host_, NULL); + } + + if (proceed) { + // Now we will resume loading automatically, either in + // original_render_view_host_ or in pending_render_view_host_. When it + // completes, we will display the renderer in DidNavigate. + renderer_state_ = LEAVING_INTERSTITIAL; + + } else { + // Don't proceed. Go back to the previously showing page. + if (renderer_state_ == LEAVING_INTERSTITIAL) { + // We said DontProceed after starting to leave the interstitial. + // Abandon whatever we were in the process of doing. + original_render_view_host_->Stop(); + } + SwapToRenderView(&original_render_view_host_, true); + if (pending_render_view_host_) + CancelRenderView(&pending_render_view_host_); + renderer_state_ = NORMAL; + InterstitialPageGone(); + } +} + +bool RenderViewHostManager::IsRenderViewInterstitial( + const RenderViewHost* render_view_host) const { + if (showing_interstitial_page()) + return render_view_host_ == render_view_host; + if (renderer_state_ == ENTERING_INTERSTITIAL) + return interstitial_render_view_host_ == render_view_host; + return false; +} + +void RenderViewHostManager::OnJavaScriptMessageBoxClosed( + IPC::Message* reply_msg, + bool success, + const std::wstring& prompt) { + RenderViewHost* rvh = render_view_host_; + if (showing_interstitial_page()) { + // No JavaScript message boxes are ever shown by interstitial pages, but + // they can be shown by the original RVH while an interstitial page is + // showing (e.g., from an onunload event handler). We should send this to + // the original RVH and not the interstitial's RVH. + // TODO(creis): Perhaps the JavascriptMessageBoxHandler should store which + // RVH created it, so that it can tell this method which RVH to reply to. + DCHECK(original_render_view_host_); + rvh = original_render_view_host_; + } + rvh->JavaScriptMessageBoxClosed(reply_msg, success, prompt); +} + + +bool RenderViewHostManager::ShouldTransitionCrossSite() { + // True if we are using process-per-site-instance (default) or + // process-per-site (kProcessPerSite). + return !CommandLine().HasSwitch(switches::kProcessPerTab); +} + +SiteInstance* RenderViewHostManager::GetSiteInstanceForEntry( + const NavigationEntry& entry, + SiteInstance* curr_instance) { + // NOTE: This is only called when ShouldTransitionCrossSite is true. + + // If the entry has an instance already, we should use it. + if (entry.site_instance()) + return entry.site_instance(); + + // (UGLY) HEURISTIC, process-per-site only: + // + // If this navigation is generated, then it probably corresponds to a search + // query. Given that search results typically lead to users navigating to + // other sites, we don't really want to use the search engine hostname to + // determine the site instance for this navigation. + // + // NOTE: This can be removed once we have a way to transition between + // RenderViews in response to a link click. + // + if (CommandLine().HasSwitch(switches::kProcessPerSite) && + entry.GetTransitionType() == PageTransition::GENERATED) + return curr_instance; + + const GURL& dest_url = entry.GetURL(); + + // If we haven't used our SiteInstance (and thus RVH) yet, then we can use it + // for this entry. We won't commit the SiteInstance to this site until the + // navigation commits (in DidNavigate), unless the navigation entry was + // restored. As session restore loads all the pages immediately we need to set + // the site first, otherwise after a restore none of the pages would share + // renderers. + if (!curr_instance->has_site()) { + // If we've already created a SiteInstance for our destination, we don't + // want to use this unused SiteInstance; use the existing one. (We don't + // do this check if the curr_instance has a site, because for now, we want + // to compare against the current URL and not the SiteInstance's site. In + // this case, there is no current URL, so comparing against the site is ok. + // See additional comments below.) + if (curr_instance->HasRelatedSiteInstance(dest_url)) { + return curr_instance->GetRelatedSiteInstance(dest_url); + } else { + if (entry.restored()) + curr_instance->SetSite(dest_url); + return curr_instance; + } + } + + // Otherwise, only create a new SiteInstance for cross-site navigation. + + // TODO(creis): Once we intercept links and script-based navigations, we + // will be able to enforce that all entries in a SiteInstance actually have + // the same site, and it will be safe to compare the URL against the + // SiteInstance's site, as follows: + // const GURL& current_url = curr_instance->site(); + // For now, though, we're in a hybrid model where you only switch + // SiteInstances if you type in a cross-site URL. This means we have to + // compare the entry's URL to the last committed entry's URL. + NavigationController* controller = delegate_->GetControllerForRenderManager(); + NavigationEntry* curr_entry = controller->GetLastCommittedEntry(); + if (showing_interstitial_page()) { + // The interstitial is currently the last committed entry, but we want to + // compare against the last non-interstitial entry. + curr_entry = controller->GetEntryAtOffset(-1); + } + // If there is no last non-interstitial entry (and curr_instance already + // has a site), then we must have been opened from another tab. We want + // to compare against the URL of the page that opened us, but we can't + // get to it directly. The best we can do is check against the site of + // the SiteInstance. This will be correct when we intercept links and + // script-based navigations, but for now, it could place some pages in a + // new process unnecessarily. We should only hit this case if a page tries + // to open a new tab to an interstitial-inducing URL, and then navigates + // the page to a different same-site URL. (This seems very unlikely in + // practice.) + const GURL& current_url = (curr_entry) ? curr_entry->GetURL() : + curr_instance->site(); + + if (SiteInstance::IsSameWebSite(current_url, dest_url)) { + return curr_instance; + } else { + // Start the new renderer in a new SiteInstance, but in the current + // BrowsingInstance. It is important to immediately give this new + // SiteInstance to a RenderViewHost (if it is different than our current + // SiteInstance), so that it is ref counted. This will happen in + // CreatePendingRenderView. + return curr_instance->GetRelatedSiteInstance(dest_url); + } +} + +bool RenderViewHostManager::CreatePendingRenderView(SiteInstance* instance) { + NavigationEntry* curr_entry = + delegate_->GetControllerForRenderManager()->GetLastCommittedEntry(); + if (curr_entry && curr_entry->GetType() == TAB_CONTENTS_WEB) { + DCHECK(!curr_entry->GetContentState().empty()); + + // TODO(creis): Should send a message to the RenderView to let it know + // we're about to switch away, so that it sends an UpdateState message. + } + + pending_render_view_host_ = + CreateRenderViewHost(instance, MSG_ROUTING_NONE, NULL); + + bool success = delegate_->CreateRenderViewForRenderManager( + pending_render_view_host_); + if (success) { + // Don't show the view until we get a DidNavigate from it. + pending_render_view_host_->view()->Hide(); + } else { + CancelRenderView(&pending_render_view_host_); + } + return success; +} + +RenderViewHost* RenderViewHostManager::CreateRenderViewHost( + SiteInstance* instance, + int routing_id, + HANDLE modal_dialog_event) { + if (render_view_factory_) { + return render_view_factory_->CreateRenderViewHost( + instance, render_view_delegate_, routing_id, modal_dialog_event); + } else { + return new RenderViewHost(instance, render_view_delegate_, routing_id, + modal_dialog_event); + } +} + +void RenderViewHostManager::SwapToRenderView( + RenderViewHost** new_render_view_host, + bool destroy_after) { + // Remember if the page was focused so we can focus the new renderer in + // that case. + bool focus_render_view = render_view_host_->view() && + render_view_host_->view()->HasFocus(); + + // Hide the current view and prepare to destroy it. + // TODO(creis): Get the old RenderViewHost to send us an UpdateState message + // before we destroy it. + if (render_view_host_->view()) + render_view_host_->view()->Hide(); + RenderViewHost* old_render_view_host = render_view_host_; + + // Swap in the pending view and make it active. + render_view_host_ = (*new_render_view_host); + (*new_render_view_host) = NULL; + + // If the view is gone, then this RenderViewHost died while it was hidden. + // We ignored the RendererGone call at the time, so we should send it now + // to make sure the sad tab shows up, etc. + if (render_view_host_->view()) + render_view_host_->view()->Show(); + else + delegate_->RendererGoneFromRenderManager(render_view_host_); + + // Make sure the size is up to date. (Fix for bug 1079768.) + delegate_->UpdateRenderViewSizeForRenderManager(); + + if (focus_render_view && render_view_host_->view()) + render_view_host_->view()->Focus(); + + RenderViewHostSwitchedDetails details; + details.new_host = render_view_host_; + details.old_host = old_render_view_host; + NotificationService::current()->Notify( + NOTIFY_RENDER_VIEW_HOST_CHANGED, + Source(delegate_->GetControllerForRenderManager()), + Details(&details)); + + if (destroy_after) + old_render_view_host->Shutdown(); + + // Let the task manager know that we've swapped RenderViewHosts, since it + // might need to update its process groupings. + delegate_->NotifySwappedFromRenderManager(); +} + +RenderViewHost* RenderViewHostManager::UpdateRendererStateNavigate( + const NavigationEntry& entry) { + // If we are in PENDING or ENTERING_INTERSTITIAL, then we want to get back + // to NORMAL and navigate as usual. + if (renderer_state_ == PENDING || renderer_state_ == ENTERING_INTERSTITIAL) { + if (pending_render_view_host_) + CancelRenderView(&pending_render_view_host_); + if (interstitial_render_view_host_) + CancelRenderView(&interstitial_render_view_host_); + renderer_state_ = NORMAL; + } + + // render_view_host_ will not be deleted before the end of this method, so we + // don't have to worry about this SiteInstance's ref count dropping to zero. + SiteInstance* curr_instance = render_view_host_->site_instance(); + + if (showing_interstitial_page()) { + // Must disable any ability to proceed from the interstitial, because we're + // about to navigate somewhere else. + DisableInterstitialProceed(true); + + if (pending_render_view_host_) + CancelRenderView(&pending_render_view_host_); + + renderer_state_ = LEAVING_INTERSTITIAL; + + // We want to compare against where we were, because we just cancelled + // where we were going. The original_render_view_host_ won't be deleted + // before the end of this method, so we don't have to worry about this + // SiteInstance's ref count dropping to zero. + curr_instance = original_render_view_host_->site_instance(); + } + + // Determine if we need a new SiteInstance for this entry. + // Again, new_instance won't be deleted before the end of this method, so it + // is safe to use a normal pointer here. + SiteInstance* new_instance = curr_instance; + if (ShouldTransitionCrossSite()) + new_instance = GetSiteInstanceForEntry(entry, curr_instance); + + if (new_instance != curr_instance) { + // New SiteInstance. + DCHECK(renderer_state_ == NORMAL || + renderer_state_ == LEAVING_INTERSTITIAL); + + // Create a pending RVH and navigate it. + bool success = CreatePendingRenderView(new_instance); + if (!success) + return NULL; + + // Check if our current RVH is live before we set up a transition. + if (!render_view_host_->IsRenderViewLive()) { + if (renderer_state_ == NORMAL) { + // The current RVH is not live. There's no reason to sit around with a + // sad tab or a newly created RVH while we wait for the pending RVH to + // navigate. Just switch to the pending RVH now and go back to NORMAL, + // without requiring a cross-site transition. (Note that we don't care + // about on{before}unload handlers if the current RVH isn't live.) + SwapToRenderView(&pending_render_view_host_, true); + return render_view_host_; + + } else if (renderer_state_ == LEAVING_INTERSTITIAL) { + // Cancel the interstitial, since it has died and we're navigating away + // anyway. + DCHECK(original_render_view_host_); + if (original_render_view_host_->IsRenderViewLive()) { + // Swap back to the original and act like a pending request (using + // the logic below). + SwapToRenderView(&original_render_view_host_, true); + renderer_state_ = NORMAL; + InterstitialPageGone(); + // Continue with the pending cross-site transition logic below. + } else { + // Both the interstitial and original are dead. Just like the NORMAL + // case, let's skip the cross-site transition entirely. We also have + // to clean up the interstitial state. + SwapToRenderView(&pending_render_view_host_, true); + CancelRenderView(&original_render_view_host_); + renderer_state_ = NORMAL; + InterstitialPageGone(); + return render_view_host_; + } + } else { + NOTREACHED(); + return render_view_host_; + } + } + // Otherwise, it's safe to treat this as a pending cross-site transition. + + // Make sure the old render view stops, in case a load is in progress. + render_view_host_->Stop(); + + // Suspend the new render view (i.e., don't let it send the cross-site + // Navigate message) until we hear back from the old renderer's + // onbeforeunload handler. If it returns false, we'll have to cancel the + // request. + pending_render_view_host_->SetNavigationsSuspended(true); + + // Tell the CrossSiteRequestManager that this RVH has a pending cross-site + // request, so that ResourceDispatcherHost will know to tell us to run the + // old page's onunload handler before it sends the response. + pending_render_view_host_->SetHasPendingCrossSiteRequest(true); + + // We now have a pending RVH. If we were in NORMAL, we should now be in + // PENDING. If we were in LEAVING_INTERSTITIAL, we should stay there. + if (renderer_state_ == NORMAL) + renderer_state_ = PENDING; + else + DCHECK(renderer_state_ == LEAVING_INTERSTITIAL); + + // Tell the old render view to run its onbeforeunload handler, since it + // doesn't otherwise know that the cross-site request is happening. This + // will trigger a call to ShouldClosePage with the reply. + render_view_host_->FirePageBeforeUnload(); + + return pending_render_view_host_; + } + + // Same SiteInstance can be used. Navigate render_view_host_ if we are in + // the NORMAL state, and original_render_view_host_ if an interstitial is + // showing. + if (renderer_state_ == NORMAL) + return render_view_host_; + + DCHECK(renderer_state_ == LEAVING_INTERSTITIAL); + return original_render_view_host_; +} + +void RenderViewHostManager::DisableInterstitialProceed(bool stop_request) { + // TODO(creis): Make sure the interstitial page disables any ability to + // proceed at this point, because we're about to abort the original request. + // This can be done by adding a new event to the NotificationService. + // We should also disable the button on the page itself, but it's ok if that + // doesn't happen immediately. + + // Stopping the request is necessary if we are navigating away, because the + // user could be requesting the same URL again, causing the HttpCache to + // ignore it. (Fixes bug 1079784.) + if (stop_request) { + original_render_view_host_->Stop(); + if (pending_render_view_host_) + pending_render_view_host_->Stop(); + } +} + +void RenderViewHostManager::InterstitialPageGone() { + DCHECK(!showing_interstitial_page()); + + NotificationService::current()->Notify( + NOTIFY_INTERSTITIAL_PAGE_CLOSED, + Source(delegate_->GetControllerForRenderManager()), + NotificationService::NoDetails()); + if (interstitial_delegate_) { + interstitial_delegate_->InterstitialClosed(); + interstitial_delegate_ = NULL; + } +} diff --git a/chrome/browser/render_view_host_manager.h b/chrome/browser/render_view_host_manager.h new file mode 100644 index 0000000..74c7798 --- /dev/null +++ b/chrome/browser/render_view_host_manager.h @@ -0,0 +1,339 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_BROWSER_RENDER_VIEW_HOST_MANAGER_H_ +#define CHROME_BROWSER_RENDER_VIEW_HOST_MANAGER_H_ + +#include + +#include + +#include "base/basictypes.h" +#include "chrome/browser/render_view_host.h" + +class InterstitialPageDelegate; +class NavigationController; +class NavigationEntry; +class Profile; +class RenderViewHostDelegate; +class RenderViewHostFactory; +class RenderWidgetHostView; +class SiteInstance; + +// Manages RenderViewHosts for a WebContents. Normally there is only one and +// it is easy to do. But we can also have interstitial pages and transitions +// of processes (and hence RenderViewHosts) that can get very complex. +class RenderViewHostManager { + public: + // Functions implemented by our owner that we need. + // + // TODO(brettw) Clean this up! These are all the functions in WebContents that + // are required to run this class. The design should probably be better such + // that these are more clear. + // + // There is additional complexity that some of the functions we need in + // WebContents are inherited and non-virtual. These are named with + // "RenderManager" so that the duplicate implementation of them will be clear. + class Delegate { + public: + // See web_contents.h's implementation for more. + virtual bool CreateRenderViewForRenderManager( + RenderViewHost* render_view_host) = 0; + virtual void BeforeUnloadFiredFromRenderManager( + bool proceed, bool* proceed_to_fire_unload) = 0; + virtual void DidStartLoadingFromRenderManager( + RenderViewHost* render_view_host, int32 page_id) = 0; + virtual void RendererGoneFromRenderManager( + RenderViewHost* render_view_host) = 0; + virtual void UpdateRenderViewSizeForRenderManager() = 0; + virtual void NotifySwappedFromRenderManager() = 0; + virtual NavigationController* GetControllerForRenderManager() = 0; + }; + + // The factory is optional. It is used by unit tests to supply custom render + // view hosts. When NULL, the regular RenderViewHost will be created. + // + // Both delegate pointers must be non-NULL and are not owned by this class. + // They must outlive this class. The RenderViewHostDelegate is what will be + // installed into all RenderViewHosts that are created. + // + // You must call Init() before using this class and Shutdown() before + // deleting it. + RenderViewHostManager(RenderViewHostFactory* render_view_factory, + RenderViewHostDelegate* render_view_delegate, + Delegate* delegate); + ~RenderViewHostManager(); + + // For arguments, see WebContents constructor. + void Init(Profile* profile, + SiteInstance* site_instance, + int routing_id, + HANDLE modal_dialog_event); + + // Schedules all RenderViewHosts for destruction. + void Shutdown(); + + // Returns the currently actuive RenderViewHost. + // + // This will be non-NULL between Init() and Shutdown(). You may want to NULL + // check it in many cases, however. Windows can send us messages during the + // destruction process after it has been shut down. + RenderViewHost* current_host() const { + return render_view_host_; + } + + // Returns the view associated with the current RenderViewHost, or NULL if + // there is no current one. + RenderWidgetHostView* current_view() const { + if (!render_view_host_) + return NULL; + return render_view_host_->view(); + } + + // Called when we want to instruct the renderer to navigate to the given + // navigation entry. It may create a new RenderViewHost or re-use an existing + // one. The RenderViewHost to navigate will be returned. Returns NULL if one + // could not be created. + RenderViewHost* Navigate(const NavigationEntry& entry); + + // Instructs the various live views to stop. Called when the user directed the + // page to stop loading. + void Stop(); + + // Notifies all RenderViewHosts (regular, interstitials, etc.) that a load is + // or is not happening. Even though the message is only for one of them, we + // don't know which one so we tell them all. + void SetIsLoading(bool is_loading); + + // Called when a renderer's main frame navigates. This handles all the logic + // associated with interstitial management. + void DidNavigateMainFrame(RenderViewHost* render_view_host); + + // Allows the WebContents to react when a cross-site response is ready to be + // delivered to a pending RenderViewHost. We must first run the onunload + // handler of the old RenderViewHost before we can allow it to proceed. + void OnCrossSiteResponse(int new_render_process_host_id, + int new_request_id); + + // Called when a provisional load on the given renderer is aborted. + void RendererAbortedProvisionalLoad(RenderViewHost* render_view_host); + + // Actually implements this RenderViewHostDelegate function for the + // WebContents. + void ShouldClosePage(bool proceed); + + // Displays the specified html in the current page. This method can be used to + // show temporary pages (such as security error pages). It can be hidden by + // calling HideInterstitialPage, in which case the original page is restored. + // An optional delegate may be passed, it is owned by the caller and must + // remain valid while the interstitial does. + void ShowInterstitialPage(const std::string& html_text, + InterstitialPageDelegate* delegate); + + // Reverts from the interstitial page to the original page. + // If |wait_for_navigation| is true, the interstitial page is removed when + // the original page has transitioned to the new contents. This is useful + // when you want to hide the interstitial page as you navigate to a new page. + // Hiding the interstitial page right away would show the previous displayed + // page. If |proceed| is true, the WebContents will expect the navigation + // to complete. If not, it will revert to the last shown page. + void HideInterstitialPage(bool wait_for_navigation, + bool proceed); + + // Returns true if the given render view host is an interstitial. + bool IsRenderViewInterstitial(const RenderViewHost* render_view_host) const; + + // Forwards the message to the RenderViewHost, which is the original one, + // not any interstitial that may be showing. + void OnJavaScriptMessageBoxClosed(IPC::Message* reply_msg, + bool success, + const std::wstring& prompt); + + // Are we showing the POST interstitial page? + // + // NOTE: the POST interstitial does NOT result in a separate RenderViewHost. + bool showing_repost_interstitial() const { + return showing_repost_interstitial_; + } + void set_showing_repost_interstitial(bool showing) { + showing_repost_interstitial_ = showing; + } + + // Returns whether we are currently showing an interstitial page. + bool showing_interstitial_page() const { + return (renderer_state_ == INTERSTITIAL) || + (renderer_state_ == LEAVING_INTERSTITIAL); + } + + // Accessors to the the interstitial delegate, that is optionaly set when + // an interstitial page is shown. + InterstitialPageDelegate* interstitial_delegate() const { + return interstitial_delegate_; + } + void set_interstitial_delegate(InterstitialPageDelegate* delegate) { + interstitial_delegate_ = delegate; + } + + private: + friend class TestWebContents; + + // RenderViewHost states. These states represent whether a cross-site + // request is pending (in the new process model) and whether an interstitial + // page is being shown. These are public to give easy access to unit tests. + enum RendererState { + // NORMAL: just showing a page normally. + // render_view_host_ is showing a page. + // pending_render_view_host_ is NULL. + // original_render_view_host_ is NULL. + // interstitial_render_view_host_ is NULL. + NORMAL = 0, + + // PENDING: creating a new RenderViewHost for a cross-site navigation. + // Never used when --process-per-tab is specified. + // render_view_host_ is showing a page. + // pending_render_view_host_ is loading a page in the background. + // original_render_view_host_ is NULL. + // interstitial_render_view_host_ is NULL. + PENDING, + + // ENTERING_INTERSTITIAL: an interstitial RenderViewHost has been created. + // and will be shown as soon as it calls DidNavigate. + // render_view_host_ is showing a page. + // pending_render_view_host_ is either NULL or suspended in the background. + // original_render_view_host_ is NULL. + // interstitial_render_view_host_ is loading in the background. + ENTERING_INTERSTITIAL, + + // INTERSTITIAL: Showing an interstitial page. + // render_view_host_ is showing the interstitial. + // pending_render_view_host_ is either NULL or suspended in the background. + // original_render_view_host_ is the hidden original page. + // interstitial_render_view_host_ is NULL. + INTERSTITIAL, + + // LEAVING_INTERSTITIAL: interstitial is still showing, but we are + // navigating to a new page that will replace it. + // render_view_host_ is showing the interstitial. + // pending_render_view_host_ is either NULL or loading a page. + // original_render_view_host_ is hidden and possibly loading a page. + // interstitial_render_view_host_ is NULL. + LEAVING_INTERSTITIAL + }; + + // Returns whether this tab should transition to a new renderer for + // cross-site URLs. Enabled unless we see the --process-per-tab command line + // switch. Can be overridden in unit tests. + bool ShouldTransitionCrossSite(); + + // Returns an appropriate SiteInstance object for the given NavigationEntry, + // possibly reusing the current SiteInstance. + // Never called if --process-per-tab is used. + SiteInstance* GetSiteInstanceForEntry(const NavigationEntry& entry, + SiteInstance* curr_instance); + + // Helper method to create a pending RenderViewHost for a cross-site + // navigation. + bool CreatePendingRenderView(SiteInstance* instance); + + // Creates a RenderViewHost using render_view_factory_ (or directly, if the + // factory is NULL). + RenderViewHost* CreateRenderViewHost(SiteInstance* instance, + int routing_id, + HANDLE modal_dialog_event); + + // Replaces the currently shown render_view_host_ with the RenderViewHost in + // the field pointed to by |new_render_view_host|, and then NULLs the field. + // Callers should only pass pointers to the pending_render_view_host_, + // interstitial_render_view_host_, or original_render_view_host_ fields of + // this object. If |destroy_after|, this method will call + // ScheduleDeferredDestroy on the previous render_view_host_. + void SwapToRenderView(RenderViewHost** new_render_view_host, + bool destroy_after); + + RenderViewHost* UpdateRendererStateNavigate(const NavigationEntry& entry); + + // Prevent the interstitial page from proceeding after we start navigating + // away from it. If |stop_request| is true, abort the pending requests + // immediately, because we are navigating away. + void DisableInterstitialProceed(bool stop_request); + + // Cleans up after an interstitial page is hidden, including removing the + // interstitial's NavigationEntry. + void InterstitialPageGone(); + + + // Our delegate, not owned by us. Guaranteed non-NULL. + Delegate* delegate_; + + // See RendererState definition above. + RendererState renderer_state_; + + // Allows tests to create their own render view host types. + RenderViewHostFactory* render_view_factory_; + + // Implemented by the owner of this class, this delegate is installed into all + // the RenderViewHosts that we create. + RenderViewHostDelegate* render_view_delegate_; + + // Our RenderView host. This object is responsible for all communication with + // a child RenderView instance. Note that this can be the page render view + // host or the interstitial RenderViewHost if the RendererState is + // INTERSTITIAL or LEAVING_INTERSTITIAL. + RenderViewHost* render_view_host_; + + // This var holds the original RenderViewHost when the interstitial page is + // showing (the RendererState is INTERSTITIAL or LEAVING_INTERSTITIAL). It + // is NULL otherwise. + RenderViewHost* original_render_view_host_; + + // The RenderViewHost of the interstitial page. This is non NULL when the + // the RendererState is ENTERING_INTERSTITIAL. + RenderViewHost* interstitial_render_view_host_; + + // A RenderViewHost used to load a cross-site page. This remains hidden + // during the PENDING RendererState until it calls DidNavigate. It can also + // exist if an interstitial page is shown. + RenderViewHost* pending_render_view_host_; + + InterstitialPageDelegate* interstitial_delegate_; + + // See comment above showing_repost_interstitial(). + bool showing_repost_interstitial_; + + DISALLOW_COPY_AND_ASSIGN(RenderViewHostManager); +}; + +// The "details" for a NOTIFY_RENDER_VIEW_HOST_CHANGED notification. The old +// host can be NULL when the first RenderViewHost is set. +struct RenderViewHostSwitchedDetails { + RenderViewHost* old_host; + RenderViewHost* new_host; +}; + +#endif // CHROME_BROWSER_RENDER_VIEW_HOST_MANAGER_H_ diff --git a/chrome/browser/ssl_blocking_page.cc b/chrome/browser/ssl_blocking_page.cc index cb5d3ac..220328a 100644 --- a/chrome/browser/ssl_blocking_page.cc +++ b/chrome/browser/ssl_blocking_page.cc @@ -87,7 +87,7 @@ SSLBlockingPage::SSLBlockingPage(SSLManager::CertError* error, NotificationService::current()->AddObserver(this, NOTIFY_INTERSTITIAL_PAGE_CLOSED, - Source(tab_)); + Source(tab_->controller())); // Register for DOM operations, this is how the blocking page notifies us of // what the user chooses. @@ -103,7 +103,7 @@ SSLBlockingPage::~SSLBlockingPage() { NotificationService::current()->RemoveObserver(this, NOTIFY_INTERSTITIAL_PAGE_CLOSED, - Source(tab_)); + Source(tab_->controller())); NotificationService::current()->RemoveObserver(this, NOTIFY_DOM_OPERATION_RESPONSE, diff --git a/chrome/browser/tab_contents.h b/chrome/browser/tab_contents.h index 76061c1..e83cd72 100644 --- a/chrome/browser/tab_contents.h +++ b/chrome/browser/tab_contents.h @@ -191,6 +191,9 @@ class TabContents : public PageNavigator, // Sets up a new NavigationController for this TabContents. // |profile| is the user profile that should be associated with // the new controller. + // + // TODO(brettw) this seems bogus and I couldn't find any legitimate need for + // it. I think it should be passed in the constructor. void SetupController(Profile* profile); // Returns the user profile associated with this TabContents (via the diff --git a/chrome/browser/tab_contents_container_view.cc b/chrome/browser/tab_contents_container_view.cc index 6a2a3d4..e0be427 100644 --- a/chrome/browser/tab_contents_container_view.cc +++ b/chrome/browser/tab_contents_container_view.cc @@ -33,6 +33,7 @@ #include "base/logging.h" #include "chrome/browser/render_view_host.h" +#include "chrome/browser/render_view_host_manager.h" #include "chrome/browser/render_widget_host_view.h" #include "chrome/browser/tab_contents.h" #include "chrome/browser/view_ids.h" @@ -204,9 +205,10 @@ void TabContentsContainerView::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { if (type == NOTIFY_RENDER_VIEW_HOST_CHANGED) { - RenderViewHost* new_rvh = Source(source)->render_view_host(); - RenderViewHost* old_rvh = Details(details).ptr(); - RenderViewHostChanged(old_rvh, new_rvh); + RenderViewHostSwitchedDetails* switched_details = + Details(details).ptr(); + RenderViewHostChanged(switched_details->old_host, + switched_details->new_host); } else if (type == NOTIFY_TAB_CONTENTS_DESTROYED) { TabContentsDestroyed(Source(source).ptr()); } else { @@ -222,7 +224,7 @@ void TabContentsContainerView::AddObservers() { // the focus subclass on the shown HWND so we intercept focus change events. NotificationService::current()->AddObserver( this, NOTIFY_RENDER_VIEW_HOST_CHANGED, - Source(tab_contents_->AsWebContents())); + Source(tab_contents_->controller())); } NotificationService::current()->AddObserver( this, NOTIFY_TAB_CONTENTS_DESTROYED, @@ -234,7 +236,7 @@ void TabContentsContainerView::RemoveObservers() { if (tab_contents_->AsWebContents()) { NotificationService::current()->RemoveObserver( this, NOTIFY_RENDER_VIEW_HOST_CHANGED, - Source(tab_contents_->AsWebContents())); + Source(tab_contents_->controller())); } NotificationService::current()->RemoveObserver( this, NOTIFY_TAB_CONTENTS_DESTROYED, diff --git a/chrome/browser/tabs/tab_strip_model.cc b/chrome/browser/tabs/tab_strip_model.cc index c71c089d..2828d51 100644 --- a/chrome/browser/tabs/tab_strip_model.cc +++ b/chrome/browser/tabs/tab_strip_model.cc @@ -271,14 +271,15 @@ bool TabStripModel::TabHasUnloadListener(int index) { // in testing and then provide better test coverage for features // like "close other tabs". WebContents* web_contents = GetContentsAt(index)->AsWebContents(); - if (web_contents) + if (web_contents) { // If the WebContents is not connected yet, then there's no unload // handler we can fire even if the WebContents has an unload listener. // One case where we hit this is in a tab that has an infinite loop // before load. return web_contents->notify_disconnection() && - !web_contents->IsShowingInterstitialPage() && + !web_contents->showing_interstitial_page() && web_contents->render_view_host()->HasUnloadListener(); + } return false; } diff --git a/chrome/browser/web_contents.cc b/chrome/browser/web_contents.cc index 9ed7927..9108c7d 100644 --- a/chrome/browser/web_contents.cc +++ b/chrome/browser/web_contents.cc @@ -219,6 +219,9 @@ WebContents::WebContents(Profile* profile, int routing_id, HANDLE modal_dialog_event) : TabContents(TAB_CONTENTS_WEB), +#pragma warning(suppress: 4355) // Okay to pass "this" here. + render_manager_(render_view_factory, this, this), + render_view_factory_(render_view_factory), is_profiling_(false), has_page_title_(false), info_bar_visible_(false), @@ -226,31 +229,18 @@ WebContents::WebContents(Profile* profile, printing_(*this), notify_disconnection_(false), message_box_active_(CreateEvent(NULL, TRUE, FALSE, NULL)), - render_view_factory_(render_view_factory), - original_render_view_host_(NULL), - interstitial_render_view_host_(NULL), - pending_render_view_host_(NULL), - renderer_state_(NORMAL), capturing_contents_(false), #pragma warning(suppress: 4355) // Okay to pass "this" here. fav_icon_helper_(this), crashed_plugin_info_bar_(NULL), suppress_javascript_messages_(false), - load_state_(net::LOAD_STATE_IDLE), - showing_repost_interstitial_(false), - interstitial_delegate_(NULL) { + load_state_(net::LOAD_STATE_IDLE) { InitWebContentsClass(); pending_install_.page_id = 0; pending_install_.callback_functor = NULL; - // Create a RenderViewHost, once we have an instance. It is important to - // immediately give this SiteInstance to a RenderViewHost so that it is - // ref counted. - if (!site_instance) - site_instance = SiteInstance::CreateSiteInstance(profile); - render_view_host_ = CreateRenderViewHost( - site_instance, this, routing_id, modal_dialog_event); + render_manager_.Init(profile, site_instance, routing_id, modal_dialog_event); // Register for notifications about all interested prefs change. PrefService* prefs = profile->GetPrefs(); @@ -290,8 +280,8 @@ void WebContents::GetContainerBounds(gfx::Rect *out) const { } void WebContents::ShowContents() { - if (render_view_host_ && render_view_host_->view()) - render_view_host_->view()->DidBecomeSelected(); + if (view()) + view()->DidBecomeSelected(); // Loop through children and send DidBecomeSelected to them, too. int count = static_cast(child_windows_.size()); @@ -317,8 +307,8 @@ void WebContents::HideContents() { } void WebContents::SizeContents(const gfx::Size& size) { - if (render_view_host_ && render_view_host_->view()) - render_view_host_->view()->SetSize(size); + if (view()) + view()->SetSize(size); if (find_in_page_controller_.get()) find_in_page_controller_->RespondToResize(size); RepositionSupressedPopupsToFit(size); @@ -336,21 +326,15 @@ void WebContents::Destroy() { // Destroy the print manager right now since a Print command may be pending. printing_.Destroy(); - // Unregister the notifications of all observed prefs change. PrefService* prefs = profile()->GetPrefs(); - if (prefs) + if (prefs) { for (int i = 0; i < kPrefsToObserveLength; ++i) prefs->RemovePrefObserver(kPrefsToObserve[i], this); + } cancelable_consumer_.CancelAllRequests(); - if (IsShowingInterstitialPage()) { - // The tab is closed while the interstitial page is showing, hide and - // destroy it. - HideInterstitialPage(false, false); - } - // Close the Find in page dialog. if (find_in_page_controller_.get()) find_in_page_controller_->Close(); @@ -359,19 +343,9 @@ void WebContents::Destroy() { // They will be cleaned up properly in plugin process. DetachPluginWindows(); - if (pending_render_view_host_) - pending_render_view_host_->Shutdown(); - if (original_render_view_host_) - original_render_view_host_->Shutdown(); - if (interstitial_render_view_host_) - interstitial_render_view_host_->Shutdown(); - NotifyDisconnected(); HungRendererWarning::HideForWebContents(this); - - render_view_host_->Shutdown(); - render_view_host_ = NULL; - + render_manager_.Shutdown(); TabContents::Destroy(); } @@ -409,7 +383,7 @@ void WebContents::OnWindowPosChanged(WINDOWPOS* window_pos) { } void WebContents::OnPaint(HDC junk_dc) { - if (render_view_host_ && !render_view_host_->IsRenderViewLive()) { + if (render_view_host() && !render_view_host()->IsRenderViewLive()) { if (!sad_tab_.get()) sad_tab_.reset(new SadTabView); CRect cr; @@ -450,8 +424,7 @@ LRESULT WebContents::OnMouseRange(UINT msg, WPARAM w_param, LPARAM l_param) { delegate()->ContentsMouseEvent(this, WM_MOUSEMOVE); break; case WM_MOUSEWHEEL: - // This message is reflected from the render_view_host_->view() - // to this window. + // This message is reflected from the view() to this window. if (GET_KEYSTATE_WPARAM(w_param) & MK_CONTROL) { WheelZoom(GET_WHEEL_DELTA_WPARAM(w_param)); return 1; @@ -532,8 +505,8 @@ void WebContents::OnSetFocus(HWND window) { // background from properly taking focus. // We NULL-check the render_view_host_ here because Windows can send us // messages during the destruction process after it has been destroyed. - if (render_view_host_ && render_view_host_->view()) { - HWND inner_hwnd = render_view_host_->view()->GetPluginHWND(); + if (view()) { + HWND inner_hwnd = view()->GetPluginHWND(); if (::IsWindow(inner_hwnd)) ::SetFocus(inner_hwnd); } @@ -554,7 +527,7 @@ void WebContents::SaveCurrentProfilingEntry() { if (is_profiling()) { NavigationProfiler* profiler = GetNavigationProfiler(); profiler->MoveActivePageToVisited(process()->host_id(), - render_view_host_->routing_id()); + render_view_host()->routing_id()); } is_profiling_ = false; } @@ -568,7 +541,7 @@ void WebContents::CreateNewProfilingEntry(const GURL& url) { TimeTicks current_time = TimeTicks::Now(); PageLoadTracker *page = new PageLoadTracker( - url, process()->host_id(), render_view_host_->routing_id(), + url, process()->host_id(), render_view_host()->routing_id(), current_time); profiler->AddActivePage(page); @@ -653,40 +626,7 @@ void WebContents::SavePage(const std::wstring& main_file, // pending RVH and goes back to the NORMAL RendererState. bool WebContents::Navigate(const NavigationEntry& entry, bool reload) { - RenderViewHost* dest_render_view_host = UpdateRendererStateNavigate(entry); - if (!dest_render_view_host) { - // We weren't able to create a pending render view host. - return false; - } - - // If the current render_view_host_ isn't live, we should create it so - // that we don't show a sad tab while the dest_render_view_host fetches - // its first page. (Bug 1145340) - if (dest_render_view_host != render_view_host_ && - !render_view_host_->IsRenderViewLive()) { - CreateRenderView(render_view_host_); - } - - // If the renderer crashed, then try to create a new one to satisfy this - // navigation request. - if (!dest_render_view_host->IsRenderViewLive()) { - if (!CreateRenderView(dest_render_view_host)) - return false; - - // Now that we've created a new renderer, be sure to hide it if it isn't - // our primary one. Otherwise, we might crash if we try to call Show() - // on it later. - if (dest_render_view_host != render_view_host_ && - dest_render_view_host->view()) { - dest_render_view_host->view()->Hide(); - } else { - // This is our primary renderer, notify here as we won't be calling - // SwapToRenderView (which does the notify). - NotificationService::current()->Notify( - NOTIFY_RENDER_VIEW_HOST_CHANGED, - Source(this), NotificationService::NoDetails()); - } - } + RenderViewHost* dest_render_view_host = render_manager_.Navigate(entry); CreateNewProfilingEntry(entry.GetURL()); @@ -696,8 +636,6 @@ bool WebContents::Navigate(const NavigationEntry& entry, bool reload) { // Navigate in the desired RenderViewHost dest_render_view_host->NavigateToEntry(entry, reload); - showing_repost_interstitial_ = false; - if (entry.GetPageID() == -1) { // HACK!! This code suppresses javascript: URLs from being added to // session history, which is what we want to do for javascript: URLs that @@ -718,371 +656,29 @@ bool WebContents::Navigate(const NavigationEntry& entry, bool reload) { return true; } -RenderViewHost* WebContents::UpdateRendererStateNavigate( - const NavigationEntry& entry) { - // If we are in PENDING or ENTERING_INTERSTITIAL, then we want to get back - // to NORMAL and navigate as usual. - if (renderer_state_ == PENDING || renderer_state_ == ENTERING_INTERSTITIAL) { - if (pending_render_view_host_) - CancelRenderView(&pending_render_view_host_); - if (interstitial_render_view_host_) - CancelRenderView(&interstitial_render_view_host_); - renderer_state_ = NORMAL; - } - - // render_view_host_ will not be deleted before the end of this method, so we - // don't have to worry about this SiteInstance's ref count dropping to zero. - SiteInstance* curr_instance = render_view_host_->site_instance(); - - if (IsShowingInterstitialPage()) { - // Must disable any ability to proceed from the interstitial, because we're - // about to navigate somewhere else. - DisableInterstitialProceed(true); - - if (pending_render_view_host_) - CancelRenderView(&pending_render_view_host_); - - renderer_state_ = LEAVING_INTERSTITIAL; - - // We want to compare against where we were, because we just cancelled - // where we were going. The original_render_view_host_ won't be deleted - // before the end of this method, so we don't have to worry about this - // SiteInstance's ref count dropping to zero. - curr_instance = original_render_view_host_->site_instance(); - } - - // Determine if we need a new SiteInstance for this entry. - // Again, new_instance won't be deleted before the end of this method, so it - // is safe to use a normal pointer here. - SiteInstance* new_instance = curr_instance; - if (ShouldTransitionCrossSite()) { - new_instance = GetSiteInstanceForEntry(entry, curr_instance); - } - - if (new_instance != curr_instance) { - // New SiteInstance. - DCHECK(renderer_state_ == NORMAL || - renderer_state_ == LEAVING_INTERSTITIAL); - - // Create a pending RVH and navigate it. - bool success = CreatePendingRenderView(new_instance); - if (!success) - return NULL; - - // Check if our current RVH is live before we set up a transition. - if (!render_view_host_->IsRenderViewLive()) { - if (renderer_state_ == NORMAL) { - // The current RVH is not live. There's no reason to sit around with a - // sad tab or a newly created RVH while we wait for the pending RVH to - // navigate. Just switch to the pending RVH now and go back to NORMAL, - // without requiring a cross-site transition. (Note that we don't care - // about on{before}unload handlers if the current RVH isn't live.) - SwapToRenderView(&pending_render_view_host_, true); - return render_view_host_; - - } else if (renderer_state_ == LEAVING_INTERSTITIAL) { - // Cancel the interstitial, since it has died and we're navigating away - // anyway. - DCHECK(original_render_view_host_); - if (original_render_view_host_->IsRenderViewLive()) { - // Swap back to the original and act like a pending request (using - // the logic below). - SwapToRenderView(&original_render_view_host_, true); - renderer_state_ = NORMAL; - InterstitialPageGone(); - // Continue with the pending cross-site transition logic below. - } else { - // Both the interstitial and original are dead. Just like the NORMAL - // case, let's skip the cross-site transition entirely. We also have - // to clean up the interstitial state. - SwapToRenderView(&pending_render_view_host_, true); - CancelRenderView(&original_render_view_host_); - renderer_state_ = NORMAL; - InterstitialPageGone(); - return render_view_host_; - } - } else { - NOTREACHED(); - return render_view_host_; - } - } - // Otherwise, it's safe to treat this as a pending cross-site transition. - - // Make sure the old render view stops, in case a load is in progress. - render_view_host_->Stop(); - - // Suspend the new render view (i.e., don't let it send the cross-site - // Navigate message) until we hear back from the old renderer's - // onbeforeunload handler. If it returns false, we'll have to cancel the - // request. - pending_render_view_host_->SetNavigationsSuspended(true); - - // Tell the CrossSiteRequestManager that this RVH has a pending cross-site - // request, so that ResourceDispatcherHost will know to tell us to run the - // old page's onunload handler before it sends the response. - pending_render_view_host_->SetHasPendingCrossSiteRequest(true); - - // We now have a pending RVH. If we were in NORMAL, we should now be in - // PENDING. If we were in LEAVING_INTERSTITIAL, we should stay there. - if (renderer_state_ == NORMAL) - renderer_state_ = PENDING; - else - DCHECK(renderer_state_ == LEAVING_INTERSTITIAL); - - // Tell the old render view to run its onbeforeunload handler, since it - // doesn't otherwise know that the cross-site request is happening. This - // will trigger a call to ShouldClosePage with the reply. - render_view_host_->FirePageBeforeUnload(); - - return pending_render_view_host_; - } - - // Same SiteInstance can be used. Navigate render_view_host_ if we are in - // the NORMAL state, and original_render_view_host_ if an interstitial is - // showing. - if (renderer_state_ == NORMAL) - return render_view_host_; - - DCHECK(renderer_state_ == LEAVING_INTERSTITIAL); - return original_render_view_host_; -} - -bool WebContents::ShouldTransitionCrossSite() { - // True if we are using process-per-site-instance (default) or - // process-per-site (kProcessPerSite). - return !CommandLine().HasSwitch(switches::kProcessPerTab); -} - -SiteInstance* WebContents::GetSiteInstanceForEntry( - const NavigationEntry& entry, SiteInstance* curr_instance) { - // NOTE: This is only called when ShouldTransitionCrossSite is true. - - // If the entry has an instance already, we should use it. - if (entry.site_instance()) - return entry.site_instance(); - - // (UGLY) HEURISTIC, process-per-site only: - // - // If this navigation is generated, then it probably corresponds to a search - // query. Given that search results typically lead to users navigating to - // other sites, we don't really want to use the search engine hostname to - // determine the site instance for this navigation. - // - // NOTE: This can be removed once we have a way to transition between - // RenderViews in response to a link click. - // - if (CommandLine().HasSwitch(switches::kProcessPerSite) && - entry.GetTransitionType() == PageTransition::GENERATED) - return curr_instance; - - const GURL& dest_url = entry.GetURL(); - - // If we haven't used our SiteInstance (and thus RVH) yet, then we can use it - // for this entry. We won't commit the SiteInstance to this site until the - // navigation commits (in DidNavigate), unless the navigation entry was - // restored. As session restore loads all the pages immediately we need to set - // the site first, otherwise after a restore none of the pages would share - // renderers. - if (!curr_instance->has_site()) { - // If we've already created a SiteInstance for our destination, we don't - // want to use this unused SiteInstance; use the existing one. (We don't - // do this check if the curr_instance has a site, because for now, we want - // to compare against the current URL and not the SiteInstance's site. In - // this case, there is no current URL, so comparing against the site is ok. - // See additional comments below.) - if (curr_instance->HasRelatedSiteInstance(dest_url)) { - return curr_instance->GetRelatedSiteInstance(dest_url); - } else { - if (entry.restored()) - curr_instance->SetSite(dest_url); - return curr_instance; - } - } - - // Otherwise, only create a new SiteInstance for cross-site navigation. - - // TODO(creis): Once we intercept links and script-based navigations, we - // will be able to enforce that all entries in a SiteInstance actually have - // the same site, and it will be safe to compare the URL against the - // SiteInstance's site, as follows: - // const GURL& current_url = curr_instance->site(); - // For now, though, we're in a hybrid model where you only switch - // SiteInstances if you type in a cross-site URL. This means we have to - // compare the entry's URL to the last committed entry's URL. - NavigationEntry* curr_entry = controller()->GetLastCommittedEntry(); - if (IsShowingInterstitialPage()) { - // The interstitial is currently the last committed entry, but we want to - // compare against the last non-interstitial entry. - curr_entry = controller()->GetEntryAtOffset(-1); - } - // If there is no last non-interstitial entry (and curr_instance already - // has a site), then we must have been opened from another tab. We want - // to compare against the URL of the page that opened us, but we can't - // get to it directly. The best we can do is check against the site of - // the SiteInstance. This will be correct when we intercept links and - // script-based navigations, but for now, it could place some pages in a - // new process unnecessarily. We should only hit this case if a page tries - // to open a new tab to an interstitial-inducing URL, and then navigates - // the page to a different same-site URL. (This seems very unlikely in - // practice.) - const GURL& current_url = (curr_entry) ? curr_entry->GetURL() : - curr_instance->site(); - - if (SiteInstance::IsSameWebSite(current_url, dest_url)) { - return curr_instance; - } else { - // Start the new renderer in a new SiteInstance, but in the current - // BrowsingInstance. It is important to immediately give this new - // SiteInstance to a RenderViewHost (if it is different than our current - // SiteInstance), so that it is ref counted. This will happen in - // CreatePendingRenderView. - return curr_instance->GetRelatedSiteInstance(dest_url); - } -} - -void WebContents::DisableInterstitialProceed(bool stop_request) { - // TODO(creis): Make sure the interstitial page disables any ability to - // proceed at this point, because we're about to abort the original request. - // This can be done by adding a new event to the NotificationService. - // We should also disable the button on the page itself, but it's ok if that - // doesn't happen immediately. - - // Stopping the request is necessary if we are navigating away, because the - // user could be requesting the same URL again, causing the HttpCache to - // ignore it. (Fixes bug 1079784.) - if (stop_request) { - original_render_view_host_->Stop(); - if (pending_render_view_host_) - pending_render_view_host_->Stop(); - } -} - -bool WebContents::CreatePendingRenderView(SiteInstance* instance) { - NavigationEntry* curr_entry = controller()->GetLastCommittedEntry(); - if (curr_entry && curr_entry->GetType() == TAB_CONTENTS_WEB) { - DCHECK(!curr_entry->GetContentState().empty()); - - // TODO(creis): Should send a message to the RenderView to let it know - // we're about to switch away, so that it sends an UpdateState message. - } - - pending_render_view_host_ = - CreateRenderViewHost(instance, this, MSG_ROUTING_NONE, NULL); - - bool success = CreateRenderView(pending_render_view_host_); - if (success) { - // Don't show the view until we get a DidNavigate from it. - pending_render_view_host_->view()->Hide(); - } else { - CancelRenderView(&pending_render_view_host_); - } - return success; -} - -void WebContents::CancelRenderView(RenderViewHost** render_view_host) { - DCHECK(*render_view_host != NULL); - - // Destroy the render view host - (*render_view_host)->Shutdown(); - (*render_view_host) = NULL; -} - -void WebContents::ShouldClosePage(bool proceed) { - // Should only see this while we have a pending renderer. Otherwise, we - // should ignore. - if (!pending_render_view_host_) { - bool proceed_to_fire_unload; - delegate()->BeforeUnloadFired(this, proceed, &proceed_to_fire_unload); - - if (proceed_to_fire_unload) { - // This is not a cross-site navigation, the tab is being closed. - render_view_host_->FirePageUnload(); - } - return; - } - - DCHECK(renderer_state_ != ENTERING_INTERSTITIAL); - DCHECK(renderer_state_ != INTERSTITIAL); - if (proceed) { - // Ok to unload the current page, so proceed with the cross-site navigate. - pending_render_view_host_->SetNavigationsSuspended(false); - } else { - // Current page says to cancel. - CancelRenderView(&pending_render_view_host_); - renderer_state_ = NORMAL; - } -} - -void WebContents::OnCrossSiteResponse(int new_render_process_host_id, - int new_request_id) { - // Should only see this while we have a pending renderer, possibly during an - // interstitial. Otherwise, we should ignore. - if (renderer_state_ != PENDING && renderer_state_ != LEAVING_INTERSTITIAL) - return; - DCHECK(pending_render_view_host_); - - // Tell the old renderer to run its onunload handler. When it finishes, it - // will send a ClosePage_ACK to the ResourceDispatcherHost with the given - // IDs (of the pending RVH's request), allowing the pending RVH's response to - // resume. - if (IsShowingInterstitialPage()) { - DCHECK(original_render_view_host_); - original_render_view_host_->ClosePage(new_render_process_host_id, - new_request_id); - } else { - render_view_host_->ClosePage(new_render_process_host_id, - new_request_id); - } - - // ResourceDispatcherHost has told us to run the onunload handler, which - // means it is not a download or unsafe page, and we are going to perform the - // navigation. Thus, we no longer need to remember that the RenderViewHost - // is part of a pending cross-site request. - pending_render_view_host_->SetHasPendingCrossSiteRequest(false); -} - void WebContents::Stop() { - render_view_host_->Stop(); - - // If we aren't in the NORMAL renderer state, we should stop the pending - // renderers. This will lead to a DidFailProvisionalLoad, which will - // properly destroy them. - if (renderer_state_ == PENDING) { - pending_render_view_host_->Stop(); - - } else if (renderer_state_ == ENTERING_INTERSTITIAL) { - interstitial_render_view_host_->Stop(); - if (pending_render_view_host_) { - pending_render_view_host_->Stop(); - } - - } else if (renderer_state_ == LEAVING_INTERSTITIAL) { - if (pending_render_view_host_) { - pending_render_view_host_->Stop(); - } - } - + render_manager_.Stop(); printing_.Stop(); } void WebContents::DidBecomeSelected() { TabContents::DidBecomeSelected(); - if (render_view_host_ && render_view_host_->view()) - render_view_host_->view()->DidBecomeSelected(); + if (render_view_host() && view()) + view()->DidBecomeSelected(); CacheManagerHost::GetInstance()->ObserveActivity(process()->host_id()); } void WebContents::WasHidden() { if (!capturing_contents_) { - // |render_view_host_| can be NULL if the user middle clicks a link to open + // |render_view_host()| can be NULL if the user middle clicks a link to open // a tab in then background, then closes the tab before selecting it. This // is because closing the tab calls WebContents::Destroy(), which removes - // the |render_view_host_|; then when we actually destroy the window, + // the |render_view_host()|; then when we actually destroy the window, // OnWindowPosChanged() notices and calls HideContents() (which calls us). - if (render_view_host_ && render_view_host_->view()) - render_view_host_->view()->WasHidden(); + if (render_view_host() && view()) + view()->WasHidden(); // Loop through children and send WasHidden to them, too. int count = static_cast(child_windows_.size()); @@ -1104,16 +700,14 @@ void WebContents::StartFinding(int request_id, bool forward, bool match_case, bool find_next) { - if (search_string.empty()) { + if (search_string.empty()) return; - } - - render_view_host_->StartFinding(request_id, search_string, forward, - match_case, find_next); + render_view_host()->StartFinding(request_id, search_string, forward, + match_case, find_next); } void WebContents::StopFinding(bool clear_selection) { - render_view_host_->StopFinding(clear_selection); + render_view_host()->StopFinding(clear_selection); } void WebContents::OpenFindInPageWindow(const Browser& browser) { @@ -1156,29 +750,29 @@ bool WebContents::AdvanceFindSelection(bool forward_direction) { } void WebContents::AlterTextSize(text_zoom::TextSize size) { - render_view_host_->AlterTextSize(size); + render_view_host()->AlterTextSize(size); // TODO(creis): should this be propagated to other and future RVHs? } void WebContents::SetPageEncoding(const std::wstring& encoding_name) { - render_view_host_->SetPageEncoding(encoding_name); + render_view_host()->SetPageEncoding(encoding_name); // TODO(creis): should this be propagated to other and future RVHs? } void WebContents::CopyImageAt(int x, int y) { - render_view_host_->CopyImageAt(x, y); + render_view_host()->CopyImageAt(x, y); } void WebContents::InspectElementAt(int x, int y) { - render_view_host_->InspectElementAt(x, y); + render_view_host()->InspectElementAt(x, y); } void WebContents::ShowJavaScriptConsole() { - render_view_host_->ShowJavaScriptConsole(); + render_view_host()->ShowJavaScriptConsole(); } void WebContents::AllowDomAutomationBindings() { - render_view_host_->AllowDomAutomationBindings(); + render_view_host()->AllowDomAutomationBindings(); // TODO(creis): should this be propagated to other and future RVHs? } @@ -1186,19 +780,7 @@ void WebContents::OnJavaScriptMessageBoxClosed(IPC::Message* reply_msg, bool success, const std::wstring& prompt) { last_javascript_message_dismissal_ = TimeTicks::Now(); - - RenderViewHost* rvh = render_view_host_; - if (IsShowingInterstitialPage()) { - // No JavaScript message boxes are ever shown by interstitial pages, but - // they can be shown by the original RVH while an interstitial page is - // showing (e.g., from an onunload event handler). We should send this to - // the original RVH and not the interstitial's RVH. - // TODO(creis): Perhaps the JavascriptMessageBoxHandler should store which - // RVH created it, so that it can tell this method which RVH to reply to. - DCHECK(original_render_view_host_); - rvh = original_render_view_host_; - } - rvh->JavaScriptMessageBoxClosed(reply_msg, success, prompt); + render_manager_.OnJavaScriptMessageBoxClosed(reply_msg, success, prompt); } // Generic NotificationObserver callback. @@ -1360,33 +942,33 @@ InfoBarView* WebContents::GetInfoBarView() { void WebContents::ExecuteJavascriptInWebFrame( const std::wstring& frame_xpath, const std::wstring& jscript) { - render_view_host_->ExecuteJavascriptInWebFrame(frame_xpath, jscript); + render_view_host()->ExecuteJavascriptInWebFrame(frame_xpath, jscript); } void WebContents::AddMessageToConsole( const std::wstring& frame_xpath, const std::wstring& msg, ConsoleMessageLevel level) { - render_view_host_->AddMessageToConsole(frame_xpath, msg, level); + render_view_host()->AddMessageToConsole(frame_xpath, msg, level); } void WebContents::Undo() { - render_view_host_->Undo(); + render_view_host()->Undo(); } void WebContents::Redo() { - render_view_host_->Redo(); + render_view_host()->Redo(); } void WebContents::Replace(const std::wstring& text) { - render_view_host_->Replace(text); + render_view_host()->Replace(text); } void WebContents::Delete() { - render_view_host_->Delete(); + render_view_host()->Delete(); } void WebContents::SelectAll() { - render_view_host_->SelectAll(); + render_view_host()->SelectAll(); } void WebContents::StartFileUpload(const std::wstring& file_path, @@ -1394,7 +976,7 @@ void WebContents::StartFileUpload(const std::wstring& file_path, const std::wstring& file, const std::wstring& submit, const std::wstring& other_values) { - render_view_host_->UploadFile(file_path, form, file, submit, other_values); + render_view_host()->UploadFile(file_path, form, file, submit, other_values); } void WebContents::SetWebApp(WebApp* web_app) { @@ -1435,35 +1017,35 @@ void WebContents::CreateShortcut() { // Request the application info. When done OnDidGetApplicationInfo is invoked // and we'll create the shortcut. - render_view_host_->GetApplicationInfo(pending_install_.page_id); + render_view_host()->GetApplicationInfo(pending_install_.page_id); } void WebContents::FillForm(const FormData& form) { - render_view_host_->FillForm(form); + render_view_host()->FillForm(form); } void WebContents::FillPasswordForm( const PasswordFormDomManager::FillData& form_data) { - render_view_host_->FillPasswordForm(form_data); + render_view_host()->FillPasswordForm(form_data); } void WebContents::DragTargetDragEnter(const WebDropData& drop_data, const gfx::Point& client_pt, const gfx::Point& screen_pt) { - render_view_host_->DragTargetDragEnter(drop_data, client_pt, screen_pt); + render_view_host()->DragTargetDragEnter(drop_data, client_pt, screen_pt); } void WebContents::DragTargetDragOver( const gfx::Point& client_pt, const gfx::Point& screen_pt) { - render_view_host_->DragTargetDragOver(client_pt, screen_pt); + render_view_host()->DragTargetDragOver(client_pt, screen_pt); } void WebContents::DragTargetDragLeave() { - render_view_host_->DragTargetDragLeave(); + render_view_host()->DragTargetDragLeave(); } void WebContents::DragTargetDrop( const gfx::Point& client_pt, const gfx::Point& screen_pt) { - render_view_host_->DragTargetDrop(client_pt, screen_pt); + render_view_host()->DragTargetDrop(client_pt, screen_pt); } PasswordManager* WebContents::GetPasswordManager() { @@ -1478,16 +1060,6 @@ PluginInstaller* WebContents::GetPluginInstaller() { return plugin_installer_.get(); } -RenderProcessHost* WebContents::process() const { - return render_view_host_->process(); -} -RenderViewHost* WebContents::render_view_host() const { - return render_view_host_; -} -SiteInstance* WebContents::site_instance() const { - return render_view_host_->site_instance(); -} - bool WebContents::IsActiveEntry(int32 page_id) { NavigationEntry* active_entry = controller()->GetActiveEntry(); return (active_entry != NULL && @@ -1516,7 +1088,9 @@ void WebContents::CreateView(int route_id, HANDLE modal_dialog_event) { // other issues. We should fix this. HWND new_view_parent_window = ::GetAncestor(GetHWND(), GA_ROOT); new_view->CreateView(new_view_parent_window, gfx::Rect()); - new_view->CreatePageView(new_view->render_view_host_); + // TODO(brettw) it seems bogus that we have to call this function on the + // newly created object and give it one of its own member variables. + new_view->CreatePageView(new_view->render_view_host()); // Don't show the view until we get enough context in ShowView. pending_views_[route_id] = new_view; @@ -1529,7 +1103,7 @@ void WebContents::CreateWidget(int route_id) { // We set the parent HWDN explicitly as pop-up HWNDs are parented and owned by // the first non-child HWND of the HWND that was specified to the CreateWindow // call. - widget_view->set_parent_hwnd(render_view_host_->view()->GetPluginHWND()); + widget_view->set_parent_hwnd(view()->GetPluginHWND()); widget_view->set_close_on_deactivate(true); // Don't show the widget until we get its position in ShowWidget. @@ -1549,13 +1123,14 @@ void WebContents::ShowView(int route_id, WebContents* new_view = iter->second; pending_views_.erase(route_id); - if (!new_view->render_view_host_->view() || - !new_view->render_view_host_->process()->channel()) { + if (!new_view->view() || + !new_view->process()->channel()) { // The view has gone away or the renderer crashed. Nothing to do. return; } - new_view->render_view_host_->Init(); + // TODO(brettw) this seems bogus to reach into here and initialize the host. + new_view->render_view_host()->Init(); AddNewContents(new_view, disposition, initial_pos, user_gesture); } @@ -1586,11 +1161,12 @@ void WebContents::ShowWidget(int route_id, const gfx::Rect& initial_pos) { widget_host->Init(); } -void WebContents::RendererReady(RenderViewHost* render_view_host) { - if (IsShowingInterstitialPage() && (render_view_host == render_view_host_)) { +void WebContents::RendererReady(RenderViewHost* rvh) { + if (render_manager_.showing_interstitial_page() && + rvh == render_view_host()) { // We are showing an interstitial page, don't notify the world. return; - } else if (render_view_host != render_view_host_) { + } else if (rvh != render_view_host()) { // Don't notify the world, since this came from a renderer in the // background. return; @@ -1600,11 +1176,11 @@ void WebContents::RendererReady(RenderViewHost* render_view_host) { SetIsCrashed(false); } -void WebContents::RendererGone(RenderViewHost* render_view_host) { +void WebContents::RendererGone(RenderViewHost* rvh) { // Ask the print preview if this renderer was valuable. - if (!printing_.OnRendererGone(render_view_host)) + if (!printing_.OnRendererGone(rvh)) return; - if (render_view_host != render_view_host_) { + if (rvh != render_view_host()) { // The pending or interstitial page's RenderViewHost is gone. If we are // showing an interstitial, this may mean that the original RenderViewHost // is gone. If so, we will call RendererGone again if we try to swap that @@ -1634,16 +1210,14 @@ void WebContents::RendererGone(RenderViewHost* render_view_host) { HungRendererWarning::HideForWebContents(this); } -void WebContents::DidNavigate(RenderViewHost* render_view_host, +void WebContents::DidNavigate(RenderViewHost* rvh, const ViewHostMsg_FrameNavigate_Params& params) { if (PageTransition::IsMainFrame(params.transition)) - UpdateRendererStateDidNavigate(render_view_host); + render_manager_.DidNavigateMainFrame(rvh); // In the case of interstitial, we don't mess with the navigation entries. - if (IsShowingInterstitialPage()) { - DCHECK(renderer_state_ != LEAVING_INTERSTITIAL); + if (render_manager_.showing_interstitial_page()) return; - } // Check for navigations we don't expect. if (!controller() || !is_active_ || params.page_id == -1) { @@ -1653,7 +1227,7 @@ void WebContents::DidNavigate(RenderViewHost* render_view_host, "invalid |page_id| if, and only if, this is the initial blank " "page for a main frame."; } - BroadcastProvisionalLoadCommit(render_view_host, params); + BroadcastProvisionalLoadCommit(rvh, params); return; } @@ -1688,7 +1262,7 @@ void WebContents::DidNavigate(RenderViewHost* render_view_host, // Run post-commit tasks. if (PageTransition::IsMainFrame(params.transition)) DidNavigateMainFramePostCommit(params); - DidNavigateAnyFramePostCommit(render_view_host, params); + DidNavigateAnyFramePostCommit(rvh, params); } NavigationEntry* WebContents::CreateNavigationEntryForCommit( @@ -1710,7 +1284,12 @@ NavigationEntry* WebContents::CreateNavigationEntryForCommit( // Update the site of the SiteInstance if it doesn't have one yet, unless we // are showing an interstitial page. If we are, we should wait until the // real page commits. - if (!site_instance()->has_site() && renderer_state_ != INTERSTITIAL) + // + // TODO(brettw) the old code only checked for INTERSTIAL, this new code also + // checks for LEAVING_INTERSTITIAL mode in the manager. Is this difference + // important? + if (!site_instance()->has_site() && + !render_manager_.showing_interstitial_page()) site_instance()->SetSite(params.url); // When the navigation is just a change in ref or a sub-frame navigation, the @@ -1973,117 +1552,8 @@ void WebContents::HandleProfilingForDidNavigate( params.page_id); profiler->AddFrameMetrics(process()->host_id(), - render_view_host_->routing_id(), frame); - } -} - -void WebContents::UpdateRendererStateDidNavigate( - RenderViewHost* render_view_host) { - if (renderer_state_ == NORMAL) { - // We should only hear this from our current renderer. - DCHECK(render_view_host == render_view_host_); - return; - } else if (renderer_state_ == PENDING) { - if (render_view_host == pending_render_view_host_) { - // The pending cross-site navigation completed, so show the renderer. - SwapToRenderView(&pending_render_view_host_, true); - renderer_state_ = NORMAL; - } else if (render_view_host == render_view_host_) { - // A navigation in the original page has taken place. Cancel the pending - // one. - CancelRenderView(&pending_render_view_host_); - renderer_state_ = NORMAL; - } else { - // No one else should be sending us DidNavigate in this state. - DCHECK(false); - return; - } - - } else if (renderer_state_ == ENTERING_INTERSTITIAL) { - if (render_view_host == interstitial_render_view_host_) { - // The interstitial renderer is ready, so show it, and keep the old - // RenderViewHost around. - original_render_view_host_ = render_view_host_; - SwapToRenderView(&interstitial_render_view_host_, false); - renderer_state_ = INTERSTITIAL; - } else if (render_view_host == render_view_host_) { - // We shouldn't get here, because the original render view was the one - // that caused the ShowInterstitial. - // However, until we intercept navigation events from JavaScript, it is - // possible to get here, if another tab tells render_view_host_ to - // navigate. To be safe, we'll cancel the interstitial and show the - // page that caused the DidNavigate. - CancelRenderView(&interstitial_render_view_host_); - if (pending_render_view_host_) - CancelRenderView(&pending_render_view_host_); - renderer_state_ = NORMAL; - } else if (render_view_host == pending_render_view_host_) { - // We shouldn't get here, because the original render view was the one - // that caused the ShowInterstitial. - // However, until we intercept navigation events from JavaScript, it is - // possible to get here, if another tab tells pending_render_view_host_ - // to navigate. To be safe, we'll cancel the interstitial and show the - // page that caused the DidNavigate. - CancelRenderView(&interstitial_render_view_host_); - SwapToRenderView(&pending_render_view_host_, true); - renderer_state_ = NORMAL; - } else { - // No one else should be sending us DidNavigate in this state. - DCHECK(false); - return; - } - - } else if (renderer_state_ == INTERSTITIAL) { - if (render_view_host == original_render_view_host_) { - // We shouldn't get here, because the original render view was the one - // that caused the ShowInterstitial. - // However, until we intercept navigation events from JavaScript, it is - // possible to get here, if another tab tells render_view_host_ to - // navigate. To be safe, we'll cancel the interstitial and show the - // page that caused the DidNavigate. - SwapToRenderView(&original_render_view_host_, true); - if (pending_render_view_host_) - CancelRenderView(&pending_render_view_host_); - renderer_state_ = NORMAL; - } else if (render_view_host == pending_render_view_host_) { - // No one else should be sending us DidNavigate in this state. - // However, until we intercept navigation events from JavaScript, it is - // possible to get here, if another tab tells pending_render_view_host_ - // to navigate. To be safe, we'll cancel the interstitial and show the - // page that caused the DidNavigate. - SwapToRenderView(&pending_render_view_host_, true); - CancelRenderView(&original_render_view_host_); - renderer_state_ = NORMAL; - } else { - // No one else should be sending us DidNavigate in this state. - DCHECK(false); - return; - } - InterstitialPageGone(); - - } else if (renderer_state_ == LEAVING_INTERSTITIAL) { - if (render_view_host == original_render_view_host_) { - // We navigated to something in the original renderer, so show it. - if (pending_render_view_host_) - CancelRenderView(&pending_render_view_host_); - SwapToRenderView(&original_render_view_host_, true); - renderer_state_ = NORMAL; - } else if (render_view_host == pending_render_view_host_) { - // We navigated to something in the pending renderer. - CancelRenderView(&original_render_view_host_); - SwapToRenderView(&pending_render_view_host_, true); - renderer_state_ = NORMAL; - } else { - // No one else should be sending us DidNavigate in this state. - DCHECK(false); - return; - } - InterstitialPageGone(); - - } else { - // No such state. - DCHECK(false); - return; + render_view_host()->routing_id(), + frame); } } @@ -2092,7 +1562,7 @@ void WebContents::BroadcastProvisionalLoadCommit( const ViewHostMsg_FrameNavigate_Params& params) { ProvisionalLoadDetails details( PageTransition::IsMainFrame(params.transition), - IsInterstitialRenderViewHost(render_view_host), + render_manager_.IsRenderViewInterstitial(render_view_host), IsInPageNavigation(params.url), params.url, params.security_info); NotificationService::current()-> @@ -2119,51 +1589,6 @@ void WebContents::UpdateWebPreferences() { render_view_host()->UpdateWebPreferences(GetWebkitPrefs()); } -void WebContents::SwapToRenderView(RenderViewHost** new_render_view_host, - bool destroy_after) { - // Remember if the page was focused so we can focus the new renderer in - // that case. - bool focus_render_view = render_view_host_->view() && - render_view_host_->view()->HasFocus(); - - // Hide the current view and prepare to destroy it. - // TODO(creis): Get the old RenderViewHost to send us an UpdateState message - // before we destroy it. - if (render_view_host_->view()) - render_view_host_->view()->Hide(); - RenderViewHost* old_render_view_host = render_view_host_; - - // Swap in the pending view and make it active. - render_view_host_ = (*new_render_view_host); - (*new_render_view_host) = NULL; - - // If the view is gone, then this RenderViewHost died while it was hidden. - // We ignored the RendererGone call at the time, so we should send it now - // to make sure the sad tab shows up, etc. - if (render_view_host_->view()) - render_view_host_->view()->Show(); - else - RendererGone(render_view_host_); - - // Make sure the size is up to date. (Fix for bug 1079768.) - UpdateRenderViewSize(); - - if (focus_render_view && render_view_host_->view()) - render_view_host_->view()->Focus(); - - NotificationService::current()->Notify( - NOTIFY_RENDER_VIEW_HOST_CHANGED, - Source(this), - Details(old_render_view_host)); - - if (destroy_after) - old_render_view_host->Shutdown(); - - // Let the task manager know that we've swapped RenderViewHosts, since it - // might need to update its process groupings. - NotifySwapped(); -} - void WebContents::UpdateRenderViewSize() { // Using same technique as OnPaint, which sets size of SadTab. CRect cr; @@ -2172,12 +1597,13 @@ void WebContents::UpdateRenderViewSize() { SizeContents(new_size); } -void WebContents::UpdateState(RenderViewHost* render_view_host, +void WebContents::UpdateState(RenderViewHost* rvh, int32 page_id, const GURL& url, const std::wstring& title, const std::string& state) { - if (render_view_host != render_view_host_ || IsShowingInterstitialPage()) { + if (rvh != render_view_host() || + render_manager_.showing_interstitial_page()) { // This UpdateState is either: // - targeted not at the current RenderViewHost. This could be that we are // showing the interstitial page and getting an update for the regular page, @@ -2234,7 +1660,7 @@ void WebContents::UpdateState(RenderViewHost* render_view_host, if (GetHWND()) { // It's possible to get this after the hwnd has been destroyed. ::SetWindowText(GetHWND(), title.c_str()); - ::SetWindowText(render_view_host_->view()->GetPluginHWND(), title.c_str()); + ::SetWindowText(view()->GetPluginHWND(), title.c_str()); } // Update the state (forms, etc.). @@ -2249,7 +1675,7 @@ void WebContents::UpdateState(RenderViewHost* render_view_host, controller()->SyncSessionWithEntryByPageID(type(), site_instance(), page_id); } -void WebContents::UpdateTitle(RenderViewHost* render_view_host, +void WebContents::UpdateTitle(RenderViewHost* rvh, int32 page_id, const std::wstring& title) { if (!controller()) return; @@ -2259,7 +1685,8 @@ void WebContents::UpdateTitle(RenderViewHost* render_view_host, response_started_ = false; NavigationEntry* entry; - if (IsShowingInterstitialPage() && (render_view_host == render_view_host_)) { + if (render_manager_.showing_interstitial_page() && + (rvh == render_view_host())) { // We are showing an interstitial page in a different RenderViewHost, so // the page_id is not sufficient to find the entry from the controller. // (both RenderViewHost page_ids overlap). We know it is the last entry, @@ -2316,9 +1743,9 @@ void WebContents::UpdateThumbnail(const GURL& url, } } -void WebContents::Close(RenderViewHost* render_view_host) { +void WebContents::Close(RenderViewHost* rvh) { // Ignore this if it comes from a RenderViewHost that we aren't showing. - if (delegate() && render_view_host == render_view_host_) + if (delegate() && rvh == render_view_host()) delegate()->CloseContents(this); } @@ -2339,7 +1766,7 @@ void WebContents::DidStopLoading(RenderViewHost* rvh, int32 page_id) { NavigationProfiler* profiler = GetNavigationProfiler(); profiler->SetLoadingEndTime(process()->host_id(), - render_view_host_->routing_id(), page_id, + render_view_host()->routing_id(), page_id, current_time); SaveCurrentProfilingEntry(); } @@ -2380,10 +1807,11 @@ void WebContents::DidStartProvisionalLoadForFrame( RenderViewHost* render_view_host, bool is_main_frame, const GURL& url) { - ProvisionalLoadDetails details(is_main_frame, - IsInterstitialRenderViewHost(render_view_host), - IsInPageNavigation(url), - url, std::string()); + ProvisionalLoadDetails details( + is_main_frame, + render_manager_.IsRenderViewInterstitial(render_view_host), + IsInPageNavigation(url), + url, std::string()); NotificationService::current()-> Notify(NOTIFY_FRAME_PROVISIONAL_LOAD_START, Source(controller()), @@ -2431,78 +1859,27 @@ void WebContents::DidFailProvisionalLoadWithError( if (!controller()) return; - // This will discard our pending entry if we cancelled the load (e.g., if we - // decided to download the file instead of load it). Only discard the pending - // entry if the URLs match, otherwise the user initiated a navigate before - // the page loaded so that the discard would discard the wrong entry. if (net::ERR_ABORTED == error_code) { + // This will discard our pending entry if we cancelled the load (e.g., if we + // decided to download the file instead of load it). Only discard the + // pending entry if the URLs match, otherwise the user initiated a navigate + // before the page loaded so that the discard would discard the wrong entry. NavigationEntry* pending_entry = controller()->GetPendingEntry(); if (pending_entry && pending_entry->GetURL() == url) controller()->DiscardPendingEntry(); - // We used to cancel the pending renderer here for cross-site downloads. - // However, it's not safe to do that because the download logic repeatedly - // looks for this TabContents based on a render view ID. Instead, we just - // leave the pending renderer around until the next navigation event - // (Navigate, DidNavigate, etc), which will clean it up properly. - // TODO(creis): All of this will go away when we move the cross-site logic - // to ResourceDispatcherHost, so that we intercept responses rather than - // navigation events. (That's necessary to support onunload anyway.) Once - // we've made that change, we won't create a pending renderer until we know - // the response is not a download. - - if (renderer_state_ == ENTERING_INTERSTITIAL) { - if ((pending_render_view_host_ && - (pending_render_view_host_ == render_view_host)) || - (!pending_render_view_host_ && - (render_view_host_ == render_view_host))) { - // The abort came from the RenderViewHost that triggered the - // interstitial. (e.g., User clicked stop after ShowInterstitial but - // before the interstitial was visible.) We should go back to NORMAL. - // Note that this is an uncommon case, because we are only in the - // ENTERING_INTERSTITIAL state in the small time window while the - // interstitial's RenderViewHost is being created. - if (pending_render_view_host_) - CancelRenderView(&pending_render_view_host_); - CancelRenderView(&interstitial_render_view_host_); - renderer_state_ = NORMAL; - } - // We can get here, at least in the following case. - // We show an interstitial, then navigate to a URL that leads to another - // interstitial. Now there's a race. The new interstitial will be - // created and we will go to ENTERING_INTERSTITIAL, but the old one will - // meanwhile destroy itself and fire DidFailProvisionalLoad. That puts - // us here. Should be safe to ignore the DidFailProvisionalLoad, from - // the perspective of the renderer state. - } else if (renderer_state_ == LEAVING_INTERSTITIAL) { - // If we've left the interstitial by seeing a download (or otherwise - // aborting a load), we should get back to the original page, because - // interstitial page doesn't make sense anymore. (For example, we may - // have clicked Proceed on a download URL.) - - // TODO(creis): This causes problems in the old process model when - // visiting a new URL from an interstitial page. - // This is because we receive a DidFailProvisionalLoad from cancelling - // the first request, which is indistinguishable from a - // DidFailProvisionalLoad from the second request (if it is a download). - // We need to find a way to distinguish these cases, because it doesn't - // make sense to keep showing the interstitial after a download. - // if (pending_render_view_host_) - // CancelRenderView(&pending_render_view_host_); - // SwapToRenderView(&original_render_view_host_, true); - // renderer_state_ = NORMAL; - // InterstitialPageGone(); - } + render_manager_.RendererAbortedProvisionalLoad(render_view_host); } // Send out a notification that we failed a provisional load with an error. - ProvisionalLoadDetails details(is_main_frame, - IsInterstitialRenderViewHost(render_view_host), - IsInPageNavigation(url), - url, std::string()); + ProvisionalLoadDetails details( + is_main_frame, + render_manager_.IsRenderViewInterstitial(render_view_host), + IsInPageNavigation(url), + url, std::string()); details.set_error_code(error_code); - showing_repost_interstitial_ = showing_repost_interstitial; + render_manager_.set_showing_repost_interstitial(showing_repost_interstitial); NotificationService::current()-> Notify(NOTIFY_FAIL_PROVISIONAL_LOAD_WITH_ERROR, @@ -2590,7 +1967,7 @@ void WebContents::StartDragging(const WebDropData& drop_data) { data->SetString(drop_data.plain_text); scoped_refptr drag_source( - new WebDragSource(GetHWND(), render_view_host_)); + new WebDragSource(GetHWND(), render_view_host())); DWORD effects; @@ -2601,7 +1978,8 @@ void WebContents::StartDragging(const WebDropData& drop_data) { DoDragDrop(data, drag_source, DROPEFFECT_COPY | DROPEFFECT_LINK, &effects); MessageLoop::current()->SetNestableTasksAllowed(old_state); - render_view_host_->DragSourceSystemDragEnded(); + if (render_view_host()) + render_view_host()->DragSourceSystemDragEnded(); } void WebContents::UpdateDragCursor(bool is_drop_target) { @@ -2969,11 +2347,17 @@ void WebContents::UpdateMaxPageIDIfNecessary(SiteInstance* site_instance, } } +void WebContents::BeforeUnloadFiredFromRenderManager( + bool proceed, + bool* proceed_to_fire_unload) { + delegate()->BeforeUnloadFired(this, proceed, proceed_to_fire_unload); +} + + HWND WebContents::GetContentHWND() { - if (!render_view_host_ || !render_view_host_->view()) + if (!view()) return NULL; - - return render_view_host_->view()->GetPluginHWND(); + return view()->GetPluginHWND(); } bool WebContents::CanDisplayFile(const std::wstring& full_path) { @@ -2989,7 +2373,7 @@ bool WebContents::CanDisplayFile(const std::wstring& full_path) { void WebContents::PrintPreview() { // We can't print interstitial page for now. - if (IsShowingInterstitialPage()) + if (render_manager_.showing_interstitial_page()) return; // If we have a FindInPage dialog, notify it that its tab was hidden. @@ -3002,7 +2386,7 @@ void WebContents::PrintPreview() { bool WebContents::PrintNow() { // We can't print interstitial page for now. - if (IsShowingInterstitialPage()) + if (render_manager_.showing_interstitial_page()) return false; // If we have a FindInPage dialog, notify it that its tab was hidden. @@ -3021,19 +2405,19 @@ void WebContents::DidCaptureContents() { } void WebContents::Cut() { - render_view_host_->Cut(); + render_view_host()->Cut(); } void WebContents::Copy() { - render_view_host_->Copy(); + render_view_host()->Copy(); } void WebContents::Paste() { - render_view_host_->Paste(); + render_view_host()->Paste(); } void WebContents::SetInitialFocus(bool reverse) { - render_view_host_->SetInitialFocus(reverse); + render_view_host()->SetInitialFocus(reverse); } void WebContents::GenerateKeywordIfNecessary( @@ -3148,21 +2532,8 @@ void WebContents::HandleKeyboardEvent(const WebKeyboardEvent& event) { event.actual_message.lParam); } -RenderViewHost* WebContents::CreateRenderViewHost( - SiteInstance* instance, - RenderViewHostDelegate* delegate, - int routing_id, - HANDLE modal_dialog_event) { - if (render_view_factory_) { - return render_view_factory_->CreateRenderViewHost( - instance, delegate, routing_id, modal_dialog_event); - } else { - return new RenderViewHost(instance, delegate, routing_id, - modal_dialog_event); - } -} - -bool WebContents::CreateRenderView(RenderViewHost* render_view_host) { +bool WebContents::CreateRenderViewForRenderManager( + RenderViewHost* render_view_host) { RenderWidgetHostHWND* view = CreatePageView(render_view_host); bool ok = render_view_host->CreateRenderView(); @@ -3199,239 +2570,25 @@ void WebContents::SetIsLoading(bool is_loading, LoadNotificationDetails* details) { if (!is_loading) { load_state_ = net::LOAD_STATE_IDLE; - load_state_host_ = std::wstring(); + load_state_host_.clear(); } TabContents::SetIsLoading(is_loading, details); - // We don't know which render_view_host this is for, so let's tell them all - render_view_host_->SetIsLoading(is_loading); - if (pending_render_view_host_) - pending_render_view_host_->SetIsLoading(is_loading); - if (original_render_view_host_) - original_render_view_host_->SetIsLoading(is_loading); + render_manager_.SetIsLoading(is_loading); } void WebContents::FileSelected(const std::wstring& path, void* params) { - render_view_host_->FileSelected(path); + render_view_host()->FileSelected(path); } void WebContents::FileSelectionCanceled(void* params) { // If the user cancels choosing a file to upload we need to pass back the // empty string. - render_view_host_->FileSelected(L""); + render_view_host()->FileSelected(std::wstring()); } /////////////////////////////////////////////////////////////////////////////// -bool WebContents::IsShowingInterstitialPage() const { - return (renderer_state_ == INTERSTITIAL) || - (renderer_state_ == LEAVING_INTERSTITIAL); -} - -void WebContents::ShowInterstitialPage(const std::string& html_text, - InterstitialPageDelegate* delegate) { - // Note that it is important that the interstitial page render view host is - // in the same process as the normal render view host for the tab, so they - // use page ids from the same pool. If they came from different processes, - // page ids may collide causing confusion in the controller (existing - // navigation entries in the controller history could get overridden with the - // interstitial entry). - SiteInstance* interstitial_instance = NULL; - - if (renderer_state_ == NORMAL) { - // render_view_host_ will not be deleted before the end of this method, so - // we don't have to worry about this SiteInstance's ref count dropping to - // zero. - interstitial_instance = render_view_host_->site_instance(); - - } else if (renderer_state_ == PENDING) { - // pending_render_view_host_ will not be deleted before the end of this - // method (when we are in this state), so we don't have to worry about this - // SiteInstance's ref count dropping to zero. - interstitial_instance = pending_render_view_host_->site_instance(); - - } else if (renderer_state_ == ENTERING_INTERSTITIAL) { - // We should never get here if we're in the process of showing an - // interstitial. - // However, until we intercept navigation events from JavaScript, it is - // possible to get here, if another tab tells render_view_host_ to - // navigate to a URL that causes an interstitial. To be safe, we'll cancel - // the first interstitial. - CancelRenderView(&interstitial_render_view_host_); - renderer_state_ = NORMAL; - - // We'd like to now show the new interstitial, but if there's a - // pending_render_view_host_, we can't tell if this JavaScript navigation - // occurred in the original or the pending renderer. That means we won't - // know where to proceed, so we can't show the interstitial. This is - // really just meant to avoid a crash until we can intercept JavaScript - // navigation events, so for now we'll kill the interstitial and go back - // to the last known good page. - if (pending_render_view_host_) { - CancelRenderView(&pending_render_view_host_); - return; - } - // Should be safe to show the interstitial for the new page. - // render_view_host_ will not be deleted before the end of this method, so - // we don't have to worry about this SiteInstance's ref count dropping to - // zero. - interstitial_instance = render_view_host_->site_instance(); - - } else if (renderer_state_ == INTERSTITIAL) { - // We should never get here if we're already showing an interstitial. - // However, until we intercept navigation events from JavaScript, it is - // possible to get here, if another tab tells render_view_host_ to - // navigate to a URL that causes an interstitial. To be safe, we'll go - // back to normal first. - if (pending_render_view_host_ != NULL) { - // There was a pending RVH. We don't know which RVH caused this call - // to ShowInterstitial, so we can't really proceed. We'll have to stay - // in the NORMAL state, showing the last good page. This is only a - // temporary fix anyway, to stave off a crash. - HideInterstitialPage(false, false); - return; - } - // Should be safe to show the interstitial for the new page. - // render_view_host_ will not be deleted before the end of this method, so - // we don't have to worry about this SiteInstance's ref count dropping to - // zero. - SwapToRenderView(&original_render_view_host_, true); - interstitial_instance = render_view_host_->site_instance(); - - } else if (renderer_state_ == LEAVING_INTERSTITIAL) { - SwapToRenderView(&original_render_view_host_, true); - interstitial_instance = NULL; - if (pending_render_view_host_) { - // We're now effectively in PENDING. - // pending_render_view_host_ will not be deleted before the end of this - // method, so we don't have to worry about this SiteInstance's ref count - // dropping to zero. - interstitial_instance = pending_render_view_host_->site_instance(); - } else { - // We're now effectively in NORMAL. - // render_view_host_ will not be deleted before the end of this method, - // so we don't have to worry about this SiteInstance's ref count dropping - // to zero. - interstitial_instance = render_view_host_->site_instance(); - } - - } else { - // No such state. - DCHECK(false); - return; - } - - // Create a pending renderer and move to ENTERING_INTERSTITIAL. - interstitial_render_view_host_ = - CreateRenderViewHost(interstitial_instance, this, MSG_ROUTING_NONE, NULL); - interstitial_delegate_ = delegate; - bool success = CreateRenderView(interstitial_render_view_host_); - if (!success) { - // TODO(creis): If this fails, should we load the interstitial in - // render_view_host_? We shouldn't just skip the interstitial... - CancelRenderView(&interstitial_render_view_host_); - return; - } - - // Don't show the view yet. - interstitial_render_view_host_->view()->Hide(); - - renderer_state_ = ENTERING_INTERSTITIAL; - - // We allow the DOM bindings as a way to get the page to talk back to us. - interstitial_render_view_host_->AllowDomAutomationBindings(); - - interstitial_render_view_host_->LoadAlternateHTMLString(html_text, false, - GURL::EmptyGURL(), - std::string()); -} - -void WebContents::HideInterstitialPage(bool wait_for_navigation, - bool proceed) { - if (renderer_state_ == NORMAL || renderer_state_ == PENDING) { - // Shouldn't get here, since there's no interstitial showing. - DCHECK(false); - return; - - } else if (renderer_state_ == ENTERING_INTERSTITIAL) { - // Unclear if it is possible to get here. (Can you hide the interstitial - // before it is shown?) If so, we should go back to NORMAL. - CancelRenderView(&interstitial_render_view_host_); - if (pending_render_view_host_) - CancelRenderView(&pending_render_view_host_); - renderer_state_ = NORMAL; - return; - } - - DCHECK(IsShowingInterstitialPage()); - DCHECK(render_view_host_ && original_render_view_host_ && - !interstitial_render_view_host_); - - if (renderer_state_ == INTERSTITIAL) { - // Disable the Proceed button on the interstitial, because the destination - // renderer might get replaced. - DisableInterstitialProceed(false); - - } else if (renderer_state_ == LEAVING_INTERSTITIAL) { - // We have already given up the ability to proceed by starting a new - // navigation. If this is a request to proceed, we must ignore it. - // (Hopefully we will have disabled the Proceed button by now, but it's - // possible to get here before that happens.) - if (proceed) - return; - } - - if (wait_for_navigation) { - // We are resuming the loading. We need to set the state to loading again - // as it was set to false when the interstitial stopped loading (so the - // throbber runs). - DidStartLoading(render_view_host_, NULL); - } - - if (proceed) { - // Now we will resume loading automatically, either in - // original_render_view_host_ or in pending_render_view_host_. When it - // completes, we will display the renderer in DidNavigate. - renderer_state_ = LEAVING_INTERSTITIAL; - - } else { - // Don't proceed. Go back to the previously showing page. - if (renderer_state_ == LEAVING_INTERSTITIAL) { - // We said DontProceed after starting to leave the interstitial. - // Abandon whatever we were in the process of doing. - original_render_view_host_->Stop(); - } - SwapToRenderView(&original_render_view_host_, true); - if (pending_render_view_host_) - CancelRenderView(&pending_render_view_host_); - renderer_state_ = NORMAL; - InterstitialPageGone(); - } -} - -void WebContents::InterstitialPageGone() { - DCHECK(!IsShowingInterstitialPage()); - - NotificationService::current()->Notify( - NOTIFY_INTERSTITIAL_PAGE_CLOSED, - Source(this), NotificationService::NoDetails()); - if (interstitial_delegate_) { - interstitial_delegate_->InterstitialClosed(); - interstitial_delegate_ = NULL; - } -} - -bool WebContents::IsInterstitialRenderViewHost( - RenderViewHost* render_view_host) const { - if (IsShowingInterstitialPage()) { - return render_view_host_ == render_view_host; - } - if (renderer_state_ == ENTERING_INTERSTITIAL) { - return interstitial_render_view_host_ == render_view_host; - } - return false; -} - bool WebContents::IsInPageNavigation(const GURL& url) const { // We compare to the last committed entry and not the active entry as the // active entry is the current pending entry (if any). @@ -3484,7 +2641,8 @@ void WebContents::InstallMissingPlugin() { void WebContents::GetAllSavableResourceLinksForCurrentPage( const GURL& page_url) { - render_view_host_->GetAllSavableResourceLinksForCurrentPage(page_url); + render_view_host()->GetAllSavableResourceLinksForCurrentPage( + page_url); } void WebContents::OnReceivedSavableResourceLinksForCurrentPage( @@ -3492,10 +2650,11 @@ void WebContents::OnReceivedSavableResourceLinksForCurrentPage( const std::vector& referrers_list, const std::vector& frames_list) { SavePackage* save_package = get_save_package(); - if (save_package) + if (save_package) { save_package->ProcessCurrentPageAllSavableResourceLinks(resources_list, referrers_list, frames_list); + } } void WebContents::GetSerializedHtmlDataForCurrentPageWithLocalLinks( @@ -3521,8 +2680,8 @@ bool WebContents::CanBlur() const { return delegate() ? delegate()->CanBlur() : true; } -void WebContents::RendererUnresponsive(RenderViewHost* render_view_host) { - if (render_view_host_ && render_view_host_->IsRenderViewLive()) +void WebContents::RendererUnresponsive(RenderViewHost* rvh) { + if (render_view_host() && render_view_host()->IsRenderViewLive()) HungRendererWarning::ShowForWebContents(this); } @@ -3533,7 +2692,7 @@ void WebContents::RendererResponsive(RenderViewHost* render_view_host) { void WebContents::LoadStateChanged(const GURL& url, net::LoadState load_state) { load_state_ = load_state; - load_state_host_ = UTF8ToWide(url.host().c_str()); + load_state_host_ = UTF8ToWide(url.host()); if (load_state_ == net::LOAD_STATE_READING_RESPONSE) response_started_ = false; if (is_loading()) diff --git a/chrome/browser/web_contents.h b/chrome/browser/web_contents.h index a48d117..2f3a7bd 100644 --- a/chrome/browser/web_contents.h +++ b/chrome/browser/web_contents.h @@ -37,6 +37,7 @@ #include "chrome/browser/fav_icon_helper.h" #include "chrome/browser/printing/print_view_manager.h" #include "chrome/browser/render_view_host_delegate.h" +#include "chrome/browser/render_view_host_manager.h" #include "chrome/browser/save_package.h" #include "chrome/browser/shell_dialogs.h" #include "chrome/browser/tab_contents.h" @@ -58,6 +59,7 @@ class WebDropTarget; class WebContents : public TabContents, public RenderViewHostDelegate, + public RenderViewHostManager::Delegate, public ChromeViews::HWNDViewContainer, public SelectFileDialog::Listener, public WebApp::Observer { @@ -119,6 +121,14 @@ class WebContents : public TabContents, // Return true if the WebContents is doing performance profiling bool is_profiling() const { return is_profiling_; } + // Various other systems need to know about our interstitials. + bool showing_interstitial_page() const { + return render_manager_.showing_interstitial_page(); + } + bool showing_repost_interstitial() const { + return render_manager_.showing_repost_interstitial(); + } + // Check with the global navigation profiler on whether to enable // profiling. Return true if profiling needs to be enabled, return // false otherwise. @@ -254,9 +264,18 @@ class WebContents : public TabContents, // should be aware that the SiteInstance could be deleted if its ref count // drops to zero (i.e., if all RenderViewHosts and NavigationEntries that // use it are deleted). - RenderProcessHost* process() const; - RenderViewHost* render_view_host() const; - SiteInstance* site_instance() const; + RenderProcessHost* process() const { + return render_manager_.current_host()->process(); + } + RenderViewHost* render_view_host() const { + return render_manager_.current_host(); + } + SiteInstance* site_instance() const { + return render_manager_.current_host()->site_instance(); + } + RenderWidgetHostView* view() const { + return render_manager_.current_view(); + } // Overridden from TabContents to return the window of the // RenderWidgetHostView. @@ -284,15 +303,23 @@ class WebContents : public TabContents, virtual void Copy(); virtual void Paste(); - // Returns whether we are currently showing an interstitial page. - bool IsShowingInterstitialPage() const; + // The rest of the system wants to interact with the delegate our render view + // host manager has. See those setters for more. + InterstitialPageDelegate* interstitial_page_delegate() const { + return render_manager_.interstitial_delegate(); + } + void set_interstitial_delegate(InterstitialPageDelegate* delegate) { + render_manager_.set_interstitial_delegate(delegate); + } // Displays the specified html in the current page. This method can be used to // show temporary pages (such as security error pages). It can be hidden by // calling HideInterstitialPage, in which case the original page is restored. // An optional delegate may be passed, it is not owned by the WebContents. void ShowInterstitialPage(const std::string& html_text, - InterstitialPageDelegate* delegate); + InterstitialPageDelegate* delegate) { + render_manager_.ShowInterstitialPage(html_text, delegate); + } // Reverts from the interstitial page to the original page. // If |wait_for_navigation| is true, the interstitial page is removed when @@ -301,56 +328,22 @@ class WebContents : public TabContents, // Hiding the interstitial page right away would show the previous displayed // page. If |proceed| is true, the WebContents will expect the navigation // to complete. If not, it will revert to the last shown page. - void HideInterstitialPage(bool wait_for_navigation, bool proceed); + void HideInterstitialPage(bool wait_for_navigation, bool proceed) { + render_manager_.HideInterstitialPage(wait_for_navigation, proceed); + } // Allows the WebContents to react when a cross-site response is ready to be // delivered to a pending RenderViewHost. We must first run the onunload // handler of the old RenderViewHost before we can allow it to proceed. void OnCrossSiteResponse(int new_render_process_host_id, - int new_request_id); + int new_request_id) { + render_manager_.OnCrossSiteResponse(new_render_process_host_id, + new_request_id); + } // Returns true if the active NavigationEntry's page_id equals page_id. bool IsActiveEntry(int32 page_id); - // RenderViewHost states. These states represent whether a cross-site - // request is pending (in the new process model) and whether an interstitial - // page is being shown. These are public to give easy access to unit tests. - enum RendererState { - // NORMAL: just showing a page normally. - // render_view_host_ is showing a page. - // pending_render_view_host_ is NULL. - // original_render_view_host_ is NULL. - // interstitial_render_view_host_ is NULL. - NORMAL = 0, - // PENDING: creating a new RenderViewHost for a cross-site navigation. - // Never used when --process-per-tab is specified. - // render_view_host_ is showing a page. - // pending_render_view_host_ is loading a page in the background. - // original_render_view_host_ is NULL. - // interstitial_render_view_host_ is NULL. - PENDING, - // ENTERING_INTERSTITIAL: an interstitial RenderViewHost has been created. - // and will be shown as soon as it calls DidNavigate. - // render_view_host_ is showing a page. - // pending_render_view_host_ is either NULL or suspended in the background. - // original_render_view_host_ is NULL. - // interstitial_render_view_host_ is loading in the background. - ENTERING_INTERSTITIAL, - // INTERSTITIAL: Showing an interstitial page. - // render_view_host_ is showing the interstitial. - // pending_render_view_host_ is either NULL or suspended in the background. - // original_render_view_host_ is the hidden original page. - // interstitial_render_view_host_ is NULL. - INTERSTITIAL, - // LEAVING_INTERSTITIAL: interstitial is still showing, but we are - // navigating to a new page that will replace it. - // render_view_host_ is showing the interstitial. - // pending_render_view_host_ is either NULL or loading a page. - // original_render_view_host_ is hidden and possibly loading a page. - // interstitial_render_view_host_ is NULL. - LEAVING_INTERSTITIAL - }; - const std::string& contents_mime_type() const { return contents_mime_type_; } @@ -358,20 +351,6 @@ class WebContents : public TabContents, // Returns true if this WebContents will notify about disconnection. bool notify_disconnection() const { return notify_disconnection_; } - // Are we showing the POST interstitial page? - // - // NOTE: the POST interstitial does NOT result in a separate RenderViewHost. - bool showing_repost_interstitial() { return showing_repost_interstitial_; } - - // Accessors to the the interstitial delegate, that is optionaly set when - // an interstitial page is shown. - InterstitialPageDelegate* interstitial_page_delegate() const { - return interstitial_delegate_; - } - void set_interstitial_delegate(InterstitialPageDelegate* delegate) { - interstitial_delegate_ = delegate; - } - protected: FRIEND_TEST(WebContentsTest, OnMessageReceived); @@ -473,7 +452,9 @@ class WebContents : public TabContents, virtual void OnReceivedSerializedHtmlData(const GURL& frame_url, const std::string& data, int32 status); - virtual void ShouldClosePage(bool proceed); + virtual void ShouldClosePage(bool proceed) { + render_manager_.ShouldClosePage(proceed); + } virtual bool CanBlur() const; virtual void RendererUnresponsive(RenderViewHost* render_view_host); virtual void RendererResponsive(RenderViewHost* render_view_host); @@ -507,15 +488,22 @@ class WebContents : public TabContents, virtual void FileSelected(const std::wstring& path, void* params); virtual void FileSelectionCanceled(void* params); - // This method initializes the given renderer if necessary and creates the - // view ID corresponding to this view host. If this method is not called and - // the process is not shared, then the WebContents will act as though the - // renderer is not running (i.e., it will render "sad tab"). - // This method is automatically called from LoadURL. + // Another part of RenderViewHostManager::Delegate. + // + // Initializes the given renderer if necessary and creates the view ID + // corresponding to this view host. If this method is not called and the + // process is not shared, then the WebContents will act as though the renderer + // is not running (i.e., it will render "sad tab"). This method is + // automatically called from LoadURL. // // If you are attaching to an already-existing RenderView, you should call // InitWithExistingID. - virtual bool CreateRenderView(RenderViewHost* render_view_host); + // + // TODO(brettw) clean this up! This logic seems out of place. This is called + // by the RenderViewHostManager, but also overridden by the DOMUIHost. Any + // logic that has to be here should have a more clear name. + virtual bool CreateRenderViewForRenderManager( + RenderViewHost* render_view_host); private: friend class TestWebContents; @@ -536,52 +524,9 @@ class WebContents : public TabContents, GearsCreateShortcutCallbackFunctor* callback_functor; }; - bool ScrollZoom(int scroll_type); void WheelZoom(int distance); - // Creates a RenderViewHost using render_view_factory_ (or directly, if the - // factory is NULL). - RenderViewHost* CreateRenderViewHost(SiteInstance* instance, - RenderViewHostDelegate* delegate, - int routing_id, - HANDLE modal_dialog_event); - - // Returns whether this tab should transition to a new renderer for - // cross-site URLs. Enabled unless we see the --process-per-tab command line - // switch. Can be overridden in unit tests. - virtual bool ShouldTransitionCrossSite(); - - // Returns an appropriate SiteInstance object for the given NavigationEntry, - // possibly reusing the current SiteInstance. - // Never called if --process-per-tab is used. - SiteInstance* GetSiteInstanceForEntry(const NavigationEntry& entry, - SiteInstance* curr_instance); - - // Prevent the interstitial page from proceeding after we start navigating - // away from it. If |stop_request| is true, abort the pending requests - // immediately, because we are navigating away. - void DisableInterstitialProceed(bool stop_request); - - // Helper method to create a pending RenderViewHost for a cross-site - // navigation. Used in the new process model. - bool CreatePendingRenderView(SiteInstance* instance); - - // Replaces the currently shown render_view_host_ with the RenderViewHost in - // the field pointed to by |new_render_view_host|, and then NULLs the field. - // Callers should only pass pointers to the pending_render_view_host_, - // interstitial_render_view_host_, or original_render_view_host_ fields of - // this object. If |destroy_after|, this method will call - // ScheduleDeferredDestroy on the previous render_view_host_. - void SwapToRenderView(RenderViewHost** new_render_view_host, - bool destroy_after); - - // Destroys the RenderViewHost in the field pointed to by |render_view_host|, - // and then NULLs the field. Callers should only pass pointers to the - // pending_render_view_host_, interstitial_render_view_host_, or - // original_render_view_host_ fields of this object. - void CancelRenderView(RenderViewHost** render_view_host); - // Backend for LoadURL that optionally creates a history entry. The // transition type will be ignored if a history entry is not created. void LoadURL(const std::wstring& url, bool create_history_entry, @@ -626,14 +571,6 @@ class WebContents : public TabContents, // it and contains page plugins. RenderWidgetHostHWND* CreatePageView(RenderViewHost* render_view_host); - // Cleans up after an interstitial page is hidden, including removing the - // interstitial's NavigationEntry. - void InterstitialPageGone(); - - // Convenience method that returns true if the specified RenderViewHost is - // this WebContents' interstitial page RenderViewHost. - bool IsInterstitialRenderViewHost(RenderViewHost* render_view_host) const; - // Navigation helpers -------------------------------------------------------- // // These functions are helpers for Navigate() and DidNavigate(). @@ -673,10 +610,6 @@ class WebContents : public TabContents, RenderViewHost* render_view_host, const ViewHostMsg_FrameNavigate_Params& params); - // Helper method to update the RendererState on a call to [Did]Navigate. - RenderViewHost* UpdateRendererStateNavigate(const NavigationEntry& entry); - void UpdateRendererStateDidNavigate(RenderViewHost* render_view_host); - // Called when navigating the main frame to close all child windows if the // domain is changing. void MaybeCloseChildWindows(const ViewHostMsg_FrameNavigate_Params& params); @@ -725,6 +658,28 @@ class WebContents : public TabContents, void UpdateMaxPageIDIfNecessary(SiteInstance* site_instance, RenderViewHost* rvh); + // RenderViewHostManager::Delegate pass-throughs ----------------------------- + + virtual void BeforeUnloadFiredFromRenderManager( + bool proceed, + bool* proceed_to_fire_unload); + virtual void DidStartLoadingFromRenderManager( + RenderViewHost* render_view_host, int32 page_id) { + DidStartLoading(render_view_host, page_id); + } + virtual void RendererGoneFromRenderManager(RenderViewHost* render_view_host) { + RendererGone(render_view_host); + } + virtual void UpdateRenderViewSizeForRenderManager() { + UpdateRenderViewSize(); + } + virtual void NotifySwappedFromRenderManager() { + NotifySwapped(); + } + virtual NavigationController* GetControllerForRenderManager() { + return controller(); + } + // Profiling ----------------------------------------------------------------- // Logs the commit of the load for profiling purposes. Used by DidNavigate. @@ -746,35 +701,11 @@ class WebContents : public TabContents, // Data ---------------------------------------------------------------------- - // Factory for creating RenderViewHosts. This is useful for unit tests. If - // this is NULL, just create a RenderViewHost directly. - RenderViewHostFactory* render_view_factory_; - - // Our RenderView host. This object is responsible for all communication with - // a child RenderView instance. Note that this can be the page render view - // host or the interstitial RenderViewHost if the RendererState is - // INTERSTITIAL or LEAVING_INTERSTITIAL. - RenderViewHost* render_view_host_; + // Manages creation and swapping of render views. + RenderViewHostManager render_manager_; - // This var holds the original RenderViewHost when the interstitial page is - // showing (the RendererState is INTERSTITIAL or LEAVING_INTERSTITIAL). It - // is NULL otherwise. - RenderViewHost* original_render_view_host_; - - // The RenderViewHost of the interstitial page. This is non NULL when the - // the RendererState is ENTERING_INTERSTITIAL. - RenderViewHost* interstitial_render_view_host_; - - // A RenderViewHost used to load a cross-site page. This remains hidden - // during the PENDING RendererState until it calls DidNavigate. It can also - // exist if an interstitial page is shown. - RenderViewHost* pending_render_view_host_; - - // Indicates if we are in the process of swapping our RenderViewHost. This - // allows us to switch to interstitial pages in different RenderViewHosts. - // In the new process model, this also allows us to render pages from - // different SiteInstances in different processes, all within the same tab. - RendererState renderer_state_; + // For testing, passed to new RenderViewHost managers. + RenderViewHostFactory* render_view_factory_; // Handles print preview and print job for this contents. printing::PrintViewManager printing_; @@ -885,12 +816,6 @@ class WebContents : public TabContents, // Non-null if we're displaying content for a web app. scoped_refptr web_app_; - // See comment above showing_repost_interstitial(). - bool showing_repost_interstitial_; - - // An optional delegate used when an interstitial page is shown that gets - // notified when the state of the interstitial changes. - InterstitialPageDelegate* interstitial_delegate_; DISALLOW_EVIL_CONSTRUCTORS(WebContents); }; diff --git a/chrome/browser/web_contents_unittest.cc b/chrome/browser/web_contents_unittest.cc index a4995fa..3b7450c 100644 --- a/chrome/browser/web_contents_unittest.cc +++ b/chrome/browser/web_contents_unittest.cc @@ -197,18 +197,41 @@ class TestWebContents : public WebContents { transition_cross_site(false) {} // Accessors for interesting fields - RendererState renderer_state() { return renderer_state_; } TestRenderViewHost* rvh() { - return static_cast(render_view_host_); + return static_cast( + render_manager_.render_view_host_); } TestRenderViewHost* pending_rvh() { - return static_cast(pending_render_view_host_); + return static_cast( + render_manager_.pending_render_view_host_); } TestRenderViewHost* interstitial_rvh() { - return static_cast(interstitial_render_view_host_); + return static_cast( + render_manager_.interstitial_render_view_host_); } TestRenderViewHost* original_rvh() { - return static_cast(original_render_view_host_); + return static_cast( + render_manager_.original_render_view_host_); + } + + // State accessors. + bool state_is_normal() const { + return render_manager_.renderer_state_ == RenderViewHostManager::NORMAL; + } + bool state_is_pending() const { + return render_manager_.renderer_state_ == RenderViewHostManager::PENDING; + } + bool state_is_entering_interstitial() const { + return render_manager_.renderer_state_ == + RenderViewHostManager::ENTERING_INTERSTITIAL; + } + bool state_is_interstitial() const { + return render_manager_.renderer_state_ == + RenderViewHostManager::INTERSTITIAL; + } + bool state_is_leaving_interstitial() const { + return render_manager_.renderer_state_ == + RenderViewHostManager::LEAVING_INTERSTITIAL; } // Ensure we create TestRenderViewHosts that don't spawn processes. @@ -242,7 +265,7 @@ class TestWebContents : public WebContents { } // Prevent interaction with views. - bool CreateRenderView(RenderViewHost* render_view_host) { + bool CreateRenderViewForRenderManager(RenderViewHost* render_view_host) { // This will go to a TestRenderViewHost. render_view_host->CreateRenderView(); return true; @@ -323,7 +346,7 @@ TEST_F(WebContentsTest, SimpleNavigation) { // Navigate to URL const GURL url("http://www.google.com"); contents->controller()->LoadURL(url, PageTransition::TYPED); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_TRUE(orig_rvh->is_loading); EXPECT_EQ(instance1, orig_rvh->site_instance()); // Controller's pending entry will have a NULL site instance until we assign @@ -335,7 +358,7 @@ TEST_F(WebContentsTest, SimpleNavigation) { ViewHostMsg_FrameNavigate_Params params; InitNavigateParams(¶ms, 1, url); contents->TestDidNavigate(orig_rvh, params); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(orig_rvh, contents->render_view_host()); EXPECT_EQ(instance1, orig_rvh->site_instance()); // Controller's entry should now have the SiteInstance, or else we won't be @@ -356,12 +379,12 @@ TEST_F(WebContentsTest, ShowInterstitialDontProceed) { // Navigate to URL const GURL url("http://www.google.com"); contents->controller()->LoadURL(url, PageTransition::TYPED); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_TRUE(orig_rvh->is_loading); // Show interstitial contents->ShowInterstitialPage(std::string("Blocked"), NULL); - EXPECT_EQ(WebContents::ENTERING_INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_entering_interstitial()); TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); EXPECT_TRUE(orig_rvh->is_loading); // Still loading in the background EXPECT_TRUE(interstitial_rvh->is_loading); @@ -370,14 +393,14 @@ TEST_F(WebContentsTest, ShowInterstitialDontProceed) { ViewHostMsg_FrameNavigate_Params params; InitNavigateParams(¶ms, 1, url); contents->TestDidNavigate(interstitial_rvh, params); - EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_interstitial()); EXPECT_EQ(interstitial_rvh, contents->render_view_host()); EXPECT_EQ(orig_rvh, contents->original_rvh()); EXPECT_FALSE(interstitial_rvh->is_loading); // Hide interstitial (don't proceed) contents->HideInterstitialPage(false, false); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(orig_rvh, contents->render_view_host()); EXPECT_TRUE(contents->original_rvh() == NULL); EXPECT_TRUE(contents->interstitial_rvh() == NULL); @@ -388,7 +411,7 @@ TEST_F(WebContentsTest, ShowInterstitialProceed) { TestRenderViewHost* orig_rvh = contents->rvh(); // The RenderViewHost's SiteInstance should not yet have a site. - EXPECT_EQ(GURL(""), contents->rvh()->site_instance()->site()); + EXPECT_EQ(GURL(), contents->rvh()->site_instance()->site()); // Navigate to URL const GURL url("http://www.google.com"); @@ -405,17 +428,17 @@ TEST_F(WebContentsTest, ShowInterstitialProceed) { // Ensure this DidNavigate hasn't changed the SiteInstance's site. // Prevents regression for bug 1163298. - EXPECT_EQ(GURL(""), contents->rvh()->site_instance()->site()); + EXPECT_EQ(GURL(), contents->rvh()->site_instance()->site()); // Hide interstitial (proceed and wait) contents->HideInterstitialPage(true, true); - EXPECT_EQ(WebContents::LEAVING_INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_leaving_interstitial()); EXPECT_EQ(interstitial_rvh, contents->render_view_host()); EXPECT_EQ(orig_rvh, contents->original_rvh()); // DidNavigate from the destination page contents->TestDidNavigate(orig_rvh, params); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(orig_rvh, contents->render_view_host()); EXPECT_TRUE(contents->original_rvh() == NULL); EXPECT_TRUE(contents->interstitial_rvh() == NULL); @@ -448,7 +471,7 @@ TEST_F(WebContentsTest, ShowInterstitialThenNavigate) { // While interstitial showing, navigate to a new URL. const GURL url2("http://www.yahoo.com"); contents->controller()->LoadURL(url2, PageTransition::TYPED); - EXPECT_EQ(WebContents::LEAVING_INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_leaving_interstitial()); EXPECT_EQ(interstitial_rvh, contents->render_view_host()); EXPECT_TRUE(orig_rvh->is_loading); EXPECT_FALSE(interstitial_rvh->is_loading); @@ -458,7 +481,7 @@ TEST_F(WebContentsTest, ShowInterstitialThenNavigate) { ViewHostMsg_FrameNavigate_Params params2; InitNavigateParams(¶ms2, 2, url2); contents->TestDidNavigate(orig_rvh, params2); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(orig_rvh, contents->render_view_host()); EXPECT_FALSE(orig_rvh->is_loading); } @@ -476,7 +499,7 @@ TEST_F(WebContentsTest, ShowInterstitialIFrameNavigate) { // Navigate to URL. const GURL url("http://www.google.com"); contents->controller()->LoadURL(url, PageTransition::TYPED); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_TRUE(orig_rvh->is_loading); ViewHostMsg_FrameNavigate_Params params1; InitNavigateParams(¶ms1, 1, url); @@ -485,7 +508,7 @@ TEST_F(WebContentsTest, ShowInterstitialIFrameNavigate) { // Show interstitial (in real world would probably be triggered by a resource // in the page). contents->ShowInterstitialPage(std::string("Blocked"), NULL); - EXPECT_EQ(WebContents::ENTERING_INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_entering_interstitial()); TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); EXPECT_TRUE(interstitial_rvh->is_loading); @@ -499,7 +522,7 @@ TEST_F(WebContentsTest, ShowInterstitialIFrameNavigate) { ViewHostMsg_FrameNavigate_Params params3; InitNavigateParams(¶ms3, 1, url); contents->TestDidNavigate(interstitial_rvh, params3); - EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_interstitial()); EXPECT_EQ(interstitial_rvh, contents->render_view_host()); EXPECT_EQ(orig_rvh, contents->original_rvh()); EXPECT_FALSE(interstitial_rvh->is_loading); @@ -522,7 +545,7 @@ TEST_F(WebContentsTest, VisitInterstitialURLTwice) { const GURL url2("https://www.google.com"); contents->controller()->LoadURL(url2, PageTransition::TYPED); contents->ShowInterstitialPage(std::string("Blocked"), NULL); - EXPECT_EQ(WebContents::ENTERING_INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_entering_interstitial()); int interstitial_delete_counter = 0; TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); interstitial_rvh->set_delete_counter(&interstitial_delete_counter); @@ -531,17 +554,17 @@ TEST_F(WebContentsTest, VisitInterstitialURLTwice) { ViewHostMsg_FrameNavigate_Params params2; InitNavigateParams(¶ms2, 2, url2); contents->TestDidNavigate(interstitial_rvh, params2); - EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_interstitial()); EXPECT_EQ(interstitial_rvh, contents->render_view_host()); // While interstitial showing, navigate to the same URL. contents->controller()->LoadURL(url2, PageTransition::TYPED); - EXPECT_EQ(WebContents::LEAVING_INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_leaving_interstitial()); EXPECT_EQ(interstitial_rvh, contents->render_view_host()); // Interstitial shown a second time in a different RenderViewHost. contents->ShowInterstitialPage(std::string("Blocked"), NULL); - EXPECT_EQ(WebContents::ENTERING_INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_entering_interstitial()); // We expect the original interstitial has been deleted. EXPECT_EQ(interstitial_delete_counter, 1); TestRenderViewHost* interstitial_rvh2 = contents->interstitial_rvh(); @@ -551,27 +574,27 @@ TEST_F(WebContentsTest, VisitInterstitialURLTwice) { ViewHostMsg_FrameNavigate_Params params3; InitNavigateParams(¶ms3, 3, url2); contents->TestDidNavigate(interstitial_rvh2, params3); - EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_interstitial()); EXPECT_EQ(interstitial_rvh2, contents->render_view_host()); // Proceed. In the old process model, we'll still have the same // RenderViewHost. contents->HideInterstitialPage(true, true); - EXPECT_EQ(WebContents::LEAVING_INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_leaving_interstitial()); ViewHostMsg_FrameNavigate_Params params4; InitNavigateParams(¶ms4, 3, url2); contents->TestDidNavigate(orig_rvh, params4); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); // We expect the second interstitial has been deleted. EXPECT_EQ(interstitial_delete_counter, 2); // Now go back. Should take us back to the original page. contents->controller()->GoBack(); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); // DidNavigate from going back. contents->TestDidNavigate(orig_rvh, params1); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(orig_rvh, contents->render_view_host()); EXPECT_TRUE(contents->original_rvh() == NULL); EXPECT_TRUE(contents->interstitial_rvh() == NULL); @@ -593,7 +616,7 @@ TEST_F(WebContentsTest, CrossSiteBoundaries) { InitNavigateParams(¶ms1, 1, url); contents->TestDidNavigate(orig_rvh, params1); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(orig_rvh, contents->render_view_host()); EXPECT_TRUE(contents->original_rvh() == NULL); EXPECT_TRUE(contents->interstitial_rvh() == NULL); @@ -601,7 +624,7 @@ TEST_F(WebContentsTest, CrossSiteBoundaries) { // Navigate to new site const GURL url2("http://www.yahoo.com"); contents->controller()->LoadURL(url2, PageTransition::TYPED); - EXPECT_EQ(WebContents::PENDING, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_pending()); TestRenderViewHost* pending_rvh = contents->pending_rvh(); int pending_rvh_delete_count = 0; pending_rvh->set_delete_counter(&pending_rvh_delete_count); @@ -612,7 +635,7 @@ TEST_F(WebContentsTest, CrossSiteBoundaries) { contents->TestDidNavigate(pending_rvh, params2); SiteInstance* instance2 = contents->site_instance(); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(pending_rvh, contents->render_view_host()); EXPECT_NE(instance1, instance2); EXPECT_TRUE(contents->pending_rvh() == NULL); @@ -624,11 +647,11 @@ TEST_F(WebContentsTest, CrossSiteBoundaries) { // stored in the NavigationEntry, so it should be the same as at the start. contents->controller()->GoBack(); TestRenderViewHost* goback_rvh = contents->pending_rvh(); - EXPECT_EQ(WebContents::PENDING, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_pending()); // DidNavigate from the back action contents->TestDidNavigate(goback_rvh, params1); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(goback_rvh, contents->render_view_host()); EXPECT_EQ(pending_rvh_delete_count, 1); EXPECT_EQ(instance1, contents->site_instance()); @@ -650,7 +673,7 @@ TEST_F(WebContentsTest, CrossSiteBoundariesAfterCrash) { InitNavigateParams(¶ms1, 1, url); contents->TestDidNavigate(orig_rvh, params1); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(orig_rvh, contents->render_view_host()); EXPECT_TRUE(contents->original_rvh() == NULL); EXPECT_TRUE(contents->interstitial_rvh() == NULL); @@ -662,7 +685,7 @@ TEST_F(WebContentsTest, CrossSiteBoundariesAfterCrash) { const GURL url2("http://www.yahoo.com"); contents->controller()->LoadURL(url2, PageTransition::TYPED); TestRenderViewHost* new_rvh = contents->rvh(); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_TRUE(contents->pending_rvh() == NULL); EXPECT_TRUE(contents->original_rvh() == NULL); EXPECT_TRUE(contents->interstitial_rvh() == NULL); @@ -675,7 +698,7 @@ TEST_F(WebContentsTest, CrossSiteBoundariesAfterCrash) { contents->TestDidNavigate(new_rvh, params2); SiteInstance* instance2 = contents->site_instance(); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(new_rvh, contents->render_view_host()); EXPECT_NE(instance1, instance2); EXPECT_TRUE(contents->pending_rvh() == NULL); @@ -697,18 +720,18 @@ TEST_F(WebContentsTest, CrossSiteInterstitialDontProceed) { InitNavigateParams(¶ms1, 1, url); contents->TestDidNavigate(orig_rvh, params1); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(orig_rvh, contents->render_view_host()); // Navigate to new site const GURL url2("https://www.google.com"); contents->controller()->LoadURL(url2, PageTransition::TYPED); - EXPECT_EQ(WebContents::PENDING, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_pending()); TestRenderViewHost* pending_rvh = contents->pending_rvh(); // Show an interstitial contents->ShowInterstitialPage(std::string("Blocked"), NULL); - EXPECT_EQ(WebContents::ENTERING_INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_entering_interstitial()); EXPECT_EQ(orig_rvh, contents->render_view_host()); EXPECT_EQ(pending_rvh, contents->pending_rvh()); TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); @@ -717,7 +740,7 @@ TEST_F(WebContentsTest, CrossSiteInterstitialDontProceed) { ViewHostMsg_FrameNavigate_Params params2; InitNavigateParams(¶ms2, 2, url2); contents->TestDidNavigate(interstitial_rvh, params2); - EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_interstitial()); EXPECT_EQ(interstitial_rvh, contents->render_view_host()); EXPECT_EQ(orig_rvh, contents->original_rvh()); EXPECT_EQ(pending_rvh, contents->pending_rvh()); @@ -725,7 +748,7 @@ TEST_F(WebContentsTest, CrossSiteInterstitialDontProceed) { // Hide interstitial (don't proceed) contents->HideInterstitialPage(false, false); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(orig_rvh, contents->render_view_host()); EXPECT_TRUE(contents->original_rvh() == NULL); EXPECT_TRUE(contents->pending_rvh() == NULL); @@ -763,7 +786,7 @@ TEST_F(WebContentsTest, CrossSiteInterstitialProceed) { ViewHostMsg_FrameNavigate_Params params2; InitNavigateParams(¶ms2, 1, url2); contents->TestDidNavigate(interstitial_rvh, params2); - EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_interstitial()); EXPECT_EQ(interstitial_rvh, contents->render_view_host()); EXPECT_EQ(orig_rvh, contents->original_rvh()); EXPECT_EQ(pending_rvh, contents->pending_rvh()); @@ -771,7 +794,7 @@ TEST_F(WebContentsTest, CrossSiteInterstitialProceed) { // Hide interstitial (proceed and wait) contents->HideInterstitialPage(true, true); - EXPECT_EQ(WebContents::LEAVING_INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_leaving_interstitial()); EXPECT_EQ(interstitial_rvh, contents->render_view_host()); EXPECT_EQ(orig_rvh, contents->original_rvh()); EXPECT_EQ(pending_rvh, contents->pending_rvh()); @@ -782,7 +805,7 @@ TEST_F(WebContentsTest, CrossSiteInterstitialProceed) { InitNavigateParams(¶ms3, 2, url2); contents->TestDidNavigate(pending_rvh, params3); SiteInstance* instance2 = contents->site_instance(); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(pending_rvh, contents->render_view_host()); EXPECT_TRUE(contents->original_rvh() == NULL); EXPECT_TRUE(contents->pending_rvh() == NULL); @@ -797,11 +820,11 @@ TEST_F(WebContentsTest, CrossSiteInterstitialProceed) { // stored in the NavigationEntry, so it should be the same as at the start. contents->controller()->GoBack(); TestRenderViewHost* goback_rvh = contents->pending_rvh(); - EXPECT_EQ(WebContents::PENDING, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_pending()); // DidNavigate from the back action contents->TestDidNavigate(goback_rvh, params1); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(goback_rvh, contents->render_view_host()); EXPECT_EQ(instance1, contents->site_instance()); EXPECT_EQ(pending_rvh_delete_count, 1); // The second page's rvh should die. @@ -829,7 +852,7 @@ TEST_F(WebContentsTest, CrossSiteInterstitialThenNavigate) { ViewHostMsg_FrameNavigate_Params params2; InitNavigateParams(¶ms2, 1, url); contents->TestDidNavigate(interstitial_rvh, params2); - EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_interstitial()); EXPECT_EQ(interstitial_rvh, contents->render_view_host()); EXPECT_EQ(orig_rvh, contents->original_rvh()); EXPECT_TRUE(contents->interstitial_rvh() == NULL); @@ -842,14 +865,14 @@ TEST_F(WebContentsTest, CrossSiteInterstitialThenNavigate) { ASSERT_TRUE(new_rvh != NULL); // Make sure the RVH is not suspended (bug #1236441). EXPECT_FALSE(new_rvh->IsNavigationSuspended()); - EXPECT_EQ(WebContents::LEAVING_INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_leaving_interstitial()); EXPECT_EQ(interstitial_rvh, contents->render_view_host()); // DidNavigate from the new page ViewHostMsg_FrameNavigate_Params params3; InitNavigateParams(¶ms3, 1, url2); contents->TestDidNavigate(new_rvh, params3); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(new_rvh, contents->render_view_host()); EXPECT_TRUE(contents->pending_rvh() == NULL); EXPECT_EQ(orig_rvh_delete_count, 1); @@ -885,7 +908,7 @@ TEST_F(WebContentsTest, CrossSiteInterstitialCrashThenNavigate) { ViewHostMsg_FrameNavigate_Params params2; InitNavigateParams(¶ms2, 1, url2); contents->TestDidNavigate(interstitial_rvh, params2); - EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_interstitial()); EXPECT_EQ(interstitial_rvh, contents->render_view_host()); EXPECT_EQ(orig_rvh, contents->original_rvh()); EXPECT_EQ(pending_rvh, contents->pending_rvh()); @@ -901,7 +924,7 @@ TEST_F(WebContentsTest, CrossSiteInterstitialCrashThenNavigate) { contents->controller()->LoadURL(url3, PageTransition::TYPED); TestRenderViewHost* new_rvh = contents->pending_rvh(); ASSERT_TRUE(new_rvh != NULL); - EXPECT_EQ(WebContents::PENDING, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_pending()); EXPECT_EQ(orig_rvh, contents->render_view_host()); EXPECT_EQ(pending_rvh_delete_count, 1); EXPECT_NE(interstitial_rvh, new_rvh); @@ -912,7 +935,7 @@ TEST_F(WebContentsTest, CrossSiteInterstitialCrashThenNavigate) { ViewHostMsg_FrameNavigate_Params params3; InitNavigateParams(¶ms3, 1, url3); contents->TestDidNavigate(new_rvh, params3); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(new_rvh, contents->render_view_host()); EXPECT_TRUE(contents->pending_rvh() == NULL); EXPECT_EQ(orig_rvh_delete_count, 1); @@ -948,7 +971,7 @@ TEST_F(WebContentsTest, CrossSiteInterstitialCrashesThenNavigate) { ViewHostMsg_FrameNavigate_Params params2; InitNavigateParams(¶ms2, 1, url2); contents->TestDidNavigate(interstitial_rvh, params2); - EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_interstitial()); EXPECT_EQ(interstitial_rvh, contents->render_view_host()); EXPECT_EQ(orig_rvh, contents->original_rvh()); EXPECT_EQ(pending_rvh, contents->pending_rvh()); @@ -965,7 +988,7 @@ TEST_F(WebContentsTest, CrossSiteInterstitialCrashesThenNavigate) { contents->controller()->LoadURL(url3, PageTransition::TYPED); TestRenderViewHost* new_rvh = contents->rvh(); ASSERT_TRUE(new_rvh != NULL); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(orig_rvh_delete_count, 1); EXPECT_EQ(pending_rvh_delete_count, 1); EXPECT_NE(interstitial_rvh, new_rvh); @@ -977,7 +1000,7 @@ TEST_F(WebContentsTest, CrossSiteInterstitialCrashesThenNavigate) { ViewHostMsg_FrameNavigate_Params params3; InitNavigateParams(¶ms3, 1, url3); contents->TestDidNavigate(new_rvh, params3); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(new_rvh, contents->render_view_host()); } @@ -1017,7 +1040,7 @@ TEST_F(WebContentsTest, NavigateTwoTabsCrossSite) { contents2->controller()->LoadURL(url2b, PageTransition::TYPED); TestRenderViewHost* pending_rvh_b = contents2->pending_rvh(); EXPECT_TRUE(pending_rvh_b != NULL); - EXPECT_EQ(WebContents::PENDING, contents2->renderer_state()); + EXPECT_TRUE(contents2->state_is_pending()); // NOTE(creis): We used to be in danger of showing a sad tab page here if the // second tab hadn't navigated somewhere first (bug 1145430). That case is @@ -1058,13 +1081,13 @@ TEST_F(WebContentsTest, CrossSiteComparesAgainstCurrentPage) { // The first RVH in contents2 isn't live yet, so we shortcut the PENDING // state and go straight to NORMAL. TestRenderViewHost* rvh2 = contents2->rvh(); - EXPECT_EQ(WebContents::NORMAL, contents2->renderer_state()); + EXPECT_TRUE(contents2->state_is_normal()); ViewHostMsg_FrameNavigate_Params params2; InitNavigateParams(¶ms2, 2, url2); contents2->TestDidNavigate(rvh2, params2); SiteInstance* instance2 = contents2->site_instance(); EXPECT_NE(instance1, instance2); - EXPECT_EQ(WebContents::NORMAL, contents2->renderer_state()); + EXPECT_TRUE(contents2->state_is_normal()); // Simulate a link click in first tab to second site. Doesn't switch // SiteInstances, because we don't intercept WebKit navigations. @@ -1073,13 +1096,13 @@ TEST_F(WebContentsTest, CrossSiteComparesAgainstCurrentPage) { contents->TestDidNavigate(orig_rvh, params3); SiteInstance* instance3 = contents->site_instance(); EXPECT_EQ(instance1, instance3); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); // Navigate to the new site. Doesn't switch SiteInstancees, because we // compare against the current URL, not the SiteInstance's site. const GURL url3("http://mail.yahoo.com"); contents->controller()->LoadURL(url3, PageTransition::TYPED); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); ViewHostMsg_FrameNavigate_Params params4; InitNavigateParams(¶ms4, 3, url3); contents->TestDidNavigate(orig_rvh, params4); @@ -1102,7 +1125,7 @@ TEST_F(WebContentsTest, CrossSiteUnloadHandlers) { ViewHostMsg_FrameNavigate_Params params1; InitNavigateParams(¶ms1, 1, url); contents->TestDidNavigate(orig_rvh, params1); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(orig_rvh, contents->render_view_host()); // Navigate to new site, but simulate an onbeforeunload denial. @@ -1110,13 +1133,13 @@ TEST_F(WebContentsTest, CrossSiteUnloadHandlers) { orig_rvh->immediate_before_unload = false; contents->controller()->LoadURL(url2, PageTransition::TYPED); orig_rvh->TestOnMsgShouldClose(false); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(orig_rvh, contents->render_view_host()); // Navigate again, but simulate an onbeforeunload approval. contents->controller()->LoadURL(url2, PageTransition::TYPED); orig_rvh->TestOnMsgShouldClose(true); - EXPECT_EQ(WebContents::PENDING, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_pending()); TestRenderViewHost* pending_rvh = contents->pending_rvh(); // We won't hear DidNavigate until the onunload handler has finished running. @@ -1128,7 +1151,7 @@ TEST_F(WebContentsTest, CrossSiteUnloadHandlers) { InitNavigateParams(¶ms2, 1, url2); contents->TestDidNavigate(pending_rvh, params2); SiteInstance* instance2 = contents->site_instance(); - EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(pending_rvh, contents->render_view_host()); EXPECT_NE(instance1, instance2); EXPECT_TRUE(contents->pending_rvh() == NULL); diff --git a/chrome/browser/web_drop_target.cc b/chrome/browser/web_drop_target.cc index f107d95..aa4dd10 100644 --- a/chrome/browser/web_drop_target.cc +++ b/chrome/browser/web_drop_target.cc @@ -116,7 +116,7 @@ DWORD WebDropTarget::OnDragEnter(IDataObject* data_object, // Don't pass messages to the renderer if an interstitial page is showing // because we don't want the interstitial page to navigate. Instead, // pass the messages on to a separate interstitial DropTarget handler. - if (web_contents_->IsShowingInterstitialPage()) + if (web_contents_->showing_interstitial_page()) return interstitial_drop_target_->OnDragEnter(data_object, effect); // TODO(tc): PopulateWebDropData is kind of slow, maybe we can do this in a @@ -144,7 +144,7 @@ DWORD WebDropTarget::OnDragOver(IDataObject* data_object, DWORD key_state, POINT cursor_position, DWORD effect) { - if (web_contents_->IsShowingInterstitialPage()) + if (web_contents_->showing_interstitial_page()) return interstitial_drop_target_->OnDragOver(data_object, effect); POINT client_pt = cursor_position; @@ -160,7 +160,7 @@ DWORD WebDropTarget::OnDragOver(IDataObject* data_object, } void WebDropTarget::OnDragLeave(IDataObject* data_object) { - if (web_contents_->IsShowingInterstitialPage()) { + if (web_contents_->showing_interstitial_page()) { interstitial_drop_target_->OnDragLeave(data_object); } else { web_contents_->DragTargetDragLeave(); @@ -172,7 +172,7 @@ DWORD WebDropTarget::OnDrop(IDataObject* data_object, DWORD key_state, POINT cursor_position, DWORD effect) { - if (web_contents_->IsShowingInterstitialPage()) + if (web_contents_->showing_interstitial_page()) return interstitial_drop_target_->OnDrop(data_object, effect); POINT client_pt = cursor_position; diff --git a/chrome/common/notification_types.h b/chrome/common/notification_types.h index 42c1804..1feae5f 100644 --- a/chrome/common/notification_types.h +++ b/chrome/common/notification_types.h @@ -364,8 +364,9 @@ enum NotificationType { NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, // This is sent when an interstitial page showing in a WebContents is closed - // (as the result of a navigation to another page). The source is the - // WebContents the interstitial page is in. + // (as the result of a navigation to another page). The source is the + // NavigationController associated with the tab. + // // Note that you should not initiate a navigation as part of the processing of // this notification, since this notification may be triggered as part of the // destruction of the tab contents (the navigation controller would reuse @@ -401,9 +402,9 @@ enum NotificationType { NOTIFY_PREF_CHANGED, // This is sent to notify that the RenderViewHost displayed in a WebContents - // has changed. Source is the WebContents for which the change happened, - // details is the previous RenderViewHost (can be NULL when the first - // RenderViewHost is set). + // has changed. Source is the NavigationController for which the change + // happened, and details is RenderViewHostSwitchedDetails + // (see render_view_host_manager.h). NOTIFY_RENDER_VIEW_HOST_CHANGED, // This notification is sent when a TabContents is being hidden, e.g. due to -- cgit v1.1