<commit_before>// Copyright (c) 2010 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/chromeos/options/wifi_config_view.h"

#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/options/network_config_view.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "grit/theme_resources.h"
#include "views/controls/button/image_button.h"
#include "views/controls/button/native_button.h"
#include "views/controls/label.h"
#include "views/grid_layout.h"
#include "views/standard_layout.h"
#include "views/window/window.h"

namespace chromeos {

// The width of the password field.
const int kPasswordWidth = 150;

WifiConfigView::WifiConfigView(NetworkConfigView* parent, WifiNetwork wifi)
    : parent_(parent),
      other_network_(false),
      can_login_(false),
      wifi_(wifi),
      ssid_textfield_(NULL),
      identity_textfield_(NULL),
      certificate_browse_button_(NULL),
      certificate_path_(),
      passphrase_textfield_(NULL),
      passphrase_visible_button_(NULL),
      autoconnect_checkbox_(NULL) {
  Init();
}

WifiConfigView::WifiConfigView(NetworkConfigView* parent)
    : parent_(parent),
      other_network_(true),
      can_login_(false),
      ssid_textfield_(NULL),
      identity_textfield_(NULL),
      certificate_browse_button_(NULL),
      certificate_path_(),
      passphrase_textfield_(NULL),
      passphrase_visible_button_(NULL),
      autoconnect_checkbox_(NULL) {
  Init();
}

void WifiConfigView::UpdateCanLogin(void) {
  bool can_login = true;
  if (other_network_) {
    // Since the user can try to connect to a non-encrypted hidden network,
    // only enforce ssid is non-empty.
    can_login = !ssid_textfield_->text().empty();
  } else {
    // Connecting to an encrypted network
    if (passphrase_textfield_ != NULL) {
      // if the network requires a passphrase, make sure it is non empty.
      can_login &= !passphrase_textfield_->text().empty();
    }
    if (identity_textfield_ != NULL) {
      // If we have an identity field, we can login if we have a non empty
      // identity and a certificate path
      can_login &= !identity_textfield_->text().empty() &&
          !certificate_path_.empty();
    }
  }

  // Update the login button enable/disable state if can_login_ changes.
  if (can_login != can_login_) {
    can_login_ = can_login;
    parent_->GetDialogClientView()->UpdateDialogButtons();
  }
}

void WifiConfigView::ContentsChanged(views::Textfield* sender,
                                     const string16& new_contents) {
  UpdateCanLogin();
}

void WifiConfigView::ButtonPressed(views::Button* sender,
                                   const views::Event& event) {
  if (sender == passphrase_visible_button_) {
    if (passphrase_textfield_)
      passphrase_textfield_->SetPassword(!passphrase_textfield_->IsPassword());
  } else if (sender == certificate_browse_button_) {
    select_file_dialog_ = SelectFileDialog::Create(this);
    select_file_dialog_->set_browser_mode(parent_->is_browser_mode());
    select_file_dialog_->SelectFile(SelectFileDialog::SELECT_OPEN_FILE,
                                    string16(), FilePath(), NULL, 0,
                                    std::string(),
                                    parent_->is_browser_mode() ?
                                        NULL :
                                        parent_->GetNativeWindow(),
                                    NULL);
  } else {
    NOTREACHED();
  }
}

void WifiConfigView::FileSelected(const FilePath& path,
                                   int index, void* params) {
  certificate_path_ = path.value();
  if (certificate_browse_button_)
    certificate_browse_button_->SetLabel(path.BaseName().ToWStringHack());
  UpdateCanLogin();  // TODO(njw) Check if the passphrase decrypts the key.
}

bool WifiConfigView::Login() {
  std::string identity_string;
  if (identity_textfield_ != NULL) {
    identity_string = UTF16ToUTF8(identity_textfield_->text());
  }
  if (other_network_) {
    CrosLibrary::Get()->GetNetworkLibrary()->ConnectToWifiNetwork(
        GetSSID(), GetPassphrase(),
        identity_string, certificate_path_,
        autoconnect_checkbox_->checked());
  } else {
    Save();
    CrosLibrary::Get()->GetNetworkLibrary()->ConnectToWifiNetwork(
        wifi_, GetPassphrase(),
        identity_string, certificate_path_);
  }
  return true;
}

bool WifiConfigView::Save() {
  // Save password and auto-connect here.
  if (!other_network_) {
    bool changed = false;

    bool auto_connect = autoconnect_checkbox_->checked();
    if (auto_connect != wifi_.auto_connect()) {
      wifi_.set_auto_connect(auto_connect);
      changed = true;
    }

    if (passphrase_textfield_) {
      const std::string& passphrase =
          UTF16ToUTF8(passphrase_textfield_->text());
      if (passphrase != wifi_.passphrase()) {
        wifi_.set_passphrase(passphrase);
        changed = true;
      }
    }

    if (changed)
      CrosLibrary::Get()->GetNetworkLibrary()->SaveWifiNetwork(wifi_);
  }
  return true;
}

const std::string WifiConfigView::GetSSID() const {
  std::string result;
  if (ssid_textfield_ != NULL)
    result = UTF16ToUTF8(ssid_textfield_->text());
  return result;
}

const std::string WifiConfigView::GetPassphrase() const {
  std::string result;
  if (passphrase_textfield_ != NULL)
    result = UTF16ToUTF8(passphrase_textfield_->text());
  return result;
}

void WifiConfigView::FocusFirstField() {
  if (ssid_textfield_)
    ssid_textfield_->RequestFocus();
  else if (identity_textfield_)
    identity_textfield_->RequestFocus();
  else if (passphrase_textfield_)
    passphrase_textfield_->RequestFocus();
}

// Parse 'path' to determine if the certificate is stored in a pkcs#11 device.
// flimflam recognizes the string "SETTINGS:" to specify authentication
// parameters. 'key_id=' indicates that the certificate is stored in a pkcs#11
// device. See src/third_party/flimflam/files/doc/service-api.txt.
static bool is_certificate_in_pkcs11(const std::string& path) {
  static const std::string settings_string("SETTINGS:");
  static const std::string pkcs11_key("key_id");
  if (path.find(settings_string) == 0) {
    std::string::size_type idx = path.find(pkcs11_key);
    if (idx != std::string::npos)
      idx = path.find_first_not_of(kWhitespaceASCII, idx + pkcs11_key.length());
    if (idx != std::string::npos && path[idx] == '=')
      return true;
  }
  return false;
}

void WifiConfigView::Init() {
  views::GridLayout* layout = CreatePanelGridLayout(this);
  SetLayoutManager(layout);

  int column_view_set_id = 0;
  views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id);
  // Label
  column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1,
                        views::GridLayout::USE_PREF, 0, 0);
  // Textfield
  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
                        views::GridLayout::USE_PREF, 0, kPasswordWidth);
  // Password visible button
  column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::FILL, 1,
                        views::GridLayout::USE_PREF, 0, 0);

  layout->StartRow(0, column_view_set_id);
  layout->AddView(new views::Label(l10n_util::GetString(
      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_NETWORK_ID)));
  if (other_network_) {
    ssid_textfield_ = new views::Textfield(views::Textfield::STYLE_DEFAULT);
    ssid_textfield_->SetController(this);
    layout->AddView(ssid_textfield_);
  } else {
    views::Label* label = new views::Label(ASCIIToWide(wifi_.name()));
    label->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
    layout->AddView(label);
  }
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);

  // Certificates stored in a pkcs11 device can not be browsed
  // and do not require a passphrase.
  bool certificate_in_pkcs11 = false;

  // Add ID and cert password if we're using 802.1x
  // XXX we're cheating and assuming 802.1x means EAP-TLS - not true
  // in general, but very common. WPA Supplicant doesn't report the
  // EAP type because it's unknown until the process begins, and we'd
  // need some kind of callback.
  if (wifi_.encrypted() && wifi_.encryption() == SECURITY_8021X) {
    layout->StartRow(0, column_view_set_id);
    layout->AddView(new views::Label(l10n_util::GetString(
        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_IDENTITY)));
    identity_textfield_ = new views::Textfield(
        views::Textfield::STYLE_DEFAULT);
    identity_textfield_->SetController(this);
    if (!wifi_.identity().empty())
      identity_textfield_->SetText(UTF8ToUTF16(wifi_.identity()));
    layout->AddView(identity_textfield_);
    layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
    layout->StartRow(0, column_view_set_id);
    layout->AddView(new views::Label(l10n_util::GetString(
        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT)));
    if (!wifi_.cert_path().empty()) {
      certificate_path_ = wifi_.cert_path();
      certificate_in_pkcs11 = is_certificate_in_pkcs11(certificate_path_);
    }
    if (certificate_in_pkcs11) {
      std::wstring label = l10n_util::GetString(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_INSTALLED);
      views::Label* cert_text = new views::Label(label);
      cert_text->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
      layout->AddView(cert_text);
    } else {
      std::wstring label;
      if (!certificate_path_.empty())
        label = UTF8ToWide(certificate_path_);
      else
        label = IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_BUTTON;
      certificate_browse_button_ = new views::NativeButton(this, label);
      layout->AddView(certificate_browse_button_);
    }
    layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
  }

  // Add passphrase if other_network or wifi is encrypted.
  if (other_network_ || (wifi_.encrypted() && !certificate_in_pkcs11)) {
    layout->StartRow(0, column_view_set_id);
    int label_text_id;
    if (wifi_.encryption() == SECURITY_8021X)
      label_text_id =
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PRIVATE_KEY_PASSWORD;
    else
      label_text_id = IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PASSPHRASE;
    layout->AddView(new views::Label(l10n_util::GetString(label_text_id)));
    passphrase_textfield_ = new views::Textfield(
        views::Textfield::STYLE_PASSWORD);
    passphrase_textfield_->SetController(this);
    if (!wifi_.passphrase().empty())
      passphrase_textfield_->SetText(UTF8ToUTF16(wifi_.passphrase()));
    layout->AddView(passphrase_textfield_);
    // Password visible button.
    passphrase_visible_button_ = new views::ImageButton(this);
    passphrase_visible_button_->SetImage(views::ImageButton::BS_NORMAL,
        ResourceBundle::GetSharedInstance().
        GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE));
    passphrase_visible_button_->SetImageAlignment(
        views::ImageButton::ALIGN_CENTER, views::ImageButton::ALIGN_MIDDLE);
    layout->AddView(passphrase_visible_button_);
    layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
  }

  // Autoconnect checkbox
  autoconnect_checkbox_ = new views::Checkbox(
      l10n_util::GetString(IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_AUTO_CONNECT));
  // For other network, default to autoconnect.
  bool autoconnect = other_network_ || wifi_.auto_connect();
  autoconnect_checkbox_->SetChecked(autoconnect);
  layout->StartRow(0, column_view_set_id);
  layout->AddView(autoconnect_checkbox_, 3, 1);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
}

}  // namespace chromeos
<commit_msg>WifiConfigView::Init(): Fetch the localized string for the cert button, rather than using the cert button ID directly, which produces garbage.<commit_after>// Copyright (c) 2010 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/chromeos/options/wifi_config_view.h"

