// 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/browser.h"

#include <windows.h>
#include <shellapi.h>

#include "base/command_line.h"
#include "base/idle_timer.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_shutdown.h"
#include "chrome/browser/browser_url_handler.h"
#include "chrome/browser/cert_store.h"
#include "chrome/browser/navigation_controller.h"
#include "chrome/browser/navigation_entry.h"
#include "chrome/browser/plugin_process_host.h"
#include "chrome/browser/plugin_service.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/render_view_host.h"
#include "chrome/browser/save_package.h"
#include "chrome/browser/ssl_error_info.h"
#include "chrome/browser/site_instance.h"
#include "chrome/browser/tabs/tab_strip.h"
#include "chrome/browser/url_fixer_upper.h"
#include "chrome/browser/user_metrics.h"
#include "chrome/browser/view_ids.h"
#include "chrome/browser/views/download_shelf_view.h"
#include "chrome/browser/views/go_button.h"
#include "chrome/browser/views/bookmark_bar_view.h"
#include "chrome/browser/views/location_bar_view.h"
#include "chrome/browser/views/toolbar_star_toggle.h"
#include "chrome/browser/vista_frame.h"
#include "chrome/browser/window_sizer.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/l10n_util.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
#include "net/base/cookie_monster.h"
#include "net/base/cookie_policy.h"
#include "net/base/net_util.h"
#include "net/base/registry_controlled_domain.h"

#include "generated_resources.h"

static BrowserList g_browserlist;

// How long we wait before updating the browser chrome while loading a page.
static const int kUIUpdateCoalescingTimeMS = 200;

// Idle time before helping prune memory consumption.
static const int kBrowserReleaseMemoryInterval = 30;  // In seconds.

// How much horizontal and vertical offset there is between newly opened
// windows.
static const int kWindowTilePixels = 10;

// How frequently we check for hung plugin windows.
static const int kDefaultHungPluginDetectFrequency = 2000;

// How long do we wait before we consider a window hung (in ms).
static const int kDefaultPluginMessageResponseTimeout = 5000;

////////////////////////////////////////////////////////////////////////////////

// A task to reduce the working set of the plugins.
class ReducePluginsWorkingSetTask : public Task {
 public:
  virtual void Run() {
    for (PluginProcessHostIterator iter; !iter.Done(); ++iter) {
      PluginProcessHost* plugin = const_cast<PluginProcessHost*>(*iter);
      DCHECK(plugin->process());
      Process process(plugin->process());
      process.ReduceWorkingSet();
    }
  }
};

// A browser task to run when the user is not using the browser.
// In our case, we're trying to be nice to the operating system and release
// memory not in use.
class BrowserIdleTask : public IdleTimerTask {
 public:
  BrowserIdleTask()
    : IdleTimerTask(
          TimeDelta::FromSeconds(kBrowserReleaseMemoryInterval), false) {
  }

  virtual void OnIdle() {
    // We're idle.  Release browser and renderer unused pages.

    // Handle the Browser.
    Process process(GetCurrentProcess());
    process.ReduceWorkingSet();

    // Handle the Renderer(s).
    RenderProcessHost::iterator renderer_iter;
    for (renderer_iter = RenderProcessHost::begin(); renderer_iter !=
       RenderProcessHost::end(); renderer_iter++) {
       Process process(renderer_iter->second->process());
       process.ReduceWorkingSet();
    }

    // Handle the Plugin(s).  We need to iterate through the plugin processes on
    // the IO thread because that thread manages the plugin process collection.
    g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
        new ReducePluginsWorkingSetTask());
  }
};

////////////////////////////////////////////////////////////////////////////////

struct Browser::UIUpdate {
  UIUpdate(const TabContents* src, unsigned flags)
      : source(src),
        changed_flags(flags) {
  }

  // The source of the update.
  const TabContents* source;

  // What changed in the UI.
  unsigned changed_flags;
};

////////////////////////////////////////////////////////////////////////////////

// static
Browser* Browser::GetBrowserForController(
    const NavigationController* controller, int* index_result) {
  BrowserList::const_iterator it;
  for (it = BrowserList::begin(); it != BrowserList::end(); ++it) {
    int index = (*it)->tabstrip_model_.GetIndexOfController(controller);
    if (index != TabStripModel::kNoTab) {
      if (index_result)
        *index_result = index;
      return *it;
    }
  }

  return NULL;
}

// static
void Browser::OpenNewBrowserWindow(Profile* profile, int show_command) {
  Browser* browser = new Browser(gfx::Rect(), show_command, profile,
                                 BrowserType::TABBED_BROWSER, L"");
  browser->AddBlankTab(true);
  browser->Show();
}

// static
void Browser::RegisterPrefs(PrefService* prefs) {
  prefs->RegisterIntegerPref(prefs::kPluginMessageResponseTimeout,
      kDefaultPluginMessageResponseTimeout);
  prefs->RegisterIntegerPref(prefs::kHungPluginDetectFrequency,
      kDefaultHungPluginDetectFrequency);
  prefs->RegisterDictionaryPref(prefs::kBrowserWindowPlacement);
  prefs->RegisterBooleanPref(prefs::kMetricsReportingEnabled, true);
  prefs->RegisterIntegerPref(prefs::kOptionsWindowLastTabIndex, 0);
}

// static
void Browser::RegisterUserPrefs(PrefService* prefs) {
  prefs->RegisterStringPref(prefs::kHomePage, L"chrome-internal:");
  prefs->RegisterIntegerPref(prefs::kCookieBehavior,
      CookiePolicy::ALLOW_ALL_COOKIES);
  prefs->RegisterBooleanPref(prefs::kShowHomeButton, false);
  prefs->RegisterStringPref(prefs::kRecentlySelectedEncoding, L"");
}

Browser::Browser(const gfx::Rect& initial_bounds,
                 int show_command,
                 Profile* profile,
                 BrowserType::Type type,
                 const std::wstring& app_name)
    : profile_(profile),
      frame_(NULL),
      initial_show_command_(show_command),
      is_processing_tab_unload_events_(false),
      controller_(this),
      toolbar_(&controller_, this),
      chrome_updater_factory_(this),
      frame_method_factory_(this),
      hung_window_detector_(&hung_plugin_action_),
      ticker_(0),
      tabstrip_model_(this, profile),
      toolbar_model_(this),
      type_(type),
      app_name_(app_name),
      idle_task_(new BrowserIdleTask()) {
  tabstrip_model_.AddObserver(this);

  CommandLine parsed_command_line;

  gfx::Rect create_bounds;
  bool maximized = false;
  WindowSizer::GetBrowserWindowBounds(app_name_, initial_bounds,
                                      &create_bounds, &maximized);
  if (parsed_command_line.HasSwitch(switches::kStartMaximized))
    maximized = true;
  if (maximized)
    initial_show_command_ = SW_SHOWMAXIMIZED;
  frame_ = ChromeFrame::CreateChromeFrame(create_bounds, this);

  toolbar_.SetID(VIEW_ID_TOOLBAR);
  toolbar_.Init(profile_);

  // See note where SIZE_TO_CONTENTS is defined in browser.h for an explanation
  // of this hack.
  if (show_command == SIZE_TO_CONTENTS) {
    // SizeToContents causes a Layout so make sure the tab strip and toolbar
    // are already initialized.
    frame_->SizeToContents(initial_bounds);
    initial_show_command_ = SW_SHOWNORMAL;
  }

  // Start a hung plugin window detector for this browser object (as long as
  // hang detection is not disabled).
  if (!parsed_command_line.HasSwitch(switches::kDisableHangMonitor))
    InitHangMonitor();

  NotificationService::current()->
      AddObserver(this, NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED,
                  NotificationService::AllSources());

  if (profile->HasSessionService()) {
    SessionService* session_service = profile->GetSessionService();
    if (session_service)
      session_service->SetWindowType(session_id_, type_);
  }

  InitCommandState();
  BrowserList::AddBrowser(this);

  encoding_auto_detect_.Init(prefs::kWebKitUsesUniversalDetector,
                             profile_->GetPrefs(), NULL);

  // Trim browser memory on idle for low & medium memory models.
  if (g_browser_process->memory_model() < BrowserProcess::HIGH_MEMORY_MODEL)
    idle_task_->Start();

  // Show the First Run information bubble if we've been told to.
  PrefService* local_state = g_browser_process->local_state();
  if (local_state->IsPrefRegistered(prefs::kShouldShowFirstRunBubble) &&
      local_state->GetBoolean(prefs::kShouldShowFirstRunBubble)) {
    // Reset the preference so we don't show the bubble for subsequent windows.
    local_state->ClearPref(prefs::kShouldShowFirstRunBubble);
    GetLocationBarView()->ShowFirstRunBubble();
  }
}

