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