SPM: Fix ExtensionViewHost key event not working

The problem is similar to WebDialogView in https://crbug.com/919183
that ExtensionViewHost does return a proper "handled/procssed" flag
for the unhandled key events. The CL makes it to return the flag
from ExtensionView instead of always returning true.

Bug: 920713
Change-Id: I9ee61c0ba5b390e43a20fce38d7393e26fe3d847
Reviewed-on: https://chromium-review.googlesource.com/c/1407196
Commit-Queue: Xiyuan Xia <xiyuan@chromium.org>
Reviewed-by: Ken Rockot <rockot@google.com>
Reviewed-by: Michael Wasserman <msw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#622513}
diff --git a/chrome/browser/extensions/extension_view_host.cc b/chrome/browser/extensions/extension_view_host.cc
index 56573ba..31c170e 100644
--- a/chrome/browser/extensions/extension_view_host.cc
+++ b/chrome/browser/extensions/extension_view_host.cc
@@ -107,10 +107,10 @@
   }
 }
 
-void ExtensionViewHost::UnhandledKeyboardEvent(
+bool ExtensionViewHost::UnhandledKeyboardEvent(
     WebContents* source,
     const content::NativeWebKeyboardEvent& event) {
-  view_->HandleKeyboardEvent(source, event);
+  return view_->HandleKeyboardEvent(source, event);
 }
 
 // ExtensionHost overrides:
@@ -203,8 +203,7 @@
       return true;
     }
   }