Browser::~Browser() {
  // The tab strip should be empty at this point.
  DCHECK(tabstrip_model_.empty());
  tabstrip_model_.RemoveObserver(this);

  BrowserList::RemoveBrowser(this);

  if (!BrowserList::HasBrowserWithProfile(profile_)) {
    // We're the last browser window with this profile. We need to nuke the
    // TabRestoreService, which will start the shutdown of the
    // NavigationControllers and allow for proper shutdown. If we don't do this
    // chrome won't shutdown cleanly, and may end up crashing when some
    // thread tries to use the IO thread (or another thread) that is no longer
    // valid.
    profile_->ResetTabRestoreService();
  }

  SessionService* session_service = profile_->GetSessionService();
  if (session_service)
    session_service->WindowClosed(session_id_);

  NotificationService::current()->
      RemoveObserver(this, NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED,
                     NotificationService::AllSources());

  ChromeViews::View* p;
  // Remove our main views from the view hierarchy to prevent
  // a double delete (the Browser is deleted before the RootView that contains
  // the toolbar and tab_strip).
  if ((p = toolbar_.GetParent()))
    p->RemoveChildView(&toolbar_);
  if (bookmark_bar_view_.get() && (p = bookmark_bar_view_->GetParent()))
    p->RemoveChildView(bookmark_bar_view_.get());

  // Stop hung plugin monitoring.
  ticker_.Stop();
  ticker_.UnregisterTickHandler(&hung_window_detector_);

  if (profile_->IsOffTheRecord() &&
      !BrowserList::IsOffTheRecordSessionActive()) {
    // We reuse the OTR cookie store across OTR windows.  If the last OTR
    // window is closed, then we want to wipe the cookie store clean, so when
    // an OTR window is open again, it starts with an empty cookie store.  This
    // also frees up the memory that the OTR cookies were using.  OTR never
    // loads or writes persistent cookies (there is no backing store), so we
    // can just delete all of the cookies in the store.
    profile_->GetRequestContext()->cookie_store()->DeleteAll(false);
  }

  // There may be pending file dialogs, we need to tell them that we've gone
  // away so they don't try and call back to us.
  if (select_file_dialog_.get())
    select_file_dialog_->ListenerDestroyed();
}

void Browser::ShowAndFit(bool resize_to_fit) {
  // Only allow one call after the browser is created.
  if (initial_show_command_ < 0) {
    // The frame is already visible, we're being invoked again either by the
    // user clicking a link in another app or from a desktop shortcut.
    frame_->Activate();
    return;
  }
  frame_->Show(initial_show_command_, resize_to_fit);
  if ((initial_show_command_ == SW_SHOWNORMAL) ||
      (initial_show_command_ == SW_SHOWMAXIMIZED))
    frame_->Activate();
  initial_show_command_ = -1;

  // Setting the focus doesn't work when the window is invisible, so any focus
  // initialization that happened before this will be lost.
  //
  // We really "should" restore the focus whenever the window becomes unhidden,
  // but I think initializing is the only time where this can happen where there
  // is some focus change we need to pick up, and this is easier than plumbing
  // through an unhide message all the way from the frame.
  //
  // If we do find there are cases where we need to restore the focus on show,
  // that should be added and this should be removed.
  TabContents* selected_tab_contents = GetSelectedTabContents();
  if (selected_tab_contents)
    selected_tab_contents->RestoreFocus();
}

void Browser::CloseFrame() {
  frame_->Close();
}

ChromeViews::View* Browser::GetToolbar() {
  return &toolbar_;
}

////////////////////////////////////////////////////////////////////////////////

void Browser::SyncWindowTitle() {
  TabContents* current_tab = GetSelectedTabContents();
  if (!current_tab || current_tab->GetTitle().empty()) {
    frame_->SetWindowTitle(l10n_util::GetString(IDS_PRODUCT_NAME));
    return;
  }

  frame_->SetWindowTitle(
      l10n_util::GetStringF(IDS_BROWSER_WINDOW_TITLE_FORMAT,
                            current_tab->GetTitle()));
}

////////////////////////////////////////////////////////////////////////////////
// Event Handlers

void Browser::WindowActivationChanged(bool is_active) {
  if (is_active)
    BrowserList::SetLastActive(this);
}

////////////////////////////////////////////////////////////////////////////////
// Toolbar creation, management

LocationBarView* Browser::GetLocationBarView() const {
  return toolbar_.GetLocationBarView();
}

////////////////////////////////////////////////////////////////////////////////
// Chrome update coalescing

void Browser::UpdateToolBar(bool should_restore_state) {
  toolbar_.Update(GetSelectedTabContents(), should_restore_state);
}

void Browser::ScheduleUIUpdate(const TabContents* source,
                               unsigned changed_flags) {
  // Synchronously update the URL.
  if (changed_flags & TabContents::INVALIDATE_URL &&
      source == GetSelectedTabContents()) {
    // Only update the URL for the current tab. Note that we do not update
    // the navigation commands since those would have already been updated
    // synchronously by NavigationStateChanged.
    UpdateToolBar(false);

    if (changed_flags == TabContents::INVALIDATE_URL)
      return;  // Just had an update URL and nothing else.
  }

  // Save the dirty bits.
  scheduled_updates_.push_back(UIUpdate(source, changed_flags));

  if (chrome_updater_factory_.empty()) {
    // No task currently scheduled, start another.
    MessageLoop::current()->PostDelayedTask(FROM_HERE,
        chrome_updater_factory_.NewRunnableMethod(
            &Browser::ProcessPendingUIUpdates),
            kUIUpdateCoalescingTimeMS);
  }
}

