autofill_popup_view.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  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. #include "shell/browser/ui/views/autofill_popup_view.h"
  5. #include <memory>
  6. #include <utility>
  7. #include "base/functional/bind.h"
  8. #include "base/i18n/rtl.h"
  9. #include "cc/paint/skia_paint_canvas.h"
  10. #include "content/public/browser/render_frame_host.h"
  11. #include "content/public/browser/render_view_host.h"
  12. #include "shell/browser/ui/autofill_popup.h"
  13. #include "ui/accessibility/ax_enums.mojom.h"
  14. #include "ui/base/dragdrop/drag_drop_types.h"
  15. #include "ui/color/color_provider.h"
  16. #include "ui/events/keycodes/keyboard_codes.h"
  17. #include "ui/gfx/canvas.h"
  18. #include "ui/gfx/geometry/point.h"
  19. #include "ui/gfx/geometry/rect.h"
  20. #include "ui/gfx/text_utils.h"
  21. #include "ui/views/border.h"
  22. #include "ui/views/focus/focus_manager.h"
  23. #include "ui/views/widget/widget.h"
  24. namespace electron {
  25. BEGIN_METADATA(AutofillPopupChildView)
  26. END_METADATA
  27. AutofillPopupView::AutofillPopupView(AutofillPopup* popup,
  28. views::Widget* parent_widget)
  29. : popup_(popup), parent_widget_(parent_widget) {
  30. CreateChildViews();
  31. SetFocusBehavior(FocusBehavior::ALWAYS);
  32. set_drag_controller(this);
  33. SetAccessibleRole(ax::mojom::Role::kMenu);
  34. SetAccessibleName(u"Autofill Menu");
  35. }
  36. AutofillPopupView::~AutofillPopupView() {
  37. if (popup_) {
  38. auto* host = popup_->frame_host_->GetRenderViewHost()->GetWidget();
  39. host->RemoveKeyPressEventCallback(keypress_callback_);
  40. popup_->view_ = nullptr;
  41. popup_ = nullptr;
  42. }
  43. RemoveObserver();
  44. if (view_proxy_.get()) {
  45. view_proxy_->ResetView();
  46. }
  47. if (GetWidget()) {
  48. GetWidget()->Close();
  49. }
  50. }
  51. void AutofillPopupView::Show() {
  52. bool visible = parent_widget_->IsVisible();
  53. visible = visible || view_proxy_;
  54. if (!popup_ || !visible || parent_widget_->IsClosed())
  55. return;
  56. const bool initialize_widget = !GetWidget();
  57. if (initialize_widget) {
  58. parent_widget_->AddObserver(this);
  59. // The widget is destroyed by the corresponding NativeWidget, so we use
  60. // a weak pointer to hold the reference and don't have to worry about
  61. // deletion.
  62. auto* widget = new views::Widget;
  63. views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
  64. params.delegate = this;
  65. params.parent = parent_widget_->GetNativeView();
  66. params.z_order = ui::ZOrderLevel::kFloatingUIElement;
  67. widget->Init(std::move(params));
  68. // No animation for popup appearance (too distracting).
  69. widget->SetVisibilityAnimationTransition(views::Widget::ANIMATE_HIDE);
  70. show_time_ = base::Time::Now();
  71. }
  72. SetBorder(views::CreateSolidBorder(
  73. kPopupBorderThickness,
  74. GetColorProvider()->GetColor(ui::kColorUnfocusedBorder)));
  75. DoUpdateBoundsAndRedrawPopup();
  76. GetWidget()->Show();
  77. if (initialize_widget)
  78. views::WidgetFocusManager::GetInstance()->AddFocusChangeListener(this);
  79. keypress_callback_ = base::BindRepeating(
  80. &AutofillPopupView::HandleKeyPressEvent, base::Unretained(this));
  81. auto* host = popup_->frame_host_->GetRenderViewHost()->GetWidget();
  82. host->AddKeyPressEventCallback(keypress_callback_);
  83. NotifyAccessibilityEventDeprecated(ax::mojom::Event::kMenuStart, true);
  84. }
  85. void AutofillPopupView::Hide() {
  86. if (popup_) {
  87. auto* host = popup_->frame_host_->GetRenderViewHost()->GetWidget();
  88. host->RemoveKeyPressEventCallback(keypress_callback_);
  89. popup_ = nullptr;
  90. }
  91. RemoveObserver();
  92. NotifyAccessibilityEventDeprecated(ax::mojom::Event::kMenuEnd, true);
  93. if (GetWidget()) {
  94. GetWidget()->Close();
  95. }
  96. }
  97. void AutofillPopupView::OnSuggestionsChanged() {
  98. if (!popup_)
  99. return;
  100. CreateChildViews();
  101. if (popup_->line_count() == 0) {
  102. popup_->Hide();
  103. return;
  104. }
  105. DoUpdateBoundsAndRedrawPopup();
  106. }
  107. void AutofillPopupView::WriteDragDataForView(views::View*,
  108. const gfx::Point&,
  109. ui::OSExchangeData*) {}
  110. int AutofillPopupView::GetDragOperationsForView(views::View*,
  111. const gfx::Point&) {
  112. return ui::DragDropTypes::DRAG_NONE;
  113. }
  114. bool AutofillPopupView::CanStartDragForView(views::View*,
  115. const gfx::Point&,
  116. const gfx::Point&) {
  117. return false;
  118. }
  119. void AutofillPopupView::OnSelectedRowChanged(
  120. std::optional<int> previous_row_selection,
  121. std::optional<int> current_row_selection) {
  122. SchedulePaint();
  123. if (current_row_selection) {
  124. int selected = current_row_selection.value_or(-1);
  125. if (selected == -1 || static_cast<size_t>(selected) >= children().size())
  126. return;
  127. children().at(selected)->NotifyAccessibilityEventDeprecated(
  128. ax::mojom::Event::kSelection, true);
  129. }
  130. }
  131. void AutofillPopupView::DrawAutofillEntry(gfx::Canvas* canvas,
  132. int index,
  133. const gfx::Rect& entry_rect) {
  134. if (!popup_)
  135. return;
  136. canvas->FillRect(entry_rect, GetColorProvider()->GetColor(
  137. popup_->GetBackgroundColorIDForRow(index)));
  138. const bool is_rtl = base::i18n::IsRTL();
  139. const int text_align =
  140. is_rtl ? gfx::Canvas::TEXT_ALIGN_RIGHT : gfx::Canvas::TEXT_ALIGN_LEFT;
  141. gfx::Rect value_rect = entry_rect;
  142. value_rect.Inset(gfx::Insets::VH(0, kEndPadding));
  143. int x_align_left = value_rect.x();
  144. const int value_width = gfx::GetStringWidth(
  145. popup_->value_at(index), popup_->GetValueFontListForRow(index));
  146. int value_x_align_left = x_align_left;
  147. value_x_align_left =
  148. is_rtl ? value_rect.right() - value_width : value_rect.x();
  149. canvas->DrawStringRectWithFlags(
  150. popup_->value_at(index), popup_->GetValueFontListForRow(index),
  151. GetColorProvider()->GetColor(ui::kColorResultsTableNormalText),
  152. gfx::Rect(value_x_align_left, value_rect.y(), value_width,
  153. value_rect.height()),
  154. text_align);
  155. // Draw the label text, if one exists.
  156. if (auto const& label = popup_->label_at(index); !label.empty()) {
  157. const int label_width =
  158. gfx::GetStringWidth(label, popup_->GetLabelFontListForRow(index));
  159. int label_x_align_left = x_align_left;
  160. label_x_align_left =
  161. is_rtl ? value_rect.x() : value_rect.right() - label_width;
  162. canvas->DrawStringRectWithFlags(
  163. label, popup_->GetLabelFontListForRow(index),
  164. GetColorProvider()->GetColor(ui::kColorResultsTableDimmedText),
  165. gfx::Rect(label_x_align_left, entry_rect.y(), label_width,
  166. entry_rect.height()),
  167. text_align);
  168. }
  169. }
  170. void AutofillPopupView::CreateChildViews() {
  171. if (!popup_)
  172. return;
  173. RemoveAllChildViews();
  174. for (int i = 0; i < popup_->line_count(); ++i) {
  175. auto* child_view = new AutofillPopupChildView(popup_->value_at(i));
  176. child_view->set_drag_controller(this);
  177. AddChildView(child_view);
  178. }
  179. }
  180. void AutofillPopupView::DoUpdateBoundsAndRedrawPopup() {
  181. if (!popup_)
  182. return;
  183. // Clamp popup_bounds_ to ensure it's never zero-width.
  184. popup_->popup_bounds_.Union(
  185. gfx::Rect(popup_->popup_bounds_.origin(), gfx::Size(1, 1)));
  186. GetWidget()->SetBounds(popup_->popup_bounds_);
  187. if (view_proxy_.get()) {
  188. view_proxy_->SetBounds(popup_->popup_bounds_in_view());
  189. }
  190. SchedulePaint();
  191. }
  192. void AutofillPopupView::OnPaint(gfx::Canvas* canvas) {
  193. if (!popup_ || static_cast<size_t>(popup_->line_count()) != children().size())
  194. return;
  195. gfx::Canvas* draw_canvas = canvas;
  196. SkBitmap bitmap;
  197. std::unique_ptr<cc::SkiaPaintCanvas> paint_canvas;
  198. if (view_proxy_.get()) {
  199. bitmap.allocN32Pixels(popup_->popup_bounds_in_view().width(),
  200. popup_->popup_bounds_in_view().height(), true);
  201. paint_canvas = std::make_unique<cc::SkiaPaintCanvas>(bitmap);
  202. draw_canvas = new gfx::Canvas(paint_canvas.get(), 1.0);
  203. }
  204. draw_canvas->DrawColor(
  205. GetColorProvider()->GetColor(ui::kColorResultsTableNormalBackground));
  206. OnPaintBorder(draw_canvas);
  207. for (int i = 0; i < popup_->line_count(); ++i) {
  208. gfx::Rect line_rect = popup_->GetRowBounds(i);
  209. DrawAutofillEntry(draw_canvas, i, line_rect);
  210. }
  211. if (view_proxy_.get()) {
  212. view_proxy_->SetBounds(popup_->popup_bounds_in_view());
  213. view_proxy_->SetBitmap(bitmap);
  214. }
  215. }
  216. void AutofillPopupView::OnMouseCaptureLost() {
  217. ClearSelection();
  218. }
  219. bool AutofillPopupView::OnMouseDragged(const ui::MouseEvent& event) {
  220. if (HitTestPoint(event.location())) {
  221. SetSelection(event.location());
  222. // We must return true in order to get future OnMouseDragged and
  223. // OnMouseReleased events.
  224. return true;
  225. }
  226. // If we move off of the popup, we lose the selection.
  227. ClearSelection();
  228. return false;
  229. }
  230. void AutofillPopupView::OnMouseExited(const ui::MouseEvent& event) {
  231. // Pressing return causes the cursor to hide, which will generate an
  232. // OnMouseExited event. Pressing return should activate the current selection
  233. // via AcceleratorPressed, so we need to let that run first.
  234. base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
  235. FROM_HERE, base::BindOnce(&AutofillPopupView::ClearSelection,
  236. weak_ptr_factory_.GetWeakPtr()));
  237. }
  238. void AutofillPopupView::OnMouseMoved(const ui::MouseEvent& event) {
  239. // A synthesized mouse move will be sent when the popup is first shown.
  240. // Don't preview a suggestion if the mouse happens to be hovering there.
  241. #if BUILDFLAG(IS_WIN)
  242. if (base::Time::Now() - show_time_ <= base::Milliseconds(50))
  243. return;
  244. #else
  245. if (event.flags() & ui::EF_IS_SYNTHESIZED)
  246. return;
  247. #endif
  248. if (HitTestPoint(event.location()))
  249. SetSelection(event.location());
  250. else
  251. ClearSelection();
  252. }
  253. bool AutofillPopupView::OnMousePressed(const ui::MouseEvent& event) {
  254. return event.GetClickCount() == 1;
  255. }
  256. void AutofillPopupView::OnMouseReleased(const ui::MouseEvent& event) {
  257. // We only care about the left click.
  258. if (event.IsOnlyLeftMouseButton() && HitTestPoint(event.location()))
  259. AcceptSelection(event.location());
  260. }
  261. void AutofillPopupView::OnGestureEvent(ui::GestureEvent* event) {
  262. switch (event->type()) {
  263. case ui::EventType::kGestureTapDown:
  264. case ui::EventType::kGestureScrollBegin:
  265. case ui::EventType::kGestureScrollUpdate:
  266. if (HitTestPoint(event->location()))
  267. SetSelection(event->location());
  268. else
  269. ClearSelection();
  270. break;
  271. case ui::EventType::kGestureTap:
  272. case ui::EventType::kGestureScrollEnd:
  273. if (HitTestPoint(event->location()))
  274. AcceptSelection(event->location());
  275. else
  276. ClearSelection();
  277. break;
  278. case ui::EventType::kGestureTapCancel:
  279. case ui::EventType::kScrollFlingStart:
  280. ClearSelection();
  281. break;
  282. default:
  283. return;
  284. }
  285. event->SetHandled();
  286. }
  287. bool AutofillPopupView::AcceleratorPressed(const ui::Accelerator& accelerator) {
  288. if (accelerator.modifiers() != ui::EF_NONE)
  289. return false;
  290. if (accelerator.key_code() == ui::VKEY_ESCAPE) {
  291. if (popup_)
  292. popup_->Hide();
  293. return true;
  294. }
  295. if (accelerator.key_code() == ui::VKEY_RETURN)
  296. return AcceptSelectedLine();
  297. return false;
  298. }
  299. bool AutofillPopupView::HandleKeyPressEvent(
  300. const input::NativeWebKeyboardEvent& event) {
  301. if (!popup_)
  302. return false;
  303. switch (event.windows_key_code) {
  304. case ui::VKEY_UP:
  305. SelectPreviousLine();
  306. return true;
  307. case ui::VKEY_DOWN:
  308. SelectNextLine();
  309. return true;
  310. case ui::VKEY_PRIOR: // Page up.
  311. SetSelectedLine(0);
  312. return true;
  313. case ui::VKEY_NEXT: // Page down.
  314. SetSelectedLine(popup_->line_count() - 1);
  315. return true;
  316. case ui::VKEY_ESCAPE:
  317. popup_->Hide();
  318. return true;
  319. case ui::VKEY_TAB:
  320. // A tab press should cause the selected line to be accepted, but still
  321. // return false so the tab key press propagates and changes the cursor
  322. // location.
  323. AcceptSelectedLine();
  324. return false;
  325. case ui::VKEY_RETURN:
  326. return AcceptSelectedLine();
  327. default:
  328. return false;
  329. }
  330. }
  331. void AutofillPopupView::OnNativeFocusChanged(gfx::NativeView focused_now) {
  332. if (GetWidget() && GetWidget()->GetNativeView() != focused_now && popup_)
  333. popup_->Hide();
  334. }
  335. void AutofillPopupView::OnWidgetBoundsChanged(views::Widget* widget,
  336. const gfx::Rect& new_bounds) {
  337. if (widget != parent_widget_)
  338. return;
  339. if (popup_)
  340. popup_->Hide();
  341. }
  342. void AutofillPopupView::AcceptSuggestion(int index) {
  343. if (!popup_)
  344. return;
  345. popup_->AcceptSuggestion(index);
  346. popup_->Hide();
  347. }
  348. bool AutofillPopupView::AcceptSelectedLine() {
  349. if (!selected_line_ || selected_line_.value() >= popup_->line_count())
  350. return false;
  351. AcceptSuggestion(selected_line_.value());
  352. return true;
  353. }
  354. void AutofillPopupView::AcceptSelection(const gfx::Point& point) {
  355. if (!popup_)
  356. return;
  357. SetSelectedLine(popup_->LineFromY(point.y()));
  358. AcceptSelectedLine();
  359. }
  360. void AutofillPopupView::SetSelectedLine(std::optional<int> selected_line) {
  361. if (!popup_)
  362. return;
  363. if (selected_line_ == selected_line)
  364. return;
  365. if (selected_line && selected_line.value() >= popup_->line_count())
  366. return;
  367. auto previous_selected_line(selected_line_);
  368. selected_line_ = selected_line;
  369. OnSelectedRowChanged(previous_selected_line, selected_line_);
  370. }
  371. void AutofillPopupView::SetSelection(const gfx::Point& point) {
  372. if (!popup_)
  373. return;
  374. SetSelectedLine(popup_->LineFromY(point.y()));
  375. }
  376. void AutofillPopupView::SelectNextLine() {
  377. if (!popup_)
  378. return;
  379. int new_selected_line = selected_line_ ? *selected_line_ + 1 : 0;
  380. if (new_selected_line >= popup_->line_count())
  381. new_selected_line = 0;
  382. SetSelectedLine(new_selected_line);
  383. }
  384. void AutofillPopupView::SelectPreviousLine() {
  385. if (!popup_)
  386. return;
  387. int new_selected_line = selected_line_.value_or(0) - 1;
  388. if (new_selected_line < 0)
  389. new_selected_line = popup_->line_count() - 1;
  390. SetSelectedLine(new_selected_line);
  391. }
  392. void AutofillPopupView::ClearSelection() {
  393. SetSelectedLine(std::nullopt);
  394. }
  395. void AutofillPopupView::RemoveObserver() {
  396. parent_widget_->RemoveObserver(this);
  397. views::WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(this);
  398. }
  399. } // namespace electron