#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/options/network_config_view.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "grit/theme_resources.h"
#include "views/controls/button/image_button.h"
#include "views/controls/button/native_button.h"
#include "views/controls/label.h"
#include "views/grid_layout.h"
#include "views/standard_layout.h"
#include "views/window/window.h"

namespace chromeos {

// The width of the password field.
const int kPasswordWidth = 150;

WifiConfigView::WifiConfigView(NetworkConfigView* parent, WifiNetwork wifi)
    : parent_(parent),
      other_network_(false),
      can_login_(false),
      wifi_(wifi),
      ssid_textfield_(NULL),
      identity_textfield_(NULL),
      certificate_browse_button_(NULL),
      certificate_path_(),
      passphrase_textfield_(NULL),
      passphrase_visible_button_(NULL),
      autoconnect_checkbox_(NULL) {
  Init();
}

WifiConfigView::WifiConfigView(NetworkConfigView* parent)
    : parent_(parent),
      other_network_(true),
      can_login_(false),
      ssid_textfield_(NULL),
      identity_textfield_(NULL),
      certificate_browse_button_(NULL),
      certificate_path_(),
      passphrase_textfield_(NULL),
      passphrase_visible_button_(NULL),
      autoconnect_checkbox_(NULL) {
  Init();
}

void WifiConfigView::UpdateCanLogin(void) {
  bool can_login = true;
  if (other_network_) {
    // Since the user can try to connect to a non-encrypted hidden network,
    // only enforce ssid is non-empty.
    can_login = !ssid_textfield_->text().empty();
  } else {
    // Connecting to an encrypted network
    if (passphrase_textfield_ != NULL) {
      // if the network requires a passphrase, make sure it is non empty.
      can_login &= !passphrase_textfield_->text().empty();
    }
    if (identity_textfield_ != NULL) {
      // If we have an identity field, we can login if we have a non empty
      // identity and a certificate path
      can_login &= !identity_textfield_->text().empty() &&
          !certificate_path_.empty();
    }
  }

  // Update the login button enable/disable state if can_login_ changes.
  if (can_login != can_login_) {
    can_login_ = can_login;
    parent_->GetDialogClientView()->UpdateDialogButtons();
  }
}

void WifiConfigView::ContentsChanged(views::Textfield* sender,
                                     const string16& new_contents) {
  UpdateCanLogin();
}

void WifiConfigView::ButtonPressed(views::Button* sender,
                                   const views::Event& event) {
  if (sender == passphrase_visible_button_) {
    if (passphrase_textfield_)
      passphrase_textfield_->SetPassword(!passphrase_textfield_->IsPassword());
  } else if (sender == certificate_browse_button_) {
    select_file_dialog_ = SelectFileDialog::Create(this);
    select_file_dialog_->set_browser_mode(parent_->is_browser_mode());
    select_file_dialog_->SelectFile(SelectFileDialog::SELECT_OPEN_FILE,
                                    string16(), FilePath(), NULL, 0,
                                    std::string(),
                                    parent_->is_browser_mode() ?
                                        NULL :
                                        parent_->GetNativeWindow(),
                                    NULL);
  } else {
    NOTREACHED();
  }
}

void WifiConfigView::FileSelected(const FilePath& path,
                                   int index, void* params) {
  certificate_path_ = path.value();
  if (certificate_browse_button_)
    certificate_browse_button_->SetLabel(path.BaseName().ToWStringHack());
  UpdateCanLogin();  // TODO(njw) Check if the passphrase decrypts the key.
}

bool WifiConfigView::Login() {
  std::string identity_string;
  if (identity_textfield_ != NULL) {
    identity_string = UTF16ToUTF8(identity_textfield_->text());
  }
  if (other_network_) {
    CrosLibrary::Get()->GetNetworkLibrary()->ConnectToWifiNetwork(
        GetSSID(), GetPassphrase(),
        identity_string, certificate_path_,
        autoconnect_checkbox_->checked());
  } else {
    Save();
    CrosLibrary::Get()->GetNetworkLibrary()->ConnectToWifiNetwork(
        wifi_, GetPassphrase(),
        identity_string, certificate_path_);
  }
  return true;
}

bool WifiConfigView::Save() {
  // Save password and auto-connect here.
  if (!other_network_) {
    bool changed = false;

    bool auto_connect = autoconnect_checkbox_->checked();
    if (auto_connect != wifi_.auto_connect()) {
      wifi_.set_auto_connect(auto_connect);
      changed = true;
    }

    if (passphrase_textfield_) {
      const std::string& passphrase =
          UTF16ToUTF8(passphrase_textfield_->text());
      if (passphrase != wifi_.passphrase()) {
        wifi_.set_passphrase(passphrase);
        changed = true;
      }
    }

    if (changed)
      CrosLibrary::Get()->GetNetworkLibrary()->SaveWifiNetwork(wifi_);
  }
  return true;
}

const std::string WifiConfigView::GetSSID() const {
  std::string result;
  if (ssid_textfield_ != NULL)
    result = UTF16ToUTF8(ssid_textfield_->text());
  return result;
}

const std::string WifiConfigView::GetPassphrase() const {
  std::string result;
  if (passphrase_textfield_ != NULL)
    result = UTF16ToUTF8(passphrase_textfield_->text());
  return result;
}

void WifiConfigView::FocusFirstField() {
  if (ssid_textfield_)
    ssid_textfield_->RequestFocus();
  else if (identity_textfield_)
    identity_textfield_->RequestFocus();
  else if (passphrase_textfield_)
    passphrase_textfield_->RequestFocus();
}

// Parse 'path' to determine if the certificate is stored in a pkcs#11 device.
// flimflam recognizes the string "SETTINGS:" to specify authentication
// parameters. 'key_id=' indicates that the certificate is stored in a pkcs#11
// device. See src/third_party/flimflam/files/doc/service-api.txt.
static bool is_certificate_in_pkcs11(const std::string& path) {
  static const std::string settings_string("SETTINGS:");
  static const std::string pkcs11_key("key_id");
  if (path.find(settings_string) == 0) {
    std::string::size_type idx = path.find(pkcs11_key);
    if (idx != std::string::npos)
      idx = path.find_first_not_of(kWhitespaceASCII, idx + pkcs11_key.length());
    if (idx != std::string::npos && path[idx] == '=')
      return true;
  }
  return false;
}

void WifiConfigView::Init() {
  views::GridLayout* layout = CreatePanelGridLayout(this);
  SetLayoutManager(layout);

  int column_view_set_id = 0;
  views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id);
  // Label
  column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1,
                        views::GridLayout::USE_PREF, 0, 0);
  // Textfield
  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
                        views::GridLayout::USE_PREF, 0, kPasswordWidth);
  // Password visible button
  column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::FILL, 1,
                        views::GridLayout::USE_PREF, 0, 0);

  layout->StartRow(0, column_view_set_id);
  layout->AddView(new views::Label(l10n_util::GetString(
      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_NETWORK_ID)));
  if (other_network_) {
    ssid_textfield_ = new views::Textfield(views::Textfield::STYLE_DEFAULT);
    ssid_textfield_->SetController(this);
    layout->AddView(ssid_textfield_);
  } else {
    views::Label* label = new views::Label(ASCIIToWide(wifi_.name()));
    label->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
    layout->AddView(label);
  }
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);

  // Certificates stored in a pkcs11 device can not be browsed
  // and do not require a passphrase.
  bool certificate_in_pkcs11 = false;

  // Add ID and cert password if we're using 802.1x
  // XXX we're cheating and assuming 802.1x means EAP-TLS - not true
  // in general, but very common. WPA Supplicant doesn't report the
  // EAP type because it's unknown until the process begins, and we'd
  // need some kind of callback.
  if (wifi_.encrypted() && wifi_.encryption() == SECURITY_8021X) {
    layout->StartRow(0, column_view_set_id);
    layout->AddView(new views::Label(l10n_util::GetString(
        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_IDENTITY)));
    identity_textfield_ = new views::Textfield(
        views::Textfield::STYLE_DEFAULT);
    identity_textfield_->SetController(this);
    if (!wifi_.identity().empty())
      identity_textfield_->SetText(UTF8ToUTF16(wifi_.identity()));
    layout->AddView(identity_textfield_);
    layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
    layout->StartRow(0, column_view_set_id);
    layout->AddView(new views::Label(l10n_util::GetString(
        IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT)));
    if (!wifi_.cert_path().empty()) {
      certificate_path_ = wifi_.cert_path();
      certificate_in_pkcs11 = is_certificate_in_pkcs11(certificate_path_);
    }
    if (certificate_in_pkcs11) {
      std::wstring label = l10n_util::GetString(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_INSTALLED);
      views::Label* cert_text = new views::Label(label);
      cert_text->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
      layout->AddView(cert_text);
    } else {
      std::wstring label;
      if (!certificate_path_.empty())
        label = UTF8ToWide(certificate_path_);
      else
        label = l10n_util::GetString(
            IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_BUTTON);
      certificate_browse_button_ = new views::NativeButton(this, label);
      layout->AddView(certificate_browse_button_);
    }
    layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
  }

  // Add passphrase if other_network or wifi is encrypted.
  if (other_network_ || (wifi_.encrypted() && !certificate_in_pkcs11)) {
    layout->StartRow(0, column_view_set_id);
    int label_text_id;
    if (wifi_.encryption() == SECURITY_8021X)
      label_text_id =
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PRIVATE_KEY_PASSWORD;
    else
      label_text_id = IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PASSPHRASE;
    layout->AddView(new views::Label(l10n_util::GetString(label_text_id)));
    passphrase_textfield_ = new views::Textfield(
        views::Textfield::STYLE_PASSWORD);
    passphrase_textfield_->SetController(this);
    if (!wifi_.passphrase().empty())
      passphrase_textfield_->SetText(UTF8ToUTF16(wifi_.passphrase()));
    layout->AddView(passphrase_textfield_);
    // Password visible button.
    passphrase_visible_button_ = new views::ImageButton(this);
    passphrase_visible_button_->SetImage(views::ImageButton::BS_NORMAL,
        ResourceBundle::GetSharedInstance().
        GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE));
    passphrase_visible_button_->SetImageAlignment(
        views::ImageButton::ALIGN_CENTER, views::ImageButton::ALIGN_MIDDLE);
    layout->AddView(passphrase_visible_button_);
    layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
  }

  // Autoconnect checkbox
  autoconnect_checkbox_ = new views::Checkbox(
      l10n_util::GetString(IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_AUTO_CONNECT));
  // For other network, default to autoconnect.
  bool autoconnect = other_network_ || wifi_.auto_connect();
  autoconnect_checkbox_->SetChecked(autoconnect);
  layout->StartRow(0, column_view_set_id);
  layout->AddView(autoconnect_checkbox_, 3, 1);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
}

}  // namespace chromeos
<|endoftext|>


<commit_before>// Copyright (c) 2009 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 "base/command_line.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/debugger/devtools_client_host.h"
#include "chrome/browser/debugger/devtools_manager.h"
#include "chrome/browser/debugger/devtools_window.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"