void Browser::ProcessPendingUIUpdates() {
#ifndef NDEBUG
  // Validate that all tabs we have pending updates for exist. This is scary
  // because the pending list must be kept in sync with any detached or
  // deleted tabs. This code does not dereference any TabContents pointers.
  for (size_t i = 0; i < scheduled_updates_.size(); i++) {
    bool found = false;
    for (int tab = 0; tab < tab_count(); tab++) {
      if (GetTabContentsAt(tab)->controller() ==
          scheduled_updates_[i].source->controller()) {
        found = true;
        break;
      }
    }
    DCHECK(found);
  }
#endif

  chrome_updater_factory_.RevokeAll();

  // We could have many updates for the same thing in the queue. This map tracks
  // the bits of the stuff we've already updated for each TabContents so we
  // don't update again.
  typedef std::map<const TabContents*, unsigned> UpdateTracker;
  UpdateTracker updated_stuff;

  for (size_t i = 0; i < scheduled_updates_.size(); i++) {
    // Do not dereference |contents|, it may be out-of-date!
    const TabContents* contents = scheduled_updates_[i].source;
    unsigned flags = scheduled_updates_[i].changed_flags;

    // Remove any bits we have already updated, and save the new bits.
    UpdateTracker::iterator updated = updated_stuff.find(contents);
    if (updated != updated_stuff.end()) {
      // Turn off bits already set.
      flags &= ~updated->second;
      if (!flags)
        continue;

      updated->second |= flags;
    } else {
      updated_stuff[contents] = flags;
    }

    // Updates to the title or favicon require a tab repaint. However, the
    // inverse is not true since updates to the title also update the window
    // title.
    bool invalidate_tab = false;
    if (flags & TabContents::INVALIDATE_TITLE ||
        flags & TabContents::INVALIDATE_FAVICON) {
      invalidate_tab = true;

      // Anything that repaints the tab means the favicon is updated.
      updated_stuff[contents] |= TabContents::INVALIDATE_FAVICON;
    }

    // Updating the URL happens synchronously in ScheduleUIUpdate.

    if (flags & TabContents::INVALIDATE_TITLE)
      SyncWindowTitle();  // We'll update the tab due to invalide_tab below.

    if (flags & TabContents::INVALIDATE_LOAD)
      GetStatusBubble()->SetStatus(GetSelectedTabContents()->GetStatusText());

    if (invalidate_tab) {  // INVALIDATE_TITLE or INVALIDATE_FAVICON.
      tabstrip_model_.UpdateTabContentsStateAt(
          tabstrip_model_.GetIndexOfController(contents->controller()));
      frame_->UpdateTitleBar();

      if (contents == GetSelectedTabContents()) {
        TabContents* current_tab = GetSelectedTabContents();
        controller_.UpdateCommandEnabled(IDC_CREATE_SHORTCUT,
            current_tab->type() == TAB_CONTENTS_WEB &&
            !current_tab->GetFavIcon().isNull());
      }
    }

    // We don't need to process INVALIDATE_STATE, since that's not visible.
  }

  scheduled_updates_.clear();
}

////////////////////////////////////////////////////////////////////////////////
// TabContentsDelegate

void Browser::OpenURLFromTab(TabContents* source,
                             const GURL& url,
                             WindowOpenDisposition disposition,
                             PageTransition::Type transition) {
  // No code for these yet
  DCHECK((disposition != NEW_POPUP) && (disposition != SAVE_TO_DISK));

  TabContents* current_tab = source ? source : GetSelectedTabContents();
  bool source_tab_was_frontmost = (current_tab == GetSelectedTabContents());
  TabContents* new_contents = NULL;

  // If the URL is part of the same web site, then load it in the same
  // SiteInstance (and thus the same process).  This is an optimization to
  // reduce process overhead; it is not necessary for compatibility.  (That is,
  // the new tab will not have script connections to the previous tab, so it
  // does not need to be part of the same SiteInstance or BrowsingInstance.)
  // Default to loading in a new SiteInstance and BrowsingInstance.
  // TODO(creis): should this apply to applications?
  SiteInstance* instance = NULL;
  // Don't use this logic when "--process-per-tab" is specified.
  if (!CommandLine().HasSwitch(switches::kProcessPerTab)) {
    if (current_tab) {
      const WebContents* const web_contents = current_tab->AsWebContents();
      if (web_contents) {
        const GURL& current_url = web_contents->GetURL();
        if (SiteInstance::IsSameWebSite(current_url, url))
          instance = web_contents->site_instance();
      }
    }
  }

  // If this is an application we can only have one tab so a new tab always
  // goes into a tabbed browser window.
  if (disposition != NEW_WINDOW && type_ == BrowserType::APPLICATION) {
    // If the disposition is OFF_THE_RECORD we don't want to create a new
    // browser that will itself create another OTR browser. This will result in
    // a browser leak (and crash below because no tab is created or selected).
    if (disposition == OFF_THE_RECORD) {
      OpenURLOffTheRecord(profile_, url);
      return;
    }

    Browser* b = GetOrCreateTabbedBrowser();
    DCHECK(b);

    // If we have just created a new browser window, make sure we select the
    // tab.
    if (b->tab_count() == 0 && disposition == NEW_BACKGROUND_TAB)
      disposition = NEW_FOREGROUND_TAB;

    b->OpenURL(url, disposition, transition);
    b->Show();
    b->MoveToFront(true);
    return;
  }

  if (profile_->IsOffTheRecord() && disposition == OFF_THE_RECORD)
    disposition = NEW_FOREGROUND_TAB;

  if (disposition == NEW_WINDOW) {
    Browser* new_browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
                                       BrowserType::TABBED_BROWSER, L"");
    new_contents = new_browser->AddTabWithURL(url, transition, true, instance);
    new_browser->Show();
  } else if ((disposition == CURRENT_TAB) && current_tab) {
    if (transition == PageTransition::TYPED ||
        transition == PageTransition::AUTO_BOOKMARK ||
        transition == PageTransition::GENERATED ||
        transition == PageTransition::START_PAGE) {
      // If the user navigates the current tab to another page in any way other
      // than by clicking a link, we want to pro-actively forget all TabStrip
      // opener relationships since we assume they're beginning a different
      // task by reusing the current tab.
      tabstrip_model_.ForgetAllOpeners();
      // In this specific case we also want to reset the group relationship,
      // since it is now technically invalid.
      tabstrip_model_.ForgetGroup(current_tab);
    }
    current_tab->controller()->LoadURL(url, transition);
    // The TabContents might have changed as part of the navigation (ex: new tab
    // page can become WebContents).
    new_contents = current_tab->controller()->active_contents();
    GetStatusBubble()->Hide();

    // Synchronously update the location bar. This allows us to immediately
    // have the URL bar update when the user types something, rather than
    // going through the normal system of ScheduleUIUpdate which has a delay.
    UpdateToolBar(false);
  } else if (disposition == OFF_THE_RECORD) {
    OpenURLOffTheRecord(profile_, url);
    return;
  } else if (disposition != SUPPRESS_OPEN) {
    new_contents =
        AddTabWithURL(url, transition, disposition != NEW_BACKGROUND_TAB,
                      instance);
  }

  if (disposition != NEW_BACKGROUND_TAB && source_tab_was_frontmost) {
    // Give the focus to the newly navigated tab, if the source tab was front-most
    new_contents->Focus();
  }
}

void Browser::NavigationStateChanged(const TabContents* source,
                                     unsigned changed_flags) {
  if (!GetSelectedTabContents()) {
    // Nothing is selected. This can happen when being restored from history,
    // bail.
    return;
  }

  // Only update the UI when something visible has changed.
  if (changed_flags && changed_flags != TabContents::INVALIDATE_STATE)
    ScheduleUIUpdate(source, changed_flags);

  // We don't schedule updates to the navigation commands since they will only
  // change once per navigation, so we don't have to worry about flickering.
  if (changed_flags & TabContents::INVALIDATE_URL) {
    UpdateNavigationCommands();
  }
}

