Add chrome to the repository.


git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/tab_contents.cc b/chrome/browser/tab_contents.cc
new file mode 100644
index 0000000..4706809
--- /dev/null
+++ b/chrome/browser/tab_contents.cc
@@ -0,0 +1,601 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/tab_contents.h"
+
+#include "chrome/browser/cert_store.h"
+#include "chrome/browser/navigation_entry.h"
+#include "chrome/browser/views/download_shelf_view.h"
+#include "chrome/browser/views/download_started_animation.h"
+#include "chrome/browser/web_contents.h"
+#include "chrome/browser/tab_contents_delegate.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/views/focus_manager.h"
+#include "chrome/views/native_scroll_bar.h"
+#include "chrome/views/view.h"
+#include "chrome/views/view_storage.h"
+
+#include "generated_resources.h"
+
+TabContents::TabContents(TabContentsType type)
+    : is_loading_(false),
+      response_started_(false),
+      is_active_(true),
+      type_(type),
+      delegate_(NULL),
+      controller_(NULL),
+      max_page_id_(-1),
+      saved_location_bar_state_(NULL),
+      is_crashed_(false),
+      shelf_visible_(false) {
+  last_focused_view_storage_id_ =
+      ChromeViews::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.
+  ChromeViews::ViewStorage* view_storage =
+      ChromeViews::ViewStorage::GetSharedInstance();
+  if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL)
+    view_storage->RemoveView(last_focused_view_storage_id_);
+}
+
+void TabContents::HideContents() {
+  // Hide the contents before adjusting its parent to avoid a full desktop
+  // flicker.
+  ShowWindow(GetContainerHWND(), SW_HIDE);
+
+  // Reset the parent to NULL to ensure hidden tabs don't receive messages.
+  SetParent(GetContainerHWND(), NULL);
+
+  // Remove any focus manager related information.
+  ChromeViews::FocusManager::UninstallFocusSubclass(GetContainerHWND());
+
+  WasHidden();
+}
+
+int32 TabContents::GetMaxPageID() {
+  if (AsWebContents())
+    return AsWebContents()->site_instance()->max_page_id();
+  else
+    return max_page_id_;
+}
+
+void TabContents::UpdateMaxPageID(int32 page_id) {
+  if (AsWebContents()) {
+    // Ensure both the SiteInstance and RenderProcessHost update their max page
+    // IDs in sync.
+    AsWebContents()->site_instance()->UpdateMaxPageID(page_id);
+    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);
+}
+
+void TabContents::SetupController(Profile* profile) {
+  DCHECK(!controller_);
+  controller_ = new NavigationController(this, profile);
+}
+
+const GURL& TabContents::GetURL() const {
+  DCHECK(controller_);
+
+  static const GURL kEmptyURL;
+
+  // We may not have a navigation entry yet
+  NavigationEntry* entry = controller_->GetActiveEntry();
+  return entry ? entry->GetDisplayURL() : kEmptyURL;
+}
+
+const std::wstring& TabContents::GetTitle() const {
+  DCHECK(controller_);
+
+  // We always want to 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.
+  NavigationEntry* entry = controller_->GetLastCommittedEntry();
+  if (entry)
+    return entry->GetTitle();
+  else if (controller_->LoadingURLLazily())
+    return controller_->GetLazyTitle();
+  return EmptyWString();
+}
+
+SkBitmap TabContents::GetFavIcon() const {
+  DCHECK(controller_);
+
+  // Like GetTitle(), we also want to use the favicon for the last committed
+  // entry rather than a pending navigation entry.
+  NavigationEntry* entry = controller_->GetLastCommittedEntry();
+  if (entry)
+    return entry->GetFavIcon();
+  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->GetSecurityStyle() : 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->GetSSLCertStatus()) ||
+      ((entry->GetSSLCertStatus() & net::CERT_STATUS_IS_EV) == 0))
+    return false;
+
+  scoped_refptr<X509Certificate> cert;
+  CertStore::GetSharedInstance()->RetrieveCert(entry->GetSSLCertID(), &cert);
+  if (!cert.get()) {
+    NOTREACHED();
+    return false;
+  }
+
+  return SSLManager::GetEVCertNames(*cert, ev_text, ev_tooltip_text);
+}
+
+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() {
+  // 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);
+}
+
+ConstrainedWindow* TabContents::CreateConstrainedDialog(
+    ChromeViews::WindowDelegate* window_delegate,
+    ChromeViews::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) && !delegate_->IsPopup(this)) {
+    if (user_gesture) {
+      // TODO(erg): Need a better policy about initial placement of
+      // popup windows.
+      gfx::Rect initial_bounds = initial_pos;
+      if (initial_bounds.x() == 0 || initial_bounds.y() == 0) {
+        ConstrainedWindow::GenerateInitialBounds(
+            initial_pos, this, &initial_bounds);
+      }
+
+      delegate_->AddNewContents(this, new_contents, disposition, initial_bounds,
+                                user_gesture);
+    } else {
+      AddConstrainedPopup(new_contents, initial_pos);
+    }
+  } else {
+    delegate_->AddNewContents(this, new_contents, disposition, initial_pos,
+                              user_gesture);
+  }
+}
+
+void TabContents::AddConstrainedPopup(TabContents* new_contents,
+                                      const gfx::Rect& initial_pos) {
+  ConstrainedWindow* window =
+      ConstrainedWindow::CreateConstrainedPopup(
+          this, initial_pos, new_contents);
+  child_windows_.push_back(window);
+
+  CRect client_rect;
+  GetClientRect(GetContainerHWND(), &client_rect);
+  gfx::Size new_size(client_rect.Width(), client_rect.Height());
+  RepositionSupressedPopupsToFit(new_size);
+}
+
+void TabContents::SetIsLoading(bool is_loading,
+                               LoadNotificationDetails* details) {
+  if (is_loading == is_loading_)
+    return;
+
+  is_loading_ = is_loading;
+  response_started_ = 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());
+}
+
+void TabContents::DidNavigateToEntry(NavigationEntry* entry) {
+  // The entry may be deleted by DidNavigateToEntry...
+  int new_page_id = entry->GetPageID();
+
+  controller_->DidNavigateToEntry(entry);
+
+  // update after informing the navigation controller so it can check the
+  // previous value of the max page id.
+  UpdateMaxPageID(new_page_id);
+}
+
+bool TabContents::Navigate(const NavigationEntry& entry, bool reload) {
+  NavigationEntry* new_entry = new NavigationEntry(entry);
+  if (new_entry->GetPageID() == -1) {
+    // This is a new navigation.  Our behavior is to always navigate to the
+    // same page (page 0) in response to a navigation.
+    new_entry->SetPageID(0);
+    new_entry->SetTitle(GetDefaultTitle());
+  }
+  DidNavigateToEntry(new_entry);
+  return true;
+}
+
+void TabContents::NotifyNavigationStateChanged(unsigned changed_flags) {
+  if (delegate_)
+    delegate_->NavigationStateChanged(this, changed_flags);
+}
+
+void TabContents::NotifyDidNavigate(NavigationType nav_type,
+                                    int relative_navigation_offset) {
+  if (delegate_)
+    delegate_->DidNavigate(nav_type, relative_navigation_offset);
+}
+
+static 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;
+}
+
+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::CloseAllSuppressedPopups() {
+  // Close all auto positioned child windows to "clean up" the workspace.
+  int count = static_cast<int>(child_windows_.size());
+  for (int i = count - 1; i >= 0; --i) {
+    ConstrainedWindow* window = child_windows_.at(i);
+    if (window->IsSuppressedConstrainedWindow())
+      window->CloseConstrainedWindow();
+  }
+}
+
+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);
+  }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TabContents, ConstrainedTabContentsDelegate implementation:
+
+void TabContents::AddNewContents(ConstrainedWindow* window,
+                                 TabContents* new_contents,
+                                 WindowOpenDisposition disposition,
+                                 const gfx::Rect& initial_pos,
+                                 bool user_gesture) {
+  AddNewContents(new_contents, disposition, initial_pos, user_gesture);
+}
+
+void TabContents::OpenURL(ConstrainedWindow* window,
+                          const GURL& url,
+                          WindowOpenDisposition disposition,
+                          PageTransition::Type transition) {
+  OpenURL(url, disposition, transition);
+}
+
+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 (::IsWindow(GetContainerHWND())) {
+    CRect client_rect;
+    GetClientRect(GetContainerHWND(), &client_rect);
+    RepositionSupressedPopupsToFit(
+        gfx::Size(client_rect.Width(), client_rect.Height()));
+  }
+}
+
+void TabContents::DetachContents(ConstrainedWindow* window,
+                                 TabContents* contents,
+                                 const gfx::Rect& contents_bounds,
+                                 const gfx::Point& mouse_pt,
+                                 int frame_component) {
+  WillClose(window);
+  if (delegate_) {
+    delegate_->StartDraggingDetachedContents(
+        this, contents, contents_bounds, mouse_pt, frame_component);
+  }
+}
+
+void TabContents::DidMoveOrResize(ConstrainedWindow* window) {
+  UpdateWindow(GetContainerHWND());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// PageNavigator methods
+
+void TabContents::OpenURL(const GURL& url,
+                          WindowOpenDisposition disposition,
+                          PageTransition::Type transition) {
+  if (delegate_)
+    delegate_->OpenURLFromTab(this, url, disposition, transition);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void TabContents::Focus() {
+  ChromeViews::FocusManager* focus_manager =
+      ChromeViews::FocusManager::GetFocusManager(GetContainerHWND());
+  DCHECK(focus_manager);
+  ChromeViews::View* v =
+      focus_manager->GetViewForWindow(GetContainerHWND(), true);
+  DCHECK(v);
+  if (v)
+    v->RequestFocus();
+}
+
+void TabContents::StoreFocus() {
+  ChromeViews::ViewStorage* view_storage =
+      ChromeViews::ViewStorage::GetSharedInstance();
+
+  if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL)
+    view_storage->RemoveView(last_focused_view_storage_id_);
+
+  ChromeViews::FocusManager* focus_manager =
+      ChromeViews::FocusManager::GetFocusManager(GetContainerHWND());
+  if (focus_manager) {
+    // |focus_manager| can be NULL if the tab has been detached but still
+    // exists.
+    ChromeViews::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.
+    HWND container_hwnd = GetContainerHWND();
+    if (container_hwnd) {
+      ChromeViews::View* focused_view = focus_manager->GetFocusedView();
+      if (focused_view) {
+        HWND hwnd = focused_view->GetRootView()->GetViewContainer()->GetHWND();
+        if (container_hwnd == hwnd || ::IsChild(container_hwnd, hwnd))
+          focus_manager->ClearFocus();
+      }
+    }
+  }
+}
+
+void TabContents::RestoreFocus() {
+  ChromeViews::ViewStorage* view_storage =
+      ChromeViews::ViewStorage::GetSharedInstance();
+  ChromeViews::View* last_focused_view =
+      view_storage->RetrieveView(last_focused_view_storage_id_);
+
+  if (!last_focused_view) {
+    SetInitialFocus();
+  } else {
+    ChromeViews::FocusManager* focus_manager =
+        ChromeViews::FocusManager::GetFocusManager(GetContainerHWND());
+    if (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::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() - ChromeViews::NativeScrollBar::GetVerticalScrollBarWidth(),
+      new_size.height());
+  int window_count = static_cast<int>(child_windows_.size());
+  for (int i = window_count - 1; i >= 0; --i) {
+    ConstrainedWindow* window = child_windows_.at(i);
+    if (window->IsSuppressedConstrainedWindow())
+      window->RepositionConstrainedWindowTo(anchor_position);
+  }
+}
+
+void TabContents::SetDownloadShelfVisible(bool visible) {
+  if (shelf_visible_ != visible) {
+    if (visible) {
+      // Invoke GetDownloadShelfView to force the shelf to be created.
+      GetDownloadShelfView();
+    }
+    shelf_visible_ = visible;
+
+    // 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(visible);
+  }
+}
+
+void TabContents::ReleaseDownloadShelfView() {
+  download_shelf_view_.release();
+}
+
+void TabContents::SetInitialFocus() {
+  ::SetFocus(GetContainerHWND());
+}
+
+void TabContents::SetIsCrashed(bool state) {
+  if (state == is_crashed_)
+    return;
+
+  is_crashed_ = state;
+  if (delegate_)
+    delegate_->CrashedStateChanged(this);
+}
+
+bool TabContents::IsCrashed() const {
+  return is_crashed_;
+}
+
+bool TabContents::SupportsURL(GURL* url) {
+  GURL u(*url);
+  if (TabContents::TypeForURL(&u) == type()) {
+    *url = u;
+    return true;
+  }
+  return false;
+}
+
+void TabContents::ToolbarSizeChanged(bool is_animating) {
+  TabContentsDelegate* d = delegate();
+  if (d)
+    d->ToolbarSizeChanged(this, is_animating);
+}
+
+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();
+}
+
+// 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);
+}
+
+// static
+void TabContents::RegisterUserPrefs(PrefService* prefs) {
+  prefs->RegisterBooleanPref(prefs::kBlockPopups, false);
+}