namespace {

// Used to block until a dev tools client window's browser is closed.
class BrowserClosedObserver : public NotificationObserver {
 public:
  BrowserClosedObserver(Browser* browser) {
    registrar_.Add(this, NotificationType::BROWSER_CLOSED,
                   Source<Browser>(browser));
    ui_test_utils::RunMessageLoop();
  }

  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details) {
    MessageLoopForUI::current()->Quit();
  }

 private:
  NotificationRegistrar registrar_;
  DISALLOW_COPY_AND_ASSIGN(BrowserClosedObserver);
};

// The delay waited in some cases where we don't have a notifications for an
// action we take.
const int kActionDelayMs = 500;

const wchar_t kConsoleTestPage[] = L"files/devtools/console_test_page.html";
const wchar_t kDebuggerTestPage[] = L"files/devtools/debugger_test_page.html";
const wchar_t kEvalTestPage[] = L"files/devtools/eval_test_page.html";
const wchar_t kJsPage[] = L"files/devtools/js_page.html";
const wchar_t kResourceTestPage[] = L"files/devtools/resource_test_page.html";
const wchar_t kSimplePage[] = L"files/devtools/simple_page.html";

class DevToolsSanityTest : public InProcessBrowserTest {
 public:
  DevToolsSanityTest() {
    set_show_window(true);
    EnableDOMAutomation();
  }

 protected:
  void RunTest(const std::string& test_name, const std::wstring& test_page) {
    OpenDevToolsWindow(test_page);
    std::string result;

    // At first check that JavaScript part of the front-end is loaded by
    // checking that global variable uiTests exists(it's created after all js
    // files have been loaded) and has runTest method.
    ASSERT_TRUE(
        ui_test_utils::ExecuteJavaScriptAndExtractString(
            client_contents_->render_view_host(),
            L"",
            L"window.domAutomationController.send("
            L"'' + (window.uiTests && (typeof uiTests.runTest)));",
            &result));

    if (result == "function") {
      ASSERT_TRUE(
          ui_test_utils::ExecuteJavaScriptAndExtractString(
              client_contents_->render_view_host(),
              L"",
              UTF8ToWide(StringPrintf("uiTests.runTest('%s')",
                                      test_name.c_str())),
              &result));
      EXPECT_EQ("[OK]", result);
    } else {
      FAIL() << "DevTools front-end is broken.";
    }
    CloseDevToolsWindow();
  }

  void OpenDevToolsWindow(const std::wstring& test_page) {
    HTTPTestServer* server = StartHTTPServer();
    GURL url = server->TestServerPageW(test_page);
    ui_test_utils::NavigateToURL(browser(), url);

    TabContents* tab = browser()->GetTabContentsAt(0);
    inspected_rvh_ = tab->render_view_host();
    DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
    devtools_manager->OpenDevToolsWindow(inspected_rvh_);

    DevToolsClientHost* client_host =
        devtools_manager->GetDevToolsClientHostFor(inspected_rvh_);
    window_ = client_host->AsDevToolsWindow();
    RenderViewHost* client_rvh = window_->GetRenderViewHost();
    client_contents_ = client_rvh->delegate()->GetAsTabContents();
    ui_test_utils::WaitForNavigation(&client_contents_->controller());
  }

  void CloseDevToolsWindow() {
    DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
    // UnregisterDevToolsClientHostFor may destroy window_ so store the browser
    // first.
    Browser* browser = window_->browser();
    devtools_manager->UnregisterDevToolsClientHostFor(inspected_rvh_);
    BrowserClosedObserver close_observer(browser);
  }

  TabContents* client_contents_;
  DevToolsWindow* window_;
  RenderViewHost* inspected_rvh_;
};

// WebInspector opens.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestHostIsPresent) {
  RunTest("testHostIsPresent", kSimplePage);
}

// Tests elements panel basics.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestElementsTreeRoot) {
  RunTest("testElementsTreeRoot", kSimplePage);
}

// Tests main resource load.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestMainResource) {
  RunTest("testMainResource", kSimplePage);
}

// Tests resources panel enabling.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestEnableResourcesTab) {
  RunTest("testEnableResourcesTab", kSimplePage);
}

// Tests resource headers.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestResourceHeaders) {
  RunTest("testResourceHeaders", kResourceTestPage);
}

// Tests profiler panel.
// Flaky, http://crbug.com/21108
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DISABLED_TestProfilerTab) {
  RunTest("testProfilerTab", kJsPage);
}

// Tests scripts panel showing.
// http://crbug.com/16767
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestShowScriptsTab) {
  RunTest("testShowScriptsTab", kDebuggerTestPage);
}

// Tests set breakpoint.
// http://crbug.com/16767
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestSetBreakpoint) {
  RunTest("testSetBreakpoint", kDebuggerTestPage);
}

// Tests that 'Pause' button works for eval.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DISABLED_TestPauseInEval) {
  RunTest("testPauseInEval", kDebuggerTestPage);
}

// Tests console eval.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestConsoleEval) {
  RunTest("testConsoleEval", kConsoleTestPage);
}

// Tests console log.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestConsoleLog) {
  RunTest("testConsoleLog", kConsoleTestPage);
}

// Tests eval global values.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestEvalGlobal) {
  RunTest("testEvalGlobal", kEvalTestPage);
}

// Tests eval on call frame.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestEvalCallFrame) {
  RunTest("testEvalCallFrame", kEvalTestPage);
}

}  // namespace
<commit_msg>Reverting 25574. Review URL: http://codereview.chromium.org/201038<commit_after>// Copyright (c) 2009 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 "base/command_line.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/debugger/devtools_client_host.h"
#include "chrome/browser/debugger/devtools_manager.h"
#include "chrome/browser/debugger/devtools_window.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"

namespace {

// Used to block until a dev tools client window's browser is closed.
class BrowserClosedObserver : public NotificationObserver {
 public:
  BrowserClosedObserver(Browser* browser) {
    registrar_.Add(this, NotificationType::BROWSER_CLOSED,
                   Source<Browser>(browser));
    ui_test_utils::RunMessageLoop();
  }

  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details) {
    MessageLoopForUI::current()->Quit();
  }

 private:
  NotificationRegistrar registrar_;
  DISALLOW_COPY_AND_ASSIGN(BrowserClosedObserver);
};

// The delay waited in some cases where we don't have a notifications for an
// action we take.
const int kActionDelayMs = 500;

const wchar_t kConsoleTestPage[] = L"files/devtools/console_test_page.html";
const wchar_t kDebuggerTestPage[] = L"files/devtools/debugger_test_page.html";
const wchar_t kEvalTestPage[] = L"files/devtools/eval_test_page.html";
const wchar_t kJsPage[] = L"files/devtools/js_page.html";
const wchar_t kResourceTestPage[] = L"files/devtools/resource_test_page.html";
const wchar_t kSimplePage[] = L"files/devtools/simple_page.html";

class DevToolsSanityTest : public InProcessBrowserTest {
 public:
  DevToolsSanityTest() {
    set_show_window(true);
    EnableDOMAutomation();
  }

 protected:
  void RunTest(const std::string& test_name, const std::wstring& test_page) {
    OpenDevToolsWindow(test_page);
    std::string result;

    // At first check that JavaScript part of the front-end is loaded by
    // checking that global variable uiTests exists(it's created after all js
    // files have been loaded) and has runTest method.
    ASSERT_TRUE(
        ui_test_utils::ExecuteJavaScriptAndExtractString(
            client_contents_->render_view_host(),
            L"",
            L"window.domAutomationController.send("
            L"'' + (window.uiTests && (typeof uiTests.runTest)));",
            &result));

    if (result == "function") {
      ASSERT_TRUE(
          ui_test_utils::ExecuteJavaScriptAndExtractString(
              client_contents_->render_view_host(),
              L"",
              UTF8ToWide(StringPrintf("uiTests.runTest('%s')",
                                      test_name.c_str())),
              &result));
      EXPECT_EQ("[OK]", result);
    } else {
      FAIL() << "DevTools front-end is broken.";
    }
    CloseDevToolsWindow();
  }

  void OpenDevToolsWindow(const std::wstring& test_page) {
    HTTPTestServer* server = StartHTTPServer();
    GURL url = server->TestServerPageW(test_page);
    ui_test_utils::NavigateToURL(browser(), url);

    TabContents* tab = browser()->GetTabContentsAt(0);
    inspected_rvh_ = tab->render_view_host();
    DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
    devtools_manager->OpenDevToolsWindow(inspected_rvh_);

    DevToolsClientHost* client_host =
        devtools_manager->GetDevToolsClientHostFor(inspected_rvh_);
    window_ = client_host->AsDevToolsWindow();
    RenderViewHost* client_rvh = window_->GetRenderViewHost();
    client_contents_ = client_rvh->delegate()->GetAsTabContents();
    ui_test_utils::WaitForNavigation(&client_contents_->controller());
  }

  void CloseDevToolsWindow() {
    DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
    // UnregisterDevToolsClientHostFor may destroy window_ so store the browser
    // first.
    Browser* browser = window_->browser();
    devtools_manager->UnregisterDevToolsClientHostFor(inspected_rvh_);
    BrowserClosedObserver close_observer(browser);
  }

  TabContents* client_contents_;
  DevToolsWindow* window_;
  RenderViewHost* inspected_rvh_;
};

// WebInspector opens.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestHostIsPresent) {
  RunTest("testHostIsPresent", kSimplePage);
}

// Tests elements panel basics.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestElementsTreeRoot) {
  RunTest("testElementsTreeRoot", kSimplePage);
}

// Tests main resource load.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestMainResource) {
  RunTest("testMainResource", kSimplePage);
}

// Tests resources panel enabling.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestEnableResourcesTab) {
  RunTest("testEnableResourcesTab", kSimplePage);
}

// Tests resource headers.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestResourceHeaders) {
  RunTest("testResourceHeaders", kResourceTestPage);
}

// Tests profiler panel.
// Flaky, http://crbug.com/21108
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DISABLED_TestProfilerTab) {
  RunTest("testProfilerTab", kJsPage);
}

// Tests scripts panel showing.
// http://crbug.com/16767
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DISABLED_TestShowScriptsTab) {
  RunTest("testShowScriptsTab", kDebuggerTestPage);
}

// Tests set breakpoint.
// http://crbug.com/16767
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DISABLED_TestSetBreakpoint) {
  RunTest("testSetBreakpoint", kDebuggerTestPage);
}

// Tests that 'Pause' button works for eval.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DISABLED_TestPauseInEval) {
  RunTest("testPauseInEval", kDebuggerTestPage);
}

// Tests console eval.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DISABLED_TestConsoleEval) {
  RunTest("testConsoleEval", kConsoleTestPage);
}

// Tests console log.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestConsoleLog) {
  RunTest("testConsoleLog", kConsoleTestPage);
}

// Tests eval global values.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DISABLED_TestEvalGlobal) {
  RunTest("testEvalGlobal", kEvalTestPage);
}

// Tests eval on call frame.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestEvalCallFrame) {
  RunTest("testEvalCallFrame", kEvalTestPage);
}

}  // namespace
<|endoftext|>


<commit_before>// 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/download/download_request_manager.h"

