diff options
author | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-12-17 19:57:24 +0000 |
---|---|---|
committer | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-12-17 19:57:24 +0000 |
commit | 7cef64b495701e8ff17b2fbb6e632aa34eee450f (patch) | |
tree | 8af439034838b28edd0223010a1f6aad4b2f61ce /chrome/browser | |
parent | a16d22388edff186095e37fecd5537beae88c7c9 (diff) | |
download | chromium_src-7cef64b495701e8ff17b2fbb6e632aa34eee450f.zip chromium_src-7cef64b495701e8ff17b2fbb6e632aa34eee450f.tar.gz chromium_src-7cef64b495701e8ff17b2fbb6e632aa34eee450f.tar.bz2 |
Reverting 7149.
Review URL: http://codereview.chromium.org/15004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@7153 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
35 files changed, 1606 insertions, 1107 deletions
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index 3a36b52..2f2b6b5 100644 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -588,7 +588,7 @@ class DocumentPrintedNotificationObserver : public NotificationObserver { class AutomationInterstitialPage : public InterstitialPage { public: - AutomationInterstitialPage(WebContents* tab, + AutomationInterstitialPage(TabContents* tab, const GURL& url, const std::string& contents) : InterstitialPage(tab, true, url), @@ -1990,7 +1990,7 @@ void AutomationProvider::ShowInterstitialPage(const IPC::Message& message, new AutomationInterstitialPage(web_contents, GURL("about:interstitial"), html_text); - interstitial->Show(); + web_contents->ShowInterstitialPage(interstitial); return; } } @@ -2001,8 +2001,8 @@ void AutomationProvider::ShowInterstitialPage(const IPC::Message& message, void AutomationProvider::HideInterstitialPage(const IPC::Message& message, int tab_handle) { WebContents* web_contents = GetWebContentsForHandle(tab_handle, NULL); - if (web_contents && web_contents->interstitial_page()) { - web_contents->interstitial_page()->DontProceed(); + if (web_contents) { + web_contents->HideInterstitialPage(false, false); Send(new AutomationMsg_HideInterstitialPageResponse(message.routing_id(), true)); return; @@ -2161,7 +2161,7 @@ void AutomationProvider::ActionOnSSLBlockingPage(const IPC::Message& message, if (entry->page_type() == NavigationEntry::INTERSTITIAL_PAGE) { TabContents* tab_contents = tab->GetTabContents(TAB_CONTENTS_WEB); InterstitialPage* ssl_blocking_page = - InterstitialPage::GetInterstitialPage(tab_contents->AsWebContents()); + InterstitialPage::GetInterstitialPage(tab_contents); if (ssl_blocking_page) { if (proceed) { AddNavigationStatusListener(tab, diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc index 862398d..87b3e9e 100644 --- a/chrome/browser/browser.cc +++ b/chrome/browser/browser.cc @@ -558,11 +558,8 @@ void Browser::GoBack() { // If we are showing an interstitial, just hide it. TabContents* current_tab = GetSelectedTabContents(); WebContents* web_contents = current_tab->AsWebContents(); - if (web_contents && web_contents->interstitial_page()) { - // The GoBack() case is a special case when an interstitial is shown because - // the "previous" page is still available, just hidden by the interstitial. - // We treat the back as a "Don't proceed", this hides the interstitial and - // reveals the previous page. + if (web_contents && web_contents->showing_interstitial_page()) { + // Pressing back on an interstitial page means "don't proceed". web_contents->interstitial_page()->DontProceed(); return; } diff --git a/chrome/browser/download/download_file.cc b/chrome/browser/download/download_file.cc index 2a5e612..f921661 100644 --- a/chrome/browser/download/download_file.cc +++ b/chrome/browser/download/download_file.cc @@ -16,8 +16,8 @@ #include "chrome/browser/download/download_manager.h" #include "chrome/browser/profile.h" #include "chrome/browser/resource_dispatcher_host.h" +#include "chrome/browser/tab_contents.h" #include "chrome/browser/tab_util.h" -#include "chrome/browser/web_contents.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/stl_util-inl.h" #include "chrome/common/win_util.h" @@ -458,9 +458,9 @@ void DownloadFileManager::RemoveDownload(int id, DownloadManager* manager) { // static DownloadManager* DownloadFileManager::DownloadManagerFromRenderIds( int render_process_id, int render_view_id) { - WebContents* contents = tab_util::GetWebContentsByID(render_process_id, + TabContents* contents = tab_util::GetTabContentsByID(render_process_id, render_view_id); - if (contents) { + if (contents && contents->type() == TAB_CONTENTS_WEB) { Profile* profile = contents->profile(); if (profile) return profile->GetDownloadManager(); diff --git a/chrome/browser/download/download_manager.cc b/chrome/browser/download/download_manager.cc index adf62d3..5328807 100644 --- a/chrome/browser/download/download_manager.cc +++ b/chrome/browser/download/download_manager.cc @@ -618,7 +618,7 @@ void DownloadManager::OnPathExistenceAvailable(DownloadCreateInfo* info) { if (!select_file_dialog_.get()) select_file_dialog_ = SelectFileDialog::Create(this); - WebContents* contents = tab_util::GetWebContentsByID( + TabContents* contents = tab_util::GetTabContentsByID( info->render_process_id, info->render_view_id); std::wstring filter = win_util::GetFileFilterFromPath(info->suggested_path); HWND owning_hwnd = @@ -1308,7 +1308,7 @@ void DownloadManager::OnCreateDownloadEntryComplete(DownloadCreateInfo info, // this start completion event. If it does, tell the origin WebContents to // display its download shelf. TabContents* contents = - tab_util::GetWebContentsByID(info.render_process_id, info.render_view_id); + tab_util::GetTabContentsByID(info.render_process_id, info.render_view_id); // If the contents no longer exists or is no longer active, we start the // download in the last active browser. This is not ideal but better than diff --git a/chrome/browser/download/download_request_manager.cc b/chrome/browser/download/download_request_manager.cc index ac4cdb5..6869d6e 100644 --- a/chrome/browser/download/download_request_manager.cc +++ b/chrome/browser/download/download_request_manager.cc @@ -9,9 +9,9 @@ #include "chrome/browser/navigation_controller.h" #include "chrome/browser/navigation_entry.h" #include "chrome/browser/constrained_window.h" +#include "chrome/browser/tab_contents.h" #include "chrome/browser/tab_contents_delegate.h" #include "chrome/browser/tab_util.h" -#include "chrome/browser/web_contents.h" #include "chrome/common/l10n_util.h" #include "chrome/common/notification_registrar.h" #include "chrome/common/notification_service.h" @@ -409,8 +409,8 @@ void DownloadRequestManager::CanDownload(int render_process_host_id, Callback* callback) { DCHECK(!ui_loop_ || MessageLoop::current() == ui_loop_); - WebContents* originating_tab = - tab_util::GetWebContentsByID(render_process_host_id, render_view_id); + TabContents* originating_tab = + tab_util::GetTabContentsByID(render_process_host_id, render_view_id); if (!originating_tab) { // The tab was closed, don't allow the download. ScheduleNotification(callback, false); diff --git a/chrome/browser/download/save_file_manager.cc b/chrome/browser/download/save_file_manager.cc index 631b55d..940e878 100644 --- a/chrome/browser/download/save_file_manager.cc +++ b/chrome/browser/download/save_file_manager.cc @@ -215,10 +215,14 @@ void SaveFileManager::RemoveSaveFile(int save_id, const std::wstring& save_url, // only on the UI thread. SavePackage* SaveFileManager::GetSavePackageFromRenderIds( int render_process_id, int render_view_id) { - WebContents* contents = tab_util::GetWebContentsByID(render_process_id, + TabContents* contents = tab_util::GetTabContentsByID(render_process_id, render_view_id); - if (contents) - return contents->save_package(); + if (contents && contents->type() == TAB_CONTENTS_WEB) { + // Convert const pointer of WebContents to pointer of WebContents. + const WebContents* web_contents = contents->AsWebContents(); + if (web_contents) + return web_contents->save_package(); + } return NULL; } diff --git a/chrome/browser/interstitial_page.cc b/chrome/browser/interstitial_page.cc index 6fee67f..5cbc37e 100644 --- a/chrome/browser/interstitial_page.cc +++ b/chrome/browser/interstitial_page.cc @@ -5,237 +5,98 @@ #include "chrome/browser/interstitial_page.h" #include "chrome/browser/browser.h" -#include "chrome/browser/browser_list.h" #include "chrome/browser/browser_resources.h" #include "chrome/browser/dom_operation_notification_details.h" #include "chrome/browser/navigation_controller.h" #include "chrome/browser/navigation_entry.h" -#include "chrome/browser/render_widget_host_view_win.h" +#include "chrome/browser/tab_contents.h" #include "chrome/browser/web_contents.h" -#include "chrome/browser/web_contents_view_win.h" -#include "chrome/views/window.h" -#include "chrome/views/window_delegate.h" -#include "net/base/escape.h" // static InterstitialPage::InterstitialPageMap* InterstitialPage::tab_to_interstitial_page_ = NULL; -InterstitialPage::InterstitialPage(WebContents* tab, - bool new_navigation, +InterstitialPage::InterstitialPage(TabContents* tab, + bool create_navigation_entry, const GURL& url) : tab_(tab), url_(url), - action_taken_(false), - enabled_(true), - new_navigation_(new_navigation), - render_view_host_(NULL), - should_revert_tab_title_(false) { + delegate_has_been_notified_(false), + create_navigation_entry_(create_navigation_entry) { InitInterstitialPageMap(); - // It would be inconsistent to create an interstitial with no new navigation - // (which is the case when the interstitial was triggered by a sub-resource on - // a page) when we have a pending entry (in the process of loading a new top - // frame). - DCHECK(new_navigation || !tab->controller()->GetPendingEntry()); + + // If there's already an interstitial in this tab, then we're about to + // replace it. We should be ok with just deleting the previous + // InterstitialPage (not hiding it first), since we're about to be shown. + InterstitialPageMap::const_iterator iter = + tab_to_interstitial_page_->find(tab_); + if (iter != tab_to_interstitial_page_->end()) { + // Deleting the InterstitialPage will also remove it from the map. + delete iter->second; + } + (*tab_to_interstitial_page_)[tab_] = this; + + // Register for DOM operations, this is how the page notifies us of the user + // selection. + notification_registrar_.Add(this, NOTIFY_DOM_OPERATION_RESPONSE, + Source<TabContents>(tab_)); } InterstitialPage::~InterstitialPage() { InterstitialPageMap::iterator iter = tab_to_interstitial_page_->find(tab_); DCHECK(iter != tab_to_interstitial_page_->end()); tab_to_interstitial_page_->erase(iter); - DCHECK(!render_view_host_); } void InterstitialPage::Show() { - // If an interstitial is already showing, close it before showing the new one. - if (tab_->interstitial_page()) - tab_->interstitial_page()->DontProceed(); + DCHECK(tab_->type() == TAB_CONTENTS_WEB); + WebContents* tab = tab_->AsWebContents(); - // Update the tab_to_interstitial_page_ map. - InterstitialPageMap::const_iterator iter = - tab_to_interstitial_page_->find(tab_); - DCHECK(iter == tab_to_interstitial_page_->end()); - (*tab_to_interstitial_page_)[tab_] = this; + if (create_navigation_entry_) { + NavigationEntry* entry = new NavigationEntry(TAB_CONTENTS_WEB); + entry->set_url(url_); + entry->set_display_url(url_); + entry->set_page_type(NavigationEntry::INTERSTITIAL_PAGE); - if (new_navigation_) {
- NavigationEntry* entry = new NavigationEntry(TAB_CONTENTS_WEB);
- entry->set_url(url_);
- entry->set_display_url(url_);
- entry->set_page_type(NavigationEntry::INTERSTITIAL_PAGE);
-
- // Give sub-classes a chance to set some states on the navigation entry.
- UpdateEntry(entry);
-
- tab_->controller()->AddTransientEntry(entry);
- }
- - DCHECK(!render_view_host_); - render_view_host_ = CreateRenderViewHost(); - - std::string data_url = "data:text/html;charset=utf-8," + - EscapePath(GetHTMLContents()); - render_view_host_->NavigateToURL(GURL(data_url)); - - notification_registrar_.Add(this, NOTIFY_TAB_CONTENTS_DESTROYED, - Source<TabContents>(tab_)); - notification_registrar_.Add(this, NOTIFY_NAV_ENTRY_COMMITTED, - Source<NavigationController>(tab_->controller())); - notification_registrar_.Add(this, NOTIFY_NAV_ENTRY_PENDING, - Source<NavigationController>(tab_->controller())); -} + // Give sub-classes a chance to set some states on the navigation entry. + UpdateEntry(entry); -void InterstitialPage::Hide() { - render_view_host_->Shutdown(); - render_view_host_ = NULL; - if (tab_->interstitial_page()) - tab_->remove_interstitial_page(); - // Let's revert to the original title if necessary. - NavigationEntry* entry = tab_->controller()->GetActiveEntry(); - if (!new_navigation_ && should_revert_tab_title_) { - entry->set_title(original_tab_title_); - tab_->NotifyNavigationStateChanged(TabContents::INVALIDATE_TITLE); + tab_->controller()->AddTransientEntry(entry); } - delete this; + + tab->ShowInterstitialPage(this); } void InterstitialPage::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { - if (type == NOTIFY_NAV_ENTRY_PENDING) { - // We are navigating away from the interstitial. Make sure clicking on the - // interstitial will have no effect. - Disable(); - return; - } - DCHECK(type == NOTIFY_TAB_CONTENTS_DESTROYED || - type == NOTIFY_NAV_ENTRY_COMMITTED); - if (!action_taken_) { - // We are navigating away from the interstitial or closing a tab with an - // interstitial. Default to DontProceed(). We don't just call Hide as - // subclasses will almost certainly override DontProceed to do some work - // (ex: close pending connections). - DontProceed(); - } else { - // User decided to proceed and either the navigation was committed or the - // tab was closed before that. - Hide(); - // WARNING: we are now deleted! - } + DCHECK(type == NOTIFY_DOM_OPERATION_RESPONSE); + std::string json = Details<DomOperationNotificationDetails>(details)->json(); + CommandReceived(json); } -RenderViewHost* InterstitialPage::CreateRenderViewHost() { - RenderViewHost* render_view_host = new RenderViewHost( - SiteInstance::CreateSiteInstance(tab()->profile()), - this, MSG_ROUTING_NONE, NULL); - RenderWidgetHostViewWin* view = - new RenderWidgetHostViewWin(render_view_host); - render_view_host->set_view(view); - view->Create(tab_->GetContentHWND()); - view->set_parent_hwnd(tab_->GetContentHWND()); - WebContentsViewWin* web_contents_view = - static_cast<WebContentsViewWin*>(tab_->view()); - render_view_host->CreateRenderView(); - // SetSize must be called after CreateRenderView or the HWND won't show. - view->SetSize(web_contents_view->GetContainerSize()); - - render_view_host->AllowDomAutomationBindings(); - return render_view_host; -} +void InterstitialPage::InterstitialClosed() {
+ delete this;
+}
void InterstitialPage::Proceed() { - DCHECK(!action_taken_); - Disable(); - action_taken_ = true; - - // Resumes the throbber. - tab_->SetIsLoading(true, NULL); - - // No need to hide if we are a new navigation, we'll get hidden when the - // navigation is committed. - if (!new_navigation_) { - Hide(); - // WARNING: we are now deleted! - } + DCHECK(tab_->type() == TAB_CONTENTS_WEB); + tab_->AsWebContents()->HideInterstitialPage(true, true); } void InterstitialPage::DontProceed() { - DCHECK(!action_taken_); - Disable(); - action_taken_ = true; - - if (new_navigation_) { + if (create_navigation_entry_) { // Since no navigation happens we have to discard the transient entry // explicitely. Note that by calling DiscardNonCommittedEntries() we also // discard the pending entry, which is what we want, since the navigation is // cancelled. tab_->controller()->DiscardNonCommittedEntries(); } + tab_->AsWebContents()->HideInterstitialPage(false, false); - Hide(); // WARNING: we are now deleted! } -void InterstitialPage::SetSize(const gfx::Size& size) { - render_view_host_->view()->SetSize(size); -} - -Profile* InterstitialPage::GetProfile() const { - return tab_->profile(); -} - -void InterstitialPage::DidNavigate( - RenderViewHost* render_view_host, - const ViewHostMsg_FrameNavigate_Params& params) { - // A fast user could have navigated away from the page that triggered the - // interstitial while the interstitial was loading, that would have disabled - // us. In that case we can dismiss ourselves. - if (!enabled_){ - DontProceed(); - return; - } - - // The RenderViewHost has loaded its contents, we can show it now. - render_view_host_->view()->Show(); - tab_->set_interstitial_page(this); - - // Notify the tab we are not loading so the throbber is stopped. It also - // causes a NOTIFY_LOAD_STOP notification, that the AutomationProvider (used - // by the UI tests) expects to consider a navigation as complete. Without this, - // navigating in a UI test to a URL that triggers an interstitial would hang. - tab_->SetIsLoading(false, NULL); -} - -void InterstitialPage::RendererGone(RenderViewHost* render_view_host) { - // Our renderer died. This should not happen in normal cases. - // Just dismiss the interstitial. - DontProceed(); -} - -void InterstitialPage::DomOperationResponse(const std::string& json_string, - int automation_id) { - if (enabled_) - CommandReceived(json_string); -} - -void InterstitialPage::UpdateTitle(RenderViewHost* render_view_host, - int32 page_id, - const std::wstring& title) { - DCHECK(render_view_host == render_view_host_); - NavigationEntry* entry = tab_->controller()->GetActiveEntry(); - // If this interstitial is shown on an existing navigation entry, we'll need - // to remember its title so we can revert to it when hidden. - if (!new_navigation_ && !should_revert_tab_title_) { - original_tab_title_ = entry->title(); - should_revert_tab_title_ = true; - } - entry->set_title(title); - tab_->NotifyNavigationStateChanged(TabContents::INVALIDATE_TITLE); -} - -void InterstitialPage::Disable() { - enabled_ = false; -} - // static void InterstitialPage::InitInterstitialPageMap() { if (!tab_to_interstitial_page_) @@ -244,10 +105,10 @@ void InterstitialPage::InitInterstitialPageMap() { // static InterstitialPage* InterstitialPage::GetInterstitialPage( - WebContents* web_contents) { + TabContents* tab_contents) { InitInterstitialPageMap(); InterstitialPageMap::const_iterator iter = - tab_to_interstitial_page_->find(web_contents); + tab_to_interstitial_page_->find(tab_contents); if (iter == tab_to_interstitial_page_->end()) return NULL; diff --git a/chrome/browser/interstitial_page.h b/chrome/browser/interstitial_page.h index ab8967f..ce17818 100644 --- a/chrome/browser/interstitial_page.h +++ b/chrome/browser/interstitial_page.h @@ -7,16 +7,16 @@ #include <string> -#include "chrome/browser/web_contents_view_win.h" #include "chrome/common/notification_registrar.h" +#include "chrome/common/notification_service.h" #include "googleurl/src/gurl.h" class NavigationEntry; -class WebContents; +class TabContents; // This class is a base class for interstitial pages, pages that show some // informative message asking for user validation before reaching the target -// page. (Navigating to a page served over bad HTTPS or a page containing +// page. (Navigating to a page served over bad HTTPS or a page contining // malware are typical cases where an interstitial is required.) // // If specified in its constructor, this class creates a navigation entry so @@ -26,27 +26,27 @@ class WebContents; // through a navigation, the WebContents closing them or the tab containing them // being closed. -class InterstitialPage : public NotificationObserver, - public RenderViewHostDelegate { +class InterstitialPage : public NotificationObserver { public: - // Creates an interstitial page to show in |tab|. |new_navigation| should be - // set to true when the interstitial is caused by loading a new page, in which - // case a temporary navigation entry is created with the URL |url| and + // Creates an interstitial page to show in |tab|. If |create_navigation_entry| + // is true, a temporary navigation entry is created with the URL |url| and // added to the navigation controller (so the interstitial page appears as a - // new navigation entry). |new_navigation| should be false when the - // interstitial was triggered by a loading a sub-resource in a page. - InterstitialPage(WebContents* tab, bool new_navigation, const GURL& url); + // new navigation entry). + InterstitialPage(TabContents* tab, + bool create_navigation_entry, + const GURL& url); virtual ~InterstitialPage(); // Shows the interstitial page in the tab. - virtual void Show(); + void Show(); - // Hides the interstitial page. Warning: this deletes the InterstitialPage. - void Hide(); + // Invoked by the tab showing the interstitial to notify that the interstitial
+ // page was closed.
+ virtual void InterstitialClosed();
// Retrieves the InterstitialPage if any associated with the specified // |tab_contents| (used by ui tests). - static InterstitialPage* GetInterstitialPage(WebContents* web_contents); + static InterstitialPage* GetInterstitialPage(TabContents* tab_contents); // Sub-classes should return the HTML that should be displayed in the page. virtual std::string GetHTMLContents() { return std::string(); } @@ -57,34 +57,7 @@ class InterstitialPage : public NotificationObserver, // Warning: 'this' has been deleted when this method returns. virtual void DontProceed(); - // Sub-classes should call this method when the user has chosen to proceed to - // the target URL. - // Warning: 'this' has been deleted when this method returns. - virtual void Proceed(); - - // Sizes the RenderViewHost showing the actual interstitial page contents. - void SetSize(const gfx::Size& size); - protected: - // NotificationObserver method: - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - - // RenderViewHostDelegate implementation: - virtual Profile* GetProfile() const; - virtual WebPreferences GetWebkitPrefs() { - return WebPreferences(); - } - virtual void DidNavigate(RenderViewHost* render_view_host, - const ViewHostMsg_FrameNavigate_Params& params); - virtual void RendererGone(RenderViewHost* render_view_host); - virtual void DomOperationResponse(const std::string& json_string, - int automation_id); - virtual void UpdateTitle(RenderViewHost* render_view_host, - int32 page_id, - const std::wstring& title); - // Invoked when the page sent a command through DOMAutomation. virtual void CommandReceived(const std::string& command) { } @@ -95,61 +68,47 @@ class InterstitialPage : public NotificationObserver, // |create_navigation_entry| set to true. virtual void UpdateEntry(NavigationEntry* entry) { } - WebContents* tab() const { return tab_; } - const GURL& url() const { return url_; } - RenderViewHost* render_view_host() const { return render_view_host_; } + // Sub-classes should call this method when the user has chosen to proceed to + // the target URL. + // Warning: 'this' has been deleted when this method returns. + virtual void Proceed(); - // Creates and shows the RenderViewHost containing the interstitial content. - // Overriden in unit tests. - virtual RenderViewHost* CreateRenderViewHost(); + TabContents* tab() const { return tab_; } + const GURL& url() const { return url_; } private: // AutomationProvider needs access to Proceed and DontProceed to simulate // user actions. friend class AutomationProvider; + // NotificationObserver method. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + // Initializes tab_to_interstitial_page_ in a thread-safe manner. // Should be called before accessing tab_to_interstitial_page_. static void InitInterstitialPageMap(); - // Disable the interstitial: - // - if it is not yet showing, then it won't be shown. - // - any command sent by the RenderViewHost will be ignored. - void Disable(); + // A flag to indicate if we've notified |delegate_| of the user's decision. + bool delegate_has_been_notified_; // The tab in which we are displayed. - WebContents* tab_; + TabContents* tab_; // The URL that is shown when the interstitial is showing. GURL url_; - // Whether this interstitial is shown as a result of a new navigation (in - // which case a transient navigation entry is created). - bool new_navigation_; - - // Whether this interstitial is enabled. See Disable() for more info. - bool enabled_; - - // Whether the Proceed or DontProceed have been called yet. - bool action_taken_; - - // Notification magic.
- NotificationRegistrar notification_registrar_;
- - // The RenderViewHost displaying the interstitial contents. - RenderViewHost* render_view_host_; - - // Whether or not we should change the title of the tab when hidden (to revert - // it to its original value). - bool should_revert_tab_title_; + // Whether a transient navigation entry should be created when the page is + // shown. + bool create_navigation_entry_; - // The original title of the tab that should be reverted to when the - // interstitial is hidden. - std::wstring original_tab_title_; + // Notification magic. + NotificationRegistrar notification_registrar_; // We keep a map of the various blocking pages shown as the UI tests need to // be able to retrieve them. - typedef std::map<WebContents*,InterstitialPage*> InterstitialPageMap; + typedef std::map<TabContents*,InterstitialPage*> InterstitialPageMap; static InterstitialPageMap* tab_to_interstitial_page_; DISALLOW_COPY_AND_ASSIGN(InterstitialPage); diff --git a/chrome/browser/login_prompt.cc b/chrome/browser/login_prompt.cc index db9d775..1e95b0c 100644 --- a/chrome/browser/login_prompt.cc +++ b/chrome/browser/login_prompt.cc @@ -84,11 +84,11 @@ class LoginHandlerImpl : public LoginHandler, SendNotifications(); } - // Returns the WebContents that needs authentication. - WebContents* GetWebContentsForLogin() { + // Returns the TabContents that needs authentication. + TabContents* GetTabContentsForLogin() { DCHECK(MessageLoop::current() == ui_loop_); - return tab_util::GetWebContentsByID(render_process_host_id_, + return tab_util::GetTabContentsByID(render_process_host_id_, tab_contents_id_); } @@ -238,7 +238,7 @@ class LoginHandlerImpl : public LoginHandler, DCHECK(MessageLoop::current() == ui_loop_); NotificationService* service = NotificationService::current(); - WebContents* requesting_contents = GetWebContentsForLogin(); + TabContents* requesting_contents = GetTabContentsForLogin(); if (!requesting_contents) return; @@ -311,7 +311,7 @@ class LoginDialogTask : public Task { } void Run() { - WebContents* parent_contents = handler_->GetWebContentsForLogin(); + TabContents* parent_contents = handler_->GetTabContentsForLogin(); if (!parent_contents) { // The request was probably cancelled. return; diff --git a/chrome/browser/navigation_controller.cc b/chrome/browser/navigation_controller.cc index 35aec38..2b3f96e 100644 --- a/chrome/browser/navigation_controller.cc +++ b/chrome/browser/navigation_controller.cc @@ -212,10 +212,13 @@ void NavigationController::Reload(bool check_for_repost) { DiscardNonCommittedEntriesInternal(); int current_index = GetCurrentEntryIndex(); if (check_for_repost_ && check_for_repost && current_index != -1 && - GetEntryAtIndex(current_index)->has_post_data()) { - // The user is asking to reload a page with POST data. Prompt to make sure - // they really want to do this. If they do, RepostFormWarningDialog calls us - // back with ReloadDontCheckForRepost. + GetEntryAtIndex(current_index)->has_post_data() && + active_contents_->AsWebContents() && + !active_contents_->AsWebContents()->showing_repost_interstitial()) { + // The user is asking to reload a page with POST data and we're not showing + // the POST interstitial. Prompt to make sure they really want to do this. + // If they do, RepostFormWarningDialog calls us back with + // ReloadDontCheckForRepost. active_contents_->Activate(); RepostFormWarningDialog::RunRepostFormWarningDialog(this); } else { @@ -535,6 +538,7 @@ const SkBitmap& NavigationController::GetLazyFavIcon() const { bool NavigationController::RendererDidNavigate( const ViewHostMsg_FrameNavigate_Params& params, + bool is_interstitial, LoadCommittedDetails* details) { // Save the previous state before we clobber it. if (GetLastCommittedEntry()) { @@ -609,6 +613,7 @@ bool NavigationController::RendererDidNavigate( details->entry = GetActiveEntry(); details->is_in_page = IsURLInPageNavigation(params.url); details->is_main_frame = PageTransition::IsMainFrame(params.transition); + details->is_interstitial = is_interstitial; details->serialized_security_info = params.security_info; details->is_content_filtered = params.is_content_filtered; NotifyNavigationEntryCommitted(details); diff --git a/chrome/browser/navigation_controller.h b/chrome/browser/navigation_controller.h index ce5a20f..bd1d2aa 100644 --- a/chrome/browser/navigation_controller.h +++ b/chrome/browser/navigation_controller.h @@ -51,7 +51,8 @@ class NavigationController { : entry(NULL), is_auto(false), is_in_page(false), - is_main_frame(true) { + is_main_frame(true), + is_interstitial(false) { } // The committed entry. This will be the active entry in the controller. @@ -84,6 +85,10 @@ class NavigationController { // sub-frame. bool is_main_frame; + // True when this navigation is for an interstitial page. Many consumers + // won't care about interstitial loads. + bool is_interstitial; + // Whether the content of this frame has been altered/blocked because it was // unsafe. bool is_content_filtered; @@ -320,6 +325,7 @@ class NavigationController { // In the case that nothing has changed, the details structure is undefined // and it will return false. bool RendererDidNavigate(const ViewHostMsg_FrameNavigate_Params& params, + bool is_interstitial, LoadCommittedDetails* details); // Notifies us that we just became active. This is used by the TabContents diff --git a/chrome/browser/navigation_controller_unittest.cc b/chrome/browser/navigation_controller_unittest.cc index 26fd338..08b43a0 100644 --- a/chrome/browser/navigation_controller_unittest.cc +++ b/chrome/browser/navigation_controller_unittest.cc @@ -794,7 +794,8 @@ TEST_F(NavigationControllerTest, NewSubframe) { params.is_post = false; NavigationController::LoadCommittedDetails details; - EXPECT_TRUE(contents->controller()->RendererDidNavigate(params, &details)); + EXPECT_TRUE(contents->controller()->RendererDidNavigate(params, false, + &details)); EXPECT_TRUE(notifications.Check1AndReset(NOTIFY_NAV_ENTRY_COMMITTED)); EXPECT_EQ(url1, details.previous_url); EXPECT_FALSE(details.is_auto); @@ -828,7 +829,8 @@ TEST_F(NavigationControllerTest, SubframeOnEmptyPage) { params.is_post = false; NavigationController::LoadCommittedDetails details; - EXPECT_FALSE(contents->controller()->RendererDidNavigate(params, &details)); + EXPECT_FALSE(contents->controller()->RendererDidNavigate(params, false, + &details)); EXPECT_EQ(0, notifications.size()); } @@ -853,7 +855,8 @@ TEST_F(NavigationControllerTest, AutoSubframe) { // Navigating should do nothing. NavigationController::LoadCommittedDetails details; - EXPECT_FALSE(contents->controller()->RendererDidNavigate(params, &details)); + EXPECT_FALSE(contents->controller()->RendererDidNavigate(params, false, + &details)); EXPECT_EQ(0, notifications.size()); // There should still be only one entry. @@ -882,7 +885,8 @@ TEST_F(NavigationControllerTest, BackSubframe) { // This should generate a new entry. NavigationController::LoadCommittedDetails details; - EXPECT_TRUE(contents->controller()->RendererDidNavigate(params, &details)); + EXPECT_TRUE(contents->controller()->RendererDidNavigate(params, false, + &details)); EXPECT_TRUE(notifications.Check1AndReset(NOTIFY_NAV_ENTRY_COMMITTED)); EXPECT_EQ(2, contents->controller()->GetEntryCount()); @@ -890,7 +894,8 @@ TEST_F(NavigationControllerTest, BackSubframe) { const GURL url3(scheme1() + ":foo3"); params.page_id = 2; params.url = url3; - EXPECT_TRUE(contents->controller()->RendererDidNavigate(params, &details)); + EXPECT_TRUE(contents->controller()->RendererDidNavigate(params, false, + &details)); EXPECT_TRUE(notifications.Check1AndReset(NOTIFY_NAV_ENTRY_COMMITTED)); EXPECT_EQ(3, contents->controller()->GetEntryCount()); EXPECT_EQ(2, contents->controller()->GetCurrentEntryIndex()); @@ -899,7 +904,8 @@ TEST_F(NavigationControllerTest, BackSubframe) { contents->controller()->GoBack(); params.url = url2; params.page_id = 1; - EXPECT_TRUE(contents->controller()->RendererDidNavigate(params, &details)); + EXPECT_TRUE(contents->controller()->RendererDidNavigate(params, false, + &details)); EXPECT_TRUE(notifications.Check1AndReset(NOTIFY_NAV_ENTRY_COMMITTED)); EXPECT_EQ(3, contents->controller()->GetEntryCount()); EXPECT_EQ(1, contents->controller()->GetCurrentEntryIndex()); @@ -908,7 +914,8 @@ TEST_F(NavigationControllerTest, BackSubframe) { contents->controller()->GoBack(); params.url = url1; params.page_id = 0; - EXPECT_TRUE(contents->controller()->RendererDidNavigate(params, &details)); + EXPECT_TRUE(contents->controller()->RendererDidNavigate(params, false, + &details)); EXPECT_TRUE(notifications.Check1AndReset(NOTIFY_NAV_ENTRY_COMMITTED)); EXPECT_EQ(3, contents->controller()->GetEntryCount()); EXPECT_EQ(0, contents->controller()->GetCurrentEntryIndex()); @@ -959,7 +966,8 @@ TEST_F(NavigationControllerTest, InPage) { // This should generate a new entry. NavigationController::LoadCommittedDetails details; - EXPECT_TRUE(contents->controller()->RendererDidNavigate(params, &details)); + EXPECT_TRUE(contents->controller()->RendererDidNavigate(params, false, + &details)); EXPECT_TRUE(notifications.Check1AndReset(NOTIFY_NAV_ENTRY_COMMITTED)); EXPECT_EQ(2, contents->controller()->GetEntryCount()); @@ -968,7 +976,7 @@ TEST_F(NavigationControllerTest, InPage) { contents->controller()->GoBack(); back_params.url = url1; back_params.page_id = 0; - EXPECT_TRUE(contents->controller()->RendererDidNavigate(back_params, + EXPECT_TRUE(contents->controller()->RendererDidNavigate(back_params, false, &details)); EXPECT_TRUE(notifications.Check1AndReset(NOTIFY_NAV_ENTRY_COMMITTED)); EXPECT_EQ(2, contents->controller()->GetEntryCount()); @@ -980,7 +988,7 @@ TEST_F(NavigationControllerTest, InPage) { contents->controller()->GoForward(); forward_params.url = url2; forward_params.page_id = 1; - EXPECT_TRUE(contents->controller()->RendererDidNavigate(forward_params, + EXPECT_TRUE(contents->controller()->RendererDidNavigate(forward_params, false, &details)); EXPECT_TRUE(notifications.Check1AndReset(NOTIFY_NAV_ENTRY_COMMITTED)); EXPECT_EQ(2, contents->controller()->GetEntryCount()); @@ -993,10 +1001,10 @@ TEST_F(NavigationControllerTest, InPage) { // one identified by an existing page ID. This would result in the second URL // losing the reference fragment when you navigate away from it and then back. contents->controller()->GoBack(); - EXPECT_TRUE(contents->controller()->RendererDidNavigate(back_params, + EXPECT_TRUE(contents->controller()->RendererDidNavigate(back_params, false, &details)); contents->controller()->GoForward(); - EXPECT_TRUE(contents->controller()->RendererDidNavigate(forward_params, + EXPECT_TRUE(contents->controller()->RendererDidNavigate(forward_params, false, &details)); EXPECT_EQ(forward_params.url, contents->controller()->GetActiveEntry()->url()); @@ -1242,7 +1250,7 @@ TEST_F(NavigationControllerTest, RestoreNavigate) { params.gesture = NavigationGestureUser; params.is_post = false; NavigationController::LoadCommittedDetails details; - controller->RendererDidNavigate(params, &details); + controller->RendererDidNavigate(params, false, &details); // There should be no longer any pending entry and one committed one. This // means that we were able to locate the entry, assign its site instance, and diff --git a/chrome/browser/provisional_load_details.cc b/chrome/browser/provisional_load_details.cc index ffc85a0..f7a9f89 100644 --- a/chrome/browser/provisional_load_details.cc +++ b/chrome/browser/provisional_load_details.cc @@ -8,11 +8,13 @@ #include "chrome/browser/ssl_manager.h" ProvisionalLoadDetails::ProvisionalLoadDetails(bool is_main_frame, + bool is_interstitial_page, bool is_in_page_navigation, const GURL& url, const std::string& security_info, bool is_content_filtered) : is_main_frame_(is_main_frame), + is_interstitial_page_(is_interstitial_page), is_in_page_navigation_(is_in_page_navigation), url_(url), error_code_(net::OK), diff --git a/chrome/browser/provisional_load_details.h b/chrome/browser/provisional_load_details.h index 5cb9501..9f63acb 100644 --- a/chrome/browser/provisional_load_details.h +++ b/chrome/browser/provisional_load_details.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_PROVISIONAL_LOAD_DETAILS_H_ -#define CHROME_BROWSER_PROVISIONAL_LOAD_DETAILS_H_ +#ifndef CHROME_BROWSER_PROVISIONAL_LOAD_DETAILS_H__ +#define CHROME_BROWSER_PROVISIONAL_LOAD_DETAILS_H__ #include "base/basictypes.h" #include "googleurl/src/gurl.h" @@ -20,6 +20,7 @@ class ProvisionalLoadDetails { public: ProvisionalLoadDetails(bool main_frame, + bool interstitial_page, bool in_page_navigation, const GURL& url, const std::string& security_info, @@ -33,6 +34,8 @@ class ProvisionalLoadDetails { bool main_frame() const { return is_main_frame_; } + bool interstitial_page() const { return is_interstitial_page_; } + bool in_page_navigation() const { return is_in_page_navigation_; } int ssl_cert_id() const { return ssl_cert_id_; } @@ -47,14 +50,15 @@ class ProvisionalLoadDetails { int error_code_; GURL url_; bool is_main_frame_; + bool is_interstitial_page_; bool is_in_page_navigation_; int ssl_cert_id_; int ssl_cert_status_; int ssl_security_bits_; bool is_content_filtered_; - DISALLOW_COPY_AND_ASSIGN(ProvisionalLoadDetails); + DISALLOW_EVIL_CONSTRUCTORS(ProvisionalLoadDetails); }; -#endif // CHROME_BROWSER_PROVISIONAL_LOAD_DETAILS_H_ +#endif // CHROME_BROWSER_PROVISIONAL_LOAD_DETAILS_H__ diff --git a/chrome/browser/render_view_host_manager.cc b/chrome/browser/render_view_host_manager.cc index 5ad576b..81e95aa 100644 --- a/chrome/browser/render_view_host_manager.cc +++ b/chrome/browser/render_view_host_manager.cc @@ -6,6 +6,7 @@ #include "base/command_line.h" #include "base/logging.h" +#include "chrome/browser/interstitial_page.h" #include "chrome/browser/navigation_controller.h" #include "chrome/browser/navigation_entry.h" #include "chrome/browser/render_widget_host_view.h" @@ -15,23 +16,37 @@ #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), - cross_navigation_pending_(false), + 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_page_(NULL) { + interstitial_page_(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, @@ -48,8 +63,21 @@ void RenderViewHostManager::Init(Profile* profile, } void RenderViewHostManager::Shutdown() { - if (pending_render_view_host_) - CancelPendingRenderView(); + 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(); @@ -95,17 +123,29 @@ RenderViewHost* RenderViewHostManager::Navigate(const NavigationEntry& entry) { } } + showing_repost_interstitial_ = false; return dest_render_view_host; } void RenderViewHostManager::Stop() { render_view_host_->Stop(); - // If we are cross-navigating, we should stop the pending renderers. This - // will lead to a DidFailProvisionalLoad, which will properly destroy them. - if (cross_navigation_pending_) { + // 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(); + } } } @@ -113,10 +153,12 @@ 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); } bool RenderViewHostManager::ShouldCloseTabOnUnresponsiveRenderer() { - if (!cross_navigation_pending_) + if (renderer_state_ != PENDING) return true; // If the tab becomes unresponsive during unload while doing a @@ -136,31 +178,119 @@ bool RenderViewHostManager::ShouldCloseTabOnUnresponsiveRenderer() { void RenderViewHostManager::DidNavigateMainFrame( RenderViewHost* render_view_host) { - if (!cross_navigation_pending_) { + 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(); - if (render_view_host == pending_render_view_host_) { - // The pending cross-site navigation completed, so show the renderer. - SwapToRenderView(&pending_render_view_host_, true); - cross_navigation_pending_ = false; - } else if (render_view_host == render_view_host_) { - // A navigation in the original page has taken place. Cancel the pending - // one. - CancelPendingRenderView(); - cross_navigation_pending_ = false; } else { - // No one else should be sending us DidNavigate in this state. + // 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. - if (!cross_navigation_pending_) + // 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_); @@ -168,7 +298,14 @@ void RenderViewHostManager::OnCrossSiteResponse(int new_render_process_host_id, // 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. - render_view_host_->ClosePage(new_render_process_host_id, new_request_id); + 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 @@ -190,6 +327,50 @@ void RenderViewHostManager::RendererAbortedProvisionalLoad( // 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) { @@ -207,21 +388,226 @@ void RenderViewHostManager::ShouldClosePage(bool proceed) { 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. - CancelPendingRenderView(); - cross_navigation_pending_ = false; + CancelRenderView(&pending_render_view_host_); + renderer_state_ = NORMAL; } } +void RenderViewHostManager::ShowInterstitialPage( + InterstitialPage* interstitial_page) { + // 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_page_ = interstitial_page; + 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( + interstitial_page->GetHTMLContents(), 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) { - render_view_host_->JavaScriptMessageBoxClosed(reply_msg, success, 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); } @@ -290,7 +676,7 @@ SiteInstance* RenderViewHostManager::GetSiteInstanceForEntry( // compare the entry's URL to the last committed entry's URL. NavigationController* controller = delegate_->GetControllerForRenderManager(); NavigationEntry* curr_entry = controller->GetLastCommittedEntry(); - if (interstitial_page_) { + 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); @@ -339,7 +725,7 @@ bool RenderViewHostManager::CreatePendingRenderView(SiteInstance* instance) { // Don't show the view until we get a DidNavigate from it. pending_render_view_host_->view()->Hide(); } else { - CancelPendingRenderView(); + CancelRenderView(&pending_render_view_host_); } return success; } @@ -408,18 +794,37 @@ void RenderViewHostManager::SwapToRenderView( RenderViewHost* RenderViewHostManager::UpdateRendererStateNavigate( const NavigationEntry& entry) { - // If we are cross-navigating, then we want to get back to normal and navigate - // as usual. - if (cross_navigation_pending_) { + // 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_) - CancelPendingRenderView(); - cross_navigation_pending_ = false; + 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. @@ -429,7 +834,8 @@ RenderViewHost* RenderViewHostManager::UpdateRendererStateNavigate( if (new_instance != curr_instance) { // New SiteInstance. - DCHECK(!cross_navigation_pending_); + DCHECK(renderer_state_ == NORMAL || + renderer_state_ == LEAVING_INTERSTITIAL); // Create a pending RVH and navigate it. bool success = CreatePendingRenderView(new_instance); @@ -438,14 +844,36 @@ RenderViewHost* RenderViewHostManager::UpdateRendererStateNavigate( // Check if our current RVH is live before we set up a transition. if (!render_view_host_->IsRenderViewLive()) { - if (!cross_navigation_pending_) { + 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 non - // cross-navigating (Note that we don't care about on{before}unload - // handlers if the current RVH isn't live.) + // 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_; @@ -467,9 +895,12 @@ RenderViewHost* RenderViewHostManager::UpdateRendererStateNavigate( // old page's onunload handler before it sends the response. pending_render_view_host_->SetHasPendingCrossSiteRequest(true, -1); - // We now have a pending RVH. - DCHECK(!cross_navigation_pending_); - cross_navigation_pending_ = 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 @@ -479,14 +910,38 @@ RenderViewHost* RenderViewHostManager::UpdateRendererStateNavigate( return pending_render_view_host_; } - // Same SiteInstance can be used. Navigate render_view_host_ if we are not - // cross navigating. - DCHECK(!cross_navigation_pending_); - return 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::CancelPendingRenderView() { - pending_render_view_host_->Shutdown(); - pending_render_view_host_ = NULL; +void RenderViewHostManager::InterstitialPageGone() { + DCHECK(!showing_interstitial_page()); + if (interstitial_page_) { + interstitial_page_->InterstitialClosed(); + interstitial_page_ = NULL; + } } diff --git a/chrome/browser/render_view_host_manager.h b/chrome/browser/render_view_host_manager.h index 40eabce..371c398 100644 --- a/chrome/browser/render_view_host_manager.h +++ b/chrome/browser/render_view_host_manager.h @@ -22,8 +22,8 @@ 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 transitions of processes (and hence -// RenderViewHosts) that can get complex. +// 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. @@ -101,9 +101,9 @@ class RenderViewHostManager { // page to stop loading. void Stop(); - // Notifies the regular and pending RenderViewHosts 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 both. + // 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); // Whether to close the tab or not when there is a hang during an unload @@ -111,7 +111,8 @@ class RenderViewHostManager { // with the navigation instead of closing the tab. bool ShouldCloseTabOnUnresponsiveRenderer(); - // Called when a renderer's main frame navigates. + // 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 @@ -127,28 +128,49 @@ class RenderViewHostManager { // WebContents. void ShouldClosePage(bool proceed); - // Forwards the message to the RenderViewHost, which is the original one. + // Displays an interstitial page 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. The passed InterstitialPage is owned by the caller and must + // remain valid while the interstitial page is shown. + void ShowInterstitialPage(InterstitialPage* interstitial_page); + + // 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); - // Sets the passed passed interstitial as the currently showing interstitial. - // |interstitial_page| should be non NULL (use the remove_interstitial_page - // method to unset the interstitial) and no interstitial page should be set - // when there is already a non NULL interstitial page set. - void set_interstitial_page(InterstitialPage* interstitial_page) { - DCHECK(!interstitial_page_ && interstitial_page); - interstitial_page_ = interstitial_page; + // 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; } - // Unsets the currently showing interstitial. - void remove_interstitial_page() { - DCHECK(interstitial_page_); - interstitial_page_ = NULL; + // Returns whether we are currently showing an interstitial page. + bool showing_interstitial_page() const { + return (renderer_state_ == INTERSTITIAL) || + (renderer_state_ == LEAVING_INTERSTITIAL); } - // Returns the currently showing interstitial, NULL if no interstitial is - // showing. + // Accessors to the the interstitial page. InterstitialPage* interstitial_page() const { return interstitial_page_; } @@ -156,6 +178,49 @@ class RenderViewHostManager { 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. @@ -186,14 +251,22 @@ class RenderViewHostManager { void SwapToRenderView(RenderViewHost** new_render_view_host, bool destroy_after); - // Helper method to terminate the pending RenderViewHost. - void CancelPendingRenderView(); - 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. + 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_; @@ -202,19 +275,31 @@ class RenderViewHostManager { RenderViewHostDelegate* render_view_delegate_; // Our RenderView host. This object is responsible for all communication with - // a child RenderView instance. + // 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 - // while a cross-site request is pending until it calls DidNavigate. + // during the PENDING RendererState until it calls DidNavigate. It can also + // exist if an interstitial page is shown. RenderViewHost* pending_render_view_host_; // The intersitial page currently shown if any, not own by this class // (the InterstitialPage is self-owned, it deletes itself when hidden). InterstitialPage* interstitial_page_; - // Whether a cross-site request is pending (in the new process model). - bool cross_navigation_pending_; + // See comment above showing_repost_interstitial(). + bool showing_repost_interstitial_; DISALLOW_COPY_AND_ASSIGN(RenderViewHostManager); }; diff --git a/chrome/browser/render_widget_host_view_win.h b/chrome/browser/render_widget_host_view_win.h index be3c6b5..064c7a0 100644 --- a/chrome/browser/render_widget_host_view_win.h +++ b/chrome/browser/render_widget_host_view_win.h @@ -37,7 +37,7 @@ static const wchar_t* const kRenderWidgetHostHWNDClass = L"Chrome_RenderWidgetHostHWND"; /////////////////////////////////////////////////////////////////////////////// -// RenderWidgetHostViewWin +// RenderWidgetHostHWND // // An object representing the "View" of a rendered web page. This object is // responsible for displaying the content of the web page, receiving windows @@ -274,7 +274,7 @@ class RenderWidgetHostViewWin : // until that bug is fixed. bool renderer_accessible_; - DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewWin); + DISALLOW_EVIL_CONSTRUCTORS(RenderWidgetHostViewWin); }; #endif // #ifndef CHROME_BROWSER_RENDER_WIDGET_HOST_VIEW_WIN_H_ diff --git a/chrome/browser/resource_dispatcher_host.cc b/chrome/browser/resource_dispatcher_host.cc index 042f178..1d1aa18 100644 --- a/chrome/browser/resource_dispatcher_host.cc +++ b/chrome/browser/resource_dispatcher_host.cc @@ -25,8 +25,8 @@ #include "chrome/browser/render_view_host_delegate.h" #include "chrome/browser/resource_request_details.h" #include "chrome/browser/safe_browsing/safe_browsing_service.h" +#include "chrome/browser/tab_contents.h" #include "chrome/browser/tab_util.h" -#include "chrome/browser/web_contents.h" #include "chrome/common/notification_source.h" #include "chrome/common/notification_types.h" #include "chrome/common/render_messages.h" @@ -2319,14 +2319,14 @@ class NotificationTask : public Task { void Run() { // Find the tab associated with this request. - WebContents* web_contents = - tab_util::GetWebContentsByID(render_process_host_id_, tab_contents_id_); + TabContents* tab_contents = + tab_util::GetTabContentsByID(render_process_host_id_, tab_contents_id_); - if (web_contents) { + if (tab_contents) { // Issue the notification. NotificationService::current()-> Notify(type_, - Source<NavigationController>(web_contents->controller()), + Source<NavigationController>(tab_contents->controller()), Details<ResourceRequestDetails>(details_.get())); } } diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc index 265e4c7..6c6433a 100644 --- a/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc +++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc @@ -43,7 +43,7 @@ static const wchar_t* const kSbDiagnosticHtml = SafeBrowsingBlockingPage::SafeBrowsingBlockingPage( SafeBrowsingService* sb_service, const SafeBrowsingService::BlockingPageParam& param) - : InterstitialPage(tab_util::GetWebContentsByID( + : InterstitialPage(tab_util::GetTabContentsByID( param.render_process_host_id, param.render_view_id), param.resource_type == ResourceType::MAIN_FRAME, param.url), @@ -55,12 +55,6 @@ SafeBrowsingBlockingPage::SafeBrowsingBlockingPage( proceed_(false), did_notify_(false), is_main_frame_(param.resource_type == ResourceType::MAIN_FRAME) { - if (!is_main_frame_) { - navigation_entry_index_to_remove_ = - tab()->controller()->GetLastCommittedEntryIndex(); - } else { - navigation_entry_index_to_remove_ = -1; - } } SafeBrowsingBlockingPage::~SafeBrowsingBlockingPage() { @@ -144,6 +138,8 @@ std::string SafeBrowsingBlockingPage::GetHTMLContents() { } void SafeBrowsingBlockingPage::CommandReceived(const std::string& command) { + DCHECK(tab()->type() == TAB_CONTENTS_WEB); + WebContents* web = tab()->AsWebContents(); if (command == "2") { // User pressed "Learn more". GURL url; @@ -154,7 +150,7 @@ void SafeBrowsingBlockingPage::CommandReceived(const std::string& command) { } else { NOTREACHED(); } - tab()->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::LINK); + web->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::LINK); return; } if (command == "3") { @@ -165,8 +161,8 @@ void SafeBrowsingBlockingPage::CommandReceived(const std::string& command) { GURL report_url = safe_browsing_util::GeneratePhishingReportUrl(kSbReportPhishingUrl, url().spec()); - Hide(); - tab()->OpenURL(report_url, GURL(), CURRENT_TAB, PageTransition::LINK); + web->HideInterstitialPage(false, false); + web->OpenURL(report_url, GURL(), CURRENT_TAB, PageTransition::LINK); return; } if (command == "4") { @@ -177,26 +173,33 @@ void SafeBrowsingBlockingPage::CommandReceived(const std::string& command) { GURL diagnostic_url(diagnostic); diagnostic_url = google_util::AppendGoogleLocaleParam(diagnostic_url); DCHECK(result_ == SafeBrowsingService::URL_MALWARE); - tab()->OpenURL(diagnostic_url, GURL(), CURRENT_TAB, PageTransition::LINK); + web->HideInterstitialPage(false, false); + web->OpenURL(diagnostic_url, GURL(), CURRENT_TAB, PageTransition::LINK); return; } proceed_ = command == "1"; - if (proceed_) - Proceed(); - else - DontProceed(); - + if (proceed_) { + if (is_main_frame_) + web->HideInterstitialPage(true, true); + else + web->HideInterstitialPage(false, false); + } else { + if (is_main_frame_) { + DontProceed(); + } else { + NavigationController* controller = web->controller(); + controller->RemoveEntryAtIndex(controller->GetLastCommittedEntryIndex(), + NewTabUIURL()); + } + } NotifyDone(); } -void SafeBrowsingBlockingPage::DontProceed() { - if (navigation_entry_index_to_remove_ != -1) { - tab()->controller()->RemoveEntryAtIndex(navigation_entry_index_to_remove_, - NewTabUIURL()); - } - InterstitialPage::DontProceed(); +void SafeBrowsingBlockingPage::InterstitialClosed() { + NotifyDone(); + InterstitialPage::InterstitialClosed(); // We are now deleted. } diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page.h b/chrome/browser/safe_browsing/safe_browsing_blocking_page.h index b0fa2ea..056d9ae 100644 --- a/chrome/browser/safe_browsing/safe_browsing_blocking_page.h +++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page.h @@ -38,7 +38,7 @@ class SafeBrowsingBlockingPage : public InterstitialPage { // InterstitialPage method: virtual std::string GetHTMLContents(); - virtual void DontProceed(); + virtual void InterstitialClosed(); protected: // InterstitialPage method: @@ -77,10 +77,6 @@ class SafeBrowsingBlockingPage : public InterstitialPage { // Whether the flagged resource is the main page (or a sub-resource is false). bool is_main_frame_; - // The index of a navigation entry that should be removed when DontProceed() - // is invoked, -1 if not entry should be removed. - int navigation_entry_index_to_remove_; - DISALLOW_COPY_AND_ASSIGN(SafeBrowsingBlockingPage); }; diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc index 5a7a617..837c11f 100644 --- a/chrome/browser/safe_browsing/safe_browsing_service.cc +++ b/chrome/browser/safe_browsing/safe_browsing_service.cc @@ -17,7 +17,6 @@ #include "chrome/browser/safe_browsing/protocol_manager.h" #include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h" #include "chrome/browser/safe_browsing/safe_browsing_database.h" -#include "chrome/browser/tab_util.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" @@ -282,20 +281,6 @@ void SafeBrowsingService::DisplayBlockingPage(const GURL& url, // Invoked on the UI thread. void SafeBrowsingService::DoDisplayBlockingPage( const BlockingPageParam& param) { - // The tab might have been closed. - if (!tab_util::GetWebContentsByID(param.render_process_host_id, - param.render_view_id)) { - // The tab is gone and we did not have a chance at showing the interstitial. - // Just act as "Don't Proceed" was chosen. - base::Thread* io_thread = g_browser_process->io_thread(); - if (!io_thread) - return; - BlockingPageParam response_param = param; - response_param.proceed = false; - io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( - this, &SafeBrowsingService::OnBlockingPageDone, response_param)); - return; - } SafeBrowsingBlockingPage* blocking_page = new SafeBrowsingBlockingPage(this, param); blocking_page->Show(); diff --git a/chrome/browser/ssl_blocking_page.cc b/chrome/browser/ssl_blocking_page.cc index 88b402c..c92ddc2 100644 --- a/chrome/browser/ssl_blocking_page.cc +++ b/chrome/browser/ssl_blocking_page.cc @@ -12,6 +12,7 @@ #include "chrome/browser/navigation_controller.h" #include "chrome/browser/navigation_entry.h" #include "chrome/browser/ssl_error_info.h" +#include "chrome/browser/tab_contents.h" #include "chrome/browser/web_contents.h" #include "chrome/common/jstemplate_builder.h" #include "chrome/common/l10n_util.h" @@ -25,7 +26,7 @@ // No error happening loading a sub-resource triggers an interstitial so far. SSLBlockingPage::SSLBlockingPage(SSLManager::CertError* error, Delegate* delegate) - : InterstitialPage(error->GetWebContents(), true, error->request_url()), + : InterstitialPage(error->GetTabContents(), true, error->request_url()), error_(error), delegate_(delegate), delegate_has_been_notified_(false) { @@ -94,11 +95,15 @@ void SSLBlockingPage::CommandReceived(const std::string& command) { } void SSLBlockingPage::Proceed() { + // We hide the interstitial page first (by calling Proceed()) as allowing the + // certificate will resume the request and we want the WebContents back to + // showing the non interstitial page (otherwise the request completion + // messages may confuse the WebContents if it is still showing the + // interstitial page). + InterstitialPage::Proceed(); + // Accepting the certificate resumes the loading of the page. NotifyAllowCertificate(); - - // This call hides and deletes the interstitial. - InterstitialPage::Proceed(); } void SSLBlockingPage::DontProceed() { diff --git a/chrome/browser/ssl_manager.cc b/chrome/browser/ssl_manager.cc index 2a875d4..f76774a0 100644 --- a/chrome/browser/ssl_manager.cc +++ b/chrome/browser/ssl_manager.cc @@ -259,10 +259,10 @@ SSLManager::ErrorHandler::ErrorHandler(ResourceDispatcherHost* rdh, void SSLManager::ErrorHandler::Dispatch() { DCHECK(MessageLoop::current() == ui_loop_); - TabContents* web_contents = - tab_util::GetWebContentsByID(render_process_host_id_, tab_contents_id_); + TabContents* tab_contents = + tab_util::GetTabContentsByID(render_process_host_id_, tab_contents_id_); - if (!web_contents) { + if (!tab_contents) { // We arrived on the UI thread, but the tab we're looking for is no longer // here. OnDispatchFailed(); @@ -270,12 +270,12 @@ void SSLManager::ErrorHandler::Dispatch() { } // Hand ourselves off to the SSLManager. - manager_ = web_contents->controller()->ssl_manager(); + manager_ = tab_contents->controller()->ssl_manager(); OnDispatched(); } -WebContents* SSLManager::ErrorHandler::GetWebContents() { - return tab_util::GetWebContentsByID(render_process_host_id_, +TabContents* SSLManager::ErrorHandler::GetTabContents() { + return tab_util::GetTabContentsByID(render_process_host_id_, tab_contents_id_); } @@ -570,6 +570,11 @@ void SSLManager::DidCommitProvisionalLoad( changed = true; } + if (details->is_interstitial) { + // We should not have any errors when loading an interstitial page, and as + // a consequence no messages. + DCHECK(pending_messages_.empty()); + } ShowPendingMessages(); } @@ -610,12 +615,14 @@ void SSLManager::DidCommitProvisionalLoad( void SSLManager::DidFailProvisionalLoadWithError( ProvisionalLoadDetails* details) { DCHECK(details); + // A transitional page is not expected to fail. + DCHECK(!details->interstitial_page()); // Ignore in-page navigations. if (details->in_page_navigation()) return; - if (details->main_frame()) + if (details->main_frame() && !details->interstitial_page()) ClearPendingMessages(); } diff --git a/chrome/browser/ssl_manager.h b/chrome/browser/ssl_manager.h index 43b71c2..29c01f3 100644 --- a/chrome/browser/ssl_manager.h +++ b/chrome/browser/ssl_manager.h @@ -34,9 +34,9 @@ class PrefService; class ResourceRedirectDetails; class ResourceRequestDetails; class SSLErrorInfo; +class TabContents; class Task; class URLRequest; -class WebContents; // The SSLManager SSLManager controls the SSL UI elements in a TabContents. It // listens for various events that influence when these elements should or @@ -75,9 +75,9 @@ class SSLManager : public NotificationObserver { // Call on the UI thread. SSLManager* manager() const { return manager_; }; - // Returns the WebContents this object is associated with. Should be + // Returns the TabContents this object is associated with. Should be // called from the UI thread. - WebContents* GetWebContents(); + TabContents* GetTabContents(); // Cancels the associated URLRequest. // This method can be called from OnDispatchFailed and OnDispatched. diff --git a/chrome/browser/ssl_policy.cc b/chrome/browser/ssl_policy.cc index f89cdcd..8ed4486 100644 --- a/chrome/browser/ssl_policy.cc +++ b/chrome/browser/ssl_policy.cc @@ -61,7 +61,9 @@ ShowUnsafeContentTask::~ShowUnsafeContentTask() { void ShowUnsafeContentTask::Run() { error_handler_->manager()->AllowShowInsecureContentForURL(main_frame_url_); // Reload the page. - error_handler_->GetWebContents()->controller()->Reload(true); + DCHECK(error_handler_->GetTabContents()->type() == TAB_CONTENTS_WEB); + WebContents* tab = error_handler_->GetTabContents()->AsWebContents(); + tab->controller()->Reload(true); } static void ShowErrorPage(SSLPolicy* policy, SSLManager::CertError* error) { @@ -89,7 +91,8 @@ static void ShowErrorPage(SSLPolicy* policy, SSLManager::CertError* error) { std::string html_text(jstemplate_builder::GetTemplateHtml(html, &strings, "template_root")); - WebContents* tab = error->GetWebContents(); + DCHECK(error->GetTabContents()->type() == TAB_CONTENTS_WEB); + WebContents* tab = error->GetTabContents()->AsWebContents(); int cert_id = CertStore::GetSharedInstance()->StoreCert( error->ssl_info().cert, tab->render_view_host()->process()->host_id()); std::string security_info = @@ -478,6 +481,7 @@ void SSLPolicy::OnFatalCertError(const GURL& main_frame_url, return; } error->CancelRequest(); + DCHECK(error->GetTabContents()->type() == TAB_CONTENTS_WEB); ShowErrorPage(this, error); // No need to degrade our security indicators because we didn't continue. } diff --git a/chrome/browser/tab_util.cc b/chrome/browser/tab_util.cc index 863b38d4..815abfe 100644 --- a/chrome/browser/tab_util.cc +++ b/chrome/browser/tab_util.cc @@ -27,7 +27,7 @@ bool tab_util::GetTabContentsID(URLRequest* request, return true; } -WebContents* tab_util::GetWebContentsByID(int render_process_id, +TabContents* tab_util::GetTabContentsByID(int render_process_id, int render_view_id) { RenderViewHost* render_view_host = RenderViewHost::FromID(render_process_id, render_view_id); diff --git a/chrome/browser/tab_util.h b/chrome/browser/tab_util.h index 6aa96f3..9b576e5 100644 --- a/chrome/browser/tab_util.h +++ b/chrome/browser/tab_util.h @@ -5,8 +5,8 @@ #ifndef CHROME_BROWSER_TAB_UTIL_H__ #define CHROME_BROWSER_TAB_UTIL_H__ +class TabContents; class URLRequest; -class WebContents; namespace tab_util { @@ -15,10 +15,10 @@ namespace tab_util { bool GetTabContentsID(URLRequest* request, int* render_process_host_id, int* routing_id); -// Helper to find the WebContents that originated the given request. Can be +// Helper to find the TabContents that originated the given request. Can be // NULL if the tab has been closed or some other error occurs. // Should only be called from the UI thread, since it accesses TabContent. -WebContents* GetWebContentsByID(int render_process_host_id, int routing_id); +TabContents* GetTabContentsByID(int render_process_host_id, int routing_id); } // namespace tab_util diff --git a/chrome/browser/task_manager_resource_providers.cc b/chrome/browser/task_manager_resource_providers.cc index 6e62e7a..ceca70c 100644 --- a/chrome/browser/task_manager_resource_providers.cc +++ b/chrome/browser/task_manager_resource_providers.cc @@ -85,9 +85,12 @@ TaskManager::Resource* TaskManagerWebContentsResourceProvider::GetResource( int render_process_host_id, int routing_id) { - WebContents* web_contents = - tab_util::GetWebContentsByID(render_process_host_id, routing_id); - if (!web_contents) // Not one of our resource. + TabContents* tab_contents = + tab_util::GetTabContentsByID(render_process_host_id, routing_id); + if (!tab_contents) // Not one of our resource. + return NULL; + WebContents* web_contents = tab_contents->AsWebContents(); + if (!web_contents) return NULL; if (!web_contents->process()->process().handle()) { diff --git a/chrome/browser/views/dom_view.cc b/chrome/browser/views/dom_view.cc index f6c92a8..ed8fa95 100644 --- a/chrome/browser/views/dom_view.cc +++ b/chrome/browser/views/dom_view.cc @@ -27,8 +27,8 @@ bool DOMView::Init(Profile* profile, SiteInstance* instance) { // a DOMUIHostFactory rather than TabContentsFactory, because DOMView's // should only be associated with instances of DOMUIHost. TabContentsType type = TabContents::TypeForURL(&contents_); - TabContents* tab_contents = TabContents::CreateWithType(type, profile, - instance); + TabContents* tab_contents = TabContents::CreateWithType(type, profile, + instance); host_ = tab_contents->AsDOMUIHost(); DCHECK(host_); diff --git a/chrome/browser/views/external_protocol_dialog.cc b/chrome/browser/views/external_protocol_dialog.cc index f4aafcb..aa9308d 100644 --- a/chrome/browser/views/external_protocol_dialog.cc +++ b/chrome/browser/views/external_protocol_dialog.cc @@ -10,7 +10,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/external_protocol_handler.h" #include "chrome/browser/tab_util.h" -#include "chrome/browser/web_contents.h" +#include "chrome/browser/tab_contents.h" #include "chrome/common/l10n_util.h" #include "chrome/views/message_box_view.h" #include "chrome/views/window.h" @@ -31,10 +31,10 @@ const int kMessageWidth = 400; void ExternalProtocolDialog::RunExternalProtocolDialog( const GURL& url, const std::wstring& command, int render_process_host_id, int routing_id) { - WebContents* web_contents = tab_util::GetWebContentsByID( + TabContents* tab_contents = tab_util::GetTabContentsByID( render_process_host_id, routing_id); ExternalProtocolDialog* handler = - new ExternalProtocolDialog(web_contents, url, command); + new ExternalProtocolDialog(tab_contents, url, command); } ExternalProtocolDialog::~ExternalProtocolDialog() { diff --git a/chrome/browser/views/external_protocol_dialog.h b/chrome/browser/views/external_protocol_dialog.h index 720ebae..db23116 100644 --- a/chrome/browser/views/external_protocol_dialog.h +++ b/chrome/browser/views/external_protocol_dialog.h @@ -17,7 +17,7 @@ class ExternalProtocolDialog : public views::DialogDelegate { // |url| - The url of the request. // |command| - the command that ShellExecute will run. // |render_process_host_id| and |routing_id| are used by - // tab_util::GetWebContentsByID to aquire the tab contents associated with + // tab_util::GetTabContentsByID to aquire the tab contents associated with // this dialog. // NOTE: There is a race between the Time of Check and the Time Of Use for // the command line. Since the caller (web page) does not have access diff --git a/chrome/browser/web_contents.cc b/chrome/browser/web_contents.cc index d4c661f..bf60d58f 100644 --- a/chrome/browser/web_contents.cc +++ b/chrome/browser/web_contents.cc @@ -19,6 +19,7 @@ #include "chrome/browser/download/download_request_manager.h" #include "chrome/browser/find_notification_details.h" #include "chrome/browser/google_util.h" +#include "chrome/browser/interstitial_page.h" #include "chrome/browser/js_before_unload_handler.h" #include "chrome/browser/jsmessage_box_handler.h" #include "chrome/browser/load_from_memory_cache_details.h" @@ -589,7 +590,7 @@ void WebContents::SavePage(const std::wstring& main_file, void WebContents::PrintPreview() { // We can't print interstitial page for now. - if (showing_interstitial_page()) + if (render_manager_.showing_interstitial_page()) return; // If we have a find bar it needs to hide as well. @@ -601,7 +602,7 @@ void WebContents::PrintPreview() { bool WebContents::PrintNow() { // We can't print interstitial page for now. - if (showing_interstitial_page()) + if (render_manager_.showing_interstitial_page()) return false; // If we have a find bar it needs to hide as well. @@ -647,7 +648,11 @@ Profile* WebContents::GetProfile() const { } void WebContents::RendererReady(RenderViewHost* rvh) { - if (rvh != render_view_host()) { + if (render_manager_.showing_interstitial_page() && + rvh == render_view_host()) { + // We are showing an interstitial page, don't notify the world. + return; + } else if (rvh != render_view_host()) { // Don't notify the world, since this came from a renderer in the // background. return; @@ -662,7 +667,10 @@ void WebContents::RendererGone(RenderViewHost* rvh) { if (!printing_.OnRendererGone(rvh)) return; if (rvh != render_view_host()) { - // The pending page's RenderViewHost is gone. + // 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 + // RenderViewHost back in, in SwapToRenderView. return; } @@ -683,12 +691,27 @@ void WebContents::DidNavigate(RenderViewHost* rvh, if (PageTransition::IsMainFrame(params.transition)) render_manager_.DidNavigateMainFrame(rvh); + // In the case of interstitial, we don't mess with the navigation entries. + // TODO(brettw) this seems like a bug. What happens if the page goes and + // does something on its own (or something that just got delayed), then + // we won't have a navigation entry for that stuff when the interstitial + // is hidden. + if (render_manager_.showing_interstitial_page()) + return; + // We can't do anything about navigations when we're inactive. if (!controller() || !is_active()) return; - // Update the site of the SiteInstance if it doesn't have one yet. - if (!GetSiteInstance()->has_site()) + // 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. + // + // 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 (!GetSiteInstance()->has_site() && + !render_manager_.showing_interstitial_page()) GetSiteInstance()->SetSite(params.url); // Need to update MIME type here because it's referred to in @@ -703,7 +726,10 @@ void WebContents::DidNavigate(RenderViewHost* rvh, contents_mime_type_ = params.contents_mime_type; NavigationController::LoadCommittedDetails details; - if (!controller()->RendererDidNavigate(params, &details)) + if (!controller()->RendererDidNavigate( + params, + render_manager_.IsRenderViewInterstitial(rvh), + &details)) return; // No navigation happened. // DO NOT ADD MORE STUFF TO THIS FUNCTION! Your component should either listen @@ -720,7 +746,18 @@ void WebContents::DidNavigate(RenderViewHost* rvh, void WebContents::UpdateState(RenderViewHost* rvh, int32 page_id, const std::string& state) { - DCHECK(rvh == render_view_host()); + 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, + // or that we are navigating from the interstitial and getting an update + // for it. + // - targeted at the interstitial page. Ignore it as we don't want to update + // the fake navigation entry. + return; + } + if (!controller()) return; @@ -751,10 +788,19 @@ void WebContents::UpdateTitle(RenderViewHost* rvh, // getting useful data. SetNotWaitingForResponse(); - DCHECK(rvh == render_view_host()); - NavigationEntry* entry = controller()->GetEntryWithPageID(type(), - GetSiteInstance(), - page_id); + NavigationEntry* entry; + 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 active entry, + // so just use that. + entry = controller()->GetActiveEntry(); + } else { + entry = controller()->GetEntryWithPageID(type(), GetSiteInstance(), + page_id); + } + if (!entry || !UpdateTitleForEntry(entry, title)) return; @@ -836,9 +882,11 @@ void WebContents::DidStartProvisionalLoadForFrame( RenderViewHost* render_view_host, bool is_main_frame, const GURL& url) { - ProvisionalLoadDetails details(is_main_frame, - controller()->IsURLInPageNavigation(url), - url, std::string(), false); + ProvisionalLoadDetails details( + is_main_frame, + render_manager_.IsRenderViewInterstitial(render_view_host), + controller()->IsURLInPageNavigation(url), + url, std::string(), false); NotificationService::current()-> Notify(NOTIFY_FRAME_PROVISIONAL_LOAD_START, Source<NavigationController>(controller()), @@ -883,7 +931,8 @@ void WebContents::DidFailProvisionalLoadWithError( RenderViewHost* render_view_host, bool is_main_frame, int error_code, - const GURL& url) { + const GURL& url, + bool showing_repost_interstitial) { if (!controller()) return; @@ -906,7 +955,7 @@ void WebContents::DidFailProvisionalLoadWithError( // in the previous tab type. If you navigate somewhere that activates the // tab with the interstitial again, you'll see a flash before the new load // commits of the interstitial page. - if (showing_interstitial_page()) { + if (render_manager_.showing_interstitial_page()) { LOG(WARNING) << "Discarding message during interstitial."; return; } @@ -923,11 +972,15 @@ void WebContents::DidFailProvisionalLoadWithError( } // Send out a notification that we failed a provisional load with an error. - ProvisionalLoadDetails details(is_main_frame, - controller()->IsURLInPageNavigation(url), - url, std::string(), false); + ProvisionalLoadDetails details( + is_main_frame, + render_manager_.IsRenderViewInterstitial(render_view_host), + controller()->IsURLInPageNavigation(url), + url, std::string(), false); details.set_error_code(error_code); + render_manager_.set_showing_repost_interstitial(showing_repost_interstitial); + NotificationService::current()-> Notify(NOTIFY_FAIL_PROVISIONAL_LOAD_WITH_ERROR, Source<NavigationController>(controller()), diff --git a/chrome/browser/web_contents.h b/chrome/browser/web_contents.h index 359a294..f7ed61e 100644 --- a/chrome/browser/web_contents.h +++ b/chrome/browser/web_contents.h @@ -124,24 +124,32 @@ class WebContents : public TabContents, // Various other systems need to know about our interstitials. bool showing_interstitial_page() const { - return render_manager_.interstitial_page() != NULL; + return render_manager_.showing_interstitial_page(); + } + bool showing_repost_interstitial() const { + return render_manager_.showing_repost_interstitial(); } - // Sets the passed passed interstitial as the currently showing interstitial. - // |interstitial_page| should be non NULL (use the remove_interstitial_page - // method to unset the interstitial) and no interstitial page should be set - // when there is already a non NULL interstitial page set. - void set_interstitial_page(InterstitialPage* interstitial_page) { - render_manager_.set_interstitial_page(interstitial_page); + // Displays the specified interstitial 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. + // |interstitial_page| is not owned by the WebContents. + void ShowInterstitialPage(InterstitialPage* interstitial_page) { + render_manager_.ShowInterstitialPage(interstitial_page); } - // Unsets the currently showing interstitial. - void remove_interstitial_page() { - render_manager_.remove_interstitial_page(); + // 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) { + render_manager_.HideInterstitialPage(wait_for_navigation, proceed); } - // Returns the currently showing interstitial, NULL if no interstitial is - // showing. + // Returns the interstitial page currently shown if any, NULL otherwise. InterstitialPage* interstitial_page() const { return render_manager_.interstitial_page(); } @@ -245,10 +253,12 @@ class WebContents : public TabContents, const GURL& target_url); virtual void DidLoadResourceFromMemoryCache(const GURL& url, const std::string& security_info); - virtual void DidFailProvisionalLoadWithError(RenderViewHost* render_view_host, - bool is_main_frame, - int error_code, - const GURL& url); + virtual void DidFailProvisionalLoadWithError( + RenderViewHost* render_view_host, + bool is_main_frame, + int error_code, + const GURL& url, + bool showing_repost_interstitial); virtual void UpdateFavIconURL(RenderViewHost* render_view_host, int32 page_id, const GURL& icon_url); virtual void DidDownloadImage(RenderViewHost* render_view_host, @@ -358,9 +368,6 @@ class WebContents : public TabContents, // Temporary until the view/contents separation is complete. friend class WebContentsViewWin; - // So InterstitialPage can access SetIsLoading. - friend class InterstitialPage; - // When CreateShortcut is invoked RenderViewHost::GetApplicationInfo is // invoked. CreateShortcut caches the state of the page needed to create the // shortcut in PendingInstall. When OnDidGetApplicationInfo is invoked, it diff --git a/chrome/browser/web_contents_unittest.cc b/chrome/browser/web_contents_unittest.cc index 8f9ed31..4811350 100644 --- a/chrome/browser/web_contents_unittest.cc +++ b/chrome/browser/web_contents_unittest.cc @@ -16,29 +16,11 @@ #include "chrome/test/testing_profile.h" #include "testing/gtest/include/gtest/gtest.h" -static void InitNavigateParams(ViewHostMsg_FrameNavigate_Params* params, - int page_id, - const GURL& url) { - params->page_id = page_id; - params->url = url; - params->referrer = GURL::EmptyGURL(); - params->transition = PageTransition::TYPED; - params->redirects = std::vector<GURL>(); - params->should_update_history = false; - params->searchable_form_url = GURL::EmptyGURL(); - params->searchable_form_element_name = std::wstring(); - params->searchable_form_encoding = std::string(); - params->password_form = PasswordForm(); - params->security_info = std::string(); - params->gesture = NavigationGestureUser; - params->is_post = false; -} - // Subclass the RenderViewHost's view so that we can call Show(), etc., // without having side-effects. class TestRenderWidgetHostView : public RenderWidgetHostView { public: - TestRenderWidgetHostView() : is_showing_(false) {} + TestRenderWidgetHostView() {} virtual RenderWidgetHost* GetRenderWidgetHost() const { return NULL; } virtual void DidBecomeSelected() {} @@ -53,8 +35,8 @@ class TestRenderWidgetHostView : public RenderWidgetHostView { virtual void Blur() {} virtual bool HasFocus() { return true; } virtual void AdvanceFocus(bool reverse) {} - virtual void Show() { is_showing_ = true; } - virtual void Hide() { is_showing_ = false; } + virtual void Show() {} + virtual void Hide() {} virtual gfx::Rect GetViewBounds() const { return gfx::Rect(); } virtual void UpdateCursor(const WebCursor& cursor) {} virtual void UpdateCursorIfOverSelf() {} @@ -68,11 +50,6 @@ class TestRenderWidgetHostView : public RenderWidgetHostView { virtual void Destroy() {} virtual void PrepareToDestroy() {} virtual void SetTooltipText(const std::wstring& tooltip_text) {} - - bool is_showing() const { return is_showing_; } - - private: - bool is_showing_; }; // Subclass RenderViewHost so that it does not create a process. @@ -207,10 +184,33 @@ class TestWebContents : public WebContents { return static_cast<TestRenderViewHost*>( render_manager_.pending_render_view_host_); } - - // State accessor. - bool cross_navigation_pending() { - return render_manager_.cross_navigation_pending_; + TestRenderViewHost* interstitial_rvh() { + return static_cast<TestRenderViewHost*>( + render_manager_.interstitial_render_view_host_); + } + TestRenderViewHost* original_rvh() { + return static_cast<TestRenderViewHost*>( + 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. @@ -250,84 +250,28 @@ class TestWebContents : public WebContents { bool transition_cross_site; }; -class TestInterstitialPage : public InterstitialPage { - public: - enum InterstitialState { - UNDECIDED = 0, // No decision taken yet. - OKED, // Proceed was called. - CANCELED // DontProceed was called. - }; - - TestInterstitialPage(WebContents* tab, - bool new_navigation, - const GURL& url, - InterstitialState* state, - bool* deleted) - : InterstitialPage(tab, new_navigation, url), - state_(state), - deleted_(deleted), - command_received_count_(0) { - *state_ = UNDECIDED; - *deleted_ = false; - } - - virtual ~TestInterstitialPage() { - *deleted_ = true; - } - - virtual void DontProceed() { - *state_ = CANCELED; - InterstitialPage::DontProceed(); - } - virtual void Proceed() { - *state_ = OKED; - InterstitialPage::Proceed(); - } - - int command_received_count() const { - return command_received_count_; - } - - void TestDomOperationResponse(const std::string& json_string) { - DomOperationResponse(json_string, 1); - } - - void TestDidNavigate(int page_id, const GURL& url) { - ViewHostMsg_FrameNavigate_Params params; - InitNavigateParams(¶ms, page_id, url); - DidNavigate(render_view_host(), params); - } - - void TestRendererGone() { - RendererGone(render_view_host()); - } - - bool is_showing() const { - return static_cast<TestRenderWidgetHostView*>(render_view_host()->view())-> - is_showing(); - } - - protected: - virtual RenderViewHost* CreateRenderViewHost() { - return new TestRenderViewHost( - SiteInstance::CreateSiteInstance(tab()->profile()), - this, MSG_ROUTING_NONE, NULL); - } - - virtual void CommandReceived(const std::string& command) { - command_received_count_++; - } - - private: - InterstitialState* state_; - bool* deleted_; - int command_received_count_; -}; - class WebContentsTest : public testing::Test { public: WebContentsTest() : contents(NULL) {} + void InitNavigateParams(ViewHostMsg_FrameNavigate_Params* params, + int pageID, + const GURL& url) { + params->page_id = pageID; + params->url = url; + params->referrer = GURL::EmptyGURL(); + params->transition = PageTransition::TYPED; + params->redirects = std::vector<GURL>(); + params->should_update_history = false; + params->searchable_form_url = GURL::EmptyGURL(); + params->searchable_form_element_name = std::wstring(); + params->searchable_form_encoding = std::string(); + params->password_form = PasswordForm(); + params->security_info = std::string(); + params->gesture = NavigationGestureUser; + params->is_post = false; + } + // testing::Test methods: virtual void SetUp() { @@ -342,7 +286,6 @@ class WebContentsTest : public testing::Test { virtual void TearDown() { // This will delete the contents. - if (contents) contents->CloseContents(); // Make sure that we flush any messages related to WebContents destruction @@ -350,13 +293,6 @@ class WebContentsTest : public testing::Test { MessageLoop::current()->RunAllPending(); } - void Navigate(int page_id, const GURL& url) { - DCHECK(contents); - ViewHostMsg_FrameNavigate_Params params; - InitNavigateParams(¶ms, page_id, url); - contents->TestDidNavigate(contents->rvh(), params); - } - scoped_ptr<WebContentsTestingProfile> profile; TestWebContents* contents; @@ -370,9 +306,9 @@ TEST_F(WebContentsTest, UpdateTitle) { InitNavigateParams(¶ms, 0, GURL("about:blank")); NavigationController::LoadCommittedDetails details; - contents->controller()->RendererDidNavigate(params, &details); + contents->controller()->RendererDidNavigate(params, false, &details); - contents->UpdateTitle(contents->rvh(), 0, L" Lots O' Whitespace\n"); + contents->UpdateTitle(NULL, 0, L" Lots O' Whitespace\n"); EXPECT_EQ(std::wstring(L"Lots O' Whitespace"), contents->GetTitle()); } @@ -381,12 +317,14 @@ TEST_F(WebContentsTest, SimpleNavigation) { TestRenderViewHost* orig_rvh = contents->rvh(); SiteInstance* instance1 = contents->GetSiteInstance(); EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); EXPECT_FALSE(orig_rvh->is_loading); // Navigate to URL const GURL url("http://www.google.com"); contents->controller()->LoadURL(url, GURL(), PageTransition::TYPED); - EXPECT_FALSE(contents->cross_navigation_pending()); + 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 @@ -398,7 +336,7 @@ TEST_F(WebContentsTest, SimpleNavigation) { ViewHostMsg_FrameNavigate_Params params; InitNavigateParams(¶ms, 1, url); contents->TestDidNavigate(orig_rvh, params); - EXPECT_FALSE(contents->cross_navigation_pending()); + 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 @@ -407,6 +345,260 @@ TEST_F(WebContentsTest, SimpleNavigation) { contents->controller()->GetActiveEntry()->site_instance()); } +// Test navigating to a page that shows an interstitial, then hiding it +// without proceeding. +TEST_F(WebContentsTest, ShowInterstitialDontProceed) { + TestRenderViewHost* orig_rvh = contents->rvh(); + EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + EXPECT_FALSE(orig_rvh->is_loading); + + // Navigate to URL + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, GURL(), PageTransition::TYPED); + EXPECT_TRUE(contents->state_is_normal()); + EXPECT_TRUE(orig_rvh->is_loading); + + // Show interstitial + const GURL interstitial_url("http://interstitial"); + InterstitialPage* interstitial = new InterstitialPage(contents, + true, + interstitial_url); + interstitial->Show(); + 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); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params; + InitNavigateParams(¶ms, 1, url); + contents->TestDidNavigate(interstitial_rvh, params); + 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_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); +} + +// Test navigating to a page that shows an interstitial, then proceeding. +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()); + + // Navigate to URL + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, GURL(), PageTransition::TYPED); + + // Show interstitial + const GURL interstitial_url("http://interstitial"); + InterstitialPage* interstitial = new InterstitialPage(contents, + true, + interstitial_url); + interstitial->Show(); + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params; + InitNavigateParams(¶ms, 1, url); + contents->TestDidNavigate(interstitial_rvh, params); + + // Ensure this DidNavigate hasn't changed the SiteInstance's site. + // Prevents regression for bug 1163298. + EXPECT_EQ(GURL(), contents->rvh()->site_instance()->site()); + + // Hide interstitial (proceed and wait) + contents->HideInterstitialPage(true, true); + 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_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); + + // The SiteInstance's site should now be updated. + EXPECT_EQ(GURL("http://google.com"), + contents->rvh()->site_instance()->site()); + + // Since we weren't viewing a page before, we shouldn't be able to go back. + EXPECT_FALSE(contents->controller()->CanGoBack()); +} + +// Test navigating to a page that shows an interstitial, then navigating away. +TEST_F(WebContentsTest, ShowInterstitialThenNavigate) { + TestRenderViewHost* orig_rvh = contents->rvh(); + + // Navigate to URL + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, GURL(), PageTransition::TYPED); + + // Show interstitial + const GURL interstitial_url("http://interstitial"); + InterstitialPage* interstitial = new InterstitialPage(contents, + true, + interstitial_url); + interstitial->Show(); + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params; + InitNavigateParams(¶ms, 1, url); + contents->TestDidNavigate(interstitial_rvh, params); + + // While interstitial showing, navigate to a new URL. + const GURL url2("http://www.yahoo.com"); + contents->controller()->LoadURL(url2, GURL(), PageTransition::TYPED); + 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); + + // DidNavigate from the new URL. In the old process model, we'll still have + // the same RenderViewHost. + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 2, url2); + contents->TestDidNavigate(orig_rvh, params2); + EXPECT_TRUE(contents->state_is_normal()); + EXPECT_EQ(orig_rvh, contents->render_view_host()); + EXPECT_FALSE(orig_rvh->is_loading); +} + +// Ensures that an interstitial cannot be cancelled if a notification for a +// navigation from an IFrame from the previous page is received while the +// interstitial is being shown (bug #1182394). +TEST_F(WebContentsTest, ShowInterstitialIFrameNavigate) { + TestRenderViewHost* orig_rvh = contents->rvh(); + EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + EXPECT_FALSE(orig_rvh->is_loading); + + // Navigate to URL. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, GURL(), PageTransition::TYPED); + EXPECT_TRUE(contents->state_is_normal()); + EXPECT_TRUE(orig_rvh->is_loading); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + // Show interstitial (in real world would probably be triggered by a resource + // in the page). + const GURL interstitial_url("http://interstitial"); + InterstitialPage* interstitial = new InterstitialPage(contents, + true, + interstitial_url); + interstitial->Show(); + EXPECT_TRUE(contents->state_is_entering_interstitial()); + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + EXPECT_TRUE(interstitial_rvh->is_loading); + + // DidNavigate from an IFrame in the initial page. + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 1, GURL("http://www.iframe.com")); + params2.transition = PageTransition::AUTO_SUBFRAME; + contents->TestDidNavigate(orig_rvh, params2); + + // Now we get the DidNavigate from the interstitial. + ViewHostMsg_FrameNavigate_Params params3; + InitNavigateParams(¶ms3, 1, url); + contents->TestDidNavigate(interstitial_rvh, params3); + 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); +} + +// Test navigating to an interstitial page from a normal page. Also test +// visiting the interstitial-inducing URL twice (bug 1079784), and test +// that going back shows the first page and not the interstitial. +TEST_F(WebContentsTest, VisitInterstitialURLTwice) { + TestRenderViewHost* orig_rvh = contents->rvh(); + + // Navigate to URL + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, GURL(), PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + // Now navigate to an interstitial-inducing URL + const GURL url2("https://www.google.com"); + contents->controller()->LoadURL(url2, GURL(), PageTransition::TYPED); + const GURL interstitial_url("http://interstitial"); + InterstitialPage* interstitial = new InterstitialPage(contents, + true, + interstitial_url); + interstitial->Show(); + 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); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 2, url2); + contents->TestDidNavigate(interstitial_rvh, params2); + 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, GURL(), PageTransition::TYPED); + EXPECT_TRUE(contents->state_is_leaving_interstitial()); + EXPECT_EQ(interstitial_rvh, contents->render_view_host()); + + // Interstitial shown a second time in a different RenderViewHost. + interstitial = new InterstitialPage(contents, true, interstitial_url); + interstitial->Show(); + 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(); + interstitial_rvh2->set_delete_counter(&interstitial_delete_counter); + + // DidNavigate from the interstitial. + ViewHostMsg_FrameNavigate_Params params3; + InitNavigateParams(¶ms3, 3, url2); + contents->TestDidNavigate(interstitial_rvh2, params3); + 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_TRUE(contents->state_is_leaving_interstitial()); + ViewHostMsg_FrameNavigate_Params params4; + InitNavigateParams(¶ms4, 3, url2); + contents->TestDidNavigate(orig_rvh, params4); + 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_TRUE(contents->state_is_normal()); + + // DidNavigate from going back. + contents->TestDidNavigate(orig_rvh, params1); + 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); +} + // Test that navigating across a site boundary creates a new RenderViewHost // with a new SiteInstance. Going back should do the same. TEST_F(WebContentsTest, CrossSiteBoundaries) { @@ -423,13 +615,15 @@ TEST_F(WebContentsTest, CrossSiteBoundaries) { InitNavigateParams(¶ms1, 1, url); contents->TestDidNavigate(orig_rvh, params1); - EXPECT_FALSE(contents->cross_navigation_pending()); + 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); // Navigate to new site const GURL url2("http://www.yahoo.com"); contents->controller()->LoadURL(url2, GURL(), PageTransition::TYPED); - EXPECT_TRUE(contents->cross_navigation_pending()); + 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); @@ -440,21 +634,23 @@ TEST_F(WebContentsTest, CrossSiteBoundaries) { contents->TestDidNavigate(pending_rvh, params2); SiteInstance* instance2 = contents->GetSiteInstance(); - EXPECT_FALSE(contents->cross_navigation_pending()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(pending_rvh, contents->render_view_host()); EXPECT_NE(instance1, instance2); EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); EXPECT_EQ(orig_rvh_delete_count, 1); // Going back should switch SiteInstances again. The first SiteInstance is // stored in the NavigationEntry, so it should be the same as at the start. contents->controller()->GoBack(); TestRenderViewHost* goback_rvh = contents->pending_rvh(); - EXPECT_TRUE(contents->cross_navigation_pending()); + EXPECT_TRUE(contents->state_is_pending()); // DidNavigate from the back action contents->TestDidNavigate(goback_rvh, params1); - EXPECT_FALSE(contents->cross_navigation_pending()); + 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->GetSiteInstance()); @@ -476,8 +672,10 @@ TEST_F(WebContentsTest, CrossSiteBoundariesAfterCrash) { InitNavigateParams(¶ms1, 1, url); contents->TestDidNavigate(orig_rvh, params1); - EXPECT_FALSE(contents->cross_navigation_pending()); + 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); // Crash the renderer. orig_rvh->is_created = false; @@ -486,8 +684,10 @@ TEST_F(WebContentsTest, CrossSiteBoundariesAfterCrash) { const GURL url2("http://www.yahoo.com"); contents->controller()->LoadURL(url2, GURL(), PageTransition::TYPED); TestRenderViewHost* new_rvh = contents->rvh(); - EXPECT_FALSE(contents->cross_navigation_pending()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); EXPECT_NE(orig_rvh, new_rvh); EXPECT_EQ(orig_rvh_delete_count, 1); @@ -497,10 +697,330 @@ TEST_F(WebContentsTest, CrossSiteBoundariesAfterCrash) { contents->TestDidNavigate(new_rvh, params2); SiteInstance* instance2 = contents->GetSiteInstance(); - EXPECT_FALSE(contents->cross_navigation_pending()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(new_rvh, contents->render_view_host()); EXPECT_NE(instance1, instance2); EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); +} + +// Test state transitions when showing an interstitial in the new process +// model, and then choosing DontProceed. +TEST_F(WebContentsTest, CrossSiteInterstitialDontProceed) { + contents->transition_cross_site = true; + TestRenderViewHost* orig_rvh = contents->rvh(); + SiteInstance* instance1 = contents->GetSiteInstance(); + + // Navigate to URL. First URL should use first RenderViewHost. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, GURL(), PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + 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, GURL(), PageTransition::TYPED); + EXPECT_TRUE(contents->state_is_pending()); + TestRenderViewHost* pending_rvh = contents->pending_rvh(); + + // Show an interstitial + const GURL interstitial_url("http://interstitial"); + InterstitialPage* interstitial = new InterstitialPage(contents, + true, + interstitial_url); + interstitial->Show(); + 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(); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 2, url2); + contents->TestDidNavigate(interstitial_rvh, params2); + 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()); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // Hide interstitial (don't proceed) + contents->HideInterstitialPage(false, false); + 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); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); +} + +// Test state transitions when showing an interstitial in the new process +// model, and then choosing Proceed. +TEST_F(WebContentsTest, CrossSiteInterstitialProceed) { + contents->transition_cross_site = true; + int orig_rvh_delete_count = 0; + TestRenderViewHost* orig_rvh = contents->rvh(); + orig_rvh->set_delete_counter(&orig_rvh_delete_count); + SiteInstance* instance1 = contents->GetSiteInstance(); + + // Navigate to URL. First URL should use first RenderViewHost. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, GURL(), PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + // Navigate to new site + const GURL url2("https://www.google.com"); + contents->controller()->LoadURL(url2, GURL(), PageTransition::TYPED); + TestRenderViewHost* pending_rvh = contents->pending_rvh(); + int pending_rvh_delete_count = 0; + pending_rvh->set_delete_counter(&pending_rvh_delete_count); + + // Show an interstitial + const GURL interstitial_url("http://interstitial"); + InterstitialPage* interstitial = new InterstitialPage(contents, + true, + interstitial_url); + interstitial->Show(); + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 1, url2); + contents->TestDidNavigate(interstitial_rvh, params2); + 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()); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // Hide interstitial (proceed and wait) + contents->HideInterstitialPage(true, true); + 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()); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // DidNavigate from the destination page should transition to new renderer + ViewHostMsg_FrameNavigate_Params params3; + InitNavigateParams(¶ms3, 2, url2); + contents->TestDidNavigate(pending_rvh, params3); + SiteInstance* instance2 = contents->GetSiteInstance(); + 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); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + EXPECT_NE(instance1, instance2); + EXPECT_EQ(orig_rvh_delete_count, 1); // The original should be gone. + + // Since we were viewing a page before, we should be able to go back. + EXPECT_TRUE(contents->controller()->CanGoBack()); + + // Going back should switch SiteInstances again. The first SiteInstance is + // stored in the NavigationEntry, so it should be the same as at the start. + contents->controller()->GoBack(); + TestRenderViewHost* goback_rvh = contents->pending_rvh(); + EXPECT_TRUE(contents->state_is_pending()); + + // DidNavigate from the back action + contents->TestDidNavigate(goback_rvh, params1); + EXPECT_TRUE(contents->state_is_normal()); + EXPECT_EQ(goback_rvh, contents->render_view_host()); + EXPECT_EQ(instance1, contents->GetSiteInstance()); + EXPECT_EQ(pending_rvh_delete_count, 1); // The second page's rvh should die. +} + +// Tests that we can transition away from an interstitial page. +TEST_F(WebContentsTest, CrossSiteInterstitialThenNavigate) { + contents->transition_cross_site = true; + int orig_rvh_delete_count = 0; + TestRenderViewHost* orig_rvh = contents->rvh(); + orig_rvh->set_delete_counter(&orig_rvh_delete_count); + + // Navigate to URL. First URL should use first RenderViewHost. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, GURL(), PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + // Show an interstitial + const GURL interstitial_url("http://interstitial"); + InterstitialPage* interstitial = new InterstitialPage(contents, + false, + interstitial_url); + interstitial->Show(); + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 1, url); + contents->TestDidNavigate(interstitial_rvh, params2); + 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); + + // Navigate to a new page. + const GURL url2("http://www.yahoo.com"); + contents->controller()->LoadURL(url2, GURL(), PageTransition::TYPED); + + TestRenderViewHost* new_rvh = contents->pending_rvh(); + ASSERT_TRUE(new_rvh != NULL); + // Make sure the RVH is not suspended (bug #1236441). + EXPECT_FALSE(new_rvh->IsNavigationSuspended()); + 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_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); +} + +// Tests that we can transition away from an interstitial page even if the +// interstitial renderer has crashed. +TEST_F(WebContentsTest, CrossSiteInterstitialCrashThenNavigate) { + contents->transition_cross_site = true; + int orig_rvh_delete_count = 0; + TestRenderViewHost* orig_rvh = contents->rvh(); + orig_rvh->set_delete_counter(&orig_rvh_delete_count); + + // Navigate to URL. First URL should use first RenderViewHost. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, GURL(), PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + // Navigate to new site + const GURL url2("https://www.google.com"); + contents->controller()->LoadURL(url2, GURL(), PageTransition::TYPED); + TestRenderViewHost* pending_rvh = contents->pending_rvh(); + int pending_rvh_delete_count = 0; + pending_rvh->set_delete_counter(&pending_rvh_delete_count); + + // Show an interstitial + const GURL interstitial_url("http://interstitial"); + InterstitialPage* interstitial = new InterstitialPage(contents, + true, + interstitial_url); + interstitial->Show(); + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 1, url2); + contents->TestDidNavigate(interstitial_rvh, params2); + 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()); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // Crash the interstitial RVH + // (by making IsRenderViewLive() == false) + interstitial_rvh->is_created = false; + + // Navigate to a new page. Since interstitial RVH is dead, we should clean + // it up and go to a new PENDING state, showing the orig_rvh. + const GURL url3("http://www.yahoo.com"); + contents->controller()->LoadURL(url3, GURL(), PageTransition::TYPED); + TestRenderViewHost* new_rvh = contents->pending_rvh(); + ASSERT_TRUE(new_rvh != NULL); + 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); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // DidNavigate from the new page + ViewHostMsg_FrameNavigate_Params params3; + InitNavigateParams(¶ms3, 1, url3); + contents->TestDidNavigate(new_rvh, params3); + 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); +} + +// Tests that we can transition away from an interstitial page even if both the +// original and interstitial renderers have crashed. +TEST_F(WebContentsTest, CrossSiteInterstitialCrashesThenNavigate) { + contents->transition_cross_site = true; + int orig_rvh_delete_count = 0; + TestRenderViewHost* orig_rvh = contents->rvh(); + orig_rvh->set_delete_counter(&orig_rvh_delete_count); + + // Navigate to URL. First URL should use first RenderViewHost. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, GURL(), PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + // Navigate to new site + const GURL url2("https://www.google.com"); + contents->controller()->LoadURL(url2, GURL(), PageTransition::TYPED); + TestRenderViewHost* pending_rvh = contents->pending_rvh(); + int pending_rvh_delete_count = 0; + pending_rvh->set_delete_counter(&pending_rvh_delete_count); + + // Show an interstitial + const GURL interstitial_url("http://interstitial"); + InterstitialPage* interstitial = new InterstitialPage(contents, + true, + interstitial_url); + interstitial->Show(); + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 1, url2); + contents->TestDidNavigate(interstitial_rvh, params2); + 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()); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // Crash both the original and interstitial RVHs + // (by making IsRenderViewLive() == false) + orig_rvh->is_created = false; + interstitial_rvh->is_created = false; + + // Navigate to a new page. Since both the interstitial and original RVHs are + // dead, we should create a new RVH, jump back to NORMAL, and navigate. + const GURL url3("http://www.yahoo.com"); + contents->controller()->LoadURL(url3, GURL(), PageTransition::TYPED); + TestRenderViewHost* new_rvh = contents->rvh(); + ASSERT_TRUE(new_rvh != NULL); + 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); + EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // DidNavigate from the new page + ViewHostMsg_FrameNavigate_Params params3; + InitNavigateParams(¶ms3, 1, url3); + contents->TestDidNavigate(new_rvh, params3); + EXPECT_TRUE(contents->state_is_normal()); + EXPECT_EQ(new_rvh, contents->render_view_host()); } // Test that opening a new tab in the same SiteInstance and then navigating @@ -542,7 +1062,7 @@ TEST_F(WebContentsTest, NavigateTwoTabsCrossSite) { contents2->controller()->LoadURL(url2b, GURL(), PageTransition::TYPED); TestRenderViewHost* pending_rvh_b = contents2->pending_rvh(); EXPECT_TRUE(pending_rvh_b != NULL); - EXPECT_TRUE(contents2->cross_navigation_pending()); + 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 @@ -580,16 +1100,16 @@ TEST_F(WebContentsTest, CrossSiteComparesAgainstCurrentPage) { contents2->SetupController(profile.get()); const GURL url2("http://www.yahoo.com"); contents2->controller()->LoadURL(url2, GURL(), PageTransition::TYPED); - // The first RVH in contents2 isn't live yet, so we shortcut the cross site - // pending. + // 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_FALSE(contents2->cross_navigation_pending()); + EXPECT_TRUE(contents2->state_is_normal()); ViewHostMsg_FrameNavigate_Params params2; InitNavigateParams(¶ms2, 2, url2); contents2->TestDidNavigate(rvh2, params2); SiteInstance* instance2 = contents2->GetSiteInstance(); EXPECT_NE(instance1, instance2); - EXPECT_FALSE(contents2->cross_navigation_pending()); + 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. @@ -598,13 +1118,13 @@ TEST_F(WebContentsTest, CrossSiteComparesAgainstCurrentPage) { contents->TestDidNavigate(orig_rvh, params3); SiteInstance* instance3 = contents->GetSiteInstance(); EXPECT_EQ(instance1, instance3); - EXPECT_FALSE(contents->cross_navigation_pending()); + 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, GURL(), PageTransition::TYPED); - EXPECT_FALSE(contents->cross_navigation_pending()); + EXPECT_TRUE(contents->state_is_normal()); ViewHostMsg_FrameNavigate_Params params4; InitNavigateParams(¶ms4, 3, url3); contents->TestDidNavigate(orig_rvh, params4); @@ -627,7 +1147,7 @@ TEST_F(WebContentsTest, CrossSiteUnloadHandlers) { ViewHostMsg_FrameNavigate_Params params1; InitNavigateParams(¶ms1, 1, url); contents->TestDidNavigate(orig_rvh, params1); - EXPECT_FALSE(contents->cross_navigation_pending()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(orig_rvh, contents->render_view_host()); // Navigate to new site, but simulate an onbeforeunload denial. @@ -635,13 +1155,13 @@ TEST_F(WebContentsTest, CrossSiteUnloadHandlers) { orig_rvh->immediate_before_unload = false; contents->controller()->LoadURL(url2, GURL(), PageTransition::TYPED); orig_rvh->TestOnMsgShouldClose(false); - EXPECT_FALSE(contents->cross_navigation_pending()); + 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, GURL(), PageTransition::TYPED); orig_rvh->TestOnMsgShouldClose(true); - EXPECT_TRUE(contents->cross_navigation_pending()); + EXPECT_TRUE(contents->state_is_pending()); TestRenderViewHost* pending_rvh = contents->pending_rvh(); // We won't hear DidNavigate until the onunload handler has finished running. @@ -653,10 +1173,12 @@ TEST_F(WebContentsTest, CrossSiteUnloadHandlers) { InitNavigateParams(¶ms2, 1, url2); contents->TestDidNavigate(pending_rvh, params2); SiteInstance* instance2 = contents->GetSiteInstance(); - EXPECT_FALSE(contents->cross_navigation_pending()); + EXPECT_TRUE(contents->state_is_normal()); EXPECT_EQ(pending_rvh, contents->render_view_host()); EXPECT_NE(instance1, instance2); EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); } // Test that NavigationEntries have the correct content state after going @@ -731,472 +1253,3 @@ TEST_F(WebContentsTest, WebKitPrefs) { EXPECT_EQ(true, webkit_prefs.javascript_enabled); } -//////////////////////////////////////////////////////////////////////////////// -// Interstitial Tests -//////////////////////////////////////////////////////////////////////////////// - -// Test navigating to a page (with the navigation initiated from the browser, -// as when a URL is typed in the location bar) that shows an interstitial and -// creates a new navigation entry, then hiding it without proceeding. -TEST_F(WebContentsTest, - ShowInterstitialFromBrowserWithNewNavigationDontProceed) { - // Navigate to a page. - GURL url1("http://www.google.com"); - Navigate(1, url1); - EXPECT_EQ(1, contents->controller()->GetEntryCount()); - - // Initiate a browser navigation that will trigger the interstitial - contents->controller()->LoadURL(GURL("http://www.evil.com"), GURL(), - PageTransition::TYPED); - - // Show an interstitial. - TestInterstitialPage::InterstitialState state = - TestInterstitialPage::UNDECIDED; - bool deleted = false; - GURL url2("http://interstitial"); - TestInterstitialPage* interstitial = - new TestInterstitialPage(contents, true, url2, &state, &deleted); - interstitial->Show(); - // The interstitial should not show until its navigation has committed. - EXPECT_FALSE(interstitial->is_showing()); - EXPECT_FALSE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == NULL); - // Let's commit the interstitial navigation. - interstitial->TestDidNavigate(1, url2); - EXPECT_TRUE(interstitial->is_showing()); - EXPECT_TRUE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == interstitial); - NavigationEntry* entry = contents->controller()->GetActiveEntry(); - ASSERT_TRUE(entry != NULL); - EXPECT_TRUE(entry->url() == url2); - - // Now don't proceed. - interstitial->DontProceed(); - EXPECT_TRUE(deleted); - EXPECT_EQ(TestInterstitialPage::CANCELED, state); - EXPECT_FALSE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == NULL); - entry = contents->controller()->GetActiveEntry(); - ASSERT_TRUE(entry != NULL); - EXPECT_TRUE(entry->url() == url1); - EXPECT_EQ(1, contents->controller()->GetEntryCount()); -} - -// Test navigating to a page (with the navigation initiated from the renderer, -// as when clicking on a link in the page) that shows an interstitial and -// creates a new navigation entry, then hiding it without proceeding. -TEST_F(WebContentsTest, - ShowInterstitiaFromRendererlWithNewNavigationDontProceed) { - // Navigate to a page. - GURL url1("http://www.google.com"); - Navigate(1, url1); - EXPECT_EQ(1, contents->controller()->GetEntryCount()); - - // Show an interstitial (no pending entry, the interstitial would have been - // triggered by clicking on a link). - TestInterstitialPage::InterstitialState state = - TestInterstitialPage::UNDECIDED; - bool deleted = false; - GURL url2("http://interstitial"); - TestInterstitialPage* interstitial = - new TestInterstitialPage(contents, true, url2, &state, &deleted); - interstitial->Show(); - // The interstitial should not show until its navigation has committed. - EXPECT_FALSE(interstitial->is_showing()); - EXPECT_FALSE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == NULL); - // Let's commit the interstitial navigation. - interstitial->TestDidNavigate(1, url2); - EXPECT_TRUE(interstitial->is_showing()); - EXPECT_TRUE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == interstitial); - NavigationEntry* entry = contents->controller()->GetActiveEntry(); - ASSERT_TRUE(entry != NULL); - EXPECT_TRUE(entry->url() == url2); - - // Now don't proceed. - interstitial->DontProceed(); - EXPECT_TRUE(deleted); - EXPECT_EQ(TestInterstitialPage::CANCELED, state); - EXPECT_FALSE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == NULL); - entry = contents->controller()->GetActiveEntry(); - ASSERT_TRUE(entry != NULL); - EXPECT_TRUE(entry->url() == url1); - EXPECT_EQ(1, contents->controller()->GetEntryCount()); -} - -// Test navigating to a page that shows an interstitial without creating a new -// navigation entry (this happens when the interstitial is triggered by a -// sub-resource in the page), then hiding it without proceeding. -TEST_F(WebContentsTest, ShowInterstitialNoNewNavigationDontProceed) { - // Navigate to a page. - GURL url1("http://www.google.com"); - Navigate(1, url1); - EXPECT_EQ(1, contents->controller()->GetEntryCount()); - - // Show an interstitial. - TestInterstitialPage::InterstitialState state = - TestInterstitialPage::UNDECIDED; - bool deleted = false; - GURL url2("http://interstitial"); - TestInterstitialPage* interstitial = - new TestInterstitialPage(contents, false, url2, &state, &deleted); - interstitial->Show(); - // The interstitial should not show until its navigation has committed. - EXPECT_FALSE(interstitial->is_showing()); - EXPECT_FALSE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == NULL); - // Let's commit the interstitial navigation. - interstitial->TestDidNavigate(1, url2); - EXPECT_TRUE(interstitial->is_showing()); - EXPECT_TRUE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == interstitial); - NavigationEntry* entry = contents->controller()->GetActiveEntry(); - ASSERT_TRUE(entry != NULL); - // The URL specified to the interstitial should have been ignored. - EXPECT_TRUE(entry->url() == url1); - - // Now don't proceed. - interstitial->DontProceed(); - EXPECT_TRUE(deleted); - EXPECT_EQ(TestInterstitialPage::CANCELED, state); - EXPECT_FALSE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == NULL); - entry = contents->controller()->GetActiveEntry(); - ASSERT_TRUE(entry != NULL); - EXPECT_TRUE(entry->url() == url1); - EXPECT_EQ(1, contents->controller()->GetEntryCount()); -} - -// Test navigating to a page (with the navigation initiated from the browser, -// as when a URL is typed in the location bar) that shows an interstitial and -// creates a new navigation entry, then proceeding. -TEST_F(WebContentsTest, ShowInterstitialFromBrowserNewNavigationProceed) { - // Navigate to a page. - GURL url1("http://www.google.com"); - Navigate(1, url1); - EXPECT_EQ(1, contents->controller()->GetEntryCount()); - - // Initiate a browser navigation that will trigger the interstitial - contents->controller()->LoadURL(GURL("http://www.evil.com"), GURL(), - PageTransition::TYPED); - - // Show an interstitial. - TestInterstitialPage::InterstitialState state = - TestInterstitialPage::UNDECIDED; - bool deleted = false; - GURL url2("http://interstitial"); - TestInterstitialPage* interstitial = - new TestInterstitialPage(contents, true, url2, &state, &deleted); - interstitial->Show(); - // The interstitial should not show until its navigation has committed. - EXPECT_FALSE(interstitial->is_showing()); - EXPECT_FALSE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == NULL); - // Let's commit the interstitial navigation. - interstitial->TestDidNavigate(1, url2); - EXPECT_TRUE(interstitial->is_showing()); - EXPECT_TRUE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == interstitial); - NavigationEntry* entry = contents->controller()->GetActiveEntry(); - ASSERT_TRUE(entry != NULL); - EXPECT_TRUE(entry->url() == url2); - - // Then proceed. - interstitial->Proceed(); - // The interstitial should show until the new navigation commits. - ASSERT_FALSE(deleted); - EXPECT_EQ(TestInterstitialPage::OKED, state); - EXPECT_TRUE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == interstitial); - - // Simulate the navigation to the page, that's when the interstitial gets - // hidden. - GURL url3("http://www.thepage.com"); - Navigate(2, url3); - - EXPECT_TRUE(deleted); - EXPECT_FALSE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == NULL); - entry = contents->controller()->GetActiveEntry(); - ASSERT_TRUE(entry != NULL); - EXPECT_TRUE(entry->url() == url3); - - EXPECT_EQ(2, contents->controller()->GetEntryCount()); -} - -// Test navigating to a page (with the navigation initiated from the renderer, -// as when clicking on a link in the page) that shows an interstitial and -// creates a new navigation entry, then proceeding. -TEST_F(WebContentsTest, ShowInterstitialFromRendererNewNavigationProceed) { - // Navigate to a page. - GURL url1("http://www.google.com"); - Navigate(1, url1); - EXPECT_EQ(1, contents->controller()->GetEntryCount()); - - // Show an interstitial. - TestInterstitialPage::InterstitialState state = - TestInterstitialPage::UNDECIDED; - bool deleted = false; - GURL url2("http://interstitial"); - TestInterstitialPage* interstitial = - new TestInterstitialPage(contents, true, url2, &state, &deleted); - interstitial->Show(); - // The interstitial should not show until its navigation has committed. - EXPECT_FALSE(interstitial->is_showing()); - EXPECT_FALSE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == NULL); - // Let's commit the interstitial navigation. - interstitial->TestDidNavigate(1, url2); - EXPECT_TRUE(interstitial->is_showing()); - EXPECT_TRUE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == interstitial); - NavigationEntry* entry = contents->controller()->GetActiveEntry(); - ASSERT_TRUE(entry != NULL); - EXPECT_TRUE(entry->url() == url2); - - // Then proceed. - interstitial->Proceed(); - // The interstitial should show until the new navigation commits. - ASSERT_FALSE(deleted); - EXPECT_EQ(TestInterstitialPage::OKED, state); - EXPECT_TRUE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == interstitial); - - // Simulate the navigation to the page, that's when the interstitial gets - // hidden. - GURL url3("http://www.thepage.com"); - Navigate(2, url3); - - EXPECT_TRUE(deleted); - EXPECT_FALSE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == NULL); - entry = contents->controller()->GetActiveEntry(); - ASSERT_TRUE(entry != NULL); - EXPECT_TRUE(entry->url() == url3); - - EXPECT_EQ(2, contents->controller()->GetEntryCount()); -} - -// Test navigating to a page that shows an interstitial without creating a new -// navigation entry (this happens when the interstitial is triggered by a -// sub-resource in the page), then proceeding. -TEST_F(WebContentsTest, ShowInterstitialNoNewNavigationProceed) { - // Navigate to a page so we have a navigation entry in the controller. - GURL url1("http://www.google.com"); - Navigate(1, url1); - EXPECT_EQ(1, contents->controller()->GetEntryCount()); - - // Show an interstitial. - TestInterstitialPage::InterstitialState state = - TestInterstitialPage::UNDECIDED; - bool deleted = false; - GURL url2("http://interstitial"); - TestInterstitialPage* interstitial = - new TestInterstitialPage(contents, false, url2, &state, &deleted); - interstitial->Show(); - // The interstitial should not show until its navigation has committed. - EXPECT_FALSE(interstitial->is_showing()); - EXPECT_FALSE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == NULL); - // Let's commit the interstitial navigation. - interstitial->TestDidNavigate(1, url2); - EXPECT_TRUE(interstitial->is_showing()); - EXPECT_TRUE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == interstitial); - NavigationEntry* entry = contents->controller()->GetActiveEntry(); - ASSERT_TRUE(entry != NULL); - // The URL specified to the interstitial should have been ignored. - EXPECT_TRUE(entry->url() == url1); - - // Then proceed. - interstitial->Proceed(); - // Since this is not a new navigation, the previous page is dismissed right - // away and shows the original page. - EXPECT_TRUE(deleted); - EXPECT_EQ(TestInterstitialPage::OKED, state); - EXPECT_FALSE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == NULL); - entry = contents->controller()->GetActiveEntry(); - ASSERT_TRUE(entry != NULL); - EXPECT_TRUE(entry->url() == url1); - - EXPECT_EQ(1, contents->controller()->GetEntryCount()); -} - -// Test navigating to a page that shows an interstitial, then navigating away. -TEST_F(WebContentsTest, ShowInterstitialThenNavigate) { - // Show interstitial. - TestInterstitialPage::InterstitialState state = - TestInterstitialPage::UNDECIDED; - bool deleted = false; - GURL url("http://interstitial"); - TestInterstitialPage* interstitial = - new TestInterstitialPage(contents, true, url, &state, &deleted); - interstitial->Show(); - interstitial->TestDidNavigate(1, url); - - // While interstitial showing, navigate to a new URL. - const GURL url2("http://www.yahoo.com"); - Navigate(1, url2); - - EXPECT_TRUE(deleted); - EXPECT_EQ(TestInterstitialPage::CANCELED, state); -} - -// Test navigating to a page that shows an interstitial, then close the tab. -TEST_F(WebContentsTest, ShowInterstitialThenCloseTab) { - // Show interstitial. - TestInterstitialPage::InterstitialState state = - TestInterstitialPage::UNDECIDED; - bool deleted = false; - GURL url("http://interstitial"); - TestInterstitialPage* interstitial = - new TestInterstitialPage(contents, true, url, &state, &deleted); - interstitial->Show(); - interstitial->TestDidNavigate(1, url); - - // Now close the tab. - contents->CloseContents(); - contents = NULL; // So we don't detroy it again on TearDown. - EXPECT_TRUE(deleted); - EXPECT_EQ(TestInterstitialPage::CANCELED, state); -} - -// Test that after Proceed is called and an interstitial is still shown, no more -// commands get executed. -TEST_F(WebContentsTest, ShowInterstitialProceedMultipleCommands) { - // Navigate to a page so we have a navigation entry in the controller. - GURL url1("http://www.google.com"); - Navigate(1, url1); - EXPECT_EQ(1, contents->controller()->GetEntryCount()); - - // Show an interstitial. - TestInterstitialPage::InterstitialState state = - TestInterstitialPage::UNDECIDED; - bool deleted = false; - GURL url2("http://interstitial"); - TestInterstitialPage* interstitial = - new TestInterstitialPage(contents, true, url2, &state, &deleted); - interstitial->Show(); - interstitial->TestDidNavigate(1, url2); - - // Run a command. - EXPECT_EQ(0, interstitial->command_received_count()); - interstitial->TestDomOperationResponse("toto"); - EXPECT_EQ(1, interstitial->command_received_count()); - - // Then proceed. - interstitial->Proceed(); - ASSERT_FALSE(deleted); - - // While the navigation to the new page is pending, send other commands, they - // should be ignored. - interstitial->TestDomOperationResponse("hello"); - interstitial->TestDomOperationResponse("hi"); - EXPECT_EQ(1, interstitial->command_received_count()); -} - -// Test showing an interstitial while another interstitial is already showing. -TEST_F(WebContentsTest, ShowInterstitialOnInterstitial) { - // Navigate to a page so we have a navigation entry in the controller. - GURL start_url("http://www.google.com"); - Navigate(1, start_url); - EXPECT_EQ(1, contents->controller()->GetEntryCount()); - - // Show an interstitial. - TestInterstitialPage::InterstitialState state1 = - TestInterstitialPage::UNDECIDED; - bool deleted1 = false; - GURL url1("http://interstitial1"); - TestInterstitialPage* interstitial1 = - new TestInterstitialPage(contents, true, url1, &state1, &deleted1); - interstitial1->Show(); - interstitial1->TestDidNavigate(1, url1); - - // Now show another interstitial. - TestInterstitialPage::InterstitialState state2 = - TestInterstitialPage::UNDECIDED; - bool deleted2 = false; - GURL url2("http://interstitial2"); - TestInterstitialPage* interstitial2 = - new TestInterstitialPage(contents, true, url2, &state2, &deleted2); - interstitial2->Show(); - interstitial2->TestDidNavigate(1, url2); - - // Showing interstitial2 should have caused interstitial1 to go away. - EXPECT_TRUE(deleted1); - EXPECT_EQ(TestInterstitialPage::CANCELED, state1); - - // Let's make sure interstitial2 is working as intended. - ASSERT_FALSE(deleted2); - EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2); - interstitial2->Proceed(); - GURL landing_url("http://www.thepage.com"); - Navigate(2, landing_url); - - EXPECT_TRUE(deleted2); - EXPECT_FALSE(contents->showing_interstitial_page()); - EXPECT_TRUE(contents->interstitial_page() == NULL); - NavigationEntry* entry = contents->controller()->GetActiveEntry(); - ASSERT_TRUE(entry != NULL); - EXPECT_TRUE(entry->url() == landing_url); - EXPECT_EQ(2, contents->controller()->GetEntryCount()); -} - -// Test that navigating away from an interstitial while it's loading cause it -// not to show. -TEST_F(WebContentsTest, NavigateBeforeInterstitialShows) { - // Show an interstitial. - TestInterstitialPage::InterstitialState state = - TestInterstitialPage::UNDECIDED; - bool deleted = false; - GURL interstitial_url("http://interstitial"); - TestInterstitialPage* interstitial = - new TestInterstitialPage(contents, true, interstitial_url, - &state, &deleted); - interstitial->Show(); - - // Let's simulate a navigation initiated from the browser before the - // interstitial finishes loading. - const GURL url("http://www.google.com"); - contents->controller()->LoadURL(url, GURL(), PageTransition::TYPED); - ASSERT_FALSE(deleted); - EXPECT_FALSE(interstitial->is_showing()); - - // Now let's make the interstitial navigation commit. - interstitial->TestDidNavigate(1, interstitial_url); - - // After it loaded the interstitial should be gone. - EXPECT_TRUE(deleted); - EXPECT_EQ(TestInterstitialPage::CANCELED, state); -} - -// Test showing an interstitial and have its renderer crash. -TEST_F(WebContentsTest, InterstitialCrasher) { - // Show an interstitial. - TestInterstitialPage::InterstitialState state = - TestInterstitialPage::UNDECIDED; - bool deleted = false; - GURL url("http://interstitial"); - TestInterstitialPage* interstitial = - new TestInterstitialPage(contents, true, url, &state, &deleted); - interstitial->Show(); - // Simulate a renderer crash before the interstitial is shown. - interstitial->TestRendererGone(); - // The interstitial should have been dismissed. - EXPECT_TRUE(deleted); - EXPECT_EQ(TestInterstitialPage::CANCELED, state); - - // Now try again but this time crash the intersitial after it was shown. - interstitial = - new TestInterstitialPage(contents, true, url, &state, &deleted); - interstitial->Show(); - interstitial->TestDidNavigate(1, url); - // Simulate a renderer crash. - interstitial->TestRendererGone(); - // The interstitial should have been dismissed. - EXPECT_TRUE(deleted); - EXPECT_EQ(TestInterstitialPage::CANCELED, state); -} diff --git a/chrome/browser/web_contents_view_win.cc b/chrome/browser/web_contents_view_win.cc index dda346c..e97f6ac 100644 --- a/chrome/browser/web_contents_view_win.cc +++ b/chrome/browser/web_contents_view_win.cc @@ -10,7 +10,6 @@ #include "chrome/browser/browser.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/download/download_request_manager.h" -#include "chrome/browser/interstitial_page.h" #include "chrome/browser/render_view_context_menu.h" #include "chrome/browser/render_view_context_menu_controller.h" #include "chrome/browser/render_view_host.h" @@ -596,8 +595,6 @@ void WebContentsViewWin::WasShown() { } void WebContentsViewWin::WasSized(const gfx::Size& size) { - if (web_contents_->interstitial_page()) - web_contents_->interstitial_page()->SetSize(size); if (web_contents_->render_widget_host_view()) web_contents_->render_widget_host_view()->SetSize(size); if (find_bar_.get()) |