-  UnhandledKeyboardEvent(source, event);
-  return true;
+  return UnhandledKeyboardEvent(source, event);
 }
 
 bool ExtensionViewHost::PreHandleGestureEvent(
diff --git a/chrome/browser/extensions/extension_view_host.h b/chrome/browser/extensions/extension_view_host.h
index fb034c1..d86583f 100644
--- a/chrome/browser/extensions/extension_view_host.h
+++ b/chrome/browser/extensions/extension_view_host.h
@@ -54,8 +54,8 @@
 
   // Handles keyboard events that were not handled by HandleKeyboardEvent().
   // Platform specific implementation may override this method to handle the
-  // event in platform specific way.
-  virtual void UnhandledKeyboardEvent(
+  // event in platform specific way. Returns whether the events are handled.
+  virtual bool UnhandledKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event);
 
diff --git a/chrome/browser/ui/views/content_test_utils.cc b/chrome/browser/ui/views/content_test_utils.cc
new file mode 100644
index 0000000..ac5d7b9
--- /dev/null
+++ b/chrome/browser/ui/views/content_test_utils.cc
@@ -0,0 +1,61 @@
+// Copyright 2019 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 "chrome/browser/ui/views/content_test_utils.h"
+
+#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/public/test/browser_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/ui_base_features.h"
+#include "ui/events/test/event_generator.h"
+
+#if defined(USE_AURA)
+#include "ui/aura/window.h"
+#endif  // defined(USE_AURA)
+
+namespace {
+
+// Helper to use inside a loop instead of using RunLoop::RunUntilIdle() to avoid
+// the loop being a busy loop that prevents renderer from doing its job. Use
+// only when there is no better way to synchronize.
+void GiveItSomeTime(base::TimeDelta delta) {
+  base::RunLoop run_loop;
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, run_loop.QuitClosure(), delta);
+  run_loop.Run();
+}
+
+}  // namespace
+
+void TestTextInputViaKeyEvent(content::WebContents* contents) {
+  // Replace the dialog content with a single text input element and focus it.
+  ASSERT_TRUE(content::WaitForLoadStop(contents));
+  ASSERT_TRUE(content::ExecuteScript(contents, R"(
+    document.body.innerHTML = '<input type="text" id="text-id">';
+    document.getElementById('text-id').focus();
+  )"));
+
+  // Generate a key event for 'a'.
+  gfx::NativeWindow event_window = contents->GetTopLevelNativeWindow();
+#if defined(USE_AURA)
+  event_window = event_window->GetRootWindow();
+#endif
+  if (features::IsUsingWindowService())
+    event_window = nullptr;
+
+  ui::test::EventGenerator generator(event_window);
+  generator.PressKey(ui::VKEY_A, ui::EF_NONE);
+
+  // Verify text input is updated.
+  std::string result;
+  while (result != "a") {
+    GiveItSomeTime(base::TimeDelta::FromMilliseconds(100));
+
+    ASSERT_TRUE(content::ExecuteScriptAndExtractString(contents, R"(
+      window.domAutomationController.send(
+          document.getElementById('text-id').value);
+    )", &result));
+  }
+}
diff --git a/chrome/browser/ui/views/content_test_utils.h b/chrome/browser/ui/views/content_test_utils.h
new file mode 100644
index 0000000..b3b3070
--- /dev/null
+++ b/chrome/browser/ui/views/content_test_utils.h
@@ -0,0 +1,18 @@
+// Copyright 2019 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.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_CONTENT_TEST_UTILS_H_
+#define CHROME_BROWSER_UI_VIEWS_CONTENT_TEST_UTILS_H_
+
+#include "base/time/time.h"
+
+namespace content {
+class WebContents;
+}
+
+// Tests that text input in |contents| can be updated via key events. Note this
+// function is destructive and replaces the page in |contents| with a input.
+void TestTextInputViaKeyEvent(content::WebContents* contents);
+
+#endif  // CHROME_BROWSER_UI_VIEWS_CONTENT_TEST_UTILS_H_
diff --git a/chrome/browser/ui/views/extensions/extension_dialog_browsertest.cc b/chrome/browser/ui/views/extensions/extension_dialog_browsertest.cc
new file mode 100644
index 0000000..9e5ea4c
--- /dev/null
+++ b/chrome/browser/ui/views/extensions/extension_dialog_browsertest.cc
@@ -0,0 +1,38 @@
+// Copyright 2019 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 "chrome/browser/ui/views/extensions/extension_dialog.h"
+
+#include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/extensions/extension_view_host.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/views/content_test_utils.h"
+#include "chrome/browser/ui/views/extensions/extension_dialog.h"
+#include "extensions/test/extension_test_message_listener.h"
+
+namespace {
+
+using ExtensionDialogTest = extensions::ExtensionBrowserTest;
+
+IN_PROC_BROWSER_TEST_F(ExtensionDialogTest, TextInputViaKeyEvent) {
+  ExtensionTestMessageListener init_listener("ready", /*will_reply=*/false);
+
+  scoped_refptr<const extensions::Extension> extension =
+      LoadExtension(test_data_dir_.AppendASCII("uitest/tab_traversal"));
+  ASSERT_TRUE(extension.get());
+
+  constexpr int kDialogWidth = 400;
+  constexpr int kDialogHeight = 300;
+  auto* dialog = ExtensionDialog::Show(
+      extension->url().Resolve("main.html"),
+      browser()->window()->GetNativeWindow(), browser()->profile(),
+      /*web_contents=*/nullptr, /*is_modal=*/true, kDialogWidth, kDialogHeight,
+      kDialogWidth, kDialogHeight, base::string16(), /*observer=*/nullptr);
+  ASSERT_TRUE(dialog);
+  ASSERT_TRUE(init_listener.WaitUntilSatisfied());
+
+  TestTextInputViaKeyEvent(dialog->host()->host_contents());
+}
+
+}  // namespace
diff --git a/chrome/browser/ui/views/web_dialog_view_browsertest.cc b/chrome/browser/ui/views/web_dialog_view_browsertest.cc
index 10f4834..7fab17d 100644
--- a/chrome/browser/ui/views/web_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/web_dialog_view_browsertest.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/views/content_test_utils.h"
 #include "chrome/browser/ui/webui/chrome_web_contents_handler.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -21,18 +22,11 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
-#include "ui/base/ui_base_features.h"
-#include "ui/events/test/event_generator.h"
 #include "ui/views/controls/webview/web_dialog_view.h"
 #include "ui/views/widget/widget.h"
 #include "ui/web_dialogs/test/test_web_dialog_delegate.h"
 
-#if defined(USE_AURA)
-#include "ui/aura/window.h"
-#endif  // defined(USE_AURA)
-
 namespace {
 
 // Initial size of WebDialog for SizeWindow test case. Note the height must be
@@ -248,39 +242,5 @@
 
 // Test that key event is translated to a text input properly.
 IN_PROC_BROWSER_TEST_F(WebDialogBrowserTest, TextInputViaKeyEvent) {
-  // Replace the dialog content with a single text input element and focus it.
-  content::WebContents* contents = view_->web_contents();
-  ASSERT_TRUE(content::WaitForLoadStop(contents));
-  ASSERT_TRUE(content::ExecuteScript(contents, R"(
-    document.body.innerHTML = '<input type="text" id="text-id">';
-    document.getElementById('text-id').focus();
-  )"));
-
-  // Generate a key event for 'a'.
-  gfx::NativeWindow event_window = view_->GetWidget()->GetNativeWindow();
-#if defined(USE_AURA)
-  event_window = event_window->GetRootWindow();
-#endif
-  if (features::IsUsingWindowService())
-    event_window = nullptr;
-
-  ui::test::EventGenerator generator(event_window);
-  generator.PressKey(ui::VKEY_A, ui::EF_NONE);
-
-  // Verify text input is updated.
-  std::string result;
-  while (result != "a") {
-    // Use PostDelayedTask instead of RunLoop::RunUntilIdle() to avoid being
-    // a busy loop that prevents renderer doing its job in time.
-    base::RunLoop run_loop;
-    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE, run_loop.QuitClosure(),
-        base::TimeDelta::FromMilliseconds(100));
-    run_loop.Run();
-
-    ASSERT_TRUE(content::ExecuteScriptAndExtractString(contents, R"(
-      window.domAutomationController.send(
-          document.getElementById('text-id').value);
-    )", &result));
-  }
+  TestTextInputViaKeyEvent(view_->web_contents());
 }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 572551d..75329b82 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -949,6 +949,8 @@
       "../browser/ui/views/chrome_cleaner_dialog_browsertest_win.cc",
       "../browser/ui/views/chrome_cleaner_reboot_dialog_browsertest_win.cc",
       "../browser/ui/views/content_setting_bubble_contents_browsertest.cc",
+      "../browser/ui/views/content_test_utils.cc",
+      "../browser/ui/views/content_test_utils.h",
       "../browser/ui/views/device_chooser_browsertest.cc",
       "../browser/ui/views/hats/hats_browsertest.cc",
       "../browser/ui/views/try_chrome_dialog_win/try_chrome_dialog_browsertest.cc",
@@ -1470,6 +1472,7 @@
         "../browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_model_browsertest_win.cc",
         "../browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_test_utils.cc",
         "../browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_test_utils.h",
+        "../browser/ui/views/extensions/extension_dialog_browsertest.cc",
         "../browser/ui/webui/media_router/media_router_dialog_controller_webui_impl_browsertest.cc",
       ]