summaryrefslogtreecommitdiffstats
path: root/chrome/browser/tab_contents/tab_contents.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/tab_contents/tab_contents.cc')
-rw-r--r--chrome/browser/tab_contents/tab_contents.cc605
1 files changed, 605 insertions, 0 deletions
diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc
new file mode 100644
index 0000000..70988e2
--- /dev/null
+++ b/chrome/browser/tab_contents/tab_contents.cc
@@ -0,0 +1,605 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/tab_contents/tab_contents.h"
+
+#include "chrome/browser/cert_store.h"
+#include "chrome/browser/views/download_shelf_view.h"
+#include "chrome/browser/views/download_started_animation.h"
+#include "chrome/browser/views/blocked_popup_container.h"
+#include "chrome/browser/tab_contents/infobar_delegate.h"
+#include "chrome/browser/tab_contents/navigation_entry.h"
+#include "chrome/browser/tab_contents/tab_contents_delegate.h"
+#include "chrome/browser/tab_contents/web_contents.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/views/native_scroll_bar.h"
+#include "chrome/views/root_view.h"
+#include "chrome/views/view.h"
+#include "chrome/views/view_storage.h"
+#include "chrome/views/widget.h"
+
+#include "generated_resources.h"
+
+namespace {
+
+BOOL CALLBACK InvalidateWindow(HWND hwnd, LPARAM lparam) {
+ // Note: erase is required to properly paint some widgets borders. This can
+ // be seen with textfields.
+ InvalidateRect(hwnd, NULL, TRUE);
+ return TRUE;
+}
+
+} // namespace
+
+TabContents::TabContents(TabContentsType type)
+ : type_(type),
+ delegate_(NULL),
+ controller_(NULL),
+ is_loading_(false),
+ is_active_(true),
+ is_crashed_(false),
+ waiting_for_response_(false),
+ shelf_visible_(false),
+ max_page_id_(-1),
+ blocked_popups_(NULL),
+ capturing_contents_(false),
+ is_being_destroyed_(false) {
+ last_focused_view_storage_id_ =
+ views::ViewStorage::GetSharedInstance()->CreateStorageID();
+}
+
+TabContents::~TabContents() {
+ // Makes sure to remove any stored view we may still have in the ViewStorage.
+ //
+ // It is possible the view went away before us, so we only do this if the
+ // view is registered.
+ views::ViewStorage* view_storage = views::ViewStorage::GetSharedInstance();
+ if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL)
+ view_storage->RemoveView(last_focused_view_storage_id_);
+}
+
+// static
+void TabContents::RegisterUserPrefs(PrefService* prefs) {
+ prefs->RegisterBooleanPref(prefs::kBlockPopups, false);
+}
+
+void TabContents::CloseContents() {
+ // Destroy our NavigationController, which will Destroy all tabs it owns.
+ controller_->Destroy();
+ // Note that the controller may have deleted us at this point,
+ // so don't touch any member variables here.
+}
+
+void TabContents::Destroy() {
+ DCHECK(!is_being_destroyed_);
+ is_being_destroyed_ = true;
+
+ // First cleanly close all child windows.
+ // TODO(mpcomplete): handle case if MaybeCloseChildWindows() already asked
+ // some of these to close. CloseWindows is async, so it might get called
+ // twice before it runs.
+ int size = static_cast<int>(child_windows_.size());
+ for (int i = size - 1; i >= 0; --i) {
+ ConstrainedWindow* window = child_windows_[i];
+ if (window)
+ window->CloseConstrainedWindow();
+ }
+
+ // Notify any observer that have a reference on this tab contents.
+ NotificationService::current()->Notify(NOTIFY_TAB_CONTENTS_DESTROYED,
+ Source<TabContents>(this),
+ NotificationService::NoDetails());
+
+ // If we still have a window handle, destroy it. GetContainerHWND can return
+ // NULL if this contents was part of a window that closed.
+ if (GetContainerHWND())
+ ::DestroyWindow(GetContainerHWND());
+
+ // Notify our NavigationController. Make sure we are deleted first, so
+ // that the controller is the last to die.
+ NavigationController* controller = controller_;
+ TabContentsType type = this->type();
+
+ delete this;
+
+ controller->TabContentsWasDestroyed(type);
+}
+
+void TabContents::SetupController(Profile* profile) {
+ DCHECK(!controller_);
+ controller_ = new NavigationController(this, profile);
+}
+
+bool TabContents::SupportsURL(GURL* url) {
+ GURL u(*url);
+ if (TabContents::TypeForURL(&u) == type()) {
+ *url = u;
+ return true;
+ }
+ return false;
+}
+
+const GURL& TabContents::GetURL() const {
+ // We may not have a navigation entry yet
+ NavigationEntry* entry = controller_->GetActiveEntry();
+ return entry ? entry->display_url() : GURL::EmptyGURL();
+}
+
+const std::wstring& TabContents::GetTitle() const {
+ // We use the title for the last committed entry rather than a pending
+ // navigation entry. For example, when the user types in a URL, we want to
+ // keep the old page's title until the new load has committed and we get a new
+ // title.
+ // The exception is with transient pages, for which we really want to use
+ // their title, as they are not committed.
+ NavigationEntry* entry = controller_->GetTransientEntry();
+ if (entry)
+ return entry->GetTitleForDisplay();
+
+ entry = controller_->GetLastCommittedEntry();
+ if (entry)
+ return entry->GetTitleForDisplay();
+ else if (controller_->LoadingURLLazily())
+ return controller_->GetLazyTitle();
+ return EmptyWString();
+}
+
+int32 TabContents::GetMaxPageID() {
+ if (GetSiteInstance())
+ return GetSiteInstance()->max_page_id();
+ else
+ return max_page_id_;
+}
+
+void TabContents::UpdateMaxPageID(int32 page_id) {
+ // Ensure both the SiteInstance and RenderProcessHost update their max page
+ // IDs in sync. Only WebContents will also have site instances, except during
+ // testing.
+ if (GetSiteInstance())
+ GetSiteInstance()->UpdateMaxPageID(page_id);
+
+ if (AsWebContents())
+ AsWebContents()->process()->UpdateMaxPageID(page_id);
+ else
+ max_page_id_ = std::max(max_page_id_, page_id);
+}
+
+const std::wstring TabContents::GetDefaultTitle() const {
+ return l10n_util::GetString(IDS_DEFAULT_TAB_TITLE);
+}
+
+SkBitmap TabContents::GetFavIcon() const {
+ // Like GetTitle(), we also want to use the favicon for the last committed
+ // entry rather than a pending navigation entry.
+ NavigationEntry* entry = controller_->GetTransientEntry();
+ if (entry)
+ return entry->favicon().bitmap();
+
+ entry = controller_->GetLastCommittedEntry();
+ if (entry)
+ return entry->favicon().bitmap();
+ else if (controller_->LoadingURLLazily())
+ return controller_->GetLazyFavIcon();
+ return SkBitmap();
+}
+
+SecurityStyle TabContents::GetSecurityStyle() const {
+ // We may not have a navigation entry yet.
+ NavigationEntry* entry = controller_->GetActiveEntry();
+ return entry ? entry->ssl().security_style() : SECURITY_STYLE_UNKNOWN;
+}
+
+bool TabContents::GetSSLEVText(std::wstring* ev_text,
+ std::wstring* ev_tooltip_text) const {
+ DCHECK(ev_text && ev_tooltip_text);
+ ev_text->clear();
+ ev_tooltip_text->clear();
+
+ NavigationEntry* entry = controller_->GetActiveEntry();
+ if (!entry ||
+ net::IsCertStatusError(entry->ssl().cert_status()) ||
+ ((entry->ssl().cert_status() & net::CERT_STATUS_IS_EV) == 0))
+ return false;
+
+ scoped_refptr<net::X509Certificate> cert;
+ CertStore::GetSharedInstance()->RetrieveCert(entry->ssl().cert_id(), &cert);
+ if (!cert.get()) {
+ NOTREACHED();
+ return false;
+ }
+
+ return SSLManager::GetEVCertNames(*cert, ev_text, ev_tooltip_text);
+}
+
+void TabContents::SetIsCrashed(bool state) {
+ if (state == is_crashed_)
+ return;
+
+ is_crashed_ = state;
+ if (delegate_)
+ delegate_->ContentsStateChanged(this);
+}
+
+void TabContents::NotifyNavigationStateChanged(unsigned changed_flags) {
+ if (delegate_)
+ delegate_->NavigationStateChanged(this, changed_flags);
+}
+
+void TabContents::DidBecomeSelected() {
+ if (controller_)
+ controller_->SetActive(true);
+
+ // Invalidate all descendants. (take care to exclude invalidating ourselves!)
+ EnumChildWindows(GetContainerHWND(), InvalidateWindow, 0);
+}
+
+void TabContents::WasHidden() {
+ NotificationService::current()->Notify(NOTIFY_TAB_CONTENTS_HIDDEN,
+ Source<TabContents>(this),
+ NotificationService::NoDetails());
+}
+
+void TabContents::Activate() {
+ if (delegate_)
+ delegate_->ActivateContents(this);
+}
+
+void TabContents::OpenURL(const GURL& url, const GURL& referrer,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition) {
+ if (delegate_)
+ delegate_->OpenURLFromTab(this, url, referrer, disposition, transition);
+}
+
+bool TabContents::NavigateToPendingEntry(bool reload) {
+ // Our benavior is just to report that the entry was committed.
+ controller()->GetPendingEntry()->set_title(GetDefaultTitle());
+ controller()->CommitPendingEntry();
+ return true;
+}
+
+ConstrainedWindow* TabContents::CreateConstrainedDialog(
+ views::WindowDelegate* window_delegate,
+ views::View* contents_view) {
+ ConstrainedWindow* window =
+ ConstrainedWindow::CreateConstrainedDialog(
+ this, gfx::Rect(), contents_view, window_delegate);
+ child_windows_.push_back(window);
+ return window;
+}
+
+void TabContents::AddNewContents(TabContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture) {
+ if (!delegate_)
+ return;
+
+ if ((disposition == NEW_POPUP) && !user_gesture) {
+ // Unrequested popups from normal pages are constrained.
+ TabContents* popup_owner = this;
+ TabContents* our_owner = delegate_->GetConstrainingContents(this);
+ if (our_owner)
+ popup_owner = our_owner;
+ popup_owner->AddConstrainedPopup(new_contents, initial_pos);
+ } else {
+ new_contents->DisassociateFromPopupCount();
+
+ delegate_->AddNewContents(this, new_contents, disposition, initial_pos,
+ user_gesture);
+
+ PopupNotificationVisibilityChanged(ShowingBlockedPopupNotification());
+ }
+}
+
+void TabContents::AddConstrainedPopup(TabContents* new_contents,
+ const gfx::Rect& initial_pos) {
+ if (!blocked_popups_) {
+ CRect client_rect;
+ GetClientRect(GetContainerHWND(), &client_rect);
+ gfx::Point anchor_position(
+ client_rect.Width() -
+ views::NativeScrollBar::GetVerticalScrollBarWidth(),
+ client_rect.Height());
+
+ blocked_popups_ = BlockedPopupContainer::Create(
+ this, profile(), anchor_position);
+ child_windows_.push_back(blocked_popups_);
+ }
+
+ blocked_popups_->AddTabContents(new_contents, initial_pos);
+ PopupNotificationVisibilityChanged(ShowingBlockedPopupNotification());
+}
+
+void TabContents::CloseAllSuppressedPopups() {
+ if (blocked_popups_)
+ blocked_popups_->CloseAllPopups();
+}
+
+void TabContents::Focus() {
+ HWND container_hwnd = GetContainerHWND();
+ if (!container_hwnd)
+ return;
+
+ views::FocusManager* focus_manager =
+ views::FocusManager::GetFocusManager(container_hwnd);
+ DCHECK(focus_manager);
+ views::View* v = focus_manager->GetViewForWindow(container_hwnd, true);
+ DCHECK(v);
+ if (v)
+ v->RequestFocus();
+}
+
+void TabContents::StoreFocus() {
+ views::ViewStorage* view_storage =
+ views::ViewStorage::GetSharedInstance();
+
+ if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL)
+ view_storage->RemoveView(last_focused_view_storage_id_);
+
+ views::FocusManager* focus_manager =
+ views::FocusManager::GetFocusManager(GetContainerHWND());
+ if (focus_manager) {
+ // |focus_manager| can be NULL if the tab has been detached but still
+ // exists.
+ views::View* focused_view = focus_manager->GetFocusedView();
+ if (focused_view)
+ view_storage->StoreView(last_focused_view_storage_id_, focused_view);
+
+ // If the focus was on the page, explicitly clear the focus so that we
+ // don't end up with the focused HWND not part of the window hierarchy.
+ // TODO(brettw) this should move to the view somehow.
+ HWND container_hwnd = GetContainerHWND();
+ if (container_hwnd) {
+ views::View* focused_view = focus_manager->GetFocusedView();
+ if (focused_view) {
+ HWND hwnd = focused_view->GetRootView()->GetWidget()->GetHWND();
+ if (container_hwnd == hwnd || ::IsChild(container_hwnd, hwnd))
+ focus_manager->ClearFocus();
+ }
+ }
+ }
+}
+
+void TabContents::RestoreFocus() {
+ views::ViewStorage* view_storage =
+ views::ViewStorage::GetSharedInstance();
+ views::View* last_focused_view =
+ view_storage->RetrieveView(last_focused_view_storage_id_);
+
+ if (!last_focused_view) {
+ SetInitialFocus();
+ } else {
+ views::FocusManager* focus_manager =
+ views::FocusManager::GetFocusManager(GetContainerHWND());
+
+ // If you hit this DCHECK, please report it to Jay (jcampan).
+ DCHECK(focus_manager != NULL) << "No focus manager when restoring focus.";
+
+ if (focus_manager && focus_manager->ContainsView(last_focused_view)) {
+ last_focused_view->RequestFocus();
+ } else {
+ // The focused view may not belong to the same window hierarchy (for
+ // example if the location bar was focused and the tab is dragged out).
+ // In that case we default to the default focus.
+ SetInitialFocus();
+ }
+ view_storage->RemoveView(last_focused_view_storage_id_);
+ }
+}
+
+void TabContents::SetInitialFocus() {
+ ::SetFocus(GetContainerHWND());
+}
+
+void TabContents::AddInfoBar(InfoBarDelegate* delegate) {
+ // Look through the existing InfoBarDelegates we have for a match. If we've
+ // already got one that matches, then we don't add the new one.
+ for (int i = 0; i < infobar_delegate_count(); ++i) {
+ if (GetInfoBarDelegateAt(i)->EqualsDelegate(delegate))
+ return;
+ }
+
+ infobar_delegates_.push_back(delegate);
+ NotificationService::current()->Notify(NOTIFY_TAB_CONTENTS_INFOBAR_ADDED,
+ Source<TabContents>(this),
+ Details<InfoBarDelegate>(delegate));
+
+ // Add ourselves as an observer for navigations the first time a delegate is
+ // added. We use this notification to expire InfoBars that need to expire on
+ // page transitions.
+ if (infobar_delegates_.size() == 1) {
+ DCHECK(controller());
+ registrar_.Add(this, NOTIFY_NAV_ENTRY_COMMITTED,
+ Source<NavigationController>(controller()));
+ }
+}
+
+void TabContents::RemoveInfoBar(InfoBarDelegate* delegate) {
+ std::vector<InfoBarDelegate*>::iterator it =
+ find(infobar_delegates_.begin(), infobar_delegates_.end(), delegate);
+ if (it != infobar_delegates_.end()) {
+ InfoBarDelegate* delegate = *it;
+ NotificationService::current()->Notify(NOTIFY_TAB_CONTENTS_INFOBAR_REMOVED,
+ Source<TabContents>(this),
+ Details<InfoBarDelegate>(delegate));
+ infobar_delegates_.erase(it);
+
+ // Remove ourselves as an observer if we are tracking no more InfoBars.
+ if (infobar_delegates_.empty()) {
+ registrar_.Remove(this, NOTIFY_NAV_ENTRY_COMMITTED,
+ Source<NavigationController>(controller()));
+ }
+ }
+}
+
+void TabContents::SetDownloadShelfVisible(bool visible) {
+ if (shelf_visible_ != visible) {
+ if (visible) {
+ // Invoke GetDownloadShelfView to force the shelf to be created.
+ GetDownloadShelfView();
+ }
+ shelf_visible_ = visible;
+
+ if (delegate_)
+ delegate_->ContentsStateChanged(this);
+ }
+
+ // SetShelfVisible can force-close the shelf, so make sure we lay out
+ // everything correctly, as if the animation had finished. This doesn't
+ // matter for showing the shelf, as the show animation will do it.
+ ToolbarSizeChanged(false);
+}
+
+void TabContents::ToolbarSizeChanged(bool is_animating) {
+ TabContentsDelegate* d = delegate();
+ if (d)
+ d->ToolbarSizeChanged(this, is_animating);
+}
+
+void TabContents::OnStartDownload(DownloadItem* download) {
+ DCHECK(download);
+ TabContents* tab_contents = this;
+
+ // Download in a constrained popup is shown in the tab that opened it.
+ TabContents* constraining_tab = delegate()->GetConstrainingContents(this);
+ if (constraining_tab)
+ tab_contents = constraining_tab;
+
+ // GetDownloadShelfView creates the download shelf if it was not yet created.
+ tab_contents->GetDownloadShelfView()->AddDownload(download);
+ tab_contents->SetDownloadShelfVisible(true);
+
+ // This animation will delete itself when it finishes, or if we become hidden
+ // or destroyed.
+ if (IsWindowVisible(GetContainerHWND())) { // For minimized windows, unit
+ // tests, etc.
+ new DownloadStartedAnimation(tab_contents);
+ }
+}
+
+DownloadShelfView* TabContents::GetDownloadShelfView() {
+ if (!download_shelf_view_.get()) {
+ download_shelf_view_.reset(new DownloadShelfView(this));
+ // The TabContents owns the download-shelf.
+ download_shelf_view_->SetParentOwned(false);
+ }
+ return download_shelf_view_.get();
+}
+
+void TabContents::MigrateShelfViewFrom(TabContents* tab_contents) {
+ download_shelf_view_.reset(tab_contents->GetDownloadShelfView());
+ download_shelf_view_->ChangeTabContents(tab_contents, this);
+ tab_contents->ReleaseDownloadShelfView();
+}
+
+void TabContents::WillClose(ConstrainedWindow* window) {
+ ConstrainedWindowList::iterator it =
+ find(child_windows_.begin(), child_windows_.end(), window);
+ if (it != child_windows_.end())
+ child_windows_.erase(it);
+
+ if (window == blocked_popups_)
+ blocked_popups_ = NULL;
+
+ if (::IsWindow(GetContainerHWND())) {
+ CRect client_rect;
+ GetClientRect(GetContainerHWND(), &client_rect);
+ RepositionSupressedPopupsToFit(
+ gfx::Size(client_rect.Width(), client_rect.Height()));
+ }
+}
+
+void TabContents::DidMoveOrResize(ConstrainedWindow* window) {
+ UpdateWindow(GetContainerHWND());
+}
+
+void TabContents::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NOTIFY_NAV_ENTRY_COMMITTED);
+ DCHECK(controller() == Source<NavigationController>(source).ptr());
+
+ NavigationController::LoadCommittedDetails& committed_details =
+ *(Details<NavigationController::LoadCommittedDetails>(details).ptr());
+ ExpireInfoBars(committed_details);
+}
+
+// static
+void TabContents::MigrateShelfView(TabContents* from, TabContents* to) {
+ bool was_shelf_visible = from->IsDownloadShelfVisible();
+ if (was_shelf_visible)
+ to->MigrateShelfViewFrom(from);
+ to->SetDownloadShelfVisible(was_shelf_visible);
+}
+
+void TabContents::SetIsLoading(bool is_loading,
+ LoadNotificationDetails* details) {
+ if (is_loading == is_loading_)
+ return;
+
+ is_loading_ = is_loading;
+ waiting_for_response_ = is_loading;
+
+ // Suppress notifications for this TabContents if we are not active.
+ if (!is_active_)
+ return;
+
+ if (delegate_)
+ delegate_->LoadingStateChanged(this);
+
+ NotificationService::current()->
+ Notify((is_loading ? NOTIFY_LOAD_START : NOTIFY_LOAD_STOP),
+ Source<NavigationController>(this->controller()),
+ details ? Details<LoadNotificationDetails>(details) :
+ NotificationService::NoDetails());
+}
+
+// TODO(brettw) This should be on the WebContentsView.
+void TabContents::RepositionSupressedPopupsToFit(const gfx::Size& new_size) {
+ // TODO(erg): There's no way to detect whether scroll bars are
+ // visible, so for beta, we're just going to assume that the
+ // vertical scroll bar is visible, and not care about covering up
+ // the horizontal scroll bar. Fixing this is half of
+ // http://b/1118139.
+ gfx::Point anchor_position(
+ new_size.width() -
+ views::NativeScrollBar::GetVerticalScrollBarWidth(),
+ new_size.height());
+
+ if (blocked_popups_)
+ blocked_popups_->RepositionConstrainedWindowTo(anchor_position);
+}
+
+void TabContents::ReleaseDownloadShelfView() {
+ download_shelf_view_.release();
+}
+
+bool TabContents::ShowingBlockedPopupNotification() const {
+ return blocked_popups_ != NULL &&
+ blocked_popups_->GetTabContentsCount() != 0;
+}
+
+namespace {
+bool TransitionIsReload(PageTransition::Type transition) {
+ return PageTransition::StripQualifier(transition) == PageTransition::RELOAD;
+}
+}
+
+void TabContents::ExpireInfoBars(
+ const NavigationController::LoadCommittedDetails& details) {
+ // Only hide InfoBars when the user has done something that makes the main
+ // frame load. We don't want various automatic or subframe navigations making
+ // it disappear.
+ if (!details.is_user_initiated_main_frame_load())
+ return;
+
+ for (int i = infobar_delegate_count() - 1; i >= 0; --i) {
+ InfoBarDelegate* delegate = GetInfoBarDelegateAt(i);
+ if (delegate->ShouldExpire(details))
+ RemoveInfoBar(delegate);
+ }
+}