add datalist element view
This commit is contained in:
parent
8404bdd568
commit
f360104bee
11 changed files with 1230 additions and 0 deletions
|
@ -17,6 +17,7 @@
|
|||
#include "atom/browser/child_web_contents_tracker.h"
|
||||
#include "atom/browser/lib/bluetooth_chooser.h"
|
||||
#include "atom/browser/native_window.h"
|
||||
#include "atom/browser/native_window_views.h"
|
||||
#include "atom/browser/net/atom_network_delegate.h"
|
||||
#include "atom/browser/osr/osr_output_device.h"
|
||||
#include "atom/browser/osr/osr_render_widget_host_view.h"
|
||||
|
@ -83,6 +84,7 @@
|
|||
#include "third_party/WebKit/public/web/WebFindOptions.h"
|
||||
#include "ui/display/screen.h"
|
||||
#include "ui/events/base_event_utils.h"
|
||||
#include "ui/gfx/geometry/rect_f.h"
|
||||
|
||||
#if !defined(OS_MACOSX)
|
||||
#include "ui/aura/window.h"
|
||||
|
@ -441,6 +443,8 @@ void WebContents::InitWithSessionAndOptions(v8::Isolate* isolate,
|
|||
registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_PENDING,
|
||||
content::Source<content::NavigationController>(controller));
|
||||
|
||||
autofill_popup_ = new AutofillPopup(web_contents->GetNativeView());
|
||||
|
||||
Init(isolate);
|
||||
AttachAsUserData(web_contents);
|
||||
}
|
||||
|
@ -740,6 +744,17 @@ void WebContents::RenderViewCreated(content::RenderViewHost* render_view_host) {
|
|||
impl->disable_hidden_ = !background_throttling_;
|
||||
}
|
||||
|
||||
void WebContents::RenderFrameCreated(content::RenderFrameHost* host) {
|
||||
Send(new AtomAutofillViewHostMsg_RoutingId(
|
||||
host->GetRoutingID(), routing_id()));
|
||||
}
|
||||
|
||||
void WebContents::RenderFrameHostChanged(content::RenderFrameHost* old_host,
|
||||
content::RenderFrameHost* new_host) {
|
||||
Send(new AtomAutofillViewHostMsg_RoutingId(
|
||||
new_host->GetRoutingID(), routing_id()));
|
||||
}
|
||||
|
||||
void WebContents::RenderViewDeleted(content::RenderViewHost* render_view_host) {
|
||||
Emit("render-view-deleted", render_view_host->GetProcess()->GetID());
|
||||
}
|
||||
|
@ -976,6 +991,8 @@ bool WebContents::OnMessageReceived(const IPC::Message& message) {
|
|||
OnGetZoomLevel)
|
||||
IPC_MESSAGE_HANDLER_CODE(ViewHostMsg_SetCursor, OnCursorChange,
|
||||
handled = false)
|
||||
IPC_MESSAGE_HANDLER(AtomAutofillViewMsg_ShowPopup, OnShowAutofillPopup)
|
||||
IPC_MESSAGE_HANDLER(AtomAutofillViewMsg_HidePopup, OnHideAutofillPopup)
|
||||
IPC_MESSAGE_UNHANDLED(handled = false)
|
||||
IPC_END_MESSAGE_MAP()
|
||||
|
||||
|
@ -1608,6 +1625,24 @@ void WebContents::OnCursorChange(const content::WebCursor& cursor) {
|
|||
}
|
||||
}
|
||||
|
||||
void WebContents::OnShowAutofillPopup(
|
||||
int routing_id,
|
||||
const gfx::RectF& bounds,
|
||||
const std::vector<base::string16>& values,
|
||||
const std::vector<base::string16>& labels) {
|
||||
auto relay = reinterpret_cast<NativeWindowViews*>(
|
||||
NativeWindow::FromWebContents(web_contents()));
|
||||
autofill_popup_->CreateView(
|
||||
routing_id,
|
||||
web_contents(),
|
||||
relay->widget(),
|
||||
bounds);
|
||||
autofill_popup_->SetItems(values, labels);
|
||||
}
|
||||
void WebContents::OnHideAutofillPopup() {
|
||||
autofill_popup_->Hide();
|
||||
}
|
||||
|
||||
void WebContents::SetSize(const SetSizeParams& params) {
|
||||
if (guest_delegate_)
|
||||
guest_delegate_->SetSize(params);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "atom/browser/api/save_page_handler.h"
|
||||
#include "atom/browser/api/trackable_object.h"
|
||||
#include "atom/browser/common_web_contents_delegate.h"
|
||||
#include "atom/browser/ui/autofill_popup.h"
|
||||
#include "content/common/cursors/webcursor.h"
|
||||
#include "content/public/browser/notification_observer.h"
|
||||
#include "content/public/browser/notification_registrar.h"
|
||||
|
@ -307,6 +308,9 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
// content::WebContentsObserver:
|
||||
void BeforeUnloadFired(const base::TimeTicks& proceed_time) override;
|
||||
void RenderViewCreated(content::RenderViewHost*) override;
|
||||
void RenderFrameCreated(content::RenderFrameHost*) override;
|
||||
void RenderFrameHostChanged(content::RenderFrameHost*,
|
||||
content::RenderFrameHost*) override;
|
||||
void RenderViewDeleted(content::RenderViewHost*) override;
|
||||
void RenderProcessGone(base::TerminationStatus status) override;
|
||||
void DocumentLoadedInFrame(
|
||||
|
@ -374,6 +378,12 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
// Called when we receive a CursorChange message from chromium.
|
||||
void OnCursorChange(const content::WebCursor& cursor);
|
||||
|
||||
void OnShowAutofillPopup(int routing_id,
|
||||
const gfx::RectF& bounds,
|
||||
const std::vector<base::string16>& values,
|
||||
const std::vector<base::string16>& labels);
|
||||
void OnHideAutofillPopup();
|
||||
|
||||
// Called when received a message from renderer.
|
||||
void OnRendererMessage(const base::string16& channel,
|
||||
const base::ListValue& args);
|
||||
|
@ -397,6 +407,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
|
||||
std::unique_ptr<AtomJavaScriptDialogManager> dialog_manager_;
|
||||
std::unique_ptr<WebViewGuestDelegate> guest_delegate_;
|
||||
AutofillPopup* autofill_popup_;
|
||||
|
||||
// The host webcontents that may contain this webcontents.
|
||||
WebContents* embedder_;
|
||||
|
|
250
atom/browser/ui/autofill_popup.cc
Normal file
250
atom/browser/ui/autofill_popup.cc
Normal file
|
@ -0,0 +1,250 @@
|
|||
// 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 "atom/browser/ui/autofill_popup.h"
|
||||
#include "atom/common/api/api_messages.h"
|
||||
#include "ui/display/display.h"
|
||||
#include "ui/display/screen.h"
|
||||
#include "ui/gfx/text_utils.h"
|
||||
#include "ui/gfx/geometry/point.h"
|
||||
#include "ui/gfx/geometry/rect.h"
|
||||
#include "ui/gfx/geometry/vector2d.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
std::pair<int, int> CalculatePopupXAndWidth(
|
||||
const display::Display& left_display,
|
||||
const display::Display& right_display,
|
||||
int popup_required_width,
|
||||
const gfx::Rect& element_bounds,
|
||||
bool is_rtl) {
|
||||
int leftmost_display_x = left_display.bounds().x();
|
||||
int rightmost_display_x =
|
||||
right_display.GetSizeInPixel().width() + right_display.bounds().x();
|
||||
|
||||
// Calculate the start coordinates for the popup if it is growing right or
|
||||
// the end position if it is growing to the left, capped to screen space.
|
||||
int right_growth_start = std::max(
|
||||
leftmost_display_x, std::min(rightmost_display_x, element_bounds.x()));
|
||||
int left_growth_end =
|
||||
std::max(leftmost_display_x,
|
||||
std::min(rightmost_display_x, element_bounds.right()));
|
||||
|
||||
int right_available = rightmost_display_x - right_growth_start;
|
||||
int left_available = left_growth_end - leftmost_display_x;
|
||||
|
||||
int popup_width =
|
||||
std::min(popup_required_width, std::max(right_available, left_available));
|
||||
|
||||
std::pair<int, int> grow_right(right_growth_start, popup_width);
|
||||
std::pair<int, int> grow_left(left_growth_end - popup_width, popup_width);
|
||||
|
||||
// Prefer to grow towards the end (right for LTR, left for RTL). But if there
|
||||
// is not enough space available in the desired direction and more space in
|
||||
// the other direction, reverse it.
|
||||
if (is_rtl) {
|
||||
return left_available >= popup_width || left_available >= right_available
|
||||
? grow_left
|
||||
: grow_right;
|
||||
}
|
||||
return right_available >= popup_width || right_available >= left_available
|
||||
? grow_right
|
||||
: grow_left;
|
||||
}
|
||||
|
||||
std::pair<int, int> CalculatePopupYAndHeight(
|
||||
const display::Display& top_display,
|
||||
const display::Display& bottom_display,
|
||||
int popup_required_height,
|
||||
const gfx::Rect& element_bounds) {
|
||||
int topmost_display_y = top_display.bounds().y();
|
||||
int bottommost_display_y =
|
||||
bottom_display.GetSizeInPixel().height() + bottom_display.bounds().y();
|
||||
|
||||
// Calculate the start coordinates for the popup if it is growing down or
|
||||
// the end position if it is growing up, capped to screen space.
|
||||
int top_growth_end = std::max(
|
||||
topmost_display_y, std::min(bottommost_display_y, element_bounds.y()));
|
||||
int bottom_growth_start =
|
||||
std::max(topmost_display_y,
|
||||
std::min(bottommost_display_y, element_bounds.bottom()));
|
||||
|
||||
int top_available = bottom_growth_start - topmost_display_y;
|
||||
int bottom_available = bottommost_display_y - top_growth_end;
|
||||
|
||||
// TODO(csharp): Restrict the popup height to what is available.
|
||||
if (bottom_available >= popup_required_height ||
|
||||
bottom_available >= top_available) {
|
||||
// The popup can appear below the field.
|
||||
return std::make_pair(bottom_growth_start, popup_required_height);
|
||||
} else {
|
||||
// The popup must appear above the field.
|
||||
return std::make_pair(top_growth_end - popup_required_height,
|
||||
popup_required_height);
|
||||
}
|
||||
}
|
||||
|
||||
display::Display GetDisplayNearestPoint(
|
||||
const gfx::Point& point,
|
||||
gfx::NativeView container_view) {
|
||||
return display::Screen::GetScreen()->GetDisplayNearestPoint(point);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AutofillPopup::AutofillPopup(gfx::NativeView container_view)
|
||||
: container_view_(container_view) {
|
||||
bold_font_list_ =
|
||||
gfx::FontList().DeriveWithWeight(gfx::Font::Weight::BOLD);
|
||||
smaller_font_list_ =
|
||||
gfx::FontList().DeriveWithSizeDelta(kSmallerFontSizeDelta);
|
||||
}
|
||||
|
||||
AutofillPopup::~AutofillPopup() {
|
||||
Hide();
|
||||
}
|
||||
|
||||
void AutofillPopup::CreateView(
|
||||
int routing_id,
|
||||
content::WebContents* web_contents,
|
||||
views::Widget* parent_widget,
|
||||
const gfx::RectF& r) {
|
||||
web_contents_ = web_contents;
|
||||
gfx::Rect lb(std::floor(r.x()), std::floor(r.y() + r.height()),
|
||||
std::floor(r.width()), std::floor(r.height()));
|
||||
gfx::Point menu_position(lb.origin());
|
||||
views::View::ConvertPointToScreen(parent_widget->GetContentsView(), &menu_position);
|
||||
popup_bounds_ = gfx::Rect(menu_position, lb.size());
|
||||
element_bounds_ = popup_bounds_;
|
||||
|
||||
view_.reset(new AutofillPopupView(this, parent_widget));
|
||||
view_->Show();
|
||||
|
||||
frame_routing_id_ = routing_id;
|
||||
}
|
||||
|
||||
void AutofillPopup::Hide() {
|
||||
if (view_.get()) {
|
||||
view_->Hide();
|
||||
view_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void AutofillPopup::SetItems(const std::vector<base::string16>& values,
|
||||
const std::vector<base::string16>& labels) {
|
||||
values_ = values;
|
||||
labels_ = labels;
|
||||
UpdatePopupBounds();
|
||||
if (view_.get()) {
|
||||
view_->OnSuggestionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void AutofillPopup::AcceptSuggestion(int index) {
|
||||
web_contents_->Send(new AtomAutofillViewMsg_AcceptSuggestion(
|
||||
frame_routing_id_, GetValueAt(index)));
|
||||
}
|
||||
|
||||
void AutofillPopup::UpdatePopupBounds() {
|
||||
int desired_width = GetDesiredPopupWidth();
|
||||
int desired_height = GetDesiredPopupHeight();
|
||||
bool is_rtl = false;
|
||||
|
||||
gfx::Point top_left_corner_of_popup =
|
||||
element_bounds_.origin() +
|
||||
gfx::Vector2d(element_bounds_.width() - desired_width, -desired_height);
|
||||
|
||||
// This is the bottom right point of the popup if the popup is below the
|
||||
// element and grows to the right (since the is the lowest and furthest right
|
||||
// the popup could go).
|
||||
gfx::Point bottom_right_corner_of_popup =
|
||||
element_bounds_.origin() +
|
||||
gfx::Vector2d(desired_width, element_bounds_.height() + desired_height);
|
||||
|
||||
display::Display top_left_display =
|
||||
GetDisplayNearestPoint(top_left_corner_of_popup, container_view_);
|
||||
display::Display bottom_right_display =
|
||||
GetDisplayNearestPoint(bottom_right_corner_of_popup, container_view_);
|
||||
|
||||
std::pair<int, int> popup_x_and_width =
|
||||
CalculatePopupXAndWidth(top_left_display, bottom_right_display,
|
||||
desired_width, element_bounds_, is_rtl);
|
||||
std::pair<int, int> popup_y_and_height = CalculatePopupYAndHeight(
|
||||
top_left_display, bottom_right_display, desired_height, element_bounds_);
|
||||
|
||||
popup_bounds_ = gfx::Rect(popup_x_and_width.first, popup_y_and_height.first,
|
||||
popup_x_and_width.second, popup_y_and_height.second);
|
||||
}
|
||||
|
||||
int AutofillPopup::GetDesiredPopupHeight() {
|
||||
return 2 * kPopupBorderThickness + values_.size() * kRowHeight;
|
||||
}
|
||||
|
||||
int AutofillPopup::GetDesiredPopupWidth() {
|
||||
int popup_width = element_bounds_.width();
|
||||
|
||||
for (int i = 0; i < values_.size(); ++i) {
|
||||
int row_size = kEndPadding + 2 * kPopupBorderThickness +
|
||||
gfx::GetStringWidth(GetValueAt(i), GetValueFontListForRow(i)) +
|
||||
gfx::GetStringWidth(GetLabelAt(i), GetLabelFontListForRow(i));
|
||||
if (GetLabelAt(i).length() > 0)
|
||||
row_size += kNamePadding + kEndPadding;
|
||||
|
||||
popup_width = std::max(popup_width, row_size);
|
||||
}
|
||||
|
||||
return popup_width;
|
||||
}
|
||||
|
||||
gfx::Rect AutofillPopup::GetRowBounds(int index) {
|
||||
int top = kPopupBorderThickness + index * kRowHeight;
|
||||
|
||||
return gfx::Rect(kPopupBorderThickness, top,
|
||||
popup_bounds_.width() - 2 * kPopupBorderThickness,
|
||||
kRowHeight);
|
||||
}
|
||||
|
||||
const gfx::FontList& AutofillPopup::GetValueFontListForRow(int index) const {
|
||||
return bold_font_list_;
|
||||
}
|
||||
|
||||
const gfx::FontList& AutofillPopup::GetLabelFontListForRow(int index) const {
|
||||
return smaller_font_list_;
|
||||
}
|
||||
|
||||
ui::NativeTheme::ColorId AutofillPopup::GetBackgroundColorIDForRow(
|
||||
int index) const {
|
||||
return index == view_->GetSelectedLine()
|
||||
? ui::NativeTheme::kColorId_ResultsTableHoveredBackground
|
||||
: ui::NativeTheme::kColorId_ResultsTableNormalBackground;
|
||||
}
|
||||
|
||||
int AutofillPopup::GetLineCount() {
|
||||
return values_.size();
|
||||
}
|
||||
|
||||
base::string16 AutofillPopup::GetValueAt(int i) {
|
||||
return values_.at(i);
|
||||
}
|
||||
|
||||
base::string16 AutofillPopup::GetLabelAt(int i) {
|
||||
return labels_.at(i);
|
||||
}
|
||||
|
||||
int AutofillPopup::LineFromY(int y) const {
|
||||
int current_height = kPopupBorderThickness;
|
||||
|
||||
for (int i = 0; i < values_.size(); ++i) {
|
||||
current_height += kRowHeight;
|
||||
|
||||
if (y <= current_height)
|
||||
return i;
|
||||
}
|
||||
|
||||
return values_.size() - 1;
|
||||
}
|
||||
|
||||
} // namespace atom
|
79
atom/browser/ui/autofill_popup.h
Normal file
79
atom/browser/ui/autofill_popup.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
// 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.
|
||||
|
||||
#ifndef ATOM_BROWSER_UI_AUTOFILL_POPUP_H_
|
||||
#define ATOM_BROWSER_UI_AUTOFILL_POPUP_H_
|
||||
|
||||
#include "atom/browser/ui/views/autofill_popup_view.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "ui/gfx/font_list.h"
|
||||
#include "ui/native_theme/native_theme.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AutofillPopupView;
|
||||
|
||||
class AutofillPopup {
|
||||
public:
|
||||
AutofillPopup(gfx::NativeView);
|
||||
~AutofillPopup();
|
||||
|
||||
void CreateView(int routing_id, content::WebContents* web_contents,
|
||||
views::Widget* widget, const gfx::RectF& bounds);
|
||||
void Hide();
|
||||
|
||||
void SetItems(const std::vector<base::string16>& values,
|
||||
const std::vector<base::string16>& labels);
|
||||
private:
|
||||
friend class AutofillPopupView;
|
||||
|
||||
void AcceptSuggestion(int index);
|
||||
|
||||
void UpdatePopupBounds();
|
||||
int GetDesiredPopupHeight();
|
||||
int GetDesiredPopupWidth();
|
||||
gfx::Rect GetRowBounds(int i);
|
||||
const gfx::FontList& GetValueFontListForRow(int index) const;
|
||||
const gfx::FontList& GetLabelFontListForRow(int index) const;
|
||||
ui::NativeTheme::ColorId GetBackgroundColorIDForRow(int index) const;
|
||||
|
||||
int GetLineCount();
|
||||
base::string16 GetValueAt(int i);
|
||||
base::string16 GetLabelAt(int i);
|
||||
int LineFromY(int y) const;
|
||||
|
||||
// The native view that contains this
|
||||
gfx::NativeView container_view_;
|
||||
|
||||
int selected_index_;
|
||||
|
||||
// Popup location
|
||||
gfx::Rect popup_bounds_;
|
||||
|
||||
// Bounds of the autofilled element
|
||||
gfx::Rect element_bounds_;
|
||||
|
||||
// Datalist suggestions
|
||||
std::vector<base::string16> values_;
|
||||
std::vector<base::string16> labels_;
|
||||
|
||||
// Font lists for the suggestions
|
||||
gfx::FontList smaller_font_list_;
|
||||
gfx::FontList bold_font_list_;
|
||||
|
||||
// For sending the accepted suggestion to the render frame that
|
||||
// asked to open the popup
|
||||
int frame_routing_id_;
|
||||
content::WebContents* web_contents_;
|
||||
|
||||
// The popup view
|
||||
std::unique_ptr<AutofillPopupView> view_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AutofillPopup);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_UI_AUTOFILL_POPUP_H_
|
414
atom/browser/ui/views/autofill_popup_view.cc
Normal file
414
atom/browser/ui/views/autofill_popup_view.cc
Normal file
|
@ -0,0 +1,414 @@
|
|||
// 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 "atom/browser/ui/views/autofill_popup_view.h"
|
||||
#include "base/bind.h"
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
#include "ui/events/keycodes/keyboard_codes.h"
|
||||
#include "ui/gfx/canvas.h"
|
||||
#include "ui/gfx/geometry/point.h"
|
||||
#include "ui/gfx/geometry/rect.h"
|
||||
#include "ui/gfx/text_utils.h"
|
||||
#include "ui/views/border.h"
|
||||
#include "ui/views/focus/focus_manager.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
AutofillPopupView::AutofillPopupView(
|
||||
AutofillPopup* popup,
|
||||
views::Widget* parent_widget)
|
||||
: popup_(popup),
|
||||
parent_widget_(parent_widget),
|
||||
weak_ptr_factory_(this) {
|
||||
CreateChildViews();
|
||||
SetFocusBehavior(FocusBehavior::ALWAYS);
|
||||
set_drag_controller(this);
|
||||
}
|
||||
|
||||
AutofillPopupView::~AutofillPopupView() {
|
||||
if (popup_) {
|
||||
auto host = popup_->web_contents_->GetRenderViewHost()->GetWidget();
|
||||
host->RemoveKeyPressEventCallback(keypress_callback_);
|
||||
popup_ = nullptr;
|
||||
}
|
||||
|
||||
RemoveObserver();
|
||||
|
||||
if (GetWidget()) {
|
||||
GetWidget()->Close();
|
||||
}
|
||||
}
|
||||
|
||||
void AutofillPopupView::Show() {
|
||||
const bool initialize_widget = !GetWidget();
|
||||
if (initialize_widget) {
|
||||
parent_widget_->AddObserver(this);
|
||||
views::FocusManager* focus_manager = parent_widget_->GetFocusManager();
|
||||
focus_manager->RegisterAccelerator(
|
||||
ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE),
|
||||
ui::AcceleratorManager::kNormalPriority,
|
||||
this);
|
||||
focus_manager->RegisterAccelerator(
|
||||
ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE),
|
||||
ui::AcceleratorManager::kNormalPriority,
|
||||
this);
|
||||
|
||||
// The widget is destroyed by the corresponding NativeWidget, so we use
|
||||
// a weak pointer to hold the reference and don't have to worry about
|
||||
// deletion.
|
||||
views::Widget* widget = new views::Widget;
|
||||
views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
|
||||
params.delegate = this;
|
||||
params.parent = parent_widget_->GetNativeView();
|
||||
widget->Init(params);
|
||||
|
||||
// No animation for popup appearance (too distracting).
|
||||
widget->SetVisibilityAnimationTransition(views::Widget::ANIMATE_HIDE);
|
||||
|
||||
show_time_ = base::Time::Now();
|
||||
}
|
||||
|
||||
SetBorder(views::CreateSolidBorder(
|
||||
kPopupBorderThickness,
|
||||
GetNativeTheme()->GetSystemColor(
|
||||
ui::NativeTheme::kColorId_UnfocusedBorderColor)));
|
||||
|
||||
DoUpdateBoundsAndRedrawPopup();
|
||||
GetWidget()->Show();
|
||||
|
||||
if (initialize_widget)
|
||||
views::WidgetFocusManager::GetInstance()->AddFocusChangeListener(this);
|
||||
|
||||
keypress_callback_ = base::Bind(&AutofillPopupView::HandleKeyPressEvent,
|
||||
base::Unretained(this));
|
||||
auto host = popup_->web_contents_->GetRenderViewHost()->GetWidget();
|
||||
host->AddKeyPressEventCallback(keypress_callback_);
|
||||
}
|
||||
|
||||
void AutofillPopupView::Hide() {
|
||||
auto host = popup_->web_contents_->GetRenderViewHost()->GetWidget();
|
||||
host->RemoveKeyPressEventCallback(keypress_callback_);
|
||||
popup_ = NULL;
|
||||
|
||||
RemoveObserver();
|
||||
|
||||
if (GetWidget()) {
|
||||
GetWidget()->Close();
|
||||
} else {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void AutofillPopupView::OnSuggestionsChanged() {
|
||||
if (popup_->GetLineCount() == 0) {
|
||||
popup_->Hide();
|
||||
return;
|
||||
}
|
||||
CreateChildViews();
|
||||
DoUpdateBoundsAndRedrawPopup();
|
||||
}
|
||||
|
||||
void AutofillPopupView::OnSelectedRowChanged(
|
||||
base::Optional<int> previous_row_selection,
|
||||
base::Optional<int> current_row_selection) {
|
||||
SchedulePaint();
|
||||
|
||||
if (current_row_selection) {
|
||||
DCHECK_LT(*current_row_selection, child_count());
|
||||
child_at(*current_row_selection)
|
||||
->NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true);
|
||||
}
|
||||
}
|
||||
|
||||
void AutofillPopupView::DrawAutofillEntry(gfx::Canvas* canvas,
|
||||
int index,
|
||||
const gfx::Rect& entry_rect) {
|
||||
canvas->FillRect(
|
||||
entry_rect,
|
||||
GetNativeTheme()->GetSystemColor(
|
||||
popup_->GetBackgroundColorIDForRow(index)));
|
||||
|
||||
const bool is_rtl = false;
|
||||
const int text_align =
|
||||
is_rtl ? gfx::Canvas::TEXT_ALIGN_RIGHT : gfx::Canvas::TEXT_ALIGN_LEFT;
|
||||
gfx::Rect value_rect = entry_rect;
|
||||
value_rect.Inset(kEndPadding, 0);
|
||||
|
||||
int x_align_left = value_rect.x();
|
||||
const int value_width = gfx::GetStringWidth(
|
||||
popup_->GetValueAt(index),
|
||||
popup_->GetValueFontListForRow(index));
|
||||
int value_x_align_left = x_align_left;
|
||||
value_x_align_left =
|
||||
is_rtl ? value_rect.right() - value_width : value_rect.x();
|
||||
|
||||
canvas->DrawStringRectWithFlags(
|
||||
popup_->GetValueAt(index),
|
||||
popup_->GetValueFontListForRow(index),
|
||||
GetNativeTheme()->GetSystemColor(
|
||||
ui::NativeTheme::kColorId_ResultsTableNormalText),
|
||||
gfx::Rect(value_x_align_left, value_rect.y(), value_width,
|
||||
value_rect.height()),
|
||||
text_align);
|
||||
|
||||
// Draw the label text, if one exists.
|
||||
if (!popup_->GetLabelAt(index).empty()) {
|
||||
const int label_width = gfx::GetStringWidth(
|
||||
popup_->GetLabelAt(index),
|
||||
popup_->GetLabelFontListForRow(index));
|
||||
int label_x_align_left = x_align_left;
|
||||
label_x_align_left =
|
||||
is_rtl ? value_rect.x() : value_rect.right() - label_width;
|
||||
|
||||
canvas->DrawStringRectWithFlags(
|
||||
popup_->GetLabelAt(index),
|
||||
popup_->GetLabelFontListForRow(index),
|
||||
GetNativeTheme()->GetSystemColor(
|
||||
ui::NativeTheme::kColorId_ResultsTableNormalDimmedText),
|
||||
gfx::Rect(label_x_align_left, entry_rect.y(), label_width,
|
||||
entry_rect.height()),
|
||||
text_align);
|
||||
}
|
||||
}
|
||||
|
||||
void AutofillPopupView::CreateChildViews() {
|
||||
RemoveAllChildViews(true);
|
||||
|
||||
for (int i = 0; i < popup_->GetLineCount(); ++i) {
|
||||
auto child_view = new AutofillPopupChildView(popup_->GetValueAt(i));
|
||||
child_view->set_drag_controller(this);
|
||||
AddChildView(child_view);
|
||||
}
|
||||
}
|
||||
|
||||
void AutofillPopupView::DoUpdateBoundsAndRedrawPopup() {
|
||||
GetWidget()->SetBounds(popup_->popup_bounds_);
|
||||
SchedulePaint();
|
||||
}
|
||||
|
||||
void AutofillPopupView::OnPaint(gfx::Canvas* canvas) {
|
||||
if (!popup_)
|
||||
return;
|
||||
|
||||
canvas->DrawColor(GetNativeTheme()->GetSystemColor(
|
||||
ui::NativeTheme::kColorId_ResultsTableNormalBackground));
|
||||
OnPaintBorder(canvas);
|
||||
|
||||
DCHECK_EQ(popup_->GetLineCount(), child_count());
|
||||
for (int i = 0; i < popup_->GetLineCount(); ++i) {
|
||||
gfx::Rect line_rect = popup_->GetRowBounds(i);
|
||||
|
||||
DrawAutofillEntry(canvas, i, line_rect);
|
||||
}
|
||||
}
|
||||
|
||||
void AutofillPopupView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
|
||||
node_data->role = ui::AX_ROLE_MENU;
|
||||
node_data->SetName("Autofill Menu");
|
||||
}
|
||||
|
||||
void AutofillPopupView::OnMouseCaptureLost() {
|
||||
ClearSelection();
|
||||
}
|
||||
|
||||
bool AutofillPopupView::OnMouseDragged(const ui::MouseEvent& event) {
|
||||
if (HitTestPoint(event.location())) {
|
||||
SetSelection(event.location());
|
||||
|
||||
// We must return true in order to get future OnMouseDragged and
|
||||
// OnMouseReleased events.
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we move off of the popup, we lose the selection.
|
||||
ClearSelection();
|
||||
return false;
|
||||
}
|
||||
|
||||
void AutofillPopupView::OnMouseExited(const ui::MouseEvent& event) {
|
||||
// Pressing return causes the cursor to hide, which will generate an
|
||||
// OnMouseExited event. Pressing return should activate the current selection
|
||||
// via AcceleratorPressed, so we need to let that run first.
|
||||
base::ThreadTaskRunnerHandle::Get()->PostTask(
|
||||
FROM_HERE, base::Bind(&AutofillPopupView::ClearSelection,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
void AutofillPopupView::OnMouseMoved(const ui::MouseEvent& event) {
|
||||
// A synthesized mouse move will be sent when the popup is first shown.
|
||||
// Don't preview a suggestion if the mouse happens to be hovering there.
|
||||
#if defined(OS_WIN)
|
||||
if (base::Time::Now() - show_time_ <= base::TimeDelta::FromMilliseconds(50))
|
||||
return;
|
||||
#else
|
||||
if (event.flags() & ui::EF_IS_SYNTHESIZED)
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (HitTestPoint(event.location()))
|
||||
SetSelection(event.location());
|
||||
else
|
||||
ClearSelection();
|
||||
}
|
||||
|
||||
bool AutofillPopupView::OnMousePressed(const ui::MouseEvent& event) {
|
||||
return event.GetClickCount() == 1;
|
||||
}
|
||||
|
||||
void AutofillPopupView::OnMouseReleased(const ui::MouseEvent& event) {
|
||||
// We only care about the left click.
|
||||
if (event.IsOnlyLeftMouseButton() && HitTestPoint(event.location()))
|
||||
AcceptSelection(event.location());
|
||||
}
|
||||
|
||||
void AutofillPopupView::OnGestureEvent(ui::GestureEvent* event) {
|
||||
switch (event->type()) {
|
||||
case ui::ET_GESTURE_TAP_DOWN:
|
||||
case ui::ET_GESTURE_SCROLL_BEGIN:
|
||||
case ui::ET_GESTURE_SCROLL_UPDATE:
|
||||
if (HitTestPoint(event->location()))
|
||||
SetSelection(event->location());
|
||||
else
|
||||
ClearSelection();
|
||||
break;
|
||||
case ui::ET_GESTURE_TAP:
|
||||
case ui::ET_GESTURE_SCROLL_END:
|
||||
if (HitTestPoint(event->location()))
|
||||
AcceptSelection(event->location());
|
||||
else
|
||||
ClearSelection();
|
||||
break;
|
||||
case ui::ET_GESTURE_TAP_CANCEL:
|
||||
case ui::ET_SCROLL_FLING_START:
|
||||
ClearSelection();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
event->SetHandled();
|
||||
}
|
||||
|
||||
bool AutofillPopupView::AcceleratorPressed(
|
||||
const ui::Accelerator& accelerator) {
|
||||
DCHECK_EQ(accelerator.modifiers(), ui::EF_NONE);
|
||||
|
||||
if (accelerator.key_code() == ui::VKEY_ESCAPE) {
|
||||
popup_->Hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (accelerator.key_code() == ui::VKEY_RETURN)
|
||||
return AcceptSelectedLine();
|
||||
|
||||
NOTREACHED();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AutofillPopupView::HandleKeyPressEvent(
|
||||
const content::NativeWebKeyboardEvent& event) {
|
||||
switch (event.windowsKeyCode) {
|
||||
case ui::VKEY_UP:
|
||||
SelectPreviousLine();
|
||||
return true;
|
||||
case ui::VKEY_DOWN:
|
||||
SelectNextLine();
|
||||
return true;
|
||||
case ui::VKEY_PRIOR: // Page up.
|
||||
SetSelectedLine(0);
|
||||
return true;
|
||||
case ui::VKEY_NEXT: // Page down.
|
||||
SetSelectedLine(popup_->GetLineCount() - 1);
|
||||
return true;
|
||||
case ui::VKEY_ESCAPE:
|
||||
popup_->Hide();
|
||||
return true;
|
||||
case ui::VKEY_TAB:
|
||||
// A tab press should cause the selected line to be accepted, but still
|
||||
// return false so the tab key press propagates and changes the cursor
|
||||
// location.
|
||||
AcceptSelectedLine();
|
||||
return false;
|
||||
case ui::VKEY_RETURN:
|
||||
return AcceptSelectedLine();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AutofillPopupView::OnNativeFocusChanged(gfx::NativeView focused_now) {
|
||||
if (GetWidget() && GetWidget()->GetNativeView() != focused_now)
|
||||
popup_->Hide();
|
||||
}
|
||||
|
||||
void AutofillPopupView::OnWidgetBoundsChanged(views::Widget* widget,
|
||||
const gfx::Rect& new_bounds) {
|
||||
DCHECK_EQ(widget, parent_widget_);
|
||||
popup_->Hide();
|
||||
}
|
||||
|
||||
void AutofillPopupView::AcceptSuggestion(int index) {
|
||||
popup_->AcceptSuggestion(index);
|
||||
popup_->Hide();
|
||||
}
|
||||
|
||||
bool AutofillPopupView::AcceptSelectedLine() {
|
||||
if (!selected_line_)
|
||||
return false;
|
||||
|
||||
DCHECK_LT(*selected_line_, popup_->GetLineCount());
|
||||
|
||||
AcceptSuggestion(*selected_line_);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AutofillPopupView::AcceptSelection(const gfx::Point& point) {
|
||||
SetSelectedLine(popup_->LineFromY(point.y()));
|
||||
AcceptSelectedLine();
|
||||
}
|
||||
|
||||
void AutofillPopupView::SetSelectedLine(base::Optional<int> selected_line) {
|
||||
if (selected_line_ == selected_line)
|
||||
return;
|
||||
|
||||
if (selected_line) {
|
||||
DCHECK_LT(*selected_line, popup_->GetLineCount());
|
||||
}
|
||||
|
||||
auto previous_selected_line(selected_line_);
|
||||
selected_line_ = selected_line;
|
||||
OnSelectedRowChanged(previous_selected_line, selected_line_);
|
||||
}
|
||||
|
||||
void AutofillPopupView::SetSelection(const gfx::Point& point) {
|
||||
SetSelectedLine(popup_->LineFromY(point.y()));
|
||||
}
|
||||
|
||||
void AutofillPopupView::SelectNextLine() {
|
||||
int new_selected_line = selected_line_ ? *selected_line_ + 1 : 0;
|
||||
if (new_selected_line >= popup_->GetLineCount())
|
||||
new_selected_line = 0;
|
||||
|
||||
SetSelectedLine(new_selected_line);
|
||||
}
|
||||
|
||||
void AutofillPopupView::SelectPreviousLine() {
|
||||
int new_selected_line = selected_line_.value_or(0) - 1;
|
||||
if (new_selected_line < 0)
|
||||
new_selected_line = popup_->GetLineCount() - 1;
|
||||
|
||||
SetSelectedLine(new_selected_line);
|
||||
}
|
||||
|
||||
void AutofillPopupView::ClearSelection() {
|
||||
SetSelectedLine(base::nullopt);
|
||||
}
|
||||
|
||||
void AutofillPopupView::RemoveObserver() {
|
||||
parent_widget_->GetFocusManager()->UnregisterAccelerators(this);
|
||||
parent_widget_->RemoveObserver(this);
|
||||
views::WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(this);
|
||||
}
|
||||
|
||||
} // namespace atom
|
147
atom/browser/ui/views/autofill_popup_view.h
Normal file
147
atom/browser/ui/views/autofill_popup_view.h
Normal file
|
@ -0,0 +1,147 @@
|
|||
// 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.
|
||||
|
||||
#ifndef ATOM_BROWSER_UI_VIEWS_AUTOFILL_POPUP_VIEW_H_
|
||||
#define ATOM_BROWSER_UI_VIEWS_AUTOFILL_POPUP_VIEW_H_
|
||||
|
||||
#include "atom/browser/ui/autofill_popup.h"
|
||||
|
||||
#include "base/optional.h"
|
||||
#include "content/public/browser/native_web_keyboard_event.h"
|
||||
#include "content/public/browser/render_widget_host.h"
|
||||
#include "ui/accessibility/ax_node_data.h"
|
||||
#include "ui/views/drag_controller.h"
|
||||
#include "ui/views/focus/widget_focus_manager.h"
|
||||
#include "ui/views/widget/widget_delegate.h"
|
||||
#include "ui/views/widget/widget_observer.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
const int kPopupBorderThickness = 1;
|
||||
const int kSmallerFontSizeDelta = -1;
|
||||
const int kEndPadding = 8;
|
||||
const int kNamePadding = 15;
|
||||
const int kRowHeight = 24;
|
||||
|
||||
class AutofillPopup;
|
||||
|
||||
// Child view only for triggering accessibility events. Rendering is handled
|
||||
// by |AutofillPopupViewViews|.
|
||||
class AutofillPopupChildView : public views::View {
|
||||
public:
|
||||
explicit AutofillPopupChildView(const base::string16& suggestion)
|
||||
: suggestion_(suggestion) {
|
||||
SetFocusBehavior(FocusBehavior::ALWAYS);
|
||||
}
|
||||
|
||||
private:
|
||||
~AutofillPopupChildView() override {}
|
||||
|
||||
// views::Views implementation
|
||||
void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
|
||||
node_data->role = ui::AX_ROLE_MENU_ITEM;
|
||||
node_data->SetName(suggestion_);
|
||||
}
|
||||
|
||||
base::string16 suggestion_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AutofillPopupChildView);
|
||||
};
|
||||
|
||||
class AutofillPopupView : public views::WidgetDelegateView,
|
||||
public views::WidgetFocusChangeListener,
|
||||
public views::WidgetObserver,
|
||||
public views::DragController {
|
||||
public:
|
||||
explicit AutofillPopupView(AutofillPopup* popup,
|
||||
views::Widget* parent_widget);
|
||||
~AutofillPopupView() override;
|
||||
|
||||
void Show();
|
||||
void Hide();
|
||||
|
||||
void OnSuggestionsChanged();
|
||||
|
||||
int GetSelectedLine() { return selected_line_.value_or(-1); }
|
||||
|
||||
void WriteDragDataForView(
|
||||
views::View*, const gfx::Point&, ui::OSExchangeData*) override {}
|
||||
int GetDragOperationsForView(views::View*, const gfx::Point&) override {
|
||||
return ui::DragDropTypes::DRAG_NONE;
|
||||
}
|
||||
bool CanStartDragForView(
|
||||
views::View*, const gfx::Point&, const gfx::Point&) override {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
void OnSelectedRowChanged(base::Optional<int> previous_row_selection,
|
||||
base::Optional<int> current_row_selection);
|
||||
|
||||
// Draw the given autofill entry in |entry_rect|.
|
||||
void DrawAutofillEntry(gfx::Canvas* canvas,
|
||||
int index,
|
||||
const gfx::Rect& entry_rect);
|
||||
|
||||
// Creates child views based on the suggestions given by |controller_|. These
|
||||
// child views are used for accessibility events only. We need child views to
|
||||
// populate the correct |AXNodeData| when user selects a suggestion.
|
||||
void CreateChildViews();
|
||||
|
||||
void DoUpdateBoundsAndRedrawPopup();
|
||||
|
||||
// views::Views implementation.
|
||||
void OnPaint(gfx::Canvas* canvas) override;
|
||||
void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
|
||||
void OnMouseCaptureLost() override;
|
||||
bool OnMouseDragged(const ui::MouseEvent& event) override;
|
||||
void OnMouseExited(const ui::MouseEvent& event) override;
|
||||
void OnMouseMoved(const ui::MouseEvent& event) override;
|
||||
bool OnMousePressed(const ui::MouseEvent& event) override;
|
||||
void OnMouseReleased(const ui::MouseEvent& event) override;
|
||||
void OnGestureEvent(ui::GestureEvent* event) override;
|
||||
bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
|
||||
bool HandleKeyPressEvent(const content::NativeWebKeyboardEvent& event);
|
||||
|
||||
// views::WidgetFocusChangeListener implementation.
|
||||
void OnNativeFocusChanged(gfx::NativeView focused_now) override;
|
||||
|
||||
// views::WidgetObserver implementation.
|
||||
void OnWidgetBoundsChanged(views::Widget* widget,
|
||||
const gfx::Rect& new_bounds) override;
|
||||
|
||||
void AcceptSuggestion(int index);
|
||||
bool AcceptSelectedLine();
|
||||
void AcceptSelection(const gfx::Point& point);
|
||||
void SetSelectedLine(base::Optional<int> selected_line);
|
||||
void SetSelection(const gfx::Point& point);
|
||||
void SelectNextLine();
|
||||
void SelectPreviousLine();
|
||||
void ClearSelection();
|
||||
|
||||
// Stop observing the widget.
|
||||
void RemoveObserver();
|
||||
|
||||
// Controller for this popup. Weak reference.
|
||||
AutofillPopup* popup_;
|
||||
|
||||
// The widget of the window that triggered this popup. Weak reference.
|
||||
views::Widget* parent_widget_;
|
||||
|
||||
// The time when the popup was shown.
|
||||
base::Time show_time_;
|
||||
|
||||
// The index of the currently selected line
|
||||
base::Optional<int> selected_line_;
|
||||
|
||||
// The registered keypress callback, responsible for switching lines on
|
||||
// key presses
|
||||
content::RenderWidgetHost::KeyPressEventCallback keypress_callback_;
|
||||
|
||||
base::WeakPtrFactory<AutofillPopupView> weak_ptr_factory_;
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_UI_VIEWS_AUTOFILL_POPUP_VIEW_H_
|
|
@ -9,6 +9,7 @@
|
|||
#include "base/values.h"
|
||||
#include "content/public/common/common_param_traits.h"
|
||||
#include "ipc/ipc_message_macros.h"
|
||||
#include "ui/gfx/geometry/rect_f.h"
|
||||
#include "ui/gfx/ipc/gfx_param_traits.h"
|
||||
|
||||
// The message starter should be declared in ipc/ipc_message_start.h. Since
|
||||
|
@ -37,6 +38,20 @@ IPC_MESSAGE_ROUTED3(AtomViewMsg_Message,
|
|||
|
||||
IPC_MESSAGE_ROUTED0(AtomViewMsg_Offscreen)
|
||||
|
||||
IPC_MESSAGE_ROUTED4(AtomAutofillViewMsg_ShowPopup,
|
||||
int /* routing_id */,
|
||||
gfx::RectF /* bounds */,
|
||||
std::vector<base::string16> /* values */,
|
||||
std::vector<base::string16> /* labels */)
|
||||
|
||||
IPC_MESSAGE_ROUTED0(AtomAutofillViewMsg_HidePopup)
|
||||
|
||||
IPC_MESSAGE_ROUTED1(AtomAutofillViewMsg_AcceptSuggestion,
|
||||
base::string16 /* suggestion */)
|
||||
|
||||
IPC_MESSAGE_ROUTED1(AtomAutofillViewHostMsg_RoutingId,
|
||||
int /* id */)
|
||||
|
||||
// Sent by the renderer when the draggable regions are updated.
|
||||
IPC_MESSAGE_ROUTED1(AtomViewHostMsg_UpdateDraggableRegions,
|
||||
std::vector<atom::DraggableRegion> /* regions */)
|
||||
|
|
205
atom/renderer/atom_autofill_agent.cc
Normal file
205
atom/renderer/atom_autofill_agent.cc
Normal file
|
@ -0,0 +1,205 @@
|
|||
// Copyright (c) 2017 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/renderer/atom_autofill_agent.h"
|
||||
|
||||
#include "atom/common/api/api_messages.h"
|
||||
#include "content/public/renderer/render_frame.h"
|
||||
#include "content/public/renderer/render_view.h"
|
||||
#include "third_party/WebKit/public/platform/WebKeyboardEvent.h"
|
||||
#include "third_party/WebKit/public/platform/WebString.h"
|
||||
#include "third_party/WebKit/public/web/WebDocument.h"
|
||||
#include "third_party/WebKit/public/web/WebLocalFrame.h"
|
||||
#include "third_party/WebKit/public/web/WebOptionElement.h"
|
||||
#include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
|
||||
#include "ui/events/keycodes/keyboard_codes.h"
|
||||
#include "ui/gfx/geometry/rect_f.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
const size_t kMaxDataLength = 1024;
|
||||
const size_t kMaxListSize = 512;
|
||||
|
||||
void GetDataListSuggestions(const blink::WebInputElement& element,
|
||||
std::vector<base::string16>* values,
|
||||
std::vector<base::string16>* labels) {
|
||||
for (const auto& option : element.filteredDataListOptions()) {
|
||||
values->push_back(option.value().utf16());
|
||||
if (option.value() != option.label())
|
||||
labels->push_back(option.label().utf16());
|
||||
else
|
||||
labels->push_back(base::string16());
|
||||
}
|
||||
}
|
||||
|
||||
void TrimStringVectorForIPC(std::vector<base::string16>* strings) {
|
||||
// Limit the size of the vector.
|
||||
if (strings->size() > kMaxListSize)
|
||||
strings->resize(kMaxListSize);
|
||||
|
||||
// Limit the size of the strings in the vector.
|
||||
for (size_t i = 0; i < strings->size(); ++i) {
|
||||
if ((*strings)[i].length() > kMaxDataLength)
|
||||
(*strings)[i].resize(kMaxDataLength);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
AutofillAgent::AutofillAgent(
|
||||
content::RenderFrame* frame)
|
||||
: content::RenderFrameObserver(frame),
|
||||
render_frame_(frame),
|
||||
weak_ptr_factory_(this) {
|
||||
render_frame_->GetWebFrame()->setAutofillClient(this);
|
||||
}
|
||||
|
||||
void AutofillAgent::OnDestruct() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
void AutofillAgent::DidChangeScrollOffset() {
|
||||
HidePopup();
|
||||
}
|
||||
|
||||
void AutofillAgent::FocusedNodeChanged(const blink::WebNode&) {
|
||||
HidePopup();
|
||||
}
|
||||
|
||||
void AutofillAgent::textFieldDidEndEditing(
|
||||
const blink::WebInputElement&) {
|
||||
HidePopup();
|
||||
}
|
||||
|
||||
void AutofillAgent::textFieldDidChange(
|
||||
const blink::WebFormControlElement& element) {
|
||||
if (!IsUserGesture() && !render_frame()->IsPasting())
|
||||
return;
|
||||
|
||||
weak_ptr_factory_.InvalidateWeakPtrs();
|
||||
base::ThreadTaskRunnerHandle::Get()->PostTask(
|
||||
FROM_HERE, base::Bind(&AutofillAgent::textFieldDidChangeImpl,
|
||||
weak_ptr_factory_.GetWeakPtr(), element));
|
||||
}
|
||||
|
||||
void AutofillAgent::textFieldDidChangeImpl(
|
||||
const blink::WebFormControlElement& element) {
|
||||
ShowSuggestionsOptions options;
|
||||
options.requires_caret_at_end = true;
|
||||
ShowSuggestions(element, options);
|
||||
}
|
||||
|
||||
void AutofillAgent::textFieldDidReceiveKeyDown(
|
||||
const blink::WebInputElement& element,
|
||||
const blink::WebKeyboardEvent& event) {
|
||||
if (event.windowsKeyCode == ui::VKEY_DOWN ||
|
||||
event.windowsKeyCode == ui::VKEY_UP) {
|
||||
ShowSuggestionsOptions options;
|
||||
options.autofill_on_empty_values = true;
|
||||
options.requires_caret_at_end = true;
|
||||
ShowSuggestions(element, options);
|
||||
}
|
||||
}
|
||||
|
||||
void AutofillAgent::openTextDataListChooser(
|
||||
const blink::WebInputElement& element) {
|
||||
ShowSuggestionsOptions options;
|
||||
options.autofill_on_empty_values = true;
|
||||
ShowSuggestions(element, options);
|
||||
}
|
||||
|
||||
void AutofillAgent::dataListOptionsChanged(
|
||||
const blink::WebInputElement& element) {
|
||||
if (!element.focused())
|
||||
return;
|
||||
|
||||
ShowSuggestionsOptions options;
|
||||
options.requires_caret_at_end = true;
|
||||
ShowSuggestions(element, options);
|
||||
}
|
||||
|
||||
AutofillAgent::ShowSuggestionsOptions::ShowSuggestionsOptions()
|
||||
: autofill_on_empty_values(false),
|
||||
requires_caret_at_end(false) {
|
||||
}
|
||||
|
||||
void AutofillAgent::ShowSuggestions(
|
||||
const blink::WebFormControlElement& element,
|
||||
const ShowSuggestionsOptions& options) {
|
||||
if (!element.isEnabled() || element.isReadOnly())
|
||||
return;
|
||||
if (!element.suggestedValue().isEmpty())
|
||||
return;
|
||||
const blink::WebInputElement* input_element = toWebInputElement(&element);
|
||||
if (input_element) {
|
||||
if (!input_element->isTextField())
|
||||
return;
|
||||
}
|
||||
|
||||
blink::WebString value = element.editingValue();
|
||||
if (value.length() > kMaxDataLength ||
|
||||
(!options.autofill_on_empty_values && value.isEmpty()) ||
|
||||
(options.requires_caret_at_end &&
|
||||
(element.selectionStart() != element.selectionEnd() ||
|
||||
element.selectionEnd() != static_cast<int>(value.length())))) {
|
||||
HidePopup();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<base::string16> data_list_values;
|
||||
std::vector<base::string16> data_list_labels;
|
||||
if (input_element) {
|
||||
GetDataListSuggestions(
|
||||
*input_element, &data_list_values, &data_list_labels);
|
||||
TrimStringVectorForIPC(&data_list_values);
|
||||
TrimStringVectorForIPC(&data_list_labels);
|
||||
}
|
||||
|
||||
ShowPopup(element, data_list_values, data_list_labels);
|
||||
}
|
||||
|
||||
void AutofillAgent::OnAcceptSuggestion(base::string16 suggestion) {
|
||||
auto element = render_frame_->GetWebFrame()->document().focusedElement();
|
||||
if (element.isFormControlElement()) {
|
||||
toWebInputElement(&element)->setAutofillValue(
|
||||
blink::WebString::fromUTF16(suggestion));
|
||||
}
|
||||
}
|
||||
|
||||
bool AutofillAgent::OnMessageReceived(const IPC::Message& message) {
|
||||
bool handled = true;
|
||||
IPC_BEGIN_MESSAGE_MAP(AutofillAgent, message)
|
||||
IPC_MESSAGE_HANDLER(AtomAutofillViewHostMsg_RoutingId,
|
||||
OnWebContentsRoutingIdReceived)
|
||||
IPC_MESSAGE_HANDLER(AtomAutofillViewMsg_AcceptSuggestion,
|
||||
OnAcceptSuggestion)
|
||||
IPC_MESSAGE_UNHANDLED(handled = false)
|
||||
IPC_END_MESSAGE_MAP()
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
void AutofillAgent::OnWebContentsRoutingIdReceived(int id) {
|
||||
web_contents_routing_id_ = id;
|
||||
}
|
||||
|
||||
bool AutofillAgent::IsUserGesture() const {
|
||||
return blink::WebUserGestureIndicator::isProcessingUserGesture();
|
||||
}
|
||||
|
||||
void AutofillAgent::HidePopup() {
|
||||
Send(new AtomAutofillViewMsg_HidePopup(web_contents_routing_id_));
|
||||
}
|
||||
|
||||
void AutofillAgent::ShowPopup(
|
||||
const blink::WebFormControlElement& element,
|
||||
const std::vector<base::string16>& values,
|
||||
const std::vector<base::string16>& labels) {
|
||||
gfx::RectF bounds =
|
||||
render_frame_->GetRenderView()->ElementBoundsInWindow(element);
|
||||
Send(new AtomAutofillViewMsg_ShowPopup(
|
||||
web_contents_routing_id_, routing_id(), bounds, values, labels));
|
||||
}
|
||||
|
||||
} // namespace atom
|
66
atom/renderer/atom_autofill_agent.h
Normal file
66
atom/renderer/atom_autofill_agent.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) 2017 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_RENDERER_ATOM_AUTOFILL_AGENT_H_
|
||||
#define ATOM_RENDERER_ATOM_AUTOFILL_AGENT_H_
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "content/public/renderer/render_frame_observer.h"
|
||||
#include "third_party/WebKit/public/web/WebAutofillClient.h"
|
||||
#include "third_party/WebKit/public/web/WebFormControlElement.h"
|
||||
#include "third_party/WebKit/public/web/WebInputElement.h"
|
||||
#include "third_party/WebKit/public/web/WebNode.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AutofillAgent : public content::RenderFrameObserver,
|
||||
public blink::WebAutofillClient {
|
||||
public:
|
||||
AutofillAgent(content::RenderFrame* frame);
|
||||
|
||||
// content::RenderFrameObserver:
|
||||
void OnDestruct() override;
|
||||
|
||||
void DidChangeScrollOffset() override;
|
||||
void FocusedNodeChanged(const blink::WebNode&) override;
|
||||
|
||||
private:
|
||||
struct ShowSuggestionsOptions {
|
||||
ShowSuggestionsOptions();
|
||||
bool autofill_on_empty_values;
|
||||
bool requires_caret_at_end;
|
||||
};
|
||||
|
||||
bool OnMessageReceived(const IPC::Message& message) override;
|
||||
void OnWebContentsRoutingIdReceived(int);
|
||||
|
||||
// blink::WebAutofillClient:
|
||||
void textFieldDidEndEditing(const blink::WebInputElement&) override;
|
||||
void textFieldDidChange(const blink::WebFormControlElement&) override;
|
||||
void textFieldDidChangeImpl(const blink::WebFormControlElement&);
|
||||
void textFieldDidReceiveKeyDown(const blink::WebInputElement&,
|
||||
const blink::WebKeyboardEvent&) override;
|
||||
void openTextDataListChooser(const blink::WebInputElement&) override;
|
||||
void dataListOptionsChanged(const blink::WebInputElement&) override;
|
||||
|
||||
bool IsUserGesture() const;
|
||||
void HidePopup();
|
||||
void ShowPopup(const blink::WebFormControlElement&,
|
||||
const std::vector<base::string16>&,
|
||||
const std::vector<base::string16>&);
|
||||
void ShowSuggestions(const blink::WebFormControlElement& element,
|
||||
const ShowSuggestionsOptions& options);
|
||||
void OnAcceptSuggestion(base::string16 suggestion);
|
||||
|
||||
content::RenderFrame* render_frame_;
|
||||
int web_contents_routing_id_;
|
||||
|
||||
base::WeakPtrFactory<AutofillAgent> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AutofillAgent);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_RENDERER_ATOM_AUTOFILL_AGENT_H_
|
|
@ -11,6 +11,7 @@
|
|||
#include "atom/common/color_util.h"
|
||||
#include "atom/common/native_mate_converters/value_converter.h"
|
||||
#include "atom/common/options_switches.h"
|
||||
#include "atom/renderer/atom_autofill_agent.h"
|
||||
#include "atom/renderer/atom_render_frame_observer.h"
|
||||
#include "atom/renderer/content_settings_observer.h"
|
||||
#include "atom/renderer/guest_view_container.h"
|
||||
|
@ -120,6 +121,7 @@ void RendererClientBase::RenderThreadStarted() {
|
|||
void RendererClientBase::RenderFrameCreated(
|
||||
content::RenderFrame* render_frame) {
|
||||
new AtomRenderFrameObserver(render_frame, this);
|
||||
new AutofillAgent(render_frame);
|
||||
new PepperHelper(render_frame);
|
||||
new ContentSettingsObserver(render_frame);
|
||||
new printing::PrintWebViewHelper(render_frame);
|
||||
|
|
|
@ -291,6 +291,8 @@
|
|||
'atom/browser/ui/accelerator_util_views.cc',
|
||||
'atom/browser/ui/atom_menu_model.cc',
|
||||
'atom/browser/ui/atom_menu_model.h',
|
||||
'atom/browser/ui/autofill_popup.cc',
|
||||
'atom/browser/ui/autofill_popup.h',
|
||||
'atom/browser/ui/certificate_trust.h',
|
||||
'atom/browser/ui/certificate_trust_mac.mm',
|
||||
'atom/browser/ui/certificate_trust_win.cc',
|
||||
|
@ -318,6 +320,8 @@
|
|||
'atom/browser/ui/tray_icon_cocoa.mm',
|
||||
'atom/browser/ui/tray_icon_observer.h',
|
||||
'atom/browser/ui/tray_icon_win.cc',
|
||||
'atom/browser/ui/views/autofill_popup_view.cc',
|
||||
'atom/browser/ui/views/autofill_popup_view.h',
|
||||
'atom/browser/ui/views/frameless_view.cc',
|
||||
'atom/browser/ui/views/frameless_view.h',
|
||||
'atom/browser/ui/views/global_menu_bar_x11.cc',
|
||||
|
@ -479,6 +483,8 @@
|
|||
'atom/renderer/api/atom_api_spell_check_client.h',
|
||||
'atom/renderer/api/atom_api_web_frame.cc',
|
||||
'atom/renderer/api/atom_api_web_frame.h',
|
||||
'atom/renderer/atom_autofill_agent.cc',
|
||||
'atom/renderer/atom_autofill_agent.h',
|
||||
'atom/renderer/atom_render_frame_observer.cc',
|
||||
'atom/renderer/atom_render_frame_observer.h',
|
||||
'atom/renderer/atom_render_view_observer.cc',
|
||||
|
|
Loading…
Reference in a new issue