#include "base/message_loop.h"
#include "base/thread.h"
#include "chrome/browser/navigation_controller.h"
#include "chrome/browser/navigation_entry.h"
#include "chrome/browser/constrained_window.h"
#include "chrome/browser/tab_contents.h"
#include "chrome/browser/tab_contents_delegate.h"
#include "chrome/browser/tab_util.h"
#include "chrome/common/l10n_util.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
#include "chrome/views/dialog_delegate.h"
#include "chrome/views/message_box_view.h"

#include "generated_resources.h"

namespace {

// DialogDelegateImpl ----------------------------------------------------------

// DialogDelegateImpl is the DialogDelegate implementation used to prompt the
// the user as to whether they want to allow multiple downloads.
// DialogDelegateImpl delegates the allow/cancel methods to the
// TabDownloadState.
//
// TabDownloadState does not directly implement DialogDelegate, rather it is
// split into DialogDelegateImpl as TabDownloadState may be deleted before
// the dialog.

class DialogDelegateImpl : public views::DialogDelegate {
 public:
  DialogDelegateImpl(TabContents* tab,
                     DownloadRequestManager::TabDownloadState* host);

  void set_host(DownloadRequestManager::TabDownloadState* host) {
    host_ = host;
  }

  // Closes the prompt.
  void CloseWindow();

 private:
  // DialogDelegate methods;
  virtual bool Cancel();
  virtual bool Accept();
  virtual views::View* GetContentsView() { return message_view_; }
  virtual std::wstring GetDialogButtonLabel(DialogButton button) const;
  virtual int GetDefaultDialogButton() const {
    return DIALOGBUTTON_CANCEL;
  }
  virtual void WindowClosing();

  // The TabDownloadState we're displaying the dialog for. May be null.
  DownloadRequestManager::TabDownloadState* host_;

  MessageBoxView* message_view_;

  ConstrainedWindow* window_;

  DISALLOW_COPY_AND_ASSIGN(DialogDelegateImpl);
};

}  // namespace

// TabDownloadState ------------------------------------------------------------

// TabDownloadState maintains the download state for a particular tab.
// TabDownloadState installs observers to update the download status
// appropriately. Additionally TabDownloadState prompts the user as necessary.
// TabDownloadState deletes itself (by invoking DownloadRequestManager::Remove)
// as necessary.

class DownloadRequestManager::TabDownloadState : public NotificationObserver {
 public:
  // Creates a new TabDownloadState. |controller| is the controller the
  // TabDownloadState tracks the state of and is the host for any dialogs that
  // are displayed. |originating_controller| is used to determine the host of
  // the initial download. If |originating_controller| is null, |controller| is
  // used. |originating_controller| is typically null, but differs from
  // |controller| in the case of a constrained popup requesting the download.
  TabDownloadState(DownloadRequestManager* host,
                   NavigationController* controller,
                   NavigationController* originating_controller);
  ~TabDownloadState();

  // Status of the download.
  void set_download_status(DownloadRequestManager::DownloadStatus status) {
    status_ = status;
  }
  DownloadRequestManager::DownloadStatus download_status() const {
    return status_;
  }

  // Invoked when a user gesture occurs (mouse click, enter or space). This
  // may result in invoking Remove on DownloadRequestManager.
  void OnUserGesture();

  // Asks the user if they really want to allow the download.
  // See description above CanDownloadOnIOThread for details on lifetime of
  // callback.
  void PromptUserForDownload(TabContents* tab,
                             DownloadRequestManager::Callback* callback);

  // Are we showing a prompt to the user?
  bool is_showing_prompt() const { return (dialog_delegate_ != NULL); }

  // NavigationController we're tracking.
  NavigationController* controller() const { return controller_; }

  // Invoked from DialogDelegateImpl. Notifies the delegates and changes the
  // status appropriately.
  void Cancel();
  void Accept();

 private:
  // NotificationObserver method.
  void Observe(NotificationType type,
               const NotificationSource& source,
               const NotificationDetails& details);

  // Notifies the callbacks as to whether the download is allowed or not.
  // Updates status_ appropriately.
  void NotifyCallbacks(bool allow);

  DownloadRequestManager* host_;

  NavigationController* controller_;

  // Host of the first page the download started on. This may be empty.
  std::string initial_page_host_;

  DownloadRequestManager::DownloadStatus status_;

  // Callbacks we need to notify. This is only non-empty if we're showing a
  // dialog.
  // See description above CanDownloadOnIOThread for details on lifetime of
  // callbacks.
  std::vector<DownloadRequestManager::Callback*> callbacks_;

  // Used to remove observers installed on NavigationController.
  NotificationRegistrar registrar_;

  // Handles showing the dialog to the user, may be null.
  DialogDelegateImpl* dialog_delegate_;

  DISALLOW_COPY_AND_ASSIGN(TabDownloadState);
};

DownloadRequestManager::TabDownloadState::TabDownloadState(
    DownloadRequestManager* host,
    NavigationController* controller,
    NavigationController* originating_controller)
    : host_(host),
      controller_(controller),
      status_(DownloadRequestManager::ALLOW_ONE_DOWNLOAD),
      dialog_delegate_(NULL) {
  Source<NavigationController> notification_source(controller);
  registrar_.Add(this, NOTIFY_NAV_ENTRY_COMMITTED, notification_source);
  registrar_.Add(this, NOTIFY_TAB_CLOSED, notification_source);

  NavigationEntry* active_entry = originating_controller ?
      originating_controller->GetActiveEntry() : controller->GetActiveEntry();
  if (active_entry)
    initial_page_host_ = active_entry->url().host();
}

DownloadRequestManager::TabDownloadState::~TabDownloadState() {
  // We should only be destroyed after the callbacks have been notified.
  DCHECK(callbacks_.empty());

  // And we should have closed the message box.
  DCHECK(!dialog_delegate_);
}

void DownloadRequestManager::TabDownloadState::OnUserGesture() {
  if (is_showing_prompt()) {
    // Don't change the state if the user clicks on the page some where.
    return;
  }

  if (status_ != DownloadRequestManager::ALLOW_ALL_DOWNLOADS &&
      status_ != DownloadRequestManager::DOWNLOADS_NOT_ALLOWED) {
    // Revert to default status.
    host_->Remove(this);
    // WARNING: We've been deleted.
    return;
  }
}

void DownloadRequestManager::TabDownloadState::PromptUserForDownload(
    TabContents* tab,
    DownloadRequestManager::Callback* callback) {
  callbacks_.push_back(callback);

  if (is_showing_prompt())
    return;  // Already showing prompt.

  if (DownloadRequestManager::delegate_)
    NotifyCallbacks(DownloadRequestManager::delegate_->ShouldAllowDownload());
  else
    dialog_delegate_ = new DialogDelegateImpl(tab, this);
}

void DownloadRequestManager::TabDownloadState::Cancel() {
  NotifyCallbacks(false);
}

void DownloadRequestManager::TabDownloadState::Accept() {
  NotifyCallbacks(true);
}

void DownloadRequestManager::TabDownloadState::Observe(
    NotificationType type,
    const NotificationSource& source,
    const NotificationDetails& details) {
  if ((type != NOTIFY_NAV_ENTRY_COMMITTED && type != NOTIFY_TAB_CLOSED) ||
       Source<NavigationController>(source).ptr() != controller_) {
    NOTREACHED();
    return;
  }

  switch(type) {
    case NOTIFY_NAV_ENTRY_COMMITTED: {
      if (is_showing_prompt()) {
        // We're prompting the user and they navigated away. Close the popup and
        // cancel the downloads.
        dialog_delegate_->CloseWindow();
        // After switch we'll notify callbacks and get deleted.
      } else if (status_ == DownloadRequestManager::ALLOW_ALL_DOWNLOADS ||
                 status_ == DownloadRequestManager::DOWNLOADS_NOT_ALLOWED) {
        // User has either allowed all downloads or canceled all downloads. Only
        // reset the download state if the user is navigating to a different
        // host (or host is empty).
        NavigationController::LoadCommittedDetails* load_details =
            Details<NavigationController::LoadCommittedDetails>(details).ptr();
        NavigationEntry* entry = load_details->entry;
        if (load_details->is_auto || !entry ||
            (!initial_page_host_.empty() &&
             !entry->url().host().empty() &&
             entry->url().host() == initial_page_host_)) {
          return;
        }
      }  // else case: we're not prompting user and user hasn't allowed or
         // disallowed downloads, break so that we get deleted after switch.
      break;
    }

    case NOTIFY_TAB_CLOSED:
      // Tab closed, no need to handle closing the dialog as it's owned by the
      // TabContents, break so that we get deleted after switch.
      break;

    default:
      NOTREACHED();
  }

  NotifyCallbacks(false);
  host_->Remove(this);
}

void DownloadRequestManager::TabDownloadState::NotifyCallbacks(bool allow) {
  if (dialog_delegate_) {
    // Reset the delegate so we don't get notified again.
    dialog_delegate_->set_host(NULL);
    dialog_delegate_ = NULL;
  }
  status_ = allow ?
      DownloadRequestManager::ALLOW_ALL_DOWNLOADS :
      DownloadRequestManager::DOWNLOADS_NOT_ALLOWED;
  std::vector<DownloadRequestManager::Callback*> callbacks;
  callbacks.swap(callbacks_);
  for (size_t i = 0; i < callbacks.size(); ++i)
    host_->ScheduleNotification(callbacks[i], allow);
}

namespace {

// DialogDelegateImpl ----------------------------------------------------------

DialogDelegateImpl::DialogDelegateImpl(
    TabContents* tab,
    DownloadRequestManager::TabDownloadState* host)
    : host_(host) {
  message_view_ = new MessageBoxView(
      MessageBoxView::kIsConfirmMessageBox,
      l10n_util::GetString(IDS_MULTI_DOWNLOAD_WARNING),
      std::wstring());
  window_ = tab->CreateConstrainedDialog(this, message_view_);
}

void DialogDelegateImpl::CloseWindow() {
  window_->CloseConstrainedWindow();
}

bool DialogDelegateImpl::Cancel() {
  if (host_)
    host_->Cancel();
  return true;
}

bool DialogDelegateImpl::Accept() {
  if (host_)
    host_->Accept();
  return true;
}

std::wstring DialogDelegateImpl::GetDialogButtonLabel(
    DialogButton button) const {
  if (button == DIALOGBUTTON_OK)
    return l10n_util::GetString(IDS_MULTI_DOWNLOAD_WARNING_ALLOW);
  if (button == DIALOGBUTTON_CANCEL)
    return l10n_util::GetString(IDS_MULTI_DOWNLOAD_WARNING_DENY);
  return std::wstring();
}

void DialogDelegateImpl::WindowClosing() {
  DCHECK(!host_);
  delete this;
}

}  // namespace

// DownloadRequestManager ------------------------------------------------------

DownloadRequestManager::DownloadRequestManager(MessageLoop* io_loop,
                                               MessageLoop* ui_loop)
    : io_loop_(io_loop),
      ui_loop_(ui_loop) {
}

DownloadRequestManager::~DownloadRequestManager() {
  // All the tabs should have closed before us, which sends notification and
  // removes from state_map_. As such, there should be no pending callbacks.
  DCHECK(state_map_.empty());
}

