Browse Source

Merge pull request #9535 from brenca/datalist-element

Add datalist element support
Cheng Zhao 8 years ago
parent
commit
7d994f2a9e

+ 21 - 0
atom/browser/api/atom_api_web_contents.cc

@@ -982,6 +982,23 @@ bool WebContents::OnMessageReceived(const IPC::Message& message) {
   return handled;
 }
 
+bool WebContents::OnMessageReceived(const IPC::Message& message,
+    content::RenderFrameHost* frame_host) {
+  bool handled = true;
+  auto relay = NativeWindowRelay::FromWebContents(web_contents());
+  if (!relay)
+    return false;
+  IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(NativeWindow, message, frame_host)
+    IPC_MESSAGE_FORWARD(AtomAutofillFrameHostMsg_ShowPopup,
+      relay->window.get(), NativeWindow::ShowAutofillPopup)
+    IPC_MESSAGE_FORWARD(AtomAutofillFrameHostMsg_HidePopup,
+      relay->window.get(), NativeWindow::HideAutofillPopup)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+
+  return handled;
+}
+
 // There are three ways of destroying a webContents:
 // 1. call webContents.destroy();
 // 2. garbage collection;
@@ -1621,6 +1638,10 @@ bool WebContents::IsOffScreen() const {
   return type_ == OFF_SCREEN;
 }
 
+bool WebContents::IsOffScreenOrEmbedderOffscreen() const {
+  return IsOffScreen() || (embedder_ && embedder_->IsOffScreen());
+}
+
 void WebContents::OnPaint(const gfx::Rect& dirty_rect, const SkBitmap& bitmap) {
   Emit("paint", dirty_rect, gfx::Image::CreateFrom1xBitmap(bitmap));
 }

+ 4 - 0
atom/browser/api/atom_api_web_contents.h

@@ -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"
@@ -180,6 +181,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
 
   // Methods for offscreen rendering
   bool IsOffScreen() const;
+  bool IsOffScreenOrEmbedderOffscreen() const;
   void OnPaint(const gfx::Rect& dirty_rect, const SkBitmap& bitmap);
   void StartPainting();
   void StopPainting();
@@ -329,6 +331,8 @@ class WebContents : public mate::TrackableObject<WebContents>,
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
   bool OnMessageReceived(const IPC::Message& message) override;
+  bool OnMessageReceived(const IPC::Message& message,
+                         content::RenderFrameHost* frame_host) override;
   void WebContentsDestroyed() override;
   void NavigationEntryCommitted(
       const content::LoadCommittedDetails& load_details) override;

+ 4 - 4
atom/browser/native_window.cc

@@ -251,7 +251,7 @@ void NativeWindow::SetSizeConstraints(
   SetContentSizeConstraints(content_constraints);
 }
 