void Browser::ReplaceContents(TabContents* source, TabContents* new_contents) {
  source->set_delegate(NULL);
  new_contents->set_delegate(this);

  RemoveScheduledUpdatesFor(source);

  int index = tabstrip_model_.GetIndexOfTabContents(source);
  tabstrip_model_.ReplaceTabContentsAt(index, new_contents);
}

void Browser::AddNewContents(TabContents* source,
                             TabContents* new_contents,
                             WindowOpenDisposition disposition,
                             const gfx::Rect& initial_pos,
                             bool user_gesture) {
  DCHECK(disposition != SAVE_TO_DISK);  // No code for this yet

  // If this is an application we can only have one tab so we need to process
  // this in tabbed browser window.
  if (tabstrip_model_.count() > 0 &&
      disposition != NEW_WINDOW && disposition != NEW_POPUP &&
      type_ != BrowserType::TABBED_BROWSER) {
    Browser* b = GetOrCreateTabbedBrowser();
    DCHECK(b);
    PageTransition::Type transition = PageTransition::LINK;
    // If we were called from an "installed webapp" we want to emulate the code
    // that is run from browser_init.cc for links from external applications.
    // This means we need to open the tab with the START PAGE transition.
    // AddNewContents doesn't support this but the TabStripModel's
    // AddTabContents method does.
    if (type_ == BrowserType::APPLICATION)
      transition = PageTransition::START_PAGE;
    b->tabstrip_model()->AddTabContents(new_contents, -1, transition, true);
    b->Show();
    b->MoveToFront(true);
    return;
  }

  if (disposition == NEW_POPUP) {
    BuildPopupWindow(source, new_contents, initial_pos);
  } else if (disposition == NEW_WINDOW) {
    Browser* new_browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
                                       BrowserType::TABBED_BROWSER, L"");
    new_browser->AddNewContents(source, new_contents, NEW_FOREGROUND_TAB,
                                initial_pos, user_gesture);
    new_browser->Show();
  } else if (disposition == CURRENT_TAB) {
    ReplaceContents(source, new_contents);
  } else if (disposition != SUPPRESS_OPEN) {
    tabstrip_model_.AddTabContents(new_contents, -1, PageTransition::LINK,
                                   disposition == NEW_FOREGROUND_TAB);
  }
}

void Browser::StartDraggingDetachedContents(TabContents* source,
                                            TabContents* new_contents,
                                            const gfx::Rect& contents_bounds,
                                            const gfx::Point& mouse_pt,
                                            int frame_component) {
  BrowserType::Type new_type = BrowserType::BROWSER;

  // If this is a minimal chrome browser, propagate to detached contents to
  // avoid having URL fields in popups.
  if (type_ == BrowserType::APPLICATION)
    new_type = type_;

  Browser* browser = new Browser(contents_bounds, SIZE_TO_CONTENTS, profile_,
                                 new_type, L"");
  browser->AddNewContents(
      source, new_contents, NEW_FOREGROUND_TAB, contents_bounds, true);
  browser->Show();
  browser->frame_->ContinueDetachConstrainedWindowDrag(
      mouse_pt, frame_component);
}

void Browser::ActivateContents(TabContents* contents) {
  tabstrip_model_.SelectTabContentsAt(
      tabstrip_model_.GetIndexOfTabContents(contents), false);
  frame_->Activate();
}

HWND Browser::GetTopLevelHWND() const {
  return frame_ ? reinterpret_cast<HWND>(frame_->GetPlatformID()) : NULL;
}

void Browser::LoadingStateChanged(TabContents* source) {
  tabstrip_model_.UpdateTabContentsLoadingAnimations();

  frame_->UpdateTitleBar();

  // Let the go button know that it should change appearance if possible.
  if (source == GetSelectedTabContents()) {
    GetGoButton()->ScheduleChangeMode(
      source->is_loading() ? GoButton::MODE_STOP : GoButton::MODE_GO);

    GetStatusBubble()->SetStatus(GetSelectedTabContents()->GetStatusText());
  }
}

void Browser::CloseContents(TabContents* source) {
  int index = tabstrip_model_.GetIndexOfTabContents(source);
  if (index == TabStripModel::kNoTab) {
    NOTREACHED() << "CloseContents called for tab not in our strip";
    return;
  }
  tabstrip_model_.CloseTabContentsAt(index);
}

void Browser::MoveContents(TabContents* source, const gfx::Rect& pos) {
  if (GetType() != BrowserType::BROWSER) {
    NOTREACHED() << "moving invalid browser type";
    return;
  }

  ::SetWindowPos(GetTopLevelHWND(), NULL, pos.x(), pos.y(), pos.width(),
                 pos.height(), 0);
  win_util::AdjustWindowToFit(GetTopLevelHWND());
}

bool Browser::IsPopup(TabContents* source) {
  // A non-tabbed BROWSER is an unconstrained popup.
  return (GetType() == BrowserType::BROWSER);
}

void Browser::ShowHtmlDialog(HtmlDialogContentsDelegate* delegate,
                             HWND parent_hwnd) {
  parent_hwnd = parent_hwnd ? parent_hwnd : GetTopLevelHWND();
  HtmlDialogView* html_view = new HtmlDialogView(this, profile_, delegate);
  ChromeViews::Window* html_dialog = ChromeViews::Window::CreateChromeWindow(
      parent_hwnd, gfx::Rect(), html_view, html_view);
  html_view->InitDialog(html_dialog);
  html_dialog->Show();
}

void Browser::Observe(NotificationType type,
                      const NotificationSource& source,
                      const NotificationDetails& details) {
  if (type == NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED) {
    TabContents* current_tab = GetSelectedTabContents();
    if (current_tab) {
      Profile* event_profile = Source<Profile>(source).ptr();
      if (event_profile->IsSameProfile(current_tab->profile())) {
        // This forces the browser to query for the BookmarkBar again.
        frame_->ShelfVisibilityChanged();
      }
    }
  } else {
    NOTREACHED() << "Got a notification we didn't register for.";
  }
}