DownloadRequestManager::DownloadStatus
    DownloadRequestManager::GetDownloadStatus(TabContents* tab) {
  TabDownloadState* state = GetDownloadState(tab->controller(), NULL, false);
  return state ? state->download_status() : ALLOW_ONE_DOWNLOAD;
}

void DownloadRequestManager::CanDownloadOnIOThread(int render_process_host_id,
                                                   int render_view_id,
                                                   Callback* callback) {
  // This is invoked on the IO thread. Schedule the task to run on the UI
  // thread so that we can query UI state.
  DCHECK(!io_loop_ || io_loop_ == MessageLoop::current());
  ui_loop_->PostTask(FROM_HERE,
      NewRunnableMethod(this, &DownloadRequestManager::CanDownload,
                        render_process_host_id, render_view_id, callback));
}

void DownloadRequestManager::OnUserGesture(TabContents* tab) {
  NavigationController* controller = tab->controller();
  if (!controller) {
    NOTREACHED();
    return;
  }

  TabDownloadState* state = GetDownloadState(controller, NULL, false);
  if (!state)
    return;

  state->OnUserGesture();
}

// static
void DownloadRequestManager::SetTestingDelegate(TestingDelegate* delegate) {
  delegate_ = delegate;
}

DownloadRequestManager::TabDownloadState* DownloadRequestManager::
    GetDownloadState(NavigationController* controller,
                     NavigationController* originating_controller,
                     bool create) {
  DCHECK(controller);
  StateMap::iterator i = state_map_.find(controller);
  if (i != state_map_.end())
    return i->second;

  if (!create)
    return NULL;

  TabDownloadState* state =
      new TabDownloadState(this, controller, originating_controller);
  state_map_[controller] = state;
  return state;
}

void DownloadRequestManager::CanDownload(int render_process_host_id,
                                         int render_view_id,
                                         Callback* callback) {
  DCHECK(!ui_loop_ || MessageLoop::current() == ui_loop_);

  TabContents* originating_tab =
      tab_util::GetTabContentsByID(render_process_host_id, render_view_id);
  if (!originating_tab) {
    // The tab was closed, don't allow the download.
    ScheduleNotification(callback, false);
    return;
  }
  CanDownloadImpl(originating_tab, callback);
}

void DownloadRequestManager::CanDownloadImpl(
    TabContents* originating_tab,
    Callback* callback) {
  TabContents* effective_tab = originating_tab;
  if (effective_tab->delegate() &&
      effective_tab->delegate()->GetConstrainingContents(effective_tab)) {
    // The tab requesting the download is a constrained popup that is not
    // shown, treat the request as if it came from the parent.
    effective_tab =
        effective_tab->delegate()->GetConstrainingContents(effective_tab);
  }

  NavigationController* controller = effective_tab->controller();
  DCHECK(controller);
  TabDownloadState* state = GetDownloadState(
      controller, originating_tab->controller(), true);
  switch (state->download_status()) {
    case ALLOW_ALL_DOWNLOADS:
      ScheduleNotification(callback, true);
      break;

    case ALLOW_ONE_DOWNLOAD:
      state->set_download_status(PROMPT_BEFORE_DOWNLOAD);
      ScheduleNotification(callback, true);
      break;

    case DOWNLOADS_NOT_ALLOWED:
      ScheduleNotification(callback, false);
      break;

    case PROMPT_BEFORE_DOWNLOAD:
      state->PromptUserForDownload(effective_tab, callback);
      break;

    default:
      NOTREACHED();
  }
}

void DownloadRequestManager::ScheduleNotification(Callback* callback,
                                                  bool allow) {
  if (io_loop_) {
    io_loop_->PostTask(FROM_HERE,
        NewRunnableMethod(this, &DownloadRequestManager::NotifyCallback,
                          callback, allow));
  } else {
    NotifyCallback(callback, allow);
  }
}

void DownloadRequestManager::NotifyCallback(Callback* callback, bool allow) {
  // We better be on the IO thread now.
  DCHECK(!io_loop_ || MessageLoop::current() == io_loop_);
  if (allow)
    callback->ContinueDownload();
  else
    callback->CancelDownload();
}

void DownloadRequestManager::Remove(TabDownloadState* state) {
  DCHECK(state_map_.find(state->controller()) != state_map_.end());
  state_map_.erase(state->controller());
  delete state;
}

// static
DownloadRequestManager::TestingDelegate* DownloadRequestManager::delegate_ =
    NULL;
<commit_msg>Fixes bug where carpet bombing dialog would come in some situations it should not have.<commit_after>// 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/download/download_request_manager.h"

#include "base/message_loop.h"
#include "base/thread.h"
#include "chrome/browser/navigation_controller.h"
#include "chrome/browser/navigation_entry.h"
#include "chrome/browser/constrained_window.h"
#include "chrome/browser/tab_contents.h"
#include "chrome/browser/tab_contents_delegate.h"
#include "chrome/browser/tab_util.h"
#include "chrome/common/l10n_util.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
#include "chrome/views/dialog_delegate.h"
#include "chrome/views/message_box_view.h"

#include "generated_resources.h"

namespace {

// DialogDelegateImpl ----------------------------------------------------------

// DialogDelegateImpl is the DialogDelegate implementation used to prompt the
// the user as to whether they want to allow multiple downloads.
// DialogDelegateImpl delegates the allow/cancel methods to the
// TabDownloadState.
//
// TabDownloadState does not directly implement DialogDelegate, rather it is
// split into DialogDelegateImpl as TabDownloadState may be deleted before
// the dialog.

class DialogDelegateImpl : public views::DialogDelegate {
 public:
  DialogDelegateImpl(TabContents* tab,
                     DownloadRequestManager::TabDownloadState* host);

  void set_host(DownloadRequestManager::TabDownloadState* host) {
    host_ = host;
  }

  // Closes the prompt.
  void CloseWindow();

 private:
  // DialogDelegate methods;
  virtual bool Cancel();
  virtual bool Accept();
  virtual views::View* GetContentsView() { return message_view_; }
  virtual std::wstring GetDialogButtonLabel(DialogButton button) const;
  virtual int GetDefaultDialogButton() const {
    return DIALOGBUTTON_CANCEL;
  }
  virtual void WindowClosing();

  // The TabDownloadState we're displaying the dialog for. May be null.
  DownloadRequestManager::TabDownloadState* host_;

  MessageBoxView* message_view_;

  ConstrainedWindow* window_;

  DISALLOW_COPY_AND_ASSIGN(DialogDelegateImpl);
};

}  // namespace

// TabDownloadState ------------------------------------------------------------

// TabDownloadState maintains the download state for a particular tab.
// TabDownloadState installs observers to update the download status
// appropriately. Additionally TabDownloadState prompts the user as necessary.
// TabDownloadState deletes itself (by invoking DownloadRequestManager::Remove)
// as necessary.

class DownloadRequestManager::TabDownloadState : public NotificationObserver {
 public:
  // Creates a new TabDownloadState. |controller| is the controller the
  // TabDownloadState tracks the state of and is the host for any dialogs that
  // are displayed. |originating_controller| is used to determine the host of
  // the initial download. If |originating_controller| is null, |controller| is
  // used. |originating_controller| is typically null, but differs from
  // |controller| in the case of a constrained popup requesting the download.
  TabDownloadState(DownloadRequestManager* host,
                   NavigationController* controller,
                   NavigationController* originating_controller);
  ~TabDownloadState();

  // Status of the download.
  void set_download_status(DownloadRequestManager::DownloadStatus status) {
    status_ = status;
  }
  DownloadRequestManager::DownloadStatus download_status() const {
    return status_;
  }

  // Invoked when a user gesture occurs (mouse click, enter or space). This
  // may result in invoking Remove on DownloadRequestManager.
  void OnUserGesture();

  // Asks the user if they really want to allow the download.
  // See description above CanDownloadOnIOThread for details on lifetime of
  // callback.
  void PromptUserForDownload(TabContents* tab,
                             DownloadRequestManager::Callback* callback);

  // Are we showing a prompt to the user?
  bool is_showing_prompt() const { return (dialog_delegate_ != NULL); }

  // NavigationController we're tracking.
  NavigationController* controller() const { return controller_; }

  // Invoked from DialogDelegateImpl. Notifies the delegates and changes the
  // status appropriately.
  void Cancel();
  void Accept();

 private:
  // NotificationObserver method.
  void Observe(NotificationType type,
               const NotificationSource& source,
               const NotificationDetails& details);

  // Notifies the callbacks as to whether the download is allowed or not.
  // Updates status_ appropriately.
  void NotifyCallbacks(bool allow);

  DownloadRequestManager* host_;

  NavigationController* controller_;

  // Host of the first page the download started on. This may be empty.
  std::string initial_page_host_;

  DownloadRequestManager::DownloadStatus status_;

  // Callbacks we need to notify. This is only non-empty if we're showing a
  // dialog.
  // See description above CanDownloadOnIOThread for details on lifetime of
  // callbacks.
  std::vector<DownloadRequestManager::Callback*> callbacks_;

  // Used to remove observers installed on NavigationController.
  NotificationRegistrar registrar_;

  // Handles showing the dialog to the user, may be null.
  DialogDelegateImpl* dialog_delegate_;

  DISALLOW_COPY_AND_ASSIGN(TabDownloadState);
};

DownloadRequestManager::TabDownloadState::TabDownloadState(
    DownloadRequestManager* host,
    NavigationController* controller,
    NavigationController* originating_controller)
    : host_(host),
      controller_(controller),
      status_(DownloadRequestManager::ALLOW_ONE_DOWNLOAD),
      dialog_delegate_(NULL) {
  Source<NavigationController> notification_source(controller);
  registrar_.Add(this, NOTIFY_NAV_ENTRY_PENDING, notification_source);
  registrar_.Add(this, NOTIFY_TAB_CLOSED, notification_source);

  NavigationEntry* active_entry = originating_controller ?
      originating_controller->GetActiveEntry() : controller->GetActiveEntry();
  if (active_entry)
    initial_page_host_ = active_entry->url().host();
}

DownloadRequestManager::TabDownloadState::~TabDownloadState() {
  // We should only be destroyed after the callbacks have been notified.
  DCHECK(callbacks_.empty());

  // And we should have closed the message box.
  DCHECK(!dialog_delegate_);
}

void DownloadRequestManager::TabDownloadState::OnUserGesture() {
  if (is_showing_prompt()) {
    // Don't change the state if the user clicks on the page some where.
    return;
  }

  if (status_ != DownloadRequestManager::ALLOW_ALL_DOWNLOADS &&
      status_ != DownloadRequestManager::DOWNLOADS_NOT_ALLOWED) {
    // Revert to default status.
    host_->Remove(this);
    // WARNING: We've been deleted.
    return;
  }
}

void DownloadRequestManager::TabDownloadState::PromptUserForDownload(
    TabContents* tab,
    DownloadRequestManager::Callback* callback) {
  callbacks_.push_back(callback);

  if (is_showing_prompt())
    return;  // Already showing prompt.

  if (DownloadRequestManager::delegate_)
    NotifyCallbacks(DownloadRequestManager::delegate_->ShouldAllowDownload());
  else
    dialog_delegate_ = new DialogDelegateImpl(tab, this);
}

