1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/chromeos/status/network_menu.h" 6 7#include <algorithm> 8 9#include "base/logging.h" 10#include "base/command_line.h" 11#include "base/stringprintf.h" 12#include "base/utf_string_conversions.h" 13#include "chrome/browser/chromeos/choose_mobile_network_dialog.h" 14#include "chrome/browser/chromeos/cros/cros_library.h" 15#include "chrome/browser/chromeos/customization_document.h" 16#include "chrome/browser/chromeos/sim_dialog_delegate.h" 17#include "chrome/browser/ui/browser.h" 18#include "chrome/browser/ui/browser_list.h" 19#include "chrome/browser/ui/views/window.h" 20#include "chrome/common/url_constants.h" 21#include "chrome/common/chrome_switches.h" 22#include "grit/generated_resources.h" 23#include "grit/theme_resources.h" 24#include "net/base/escape.h" 25#include "ui/base/l10n/l10n_util.h" 26#include "ui/base/resource/resource_bundle.h" 27#include "ui/gfx/canvas_skia.h" 28#include "ui/gfx/skbitmap_operations.h" 29#include "views/controls/menu/menu_2.h" 30#include "views/window/window.h" 31 32namespace { 33 34// Amount to fade icons while connecting. 35const double kConnectingImageAlpha = 0.5; 36 37// Replace '&' in a string with "&&" to allow it to be a menu item label. 38std::string EscapeAmpersands(const std::string& input) { 39 std::string str = input; 40 size_t found = str.find('&'); 41 while (found != std::string::npos) { 42 str.replace(found, 1, "&&"); 43 found = str.find('&', found + 2); 44 } 45 return str; 46} 47 48} // namespace 49 50namespace chromeos { 51 52class MoreMenuModel : public NetworkMenuModel { 53 public: 54 explicit MoreMenuModel(NetworkMenu* owner); 55 virtual ~MoreMenuModel() {} 56 57 // NetworkMenuModel implementation. 58 virtual void InitMenuItems(bool is_browser_mode, 59 bool should_open_button_options); 60 61 private: 62 friend class MainMenuModel; 63 DISALLOW_COPY_AND_ASSIGN(MoreMenuModel); 64}; 65 66class VPNMenuModel : public NetworkMenuModel { 67 public: 68 explicit VPNMenuModel(NetworkMenu* owner); 69 virtual ~VPNMenuModel() {} 70 71 // NetworkMenuModel implementation. 72 virtual void InitMenuItems(bool is_browser_mode, 73 bool should_open_button_options); 74 75 static SkBitmap IconForDisplay(const Network* network); 76 77 private: 78 DISALLOW_COPY_AND_ASSIGN(VPNMenuModel); 79}; 80 81class MainMenuModel : public NetworkMenuModel { 82 public: 83 explicit MainMenuModel(NetworkMenu* owner); 84 virtual ~MainMenuModel() {} 85 86 // NetworkMenuModel implementation. 87 virtual void InitMenuItems(bool is_browser_mode, 88 bool should_open_button_options); 89 90 private: 91 scoped_ptr<NetworkMenuModel> vpn_menu_model_; 92 scoped_ptr<MoreMenuModel> more_menu_model_; 93 94 DISALLOW_COPY_AND_ASSIGN(MainMenuModel); 95}; 96 97//////////////////////////////////////////////////////////////////////////////// 98// NetworkMenuModel, public methods: 99 100bool NetworkMenuModel::ConnectToNetworkAt(int index, 101 const std::string& passphrase, 102 const std::string& ssid, 103 int auto_connect) const { 104 int flags = menu_items_[index].flags; 105 NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); 106 const std::string& service_path = menu_items_[index].service_path; 107 if (flags & FLAG_WIFI) { 108 WifiNetwork* wifi = cros->FindWifiNetworkByPath(service_path); 109 if (wifi) { 110 // Connect or reconnect. 111 if (auto_connect >= 0) 112 wifi->SetAutoConnect(auto_connect ? true : false); 113 if (wifi->connecting_or_connected()) { 114 // Show the config settings for the active network. 115 owner_->ShowTabbedNetworkSettings(wifi); 116 return true; 117 } 118 if (wifi->IsPassphraseRequired()) { 119 // Show the connection UI if we require a passphrase. 120 ShowNetworkConfigView(new NetworkConfigView(wifi)); 121 return true; 122 } else { 123 cros->ConnectToWifiNetwork(wifi); 124 // Connection failures are responsible for updating the UI, including 125 // reopening dialogs. 126 return true; 127 } 128 } else { 129 // If we are attempting to connect to a network that no longer exists, 130 // display a notification. 131 LOG(WARNING) << "Wi-fi network does not exist to connect to: " 132 << service_path; 133 // TODO(stevenjb): Show notification. 134 } 135 } else if (flags & FLAG_CELLULAR) { 136 CellularNetwork* cellular = cros->FindCellularNetworkByPath( 137 service_path); 138 if (cellular) { 139 if ((cellular->activation_state() != ACTIVATION_STATE_ACTIVATED && 140 cellular->activation_state() != ACTIVATION_STATE_UNKNOWN) || 141 cellular->needs_new_plan()) { 142 ActivateCellular(cellular); 143 return true; 144 } else if (cellular->connecting_or_connected()) { 145 // Cellular network is connecting or connected, 146 // so we show the config settings for the cellular network. 147 owner_->ShowTabbedNetworkSettings(cellular); 148 return true; 149 } 150 // Clicked on a disconnected cellular network, so connect to it. 151 cros->ConnectToCellularNetwork(cellular); 152 } else { 153 // If we are attempting to connect to a network that no longer exists, 154 // display a notification. 155 LOG(WARNING) << "Cellular network does not exist to connect to: " 156 << service_path; 157 // TODO(stevenjb): Show notification. 158 } 159 } else if (flags & FLAG_ADD_WIFI) { 160 ShowOther(TYPE_WIFI); 161 } else if (flags & FLAG_ADD_CELLULAR) { 162 ShowOtherCellular(); 163 } else if (flags & FLAG_ADD_VPN) { 164 ShowOther(TYPE_VPN); 165 } else if (flags & FLAG_VPN) { 166 VirtualNetwork* vpn = cros->FindVirtualNetworkByPath(service_path); 167 if (vpn) { 168 // Connect or reconnect. 169 if (vpn->connecting_or_connected()) { 170 // Show the config settings for the connected network. 171 if (cros->connected_network()) 172 owner_->ShowTabbedNetworkSettings(cros->connected_network()); 173 return true; 174 } 175 // Show the connection UI if info for a field is missing. 176 if (vpn->NeedMoreInfoToConnect()) { 177 ShowNetworkConfigView(new NetworkConfigView(vpn)); 178 return true; 179 } 180 cros->ConnectToVirtualNetwork(vpn); 181 // Connection failures are responsible for updating the UI, including 182 // reopening dialogs. 183 return true; 184 } else { 185 // If we are attempting to connect to a network that no longer exists, 186 // display a notification. 187 LOG(WARNING) << "VPN does not exist to connect to: " << service_path; 188 // TODO(stevenjb): Show notification. 189 } 190 } 191 return true; 192} 193 194//////////////////////////////////////////////////////////////////////////////// 195// NetworkMenuModel, ui::MenuModel implementation: 196 197int NetworkMenuModel::GetItemCount() const { 198 return static_cast<int>(menu_items_.size()); 199} 200 201ui::MenuModel::ItemType NetworkMenuModel::GetTypeAt(int index) const { 202 return menu_items_[index].type; 203} 204 205string16 NetworkMenuModel::GetLabelAt(int index) const { 206 return menu_items_[index].label; 207} 208 209const gfx::Font* NetworkMenuModel::GetLabelFontAt(int index) const { 210 return (menu_items_[index].flags & FLAG_ASSOCIATED) ? 211 &ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BoldFont) : 212 NULL; 213} 214 215bool NetworkMenuModel::IsItemCheckedAt(int index) const { 216 // All ui::MenuModel::TYPE_CHECK menu items are checked. 217 return true; 218} 219 220bool NetworkMenuModel::GetIconAt(int index, SkBitmap* icon) { 221 if (!menu_items_[index].icon.empty()) { 222 *icon = menu_items_[index].icon; 223 return true; 224 } 225 return false; 226} 227 228bool NetworkMenuModel::IsEnabledAt(int index) const { 229 return !(menu_items_[index].flags & FLAG_DISABLED); 230} 231 232ui::MenuModel* NetworkMenuModel::GetSubmenuModelAt(int index) const { 233 return menu_items_[index].sub_menu_model; 234} 235 236void NetworkMenuModel::ActivatedAt(int index) { 237 // When we are refreshing the menu, ignore menu item activation. 238 if (owner_->refreshing_menu_) 239 return; 240 241 NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); 242 int flags = menu_items_[index].flags; 243 if (flags & FLAG_OPTIONS) { 244 owner_->OpenButtonOptions(); 245 } else if (flags & FLAG_TOGGLE_ETHERNET) { 246 cros->EnableEthernetNetworkDevice(!cros->ethernet_enabled()); 247 } else if (flags & FLAG_TOGGLE_WIFI) { 248 cros->EnableWifiNetworkDevice(!cros->wifi_enabled()); 249 } else if (flags & FLAG_TOGGLE_CELLULAR) { 250 const NetworkDevice* cellular = cros->FindCellularDevice(); 251 if (!cellular) { 252 LOG(ERROR) << "No cellular device found, it should be available."; 253 cros->EnableCellularNetworkDevice(!cros->cellular_enabled()); 254 } else if (cellular->sim_lock_state() == SIM_UNLOCKED || 255 cellular->sim_lock_state() == SIM_UNKNOWN) { 256 cros->EnableCellularNetworkDevice(!cros->cellular_enabled()); 257 } else { 258 SimDialogDelegate::ShowDialog(owner_->GetNativeWindow(), 259 SimDialogDelegate::SIM_DIALOG_UNLOCK); 260 } 261 } else if (flags & FLAG_TOGGLE_OFFLINE) { 262 cros->EnableOfflineMode(!cros->offline_mode()); 263 } else if (flags & FLAG_ETHERNET) { 264 if (cros->ethernet_connected()) { 265 owner_->ShowTabbedNetworkSettings(cros->ethernet_network()); 266 } 267 } else if (flags & (FLAG_WIFI | FLAG_ADD_WIFI | 268 FLAG_CELLULAR | FLAG_ADD_CELLULAR | 269 FLAG_VPN | FLAG_ADD_VPN)) { 270 ConnectToNetworkAt(index, std::string(), std::string(), -1); 271 } else if (flags & FLAG_DISCONNECT_VPN) { 272 const VirtualNetwork* active_vpn = cros->virtual_network(); 273 if (active_vpn) 274 cros->DisconnectFromNetwork(active_vpn); 275 } else if (flags & FLAG_VIEW_ACCOUNT) { 276 Browser* browser = BrowserList::GetLastActive(); 277 if (browser) 278 browser->ShowSingletonTab(GURL(top_up_url_)); 279 } 280} 281 282//////////////////////////////////////////////////////////////////////////////// 283// NetworkMenuModel, private methods: 284 285// TODO(stevenjb): deprecate this once we've committed to tabbed settings 286// and the embedded menu UI (and fully deprecated NetworkConfigView). 287// Meanwhile, if MenuUI::IsEnabled() is true, always show the settings UI, 288// otherwise show NetworkConfigView only to get passwords when not connected. 289void NetworkMenuModel::ShowNetworkConfigView(NetworkConfigView* view) const { 290 view->set_browser_mode(owner_->IsBrowserMode()); 291 views::Window* window = browser::CreateViewsWindow( 292 owner_->GetNativeWindow(), gfx::Rect(), view); 293 window->SetIsAlwaysOnTop(true); 294 window->Show(); 295} 296 297void NetworkMenuModel::ActivateCellular(const CellularNetwork* cellular) const { 298 DCHECK(cellular); 299 Browser* browser = BrowserList::GetLastActive(); 300 if (!browser) 301 return; 302 browser->OpenMobilePlanTabAndActivate(); 303} 304 305void NetworkMenuModel::ShowOther(ConnectionType type) const { 306 ShowNetworkConfigView(new NetworkConfigView(type)); 307} 308 309void NetworkMenuModel::ShowOtherCellular() const { 310 ChooseMobileNetworkDialog::ShowDialog(owner_->GetNativeWindow()); 311} 312 313//////////////////////////////////////////////////////////////////////////////// 314// MainMenuModel 315 316MainMenuModel::MainMenuModel(NetworkMenu* owner) 317 : NetworkMenuModel(owner), 318 vpn_menu_model_(new VPNMenuModel(owner)), 319 more_menu_model_(new MoreMenuModel(owner)) { 320} 321 322void MainMenuModel::InitMenuItems(bool is_browser_mode, 323 bool should_open_button_options) { 324 // This gets called on initialization, so any changes should be reflected 325 // in CrosMock::SetNetworkLibraryStatusAreaExpectations(). 326 327 menu_items_.clear(); 328 329 NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); 330 if (cros->IsLocked()) { 331 menu_items_.push_back( 332 MenuItem(ui::MenuModel::TYPE_COMMAND, 333 l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_LOCKED), 334 SkBitmap(), std::string(), FLAG_DISABLED)); 335 return; 336 } 337 338 // Populate our MenuItems with the current list of networks. 339 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 340 string16 label; 341 342 // Ethernet 343 bool ethernet_available = cros->ethernet_available(); 344 bool ethernet_enabled = cros->ethernet_enabled(); 345 if (ethernet_available && ethernet_enabled) { 346 bool ethernet_connected = cros->ethernet_connected(); 347 bool ethernet_connecting = cros->ethernet_connecting(); 348 349 if (ethernet_connecting) { 350 label = l10n_util::GetStringFUTF16( 351 IDS_STATUSBAR_NETWORK_DEVICE_STATUS, 352 l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET), 353 l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_CONNECTING)); 354 } else { 355 label = l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET); 356 } 357 const SkBitmap* icon = rb.GetBitmapNamed(IDR_STATUSBAR_WIRED_BLACK); 358 const SkBitmap* badge = ethernet_connecting || ethernet_connected ? 359 NULL : rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_DISCONNECTED); 360 int flag = FLAG_ETHERNET; 361 if (ethernet_connecting || ethernet_connected) 362 flag |= FLAG_ASSOCIATED; 363 menu_items_.push_back( 364 MenuItem(ui::MenuModel::TYPE_COMMAND, label, 365 NetworkMenu::IconForDisplay(icon, badge), std::string(), 366 flag)); 367 } 368 369 // Wifi Networks 370 bool wifi_available = cros->wifi_available(); 371 bool wifi_enabled = cros->wifi_enabled(); 372 if (wifi_available && wifi_enabled) { 373 const WifiNetworkVector& wifi_networks = cros->wifi_networks(); 374 const WifiNetwork* active_wifi = cros->wifi_network(); 375 376 bool separator_added = false; 377 // List Wifi networks. 378 for (size_t i = 0; i < wifi_networks.size(); ++i) { 379 // Ampersand is a valid character in an SSID, but menu2 uses it to mark 380 // "mnemonics" for keyboard shortcuts. 381 std::string wifi_name = EscapeAmpersands(wifi_networks[i]->name()); 382 if (wifi_networks[i]->connecting()) { 383 label = l10n_util::GetStringFUTF16( 384 IDS_STATUSBAR_NETWORK_DEVICE_STATUS, 385 UTF8ToUTF16(wifi_name), 386 l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_CONNECTING)); 387 } else { 388 label = UTF8ToUTF16(wifi_name); 389 } 390 391 // First add a separator if necessary. 392 if (!separator_added) { 393 separator_added = true; 394 if (!menu_items_.empty()) { // Don't add if first menu item. 395 menu_items_.push_back(MenuItem()); // Separator 396 } 397 } 398 399 const SkBitmap* icon = NetworkMenu::IconForNetworkStrength( 400 wifi_networks[i], true); 401 const SkBitmap* badge = wifi_networks[i]->encrypted() ? 402 rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE) : NULL; 403 int flag = FLAG_WIFI; 404 // If a network is not connectable from login/oobe, we disable it. 405 // We do not allow configuring a network (e.g. 802.1x) from login/oobe. 406 if (!owner_->IsBrowserMode() && !wifi_networks[i]->connectable()) 407 flag |= FLAG_DISABLED; 408 if (active_wifi 409 && wifi_networks[i]->service_path() == active_wifi->service_path()) 410 flag |= FLAG_ASSOCIATED; 411 menu_items_.push_back( 412 MenuItem(ui::MenuModel::TYPE_COMMAND, label, 413 NetworkMenu::IconForDisplay(icon, badge), 414 wifi_networks[i]->service_path(), flag)); 415 } 416 if (!separator_added && !menu_items_.empty()) 417 menu_items_.push_back(MenuItem()); 418 menu_items_.push_back(MenuItem( 419 ui::MenuModel::TYPE_COMMAND, 420 l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_OTHER_WIFI_NETWORKS), 421 *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0_BLACK), 422 std::string(), FLAG_ADD_WIFI)); 423 } 424 425 // Cellular Networks 426 bool cellular_available = cros->cellular_available(); 427 bool cellular_enabled = cros->cellular_enabled(); 428 if (cellular_available && cellular_enabled) { 429 const CellularNetworkVector& cell_networks = cros->cellular_networks(); 430 const CellularNetwork* active_cellular = cros->cellular_network(); 431 432 bool separator_added = false; 433 // List Cellular networks. 434 for (size_t i = 0; i < cell_networks.size(); ++i) { 435 chromeos::ActivationState activation_state = 436 cell_networks[i]->activation_state(); 437 438 // If we are on the OOBE/login screen, do not show activating 3G option. 439 if (!is_browser_mode && activation_state != ACTIVATION_STATE_ACTIVATED) 440 continue; 441 442 // Ampersand is a valid character in a network name, but menu2 uses it 443 // to mark "mnemonics" for keyboard shortcuts. http://crosbug.com/14697 444 std::string network_name = EscapeAmpersands(cell_networks[i]->name()); 445 if (activation_state == ACTIVATION_STATE_NOT_ACTIVATED || 446 activation_state == ACTIVATION_STATE_PARTIALLY_ACTIVATED) { 447 label = l10n_util::GetStringFUTF16( 448 IDS_STATUSBAR_NETWORK_DEVICE_ACTIVATE, 449 UTF8ToUTF16(network_name)); 450 } else if (activation_state == ACTIVATION_STATE_ACTIVATING) { 451 label = l10n_util::GetStringFUTF16( 452 IDS_STATUSBAR_NETWORK_DEVICE_STATUS, 453 UTF8ToUTF16(network_name), 454 l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_ACTIVATING)); 455 } else if (cell_networks[i]->connecting()) { 456 label = l10n_util::GetStringFUTF16( 457 IDS_STATUSBAR_NETWORK_DEVICE_STATUS, 458 UTF8ToUTF16(network_name), 459 l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_CONNECTING)); 460 } else { 461 label = UTF8ToUTF16(network_name); 462 } 463 464 // First add a separator if necessary. 465 if (!separator_added) { 466 separator_added = true; 467 if (!menu_items_.empty()) { // Don't add if first menu item. 468 menu_items_.push_back(MenuItem()); // Separator 469 } 470 } 471 472 const SkBitmap* icon = NetworkMenu::IconForNetworkStrength( 473 cell_networks[i], true); 474 const SkBitmap* badge = NetworkMenu::BadgeForNetworkTechnology( 475 cell_networks[i]); 476 const SkBitmap* roaming_badge = NetworkMenu::BadgeForRoamingStatus( 477 cell_networks[i]); 478 int flag = FLAG_CELLULAR; 479 bool isActive = active_cellular && 480 cell_networks[i]->service_path() == active_cellular->service_path() && 481 (cell_networks[i]->connecting() || cell_networks[i]->connected()); 482 bool supports_data_plan = 483 active_cellular && active_cellular->SupportsDataPlan(); 484 if (isActive) 485 flag |= FLAG_ASSOCIATED; 486 menu_items_.push_back( 487 MenuItem(ui::MenuModel::TYPE_COMMAND, label, 488 NetworkMenu::IconForDisplay(icon, badge, roaming_badge, 489 NULL), 490 cell_networks[i]->service_path(), flag)); 491 if (isActive && supports_data_plan) { 492 label.clear(); 493 if (active_cellular->needs_new_plan()) { 494 label = l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_NO_PLAN_LABEL); 495 } else { 496 const chromeos::CellularDataPlan* plan = 497 cros->GetSignificantDataPlan(active_cellular->service_path()); 498 if (plan) 499 label = plan->GetUsageInfo(); 500 } 501 if (label.length()) { 502 menu_items_.push_back( 503 MenuItem(ui::MenuModel::TYPE_COMMAND, 504 label, SkBitmap(), 505 std::string(), FLAG_DISABLED)); 506 } 507 } 508 } 509 const NetworkDevice* cellular_device = cros->FindCellularDevice(); 510 if (cellular_device) { 511 // Add "View Account" with top up URL if we know that. 512 ServicesCustomizationDocument* customization = 513 ServicesCustomizationDocument::GetInstance(); 514 if (is_browser_mode && customization->IsReady()) { 515 std::string carrier_id = cros->GetCellularHomeCarrierId(); 516 // If we don't have top up URL cached. 517 if (carrier_id != carrier_id_) { 518 // Mark that we've checked this carrier ID. 519 carrier_id_ = carrier_id; 520 top_up_url_.clear(); 521 // Ignoring deal restrictions, use any carrier information available. 522 const ServicesCustomizationDocument::CarrierDeal* deal = 523 customization->GetCarrierDeal(carrier_id, false); 524 if (deal && !deal->top_up_url.empty()) 525 top_up_url_ = deal->top_up_url; 526 } 527 if (!top_up_url_.empty()) { 528 menu_items_.push_back(MenuItem( 529 ui::MenuModel::TYPE_COMMAND, 530 l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_VIEW_ACCOUNT), 531 SkBitmap(), 532 std::string(), FLAG_VIEW_ACCOUNT)); 533 } 534 } 535 536 if (cellular_device->support_network_scan()) { 537 // For GSM add mobile network scan. 538 if (!separator_added && !menu_items_.empty()) 539 menu_items_.push_back(MenuItem()); 540 541 menu_items_.push_back(MenuItem( 542 ui::MenuModel::TYPE_COMMAND, 543 l10n_util::GetStringUTF16( 544 IDS_OPTIONS_SETTINGS_OTHER_CELLULAR_NETWORKS), 545 *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0_BLACK), 546 std::string(), FLAG_ADD_CELLULAR)); 547 } 548 } 549 } 550 551 // No networks available message. 552 if (menu_items_.empty()) { 553 label = l10n_util::GetStringFUTF16(IDS_STATUSBAR_NETWORK_MENU_ITEM_INDENT, 554 l10n_util::GetStringUTF16(IDS_STATUSBAR_NO_NETWORKS_MESSAGE)); 555 menu_items_.push_back(MenuItem(ui::MenuModel::TYPE_COMMAND, label, 556 SkBitmap(), std::string(), FLAG_DISABLED)); 557 } 558 559 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableVPN)) { 560 // If there's a connected network, add submenu for Private Networks. 561 const Network* connected_network = cros->connected_network(); 562 if (connected_network) { 563 menu_items_.push_back(MenuItem()); // Separator 564 menu_items_.push_back(MenuItem( 565 ui::MenuModel::TYPE_SUBMENU, 566 l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_PRIVATE_NETWORKS), 567 VPNMenuModel::IconForDisplay(connected_network), 568 vpn_menu_model_.get(), FLAG_NONE)); 569 vpn_menu_model_->InitMenuItems( 570 is_browser_mode, should_open_button_options); 571 } 572 } 573 574 // Enable / disable wireless. 575 if (wifi_available || cellular_available) { 576 menu_items_.push_back(MenuItem()); // Separator 577 578 if (wifi_available) { 579 // Add 'Scanning...' 580 if (cros->wifi_scanning()) { 581 label = l10n_util::GetStringUTF16(IDS_STATUSBAR_WIFI_SCANNING_MESSAGE); 582 menu_items_.push_back(MenuItem(ui::MenuModel::TYPE_COMMAND, label, 583 SkBitmap(), std::string(), FLAG_DISABLED)); 584 } 585 586 int id = wifi_enabled ? IDS_STATUSBAR_NETWORK_DEVICE_DISABLE : 587 IDS_STATUSBAR_NETWORK_DEVICE_ENABLE; 588 label = l10n_util::GetStringFUTF16(id, 589 l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_WIFI)); 590 menu_items_.push_back(MenuItem(ui::MenuModel::TYPE_COMMAND, label, 591 SkBitmap(), std::string(), FLAG_TOGGLE_WIFI)); 592 } 593 594 if (cellular_available) { 595 const NetworkDevice* cellular = cros->FindCellularDevice(); 596 bool is_locked = false; 597 if (!cellular) { 598 LOG(ERROR) << "Didn't find cellular device."; 599 } else { 600 // If cellular is SIM locked then show "Enable" action. 601 is_locked = cellular->sim_lock_state() == SIM_LOCKED_PIN || 602 cellular->sim_lock_state() == SIM_LOCKED_PUK; 603 } 604 int id; 605 if (cellular_enabled && !is_locked) 606 id = IDS_STATUSBAR_NETWORK_DEVICE_DISABLE; 607 else 608 id = IDS_STATUSBAR_NETWORK_DEVICE_ENABLE; 609 label = l10n_util::GetStringFUTF16(id, 610 l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_CELLULAR)); 611 menu_items_.push_back(MenuItem(ui::MenuModel::TYPE_COMMAND, label, 612 SkBitmap(), std::string(), FLAG_TOGGLE_CELLULAR)); 613 } 614 } 615 616 // Offline mode. 617 // TODO(chocobo): Uncomment once we figure out how to do offline mode. 618 // menu_items_.push_back(MenuItem(cros->offline_mode() ? 619 // ui::MenuModel::TYPE_CHECK : ui::MenuModel::TYPE_COMMAND, 620 // l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_OFFLINE_MODE), 621 // SkBitmap(), std::string(), FLAG_TOGGLE_OFFLINE)); 622 623 // Additional links like: 624 // * Network settings; 625 // * IP Address on active interface; 626 // * Hardware addresses for wifi and ethernet. 627 menu_items_.push_back(MenuItem()); // Separator 628 more_menu_model_->InitMenuItems(is_browser_mode, should_open_button_options); 629 if (is_browser_mode) { 630 // In browser mode we do not want separate submenu, inline items. 631 menu_items_.insert( 632 menu_items_.end(), 633 more_menu_model_->menu_items_.begin(), 634 more_menu_model_->menu_items_.end()); 635 } else { 636 if (!more_menu_model_->menu_items_.empty()) { 637 menu_items_.push_back(MenuItem( 638 ui::MenuModel::TYPE_SUBMENU, 639 l10n_util::GetStringUTF16(IDS_LANGUAGES_MORE), 640 SkBitmap(), more_menu_model_.get(), FLAG_NONE)); 641 } 642 } 643} 644 645//////////////////////////////////////////////////////////////////////////////// 646// VPNMenuModel 647 648VPNMenuModel::VPNMenuModel(NetworkMenu* owner) 649 : NetworkMenuModel(owner) { 650} 651 652void VPNMenuModel::InitMenuItems(bool is_browser_mode, 653 bool should_open_button_options) { 654 // This gets called on initialization, so any changes should be reflected 655 // in CrosMock::SetNetworkLibraryStatusAreaExpectations(). 656 657 menu_items_.clear(); 658 659 // VPN only applies if there's a connected underlying network. 660 NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); 661 const Network* connected_network = cros->connected_network(); 662 if (!connected_network) 663 return; 664 665 // Populate our MenuItems with the current list of virtual networks. 666 const VirtualNetworkVector& virtual_networks = cros->virtual_networks(); 667 const VirtualNetwork* active_vpn = cros->virtual_network(); 668 SkBitmap icon = VPNMenuModel::IconForDisplay(connected_network); 669 bool separator_added = false; 670 string16 label; 671 672 for (size_t i = 0; i < virtual_networks.size(); ++i) { 673 const VirtualNetwork* vpn = virtual_networks[i]; 674 if (vpn->connecting()) { 675 label = l10n_util::GetStringFUTF16( 676 IDS_STATUSBAR_NETWORK_DEVICE_STATUS, 677 UTF8ToUTF16(vpn->name()), 678 l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_CONNECTING)); 679 } else { 680 label = UTF8ToUTF16(vpn->name()); 681 } 682 683 // First add a separator if necessary. 684 if (!separator_added) { 685 separator_added = true; 686 if (!menu_items_.empty()) { // Don't add if first menu item. 687 menu_items_.push_back(MenuItem()); // Separator 688 } 689 } 690 691 int flag = FLAG_VPN; 692 if (!vpn->connectable()) 693 flag |= FLAG_DISABLED; 694 if (active_vpn && vpn->service_path() == active_vpn->service_path()) 695 flag |= FLAG_ASSOCIATED; 696 menu_items_.push_back( 697 MenuItem(ui::MenuModel::TYPE_COMMAND, label, icon, vpn->service_path(), 698 flag)); 699 } 700 701 // Add option to add/disconnect from vpn. 702 if (!menu_items_.empty()) { // Add separator if menu is not empty. 703 menu_items_.push_back(MenuItem()); 704 } 705 menu_items_.push_back(MenuItem( 706 ui::MenuModel::TYPE_COMMAND, 707 l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_ADD_VPN), 708 SkBitmap(), std::string(), FLAG_ADD_VPN)); 709 if (active_vpn) { 710 menu_items_.push_back(MenuItem( 711 ui::MenuModel::TYPE_COMMAND, 712 l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DISCONNECT_VPN), 713 SkBitmap(), std::string(), FLAG_DISCONNECT_VPN)); 714 } 715} 716 717// static 718SkBitmap VPNMenuModel::IconForDisplay(const Network* network) { 719 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 720 const SkBitmap* icon = NULL; 721 const SkBitmap* bottom_right_badge = NULL; 722 const SkBitmap* top_left_badge = NULL; 723 // We know for sure |network| is the active network, so no more checking 724 // is needed by BadgeForPrivateNetworkStatus, hence pass in NULL. 725 const SkBitmap* bottom_left_badge = 726 NetworkMenu::BadgeForPrivateNetworkStatus(NULL); 727 728 switch (network->type()) { 729 case TYPE_ETHERNET : 730 icon = rb.GetBitmapNamed(IDR_STATUSBAR_WIRED_BLACK); 731 break; 732 case TYPE_WIFI : 733 icon = NetworkMenu::IconForNetworkStrength( 734 static_cast<const WifiNetwork*>(network), true); 735 break; 736 case TYPE_CELLULAR : { 737 const CellularNetwork* cellular = 738 static_cast<const CellularNetwork*>(network); 739 icon = NetworkMenu::IconForNetworkStrength(cellular, true); 740 bottom_right_badge = NetworkMenu::BadgeForNetworkTechnology(cellular); 741 top_left_badge = NetworkMenu::BadgeForRoamingStatus(cellular); 742 break; 743 } 744 default: 745 LOG(WARNING) << "VPN not handled for connection type " << network->type(); 746 return SkBitmap(); 747 } 748 749 return NetworkMenu::IconForDisplay(icon, bottom_right_badge, top_left_badge, 750 bottom_left_badge); 751} 752 753//////////////////////////////////////////////////////////////////////////////// 754// MoreMenuModel 755 756MoreMenuModel::MoreMenuModel(NetworkMenu* owner) 757 : NetworkMenuModel(owner) { 758} 759 760void MoreMenuModel::InitMenuItems( 761 bool is_browser_mode, bool should_open_button_options) { 762 // This gets called on initialization, so any changes should be reflected 763 // in CrosMock::SetNetworkLibraryStatusAreaExpectations(). 764 765 menu_items_.clear(); 766 MenuItemVector link_items; 767 MenuItemVector address_items; 768 769 NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); 770 bool oobe = !should_open_button_options; // we don't show options for OOBE. 771 if (!oobe) { 772 string16 label = l10n_util::GetStringUTF16(is_browser_mode ? 773 IDS_STATUSBAR_NETWORK_OPEN_OPTIONS_DIALOG : 774 IDS_STATUSBAR_NETWORK_OPEN_PROXY_SETTINGS_DIALOG); 775 link_items.push_back(MenuItem(ui::MenuModel::TYPE_COMMAND, label, 776 SkBitmap(), std::string(), FLAG_OPTIONS)); 777 } 778 779 bool connected = cros->Connected(); // always call for test expectations. 780 if (connected) { 781 std::string ip_address = cros->IPAddress(); 782 if (!ip_address.empty()) { 783 address_items.push_back(MenuItem(ui::MenuModel::TYPE_COMMAND, 784 ASCIIToUTF16(cros->IPAddress()), SkBitmap(), std::string(), 785 FLAG_DISABLED)); 786 } 787 } 788 789 if (!is_browser_mode) { 790 const NetworkDevice* ether = cros->FindEthernetDevice(); 791 if (ether) { 792 std::string hardware_address; 793 cros->GetIPConfigs(ether->device_path(), &hardware_address, 794 NetworkLibrary::FORMAT_COLON_SEPARATED_HEX); 795 if (!hardware_address.empty()) { 796 std::string label = l10n_util::GetStringUTF8( 797 IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET) + " " + hardware_address; 798 address_items.push_back(MenuItem(ui::MenuModel::TYPE_COMMAND, 799 UTF8ToUTF16(label), SkBitmap(), std::string(), FLAG_DISABLED)); 800 } 801 } 802 803 if (cros->wifi_enabled()) { 804 const NetworkDevice* wifi = cros->FindWifiDevice(); 805 if (wifi) { 806 std::string hardware_address; 807 cros->GetIPConfigs(wifi->device_path(), 808 &hardware_address, NetworkLibrary::FORMAT_COLON_SEPARATED_HEX); 809 if (!hardware_address.empty()) { 810 std::string label = l10n_util::GetStringUTF8( 811 IDS_STATUSBAR_NETWORK_DEVICE_WIFI) + " " + hardware_address; 812 address_items.push_back(MenuItem(ui::MenuModel::TYPE_COMMAND, 813 UTF8ToUTF16(label), SkBitmap(), std::string(), FLAG_DISABLED)); 814 } 815 } 816 } 817 } 818 819 menu_items_ = link_items; 820 if (!menu_items_.empty() && address_items.size() > 1) 821 menu_items_.push_back(MenuItem()); // Separator 822 menu_items_.insert(menu_items_.end(), 823 address_items.begin(), address_items.end()); 824} 825 826//////////////////////////////////////////////////////////////////////////////// 827// NetworkMenu 828 829// static 830const int NetworkMenu::kNumBarsImages = 4; 831 832// NOTE: Use an array rather than just calculating a resource number to avoid 833// creating implicit ordering dependencies on the resource values. 834// static 835const int NetworkMenu::kBarsImages[kNumBarsImages] = { 836 IDR_STATUSBAR_NETWORK_BARS1, 837 IDR_STATUSBAR_NETWORK_BARS2, 838 IDR_STATUSBAR_NETWORK_BARS3, 839 IDR_STATUSBAR_NETWORK_BARS4, 840}; 841// static 842const int NetworkMenu::kBarsImagesBlack[kNumBarsImages] = { 843 IDR_STATUSBAR_NETWORK_BARS1_BLACK, 844 IDR_STATUSBAR_NETWORK_BARS2_BLACK, 845 IDR_STATUSBAR_NETWORK_BARS3_BLACK, 846 IDR_STATUSBAR_NETWORK_BARS4_BLACK, 847}; 848 849// static 850const int NetworkMenu::kBarsImagesOrange[kNumBarsImages] = { 851 IDR_STATUSBAR_NETWORK_BARS1_ORANGE, 852 IDR_STATUSBAR_NETWORK_BARS2_ORANGE, 853 IDR_STATUSBAR_NETWORK_BARS3_ORANGE, 854 IDR_STATUSBAR_NETWORK_BARS4_ORANGE, 855}; 856#if 0 857// static 858const int NetworkMenu::kBarsImagesVLowData[kNumBarsImages] = { 859 IDR_STATUSBAR_NETWORK_BARS1_RED, 860 IDR_STATUSBAR_NETWORK_BARS2_RED, 861 IDR_STATUSBAR_NETWORK_BARS3_RED, 862 IDR_STATUSBAR_NETWORK_BARS4_RED, 863}; 864#endif 865 866// static 867SkBitmap NetworkMenu::kAnimatingImages[kNumBarsImages]; 868 869// static 870SkBitmap NetworkMenu::kAnimatingImagesBlack[kNumBarsImages]; 871 872NetworkMenu::NetworkMenu() : min_width_(-1) { 873 main_menu_model_.reset(new MainMenuModel(this)); 874 network_menu_.reset(new views::Menu2(main_menu_model_.get())); 875} 876 877NetworkMenu::~NetworkMenu() { 878} 879 880void NetworkMenu::SetFirstLevelMenuWidth(int width) { 881 min_width_ = width; 882 // This actually has no effect since menu is rebuilt before showing. 883 network_menu_->SetMinimumWidth(width); 884} 885 886void NetworkMenu::CancelMenu() { 887 network_menu_->CancelMenu(); 888} 889 890void NetworkMenu::UpdateMenu() { 891 refreshing_menu_ = true; 892 main_menu_model_->InitMenuItems(IsBrowserMode(), ShouldOpenButtonOptions()); 893 network_menu_->Rebuild(); 894 refreshing_menu_ = false; 895} 896 897// static 898const SkBitmap* NetworkMenu::IconForNetworkStrength(const WifiNetwork* wifi, 899 bool black) { 900 DCHECK(wifi); 901 if (wifi->strength() == 0) { 902 return ResourceBundle::GetSharedInstance().GetBitmapNamed( 903 black ? IDR_STATUSBAR_NETWORK_BARS0_BLACK : 904 IDR_STATUSBAR_NETWORK_BARS0); 905 } 906 int index = static_cast<int>(wifi->strength() / 100.0 * 907 nextafter(static_cast<float>(kNumBarsImages), 0)); 908 index = std::max(std::min(index, kNumBarsImages - 1), 0); 909 const int* images = black ? kBarsImagesBlack : kBarsImages; 910 return ResourceBundle::GetSharedInstance().GetBitmapNamed(images[index]); 911} 912 913// static 914const SkBitmap* NetworkMenu::IconForNetworkStrength( 915 const CellularNetwork* cellular, bool black) { 916 DCHECK(cellular); 917 // If no data, then we show 0 bars. 918 if (cellular->strength() == 0 || 919 cellular->data_left() == CellularNetwork::DATA_NONE) { 920 return ResourceBundle::GetSharedInstance().GetBitmapNamed( 921 black ? IDR_STATUSBAR_NETWORK_BARS0_BLACK : 922 IDR_STATUSBAR_NETWORK_BARS0); 923 } 924 int index = static_cast<int>(cellular->strength() / 100.0 * 925 nextafter(static_cast<float>(kNumBarsImages), 0)); 926 index = std::max(std::min(index, kNumBarsImages - 1), 0); 927 const int* images = black ? kBarsImagesBlack : kBarsImages; 928 return ResourceBundle::GetSharedInstance().GetBitmapNamed(images[index]); 929} 930 931// static 932const SkBitmap* NetworkMenu::IconForNetworkConnecting(double animation_value, 933 bool black) { 934 // Fade bars a bit and show the different bar states. 935 const int* source_image_ids = black ? kBarsImagesBlack : kBarsImages; 936 SkBitmap* images = black ? kAnimatingImagesBlack : kAnimatingImages; 937 int index = static_cast<int>(animation_value * 938 nextafter(static_cast<float>(kNumBarsImages), 0)); 939 index = std::max(std::min(index, kNumBarsImages - 1), 0); 940 941 // Lazily cache images. 942 if (images[index].empty()) { 943 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 944 SkBitmap source = *rb.GetBitmapNamed(source_image_ids[index]); 945 946 // Create an empty image to fade against. 947 SkBitmap empty_image; 948 empty_image.setConfig(SkBitmap::kARGB_8888_Config, 949 source.width(), 950 source.height(), 951 0); 952 empty_image.allocPixels(); 953 empty_image.eraseARGB(0, 0, 0, 0); 954 955 images[index] = 956 SkBitmapOperations::CreateBlendedBitmap( 957 empty_image, 958 source, 959 kConnectingImageAlpha); 960 } 961 return &images[index]; 962} 963 964// static 965const SkBitmap* NetworkMenu::BadgeForNetworkTechnology( 966 const CellularNetwork* cellular) { 967 if (!cellular) 968 return NULL; 969 970 int id = -1; 971 switch (cellular->network_technology()) { 972 case NETWORK_TECHNOLOGY_EVDO: 973 switch (cellular->data_left()) { 974 case CellularNetwork::DATA_NONE: 975 id = IDR_STATUSBAR_NETWORK_3G_ERROR; 976 break; 977 case CellularNetwork::DATA_VERY_LOW: 978 case CellularNetwork::DATA_LOW: 979 case CellularNetwork::DATA_NORMAL: 980 id = IDR_STATUSBAR_NETWORK_3G; 981 break; 982 case CellularNetwork::DATA_UNKNOWN: 983 id = IDR_STATUSBAR_NETWORK_3G_UNKNOWN; 984 break; 985 } 986 break; 987 case NETWORK_TECHNOLOGY_1XRTT: 988 switch (cellular->data_left()) { 989 case CellularNetwork::DATA_NONE: 990 id = IDR_STATUSBAR_NETWORK_1X_ERROR; 991 break; 992 case CellularNetwork::DATA_VERY_LOW: 993 case CellularNetwork::DATA_LOW: 994 case CellularNetwork::DATA_NORMAL: 995 id = IDR_STATUSBAR_NETWORK_1X; 996 break; 997 case CellularNetwork::DATA_UNKNOWN: 998 id = IDR_STATUSBAR_NETWORK_1X_UNKNOWN; 999 break; 1000 } 1001 break; 1002 // Note: we may not be able to obtain data usage info 1003 // from GSM carriers, so there may not be a reason 1004 // to create _ERROR or _UNKNOWN versions of the following 1005 // icons. 1006 case NETWORK_TECHNOLOGY_GPRS: 1007 id = IDR_STATUSBAR_NETWORK_GPRS; 1008 break; 1009 case NETWORK_TECHNOLOGY_EDGE: 1010 id = IDR_STATUSBAR_NETWORK_EDGE; 1011 break; 1012 case NETWORK_TECHNOLOGY_UMTS: 1013 id = IDR_STATUSBAR_NETWORK_3G; 1014 break; 1015 case NETWORK_TECHNOLOGY_HSPA: 1016 id = IDR_STATUSBAR_NETWORK_HSPA; 1017 break; 1018 case NETWORK_TECHNOLOGY_HSPA_PLUS: 1019 id = IDR_STATUSBAR_NETWORK_HSPA_PLUS; 1020 break; 1021 case NETWORK_TECHNOLOGY_LTE: 1022 id = IDR_STATUSBAR_NETWORK_LTE; 1023 break; 1024 case NETWORK_TECHNOLOGY_LTE_ADVANCED: 1025 id = IDR_STATUSBAR_NETWORK_LTE_ADVANCED; 1026 break; 1027 case NETWORK_TECHNOLOGY_UNKNOWN: 1028 break; 1029 } 1030 if (id == -1) 1031 return NULL; 1032 else 1033 return ResourceBundle::GetSharedInstance().GetBitmapNamed(id); 1034} 1035 1036// static 1037const SkBitmap* NetworkMenu::BadgeForRoamingStatus( 1038 const CellularNetwork* cellular) { 1039 if (cellular->roaming_state() == ROAMING_STATE_ROAMING) 1040 return ResourceBundle::GetSharedInstance().GetBitmapNamed( 1041 IDR_STATUSBAR_NETWORK_ROAMING); 1042 else 1043 return NULL; 1044} 1045 1046const SkBitmap* NetworkMenu::BadgeForPrivateNetworkStatus( 1047 const Network* network) { 1048 // If network is not null, check if it's the active network with vpn on it. 1049 if (network) { 1050 NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); 1051 if (!(cros->virtual_network() && network == cros->connected_network())) 1052 return NULL; 1053 } 1054 // TODO(kuan): yellow lock icon not ready yet; for now, return the black one 1055 // used by secure wifi network. 1056 return ResourceBundle::GetSharedInstance().GetBitmapNamed( 1057 IDR_STATUSBAR_NETWORK_SECURE); 1058} 1059 1060// static 1061SkBitmap NetworkMenu::IconForDisplay(const SkBitmap* icon, 1062 const SkBitmap* badge) { 1063 return IconForDisplay(icon, badge, NULL, NULL); 1064} 1065 1066// static 1067SkBitmap NetworkMenu::IconForDisplay(const SkBitmap* icon, 1068 const SkBitmap* bottom_right_badge, 1069 const SkBitmap* top_left_badge, 1070 const SkBitmap* bottom_left_badge) { 1071 DCHECK(icon); 1072 if (bottom_right_badge == NULL && top_left_badge == NULL && 1073 bottom_left_badge == NULL) 1074 return *icon; 1075 1076 static const int kTopLeftBadgeX = 0; 1077 static const int kTopLeftBadgeY = 0; 1078 static const int kBottomRightBadgeX = 14; 1079 static const int kBottomRightBadgeY = 14; 1080 static const int kBottomLeftBadgeX = 0; 1081 static const int kBottomLeftBadgeY = 14; 1082 1083 gfx::CanvasSkia canvas(icon->width(), icon->height(), false); 1084 canvas.DrawBitmapInt(*icon, 0, 0); 1085 if (bottom_right_badge != NULL) 1086 canvas.DrawBitmapInt(*bottom_right_badge, 1087 kBottomRightBadgeX, 1088 kBottomRightBadgeY); 1089 if (top_left_badge != NULL) 1090 canvas.DrawBitmapInt(*top_left_badge, kTopLeftBadgeX, kTopLeftBadgeY); 1091 if (bottom_left_badge != NULL) 1092 canvas.DrawBitmapInt(*bottom_left_badge, kBottomLeftBadgeX, 1093 kBottomLeftBadgeY); 1094 return canvas.ExtractBitmap(); 1095} 1096 1097void NetworkMenu::ShowTabbedNetworkSettings(const Network* network) const { 1098 DCHECK(network); 1099 Browser* browser = BrowserList::GetLastActive(); 1100 if (!browser) 1101 return; 1102 std::string page = StringPrintf("%s?servicePath=%s&networkType=%d", 1103 chrome::kInternetOptionsSubPage, 1104 EscapeUrlEncodedData(network->service_path()).c_str(), 1105 network->type()); 1106 browser->ShowOptionsTab(page); 1107} 1108 1109//////////////////////////////////////////////////////////////////////////////// 1110// NetworkMenu, views::ViewMenuDelegate implementation: 1111 1112void NetworkMenu::RunMenu(views::View* source, const gfx::Point& pt) { 1113 refreshing_menu_ = true; 1114 NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); 1115 cros->RequestNetworkScan(); 1116 1117 // Build initial menu items. They will be updated when UpdateMenu is 1118 // called from NetworkChanged. 1119 main_menu_model_->InitMenuItems(IsBrowserMode(), ShouldOpenButtonOptions()); 1120 network_menu_->Rebuild(); 1121 1122 // Restore menu width, if it was set up. 1123 // NOTE: width isn't checked for correctness here since all width-related 1124 // logic implemented inside |network_menu_|. 1125 if (min_width_ != -1) 1126 network_menu_->SetMinimumWidth(min_width_); 1127 refreshing_menu_ = false; 1128 network_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT); 1129} 1130 1131} // namespace chromeos 1132 1133 1134