| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/run_loop.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "content/common/frame_messages.h" |
| #include "content/common/frame_replication_state.h" |
| #include "content/common/input_messages.h" |
| #include "content/common/text_input_client_messages.h" |
| #include "content/public/browser/native_web_keyboard_event.h" |
| #include "content/public/common/web_preferences.h" |
| #include "content/public/test/render_view_test.h" |
| #include "content/renderer/render_view_impl.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/web/web_frame_content_dumper.h" |
| #include "third_party/blink/public/web/web_local_frame.h" |
| #include "third_party/blink/public/web/web_view.h" |
| |
| #include <Carbon/Carbon.h> // for the kVK_* constants. |
| #include <Cocoa/Cocoa.h> |
| |
| using blink::WebFrameContentDumper; |
| using blink::WebImeTextSpan; |
| |
| namespace content { |
| |
| NSEvent* CmdDeadKeyEvent(NSEventType type, unsigned short code) { |
| UniChar uniChar = 0; |
| switch(code) { |
| case kVK_UpArrow: |
| uniChar = NSUpArrowFunctionKey; |
| break; |
| case kVK_DownArrow: |
| uniChar = NSDownArrowFunctionKey; |
| break; |
| default: |
| CHECK(false); |
| } |
| NSString* s = [NSString stringWithFormat:@"%C", uniChar]; |
| |
| return [NSEvent keyEventWithType:type |
| location:NSZeroPoint |
| modifierFlags:NSCommandKeyMask |
| timestamp:0.0 |
| windowNumber:0 |
| context:nil |
| characters:s |
| charactersIgnoringModifiers:s |
| isARepeat:NO |
| keyCode:code]; |
| } |
| |
| // Test that cmd-up/down scrolls the page exactly if it is not intercepted by |
| // javascript. |
| TEST_F(RenderViewTest, MacTestCmdUp) { |
| const char* kRawHtml = |
| "<!DOCTYPE html>" |
| "<style>" |
| " /* Add a vertical scrollbar */" |
| " body { height: 10128px; }" |
| "</style>" |
| "<div id='keydown'></div>" |
| "<div id='scroll'></div>" |
| "<script>" |
| " var allowKeyEvents = true;" |
| " var scroll = document.getElementById('scroll');" |
| " var result = document.getElementById('keydown');" |
| " onkeydown = function(event) {" |
| " result.textContent =" |
| " event.keyCode + ',' +" |
| " event.shiftKey + ',' +" |
| " event.ctrlKey + ',' +" |
| " event.metaKey + ',' +" |
| " event.altKey;" |
| " return allowKeyEvents;" |
| " }" |
| "</script>"; |
| |
| WebPreferences prefs; |
| prefs.enable_scroll_animator = false; |
| |
| RenderViewImpl* view = static_cast<RenderViewImpl*>(view_); |
| view->OnUpdateWebPreferences(prefs); |
| |
| const int kMaxOutputCharacters = 1024; |
| std::string output; |
| |
| NSEvent* arrowDownKeyDown = CmdDeadKeyEvent(NSKeyDown, kVK_DownArrow); |
| NSEvent* arrowUpKeyDown = CmdDeadKeyEvent(NSKeyDown, kVK_UpArrow); |
| |
| // First test when javascript does not eat keypresses -- should scroll. |
| view->set_send_content_state_immediately(true); |
| LoadHTML(kRawHtml); |
| render_thread_->sink().ClearMessages(); |
| |
| const char* kArrowDownScrollDown = "40,false,false,true,false\n9844"; |
| view->OnSetEditCommandsForNextKeyEvent( |
| EditCommands(1, EditCommand("moveToEndOfDocument", ""))); |
| SendNativeKeyEvent(NativeWebKeyboardEvent(arrowDownKeyDown)); |
| base::RunLoop().RunUntilIdle(); |
| ExecuteJavaScriptForTests("scroll.textContent = window.pageYOffset"); |
| output = WebFrameContentDumper::DumpWebViewAsText(view->GetWebView(), |
| kMaxOutputCharacters) |
| .Ascii(); |
| EXPECT_EQ(kArrowDownScrollDown, output); |
| |
| const char* kArrowUpScrollUp = "38,false,false,true,false\n0"; |
| view->OnSetEditCommandsForNextKeyEvent( |
| EditCommands(1, EditCommand("moveToBeginningOfDocument", ""))); |
| SendNativeKeyEvent(NativeWebKeyboardEvent(arrowUpKeyDown)); |
| base::RunLoop().RunUntilIdle(); |
| ExecuteJavaScriptForTests("scroll.textContent = window.pageYOffset"); |
| output = WebFrameContentDumper::DumpWebViewAsText(view->GetWebView(), |
| kMaxOutputCharacters) |
| .Ascii(); |
| EXPECT_EQ(kArrowUpScrollUp, output); |
| |
| // Now let javascript eat the key events -- no scrolling should happen. |
| // Set a scroll position slightly down the page to ensure that it does not |
| // move. |
| ExecuteJavaScriptForTests("allowKeyEvents = false; window.scrollTo(0, 100)"); |
| |
| const char* kArrowDownNoScroll = "40,false,false,true,false\n100"; |
| view->OnSetEditCommandsForNextKeyEvent( |
| EditCommands(1, EditCommand("moveToEndOfDocument", ""))); |
| SendNativeKeyEvent(NativeWebKeyboardEvent(arrowDownKeyDown)); |
| base::RunLoop().RunUntilIdle(); |
| ExecuteJavaScriptForTests("scroll.textContent = window.pageYOffset"); |
| output = WebFrameContentDumper::DumpWebViewAsText(view->GetWebView(), |
| kMaxOutputCharacters) |
| .Ascii(); |
| EXPECT_EQ(kArrowDownNoScroll, output); |
| |
| const char* kArrowUpNoScroll = "38,false,false,true,false\n100"; |
| view->OnSetEditCommandsForNextKeyEvent( |
| EditCommands(1, EditCommand("moveToBeginningOfDocument", ""))); |
| SendNativeKeyEvent(NativeWebKeyboardEvent(arrowUpKeyDown)); |
| base::RunLoop().RunUntilIdle(); |
| ExecuteJavaScriptForTests("scroll.textContent = window.pageYOffset"); |
| output = WebFrameContentDumper::DumpWebViewAsText(view->GetWebView(), |
| kMaxOutputCharacters) |
| .Ascii(); |
| EXPECT_EQ(kArrowUpNoScroll, output); |
| } |
| |
| // TODO(ekaramad): This test could be removed once we do not send irrelevant |
| // IPCs from browser during the time RenderViewImpl is swapped out |
| // (https://crbug.com/669219). |
| // This test verfies that when RenderViewImpl is swapped out, handling IPCs |
| // which need a WebFrameWidget will not lead to a crash. |
| TEST_F(RenderViewTest, HandleIPCsInSwappedOutState) { |
| LoadHTML("<input/>"); |
| |
| // Normally, we have a WebFrameWidget. |
| EXPECT_TRUE(GetWebWidget()->IsWebFrameWidget()); |
| |
| // Swap out the main frame so that the frame widget is destroyed. |
| auto* view = static_cast<RenderViewImpl*>(view_); |
| auto* main_frame = view->GetMainRenderFrame(); |
| main_frame->OnMessageReceived(FrameMsg_SwapOut( |
| main_frame->GetRoutingID(), 123, true, FrameReplicationState())); |
| |
| // We no longer have a frame widget. |
| EXPECT_FALSE(GetWebWidget()->IsWebFrameWidget()); |
| |
| int routing_id = view->GetRoutingID(); |
| // Now simulate some TextInputClientMac IPCs. These will be handled by |
| // RenderWidget which forwards them to the TextInputClientObserver |
| using Range = gfx::Range; |
| using Point = gfx::Point; |
| view->OnMessageReceived( |
| TextInputClientMsg_CharacterIndexForPoint(routing_id, Point())); |
| view->OnMessageReceived( |
| TextInputClientMsg_FirstRectForCharacterRange(routing_id, Range())); |
| view->OnMessageReceived( |
| TextInputClientMsg_StringForRange(routing_id, Range())); |
| view->OnMessageReceived( |
| TextInputClientMsg_CharacterIndexForPoint(routing_id, Point())); |
| } |
| |
| } // namespace content |