void DownloadRequestManager::TabDownloadState::Cancel() {
  NotifyCallbacks(false);
}

void DownloadRequestManager::TabDownloadState::Accept() {
  NotifyCallbacks(true);
}

void DownloadRequestManager::TabDownloadState::Observe(
    NotificationType type,
    const NotificationSource& source,
    const NotificationDetails& details) {
  if ((type != NOTIFY_NAV_ENTRY_PENDING && type != NOTIFY_TAB_CLOSED) ||
       Source<NavigationController>(source).ptr() != controller_) {
    NOTREACHED();
    return;
  }

  switch(type) {
    case NOTIFY_NAV_ENTRY_PENDING: {
      // NOTE: resetting state on a pending navigate isn't ideal. In particular
      // it is possible that queued up downloads for the page before the
      // pending navigate will be delivered to us after we process this
      // request. If this happens we may let a download through that we
      // shouldn't have. But this is rather rare, and it is difficult to get
      // 100% right, so we don't deal with it.
      NavigationEntry* entry = controller_->GetPendingEntry();
      if (!entry)
        return;

      if (PageTransition::IsRedirect(entry->transition_type())) {
        // Redirects don't count.
        return;
      }

      if (is_showing_prompt()) {
        // We're prompting the user and they navigated away. Close the popup and
        // cancel the downloads.
        dialog_delegate_->CloseWindow();
        // After switch we'll notify callbacks and get deleted.
      } else if (status_ == DownloadRequestManager::ALLOW_ALL_DOWNLOADS ||
                 status_ == DownloadRequestManager::DOWNLOADS_NOT_ALLOWED) {
        // User has either allowed all downloads or canceled all downloads. Only
        // reset the download state if the user is navigating to a different
        // host (or host is empty).
        if (!initial_page_host_.empty() && !entry->url().host().empty() &&
            entry->url().host() == initial_page_host_) {
          return;
        }
      }  // else case: we're not prompting user and user hasn't allowed or
         // disallowed downloads, break so that we get deleted after switch.
      break;
    }

    case NOTIFY_TAB_CLOSED:
      // Tab closed, no need to handle closing the dialog as it's owned by the
      // TabContents, break so that we get deleted after switch.
      break;

    default:
      NOTREACHED();
  }

  NotifyCallbacks(false);
  host_->Remove(this);
}

void DownloadRequestManager::TabDownloadState::NotifyCallbacks(bool allow) {
  if (dialog_delegate_) {
    // Reset the delegate so we don't get notified again.
    dialog_delegate_->set_host(NULL);
    dialog_delegate_ = NULL;
  }
  status_ = allow ?
      DownloadRequestManager::ALLOW_ALL_DOWNLOADS :
      DownloadRequestManager::DOWNLOADS_NOT_ALLOWED;
  std::vector<DownloadRequestManager::Callback*> callbacks;
  callbacks.swap(callbacks_);
  for (size_t i = 0; i < callbacks.size(); ++i)
    host_->ScheduleNotification(callbacks[i], allow);
}

namespace {

// DialogDelegateImpl ----------------------------------------------------------

DialogDelegateImpl::DialogDelegateImpl(
    TabContents* tab,
    DownloadRequestManager::TabDownloadState* host)
    : host_(host) {
  message_view_ = new MessageBoxView(
      MessageBoxView::kIsConfirmMessageBox,
      l10n_util::GetString(IDS_MULTI_DOWNLOAD_WARNING),
      std::wstring());
  window_ = tab->CreateConstrainedDialog(this, message_view_);
}

void DialogDelegateImpl::CloseWindow() {
  window_->CloseConstrainedWindow();
}

bool DialogDelegateImpl::Cancel() {
  if (host_)
    host_->Cancel();
  return true;
}

bool DialogDelegateImpl::Accept() {
  if (host_)
    host_->Accept();
  return true;
}

std::wstring DialogDelegateImpl::GetDialogButtonLabel(
    DialogButton button) const {
  if (button == DIALOGBUTTON_OK)
    return l10n_util::GetString(IDS_MULTI_DOWNLOAD_WARNING_ALLOW);
  if (button == DIALOGBUTTON_CANCEL)
    return l10n_util::GetString(IDS_MULTI_DOWNLOAD_WARNING_DENY);
  return std::wstring();
}

void DialogDelegateImpl::WindowClosing() {
  DCHECK(!host_);
  delete this;
}

}  // namespace

// DownloadRequestManager ------------------------------------------------------

DownloadRequestManager::DownloadRequestManager(MessageLoop* io_loop,
                                               MessageLoop* ui_loop)
    : io_loop_(io_loop),
      ui_loop_(ui_loop) {
}

DownloadRequestManager::~DownloadRequestManager() {
  // All the tabs should have closed before us, which sends notification and
  // removes from state_map_. As such, there should be no pending callbacks.
  DCHECK(state_map_.empty());
}

DownloadRequestManager::DownloadStatus
    DownloadRequestManager::GetDownloadStatus(TabContents* tab) {
  TabDownloadState* state = GetDownloadState(tab->controller(), NULL, false);
  return state ? state->download_status() : ALLOW_ONE_DOWNLOAD;
}

void DownloadRequestManager::CanDownloadOnIOThread(int render_process_host_id,
                                                   int render_view_id,
                                                   Callback* callback) {
  // This is invoked on the IO thread. Schedule the task to run on the UI
  // thread so that we can query UI state.
  DCHECK(!io_loop_ || io_loop_ == MessageLoop::current());
  ui_loop_->PostTask(FROM_HERE,
      NewRunnableMethod(this, &DownloadRequestManager::CanDownload,
                        render_process_host_id, render_view_id, callback));
}

void DownloadRequestManager::OnUserGesture(TabContents* tab) {
  NavigationController* controller = tab->controller();
  if (!controller) {
    NOTREACHED();
    return;
  }

  TabDownloadState* state = GetDownloadState(controller, NULL, false);
  if (!state)
    return;

  state->OnUserGesture();
}

// static
void DownloadRequestManager::SetTestingDelegate(TestingDelegate* delegate) {
  delegate_ = delegate;
}

DownloadRequestManager::TabDownloadState* DownloadRequestManager::
    GetDownloadState(NavigationController* controller,
                     NavigationController* originating_controller,
                     bool create) {
  DCHECK(controller);
  StateMap::iterator i = state_map_.find(controller);
  if (i != state_map_.end())
    return i->second;

  if (!create)
    return NULL;

  TabDownloadState* state =
      new TabDownloadState(this, controller, originating_controller);
  state_map_[controller] = state;
  return state;
}

void DownloadRequestManager::CanDownload(int render_process_host_id,
                                         int render_view_id,
                                         Callback* callback) {
  DCHECK(!ui_loop_ || MessageLoop::current() == ui_loop_);

  TabContents* originating_tab =
      tab_util::GetTabContentsByID(render_process_host_id, render_view_id);
  if (!originating_tab) {
    // The tab was closed, don't allow the download.
    ScheduleNotification(callback, false);
    return;
  }
  CanDownloadImpl(originating_tab, callback);
}

void DownloadRequestManager::CanDownloadImpl(
    TabContents* originating_tab,
    Callback* callback) {
  TabContents* effective_tab = originating_tab;
  if (effective_tab->delegate() &&
      effective_tab->delegate()->GetConstrainingContents(effective_tab)) {
    // The tab requesting the download is a constrained popup that is not
    // shown, treat the request as if it came from the parent.
    effective_tab =
        effective_tab->delegate()->GetConstrainingContents(effective_tab);
  }

  NavigationController* controller = effective_tab->controller();
  DCHECK(controller);
  TabDownloadState* state = GetDownloadState(
      controller, originating_tab->controller(), true);
  switch (state->download_status()) {
    case ALLOW_ALL_DOWNLOADS:
      ScheduleNotification(callback, true);
      break;

    case ALLOW_ONE_DOWNLOAD:
      state->set_download_status(PROMPT_BEFORE_DOWNLOAD);
      ScheduleNotification(callback, true);
      break;

    case DOWNLOADS_NOT_ALLOWED:
      ScheduleNotification(callback, false);
      break;

    case PROMPT_BEFORE_DOWNLOAD:
      state->PromptUserForDownload(effective_tab, callback);
      break;

    default:
      NOTREACHED();
  }
}

void DownloadRequestManager::ScheduleNotification(Callback* callback,
                                                  bool allow) {
  if (io_loop_) {
    io_loop_->PostTask(FROM_HERE,
        NewRunnableMethod(this, &DownloadRequestManager::NotifyCallback,
                          callback, allow));
  } else {
    NotifyCallback(callback, allow);
  }
}

void DownloadRequestManager::NotifyCallback(Callback* callback, bool allow) {
  // We better be on the IO thread now.
  DCHECK(!io_loop_ || MessageLoop::current() == io_loop_);
  if (allow)
    callback->ContinueDownload();
  else
    callback->CancelDownload();
}

void DownloadRequestManager::Remove(TabDownloadState* state) {
  DCHECK(state_map_.find(state->controller()) != state_map_.end());
  state_map_.erase(state->controller());
  delete state;
}

// static
DownloadRequestManager::TestingDelegate* DownloadRequestManager::delegate_ =
    NULL;
<|endoftext|>


<commit_before>/* Copyright 2021, The TensorFlow Federated Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
==============================================================================*/

#include "tensorflow_federated/cc/core/impl/executors/session_provider.h"

#include "absl/container/flat_hash_set.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include "absl/synchronization/mutex.h"
#include "tensorflow/core/common_runtime/device_mgr.h"
#include "tensorflow/core/framework/attr_value.pb.h"
#include "tensorflow/core/framework/device_factory.h"
#include "tensorflow/core/framework/function.pb.h"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/types.h"
#include "tensorflow/core/graph/default_device.h"
#include "tensorflow/core/public/session_options.h"

