Roll src/third_party/WebKit eac3800:0237a66 (svn 202606:202607)
[chromium-blink-merge.git] / chrome / browser / chromeos / net / network_portal_notification_controller.cc
blob92a7278272799db59b96266c5ca47e3cf07c1503
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),
240 ignore_no_network_for_testing_(false),
241 weak_factory_(this) {}
243 NetworkPortalNotificationController::~NetworkPortalNotificationController() {
246 void NetworkPortalNotificationController::DefaultNetworkChanged(
247 const NetworkState* network) {
248 if (!network)
249 return;
250 Profile* profile = GetProfileForPrimaryUser();
251 extensions::NetworkingConfigService* networking_config_service =
252 GetNetworkingConfigService(profile);
253 if (!networking_config_service)
254 return;
255 networking_config_service->ResetAuthenticationResult();
258 void NetworkPortalNotificationController::OnPortalDetectionCompleted(
259 const NetworkState* network,
260 const NetworkPortalDetector::CaptivePortalState& state) {
261 if (!IsPortalNotificationEnabled())
262 return;
264 if (!network ||
265 state.status != NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL) {
266 last_network_path_.clear();
268 // In browser tests we initiate fake network portal detection, but network
269 // state usually stays connected. This way, after dialog is shown, it is
270 // immediately closed. The testing check below prevents dialog from closing.
271 if (dialog_ &&
272 (!ignore_no_network_for_testing_ ||
273 state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE)) {
274 dialog_->Close();
277 CloseNotification();
278 return;
281 // Don't do anything if we're currently activating the device.
282 if (MobileActivator::GetInstance()->RunningActivation())
283 return;
285 // Don't do anything if notification for |network| already was
286 // displayed.
287 if (network->path() == last_network_path_)
288 return;
289 last_network_path_ = network->path();
291 if (ash::Shell::HasInstance()) {
292 ash::Shell::GetInstance()
293 ->system_tray_notifier()
294 ->NotifyOnCaptivePortalDetected(network->path());
297 message_center::MessageCenter::Get()->AddNotification(
298 GetNotification(network, state));
301 void NetworkPortalNotificationController::ShowDialog() {
302 if (dialog_)
303 return;
305 Profile* signin_profile = ProfileHelper::GetSigninProfile();
306 dialog_ = new NetworkPortalWebDialog(weak_factory_.GetWeakPtr());
307 dialog_->SetWidget(views::Widget::GetWidgetForNativeWindow(
308 chrome::ShowWebDialog(nullptr, signin_profile, dialog_)));
311 void NetworkPortalNotificationController::OnDialogDestroyed(
312 const NetworkPortalWebDialog* dialog) {
313 if (dialog == dialog_) {
314 dialog_ = nullptr;
315 ProfileHelper::Get()->ClearSigninProfile(base::Closure());
319 scoped_ptr<message_center::Notification>
320 NetworkPortalNotificationController::CreateDefaultCaptivePortalNotification(
321 const NetworkState* network) {
322 message_center::RichNotificationData data;
323 scoped_refptr<NetworkPortalNotificationControllerDelegate> delegate(
324 new NetworkPortalNotificationControllerDelegate(
325 std::string(), network->guid(), weak_factory_.GetWeakPtr()));
326 gfx::Image& icon = GetImageForNotification();
327 message_center::NotifierId notifier_id(
328 message_center::NotifierId::SYSTEM_COMPONENT,
329 ash::system_notifier::kNotifierNetworkPortalDetector);
330 base::string16 notificationText;
331 bool is_wifi = NetworkTypePattern::WiFi().MatchesType(network->type());
332 scoped_ptr<Notification> notification(new Notification(
333 message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId,
334 l10n_util::GetStringUTF16(
335 is_wifi ? IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIFI
336 : IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIRED),
337 l10n_util::GetStringFUTF16(
338 is_wifi ? IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIFI
339 : IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIRED,
340 base::UTF8ToUTF16(network->name())),
341 icon, base::string16(), GURL(), notifier_id, data, delegate.get()));
342 notification->SetSystemPriority();
343 return notification.Pass();
346 scoped_ptr<message_center::Notification> NetworkPortalNotificationController::
347 CreateCaptivePortalNotificationForExtension(
348 const NetworkState* network,
349 extensions::NetworkingConfigService* networking_config_service,
350 const extensions::Extension* extension) {
351 message_center::RichNotificationData data;
352 scoped_refptr<NetworkPortalNotificationControllerDelegate> delegate(
353 new NetworkPortalNotificationControllerDelegate(
354 extension->id(), network->guid(), weak_factory_.GetWeakPtr()));
355 gfx::Image& icon = GetImageForNotification();
356 message_center::NotifierId notifier_id(
357 message_center::NotifierId::SYSTEM_COMPONENT,
358 ash::system_notifier::kNotifierNetworkPortalDetector);
360 extensions::NetworkingConfigService::AuthenticationResult
361 authentication_result =
362 networking_config_service->GetAuthenticationResult();
363 base::string16 notificationText;
364 if (authentication_result.authentication_state ==
365 extensions::NetworkingConfigService::NOTRY ||
366 network->guid() != authentication_result.guid) {
367 notificationText = l10n_util::GetStringFUTF16(
368 IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_ASK_WIFI,
369 base::UTF8ToUTF16(network->name()));
370 data.buttons.push_back(
371 message_center::ButtonInfo(l10n_util::GetStringFUTF16(
372 IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION,
373 base::UTF8ToUTF16(extension->name()))));
374 data.buttons.push_back(message_center::ButtonInfo(l10n_util::GetStringUTF16(
375 IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_PORTAL)));
376 } else {
377 notificationText = l10n_util::GetStringFUTF16(
378 IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_FAILED_WIFI,
379 base::UTF8ToUTF16(network->name()));
380 data.buttons.push_back(
381 message_center::ButtonInfo(l10n_util::GetStringFUTF16(
382 IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION_RETRY,
383 base::UTF8ToUTF16(extension->name()))));
384 data.buttons.push_back(message_center::ButtonInfo(l10n_util::GetStringUTF16(
385 IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_PORTAL)));
387 scoped_ptr<Notification> notification(new Notification(
388 message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId,
389 l10n_util::GetStringUTF16(IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIFI),
390 notificationText, icon, base::string16() /* display_source */, GURL(),
391 notifier_id, data, delegate.get()));
392 notification->SetSystemPriority();
393 return notification.Pass();
396 scoped_ptr<Notification> NetworkPortalNotificationController::GetNotification(
397 const NetworkState* network,
398 const NetworkPortalDetector::CaptivePortalState& state) {
399 base::string16 notificationText;
400 Profile* profile = GetProfileForPrimaryUser();
401 extensions::NetworkingConfigService* networking_config_service =
402 GetNetworkingConfigService(profile);
403 std::string extension_id;
404 const extensions::Extension* extension = nullptr;
405 if (networking_config_service) {
406 extension = LookupExtensionForRawSsid(networking_config_service,
407 network->raw_ssid());
409 if (extension) {
410 return CreateCaptivePortalNotificationForExtension(
411 network, networking_config_service, extension);
412 } else {
413 return CreateDefaultCaptivePortalNotification(network);
417 void NetworkPortalNotificationController::OnExtensionFinishedAuthentication() {
418 if (!retry_detection_callback_.is_null())
419 retry_detection_callback_.Run();
422 void NetworkPortalNotificationController::SetIgnoreNoNetworkForTesting() {
423 ignore_no_network_for_testing_ = true;
426 void NetworkPortalNotificationController::CloseDialog() {
427 if (dialog_)
428 dialog_->Close();
431 const NetworkPortalWebDialog*
432 NetworkPortalNotificationController::GetDialogForTesting() const {
433 return dialog_;
436 } // namespace chromeos