void Browser::UpdateNavigationCommands() {
  const TabContents* const current_tab = GetSelectedTabContents();
  NavigationController* nc = current_tab->controller();
  controller_.UpdateCommandEnabled(IDC_BACK, nc->CanGoBack());
  controller_.UpdateCommandEnabled(IDC_FORWARD, nc->CanGoForward());

  const WebContents* const web_contents = current_tab->AsWebContents();

  if (web_contents) {
    controller_.UpdateCommandEnabled(IDC_STAR, true);
    SetStarredButtonToggled(web_contents->is_starred());

    // View-source should not be enabled if already in view-source mode.
    controller_.UpdateCommandEnabled(IDC_VIEWSOURCE,
        current_tab->type() != TAB_CONTENTS_VIEW_SOURCE &&
        current_tab->controller()->GetActiveEntry());

    controller_.UpdateCommandEnabled(IDC_ZOOM, true);
    bool enable_encoding =
        SavePackage::IsSavableContents(web_contents->contents_mime_type()) &&
        SavePackage::IsSavableURL(current_tab->GetURL());
    controller_.UpdateCommandEnabled(IDC_ENCODING, enable_encoding);

    controller_.UpdateCommandEnabled(IDC_SAVEPAGE,
        SavePackage::IsSavableURL(current_tab->GetURL()));
    controller_.UpdateCommandEnabled(IDC_SHOW_JS_CONSOLE, true);
  } else {
    controller_.UpdateCommandEnabled(IDC_VIEWSOURCE, false);
    controller_.UpdateCommandEnabled(IDC_SHOW_JS_CONSOLE, false);

    // Both disable the starring button and ensure it doesn't show a star.
    controller_.UpdateCommandEnabled(IDC_STAR, false);
    SetStarredButtonToggled(false);
    controller_.UpdateCommandEnabled(IDC_ZOOM, false);
    controller_.UpdateCommandEnabled(IDC_ENCODING, false);

    controller_.UpdateCommandEnabled(IDC_SAVEPAGE, false);
  }

  controller_.UpdateCommandEnabled(IDC_CREATE_SHORTCUT,
                                   current_tab->type() == TAB_CONTENTS_WEB &&
                                       !current_tab->GetFavIcon().isNull());
  controller_.UpdateCommandEnabled(IDC_FIND, web_contents != NULL);
  controller_.UpdateCommandEnabled(IDC_PRINT, web_contents != NULL);
  controller_.UpdateCommandEnabled(IDC_DUPLICATE,
                                   CanDuplicateContentsAt(selected_index()));

  controller_.UpdateCommandEnabled(IDC_GOOFFTHERECORD,
      profile_->GetOffTheRecordProfile() != profile_);
}

// Notification that the starredness of a tab changed.
void Browser::URLStarredChanged(TabContents* source, bool starred) {
  if (source == GetSelectedTabContents())
    SetStarredButtonToggled(starred);
}

StatusBubble* Browser::GetStatusBubble() {
  return frame_->GetStatusBubble();
}

// Called whenever the window is moved so that we can update the position
// of any WS_POPUP HWNDs.
void Browser::WindowMoved() {
  GetStatusBubble()->Reposition();

  // Close the omnibox popup, if any.
  LocationBarView* location_bar = GetLocationBarView();
  if (location_bar)
    location_bar->location_entry()->ClosePopup();
}

void Browser::ContentsMouseEvent(TabContents* source, UINT message) {
  if (source == GetSelectedTabContents()) {
    if (message == WM_MOUSEMOVE) {
      GetStatusBubble()->MouseMoved();
    } else if (message == WM_MOUSELEAVE) {
      GetStatusBubble()->SetURL(GURL(), std::wstring());
    }
  }
}

void Browser::UpdateTargetURL(TabContents* source, const GURL& url) {
  if (source == GetSelectedTabContents()) {
    PrefService* prefs = profile_->GetPrefs();
    GetStatusBubble()->SetURL(url, prefs->GetString(prefs::kAcceptLanguages));
  }
}

void Browser::SetStarredButtonToggled(bool starred) {
  toolbar_.star_button()->SetToggled(starred);
}

GoButton* Browser::GetGoButton() {
  return toolbar_.GetGoButton();
}

void Browser::ContentsZoomChange(bool zoom_in) {
  controller_.ExecuteCommand(zoom_in ? IDC_ZOOM_PLUS : IDC_ZOOM_MINUS);
}

bool Browser::IsApplication() const {
  return type_ == BrowserType::APPLICATION;
}

void Browser::CrashedStateChanged(TabContents* source) {
  int index = tabstrip_model_.GetIndexOfTabContents(source);
  if (index != TabStripModel::kNoTab)
    tabstrip_model_.UpdateTabContentsStateAt(index);
}

bool Browser::ShouldDisplayURLField() {
  return !IsApplication();
}

BookmarkBarView* Browser::GetBookmarkBarView() {
  TabContents* current_tab = GetSelectedTabContents();
  if (!current_tab || !current_tab->profile())
    return NULL;

  if (!bookmark_bar_view_.get())
    bookmark_bar_view_.reset(new BookmarkBarView(current_tab->profile(), this));
  else
    bookmark_bar_view_->SetProfile(current_tab->profile());
  bookmark_bar_view_->SetPageNavigator(current_tab);
  return bookmark_bar_view_.get();
}

void Browser::SaveWindowPlacementToDatabase() {
  // We don't want to be the ones who cause lazy initialization of the session
  // service. This function gets called during initial window showing, and we
  // don't want to bring in the session service this early.
  if (!profile()->HasSessionService())
    return;
  SessionService* session_service = profile()->GetSessionService();
  if (!session_service)
    return;

  WINDOWPLACEMENT wp;
  wp.length = sizeof(wp);

  HWND hwnd = reinterpret_cast<HWND>(frame_->GetPlatformID());
  if (!::GetWindowPlacement(hwnd, &wp))
    return;

  session_service->SetWindowBounds(session_id_,
                                   gfx::Rect(wp.rcNormalPosition),
                                   (wp.showCmd & SW_MAXIMIZE) == SW_MAXIMIZE);
}

void Browser::SaveWindowPlacement() {
  WINDOWPLACEMENT wp;
  wp.length = sizeof(wp);

  HWND hwnd = reinterpret_cast<HWND>(frame_->GetPlatformID());
  if (!::GetWindowPlacement(hwnd, &wp))
    return;

  PrefService* prefs = g_browser_process->local_state();
  DCHECK(prefs);
  std::wstring name(prefs::kBrowserWindowPlacement);
  if (!app_name_.empty()) {
    name.append(L"_");
    name.append(app_name_);
  }

  DictionaryValue* win_pref = prefs->GetMutableDictionary(name.c_str());
  DCHECK(win_pref);
  win_pref->SetInteger(L"top", wp.rcNormalPosition.top);
  win_pref->SetInteger(L"left", wp.rcNormalPosition.left);
  win_pref->SetInteger(L"bottom", wp.rcNormalPosition.bottom);
  win_pref->SetInteger(L"right", wp.rcNormalPosition.right);
  win_pref->SetBoolean(L"maximized", wp.showCmd == SW_SHOWMAXIMIZED);
}

void Browser::FocusLocationBar() {
  LocationBarView* location_bar = GetLocationBarView();
  if (location_bar)
    location_bar->location_entry()->SetFocus();
}

void Browser::SyncHistoryWithTabs(int index) {
  if (!profile()->HasSessionService())
    return;
  SessionService* session_service = profile()->GetSessionService();
  if (session_service) {
    for (int i = index; i < tab_count(); ++i) {
      TabContents* contents = GetTabContentsAt(i);
      if (contents) {
        session_service->SetTabIndexInWindow(
            session_id(), contents->controller()->session_id(), i);
      }
    }
  }
}

void Browser::ToolbarSizeChanged(TabContents* source, bool is_animating) {
  if (source == GetSelectedTabContents() || source == NULL) {
    // This will refresh the shelf if needed.
    frame_->SelectedTabToolbarSizeChanged(is_animating);
  }
}

void Browser::MoveToFront(bool should_activate) {
  frame_->Activate();
}

bool Browser::ShouldCloseWindow() {
  if (is_processing_tab_unload_events_) {
    return false;
  }
  is_processing_tab_unload_events_ = true;

  for (int i = 0; i < tab_count(); ++i) {
    if (tabstrip_model_.TabHasUnloadListener(i))
      tabs_needing_before_unload_fired_.push_back(GetTabContentsAt(i));
  }

  if (tabs_needing_before_unload_fired_.empty()) {
    is_processing_tab_unload_events_ = false;
    return true;
  }

  ProcessPendingBeforeUnloadTabs();
  return false;
}