namespace tensorflow_federated {

// A process level counter for SessionProvider creation. This ensures
// each session provider is creating unique containers for each session
// created in the process. This is necessary because the TesnorFlow
// ResourceManager shares resource objects across Sessions in the same process,
// but we need to ability to clear an individual Session's container after
// each call.
ABSL_CONST_INIT static absl::Mutex function_id_mutex(absl::kConstInit);
int32_t GetNextFunctionId() {
  absl::MutexLock function_id_lock(&function_id_mutex);
  static int32_t function_id = 0;
  return function_id++;
}

const tensorflow::SessionOptions& get_session_options() {
  // Creates default SessionOptions on first call, and re-uses them for all
  // future calls.
  static tensorflow::SessionOptions* session_options = []() {
    tensorflow::SessionOptions* options_pb = new tensorflow::SessionOptions();
    tensorflow::GraphOptions* graph_options_pb =
        options_pb->config.mutable_graph_options();
    // Disable JIT/runtime Grappler. TFF typically has short lived sessions,
    // meaning that the benefit from running grappler is generally not realized
    // and can be very expensive (anecdotally ~30% of CPU time).
    graph_options_pb->mutable_rewrite_options()->set_disable_meta_optimizer(
        true);
    graph_options_pb->mutable_optimizer_options()->set_opt_level(
        tensorflow::OptimizerOptions::L0);
    graph_options_pb->mutable_optimizer_options()->set_global_jit_level(
        tensorflow::OptimizerOptions::OFF);
    // Don't eagerly allocate all GPU memory in each session.
    options_pb->config.mutable_gpu_options()->set_allow_growth(true);
    // Let the session know the graph will not change.
    auto experimental_pb = options_pb->config.mutable_experimental();
    experimental_pb->set_optimize_for_static_graph(true);
    experimental_pb->set_disable_output_partition_graphs(true);
    options_pb->config.set_allow_soft_placement(true);
    return options_pb;
  }();
  return *session_options;
}

struct AcceleratorDevices {
  const int16_t num_gpus = 0;
  const int16_t num_tpus = 0;
};

const AcceleratorDevices& GetAcceleratorDevices() {
  static const AcceleratorDevices* accelerator_devices =
      []() -> AcceleratorDevices* {
    int16_t num_gpus = 0;
    int16_t num_tpus = 0;
    std::vector<std::string> devices;
    tensorflow::Status s =
        tensorflow::DeviceFactory::ListAllPhysicalDevices(&devices);
    if (!s.ok()) {
      LOG(ERROR) << "Error detecting physical devices, defaulting to CPU only: "
                 << s;
      return new AcceleratorDevices{0};
    }
    LOG_FIRST_N(INFO, 1) << "Found devices: [" << absl::StrJoin(devices, ",")
                         << "]";
    for (absl::string_view device : devices) {
      std::vector<absl::string_view> device_parts = absl::StrSplit(device, ':');
      if (device_parts.size() != 3) {
        LOG(ERROR) << "Unknown device name format: [" << device << "]";
        continue;
      }
      auto device_type = device_parts[1];
      if (device_type == tensorflow::DEVICE_GPU) {
        LOG_FIRST_N(INFO, 1) << "Found GPU device: [" << device << "]";
        ++num_gpus;
      } else if (device_type == tensorflow::DEVICE_TPU) {
        LOG_FIRST_N(INFO, 1) << "Found TPU device: [" << device << "]";
        ++num_tpus;
      } else {
        LOG_FIRST_N(INFO, 1) << "Skipping device: [" << device << "]";
      }
    }
    return new AcceleratorDevices{num_gpus, num_tpus};
  }();
  return *accelerator_devices;
}

void SetDevice(absl::string_view device, tensorflow::GraphDef* graph_def,
               const char* device_type) {
  for (tensorflow::NodeDef& node_pb : *graph_def->mutable_node()) {
    // Annotating ReduceDataset with _xla_compile_device_type will denote to
    // Tensorflow to decompose the op into dataset iteration and reduce_fn and
    // compile the reduce_fn.
    // TODO(b/233627338): Currently only supported for TPU.  Enable for CPU/GPU
    // once MLIR-based TF2XLA bridge supports CPU/GPU.
    if (node_pb.op() == "ReduceDataset" &&
        !(strcmp(device_type, tensorflow::DEVICE_TPU))) {
      (*node_pb.mutable_attr())["_xla_compile_device_type"].set_s(device_type);
    }
    if (!node_pb.device().empty()) {
      VLOG(5) << "Skipping already placed node [" << node_pb.name() << "] ("
              << node_pb.op() << ") on " << node_pb.device();
      continue;
    } else if (tensorflow::KernelDefAvailable(device_type, node_pb)) {
      VLOG(5) << "Placing node [" << node_pb.name() << "] (" << node_pb.op()
              << ") on device [" << device << "]";
      node_pb.set_device(device.data(), device.size());
    } else {
      VLOG(5) << "Leaving node [" << node_pb.name() << "] alone.";
    }
  }
}

const absl::flat_hash_set<std::string>& GetOpsWithContainerAttr() {
  // Get the list of all ops that have an `attr` named `container`. We assume
  // this will be all ops that construct resources and allow us to configure
  // which container those resources will be placed in. This maybe fragile
  // because an op _might_ not expose the container attr, or might name it
  // something different.
  static auto* op_names = []() -> absl::flat_hash_set<std::string>* {
    std::vector<tensorflow::OpDef> ops;
    tensorflow::OpRegistry::Global()->GetRegisteredOps(&ops);
    auto op_has_container_attr =
        [](const tensorflow::OpDef& op_def_pb) -> bool {
      for (const auto& attr_def_pb : op_def_pb.attr()) {
        if (attr_def_pb.name() == "container") {
          return true;
        }
      }
      return false;
    };
    auto* op_names = new absl::flat_hash_set<std::string>;
    for (const auto& op_def_pb : ops) {
      if (op_has_container_attr(op_def_pb)) {
        op_names->emplace(op_def_pb.name());
      }
    }
    return op_names;
  }();
  return *op_names;
}

// Rewrites the `container` attr of any op that supports, creating a new
// `GraphDef` with altered `NodeDef`s.
tensorflow::GraphDef ReplaceContainers(
    const tensorflow::GraphDef& original_graph, const std::string& container) {
  // Go through the graph and explicitly set the `container` attr on any op that
  // supports a `container` attr.
  const auto& ops_with_container_attrs = GetOpsWithContainerAttr();
  tensorflow::GraphDef graph = original_graph;
  for (tensorflow::NodeDef& node_pb : *graph.mutable_node()) {
    if (ops_with_container_attrs.contains(node_pb.op())) {
      (*node_pb.mutable_attr())["container"].set_s(container);
    }
  }
  for (tensorflow::FunctionDef& function_pb :
       *graph.mutable_library()->mutable_function()) {
    for (tensorflow::NodeDef& node_pb : *function_pb.mutable_node_def()) {
      if (ops_with_container_attrs.contains(node_pb.op())) {
        (*node_pb.mutable_attr())["container"].set_s(container);
      }
    }
  }
  return graph;
}

SessionProvider::SessionProvider(tensorflow::GraphDef&& graph,
                                 int32_t max_active_sessions)
    : active_sessions_(0), graph_(graph), function_id_(GetNextFunctionId()) {
  if (max_active_sessions > 0) {
    max_active_sessions_ = max_active_sessions;
  } else {
    // Treat 0 and lower as unlimited, otherwise any lower number would result
    // in permanent runtime hangs.
    max_active_sessions_ = std::numeric_limits<int32_t>::max();
  }

  maybe_open_cpus_ = std::thread::hardware_concurrency();
}

absl::StatusOr<std::unique_ptr<tensorflow::Session>>
SessionProvider::CreateSession(const int16_t session_id) {
  const std::string container = absl::StrCat(function_id_, "/", session_id);
  std::unique_ptr<tensorflow::Session> session;
  {
    tensorflow::Session* raw_session;
    tensorflow::Status status =
        tensorflow::NewSession(get_session_options(), &raw_session);
    if (!status.ok()) {
      return absl::InternalError(absl::StrCat(
          "Failed to create TensorFlow session: ", status.error_message()));
    }
    session.reset(raw_session);
  }
  tensorflow::GraphDef graph_def = ReplaceContainers(graph_, container);
  const AcceleratorDevices& devices = GetAcceleratorDevices();

  if (devices.num_gpus > 0 && devices.num_tpus > 0) {
    return absl::UnimplementedError(
        "Can't create a TensorFlow session for hardware with both GPUs and "
        "TPUs.");
  }

  if (devices.num_gpus > 0) {
    // If we have GPUs, round robin the session by explicitly setting the
    // `device` attr of the GPU-capable kernels.
    const int16_t device_id = session_id % devices.num_gpus;
    const std::string& device =
        absl::StrCat("/device:", tensorflow::DEVICE_GPU, ":", device_id);
    VLOG(2) << "Pinning function [" << function_id_ << "] session ["
            << session_id << "] to device [" << device << "]";
    SetDevice(device, &graph_def, tensorflow::DEVICE_GPU);
  }
  if (devices.num_tpus > 0) {
    // If we have TPUs, round robin the session by explicitly setting the
    // `device` attr of the TPU-capable kernels.
    const int16_t device_id = session_id % devices.num_tpus;
    const std::string& device =
        absl::StrCat("/device:", tensorflow::DEVICE_TPU, ":", device_id);
    VLOG(2) << "Pinning function [" << function_id_ << "] session ["
            << session_id << "] to device [" << device << "]";
    SetDevice(device, &graph_def, tensorflow::DEVICE_TPU);
  }
  auto status = session->Create(graph_def);
  if (!status.ok()) {
    LOG(ERROR) << status;
    for (absl::string_view line :
         absl::StrSplit(graph_def.Utf8DebugString(), '\n')) {
      LOG(ERROR) << line;
    }
    return absl::InternalError(absl::StrCat(
        "Failed to create graph in session: ", status.error_message()));
  }
  return std::move(session);
}

absl::StatusOr<SessionProvider::SessionWithResourceContainer>
SessionProvider::TakeSession() {
  lock_.LockWhen(
      absl::Condition(this, &SessionProvider::SessionOrCpuAvailable));
  active_sessions_++;
  if (!sessions_.empty()) {
    SessionProvider::SessionWithResourceContainer session(
        std::move(sessions_.back()));
    sessions_.pop_back();
    lock_.Unlock();
    return std::move(session);
  }
  maybe_open_cpus_--;
  // Build a container name based on the number of sessions created so that
  // each session gets its own container.
  const int16_t session_id = session_creation_counter_++;
  lock_.Unlock();
  auto session = CreateSession(session_id);
  lock_.Lock();
  maybe_open_cpus_++;
  lock_.Unlock();
  if (session.ok()) {
    return SessionProvider::SessionWithResourceContainer{
        TFF_TRY(std::move(session)), function_id_, session_id};
  } else {
    return session.status();
  }
}

void SessionProvider::ReturnSession(
    SessionProvider::SessionWithResourceContainer&& session) {
  session.ClearResourceContainers();
  lock_.Lock();
  active_sessions_--;
  sessions_.emplace_back(std::move(session));
  lock_.Unlock();
}

}  // namespace tensorflow_federated
<commit_msg>Only explicitly place ReduceDataset on a TPU.<commit_after>/* Copyright 2021, The TensorFlow Federated Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
==============================================================================*/

#include "tensorflow_federated/cc/core/impl/executors/session_provider.h"

#include "absl/container/flat_hash_set.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include "absl/synchronization/mutex.h"
#include "tensorflow/core/common_runtime/device_mgr.h"
#include "tensorflow/core/framework/attr_value.pb.h"
#include "tensorflow/core/framework/device_factory.h"
#include "tensorflow/core/framework/function.pb.h"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/types.h"
#include "tensorflow/core/graph/default_device.h"
#include "tensorflow/core/public/session_options.h"

