Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 #import "chrome/browser/ui/cocoa/download/download_shelf_controller.h" | 5 #import "chrome/browser/ui/cocoa/download/download_shelf_controller.h" |
| 6 | 6 |
| 7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
| 8 | 8 |
| 9 #include <memory> | 9 #include <memory> |
| 10 #include <utility> | 10 #include <utility> |
| 11 | 11 |
| 12 #import "base/mac/scoped_block.h" | 12 #import "base/mac/scoped_block.h" |
| 13 #import "base/mac/scoped_nsobject.h" | 13 #import "base/mac/scoped_nsobject.h" |
| 14 #include "base/run_loop.h" | |
| 15 #include "chrome/browser/download/download_shelf.h" | 14 #include "chrome/browser/download/download_shelf.h" |
| 16 #import "chrome/browser/ui/cocoa/download/download_item_controller.h" | 15 #import "chrome/browser/ui/cocoa/download/download_item_controller.h" |
| 17 #include "chrome/browser/ui/cocoa/test/cocoa_profile_test.h" | 16 #include "chrome/browser/ui/cocoa/test/cocoa_profile_test.h" |
| 18 #import "chrome/browser/ui/cocoa/view_resizer_pong.h" | 17 #import "chrome/browser/ui/cocoa/view_resizer_pong.h" |
| 19 #include "content/public/test/mock_download_item.h" | 18 #include "content/public/test/mock_download_item.h" |
| 20 #include "testing/gtest/include/gtest/gtest.h" | 19 #include "testing/gtest/include/gtest/gtest.h" |
| 21 #include "testing/platform_test.h" | 20 #include "testing/platform_test.h" |
| 22 #import "third_party/ocmock/OCMock/OCMock.h" | 21 #import "third_party/ocmock/OCMock/OCMock.h" |
| 23 #import "third_party/ocmock/gtest_support.h" | 22 #import "third_party/ocmock/gtest_support.h" |
| 24 #import "ui/events/test/cocoa_test_event_utils.h" | 23 #import "ui/events/test/cocoa_test_event_utils.h" |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 164 andReturn:wrappedMockDownload.get()] wrappedMockDownload]; | 163 andReturn:wrappedMockDownload.get()] wrappedMockDownload]; |
| 165 return [item_controller retain]; | 164 return [item_controller retain]; |
| 166 } | 165 } |
| 167 | 166 |
| 168 TEST_VIEW(DownloadShelfControllerTest, [shelf_ view]); | 167 TEST_VIEW(DownloadShelfControllerTest, [shelf_ view]); |
| 169 | 168 |
| 170 // Removing the last download from the shelf should cause it to close | 169 // Removing the last download from the shelf should cause it to close |
| 171 // immediately. | 170 // immediately. |
| 172 TEST_F(DownloadShelfControllerTest, AddAndRemoveDownload) { | 171 TEST_F(DownloadShelfControllerTest, AddAndRemoveDownload) { |
| 173 base::scoped_nsobject<DownloadItemController> item(CreateItemController()); | 172 base::scoped_nsobject<DownloadItemController> item(CreateItemController()); |
| 174 [shelf_ showDownloadShelf:YES | 173 [shelf_ showDownloadShelf:YES isUserAction:NO animate:NO]; |
| 175 isUserAction:NO]; | |
| 176 EXPECT_TRUE([shelf_ isVisible]); | 174 EXPECT_TRUE([shelf_ isVisible]); |
| 177 EXPECT_TRUE([shelf_ bridge]->IsShowing()); | 175 EXPECT_TRUE([shelf_ bridge]->IsShowing()); |
| 178 [shelf_ add:item]; | 176 [shelf_ add:item]; |
| 179 [shelf_ remove:item]; | 177 [shelf_ remove:item]; |
| 180 EXPECT_FALSE([shelf_ isVisible]); | 178 EXPECT_FALSE([shelf_ isVisible]); |
| 181 EXPECT_FALSE([shelf_ bridge]->IsShowing()); | 179 EXPECT_FALSE([shelf_ bridge]->IsShowing()); |
| 182 // The shelf should be closed without scheduling an autoClose. | 180 // The shelf should be closed without scheduling an autoClose. |
| 183 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_); | 181 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_); |
| 184 } | 182 } |
| 185 | 183 |
| 186 // Test that the shelf doesn't close automatically after a removal if there are | 184 // Test that the shelf doesn't close automatically after a removal if there are |
| 187 // active download items still on the shelf. | 185 // active download items still on the shelf. |
| 188 TEST_F(DownloadShelfControllerTest, AddAndRemoveWithActiveItem) { | 186 TEST_F(DownloadShelfControllerTest, AddAndRemoveWithActiveItem) { |
| 189 base::scoped_nsobject<DownloadItemController> item1(CreateItemController()); | 187 base::scoped_nsobject<DownloadItemController> item1(CreateItemController()); |
| 190 base::scoped_nsobject<DownloadItemController> item2(CreateItemController()); | 188 base::scoped_nsobject<DownloadItemController> item2(CreateItemController()); |
| 191 [shelf_ showDownloadShelf:YES | 189 [shelf_ showDownloadShelf:YES isUserAction:NO animate:NO]; |
| 192 isUserAction:NO]; | |
| 193 EXPECT_TRUE([shelf_ isVisible]); | 190 EXPECT_TRUE([shelf_ isVisible]); |
| 194 [shelf_ add:item1.get()]; | 191 [shelf_ add:item1.get()]; |
| 195 [shelf_ add:item2.get()]; | 192 [shelf_ add:item2.get()]; |
| 196 [shelf_ remove:item1.get()]; | 193 [shelf_ remove:item1.get()]; |
| 197 EXPECT_TRUE([shelf_ isVisible]); | 194 EXPECT_TRUE([shelf_ isVisible]); |
| 198 [shelf_ remove:item2.get()]; | 195 [shelf_ remove:item2.get()]; |
| 199 EXPECT_FALSE([shelf_ isVisible]); | 196 EXPECT_FALSE([shelf_ isVisible]); |
| 200 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_); | 197 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_); |
| 201 } | 198 } |
| 202 | 199 |
| 203 // DownloadShelf::Unhide() should cause the shelf to be displayed if there are | 200 // DownloadShelf::Unhide() should cause the shelf to be displayed if there are |
| 204 // active downloads on it. | 201 // active downloads on it. |
| 205 TEST_F(DownloadShelfControllerTest, HideAndUnhide) { | 202 TEST_F(DownloadShelfControllerTest, HideAndUnhide) { |
| 206 base::scoped_nsobject<DownloadItemController> item(CreateItemController()); | 203 base::scoped_nsobject<DownloadItemController> item(CreateItemController()); |
| 207 [shelf_ showDownloadShelf:YES | 204 [shelf_ showDownloadShelf:YES isUserAction:NO animate:NO]; |
| 208 isUserAction:NO]; | |
| 209 EXPECT_TRUE([shelf_ isVisible]); | 205 EXPECT_TRUE([shelf_ isVisible]); |
| 210 [shelf_ add:item.get()]; | 206 [shelf_ add:item.get()]; |
| 211 [shelf_ bridge]->Hide(); | 207 [shelf_ bridge]->Hide(); |
| 212 EXPECT_FALSE([shelf_ isVisible]); | 208 EXPECT_FALSE([shelf_ isVisible]); |
| 213 [shelf_ bridge]->Unhide(); | 209 [shelf_ bridge]->Unhide(); |
| 214 EXPECT_TRUE([shelf_ isVisible]); | 210 EXPECT_TRUE([shelf_ isVisible]); |
| 215 [shelf_ remove:item.get()]; | 211 [shelf_ remove:item.get()]; |
| 216 EXPECT_FALSE([shelf_ isVisible]); | 212 EXPECT_FALSE([shelf_ isVisible]); |
| 217 } | 213 } |
| 218 | 214 |
| 219 // DownloadShelf::Unhide() shouldn't cause the shelf to be displayed if all | 215 // DownloadShelf::Unhide() shouldn't cause the shelf to be displayed if all |
| 220 // active downloads are removed from the shelf while the shelf was hidden. | 216 // active downloads are removed from the shelf while the shelf was hidden. |
| 221 TEST_F(DownloadShelfControllerTest, HideAutocloseUnhide) { | 217 TEST_F(DownloadShelfControllerTest, HideAutocloseUnhide) { |
| 222 base::scoped_nsobject<DownloadItemController> item(CreateItemController()); | 218 base::scoped_nsobject<DownloadItemController> item(CreateItemController()); |
| 223 [shelf_ showDownloadShelf:YES | 219 [shelf_ showDownloadShelf:YES isUserAction:NO animate:NO]; |
| 224 isUserAction:NO]; | |
| 225 EXPECT_TRUE([shelf_ isVisible]); | 220 EXPECT_TRUE([shelf_ isVisible]); |
| 226 [shelf_ add:item.get()]; | 221 [shelf_ add:item.get()]; |
| 227 [shelf_ bridge]->Hide(); | 222 [shelf_ bridge]->Hide(); |
| 228 EXPECT_FALSE([shelf_ isVisible]); | 223 EXPECT_FALSE([shelf_ isVisible]); |
| 229 [shelf_ remove:item.get()]; | 224 [shelf_ remove:item.get()]; |
| 230 EXPECT_FALSE([shelf_ isVisible]); | 225 EXPECT_FALSE([shelf_ isVisible]); |
| 231 [shelf_ bridge]->Unhide(); | 226 [shelf_ bridge]->Unhide(); |
| 232 EXPECT_FALSE([shelf_ isVisible]); | 227 EXPECT_FALSE([shelf_ isVisible]); |
| 233 } | 228 } |
| 234 | 229 |
| 235 // Test of autoclosing behavior after opening a download item. The mouse is on | 230 // Test of autoclosing behavior after opening a download item. The mouse is on |
| 236 // the download shelf at the time the autoclose is scheduled. | 231 // the download shelf at the time the autoclose is scheduled. |
| 237 TEST_F(DownloadShelfControllerTest, AutoCloseAfterOpenWithMouseInShelf) { | 232 TEST_F(DownloadShelfControllerTest, AutoCloseAfterOpenWithMouseInShelf) { |
| 238 base::scoped_nsobject<DownloadItemController> item(CreateItemController()); | 233 base::scoped_nsobject<DownloadItemController> item(CreateItemController()); |
| 239 [shelf_ showDownloadShelf:YES | 234 [shelf_ showDownloadShelf:YES isUserAction:NO animate:NO]; |
| 240 isUserAction:NO]; | |
| 241 EXPECT_TRUE([shelf_ isVisible]); | 235 EXPECT_TRUE([shelf_ isVisible]); |
| 242 [shelf_ add:item.get()]; | 236 [shelf_ add:item.get()]; |
| 243 // Expect 2 cancelAutoClose calls: From the showDownloadShelf: call and the | 237 // Expect 2 cancelAutoClose calls: From the showDownloadShelf: call and the |
| 244 // add: call. | 238 // add: call. |
| 245 EXPECT_EQ(2, shelf_.get()->cancelAutoCloseCount_); | 239 EXPECT_EQ(2, shelf_.get()->cancelAutoCloseCount_); |
| 246 shelf_.get()->cancelAutoCloseCount_ = 0; | 240 shelf_.get()->cancelAutoCloseCount_ = 0; |
| 247 | 241 |
| 248 // The mouse enters the shelf. | 242 // The mouse enters the shelf. |
| 249 [shelf_ mouseEntered:cocoa_test_event_utils::EnterEvent()]; | 243 [shelf_ mouseEntered:cocoa_test_event_utils::EnterEvent()]; |
| 250 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_); | 244 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_); |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 266 | 260 |
| 267 // The mouse enters the shelf again. The autoClose should be cancelled. | 261 // The mouse enters the shelf again. The autoClose should be cancelled. |
| 268 [shelf_ mouseEntered:cocoa_test_event_utils::EnterEvent()]; | 262 [shelf_ mouseEntered:cocoa_test_event_utils::EnterEvent()]; |
| 269 EXPECT_EQ(1, shelf_.get()->scheduleAutoCloseCount_); | 263 EXPECT_EQ(1, shelf_.get()->scheduleAutoCloseCount_); |
| 270 EXPECT_EQ(1, shelf_.get()->cancelAutoCloseCount_); | 264 EXPECT_EQ(1, shelf_.get()->cancelAutoCloseCount_); |
| 271 } | 265 } |
| 272 | 266 |
| 273 // Test of autoclosing behavior after opening a download item. | 267 // Test of autoclosing behavior after opening a download item. |
| 274 TEST_F(DownloadShelfControllerTest, AutoCloseAfterOpenWithMouseOffShelf) { | 268 TEST_F(DownloadShelfControllerTest, AutoCloseAfterOpenWithMouseOffShelf) { |
| 275 base::scoped_nsobject<DownloadItemController> item(CreateItemController()); | 269 base::scoped_nsobject<DownloadItemController> item(CreateItemController()); |
| 276 [shelf_ showDownloadShelf:YES | 270 [shelf_ showDownloadShelf:YES isUserAction:NO animate:NO]; |
| 277 isUserAction:NO]; | |
| 278 EXPECT_TRUE([shelf_ isVisible]); | 271 EXPECT_TRUE([shelf_ isVisible]); |
| 279 [shelf_ add:item.get()]; | 272 [shelf_ add:item.get()]; |
| 280 | 273 |
| 281 // The download is opened. | 274 // The download is opened. |
| 282 EXPECT_CALL(*[[item wrappedMockDownload] mockDownload], GetOpened()) | 275 EXPECT_CALL(*[[item wrappedMockDownload] mockDownload], GetOpened()) |
| 283 .WillRepeatedly(Return(true)); | 276 .WillRepeatedly(Return(true)); |
| 284 [shelf_ downloadWasOpened:item.get()]; | 277 [shelf_ downloadWasOpened:item.get()]; |
| 285 | 278 |
| 286 // The shelf should be closed immediately since the mouse is not over the | 279 // The shelf should be closed immediately since the mouse is not over the |
| 287 // shelf. | 280 // shelf. |
| 288 EXPECT_FALSE([shelf_ isVisible]); | 281 EXPECT_FALSE([shelf_ isVisible]); |
| 289 } | 282 } |
| 290 | 283 |
| 291 // Test that if the shelf is closed while an autoClose is pending, the pending | 284 // Test that if the shelf is closed while an autoClose is pending, the pending |
| 292 // autoClose is cancelled. | 285 // autoClose is cancelled. |
| 293 TEST_F(DownloadShelfControllerTest, CloseWithPendingAutoClose) { | 286 TEST_F(DownloadShelfControllerTest, CloseWithPendingAutoClose) { |
| 294 base::scoped_nsobject<DownloadItemController> item(CreateItemController()); | 287 base::scoped_nsobject<DownloadItemController> item(CreateItemController()); |
| 295 [shelf_ showDownloadShelf:YES | 288 [shelf_ showDownloadShelf:YES isUserAction:NO animate:NO]; |
| 296 isUserAction:NO]; | |
| 297 EXPECT_TRUE([shelf_ isVisible]); | 289 EXPECT_TRUE([shelf_ isVisible]); |
| 298 [shelf_ add:item.get()]; | 290 [shelf_ add:item.get()]; |
| 299 // Expect 2 cancelAutoClose calls: From the showDownloadShelf: call and the | 291 // Expect 2 cancelAutoClose calls: From the showDownloadShelf: call and the |
| 300 // add: call. | 292 // add: call. |
| 301 EXPECT_EQ(2, shelf_.get()->cancelAutoCloseCount_); | 293 EXPECT_EQ(2, shelf_.get()->cancelAutoCloseCount_); |
| 302 shelf_.get()->cancelAutoCloseCount_ = 0; | 294 shelf_.get()->cancelAutoCloseCount_ = 0; |
| 303 | 295 |
| 304 // The mouse enters the shelf. | 296 // The mouse enters the shelf. |
| 305 [shelf_ mouseEntered:cocoa_test_event_utils::EnterEvent()]; | 297 [shelf_ mouseEntered:cocoa_test_event_utils::EnterEvent()]; |
| 306 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_); | 298 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_); |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 326 [shelf_ remove:item]; | 318 [shelf_ remove:item]; |
| 327 EXPECT_EQ(1, shelf_.get()->scheduleAutoCloseCount_); | 319 EXPECT_EQ(1, shelf_.get()->scheduleAutoCloseCount_); |
| 328 EXPECT_EQ(1, shelf_.get()->cancelAutoCloseCount_); | 320 EXPECT_EQ(1, shelf_.get()->cancelAutoCloseCount_); |
| 329 EXPECT_FALSE([shelf_ isVisible]); | 321 EXPECT_FALSE([shelf_ isVisible]); |
| 330 } | 322 } |
| 331 | 323 |
| 332 // That that the shelf cancels a pending autoClose if a new download item is | 324 // That that the shelf cancels a pending autoClose if a new download item is |
| 333 // added to it. | 325 // added to it. |
| 334 TEST_F(DownloadShelfControllerTest, AddItemWithPendingAutoClose) { | 326 TEST_F(DownloadShelfControllerTest, AddItemWithPendingAutoClose) { |
| 335 base::scoped_nsobject<DownloadItemController> item(CreateItemController()); | 327 base::scoped_nsobject<DownloadItemController> item(CreateItemController()); |
| 336 [shelf_ showDownloadShelf:YES | 328 [shelf_ showDownloadShelf:YES isUserAction:NO animate:NO]; |
| 337 isUserAction:NO]; | |
| 338 EXPECT_TRUE([shelf_ isVisible]); | 329 EXPECT_TRUE([shelf_ isVisible]); |
| 339 [shelf_ add:item.get()]; | 330 [shelf_ add:item.get()]; |
| 340 // Expect 2 cancelAutoClose calls: From the showDownloadShelf: call and the | 331 // Expect 2 cancelAutoClose calls: From the showDownloadShelf: call and the |
| 341 // add: call. | 332 // add: call. |
| 342 EXPECT_EQ(2, shelf_.get()->cancelAutoCloseCount_); | 333 EXPECT_EQ(2, shelf_.get()->cancelAutoCloseCount_); |
| 343 shelf_.get()->cancelAutoCloseCount_ = 0; | 334 shelf_.get()->cancelAutoCloseCount_ = 0; |
| 344 | 335 |
| 345 // The mouse enters the shelf. | 336 // The mouse enters the shelf. |
| 346 [shelf_ mouseEntered:cocoa_test_event_utils::EnterEvent()]; | 337 [shelf_ mouseEntered:cocoa_test_event_utils::EnterEvent()]; |
| 347 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_); | 338 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_); |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 366 base::scoped_nsobject<DownloadItemController> item2(CreateItemController()); | 357 base::scoped_nsobject<DownloadItemController> item2(CreateItemController()); |
| 367 [shelf_ add:item.get()]; | 358 [shelf_ add:item.get()]; |
| 368 EXPECT_EQ(1, shelf_.get()->scheduleAutoCloseCount_); | 359 EXPECT_EQ(1, shelf_.get()->scheduleAutoCloseCount_); |
| 369 EXPECT_EQ(1, shelf_.get()->cancelAutoCloseCount_); | 360 EXPECT_EQ(1, shelf_.get()->cancelAutoCloseCount_); |
| 370 EXPECT_TRUE([shelf_ isVisible]); | 361 EXPECT_TRUE([shelf_ isVisible]); |
| 371 } | 362 } |
| 372 | 363 |
| 373 // Test that pending autoClose calls are cancelled when exiting. | 364 // Test that pending autoClose calls are cancelled when exiting. |
| 374 TEST_F(DownloadShelfControllerTest, CancelAutoCloseOnExit) { | 365 TEST_F(DownloadShelfControllerTest, CancelAutoCloseOnExit) { |
| 375 base::scoped_nsobject<DownloadItemController> item(CreateItemController()); | 366 base::scoped_nsobject<DownloadItemController> item(CreateItemController()); |
| 376 [shelf_ showDownloadShelf:YES | 367 [shelf_ showDownloadShelf:YES isUserAction:NO animate:NO]; |
| 377 isUserAction:NO]; | |
| 378 EXPECT_TRUE([shelf_ isVisible]); | 368 EXPECT_TRUE([shelf_ isVisible]); |
| 379 [shelf_ add:item.get()]; | 369 [shelf_ add:item.get()]; |
| 380 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_); | 370 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_); |
| 381 EXPECT_EQ(2, shelf_.get()->cancelAutoCloseCount_); | 371 EXPECT_EQ(2, shelf_.get()->cancelAutoCloseCount_); |
| 382 | 372 |
| 383 [shelf_ browserWillBeDestroyed]; | 373 [shelf_ browserWillBeDestroyed]; |
| 384 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_); | 374 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_); |
| 385 EXPECT_EQ(3, shelf_.get()->cancelAutoCloseCount_); | 375 EXPECT_EQ(3, shelf_.get()->cancelAutoCloseCount_); |
| 386 shelf_.reset(); | 376 shelf_.reset(); |
| 387 } | 377 } |
| 388 | 378 |
| 389 // The view should not be hidden when the shelf is shown. | 379 // The view should not be hidden when the shelf is open. |
| 390 // The view should be hidden after the closing animation. | 380 // The view should be hidden when the shelf is closed. |
| 391 // Failing flakily on Mac 10.9, see: crbug.com/687447. | 381 TEST_F(DownloadShelfControllerTest, ViewVisibility) { |
| 392 TEST_F(DownloadShelfControllerTest, DISABLED_ViewVisibility) { | 382 [shelf_ showDownloadShelf:YES isUserAction:NO animate:NO]; |
| 393 [shelf_ showDownloadShelf:YES isUserAction:NO]; | |
| 394 EXPECT_FALSE([[shelf_ view] isHidden]); | 383 EXPECT_FALSE([[shelf_ view] isHidden]); |
| 395 | 384 |
| 396 base::RunLoop run_loop; | 385 [shelf_ showDownloadShelf:NO isUserAction:NO animate:NO]; |
| 397 base::RunLoop* const run_loop_ptr = &run_loop; | |
| 398 | |
| 399 [shelf_ setCloseAnimationHandler:^{ | |
| 400 run_loop_ptr->Quit(); | |
| 401 }]; | |
| 402 [shelf_ showDownloadShelf:NO isUserAction:NO]; | |
| 403 run_loop.Run(); | |
| 404 EXPECT_TRUE([[shelf_ view] isHidden]); | 386 EXPECT_TRUE([[shelf_ view] isHidden]); |
| 405 | 387 |
| 406 [shelf_ showDownloadShelf:YES isUserAction:NO]; | 388 [shelf_ showDownloadShelf:YES isUserAction:NO animate:NO]; |
| 407 EXPECT_FALSE([[shelf_ view] isHidden]); | 389 EXPECT_FALSE([[shelf_ view] isHidden]); |
| 408 } | 390 } |
|
Nico
2017/02/17 15:53:46
i think we have 0 coverage for animate:YES
Sidney San Martín
2017/02/17 16:07:08
This is true. Unfortunately the only test which ac
Nico
2017/02/17 16:13:09
:-/
| |
| 409 | 391 |
| 410 } // namespace | 392 } // namespace |
| OLD | NEW |