void Browser::ProcessPendingBeforeUnloadTabs() {
  DCHECK(is_processing_tab_unload_events_);

  TabContents* tab = tabs_needing_before_unload_fired_.back();
  tab->AsWebContents()->render_view_host()->AttemptToClosePage(true);
}

void Browser::ProcessPendingUnloadTabs() {
  DCHECK(is_processing_tab_unload_events_);

  TabContents* tab = tabs_needing_unload_fired_.back();
  tab->AsWebContents()->render_view_host()->OnProceedWithClosePage(true);
}

void Browser::BeforeUnloadFired(TabContents* tab, bool proceed) {
  DCHECK(is_processing_tab_unload_events_);

  if (!proceed) {
    tabs_needing_before_unload_fired_.clear();
    tabs_needing_unload_fired_.clear();
    is_processing_tab_unload_events_ = false;
    return;
  }

  tabs_needing_unload_fired_.push_back(tab);

  for (UnloadListenerVector::iterator it =
           tabs_needing_before_unload_fired_.begin();
       it != tabs_needing_before_unload_fired_.end();
       ++it) {
    if (*it == tab) {
      tabs_needing_before_unload_fired_.erase(it);
      break;
    }
  }

  if (tabs_needing_before_unload_fired_.empty()) {
    // We've finished firing all beforeunload events and can proceed with unload
    // events.
    // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting
    // somewhere around here so that we have accurate measurements of shutdown
    // time.
    ProcessPendingUnloadTabs();
  } else {
    ProcessPendingBeforeUnloadTabs();
  }
}

void Browser::UnloadFired(TabContents* tab) {
  DCHECK(is_processing_tab_unload_events_);

  for (UnloadListenerVector::iterator it = tabs_needing_unload_fired_.begin();
       it != tabs_needing_unload_fired_.end();
       ++it) {
    if (*it == tab) {
      tabs_needing_unload_fired_.erase(it);
      break;
    }
  }

  if (tabs_needing_unload_fired_.empty()) {
    // We've finished all the unload events and can proceed to close the
    // browser.
    is_processing_tab_unload_events_ = false;
    OnWindowClosing();
  } else {
    ProcessPendingUnloadTabs();
  }
}


void Browser::OnWindowClosing() {
  if (!ShouldCloseWindow())
    return;

  if (BrowserList::size() == 1)
    browser_shutdown::OnShutdownStarting(browser_shutdown::WINDOW_CLOSE);

  // Don't use HasSessionService here, we want to force creation of the
  // session service so that user can restore what was open.
  SessionService* session_service = profile()->GetSessionService();
  if (session_service)
    session_service->WindowClosing(session_id());

  CloseAllTabs();
}

// Tab Creation Functions

TabContents* Browser::AddTabWithURL(
    const GURL& url, PageTransition::Type transition, bool foreground,
    SiteInstance* instance) {
  if (type_ == BrowserType::APPLICATION && tabstrip_model_.count() == 1) {
    NOTREACHED() << "Cannot add a tab in a mono tab application.";
    return NULL;
  }

  GURL url_to_load = url;
  if (url_to_load.is_empty())
    url_to_load = GURL(profile_->GetPrefs()->GetString(prefs::kHomePage));
  TabContents* contents =
      CreateTabContentsForURL(url_to_load, profile_, transition, false,
                              instance);
  tabstrip_model_.AddTabContents(contents, -1, transition, foreground);
  // By default, content believes it is not hidden.  When adding contents
  // in the background, tell it that it's hidden.
  if (!foreground)
    contents->WasHidden();
  return contents;
}

TabContents* Browser::AddWebApplicationTab(Profile* profile,
                                           WebApp* web_app,
                                           bool lazy) {
  DCHECK(web_app);

  // TODO(acw): Do we need an "application launched" transition type?
  // TODO(creis): Should we reuse the current instance (ie. process) here?
  TabContents* contents =
      CreateTabContentsForURL(web_app->url(), profile, PageTransition::LINK,
                              lazy, NULL);
  if (contents->AsWebContents())
    contents->AsWebContents()->SetWebApp(web_app);

  if (lazy) {
    contents->controller()->LoadURLLazily(
        web_app->url(), PageTransition::LINK, web_app->name(), NULL);
  }
  tabstrip_model_.AddTabContents(contents, -1, PageTransition::LINK, !lazy);
  return contents;
}

TabContents* Browser::AddTabWithNavigationController(
    NavigationController* ctrl, PageTransition::Type type) {
  TabContents* tc = ctrl->active_contents();
  tabstrip_model_.AddTabContents(tc, -1, type, true);
  return tc;
}

NavigationController* Browser::AddRestoredTab(
    const std::vector<TabNavigation>& navigations,
    int selected_navigation,
    bool select) {
  NavigationController* restored_controller =
      BuildRestoredNavigationController(navigations, selected_navigation);

  tabstrip_model_.AppendTabContents(restored_controller->active_contents(),
                                    select);
  if (profile_->HasSessionService()) {
    SessionService* session_service = profile_->GetSessionService();
    if (session_service)
      session_service->TabRestored(restored_controller);
  }
  return restored_controller;
}

void Browser::ReplaceRestoredTab(
    const std::vector<TabNavigation>& navigations,
    int selected_navigation) {
  NavigationController* restored_controller =
      BuildRestoredNavigationController(navigations, selected_navigation);

  tabstrip_model_.ReplaceNavigationControllerAt(
      tabstrip_model_.selected_index(),
      restored_controller);
}

////////////////////////////////////////////////////////////////////////////////
// Browser, TabStripModelDelegate implementation:

void Browser::CreateNewStripWithContents(TabContents* detached_contents,
                                         const gfx::Point& drop_point) {
  DCHECK(type_ == BrowserType::TABBED_BROWSER);

  // Create an empty new browser window the same size as the old one.
  CRect browser_rect;
  GetWindowRect(reinterpret_cast<HWND>(frame_->GetPlatformID()), &browser_rect);
  gfx::Rect rect(0, 0);
  if (drop_point.x() != 0 || drop_point.y() != 0) {
    rect.SetRect(drop_point.x(), drop_point.y(), browser_rect.Width(),
                 browser_rect.Height());
  }
  Browser* new_window =
      new Browser(rect, SW_SHOWNORMAL, profile_, BrowserType::TABBED_BROWSER,
                  std::wstring());
  // Need to do this _before_ appending the TabContents so that the window is
  // appropriately sized.
  new_window->Show();
  new_window->tabstrip_model()->AppendTabContents(detached_contents, true);

  // When we detach a tab we need to make sure any associated Find window moves
  // along with it to its new home (basically we just make new_window the parent
  // of the Find window).
  new_window->AdoptFindWindow(detached_contents);
}

int Browser::GetDragActions() const {
  int result = 0;
  if (BrowserList::GetBrowserCountForType(profile_,
                                          BrowserType::TABBED_BROWSER) > 1 ||
      tab_count() > 1)
    result |= TAB_TEAROFF_ACTION;
  if (tab_count() > 1)
    result |= TAB_MOVE_ACTION;
  return result;
}