-extensions::SizeConstraints NativeWindow::GetSizeConstraints() {
+extensions::SizeConstraints NativeWindow::GetSizeConstraints() const {
   extensions::SizeConstraints content_constraints = GetContentSizeConstraints();
   extensions::SizeConstraints window_constraints;
   if (content_constraints.HasMaximumSize()) {
@@ -272,7 +272,7 @@ void NativeWindow::SetContentSizeConstraints(
   size_constraints_ = size_constraints;
 }
 
-extensions::SizeConstraints NativeWindow::GetContentSizeConstraints() {
+extensions::SizeConstraints NativeWindow::GetContentSizeConstraints() const {
   return size_constraints_;
 }
 
@@ -282,7 +282,7 @@ void NativeWindow::SetMinimumSize(const gfx::Size& size) {
   SetSizeConstraints(size_constraints);
 }
 
-gfx::Size NativeWindow::GetMinimumSize() {
+gfx::Size NativeWindow::GetMinimumSize() const {
   return GetSizeConstraints().GetMinimumSize();
 }
 
@@ -292,7 +292,7 @@ void NativeWindow::SetMaximumSize(const gfx::Size& size) {
   SetSizeConstraints(size_constraints);
 }
 
-gfx::Size NativeWindow::GetMaximumSize() {
+gfx::Size NativeWindow::GetMaximumSize() const {
   return GetSizeConstraints().GetMaximumSize();
 }
 

+ 19 - 9
atom/browser/native_window.h

@@ -18,10 +18,12 @@
 #include "base/observer_list.h"
 #include "base/supports_user_data.h"
 #include "content/public/browser/readback_types.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "extensions/browser/app_window/size_constraints.h"
 #include "native_mate/persistent_dictionary.h"
+#include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_skia.h"
 
@@ -98,14 +100,14 @@ class NativeWindow : public base::SupportsUserData,
   virtual gfx::Rect GetContentBounds();
   virtual void SetSizeConstraints(
       const extensions::SizeConstraints& size_constraints);
-  virtual extensions::SizeConstraints GetSizeConstraints();
+  virtual extensions::SizeConstraints GetSizeConstraints() const;
   virtual void SetContentSizeConstraints(
       const extensions::SizeConstraints& size_constraints);
-  virtual extensions::SizeConstraints GetContentSizeConstraints();
+  virtual extensions::SizeConstraints GetContentSizeConstraints() const;
   virtual void SetMinimumSize(const gfx::Size& size);
-  virtual gfx::Size GetMinimumSize();
+  virtual gfx::Size GetMinimumSize() const;
   virtual void SetMaximumSize(const gfx::Size& size);
-  virtual gfx::Size GetMaximumSize();
+  virtual gfx::Size GetMaximumSize() const;
   virtual void SetSheetOffset(const double offsetX, const double offsetY);
   virtual double GetSheetOffsetX();
   virtual double GetSheetOffsetY();
@@ -147,9 +149,9 @@ class NativeWindow : public base::SupportsUserData,
   virtual void SetMenu(AtomMenuModel* menu);
   virtual void SetParentWindow(NativeWindow* parent);
   virtual void SetBrowserView(NativeBrowserView* browser_view) = 0;
-  virtual gfx::NativeView GetNativeView() = 0;
-  virtual gfx::NativeWindow GetNativeWindow() = 0;
-  virtual gfx::AcceleratedWidget GetAcceleratedWidget() = 0;
+  virtual gfx::NativeView GetNativeView() const = 0;
+  virtual gfx::NativeWindow GetNativeWindow() const = 0;
+  virtual gfx::AcceleratedWidget GetAcceleratedWidget() const = 0;
 
   // Taskbar/Dock APIs.
   enum ProgressState {
@@ -215,6 +217,12 @@ class NativeWindow : public base::SupportsUserData,
   virtual void HandleKeyboardEvent(
       content::WebContents*,
       const content::NativeWebKeyboardEvent& event) {}
+  virtual void ShowAutofillPopup(
+    content::RenderFrameHost* frame_host,
+    const gfx::RectF& bounds,
+    const std::vector<base::string16>& values,
+    const std::vector<base::string16>& labels) {}
+  virtual void HideAutofillPopup(content::RenderFrameHost* frame_host) {}
 
   // Public API used by platform-dependent delegates and observers to send UI
   // related notifications.
@@ -281,8 +289,10 @@ class NativeWindow : public base::SupportsUserData,
       const std::vector<DraggableRegion>& regions);
 
   // Converts between content bounds and window bounds.
-  virtual gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& bounds) = 0;
-  virtual gfx::Rect WindowBoundsToContentBounds(const gfx::Rect& bounds) = 0;
+  virtual gfx::Rect ContentBoundsToWindowBounds(
+      const gfx::Rect& bounds) const = 0;
+  virtual gfx::Rect WindowBoundsToContentBounds(
+      const gfx::Rect& bounds) const = 0;
 
   // Called when the window needs to update its draggable region.
   virtual void UpdateDraggableRegions(

+ 5 - 5
atom/browser/native_window_mac.h

@@ -89,9 +89,9 @@ class NativeWindowMac : public NativeWindow,
   void SetContentProtection(bool enable) override;
   void SetBrowserView(NativeBrowserView* browser_view) override;
   void SetParentWindow(NativeWindow* parent) override;
-  gfx::NativeView GetNativeView() override;
-  gfx::NativeWindow GetNativeWindow() override;
-  gfx::AcceleratedWidget GetAcceleratedWidget() override;
+  gfx::NativeView GetNativeView() const override;
+  gfx::NativeWindow GetNativeWindow() const override;
+  gfx::AcceleratedWidget GetAcceleratedWidget() const override;
   void SetProgressBar(double progress, const ProgressState state) override;
   void SetOverlayIcon(const gfx::Image& overlay,
                       const std::string& description) override;
@@ -140,8 +140,8 @@ class NativeWindowMac : public NativeWindow,
 
  private:
   // NativeWindow:
-  gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& bounds);
-  gfx::Rect WindowBoundsToContentBounds(const gfx::Rect& bounds);
+  gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& bounds) const;
+  gfx::Rect WindowBoundsToContentBounds(const gfx::Rect& bounds) const;
   void UpdateDraggableRegions(
       const std::vector<DraggableRegion>& regions) override;
 

+ 5 - 5
atom/browser/native_window_mac.mm

@@ -1314,15 +1314,15 @@ void NativeWindowMac::SetParentWindow(NativeWindow* parent) {
     [parent->GetNativeWindow() addChildWindow:window_ ordered:NSWindowAbove];
 }
 
-gfx::NativeView NativeWindowMac::GetNativeView() {
+gfx::NativeView NativeWindowMac::GetNativeView() const {
   return inspectable_web_contents()->GetView()->GetNativeView();
 }
 
-gfx::NativeWindow NativeWindowMac::GetNativeWindow() {
+gfx::NativeWindow NativeWindowMac::GetNativeWindow() const {
   return window_;
 }
 
-gfx::AcceleratedWidget NativeWindowMac::GetAcceleratedWidget() {
+gfx::AcceleratedWidget NativeWindowMac::GetAcceleratedWidget() const {
   return inspectable_web_contents()->GetView()->GetNativeView();
 }
 
@@ -1498,7 +1498,7 @@ std::vector<gfx::Rect> NativeWindowMac::CalculateNonDraggableRegions(
 }
 
 gfx::Rect NativeWindowMac::ContentBoundsToWindowBounds(
-    const gfx::Rect& bounds) {
+    const gfx::Rect& bounds) const {
   if (has_frame()) {
     gfx::Rect window_bounds(
         [window_ frameRectForContentRect:bounds.ToCGRect()]);
@@ -1511,7 +1511,7 @@ gfx::Rect NativeWindowMac::ContentBoundsToWindowBounds(
 }
 
 gfx::Rect NativeWindowMac::WindowBoundsToContentBounds(
-    const gfx::Rect& bounds) {
+    const gfx::Rect& bounds) const {
   if (has_frame()) {
     gfx::Rect content_bounds(
         [window_ contentRectForFrameRect:bounds.ToCGRect()]);

+ 30 - 7
atom/browser/native_window_views.cc

@@ -7,6 +7,7 @@
 #include <string>
 #include <vector>
 
+#include "atom/browser/api/atom_api_web_contents.h"
 #include "atom/browser/native_browser_view_views.h"
 #include "atom/browser/ui/views/menu_bar.h"
 #include "atom/browser/window_list.h"
@@ -319,6 +320,8 @@ NativeWindowViews::NativeWindowViews(
   window_->CenterWindow(size);
   Layout();
 
+  autofill_popup_.reset(new AutofillPopup(GetNativeView()));
+
 #if defined(OS_WIN)
   // Save initial window state.
   if (fullscreen)
@@ -924,11 +927,11 @@ void NativeWindowViews::SetParentWindow(NativeWindow* parent) {
 #endif
 }
 
-gfx::NativeView NativeWindowViews::GetNativeView() {
+gfx::NativeView NativeWindowViews::GetNativeView() const {
   return window_->GetNativeView();
 }
 
-gfx::NativeWindow NativeWindowViews::GetNativeWindow() {
+gfx::NativeWindow NativeWindowViews::GetNativeWindow() const {
   return window_->GetNativeWindow();
 }
 
@@ -999,7 +1002,7 @@ bool NativeWindowViews::IsVisibleOnAllWorkspaces() {
   return false;
 }
 
-gfx::AcceleratedWidget NativeWindowViews::GetAcceleratedWidget() {
+gfx::AcceleratedWidget NativeWindowViews::GetAcceleratedWidget() const {
   return GetNativeWindow()->GetHost()->GetAcceleratedWidget();
 }
 
@@ -1180,7 +1183,7 @@ void NativeWindowViews::OnWidgetMove() {
 }
 
 gfx::Rect NativeWindowViews::ContentBoundsToWindowBounds(
-    const gfx::Rect& bounds) {
+    const gfx::Rect& bounds) const {
   if (!has_frame())
     return bounds;
 
@@ -1201,7 +1204,7 @@ gfx::Rect NativeWindowViews::ContentBoundsToWindowBounds(
 }
 
 gfx::Rect NativeWindowViews::WindowBoundsToContentBounds(
-    const gfx::Rect& bounds) {
+    const gfx::Rect& bounds) const {
   if (!has_frame())
     return bounds;
 
@@ -1269,6 +1272,26 @@ void NativeWindowViews::HandleKeyboardEvent(
   }
 }
 
+void NativeWindowViews::ShowAutofillPopup(
+    content::RenderFrameHost* frame_host,
+    const gfx::RectF& bounds,
+    const std::vector<base::string16>& values,
+    const std::vector<base::string16>& labels) {
+  auto wc = atom::api::WebContents::FromWrappedClass(
+    v8::Isolate::GetCurrent(), web_contents());
+  autofill_popup_->CreateView(
+    frame_host,
+    wc->IsOffScreenOrEmbedderOffscreen(),
+    widget(),
+    bounds);
+  autofill_popup_->SetItems(values, labels);
+}
+
+void NativeWindowViews::HideAutofillPopup(
+    content::RenderFrameHost* frame_host) {
+  autofill_popup_->Hide();
+}
+
 void NativeWindowViews::Layout() {
   const auto size = GetContentsBounds().size();
   const auto menu_bar_bounds =
@@ -1306,11 +1329,11 @@ void NativeWindowViews::Layout() {
   }
 }
 
-gfx::Size NativeWindowViews::GetMinimumSize() {
+gfx::Size NativeWindowViews::GetMinimumSize() const {
   return NativeWindow::GetMinimumSize();
 }
 
-gfx::Size NativeWindowViews::GetMaximumSize() {
+gfx::Size NativeWindowViews::GetMaximumSize() const {
   return NativeWindow::GetMaximumSize();
 }
 

+ 16 - 7
atom/browser/native_window_views.h

@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "atom/browser/ui/accelerator_util.h"
+#include "atom/browser/ui/autofill_popup.h"
 #include "ui/views/widget/widget_delegate.h"
 #include "ui/views/widget/widget_observer.h"
 
@@ -106,8 +107,8 @@ class NativeWindowViews : public NativeWindow,
   void SetMenu(AtomMenuModel* menu_model) override;
   void SetBrowserView(NativeBrowserView* browser_view) override;
   void SetParentWindow(NativeWindow* parent) override;
-  gfx::NativeView GetNativeView() override;
-  gfx::NativeWindow GetNativeWindow() override;
+  gfx::NativeView GetNativeView() const override;
+  gfx::NativeWindow GetNativeWindow() const override;
   void SetOverlayIcon(const gfx::Image& overlay,
                       const std::string& description) override;
   void SetProgressBar(double progress, const ProgressState state) override;
@@ -118,7 +119,7 @@ class NativeWindowViews : public NativeWindow,
   void SetVisibleOnAllWorkspaces(bool visible) override;
   bool IsVisibleOnAllWorkspaces() override;
 
-  gfx::AcceleratedWidget GetAcceleratedWidget() override;
+  gfx::AcceleratedWidget GetAcceleratedWidget() const override;
 
 #if defined(OS_WIN)
   void SetIcon(HICON small_icon, HICON app_icon);
@@ -171,16 +172,22 @@ class NativeWindowViews : public NativeWindow,
 #endif
 
   // NativeWindow:
-  gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& bounds) override;
-  gfx::Rect WindowBoundsToContentBounds(const gfx::Rect& bounds) override;
+  gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& bounds) const override;
+  gfx::Rect WindowBoundsToContentBounds(const gfx::Rect& bounds) const override;
   void HandleKeyboardEvent(
       content::WebContents*,
       const content::NativeWebKeyboardEvent& event) override;
+  void ShowAutofillPopup(
+    content::RenderFrameHost* frame_host,
+    const gfx::RectF& bounds,
+    const std::vector<base::string16>& values,
+    const std::vector<base::string16>& labels) override;
+  void HideAutofillPopup(content::RenderFrameHost* frame_host) override;
 
   // views::View:
   void Layout() override;
-  gfx::Size GetMinimumSize() override;
-  gfx::Size GetMaximumSize() override;
+  gfx::Size GetMinimumSize() const override;
+  gfx::Size GetMaximumSize() const override;
   bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
 
   // Register accelerators supported by the menu model.
@@ -194,6 +201,8 @@ class NativeWindowViews : public NativeWindow,
 
   NativeBrowserView* browser_view_;
 
+  std::unique_ptr<AutofillPopup> autofill_popup_;
+
   std::unique_ptr<MenuBar> menu_bar_;
   bool menu_bar_autohide_;
   bool menu_bar_visible_;

+ 161 - 13
atom/browser/osr/osr_render_widget_host_view.cc

@@ -4,6 +4,7 @@
 
 #include "atom/browser/osr/osr_render_widget_host_view.h"
 
+#include <algorithm>
 #include <vector>
 
 #include "base/callback_helpers.h"
@@ -22,9 +23,12 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/context_factory.h"
 #include "media/base/video_frame.h"
+#include "third_party/WebKit/public/platform/WebInputEvent.h"
 #include "ui/compositor/compositor.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_type.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/event_constants.h"
 #include "ui/events/latency_info.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/native_widget_types.h"
@@ -37,6 +41,70 @@ namespace {
 const float kDefaultScaleFactor = 1.0;
 const int kFrameRetryLimit = 2;
 
+ui::MouseEvent UiMouseEventFromWebMouseEvent(blink::WebMouseEvent event) {
+  ui::EventType type = ui::EventType::ET_UNKNOWN;
+  switch (event.type()) {
+    case blink::WebInputEvent::MouseDown:
+      type = ui::EventType::ET_MOUSE_PRESSED;
+      break;
+    case blink::WebInputEvent::MouseUp:
+      type = ui::EventType::ET_MOUSE_RELEASED;
+      break;
+    case blink::WebInputEvent::MouseMove:
+      type = ui::EventType::ET_MOUSE_MOVED;
+      break;
+    case blink::WebInputEvent::MouseEnter:
+      type = ui::EventType::ET_MOUSE_ENTERED;
+      break;
+    case blink::WebInputEvent::MouseLeave:
+      type = ui::EventType::ET_MOUSE_EXITED;
+      break;
+    case blink::WebInputEvent::MouseWheel:
+      type = ui::EventType::ET_MOUSEWHEEL;
+      break;
+    default:
+      type = ui::EventType::ET_UNKNOWN;
+      break;
+  }
+
+  int button_flags = 0;
+  switch (event.button) {
+    case blink::WebMouseEvent::Button::X1:
+      button_flags |= ui::EventFlags::EF_BACK_MOUSE_BUTTON;
+      break;
+    case blink::WebMouseEvent::Button::X2:
+      button_flags |= ui::EventFlags::EF_FORWARD_MOUSE_BUTTON;
+      break;
+    case blink::WebMouseEvent::Button::Left:
+      button_flags |= ui::EventFlags::EF_LEFT_MOUSE_BUTTON;
+      break;
+    case blink::WebMouseEvent::Button::Middle:
+      button_flags |= ui::EventFlags::EF_MIDDLE_MOUSE_BUTTON;
+      break;
+    case blink::WebMouseEvent::Button::Right:
+      button_flags |= ui::EventFlags::EF_RIGHT_MOUSE_BUTTON;
+      break;
+    default:
+      button_flags = 0;
+      break;
+  }
+
+  ui::MouseEvent ui_event(type,
+    gfx::Point(std::floor(event.x), std::floor(event.y)),
+    gfx::Point(std::floor(event.x), std::floor(event.y)),
+    ui::EventTimeForNow(),
+    button_flags, button_flags);
+  ui_event.SetClickCount(event.clickCount);
+
+  return ui_event;
+}
+
+ui::MouseWheelEvent UiMouseWheelEventFromWebMouseEvent(
+    blink::WebMouseWheelEvent event) {
+  return ui::MouseWheelEvent(UiMouseEventFromWebMouseEvent(event),
+    std::floor(event.deltaX), std::floor(event.deltaY));
+}
+
 #if !defined(OS_MACOSX)
 
 const int kResizeLockTimeoutMs = 67;
@@ -619,6 +687,8 @@ void OffScreenRenderWidgetHostView::Destroy() {
         child_host_view_->CancelWidget();
       for (auto guest_host_view : guest_host_views_)
         guest_host_view->CancelWidget();
+      for (auto proxy_view : proxy_views_)
+        proxy_view->RemoveObserver();
       Hide();
     }
   }
@@ -835,6 +905,22 @@ void OffScreenRenderWidgetHostView::RemoveGuestHostView(
   guest_host_views_.erase(guest_host);
 }
 
+void OffScreenRenderWidgetHostView::AddViewProxy(OffscreenViewProxy* proxy) {
+  proxy->SetObserver(this);
+  proxy_views_.insert(proxy);
+}
+
+void OffScreenRenderWidgetHostView::RemoveViewProxy(OffscreenViewProxy* proxy) {
+  proxy->RemoveObserver();
+  proxy_views_.erase(proxy);
+}
+
+void OffScreenRenderWidgetHostView::ProxyViewDestroyed(
+    OffscreenViewProxy* proxy) {
+  proxy_views_.erase(proxy);
+  Invalidate();
+}
+
 void OffScreenRenderWidgetHostView::RegisterGuestViewFrameSwappedCallback(
     content::RenderWidgetHostViewGuest* guest_host_view) {
   guest_host_view->RegisterFrameSwappedCallback(base::MakeUnique<base::Closure>(
@@ -906,12 +992,16 @@ void CopyBitmapTo(
   char* dest = static_cast<char*>(destination.getPixels());
   int pixelsize = source.bytesPerPixel();
 
-  if (pos.x() + pos.width() <= destination.width() &&
-    pos.y() + pos.height() <= destination.height()) {
-    for (int i = 0; i < pos.height(); i++) {
+  int width = pos.x() + pos.width() <= destination.width() ? pos.width()
+    : pos.width() - ((pos.x() + pos.width()) - destination.width());
+  int height = pos.y() + pos.height() <= destination.height() ? pos.height()
+    : pos.height() - ((pos.y() + pos.height()) - destination.height());
+
+  if (width > 0 && height > 0) {
+    for (int i = 0; i < height; i++) {
       memcpy(dest + ((pos.y() + i) * destination.width() + pos.x()) * pixelsize,
         src + (i * source.width()) * pixelsize,
-        pos.width() * pixelsize);
+        width * pixelsize);
     }
   }
 
@@ -926,19 +1016,41 @@ void OffScreenRenderWidgetHostView::OnPaint(
 
   if (parent_callback_) {
     parent_callback_.Run(damage_rect, bitmap);
-  } else if (popup_host_view_ && popup_bitmap_.get()) {
-    gfx::Rect pos = popup_host_view_->popup_position_;
+  } else {
     gfx::Rect damage(damage_rect);
-    damage.Union(pos);
 
-    SkBitmap copy = SkBitmapOperations::CreateTiledBitmap(bitmap,
-      pos.x(), pos.y(), pos.width(), pos.height());
+    std::vector<gfx::Rect> damages;
+    std::vector<const SkBitmap*> bitmaps;
+    std::vector<SkBitmap> originals;
+
+    if (popup_host_view_ && popup_bitmap_.get()) {
+      gfx::Rect pos = popup_host_view_->popup_position_;
+      damage.Union(pos);
+      damages.push_back(pos);
+      bitmaps.push_back(popup_bitmap_.get());
+      originals.push_back(SkBitmapOperations::CreateTiledBitmap(bitmap,
+        pos.x(), pos.y(), pos.width(), pos.height()));
+    }
 
-    CopyBitmapTo(bitmap, *popup_bitmap_, pos);
+    for (auto proxy_view : proxy_views_) {
+      gfx::Rect pos = proxy_view->GetBounds();
+      damage.Union(pos);
+      damages.push_back(pos);
+      bitmaps.push_back(proxy_view->GetBitmap());
+      originals.push_back(SkBitmapOperations::CreateTiledBitmap(bitmap,
+        pos.x(), pos.y(), pos.width(), pos.height()));
+    }
+
+    for (size_t i = 0; i < damages.size(); i++) {
+      CopyBitmapTo(bitmap, *(bitmaps[i]), damages[i]);
+    }
+
+    damage.Intersect(GetViewBounds());
     callback_.Run(damage, bitmap);
-    CopyBitmapTo(bitmap, copy, pos);
-  } else {
-    callback_.Run(damage_rect, bitmap);
+
+    for (size_t i = 0; i < damages.size(); i++) {
+      CopyBitmapTo(bitmap, originals[i], damages[i]);
+    }
   }
 
   ReleaseResize();
@@ -951,6 +1063,11 @@ void OffScreenRenderWidgetHostView::OnPopupPaint(
   InvalidateBounds(popup_host_view_->popup_position_);
 }
 
+void OffScreenRenderWidgetHostView::OnProxyViewPaint(
+    const gfx::Rect& damage_rect) {
+  InvalidateBounds(damage_rect);
+}
+
 void OffScreenRenderWidgetHostView::HoldResize() {
   if (!hold_resize_)
     hold_resize_ = true;
@@ -992,6 +1109,21 @@ void OffScreenRenderWidgetHostView::ProcessKeyboardEvent(
 void OffScreenRenderWidgetHostView::ProcessMouseEvent(
     const blink::WebMouseEvent& event,
     const ui::LatencyInfo& latency) {
+  for (auto proxy_view : proxy_views_) {
+    gfx::Rect bounds = proxy_view->GetBounds();
+    if (bounds.Contains(event.x, event.y)) {
+      blink::WebMouseEvent proxy_event(event);
+      proxy_event.x -= bounds.x();
+      proxy_event.y -= bounds.y();
+      proxy_event.windowX = proxy_event.x;
+      proxy_event.windowY = proxy_event.y;
+
+      ui::MouseEvent ui_event = UiMouseEventFromWebMouseEvent(proxy_event);
+      proxy_view->OnEvent(&ui_event);
+      return;
+    }
+  }
+
   if (!IsPopupWidget()) {
     if (popup_host_view_ &&
         popup_host_view_->popup_position_.Contains(event.x, event.y)) {
@@ -1005,6 +1137,7 @@ void OffScreenRenderWidgetHostView::ProcessMouseEvent(
       return;
     }
   }
+
   if (!render_widget_host_)
     return;
   render_widget_host_->ForwardMouseEvent(event);
@@ -1013,6 +1146,21 @@ void OffScreenRenderWidgetHostView::ProcessMouseEvent(
 void OffScreenRenderWidgetHostView::ProcessMouseWheelEvent(
     const blink::WebMouseWheelEvent& event,
     const ui::LatencyInfo& latency) {
+  for (auto proxy_view : proxy_views_) {
+    gfx::Rect bounds = proxy_view->GetBounds();
+    if (bounds.Contains(event.x, event.y)) {
+      blink::WebMouseWheelEvent proxy_event(event);
+      proxy_event.x -= bounds.x();
+      proxy_event.y -= bounds.y();
+      proxy_event.windowX = proxy_event.x;
+      proxy_event.windowY = proxy_event.y;
+
+      ui::MouseWheelEvent ui_event =
+        UiMouseWheelEventFromWebMouseEvent(proxy_event);
+      proxy_view->OnEvent(&ui_event);
+      return;
+    }
+  }
   if (!IsPopupWidget()) {
     if (popup_host_view_) {
       if (popup_host_view_->popup_position_.Contains(event.x, event.y)) {

+ 8 - 1
atom/browser/osr/osr_render_widget_host_view.h

@@ -16,6 +16,7 @@
 #include "atom/browser/native_window.h"
 #include "atom/browser/native_window_observer.h"
 #include "atom/browser/osr/osr_output_device.h"
+#include "atom/browser/osr/osr_view_proxy.h"
 #include "base/process/kill.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
@@ -68,7 +69,8 @@ class OffScreenRenderWidgetHostView
 #if !defined(OS_MACOSX)
       public content::DelegatedFrameHostClient,
 #endif
-      public NativeWindowObserver {
+      public NativeWindowObserver,
+      public OffscreenViewProxyObserver {
  public:
   OffScreenRenderWidgetHostView(bool transparent,
                                 const OnPaintCallback& callback,
@@ -208,6 +210,9 @@ class OffScreenRenderWidgetHostView
   void CancelWidget();
   void AddGuestHostView(OffScreenRenderWidgetHostView* guest_host);
   void RemoveGuestHostView(OffScreenRenderWidgetHostView* guest_host);
+  void AddViewProxy(OffscreenViewProxy* proxy);
+  void RemoveViewProxy(OffscreenViewProxy* proxy);
+  void ProxyViewDestroyed(OffscreenViewProxy* proxy);
 
   void RegisterGuestViewFrameSwappedCallback(
       content::RenderWidgetHostViewGuest* guest_host_view);
@@ -216,6 +221,7 @@ class OffScreenRenderWidgetHostView
 
   void OnPaint(const gfx::Rect& damage_rect, const SkBitmap& bitmap);
   void OnPopupPaint(const gfx::Rect& damage_rect, const SkBitmap& bitmap);
+  void OnProxyViewPaint(const gfx::Rect& damage_rect);
 
   bool IsPopupWidget() const {
     return popup_type_ != blink::WebPopupTypeNone;
@@ -273,6 +279,7 @@ class OffScreenRenderWidgetHostView
   std::unique_ptr<SkBitmap> popup_bitmap_;
   OffScreenRenderWidgetHostView* child_host_view_;
   std::set<OffScreenRenderWidgetHostView*> guest_host_views_;
+  std::set<OffscreenViewProxy*> proxy_views_;
 
   NativeWindow* native_window_;
   OffScreenOutputDevice* software_output_device_;

+ 58 - 0
atom/browser/osr/osr_view_proxy.cc

@@ -0,0 +1,58 @@
+// 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/browser/osr/osr_view_proxy.h"
+
+namespace atom {
+
+OffscreenViewProxy::OffscreenViewProxy(views::View* view)
+    : view_(view), observer_(nullptr) {
+  view_bitmap_.reset(new SkBitmap);
+}
+
+OffscreenViewProxy::~OffscreenViewProxy() {
+  if (observer_) {
+    observer_->ProxyViewDestroyed(this);
+  }
+}
+
+void OffscreenViewProxy::SetObserver(OffscreenViewProxyObserver* observer) {
+  if (observer_) {
+    observer_->ProxyViewDestroyed(this);
+  }
+  observer_ = observer;
+}
+
+void OffscreenViewProxy::RemoveObserver() {
+  observer_ = nullptr;
+}
+
+const SkBitmap* OffscreenViewProxy::GetBitmap() const {
+  return view_bitmap_.get();
+}
+
+void OffscreenViewProxy::SetBitmap(const SkBitmap& bitmap) {
+  if (view_bounds_.width() == bitmap.width() &&
+      view_bounds_.height() == bitmap.height() &&
+      observer_) {
+    view_bitmap_.reset(new SkBitmap(bitmap));
+    observer_->OnProxyViewPaint(view_bounds_);
+  }
+}
+
+const gfx::Rect& OffscreenViewProxy::GetBounds() {
+  return view_bounds_;
+}
+
+void OffscreenViewProxy::SetBounds(const gfx::Rect& bounds) {
+  view_bounds_ = bounds;
+}
+
+void OffscreenViewProxy::OnEvent(ui::Event* event) {
+  if (view_) {
+    view_->OnEvent(event);
+  }
+}
+
+}  // namespace atom

+ 53 - 0
atom/browser/osr/osr_view_proxy.h

@@ -0,0 +1,53 @@
+// 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_BROWSER_OSR_OSR_VIEW_PROXY_H_
+#define ATOM_BROWSER_OSR_OSR_VIEW_PROXY_H_
+
+#include <set>
+
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/events/event.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/views/view.h"
+
+namespace atom {
+
+class OffscreenViewProxy;
+
+class OffscreenViewProxyObserver {
+ public:
+  virtual void OnProxyViewPaint(const gfx::Rect& damage_rect) = 0;
+  virtual void ProxyViewDestroyed(OffscreenViewProxy* proxy) = 0;
+};
+
+class OffscreenViewProxy {
+ public:
+  explicit OffscreenViewProxy(views::View* view);
+  ~OffscreenViewProxy();
+
+  void SetObserver(OffscreenViewProxyObserver* observer);
+  void RemoveObserver();
+
+  const SkBitmap* GetBitmap() const;
+  void SetBitmap(const SkBitmap& bitmap);
+
+  const gfx::Rect& GetBounds();
+  void SetBounds(const gfx::Rect& bounds);
+
+  void OnEvent(ui::Event* event);
+
+  void ResetView() { view_ = nullptr; }
+ private:
+  views::View* view_;
+
+  gfx::Rect view_bounds_;
+  std::unique_ptr<SkBitmap> view_bitmap_;
+
+  OffscreenViewProxyObserver* observer_;
+};
+
+}  // namespace atom
+
+#endif  // ATOM_BROWSER_OSR_OSR_VIEW_PROXY_H_

+ 266 - 0
atom/browser/ui/autofill_popup.cc

@@ -0,0 +1,266 @@
+// 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 <algorithm>
+#include <utility>
+#include <vector>
+
+#include "atom/browser/osr/osr_render_widget_host_view.h"
+#include "atom/browser/osr/osr_view_proxy.h"
+#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/geometry/point.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/vector2d.h"
+#include "ui/gfx/text_utils.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), view_(nullptr) {
+  bold_font_list_ =
+    gfx::FontList().DeriveWithWeight(gfx::Font::Weight::BOLD);
+  smaller_font_list_ =
+    gfx::FontList().DeriveWithSizeDelta(kSmallerFontSizeDelta);
+}
+
+AutofillPopup::~AutofillPopup() {
+  Hide();
+}
+
+void AutofillPopup::CreateView(
+    content::RenderFrameHost* frame_host,
+    bool offscreen,
+    views::Widget* parent_widget,
+    const gfx::RectF& r) {
+  frame_host_ = frame_host;
+  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());
+  popup_bounds_in_view_ = lb;
+  views::View::ConvertPointToScreen(
+    parent_widget->GetContentsView(), &menu_position);
+  popup_bounds_ = gfx::Rect(menu_position, lb.size());
+  element_bounds_ = popup_bounds_;
+
+  Hide();
+  view_ = new AutofillPopupView(this, parent_widget);
+  view_->Show();
+
+  if (offscreen) {
+    auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
+        frame_host_->GetView());
+    view_->view_proxy_.reset(new OffscreenViewProxy(view_));
+    osr_rwhv->AddViewProxy(view_->view_proxy_.get());
+  }
+}
+
+void AutofillPopup::Hide() {
+  if (view_) {
+    view_->Hide();
+    view_ = nullptr;
+  }
+}
+
+void AutofillPopup::SetItems(const std::vector<base::string16>& values,
+                            const std::vector<base::string16>& labels) {
+  values_ = values;
+  labels_ = labels;
+  UpdatePopupBounds();
+  if (view_) {
+    view_->OnSuggestionsChanged();
+  }
+}
+
+void AutofillPopup::AcceptSuggestion(int index) {
+  frame_host_->Send(new AtomAutofillFrameMsg_AcceptSuggestion(
+    frame_host_->GetRoutingID(), 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);
+  popup_bounds_in_view_ = gfx::Rect(popup_bounds_in_view_.origin(),
+      gfx::Size(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 (size_t 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 (view_ && 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 (size_t i = 0; i < values_.size(); ++i) {
+    current_height += kRowHeight;
+
+    if (y <= current_height)
+      return i;
+  }
+
+  return values_.size() - 1;
+}
+
+}  // namespace atom

+ 82 - 0
atom/browser/ui/autofill_popup.h

@@ -0,0 +1,82 @@
+// 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 <vector>
+
+#include "atom/browser/ui/views/autofill_popup_view.h"
+#include "content/public/browser/render_frame_host.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:
+  explicit AutofillPopup(gfx::NativeView);
+  ~AutofillPopup();
+
+  void CreateView(content::RenderFrameHost* render_frame,
+    bool offscreen, 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_;
+  gfx::Rect popup_bounds_in_view_;
+
+  // 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
+  content::RenderFrameHost* frame_host_;
+
+  // The popup view. The lifetime is managed by the owning Widget
+  AutofillPopupView* view_;
+
+  DISALLOW_COPY_AND_ASSIGN(AutofillPopup);
+};
+
+}  // namespace atom
+
+#endif  // ATOM_BROWSER_UI_AUTOFILL_POPUP_H_

+ 471 - 0
atom/browser/ui/views/autofill_popup_view.cc

@@ -0,0 +1,471 @@
+// 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 "base/i18n/rtl.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),
+      view_proxy_(nullptr),
+      weak_ptr_factory_(this) {
+  CreateChildViews();
+  SetFocusBehavior(FocusBehavior::ALWAYS);
+  set_drag_controller(this);
+}
+
+AutofillPopupView::~AutofillPopupView() {
+  if (popup_) {
+    auto host = popup_->frame_host_->GetRenderViewHost()->GetWidget();
+    host->RemoveKeyPressEventCallback(keypress_callback_);
+    popup_->view_ = nullptr;
+    popup_ = nullptr;
+  }
+
+  RemoveObserver();
+
+  if (view_proxy_.get()) {
+    view_proxy_->ResetView();
+  }
+
+  if (GetWidget()) {
+    GetWidget()->Close();
+  }
+}
+
+void AutofillPopupView::Show() {
+  if (!popup_)
+    return;
+
+  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_->frame_host_->GetRenderViewHost()->GetWidget();
+  host->AddKeyPressEventCallback(keypress_callback_);
+
+  NotifyAccessibilityEvent(ui::AX_EVENT_MENU_START, true);
+}
+
+void AutofillPopupView::Hide() {
+  if (popup_) {
+    auto host = popup_->frame_host_->GetRenderViewHost()->GetWidget();
+    host->RemoveKeyPressEventCallback(keypress_callback_);
+    popup_ = nullptr;
+  }
+
+  RemoveObserver();
+  NotifyAccessibilityEvent(ui::AX_EVENT_MENU_END, true);
+
+  if (GetWidget()) {
+    GetWidget()->Close();
+  }
+}
+
+void AutofillPopupView::OnSuggestionsChanged() {
+  if (!popup_)
+    return;
+
+  CreateChildViews();
+  if (popup_->GetLineCount() == 0) {
+    popup_->Hide();
+    return;
+  }
+  DoUpdateBoundsAndRedrawPopup();
+}
+
+void AutofillPopupView::OnSelectedRowChanged(
+    base::Optional<int> previous_row_selection,
+    base::Optional<int> current_row_selection) {
+  SchedulePaint();
+
+  if (current_row_selection) {
+    int selected = current_row_selection.value_or(-1);
+    if (selected == -1 || selected >= child_count())
+      return;
+    child_at(selected)->NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true);
+  }
+}
+
+void AutofillPopupView::DrawAutofillEntry(gfx::Canvas* canvas,
+                                          int index,
+                                          const gfx::Rect& entry_rect) {
+  if (!popup_)
+    return;
+
+  canvas->FillRect(
+      entry_rect,
+      GetNativeTheme()->GetSystemColor(
+          popup_->GetBackgroundColorIDForRow(index)));
+
+  const bool is_rtl = base::i18n::IsRTL();
+  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() {
+  if (!popup_)
+    return;
+
+  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() {
+  if (!popup_)
+    return;
+
+  GetWidget()->SetBounds(popup_->popup_bounds_);
+  SchedulePaint();
+}
+
+void AutofillPopupView::OnPaint(gfx::Canvas* canvas) {
+  if (!popup_ || popup_->GetLineCount() != child_count())
+    return;
+  gfx::Canvas* draw_canvas = canvas;
+  SkBitmap bitmap;
+
+  if (view_proxy_.get()) {
+    bitmap.allocN32Pixels(popup_->popup_bounds_in_view_.width(),
+                          popup_->popup_bounds_in_view_.height(),
+                          true);
+    draw_canvas = new gfx::Canvas(new SkCanvas(bitmap), 1.0);
+  }
+
+  draw_canvas->DrawColor(GetNativeTheme()->GetSystemColor(
+      ui::NativeTheme::kColorId_ResultsTableNormalBackground));
+  OnPaintBorder(draw_canvas);
+
+  for (int i = 0; i < popup_->GetLineCount(); ++i) {
+    gfx::Rect line_rect = popup_->GetRowBounds(i);
+
+    DrawAutofillEntry(draw_canvas, i, line_rect);
+  }
+
+  if (view_proxy_.get()) {
+    view_proxy_->SetBounds(popup_->popup_bounds_in_view_);
+    view_proxy_->SetBitmap(bitmap);
+  }
+}
+
+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) {
+  if (accelerator.modifiers() != ui::EF_NONE)
+    return false;
+
+  if (accelerator.key_code() == ui::VKEY_ESCAPE) {
+    if (popup_)
+      popup_->Hide();
+    return true;
+  }
+
+  if (accelerator.key_code() == ui::VKEY_RETURN)
+    return AcceptSelectedLine();
+
+  return false;
+}
+
+bool AutofillPopupView::HandleKeyPressEvent(
+    const content::NativeWebKeyboardEvent& event) {
+  if (!popup_)
+    return false;
+  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_)
+    popup_->Hide();
+}
+
+void AutofillPopupView::OnWidgetBoundsChanged(views::Widget* widget,
+                                              const gfx::Rect& new_bounds) {
+  if (widget != parent_widget_)
+    return;
+  if (popup_)
+    popup_->Hide();
+}
+
+void AutofillPopupView::AcceptSuggestion(int index) {
+  if (!popup_)
+    return;
+
+  popup_->AcceptSuggestion(index);
+  popup_->Hide();
+}
+
+bool AutofillPopupView::AcceptSelectedLine() {
+  if (!selected_line_ || selected_line_.value() >= popup_->GetLineCount())
+    return false;
+
+  AcceptSuggestion(selected_line_.value());
+  return true;
+}
+
+void AutofillPopupView::AcceptSelection(const gfx::Point& point) {
+  if (!popup_)
+    return;
+
+  SetSelectedLine(popup_->LineFromY(point.y()));
+  AcceptSelectedLine();
+}
+
+void AutofillPopupView::SetSelectedLine(base::Optional<int> selected_line) {
+  if (!popup_)
+    return;
+  if (selected_line_ == selected_line)
+    return;
+  if (selected_line && selected_line.value() >= popup_->GetLineCount())
+    return;
+
+  auto previous_selected_line(selected_line_);
+  selected_line_ = selected_line;
+  OnSelectedRowChanged(previous_selected_line, selected_line_);
+}
+
+void AutofillPopupView::SetSelection(const gfx::Point& point) {
+  if (!popup_)
+    return;
+
+  SetSelectedLine(popup_->LineFromY(point.y()));
+}
+
+void AutofillPopupView::SelectNextLine() {
+  if (!popup_)
+    return;
+
+  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() {
+  if (!popup_)
+    return;
+
+  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

+ 152 - 0
atom/browser/ui/views/autofill_popup_view.h

@@ -0,0 +1,152 @@
+// 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 "atom/browser/osr/osr_view_proxy.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:
+  friend class AutofillPopup;
+
+  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_;
+
+  std::unique_ptr<OffscreenViewProxy> view_proxy_;
+
+  // 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_

+ 1 - 1
atom/browser/ui/views/menu_bar.cc

@@ -119,7 +119,7 @@ bool MenuBar::GetMenuButtonFromScreenPoint(const gfx::Point& point,
 
   for (int i = 0; i < child_count(); ++i) {
     views::View* view = child_at(i);
-    if (view->bounds().Contains(location) &&
+    if (view->GetMirroredBounds().Contains(location) &&
         (menu_model_->GetTypeAt(i) == AtomMenuModel::TYPE_SUBMENU)) {
       *menu_model = menu_model_->GetSubmenuModelAt(i);
       *button = static_cast<views::MenuButton*>(view);

+ 11 - 0
atom/common/api/api_messages.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,16 @@ IPC_MESSAGE_ROUTED3(AtomViewMsg_Message,
 
 IPC_MESSAGE_ROUTED0(AtomViewMsg_Offscreen)
 
+IPC_MESSAGE_ROUTED3(AtomAutofillFrameHostMsg_ShowPopup,
+                    gfx::RectF /* bounds */,
+                    std::vector<base::string16> /* values */,
+                    std::vector<base::string16> /* labels */)
+
+IPC_MESSAGE_ROUTED0(AtomAutofillFrameHostMsg_HidePopup)
+
+IPC_MESSAGE_ROUTED1(AtomAutofillFrameMsg_AcceptSuggestion,
+                    base::string16 /* suggestion */)
+
 // Sent by the renderer when the draggable regions are updated.
 IPC_MESSAGE_ROUTED1(AtomViewHostMsg_UpdateDraggableRegions,
                     std::vector<atom::DraggableRegion> /* regions */)

+ 233 - 0
atom/renderer/atom_autofill_agent.cc

@@ -0,0 +1,233 @@
+// 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 <vector>
+
+#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),
+    helper_(new Helper(this)),
+    focused_node_was_last_clicked_(false),
+    was_focused_before_now_(false),
+    weak_ptr_factory_(this) {
+  render_frame()->GetWebFrame()->setAutofillClient(this);
+}
+
+void AutofillAgent::OnDestruct() {
+  delete this;
+}
+
+void AutofillAgent::DidChangeScrollOffset() {
+  HidePopup();
+}
+
+void AutofillAgent::FocusedNodeChanged(const blink::WebNode&) {
+  focused_node_was_last_clicked_ = false;
+  was_focused_before_now_ = false;
+  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;
+  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);
+}
+
+AutofillAgent::Helper::Helper(AutofillAgent* agent)
+  : content::RenderViewObserver(agent->render_frame()->GetRenderView()),
+    agent_(agent) {
+}
+
+void AutofillAgent::Helper::OnMouseDown(const blink::WebNode& node) {
+  agent_->focused_node_was_last_clicked_ = !node.isNull() && node.focused();
+}
+
+void AutofillAgent::Helper::FocusChangeComplete() {
+  agent_->DoFocusChangeComplete();
+}
+
+bool AutofillAgent::OnMessageReceived(const IPC::Message& message) {
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(AutofillAgent, message)
+    IPC_MESSAGE_HANDLER(AtomAutofillFrameMsg_AcceptSuggestion,
+      OnAcceptSuggestion)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+
+  return handled;
+}
+
+bool AutofillAgent::IsUserGesture() const {
+  return blink::WebUserGestureIndicator::isProcessingUserGesture();
+}
+
+void AutofillAgent::HidePopup() {
+  Send(new AtomAutofillFrameHostMsg_HidePopup(render_frame()->GetRoutingID()));
+}
+
+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 AtomAutofillFrameHostMsg_ShowPopup(
+    render_frame()->GetRoutingID(), bounds, values, labels));
+}
+
+void AutofillAgent::OnAcceptSuggestion(base::string16 suggestion) {
+  auto element = render_frame()->GetWebFrame()->document().focusedElement();
+  if (element.isFormControlElement()) {
+    toWebInputElement(&element)->setSuggestedValue(
+      blink::WebString::fromUTF16(suggestion));
+  }
+}
+
+void AutofillAgent::DoFocusChangeComplete() {
+  auto element = render_frame()->GetWebFrame()->document().focusedElement();
+  if (element.isNull() || !element.isFormControlElement())
+    return;
+
+  if (focused_node_was_last_clicked_ && was_focused_before_now_) {
+    ShowSuggestionsOptions options;
+    options.autofill_on_empty_values = true;
+    auto input_element = toWebInputElement(&element);
+    if (input_element)
+      ShowSuggestions(*input_element, options);
+  }
+
+  was_focused_before_now_ = true;
+  focused_node_was_last_clicked_ = false;
+}
+
+}  // namespace atom

+ 91 - 0
atom/renderer/atom_autofill_agent.h

@@ -0,0 +1,91 @@
+// 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 <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "content/public/renderer/render_frame_observer.h"
+#include "content/public/renderer/render_view_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:
+  explicit AutofillAgent(content::RenderFrame* frame);
+
+  // content::RenderFrameObserver:
+  void OnDestruct() override;
+
+  void DidChangeScrollOffset() override;
+  void FocusedNodeChanged(const blink::WebNode&) override;
+
+ private:
+  class Helper : public content::RenderViewObserver {
+   public:
+    explicit Helper(AutofillAgent* agent);
+
+    // content::RenderViewObserver implementation.
+    void OnDestruct() override {}
+    void OnMouseDown(const blink::WebNode&) override;
+    void FocusChangeComplete() override;
+
+   private:
+    AutofillAgent* agent_;
+  };
+  friend class Helper;
+
+  struct ShowSuggestionsOptions {
+    ShowSuggestionsOptions();
+    bool autofill_on_empty_values;
+    bool requires_caret_at_end;
+  };
+
+  bool OnMessageReceived(const IPC::Message& message) override;
+
+  // 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);
+
+  void DoFocusChangeComplete();
+
+  std::unique_ptr<Helper> helper_;
+
+  // True when the last click was on the focused node.
+  bool focused_node_was_last_clicked_;
+
+  // This is set to false when the focus changes, then set back to true soon
+  // afterwards. This helps track whether an event happened after a node was
+  // already focused, or if it caused the focus to change.
+  bool was_focused_before_now_;
+
+  base::WeakPtrFactory<AutofillAgent> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(AutofillAgent);
+};
+
+}  // namespace atom
+
+#endif  // ATOM_RENDERER_ATOM_AUTOFILL_AGENT_H_

+ 2 - 0
atom/renderer/renderer_client_base.cc

@@ -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);

+ 8 - 0
filenames.gypi

@@ -244,6 +244,8 @@
       'atom/browser/osr/osr_render_widget_host_view.cc',
       'atom/browser/osr/osr_render_widget_host_view.h',
       'atom/browser/osr/osr_render_widget_host_view_mac.mm',
+      'atom/browser/osr/osr_view_proxy.cc',
+      'atom/browser/osr/osr_view_proxy.h',
       'atom/browser/net/about_protocol_handler.cc',
       'atom/browser/net/about_protocol_handler.h',
       'atom/browser/net/asar/asar_protocol_handler.cc',
@@ -291,6 +293,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 +322,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 +485,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',