Add ICU message format support
[chromium-blink-merge.git] / chrome / browser / chromeos / net / network_portal_notification_controller.cc
blobbe0ba83f621a2d4491530b7dc1a478c2362b8c26
1 // Copyright 2014 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.
5 #include "chrome/browser/chromeos/net/network_portal_notification_controller.h"
7 #include <vector>
9 #include "ash/shell.h"
10 #include "ash/system/system_notifier.h"
11 #include "ash/system/tray/system_tray_notifier.h"
12 #include "base/basictypes.h"
13 #include "base/command_line.h"
14 #include "base/compiler_specific.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/metrics/histogram.h"
19 #include "base/prefs/pref_service.h"
20 #include "base/strings/string16.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "chrome/browser/browser_process.h"
24 #include "chrome/browser/browser_process_platform_part.h"
25 #include "chrome/browser/chromeos/mobile/mobile_activator.h"
26 #include "chrome/browser/chromeos/net/network_portal_web_dialog.h"
27 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
28 #include "chrome/browser/chromeos/policy/consumer_management_service.h"
29 #include "chrome/browser/chromeos/profiles/profile_helper.h"
30 #include "chrome/browser/profiles/profile_manager.h"
31 #include "chrome/browser/ui/browser_dialogs.h"
32 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
33 #include "chrome/browser/ui/singleton_tabs.h"
34 #include "chrome/common/pref_names.h"
35 #include "chrome/grit/generated_resources.h"
36 #include "chrome/grit/theme_resources.h"
37 #include "chromeos/chromeos_switches.h"
38 #include "chromeos/network/network_state.h"
39 #include "chromeos/network/network_type_pattern.h"
40 #include "components/captive_portal/captive_portal_detector.h"
41 #include "components/user_manager/user_manager.h"
42 #include "extensions/browser/api/networking_config/networking_config_service.h"
43 #include "extensions/browser/api/networking_config/networking_config_service_factory.h"
44 #include "third_party/cros_system_api/dbus/service_constants.h"
45 #include "ui/base/l10n/l10n_util.h"
46 #include "ui/base/resource/resource_bundle.h"
47 #include "ui/message_center/message_center.h"
48 #include "ui/message_center/notification.h"
49 #include "ui/message_center/notification_types.h"
50 #include "ui/message_center/notifier_settings.h"
51 #include "ui/views/widget/widget.h"
53 using message_center::Notification;
55 namespace chromeos {
57 namespace {
59 bool IsPortalNotificationEnabled() {
60 return !base::CommandLine::ForCurrentProcess()->HasSwitch(
61 switches::kDisableNetworkPortalNotification);
64 void CloseNotification() {
65 message_center::MessageCenter::Get()->RemoveNotification(
66 NetworkPortalNotificationController::kNotificationId, false);
69 Profile* GetProfileForPrimaryUser() {
70 const user_manager::User* primary_user =
71 user_manager::UserManager::Get()->GetPrimaryUser();
72 if (!primary_user)
73 return nullptr;
74 return ProfileHelper::Get()->GetProfileByUser(primary_user);
77 // Note that NetworkingConfigService may change after login as the profile
78 // switches from the login to the user's profile.
79 extensions::NetworkingConfigService* GetNetworkingConfigService(
80 Profile* profile) {
81 if (!profile)
82 return nullptr;
83 return extensions::NetworkingConfigServiceFactory::GetForBrowserContext(
84 profile);
87 const extensions::Extension* LookupExtensionForRawSsid(
88 extensions::NetworkingConfigService* networking_config_service,
89 const std::vector<uint8_t>& raw_ssid) {
90 DCHECK(networking_config_service);
91 Profile* profile = GetProfileForPrimaryUser();
92 if (!profile || !networking_config_service)
93 return nullptr;
94 std::string extension_id;
95 std::string hex_ssid =
96 base::HexEncode(vector_as_array(&raw_ssid), raw_ssid.size());
97 extension_id =
98 networking_config_service->LookupExtensionIdForHexSsid(hex_ssid);
99 if (extension_id.empty())
100 return nullptr;
101 return extensions::ExtensionRegistry::Get(profile)
102 ->GetExtensionById(extension_id, extensions::ExtensionRegistry::ENABLED);
105 class NetworkPortalNotificationControllerDelegate
106 : public message_center::NotificationDelegate {
107 public:
108 explicit NetworkPortalNotificationControllerDelegate(
109 const std::string& extension_id,
110 const std::string& guid,
111 base::WeakPtr<NetworkPortalNotificationController> controller)
112 : extension_id_(extension_id),
113 guid_(guid),
114 clicked_(false),
115 controller_(controller) {}
117 // Overridden from message_center::NotificationDelegate:
118 void Display() override;
119 void Close(bool by_user) override;
120 void Click() override;
121 void ButtonClick(int button_click) override;
123 private:
124 ~NetworkPortalNotificationControllerDelegate() override {}
126 // ID of the extension responsible for network configuration of the network
127 // that this notification is generated for. Empty if none.
128 std::string extension_id_;
130 // GUID of the network this notification is generated for.
131 std::string guid_;
133 bool clicked_;
135 base::WeakPtr<NetworkPortalNotificationController> controller_;
137 DISALLOW_COPY_AND_ASSIGN(NetworkPortalNotificationControllerDelegate);
140 void NetworkPortalNotificationControllerDelegate::Display() {
141 UMA_HISTOGRAM_ENUMERATION(
142 NetworkPortalNotificationController::kNotificationMetric,
143 NetworkPortalNotificationController::NOTIFICATION_METRIC_DISPLAYED,
144 NetworkPortalNotificationController::NOTIFICATION_METRIC_COUNT);
147 void NetworkPortalNotificationControllerDelegate::Close(bool by_user) {
148 if (clicked_)
149 return;
150 NetworkPortalNotificationController::UserActionMetric metric =
151 by_user ? NetworkPortalNotificationController::USER_ACTION_METRIC_CLOSED
152 : NetworkPortalNotificationController::USER_ACTION_METRIC_IGNORED;
153 UMA_HISTOGRAM_ENUMERATION(
154 NetworkPortalNotificationController::kUserActionMetric, metric,
155 NetworkPortalNotificationController::USER_ACTION_METRIC_COUNT);
158 void NetworkPortalNotificationControllerDelegate::Click() {
159 clicked_ = true;
160 UMA_HISTOGRAM_ENUMERATION(
161 NetworkPortalNotificationController::kUserActionMetric,
162 NetworkPortalNotificationController::USER_ACTION_METRIC_CLICKED,
163 NetworkPortalNotificationController::USER_ACTION_METRIC_COUNT);
165 Profile* profile = ProfileManager::GetActiveUserProfile();
167 const bool disable_bypass_proxy_switch_present =
168 base::CommandLine::ForCurrentProcess()->HasSwitch(
169 chromeos::switches::kDisableCaptivePortalBypassProxy);
170 const bool use_incognito_profile =
171 disable_bypass_proxy_switch_present
172 ? false
173 : (profile &&
174 profile->GetPrefs()->GetBoolean(
175 prefs::kCaptivePortalAuthenticationIgnoresProxy));
177 if (use_incognito_profile) {
178 if (controller_)
179 controller_->ShowDialog();
180 } else {
181 if (!profile)
182 return;
183 chrome::ScopedTabbedBrowserDisplayer displayer(
184 profile, chrome::HOST_DESKTOP_TYPE_ASH);
185 GURL url(captive_portal::CaptivePortalDetector::kDefaultURL);
186 chrome::ShowSingletonTab(displayer.browser(), url);
188 CloseNotification();
191 void NetworkPortalNotificationControllerDelegate::ButtonClick(
192 int button_index) {
193 if (button_index ==
194 NetworkPortalNotificationController::kUseExtensionButtonIndex) {
195 Profile* profile = GetProfileForPrimaryUser();
196 // The user decided to notify the extension to authenticate to the captive
197 // portal. Notify the NetworkingConfigService, which in turn will notify the
198 // extension. OnExtensionFinsihedAuthentication will be called back if the
199 // authentication succeeded.
200 extensions::NetworkingConfigServiceFactory::GetForBrowserContext(profile)
201 ->DispatchPortalDetectedEvent(
202 extension_id_, guid_,
203 base::Bind(&NetworkPortalNotificationController::
204 OnExtensionFinishedAuthentication,
205 controller_));
206 } else if (button_index ==
207 NetworkPortalNotificationController::kOpenPortalButtonIndex) {
208 Click();
212 gfx::Image& GetImageForNotification() {
213 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
214 gfx::Image& icon = bundle.GetImageNamed(IDR_PORTAL_DETECTION_ALERT);
215 return icon;
218 } // namespace
220 // static
221 const int NetworkPortalNotificationController::kUseExtensionButtonIndex = 0;
223 // static
224 const int NetworkPortalNotificationController::kOpenPortalButtonIndex = 1;
226 // static
227 const char NetworkPortalNotificationController::kNotificationId[] =
228 "chrome://net/network_portal_detector";
230 // static
231 const char NetworkPortalNotificationController::kNotificationMetric[] =
232 "CaptivePortal.Notification.Status";
234 // static
235 const char NetworkPortalNotificationController::kUserActionMetric[] =
236 "CaptivePortal.Notification.UserAction";
238 NetworkPortalNotificationController::NetworkPortalNotificationController()
239 : dialog_(nullptr), ignore_no_network_for_testing_(false) {
242 NetworkPortalNotificationController::~NetworkPortalNotificationController() {
245 void NetworkPortalNotificationController::DefaultNetworkChanged(
246 const NetworkState* network) {
247 if (!network)
248 return;
249 Profile* profile = GetProfileForPrimaryUser();
250 extensions::NetworkingConfigService* networking_config_service =
251 GetNetworkingConfigService(profile);
252 if (!networking_config_service)
253 return;
254 networking_config_service->ResetAuthenticationResult();
257 void NetworkPortalNotificationController::OnPortalDetectionCompleted(
258 const NetworkState* network,
259 const NetworkPortalDetector::CaptivePortalState& state) {
260 if (!IsPortalNotificationEnabled())
261 return;
263 if (!network ||
264 state.status != NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL) {
265 last_network_path_.clear();
267 // In browser tests we initiate fake network portal detection, but network
268 // state usually stays connected. This way, after dialog is shown, it is
269 // immediately closed. The testing check below prevents dialog from closing.
270 if (dialog_ &&
271 (!ignore_no_network_for_testing_ ||
272 state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE)) {
273 dialog_->Close();
276 CloseNotification();
277 return;
280 // Don't do anything if we're currently activating the device.
281 if (MobileActivator::GetInstance()->RunningActivation())
282 return;
284 // Don't do anything if notification for |network| already was
285 // displayed.
286 if (network->path() == last_network_path_)
287 return;
288 last_network_path_ = network->path();
290 if (ash::Shell::HasInstance()) {
291 ash::Shell::GetInstance()
292 ->system_tray_notifier()
293 ->NotifyOnCaptivePortalDetected(network->path());
296 message_center::MessageCenter::Get()->AddNotification(
297 GetNotification(network, state));
300 void NetworkPortalNotificationController::ShowDialog() {
301 if (dialog_)
302 return;
304 Profile* signin_profile = ProfileHelper::GetSigninProfile();
305 dialog_ = new NetworkPortalWebDialog(AsWeakPtr());
306 dialog_->SetWidget(views::Widget::GetWidgetForNativeWindow(
307 chrome::ShowWebDialog(nullptr, signin_profile, dialog_)));
310 void NetworkPortalNotificationController::OnDialogDestroyed(
311 const NetworkPortalWebDialog* dialog) {
312 if (dialog == dialog_) {
313 dialog_ = nullptr;
314 ProfileHelper::Get()->ClearSigninProfile(base::Closure());
318 scoped_ptr<message_center::Notification>
319 NetworkPortalNotificationController::CreateDefaultCaptivePortalNotification(
320 const NetworkState* network) {
321 message_center::RichNotificationData data;
322 scoped_refptr<NetworkPortalNotificationControllerDelegate> delegate(
323 new NetworkPortalNotificationControllerDelegate(
324 std::string(), network->guid(), AsWeakPtr()));
325 gfx::Image& icon = GetImageForNotification();
326 message_center::NotifierId notifier_id(
327 message_center::NotifierId::SYSTEM_COMPONENT,
328 ash::system_notifier::kNotifierNetworkPortalDetector);
329 base::string16 notificationText;
330 bool is_wifi = NetworkTypePattern::WiFi().MatchesType(network->type());
331 scoped_ptr<Notification> notification(new Notification(
332 message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId,
333 l10n_util::GetStringUTF16(
334 is_wifi ? IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIFI
335 : IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIRED),
336 l10n_util::GetStringFUTF16(
337 is_wifi ? IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIFI
338 : IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIRED,
339 base::UTF8ToUTF16(network->name())),
340 icon, base::string16(), notifier_id, data, delegate.get()));
341 notification->SetSystemPriority();
342 return notification.Pass();
345 scoped_ptr<message_center::Notification> NetworkPortalNotificationController::
346 CreateCaptivePortalNotificationForExtension(
347 const NetworkState* network,
348 extensions::NetworkingConfigService* networking_config_service,
349 const extensions::Extension* extension) {
350 message_center::RichNotificationData data;
351 scoped_refptr<NetworkPortalNotificationControllerDelegate> delegate(
352 new NetworkPortalNotificationControllerDelegate(
353 extension->id(), network->guid(), AsWeakPtr()));
354 gfx::Image& icon = GetImageForNotification();
355 message_center::NotifierId notifier_id(
356 message_center::NotifierId::SYSTEM_COMPONENT,
357 ash::system_notifier::kNotifierNetworkPortalDetector);
359 extensions::NetworkingConfigService::AuthenticationResult
360 authentication_result =
361 networking_config_service->GetAuthenticationResult();
362 base::string16 notificationText;
363 if (authentication_result.authentication_state ==
364 extensions::NetworkingConfigService::NOTRY ||
365 network->guid() != authentication_result.guid) {
366 notificationText = l10n_util::GetStringFUTF16(
367 IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_ASK_WIFI,
368 base::UTF8ToUTF16(network->name()));
369 data.buttons.push_back(
370 message_center::ButtonInfo(l10n_util::GetStringFUTF16(
371 IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION,
372 base::UTF8ToUTF16(extension->name()))));
373 data.buttons.push_back(message_center::ButtonInfo(l10n_util::GetStringUTF16(
374 IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_PORTAL)));
375 } else {
376 notificationText = l10n_util::GetStringFUTF16(
377 IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_FAILED_WIFI,
378 base::UTF8ToUTF16(network->name()));
379 data.buttons.push_back(
380 message_center::ButtonInfo(l10n_util::GetStringFUTF16(
381 IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION_RETRY,
382 base::UTF8ToUTF16(extension->name()))));
383 data.buttons.push_back(message_center::ButtonInfo(l10n_util::GetStringUTF16(
384 IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_PORTAL)));
386 scoped_ptr<Notification> notification(new Notification(
387 message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId,
388 l10n_util::GetStringUTF16(IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIFI),
389 notificationText, icon, base::string16() /* display_source */,
390 notifier_id, data, delegate.get()));
391 notification->SetSystemPriority();
392 return notification.Pass();
395 scoped_ptr<Notification> NetworkPortalNotificationController::GetNotification(
396 const NetworkState* network,
397 const NetworkPortalDetector::CaptivePortalState& state) {
398 base::string16 notificationText;
399 Profile* profile = GetProfileForPrimaryUser();
400 extensions::NetworkingConfigService* networking_config_service =
401 GetNetworkingConfigService(profile);
402 std::string extension_id;
403 const extensions::Extension* extension = nullptr;
404 if (networking_config_service) {
405 extension = LookupExtensionForRawSsid(networking_config_service,
406 network->raw_ssid());
408 if (extension) {
409 return CreateCaptivePortalNotificationForExtension(
410 network, networking_config_service, extension);
411 } else {
412 return CreateDefaultCaptivePortalNotification(network);
416 void NetworkPortalNotificationController::OnExtensionFinishedAuthentication() {
417 if (!retry_detection_callback_.is_null())
418 retry_detection_callback_.Run();
421 void NetworkPortalNotificationController::SetIgnoreNoNetworkForTesting() {
422 ignore_no_network_for_testing_ = true;
425 void NetworkPortalNotificationController::CloseDialog() {
426 if (dialog_)
427 dialog_->Close();
430 const NetworkPortalWebDialog*
431 NetworkPortalNotificationController::GetDialogForTesting() const {
432 return dialog_;
435 } // namespace chromeos