TabContents* Browser::CreateTabContentsForURL(
    const GURL& url, Profile* profile, PageTransition::Type transition,
    bool defer_load, SiteInstance* instance) const {
  // Create an appropriate tab contents.
  GURL real_url = url;
  TabContentsType type = TabContents::TypeForURL(&real_url);
  DCHECK(type != TAB_CONTENTS_UNKNOWN_TYPE);

  TabContents* contents =
    TabContents::CreateWithType(type, GetTopLevelHWND(), profile, instance);
  contents->SetupController(profile);

  if (!defer_load) {
    // Load the initial URL before adding the new tab contents to the tab strip
    // so that the tab contents has navigation state.
    contents->controller()->LoadURL(url, transition);
  }

  return contents;
}

void Browser::ShowApplicationMenu(const gfx::Point p) {
  if (!frame_)
    return;

  HWND hwnd = reinterpret_cast<HWND>(frame_->GetPlatformID());
  CPoint t;
  t.x = p.x();
  t.y = p.y();
  RunSimpleFrameMenu(t, hwnd);
}

void Browser::ValidateLoadingAnimations() {
  if (frame_)
    frame_->ValidateThrobber();
}

void Browser::CloseFrameAfterDragSession() {
  // This is scheduled to run after we return to the message loop because
  // otherwise the frame will think the drag session is still active and ignore
  // the request.
  MessageLoop::current()->PostTask(FROM_HERE,
      frame_method_factory_.NewRunnableMethod(&Browser::CloseFrame));
}

////////////////////////////////////////////////////////////////////////////////
// Browser, TabStripModelObserver implementation:

void Browser::TabInsertedAt(TabContents* contents,
                            int index,
                            bool foreground) {
  contents->set_delegate(this);

  NavigationController* controller = contents->controller();
  DCHECK(controller);
  NotificationService::current()->
      Notify(NOTIFY_TAB_APPENDED,
             Source<NavigationController>(controller),
              Details<Browser>(this));

  contents->controller()->SetWindowID(session_id());

  SyncHistoryWithTabs(tabstrip_model_.GetIndexOfTabContents(contents));

  // When a tab is dropped into a tab strip we need to make sure that the
  // associated Find window is moved along with it. We therefore change the
  // parent of the Find window (if the parent is already correctly set this
  // does nothing).
  AdoptFindWindow(contents);
}

void Browser::TabClosingAt(TabContents* contents, int index) {
  NavigationController* controller = contents->controller();
  DCHECK(controller);
  NotificationService::current()->
      Notify(NOTIFY_TAB_CLOSING,
              Source<NavigationController>(controller),
              NotificationService::NoDetails());

  // Sever the TabContents' connection back to us.
  contents->set_delegate(NULL);

  if (contents == GetSelectedTabContents()) {
    // We need to reset the current tab contents to NULL before it gets
    // freed. This is because the focus manager performs some operation
    // on the selected tab contents when it is removed.
    frame_->ShowTabContents(NULL);
  }
}

void Browser::TabDetachedAt(TabContents* contents, int index) {
  // TODO(beng): (http://b/1085418) figure out if we really need to do this
  //             here - surely the subsequent selection of another tab would
  //             result in this action taking place?
  if (contents == GetSelectedTabContents())
    RemoveShelvesForTabContents(contents);

  contents->set_delegate(NULL);
  if (!tabstrip_model_.closing_all())
    SyncHistoryWithTabs(0);

  RemoveScheduledUpdatesFor(contents);
}

void Browser::TabSelectedAt(TabContents* old_contents,
                            TabContents* new_contents,
                            int index,
                            bool user_gesture) {
  if (new_contents == old_contents)
    return;

  // If we have any update pending, do it now.
  if (!chrome_updater_factory_.empty() && old_contents)
    ProcessPendingUIUpdates();

  LocationBarView* location_bar = GetLocationBarView();
  if (old_contents) {
    // Have the contents remember where focus was.
    old_contents->StoreFocus();

    // Save what the user's currently typing, so it can be restored when we
    // switch back to this tab.
    if (location_bar)
      location_bar->location_entry()->SaveStateToTab(old_contents);
  }

  // Tell the frame what happened so that the TabContents gets resized, etc.
  frame_->ShowTabContents(new_contents);

  // Inform the tab that it is now selected.
  new_contents->DidBecomeSelected();
  if (BrowserList::GetLastActive() == this)
    new_contents->RestoreFocus();

  // Propagate the profile to the location bar.
  toolbar_.SetProfile(new_contents->profile());
  UpdateToolBar(true);

  // Force the go/stop button to change.
  if (new_contents->AsWebContents()) {
    GetGoButton()->ChangeMode(
        new_contents->is_loading() ? GoButton::MODE_STOP : GoButton::MODE_GO);
  } else {
    GetGoButton()->ChangeMode(GoButton::MODE_GO);
  }

  // Update other parts of the toolbar.
  UpdateNavigationCommands();

  // Reset the status bubble.
  GetStatusBubble()->Hide();

  // Show the loading state (if any).
  GetStatusBubble()->SetStatus(GetSelectedTabContents()->GetStatusText());

  SyncWindowTitle();

  // Update sessions. Don't force creation of sessions. If sessions doesn't
  // exist, the change will be picked up by sessions when created.
  if (profile_->HasSessionService()) {
    SessionService* session_service = profile_->GetSessionService();
    if (session_service && !tabstrip_model_.closing_all()) {
      session_service->SetSelectedTabInWindow(session_id(),
                                              tabstrip_model_.selected_index());
    }
  }
}

void Browser::TabMoved(TabContents* contents,
                       int from_index,
                       int to_index) {
  DCHECK(from_index >= 0 && to_index >= 0);
  // Notify the history service.
  SyncHistoryWithTabs(std::min(from_index, to_index));
}

void Browser::TabStripEmpty() {
  // We need to reset the frame contents just in case this wasn't done while
  // detaching the tab. This happens when dragging out the last tab.
  frame_->ShowTabContents(NULL);

  // Close the frame after we return to the message loop (not immediately,
  // otherwise it will destroy this object before the stack has a chance to
  // cleanly unwind.)
  // Note: This will be called several times if TabStripEmpty is called several
  //       times. This is because it does not close the window if tabs are
  //       still present.
  // NOTE: If you change to be immediate (no invokeLater) then you'll need to
  //       update BrowserList::CloseAllBrowsers.
  MessageLoop::current()->PostTask(FROM_HERE,
      frame_method_factory_.NewRunnableMethod(&Browser::CloseFrame));
}

void Browser::RemoveShelvesForTabContents(TabContents* contents) {
  ChromeViews::View* shelf = contents->GetDownloadShelfView();
  if (shelf && shelf->GetParent() != NULL)
    shelf->GetParent()->RemoveChildView(shelf);

  ChromeViews::View* info_bar = contents->GetInfoBarView();
  if (info_bar && info_bar->GetParent() != NULL)
    info_bar->GetParent()->RemoveChildView(info_bar);
}

BrowserType::Type Browser::GetType() const {
  return type_;
}

void Browser::InitHangMonitor() {
  PrefService* pref_service = g_browser_process->local_state();
  DCHECK(pref_service != NULL);
  int plugin_message_response_timeout =
      pref_service->GetInteger(prefs::kPluginMessageResponseTimeout);
  int hung_plugin_detect_freq =
      pref_service->GetInteger(prefs::kHungPluginDetectFrequency);
  if ((hung_plugin_detect_freq > 0) &&
      hung_window_detector_.Initialize(GetTopLevelHWND(),
                                       plugin_message_response_timeout)) {
    ticker_.set_tick_interval(hung_plugin_detect_freq);
    ticker_.RegisterTickHandler(&hung_window_detector_);
    ticker_.Start();

    pref_service->SetInteger(prefs::kPluginMessageResponseTimeout,
                             plugin_message_response_timeout);
    pref_service->SetInteger(prefs::kHungPluginDetectFrequency,
                             hung_plugin_detect_freq);
  }
}


