bridged_native_widget_unittest.mm 78 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987
  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.
  4. #include "base/memory/raw_ptr.h"
  5. #import "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h"
  6. #import <Cocoa/Cocoa.h>
  7. #include <objc/runtime.h>
  8. #include <memory>
  9. #include "base/bind.h"
  10. #import "base/mac/foundation_util.h"
  11. #import "base/mac/mac_util.h"
  12. #import "base/mac/scoped_objc_class_swizzler.h"
  13. #include "base/strings/stringprintf.h"
  14. #include "base/strings/sys_string_conversions.h"
  15. #include "base/strings/utf_string_conversions.h"
  16. #include "base/test/task_environment.h"
  17. #import "components/remote_cocoa/app_shim/bridged_content_view.h"
  18. #import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
  19. #import "components/remote_cocoa/app_shim/views_nswindow_delegate.h"
  20. #import "testing/gtest_mac.h"
  21. #include "ui/base/cocoa/find_pasteboard.h"
  22. #import "ui/base/cocoa/window_size_constants.h"
  23. #include "ui/base/ime/input_method.h"
  24. #import "ui/base/test/cocoa_helper.h"
  25. #include "ui/display/screen.h"
  26. #include "ui/events/test/cocoa_test_event_utils.h"
  27. #import "ui/gfx/mac/coordinate_conversion.h"
  28. #import "ui/views/cocoa/native_widget_mac_ns_window_host.h"
  29. #import "ui/views/cocoa/text_input_host.h"
  30. #include "ui/views/controls/textfield/textfield.h"
  31. #include "ui/views/controls/textfield/textfield_controller.h"
  32. #include "ui/views/controls/textfield/textfield_model.h"
  33. #include "ui/views/test/test_views_delegate.h"
  34. #include "ui/views/view.h"
  35. #include "ui/views/widget/native_widget_mac.h"
  36. #include "ui/views/widget/root_view.h"
  37. #include "ui/views/widget/widget.h"
  38. #include "ui/views/widget/widget_observer.h"
  39. using base::ASCIIToUTF16;
  40. using base::SysNSStringToUTF8;
  41. using base::SysNSStringToUTF16;
  42. using base::SysUTF8ToNSString;
  43. using base::SysUTF16ToNSString;
  44. #define EXPECT_EQ_RANGE(a, b) \
  45. EXPECT_EQ(a.location, b.location); \
  46. EXPECT_EQ(a.length, b.length);
  47. // Helpers to verify an expectation against both the actual toolkit-views
  48. // behaviour and the Cocoa behaviour.
  49. #define EXPECT_NSEQ_3(expected_literal, expected_cocoa, actual_views) \
  50. EXPECT_NSEQ(expected_literal, actual_views); \
  51. EXPECT_NSEQ(expected_cocoa, actual_views);
  52. #define EXPECT_EQ_RANGE_3(expected_literal, expected_cocoa, actual_views) \
  53. EXPECT_EQ_RANGE(expected_literal, actual_views); \
  54. EXPECT_EQ_RANGE(expected_cocoa, actual_views);
  55. #define EXPECT_EQ_3(expected_literal, expected_cocoa, actual_views) \
  56. EXPECT_EQ(expected_literal, actual_views); \
  57. EXPECT_EQ(expected_cocoa, actual_views);
  58. namespace {
  59. // Implemented NSResponder action messages for use in tests.
  60. NSArray* const kMoveActions = @[
  61. @"moveForward:",
  62. @"moveRight:",
  63. @"moveBackward:",
  64. @"moveLeft:",
  65. @"moveUp:",
  66. @"moveDown:",
  67. @"moveWordForward:",
  68. @"moveWordBackward:",
  69. @"moveToBeginningOfLine:",
  70. @"moveToEndOfLine:",
  71. @"moveToBeginningOfParagraph:",
  72. @"moveToEndOfParagraph:",
  73. @"moveToEndOfDocument:",
  74. @"moveToBeginningOfDocument:",
  75. @"pageDown:",
  76. @"pageUp:",
  77. @"moveWordRight:",
  78. @"moveWordLeft:",
  79. @"moveToLeftEndOfLine:",
  80. @"moveToRightEndOfLine:"
  81. ];
  82. NSArray* const kSelectActions = @[
  83. @"moveBackwardAndModifySelection:",
  84. @"moveForwardAndModifySelection:",
  85. @"moveWordForwardAndModifySelection:",
  86. @"moveWordBackwardAndModifySelection:",
  87. @"moveUpAndModifySelection:",
  88. @"moveDownAndModifySelection:",
  89. @"moveToBeginningOfLineAndModifySelection:",
  90. @"moveToEndOfLineAndModifySelection:",
  91. @"moveToBeginningOfParagraphAndModifySelection:",
  92. @"moveToEndOfParagraphAndModifySelection:",
  93. @"moveToEndOfDocumentAndModifySelection:",
  94. @"moveToBeginningOfDocumentAndModifySelection:",
  95. @"pageDownAndModifySelection:",
  96. @"pageUpAndModifySelection:",
  97. @"moveParagraphForwardAndModifySelection:",
  98. @"moveParagraphBackwardAndModifySelection:",
  99. @"moveRightAndModifySelection:",
  100. @"moveLeftAndModifySelection:",
  101. @"moveWordRightAndModifySelection:",
  102. @"moveWordLeftAndModifySelection:",
  103. @"moveToLeftEndOfLineAndModifySelection:",
  104. @"moveToRightEndOfLineAndModifySelection:"
  105. ];
  106. NSArray* const kDeleteActions = @[
  107. @"deleteForward:", @"deleteBackward:", @"deleteWordForward:",
  108. @"deleteWordBackward:", @"deleteToBeginningOfLine:", @"deleteToEndOfLine:",
  109. @"deleteToBeginningOfParagraph:", @"deleteToEndOfParagraph:"
  110. ];
  111. // This omits @"insertText:":. See BridgedNativeWidgetTest.NilTextInputClient.
  112. NSArray* const kMiscActions = @[ @"cancelOperation:", @"transpose:", @"yank:" ];
  113. // Empty range shortcut for readibility.
  114. NSRange EmptyRange() {
  115. return NSMakeRange(NSNotFound, 0);
  116. }
  117. // Sets |composition_text| as the composition text with caret placed at
  118. // |caret_pos| and updates |caret_range|.
  119. void SetCompositionText(ui::TextInputClient* client,
  120. const std::u16string& composition_text,
  121. const int caret_pos,
  122. NSRange* caret_range) {
  123. ui::CompositionText composition;
  124. composition.selection = gfx::Range(caret_pos);
  125. composition.text = composition_text;
  126. client->SetCompositionText(composition);
  127. if (caret_range)
  128. *caret_range = NSMakeRange(caret_pos, 0);
  129. }
  130. // Returns a zero width rectangle corresponding to current caret position.
  131. gfx::Rect GetCaretBounds(const ui::TextInputClient* client) {
  132. gfx::Rect caret_bounds = client->GetCaretBounds();
  133. caret_bounds.set_width(0);
  134. return caret_bounds;
  135. }
  136. // Returns a zero width rectangle corresponding to caret bounds when it's placed
  137. // at |caret_pos| and updates |caret_range|.
  138. gfx::Rect GetCaretBoundsForPosition(ui::TextInputClient* client,
  139. const std::u16string& composition_text,
  140. const int caret_pos,
  141. NSRange* caret_range) {
  142. SetCompositionText(client, composition_text, caret_pos, caret_range);
  143. return GetCaretBounds(client);
  144. }
  145. // Returns the expected boundary rectangle for characters of |composition_text|
  146. // within the |query_range|.
  147. gfx::Rect GetExpectedBoundsForRange(ui::TextInputClient* client,
  148. const std::u16string& composition_text,
  149. NSRange query_range) {
  150. gfx::Rect left_caret = GetCaretBoundsForPosition(
  151. client, composition_text, query_range.location, nullptr);
  152. gfx::Rect right_caret = GetCaretBoundsForPosition(
  153. client, composition_text, query_range.location + query_range.length,
  154. nullptr);
  155. // The expected bounds correspond to the area between the left and right caret
  156. // positions.
  157. return gfx::Rect(left_caret.x(), left_caret.y(),
  158. right_caret.x() - left_caret.x(), left_caret.height());
  159. }
  160. // Uses the NSTextInputClient protocol to extract a substring from |view|.
  161. NSString* GetViewStringForRange(NSView<NSTextInputClient>* view,
  162. NSRange range) {
  163. return [[view attributedSubstringForProposedRange:range actualRange:nullptr]
  164. string];
  165. }
  166. // The behavior of NSTextView for RTL strings is buggy for some move and select
  167. // commands, but only when the command is received when there is a selection
  168. // active. E.g. moveRight: moves a cursor right in an RTL string, but it moves
  169. // to the left-end of a selection. See TestEditingCommands() for specifics.
  170. // This is filed as rdar://27863290.
  171. bool IsRTLMoveBuggy(SEL sel) {
  172. return sel == @selector(moveWordRight:) || sel == @selector(moveWordLeft:) ||
  173. sel == @selector(moveRight:) || sel == @selector(moveLeft:);
  174. }
  175. bool IsRTLSelectBuggy(SEL sel) {
  176. return sel == @selector(moveWordRightAndModifySelection:) ||
  177. sel == @selector(moveWordLeftAndModifySelection:) ||
  178. sel == @selector(moveRightAndModifySelection:) ||
  179. sel == @selector(moveLeftAndModifySelection:);
  180. }
  181. // Used by InterpretKeyEventsDonorForNSView to simulate IME behavior.
  182. using InterpretKeyEventsCallback = base::RepeatingCallback<void(id)>;
  183. InterpretKeyEventsCallback* g_fake_interpret_key_events = nullptr;
  184. // Used by UpdateWindowsDonorForNSApp to hook -[NSApp updateWindows].
  185. base::RepeatingClosure* g_update_windows_closure = nullptr;
  186. // Used to provide a return value for +[NSTextInputContext currentInputContext].
  187. NSTextInputContext* g_fake_current_input_context = nullptr;
  188. } // namespace
  189. // Subclass of BridgedContentView with an override of interpretKeyEvents:. Note
  190. // the size of the class must match BridgedContentView since the method table
  191. // is swapped out at runtime. This is basically a mock, but mocks are banned
  192. // under ui/views. Method swizzling causes these tests to flake when
  193. // parallelized in the same process.
  194. @interface InterpretKeyEventMockedBridgedContentView : BridgedContentView
  195. @end
  196. @implementation InterpretKeyEventMockedBridgedContentView
  197. - (void)interpretKeyEvents:(NSArray<NSEvent*>*)eventArray {
  198. ASSERT_TRUE(g_fake_interpret_key_events);
  199. g_fake_interpret_key_events->Run(self);
  200. }
  201. @end
  202. @interface UpdateWindowsDonorForNSApp : NSApplication
  203. @end
  204. @implementation UpdateWindowsDonorForNSApp
  205. - (void)updateWindows {
  206. ASSERT_TRUE(g_update_windows_closure);
  207. g_update_windows_closure->Run();
  208. }
  209. @end
  210. @interface CurrentInputContextDonorForNSTextInputContext : NSTextInputContext
  211. @end
  212. @implementation CurrentInputContextDonorForNSTextInputContext
  213. + (NSTextInputContext*)currentInputContext {
  214. return g_fake_current_input_context;
  215. }
  216. @end
  217. // Let's not mess with the machine's actual find pasteboard!
  218. @interface MockFindPasteboard : FindPasteboard
  219. @end
  220. @implementation MockFindPasteboard {
  221. base::scoped_nsobject<NSString> _text;
  222. }
  223. + (FindPasteboard*)sharedInstance {
  224. static MockFindPasteboard* instance = nil;
  225. if (!instance) {
  226. instance = [[MockFindPasteboard alloc] init];
  227. }
  228. return instance;
  229. }
  230. - (instancetype)init {
  231. self = [super init];
  232. if (self) {
  233. _text.reset([[NSString alloc] init]);
  234. }
  235. return self;
  236. }
  237. - (void)loadTextFromPasteboard:(NSNotification*)notification {
  238. // No-op
  239. }
  240. - (void)setFindText:(NSString*)newText {
  241. _text.reset([newText copy]);
  242. }
  243. - (NSString*)findText {
  244. return _text;
  245. }
  246. @end
  247. namespace views {
  248. namespace test {
  249. // Provides the |parent| argument to construct a NativeWidgetNSWindowBridge.
  250. class MockNativeWidgetMac : public NativeWidgetMac {
  251. public:
  252. explicit MockNativeWidgetMac(internal::NativeWidgetDelegate* delegate)
  253. : NativeWidgetMac(delegate) {}
  254. MockNativeWidgetMac(const MockNativeWidgetMac&) = delete;
  255. MockNativeWidgetMac& operator=(const MockNativeWidgetMac&) = delete;
  256. using NativeWidgetMac::GetInProcessNSWindowBridge;
  257. using NativeWidgetMac::GetNSWindowHost;
  258. // internal::NativeWidgetPrivate:
  259. void InitNativeWidget(Widget::InitParams params) override {
  260. ownership_ = params.ownership;
  261. base::scoped_nsobject<NativeWidgetMacNSWindow> window(
  262. [[NativeWidgetMacNSWindow alloc]
  263. initWithContentRect:ui::kWindowSizeDeterminedLater
  264. styleMask:NSWindowStyleMaskBorderless
  265. backing:NSBackingStoreBuffered
  266. defer:NO]);
  267. GetNSWindowHost()->CreateInProcessNSWindowBridge(window);
  268. if (auto* parent =
  269. NativeWidgetMacNSWindowHost::GetFromNativeView(params.parent)) {
  270. GetNSWindowHost()->SetParent(parent);
  271. }
  272. GetNSWindowHost()->InitWindow(params,
  273. ConvertBoundsToScreenIfNeeded(params.bounds));
  274. // Usually the bridge gets initialized here. It is skipped to run extra
  275. // checks in tests, and so that a second window isn't created.
  276. delegate()->OnNativeWidgetCreated();
  277. // To allow events to dispatch to a view, it needs a way to get focus.
  278. SetFocusManager(GetWidget()->GetFocusManager());
  279. }
  280. void ReorderNativeViews() override {
  281. // Called via Widget::Init to set the content view. No-op in these tests.
  282. }
  283. };
  284. // Helper test base to construct a NativeWidgetNSWindowBridge with a valid
  285. // parent.
  286. class BridgedNativeWidgetTestBase : public ui::CocoaTest {
  287. public:
  288. struct SkipInitialization {};
  289. BridgedNativeWidgetTestBase()
  290. : widget_(new Widget),
  291. native_widget_mac_(new MockNativeWidgetMac(widget_.get())) {}
  292. explicit BridgedNativeWidgetTestBase(SkipInitialization tag)
  293. : native_widget_mac_(nullptr) {}
  294. BridgedNativeWidgetTestBase(const BridgedNativeWidgetTestBase&) = delete;
  295. BridgedNativeWidgetTestBase& operator=(const BridgedNativeWidgetTestBase&) =
  296. delete;
  297. remote_cocoa::NativeWidgetNSWindowBridge* bridge() {
  298. return native_widget_mac_->GetInProcessNSWindowBridge();
  299. }
  300. NativeWidgetMacNSWindowHost* GetNSWindowHost() {
  301. return native_widget_mac_->GetNSWindowHost();
  302. }
  303. // Generate an autoreleased KeyDown NSEvent* in |widget_| for pressing the
  304. // corresponding |key_code|.
  305. NSEvent* VkeyKeyDown(ui::KeyboardCode key_code) {
  306. return cocoa_test_event_utils::SynthesizeKeyEvent(
  307. widget_->GetNativeWindow().GetNativeNSWindow(), true /* keyDown */,
  308. key_code, 0);
  309. }
  310. // Generate an autoreleased KeyDown NSEvent* using the given keycode, and
  311. // representing the first unicode character of |chars|.
  312. NSEvent* UnicodeKeyDown(int key_code, NSString* chars) {
  313. return cocoa_test_event_utils::KeyEventWithKeyCode(
  314. key_code, [chars characterAtIndex:0], NSEventTypeKeyDown, 0);
  315. }
  316. // Overridden from testing::Test:
  317. void SetUp() override {
  318. ui::CocoaTest::SetUp();
  319. Widget::InitParams init_params;
  320. init_params.native_widget = native_widget_mac_.get();
  321. init_params.type = type_;
  322. init_params.ownership = ownership_;
  323. init_params.opacity = opacity_;
  324. init_params.bounds = bounds_;
  325. init_params.shadow_type = shadow_type_;
  326. if (native_widget_mac_)
  327. native_widget_mac_->GetWidget()->Init(std::move(init_params));
  328. }
  329. void TearDown() override {
  330. // ui::CocoaTest::TearDown will wait until all NSWindows are destroyed, so
  331. // be sure to destroy the widget (which will destroy its NSWindow)
  332. // beforehand.
  333. widget_.reset();
  334. ui::CocoaTest::TearDown();
  335. }
  336. NSWindow* bridge_window() const {
  337. if (auto* bridge = native_widget_mac_->GetInProcessNSWindowBridge())
  338. return bridge->ns_window();
  339. return nil;
  340. }
  341. protected:
  342. std::unique_ptr<Widget> widget_;
  343. raw_ptr<MockNativeWidgetMac> native_widget_mac_; // Weak. Owned by |widget_|.
  344. // Use a frameless window, otherwise Widget will try to center the window
  345. // before the tests covering the Init() flow are ready to do that.
  346. Widget::InitParams::Type type_ = Widget::InitParams::TYPE_WINDOW_FRAMELESS;
  347. // To control the lifetime without an actual window that must be closed,
  348. // tests in this file need to use WIDGET_OWNS_NATIVE_WIDGET.
  349. Widget::InitParams::Ownership ownership_ =
  350. Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  351. // Opacity defaults to "infer" which is usually updated by ViewsDelegate.
  352. Widget::InitParams::WindowOpacity opacity_ =
  353. Widget::InitParams::WindowOpacity::kOpaque;
  354. gfx::Rect bounds_ = gfx::Rect(100, 100, 100, 100);
  355. Widget::InitParams::ShadowType shadow_type_ =
  356. Widget::InitParams::ShadowType::kDefault;
  357. private:
  358. TestViewsDelegate test_views_delegate_;
  359. display::ScopedNativeScreen screen_;
  360. };
  361. class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase,
  362. public TextfieldController {
  363. public:
  364. using HandleKeyEventCallback =
  365. base::RepeatingCallback<bool(Textfield*, const ui::KeyEvent& key_event)>;
  366. BridgedNativeWidgetTest();
  367. BridgedNativeWidgetTest(const BridgedNativeWidgetTest&) = delete;
  368. BridgedNativeWidgetTest& operator=(const BridgedNativeWidgetTest&) = delete;
  369. ~BridgedNativeWidgetTest() override;
  370. // Install a textfield with input type |text_input_type| in the view hierarchy
  371. // and make it the text input client. Also initializes |dummy_text_view_|.
  372. Textfield* InstallTextField(
  373. const std::u16string& text,
  374. ui::TextInputType text_input_type = ui::TEXT_INPUT_TYPE_TEXT);
  375. Textfield* InstallTextField(const std::string& text);
  376. // Returns the actual current text for |ns_view_|, or the selected substring.
  377. NSString* GetActualText();
  378. NSString* GetActualSelectedText();
  379. // Returns the expected current text from |dummy_text_view_|, or the selected
  380. // substring.
  381. NSString* GetExpectedText();
  382. NSString* GetExpectedSelectedText();
  383. // Returns the actual selection range for |ns_view_|.
  384. NSRange GetActualSelectionRange();
  385. // Returns the expected selection range from |dummy_text_view_|.
  386. NSRange GetExpectedSelectionRange();
  387. // Set the selection range for the installed textfield and |dummy_text_view_|.
  388. void SetSelectionRange(NSRange range);
  389. // Perform command |sel| on |ns_view_| and |dummy_text_view_|.
  390. void PerformCommand(SEL sel);
  391. // Make selection from |start| to |end| on installed views::Textfield and
  392. // |dummy_text_view_|. If |start| > |end|, extend selection to left from
  393. // |start|.
  394. void MakeSelection(int start, int end);
  395. // Helper method to set the private |keyDownEvent_| field on |ns_view_|.
  396. void SetKeyDownEvent(NSEvent* event);
  397. // Sets a callback to run on the next HandleKeyEvent().
  398. void SetHandleKeyEventCallback(HandleKeyEventCallback callback);
  399. // testing::Test:
  400. void SetUp() override;
  401. void TearDown() override;
  402. // TextfieldController:
  403. bool HandleKeyEvent(Textfield* sender,
  404. const ui::KeyEvent& key_event) override;
  405. protected:
  406. // Test delete to beginning of line or paragraph based on |sel|. |sel| can be
  407. // either deleteToBeginningOfLine: or deleteToBeginningOfParagraph:.
  408. void TestDeleteBeginning(SEL sel);
  409. // Test delete to end of line or paragraph based on |sel|. |sel| can be
  410. // either deleteToEndOfLine: or deleteToEndOfParagraph:.
  411. void TestDeleteEnd(SEL sel);
  412. // Test editing commands in |selectors| against the expectations set by
  413. // |dummy_text_view_|. This is done by selecting every substring within a set
  414. // of test strings (both RTL and non-RTL by default) and performing every
  415. // selector on both the NSTextView and the BridgedContentView hosting a
  416. // focused views::TextField to ensure the resulting text and selection ranges
  417. // match. |selectors| is an NSArray of NSStrings. |cases| determines whether
  418. // RTL strings are to be tested.
  419. void TestEditingCommands(NSArray* selectors);
  420. std::unique_ptr<views::View> view_;
  421. // Weak. Owned by bridge().
  422. BridgedContentView* ns_view_;
  423. // An NSTextView which helps set the expectations for our tests.
  424. base::scoped_nsobject<NSTextView> dummy_text_view_;
  425. HandleKeyEventCallback handle_key_event_callback_;
  426. base::test::SingleThreadTaskEnvironment task_environment_{
  427. base::test::SingleThreadTaskEnvironment::MainThreadType::UI};
  428. };
  429. // Class that counts occurrences of a VKEY_RETURN accelerator, marking them
  430. // processed.
  431. class EnterAcceleratorView : public View {
  432. public:
  433. EnterAcceleratorView() { AddAccelerator({ui::VKEY_RETURN, 0}); }
  434. int count() const { return count_; }
  435. // View:
  436. bool AcceleratorPressed(const ui::Accelerator& accelerator) override {
  437. ++count_;
  438. return true;
  439. }
  440. private:
  441. int count_ = 0;
  442. };
  443. BridgedNativeWidgetTest::BridgedNativeWidgetTest() = default;
  444. BridgedNativeWidgetTest::~BridgedNativeWidgetTest() = default;
  445. Textfield* BridgedNativeWidgetTest::InstallTextField(
  446. const std::u16string& text,
  447. ui::TextInputType text_input_type) {
  448. Textfield* textfield = new Textfield();
  449. textfield->SetText(text);
  450. textfield->SetTextInputType(text_input_type);
  451. textfield->set_controller(this);
  452. view_->RemoveAllChildViews();
  453. view_->AddChildView(textfield);
  454. textfield->SetBoundsRect(bounds_);
  455. // Request focus so the InputMethod can dispatch events to the RootView, and
  456. // have them delivered to the textfield. Note that focusing a textfield
  457. // schedules a task to flash the cursor, so this requires |message_loop_|.
  458. textfield->RequestFocus();
  459. GetNSWindowHost()->text_input_host()->SetTextInputClient(textfield);
  460. // Initialize the dummy text view. Initializing this with NSZeroRect causes
  461. // weird NSTextView behavior on OSX 10.9.
  462. dummy_text_view_.reset(
  463. [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]);
  464. [dummy_text_view_ setString:SysUTF16ToNSString(text)];
  465. return textfield;
  466. }
  467. Textfield* BridgedNativeWidgetTest::InstallTextField(const std::string& text) {
  468. return InstallTextField(base::ASCIIToUTF16(text));
  469. }
  470. NSString* BridgedNativeWidgetTest::GetActualText() {
  471. return GetViewStringForRange(ns_view_, EmptyRange());
  472. }
  473. NSString* BridgedNativeWidgetTest::GetActualSelectedText() {
  474. return GetViewStringForRange(ns_view_, [ns_view_ selectedRange]);
  475. }
  476. NSString* BridgedNativeWidgetTest::GetExpectedText() {
  477. return GetViewStringForRange(dummy_text_view_, EmptyRange());
  478. }
  479. NSString* BridgedNativeWidgetTest::GetExpectedSelectedText() {
  480. return GetViewStringForRange(dummy_text_view_,
  481. [dummy_text_view_ selectedRange]);
  482. }
  483. NSRange BridgedNativeWidgetTest::GetActualSelectionRange() {
  484. return [ns_view_ selectedRange];
  485. }
  486. NSRange BridgedNativeWidgetTest::GetExpectedSelectionRange() {
  487. return [dummy_text_view_ selectedRange];
  488. }
  489. void BridgedNativeWidgetTest::SetSelectionRange(NSRange range) {
  490. ui::TextInputClient* client = [ns_view_ textInputClient];
  491. client->SetEditableSelectionRange(gfx::Range(range));
  492. [dummy_text_view_ setSelectedRange:range];
  493. }
  494. void BridgedNativeWidgetTest::PerformCommand(SEL sel) {
  495. [ns_view_ doCommandBySelector:sel];
  496. [dummy_text_view_ doCommandBySelector:sel];
  497. }
  498. void BridgedNativeWidgetTest::MakeSelection(int start, int end) {
  499. ui::TextInputClient* client = [ns_view_ textInputClient];
  500. const gfx::Range range(start, end);
  501. // Although a gfx::Range is directed, the underlying model will not choose an
  502. // affinity until the cursor is moved.
  503. client->SetEditableSelectionRange(range);
  504. // Set the range without an affinity. The first @selector sent to the text
  505. // field determines the affinity. Note that Range::ToNSRange() may discard
  506. // the direction since NSRange has no direction.
  507. [dummy_text_view_ setSelectedRange:range.ToNSRange()];
  508. }
  509. void BridgedNativeWidgetTest::SetKeyDownEvent(NSEvent* event) {
  510. ns_view_.keyDownEventForTesting = event;
  511. }
  512. void BridgedNativeWidgetTest::SetHandleKeyEventCallback(
  513. HandleKeyEventCallback callback) {
  514. handle_key_event_callback_ = std::move(callback);
  515. }
  516. void BridgedNativeWidgetTest::SetUp() {
  517. BridgedNativeWidgetTestBase::SetUp();
  518. view_ = std::make_unique<views::internal::RootView>(widget_.get());
  519. base::scoped_nsobject<NSWindow> window([bridge_window() retain]);
  520. // The delegate should exist before setting the root view.
  521. EXPECT_TRUE([window delegate]);
  522. GetNSWindowHost()->SetRootView(view_.get());
  523. bridge()->CreateContentView(GetNSWindowHost()->GetRootViewNSViewId(),
  524. view_->bounds());
  525. ns_view_ = bridge()->ns_view();
  526. // Pretend it has been shown via NativeWidgetMac::Show().
  527. [window orderFront:nil];
  528. [window makeFirstResponder:bridge()->ns_view()];
  529. }
  530. void BridgedNativeWidgetTest::TearDown() {
  531. // Clear kill buffer so that no state persists between tests.
  532. TextfieldModel::ClearKillBuffer();
  533. if (GetNSWindowHost()) {
  534. GetNSWindowHost()->SetRootView(nullptr);
  535. bridge()->DestroyContentView();
  536. }
  537. view_.reset();
  538. BridgedNativeWidgetTestBase::TearDown();
  539. }
  540. bool BridgedNativeWidgetTest::HandleKeyEvent(Textfield* sender,
  541. const ui::KeyEvent& key_event) {
  542. if (handle_key_event_callback_)
  543. return handle_key_event_callback_.Run(sender, key_event);
  544. return false;
  545. }
  546. void BridgedNativeWidgetTest::TestDeleteBeginning(SEL sel) {
  547. InstallTextField("foo bar baz");
  548. EXPECT_EQ_RANGE_3(NSMakeRange(11, 0), GetExpectedSelectionRange(),
  549. GetActualSelectionRange());
  550. // Move the caret to the beginning of the line.
  551. SetSelectionRange(NSMakeRange(0, 0));
  552. // Verify no deletion takes place.
  553. PerformCommand(sel);
  554. EXPECT_NSEQ_3(@"foo bar baz", GetExpectedText(), GetActualText());
  555. EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(),
  556. GetActualSelectionRange());
  557. // Move the caret as- "foo| bar baz".
  558. SetSelectionRange(NSMakeRange(3, 0));
  559. PerformCommand(sel);
  560. // Verify state is "| bar baz".
  561. EXPECT_NSEQ_3(@" bar baz", GetExpectedText(), GetActualText());
  562. EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(),
  563. GetActualSelectionRange());
  564. // Make a selection as- " bar |baz|".
  565. SetSelectionRange(NSMakeRange(5, 3));
  566. PerformCommand(sel);
  567. // Verify only the selection is deleted so that the state is " bar |".
  568. EXPECT_NSEQ_3(@" bar ", GetExpectedText(), GetActualText());
  569. EXPECT_EQ_RANGE_3(NSMakeRange(5, 0), GetExpectedSelectionRange(),
  570. GetActualSelectionRange());
  571. // Verify yanking inserts the deleted text.
  572. PerformCommand(@selector(yank:));
  573. EXPECT_NSEQ_3(@" bar baz", GetExpectedText(), GetActualText());
  574. EXPECT_EQ_RANGE_3(NSMakeRange(8, 0), GetExpectedSelectionRange(),
  575. GetActualSelectionRange());
  576. }
  577. void BridgedNativeWidgetTest::TestDeleteEnd(SEL sel) {
  578. InstallTextField("foo bar baz");
  579. EXPECT_EQ_RANGE_3(NSMakeRange(11, 0), GetExpectedSelectionRange(),
  580. GetActualSelectionRange());
  581. // Caret is at the end of the line. Verify no deletion takes place.
  582. PerformCommand(sel);
  583. EXPECT_NSEQ_3(@"foo bar baz", GetExpectedText(), GetActualText());
  584. EXPECT_EQ_RANGE_3(NSMakeRange(11, 0), GetExpectedSelectionRange(),
  585. GetActualSelectionRange());
  586. // Move the caret as- "foo bar| baz".
  587. SetSelectionRange(NSMakeRange(7, 0));
  588. PerformCommand(sel);
  589. // Verify state is "foo bar|".
  590. EXPECT_NSEQ_3(@"foo bar", GetExpectedText(), GetActualText());
  591. EXPECT_EQ_RANGE_3(NSMakeRange(7, 0), GetExpectedSelectionRange(),
  592. GetActualSelectionRange());
  593. // Make a selection as- "|foo |bar".
  594. SetSelectionRange(NSMakeRange(0, 4));
  595. PerformCommand(sel);
  596. // Verify only the selection is deleted so that the state is "|bar".
  597. EXPECT_NSEQ_3(@"bar", GetExpectedText(), GetActualText());
  598. EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(),
  599. GetActualSelectionRange());
  600. // Verify yanking inserts the deleted text.
  601. PerformCommand(@selector(yank:));
  602. EXPECT_NSEQ_3(@"foo bar", GetExpectedText(), GetActualText());
  603. EXPECT_EQ_RANGE_3(NSMakeRange(4, 0), GetExpectedSelectionRange(),
  604. GetActualSelectionRange());
  605. }
  606. void BridgedNativeWidgetTest::TestEditingCommands(NSArray* selectors) {
  607. struct {
  608. std::u16string test_string;
  609. bool is_rtl;
  610. } test_cases[] = {
  611. {u"ab c", false},
  612. {u"\x0634\x0632 \x064A", true},
  613. };
  614. for (const auto& test_case : test_cases) {
  615. for (NSString* selector_string in selectors) {
  616. SEL sel = NSSelectorFromString(selector_string);
  617. const int len = test_case.test_string.length();
  618. for (int i = 0; i <= len; i++) {
  619. for (int j = 0; j <= len; j++) {
  620. SCOPED_TRACE(base::StringPrintf(
  621. "Testing range [%d-%d] for case %s and selector %s\n", i, j,
  622. base::UTF16ToUTF8(test_case.test_string).c_str(),
  623. base::SysNSStringToUTF8(selector_string).c_str()));
  624. InstallTextField(test_case.test_string);
  625. MakeSelection(i, j);
  626. // Sanity checks for MakeSelection().
  627. EXPECT_NSEQ(GetExpectedSelectedText(), GetActualSelectedText());
  628. EXPECT_EQ_RANGE_3(NSMakeRange(std::min(i, j), std::abs(i - j)),
  629. GetExpectedSelectionRange(),
  630. GetActualSelectionRange());
  631. // Bail out early for selection-modifying commands that are buggy in
  632. // Cocoa, since the selected text will not match.
  633. if (test_case.is_rtl && i != j && IsRTLSelectBuggy(sel))
  634. continue;
  635. PerformCommand(sel);
  636. EXPECT_NSEQ(GetExpectedSelectedText(), GetActualSelectedText());
  637. EXPECT_NSEQ(GetExpectedText(), GetActualText());
  638. // Spot-check some Cocoa RTL bugs. These only manifest when there is a
  639. // selection (i != j), not for regular cursor moves.
  640. if (test_case.is_rtl && i != j && IsRTLMoveBuggy(sel)) {
  641. if (sel == @selector(moveRight:)) {
  642. // Surely moving right with an rtl string moves to the start of a
  643. // range (i.e. min). But Cocoa moves to the end.
  644. EXPECT_EQ_RANGE(NSMakeRange(std::max(i, j), 0),
  645. GetExpectedSelectionRange());
  646. EXPECT_EQ_RANGE(NSMakeRange(std::min(i, j), 0),
  647. GetActualSelectionRange());
  648. } else if (sel == @selector(moveLeft:)) {
  649. EXPECT_EQ_RANGE(NSMakeRange(std::min(i, j), 0),
  650. GetExpectedSelectionRange());
  651. EXPECT_EQ_RANGE(NSMakeRange(std::max(i, j), 0),
  652. GetActualSelectionRange());
  653. }
  654. continue;
  655. }
  656. EXPECT_EQ_RANGE(GetExpectedSelectionRange(),
  657. GetActualSelectionRange());
  658. }
  659. }
  660. }
  661. }
  662. }
  663. // The TEST_VIEW macro expects the view it's testing to have a superview. In
  664. // these tests, the NSView bridge is a contentView, at the root. These mimic
  665. // what TEST_VIEW usually does.
  666. TEST_F(BridgedNativeWidgetTest, BridgedNativeWidgetTest_TestViewAddRemove) {
  667. base::scoped_nsobject<BridgedContentView> view([bridge()->ns_view() retain]);
  668. base::scoped_nsobject<NSWindow> window([bridge_window() retain]);
  669. EXPECT_NSEQ([window contentView], view);
  670. EXPECT_NSEQ(window, [view window]);
  671. // The superview of a contentView is an NSNextStepFrame.
  672. EXPECT_TRUE([view superview]);
  673. EXPECT_TRUE([view bridge]);
  674. // Ensure the tracking area to propagate mouseMoved: events to the RootView is
  675. // installed.
  676. EXPECT_EQ(1u, [[view trackingAreas] count]);
  677. // Closing the window should tear down the C++ bridge, remove references to
  678. // any C++ objects in the ObjectiveC object, and remove it from the hierarchy.
  679. [window close];
  680. EXPECT_FALSE([view bridge]);
  681. EXPECT_FALSE([view superview]);
  682. EXPECT_FALSE([view window]);
  683. EXPECT_EQ(0u, [[view trackingAreas] count]);
  684. EXPECT_FALSE([window contentView]);
  685. EXPECT_FALSE([window delegate]);
  686. }
  687. TEST_F(BridgedNativeWidgetTest, BridgedNativeWidgetTest_TestViewDisplay) {
  688. [bridge()->ns_view() display];
  689. }
  690. // Test that resizing the window resizes the root view appropriately.
  691. TEST_F(BridgedNativeWidgetTest, ViewSizeTracksWindow) {
  692. const int kTestNewWidth = 400;
  693. const int kTestNewHeight = 300;
  694. // |bridge_window()| is borderless, so these should align.
  695. NSSize window_size = [bridge_window() frame].size;
  696. EXPECT_EQ(view_->width(), static_cast<int>(window_size.width));
  697. EXPECT_EQ(view_->height(), static_cast<int>(window_size.height));
  698. // Make sure a resize actually occurs.
  699. EXPECT_NE(kTestNewWidth, view_->width());
  700. EXPECT_NE(kTestNewHeight, view_->height());
  701. [bridge_window() setFrame:NSMakeRect(0, 0, kTestNewWidth, kTestNewHeight)
  702. display:NO];
  703. EXPECT_EQ(kTestNewWidth, view_->width());
  704. EXPECT_EQ(kTestNewHeight, view_->height());
  705. }
  706. TEST_F(BridgedNativeWidgetTest, GetInputMethodShouldNotReturnNull) {
  707. EXPECT_TRUE(native_widget_mac_->GetInputMethod());
  708. }
  709. // A simpler test harness for testing initialization flows.
  710. class BridgedNativeWidgetInitTest : public BridgedNativeWidgetTestBase {
  711. public:
  712. BridgedNativeWidgetInitTest()
  713. : BridgedNativeWidgetTestBase(SkipInitialization()) {}
  714. BridgedNativeWidgetInitTest(const BridgedNativeWidgetInitTest&) = delete;
  715. BridgedNativeWidgetInitTest& operator=(const BridgedNativeWidgetInitTest&) =
  716. delete;
  717. // Prepares a new |window_| and |widget_| for a call to PerformInit().
  718. void CreateNewWidgetToInit() {
  719. widget_ = std::make_unique<Widget>();
  720. native_widget_mac_ = new MockNativeWidgetMac(widget_.get());
  721. }
  722. void PerformInit() {
  723. Widget::InitParams init_params;
  724. init_params.native_widget = native_widget_mac_.get();
  725. init_params.type = type_;
  726. init_params.ownership = ownership_;
  727. init_params.opacity = opacity_;
  728. init_params.bounds = bounds_;
  729. init_params.shadow_type = shadow_type_;
  730. widget_->Init(std::move(init_params));
  731. }
  732. };
  733. // Test that NativeWidgetNSWindowBridge remains sane if Init() is never called.
  734. TEST_F(BridgedNativeWidgetInitTest, InitNotCalled) {
  735. // Don't use a Widget* as the delegate. ~Widget() checks for Widget::
  736. // |native_widget_destroyed_| being set to true. That can only happen with a
  737. // non-null WidgetDelegate, which is only set in Widget::Init(). Then, since
  738. // neither Widget nor NativeWidget take ownership, use a unique_ptr.
  739. std::unique_ptr<MockNativeWidgetMac> native_widget(
  740. new MockNativeWidgetMac(nullptr));
  741. native_widget_mac_ = native_widget.get();
  742. EXPECT_FALSE(bridge());
  743. EXPECT_FALSE(GetNSWindowHost()->GetInProcessNSWindow());
  744. }
  745. // Tests the shadow type given in InitParams.
  746. // Disabled because shadows are disabled on the bots - see
  747. // https://crbug.com/899286.
  748. TEST_F(BridgedNativeWidgetInitTest, DISABLED_ShadowType) {
  749. // Verify Widget::InitParam defaults and arguments added from SetUp().
  750. EXPECT_EQ(Widget::InitParams::TYPE_WINDOW_FRAMELESS, type_);
  751. EXPECT_EQ(Widget::InitParams::WindowOpacity::kOpaque, opacity_);
  752. EXPECT_EQ(Widget::InitParams::ShadowType::kDefault, shadow_type_);
  753. CreateNewWidgetToInit();
  754. EXPECT_FALSE(
  755. [bridge_window() hasShadow]); // Default for NSWindowStyleMaskBorderless.
  756. PerformInit();
  757. // Borderless is 0, so isn't really a mask. Check that nothing is set.
  758. EXPECT_EQ(NSWindowStyleMaskBorderless, [bridge_window() styleMask]);
  759. EXPECT_TRUE(
  760. [bridge_window() hasShadow]); // ShadowType::kDefault means a shadow.
  761. CreateNewWidgetToInit();
  762. shadow_type_ = Widget::InitParams::ShadowType::kNone;
  763. PerformInit();
  764. EXPECT_FALSE([bridge_window() hasShadow]); // Preserves lack of shadow.
  765. // Default for Widget::InitParams::TYPE_WINDOW.
  766. CreateNewWidgetToInit();
  767. PerformInit();
  768. EXPECT_FALSE(
  769. [bridge_window() hasShadow]); // ShadowType::kNone removes shadow.
  770. shadow_type_ = Widget::InitParams::ShadowType::kDefault;
  771. CreateNewWidgetToInit();
  772. PerformInit();
  773. EXPECT_TRUE([bridge_window() hasShadow]); // Preserves shadow.
  774. widget_.reset();
  775. }
  776. // Ensure a nil NSTextInputContext is returned when the ui::TextInputClient is
  777. // not editable, a password field, or unset.
  778. TEST_F(BridgedNativeWidgetTest, InputContext) {
  779. const std::u16string test_string = u"test_str";
  780. InstallTextField(test_string, ui::TEXT_INPUT_TYPE_PASSWORD);
  781. EXPECT_FALSE([ns_view_ inputContext]);
  782. InstallTextField(test_string, ui::TEXT_INPUT_TYPE_TEXT);
  783. EXPECT_TRUE([ns_view_ inputContext]);
  784. GetNSWindowHost()->text_input_host()->SetTextInputClient(nullptr);
  785. EXPECT_FALSE([ns_view_ inputContext]);
  786. InstallTextField(test_string, ui::TEXT_INPUT_TYPE_NONE);
  787. EXPECT_FALSE([ns_view_ inputContext]);
  788. }
  789. // Test getting complete string using text input protocol.
  790. TEST_F(BridgedNativeWidgetTest, TextInput_GetCompleteString) {
  791. const std::string test_string = "foo bar baz";
  792. InstallTextField(test_string);
  793. NSRange range = NSMakeRange(0, test_string.size());
  794. NSRange actual_range;
  795. NSAttributedString* actual_text =
  796. [ns_view_ attributedSubstringForProposedRange:range
  797. actualRange:&actual_range];
  798. NSRange expected_range;
  799. NSAttributedString* expected_text =
  800. [dummy_text_view_ attributedSubstringForProposedRange:range
  801. actualRange:&expected_range];
  802. EXPECT_NSEQ_3(@"foo bar baz", [expected_text string], [actual_text string]);
  803. EXPECT_EQ_RANGE_3(range, expected_range, actual_range);
  804. }
  805. // Test getting middle substring using text input protocol.
  806. TEST_F(BridgedNativeWidgetTest, TextInput_GetMiddleSubstring) {
  807. const std::string test_string = "foo bar baz";
  808. InstallTextField(test_string);
  809. NSRange range = NSMakeRange(4, 3);
  810. NSRange actual_range;
  811. NSAttributedString* actual_text =
  812. [ns_view_ attributedSubstringForProposedRange:range
  813. actualRange:&actual_range];
  814. NSRange expected_range;
  815. NSAttributedString* expected_text =
  816. [dummy_text_view_ attributedSubstringForProposedRange:range
  817. actualRange:&expected_range];
  818. EXPECT_NSEQ_3(@"bar", [expected_text string], [actual_text string]);
  819. EXPECT_EQ_RANGE_3(range, expected_range, actual_range);
  820. }
  821. // Test getting ending substring using text input protocol.
  822. TEST_F(BridgedNativeWidgetTest, TextInput_GetEndingSubstring) {
  823. const std::string test_string = "foo bar baz";
  824. InstallTextField(test_string);
  825. NSRange range = NSMakeRange(8, 100);
  826. NSRange actual_range;
  827. NSAttributedString* actual_text =
  828. [ns_view_ attributedSubstringForProposedRange:range
  829. actualRange:&actual_range];
  830. NSRange expected_range;
  831. NSAttributedString* expected_text =
  832. [dummy_text_view_ attributedSubstringForProposedRange:range
  833. actualRange:&expected_range];
  834. EXPECT_NSEQ_3(@"baz", [expected_text string], [actual_text string]);
  835. EXPECT_EQ_RANGE_3(NSMakeRange(8, 3), expected_range, actual_range);
  836. }
  837. // Test getting empty substring using text input protocol.
  838. TEST_F(BridgedNativeWidgetTest, TextInput_GetEmptySubstring) {
  839. const std::string test_string = "foo bar baz";
  840. InstallTextField(test_string);
  841. // Try with EmptyRange(). This behaves specially and should return the
  842. // complete string and the corresponding text range.
  843. NSRange range = EmptyRange();
  844. NSRange actual_range = EmptyRange();
  845. NSRange expected_range = EmptyRange();
  846. NSAttributedString* actual_text =
  847. [ns_view_ attributedSubstringForProposedRange:range
  848. actualRange:&actual_range];
  849. NSAttributedString* expected_text =
  850. [dummy_text_view_ attributedSubstringForProposedRange:range
  851. actualRange:&expected_range];
  852. EXPECT_NSEQ_3(@"foo bar baz", [expected_text string], [actual_text string]);
  853. EXPECT_EQ_RANGE_3(NSMakeRange(0, test_string.length()), expected_range,
  854. actual_range);
  855. // Try with a valid empty range.
  856. range = NSMakeRange(2, 0);
  857. actual_range = EmptyRange();
  858. expected_range = EmptyRange();
  859. actual_text = [ns_view_ attributedSubstringForProposedRange:range
  860. actualRange:&actual_range];
  861. expected_text =
  862. [dummy_text_view_ attributedSubstringForProposedRange:range
  863. actualRange:&expected_range];
  864. EXPECT_NSEQ_3(nil, [expected_text string], [actual_text string]);
  865. EXPECT_EQ_RANGE_3(range, expected_range, actual_range);
  866. // Try with an out of bounds empty range.
  867. range = NSMakeRange(20, 0);
  868. actual_range = EmptyRange();
  869. expected_range = EmptyRange();
  870. actual_text = [ns_view_ attributedSubstringForProposedRange:range
  871. actualRange:&actual_range];
  872. expected_text =
  873. [dummy_text_view_ attributedSubstringForProposedRange:range
  874. actualRange:&expected_range];
  875. EXPECT_NSEQ_3(nil, [expected_text string], [actual_text string]);
  876. EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), expected_range, actual_range);
  877. }
  878. // Test inserting text using text input protocol.
  879. TEST_F(BridgedNativeWidgetTest, TextInput_InsertText) {
  880. const std::string test_string = "foo";
  881. InstallTextField(test_string);
  882. [ns_view_ insertText:SysUTF8ToNSString(test_string)
  883. replacementRange:EmptyRange()];
  884. [dummy_text_view_ insertText:SysUTF8ToNSString(test_string)
  885. replacementRange:EmptyRange()];
  886. EXPECT_NSEQ_3(@"foofoo", GetExpectedText(), GetActualText());
  887. }
  888. // Test replacing text using text input protocol.
  889. TEST_F(BridgedNativeWidgetTest, TextInput_ReplaceText) {
  890. const std::string test_string = "foo bar";
  891. InstallTextField(test_string);
  892. [ns_view_ insertText:@"baz" replacementRange:NSMakeRange(4, 3)];
  893. [dummy_text_view_ insertText:@"baz" replacementRange:NSMakeRange(4, 3)];
  894. EXPECT_NSEQ_3(@"foo baz", GetExpectedText(), GetActualText());
  895. }
  896. // Test IME composition using text input protocol.
  897. TEST_F(BridgedNativeWidgetTest, TextInput_Compose) {
  898. const std::string test_string = "foo ";
  899. InstallTextField(test_string);
  900. EXPECT_EQ_3(NO, [dummy_text_view_ hasMarkedText], [ns_view_ hasMarkedText]);
  901. // As per NSTextInputClient documentation, markedRange should return
  902. // {NSNotFound, 0} iff hasMarked returns false. However, NSTextView returns
  903. // {text_length, text_length} for this case. We maintain consistency with the
  904. // documentation, hence the EXPECT_FALSE check.
  905. EXPECT_FALSE(
  906. NSEqualRanges([dummy_text_view_ markedRange], [ns_view_ markedRange]));
  907. EXPECT_EQ_RANGE(EmptyRange(), [ns_view_ markedRange]);
  908. // Start composition.
  909. NSString* compositionText = @"bar";
  910. NSUInteger compositionLength = [compositionText length];
  911. [ns_view_ setMarkedText:compositionText
  912. selectedRange:NSMakeRange(0, 2)
  913. replacementRange:EmptyRange()];
  914. [dummy_text_view_ setMarkedText:compositionText
  915. selectedRange:NSMakeRange(0, 2)
  916. replacementRange:EmptyRange()];
  917. EXPECT_EQ_3(YES, [dummy_text_view_ hasMarkedText], [ns_view_ hasMarkedText]);
  918. EXPECT_EQ_RANGE_3(NSMakeRange(test_string.size(), compositionLength),
  919. [dummy_text_view_ markedRange], [ns_view_ markedRange]);
  920. EXPECT_EQ_RANGE_3(NSMakeRange(test_string.size(), 2),
  921. GetExpectedSelectionRange(), GetActualSelectionRange());
  922. // Confirm composition.
  923. [ns_view_ unmarkText];
  924. [dummy_text_view_ unmarkText];
  925. EXPECT_EQ_3(NO, [dummy_text_view_ hasMarkedText], [ns_view_ hasMarkedText]);
  926. EXPECT_FALSE(
  927. NSEqualRanges([dummy_text_view_ markedRange], [ns_view_ markedRange]));
  928. EXPECT_EQ_RANGE(EmptyRange(), [ns_view_ markedRange]);
  929. EXPECT_NSEQ_3(@"foo bar", GetExpectedText(), GetActualText());
  930. EXPECT_EQ_RANGE_3(NSMakeRange([GetActualText() length], 0),
  931. GetExpectedSelectionRange(), GetActualSelectionRange());
  932. }
  933. // Test IME composition for accented characters.
  934. TEST_F(BridgedNativeWidgetTest, TextInput_AccentedCharacter) {
  935. InstallTextField("abc");
  936. // Simulate action messages generated when the key 'a' is pressed repeatedly
  937. // and leads to the showing of an IME candidate window. To simulate an event,
  938. // set the private keyDownEvent field on the BridgedContentView.
  939. // First an insertText: message with key 'a' is generated.
  940. SetKeyDownEvent(cocoa_test_event_utils::SynthesizeKeyEvent(
  941. widget_->GetNativeWindow().GetNativeNSWindow(), true, ui::VKEY_A, 0));
  942. [ns_view_ insertText:@"a" replacementRange:EmptyRange()];
  943. [dummy_text_view_ insertText:@"a" replacementRange:EmptyRange()];
  944. SetKeyDownEvent(nil);
  945. EXPECT_EQ_3(NO, [dummy_text_view_ hasMarkedText], [ns_view_ hasMarkedText]);
  946. EXPECT_NSEQ_3(@"abca", GetExpectedText(), GetActualText());
  947. // Next the IME popup appears. On selecting the accented character using arrow
  948. // keys, setMarkedText action message is generated which replaces the earlier
  949. // inserted 'a'.
  950. SetKeyDownEvent(cocoa_test_event_utils::SynthesizeKeyEvent(
  951. widget_->GetNativeWindow().GetNativeNSWindow(), true, ui::VKEY_RIGHT, 0));
  952. [ns_view_ setMarkedText:@"à"
  953. selectedRange:NSMakeRange(0, 1)
  954. replacementRange:NSMakeRange(3, 1)];
  955. [dummy_text_view_ setMarkedText:@"à"
  956. selectedRange:NSMakeRange(0, 1)
  957. replacementRange:NSMakeRange(3, 1)];
  958. SetKeyDownEvent(nil);
  959. EXPECT_EQ_3(YES, [dummy_text_view_ hasMarkedText], [ns_view_ hasMarkedText]);
  960. EXPECT_EQ_RANGE_3(NSMakeRange(3, 1), [dummy_text_view_ markedRange],
  961. [ns_view_ markedRange]);
  962. EXPECT_EQ_RANGE_3(NSMakeRange(3, 1), GetExpectedSelectionRange(),
  963. GetActualSelectionRange());
  964. EXPECT_NSEQ_3(@"abcà", GetExpectedText(), GetActualText());
  965. // On pressing enter, the marked text is confirmed.
  966. SetKeyDownEvent(cocoa_test_event_utils::SynthesizeKeyEvent(
  967. widget_->GetNativeWindow().GetNativeNSWindow(), true, ui::VKEY_RETURN,
  968. 0));
  969. [ns_view_ insertText:@"à" replacementRange:EmptyRange()];
  970. [dummy_text_view_ insertText:@"à" replacementRange:EmptyRange()];
  971. SetKeyDownEvent(nil);
  972. EXPECT_EQ_3(NO, [dummy_text_view_ hasMarkedText], [ns_view_ hasMarkedText]);
  973. EXPECT_EQ_RANGE_3(NSMakeRange(4, 0), GetExpectedSelectionRange(),
  974. GetActualSelectionRange());
  975. EXPECT_NSEQ_3(@"abcà", GetExpectedText(), GetActualText());
  976. }
  977. // Test moving the caret left and right using text input protocol.
  978. TEST_F(BridgedNativeWidgetTest, TextInput_MoveLeftRight) {
  979. InstallTextField("foo");
  980. EXPECT_EQ_RANGE_3(NSMakeRange(3, 0), GetExpectedSelectionRange(),
  981. GetActualSelectionRange());
  982. // Move right not allowed, out of range.
  983. PerformCommand(@selector(moveRight:));
  984. EXPECT_EQ_RANGE_3(NSMakeRange(3, 0), GetExpectedSelectionRange(),
  985. GetActualSelectionRange());
  986. // Move left.
  987. PerformCommand(@selector(moveLeft:));
  988. EXPECT_EQ_RANGE_3(NSMakeRange(2, 0), GetExpectedSelectionRange(),
  989. GetActualSelectionRange());
  990. // Move right.
  991. PerformCommand(@selector(moveRight:));
  992. EXPECT_EQ_RANGE_3(NSMakeRange(3, 0), GetExpectedSelectionRange(),
  993. GetActualSelectionRange());
  994. }
  995. // Test backward delete using text input protocol.
  996. TEST_F(BridgedNativeWidgetTest, TextInput_DeleteBackward) {
  997. InstallTextField("a");
  998. EXPECT_EQ_RANGE_3(NSMakeRange(1, 0), GetExpectedSelectionRange(),
  999. GetActualSelectionRange());
  1000. // Delete one character.
  1001. PerformCommand(@selector(deleteBackward:));
  1002. EXPECT_NSEQ_3(nil, GetExpectedText(), GetActualText());
  1003. EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(),
  1004. GetActualSelectionRange());
  1005. // Verify that deletion did not modify the kill buffer.
  1006. PerformCommand(@selector(yank:));
  1007. EXPECT_NSEQ_3(nil, GetExpectedText(), GetActualText());
  1008. EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(),
  1009. GetActualSelectionRange());
  1010. // Try to delete again on an empty string.
  1011. PerformCommand(@selector(deleteBackward:));
  1012. EXPECT_NSEQ_3(nil, GetExpectedText(), GetActualText());
  1013. EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(),
  1014. GetActualSelectionRange());
  1015. }
  1016. // Test forward delete using text input protocol.
  1017. TEST_F(BridgedNativeWidgetTest, TextInput_DeleteForward) {
  1018. InstallTextField("a");
  1019. EXPECT_EQ_RANGE_3(NSMakeRange(1, 0), GetExpectedSelectionRange(),
  1020. GetActualSelectionRange());
  1021. // At the end of the string, can't delete forward.
  1022. PerformCommand(@selector(deleteForward:));
  1023. EXPECT_NSEQ_3(@"a", GetExpectedText(), GetActualText());
  1024. EXPECT_EQ_RANGE_3(NSMakeRange(1, 0), GetExpectedSelectionRange(),
  1025. GetActualSelectionRange());
  1026. // Should succeed after moving left first.
  1027. PerformCommand(@selector(moveLeft:));
  1028. PerformCommand(@selector(deleteForward:));
  1029. EXPECT_NSEQ_3(nil, GetExpectedText(), GetActualText());
  1030. EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(),
  1031. GetActualSelectionRange());
  1032. // Verify that deletion did not modify the kill buffer.
  1033. PerformCommand(@selector(yank:));
  1034. EXPECT_NSEQ_3(nil, GetExpectedText(), GetActualText());
  1035. EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(),
  1036. GetActualSelectionRange());
  1037. }
  1038. // Test forward word deletion using text input protocol.
  1039. TEST_F(BridgedNativeWidgetTest, TextInput_DeleteWordForward) {
  1040. InstallTextField("foo bar baz");
  1041. EXPECT_EQ_RANGE_3(NSMakeRange(11, 0), GetExpectedSelectionRange(),
  1042. GetActualSelectionRange());
  1043. // Caret is at the end of the line. Verify no deletion takes place.
  1044. PerformCommand(@selector(deleteWordForward:));
  1045. EXPECT_NSEQ_3(@"foo bar baz", GetExpectedText(), GetActualText());
  1046. EXPECT_EQ_RANGE_3(NSMakeRange(11, 0), GetExpectedSelectionRange(),
  1047. GetActualSelectionRange());
  1048. // Move the caret as- "foo b|ar baz".
  1049. SetSelectionRange(NSMakeRange(5, 0));
  1050. PerformCommand(@selector(deleteWordForward:));
  1051. // Verify state is "foo b| baz"
  1052. EXPECT_NSEQ_3(@"foo b baz", GetExpectedText(), GetActualText());
  1053. EXPECT_EQ_RANGE_3(NSMakeRange(5, 0), GetExpectedSelectionRange(),
  1054. GetActualSelectionRange());
  1055. // Make a selection as- "|fo|o b baz".
  1056. SetSelectionRange(NSMakeRange(0, 2));
  1057. PerformCommand(@selector(deleteWordForward:));
  1058. // Verify only the selection is deleted and state is "|o b baz".
  1059. EXPECT_NSEQ_3(@"o b baz", GetExpectedText(), GetActualText());
  1060. EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(),
  1061. GetActualSelectionRange());
  1062. // Verify that deletion did not modify the kill buffer.
  1063. PerformCommand(@selector(yank:));
  1064. EXPECT_NSEQ_3(@"o b baz", GetExpectedText(), GetActualText());
  1065. EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(),
  1066. GetActualSelectionRange());
  1067. }
  1068. // Test backward word deletion using text input protocol.
  1069. TEST_F(BridgedNativeWidgetTest, TextInput_DeleteWordBackward) {
  1070. InstallTextField("foo bar baz");
  1071. EXPECT_EQ_RANGE_3(NSMakeRange(11, 0), GetExpectedSelectionRange(),
  1072. GetActualSelectionRange());
  1073. // Move the caret to the beginning of the line.
  1074. SetSelectionRange(NSMakeRange(0, 0));
  1075. // Verify no deletion takes place.
  1076. PerformCommand(@selector(deleteWordBackward:));
  1077. EXPECT_NSEQ_3(@"foo bar baz", GetExpectedText(), GetActualText());
  1078. EXPECT_EQ_RANGE_3(NSMakeRange(0, 0), GetExpectedSelectionRange(),
  1079. GetActualSelectionRange());
  1080. // Move the caret as- "foo ba|r baz".
  1081. SetSelectionRange(NSMakeRange(6, 0));
  1082. PerformCommand(@selector(deleteWordBackward:));
  1083. // Verify state is "foo |r baz".
  1084. EXPECT_NSEQ_3(@"foo r baz", GetExpectedText(), GetActualText());
  1085. EXPECT_EQ_RANGE_3(NSMakeRange(4, 0), GetExpectedSelectionRange(),
  1086. GetActualSelectionRange());
  1087. // Make a selection as- "f|oo r b|az".
  1088. SetSelectionRange(NSMakeRange(1, 6));
  1089. PerformCommand(@selector(deleteWordBackward:));
  1090. // Verify only the selection is deleted and state is "f|az"
  1091. EXPECT_NSEQ_3(@"faz", GetExpectedText(), GetActualText());
  1092. EXPECT_EQ_RANGE_3(NSMakeRange(1, 0), GetExpectedSelectionRange(),
  1093. GetActualSelectionRange());
  1094. // Verify that deletion did not modify the kill buffer.
  1095. PerformCommand(@selector(yank:));
  1096. EXPECT_NSEQ_3(@"faz", GetExpectedText(), GetActualText());
  1097. EXPECT_EQ_RANGE_3(NSMakeRange(1, 0), GetExpectedSelectionRange(),
  1098. GetActualSelectionRange());
  1099. }
  1100. // Test deleting to beginning/end of line/paragraph using text input protocol.
  1101. TEST_F(BridgedNativeWidgetTest, TextInput_DeleteToBeginningOfLine) {
  1102. TestDeleteBeginning(@selector(deleteToBeginningOfLine:));
  1103. }
  1104. TEST_F(BridgedNativeWidgetTest, TextInput_DeleteToEndOfLine) {
  1105. TestDeleteEnd(@selector(deleteToEndOfLine:));
  1106. }
  1107. TEST_F(BridgedNativeWidgetTest, TextInput_DeleteToBeginningOfParagraph) {
  1108. TestDeleteBeginning(@selector(deleteToBeginningOfParagraph:));
  1109. }
  1110. TEST_F(BridgedNativeWidgetTest, TextInput_DeleteToEndOfParagraph) {
  1111. TestDeleteEnd(@selector(deleteToEndOfParagraph:));
  1112. }
  1113. // Test move commands against expectations set by |dummy_text_view_|.
  1114. TEST_F(BridgedNativeWidgetTest, TextInput_MoveEditingCommands) {
  1115. TestEditingCommands(kMoveActions);
  1116. }
  1117. // Test move and select commands against expectations set by |dummy_text_view_|.
  1118. TEST_F(BridgedNativeWidgetTest, TextInput_MoveAndSelectEditingCommands) {
  1119. TestEditingCommands(kSelectActions);
  1120. }
  1121. // Test delete commands against expectations set by |dummy_text_view_|.
  1122. TEST_F(BridgedNativeWidgetTest, TextInput_DeleteCommands) {
  1123. TestEditingCommands(kDeleteActions);
  1124. }
  1125. // Test that we don't crash during an action message even if the TextInputClient
  1126. // is nil. Regression test for crbug.com/615745.
  1127. TEST_F(BridgedNativeWidgetTest, NilTextInputClient) {
  1128. GetNSWindowHost()->text_input_host()->SetTextInputClient(nullptr);
  1129. NSMutableArray* selectors = [NSMutableArray array];
  1130. [selectors addObjectsFromArray:kMoveActions];
  1131. [selectors addObjectsFromArray:kSelectActions];
  1132. [selectors addObjectsFromArray:kDeleteActions];
  1133. // -insertText: is omitted from this list to avoid a DCHECK in
  1134. // doCommandBySelector:. AppKit never passes -insertText: to
  1135. // doCommandBySelector: (it calls -insertText: directly instead).
  1136. [selectors addObjectsFromArray:kMiscActions];
  1137. for (NSString* selector in selectors)
  1138. [ns_view_ doCommandBySelector:NSSelectorFromString(selector)];
  1139. [ns_view_ insertText:@""];
  1140. }
  1141. // Test transpose command against expectations set by |dummy_text_view_|.
  1142. TEST_F(BridgedNativeWidgetTest, TextInput_Transpose) {
  1143. TestEditingCommands(@[ @"transpose:" ]);
  1144. }
  1145. // Test firstRectForCharacterRange:actualRange for cases where query range is
  1146. // empty or outside composition range.
  1147. TEST_F(BridgedNativeWidgetTest, TextInput_FirstRectForCharacterRange_Caret) {
  1148. InstallTextField("");
  1149. ui::TextInputClient* client = [ns_view_ textInputClient];
  1150. // No composition. Ensure bounds and range corresponding to the current caret
  1151. // position are returned.
  1152. // Initially selection range will be [0, 0].
  1153. NSRange caret_range = NSMakeRange(0, 0);
  1154. NSRange query_range = NSMakeRange(1, 1);
  1155. NSRange actual_range;
  1156. NSRect rect = [ns_view_ firstRectForCharacterRange:query_range
  1157. actualRange:&actual_range];
  1158. EXPECT_EQ(GetCaretBounds(client), gfx::ScreenRectFromNSRect(rect));
  1159. EXPECT_EQ_RANGE(caret_range, actual_range);
  1160. // Set composition with caret before second character ('e').
  1161. const std::u16string test_string = u"test_str";
  1162. const size_t kTextLength = 8;
  1163. SetCompositionText(client, test_string, 1, &caret_range);
  1164. // Test bounds returned for empty ranges within composition range. As per
  1165. // Apple's documentation for firstRectForCharacterRange:actualRange:, for an
  1166. // empty query range, the returned rectangle should coincide with the
  1167. // insertion point and have zero width. However in our implementation, if the
  1168. // empty query range lies within the composition range, we return a zero width
  1169. // rectangle corresponding to the query range location.
  1170. // Test bounds returned for empty range before second character ('e') are same
  1171. // as caret bounds when placed before second character.
  1172. query_range = NSMakeRange(1, 0);
  1173. rect = [ns_view_ firstRectForCharacterRange:query_range
  1174. actualRange:&actual_range];
  1175. EXPECT_EQ(GetCaretBoundsForPosition(client, test_string, 1, &caret_range),
  1176. gfx::ScreenRectFromNSRect(rect));
  1177. EXPECT_EQ_RANGE(query_range, actual_range);
  1178. // Test bounds returned for empty range after the composition text are same as
  1179. // caret bounds when placed after the composition text.
  1180. query_range = NSMakeRange(kTextLength, 0);
  1181. rect = [ns_view_ firstRectForCharacterRange:query_range
  1182. actualRange:&actual_range];
  1183. EXPECT_NE(GetCaretBoundsForPosition(client, test_string, 1, &caret_range),
  1184. gfx::ScreenRectFromNSRect(rect));
  1185. EXPECT_EQ(
  1186. GetCaretBoundsForPosition(client, test_string, kTextLength, &caret_range),
  1187. gfx::ScreenRectFromNSRect(rect));
  1188. EXPECT_EQ_RANGE(query_range, actual_range);
  1189. // Query outside composition range. Ensure bounds and range corresponding to
  1190. // the current caret position are returned.
  1191. query_range = NSMakeRange(kTextLength + 1, 0);
  1192. rect = [ns_view_ firstRectForCharacterRange:query_range
  1193. actualRange:&actual_range];
  1194. EXPECT_EQ(GetCaretBounds(client), gfx::ScreenRectFromNSRect(rect));
  1195. EXPECT_EQ_RANGE(caret_range, actual_range);
  1196. // Make sure not crashing by passing null pointer instead of actualRange.
  1197. rect = [ns_view_ firstRectForCharacterRange:query_range actualRange:nullptr];
  1198. }
  1199. // Test firstRectForCharacterRange:actualRange for non-empty query ranges within
  1200. // the composition range.
  1201. TEST_F(BridgedNativeWidgetTest, TextInput_FirstRectForCharacterRange) {
  1202. InstallTextField("");
  1203. ui::TextInputClient* client = [ns_view_ textInputClient];
  1204. const std::u16string test_string = u"test_str";
  1205. const size_t kTextLength = 8;
  1206. SetCompositionText(client, test_string, 1, nullptr);
  1207. // Query bounds for the whole composition string.
  1208. NSRange query_range = NSMakeRange(0, kTextLength);
  1209. NSRange actual_range;
  1210. NSRect rect = [ns_view_ firstRectForCharacterRange:query_range
  1211. actualRange:&actual_range];
  1212. EXPECT_EQ(GetExpectedBoundsForRange(client, test_string, query_range),
  1213. gfx::ScreenRectFromNSRect(rect));
  1214. EXPECT_EQ_RANGE(query_range, actual_range);
  1215. // Query bounds for the substring "est_".
  1216. query_range = NSMakeRange(1, 4);
  1217. rect = [ns_view_ firstRectForCharacterRange:query_range
  1218. actualRange:&actual_range];
  1219. EXPECT_EQ(GetExpectedBoundsForRange(client, test_string, query_range),
  1220. gfx::ScreenRectFromNSRect(rect));
  1221. EXPECT_EQ_RANGE(query_range, actual_range);
  1222. }
  1223. // Test simulated codepaths for IMEs that do not always "mark" text. E.g.
  1224. // phonetic languages such as Korean and Vietnamese.
  1225. TEST_F(BridgedNativeWidgetTest, TextInput_SimulatePhoneticIme) {
  1226. Textfield* textfield = InstallTextField("");
  1227. EXPECT_TRUE([ns_view_ textInputClient]);
  1228. object_setClass(ns_view_, [InterpretKeyEventMockedBridgedContentView class]);
  1229. // Sequence of calls (and corresponding keyDown events) obtained via tracing
  1230. // with 2-Set Korean IME and pressing q, o, then Enter on the keyboard.
  1231. NSEvent* q_in_ime = UnicodeKeyDown(12, @"ㅂ");
  1232. InterpretKeyEventsCallback handle_q_in_ime = base::BindRepeating([](id view) {
  1233. [view insertText:@"ㅂ" replacementRange:NSMakeRange(NSNotFound, 0)];
  1234. });
  1235. NSEvent* o_in_ime = UnicodeKeyDown(31, @"ㅐ");
  1236. InterpretKeyEventsCallback handle_o_in_ime = base::BindRepeating([](id view) {
  1237. [view insertText:@"배" replacementRange:NSMakeRange(0, 1)];
  1238. });
  1239. InterpretKeyEventsCallback handle_return_in_ime =
  1240. base::BindRepeating([](id view) {
  1241. // When confirming the composition, AppKit repeats itself.
  1242. [view insertText:@"배" replacementRange:NSMakeRange(0, 1)];
  1243. [view insertText:@"배" replacementRange:NSMakeRange(0, 1)];
  1244. [view doCommandBySelector:@selector(insertNewLine:)];
  1245. });
  1246. // Add a hook for the KeyEvent being received by the TextfieldController. E.g.
  1247. // this is where the Omnibox would start to search when Return is pressed.
  1248. bool saw_vkey_return = false;
  1249. SetHandleKeyEventCallback(base::BindRepeating(
  1250. [](bool* saw_return, Textfield* textfield, const ui::KeyEvent& event) {
  1251. if (event.key_code() == ui::VKEY_RETURN) {
  1252. EXPECT_FALSE(*saw_return);
  1253. *saw_return = true;
  1254. EXPECT_EQ(base::SysNSStringToUTF16(@"배"), textfield->GetText());
  1255. }
  1256. return false;
  1257. },
  1258. &saw_vkey_return));
  1259. EXPECT_EQ(u"", textfield->GetText());
  1260. g_fake_interpret_key_events = &handle_q_in_ime;
  1261. [ns_view_ keyDown:q_in_ime];
  1262. EXPECT_EQ(base::SysNSStringToUTF16(@"ㅂ"), textfield->GetText());
  1263. EXPECT_FALSE(saw_vkey_return);
  1264. g_fake_interpret_key_events = &handle_o_in_ime;
  1265. [ns_view_ keyDown:o_in_ime];
  1266. EXPECT_EQ(base::SysNSStringToUTF16(@"배"), textfield->GetText());
  1267. EXPECT_FALSE(saw_vkey_return);
  1268. // Note the "Enter" should not replace the replacement range, even though a
  1269. // replacement range was set.
  1270. g_fake_interpret_key_events = &handle_return_in_ime;
  1271. [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_RETURN)];
  1272. EXPECT_EQ(base::SysNSStringToUTF16(@"배"), textfield->GetText());
  1273. // VKEY_RETURN should be seen by via the unhandled key event handler (but not
  1274. // via -insertText:.
  1275. EXPECT_TRUE(saw_vkey_return);
  1276. g_fake_interpret_key_events = nullptr;
  1277. }
  1278. // Test simulated codepaths for typing 'm', 'o', 'o', Enter in the Telex IME.
  1279. // This IME does not mark text, but, unlike 2-set Korean, it re-inserts the
  1280. // entire word on each keypress, even though only the last character in the word
  1281. // can be modified. This prevents the keypress being treated as a "character"
  1282. // event (which is unavoidably unfortunate for the Undo buffer), but also led to
  1283. // a codepath that suppressed a VKEY_RETURN when it should not, since there is
  1284. // no candidate IME window to dismiss for this IME.
  1285. TEST_F(BridgedNativeWidgetTest, TextInput_SimulateTelexMoo) {
  1286. Textfield* textfield = InstallTextField("");
  1287. EXPECT_TRUE([ns_view_ textInputClient]);
  1288. EnterAcceleratorView* enter_view = new EnterAcceleratorView();
  1289. textfield->parent()->AddChildView(enter_view);
  1290. // Sequence of calls (and corresponding keyDown events) obtained via tracing
  1291. // with Telex IME and pressing 'm', 'o', 'o', then Enter on the keyboard.
  1292. // Note that without the leading 'm', only one character changes, which could
  1293. // allow the keypress to be treated as a character event, which would not
  1294. // produce the bug.
  1295. NSEvent* m_in_ime = UnicodeKeyDown(46, @"m");
  1296. InterpretKeyEventsCallback handle_m_in_ime = base::BindRepeating([](id view) {
  1297. [view insertText:@"m" replacementRange:NSMakeRange(NSNotFound, 0)];
  1298. });
  1299. // Note that (unlike Korean IME), Telex generates a latin "o" for both events:
  1300. // it doesn't associate a unicode character on the second NSEvent.
  1301. NSEvent* o_in_ime = UnicodeKeyDown(31, @"o");
  1302. InterpretKeyEventsCallback handle_first_o_in_ime =
  1303. base::BindRepeating([](id view) {
  1304. // Note the whole word is replaced, not just the last character.
  1305. [view insertText:@"mo" replacementRange:NSMakeRange(0, 1)];
  1306. });
  1307. InterpretKeyEventsCallback handle_second_o_in_ime =
  1308. base::BindRepeating([](id view) {
  1309. [view insertText:@"mô" replacementRange:NSMakeRange(0, 2)];
  1310. });
  1311. InterpretKeyEventsCallback handle_return_in_ime =
  1312. base::BindRepeating([](id view) {
  1313. // Note the previous -insertText: repeats, even though it is unchanged.
  1314. // But the IME also follows with an -insertNewLine:.
  1315. [view insertText:@"mô" replacementRange:NSMakeRange(0, 2)];
  1316. [view doCommandBySelector:@selector(insertNewLine:)];
  1317. });
  1318. EXPECT_EQ(u"", textfield->GetText());
  1319. EXPECT_EQ(0, enter_view->count());
  1320. object_setClass(ns_view_, [InterpretKeyEventMockedBridgedContentView class]);
  1321. g_fake_interpret_key_events = &handle_m_in_ime;
  1322. [ns_view_ keyDown:m_in_ime];
  1323. EXPECT_EQ(base::SysNSStringToUTF16(@"m"), textfield->GetText());
  1324. EXPECT_EQ(0, enter_view->count());
  1325. g_fake_interpret_key_events = &handle_first_o_in_ime;
  1326. [ns_view_ keyDown:o_in_ime];
  1327. EXPECT_EQ(base::SysNSStringToUTF16(@"mo"), textfield->GetText());
  1328. EXPECT_EQ(0, enter_view->count());
  1329. g_fake_interpret_key_events = &handle_second_o_in_ime;
  1330. [ns_view_ keyDown:o_in_ime];
  1331. EXPECT_EQ(base::SysNSStringToUTF16(@"mô"), textfield->GetText());
  1332. EXPECT_EQ(0, enter_view->count());
  1333. g_fake_interpret_key_events = &handle_return_in_ime;
  1334. [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_RETURN)];
  1335. EXPECT_EQ(base::SysNSStringToUTF16(@"mô"),
  1336. textfield->GetText()); // No change.
  1337. EXPECT_EQ(1, enter_view->count()); // Now we see the accelerator.
  1338. }
  1339. // Simulate 'a' and candidate selection keys. This should just insert "啊",
  1340. // suppressing accelerators.
  1341. TEST_F(BridgedNativeWidgetTest, TextInput_NoAcceleratorPinyinSelectWord) {
  1342. Textfield* textfield = InstallTextField("");
  1343. EXPECT_TRUE([ns_view_ textInputClient]);
  1344. EnterAcceleratorView* enter_view = new EnterAcceleratorView();
  1345. textfield->parent()->AddChildView(enter_view);
  1346. // Sequence of calls (and corresponding keyDown events) obtained via tracing
  1347. // with Pinyin IME and pressing 'a', Tab, PageDown, PageUp, Right, Down, Left,
  1348. // and finally Up on the keyboard.
  1349. // Note 0 is the actual keyCode for 'a', not a placeholder.
  1350. NSEvent* a_in_ime = UnicodeKeyDown(0, @"a");
  1351. InterpretKeyEventsCallback handle_a_in_ime = base::BindRepeating([](id view) {
  1352. // Pinyin does not change composition text while selecting candidate words.
  1353. [view setMarkedText:@"a"
  1354. selectedRange:NSMakeRange(1, 0)
  1355. replacementRange:NSMakeRange(NSNotFound, 0)];
  1356. });
  1357. InterpretKeyEventsCallback handle_tab_in_ime =
  1358. base::BindRepeating([](id view) {
  1359. [view setMarkedText:@"ā"
  1360. selectedRange:NSMakeRange(0, 1)
  1361. replacementRange:NSMakeRange(NSNotFound, 0)];
  1362. });
  1363. // Composition text will not change in candidate selection.
  1364. InterpretKeyEventsCallback handle_candidate_select_in_ime =
  1365. base::BindRepeating([](id view) {});
  1366. InterpretKeyEventsCallback handle_space_in_ime =
  1367. base::BindRepeating([](id view) {
  1368. // Space will confirm the composition.
  1369. [view insertText:@"啊" replacementRange:NSMakeRange(NSNotFound, 0)];
  1370. });
  1371. InterpretKeyEventsCallback handle_enter_in_ime =
  1372. base::BindRepeating([](id view) {
  1373. // Space after Space will generate -insertNewLine:.
  1374. [view doCommandBySelector:@selector(insertNewLine:)];
  1375. });
  1376. EXPECT_EQ(u"", textfield->GetText());
  1377. EXPECT_EQ(0, enter_view->count());
  1378. object_setClass(ns_view_, [InterpretKeyEventMockedBridgedContentView class]);
  1379. g_fake_interpret_key_events = &handle_a_in_ime;
  1380. [ns_view_ keyDown:a_in_ime];
  1381. EXPECT_EQ(base::SysNSStringToUTF16(@"a"), textfield->GetText());
  1382. EXPECT_EQ(0, enter_view->count());
  1383. g_fake_interpret_key_events = &handle_tab_in_ime;
  1384. [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_RETURN)];
  1385. EXPECT_EQ(base::SysNSStringToUTF16(@"ā"), textfield->GetText());
  1386. EXPECT_EQ(0, enter_view->count()); // Not seen as an accelerator.
  1387. // Pinyin changes candidate word on this sequence of keys without changing the
  1388. // composition text. At the end of this sequence, the word "啊" should be
  1389. // selected.
  1390. const ui::KeyboardCode key_seqence[] = {ui::VKEY_NEXT, ui::VKEY_PRIOR,
  1391. ui::VKEY_RIGHT, ui::VKEY_DOWN,
  1392. ui::VKEY_LEFT, ui::VKEY_UP};
  1393. g_fake_interpret_key_events = &handle_candidate_select_in_ime;
  1394. for (auto key : key_seqence) {
  1395. [ns_view_ keyDown:VkeyKeyDown(key)];
  1396. EXPECT_EQ(base::SysNSStringToUTF16(@"ā"),
  1397. textfield->GetText()); // No change.
  1398. EXPECT_EQ(0, enter_view->count());
  1399. }
  1400. // Space to confirm composition
  1401. g_fake_interpret_key_events = &handle_space_in_ime;
  1402. [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_SPACE)];
  1403. EXPECT_EQ(base::SysNSStringToUTF16(@"啊"), textfield->GetText());
  1404. EXPECT_EQ(0, enter_view->count());
  1405. // The next Enter should be processed as accelerator.
  1406. g_fake_interpret_key_events = &handle_enter_in_ime;
  1407. [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_RETURN)];
  1408. EXPECT_EQ(base::SysNSStringToUTF16(@"啊"), textfield->GetText());
  1409. EXPECT_EQ(1, enter_view->count());
  1410. }
  1411. // Simulate 'a', Enter in Hiragana. This should just insert "あ", suppressing
  1412. // accelerators.
  1413. TEST_F(BridgedNativeWidgetTest, TextInput_NoAcceleratorEnterComposition) {
  1414. Textfield* textfield = InstallTextField("");
  1415. EXPECT_TRUE([ns_view_ textInputClient]);
  1416. EnterAcceleratorView* enter_view = new EnterAcceleratorView();
  1417. textfield->parent()->AddChildView(enter_view);
  1418. // Sequence of calls (and corresponding keyDown events) obtained via tracing
  1419. // with Hiragana IME and pressing 'a', then Enter on the keyboard.
  1420. // Note 0 is the actual keyCode for 'a', not a placeholder.
  1421. NSEvent* a_in_ime = UnicodeKeyDown(0, @"a");
  1422. InterpretKeyEventsCallback handle_a_in_ime = base::BindRepeating([](id view) {
  1423. // TODO(crbug/612675): |text| should be an NSAttributedString.
  1424. [view setMarkedText:@"あ"
  1425. selectedRange:NSMakeRange(1, 0)
  1426. replacementRange:NSMakeRange(NSNotFound, 0)];
  1427. });
  1428. InterpretKeyEventsCallback handle_first_return_in_ime =
  1429. base::BindRepeating([](id view) {
  1430. [view insertText:@"あ" replacementRange:NSMakeRange(NSNotFound, 0)];
  1431. // Note there is no call to -insertNewLine: here.
  1432. });
  1433. InterpretKeyEventsCallback handle_second_return_in_ime = base::BindRepeating(
  1434. [](id view) { [view doCommandBySelector:@selector(insertNewLine:)]; });
  1435. EXPECT_EQ(u"", textfield->GetText());
  1436. EXPECT_EQ(0, enter_view->count());
  1437. object_setClass(ns_view_, [InterpretKeyEventMockedBridgedContentView class]);
  1438. g_fake_interpret_key_events = &handle_a_in_ime;
  1439. [ns_view_ keyDown:a_in_ime];
  1440. EXPECT_EQ(base::SysNSStringToUTF16(@"あ"), textfield->GetText());
  1441. EXPECT_EQ(0, enter_view->count());
  1442. g_fake_interpret_key_events = &handle_first_return_in_ime;
  1443. [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_RETURN)];
  1444. EXPECT_EQ(base::SysNSStringToUTF16(@"あ"), textfield->GetText());
  1445. EXPECT_EQ(0, enter_view->count()); // Not seen as an accelerator.
  1446. g_fake_interpret_key_events = &handle_second_return_in_ime;
  1447. [ns_view_
  1448. keyDown:VkeyKeyDown(ui::VKEY_RETURN)]; // Sanity check: send Enter again.
  1449. EXPECT_EQ(base::SysNSStringToUTF16(@"あ"),
  1450. textfield->GetText()); // No change.
  1451. EXPECT_EQ(1, enter_view->count()); // Now we see the accelerator.
  1452. }
  1453. // Simulate 'a', Tab, Enter, Enter in Hiragana. This should just insert "a",
  1454. // suppressing accelerators.
  1455. TEST_F(BridgedNativeWidgetTest, TextInput_NoAcceleratorTabEnterComposition) {
  1456. Textfield* textfield = InstallTextField("");
  1457. EXPECT_TRUE([ns_view_ textInputClient]);
  1458. EnterAcceleratorView* enter_view = new EnterAcceleratorView();
  1459. textfield->parent()->AddChildView(enter_view);
  1460. // Sequence of calls (and corresponding keyDown events) obtained via tracing
  1461. // with Hiragana IME and pressing 'a', Tab, then Enter on the keyboard.
  1462. NSEvent* a_in_ime = UnicodeKeyDown(0, @"a");
  1463. InterpretKeyEventsCallback handle_a_in_ime = base::BindRepeating([](id view) {
  1464. // TODO(crbug/612675): |text| should have an underline.
  1465. [view setMarkedText:@"あ"
  1466. selectedRange:NSMakeRange(1, 0)
  1467. replacementRange:NSMakeRange(NSNotFound, 0)];
  1468. });
  1469. InterpretKeyEventsCallback handle_tab_in_ime =
  1470. base::BindRepeating([](id view) {
  1471. // TODO(crbug/612675): |text| should be an NSAttributedString (now with
  1472. // a different underline color).
  1473. [view setMarkedText:@"a"
  1474. selectedRange:NSMakeRange(0, 1)
  1475. replacementRange:NSMakeRange(NSNotFound, 0)];
  1476. // Note there is no -insertTab: generated.
  1477. });
  1478. InterpretKeyEventsCallback handle_first_return_in_ime =
  1479. base::BindRepeating([](id view) {
  1480. // Do *nothing*. Enter does not confirm nor change the composition, it
  1481. // just dismisses the IME window, leaving the text marked.
  1482. });
  1483. InterpretKeyEventsCallback handle_second_return_in_ime =
  1484. base::BindRepeating([](id view) {
  1485. // The second return will confirm the composition.
  1486. [view insertText:@"a" replacementRange:NSMakeRange(NSNotFound, 0)];
  1487. });
  1488. InterpretKeyEventsCallback handle_third_return_in_ime =
  1489. base::BindRepeating([](id view) {
  1490. // Only the third return will generate -insertNewLine:.
  1491. [view doCommandBySelector:@selector(insertNewLine:)];
  1492. });
  1493. EXPECT_EQ(u"", textfield->GetText());
  1494. EXPECT_EQ(0, enter_view->count());
  1495. object_setClass(ns_view_, [InterpretKeyEventMockedBridgedContentView class]);
  1496. g_fake_interpret_key_events = &handle_a_in_ime;
  1497. [ns_view_ keyDown:a_in_ime];
  1498. EXPECT_EQ(base::SysNSStringToUTF16(@"あ"), textfield->GetText());
  1499. EXPECT_EQ(0, enter_view->count());
  1500. g_fake_interpret_key_events = &handle_tab_in_ime;
  1501. [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_TAB)];
  1502. // Tab will switch to a Romanji (Latin) character.
  1503. EXPECT_EQ(base::SysNSStringToUTF16(@"a"), textfield->GetText());
  1504. EXPECT_EQ(0, enter_view->count());
  1505. g_fake_interpret_key_events = &handle_first_return_in_ime;
  1506. [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_RETURN)];
  1507. // Enter just dismisses the IME window. The composition is still active.
  1508. EXPECT_EQ(base::SysNSStringToUTF16(@"a"), textfield->GetText());
  1509. EXPECT_EQ(0, enter_view->count()); // Not seen as an accelerator.
  1510. g_fake_interpret_key_events = &handle_second_return_in_ime;
  1511. [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_RETURN)];
  1512. // Enter now confirms the composition (unmarks text). Note there is still no
  1513. // IME window visible but, since there is marked text, IME is still active.
  1514. EXPECT_EQ(base::SysNSStringToUTF16(@"a"), textfield->GetText());
  1515. EXPECT_EQ(0, enter_view->count()); // Not seen as an accelerator.
  1516. g_fake_interpret_key_events = &handle_third_return_in_ime;
  1517. [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_RETURN)]; // Send Enter a third time.
  1518. EXPECT_EQ(base::SysNSStringToUTF16(@"a"),
  1519. textfield->GetText()); // No change.
  1520. EXPECT_EQ(1, enter_view->count()); // Now we see the accelerator.
  1521. }
  1522. // Test a codepath that could hypothetically cause [NSApp updateWindows] to be
  1523. // called recursively due to IME dismissal during teardown triggering a focus
  1524. // change. Twice.
  1525. TEST_F(BridgedNativeWidgetTest, TextInput_RecursiveUpdateWindows) {
  1526. Textfield* textfield = InstallTextField("");
  1527. EXPECT_TRUE([ns_view_ textInputClient]);
  1528. object_setClass(ns_view_, [InterpretKeyEventMockedBridgedContentView class]);
  1529. base::mac::ScopedObjCClassSwizzler update_windows_swizzler(
  1530. [NSApplication class], [UpdateWindowsDonorForNSApp class],
  1531. @selector(updateWindows));
  1532. base::mac::ScopedObjCClassSwizzler current_input_context_swizzler(
  1533. [NSTextInputContext class],
  1534. [CurrentInputContextDonorForNSTextInputContext class],
  1535. @selector(currentInputContext));
  1536. int vkey_return_count = 0;
  1537. // Everything happens with this one event.
  1538. NSEvent* return_with_fake_ime = cocoa_test_event_utils::SynthesizeKeyEvent(
  1539. widget_->GetNativeWindow().GetNativeNSWindow(), true, ui::VKEY_RETURN, 0);
  1540. InterpretKeyEventsCallback generate_return_and_fake_ime = base::BindRepeating(
  1541. [](int* saw_return_count, id view) {
  1542. EXPECT_EQ(0, *saw_return_count);
  1543. // First generate the return to simulate an input context change.
  1544. [view insertText:@"\r" replacementRange:NSMakeRange(NSNotFound, 0)];
  1545. EXPECT_EQ(1, *saw_return_count);
  1546. },
  1547. &vkey_return_count);
  1548. bool saw_update_windows = false;
  1549. base::RepeatingClosure update_windows_closure = base::BindRepeating(
  1550. [](bool* saw_update_windows, BridgedContentView* view,
  1551. NativeWidgetMacNSWindowHost* host, Textfield* textfield) {
  1552. // Ensure updateWindows is not invoked recursively.
  1553. EXPECT_FALSE(*saw_update_windows);
  1554. *saw_update_windows = true;
  1555. // Inside updateWindows, assume the IME got dismissed and wants to
  1556. // insert its last bit of text for the old input context.
  1557. [view insertText:@"배" replacementRange:NSMakeRange(0, 1)];
  1558. // This is triggered by the setTextInputClient:nullptr in
  1559. // SetHandleKeyEventCallback(), so -inputContext should also be nil.
  1560. EXPECT_FALSE([view inputContext]);
  1561. // Ensure we can't recursively call updateWindows. A TextInputClient
  1562. // reacting to InsertChar could theoretically do this, but toolkit-views
  1563. // DCHECKs if there is recursive event dispatch, so call
  1564. // setTextInputClient directly.
  1565. host->text_input_host()->SetTextInputClient(textfield);
  1566. // Finally simulate what -[NSApp updateWindows] should _actually_ do,
  1567. // which is to update the input context (from the first responder).
  1568. g_fake_current_input_context = [view inputContext];
  1569. // Now, the |textfield| set above should have been set again.
  1570. EXPECT_TRUE(g_fake_current_input_context);
  1571. },
  1572. &saw_update_windows, ns_view_, GetNSWindowHost(), textfield);
  1573. SetHandleKeyEventCallback(base::BindRepeating(
  1574. [](int* saw_return_count, BridgedContentView* view,
  1575. NativeWidgetMacNSWindowHost* host, Textfield* textfield,
  1576. const ui::KeyEvent& event) {
  1577. if (event.key_code() == ui::VKEY_RETURN) {
  1578. *saw_return_count += 1;
  1579. // Simulate Textfield::OnBlur() by clearing the input method.
  1580. // Textfield needs to be in a Widget to do this normally.
  1581. host->text_input_host()->SetTextInputClient(nullptr);
  1582. }
  1583. return false;
  1584. },
  1585. &vkey_return_count, ns_view_, GetNSWindowHost()));
  1586. // Starting text (just insert it).
  1587. [ns_view_ insertText:@"ㅂ" replacementRange:NSMakeRange(NSNotFound, 0)];
  1588. EXPECT_EQ(base::SysNSStringToUTF16(@"ㅂ"), textfield->GetText());
  1589. g_fake_interpret_key_events = &generate_return_and_fake_ime;
  1590. g_update_windows_closure = &update_windows_closure;
  1591. g_fake_current_input_context = [ns_view_ inputContext];
  1592. EXPECT_TRUE(g_fake_current_input_context);
  1593. [ns_view_ keyDown:return_with_fake_ime];
  1594. // We should see one VKEY_RETURNs and one updateWindows. In particular, note
  1595. // that there is not a second VKEY_RETURN seen generated by keyDown: thinking
  1596. // the event has been unhandled. This is because it was handled when the fake
  1597. // IME sent \r.
  1598. EXPECT_TRUE(saw_update_windows);
  1599. EXPECT_EQ(1, vkey_return_count);
  1600. // The text inserted during updateWindows should have been inserted, even
  1601. // though we were trying to change the input context.
  1602. EXPECT_EQ(base::SysNSStringToUTF16(@"배"), textfield->GetText());
  1603. EXPECT_TRUE(g_fake_current_input_context);
  1604. g_fake_current_input_context = nullptr;
  1605. g_fake_interpret_key_events = nullptr;
  1606. g_update_windows_closure = nullptr;
  1607. }
  1608. // Write selection text to the pasteboard.
  1609. TEST_F(BridgedNativeWidgetTest, TextInput_WriteToPasteboard) {
  1610. const std::string test_string = "foo bar baz";
  1611. InstallTextField(test_string);
  1612. NSArray* types =
  1613. @[ NSStringPboardType, base::mac::CFToNSCast(kUTTypeUTF8PlainText) ];
  1614. // Try to write with no selection. This will succeed, but the string will be
  1615. // empty.
  1616. {
  1617. NSPasteboard* pboard = [NSPasteboard pasteboardWithUniqueName];
  1618. BOOL wrote_to_pboard = [ns_view_ writeSelectionToPasteboard:pboard
  1619. types:types];
  1620. EXPECT_TRUE(wrote_to_pboard);
  1621. NSArray* objects = [pboard readObjectsForClasses:@ [[NSString class]]
  1622. options:0];
  1623. EXPECT_EQ(1u, [objects count]);
  1624. EXPECT_NSEQ(@"", [objects lastObject]);
  1625. }
  1626. // Write a selection successfully.
  1627. {
  1628. SetSelectionRange(NSMakeRange(4, 7));
  1629. NSPasteboard* pboard = [NSPasteboard pasteboardWithUniqueName];
  1630. BOOL wrote_to_pboard = [ns_view_ writeSelectionToPasteboard:pboard
  1631. types:types];
  1632. EXPECT_TRUE(wrote_to_pboard);
  1633. NSArray* objects = [pboard readObjectsForClasses:@ [[NSString class]]
  1634. options:0];
  1635. EXPECT_EQ(1u, [objects count]);
  1636. EXPECT_NSEQ(@"bar baz", [objects lastObject]);
  1637. }
  1638. }
  1639. TEST_F(BridgedNativeWidgetTest, WriteToFindPasteboard) {
  1640. base::mac::ScopedObjCClassSwizzler swizzler([FindPasteboard class],
  1641. [MockFindPasteboard class],
  1642. @selector(sharedInstance));
  1643. EXPECT_NSEQ(@"", [[FindPasteboard sharedInstance] findText]);
  1644. const std::string test_string = "foo bar baz";
  1645. InstallTextField(test_string);
  1646. SetSelectionRange(NSMakeRange(4, 7));
  1647. [ns_view_ copyToFindPboard:nil];
  1648. EXPECT_NSEQ(@"bar baz", [[FindPasteboard sharedInstance] findText]);
  1649. // Don't overwrite with empty selection
  1650. SetSelectionRange(NSMakeRange(0, 0));
  1651. [ns_view_ copyToFindPboard:nil];
  1652. EXPECT_NSEQ(@"bar baz", [[FindPasteboard sharedInstance] findText]);
  1653. }
  1654. } // namespace test
  1655. } // namespace views