namespace tensorflow_federated {

// A process level counter for SessionProvider creation. This ensures
// each session provider is creating unique containers for each session
// created in the process. This is necessary because the TesnorFlow
// ResourceManager shares resource objects across Sessions in the same process,
// but we need to ability to clear an individual Session's container after
// each call.
ABSL_CONST_INIT static absl::Mutex function_id_mutex(absl::kConstInit);
int32_t GetNextFunctionId() {
  absl::MutexLock function_id_lock(&function_id_mutex);
  static int32_t function_id = 0;
  return function_id++;
}

const tensorflow::SessionOptions& get_session_options() {
  // Creates default SessionOptions on first call, and re-uses them for all
  // future calls.
  static tensorflow::SessionOptions* session_options = []() {
    tensorflow::SessionOptions* options_pb = new tensorflow::SessionOptions();
    tensorflow::GraphOptions* graph_options_pb =
        options_pb->config.mutable_graph_options();
    // Disable JIT/runtime Grappler. TFF typically has short lived sessions,
    // meaning that the benefit from running grappler is generally not realized
    // and can be very expensive (anecdotally ~30% of CPU time).
    graph_options_pb->mutable_rewrite_options()->set_disable_meta_optimizer(
        true);
    graph_options_pb->mutable_optimizer_options()->set_opt_level(
        tensorflow::OptimizerOptions::L0);
    graph_options_pb->mutable_optimizer_options()->set_global_jit_level(
        tensorflow::OptimizerOptions::OFF);
    // Don't eagerly allocate all GPU memory in each session.
    options_pb->config.mutable_gpu_options()->set_allow_growth(true);
    // Let the session know the graph will not change.
    auto experimental_pb = options_pb->config.mutable_experimental();
    experimental_pb->set_optimize_for_static_graph(true);
    experimental_pb->set_disable_output_partition_graphs(true);
    options_pb->config.set_allow_soft_placement(true);
    return options_pb;
  }();
  return *session_options;
}

struct AcceleratorDevices {
  const int16_t num_gpus = 0;
  const int16_t num_tpus = 0;
};

const AcceleratorDevices& GetAcceleratorDevices() {
  static const AcceleratorDevices* accelerator_devices =
      []() -> AcceleratorDevices* {
    int16_t num_gpus = 0;
    int16_t num_tpus = 0;
    std::vector<std::string> devices;
    tensorflow::Status s =
        tensorflow::DeviceFactory::ListAllPhysicalDevices(&devices);
    if (!s.ok()) {
      LOG(ERROR) << "Error detecting physical devices, defaulting to CPU only: "
                 << s;
      return new AcceleratorDevices{0};
    }
    LOG_FIRST_N(INFO, 1) << "Found devices: [" << absl::StrJoin(devices, ",")
                         << "]";
    for (absl::string_view device : devices) {
      std::vector<absl::string_view> device_parts = absl::StrSplit(device, ':');
      if (device_parts.size() != 3) {
        LOG(ERROR) << "Unknown device name format: [" << device << "]";
        continue;
      }
      auto device_type = device_parts[1];
      if (device_type == tensorflow::DEVICE_GPU) {
        LOG_FIRST_N(INFO, 1) << "Found GPU device: [" << device << "]";
        ++num_gpus;
      } else if (device_type == tensorflow::DEVICE_TPU) {
        LOG_FIRST_N(INFO, 1) << "Found TPU device: [" << device << "]";
        ++num_tpus;
      } else {
        LOG_FIRST_N(INFO, 1) << "Skipping device: [" << device << "]";
      }
    }
    return new AcceleratorDevices{num_gpus, num_tpus};
  }();
  return *accelerator_devices;
}

void SetDevice(absl::string_view device, tensorflow::GraphDef* graph_def,
               const char* device_type) {
  for (tensorflow::NodeDef& node_pb : *graph_def->mutable_node()) {
    // Annotating ReduceDataset with _xla_compile_device_type will denote to
    // Tensorflow to decompose the op into dataset iteration and reduce_fn and
    // compile the reduce_fn.
    // TODO(b/233627338): Currently only supported for TPU.  Enable for CPU/GPU
    // once MLIR-based TF2XLA bridge supports CPU/GPU.
    if (node_pb.op() == "ReduceDataset" &&
        !(strcmp(device_type, tensorflow::DEVICE_TPU))) {
      (*node_pb.mutable_attr())["_xla_compile_device_type"].set_s(device_type);
      node_pb.set_device(device.data(), device.size());
      VLOG(5) << "Placing node [" << node_pb.name() << "] (" << node_pb.op()
              << ") on device [" << device << "]"
              << "and marking for compilation on device type [" << device_type
              << "]";
    }
    if (!node_pb.device().empty()) {
      VLOG(5) << "Skipping already placed node [" << node_pb.name() << "] ("
              << node_pb.op() << ") on " << node_pb.device();
      continue;
      // Note: Don't place general ops directly on TPU.
    } else if (tensorflow::KernelDefAvailable(device_type, node_pb) &&
               strcmp(device_type, tensorflow::DEVICE_TPU) != 0) {
      VLOG(5) << "Placing node [" << node_pb.name() << "] (" << node_pb.op()
              << ") on device [" << device << "]";
      node_pb.set_device(device.data(), device.size());
    } else {
      VLOG(5) << "Leaving node [" << node_pb.name() << "] alone.";
    }
  }
}

const absl::flat_hash_set<std::string>& GetOpsWithContainerAttr() {
  // Get the list of all ops that have an `attr` named `container`. We assume
  // this will be all ops that construct resources and allow us to configure
  // which container those resources will be placed in. This maybe fragile
  // because an op _might_ not expose the container attr, or might name it
  // something different.
  static auto* op_names = []() -> absl::flat_hash_set<std::string>* {
    std::vector<tensorflow::OpDef> ops;
    tensorflow::OpRegistry::Global()->GetRegisteredOps(&ops);
    auto op_has_container_attr =
        [](const tensorflow::OpDef& op_def_pb) -> bool {
      for (const auto& attr_def_pb : op_def_pb.attr()) {
        if (attr_def_pb.name() == "container") {
          return true;
        }
      }
      return false;
    };
    auto* op_names = new absl::flat_hash_set<std::string>;
    for (const auto& op_def_pb : ops) {
      if (op_has_container_attr(op_def_pb)) {
        op_names->emplace(op_def_pb.name());
      }
    }
    return op_names;
  }();
  return *op_names;
}

// Rewrites the `container` attr of any op that supports, creating a new
// `GraphDef` with altered `NodeDef`s.
tensorflow::GraphDef ReplaceContainers(
    const tensorflow::GraphDef& original_graph, const std::string& container) {
  // Go through the graph and explicitly set the `container` attr on any op that
  // supports a `container` attr.
  const auto& ops_with_container_attrs = GetOpsWithContainerAttr();
  tensorflow::GraphDef graph = original_graph;
  for (tensorflow::NodeDef& node_pb : *graph.mutable_node()) {
    if (ops_with_container_attrs.contains(node_pb.op())) {
      (*node_pb.mutable_attr())["container"].set_s(container);
    }
  }
  for (tensorflow::FunctionDef& function_pb :
       *graph.mutable_library()->mutable_function()) {
    for (tensorflow::NodeDef& node_pb : *function_pb.mutable_node_def()) {
      if (ops_with_container_attrs.contains(node_pb.op())) {
        (*node_pb.mutable_attr())["container"].set_s(container);
      }
    }
  }
  return graph;
}

SessionProvider::SessionProvider(tensorflow::GraphDef&& graph,
                                 int32_t max_active_sessions)
    : active_sessions_(0), graph_(graph), function_id_(GetNextFunctionId()) {
  if (max_active_sessions > 0) {
    max_active_sessions_ = max_active_sessions;
  } else {
    // Treat 0 and lower as unlimited, otherwise any lower number would result
    // in permanent runtime hangs.
    max_active_sessions_ = std::numeric_limits<int32_t>::max();
  }

  maybe_open_cpus_ = std::thread::hardware_concurrency();
}

absl::StatusOr<std::unique_ptr<tensorflow::Session>>
SessionProvider::CreateSession(const int16_t session_id) {
  const std::string container = absl::StrCat(function_id_, "/", session_id);
  std::unique_ptr<tensorflow::Session> session;
  {
    tensorflow::Session* raw_session;
    tensorflow::Status status =
        tensorflow::NewSession(get_session_options(), &raw_session);
    if (!status.ok()) {
      return absl::InternalError(absl::StrCat(
          "Failed to create TensorFlow session: ", status.error_message()));
    }
    session.reset(raw_session);
  }
  tensorflow::GraphDef graph_def = ReplaceContainers(graph_, container);
  const AcceleratorDevices& devices = GetAcceleratorDevices();

  if (devices.num_gpus > 0 && devices.num_tpus > 0) {
    return absl::UnimplementedError(
        "Can't create a TensorFlow session for hardware with both GPUs and "
        "TPUs.");
  }

  if (devices.num_gpus > 0) {
    // If we have GPUs, round robin the session by explicitly setting the
    // `device` attr of the GPU-capable kernels.
    const int16_t device_id = session_id % devices.num_gpus;
    const std::string& device =
        absl::StrCat("/device:", tensorflow::DEVICE_GPU, ":", device_id);
    VLOG(2) << "Pinning function [" << function_id_ << "] session ["
            << session_id << "] to device [" << device << "]";
    SetDevice(device, &graph_def, tensorflow::DEVICE_GPU);
  }
  if (devices.num_tpus > 0) {
    // If we have TPUs, round robin the session by explicitly setting the
    // `device` attr of the TPU-capable kernels.
    const int16_t device_id = session_id % devices.num_tpus;
    const std::string& device =
        absl::StrCat("/device:", tensorflow::DEVICE_TPU, ":", device_id);
    VLOG(2) << "Pinning function [" << function_id_ << "] session ["
            << session_id << "] to device [" << device << "]";
    SetDevice(device, &graph_def, tensorflow::DEVICE_TPU);
  }
  auto status = session->Create(graph_def);
  if (!status.ok()) {
    LOG(ERROR) << status;
    for (absl::string_view line :
         absl::StrSplit(graph_def.Utf8DebugString(), '\n')) {
      LOG(ERROR) << line;
    }
    return absl::InternalError(absl::StrCat(
        "Failed to create graph in session: ", status.error_message()));
  }
  return std::move(session);
}

absl::StatusOr<SessionProvider::SessionWithResourceContainer>
SessionProvider::TakeSession() {
  lock_.LockWhen(
      absl::Condition(this, &SessionProvider::SessionOrCpuAvailable));
  active_sessions_++;
  if (!sessions_.empty()) {
    SessionProvider::SessionWithResourceContainer session(
        std::move(sessions_.back()));
    sessions_.pop_back();
    lock_.Unlock();
    return std::move(session);
  }
  maybe_open_cpus_--;
  // Build a container name based on the number of sessions created so that
  // each session gets its own container.
  const int16_t session_id = session_creation_counter_++;
  lock_.Unlock();
  auto session = CreateSession(session_id);
  lock_.Lock();
  maybe_open_cpus_++;
  lock_.Unlock();
  if (session.ok()) {
    return SessionProvider::SessionWithResourceContainer{
        TFF_TRY(std::move(session)), function_id_, session_id};
  } else {
    return session.status();
  }
}

void SessionProvider::ReturnSession(
    SessionProvider::SessionWithResourceContainer&& session) {
  session.ClearResourceContainers();
  lock_.Lock();
  active_sessions_--;
  sessions_.emplace_back(std::move(session));
  lock_.Unlock();
}

}  // namespace tensorflow_federated
<|endoftext|>