Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/notifications/message_center_notification_manager.h" | 5 #include "chrome/browser/notifications/message_center_notification_manager.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/memory/ptr_util.h" | 11 #include "base/memory/ptr_util.h" |
| 12 #include "base/stl_util.h" | |
| 13 #include "base/strings/stringprintf.h" | 12 #include "base/strings/stringprintf.h" |
| 14 #include "build/build_config.h" | 13 #include "build/build_config.h" |
| 15 #include "chrome/browser/extensions/api/notification_provider/notification_provi der_api.h" | 14 #include "chrome/browser/extensions/api/notification_provider/notification_provi der_api.h" |
| 16 #include "chrome/browser/notifications/extension_welcome_notification.h" | 15 #include "chrome/browser/notifications/extension_welcome_notification.h" |
| 17 #include "chrome/browser/notifications/extension_welcome_notification_factory.h" | 16 #include "chrome/browser/notifications/extension_welcome_notification_factory.h" |
| 18 #include "chrome/browser/notifications/fullscreen_notification_blocker.h" | 17 #include "chrome/browser/notifications/fullscreen_notification_blocker.h" |
| 19 #include "chrome/browser/notifications/message_center_settings_controller.h" | 18 #include "chrome/browser/notifications/message_center_settings_controller.h" |
| 20 #include "chrome/browser/notifications/notification.h" | 19 #include "chrome/browser/notifications/notification.h" |
| 21 #include "chrome/browser/notifications/notification_conversion_helper.h" | 20 #include "chrome/browser/notifications/notification_conversion_helper.h" |
| 22 #include "chrome/browser/notifications/profile_notification.h" | 21 #include "chrome/browser/notifications/profile_notification.h" |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 69 | 68 |
| 70 #if defined(OS_WIN) || defined(OS_MACOSX) \ | 69 #if defined(OS_WIN) || defined(OS_MACOSX) \ |
| 71 || (defined(OS_LINUX) && !defined(OS_CHROMEOS)) | 70 || (defined(OS_LINUX) && !defined(OS_CHROMEOS)) |
| 72 // On Windows, Linux and Mac, the notification manager owns the tray icon and | 71 // On Windows, Linux and Mac, the notification manager owns the tray icon and |
| 73 // views.Other platforms have global ownership and Create will return NULL. | 72 // views.Other platforms have global ownership and Create will return NULL. |
| 74 tray_.reset(message_center::CreateMessageCenterTray()); | 73 tray_.reset(message_center::CreateMessageCenterTray()); |
| 75 #endif | 74 #endif |
| 76 } | 75 } |
| 77 | 76 |
| 78 MessageCenterNotificationManager::~MessageCenterNotificationManager() { | 77 MessageCenterNotificationManager::~MessageCenterNotificationManager() { |
| 79 message_center_->SetNotifierSettingsProvider(NULL); | 78 message_center_->SetNotifierSettingsProvider(nullptr); |
| 80 message_center_->RemoveObserver(this); | 79 message_center_->RemoveObserver(this); |
| 81 | 80 |
| 82 base::STLDeleteContainerPairSecondPointers(profile_notifications_.begin(), | |
| 83 profile_notifications_.end()); | |
| 84 profile_notifications_.clear(); | 81 profile_notifications_.clear(); |
| 85 } | 82 } |
| 86 | 83 |
| 87 //////////////////////////////////////////////////////////////////////////////// | 84 //////////////////////////////////////////////////////////////////////////////// |
| 88 // NotificationUIManager | 85 // NotificationUIManager |
| 89 | 86 |
| 90 void MessageCenterNotificationManager::Add(const Notification& notification, | 87 void MessageCenterNotificationManager::Add(const Notification& notification, |
| 91 Profile* profile) { | 88 Profile* profile) { |
| 92 if (Update(notification, profile)) | 89 if (Update(notification, profile)) |
| 93 return; | 90 return; |
| 94 | 91 |
| 95 ProfileNotification* profile_notification = | 92 std::unique_ptr<ProfileNotification> profile_notification_ptr = |
| 96 new ProfileNotification(profile, notification); | 93 base::MakeUnique<ProfileNotification>(profile, notification); |
| 94 ProfileNotification* profile_notification = profile_notification_ptr.get(); | |
| 97 | 95 |
| 98 ExtensionWelcomeNotificationFactory::GetForBrowserContext(profile)-> | 96 ExtensionWelcomeNotificationFactory::GetForBrowserContext(profile)-> |
| 99 ShowWelcomeNotificationIfNecessary(profile_notification->notification()); | 97 ShowWelcomeNotificationIfNecessary(profile_notification->notification()); |
| 100 | 98 |
| 101 // WARNING: You MUST use AddProfileNotification or update the message center | 99 // WARNING: You MUST use AddProfileNotification or update the message center |
| 102 // via the notification within a ProfileNotification object or the profile ID | 100 // via the notification within a ProfileNotification object or the profile ID |
| 103 // will not be correctly set for ChromeOS. | 101 // will not be correctly set for ChromeOS. |
| 104 // Takes ownership of profile_notification. | 102 // Takes ownership of profile_notification. |
| 105 AddProfileNotification(profile_notification); | 103 AddProfileNotification(std::move(profile_notification_ptr)); |
| 106 | 104 |
| 107 // TODO(liyanhou): Change the logic to only send notifications to one party. | 105 // TODO(liyanhou): Change the logic to only send notifications to one party. |
| 108 // Currently, if there is an app with notificationProvider permission, | 106 // Currently, if there is an app with notificationProvider permission, |
| 109 // notifications will go to both message center and the app. | 107 // notifications will go to both message center and the app. |
| 110 // Change it to send notifications to message center only when the user chose | 108 // Change it to send notifications to message center only when the user chose |
| 111 // default message center (extension_id.empty()). | 109 // default message center (extension_id.empty()). |
| 112 | 110 |
| 113 // If there exist apps/extensions that have notificationProvider permission, | 111 // If there exist apps/extensions that have notificationProvider permission, |
| 114 // route notifications to one of the apps/extensions. | 112 // route notifications to one of the apps/extensions. |
| 115 std::string extension_id = GetExtensionTakingOverNotifications(profile); | 113 std::string extension_id = GetExtensionTakingOverNotifications(profile); |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 127 const std::string& tag = notification.tag(); | 125 const std::string& tag = notification.tag(); |
| 128 if (tag.empty()) | 126 if (tag.empty()) |
| 129 return false; | 127 return false; |
| 130 | 128 |
| 131 const GURL origin_url = notification.origin_url(); | 129 const GURL origin_url = notification.origin_url(); |
| 132 DCHECK(origin_url.is_valid()); | 130 DCHECK(origin_url.is_valid()); |
| 133 | 131 |
| 134 // Since tag is provided by arbitrary JS, we need to use origin_url | 132 // Since tag is provided by arbitrary JS, we need to use origin_url |
| 135 // (which is an app url in case of app/extension) to scope the tags | 133 // (which is an app url in case of app/extension) to scope the tags |
| 136 // in the given profile. | 134 // in the given profile. |
| 137 for (NotificationMap::iterator iter = profile_notifications_.begin(); | 135 for (auto iter = profile_notifications_.begin(); |
| 138 iter != profile_notifications_.end(); ++iter) { | 136 iter != profile_notifications_.end(); ++iter) { |
| 139 ProfileNotification* old_notification = (*iter).second; | 137 ProfileNotification* old_notification = (*iter).second.get(); |
| 140 if (old_notification->notification().tag() == tag && | 138 if (old_notification->notification().tag() == tag && |
| 141 old_notification->notification().origin_url() == origin_url && | 139 old_notification->notification().origin_url() == origin_url && |
| 142 old_notification->profile_id() == | 140 old_notification->profile_id() == |
| 143 NotificationUIManager::GetProfileID(profile)) { | 141 NotificationUIManager::GetProfileID(profile)) { |
| 144 // Changing the type from non-progress to progress does not count towards | 142 // Changing the type from non-progress to progress does not count towards |
| 145 // the immediate update allowed in the message center. | 143 // the immediate update allowed in the message center. |
| 146 std::string old_id = old_notification->notification().id(); | 144 std::string old_id = old_notification->notification().id(); |
| 147 | 145 |
| 148 // Add/remove notification in the local list but just update the same | 146 // Add/remove notification in the local list but just update the same |
| 149 // one in MessageCenter. | 147 // one in MessageCenter. |
| 150 ProfileNotification* new_notification = | 148 std::unique_ptr<ProfileNotification> new_notification = |
| 151 new ProfileNotification(profile, notification); | 149 base::MakeUnique<ProfileNotification>(profile, notification); |
| 150 const Notification& notification = new_notification->notification(); | |
| 152 // Delete the old one after the new one is created to ensure we don't run | 151 // Delete the old one after the new one is created to ensure we don't run |
| 153 // out of KeepAlives. | 152 // out of KeepAlives. |
| 154 delete old_notification; | |
| 155 profile_notifications_.erase(old_id); | 153 profile_notifications_.erase(old_id); |
| 156 profile_notifications_[new_notification->notification().id()] = | 154 profile_notifications_[notification.id()] = std::move(new_notification); |
| 157 new_notification; | |
| 158 | 155 |
| 159 // TODO(liyanhou): Add routing updated notifications to alternative | 156 // TODO(liyanhou): Add routing updated notifications to alternative |
| 160 // providers. | 157 // providers. |
| 161 | 158 |
| 162 // Non-persistent Web Notifications rely on receiving the Display() event | 159 // Non-persistent Web Notifications rely on receiving the Display() event |
| 163 // to inform the developer, even when replacing a previous notification. | 160 // to inform the developer, even when replacing a previous notification. |
| 164 if (notification.notifier_id().type == NotifierId::WEB_PAGE) | 161 if (notification.notifier_id().type == NotifierId::WEB_PAGE) |
| 165 notification.delegate()->Display(); | 162 notification.delegate()->Display(); |
| 166 | 163 |
| 167 // WARNING: You MUST use AddProfileNotification or update the message | 164 // WARNING: You MUST use AddProfileNotification or update the message |
| 168 // center via the notification within a ProfileNotification object or the | 165 // center via the notification within a ProfileNotification object or the |
| 169 // profile ID will not be correctly set for ChromeOS. | 166 // profile ID will not be correctly set for ChromeOS. |
| 170 message_center_->UpdateNotification( | 167 message_center_->UpdateNotification( |
| 171 old_id, base::MakeUnique<message_center::Notification>( | 168 old_id, base::MakeUnique<message_center::Notification>(notification)); |
| 172 new_notification->notification())); | |
| 173 | 169 |
| 174 return true; | 170 return true; |
| 175 } | 171 } |
| 176 } | 172 } |
| 177 return false; | 173 return false; |
| 178 } | 174 } |
| 179 | 175 |
| 180 const Notification* MessageCenterNotificationManager::FindById( | 176 const Notification* MessageCenterNotificationManager::FindById( |
| 181 const std::string& delegate_id, | 177 const std::string& delegate_id, |
| 182 ProfileID profile_id) const { | 178 ProfileID profile_id) const { |
| 183 // The profile pointer can be weak, the instance may have been destroyed, so | 179 // The profile pointer can be weak, the instance may have been destroyed, so |
| 184 // no profile method should be called inside this function. | 180 // no profile method should be called inside this function. |
| 185 | 181 |
| 186 std::string profile_notification_id = | 182 std::string profile_notification_id = |
| 187 ProfileNotification::GetProfileNotificationId(delegate_id, profile_id); | 183 ProfileNotification::GetProfileNotificationId(delegate_id, profile_id); |
| 188 NotificationMap::const_iterator iter = | 184 auto iter = profile_notifications_.find(profile_notification_id); |
| 189 profile_notifications_.find(profile_notification_id); | |
| 190 if (iter == profile_notifications_.end()) | 185 if (iter == profile_notifications_.end()) |
| 191 return NULL; | 186 return nullptr; |
| 192 return &(iter->second->notification()); | 187 return &(iter->second->notification()); |
| 193 } | 188 } |
| 194 | 189 |
| 195 bool MessageCenterNotificationManager::CancelById( | 190 bool MessageCenterNotificationManager::CancelById( |
| 196 const std::string& delegate_id, | 191 const std::string& delegate_id, |
| 197 ProfileID profile_id) { | 192 ProfileID profile_id) { |
| 198 // The profile pointer can be weak, the instance may have been destroyed, so | 193 // The profile pointer can be weak, the instance may have been destroyed, so |
| 199 // no profile method should be called inside this function. | 194 // no profile method should be called inside this function. |
| 200 | 195 |
| 201 std::string profile_notification_id = | 196 std::string profile_notification_id = |
| 202 ProfileNotification::GetProfileNotificationId(delegate_id, profile_id); | 197 ProfileNotification::GetProfileNotificationId(delegate_id, profile_id); |
| 203 // See if this ID hasn't been shown yet. | 198 // See if this ID hasn't been shown yet. |
| 204 // If it has been shown, remove it. | 199 // If it has been shown, remove it. |
| 205 NotificationMap::iterator iter = | 200 auto iter = profile_notifications_.find(profile_notification_id); |
| 206 profile_notifications_.find(profile_notification_id); | |
| 207 if (iter == profile_notifications_.end()) | 201 if (iter == profile_notifications_.end()) |
| 208 return false; | 202 return false; |
| 209 | 203 |
| 210 RemoveProfileNotification(iter->second); | 204 RemoveProfileNotification(iter->second.get()); |
| 211 message_center_->RemoveNotification(profile_notification_id, | 205 message_center_->RemoveNotification(profile_notification_id, |
| 212 /* by_user */ false); | 206 /* by_user */ false); |
| 213 return true; | 207 return true; |
| 214 } | 208 } |
| 215 | 209 |
| 216 std::set<std::string> | 210 std::set<std::string> |
| 217 MessageCenterNotificationManager::GetAllIdsByProfileAndSourceOrigin( | 211 MessageCenterNotificationManager::GetAllIdsByProfileAndSourceOrigin( |
| 218 ProfileID profile_id, | 212 ProfileID profile_id, |
| 219 const GURL& source) { | 213 const GURL& source) { |
| 220 std::set<std::string> delegate_ids; | 214 std::set<std::string> delegate_ids; |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 239 | 233 |
| 240 return delegate_ids; | 234 return delegate_ids; |
| 241 } | 235 } |
| 242 | 236 |
| 243 bool MessageCenterNotificationManager::CancelAllBySourceOrigin( | 237 bool MessageCenterNotificationManager::CancelAllBySourceOrigin( |
| 244 const GURL& source) { | 238 const GURL& source) { |
| 245 // Same pattern as CancelById, but more complicated than the above | 239 // Same pattern as CancelById, but more complicated than the above |
| 246 // because there may be multiple notifications from the same source. | 240 // because there may be multiple notifications from the same source. |
| 247 bool removed = false; | 241 bool removed = false; |
| 248 | 242 |
| 249 for (NotificationMap::iterator loopiter = profile_notifications_.begin(); | 243 for (auto loopiter = profile_notifications_.begin(); |
| 250 loopiter != profile_notifications_.end(); ) { | 244 loopiter != profile_notifications_.end();) { |
| 251 NotificationMap::iterator curiter = loopiter++; | 245 auto curiter = loopiter++; |
| 252 if ((*curiter).second->notification().origin_url() == source) { | 246 if ((*curiter).second->notification().origin_url() == source) { |
| 253 const std::string id = curiter->first; | 247 const std::string id = curiter->first; |
| 254 RemoveProfileNotification(curiter->second); | 248 RemoveProfileNotification(curiter->second.get()); |
| 255 message_center_->RemoveNotification(id, /* by_user */ false); | 249 message_center_->RemoveNotification(id, /* by_user */ false); |
| 256 removed = true; | 250 removed = true; |
| 257 } | 251 } |
| 258 } | 252 } |
| 259 return removed; | 253 return removed; |
| 260 } | 254 } |
| 261 | 255 |
| 262 bool MessageCenterNotificationManager::CancelAllByProfile( | 256 bool MessageCenterNotificationManager::CancelAllByProfile( |
| 263 ProfileID profile_id) { | 257 ProfileID profile_id) { |
| 264 // Same pattern as CancelAllBySourceOrigin. | 258 // Same pattern as CancelAllBySourceOrigin. |
| 265 bool removed = false; | 259 bool removed = false; |
| 266 | 260 |
| 267 for (NotificationMap::iterator loopiter = profile_notifications_.begin(); | 261 for (auto loopiter = profile_notifications_.begin(); |
| 268 loopiter != profile_notifications_.end(); ) { | 262 loopiter != profile_notifications_.end();) { |
| 269 NotificationMap::iterator curiter = loopiter++; | 263 auto curiter = loopiter++; |
| 270 if (profile_id == (*curiter).second->profile_id()) { | 264 if (profile_id == (*curiter).second->profile_id()) { |
| 271 const std::string id = curiter->first; | 265 const std::string id = curiter->first; |
| 272 RemoveProfileNotification(curiter->second); | 266 RemoveProfileNotification(curiter->second.get()); |
| 273 message_center_->RemoveNotification(id, /* by_user */ false); | 267 message_center_->RemoveNotification(id, /* by_user */ false); |
| 274 removed = true; | 268 removed = true; |
| 275 } | 269 } |
| 276 } | 270 } |
| 277 return removed; | 271 return removed; |
| 278 } | 272 } |
| 279 | 273 |
| 280 void MessageCenterNotificationManager::CancelAll() { | 274 void MessageCenterNotificationManager::CancelAll() { |
| 281 message_center_->RemoveAllNotifications( | 275 message_center_->RemoveAllNotifications( |
| 282 false /* by_user */, message_center::MessageCenter::RemoveType::ALL); | 276 false /* by_user */, message_center::MessageCenter::RemoveType::ALL); |
| 283 } | 277 } |
| 284 | 278 |
| 285 //////////////////////////////////////////////////////////////////////////////// | 279 //////////////////////////////////////////////////////////////////////////////// |
| 286 // MessageCenter::Observer | 280 // MessageCenter::Observer |
| 287 void MessageCenterNotificationManager::OnNotificationRemoved( | 281 void MessageCenterNotificationManager::OnNotificationRemoved( |
| 288 const std::string& id, | 282 const std::string& id, |
| 289 bool by_user) { | 283 bool by_user) { |
| 290 NotificationMap::const_iterator iter = profile_notifications_.find(id); | 284 auto iter = profile_notifications_.find(id); |
| 291 if (iter != profile_notifications_.end()) | 285 if (iter != profile_notifications_.end()) |
| 292 RemoveProfileNotification(iter->second); | 286 RemoveProfileNotification(iter->second.get()); |
| 293 } | 287 } |
| 294 | 288 |
| 295 void MessageCenterNotificationManager::OnCenterVisibilityChanged( | 289 void MessageCenterNotificationManager::OnCenterVisibilityChanged( |
| 296 message_center::Visibility visibility) { | 290 message_center::Visibility visibility) { |
| 297 } | 291 } |
| 298 | 292 |
| 299 void MessageCenterNotificationManager::OnNotificationUpdated( | 293 void MessageCenterNotificationManager::OnNotificationUpdated( |
| 300 const std::string& id) { | 294 const std::string& id) { |
| 301 } | 295 } |
| 302 | 296 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 341 | 335 |
| 342 // Send the notification to the alternate provider extension/app. | 336 // Send the notification to the alternate provider extension/app. |
| 343 extensions::NotificationProviderEventRouter event_router(profile); | 337 extensions::NotificationProviderEventRouter event_router(profile); |
| 344 event_router.CreateNotification(extension_id, | 338 event_router.CreateNotification(extension_id, |
| 345 notification.notifier_id().id, | 339 notification.notifier_id().id, |
| 346 notification.delegate_id(), | 340 notification.delegate_id(), |
| 347 options); | 341 options); |
| 348 } | 342 } |
| 349 | 343 |
| 350 void MessageCenterNotificationManager::AddProfileNotification( | 344 void MessageCenterNotificationManager::AddProfileNotification( |
| 351 ProfileNotification* profile_notification) { | 345 std::unique_ptr<ProfileNotification> profile_notification) { |
| 352 const Notification& notification = profile_notification->notification(); | 346 const Notification& notification = profile_notification->notification(); |
| 353 std::string id = notification.id(); | 347 std::string id = notification.id(); |
| 354 // Notification ids should be unique. | 348 // Notification ids should be unique. |
| 355 DCHECK(profile_notifications_.find(id) == profile_notifications_.end()); | 349 DCHECK(profile_notifications_.find(id) == profile_notifications_.end()); |
| 356 profile_notifications_[id] = profile_notification; | 350 profile_notifications_[id] = std::move(profile_notification); |
| 357 } | 351 } |
| 358 | 352 |
| 359 void MessageCenterNotificationManager::RemoveProfileNotification( | 353 void MessageCenterNotificationManager::RemoveProfileNotification( |
| 360 ProfileNotification* profile_notification) { | 354 ProfileNotification* profile_notification) { |
| 361 std::string id = profile_notification->notification().id(); | 355 std::string id = profile_notification->notification().id(); |
| 362 profile_notifications_.erase(id); | 356 profile_notifications_.erase(id); |
| 363 delete profile_notification; | |
|
Nico
2016/09/22 15:56:11
I found this one confusing. RemoveProfileNotificat
Avi (use Gerrit)
2016/09/22 19:17:17
Done.
| |
| 364 } | 357 } |
| 365 | 358 |
| 366 ProfileNotification* MessageCenterNotificationManager::FindProfileNotification( | 359 ProfileNotification* MessageCenterNotificationManager::FindProfileNotification( |
| 367 const std::string& id) const { | 360 const std::string& id) const { |
| 368 NotificationMap::const_iterator iter = profile_notifications_.find(id); | 361 auto iter = profile_notifications_.find(id); |
| 369 if (iter == profile_notifications_.end()) | 362 if (iter == profile_notifications_.end()) |
| 370 return NULL; | 363 return nullptr; |
| 371 | 364 |
| 372 return (*iter).second; | 365 return (*iter).second.get(); |
| 373 } | 366 } |
| 374 | 367 |
| 375 std::string | 368 std::string |
| 376 MessageCenterNotificationManager::GetExtensionTakingOverNotifications( | 369 MessageCenterNotificationManager::GetExtensionTakingOverNotifications( |
| 377 Profile* profile) { | 370 Profile* profile) { |
| 378 // TODO(liyanhou): When additional settings in Chrome Settings is implemented, | 371 // TODO(liyanhou): When additional settings in Chrome Settings is implemented, |
| 379 // change choosing the last app with permission to a user selected app. | 372 // change choosing the last app with permission to a user selected app. |
| 380 extensions::ExtensionRegistry* registry = | 373 extensions::ExtensionRegistry* registry = |
| 381 extensions::ExtensionRegistry::Get(profile); | 374 extensions::ExtensionRegistry::Get(profile); |
| 382 DCHECK(registry); | 375 DCHECK(registry); |
| 383 std::string extension_id; | 376 std::string extension_id; |
| 384 for (extensions::ExtensionSet::const_iterator it = | 377 for (extensions::ExtensionSet::const_iterator it = |
| 385 registry->enabled_extensions().begin(); | 378 registry->enabled_extensions().begin(); |
| 386 it != registry->enabled_extensions().end(); | 379 it != registry->enabled_extensions().end(); |
| 387 ++it) { | 380 ++it) { |
| 388 if ((*it->get()).permissions_data()->HasAPIPermission( | 381 if ((*it->get()).permissions_data()->HasAPIPermission( |
| 389 extensions::APIPermission::ID::kNotificationProvider)) { | 382 extensions::APIPermission::ID::kNotificationProvider)) { |
| 390 extension_id = (*it->get()).id(); | 383 extension_id = (*it->get()).id(); |
| 391 return extension_id; | 384 return extension_id; |
| 392 } | 385 } |
| 393 } | 386 } |
| 394 return extension_id; | 387 return extension_id; |
| 395 } | 388 } |
| OLD | NEW |