| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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 <Cocoa/Cocoa.h> | 5 #import <Cocoa/Cocoa.h> |
| 6 #include <stdint.h> | 6 #include <stdint.h> |
| 7 | 7 |
| 8 #include <memory> | 8 #include <memory> |
| 9 | 9 |
| 10 #include "base/mac/scoped_cftyperef.h" | 10 #include "base/mac/scoped_cftyperef.h" |
| 11 #import "base/mac/scoped_objc_class_swizzler.h" | |
| 12 #include "base/mac/sdk_forward_declarations.h" | 11 #include "base/mac/sdk_forward_declarations.h" |
| 13 #include "base/macros.h" | 12 #include "base/macros.h" |
| 14 #include "testing/gtest/include/gtest/gtest.h" | 13 #include "testing/gtest/include/gtest/gtest.h" |
| 15 #include "ui/events/event_constants.h" | 14 #include "ui/events/event_constants.h" |
| 16 #include "ui/events/event_utils.h" | 15 #include "ui/events/event_utils.h" |
| 17 #import "ui/events/test/cocoa_test_event_utils.h" | 16 #import "ui/events/test/cocoa_test_event_utils.h" |
| 18 #include "ui/gfx/geometry/point.h" | 17 #include "ui/gfx/geometry/point.h" |
| 19 #import "ui/gfx/test/ui_cocoa_test_helper.h" | 18 #import "ui/gfx/test/ui_cocoa_test_helper.h" |
| 20 | 19 |
| 21 namespace { | |
| 22 | |
| 23 NSWindow* g_test_window = nil; | |
| 24 | |
| 25 } // namespace | |
| 26 | |
| 27 // Mac APIs for creating test events are frustrating. Quartz APIs have, e.g., | |
| 28 // CGEventCreateMouseEvent() which can't set a window or modifier flags. | |
| 29 // Cocoa APIs have +[NSEvent mouseEventWithType:..] which can't set | |
| 30 // buttonNumber or scroll deltas. To work around this, these tests use some | |
| 31 // Objective C magic to donate member functions to NSEvent temporarily. | |
| 32 @interface MiddleMouseButtonNumberDonor : NSObject | |
| 33 @end | |
| 34 | |
| 35 @interface TestWindowDonor : NSObject | |
| 36 @end | |
| 37 | |
| 38 @implementation MiddleMouseButtonNumberDonor | |
| 39 - (NSInteger)buttonNumber { return 2; } | |
| 40 @end | |
| 41 | |
| 42 @implementation TestWindowDonor | |
| 43 - (NSWindow*)window { return g_test_window; } | |
| 44 @end | |
| 45 | |
| 46 namespace ui { | 20 namespace ui { |
| 47 | 21 |
| 48 namespace { | 22 namespace { |
| 49 | 23 |
| 24 // Although CGEventFlags is just a typedef to int in 10.10 and earlier headers, |
| 25 // the 10.11 header makes this a CF_ENUM, but doesn't give an option for "none". |
| 26 const CGEventFlags kNoEventFlags = static_cast<CGEventFlags>(0); |
| 27 |
| 50 class EventsMacTest : public CocoaTest { | 28 class EventsMacTest : public CocoaTest { |
| 51 public: | 29 public: |
| 52 EventsMacTest() {} | 30 EventsMacTest() {} |
| 53 | 31 |
| 54 gfx::Point Flip(gfx::Point window_location) { | 32 gfx::Point Flip(gfx::Point window_location) { |
| 55 NSRect window_frame = [test_window() frame]; | 33 NSRect window_frame = [test_window() frame]; |
| 56 CGFloat content_height = | 34 CGFloat content_height = |
| 57 NSHeight([test_window() contentRectForFrameRect:window_frame]); | 35 NSHeight([test_window() contentRectForFrameRect:window_frame]); |
| 58 window_location.set_y(content_height - window_location.y()); | 36 window_location.set_y(content_height - window_location.y()); |
| 59 return window_location; | 37 return window_location; |
| 60 } | 38 } |
| 61 | 39 |
| 62 void SwizzleMiddleMouseButton() { | 40 NSEvent* TestMouseEvent(CGEventType type, |
| 63 DCHECK(!swizzler_); | 41 const gfx::Point& window_location, |
| 64 swizzler_.reset(new base::mac::ScopedObjCClassSwizzler( | 42 CGEventFlags event_flags) { |
| 65 [NSEvent class], | 43 // CGEventCreateMouseEvent() ignores the CGMouseButton parameter unless |
| 66 [MiddleMouseButtonNumberDonor class], | 44 // |type| is one of kCGEventOtherMouse{Up,Down,Dragged}. It can be an |
| 67 @selector(buttonNumber))); | 45 // integer up to 31. However, constants are only supplied up to 2. For now, |
| 68 } | 46 // just assume "other" means the third/center mouse button, and rely on |
| 69 | 47 // Quartz ignoring it when the type is not "other". |
| 70 void SwizzleTestWindow() { | 48 CGMouseButton other_button = kCGMouseButtonCenter; |
| 71 DCHECK(!g_test_window); | 49 base::ScopedCFTypeRef<CGEventRef> mouse(CGEventCreateMouseEvent( |
| 72 DCHECK(!swizzler_); | 50 nullptr, type, TestWindowPointToScreen(window_location), other_button)); |
| 73 g_test_window = test_window(); | 51 CGEventSetFlags(mouse, event_flags); |
| 74 swizzler_.reset(new base::mac::ScopedObjCClassSwizzler( | 52 return EventWithTestWindow(mouse); |
| 75 [NSEvent class], [TestWindowDonor class], @selector(window))); | |
| 76 } | |
| 77 | |
| 78 void ClearSwizzle() { | |
| 79 swizzler_.reset(); | |
| 80 g_test_window = nil; | |
| 81 } | |
| 82 | |
| 83 NSEvent* TestMouseEvent(NSEventType type, | |
| 84 const gfx::Point &window_location, | |
| 85 NSInteger modifier_flags) { | |
| 86 NSPoint point = NSPointFromCGPoint(Flip(window_location).ToCGPoint()); | |
| 87 return [NSEvent mouseEventWithType:type | |
| 88 location:point | |
| 89 modifierFlags:modifier_flags | |
| 90 timestamp:0 | |
| 91 windowNumber:[test_window() windowNumber] | |
| 92 context:nil | |
| 93 eventNumber:0 | |
| 94 clickCount:0 | |
| 95 pressure:1.0]; | |
| 96 } | 53 } |
| 97 | 54 |
| 98 NSEvent* TestScrollEvent(const gfx::Point& window_location, | 55 NSEvent* TestScrollEvent(const gfx::Point& window_location, |
| 99 int32_t delta_x, | 56 int32_t delta_x, |
| 100 int32_t delta_y) { | 57 int32_t delta_y) { |
| 101 SwizzleTestWindow(); | 58 base::ScopedCFTypeRef<CGEventRef> scroll(CGEventCreateScrollWheelEvent( |
| 102 base::ScopedCFTypeRef<CGEventRef> scroll( | 59 nullptr, kCGScrollEventUnitLine, 2, delta_y, delta_x)); |
| 103 CGEventCreateScrollWheelEvent(NULL, | 60 CGEventSetLocation(scroll, TestWindowPointToScreen(window_location)); |
| 104 kCGScrollEventUnitLine, | 61 return EventWithTestWindow(scroll); |
| 105 2, | 62 } |
| 106 delta_y, | 63 |
| 107 delta_x)); | 64 private: |
| 65 CGPoint TestWindowPointToScreen(const gfx::Point& window_location) { |
| 108 // CGEvents are always in global display coordinates. These are like screen | 66 // CGEvents are always in global display coordinates. These are like screen |
| 109 // coordinates, but flipped. But first the point needs to be converted out | 67 // coordinates, but flipped. But first the point needs to be converted out |
| 110 // of window coordinates (which also requires flipping). | 68 // of window coordinates (which also requires flipping). |
| 111 NSPoint window_point = | 69 NSPoint window_point = |
| 112 NSPointFromCGPoint(Flip(window_location).ToCGPoint()); | 70 NSPointFromCGPoint(Flip(window_location).ToCGPoint()); |
| 113 NSRect window_rect = NSMakeRect(window_point.x, window_point.y, 0, 0); | 71 NSRect window_rect = NSMakeRect(window_point.x, window_point.y, 0, 0); |
| 114 NSPoint screen_point = | 72 NSPoint screen_point = |
| 115 [test_window() convertRectToScreen:window_rect].origin; | 73 [test_window() convertRectToScreen:window_rect].origin; |
| 116 CGFloat primary_screen_height = | 74 CGFloat primary_screen_height = |
| 117 NSHeight([[[NSScreen screens] firstObject] frame]); | 75 NSHeight([[[NSScreen screens] firstObject] frame]); |
| 118 screen_point.y = primary_screen_height - screen_point.y; | 76 screen_point.y = primary_screen_height - screen_point.y; |
| 119 CGEventSetLocation(scroll, NSPointToCGPoint(screen_point)); | 77 return NSPointToCGPoint(screen_point); |
| 120 return [NSEvent eventWithCGEvent:scroll]; | |
| 121 } | 78 } |
| 122 | 79 |
| 123 private: | 80 NSEvent* EventWithTestWindow(CGEventRef event) { |
| 124 std::unique_ptr<base::mac::ScopedObjCClassSwizzler> swizzler_; | 81 // These CGEventFields were made public in the 10.7 SDK, but don't help to |
| 82 // populate the -[NSEvent window] pointer when creating an event with |
| 83 // +[NSEvent eventWithCGEvent:]. Set that separately, using reflection. |
| 84 CGEventSetIntegerValueField(event, kCGMouseEventWindowUnderMousePointer, |
| 85 [test_window() windowNumber]); |
| 86 CGEventSetIntegerValueField( |
| 87 event, kCGMouseEventWindowUnderMousePointerThatCanHandleThisEvent, |
| 88 [test_window() windowNumber]); |
| 89 NSEvent* ns_event = [NSEvent eventWithCGEvent:event]; |
| 90 EXPECT_EQ(nil, [ns_event window]); // Verify assumptions. |
| 91 [ns_event setValue:test_window() forKey:@"_window"]; |
| 92 EXPECT_EQ(test_window(), [ns_event window]); |
| 93 return ns_event; |
| 94 } |
| 125 | 95 |
| 126 DISALLOW_COPY_AND_ASSIGN(EventsMacTest); | 96 DISALLOW_COPY_AND_ASSIGN(EventsMacTest); |
| 127 }; | 97 }; |
| 128 | 98 |
| 129 } // namespace | 99 } // namespace |
| 130 | 100 |
| 131 TEST_F(EventsMacTest, EventFlagsFromNative) { | 101 TEST_F(EventsMacTest, EventFlagsFromNative) { |
| 132 // Left click. | 102 // Left click. |
| 133 NSEvent* left = cocoa_test_event_utils::MouseEventWithType(NSLeftMouseUp, 0); | 103 NSEvent* left = cocoa_test_event_utils::MouseEventWithType(NSLeftMouseUp, 0); |
| 134 EXPECT_EQ(EF_LEFT_MOUSE_BUTTON, EventFlagsFromNative(left)); | 104 EXPECT_EQ(EF_LEFT_MOUSE_BUTTON, EventFlagsFromNative(left)); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 185 NSLeftMouseUp, NSCommandKeyMask | NSAlternateKeyMask); | 155 NSLeftMouseUp, NSCommandKeyMask | NSAlternateKeyMask); |
| 186 EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_COMMAND_DOWN | EF_ALT_DOWN, | 156 EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_COMMAND_DOWN | EF_ALT_DOWN, |
| 187 EventFlagsFromNative(cmdalt)); | 157 EventFlagsFromNative(cmdalt)); |
| 188 } | 158 } |
| 189 | 159 |
| 190 // Tests mouse button presses and mouse wheel events. | 160 // Tests mouse button presses and mouse wheel events. |
| 191 TEST_F(EventsMacTest, ButtonEvents) { | 161 TEST_F(EventsMacTest, ButtonEvents) { |
| 192 gfx::Point location(5, 10); | 162 gfx::Point location(5, 10); |
| 193 gfx::Vector2d offset; | 163 gfx::Vector2d offset; |
| 194 | 164 |
| 195 NSEvent* event = TestMouseEvent(NSLeftMouseDown, location, 0); | 165 NSEvent* event = |
| 166 TestMouseEvent(kCGEventLeftMouseDown, location, kNoEventFlags); |
| 196 EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromNative(event)); | 167 EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromNative(event)); |
| 197 EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, ui::EventFlagsFromNative(event)); | 168 EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, ui::EventFlagsFromNative(event)); |
| 198 EXPECT_EQ(location, ui::EventLocationFromNative(event)); | 169 EXPECT_EQ(location, ui::EventLocationFromNative(event)); |
| 199 | 170 |
| 200 SwizzleMiddleMouseButton(); | 171 event = |
| 201 event = TestMouseEvent(NSOtherMouseDown, location, NSShiftKeyMask); | 172 TestMouseEvent(kCGEventOtherMouseDown, location, kCGEventFlagMaskShift); |
| 202 EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromNative(event)); | 173 EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromNative(event)); |
| 203 EXPECT_EQ(ui::EF_MIDDLE_MOUSE_BUTTON | ui::EF_SHIFT_DOWN, | 174 EXPECT_EQ(ui::EF_MIDDLE_MOUSE_BUTTON | ui::EF_SHIFT_DOWN, |
| 204 ui::EventFlagsFromNative(event)); | 175 ui::EventFlagsFromNative(event)); |
| 205 EXPECT_EQ(location, ui::EventLocationFromNative(event)); | 176 EXPECT_EQ(location, ui::EventLocationFromNative(event)); |
| 206 ClearSwizzle(); | |
| 207 | 177 |
| 208 event = TestMouseEvent(NSRightMouseUp, location, 0); | 178 event = TestMouseEvent(kCGEventRightMouseUp, location, kNoEventFlags); |
| 209 EXPECT_EQ(ui::ET_MOUSE_RELEASED, ui::EventTypeFromNative(event)); | 179 EXPECT_EQ(ui::ET_MOUSE_RELEASED, ui::EventTypeFromNative(event)); |
| 210 EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, ui::EventFlagsFromNative(event)); | 180 EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, ui::EventFlagsFromNative(event)); |
| 211 EXPECT_EQ(location, ui::EventLocationFromNative(event)); | 181 EXPECT_EQ(location, ui::EventLocationFromNative(event)); |
| 212 | 182 |
| 213 // Scroll up. | 183 // Scroll up. |
| 214 event = TestScrollEvent(location, 0, 1); | 184 event = TestScrollEvent(location, 0, 1); |
| 215 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event)); | 185 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event)); |
| 216 EXPECT_EQ(0, ui::EventFlagsFromNative(event)); | 186 EXPECT_EQ(0, ui::EventFlagsFromNative(event)); |
| 217 EXPECT_EQ(location.ToString(), ui::EventLocationFromNative(event).ToString()); | 187 EXPECT_EQ(location.ToString(), ui::EventLocationFromNative(event).ToString()); |
| 218 offset = ui::GetMouseWheelOffset(event); | 188 offset = ui::GetMouseWheelOffset(event); |
| 219 EXPECT_GT(offset.y(), 0); | 189 EXPECT_GT(offset.y(), 0); |
| 220 EXPECT_EQ(0, offset.x()); | 190 EXPECT_EQ(0, offset.x()); |
| 221 ClearSwizzle(); | |
| 222 | 191 |
| 223 // Scroll down. | 192 // Scroll down. |
| 224 event = TestScrollEvent(location, 0, -1); | 193 event = TestScrollEvent(location, 0, -1); |
| 225 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event)); | 194 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event)); |
| 226 EXPECT_EQ(0, ui::EventFlagsFromNative(event)); | 195 EXPECT_EQ(0, ui::EventFlagsFromNative(event)); |
| 227 EXPECT_EQ(location, ui::EventLocationFromNative(event)); | 196 EXPECT_EQ(location, ui::EventLocationFromNative(event)); |
| 228 offset = ui::GetMouseWheelOffset(event); | 197 offset = ui::GetMouseWheelOffset(event); |
| 229 EXPECT_LT(offset.y(), 0); | 198 EXPECT_LT(offset.y(), 0); |
| 230 EXPECT_EQ(0, offset.x()); | 199 EXPECT_EQ(0, offset.x()); |
| 231 ClearSwizzle(); | |
| 232 | 200 |
| 233 // Scroll left. | 201 // Scroll left. |
| 234 event = TestScrollEvent(location, 1, 0); | 202 event = TestScrollEvent(location, 1, 0); |
| 235 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event)); | 203 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event)); |
| 236 EXPECT_EQ(0, ui::EventFlagsFromNative(event)); | 204 EXPECT_EQ(0, ui::EventFlagsFromNative(event)); |
| 237 EXPECT_EQ(location, ui::EventLocationFromNative(event)); | 205 EXPECT_EQ(location, ui::EventLocationFromNative(event)); |
| 238 offset = ui::GetMouseWheelOffset(event); | 206 offset = ui::GetMouseWheelOffset(event); |
| 239 EXPECT_EQ(0, offset.y()); | 207 EXPECT_EQ(0, offset.y()); |
| 240 EXPECT_GT(offset.x(), 0); | 208 EXPECT_GT(offset.x(), 0); |
| 241 ClearSwizzle(); | |
| 242 | 209 |
| 243 // Scroll right. | 210 // Scroll right. |
| 244 event = TestScrollEvent(location, -1, 0); | 211 event = TestScrollEvent(location, -1, 0); |
| 245 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event)); | 212 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event)); |
| 246 EXPECT_EQ(0, ui::EventFlagsFromNative(event)); | 213 EXPECT_EQ(0, ui::EventFlagsFromNative(event)); |
| 247 EXPECT_EQ(location, ui::EventLocationFromNative(event)); | 214 EXPECT_EQ(location, ui::EventLocationFromNative(event)); |
| 248 offset = ui::GetMouseWheelOffset(event); | 215 offset = ui::GetMouseWheelOffset(event); |
| 249 EXPECT_EQ(0, offset.y()); | 216 EXPECT_EQ(0, offset.y()); |
| 250 EXPECT_LT(offset.x(), 0); | 217 EXPECT_LT(offset.x(), 0); |
| 251 ClearSwizzle(); | |
| 252 } | 218 } |
| 253 | 219 |
| 254 // Test correct location when the window has a native titlebar. | 220 // Test correct location when the window has a native titlebar. |
| 255 TEST_F(EventsMacTest, NativeTitlebarEventLocation) { | 221 TEST_F(EventsMacTest, NativeTitlebarEventLocation) { |
| 256 gfx::Point location(5, 10); | 222 gfx::Point location(5, 10); |
| 257 NSUInteger style_mask = NSTitledWindowMask | NSClosableWindowMask | | 223 NSUInteger style_mask = NSTitledWindowMask | NSClosableWindowMask | |
| 258 NSMiniaturizableWindowMask | NSResizableWindowMask; | 224 NSMiniaturizableWindowMask | NSResizableWindowMask; |
| 259 | 225 |
| 260 // First check that the window provided by ui::CocoaTest is how we think. | 226 // First check that the window provided by ui::CocoaTest is how we think. |
| 261 DCHECK_EQ(NSBorderlessWindowMask, [test_window() styleMask]); | 227 DCHECK_EQ(NSBorderlessWindowMask, [test_window() styleMask]); |
| 262 [test_window() setStyleMask:style_mask]; | 228 [test_window() setStyleMask:style_mask]; |
| 263 DCHECK_EQ(style_mask, [test_window() styleMask]); | 229 DCHECK_EQ(style_mask, [test_window() styleMask]); |
| 264 | 230 |
| 265 // EventLocationFromNative should behave the same as the ButtonEvents test. | 231 // EventLocationFromNative should behave the same as the ButtonEvents test. |
| 266 NSEvent* event = TestMouseEvent(NSLeftMouseDown, location, 0); | 232 NSEvent* event = |
| 233 TestMouseEvent(kCGEventLeftMouseDown, location, kNoEventFlags); |
| 267 EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromNative(event)); | 234 EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromNative(event)); |
| 268 EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, ui::EventFlagsFromNative(event)); | 235 EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, ui::EventFlagsFromNative(event)); |
| 269 EXPECT_EQ(location, ui::EventLocationFromNative(event)); | 236 EXPECT_EQ(location, ui::EventLocationFromNative(event)); |
| 270 | 237 |
| 271 // And be explicit, to ensure the test doesn't depend on some property of the | 238 // And be explicit, to ensure the test doesn't depend on some property of the |
| 272 // test harness. The change to the frame rect could be OS-specfic, so set it | 239 // test harness. The change to the frame rect could be OS-specfic, so set it |
| 273 // to a known value. | 240 // to a known value. |
| 274 const CGFloat kTestHeight = 400; | 241 const CGFloat kTestHeight = 400; |
| 275 NSRect content_rect = NSMakeRect(0, 0, 600, kTestHeight); | 242 NSRect content_rect = NSMakeRect(0, 0, 600, kTestHeight); |
| 276 NSRect frame_rect = [test_window() frameRectForContentRect:content_rect]; | 243 NSRect frame_rect = [test_window() frameRectForContentRect:content_rect]; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 315 event = cocoa_test_event_utils::MouseEventWithType(NSMouseMoved, 0); | 282 event = cocoa_test_event_utils::MouseEventWithType(NSMouseMoved, 0); |
| 316 EXPECT_EQ(ui::ET_MOUSE_MOVED, ui::EventTypeFromNative(event)); | 283 EXPECT_EQ(ui::ET_MOUSE_MOVED, ui::EventTypeFromNative(event)); |
| 317 | 284 |
| 318 event = cocoa_test_event_utils::EnterEvent(); | 285 event = cocoa_test_event_utils::EnterEvent(); |
| 319 EXPECT_EQ(ui::ET_MOUSE_ENTERED, ui::EventTypeFromNative(event)); | 286 EXPECT_EQ(ui::ET_MOUSE_ENTERED, ui::EventTypeFromNative(event)); |
| 320 event = cocoa_test_event_utils::ExitEvent(); | 287 event = cocoa_test_event_utils::ExitEvent(); |
| 321 EXPECT_EQ(ui::ET_MOUSE_EXITED, ui::EventTypeFromNative(event)); | 288 EXPECT_EQ(ui::ET_MOUSE_EXITED, ui::EventTypeFromNative(event)); |
| 322 } | 289 } |
| 323 | 290 |
| 324 } // namespace ui | 291 } // namespace ui |
| OLD | NEW |