Browser* Browser::GetOrCreateTabbedBrowser() {
  Browser* browser = BrowserList::FindBrowserWithType(
      profile_, BrowserType::TABBED_BROWSER);
  if (!browser) {
    browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
                          BrowserType::TABBED_BROWSER, std::wstring());
  }
  return browser;
}

void Browser::RemoveScheduledUpdatesFor(TabContents* contents) {
  if (!contents)
    return;

  // Remove any pending UI updates for the detached tab.
  UpdateVector::iterator cur_update = scheduled_updates_.begin();
  while (cur_update != scheduled_updates_.end()) {
    if (cur_update->source == contents) {
      cur_update = scheduled_updates_.erase(cur_update);
    } else {
      ++cur_update;
    }
  }
}

void Browser::ShowNativeUI(const GURL& url) {
  int i, c;
  TabContents* tc;
  for (i = 0, c = tabstrip_model_.count(); i < c; ++i) {
    tc = tabstrip_model_.GetTabContentsAt(i);
    if (tc->type() == TAB_CONTENTS_NATIVE_UI &&
        tc->GetURL() == url) {
      tabstrip_model_.SelectTabContentsAt(i, false);
      return;
    }
  }

  TabContents* contents = CreateTabContentsForURL(url, profile_,
                                                  PageTransition::LINK, false,
                                                  NULL);
  AddNewContents(NULL, contents, NEW_FOREGROUND_TAB, gfx::Rect(), true);
}

NavigationController* Browser::BuildRestoredNavigationController(
      const std::vector<TabNavigation>& navigations,
      int selected_navigation) {
  if (!navigations.empty()) {
    DCHECK(selected_navigation >= 0 &&
           selected_navigation < static_cast<int>(navigations.size()));
    // We should have a valid URL, if we don't fall back to the default.
    GURL url = navigations[selected_navigation].url;
    if (url.is_empty())
      url = GURL(profile_->GetPrefs()->GetString(prefs::kHomePage));

    // Create a NavigationController. This constructor creates the appropriate
    // set of TabContents.
    return new NavigationController(
        profile_, navigations, selected_navigation, GetTopLevelHWND());
  } else {
    // No navigations. Create a tab with about:blank.
    TabContents* contents =
        CreateTabContentsForURL(GURL("about:blank"), profile_,
                                PageTransition::START_PAGE, false, NULL);
    return new NavigationController(contents, profile_);
  }
}

// static
void Browser::OpenURLOffTheRecord(Profile* profile, const GURL& url) {
  Profile* off_the_record_profile = profile->GetOffTheRecordProfile();
  Browser* browser = BrowserList::FindBrowserWithType(
      off_the_record_profile, BrowserType::TABBED_BROWSER);
  if (browser == NULL) {
    browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, off_the_record_profile,
                          BrowserType::TABBED_BROWSER, L"");
  }
  browser->AddTabWithURL(url, PageTransition::LINK, true, NULL);
  browser->Show();
  browser->MoveToFront(true);
}

// static
std::wstring Browser::ComputePopupTitle(const GURL& url,
                                        const std::wstring& title) {
  std::wstring result(title);
  Tab::FormatTitleForDisplay(&result);
  return result;
}

void Browser::ConvertToTabbedBrowser() {
  if (GetType() != BrowserType::BROWSER) {
    NOTREACHED();
    return;
  }

  int tab_strip_index = tabstrip_model_.selected_index();
  TabContents* contents = tabstrip_model_.DetachTabContentsAt(tab_strip_index);
  Browser* browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
                                 BrowserType::TABBED_BROWSER, L"");
  browser->AddNewContents(
    NULL, contents, NEW_FOREGROUND_TAB, gfx::Rect(), true);
  browser->Show();
}

void Browser::BuildPopupWindow(TabContents* source,
                               TabContents* new_contents,
                               const gfx::Rect& initial_pos) {
  Browser* browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
                                 BrowserType::BROWSER, std::wstring());
  browser->AddNewContents(source, new_contents,
                          NEW_FOREGROUND_TAB, gfx::Rect(), true);

  // TODO(erg): Need to move all popup sizing logic here, instead of
  // having it spread across three files.

  // For newly opened popup windows, the incoming width/height
  // numbers are for the content area, but x/y are for the actual
  // window position. Thus we can't just call MoveContents().
  gfx::Rect window_rect =
      browser->frame()->GetBoundsForContentBounds(initial_pos);
  window_rect.set_origin(initial_pos.origin());
  ::SetWindowPos(browser->GetTopLevelHWND(), NULL,
                 window_rect.x(), window_rect.y(),
                 window_rect.width(), window_rect.height(), 0);
  win_util::AdjustWindowToFit(browser->GetTopLevelHWND());

  browser->Show();
}

void Browser::ConvertContentsToApplication(TabContents* contents) {
  if (!contents->AsWebContents() || !contents->AsWebContents()->web_app()) {
    NOTREACHED();
    return;
  }

  int index = tabstrip_model_.GetIndexOfTabContents(contents);
  if (index < 0)
    return;

  WebApp* app = contents->AsWebContents()->web_app();
  const std::wstring& app_name =
      app->name().empty() ? ComputeApplicationNameFromURL(app->url()) :
                            app->name();
  RegisterAppPrefs(app_name);

  tabstrip_model_.DetachTabContentsAt(index);
  Browser* browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
                                 BrowserType::APPLICATION, app_name);
  browser->AddNewContents(
    NULL, contents, NEW_FOREGROUND_TAB, gfx::Rect(), true);
  browser->Show();
}

// static
std::wstring Browser::ComputeApplicationNameFromURL(const GURL& url) {
  std::string t;
  t.append(url.host());
  t.append("_");
  t.append(url.path());
  return UTF8ToWide(t);
}

// static
void Browser::OpenWebApplication(Profile* profile,
                                 WebApp* app,
                                 int show_command) {
  const std::wstring& app_name =
      app->name().empty() ? ComputeApplicationNameFromURL(app->url()) :
                            app->name();

  RegisterAppPrefs(app_name);
  Browser* browser = new Browser(gfx::Rect(), show_command, profile,
                                 BrowserType::APPLICATION, app_name);
  browser->AddWebApplicationTab(profile, app, false);
  browser->Show();
}

// static
void Browser::RegisterAppPrefs(const std::wstring& app_name) {
  // A set of apps that we've already started.
  static std::set<std::wstring>* g_app_names = NULL;

  if (!g_app_names)
    g_app_names = new std::set<std::wstring>;

  // Only register once for each app name.
  if (g_app_names->find(app_name) != g_app_names->end())
    return;
  g_app_names->insert(app_name);

  // We need to register the window position pref.
  std::wstring window_pref(prefs::kBrowserWindowPlacement);
  window_pref.append(L"_");
  window_pref.append(app_name);
  PrefService* prefs = g_browser_process->local_state();
  DCHECK(prefs);

  prefs->RegisterDictionaryPref(window_pref.c_str());
}

NavigationController* Browser::GetSelectedNavigationController() const {
  TabContents* tc = GetSelectedTabContents();
  if (tc)
    return tc->controller();
  else
    return NULL;
}