summaryrefslogtreecommitdiffstats
path: root/chrome/browser/interstitial_page.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/interstitial_page.cc')
-rw-r--r--chrome/browser/interstitial_page.cc211
1 files changed, 175 insertions, 36 deletions
diff --git a/chrome/browser/interstitial_page.cc b/chrome/browser/interstitial_page.cc
index e400730..514e822 100644
--- a/chrome/browser/interstitial_page.cc
+++ b/chrome/browser/interstitial_page.cc
@@ -5,54 +5,59 @@
#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/tab_contents.h"
+#include "chrome/browser/render_widget_host_view_win.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(TabContents* tab,
- bool create_navigation_entry,
+InterstitialPage::InterstitialPage(WebContents* tab,
+ bool new_navigation,
const GURL& url)
: tab_(tab),
url_(url),
- delegate_has_been_notified_(false),
- create_navigation_entry_(create_navigation_entry) {
+ action_taken_(false),
+ enabled_(true),
+ new_navigation_(new_navigation),
+ render_view_host_(NULL),
+ should_revert_tab_title_(false) {
InitInterstitialPageMap();
-
- // 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_));
+ // 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());
}
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() {
- DCHECK(tab_->type() == TAB_CONTENTS_WEB);
- WebContents* tab = tab_->AsWebContents();
+ // If an interstitial is already showing, close it before showing the new one.
+ if (tab_->interstitial_page())
+ tab_->interstitial_page()->DontProceed();
+
+ // 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_) {
+ if (new_navigation_) {
NavigationEntry* entry = new NavigationEntry(TAB_CONTENTS_WEB);
entry->set_url(url_);
entry->set_display_url(url_);
@@ -64,39 +69,173 @@ void InterstitialPage::Show() {
tab_->controller()->AddTransientEntry(entry);
}
- tab->ShowInterstitialPage(this);
+ 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()));
+}
+
+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);
+ }
+ delete this;
}
void InterstitialPage::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
- DCHECK(type == NOTIFY_DOM_OPERATION_RESPONSE);
- std::string json = Details<DomOperationNotificationDetails>(details)->json();
- CommandReceived(json);
+ 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!
+ }
}
-void InterstitialPage::InterstitialClosed() {
- delete this;
+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::Proceed() {
- DCHECK(tab_->type() == TAB_CONTENTS_WEB);
- tab_->AsWebContents()->HideInterstitialPage(true, true);
+ 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!
+ }
}
void InterstitialPage::DontProceed() {
- if (create_navigation_entry_) {
+ DCHECK(!action_taken_);
+ Disable();
+ action_taken_ = true;
+
+ if (new_navigation_) {
// 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_)
@@ -105,10 +244,10 @@ void InterstitialPage::InitInterstitialPageMap() {
// static
InterstitialPage* InterstitialPage::GetInterstitialPage(
- TabContents* tab_contents) {
+ WebContents* web_contents) {
InitInterstitialPageMap();
InterstitialPageMap::const_iterator iter =
- tab_to_interstitial_page_->find(tab_contents);
+ tab_to_interstitial_page_->find(web_contents);
if (iter == tab_to_interstitial_page_->end())
return NULL;