1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/strings/string16.h" 6 #include "base/strings/string_util.h" 7 #include "content/public/browser/native_web_keyboard_event.h" 8 #include "content/public/test/render_view_test.h" 9 #include "content/renderer/render_view_impl.h" 10 #include "testing/gtest/include/gtest/gtest.h" 11 #include "webkit/common/webpreferences.h" 12 13 #include <Cocoa/Cocoa.h> 14 #include <Carbon/Carbon.h> // for the kVK_* constants. 15 16 namespace content { 17 18 NSEvent* CmdDeadKeyEvent(NSEventType type, unsigned short code) { 19 UniChar uniChar = 0; 20 switch(code) { 21 case kVK_UpArrow: 22 uniChar = NSUpArrowFunctionKey; 23 break; 24 case kVK_DownArrow: 25 uniChar = NSDownArrowFunctionKey; 26 break; 27 default: 28 CHECK(false); 29 } 30 NSString* s = [NSString stringWithFormat:@"%C", uniChar]; 31 32 return [NSEvent keyEventWithType:type 33 location:NSZeroPoint 34 modifierFlags:NSCommandKeyMask 35 timestamp:0.0 36 windowNumber:0 37 context:nil 38 characters:s 39 charactersIgnoringModifiers:s 40 isARepeat:NO 41 keyCode:code]; 42 } 43 44 // Test that cmd-up/down scrolls the page exactly if it is not intercepted by 45 // javascript. 46 TEST_F(RenderViewTest, MacTestCmdUp) { 47 // Some preprocessor trickery so that we can have literal html in our source, 48 // makes it easier to copy html to and from an html file for testing (the 49 // preprocessor will remove the newlines at the line ends, turning this into 50 // a single long line). 51 #define HTML(s) #s 52 const char* kRawHtml = HTML( 53 <html> 54 <head><title></title> 55 <script type='text/javascript' language='javascript'> 56 function OnKeyEvent(ev) { 57 var result = document.getElementById(ev.type); 58 result.innerText = (ev.which || ev.keyCode) + ',' + 59 ev.shiftKey + ',' + 60 ev.ctrlKey + ',' + 61 ev.metaKey + ',' + 62 ev.altKey; 63 return %s; /* Replace with "return true;" when testing in an html file. */ 64 } 65 function OnScroll(ev) { 66 var result = document.getElementById("scroll"); 67 result.innerText = window.pageYOffset; 68 return true; 69 } 70 </script> 71 <style type="text/css"> 72 p { border-bottom:5000px solid black; } /* enforce vertical scroll bar */ 73 </style> 74 </head> 75 <body 76 onscroll='return OnScroll(event);' 77 onkeydown='return OnKeyEvent(event);'> 78 <div id='keydown' contenteditable='true'> </div> 79 <div id='scroll' contenteditable='true'> </div> 80 <p>p1 81 <p>p2 82 </body> 83 </html> 84 ); 85 #undef HTML 86 87 WebPreferences prefs; 88 prefs.enable_scroll_animator = false; 89 90 RenderViewImpl* view = static_cast<RenderViewImpl*>(view_); 91 view->OnUpdateWebPreferences(prefs); 92 93 const int kMaxOutputCharacters = 1024; 94 string16 output; 95 char htmlBuffer[2048]; 96 97 NSEvent* arrowDownKeyDown = CmdDeadKeyEvent(NSKeyDown, kVK_DownArrow); 98 NSEvent* arrowUpKeyDown = CmdDeadKeyEvent(NSKeyDown, kVK_UpArrow); 99 100 // First test when javascript does not eat keypresses -- should scroll. 101 sprintf(htmlBuffer, kRawHtml, "true"); 102 view->set_send_content_state_immediately(true); 103 LoadHTML(htmlBuffer); 104 render_thread_->sink().ClearMessages(); 105 106 const char* kArrowDownScrollDown = 107 "40,false,false,true,false\n10128\np1\n\np2"; 108 view->OnSetEditCommandsForNextKeyEvent( 109 EditCommands(1, EditCommand("moveToEndOfDocument", ""))); 110 SendNativeKeyEvent(NativeWebKeyboardEvent(arrowDownKeyDown)); 111 ProcessPendingMessages(); 112 output = GetMainFrame()->contentAsText(kMaxOutputCharacters); 113 EXPECT_EQ(kArrowDownScrollDown, UTF16ToASCII(output)); 114 115 const char* kArrowUpScrollUp = 116 "38,false,false,true,false\n0\np1\n\np2"; 117 view->OnSetEditCommandsForNextKeyEvent( 118 EditCommands(1, EditCommand("moveToBeginningOfDocument", ""))); 119 SendNativeKeyEvent(NativeWebKeyboardEvent(arrowUpKeyDown)); 120 ProcessPendingMessages(); 121 output = GetMainFrame()->contentAsText(kMaxOutputCharacters); 122 EXPECT_EQ(kArrowUpScrollUp, UTF16ToASCII(output)); 123 124 125 // Now let javascript eat the key events -- no scrolling should happen 126 sprintf(htmlBuffer, kRawHtml, "false"); 127 view->set_send_content_state_immediately(true); 128 LoadHTML(htmlBuffer); 129 render_thread_->sink().ClearMessages(); 130 131 const char* kArrowDownNoScroll = 132 "40,false,false,true,false\np1\n\np2"; 133 view->OnSetEditCommandsForNextKeyEvent( 134 EditCommands(1, EditCommand("moveToEndOfDocument", ""))); 135 SendNativeKeyEvent(NativeWebKeyboardEvent(arrowDownKeyDown)); 136 ProcessPendingMessages(); 137 output = GetMainFrame()->contentAsText(kMaxOutputCharacters); 138 EXPECT_EQ(kArrowDownNoScroll, UTF16ToASCII(output)); 139 140 const char* kArrowUpNoScroll = 141 "38,false,false,true,false\np1\n\np2"; 142 view->OnSetEditCommandsForNextKeyEvent( 143 EditCommands(1, EditCommand("moveToBeginningOfDocument", ""))); 144 SendNativeKeyEvent(NativeWebKeyboardEvent(arrowUpKeyDown)); 145 ProcessPendingMessages(); 146 output = GetMainFrame()->contentAsText(kMaxOutputCharacters); 147 EXPECT_EQ(kArrowUpNoScroll, UTF16ToASCII(output)); 148 } 149 150 } // namespace content 151