Merge ~osomon/oxide:autofill into oxide:master

Proposed by Olivier Tilloy
Status: Needs review
Proposed branch: ~osomon/oxide:autofill
Merge into: oxide:master
Diff against target: 5836 lines (+4175/-459)
69 files modified
qt/core/BUILD.gn (+7/-0)
qt/core/browser/contents_view_impl.cc (+1/-1)
qt/core/browser/oxide_qt_browser_platform_integration.cc (+6/-1)
qt/core/browser/oxide_qt_browser_platform_integration.h (+2/-1)
qt/core/browser/oxide_qt_web_context.cc (+22/-3)
qt/core/browser/oxide_qt_web_context.h (+4/-1)
qt/core/browser/oxide_qt_web_view.cc (+26/-1)
qt/core/browser/oxide_qt_web_view.h (+5/-1)
qt/core/browser/web_autofill_popup_host.cc (+139/-0)
qt/core/browser/web_autofill_popup_host.h (+78/-0)
qt/core/glue/autofill_suggestion.cc (+30/-0)
qt/core/glue/autofill_suggestion.h (+50/-0)
qt/core/glue/auxiliary_ui_factory.h (+10/-1)
qt/core/glue/oxide_qt_web_context_proxy.h (+4/-1)
qt/core/glue/web_autofill_popup.h (+43/-0)
qt/core/glue/web_autofill_popup_client.h (+40/-0)
qt/qmlplugin/oxide.qmltypes (+7/-423)
qt/qmlplugin/oxide_qml_plugin.cc (+3/-1)
qt/quick/api/oxideqquickwebcontext.cc (+31/-0)
qt/quick/api/oxideqquickwebcontext.h (+7/-1)
qt/quick/qquick_legacy_auxiliary_ui_factory.cc (+13/-1)
qt/quick/qquick_legacy_auxiliary_ui_factory.h (+5/-1)
qt/tests/qmltests/api/tst_WebContext_autofillEnabled.qml (+38/-0)
qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_autofill_profiles.html (+23/-0)
qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_autofill_profiles.qml (+300/-0)
qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_datalist.html (+37/-0)
qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_datalist.qml (+157/-0)
qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_passwords.html (+11/-0)
qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_passwords.qml (+105/-0)
qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_single_fields.html (+11/-0)
qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_single_fields.qml (+359/-0)
qt/uitk/lib/CMakeLists.txt (+2/-1)
qt/uitk/lib/resources.qrc (+1/-0)
qt/uitk/lib/resources/WebAutofillPopup.qml (+139/-0)
qt/uitk/lib/uitk_auxiliary_ui_factory.cc (+12/-1)
qt/uitk/lib/uitk_auxiliary_ui_factory.h (+5/-1)
qt/uitk/lib/uitk_web_autofill_popup.cc (+297/-0)
qt/uitk/lib/uitk_web_autofill_popup.h (+87/-0)
shared/BUILD.gn (+30/-1)
shared/browser/autofill/autofill_client.cc (+272/-0)
shared/browser/autofill/autofill_client.h (+116/-0)
shared/browser/autofill/autofill_popup_controller.cc (+163/-0)
shared/browser/autofill/autofill_popup_controller.h (+92/-0)
shared/browser/autofill/web_autofill_popup.h (+42/-0)
shared/browser/autofill/web_autofill_popup_client.h (+41/-0)
shared/browser/filtered_pref_store.cc (+181/-0)
shared/browser/filtered_pref_store.h (+107/-0)
shared/browser/filtered_pref_store_unittest.cc (+209/-0)
shared/browser/oxide_browser_context.cc (+100/-4)
shared/browser/oxide_browser_context.h (+26/-2)
shared/browser/oxide_browser_platform_integration.cc (+6/-1)
shared/browser/oxide_browser_platform_integration.h (+4/-1)
shared/browser/oxide_content_browser_client.cc (+41/-0)
shared/browser/oxide_content_browser_client.h (+6/-2)
shared/browser/oxide_user_agent_settings.cc (+45/-1)
shared/browser/oxide_user_agent_settings.h (+10/-1)
shared/browser/oxide_web_view.cc (+10/-2)
shared/browser/personal_data_manager_factory.cc (+72/-0)
shared/browser/personal_data_manager_factory.h (+60/-0)
shared/browser/resources/browser_manifest_overlay.json (+15/-0)
shared/browser/resources/renderer_manifest_overlay.json (+14/-0)
shared/browser/web_contents_client.cc (+9/-1)
shared/browser/web_contents_client.h (+13/-1)
shared/browser/web_data_service_factory.cc (+100/-0)
shared/browser/web_data_service_factory.h (+63/-0)
shared/browser/web_data_service_wrapper.cc (+114/-0)
shared/browser/web_data_service_wrapper.h (+83/-0)
shared/oxide_resources.grd (+2/-0)
shared/renderer/oxide_content_renderer_client.cc (+12/-1)
Reviewer Review Type Date Requested Status
Chris Coulson Needs Fixing
Review via email: mp+318166@code.launchpad.net

Commit message

Add support for autofill popups in UbuntuWebView (LP: #1214048).

This initial implementation supports single text fields, datalist entries and autofill profiles.
Credit cards and passwords are not supported.
There is no API to manage stored profiles, this will be implemented as a followup feature.

To post a comment you must log in.
~osomon/oxide:autofill updated
8f3dace... by Olivier Tilloy

Fix a unit test when run in single process mode.

acc726e... by Olivier Tilloy

Reduce the height of the autofill entries.

2bf8c7c... by Olivier Tilloy

Preview autofill entry under mouse cursor.

fca284a... by Olivier Tilloy

Highlight currently selected suggestion.

32f4f1e... by Olivier Tilloy

Simplify focus handling.

0fd49b9... by Olivier Tilloy

Fix FTBFS.

305a4b1... by Olivier Tilloy

Handle validation when no suggestion is selected.

c6f3be8... by Olivier Tilloy

Add a unit test for autofill popup destruction.

ff81c57... by Olivier Tilloy

Ensure unit tests are locale-independent.

Revision history for this message
Chris Coulson (chrisccoulson) wrote :

I haven't had time to complete the review of this yet, so I've left you a few comments in the meantime to unblock you :)

review: Needs Fixing
Revision history for this message
Chris Coulson (chrisccoulson) wrote :

I've left a few more comments now. I've not yet finished reviewing it though, so there will most likely be some more later.

review: Needs Fixing
~osomon/oxide:autofill updated
f7170cf... by Olivier Tilloy

Fix build errors when rebased on latest master.

12ec53f... by Olivier Tilloy

Always use an InMemoryPrefStore.

Persisting the value of OxideQQuickWebContext::autofillEnabled would be
inconsistent with every other setting exposed by the API
(eg, we don't persist popupBlockerEnabled or doNotTrackEnabled).

2c47ca7... by Olivier Tilloy

Move CreateAutofillPopup to WebContentsClient.

bd0082a... by Olivier Tilloy

Bump version for new WebContext API to 1.23.

b7f468f... by Olivier Tilloy

Drop default case as there's already a branch for every value.

579a028... by Olivier Tilloy

Do not modify oxide::ContentClient to access the application locale.

Instead, expose the application locale as a new method on BrowserPlatformIntegration.

e1aa089... by Olivier Tilloy

Marked a private helper private.

f0aa5a2... by Olivier Tilloy

Ensure that autofill works in incognito mode, as expected.

Revision history for this message
Olivier Tilloy (osomon) wrote :

I addressed the first round of comments from two days ago. Still pending: add tests for autofill behaviour in incognito mode.

~osomon/oxide:autofill updated
1d7be1b... by Olivier Tilloy

Add tests for autofill in incognito webviews.

e48baae... by Olivier Tilloy

Make WebAutofillPopupHost::Init not return anything, for consistency.

4017f9d... by Olivier Tilloy

Remove redundant std::move().

4116cd7... by Olivier Tilloy

Rename methods to start with an uppercase letter, for consistency with newly-written code.

31d4425... by Olivier Tilloy

Take timeout into account.

dc3f850... by Olivier Tilloy

Do not depend on the omnibox component in release builds.

f4c8cba... by Olivier Tilloy

Rename files for consistency with newly-added code.

4fd1d34... by Olivier Tilloy

Remove unused references to gfx::NativeView (always null) in AutofillPopupController.

88dfc51... by Olivier Tilloy

Remove unused method.

31c9a11... by Olivier Tilloy

Make ownership of AutofillPopupController explicit.

Revision history for this message
Olivier Tilloy (osomon) wrote :

I addressed all comments (and answered questions) in the second review round except for the last one (the suggestion to re-implement a custom WebDataServiceWrapper), which is pending.

~osomon/oxide:autofill updated
8dcafd5... by Olivier Tilloy

Fork WebDataServiceWrapper to remove bits that oxide doesn't need.

Revision history for this message
Olivier Tilloy (osomon) wrote :

WebDataServiceWrapper has been forked to remove all the bits that oxide doesn't need, thus removing the need for the dependency on omnibox in a component build.

~osomon/oxide:autofill updated
bc34ffb... by Olivier Tilloy

New FilteredPrefStore class that stores all preferences in memory except for a selected set which are persisted on disk.

133ba6a... by Olivier Tilloy

Unit tests for FilteredPrefStore.

1ac8924... by Olivier Tilloy

Remove stale line that remained after rebasing on master.

Unmerged commits

1ac8924... by Olivier Tilloy

Remove stale line that remained after rebasing on master.

133ba6a... by Olivier Tilloy

Unit tests for FilteredPrefStore.

bc34ffb... by Olivier Tilloy

New FilteredPrefStore class that stores all preferences in memory except for a selected set which are persisted on disk.

8dcafd5... by Olivier Tilloy

Fork WebDataServiceWrapper to remove bits that oxide doesn't need.

31c9a11... by Olivier Tilloy

Make ownership of AutofillPopupController explicit.

88dfc51... by Olivier Tilloy

Remove unused method.

4fd1d34... by Olivier Tilloy

Remove unused references to gfx::NativeView (always null) in AutofillPopupController.

f4c8cba... by Olivier Tilloy

Rename files for consistency with newly-added code.

dc3f850... by Olivier Tilloy

Do not depend on the omnibox component in release builds.

31d4425... by Olivier Tilloy

Take timeout into account.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/qt/core/BUILD.gn b/qt/core/BUILD.gn
2index 4a12427..6dd0eb2 100644
3--- a/qt/core/BUILD.gn
4+++ b/qt/core/BUILD.gn
5@@ -92,6 +92,7 @@ source_set("core_sources") {
6 ":core_moc_gen",
7 ":oxide_version",
8 "//base",
9+ "//components/autofill/core/browser",
10 "//components/sessions",
11 "//content/public/browser",
12 "//content/public/common",
13@@ -269,6 +270,8 @@ source_set("core_sources") {
14 "browser/touch_selection/touch_editing_menu_host.h",
15 "browser/touch_selection/touch_handle_drawable_host.cc",
16 "browser/touch_selection/touch_handle_drawable_host.h",
17+ "browser/web_autofill_popup_host.cc",
18+ "browser/web_autofill_popup_host.h",
19 "browser/web_contents_id_tracker.cc",
20 "browser/web_contents_id_tracker.h",
21 "browser/web_context_menu_host.cc",
22@@ -278,6 +281,8 @@ source_set("core_sources") {
23 "browser/web_preferences.cc",
24 "browser/web_preferences.h",
25 "common/oxide_qt_export.h",
26+ "glue/autofill_suggestion.cc",
27+ "glue/autofill_suggestion.h",
28 "glue/chrome_controller.cc",
29 "glue/chrome_controller.h",
30 "glue/chrome_controller_client.h",
31@@ -325,6 +330,8 @@ source_set("core_sources") {
32 "glue/screen_utils.h",
33 "glue/touch_editing_menu.h",
34 "glue/touch_editing_menu_client.h",
35+ "glue/web_autofill_popup.h",
36+ "glue/web_autofill_popup_client.h",
37 "glue/web_context_menu.h",
38 "glue/web_context_menu_actions.h",
39 "glue/web_context_menu_client.h",
40diff --git a/qt/core/browser/contents_view_impl.cc b/qt/core/browser/contents_view_impl.cc
41index e683c42..b648fc6 100644
42--- a/qt/core/browser/contents_view_impl.cc
43+++ b/qt/core/browser/contents_view_impl.cc
44@@ -1,5 +1,5 @@
45 // vim:expandtab:shiftwidth=2:tabstop=2:
46-// Copyright (C) 2016 Canonical Ltd.
47+// Copyright (C) 2016-2017 Canonical Ltd.
48
49 // This library is free software; you can redistribute it and/or
50 // modify it under the terms of the GNU Lesser General Public
51diff --git a/qt/core/browser/oxide_qt_browser_platform_integration.cc b/qt/core/browser/oxide_qt_browser_platform_integration.cc
52index 8c6455e..ee2226c 100644
53--- a/qt/core/browser/oxide_qt_browser_platform_integration.cc
54+++ b/qt/core/browser/oxide_qt_browser_platform_integration.cc
55@@ -1,5 +1,5 @@
56 // vim:expandtab:shiftwidth=2:tabstop=2:
57-// Copyright (C) 2014-2016 Canonical Ltd.
58+// Copyright (C) 2014-2017 Canonical Ltd.
59
60 // This library is free software; you can redistribute it and/or
61 // modify it under the terms of the GNU Lesser General Public
62@@ -20,6 +20,7 @@
63 #include <QDesktopServices>
64 #include <QEvent>
65 #include <QGuiApplication>
66+#include <QLocale>
67 #include <QStyleHints>
68 #include <QPointer>
69 #include <QString>
70@@ -183,6 +184,10 @@ std::string BrowserPlatformIntegration::GetApplicationName() {
71 return application_name_;
72 }
73
74+std::string BrowserPlatformIntegration::GetApplicationLocale() {
75+ return QLocale::system().name().toStdString();
76+}
77+
78 std::unique_ptr<oxide::DragSource> BrowserPlatformIntegration::CreateDragSource(
79 oxide::DragSourceClient* client) {
80 return base::WrapUnique(new DragSource(client));
81diff --git a/qt/core/browser/oxide_qt_browser_platform_integration.h b/qt/core/browser/oxide_qt_browser_platform_integration.h
82index 0f7eeb1..f676a1d 100644
83--- a/qt/core/browser/oxide_qt_browser_platform_integration.h
84+++ b/qt/core/browser/oxide_qt_browser_platform_integration.h
85@@ -1,5 +1,5 @@
86 // vim:expandtab:shiftwidth=2:tabstop=2:
87-// Copyright (C) 2014-2015 Canonical Ltd.
88+// Copyright (C) 2014-2017 Canonical Ltd.
89
90 // This library is free software; you can redistribute it and/or
91 // modify it under the terms of the GNU Lesser General Public
92@@ -63,6 +63,7 @@ class BrowserPlatformIntegration : public QObject,
93 ApplicationState GetApplicationState() override;
94 virtual int GetClickInterval() override;
95 std::string GetApplicationName() override;
96+ std::string GetApplicationLocale() override;
97 std::unique_ptr<oxide::DragSource> CreateDragSource(
98 oxide::DragSourceClient* client) override;
99 void CreateVibrationManager(
100diff --git a/qt/core/browser/oxide_qt_web_context.cc b/qt/core/browser/oxide_qt_web_context.cc
101index 504d165..fc1492f 100644
102--- a/qt/core/browser/oxide_qt_web_context.cc
103+++ b/qt/core/browser/oxide_qt_web_context.cc
104@@ -1,5 +1,5 @@
105 // vim:expandtab:shiftwidth=2:tabstop=2:
106-// Copyright (C) 2013-2016 Canonical Ltd.
107+// Copyright (C) 2013-2017 Canonical Ltd.
108
109 // This library is free software; you can redistribute it and/or
110 // modify it under the terms of the GNU Lesser General Public
111@@ -138,7 +138,8 @@ struct WebContext::ConstructProperties {
112 devtools_enabled(false),
113 devtools_port(-1),
114 legacy_user_agent_override_enabled(false),
115- do_not_track(false) {}
116+ do_not_track(false),
117+ autofill_enabled(false) {}
118
119 std::string product;
120 std::string user_agent;
121@@ -158,6 +159,7 @@ struct WebContext::ConstructProperties {
122 std::vector<UserAgentSettings::UserAgentOverride> user_agent_overrides;
123 bool legacy_user_agent_override_enabled;
124 bool do_not_track;
125+ bool autofill_enabled;
126 };
127
128 class SetCookiesContext : public base::RefCounted<SetCookiesContext> {
129@@ -499,6 +501,7 @@ BrowserContext* WebContext::GetContext() {
130 ua_settings->SetIsPopupBlockerEnabled(
131 construct_props_->popup_blocker_enabled);
132 ua_settings->SetDoNotTrack(construct_props_->do_not_track);
133+ ua_settings->SetAutofillEnabled(construct_props_->autofill_enabled);
134
135 context_->SetCookiePolicy(construct_props_->cookie_policy);
136
137@@ -999,7 +1002,7 @@ void WebContext::DefaultVideoDeviceChanged() {
138 }
139
140 bool WebContext::doNotTrack() const {
141- if (IsInitialized()) {
142+ if (IsInitialized()) {
143 return UserAgentSettings::Get(context_.get())->GetDoNotTrack();
144 }
145
146@@ -1014,5 +1017,21 @@ void WebContext::setDoNotTrack(bool dnt) {
147 }
148 }
149
150+bool WebContext::autofillEnabled() const {
151+ if (IsInitialized()) {
152+ return UserAgentSettings::Get(context_.get())->IsAutofillEnabled();
153+ }
154+
155+ return construct_props_->autofill_enabled;
156+}
157+
158+void WebContext::setAutofillEnabled(bool enabled) {
159+ if (IsInitialized()) {
160+ UserAgentSettings::Get(context_.get())->SetAutofillEnabled(enabled);
161+ } else {
162+ construct_props_->autofill_enabled = enabled;
163+ }
164+}
165+
166 } // namespace qt
167 } // namespace oxide
168diff --git a/qt/core/browser/oxide_qt_web_context.h b/qt/core/browser/oxide_qt_web_context.h
169index 1425b38..1dd4134 100644
170--- a/qt/core/browser/oxide_qt_web_context.h
171+++ b/qt/core/browser/oxide_qt_web_context.h
172@@ -1,5 +1,5 @@
173 // vim:expandtab:shiftwidth=2:tabstop=2:
174-// Copyright (C) 2013-2016 Canonical Ltd.
175+// Copyright (C) 2013-2017 Canonical Ltd.
176
177 // This library is free software; you can redistribute it and/or
178 // modify it under the terms of the GNU Lesser General Public
179@@ -144,6 +144,9 @@ class WebContext : public WebContextProxy,
180 bool doNotTrack() const override;
181 void setDoNotTrack(bool dnt) override;
182
183+ bool autofillEnabled() const override;
184+ void setAutofillEnabled(bool enabled) override;
185+
186 // oxide::MediaCaptureDevicesContextClient implementation
187 void DefaultAudioDeviceChanged() override;
188 void DefaultVideoDeviceChanged() override;
189diff --git a/qt/core/browser/oxide_qt_web_view.cc b/qt/core/browser/oxide_qt_web_view.cc
190index ab4d2d2..dd97d3d 100644
191--- a/qt/core/browser/oxide_qt_web_view.cc
192+++ b/qt/core/browser/oxide_qt_web_view.cc
193@@ -1,5 +1,5 @@
194 // vim:expandtab:shiftwidth=2:tabstop=2:
195-// Copyright (C) 2013-2016 Canonical Ltd.
196+// Copyright (C) 2013-2017 Canonical Ltd.
197
198 // This library is free software; you can redistribute it and/or
199 // modify it under the terms of the GNU Lesser General Public
200@@ -25,6 +25,7 @@
201
202 #include <QGuiApplication>
203 #include <QInputEvent>
204+#include <QRectF>
205 #include <QScreen>
206 #include <QString>
207 #include <QtDebug>
208@@ -75,6 +76,7 @@
209 #include "qt/core/glue/oxide_qt_web_frame_proxy_client.h"
210 #include "qt/core/glue/oxide_qt_web_view_proxy_client.h"
211 #include "qt/core/glue/touch_editing_menu.h"
212+#include "qt/core/glue/web_autofill_popup.h"
213 #include "qt/core/glue/web_context_menu.h"
214 #include "qt/core/glue/web_context_menu_params.h"
215 #include "shared/browser/oxide_browser_process_main.h"
216@@ -103,6 +105,7 @@
217 #include "oxide_qt_type_conversions.h"
218 #include "oxide_qt_web_context.h"
219 #include "oxide_qt_web_frame.h"
220+#include "web_autofill_popup_host.h"
221 #include "web_contents_id_tracker.h"
222 #include "web_context_menu_host.h"
223 #include "web_preferences.h"
224@@ -693,6 +696,28 @@ std::unique_ptr<oxide::WebContextMenu> WebView::CreateContextMenu(
225 return std::move(host);
226 }
227
228+std::unique_ptr<oxide::WebAutofillPopup> WebView::CreateAutofillPopup(
229+ const std::vector<autofill::Suggestion>& suggestions,
230+ const gfx::RectF& bounds,
231+ oxide::WebAutofillPopupClient* client) {
232+ std::unique_ptr<WebAutofillPopupHost> host =
233+ base::MakeUnique<WebAutofillPopupHost>(client);
234+ QRectF qt_bounds = ToQt(
235+ DpiUtils::ConvertChromiumPixelsToQt(bounds, contents_view_->GetScreen()));
236+ std::unique_ptr<WebAutofillPopup> popup =
237+ aux_ui_factory_->CreateWebAutofillPopup(
238+ WebAutofillPopupHost::BuildSuggestions(suggestions),
239+ qt_bounds,
240+ host.get());
241+ if (!popup) {
242+ return nullptr;
243+ }
244+
245+ host->Init(std::move(popup));
246+
247+ return std::move(host);
248+}
249+
250 std::unique_ptr<oxide::TouchEditingMenuController>
251 WebView::CreateOverrideTouchEditingMenuController(
252 oxide::TouchEditingMenuControllerClient* client) {
253diff --git a/qt/core/browser/oxide_qt_web_view.h b/qt/core/browser/oxide_qt_web_view.h
254index 5da67af..f0d3e50 100644
255--- a/qt/core/browser/oxide_qt_web_view.h
256+++ b/qt/core/browser/oxide_qt_web_view.h
257@@ -1,5 +1,5 @@
258 // vim:expandtab:shiftwidth=2:tabstop=2:
259-// Copyright (C) 2013-2016 Canonical Ltd.
260+// Copyright (C) 2013-2017 Canonical Ltd.
261
262 // This library is free software; you can redistribute it and/or
263 // modify it under the terms of the GNU Lesser General Public
264@@ -154,6 +154,10 @@ class WebView : public oxide::WebViewClient,
265 const content::ContextMenuParams& params,
266 const std::vector<content::MenuItem>& items,
267 oxide::WebContextMenuClient* client) override;
268+ std::unique_ptr<oxide::WebAutofillPopup> CreateAutofillPopup(
269+ const std::vector<autofill::Suggestion>& suggestions,
270+ const gfx::RectF& bounds,
271+ oxide::WebAutofillPopupClient* client) override;
272 std::unique_ptr<oxide::TouchEditingMenuController>
273 CreateOverrideTouchEditingMenuController(
274 oxide::TouchEditingMenuControllerClient* client) override;
275diff --git a/qt/core/browser/web_autofill_popup_host.cc b/qt/core/browser/web_autofill_popup_host.cc
276new file mode 100644
277index 0000000..05fe479
278--- /dev/null
279+++ b/qt/core/browser/web_autofill_popup_host.cc
280@@ -0,0 +1,139 @@
281+// vim:expandtab:shiftwidth=2:tabstop=2:
282+// Copyright (C) 2017 Canonical Ltd.
283+
284+// This library is free software; you can redistribute it and/or
285+// modify it under the terms of the GNU Lesser General Public
286+// License as published by the Free Software Foundation; either
287+// version 2.1 of the License, or (at your option) any later version.
288+
289+// This library is distributed in the hope that it will be useful,
290+// but WITHOUT ANY WARRANTY; without even the implied warranty of
291+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
292+// Lesser General Public License for more details.
293+
294+// You should have received a copy of the GNU Lesser General Public
295+// License along with this library; if not, write to the Free Software
296+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
297+
298+#include "web_autofill_popup_host.h"
299+
300+#include "base/logging.h"
301+#include "base/strings/utf_string_conversions.h"
302+#include "components/autofill/core/browser/popup_item_ids.h"
303+#include "components/autofill/core/browser/suggestion.h"
304+
305+#include "qt/core/glue/web_autofill_popup.h"
306+#include "shared/browser/autofill/web_autofill_popup_client.h"
307+
308+namespace oxide {
309+namespace qt {
310+
311+void WebAutofillPopupHost::Show() {
312+ popup_->Show();
313+}
314+
315+void WebAutofillPopupHost::UpdateDataListValues(
316+ const std::vector<base::string16>& values,
317+ const std::vector<base::string16>& labels) {
318+ std::vector<AutofillSuggestion> suggestions;
319+ for (unsigned int i = 0; i < values.size(); ++i) {
320+ AutofillSuggestion suggestion;
321+ suggestion.frontend_id = autofill::POPUP_ITEM_ID_DATALIST_ENTRY;
322+ suggestion.type = AutofillSuggestion::Type::DataListEntry;
323+ suggestion.value = QString::fromStdString(base::UTF16ToUTF8(values[i]));
324+ suggestion.label = QString::fromStdString(base::UTF16ToUTF8(labels[i]));
325+ suggestions.push_back(suggestion);
326+ }
327+
328+ if (suggestions.size() >
329+ static_cast<size_t>(std::numeric_limits<int>::max() + 1)) {
330+ LOG(WARNING)
331+ << "Truncating autofill popup - there are too many suggestions!";
332+ suggestions.resize(std::numeric_limits<int>::max() + 1);
333+ }
334+
335+ popup_->UpdateDataListValues(suggestions);
336+}
337+
338+void WebAutofillPopupHost::Hide() {
339+ popup_->Hide();
340+}
341+
342+void WebAutofillPopupHost::Close() {
343+ client_->Close();
344+}
345+
346+void WebAutofillPopupHost::SelectSuggestion(
347+ const AutofillSuggestion& suggestion) {
348+ client_->SelectSuggestion(suggestion.frontend_id,
349+ base::UTF8ToUTF16(suggestion.value.toStdString()));
350+}
351+
352+void WebAutofillPopupHost::ClearSelection() {
353+ client_->ClearSelection();
354+}
355+
356+void WebAutofillPopupHost::AcceptSuggestion(
357+ const AutofillSuggestion& suggestion) {
358+ client_->AcceptSuggestion(suggestion.frontend_id,
359+ base::UTF8ToUTF16(suggestion.value.toStdString()));
360+}
361+
362+bool WebAutofillPopupHost::RemoveSuggestion(
363+ const AutofillSuggestion& suggestion) {
364+ return client_->RemoveSuggestion(
365+ suggestion.frontend_id,
366+ base::UTF8ToUTF16(suggestion.value.toStdString()));
367+}
368+
369+WebAutofillPopupHost::WebAutofillPopupHost(
370+ oxide::WebAutofillPopupClient* client)
371+ : client_(client) {}
372+
373+WebAutofillPopupHost::~WebAutofillPopupHost() = default;
374+
375+void WebAutofillPopupHost::Init(std::unique_ptr<qt::WebAutofillPopup> popup) {
376+ popup_ = std::move(popup);
377+}
378+
379+// static
380+std::vector<AutofillSuggestion> WebAutofillPopupHost::BuildSuggestions(
381+ const std::vector<autofill::Suggestion>& suggestions) {
382+ std::vector<autofill::Suggestion> local_suggestions = suggestions;
383+ if (local_suggestions.size() >
384+ static_cast<size_t>(std::numeric_limits<int>::max() + 1)) {
385+ LOG(WARNING)
386+ << "Truncating autofill popup - there are too many suggestions!";
387+ local_suggestions.resize(std::numeric_limits<int>::max() + 1);
388+ }
389+
390+ std::vector<AutofillSuggestion> rv;
391+ for (const auto& suggestion : local_suggestions) {
392+ int fid = suggestion.frontend_id;
393+ if (fid > 0 ||
394+ fid == autofill::POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY ||
395+ fid == autofill::POPUP_ITEM_ID_PASSWORD_ENTRY ||
396+ fid == autofill::POPUP_ITEM_ID_DATALIST_ENTRY) {
397+ AutofillSuggestion as;
398+ as.frontend_id = fid;
399+ if (fid > 0) {
400+ as.type = AutofillSuggestion::Type::ProfileEntry;
401+ } else if (fid == autofill::POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY) {
402+ as.type = AutofillSuggestion::Type::AutoCompleteEntry;
403+ } else if (fid == autofill::POPUP_ITEM_ID_PASSWORD_ENTRY) {
404+ as.type = AutofillSuggestion::Type::PasswordEntry;
405+ } else if (fid == autofill::POPUP_ITEM_ID_DATALIST_ENTRY) {
406+ as.type = AutofillSuggestion::Type::DataListEntry;
407+ }
408+ as.value = QString::fromStdString(base::UTF16ToUTF8(suggestion.value));
409+ as.label = QString::fromStdString(base::UTF16ToUTF8(suggestion.label));
410+ as.icon = QString::fromStdString(base::UTF16ToUTF8(suggestion.icon));
411+ rv.push_back(as);
412+ }
413+ }
414+
415+ return std::move(rv);
416+}
417+
418+} // namespace qt
419+} // namespace oxide
420diff --git a/qt/core/browser/web_autofill_popup_host.h b/qt/core/browser/web_autofill_popup_host.h
421new file mode 100644
422index 0000000..513e778
423--- /dev/null
424+++ b/qt/core/browser/web_autofill_popup_host.h
425@@ -0,0 +1,78 @@
426+// vim:expandtab:shiftwidth=2:tabstop=2:
427+// Copyright (C) 2017 Canonical Ltd.
428+
429+// This library is free software; you can redistribute it and/or
430+// modify it under the terms of the GNU Lesser General Public
431+// License as published by the Free Software Foundation; either
432+// version 2.1 of the License, or (at your option) any later version.
433+
434+// This library is distributed in the hope that it will be useful,
435+// but WITHOUT ANY WARRANTY; without even the implied warranty of
436+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
437+// Lesser General Public License for more details.
438+
439+// You should have received a copy of the GNU Lesser General Public
440+// License along with this library; if not, write to the Free Software
441+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
442+
443+#ifndef _OXIDE_QT_CORE_BROWSER_WEB_AUTOFILL_POPUP_HOST_H_
444+#define _OXIDE_QT_CORE_BROWSER_WEB_AUTOFILL_POPUP_HOST_H_
445+
446+#include <memory>
447+#include <vector>
448+
449+#include "base/macros.h"
450+
451+#include "qt/core/glue/autofill_suggestion.h"
452+#include "qt/core/glue/web_autofill_popup_client.h"
453+#include "shared/browser/autofill/web_autofill_popup.h"
454+
455+namespace autofill {
456+struct Suggestion;
457+}
458+
459+namespace oxide {
460+
461+class WebAutofillPopupClient;
462+
463+namespace qt {
464+
465+class WebAutofillPopup;
466+
467+class WebAutofillPopupHost : public oxide::WebAutofillPopup,
468+ public WebAutofillPopupClient {
469+ public:
470+ WebAutofillPopupHost(oxide::WebAutofillPopupClient* client);
471+ ~WebAutofillPopupHost() override;
472+
473+ void Init(std::unique_ptr<qt::WebAutofillPopup> popup);
474+
475+ static std::vector<AutofillSuggestion> BuildSuggestions(
476+ const std::vector<autofill::Suggestion>& suggestions);
477+
478+ private:
479+ // oxide::WebAutofillPopup implementation
480+ void Show() override;
481+ void UpdateDataListValues(
482+ const std::vector<base::string16>& values,
483+ const std::vector<base::string16>& labels) override;
484+ void Hide() override;
485+
486+ // WebAutofillPopupClient implementation
487+ void Close() override;
488+ void SelectSuggestion(const AutofillSuggestion& suggestion) override;
489+ void ClearSelection() override;
490+ void AcceptSuggestion(const AutofillSuggestion& suggestion) override;
491+ bool RemoveSuggestion(const AutofillSuggestion& suggestion) override;
492+
493+ oxide::WebAutofillPopupClient* client_; // Owns us
494+
495+ std::unique_ptr<qt::WebAutofillPopup> popup_;
496+
497+ DISALLOW_COPY_AND_ASSIGN(WebAutofillPopupHost);
498+};
499+
500+} // namespace qt
501+} // namespace oxide
502+
503+#endif // _OXIDE_QT_CORE_BROWSER_WEB_AUTOFILL_POPUP_HOST_H_
504diff --git a/qt/core/glue/autofill_suggestion.cc b/qt/core/glue/autofill_suggestion.cc
505new file mode 100644
506index 0000000..5344a3d
507--- /dev/null
508+++ b/qt/core/glue/autofill_suggestion.cc
509@@ -0,0 +1,30 @@
510+// vim:expandtab:shiftwidth=2:tabstop=2:
511+// Copyright (C) 2017 Canonical Ltd.
512+
513+// This library is free software; you can redistribute it and/or
514+// modify it under the terms of the GNU Lesser General Public
515+// License as published by the Free Software Foundation; either
516+// version 2.1 of the License, or (at your option) any later version.
517+
518+// This library is distributed in the hope that it will be useful,
519+// but WITHOUT ANY WARRANTY; without even the implied warranty of
520+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
521+// Lesser General Public License for more details.
522+
523+// You should have received a copy of the GNU Lesser General Public
524+// License along with this library; if not, write to the Free Software
525+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
526+
527+#include "autofill_suggestion.h"
528+
529+namespace oxide {
530+namespace qt {
531+
532+AutofillSuggestion::AutofillSuggestion() = default;
533+
534+AutofillSuggestion::AutofillSuggestion(const AutofillSuggestion& other) = default;
535+
536+AutofillSuggestion::~AutofillSuggestion() = default;
537+
538+} // namespace qt
539+} // namespace oxide
540diff --git a/qt/core/glue/autofill_suggestion.h b/qt/core/glue/autofill_suggestion.h
541new file mode 100644
542index 0000000..01e4ad2
543--- /dev/null
544+++ b/qt/core/glue/autofill_suggestion.h
545@@ -0,0 +1,50 @@
546+// vim:expandtab:shiftwidth=2:tabstop=2:
547+// Copyright (C) 2017 Canonical Ltd.
548+
549+// This library is free software; you can redistribute it and/or
550+// modify it under the terms of the GNU Lesser General Public
551+// License as published by the Free Software Foundation; either
552+// version 2.1 of the License, or (at your option) any later version.
553+
554+// This library is distributed in the hope that it will be useful,
555+// but WITHOUT ANY WARRANTY; without even the implied warranty of
556+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
557+// Lesser General Public License for more details.
558+
559+// You should have received a copy of the GNU Lesser General Public
560+// License along with this library; if not, write to the Free Software
561+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
562+
563+#ifndef _OXIDE_QT_CORE_GLUE_AUTOFILL_SUGGESTION_H_
564+#define _OXIDE_QT_CORE_GLUE_AUTOFILL_SUGGESTION_H_
565+
566+#include <QString>
567+
568+#include "qt/core/api/oxideqglobal.h"
569+
570+namespace oxide {
571+namespace qt {
572+
573+struct OXIDE_QTCORE_EXPORT AutofillSuggestion {
574+ AutofillSuggestion();
575+ AutofillSuggestion(const AutofillSuggestion& other);
576+ ~AutofillSuggestion();
577+
578+ enum class Type {
579+ ProfileEntry,
580+ AutoCompleteEntry,
581+ PasswordEntry,
582+ DataListEntry
583+ };
584+
585+ int frontend_id;
586+ Type type;
587+ QString value;
588+ QString label;
589+ QString icon;
590+};
591+
592+} // namespace qt
593+} // namespace oxide
594+
595+#endif // _OXIDE_QT_CORE_GLUE_AUTOFILL_SUGGESTION_H_
596diff --git a/qt/core/glue/auxiliary_ui_factory.h b/qt/core/glue/auxiliary_ui_factory.h
597index 4150b1b..557cbcf 100644
598--- a/qt/core/glue/auxiliary_ui_factory.h
599+++ b/qt/core/glue/auxiliary_ui_factory.h
600@@ -1,5 +1,5 @@
601 // vim:expandtab:shiftwidth=2:tabstop=2:
602-// Copyright (C) 2016 Canonical Ltd.
603+// Copyright (C) 2016-2017 Canonical Ltd.
604
605 // This library is free software; you can redistribute it and/or
606 // modify it under the terms of the GNU Lesser General Public
607@@ -23,10 +23,12 @@
608
609 #include <QtGlobal>
610
611+#include "qt/core/glue/autofill_suggestion.h"
612 #include "qt/core/glue/edit_capability_flags.h"
613 #include "qt/core/glue/menu_item.h"
614
615 QT_BEGIN_NAMESPACE
616+class QRectF;
617 class QString;
618 class QUrl;
619 QT_END_NAMESPACE
620@@ -39,6 +41,8 @@ class JavaScriptDialogClient;
621 enum class JavaScriptDialogType;
622 class TouchEditingMenu;
623 class TouchEditingMenuClient;
624+class WebAutofillPopup;
625+class WebAutofillPopupClient;
626 class WebContextMenu;
627 class WebContextMenuClient;
628 struct WebContextMenuParams;
629@@ -52,6 +56,11 @@ class AuxiliaryUIFactory {
630 const std::vector<MenuItem>& items,
631 WebContextMenuClient* client) = 0;
632
633+ virtual std::unique_ptr<WebAutofillPopup> CreateWebAutofillPopup(
634+ const std::vector<AutofillSuggestion>& suggestions,
635+ const QRectF& bounds,
636+ WebAutofillPopupClient* client) = 0;
637+
638 virtual std::unique_ptr<TouchEditingMenu> CreateTouchEditingMenu(
639 EditCapabilityFlags edit_flags,
640 TouchEditingMenuClient* client) = 0;
641diff --git a/qt/core/glue/oxide_qt_web_context_proxy.h b/qt/core/glue/oxide_qt_web_context_proxy.h
642index ff13a71..49f0180 100644
643--- a/qt/core/glue/oxide_qt_web_context_proxy.h
644+++ b/qt/core/glue/oxide_qt_web_context_proxy.h
645@@ -1,5 +1,5 @@
646 // vim:expandtab:shiftwidth=2:tabstop=2:
647-// Copyright (C) 2013-2016 Canonical Ltd.
648+// Copyright (C) 2013-2017 Canonical Ltd.
649
650 // This library is free software; you can redistribute it and/or
651 // modify it under the terms of the GNU Lesser General Public
652@@ -137,6 +137,9 @@ class OXIDE_QTCORE_EXPORT WebContextProxy : public ProxyBase<WebContext> {
653
654 virtual bool doNotTrack() const = 0;
655 virtual void setDoNotTrack(bool dnt) = 0;
656+
657+ virtual bool autofillEnabled() const = 0;
658+ virtual void setAutofillEnabled(bool enabled) = 0;
659 };
660
661 } // namespace qt
662diff --git a/qt/core/glue/web_autofill_popup.h b/qt/core/glue/web_autofill_popup.h
663new file mode 100644
664index 0000000..c28d561
665--- /dev/null
666+++ b/qt/core/glue/web_autofill_popup.h
667@@ -0,0 +1,43 @@
668+// vim:expandtab:shiftwidth=2:tabstop=2:
669+// Copyright (C) 2017 Canonical Ltd.
670+
671+// This library is free software; you can redistribute it and/or
672+// modify it under the terms of the GNU Lesser General Public
673+// License as published by the Free Software Foundation; either
674+// version 2.1 of the License, or (at your option) any later version.
675+
676+// This library is distributed in the hope that it will be useful,
677+// but WITHOUT ANY WARRANTY; without even the implied warranty of
678+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
679+// Lesser General Public License for more details.
680+
681+// You should have received a copy of the GNU Lesser General Public
682+// License along with this library; if not, write to the Free Software
683+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
684+
685+#ifndef _OXIDE_QT_CORE_GLUE_WEB_AUTOFILL_POPUP_H_
686+#define _OXIDE_QT_CORE_GLUE_WEB_AUTOFILL_POPUP_H_
687+
688+#include <vector>
689+
690+namespace oxide {
691+namespace qt {
692+
693+class AutofillSuggestion;
694+
695+class WebAutofillPopup {
696+ public:
697+ virtual ~WebAutofillPopup() = default;
698+
699+ virtual void Show() = 0;
700+
701+ virtual void UpdateDataListValues(
702+ const std::vector<AutofillSuggestion>& suggestions) = 0;
703+
704+ virtual void Hide() = 0;
705+};
706+
707+} // namespace qt
708+} // namespace oxide
709+
710+#endif // _OXIDE_QT_CORE_GLUE_WEB_AUTOFILL_POPUP_H_
711diff --git a/qt/core/glue/web_autofill_popup_client.h b/qt/core/glue/web_autofill_popup_client.h
712new file mode 100644
713index 0000000..b31cae4
714--- /dev/null
715+++ b/qt/core/glue/web_autofill_popup_client.h
716@@ -0,0 +1,40 @@
717+// vim:expandtab:shiftwidth=2:tabstop=2:
718+// Copyright (C) 2017 Canonical Ltd.
719+
720+// This library is free software; you can redistribute it and/or
721+// modify it under the terms of the GNU Lesser General Public
722+// License as published by the Free Software Foundation; either
723+// version 2.1 of the License, or (at your option) any later version.
724+
725+// This library is distributed in the hope that it will be useful,
726+// but WITHOUT ANY WARRANTY; without even the implied warranty of
727+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
728+// Lesser General Public License for more details.
729+
730+// You should have received a copy of the GNU Lesser General Public
731+// License along with this library; if not, write to the Free Software
732+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
733+
734+#ifndef _OXIDE_QT_CORE_GLUE_WEB_AUTOFILL_POPUP_CLIENT_H_
735+#define _OXIDE_QT_CORE_GLUE_WEB_AUTOFILL_POPUP_CLIENT_H_
736+
737+namespace oxide {
738+namespace qt {
739+
740+struct AutofillSuggestion;
741+
742+class WebAutofillPopupClient {
743+ public:
744+ virtual ~WebAutofillPopupClient() {}
745+
746+ virtual void Close() = 0;
747+ virtual void SelectSuggestion(const AutofillSuggestion& suggestion) = 0;
748+ virtual void ClearSelection() = 0;
749+ virtual void AcceptSuggestion(const AutofillSuggestion& suggestion) = 0;
750+ virtual bool RemoveSuggestion(const AutofillSuggestion& suggestion) = 0;
751+};
752+
753+} // namespace qt
754+} // namespace oxide
755+
756+#endif // _OXIDE_QT_CORE_GLUE_WEB_AUTOFILL_POPUP_CLIENT_H_
757diff --git a/qt/qmlplugin/oxide.qmltypes b/qt/qmlplugin/oxide.qmltypes
758index 8a111b6..ab9848f 100644
759--- a/qt/qmlplugin/oxide.qmltypes
760+++ b/qt/qmlplugin/oxide.qmltypes
761@@ -4,10 +4,10 @@ import QtQuick.tooling 1.2
762 // It is used for QML tooling purposes only.
763 //
764 // This file was auto-generated by:
765-// 'qmlplugindump -v -noinstantiate com.canonical.Oxide 1.19 com/canonical/Oxide'
766+// 'qmlplugindump -v -noinstantiate com.canonical.Oxide 1.23 com/canonical/Oxide'
767
768 Module {
769- dependencies: []
770+ dependencies: ["QtQuick 2.0"]
771 Component {
772 name: "OxideQCertificateError"
773 prototype: "QObject"
774@@ -414,11 +414,12 @@ Module {
775 prototype: "QObject"
776 exports: [
777 "WebContext 1.0",
778+ "WebContext 1.23",
779 "WebContext 1.3",
780 "WebContext 1.6",
781 "WebContext 1.9"
782 ]
783- exportMetaObjectRevisions: [0, 1, 2, 3]
784+ exportMetaObjectRevisions: [0, 1, 2, 3, 4]
785 Enum {
786 name: "CookiePolicy"
787 values: {
788@@ -475,6 +476,7 @@ Module {
789 Property { name: "defaultVideoCaptureDeviceId"; revision: 3; type: "string" }
790 Property { name: "userAgentOverrides"; revision: 3; type: "QVariantList" }
791 Property { name: "doNotTrackEnabled"; revision: 3; type: "bool" }
792+ Property { name: "autofillEnabled"; revision: 4; type: "bool" }
793 Signal { name: "devtoolsBindIpChanged" }
794 Signal { name: "hostMappingRulesChanged"; revision: 1 }
795 Signal { name: "allowedExtraUrlSchemesChanged"; revision: 1 }
796@@ -483,6 +485,7 @@ Module {
797 Signal { name: "defaultVideoCaptureDeviceIdChanged"; revision: 3 }
798 Signal { name: "userAgentOverridesChanged"; revision: 3 }
799 Signal { name: "doNotTrackEnabledChanged"; revision: 3 }
800+ Signal { name: "autofillEnabledChanged"; revision: 4 }
801 Method {
802 name: "addUserScript"
803 Parameter { name: "script"; type: "OxideQQuickUserScript"; isPointer: true }
804@@ -575,7 +578,7 @@ Module {
805 "WebView 1.8",
806 "WebView 1.9"
807 ]
808- exportMetaObjectRevisions: [0, 6, 7, 8, 9, 1, 2, 3, 4, 5]
809+ exportMetaObjectRevisions: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
810 attachedType: "OxideQQuickWebViewAttached"
811 Enum {
812 name: "LogMessageSeverityLevel"
813@@ -972,423 +975,4 @@ Module {
814 Property { name: "touchEnabled"; type: "bool" }
815 Signal { name: "sansSerifFontFamilyChanged" }
816 }
817- Component {
818- name: "QAbstractItemModel"
819- prototype: "QObject"
820- Enum {
821- name: "LayoutChangeHint"
822- values: {
823- "NoLayoutChangeHint": 0,
824- "VerticalSortHint": 1,
825- "HorizontalSortHint": 2
826- }
827- }
828- Signal {
829- name: "dataChanged"
830- Parameter { name: "topLeft"; type: "QModelIndex" }
831- Parameter { name: "bottomRight"; type: "QModelIndex" }
832- Parameter { name: "roles"; type: "QVector<int>" }
833- }
834- Signal {
835- name: "dataChanged"
836- Parameter { name: "topLeft"; type: "QModelIndex" }
837- Parameter { name: "bottomRight"; type: "QModelIndex" }
838- }
839- Signal {
840- name: "headerDataChanged"
841- Parameter { name: "orientation"; type: "Qt::Orientation" }
842- Parameter { name: "first"; type: "int" }
843- Parameter { name: "last"; type: "int" }
844- }
845- Signal {
846- name: "layoutChanged"
847- Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
848- Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" }
849- }
850- Signal {
851- name: "layoutChanged"
852- Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
853- }
854- Signal { name: "layoutChanged" }
855- Signal {
856- name: "layoutAboutToBeChanged"
857- Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
858- Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" }
859- }
860- Signal {
861- name: "layoutAboutToBeChanged"
862- Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
863- }
864- Signal { name: "layoutAboutToBeChanged" }
865- Signal {
866- name: "rowsAboutToBeInserted"
867- Parameter { name: "parent"; type: "QModelIndex" }
868- Parameter { name: "first"; type: "int" }
869- Parameter { name: "last"; type: "int" }
870- }
871- Signal {
872- name: "rowsInserted"
873- Parameter { name: "parent"; type: "QModelIndex" }
874- Parameter { name: "first"; type: "int" }
875- Parameter { name: "last"; type: "int" }
876- }
877- Signal {
878- name: "rowsAboutToBeRemoved"
879- Parameter { name: "parent"; type: "QModelIndex" }
880- Parameter { name: "first"; type: "int" }
881- Parameter { name: "last"; type: "int" }
882- }
883- Signal {
884- name: "rowsRemoved"
885- Parameter { name: "parent"; type: "QModelIndex" }
886- Parameter { name: "first"; type: "int" }
887- Parameter { name: "last"; type: "int" }
888- }
889- Signal {
890- name: "columnsAboutToBeInserted"
891- Parameter { name: "parent"; type: "QModelIndex" }
892- Parameter { name: "first"; type: "int" }
893- Parameter { name: "last"; type: "int" }
894- }
895- Signal {
896- name: "columnsInserted"
897- Parameter { name: "parent"; type: "QModelIndex" }
898- Parameter { name: "first"; type: "int" }
899- Parameter { name: "last"; type: "int" }
900- }
901- Signal {
902- name: "columnsAboutToBeRemoved"
903- Parameter { name: "parent"; type: "QModelIndex" }
904- Parameter { name: "first"; type: "int" }
905- Parameter { name: "last"; type: "int" }
906- }
907- Signal {
908- name: "columnsRemoved"
909- Parameter { name: "parent"; type: "QModelIndex" }
910- Parameter { name: "first"; type: "int" }
911- Parameter { name: "last"; type: "int" }
912- }
913- Signal { name: "modelAboutToBeReset" }
914- Signal { name: "modelReset" }
915- Signal {
916- name: "rowsAboutToBeMoved"
917- Parameter { name: "sourceParent"; type: "QModelIndex" }
918- Parameter { name: "sourceStart"; type: "int" }
919- Parameter { name: "sourceEnd"; type: "int" }
920- Parameter { name: "destinationParent"; type: "QModelIndex" }
921- Parameter { name: "destinationRow"; type: "int" }
922- }
923- Signal {
924- name: "rowsMoved"
925- Parameter { name: "parent"; type: "QModelIndex" }
926- Parameter { name: "start"; type: "int" }
927- Parameter { name: "end"; type: "int" }
928- Parameter { name: "destination"; type: "QModelIndex" }
929- Parameter { name: "row"; type: "int" }
930- }
931- Signal {
932- name: "columnsAboutToBeMoved"
933- Parameter { name: "sourceParent"; type: "QModelIndex" }
934- Parameter { name: "sourceStart"; type: "int" }
935- Parameter { name: "sourceEnd"; type: "int" }
936- Parameter { name: "destinationParent"; type: "QModelIndex" }
937- Parameter { name: "destinationColumn"; type: "int" }
938- }
939- Signal {
940- name: "columnsMoved"
941- Parameter { name: "parent"; type: "QModelIndex" }
942- Parameter { name: "start"; type: "int" }
943- Parameter { name: "end"; type: "int" }
944- Parameter { name: "destination"; type: "QModelIndex" }
945- Parameter { name: "column"; type: "int" }
946- }
947- Method { name: "submit"; type: "bool" }
948- Method { name: "revert" }
949- Method {
950- name: "hasIndex"
951- type: "bool"
952- Parameter { name: "row"; type: "int" }
953- Parameter { name: "column"; type: "int" }
954- Parameter { name: "parent"; type: "QModelIndex" }
955- }
956- Method {
957- name: "hasIndex"
958- type: "bool"
959- Parameter { name: "row"; type: "int" }
960- Parameter { name: "column"; type: "int" }
961- }
962- Method {
963- name: "index"
964- type: "QModelIndex"
965- Parameter { name: "row"; type: "int" }
966- Parameter { name: "column"; type: "int" }
967- Parameter { name: "parent"; type: "QModelIndex" }
968- }
969- Method {
970- name: "index"
971- type: "QModelIndex"
972- Parameter { name: "row"; type: "int" }
973- Parameter { name: "column"; type: "int" }
974- }
975- Method {
976- name: "parent"
977- type: "QModelIndex"
978- Parameter { name: "child"; type: "QModelIndex" }
979- }
980- Method {
981- name: "sibling"
982- type: "QModelIndex"
983- Parameter { name: "row"; type: "int" }
984- Parameter { name: "column"; type: "int" }
985- Parameter { name: "idx"; type: "QModelIndex" }
986- }
987- Method {
988- name: "rowCount"
989- type: "int"
990- Parameter { name: "parent"; type: "QModelIndex" }
991- }
992- Method { name: "rowCount"; type: "int" }
993- Method {
994- name: "columnCount"
995- type: "int"
996- Parameter { name: "parent"; type: "QModelIndex" }
997- }
998- Method { name: "columnCount"; type: "int" }
999- Method {
1000- name: "hasChildren"
1001- type: "bool"
1002- Parameter { name: "parent"; type: "QModelIndex" }
1003- }
1004- Method { name: "hasChildren"; type: "bool" }
1005- Method {
1006- name: "data"
1007- type: "QVariant"
1008- Parameter { name: "index"; type: "QModelIndex" }
1009- Parameter { name: "role"; type: "int" }
1010- }
1011- Method {
1012- name: "data"
1013- type: "QVariant"
1014- Parameter { name: "index"; type: "QModelIndex" }
1015- }
1016- Method {
1017- name: "setData"
1018- type: "bool"
1019- Parameter { name: "index"; type: "QModelIndex" }
1020- Parameter { name: "value"; type: "QVariant" }
1021- Parameter { name: "role"; type: "int" }
1022- }
1023- Method {
1024- name: "setData"
1025- type: "bool"
1026- Parameter { name: "index"; type: "QModelIndex" }
1027- Parameter { name: "value"; type: "QVariant" }
1028- }
1029- Method {
1030- name: "headerData"
1031- type: "QVariant"
1032- Parameter { name: "section"; type: "int" }
1033- Parameter { name: "orientation"; type: "Qt::Orientation" }
1034- Parameter { name: "role"; type: "int" }
1035- }
1036- Method {
1037- name: "headerData"
1038- type: "QVariant"
1039- Parameter { name: "section"; type: "int" }
1040- Parameter { name: "orientation"; type: "Qt::Orientation" }
1041- }
1042- Method {
1043- name: "fetchMore"
1044- Parameter { name: "parent"; type: "QModelIndex" }
1045- }
1046- Method {
1047- name: "canFetchMore"
1048- type: "bool"
1049- Parameter { name: "parent"; type: "QModelIndex" }
1050- }
1051- Method {
1052- name: "flags"
1053- type: "Qt::ItemFlags"
1054- Parameter { name: "index"; type: "QModelIndex" }
1055- }
1056- Method {
1057- name: "match"
1058- type: "QModelIndexList"
1059- Parameter { name: "start"; type: "QModelIndex" }
1060- Parameter { name: "role"; type: "int" }
1061- Parameter { name: "value"; type: "QVariant" }
1062- Parameter { name: "hits"; type: "int" }
1063- Parameter { name: "flags"; type: "Qt::MatchFlags" }
1064- }
1065- Method {
1066- name: "match"
1067- type: "QModelIndexList"
1068- Parameter { name: "start"; type: "QModelIndex" }
1069- Parameter { name: "role"; type: "int" }
1070- Parameter { name: "value"; type: "QVariant" }
1071- Parameter { name: "hits"; type: "int" }
1072- }
1073- Method {
1074- name: "match"
1075- type: "QModelIndexList"
1076- Parameter { name: "start"; type: "QModelIndex" }
1077- Parameter { name: "role"; type: "int" }
1078- Parameter { name: "value"; type: "QVariant" }
1079- }
1080- }
1081- Component { name: "QAbstractListModel"; prototype: "QAbstractItemModel" }
1082- Component {
1083- name: "QQuickItem"
1084- defaultProperty: "data"
1085- prototype: "QObject"
1086- Enum {
1087- name: "TransformOrigin"
1088- values: {
1089- "TopLeft": 0,
1090- "Top": 1,
1091- "TopRight": 2,
1092- "Left": 3,
1093- "Center": 4,
1094- "Right": 5,
1095- "BottomLeft": 6,
1096- "Bottom": 7,
1097- "BottomRight": 8
1098- }
1099- }
1100- Property { name: "parent"; type: "QQuickItem"; isPointer: true }
1101- Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
1102- Property { name: "resources"; type: "QObject"; isList: true; isReadonly: true }
1103- Property { name: "children"; type: "QQuickItem"; isList: true; isReadonly: true }
1104- Property { name: "x"; type: "double" }
1105- Property { name: "y"; type: "double" }
1106- Property { name: "z"; type: "double" }
1107- Property { name: "width"; type: "double" }
1108- Property { name: "height"; type: "double" }
1109- Property { name: "opacity"; type: "double" }
1110- Property { name: "enabled"; type: "bool" }
1111- Property { name: "visible"; type: "bool" }
1112- Property { name: "visibleChildren"; type: "QQuickItem"; isList: true; isReadonly: true }
1113- Property { name: "states"; type: "QQuickState"; isList: true; isReadonly: true }
1114- Property { name: "transitions"; type: "QQuickTransition"; isList: true; isReadonly: true }
1115- Property { name: "state"; type: "string" }
1116- Property { name: "childrenRect"; type: "QRectF"; isReadonly: true }
1117- Property { name: "anchors"; type: "QQuickAnchors"; isReadonly: true; isPointer: true }
1118- Property { name: "left"; type: "QQuickAnchorLine"; isReadonly: true }
1119- Property { name: "right"; type: "QQuickAnchorLine"; isReadonly: true }
1120- Property { name: "horizontalCenter"; type: "QQuickAnchorLine"; isReadonly: true }
1121- Property { name: "top"; type: "QQuickAnchorLine"; isReadonly: true }
1122- Property { name: "bottom"; type: "QQuickAnchorLine"; isReadonly: true }
1123- Property { name: "verticalCenter"; type: "QQuickAnchorLine"; isReadonly: true }
1124- Property { name: "baseline"; type: "QQuickAnchorLine"; isReadonly: true }
1125- Property { name: "baselineOffset"; type: "double" }
1126- Property { name: "clip"; type: "bool" }
1127- Property { name: "focus"; type: "bool" }
1128- Property { name: "activeFocus"; type: "bool"; isReadonly: true }
1129- Property { name: "activeFocusOnTab"; revision: 1; type: "bool" }
1130- Property { name: "rotation"; type: "double" }
1131- Property { name: "scale"; type: "double" }
1132- Property { name: "transformOrigin"; type: "TransformOrigin" }
1133- Property { name: "transformOriginPoint"; type: "QPointF"; isReadonly: true }
1134- Property { name: "transform"; type: "QQuickTransform"; isList: true; isReadonly: true }
1135- Property { name: "smooth"; type: "bool" }
1136- Property { name: "antialiasing"; type: "bool" }
1137- Property { name: "implicitWidth"; type: "double" }
1138- Property { name: "implicitHeight"; type: "double" }
1139- Property { name: "layer"; type: "QQuickItemLayer"; isReadonly: true; isPointer: true }
1140- Signal {
1141- name: "childrenRectChanged"
1142- Parameter { type: "QRectF" }
1143- }
1144- Signal {
1145- name: "baselineOffsetChanged"
1146- Parameter { type: "double" }
1147- }
1148- Signal {
1149- name: "stateChanged"
1150- Parameter { type: "string" }
1151- }
1152- Signal {
1153- name: "focusChanged"
1154- Parameter { type: "bool" }
1155- }
1156- Signal {
1157- name: "activeFocusChanged"
1158- Parameter { type: "bool" }
1159- }
1160- Signal {
1161- name: "activeFocusOnTabChanged"
1162- revision: 1
1163- Parameter { type: "bool" }
1164- }
1165- Signal {
1166- name: "parentChanged"
1167- Parameter { type: "QQuickItem"; isPointer: true }
1168- }
1169- Signal {
1170- name: "transformOriginChanged"
1171- Parameter { type: "TransformOrigin" }
1172- }
1173- Signal {
1174- name: "smoothChanged"
1175- Parameter { type: "bool" }
1176- }
1177- Signal {
1178- name: "antialiasingChanged"
1179- Parameter { type: "bool" }
1180- }
1181- Signal {
1182- name: "clipChanged"
1183- Parameter { type: "bool" }
1184- }
1185- Signal {
1186- name: "windowChanged"
1187- revision: 1
1188- Parameter { name: "window"; type: "QQuickWindow"; isPointer: true }
1189- }
1190- Method { name: "update" }
1191- Method {
1192- name: "grabToImage"
1193- revision: 2
1194- type: "bool"
1195- Parameter { name: "callback"; type: "QJSValue" }
1196- Parameter { name: "targetSize"; type: "QSize" }
1197- }
1198- Method {
1199- name: "grabToImage"
1200- revision: 2
1201- type: "bool"
1202- Parameter { name: "callback"; type: "QJSValue" }
1203- }
1204- Method {
1205- name: "contains"
1206- type: "bool"
1207- Parameter { name: "point"; type: "QPointF" }
1208- }
1209- Method {
1210- name: "mapFromItem"
1211- Parameter { type: "QQmlV4Function"; isPointer: true }
1212- }
1213- Method {
1214- name: "mapToItem"
1215- Parameter { type: "QQmlV4Function"; isPointer: true }
1216- }
1217- Method { name: "forceActiveFocus" }
1218- Method {
1219- name: "forceActiveFocus"
1220- Parameter { name: "reason"; type: "Qt::FocusReason" }
1221- }
1222- Method {
1223- name: "nextItemInFocusChain"
1224- revision: 1
1225- type: "QQuickItem*"
1226- Parameter { name: "forward"; type: "bool" }
1227- }
1228- Method { name: "nextItemInFocusChain"; revision: 1; type: "QQuickItem*" }
1229- Method {
1230- name: "childAt"
1231- type: "QQuickItem*"
1232- Parameter { name: "x"; type: "double" }
1233- Parameter { name: "y"; type: "double" }
1234- }
1235- }
1236 }
1237diff --git a/qt/qmlplugin/oxide_qml_plugin.cc b/qt/qmlplugin/oxide_qml_plugin.cc
1238index 1597b5d..b56fb51 100644
1239--- a/qt/qmlplugin/oxide_qml_plugin.cc
1240+++ b/qt/qmlplugin/oxide_qml_plugin.cc
1241@@ -1,5 +1,5 @@
1242 // vim:expandtab:shiftwidth=2:tabstop=2:
1243-// Copyright (C) 2013-2016 Canonical Ltd.
1244+// Copyright (C) 2013-2017 Canonical Ltd.
1245
1246 // This library is free software; you can redistribute it and/or
1247 // modify it under the terms of the GNU Lesser General Public
1248@@ -215,6 +215,8 @@ class OxideQmlPlugin : public QQmlExtensionPlugin {
1249 qmlRegisterUncreatableType<OxideQQuickNavigationHistory, 1>(
1250 uri, 1, 19, "NavigationHistory",
1251 "NavigationHistory is accessed via WebView.navigationHistory");
1252+
1253+ qmlRegisterType<OxideQQuickWebContext, 4>(uri, 1, 23, "WebContext");
1254 }
1255 };
1256
1257diff --git a/qt/quick/api/oxideqquickwebcontext.cc b/qt/quick/api/oxideqquickwebcontext.cc
1258index 6fe1ef7..1946af0 100644
1259--- a/qt/quick/api/oxideqquickwebcontext.cc
1260+++ b/qt/quick/api/oxideqquickwebcontext.cc
1261@@ -1561,4 +1561,35 @@ void OxideQQuickWebContext::setDoNotTrack(bool dnt) {
1262 emit doNotTrackEnabledChanged();
1263 }
1264
1265+/*!
1266+\qmlproperty bool WebContext::autofillEnabled
1267+\since OxideQt 1.22
1268+
1269+Whether to enable autofill. The default is false.
1270+
1271+Autofill is most probably neither useful nor desirable outside of the browser
1272+use case, and as such is disabled by default.
1273+
1274+The base Oxide WebView does not know how to display autofill suggestions,
1275+use the WebView from the Oxide.Ubuntu module if you need them.
1276+*/
1277+
1278+bool OxideQQuickWebContext::autofillEnabled() const {
1279+ Q_D(const OxideQQuickWebContext);
1280+
1281+ return d->proxy_->autofillEnabled();
1282+}
1283+
1284+void OxideQQuickWebContext::setAutofillEnabled(bool enabled) {
1285+ Q_D(OxideQQuickWebContext);
1286+
1287+ if (autofillEnabled() == enabled) {
1288+ return;
1289+ }
1290+
1291+ d->proxy_->setAutofillEnabled(enabled);
1292+
1293+ emit autofillEnabledChanged();
1294+}
1295+
1296 #include "moc_oxideqquickwebcontext.cpp"
1297diff --git a/qt/quick/api/oxideqquickwebcontext.h b/qt/quick/api/oxideqquickwebcontext.h
1298index 99de73a..f8ca6fa 100644
1299--- a/qt/quick/api/oxideqquickwebcontext.h
1300+++ b/qt/quick/api/oxideqquickwebcontext.h
1301@@ -1,5 +1,5 @@
1302 // vim:expandtab:shiftwidth=2:tabstop=2:
1303-// Copyright (C) 2013-2015 Canonical Ltd.
1304+// Copyright (C) 2013-2017 Canonical Ltd.
1305
1306 // This library is free software; you can redistribute it and/or
1307 // modify it under the terms of the GNU Lesser General Public
1308@@ -77,6 +77,8 @@ class OXIDE_QTQUICK_EXPORT OxideQQuickWebContext : public QObject,
1309
1310 Q_PROPERTY(bool doNotTrackEnabled READ doNotTrack WRITE setDoNotTrack NOTIFY doNotTrackEnabledChanged REVISION 3)
1311
1312+ Q_PROPERTY(bool autofillEnabled READ autofillEnabled WRITE setAutofillEnabled NOTIFY autofillEnabledChanged REVISION 4)
1313+
1314 Q_ENUMS(CookiePolicy)
1315 Q_ENUMS(SessionCookieMode)
1316
1317@@ -173,6 +175,9 @@ class OXIDE_QTQUICK_EXPORT OxideQQuickWebContext : public QObject,
1318 bool doNotTrack() const;
1319 void setDoNotTrack(bool dnt);
1320
1321+ bool autofillEnabled() const;
1322+ void setAutofillEnabled(bool enabled);
1323+
1324 Q_SIGNALS:
1325 void productChanged();
1326 void userAgentChanged();
1327@@ -196,6 +201,7 @@ class OXIDE_QTQUICK_EXPORT OxideQQuickWebContext : public QObject,
1328 Q_REVISION(3) void defaultVideoCaptureDeviceIdChanged();
1329 Q_REVISION(3) void userAgentOverridesChanged();
1330 Q_REVISION(3) void doNotTrackEnabledChanged();
1331+ Q_REVISION(4) void autofillEnabledChanged();
1332
1333 protected:
1334 // QQmlParserStatus implementation
1335diff --git a/qt/quick/qquick_legacy_auxiliary_ui_factory.cc b/qt/quick/qquick_legacy_auxiliary_ui_factory.cc
1336index c0084c2..e50b6c3 100644
1337--- a/qt/quick/qquick_legacy_auxiliary_ui_factory.cc
1338+++ b/qt/quick/qquick_legacy_auxiliary_ui_factory.cc
1339@@ -1,5 +1,5 @@
1340 // vim:expandtab:shiftwidth=2:tabstop=2:
1341-// Copyright (C) 2016 Canonical Ltd.
1342+// Copyright (C) 2016-2017 Canonical Ltd.
1343
1344 // This library is free software; you can redistribute it and/or
1345 // modify it under the terms of the GNU Lesser General Public
1346@@ -21,6 +21,7 @@
1347
1348 #include "qt/core/glue/javascript_dialog_type.h"
1349 #include "qt/core/glue/touch_editing_menu.h"
1350+#include "qt/core/glue/web_autofill_popup.h"
1351
1352 #include "qquick_legacy_alert_dialog.h"
1353 #include "qquick_legacy_before_unload_dialog.h"
1354@@ -31,11 +32,13 @@
1355 namespace oxide {
1356 namespace qquick {
1357
1358+using qt::AutofillSuggestion;
1359 using qt::EditCapabilityFlags;
1360 using qt::JavaScriptDialogClient;
1361 using qt::JavaScriptDialogType;
1362 using qt::MenuItem;
1363 using qt::TouchEditingMenuClient;
1364+using qt::WebAutofillPopupClient;
1365 using qt::WebContextMenuClient;
1366 using qt::WebContextMenuParams;
1367
1368@@ -52,6 +55,15 @@ LegacyAuxiliaryUIFactory::CreateWebContextMenu(
1369 new LegacyWebContextMenu(item_, context_menu_, params, client));
1370 }
1371
1372+std::unique_ptr<qt::WebAutofillPopup>
1373+LegacyAuxiliaryUIFactory::CreateWebAutofillPopup(
1374+ const std::vector<AutofillSuggestion>& suggestions,
1375+ const QRectF& bounds,
1376+ WebAutofillPopupClient* client) {
1377+ Q_ASSERT(0);
1378+ return nullptr;
1379+}
1380+
1381 std::unique_ptr<qt::TouchEditingMenu>
1382 LegacyAuxiliaryUIFactory::CreateTouchEditingMenu(
1383 EditCapabilityFlags edit_flags,
1384diff --git a/qt/quick/qquick_legacy_auxiliary_ui_factory.h b/qt/quick/qquick_legacy_auxiliary_ui_factory.h
1385index a7cec12..e969e6e 100644
1386--- a/qt/quick/qquick_legacy_auxiliary_ui_factory.h
1387+++ b/qt/quick/qquick_legacy_auxiliary_ui_factory.h
1388@@ -1,5 +1,5 @@
1389 // vim:expandtab:shiftwidth=2:tabstop=2:
1390-// Copyright (C) 2016 Canonical Ltd.
1391+// Copyright (C) 2016-2017 Canonical Ltd.
1392
1393 // This library is free software; you can redistribute it and/or
1394 // modify it under the terms of the GNU Lesser General Public
1395@@ -59,6 +59,10 @@ class LegacyAuxiliaryUIFactory : public qt::AuxiliaryUIFactory {
1396 const qt::WebContextMenuParams& params,
1397 const std::vector<qt::MenuItem>& items,
1398 qt::WebContextMenuClient* client) override;
1399+ std::unique_ptr<qt::WebAutofillPopup> CreateWebAutofillPopup(
1400+ const std::vector<qt::AutofillSuggestion>& suggestions,
1401+ const QRectF& bounds,
1402+ qt::WebAutofillPopupClient* client) override;
1403 std::unique_ptr<qt::TouchEditingMenu> CreateTouchEditingMenu(
1404 qt::EditCapabilityFlags edit_flags,
1405 qt::TouchEditingMenuClient* client) override;
1406diff --git a/qt/tests/qmltests/api/tst_WebContext_autofillEnabled.qml b/qt/tests/qmltests/api/tst_WebContext_autofillEnabled.qml
1407new file mode 100644
1408index 0000000..7350196
1409--- /dev/null
1410+++ b/qt/tests/qmltests/api/tst_WebContext_autofillEnabled.qml
1411@@ -0,0 +1,38 @@
1412+import QtQuick 2.0
1413+import QtTest 1.0
1414+import com.canonical.Oxide 1.22
1415+import Oxide.testsupport 1.0
1416+
1417+TestWebView {
1418+ id: webView
1419+
1420+ SignalSpy {
1421+ id: spy
1422+ signalName: "autofillEnabledChanged"
1423+ }
1424+
1425+ TestCase {
1426+ name: "WebContext_autofillEnabled"
1427+ when: windowShown
1428+
1429+ function test_WebContext_autofillEnabled1_default() {
1430+ compare(webView.context.autofillEnabled, false,
1431+ "Autofill should be disabled by default");
1432+ }
1433+
1434+ function test_WebContext_autofillEnabled2_get_set() {
1435+ spy.target = webView.context;
1436+
1437+ verify(!webView.context.autofillEnabled);
1438+ compare(spy.count, 0);
1439+
1440+ webView.context.autofillEnabled = true;
1441+ compare(spy.count, 1);
1442+ verify(webView.context.autofillEnabled);
1443+
1444+ webView.context.autofillEnabled = false;
1445+ compare(spy.count, 2);
1446+ verify(!webView.context.autofillEnabled);
1447+ }
1448+ }
1449+}
1450diff --git a/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_autofill_profiles.html b/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_autofill_profiles.html
1451new file mode 100644
1452index 0000000..e28755c
1453--- /dev/null
1454+++ b/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_autofill_profiles.html
1455@@ -0,0 +1,23 @@
1456+<html>
1457+ <body>
1458+ <form action="empty.html">
1459+ <input id="firstname" name="firstname" type="text" required autofocus />
1460+ <br />
1461+ <input id="lastname" name="lastname" type="text" required />
1462+ <br />
1463+ <input id="email" name="email" type="text" required />
1464+ <br />
1465+ <input id="country" name="country" type="text" required />
1466+ <br />
1467+ <input id="state" name="state" type="text" />
1468+ <br />
1469+ <input id="zipcode" name="zipcode" type="text" required />
1470+ <br />
1471+ <input id="city" name="city" type="text" required />
1472+ <br />
1473+ <input id="street" name="street" type="text" required />
1474+ <br />
1475+ <input type="submit" value="Submit" />
1476+ </form>
1477+ </body>
1478+</html>
1479diff --git a/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_autofill_profiles.qml b/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_autofill_profiles.qml
1480new file mode 100644
1481index 0000000..e65d221
1482--- /dev/null
1483+++ b/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_autofill_profiles.qml
1484@@ -0,0 +1,300 @@
1485+import QtQuick 2.0
1486+import QtTest 1.0
1487+import com.canonical.Oxide 1.22
1488+import Oxide.Ubuntu 1.0
1489+import Oxide.testsupport 1.0
1490+import Ubuntu.Components 1.3
1491+
1492+FocusScope {
1493+ width: 800
1494+ height: 600
1495+
1496+ focus: true
1497+
1498+ UbuntuTestWebView {
1499+ id: webView
1500+ objectName: "webView"
1501+ anchors.fill: parent
1502+ visible: false
1503+ }
1504+
1505+ UbuntuTestWebView {
1506+ id: incognitoWebView
1507+ objectName: "incognitoWebView"
1508+ anchors.fill: parent
1509+ incognito: true
1510+ visible: false
1511+ }
1512+
1513+ TestCase {
1514+ name: "WebViewAutofillPopup_autofill_profiles"
1515+ when: windowShown
1516+
1517+ function loadForm(webView) {
1518+ webView.visible = true;
1519+ webView.focus = true;
1520+ webView.context.autofillEnabled = true;
1521+ webView.url =
1522+ "http://testsuite/tst_WebViewAutofillPopup_autofill_profiles.html";
1523+ verify(webView.waitForLoadSucceeded());
1524+ }
1525+
1526+ function verify_field_focused(webView, field) {
1527+ verify(TestUtils.waitFor(function() {
1528+ return webView.getTestApi().evaluateCode(
1529+ "document.activeElement.id") == field; }));
1530+ }
1531+
1532+ function compare_field_value(webView, field, value) {
1533+ verify(TestUtils.waitFor(function() {
1534+ return webView.getTestApi().evaluateCode(
1535+ "document.querySelector('#%1').value".arg(field)) == value; }));
1536+ }
1537+
1538+ function typeStringInField(webView, string, field) {
1539+ verify_field_focused(webView, field);
1540+ for (var i = 0; i < string.length; ++i) {
1541+ keyClick(string[i]);
1542+ }
1543+ compare_field_value(webView, field, string);
1544+ }
1545+
1546+ function inputAndValidateProfile(webView, data) {
1547+ var fields = ["firstname", "lastname", "email", "country", "state",
1548+ "zipcode", "city", "street"];
1549+ for (var i = 0; i < fields.length; ++i) {
1550+ typeStringInField(webView, data[i], fields[i]);
1551+ keyClick(Qt.Key_Tab);
1552+ }
1553+ keyClick(Qt.Key_Enter);
1554+ verify(webView.waitForLoadSucceeded());
1555+ loadForm(webView);
1556+ }
1557+
1558+ function initTestCase() {
1559+ loadForm(webView);
1560+
1561+ // Initial population of the autofill profiles database
1562+ var data = [
1563+ ["John", "Doe", "john.doe@nospam.org",
1564+ "US", "New York", "10016", "NYC", "Park Avenue"],
1565+ ["John", "Lennon", "john@thebeatles.com",
1566+ "UK", "", "L25 6DA", "Liverpool", "Beaconsfield Road"],
1567+ ["Bruce", "Lee", "bruce@lee.com",
1568+ "US", "California", "94108", "San Francisco", "Joice Street"],
1569+ ];
1570+ for (var i = 0; i < data.length; ++i) {
1571+ inputAndValidateProfile(webView, data[i]);
1572+ }
1573+ }
1574+
1575+ function getAutofillPopup(webView) {
1576+ return TestSupport.findItemInScene(
1577+ TestWindow.rootItem, "%1_WebAutofillPopup".arg(webView.objectName));
1578+ }
1579+
1580+ function waitForAutofillPopupToClose(webView) {
1581+ var popup = getAutofillPopup(webView);
1582+ if (!popup) {
1583+ return true;
1584+ }
1585+
1586+ var helper = TestSupport.createQObjectTestHelper(popup);
1587+
1588+ return TestUtils.waitFor(function() {
1589+ return !getAutofillPopup(webView); }) &&
1590+ TestUtils.waitFor(function() { return helper.destroyed; });
1591+ }
1592+
1593+ function waitForAutofillPopup(webView, timeout) {
1594+ return TestUtils.waitFor(function() {
1595+ return !!getAutofillPopup(webView) &&
1596+ getAutofillPopup(webView).visible; },
1597+ timeout);
1598+ }
1599+
1600+ function getSuggestion(webView, index) {
1601+ return TestSupport.findItemInScene(
1602+ TestWindow.rootItem,
1603+ "%1_WebAutofillPopup_item%2".arg(webView.objectName).arg(index));
1604+ }
1605+
1606+ function compareBoundsToClientRect(bounds, rect) {
1607+ compare(bounds.x, rect.x);
1608+ compare(bounds.y, rect.y);
1609+ compare(bounds.width, rect.width);
1610+ compare(bounds.height, rect.height);
1611+ }
1612+
1613+ function init() {
1614+ loadForm(webView);
1615+ }
1616+
1617+ function get_webviews_data() {
1618+ var data = [ { webView: webView } ];
1619+ if (incognitoWebView.incognito) {
1620+ // When run in single process mode, incognito cannot be set
1621+ data.push({ webView: incognitoWebView });
1622+ }
1623+ return data;
1624+ }
1625+
1626+ function test_initial_state_data() {
1627+ return get_webviews_data();
1628+ }
1629+
1630+ function test_initial_state(data) {
1631+ loadForm(data.webView);
1632+ typeStringInField(data.webView, "jo", "firstname");
1633+ var popup = getAutofillPopup(data.webView);
1634+ var r = data.webView.getTestApi()
1635+ .getBoundingClientRectForSelector("#firstname");
1636+ compareBoundsToClientRect(popup.elementBounds, r);
1637+ compare(popup.currentIndex, -1);
1638+ compare(popup.count, 2);
1639+ compare(getSuggestion(data.webView, 0).value, "John");
1640+ compare(getSuggestion(data.webView, 1).value, "John");
1641+ data.webView.visible = false;
1642+ }
1643+
1644+ function test_toggle_autofillEnabled_data() {
1645+ return get_webviews_data();
1646+ }
1647+
1648+ function test_toggle_autofillEnabled(data) {
1649+ loadForm(data.webView);
1650+ typeStringInField(data.webView, "j", "firstname");
1651+ verify(waitForAutofillPopup(data.webView));
1652+ keyClick(Qt.Key_Backspace);
1653+ verify(waitForAutofillPopupToClose(data.webView));
1654+
1655+ data.webView.context.autofillEnabled = false;
1656+ typeStringInField(data.webView, "j", "firstname");
1657+ verify(!waitForAutofillPopup(data.webView, 1000));
1658+ keyClick(Qt.Key_Backspace);
1659+
1660+ data.webView.context.autofillEnabled = true;
1661+ typeStringInField(data.webView, "j", "firstname");
1662+ verify(waitForAutofillPopup(data.webView));
1663+ data.webView.visible = false;
1664+ }
1665+
1666+ function test_escape_dismisses_popup_and_resets_field_value_data() {
1667+ return get_webviews_data();
1668+ }
1669+
1670+ function test_escape_dismisses_popup_and_resets_field_value(data) {
1671+ loadForm(data.webView);
1672+ typeStringInField(data.webView, "br", "firstname");
1673+ verify(waitForAutofillPopup(data.webView));
1674+ var popup = getAutofillPopup(data.webView);
1675+ compare(popup.count, 1);
1676+ compare(getSuggestion(data.webView, 0).value, "Bruce");
1677+ keyClick(Qt.Key_Down);
1678+ tryCompare(popup, "currentIndex", 0);
1679+ keyClick(Qt.Key_Escape);
1680+ verify(waitForAutofillPopupToClose(data.webView));
1681+ compare_field_value(data.webView, "firstname", "br");
1682+ compare_field_value(data.webView, "lastname", "");
1683+ compare_field_value(data.webView, "email", "");
1684+ compare_field_value(data.webView, "country", "");
1685+ compare_field_value(data.webView, "state", "");
1686+ compare_field_value(data.webView, "zipcode", "");
1687+ compare_field_value(data.webView, "city", "");
1688+ compare_field_value(data.webView, "street", "");
1689+ data.webView.visible = false;
1690+ }
1691+
1692+ function test_validate_suggestion_with_mouse_data() {
1693+ return get_webviews_data();
1694+ }
1695+
1696+ function test_validate_suggestion_with_mouse(data) {
1697+ loadForm(data.webView);
1698+ typeStringInField(data.webView, "br", "firstname");
1699+ verify(waitForAutofillPopup(data.webView));
1700+ var popup = getAutofillPopup(data.webView);
1701+ compare(popup.count, 1);
1702+ compare(getSuggestion(data.webView, 0).value, "Bruce");
1703+ var item = getSuggestion(data.webView, 0);
1704+ mouseClick(item);
1705+ verify(waitForAutofillPopupToClose(data.webView));
1706+ verify_field_focused(data.webView, "firstname");
1707+ compare_field_value(data.webView, "firstname", "Bruce");
1708+ compare_field_value(data.webView, "lastname", "Lee");
1709+ compare_field_value(data.webView, "email", "bruce@lee.com");
1710+ compare_field_value(data.webView, "state", "California");
1711+ compare_field_value(data.webView, "zipcode", "94108");
1712+ compare_field_value(data.webView, "city", "San Francisco");
1713+ compare_field_value(data.webView, "street", "Joice Street");
1714+ data.webView.visible = false;
1715+ }
1716+
1717+ function test_validate_suggestion_with_keyboard_data() {
1718+ return [
1719+ { webView: webView, key: Qt.Key_Enter, focus: "lastname" },
1720+ { webView: webView, key: Qt.Key_Return, focus: "lastname" },
1721+ { webView: webView, key: Qt.Key_Tab, focus: "email" },
1722+ { webView: incognitoWebView, key: Qt.Key_Enter, focus: "lastname" },
1723+ { webView: incognitoWebView, key: Qt.Key_Return, focus: "lastname" },
1724+ { webView: incognitoWebView, key: Qt.Key_Tab, focus: "email" },
1725+ ];
1726+ }
1727+
1728+ function test_validate_suggestion_with_keyboard(data) {
1729+ loadForm(data.webView);
1730+ keyClick(Qt.Key_Tab);
1731+ typeStringInField(data.webView, "len", "lastname");
1732+ var popup = getAutofillPopup(data.webView);
1733+ compare(popup.count, 1);
1734+ compare(getSuggestion(data.webView, 0).value, "Lennon");
1735+ keyClick(Qt.Key_Down);
1736+ tryCompare(popup, "currentIndex", 0);
1737+ keyClick(data.key);
1738+ verify(waitForAutofillPopupToClose(data.webView));
1739+ verify_field_focused(data.webView, data.focus);
1740+ compare_field_value(data.webView, "firstname", "John");
1741+ compare_field_value(data.webView, "lastname", "Lennon");
1742+ compare_field_value(data.webView, "email", "john@thebeatles.com");
1743+ compare_field_value(data.webView, "state", "");
1744+ compare_field_value(data.webView, "zipcode", "L25 6DA");
1745+ compare_field_value(data.webView, "city", "Liverpool");
1746+ compare_field_value(data.webView, "street", "Beaconsfield Road");
1747+ data.webView.visible = false;
1748+ }
1749+
1750+ function test_shiftdel_deletes_suggestion() {
1751+ var data = ["Paul", "McCartney", "paul@thebeatles.com",
1752+ "UK", "", "L18 1DE", "Liverpool", "Penny Lane"];
1753+ inputAndValidateProfile(webView, data);
1754+ for (var i = 0; i < 6; ++i) {
1755+ keyClick(Qt.Key_Tab);
1756+ }
1757+ typeStringInField(webView, "Live", "city");
1758+ verify(waitForAutofillPopup(webView));
1759+ var popup = getAutofillPopup(webView);
1760+ compare(popup.count, 2);
1761+ compare(getSuggestion(webView, 0).label, "Penny Lane");
1762+ compare(getSuggestion(webView, 1).label, "Beaconsfield Road");
1763+ keyClick(Qt.Key_Down);
1764+ tryCompare(popup, "currentIndex", 0);
1765+ keyClick(Qt.Key_Delete, Qt.ShiftModifier);
1766+ tryCompare(popup, "currentIndex", -1);
1767+ tryCompare(popup, "count", 1);
1768+ compare(getSuggestion(webView, 0).label, "Beaconsfield Road");
1769+ }
1770+
1771+ function test_incognito_profile_not_saved() {
1772+ if (!incognitoWebView.incognito) {
1773+ skip("WebView.incognito cannot be set in single process mode");
1774+ }
1775+ loadForm(incognitoWebView);
1776+ var data = ["Paul", "McCartney", "paul@thebeatles.com",
1777+ "UK", "", "L18 1DE", "Liverpool", "Penny Lane"];
1778+ inputAndValidateProfile(incognitoWebView, data);
1779+ typeStringInField(incognitoWebView, "P", "firstname");
1780+ verify(!waitForAutofillPopup(incognitoWebView, 1000));
1781+ incognitoWebView.visible = false;
1782+ }
1783+ }
1784+}
1785diff --git a/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_datalist.html b/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_datalist.html
1786new file mode 100644
1787index 0000000..cded135
1788--- /dev/null
1789+++ b/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_datalist.html
1790@@ -0,0 +1,37 @@
1791+<html>
1792+ <body>
1793+ <form action="tst_WebViewAutofillPopup_datalist.html">
1794+ <input id="static" name="static" list="staticdatalist" type="text" autofocus />
1795+ <datalist id="staticdatalist">
1796+ <option>bar</option>
1797+ <option>baz</option>
1798+ <option>qux</option>
1799+ </datalist>
1800+ <br />
1801+ <input id="static2" name="static2" list="staticdatalist" type="text" />
1802+ <br />
1803+ <input id="dynamic" name="dynamic" list="dynamicdatalist" type="text" />
1804+ <datalist id="dynamicdatalist"></datalist>
1805+ <script>
1806+ function createDatalist() {
1807+ var value = document.getElementById("dynamic").value;
1808+ var list = [];
1809+ if (value == "f") {
1810+ list = ["foo", "flip"];
1811+ } else if (value == "b") {
1812+ list = ["bar", "beard", "beam"];
1813+ }
1814+ var datalist = document.getElementById("dynamicdatalist");
1815+ datalist.innerHTML = "";
1816+ for (var i = 0; i < list.length; ++i) {
1817+ var option = document.createElement('option');
1818+ option.value = list[i];
1819+ datalist.appendChild(option);
1820+ }
1821+ }
1822+ document.getElementById("dynamic").addEventListener("input", createDatalist);
1823+ </script>
1824+ <input type="submit" value="Submit" />
1825+ </form>
1826+ </body>
1827+</html>
1828diff --git a/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_datalist.qml b/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_datalist.qml
1829new file mode 100644
1830index 0000000..1288857
1831--- /dev/null
1832+++ b/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_datalist.qml
1833@@ -0,0 +1,157 @@
1834+import QtQuick 2.0
1835+import QtTest 1.0
1836+import com.canonical.Oxide 1.22
1837+import Oxide.Ubuntu 1.0
1838+import Oxide.testsupport 1.0
1839+import Ubuntu.Components 1.3
1840+
1841+UbuntuTestWebView {
1842+ id: webView
1843+ objectName: "webView"
1844+
1845+ width: 800
1846+ height: 600
1847+
1848+ focus: true
1849+
1850+ TestCase {
1851+ name: "WebViewAutofillPopup_datalist"
1852+ when: windowShown
1853+
1854+ function verify_field_focused(field) {
1855+ verify(TestUtils.waitFor(function() {
1856+ return webView.getTestApi().evaluateCode(
1857+ "document.activeElement.id") == field; }));
1858+ }
1859+
1860+ function compare_field_value(field, value) {
1861+ verify(TestUtils.waitFor(function() {
1862+ return webView.getTestApi().evaluateCode(
1863+ "document.querySelector('#%1').value".arg(field)) == value; }));
1864+ }
1865+
1866+ function typeStringInField(string, field) {
1867+ verify_field_focused(field);
1868+ for (var i = 0; i < string.length; ++i) {
1869+ keyClick(string[i]);
1870+ }
1871+ compare_field_value(field, string);
1872+ }
1873+
1874+ function initTestCase() {
1875+ webView.context.autofillEnabled = true;
1876+ }
1877+
1878+ function getAutofillPopup() {
1879+ return TestSupport.findItemInScene(TestWindow.rootItem,
1880+ "webView_WebAutofillPopup");
1881+ }
1882+
1883+ function waitForAutofillPopupToClose() {
1884+ var popup = getAutofillPopup();
1885+ if (!popup) {
1886+ return true;
1887+ }
1888+
1889+ var helper = TestSupport.createQObjectTestHelper(popup);
1890+
1891+ return TestUtils.waitFor(function() { return !getAutofillPopup(); }) &&
1892+ TestUtils.waitFor(function() { return helper.destroyed; });
1893+ }
1894+
1895+ function waitForAutofillPopup() {
1896+ return TestUtils.waitFor(function() {
1897+ return !!getAutofillPopup() && getAutofillPopup().visible; });
1898+ }
1899+
1900+ function getSuggestion(index) {
1901+ return TestSupport.findItemInScene(
1902+ TestWindow.rootItem, "webView_WebAutofillPopup_item%1".arg(index));
1903+ }
1904+
1905+ function init() {
1906+ webView.url =
1907+ "http://testsuite/tst_WebViewAutofillPopup_datalist.html";
1908+ verify(webView.waitForLoadSucceeded());
1909+ }
1910+
1911+ function test_static_datalist() {
1912+ typeStringInField("b", "static");
1913+ verify(waitForAutofillPopup());
1914+ var popup = getAutofillPopup();
1915+ compare(popup.count, 2);
1916+ compare(getSuggestion(0).value, "bar");
1917+ compare(getSuggestion(1).value, "baz");
1918+ keyClick("a");
1919+ compare(popup.count, 2);
1920+ keyClick("e");
1921+ verify(waitForAutofillPopupToClose());
1922+
1923+ keyClick(Qt.Key_Backspace);
1924+ verify(waitForAutofillPopup());
1925+ popup = getAutofillPopup();
1926+ compare(popup.count, 2);
1927+ keyClick(Qt.Key_Backspace);
1928+ keyClick(Qt.Key_Backspace);
1929+ verify(waitForAutofillPopupToClose());
1930+
1931+ typeStringInField("q", "static");
1932+ verify(waitForAutofillPopup());
1933+ popup = getAutofillPopup();
1934+ compare(popup.count, 1);
1935+ compare(getSuggestion(0).value, "qux");
1936+ compare(popup.currentIndex, -1);
1937+ keyClick(Qt.Key_Down);
1938+ compare(popup.currentIndex, 0);
1939+ keyClick(Qt.Key_Return);
1940+ verify(waitForAutofillPopupToClose());
1941+ compare_field_value("static", "qux");
1942+ }
1943+
1944+ function test_combined_datalist_autofill_suggestions() {
1945+ keyClick(Qt.Key_Tab);
1946+ typeStringInField("quantal", "static2");
1947+ keyClick(Qt.Key_Enter);
1948+ verify(webView.waitForLoadSucceeded());
1949+
1950+ keyClick(Qt.Key_Tab);
1951+ typeStringInField("q", "static2");
1952+ verify(waitForAutofillPopup());
1953+ var popup = getAutofillPopup();
1954+ compare(popup.count, 2);
1955+ compare(getSuggestion(0).value, "qux");
1956+ compare(getSuggestion(1).value, "quantal");
1957+ }
1958+
1959+ function test_dynamic_datalist() {
1960+ keyClick(Qt.Key_Tab);
1961+ keyClick(Qt.Key_Tab);
1962+ typeStringInField("f", "dynamic");
1963+ verify(waitForAutofillPopup());
1964+ var popup = getAutofillPopup();
1965+ compare(popup.count, 2);
1966+ compare(getSuggestion(0).value, "foo");
1967+ compare(getSuggestion(1).value, "flip");
1968+ keyClick("a");
1969+ verify(waitForAutofillPopupToClose());
1970+ keyClick(Qt.Key_Backspace);
1971+ verify(waitForAutofillPopup());
1972+ keyClick(Qt.Key_Backspace);
1973+ verify(waitForAutofillPopupToClose());
1974+ compare_field_value("dynamic", "");
1975+
1976+ typeStringInField("b", "dynamic");
1977+ verify(waitForAutofillPopup());
1978+ popup = getAutofillPopup();
1979+ compare(popup.count, 3);
1980+ compare(getSuggestion(0).value, "bar");
1981+ compare(getSuggestion(1).value, "beard");
1982+ compare(getSuggestion(2).value, "beam");
1983+ keyClick(Qt.Key_Up);
1984+ compare(popup.currentIndex, 2);
1985+ keyClick(Qt.Key_Enter);
1986+ verify(waitForAutofillPopupToClose());
1987+ compare_field_value("dynamic", "beam");
1988+ }
1989+ }
1990+}
1991diff --git a/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_passwords.html b/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_passwords.html
1992new file mode 100644
1993index 0000000..8a712e6
1994--- /dev/null
1995+++ b/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_passwords.html
1996@@ -0,0 +1,11 @@
1997+<html>
1998+ <body>
1999+ <form action="empty.html">
2000+ <input id="username" name="username" type="text" required autofocus />
2001+ <br />
2002+ <input id="password" name="password" type="password" required />
2003+ <br />
2004+ <input type="submit" value="Submit" />
2005+ </form>
2006+ </body>
2007+</html>
2008diff --git a/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_passwords.qml b/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_passwords.qml
2009new file mode 100644
2010index 0000000..b1aede3
2011--- /dev/null
2012+++ b/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_passwords.qml
2013@@ -0,0 +1,105 @@
2014+import QtQuick 2.0
2015+import QtTest 1.0
2016+import com.canonical.Oxide 1.22
2017+import Oxide.Ubuntu 1.0
2018+import Oxide.testsupport 1.0
2019+import Ubuntu.Components 1.3
2020+
2021+UbuntuTestWebView {
2022+ id: webView
2023+ objectName: "webView"
2024+
2025+ width: 800
2026+ height: 600
2027+
2028+ focus: true
2029+
2030+ TestCase {
2031+ name: "WebViewAutofillPopup_passwords"
2032+ when: windowShown
2033+
2034+ function loadForm() {
2035+ webView.url =
2036+ "http://testsuite/tst_WebViewAutofillPopup_passwords.html";
2037+ verify(webView.waitForLoadSucceeded());
2038+ }
2039+
2040+ function verify_field_focused(field) {
2041+ verify(TestUtils.waitFor(function() {
2042+ return webView.getTestApi().evaluateCode(
2043+ "document.activeElement.id") == field; }));
2044+ }
2045+
2046+ function compare_field_value(field, value) {
2047+ verify(TestUtils.waitFor(function() {
2048+ return webView.getTestApi().evaluateCode(
2049+ "document.querySelector('#%1').value".arg(field)) == value; }));
2050+ }
2051+
2052+ function typeStringInField(string, field) {
2053+ verify_field_focused(field);
2054+ for (var i = 0; i < string.length; ++i) {
2055+ keyClick(string[i]);
2056+ }
2057+ compare_field_value(field, string);
2058+ }
2059+
2060+ function initTestCase() {
2061+ webView.context.autofillEnabled = true;
2062+ }
2063+
2064+ function getAutofillPopup() {
2065+ return TestSupport.findItemInScene(TestWindow.rootItem,
2066+ "webView_WebAutofillPopup");
2067+ }
2068+
2069+ function waitForAutofillPopupToClose() {
2070+ var popup = getAutofillPopup();
2071+ if (!popup) {
2072+ return true;
2073+ }
2074+
2075+ var helper = TestSupport.createQObjectTestHelper(popup);
2076+
2077+ return TestUtils.waitFor(function() { return !getAutofillPopup(); }) &&
2078+ TestUtils.waitFor(function() { return helper.destroyed; });
2079+ }
2080+
2081+ function waitForAutofillPopup(timeout) {
2082+ return TestUtils.waitFor(function() {
2083+ return !!getAutofillPopup() && getAutofillPopup().visible; },
2084+ timeout);
2085+ }
2086+
2087+ function getSuggestion(index) {
2088+ return TestSupport.findItemInScene(
2089+ TestWindow.rootItem, "webView_WebAutofillPopup_item%1".arg(index));
2090+ }
2091+
2092+ function init() {
2093+ loadForm();
2094+ }
2095+
2096+ // Autofill is not implemented yet for passwords
2097+ function test_passwords_not_autofilled() {
2098+ typeStringInField("myuser", "username");
2099+ keyClick(Qt.Key_Tab);
2100+ typeStringInField("mypass", "password");
2101+ keyClick(Qt.Key_Enter);
2102+ verify(webView.waitForLoadSucceeded());
2103+
2104+ loadForm();
2105+ typeStringInField("my", "username");
2106+ verify(waitForAutofillPopup());
2107+ var popup = getAutofillPopup();
2108+ compare(popup.count, 1);
2109+ compare(getSuggestion(0).value, "myuser");
2110+ keyClick(Qt.Key_Down);
2111+ compare(popup.currentIndex, 0);
2112+ keyClick(Qt.Key_Enter);
2113+ verify(waitForAutofillPopupToClose());
2114+ compare_field_value("username", "myuser");
2115+ compare_field_value("password", "");
2116+ }
2117+ }
2118+}
2119diff --git a/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_single_fields.html b/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_single_fields.html
2120new file mode 100644
2121index 0000000..7389fe5
2122--- /dev/null
2123+++ b/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_single_fields.html
2124@@ -0,0 +1,11 @@
2125+<html>
2126+ <body>
2127+ <form action="empty.html">
2128+ <input id="field1" name="field1" type="text" autofocus />
2129+ <br />
2130+ <input id="field2" name="field2" type="text" />
2131+ <br />
2132+ <input type="submit" value="Submit" />
2133+ </form>
2134+ </body>
2135+</html>
2136diff --git a/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_single_fields.qml b/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_single_fields.qml
2137new file mode 100644
2138index 0000000..26a88fb
2139--- /dev/null
2140+++ b/qt/tests/qmltests/ubuntu_ui/tst_WebViewAutofillPopup_single_fields.qml
2141@@ -0,0 +1,359 @@
2142+import QtQuick 2.0
2143+import QtTest 1.0
2144+import com.canonical.Oxide 1.22
2145+import Oxide.Ubuntu 1.0
2146+import Oxide.testsupport 1.0
2147+import Ubuntu.Components 1.3
2148+
2149+FocusScope {
2150+ id: testRoot
2151+
2152+ width: 800
2153+ height: 600
2154+
2155+ focus: true
2156+
2157+ UbuntuTestWebView {
2158+ id: webView
2159+ objectName: "webView"
2160+ anchors.fill: parent
2161+ visible: false
2162+ }
2163+
2164+ UbuntuTestWebView {
2165+ id: incognitoWebView
2166+ objectName: "incognitoWebView"
2167+ anchors.fill: parent
2168+ incognito: true
2169+ visible: false
2170+ }
2171+
2172+ Component {
2173+ id: webViewFactory
2174+ UbuntuTestWebView {
2175+ objectName: "webView2"
2176+ anchors.fill: parent
2177+ }
2178+ }
2179+
2180+ TestCase {
2181+ name: "WebViewAutofillPopup_single_fields"
2182+ when: windowShown
2183+
2184+ function loadForm(webView) {
2185+ webView.visible = true;
2186+ webView.focus = true;
2187+ webView.context.autofillEnabled = true;
2188+ webView.url =
2189+ "http://testsuite/tst_WebViewAutofillPopup_single_fields.html";
2190+ verify(webView.waitForLoadSucceeded());
2191+ }
2192+
2193+ function verify_field_focused(webView, field) {
2194+ verify(TestUtils.waitFor(function() {
2195+ return webView.getTestApi().evaluateCode(
2196+ "document.activeElement.id") == field; }));
2197+ }
2198+
2199+ function compare_field_value(webView, field, value) {
2200+ verify(TestUtils.waitFor(function() {
2201+ return webView.getTestApi().evaluateCode(
2202+ "document.querySelector('#%1').value".arg(field)) == value; }));
2203+ }
2204+
2205+ function typeStringInField(webView, string, field) {
2206+ verify_field_focused(webView, field);
2207+ for (var i = 0; i < string.length; ++i) {
2208+ keyClick(string[i]);
2209+ }
2210+ compare_field_value(webView, field, string);
2211+ }
2212+
2213+ function initTestCase() {
2214+ loadForm(webView);
2215+ // Initial population of the autofill database
2216+ var data = ["foo", "for", "four"];
2217+ for (var i = 0; i < data.length; ++i) {
2218+ webView.url =
2219+ "http://testsuite/tst_WebViewAutofillPopup_single_fields.html";
2220+ verify(webView.waitForLoadSucceeded());
2221+ typeStringInField(webView, data[i], "field1");
2222+ keyClick(Qt.Key_Enter);
2223+ verify(webView.waitForLoadSucceeded());
2224+ }
2225+ }
2226+
2227+ function getAutofillPopup(webView) {
2228+ return TestSupport.findItemInScene(
2229+ TestWindow.rootItem, "%1_WebAutofillPopup".arg(webView.objectName));
2230+ }
2231+
2232+ function waitForAutofillPopupToClose(webView) {
2233+ var popup = getAutofillPopup(webView);
2234+ if (!popup) {
2235+ return true;
2236+ }
2237+
2238+ var helper = TestSupport.createQObjectTestHelper(popup);
2239+
2240+ return TestUtils.waitFor(function() {
2241+ return !getAutofillPopup(webView); }) &&
2242+ TestUtils.waitFor(function() { return helper.destroyed; });
2243+ }
2244+
2245+ function waitForAutofillPopup(webView, timeout) {
2246+ return TestUtils.waitFor(function() {
2247+ return !!getAutofillPopup(webView) &&
2248+ getAutofillPopup(webView).visible; },
2249+ timeout);
2250+ }
2251+
2252+ function getSuggestion(webView, index) {
2253+ return TestSupport.findItemInScene(
2254+ TestWindow.rootItem,
2255+ "%1_WebAutofillPopup_item%2".arg(webView.objectName).arg(index));
2256+ }
2257+
2258+ function compareBoundsToClientRect(bounds, rect) {
2259+ compare(bounds.x, rect.x);
2260+ compare(bounds.y, rect.y);
2261+ compare(bounds.width, rect.width);
2262+ compare(bounds.height, rect.height);
2263+ }
2264+
2265+ function init() {
2266+ loadForm(webView);
2267+ }
2268+
2269+ function get_webviews_data() {
2270+ var data = [ { webView: webView } ];
2271+ if (incognitoWebView.incognito) {
2272+ // When run in single process mode, incognito cannot be set
2273+ data.push({ webView: incognitoWebView });
2274+ }
2275+ return data;
2276+ }
2277+
2278+ function test_initial_state_data() {
2279+ return get_webviews_data();
2280+ }
2281+
2282+ function test_initial_state(data) {
2283+ loadForm(data.webView);
2284+ typeStringInField(data.webView, "f", "field1");
2285+ verify(waitForAutofillPopup(data.webView));
2286+ var popup = getAutofillPopup(data.webView);
2287+ var r =
2288+ data.webView.getTestApi().getBoundingClientRectForSelector("#field1");
2289+ compareBoundsToClientRect(popup.elementBounds, r);
2290+ compare(popup.currentIndex, -1);
2291+ compare(popup.count, 3);
2292+ compare(getSuggestion(data.webView, 0).value, "foo");
2293+ compare(getSuggestion(data.webView, 1).value, "for");
2294+ compare(getSuggestion(data.webView, 2).value, "four");
2295+ data.webView.visible = false;
2296+ }
2297+
2298+ function test_destroy_on_webview_close() {
2299+ var webView2 = webViewFactory.createObject(testRoot);
2300+ loadForm(webView2);
2301+ typeStringInField(webView2, "f", "field1");
2302+
2303+ verify(TestUtils.waitFor(function() {
2304+ return !!getAutofillPopup(webView2) &&
2305+ getAutofillPopup(webView2).visible; }));
2306+ var popup = getAutofillPopup(webView2);
2307+ var helper = TestSupport.createQObjectTestHelper(popup);
2308+
2309+ webView2.destroy();
2310+ verify(TestUtils.waitFor(function() { return helper.destroyed; }));
2311+ }
2312+
2313+ function test_down_arrow_key_navigation_data() {
2314+ return get_webviews_data();
2315+ }
2316+
2317+ function test_down_arrow_key_navigation(data) {
2318+ loadForm(data.webView);
2319+ typeStringInField(data.webView, "f", "field1");
2320+ verify(waitForAutofillPopup(data.webView));
2321+ var popup = getAutofillPopup(data.webView);
2322+ keyClick(Qt.Key_Down);
2323+ compare(popup.currentIndex, 0);
2324+ keyClick(Qt.Key_Down);
2325+ compare(popup.currentIndex, 1);
2326+ keyClick(Qt.Key_Down);
2327+ compare(popup.currentIndex, 2);
2328+ keyClick(Qt.Key_Down);
2329+ compare(popup.currentIndex, 0);
2330+ data.webView.visible = false;
2331+ }
2332+
2333+ function test_up_arrow_key_navigation_data() {
2334+ return get_webviews_data();
2335+ }
2336+
2337+ function test_up_arrow_key_navigation(data) {
2338+ loadForm(data.webView);
2339+ typeStringInField(data.webView, "f", "field1");
2340+ verify(waitForAutofillPopup(data.webView));
2341+ var popup = getAutofillPopup(data.webView);
2342+ keyClick(Qt.Key_Up);
2343+ compare(popup.currentIndex, 2);
2344+ keyClick(Qt.Key_Up);
2345+ compare(popup.currentIndex, 1);
2346+ keyClick(Qt.Key_Up);
2347+ compare(popup.currentIndex, 0);
2348+ keyClick(Qt.Key_Up);
2349+ compare(popup.currentIndex, 2);
2350+ data.webView.visible = false;
2351+ }
2352+
2353+ function test_escape_dismisses_popup_data() {
2354+ return get_webviews_data();
2355+ }
2356+
2357+ function test_escape_dismisses_popup(data) {
2358+ loadForm(data.webView);
2359+ typeStringInField(data.webView, "f", "field1");
2360+ verify(waitForAutofillPopup(data.webView));
2361+ keyClick(Qt.Key_Escape);
2362+ verify(waitForAutofillPopupToClose(data.webView));
2363+ data.webView.visible = false;
2364+ }
2365+
2366+ function test_escape_dismisses_popup_and_resets_field_value_data() {
2367+ return get_webviews_data();
2368+ }
2369+
2370+ function test_escape_dismisses_popup_and_resets_field_value(data) {
2371+ loadForm(data.webView);
2372+ typeStringInField(data.webView, "f", "field1");
2373+ verify(waitForAutofillPopup(data.webView));
2374+ var popup = getAutofillPopup(data.webView);
2375+ keyClick(Qt.Key_Down);
2376+ compare(popup.currentIndex, 0);
2377+ keyClick(Qt.Key_Escape);
2378+ verify(waitForAutofillPopupToClose(data.webView));
2379+ compare_field_value(data.webView, "field1", "f");
2380+ data.webView.visible = false;
2381+ }
2382+
2383+ function test_validate_suggestion_with_mouse_data() {
2384+ return get_webviews_data();
2385+ }
2386+
2387+ function test_validate_suggestion_with_mouse(data) {
2388+ loadForm(data.webView);
2389+ typeStringInField(data.webView, "f", "field1");
2390+ verify(waitForAutofillPopup(data.webView));
2391+ var popup = getAutofillPopup(data.webView);
2392+ var item = getSuggestion(data.webView, 1);
2393+ mouseClick(item);
2394+ verify(waitForAutofillPopupToClose(data.webView));
2395+ verify_field_focused(data.webView, "field1");
2396+ compare_field_value(data.webView, "field1", "for");
2397+ data.webView.visible = false;
2398+ }
2399+
2400+ function test_validate_suggestion_with_keyboard_data() {
2401+ return [
2402+ { webView: webView, key: Qt.Key_Enter, focus: "field1" },
2403+ { webView: webView, key: Qt.Key_Return, focus: "field1" },
2404+ { webView: webView, key: Qt.Key_Tab, focus: "field2" },
2405+ { webView: incognitoWebView, key: Qt.Key_Enter, focus: "field1" },
2406+ { webView: incognitoWebView, key: Qt.Key_Return, focus: "field1" },
2407+ { webView: incognitoWebView, key: Qt.Key_Tab, focus: "field2" },
2408+ ];
2409+ }
2410+
2411+ function test_validate_suggestion_with_keyboard(data) {
2412+ loadForm(data.webView);
2413+ typeStringInField(data.webView, "f", "field1");
2414+ verify(waitForAutofillPopup(data.webView));
2415+ var popup = getAutofillPopup(data.webView);
2416+ keyClick(Qt.Key_Down);
2417+ compare(popup.currentIndex, 0);
2418+ keyClick(data.key);
2419+ verify(waitForAutofillPopupToClose(data.webView));
2420+ verify_field_focused(data.webView, data.focus);
2421+ compare_field_value(data.webView, "field1", "foo");
2422+ data.webView.visible = false;
2423+ }
2424+
2425+ function test_shiftdel_deletes_suggestion() {
2426+ typeStringInField(webView, "f", "field1");
2427+ verify(waitForAutofillPopup(webView));
2428+ keyClick("l");
2429+ verify(waitForAutofillPopupToClose(webView));
2430+ keyClick("i");
2431+ keyClick("p");
2432+ compare_field_value(webView, "field1", "flip");
2433+ keyClick(Qt.Key_Enter);
2434+ verify(webView.waitForLoadSucceeded());
2435+ webView.url =
2436+ "http://testsuite/tst_WebViewAutofillPopup_single_fields.html";
2437+ verify(webView.waitForLoadSucceeded());
2438+ typeStringInField(webView, "f", "field1");
2439+ verify(waitForAutofillPopup(webView));
2440+ var popup = getAutofillPopup(webView);
2441+ compare(popup.count, 4);
2442+ compare(getSuggestion(webView, 0).value, "flip");
2443+ keyClick(Qt.Key_Down);
2444+ compare(popup.currentIndex, 0);
2445+ keyClick(Qt.Key_Delete, Qt.ShiftModifier);
2446+ tryCompare(popup, "currentIndex", -1);
2447+ tryCompare(popup, "count", 3);
2448+ }
2449+
2450+ function test_tab_no_suggestion_data() {
2451+ return get_webviews_data();
2452+ }
2453+
2454+ function test_tab_no_suggestion(data) {
2455+ loadForm(data.webView);
2456+ typeStringInField(data.webView, "f", "field1");
2457+ verify(waitForAutofillPopup(data.webView));
2458+ var popup = getAutofillPopup(data.webView);
2459+ compare(popup.currentIndex, -1);
2460+ verify_field_focused(data.webView, "field1");
2461+ keyClick(Qt.Key_Tab);
2462+ verify(waitForAutofillPopupToClose(data.webView));
2463+ verify_field_focused(data.webView, "field2");
2464+ data.webView.visible = false;
2465+ }
2466+
2467+ function test_incognito_data_not_saved() {
2468+ if (!incognitoWebView.incognito) {
2469+ skip("WebView.incognito cannot be set in single process mode");
2470+ }
2471+ loadForm(incognitoWebView);
2472+ typeStringInField(incognitoWebView, "bar", "field1");
2473+ keyClick(Qt.Key_Enter);
2474+ verify(incognitoWebView.waitForLoadSucceeded());
2475+ loadForm(incognitoWebView);
2476+ typeStringInField(incognitoWebView, "b", "field1");
2477+ verify(!waitForAutofillPopup(incognitoWebView, 1000));
2478+ incognitoWebView.visible = false;
2479+ }
2480+
2481+ function test_zlast_validate_no_suggestion_data() {
2482+ return [
2483+ { key: Qt.Key_Enter },
2484+ { key: Qt.Key_Return },
2485+ ];
2486+ }
2487+
2488+ // This test needs to be run last as it adds a suggestion that is
2489+ // not removed afterwards, thus breaking other tests' assumptions
2490+ function test_zlast_validate_no_suggestion(data) {
2491+ typeStringInField(webView, "f", "field1");
2492+ verify(waitForAutofillPopup(webView));
2493+ var popup = getAutofillPopup(webView);
2494+ compare(popup.currentIndex, -1);
2495+ keyClick(data.key);
2496+ verify(waitForAutofillPopupToClose(webView));
2497+ verify(webView.waitForLoadSucceeded());
2498+ }
2499+ }
2500+}
2501diff --git a/qt/uitk/lib/CMakeLists.txt b/qt/uitk/lib/CMakeLists.txt
2502index 9e76ebe..8f73100 100644
2503--- a/qt/uitk/lib/CMakeLists.txt
2504+++ b/qt/uitk/lib/CMakeLists.txt
2505@@ -1,6 +1,6 @@
2506 # vim:expandtab:shiftwidth=2:tabstop=2:
2507
2508-# Copyright (C) 2016 Canonical Ltd.
2509+# Copyright (C) 2016-2017 Canonical Ltd.
2510
2511 # This library is free software; you can redistribute it and/or
2512 # modify it under the terms of the GNU Lesser General Public
2513@@ -27,6 +27,7 @@ set(OXIDE_UITKLIB_SRCS
2514 uitk_javascript_dialog.cc
2515 uitk_touch_editing_menu.cc
2516 uitk_touch_handle_drawable.cc
2517+ uitk_web_autofill_popup.cc
2518 uitk_web_context_menu.cc
2519 uitk_web_popup_menu.cc)
2520
2521diff --git a/qt/uitk/lib/resources.qrc b/qt/uitk/lib/resources.qrc
2522index f2cf4ba..83f258a 100644
2523--- a/qt/uitk/lib/resources.qrc
2524+++ b/qt/uitk/lib/resources.qrc
2525@@ -6,6 +6,7 @@
2526 <file alias="TouchEditingMenu.qml">resources/TouchEditingMenu.qml</file>
2527 <file alias="TouchHandle@27.png">resources/TouchHandle@27.png</file>
2528 <file alias="TouchHandle.qml">resources/TouchHandle.qml</file>
2529+ <file alias="WebAutofillPopup.qml">resources/WebAutofillPopup.qml</file>
2530 <file alias="WebContextMenuMobile.qml">resources/WebContextMenuMobile.qml</file>
2531 <file alias="WebContextMenuDesktop.qml">resources/WebContextMenuDesktop.qml</file>
2532 <file alias="WebPopupMenu.qml">resources/WebPopupMenu.qml</file>
2533diff --git a/qt/uitk/lib/resources/WebAutofillPopup.qml b/qt/uitk/lib/resources/WebAutofillPopup.qml
2534new file mode 100644
2535index 0000000..848cf0e
2536--- /dev/null
2537+++ b/qt/uitk/lib/resources/WebAutofillPopup.qml
2538@@ -0,0 +1,139 @@
2539+// vim:expandtab:shiftwidth=2:tabstop=2:
2540+// Copyright (C) 2017 Canonical Ltd.
2541+
2542+// This library is free software; you can redistribute it and/or
2543+// modify it under the terms of the GNU Lesser General Public
2544+// License as published by the Free Software Foundation; either
2545+// version 2.1 of the License, or (at your option) any later version.
2546+
2547+// This library is distributed in the hope that it will be useful,
2548+// but WITHOUT ANY WARRANTY; without even the implied warranty of
2549+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2550+// Lesser General Public License for more details.
2551+
2552+// You should have received a copy of the GNU Lesser General Public
2553+// License along with this library; if not, write to the Free Software
2554+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2555+
2556+import QtQuick 2.4
2557+import Ubuntu.Components 1.3
2558+
2559+FocusScope {
2560+ objectName: parent && parent.objectName ? parent.objectName + "_WebAutofillPopup" : ""
2561+
2562+ property alias model: listview.model
2563+ property rect elementBounds
2564+ readonly property int currentIndex: listview.currentIndex
2565+ readonly property int count: listview.count
2566+
2567+ signal close()
2568+ signal acceptedSuggestion(int index)
2569+ signal removeSuggestion(int index)
2570+
2571+ focus: true
2572+ clip: true
2573+
2574+ x: elementBounds.x
2575+ y: elementBounds.y + elementBounds.height
2576+ width: elementBounds.width
2577+ readonly property real maxHeight: parent ? parent.height - elementBounds.y - elementBounds.height : 0
2578+ height: Math.min(maxHeight, listview.count * listview.itemHeight)
2579+
2580+ Rectangle {
2581+ anchors.fill: parent
2582+ color: theme.palette.normal.overlay
2583+ function newColorWithAlpha(color, alpha) {
2584+ return Qt.rgba(color.r, color.g, color.b, alpha);
2585+ }
2586+ border {
2587+ width: units.dp(1)
2588+ color: newColorWithAlpha(theme.palette.normal.base, 0.4)
2589+ }
2590+ }
2591+
2592+ MouseArea {
2593+ anchors.fill: parent
2594+ hoverEnabled: true
2595+ onPositionChanged: listview.currentIndex = listview.indexAt(mouse.x, mouse.y)
2596+ onExited: listview.currentIndex = -1
2597+ }
2598+
2599+ ListView {
2600+ id: listview
2601+
2602+ property real itemHeight: 0
2603+ anchors.fill: parent
2604+
2605+ delegate: ListItem {
2606+ objectName: "%1_item%2".arg(listview.parent.objectName).arg(index)
2607+ readonly property string value: model.value
2608+ readonly property string label: model.label
2609+ height: units.gu(5)
2610+ ListItemLayout {
2611+ anchors.verticalCenter: parent.verticalCenter
2612+ title {
2613+ text: model.value
2614+ textSize: Label.Small
2615+ }
2616+ subtitle {
2617+ text: model.label
2618+ textSize: Label.XSmall
2619+ }
2620+ }
2621+ color: ListView.isCurrentItem ? theme.palette.normal.focus : "transparent"
2622+ Component.onCompleted: listview.itemHeight = height
2623+
2624+ onClicked: acceptedSuggestion(index)
2625+
2626+ property bool removalRequested: false
2627+ Keys.onDeletePressed: {
2628+ if (event.modifiers & Qt.ShiftModifier) {
2629+ removalRequested = true;
2630+ removeSuggestion(index);
2631+ } else {
2632+ event.accepted = false;
2633+ }
2634+ }
2635+ ListView.onRemove: {
2636+ if (removalRequested) {
2637+ listview.currentIndex = -1;
2638+ }
2639+ }
2640+ }
2641+
2642+ Keys.onEnterPressed: {
2643+ if (currentItem) {
2644+ currentItem.clicked();
2645+ } else {
2646+ event.accepted = false;
2647+ }
2648+ }
2649+ Keys.onReturnPressed: {
2650+ if (currentItem) {
2651+ currentItem.clicked();
2652+ } else {
2653+ event.accepted = false;
2654+ }
2655+ }
2656+ Keys.onTabPressed: {
2657+ event.accepted = false;
2658+ if (currentItem) {
2659+ currentItem.clicked();
2660+ }
2661+ }
2662+
2663+ keyNavigationWraps: true
2664+ currentIndex: -1
2665+
2666+ Component.onCompleted: {
2667+ // Work around the UITK ListViewProxy extension that forces
2668+ // activeFocusOnTab, thus breaking tab key propagation. Setting
2669+ // activeFocusOnTab to false needs to be done before the item is the
2670+ // active focus item.
2671+ activeFocusOnTab = false;
2672+ focus = true;
2673+ }
2674+ }
2675+
2676+ Keys.onEscapePressed: close()
2677+}
2678diff --git a/qt/uitk/lib/uitk_auxiliary_ui_factory.cc b/qt/uitk/lib/uitk_auxiliary_ui_factory.cc
2679index e6f64fb..48731e0 100644
2680--- a/qt/uitk/lib/uitk_auxiliary_ui_factory.cc
2681+++ b/qt/uitk/lib/uitk_auxiliary_ui_factory.cc
2682@@ -1,5 +1,5 @@
2683 // vim:expandtab:shiftwidth=2:tabstop=2:
2684-// Copyright (C) 2016 Canonical Ltd.
2685+// Copyright (C) 2016-2017 Canonical Ltd.
2686
2687 // This library is free software; you can redistribute it and/or
2688 // modify it under the terms of the GNU Lesser General Public
2689@@ -38,6 +38,7 @@
2690 #include "uitk_before_unload_dialog.h"
2691 #include "uitk_javascript_dialog.h"
2692 #include "uitk_touch_editing_menu.h"
2693+#include "uitk_web_autofill_popup.h"
2694 #include "uitk_web_context_menu.h"
2695
2696 static void InitResources() {
2697@@ -47,11 +48,13 @@ static void InitResources() {
2698 namespace oxide {
2699 namespace uitk {
2700
2701+using qt::AutofillSuggestion;
2702 using qt::EditCapabilityFlags;
2703 using qt::GetScreenFormFactor;
2704 using qt::MenuItem;
2705 using qt::ScreenFormFactor;
2706 using qt::TouchEditingMenuClient;
2707+using qt::WebAutofillPopupClient;
2708 using qt::WebContextMenuAction;
2709 using qt::WebContextMenuClient;
2710 using qt::WebContextMenuParams;
2711@@ -230,6 +233,14 @@ std::unique_ptr<qt::WebContextMenu> AuxiliaryUIFactory::CreateWebContextMenu(
2712 IsItemOnMobileScreen(item_));
2713 }
2714
2715+std::unique_ptr<qt::WebAutofillPopup>
2716+AuxiliaryUIFactory::CreateWebAutofillPopup(
2717+ const std::vector<AutofillSuggestion>& suggestions,
2718+ const QRectF& bounds,
2719+ WebAutofillPopupClient* client) {
2720+ return WebAutofillPopup::Create(item_, suggestions, bounds, client);
2721+}
2722+
2723 std::unique_ptr<qt::TouchEditingMenu>
2724 AuxiliaryUIFactory::CreateTouchEditingMenu(
2725 EditCapabilityFlags edit_flags,
2726diff --git a/qt/uitk/lib/uitk_auxiliary_ui_factory.h b/qt/uitk/lib/uitk_auxiliary_ui_factory.h
2727index 3d169e4..d0ae8e7 100644
2728--- a/qt/uitk/lib/uitk_auxiliary_ui_factory.h
2729+++ b/qt/uitk/lib/uitk_auxiliary_ui_factory.h
2730@@ -1,5 +1,5 @@
2731 // vim:expandtab:shiftwidth=2:tabstop=2:
2732-// Copyright (C) 2016 Canonical Ltd.
2733+// Copyright (C) 2016-2017 Canonical Ltd.
2734
2735 // This library is free software; you can redistribute it and/or
2736 // modify it under the terms of the GNU Lesser General Public
2737@@ -56,6 +56,10 @@ class AuxiliaryUIFactory : public qt::AuxiliaryUIFactory {
2738 const qt::WebContextMenuParams& params,
2739 const std::vector<qt::MenuItem>& items,
2740 qt::WebContextMenuClient* client) override;
2741+ std::unique_ptr<qt::WebAutofillPopup> CreateWebAutofillPopup(
2742+ const std::vector<qt::AutofillSuggestion>& suggestions,
2743+ const QRectF& bounds,
2744+ qt::WebAutofillPopupClient* client) override;
2745 std::unique_ptr<qt::TouchEditingMenu> CreateTouchEditingMenu(
2746 qt::EditCapabilityFlags edit_flags,
2747 qt::TouchEditingMenuClient* client) override;
2748diff --git a/qt/uitk/lib/uitk_web_autofill_popup.cc b/qt/uitk/lib/uitk_web_autofill_popup.cc
2749new file mode 100644
2750index 0000000..4085608
2751--- /dev/null
2752+++ b/qt/uitk/lib/uitk_web_autofill_popup.cc
2753@@ -0,0 +1,297 @@
2754+// vim:expandtab:shiftwidth=2:tabstop=2:
2755+// Copyright (C) 2017 Canonical Ltd.
2756+
2757+// This library is free software; you can redistribute it and/or
2758+// modify it under the terms of the GNU Lesser General Public
2759+// License as published by the Free Software Foundation; either
2760+// version 2.1 of the License, or (at your option) any later version.
2761+
2762+// This library is distributed in the hope that it will be useful,
2763+// but WITHOUT ANY WARRANTY; without even the implied warranty of
2764+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2765+// Lesser General Public License for more details.
2766+
2767+// You should have received a copy of the GNU Lesser General Public
2768+// License along with this library; if not, write to the Free Software
2769+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2770+
2771+#include "uitk_web_autofill_popup.h"
2772+
2773+#include <QAbstractListModel>
2774+#include <QMetaObject>
2775+#include <QQmlComponent>
2776+#include <QQmlEngine>
2777+#include <QQmlError>
2778+#include <QQmlProperty>
2779+#include <QQuickItem>
2780+#include <QtDebug>
2781+#include <QVariant>
2782+#include <QVector>
2783+
2784+#include "qt/core/glue/autofill_suggestion.h"
2785+#include "qt/core/glue/web_autofill_popup_client.h"
2786+
2787+namespace oxide {
2788+namespace uitk {
2789+
2790+using qt::AutofillSuggestion;
2791+
2792+class WebAutofillPopup::Model : public QAbstractListModel {
2793+ public:
2794+ Model(const std::vector<AutofillSuggestion>& suggestions);
2795+ ~Model() override;
2796+
2797+ enum Role {
2798+ RoleType = Qt::UserRole,
2799+ RoleValue,
2800+ RoleLabel,
2801+ RoleIcon
2802+ };
2803+
2804+ // QAbstractItemModel implementation
2805+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
2806+ QVariant data(const QModelIndex& index,
2807+ int role = Qt::DisplayRole) const override;
2808+ QHash<int, QByteArray> roleNames() const override;
2809+
2810+ protected:
2811+ friend class WebAutofillPopup;
2812+ void updateDataListValues(const std::vector<AutofillSuggestion>& suggestions);
2813+ void removeSuggestion(int index);
2814+
2815+ QVector<AutofillSuggestion> suggestions_;
2816+};
2817+
2818+WebAutofillPopup::Model::Model(
2819+ const std::vector<AutofillSuggestion>& suggestions) {
2820+ suggestions_ = QVector<AutofillSuggestion>::fromStdVector(suggestions);
2821+}
2822+
2823+WebAutofillPopup::Model::~Model() = default;
2824+
2825+int WebAutofillPopup::Model::rowCount(const QModelIndex& parent) const {
2826+ return suggestions_.size();
2827+}
2828+
2829+QVariant WebAutofillPopup::Model::data(const QModelIndex& index,
2830+ int role) const {
2831+ Q_ASSERT(index.isValid());
2832+ Q_ASSERT(index.row() >= 0 && index.row() < suggestions_.size());
2833+ Q_ASSERT(index.column() == 0);
2834+ Q_ASSERT(!index.parent().isValid());
2835+
2836+ const AutofillSuggestion& suggestion = suggestions_[index.row()];
2837+
2838+ switch (role) {
2839+ case RoleType: {
2840+ switch (suggestion.type) {
2841+ case AutofillSuggestion::Type::ProfileEntry:
2842+ return QStringLiteral("profile");
2843+ case AutofillSuggestion::Type::AutoCompleteEntry:
2844+ return QStringLiteral("autocomplete");
2845+ case AutofillSuggestion::Type::PasswordEntry:
2846+ return QStringLiteral("password");
2847+ case AutofillSuggestion::Type::DataListEntry:
2848+ return QStringLiteral("datalist");
2849+ default:
2850+ Q_UNREACHABLE();
2851+ }
2852+ }
2853+ case RoleValue:
2854+ return suggestion.value;
2855+ case RoleLabel:
2856+ return suggestion.label;
2857+ case RoleIcon:
2858+ return suggestion.icon;
2859+ default:
2860+ Q_UNREACHABLE();
2861+ }
2862+}
2863+
2864+QHash<int, QByteArray> WebAutofillPopup::Model::roleNames() const {
2865+ static QHash<int, QByteArray> roles;
2866+
2867+ if (roles.size() > 0) {
2868+ return roles;
2869+ }
2870+
2871+ roles[RoleType] = "type";
2872+ roles[RoleValue] = "value";
2873+ roles[RoleLabel] = "label";
2874+ roles[RoleIcon] = "icon";
2875+
2876+ return roles;
2877+}
2878+
2879+void WebAutofillPopup::Model::updateDataListValues(
2880+ const std::vector<AutofillSuggestion>& suggestions) {
2881+ // Remove all the old data list values, which should always be at the top of
2882+ // the list if they are present.
2883+ if (!suggestions_.isEmpty()) {
2884+ int i = suggestions_.size() - 1;
2885+ while (i >= 0 &&
2886+ suggestions_[i--].type != AutofillSuggestion::Type::DataListEntry);
2887+ if (i >= 0) {
2888+ beginRemoveRows(QModelIndex(), 0, i);
2889+ suggestions_.remove(0, i + 1);
2890+ endRemoveRows();
2891+ }
2892+ }
2893+
2894+ // Prepend the new data list values to the suggestions we already have.
2895+ beginInsertRows(QModelIndex(), 0, suggestions.size() - 1);
2896+ suggestions_ =
2897+ QVector<AutofillSuggestion>::fromStdVector(suggestions) + suggestions_;
2898+ endInsertRows();
2899+}
2900+
2901+void WebAutofillPopup::Model::removeSuggestion(int index) {
2902+ beginRemoveRows(QModelIndex(), index, index);
2903+ suggestions_.remove(index);
2904+ endRemoveRows();
2905+}
2906+
2907+void WebAutofillPopup::Show() {
2908+ item_->setVisible(true);
2909+}
2910+
2911+void WebAutofillPopup::UpdateDataListValues(
2912+ const std::vector<AutofillSuggestion>& suggestions) {
2913+ model_->updateDataListValues(suggestions);
2914+}
2915+
2916+void WebAutofillPopup::Hide() {
2917+ item_->setVisible(false);
2918+}
2919+
2920+WebAutofillPopup::WebAutofillPopup(
2921+ const std::vector<AutofillSuggestion>& suggestions,
2922+ const QRectF& bounds,
2923+ qt::WebAutofillPopupClient* client)
2924+ : model_(new Model(suggestions)),
2925+ bounds_(bounds),
2926+ client_(client) {}
2927+
2928+bool WebAutofillPopup::Init(QQuickItem* parent) {
2929+ QQmlEngine* engine = qmlEngine(parent);
2930+ if (!engine) {
2931+ qWarning() <<
2932+ "uitk::WebAutofillPopup: Failed to create autofill popup - "
2933+ "cannot determine QQmlEngine for parent item";
2934+ return false;
2935+ }
2936+
2937+ QQmlComponent component(engine);
2938+ component.loadUrl(QUrl("qrc:///WebAutofillPopup.qml"));
2939+
2940+ if (component.isError()) {
2941+ qCritical() <<
2942+ "uitk::WebAutofillPopup: Failed to initialize autofill popup component "
2943+ "because of the following errors: ";
2944+ for (const auto& error : component.errors()) {
2945+ qCritical() << error;
2946+ }
2947+ return false;
2948+ }
2949+
2950+ Q_ASSERT(component.isReady());
2951+
2952+ QObject* popup = component.beginCreate(engine->rootContext());
2953+ if (!popup) {
2954+ qCritical() <<
2955+ "uitk::WebAutofillPopup: Failed to create autofill popup instance";
2956+ return false;
2957+ }
2958+
2959+ item_.reset(qobject_cast<QQuickItem*>(popup));
2960+ if (!item_) {
2961+ qCritical() <<
2962+ "uitk::WebAutofillPopup: Autofill popup instance is not a QQuickItem";
2963+ delete popup;
2964+ return false;
2965+ }
2966+
2967+ item_->setProperty("model", QVariant::fromValue(model_.get()));
2968+ item_->setProperty("elementBounds", bounds_);
2969+ item_->setParentItem(parent);
2970+
2971+ component.completeCreate();
2972+
2973+ connect(item_.get(), SIGNAL(close()), this, SLOT(OnCloseRequested()));
2974+ connect(item_.get(), SIGNAL(currentIndexChanged()),
2975+ this, SLOT(OnSuggestionSelected()));
2976+ connect(item_.get(), SIGNAL(acceptedSuggestion(int)),
2977+ this, SLOT(OnSuggestionAccepted(int)));
2978+ connect(item_.get(), SIGNAL(removeSuggestion(int)),
2979+ this, SLOT(OnSuggestionRemovalRequested(int)));
2980+
2981+ return true;
2982+}
2983+
2984+void WebAutofillPopup::OnCloseRequested() {
2985+ client_->Close();
2986+}
2987+
2988+void WebAutofillPopup::OnSuggestionSelected() {
2989+ int index =
2990+ QQmlProperty(item_.get(), QStringLiteral("currentIndex")).read().toInt();
2991+
2992+ if (index == -1) {
2993+ client_->ClearSelection();
2994+ return;
2995+ }
2996+
2997+ if (index < 0 || index >= model_->rowCount()) {
2998+ qWarning() << "uitk::WebAutofillPopup: invalid index";
2999+ return;
3000+ }
3001+
3002+ const AutofillSuggestion& suggestion = model_->suggestions_[index];
3003+ client_->SelectSuggestion(suggestion);
3004+}
3005+
3006+void WebAutofillPopup::OnSuggestionAccepted(int index) {
3007+ if (index < 0 || index >= model_->rowCount()) {
3008+ qWarning() << "uitk::WebAutofillPopup: invalid index";
3009+ return;
3010+ }
3011+
3012+ const AutofillSuggestion& suggestion = model_->suggestions_[index];
3013+ client_->AcceptSuggestion(suggestion);
3014+}
3015+
3016+void WebAutofillPopup::OnSuggestionRemovalRequested(int index) {
3017+ if (index < 0 || index >= model_->rowCount()) {
3018+ qWarning() << "uitk::WebAutofillPopup: invalid index";
3019+ return;
3020+ }
3021+
3022+ const AutofillSuggestion& suggestion = model_->suggestions_[index];
3023+ if (client_->RemoveSuggestion(suggestion)) {
3024+ model_->removeSuggestion(index);
3025+ }
3026+}
3027+
3028+// static
3029+std::unique_ptr<WebAutofillPopup> WebAutofillPopup::Create(
3030+ QQuickItem* parent,
3031+ const std::vector<AutofillSuggestion>& suggestions,
3032+ const QRectF& bounds,
3033+ qt::WebAutofillPopupClient* client) {
3034+ if (suggestions.empty()) {
3035+ return nullptr;
3036+ }
3037+
3038+ std::unique_ptr<WebAutofillPopup> popup(
3039+ new WebAutofillPopup(suggestions, bounds, client));
3040+ if (!popup->Init(parent)) {
3041+ return nullptr;
3042+ }
3043+
3044+ return std::move(popup);
3045+}
3046+
3047+WebAutofillPopup::~WebAutofillPopup() = default;
3048+
3049+} // namespace uitk
3050+} // namespace oxide
3051diff --git a/qt/uitk/lib/uitk_web_autofill_popup.h b/qt/uitk/lib/uitk_web_autofill_popup.h
3052new file mode 100644
3053index 0000000..4dfbf38
3054--- /dev/null
3055+++ b/qt/uitk/lib/uitk_web_autofill_popup.h
3056@@ -0,0 +1,87 @@
3057+// vim:expandtab:shiftwidth=2:tabstop=2:
3058+// Copyright (C) 2017 Canonical Ltd.
3059+
3060+// This library is free software; you can redistribute it and/or
3061+// modify it under the terms of the GNU Lesser General Public
3062+// License as published by the Free Software Foundation; either
3063+// version 2.1 of the License, or (at your option) any later version.
3064+
3065+// This library is distributed in the hope that it will be useful,
3066+// but WITHOUT ANY WARRANTY; without even the implied warranty of
3067+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3068+// Lesser General Public License for more details.
3069+
3070+// You should have received a copy of the GNU Lesser General Public
3071+// License along with this library; if not, write to the Free Software
3072+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
3073+
3074+#ifndef _OXIDE_UITK_LIB_WEB_AUTOFILL_POPUP_H_
3075+#define _OXIDE_UITK_LIB_WEB_AUTOFILL_POPUP_H_
3076+
3077+#include "qt/core/glue/web_autofill_popup.h"
3078+
3079+#include <memory>
3080+#include <vector>
3081+
3082+#include <QObject>
3083+#include <QRectF>
3084+#include <QtGlobal>
3085+
3086+QT_BEGIN_NAMESPACE
3087+class QQuickItem;
3088+QT_END_NAMESPACE
3089+
3090+namespace oxide {
3091+
3092+namespace qt {
3093+struct AutofillSuggestion;
3094+class WebAutofillPopupClient;
3095+}
3096+
3097+namespace uitk {
3098+
3099+class WebAutofillPopup : public QObject,
3100+ public qt::WebAutofillPopup {
3101+ Q_OBJECT
3102+ Q_DISABLE_COPY(WebAutofillPopup)
3103+
3104+ public:
3105+ static std::unique_ptr<WebAutofillPopup> Create(
3106+ QQuickItem* parent,
3107+ const std::vector<qt::AutofillSuggestion>& suggestions,
3108+ const QRectF& bounds,
3109+ qt::WebAutofillPopupClient* client);
3110+ ~WebAutofillPopup() override;
3111+
3112+ private Q_SLOTS:
3113+ void OnCloseRequested();
3114+ void OnSuggestionSelected();
3115+ void OnSuggestionAccepted(int index);
3116+ void OnSuggestionRemovalRequested(int index);
3117+
3118+ private:
3119+ WebAutofillPopup(const std::vector<qt::AutofillSuggestion>& suggestions,
3120+ const QRectF& bounds,
3121+ qt::WebAutofillPopupClient* client);
3122+ bool Init(QQuickItem* parent);
3123+
3124+ // qt::WebAutofillPopup implementation
3125+ void Show() override;
3126+ void UpdateDataListValues(
3127+ const std::vector<qt::AutofillSuggestion>& suggestions) override;
3128+ void Hide() override;
3129+
3130+ class Model;
3131+ std::unique_ptr<Model> model_;
3132+
3133+ QRectF bounds_;
3134+
3135+ qt::WebAutofillPopupClient* client_;
3136+
3137+ std::unique_ptr<QQuickItem> item_;
3138+};
3139+
3140+} // namespace uitk
3141+} // namespace oxide
3142+
3143+#endif // _OXIDE_UITK_LIB_WEB_AUTOFILL_POPUP_H_
3144diff --git a/shared/BUILD.gn b/shared/BUILD.gn
3145index 77eaeca..1aaf81d 100644
3146--- a/shared/BUILD.gn
3147+++ b/shared/BUILD.gn
3148@@ -1,6 +1,6 @@
3149 # vim:expandtab:shiftwidth=2:tabstop=2:
3150
3151-# Copyright (C) 2016 Canonical Ltd.
3152+# Copyright (C) 2016-2017 Canonical Ltd.
3153
3154 # This library is free software; you can redistribute it and/or
3155 # modify it under the terms of the GNU Lesser General Public
3156@@ -113,6 +113,7 @@ repack_locales("shared_repack_locales") {
3157 output_locales = locales
3158
3159 source_patterns = [
3160+ "${root_gen_dir}/components/strings/components_strings_",
3161 "${root_gen_dir}/content/app/strings/content_strings_",
3162 "${root_gen_dir}/ui/strings/app_locale_settings_",
3163 ]
3164@@ -120,6 +121,7 @@ repack_locales("shared_repack_locales") {
3165 output_dir = "${root_out_dir}/chromium_l10n"
3166
3167 deps = [
3168+ "//components/strings",
3169 "//content/app/strings",
3170 "//ui/strings:app_locale_settings",
3171 ]
3172@@ -180,13 +182,23 @@ component("shared") {
3173 "//base:i18n",
3174 "//cc",
3175 "//cc/surfaces",
3176+ "//components/autofill/content/browser",
3177+ "//components/autofill/content/renderer",
3178+ "//components/autofill/core/browser",
3179 "//components/keyed_service/content",
3180 "//components/keyed_service/core",
3181+ "//components/password_manager/content/browser",
3182+ "//components/pref_registry",
3183+ "//components/prefs",
3184+ "//components/sync",
3185+ "//components/user_prefs",
3186 "//components/sessions",
3187+ "//components/webdata/common",
3188 "//content/public/app:both",
3189 "//content/public/browser",
3190 "//content/public/child",
3191 "//content/public/common",
3192+ "//content/public/common:service_names",
3193 "//content/public/gpu",
3194 "//content/public/renderer",
3195 "//content/public/utility",
3196@@ -198,6 +210,7 @@ component("shared") {
3197 "//device/vibration:mojo_bindings",
3198 "//extensions/common",
3199 "//gin",
3200+ "//google_apis",
3201 "//gpu/command_buffer/client",
3202 "//gpu/command_buffer/common",
3203 "//gpu/command_buffer/service",
3204@@ -244,6 +257,12 @@ component("shared") {
3205 "app/oxide_main.cc",
3206 "app/oxide_main.h",
3207 "app/oxide_platform_delegate.h",
3208+ "browser/autofill/autofill_client.cc",
3209+ "browser/autofill/autofill_client.h",
3210+ "browser/autofill/autofill_popup_controller.cc",
3211+ "browser/autofill/autofill_popup_controller.h",
3212+ "browser/autofill/web_autofill_popup.h",
3213+ "browser/autofill/web_autofill_popup_client.h",
3214 "browser/browser_object_weak_ptrs.h",
3215 "browser/chrome_controller.cc",
3216 "browser/chrome_controller.h",
3217@@ -299,6 +318,8 @@ component("shared") {
3218 "browser/device/power_save_blocker.h",
3219 "browser/device/power_save_blocker_linux.cc",
3220 "browser/display_form_factor.h",
3221+ "browser/filtered_pref_store.cc",
3222+ "browser/filtered_pref_store.h",
3223 "browser/input/input_method_context.h",
3224 "browser/input/input_method_context_client.cc",
3225 "browser/input/input_method_context_client.h",
3226@@ -454,6 +475,8 @@ component("shared") {
3227 "browser/permissions/oxide_permission_request_response.h",
3228 "browser/permissions/oxide_temporary_saved_permission_context.cc",
3229 "browser/permissions/oxide_temporary_saved_permission_context.h",
3230+ "browser/personal_data_manager_factory.cc",
3231+ "browser/personal_data_manager_factory.h",
3232 "browser/screen.cc",
3233 "browser/screen.h",
3234 "browser/screen_observer.cc",
3235@@ -487,6 +510,10 @@ component("shared") {
3236 "browser/web_contents_data_tracker.h",
3237 "browser/web_contents_helper.cc",
3238 "browser/web_contents_helper.h",
3239+ "browser/web_data_service_factory.cc",
3240+ "browser/web_data_service_factory.h",
3241+ "browser/web_data_service_wrapper.cc",
3242+ "browser/web_data_service_wrapper.h",
3243 "browser/web_popup_menu.h",
3244 "browser/web_popup_menu_client.h",
3245 "browser/web_popup_menu_host.cc",
3246@@ -714,6 +741,7 @@ test_executable("shared_unittests") {
3247 "//base",
3248 "//base/test:test_support",
3249 "//cc",
3250+ "//components/prefs:test_support",
3251 "//content/public/browser",
3252 "//content/public/common",
3253 "//content/test:test_support",
3254@@ -729,6 +757,7 @@ test_executable("shared_unittests") {
3255 ]
3256
3257 sources = [
3258+ "browser/filtered_pref_store_unittest.cc",
3259 "browser/javascript_dialogs/javascript_dialog_contents_helper_unittest.cc",
3260 "browser/javascript_dialogs/javascript_dialog_host_unittest.cc",
3261 "browser/javascript_dialogs/javascript_dialog_testing_utils.cc",
3262diff --git a/shared/browser/autofill/autofill_client.cc b/shared/browser/autofill/autofill_client.cc
3263new file mode 100644
3264index 0000000..bbf22ee
3265--- /dev/null
3266+++ b/shared/browser/autofill/autofill_client.cc
3267@@ -0,0 +1,272 @@
3268+// vim:expandtab:shiftwidth=2:tabstop=2:
3269+// Copyright (C) 2016-2017 Canonical Ltd.
3270+
3271+// This library is free software; you can redistribute it and/or
3272+// modify it under the terms of the GNU Lesser General Public
3273+// License as published by the Free Software Foundation; either
3274+// version 2.1 of the License, or (at your option) any later version.
3275+
3276+// This library is distributed in the hope that it will be useful,
3277+// but WITHOUT ANY WARRANTY; without even the implied warranty of
3278+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3279+// Lesser General Public License for more details.
3280+
3281+// You should have received a copy of the GNU Lesser General Public
3282+// License along with this library; if not, write to the Free Software
3283+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
3284+
3285+#include "autofill_client.h"
3286+
3287+#include "base/logging.h"
3288+#include "components/autofill/content/browser/content_autofill_driver.h"
3289+#include "components/autofill/content/browser/content_autofill_driver_factory.h"
3290+#include "components/autofill/core/common/autofill_pref_names.h"
3291+#include "components/prefs/pref_service.h"
3292+#include "content/public/browser/browser_context.h"
3293+#include "content/public/browser/navigation_entry.h"
3294+#include "content/public/browser/render_frame_host.h"
3295+#include "content/public/browser/ssl_status.h"
3296+#include "google_apis/gaia/identity_provider.h"
3297+
3298+#include "shared/browser/oxide_browser_context.h"
3299+#include "shared/browser/personal_data_manager_factory.h"
3300+#include "shared/browser/web_data_service_factory.h"
3301+
3302+#include "autofill_popup_controller.h"
3303+
3304+namespace oxide {
3305+
3306+DEFINE_WEB_CONTENTS_USER_DATA_KEY(oxide::AutofillClient);
3307+
3308+class DummyIdentityProvider : public IdentityProvider {
3309+ public:
3310+ explicit DummyIdentityProvider();
3311+ ~DummyIdentityProvider() override;
3312+
3313+ std::string GetActiveUsername() override;
3314+ std::string GetActiveAccountId() override;
3315+ OAuth2TokenService* GetTokenService() override;
3316+ bool RequestLogin() override;
3317+
3318+ private:
3319+ DISALLOW_COPY_AND_ASSIGN(DummyIdentityProvider);
3320+};
3321+
3322+DummyIdentityProvider::DummyIdentityProvider() {}
3323+
3324+DummyIdentityProvider::~DummyIdentityProvider() {}
3325+
3326+std::string DummyIdentityProvider::GetActiveUsername() {
3327+ return std::string();
3328+}
3329+
3330+std::string DummyIdentityProvider::GetActiveAccountId() {
3331+ return std::string();
3332+}
3333+
3334+OAuth2TokenService* DummyIdentityProvider::GetTokenService() {
3335+ return nullptr;
3336+}
3337+
3338+bool DummyIdentityProvider::RequestLogin() {
3339+ return false;
3340+}
3341+
3342+AutofillClient::AutofillClient(content::WebContents* web_contents)
3343+ : content::WebContentsObserver(web_contents) {
3344+ DCHECK(web_contents);
3345+}
3346+
3347+AutofillClient::~AutofillClient() {
3348+ DCHECK(!popup_controller_);
3349+}
3350+
3351+void AutofillClient::DidHideAutofillPopup() {
3352+ DCHECK(popup_controller_);
3353+
3354+ base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
3355+ popup_controller_.release());
3356+}
3357+
3358+autofill::PersonalDataManager* AutofillClient::GetPersonalDataManager() {
3359+ return PersonalDataManagerFactory::GetForContext(
3360+ web_contents()->GetBrowserContext());
3361+}
3362+
3363+scoped_refptr<autofill::AutofillWebDataService>
3364+AutofillClient::GetDatabase() {
3365+ return WebDataServiceFactory::GetAutofillWebDataForContext(
3366+ web_contents()->GetBrowserContext());
3367+}
3368+
3369+PrefService* AutofillClient::GetPrefs() {
3370+ return BrowserContext::FromContent(web_contents()->GetBrowserContext())
3371+ ->GetPrefs();
3372+}
3373+
3374+syncer::SyncService* AutofillClient::GetSyncService() {
3375+ return nullptr;
3376+}
3377+
3378+IdentityProvider* AutofillClient::GetIdentityProvider() {
3379+ if (!identity_provider_) {
3380+ identity_provider_.reset(new DummyIdentityProvider());
3381+ }
3382+
3383+ return identity_provider_.get();
3384+}
3385+
3386+rappor::RapporServiceImpl* AutofillClient::GetRapporServiceImpl() {
3387+ return nullptr;
3388+}
3389+
3390+ukm::UkmService* AutofillClient::GetUkmService() {
3391+ return nullptr;
3392+}
3393+
3394+void AutofillClient::ShowAutofillSettings() {
3395+ NOTIMPLEMENTED();
3396+}
3397+
3398+void AutofillClient::ShowUnmaskPrompt(
3399+ const autofill::CreditCard& card,
3400+ UnmaskCardReason reason,
3401+ base::WeakPtr<autofill::CardUnmaskDelegate> delegate) {
3402+ NOTIMPLEMENTED();
3403+}
3404+
3405+void AutofillClient::OnUnmaskVerificationResult(
3406+ PaymentsRpcResult result) {
3407+ NOTIMPLEMENTED();
3408+}
3409+
3410+void AutofillClient::ConfirmSaveCreditCardLocally(
3411+ const autofill::CreditCard& card,
3412+ const base::Closure& callback) {
3413+ NOTIMPLEMENTED();
3414+}
3415+
3416+void AutofillClient::ConfirmSaveCreditCardToCloud(
3417+ const autofill::CreditCard& card,
3418+ std::unique_ptr<base::DictionaryValue> legal_message,
3419+ const base::Closure& callback) {
3420+ NOTIMPLEMENTED();
3421+}
3422+
3423+void AutofillClient::ConfirmCreditCardFillAssist(
3424+ const autofill::CreditCard& card,
3425+ const base::Closure& callback) {
3426+ NOTIMPLEMENTED();
3427+}
3428+
3429+void AutofillClient::LoadRiskData(
3430+ const base::Callback<void(const std::string&)>& callback) {
3431+ NOTIMPLEMENTED();
3432+}
3433+
3434+bool AutofillClient::HasCreditCardScanFeature() {
3435+ return false;
3436+}
3437+
3438+void AutofillClient::ScanCreditCard(
3439+ const CreditCardScanCallback& callback) {
3440+ NOTIMPLEMENTED();
3441+}
3442+
3443+void AutofillClient::ShowAutofillPopup(
3444+ const gfx::RectF& element_bounds,
3445+ base::i18n::TextDirection text_direction,
3446+ const std::vector<autofill::Suggestion>& suggestions,
3447+ base::WeakPtr<autofill::AutofillPopupDelegate> delegate) {
3448+ // Will delete or reuse the old |popup_controller_|.
3449+ popup_controller_.reset(
3450+ AutofillPopupController::GetOrCreate(
3451+ popup_controller_, delegate, web_contents(), element_bounds,
3452+ text_direction, base::Bind(&AutofillClient::DidHideAutofillPopup,
3453+ base::Unretained(this))));
3454+
3455+ popup_controller_->Show(suggestions);
3456+}
3457+
3458+void AutofillClient::UpdateAutofillPopupDataListValues(
3459+ const std::vector<base::string16>& values,
3460+ const std::vector<base::string16>& labels) {
3461+ if (popup_controller_.get()) {
3462+ popup_controller_->UpdateDataListValues(values, labels);
3463+ }
3464+}
3465+
3466+void AutofillClient::HideAutofillPopup() {
3467+ if (popup_controller_.get()) {
3468+ popup_controller_->Hide();
3469+ }
3470+}
3471+
3472+bool AutofillClient::IsAutocompleteEnabled() {
3473+ return GetPrefs()->GetBoolean(autofill::prefs::kAutofillEnabled);
3474+}
3475+
3476+void AutofillClient::MainFrameWasResized(bool width_changed) {
3477+ HideAutofillPopup();
3478+}
3479+
3480+void AutofillClient::WebContentsDestroyed() {
3481+ HideAutofillPopup();
3482+}
3483+
3484+void AutofillClient::PropagateAutofillPredictions(
3485+ content::RenderFrameHost* rfh,
3486+ const std::vector<autofill::FormStructure*>& forms) {
3487+ NOTIMPLEMENTED();
3488+}
3489+
3490+void AutofillClient::DidFillOrPreviewField(
3491+ const base::string16& autofilled_value,
3492+ const base::string16& profile_full_name) {}
3493+
3494+void AutofillClient::OnFirstUserGestureObserved() {
3495+ autofill::ContentAutofillDriverFactory* factory =
3496+ autofill::ContentAutofillDriverFactory::FromWebContents(web_contents());
3497+ DCHECK(factory);
3498+
3499+ for (content::RenderFrameHost* frame : web_contents()->GetAllFrames()) {
3500+ if (!frame->IsRenderFrameLive()) {
3501+ continue;
3502+ }
3503+ autofill::ContentAutofillDriver* driver = factory->DriverForFrame(frame);
3504+ DCHECK(driver);
3505+ driver->NotifyFirstUserGestureObservedInTab();
3506+ }
3507+}
3508+
3509+bool AutofillClient::IsContextSecure() {
3510+ content::SSLStatus ssl_status;
3511+ content::NavigationEntry* navigation_entry =
3512+ web_contents()->GetController().GetLastCommittedEntry();
3513+ if (!navigation_entry) {
3514+ return false;
3515+ }
3516+
3517+ ssl_status = navigation_entry->GetSSL();
3518+ return navigation_entry->GetURL().SchemeIsCryptographic() &&
3519+ ssl_status.certificate &&
3520+ (!net::IsCertStatusError(ssl_status.cert_status) ||
3521+ net::IsCertStatusMinorError(ssl_status.cert_status)) &&
3522+ !(ssl_status.content_status &
3523+ content::SSLStatus::RAN_INSECURE_CONTENT);
3524+}
3525+
3526+bool AutofillClient::ShouldShowSigninPromo() {
3527+ NOTIMPLEMENTED();
3528+ return false;
3529+}
3530+
3531+void AutofillClient::StartSigninFlow() {
3532+ NOTIMPLEMENTED();
3533+}
3534+
3535+void AutofillClient::ShowHttpNotSecureExplanation() {
3536+ NOTIMPLEMENTED();
3537+}
3538+
3539+} // namespace oxide
3540diff --git a/shared/browser/autofill/autofill_client.h b/shared/browser/autofill/autofill_client.h
3541new file mode 100644
3542index 0000000..b31fe3c
3543--- /dev/null
3544+++ b/shared/browser/autofill/autofill_client.h
3545@@ -0,0 +1,116 @@
3546+// vim:expandtab:shiftwidth=2:tabstop=2:
3547+// Copyright (C) 2016-2017 Canonical Ltd.
3548+
3549+// This library is free software; you can redistribute it and/or
3550+// modify it under the terms of the GNU Lesser General Public
3551+// License as published by the Free Software Foundation; either
3552+// version 2.1 of the License, or (at your option) any later version.
3553+
3554+// This library is distributed in the hope that it will be useful,
3555+// but WITHOUT ANY WARRANTY; without even the implied warranty of
3556+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3557+// Lesser General Public License for more details.
3558+
3559+// You should have received a copy of the GNU Lesser General Public
3560+// License along with this library; if not, write to the Free Software
3561+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
3562+
3563+#ifndef _OXIDE_SHARED_BROWSER_AUTOFILL_AUTOFILL_CLIENT_H_
3564+#define _OXIDE_SHARED_BROWSER_AUTOFILL_AUTOFILL_CLIENT_H_
3565+
3566+#include <memory>
3567+#include <string>
3568+#include <vector>
3569+
3570+#include "base/callback.h"
3571+#include "base/compiler_specific.h"
3572+#include "base/i18n/rtl.h"
3573+#include "base/macros.h"
3574+#include "base/memory/weak_ptr.h"
3575+#include "components/autofill/core/browser/autofill_client.h"
3576+#include "content/public/browser/web_contents_observer.h"
3577+#include "content/public/browser/web_contents_user_data.h"
3578+
3579+namespace content {
3580+class WebContents;
3581+}
3582+
3583+namespace oxide {
3584+
3585+class AutofillPopupController;
3586+class DummyIdentityProvider;
3587+
3588+class AutofillClient
3589+ : public autofill::AutofillClient,
3590+ public content::WebContentsUserData<AutofillClient>,
3591+ public content::WebContentsObserver {
3592+ public:
3593+ ~AutofillClient() override;
3594+
3595+ // AutofillClient:
3596+ autofill::PersonalDataManager* GetPersonalDataManager() override;
3597+ scoped_refptr<autofill::AutofillWebDataService> GetDatabase() override;
3598+ PrefService* GetPrefs() override;
3599+ syncer::SyncService* GetSyncService() override;
3600+ IdentityProvider* GetIdentityProvider() override;
3601+ rappor::RapporServiceImpl* GetRapporServiceImpl() override;
3602+ ukm::UkmService* GetUkmService() override;
3603+ void ShowAutofillSettings() override;
3604+ void ShowUnmaskPrompt(
3605+ const autofill::CreditCard& card,
3606+ UnmaskCardReason reason,
3607+ base::WeakPtr<autofill::CardUnmaskDelegate> delegate) override;
3608+ void OnUnmaskVerificationResult(PaymentsRpcResult result) override;
3609+ void ConfirmSaveCreditCardLocally(const autofill::CreditCard& card,
3610+ const base::Closure& callback) override;
3611+ void ConfirmSaveCreditCardToCloud(
3612+ const autofill::CreditCard& card,
3613+ std::unique_ptr<base::DictionaryValue> legal_message,
3614+ const base::Closure& callback) override;
3615+ void ConfirmCreditCardFillAssist(const autofill::CreditCard& card,
3616+ const base::Closure& callback) override;
3617+ void LoadRiskData(
3618+ const base::Callback<void(const std::string&)>& callback) override;
3619+ bool HasCreditCardScanFeature() override;
3620+ void ScanCreditCard(const CreditCardScanCallback& callback) override;
3621+ void ShowAutofillPopup(
3622+ const gfx::RectF& element_bounds,
3623+ base::i18n::TextDirection text_direction,
3624+ const std::vector<autofill::Suggestion>& suggestions,
3625+ base::WeakPtr<autofill::AutofillPopupDelegate> delegate) override;
3626+ void UpdateAutofillPopupDataListValues(
3627+ const std::vector<base::string16>& values,
3628+ const std::vector<base::string16>& labels) override;
3629+ void HideAutofillPopup() override;
3630+ bool IsAutocompleteEnabled() override;
3631+ void PropagateAutofillPredictions(
3632+ content::RenderFrameHost* rfh,
3633+ const std::vector<autofill::FormStructure*>& forms) override;
3634+ void DidFillOrPreviewField(const base::string16& autofilled_value,
3635+ const base::string16& profile_full_name) override;
3636+ void OnFirstUserGestureObserved() override;
3637+ bool IsContextSecure() override;
3638+ bool ShouldShowSigninPromo() override;
3639+ void StartSigninFlow() override;
3640+ void ShowHttpNotSecureExplanation() override;
3641+
3642+ // content::WebContentsObserver implementation.
3643+ void MainFrameWasResized(bool width_changed) override;
3644+ void WebContentsDestroyed() override;
3645+
3646+ private:
3647+ explicit AutofillClient(content::WebContents* web_contents);
3648+ friend class content::WebContentsUserData<AutofillClient>;
3649+
3650+ void DidHideAutofillPopup();
3651+
3652+ std::unique_ptr<AutofillPopupController> popup_controller_;
3653+
3654+ std::unique_ptr<DummyIdentityProvider> identity_provider_;
3655+
3656+ DISALLOW_COPY_AND_ASSIGN(AutofillClient);
3657+};
3658+
3659+} // namespace oxide
3660+
3661+#endif // _OXIDE_SHARED_BROWSER_AUTOFILL_AUTOFILL_CLIENT_H_
3662diff --git a/shared/browser/autofill/autofill_popup_controller.cc b/shared/browser/autofill/autofill_popup_controller.cc
3663new file mode 100644
3664index 0000000..a79fe61
3665--- /dev/null
3666+++ b/shared/browser/autofill/autofill_popup_controller.cc
3667@@ -0,0 +1,163 @@
3668+// vim:expandtab:shiftwidth=2:tabstop=2:
3669+// Copyright (C) 2017 Canonical Ltd.
3670+
3671+// This library is free software; you can redistribute it and/or
3672+// modify it under the terms of the GNU Lesser General Public
3673+// License as published by the Free Software Foundation; either
3674+// version 2.1 of the License, or (at your option) any later version.
3675+
3676+// This library is distributed in the hope that it will be useful,
3677+// but WITHOUT ANY WARRANTY; without even the implied warranty of
3678+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3679+// Lesser General Public License for more details.
3680+
3681+// You should have received a copy of the GNU Lesser General Public
3682+// License along with this library; if not, write to the Free Software
3683+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
3684+
3685+#include "autofill_popup_controller.h"
3686+
3687+#include "components/autofill/core/browser/autofill_popup_delegate.h"
3688+#include "components/autofill/core/browser/suggestion.h"
3689+
3690+#include "shared/browser/chrome_controller.h"
3691+#include "shared/browser/oxide_web_contents_view.h"
3692+#include "shared/browser/oxide_web_contents_view_client.h"
3693+#include "shared/browser/web_contents_client.h"
3694+#include "shared/browser/web_contents_helper.h"
3695+
3696+#include "web_autofill_popup.h"
3697+
3698+namespace oxide {
3699+
3700+// static
3701+AutofillPopupController* AutofillPopupController::GetOrCreate(
3702+ std::unique_ptr<AutofillPopupController>& previous,
3703+ base::WeakPtr<autofill::AutofillPopupDelegate> delegate,
3704+ content::WebContents* web_contents,
3705+ const gfx::RectF& element_bounds,
3706+ base::i18n::TextDirection text_direction,
3707+ const base::Closure& hidden_callback) {
3708+ if (previous.get() && previous->web_contents_ == web_contents &&
3709+ previous->delegate_.get() == delegate.get() &&
3710+ previous->element_bounds_ == element_bounds) {
3711+ previous->ClearState();
3712+ return previous.release();
3713+ }
3714+
3715+ if (previous.get()) {
3716+ previous->Hide();
3717+ }
3718+
3719+ return new AutofillPopupController(
3720+ delegate, web_contents, element_bounds, text_direction, hidden_callback);
3721+}
3722+
3723+AutofillPopupController::AutofillPopupController(
3724+ base::WeakPtr<autofill::AutofillPopupDelegate> delegate,
3725+ content::WebContents* web_contents,
3726+ const gfx::RectF& element_bounds,
3727+ base::i18n::TextDirection text_direction,
3728+ const base::Closure& hidden_callback)
3729+ : web_contents_(web_contents),
3730+ element_bounds_(element_bounds),
3731+ delegate_(delegate),
3732+ hidden_callback_(hidden_callback) {
3733+ ClearState();
3734+}
3735+
3736+AutofillPopupController::~AutofillPopupController() {
3737+ hidden_callback_.Reset();
3738+}
3739+
3740+void AutofillPopupController::Show(
3741+ const std::vector<autofill::Suggestion>& suggestions) {
3742+ suggestions_ = suggestions;
3743+
3744+ WebContentsHelper* helper =
3745+ WebContentsHelper::FromWebContents(web_contents_);
3746+ if (!helper->client()) {
3747+ return;
3748+ }
3749+
3750+ gfx::Vector2d top_content_offset;
3751+ ChromeController* chrome_controller =
3752+ ChromeController::FromWebContents(web_contents_);
3753+ if (chrome_controller) {
3754+ top_content_offset =
3755+ gfx::Vector2d(0, chrome_controller->GetTopContentOffset());
3756+ }
3757+
3758+ popup_ = helper->client()->CreateAutofillPopup(
3759+ suggestions, element_bounds_ + top_content_offset, this);
3760+ if (!popup_) {
3761+ return;
3762+ }
3763+
3764+ popup_->Show();
3765+
3766+ delegate_->OnPopupShown();
3767+}
3768+
3769+void AutofillPopupController::UpdateDataListValues(
3770+ const std::vector<base::string16>& values,
3771+ const std::vector<base::string16>& labels) {
3772+ if (popup_) {
3773+ popup_->UpdateDataListValues(values, labels);
3774+ }
3775+}
3776+
3777+void AutofillPopupController::Hide() {
3778+ if (hidden_callback_.is_null()) {
3779+ return;
3780+ }
3781+ base::Closure hidden_callback = std::move(hidden_callback_);
3782+ hidden_callback_.Reset();
3783+
3784+ if (delegate_) {
3785+ delegate_->OnPopupHidden();
3786+ }
3787+
3788+ if (popup_) {
3789+ popup_->Hide();
3790+ }
3791+
3792+ hidden_callback.Run();
3793+}
3794+
3795+void AutofillPopupController::Close() {
3796+ Hide();
3797+}
3798+
3799+void AutofillPopupController::SelectSuggestion(int frontend_id,
3800+ const base::string16& value) {
3801+ delegate_->DidSelectSuggestion(value, frontend_id);
3802+}
3803+
3804+void AutofillPopupController::ClearSelection() {
3805+ delegate_->ClearPreviewedForm();
3806+}
3807+
3808+void AutofillPopupController::AcceptSuggestion(int frontend_id,
3809+ const base::string16& value) {
3810+ int position = 0;
3811+ for (size_t i = 0; i < suggestions_.size(); ++i) {
3812+ const autofill::Suggestion& suggestion = suggestions_[i];
3813+ if (suggestion.frontend_id == frontend_id && suggestion.value == value) {
3814+ position = i;
3815+ break;
3816+ }
3817+ }
3818+ delegate_->DidAcceptSuggestion(value, frontend_id, position);
3819+}
3820+
3821+bool AutofillPopupController::RemoveSuggestion(int frontend_id,
3822+ const base::string16& value) {
3823+ return delegate_->RemoveSuggestion(value, frontend_id);
3824+}
3825+
3826+void AutofillPopupController::ClearState() {
3827+ suggestions_.clear();
3828+}
3829+
3830+} // namespace oxide
3831diff --git a/shared/browser/autofill/autofill_popup_controller.h b/shared/browser/autofill/autofill_popup_controller.h
3832new file mode 100644
3833index 0000000..e7d9be0
3834--- /dev/null
3835+++ b/shared/browser/autofill/autofill_popup_controller.h
3836@@ -0,0 +1,92 @@
3837+// vim:expandtab:shiftwidth=2:tabstop=2:
3838+// Copyright (C) 2017 Canonical Ltd.
3839+
3840+// This library is free software; you can redistribute it and/or
3841+// modify it under the terms of the GNU Lesser General Public
3842+// License as published by the Free Software Foundation; either
3843+// version 2.1 of the License, or (at your option) any later version.
3844+
3845+// This library is distributed in the hope that it will be useful,
3846+// but WITHOUT ANY WARRANTY; without even the implied warranty of
3847+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3848+// Lesser General Public License for more details.
3849+
3850+// You should have received a copy of the GNU Lesser General Public
3851+// License along with this library; if not, write to the Free Software
3852+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
3853+
3854+#ifndef _OXIDE_SHARED_BROWSER_AUTOFILL_AUTOFILL_POPUP_CONTROLLER_H_
3855+#define _OXIDE_SHARED_BROWSER_AUTOFILL_AUTOFILL_POPUP_CONTROLLER_H_
3856+
3857+#include <vector>
3858+
3859+#include "base/callback.h"
3860+#include "base/i18n/rtl.h"
3861+#include "base/memory/weak_ptr.h"
3862+#include "base/strings/string16.h"
3863+#include "ui/gfx/geometry/rect_f.h"
3864+#include "ui/gfx/native_widget_types.h"
3865+
3866+#include "web_autofill_popup_client.h"
3867+
3868+namespace autofill {
3869+class AutofillPopupDelegate;
3870+struct Suggestion;
3871+}
3872+
3873+namespace content {
3874+class WebContents;
3875+}
3876+
3877+namespace oxide {
3878+
3879+class WebAutofillPopup;
3880+
3881+class AutofillPopupController : public WebAutofillPopupClient {
3882+ public:
3883+ ~AutofillPopupController();
3884+
3885+ static AutofillPopupController* GetOrCreate(
3886+ std::unique_ptr<AutofillPopupController>& previous,
3887+ base::WeakPtr<autofill::AutofillPopupDelegate> delegate,
3888+ content::WebContents* web_contents,
3889+ const gfx::RectF& element_bounds,
3890+ base::i18n::TextDirection text_direction,
3891+ const base::Closure& hidden_callback);
3892+
3893+ void Show(const std::vector<autofill::Suggestion>& suggestions);
3894+ void UpdateDataListValues(const std::vector<base::string16>& values,
3895+ const std::vector<base::string16>& labels);
3896+ void Hide();
3897+
3898+ private:
3899+ AutofillPopupController(
3900+ base::WeakPtr<autofill::AutofillPopupDelegate> delegate,
3901+ content::WebContents* web_contents,
3902+ const gfx::RectF& element_bounds,
3903+ base::i18n::TextDirection text_direction,
3904+ const base::Closure& hidden_callback);
3905+
3906+ void ClearState();
3907+
3908+ // WebAutofillPopupClient implementation
3909+ void Close() override;
3910+ void SelectSuggestion(int frontend_id, const base::string16& value) override;
3911+ void ClearSelection() override;
3912+ void AcceptSuggestion(int frontend_id, const base::string16& value) override;
3913+ bool RemoveSuggestion(int frontend_id, const base::string16& value) override;
3914+
3915+ content::WebContents* web_contents_;
3916+ gfx::RectF element_bounds_;
3917+ base::WeakPtr<autofill::AutofillPopupDelegate> delegate_;
3918+ base::i18n::TextDirection text_direction_;
3919+ std::vector<autofill::Suggestion> suggestions_;
3920+
3921+ std::unique_ptr<WebAutofillPopup> popup_;
3922+
3923+ base::Closure hidden_callback_;
3924+};
3925+
3926+} // namespace oxide
3927+
3928+#endif // _OXIDE_SHARED_BROWSER_AUTOFILL_AUTOFILL_POPUP_CONTROLLER_H_
3929diff --git a/shared/browser/autofill/web_autofill_popup.h b/shared/browser/autofill/web_autofill_popup.h
3930new file mode 100644
3931index 0000000..7d58fdc
3932--- /dev/null
3933+++ b/shared/browser/autofill/web_autofill_popup.h
3934@@ -0,0 +1,42 @@
3935+// vim:expandtab:shiftwidth=2:tabstop=2:
3936+// Copyright (C) 2017 Canonical Ltd.
3937+
3938+// This library is free software; you can redistribute it and/or
3939+// modify it under the terms of the GNU Lesser General Public
3940+// License as published by the Free Software Foundation; either
3941+// version 2.1 of the License, or (at your option) any later version.
3942+
3943+// This library is distributed in the hope that it will be useful,
3944+// but WITHOUT ANY WARRANTY; without even the implied warranty of
3945+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3946+// Lesser General Public License for more details.
3947+
3948+// You should have received a copy of the GNU Lesser General Public
3949+// License along with this library; if not, write to the Free Software
3950+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
3951+
3952+#ifndef _OXIDE_SHARED_BROWSER_WEB_AUTOFILL_POPUP_H_
3953+#define _OXIDE_SHARED_BROWSER_WEB_AUTOFILL_POPUP_H_
3954+
3955+#include <vector>
3956+
3957+#include "base/strings/string16.h"
3958+
3959+namespace oxide {
3960+
3961+class WebAutofillPopup {
3962+ public:
3963+ virtual ~WebAutofillPopup() {}
3964+
3965+ virtual void Show() = 0;
3966+
3967+ virtual void UpdateDataListValues(
3968+ const std::vector<base::string16>& values,
3969+ const std::vector<base::string16>& labels) = 0;
3970+
3971+ virtual void Hide() = 0;
3972+};
3973+
3974+} // namespace oxide
3975+
3976+#endif // _OXIDE_SHARED_BROWSER_WEB_AUTOFILL_POPUP_H_
3977diff --git a/shared/browser/autofill/web_autofill_popup_client.h b/shared/browser/autofill/web_autofill_popup_client.h
3978new file mode 100644
3979index 0000000..4eecc06
3980--- /dev/null
3981+++ b/shared/browser/autofill/web_autofill_popup_client.h
3982@@ -0,0 +1,41 @@
3983+// vim:expandtab:shiftwidth=2:tabstop=2:
3984+// Copyright (C) 2017 Canonical Ltd.
3985+
3986+// This library is free software; you can redistribute it and/or
3987+// modify it under the terms of the GNU Lesser General Public
3988+// License as published by the Free Software Foundation; either
3989+// version 2.1 of the License, or (at your option) any later version.
3990+
3991+// This library is distributed in the hope that it will be useful,
3992+// but WITHOUT ANY WARRANTY; without even the implied warranty of
3993+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3994+// Lesser General Public License for more details.
3995+
3996+// You should have received a copy of the GNU Lesser General Public
3997+// License along with this library; if not, write to the Free Software
3998+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
3999+
4000+#ifndef _OXIDE_SHARED_BROWSER_AUTOFILL_WEB_AUTOFILL_POPUP_CLIENT_H_
4001+#define _OXIDE_SHARED_BROWSER_AUTOFILL_WEB_AUTOFILL_POPUP_CLIENT_H_
4002+
4003+#include "base/strings/string16.h"
4004+
4005+namespace oxide {
4006+
4007+class WebAutofillPopupClient {
4008+ public:
4009+ virtual ~WebAutofillPopupClient() {}
4010+
4011+ virtual void Close() = 0;
4012+ virtual void SelectSuggestion(int frontend_id,
4013+ const base::string16& value) = 0;
4014+ virtual void ClearSelection() = 0;
4015+ virtual void AcceptSuggestion(int frontend_id,
4016+ const base::string16& value) = 0;
4017+ virtual bool RemoveSuggestion(int frontend_id,
4018+ const base::string16& value) = 0;
4019+};
4020+
4021+} // namespace oxide
4022+
4023+#endif // _OXIDE_SHARED_BROWSER_AUTOFILL_WEB_AUTOFILL_POPUP_CLIENT_H_
4024diff --git a/shared/browser/filtered_pref_store.cc b/shared/browser/filtered_pref_store.cc
4025new file mode 100644
4026index 0000000..bfc7931
4027--- /dev/null
4028+++ b/shared/browser/filtered_pref_store.cc
4029@@ -0,0 +1,181 @@
4030+// vim:expandtab:shiftwidth=2:tabstop=2:
4031+// Copyright (C) 2017 Canonical Ltd.
4032+
4033+// This library is free software; you can redistribute it and/or
4034+// modify it under the terms of the GNU Lesser General Public
4035+// License as published by the Free Software Foundation; either
4036+// version 2.1 of the License, or (at your option) any later version.
4037+
4038+// This library is distributed in the hope that it will be useful,
4039+// but WITHOUT ANY WARRANTY; without even the implied warranty of
4040+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4041+// Lesser General Public License for more details.
4042+
4043+// You should have received a copy of the GNU Lesser General Public
4044+// License along with this library; if not, write to the Free Software
4045+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
4046+
4047+#include "filtered_pref_store.h"
4048+
4049+#include "base/stl_util.h"
4050+#include "base/values.h"
4051+#include "components/prefs/in_memory_pref_store.h"
4052+#include "components/prefs/json_pref_store.h"
4053+
4054+namespace oxide {
4055+
4056+FilteredPrefStore::AggregatingObserver::AggregatingObserver(
4057+ FilteredPrefStore* outer)
4058+ : outer_(outer) {}
4059+
4060+void FilteredPrefStore::AggregatingObserver::OnPrefValueChanged(
4061+ const std::string& key) {
4062+ if (!outer_->IsInitializationComplete()) {
4063+ return;
4064+ }
4065+
4066+ for (auto& observer : outer_->observers_) {
4067+ observer.OnPrefValueChanged(key);
4068+ }
4069+}
4070+
4071+void FilteredPrefStore::AggregatingObserver::OnInitializationCompleted(
4072+ bool succeeded) {
4073+ // This always comes the persistent pref store. In-memory pref stores don't
4074+ // notify observers of their initialization status.
4075+ if (succeeded && outer_->read_error_delegate_) {
4076+ PersistentPrefStore::PrefReadError read_error = outer_->GetReadError();
4077+ if (read_error != PersistentPrefStore::PREF_READ_ERROR_NONE)
4078+ outer_->read_error_delegate_->OnError(read_error);
4079+ }
4080+
4081+ for (auto& observer : outer_->observers_) {
4082+ observer.OnInitializationCompleted(true);
4083+ }
4084+}
4085+
4086+FilteredPrefStore::FilteredPrefStore(
4087+ const scoped_refptr<InMemoryPrefStore>& volatile_pref_store,
4088+ const scoped_refptr<JsonPrefStore>& persistent_pref_store,
4089+ const std::set<std::string>& persistent_pref_names)
4090+ : volatile_pref_store_(volatile_pref_store),
4091+ persistent_pref_store_(persistent_pref_store),
4092+ persistent_preference_names_(persistent_pref_names),
4093+ aggregating_observer_(this) {
4094+ volatile_pref_store_->AddObserver(&aggregating_observer_);
4095+ persistent_pref_store_->AddObserver(&aggregating_observer_);
4096+}
4097+
4098+void FilteredPrefStore::AddObserver(Observer* observer) {
4099+ observers_.AddObserver(observer);
4100+}
4101+
4102+void FilteredPrefStore::RemoveObserver(Observer* observer) {
4103+ observers_.RemoveObserver(observer);
4104+}
4105+
4106+bool FilteredPrefStore::HasObservers() const {
4107+ return observers_.might_have_observers();
4108+}
4109+
4110+bool FilteredPrefStore::IsInitializationComplete() const {
4111+ return persistent_pref_store_->IsInitializationComplete();
4112+}
4113+
4114+bool FilteredPrefStore::GetValue(const std::string& key,
4115+ const base::Value** result) const {
4116+ return StoreForKey(key)->GetValue(key, result);
4117+}
4118+
4119+std::unique_ptr<base::DictionaryValue> FilteredPrefStore::GetValues() const {
4120+ auto values = volatile_pref_store_->GetValues();
4121+ auto persistent_pref_store_values = persistent_pref_store_->GetValues();
4122+ for (const auto& key : persistent_preference_names_) {
4123+ const base::Value* value = nullptr;
4124+ if (persistent_pref_store_values->Get(key, &value)) {
4125+ values->Set(key, value->CreateDeepCopy());
4126+ } else {
4127+ values->Remove(key, nullptr);
4128+ }
4129+ }
4130+ return values;
4131+}
4132+
4133+void FilteredPrefStore::SetValue(const std::string& key,
4134+ std::unique_ptr<base::Value> value,
4135+ uint32_t flags) {
4136+ StoreForKey(key)->SetValue(key, std::move(value), flags);
4137+}
4138+
4139+void FilteredPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
4140+ StoreForKey(key)->RemoveValue(key, flags);
4141+}
4142+
4143+bool FilteredPrefStore::GetMutableValue(const std::string& key,
4144+ base::Value** result) {
4145+ return StoreForKey(key)->GetMutableValue(key, result);
4146+}
4147+
4148+void FilteredPrefStore::ReportValueChanged(const std::string& key,
4149+ uint32_t flags) {
4150+ StoreForKey(key)->ReportValueChanged(key, flags);
4151+}
4152+
4153+void FilteredPrefStore::SetValueSilently(const std::string& key,
4154+ std::unique_ptr<base::Value> value,
4155+ uint32_t flags) {
4156+ StoreForKey(key)->SetValueSilently(key, std::move(value), flags);
4157+}
4158+
4159+bool FilteredPrefStore::ReadOnly() const {
4160+ return persistent_pref_store_->ReadOnly();
4161+}
4162+
4163+PersistentPrefStore::PrefReadError FilteredPrefStore::GetReadError() const {
4164+ return persistent_pref_store_->GetReadError();
4165+}
4166+
4167+PersistentPrefStore::PrefReadError FilteredPrefStore::ReadPrefs() {
4168+ return persistent_pref_store_->ReadPrefs();
4169+}
4170+
4171+void FilteredPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
4172+ read_error_delegate_.reset(error_delegate);
4173+ persistent_pref_store_->ReadPrefsAsync(nullptr);
4174+}
4175+
4176+void FilteredPrefStore::CommitPendingWrite() {
4177+ persistent_pref_store_->CommitPendingWrite();
4178+}
4179+
4180+void FilteredPrefStore::SchedulePendingLossyWrites() {
4181+ persistent_pref_store_->SchedulePendingLossyWrites();
4182+}
4183+
4184+void FilteredPrefStore::ClearMutableValues() {
4185+ NOTIMPLEMENTED();
4186+}
4187+
4188+FilteredPrefStore::~FilteredPrefStore() {
4189+ volatile_pref_store_->RemoveObserver(&aggregating_observer_);
4190+ persistent_pref_store_->RemoveObserver(&aggregating_observer_);
4191+}
4192+
4193+PersistentPrefStore* FilteredPrefStore::StoreForKey(const std::string& key) {
4194+ if (base::ContainsKey(persistent_preference_names_, key)) {
4195+ return persistent_pref_store_.get();
4196+ } else {
4197+ return volatile_pref_store_.get();
4198+ }
4199+}
4200+
4201+const PersistentPrefStore* FilteredPrefStore::StoreForKey(
4202+ const std::string& key) const {
4203+ if (base::ContainsKey(persistent_preference_names_, key)) {
4204+ return persistent_pref_store_.get();
4205+ } else {
4206+ return volatile_pref_store_.get();
4207+ }
4208+}
4209+
4210+} // namespace oxide
4211diff --git a/shared/browser/filtered_pref_store.h b/shared/browser/filtered_pref_store.h
4212new file mode 100644
4213index 0000000..07eee99
4214--- /dev/null
4215+++ b/shared/browser/filtered_pref_store.h
4216@@ -0,0 +1,107 @@
4217+// vim:expandtab:shiftwidth=2:tabstop=2:
4218+// Copyright (C) 2017 Canonical Ltd.
4219+
4220+// This library is free software; you can redistribute it and/or
4221+// modify it under the terms of the GNU Lesser General Public
4222+// License as published by the Free Software Foundation; either
4223+// version 2.1 of the License, or (at your option) any later version.
4224+
4225+// This library is distributed in the hope that it will be useful,
4226+// but WITHOUT ANY WARRANTY; without even the implied warranty of
4227+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4228+// Lesser General Public License for more details.
4229+
4230+// You should have received a copy of the GNU Lesser General Public
4231+// License along with this library; if not, write to the Free Software
4232+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
4233+
4234+#ifndef _OXIDE_SHARED_BROWSER_FILTERED_PREF_STORE_H_
4235+#define _OXIDE_SHARED_BROWSER_FILTERED_PREF_STORE_H_
4236+
4237+#include <set>
4238+#include <string>
4239+
4240+#include "base/macros.h"
4241+#include "base/memory/ref_counted.h"
4242+#include "base/observer_list.h"
4243+#include "components/prefs/persistent_pref_store.h"
4244+
4245+class InMemoryPrefStore;
4246+class JsonPrefStore;
4247+
4248+namespace oxide {
4249+
4250+// Inspired by chromium's SegregatedPrefStore: all preferences are stored
4251+// in memory (and thus not persisted) except for a selected set which are
4252+// persisted on disk.
4253+class FilteredPrefStore : public PersistentPrefStore {
4254+ public:
4255+ FilteredPrefStore(
4256+ const scoped_refptr<InMemoryPrefStore>& volatile_pref_store,
4257+ const scoped_refptr<JsonPrefStore>& persistent_pref_store,
4258+ const std::set<std::string>& persistent_pref_names);
4259+
4260+ // PrefStore:
4261+ void AddObserver(Observer* observer) override;
4262+ void RemoveObserver(Observer* observer) override;
4263+ bool HasObservers() const override;
4264+ bool IsInitializationComplete() const override;
4265+ bool GetValue(const std::string& key,
4266+ const base::Value** result) const override;
4267+ std::unique_ptr<base::DictionaryValue> GetValues() const override;
4268+
4269+ // WriteablePrefStore:
4270+ void SetValue(const std::string& key,
4271+ std::unique_ptr<base::Value> value,
4272+ uint32_t flags) override;
4273+ void RemoveValue(const std::string& key, uint32_t flags) override;
4274+
4275+ // PersistentPrefStore:
4276+ bool GetMutableValue(const std::string& key, base::Value** result) override;
4277+ void ReportValueChanged(const std::string& key, uint32_t flags) override;
4278+ void SetValueSilently(const std::string& key,
4279+ std::unique_ptr<base::Value> value,
4280+ uint32_t flags) override;
4281+ bool ReadOnly() const override;
4282+ PrefReadError GetReadError() const override;
4283+ PrefReadError ReadPrefs() override;
4284+ void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override;
4285+ void CommitPendingWrite() override;
4286+ void SchedulePendingLossyWrites() override;
4287+
4288+ void ClearMutableValues() override;
4289+
4290+ private:
4291+ class AggregatingObserver : public PrefStore::Observer {
4292+ public:
4293+ explicit AggregatingObserver(FilteredPrefStore* outer);
4294+
4295+ // PrefStore::Observer:
4296+ void OnPrefValueChanged(const std::string& key) override;
4297+ void OnInitializationCompleted(bool succeeded) override;
4298+
4299+ private:
4300+ FilteredPrefStore* outer_;
4301+
4302+ DISALLOW_COPY_AND_ASSIGN(AggregatingObserver);
4303+ };
4304+
4305+ ~FilteredPrefStore() override;
4306+
4307+ PersistentPrefStore* StoreForKey(const std::string& key);
4308+ const PersistentPrefStore* StoreForKey(const std::string& key) const;
4309+
4310+ scoped_refptr<InMemoryPrefStore> volatile_pref_store_;
4311+ scoped_refptr<JsonPrefStore> persistent_pref_store_;
4312+ std::set<std::string> persistent_preference_names_;
4313+
4314+ std::unique_ptr<PersistentPrefStore::ReadErrorDelegate> read_error_delegate_;
4315+ base::ObserverList<PrefStore::Observer, true> observers_;
4316+ AggregatingObserver aggregating_observer_;
4317+
4318+ DISALLOW_COPY_AND_ASSIGN(FilteredPrefStore);
4319+};
4320+
4321+} // namespace oxide
4322+
4323+#endif // _OXIDE_SHARED_BROWSER_FILTERED_PREF_STORE_H_
4324diff --git a/shared/browser/filtered_pref_store_unittest.cc b/shared/browser/filtered_pref_store_unittest.cc
4325new file mode 100644
4326index 0000000..90e67d8
4327--- /dev/null
4328+++ b/shared/browser/filtered_pref_store_unittest.cc
4329@@ -0,0 +1,209 @@
4330+// vim:expandtab:shiftwidth=2:tabstop=2:
4331+// Copyright (C) 2017 Canonical Ltd.
4332+
4333+// This library is free software; you can redistribute it and/or
4334+// modify it under the terms of the GNU Lesser General Public
4335+// License as published by the Free Software Foundation; either
4336+// version 2.1 of the License, or (at your option) any later version.
4337+
4338+// This library is distributed in the hope that it will be useful,
4339+// but WITHOUT ANY WARRANTY; without even the implied warranty of
4340+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4341+// Lesser General Public License for more details.
4342+
4343+// You should have received a copy of the GNU Lesser General Public
4344+// License along with this library; if not, write to the Free Software
4345+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
4346+
4347+#include "base/files/file_util.h"
4348+#include "base/files/scoped_temp_dir.h"
4349+#include "base/macros.h"
4350+#include "base/memory/ptr_util.h"
4351+#include "base/message_loop/message_loop.h"
4352+#include "base/run_loop.h"
4353+#include "base/values.h"
4354+#include "components/prefs/in_memory_pref_store.h"
4355+#include "components/prefs/json_pref_store.h"
4356+#include "components/prefs/pref_filter.h"
4357+#include "components/prefs/pref_store_observer_mock.h"
4358+#include "testing/gtest/include/gtest/gtest.h"
4359+
4360+#include "filtered_pref_store.h"
4361+
4362+namespace {
4363+
4364+const char kPersistentStoreFileName[] = "store.json";
4365+const char kEmptyJson[] = "{}";
4366+const char kInvalidJson[] = "invalid";
4367+
4368+const char kPersistentPref[] = "persistent_pref";
4369+const char kVolatilePref[] = "volatile_pref";
4370+const char kSharedPref[] = "shared_pref";
4371+
4372+} // namespace
4373+
4374+namespace oxide {
4375+
4376+class FilteredPrefStoreTest : public testing::Test {
4377+ public:
4378+ FilteredPrefStoreTest() = default;
4379+
4380+ protected:
4381+ void SetUp() override {
4382+ volatile_store_ = new InMemoryPrefStore;
4383+
4384+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
4385+ persistent_store_filepath_ =
4386+ temp_dir_.GetPath().AppendASCII(kPersistentStoreFileName);
4387+ persistent_store_ = new JsonPrefStore(persistent_store_filepath_,
4388+ message_loop_.task_runner(),
4389+ std::unique_ptr<PrefFilter>());
4390+ std::set<std::string> persistent_pref_names;
4391+ persistent_pref_names.insert(kPersistentPref);
4392+ persistent_pref_names.insert(kSharedPref);
4393+
4394+ filtered_store_ = new FilteredPrefStore(volatile_store_, persistent_store_,
4395+ persistent_pref_names);
4396+
4397+ filtered_store_->AddObserver(&observer_);
4398+ }
4399+
4400+ void TearDown() override {
4401+ filtered_store_->RemoveObserver(&observer_);
4402+ base::RunLoop().RunUntilIdle();
4403+ }
4404+
4405+ base::ScopedTempDir temp_dir_;
4406+ base::FilePath persistent_store_filepath_;
4407+ base::MessageLoop message_loop_;
4408+
4409+ PrefStoreObserverMock observer_;
4410+
4411+ scoped_refptr<InMemoryPrefStore> volatile_store_;
4412+ scoped_refptr<JsonPrefStore> persistent_store_;
4413+ scoped_refptr<FilteredPrefStore> filtered_store_;
4414+
4415+ DISALLOW_COPY_AND_ASSIGN(FilteredPrefStoreTest);
4416+};
4417+
4418+TEST_F(FilteredPrefStoreTest, ReadPrefsNoFile) {
4419+ ASSERT_FALSE(base::PathExists(persistent_store_filepath_));
4420+ EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
4421+ filtered_store_->ReadPrefs());
4422+}
4423+
4424+TEST_F(FilteredPrefStoreTest, ReadPrefsParseError) {
4425+ ASSERT_LT(0, base::WriteFile(persistent_store_filepath_,
4426+ kInvalidJson, arraysize(kInvalidJson) - 1));
4427+ ASSERT_TRUE(base::PathExists(persistent_store_filepath_));
4428+ EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
4429+ filtered_store_->ReadPrefs());
4430+}
4431+
4432+TEST_F(FilteredPrefStoreTest, ReadPrefsSuccess) {
4433+ ASSERT_LT(0, base::WriteFile(persistent_store_filepath_,
4434+ kEmptyJson, arraysize(kEmptyJson) - 1));
4435+ ASSERT_TRUE(base::PathExists(persistent_store_filepath_));
4436+ EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE,
4437+ filtered_store_->ReadPrefs());
4438+}
4439+
4440+TEST_F(FilteredPrefStoreTest, IsInitializationComplete) {
4441+ EXPECT_FALSE(filtered_store_->IsInitializationComplete());
4442+ filtered_store_->ReadPrefs();
4443+ EXPECT_TRUE(filtered_store_->IsInitializationComplete());
4444+}
4445+
4446+TEST_F(FilteredPrefStoreTest, Observer) {
4447+ EXPECT_FALSE(observer_.initialized);
4448+ filtered_store_->ReadPrefs();
4449+ EXPECT_TRUE(observer_.initialized);
4450+ EXPECT_TRUE(observer_.initialization_success);
4451+ EXPECT_TRUE(observer_.changed_keys.empty());
4452+ filtered_store_->SetValue(kPersistentPref,
4453+ base::MakeUnique<base::Value>(1),
4454+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
4455+ observer_.VerifyAndResetChangedKey(kPersistentPref);
4456+ filtered_store_->SetValue(kVolatilePref,
4457+ base::MakeUnique<base::Value>(2),
4458+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
4459+ observer_.VerifyAndResetChangedKey(kVolatilePref);
4460+}
4461+
4462+TEST_F(FilteredPrefStoreTest, StoreValues) {
4463+ PrefStoreObserverMock volatile_observer;
4464+ volatile_store_->AddObserver(&volatile_observer);
4465+
4466+ PrefStoreObserverMock persistent_observer;
4467+ persistent_store_->AddObserver(&persistent_observer);
4468+
4469+ filtered_store_->ReadPrefs();
4470+ EXPECT_TRUE(persistent_observer.initialized);
4471+ EXPECT_TRUE(persistent_observer.initialization_success);
4472+
4473+ filtered_store_->SetValue(kPersistentPref,
4474+ base::MakeUnique<base::Value>(3),
4475+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
4476+ persistent_observer.VerifyAndResetChangedKey(kPersistentPref);
4477+ EXPECT_TRUE(persistent_store_->GetValue(kPersistentPref, nullptr));
4478+ EXPECT_FALSE(volatile_store_->GetValue(kPersistentPref, nullptr));
4479+ EXPECT_TRUE(filtered_store_->GetValue(kPersistentPref, nullptr));
4480+
4481+ filtered_store_->SetValue(kVolatilePref,
4482+ base::MakeUnique<base::Value>(4),
4483+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
4484+ volatile_observer.VerifyAndResetChangedKey(kVolatilePref);
4485+ EXPECT_TRUE(volatile_store_->GetValue(kVolatilePref, nullptr));
4486+ EXPECT_FALSE(persistent_store_->GetValue(kVolatilePref, nullptr));
4487+ EXPECT_TRUE(filtered_store_->GetValue(kVolatilePref, nullptr));
4488+}
4489+
4490+TEST_F(FilteredPrefStoreTest, ReadValues) {
4491+ filtered_store_->ReadPrefs();
4492+ persistent_store_->SetValue(kPersistentPref,
4493+ base::MakeUnique<base::Value>(5),
4494+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
4495+ volatile_store_->SetValue(kVolatilePref,
4496+ base::MakeUnique<base::Value>(6),
4497+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
4498+
4499+ EXPECT_TRUE(persistent_store_->GetValue(kPersistentPref, nullptr));
4500+ EXPECT_FALSE(persistent_store_->GetValue(kVolatilePref, nullptr));
4501+
4502+ EXPECT_TRUE(volatile_store_->GetValue(kVolatilePref, nullptr));
4503+ EXPECT_FALSE(volatile_store_->GetValue(kPersistentPref, nullptr));
4504+
4505+ EXPECT_TRUE(filtered_store_->GetValue(kPersistentPref, nullptr));
4506+ EXPECT_TRUE(filtered_store_->GetValue(kVolatilePref, nullptr));
4507+}
4508+
4509+TEST_F(FilteredPrefStoreTest, GetValues) {
4510+ filtered_store_->ReadPrefs();
4511+ persistent_store_->SetValue(kPersistentPref,
4512+ base::MakeUnique<base::Value>(7),
4513+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
4514+ volatile_store_->SetValue(kVolatilePref,
4515+ base::MakeUnique<base::Value>(8),
4516+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
4517+
4518+ persistent_store_->SetValue(kSharedPref,
4519+ base::MakeUnique<base::Value>(9),
4520+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
4521+ volatile_store_->SetValue(kSharedPref,
4522+ base::MakeUnique<base::Value>(10),
4523+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
4524+
4525+ auto values = filtered_store_->GetValues();
4526+ const base::Value* value = nullptr;
4527+
4528+ EXPECT_TRUE(values->Get(kPersistentPref, &value));
4529+ EXPECT_TRUE(base::Value(7).Equals(value));
4530+
4531+ EXPECT_TRUE(values->Get(kVolatilePref, &value));
4532+ EXPECT_TRUE(base::Value(8).Equals(value));
4533+
4534+ EXPECT_TRUE(values->Get(kSharedPref, &value));
4535+ EXPECT_TRUE(base::Value(9).Equals(value));
4536+}
4537+
4538+} // namespace oxide
4539diff --git a/shared/browser/oxide_browser_context.cc b/shared/browser/oxide_browser_context.cc
4540index 5652924..265efc5 100644
4541--- a/shared/browser/oxide_browser_context.cc
4542+++ b/shared/browser/oxide_browser_context.cc
4543@@ -1,5 +1,5 @@
4544 // vim:expandtab:shiftwidth=2:tabstop=2:
4545-// Copyright (C) 2013-2016 Canonical Ltd.
4546+// Copyright (C) 2013-2017 Canonical Ltd.
4547
4548 // This library is free software; you can redistribute it and/or
4549 // modify it under the terms of the GNU Lesser General Public
4550@@ -35,7 +35,16 @@
4551 #include "base/threading/sequenced_worker_pool.h"
4552 #include "base/threading/thread_restrictions.h"
4553 #include "base/threading/worker_pool.h"
4554+#include "components/autofill/core/browser/autofill_manager.h"
4555+#include "components/autofill/core/common/autofill_pref_names.h"
4556 #include "components/keyed_service/content/browser_context_dependency_manager.h"
4557+#include "components/pref_registry/pref_registry_syncable.h"
4558+#include "components/prefs/in_memory_pref_store.h"
4559+#include "components/prefs/json_pref_store.h"
4560+#include "components/prefs/overlay_user_pref_store.h"
4561+#include "components/prefs/pref_filter.h"
4562+#include "components/prefs/pref_notifier_impl.h"
4563+#include "components/prefs/pref_value_store.h"
4564 #include "content/browser/loader/resource_dispatcher_host_impl.h" // nogncheck
4565 #include "content/public/browser/browser_thread.h"
4566 #include "content/public/browser/render_process_host.h"
4567@@ -67,6 +76,7 @@
4568 #include "shared/common/oxide_constants.h"
4569 #include "shared/common/oxide_content_client.h"
4570
4571+#include "filtered_pref_store.h"
4572 #include "oxide_browser_context_delegate.h"
4573 #include "oxide_browser_context_destroyer.h"
4574 #include "oxide_browser_process_main.h"
4575@@ -123,6 +133,8 @@ void CleanupOldCacheDir(const base::FilePath& path) {
4576 base::DeleteFile(path, true);
4577 }
4578
4579+void DoNothingHandleReadError(PersistentPrefStore::PrefReadError error) {}
4580+
4581 } // namespace
4582
4583 class MainURLRequestContextGetter : public URLRequestContextGetter {
4584@@ -527,6 +539,41 @@ net::CookieStore* BrowserContextIOData::GetCookieStore() const {
4585 return cookie_store_owner_->store();
4586 }
4587
4588+ClonablePrefService::ClonablePrefService(
4589+ PrefNotifierImpl* pref_notifier,
4590+ PrefValueStore* pref_value_store,
4591+ PersistentPrefStore* user_prefs,
4592+ PrefRegistry* pref_registry,
4593+ base::Callback<void(PersistentPrefStore::PrefReadError)>
4594+ read_error_callback,
4595+ bool async)
4596+ : PrefService(pref_notifier,
4597+ pref_value_store,
4598+ user_prefs,
4599+ pref_registry,
4600+ read_error_callback,
4601+ async) {
4602+}
4603+
4604+ClonablePrefService::~ClonablePrefService() {}
4605+
4606+ClonablePrefService* ClonablePrefService::CreateIncognitoPrefService() {
4607+ PrefNotifierImpl* pref_notifier = new PrefNotifierImpl();
4608+ OverlayUserPrefStore* incognito_pref_store =
4609+ new OverlayUserPrefStore(user_pref_store_.get());
4610+ return new ClonablePrefService(
4611+ pref_notifier,
4612+ pref_value_store_->CloneAndSpecialize(nullptr,
4613+ nullptr,
4614+ nullptr,
4615+ nullptr,
4616+ incognito_pref_store,
4617+ nullptr,
4618+ pref_registry_->defaults().get(),
4619+ pref_notifier),
4620+ incognito_pref_store, pref_registry_.get(), read_error_callback_, false);
4621+}
4622+
4623 class BrowserContextImpl;
4624
4625 class OTRBrowserContextImpl : public BrowserContext {
4626@@ -578,6 +625,9 @@ OTRBrowserContextImpl::OTRBrowserContextImpl(
4627 original_context_(original) {
4628 BrowserContextDependencyManager::GetInstance()
4629 ->CreateBrowserContextServices(this);
4630+
4631+ prefs_.reset(static_cast<ClonablePrefService*>(original->GetPrefs())
4632+ ->CreateIncognitoPrefService());
4633 }
4634
4635 BrowserContext* BrowserContextImpl::GetOffTheRecordContext() {
4636@@ -597,8 +647,15 @@ BrowserContextImpl::~BrowserContextImpl() {
4637
4638 BrowserContextImpl::BrowserContextImpl(const BrowserContext::Params& params)
4639 : BrowserContext(new BrowserContextIODataImpl(params)) {
4640- if (!GetPath().empty()) {
4641- base::FilePath gpu_cache = GetPath().Append(FILE_PATH_LITERAL("GPUCache"));
4642+ base::FilePath path = GetPath();
4643+ if (!path.empty()) {
4644+ if (!base::PathExists(path)) {
4645+ if (!base::CreateDirectory(path)) {
4646+ LOG(ERROR) << "Failed to create profile data path: " << path.value();
4647+ }
4648+ }
4649+
4650+ base::FilePath gpu_cache = path.Append(FILE_PATH_LITERAL("GPUCache"));
4651 content::BrowserThread::PostTask(
4652 content::BrowserThread::FILE,
4653 FROM_HERE,
4654@@ -607,6 +664,44 @@ BrowserContextImpl::BrowserContextImpl(const BrowserContext::Params& params)
4655
4656 BrowserContextDependencyManager::GetInstance()
4657 ->CreateBrowserContextServices(this);
4658+
4659+ autofill::AutofillManager::RegisterProfilePrefs(pref_registry_.get());
4660+ pref_registry_->SetDefaultPrefValue(
4661+ autofill::prefs::kAutofillEnabled,
4662+ new base::Value(false));
4663+ PrefNotifierImpl* pref_notifier = new PrefNotifierImpl();
4664+ scoped_refptr<PersistentPrefStore> user_prefs;
4665+ if (!path.empty()) {
4666+ base::FilePath pref_store_path =
4667+ path.Append(FILE_PATH_LITERAL("Preferences"));
4668+ scoped_refptr<base::SequencedTaskRunner> task_runner =
4669+ JsonPrefStore::GetTaskRunnerForFile(
4670+ pref_store_path, content::BrowserThread::GetBlockingPool());
4671+ // Persist only selected preferences on disk
4672+ scoped_refptr<InMemoryPrefStore> volatile_pref_store(new InMemoryPrefStore);
4673+ scoped_refptr<JsonPrefStore> persistent_pref_store(new JsonPrefStore(
4674+ pref_store_path, task_runner, std::unique_ptr<PrefFilter>()));
4675+ std::set<std::string> persistent_pref_names;
4676+ persistent_pref_names.insert(autofill::prefs::kAutofillLastVersionDeduped);
4677+ user_prefs = make_scoped_refptr(new FilteredPrefStore(
4678+ volatile_pref_store, persistent_pref_store, persistent_pref_names));
4679+ } else {
4680+ user_prefs = make_scoped_refptr(new InMemoryPrefStore);
4681+ }
4682+ prefs_.reset(new ClonablePrefService(
4683+ pref_notifier,
4684+ new PrefValueStore(nullptr,
4685+ nullptr,
4686+ nullptr,
4687+ nullptr,
4688+ user_prefs.get(),
4689+ nullptr,
4690+ pref_registry_->defaults().get(),
4691+ pref_notifier),
4692+ user_prefs.get(),
4693+ pref_registry_.get(),
4694+ base::Bind(&DoNothingHandleReadError),
4695+ true));
4696 }
4697
4698 void BrowserContext::Deleter::operator()(BrowserContext* context) {
4699@@ -702,7 +797,8 @@ BrowserContext::CreateMediaRequestContextForStoragePartition(
4700 }
4701
4702 BrowserContext::BrowserContext(BrowserContextIOData* io_data)
4703- : io_data_(io_data) {
4704+ : pref_registry_(new user_prefs::PrefRegistrySyncable),
4705+ io_data_(io_data) {
4706 CHECK(BrowserProcessMain::GetInstance()->IsRunning()) <<
4707 "The main browser process components must be started before " <<
4708 "creating a context";
4709diff --git a/shared/browser/oxide_browser_context.h b/shared/browser/oxide_browser_context.h
4710index e7f30d5..de32778 100644
4711--- a/shared/browser/oxide_browser_context.h
4712+++ b/shared/browser/oxide_browser_context.h
4713@@ -1,5 +1,5 @@
4714 // vim:expandtab:shiftwidth=2:tabstop=2:
4715-// Copyright (C) 2013-2016 Canonical Ltd.
4716+// Copyright (C) 2013-2017 Canonical Ltd.
4717
4718 // This library is free software; you can redistribute it and/or
4719 // modify it under the terms of the GNU Lesser General Public
4720@@ -25,6 +25,7 @@
4721 #include "base/memory/ref_counted.h"
4722 #include "base/synchronization/lock.h"
4723 #include "base/threading/non_thread_safe.h"
4724+#include "components/prefs/pref_service.h"
4725 #include "content/public/browser/browser_context.h"
4726 #include "content/public/browser/content_browser_client.h"
4727 #include "content/public/browser/cookie_store_factory.h"
4728@@ -49,6 +50,10 @@ class TransportSecurityState;
4729
4730 }
4731
4732+namespace user_prefs {
4733+class PrefRegistrySyncable;
4734+}
4735+
4736 namespace oxide {
4737
4738 class BrowserContext;
4739@@ -58,6 +63,7 @@ class BrowserContextDelegate;
4740 class BrowserContextImpl;
4741 class BrowserContextSharedData;
4742 class BrowserContextSharedIOData;
4743+class ClonablePrefService;
4744 class CookieStoreOwner;
4745 class CookieStoreProxy;
4746 class GeolocationPermissionContext;
4747@@ -136,7 +142,20 @@ class BrowserContextIOData {
4748 temporary_saved_permission_context_;
4749 };
4750
4751-class BrowserContext;
4752+class ClonablePrefService : public PrefService {
4753+ public:
4754+ ClonablePrefService(
4755+ PrefNotifierImpl* pref_notifier,
4756+ PrefValueStore* pref_value_store,
4757+ PersistentPrefStore* user_prefs,
4758+ PrefRegistry* pref_registry,
4759+ base::Callback<void(PersistentPrefStore::PrefReadError)>
4760+ read_error_callback,
4761+ bool async);
4762+ ~ClonablePrefService() override;
4763+
4764+ ClonablePrefService* CreateIncognitoPrefService();
4765+};
4766
4767 // This class holds the context needed for a browsing session. It lives on
4768 // and must only be accessed on the UI thread
4769@@ -230,11 +249,16 @@ class OXIDE_SHARED_EXPORT BrowserContext : public content::BrowserContext,
4770
4771 BrowserContextIOData* GetIOData() const;
4772
4773+ PrefService* GetPrefs() { return prefs_.get(); }
4774+
4775 protected:
4776 BrowserContext(BrowserContextIOData* io_data);
4777
4778 BrowserContextIOData* io_data() const { return io_data_; }
4779
4780+ scoped_refptr<user_prefs::PrefRegistrySyncable> pref_registry_;
4781+ std::unique_ptr<ClonablePrefService> prefs_;
4782+
4783 private:
4784 // content::BrowserContext implementation
4785 std::unique_ptr<content::ZoomLevelDelegate> CreateZoomLevelDelegate(
4786diff --git a/shared/browser/oxide_browser_platform_integration.cc b/shared/browser/oxide_browser_platform_integration.cc
4787index e30abbe..10340a8 100644
4788--- a/shared/browser/oxide_browser_platform_integration.cc
4789+++ b/shared/browser/oxide_browser_platform_integration.cc
4790@@ -1,5 +1,5 @@
4791 // vim:expandtab:shiftwidth=2:tabstop=2:
4792-// Copyright (C) 2014-2015 Canonical Ltd.
4793+// Copyright (C) 2014-2017 Canonical Ltd.
4794
4795 // This library is free software; you can redistribute it and/or
4796 // modify it under the terms of the GNU Lesser General Public
4797@@ -30,6 +30,7 @@ namespace oxide {
4798 namespace {
4799
4800 const char kDefaultApplicationName[] = "Oxide";
4801+const char kDefaultApplicationLocale[] = "C";
4802
4803 BrowserPlatformIntegration* g_instance;
4804
4805@@ -88,6 +89,10 @@ std::string BrowserPlatformIntegration::GetApplicationName() {
4806 return kDefaultApplicationName;
4807 }
4808
4809+std::string BrowserPlatformIntegration::GetApplicationLocale() {
4810+ return kDefaultApplicationLocale;
4811+}
4812+
4813 std::unique_ptr<DragSource> BrowserPlatformIntegration::CreateDragSource(
4814 DragSourceClient* client) {
4815 return nullptr;
4816diff --git a/shared/browser/oxide_browser_platform_integration.h b/shared/browser/oxide_browser_platform_integration.h
4817index 57f8568..18c6f93 100644
4818--- a/shared/browser/oxide_browser_platform_integration.h
4819+++ b/shared/browser/oxide_browser_platform_integration.h
4820@@ -1,5 +1,5 @@
4821 // vim:expandtab:shiftwidth=2:tabstop=2:
4822-// Copyright (C) 2014-2015 Canonical Ltd.
4823+// Copyright (C) 2014-2017 Canonical Ltd.
4824
4825 // This library is free software; you can redistribute it and/or
4826 // modify it under the terms of the GNU Lesser General Public
4827@@ -116,6 +116,9 @@ class OXIDE_SHARED_EXPORT BrowserPlatformIntegration {
4828 // Get the application name. Can be called on any thread
4829 virtual std::string GetApplicationName();
4830
4831+ // Get the application locale. Can be called on any thread
4832+ virtual std::string GetApplicationLocale();
4833+
4834 // Create a new DragSource implementation. Ownership of |client| is not
4835 // transferred, and |client| will outlive the returned DragSource.
4836 // Called on the UI thread
4837diff --git a/shared/browser/oxide_content_browser_client.cc b/shared/browser/oxide_content_browser_client.cc
4838index c5b627a..b26fa9e 100644
4839--- a/shared/browser/oxide_content_browser_client.cc
4840+++ b/shared/browser/oxide_content_browser_client.cc
4841@@ -22,9 +22,12 @@
4842 #include <vector>
4843
4844 #include "base/command_line.h"
4845+#include "base/json/json_reader.h"
4846 #include "base/logging.h"
4847 #include "base/memory/ptr_util.h"
4848 #include "base/memory/ref_counted.h"
4849+#include "components/autofill/content/browser/content_autofill_driver_factory.h"
4850+#include "components/password_manager/content/browser/content_password_manager_driver_factory.h"
4851 #include "content/public/browser/certificate_request_result_type.h"
4852 #include "content/public/browser/interstitial_page.h"
4853 #include "content/public/browser/permission_type.h"
4854@@ -34,10 +37,12 @@
4855 #include "content/public/browser/resource_dispatcher_host.h"
4856 #include "content/public/common/content_switches.h"
4857 #include "content/public/common/service_info.h"
4858+#include "content/public/common/service_names.mojom.h"
4859 #include "content/public/common/web_preferences.h"
4860 #include "ppapi/features/features.h"
4861 #include "services/device/public/interfaces/constants.mojom.h"
4862 #include "services/service_manager/public/cpp/interface_registry.h"
4863+#include "ui/base/resource/resource_bundle.h"
4864 #include "ui/display/display.h"
4865 #include "ui/native_theme/native_theme_switches.h"
4866
4867@@ -79,6 +84,8 @@
4868 #include "pepper/oxide_pepper_host_factory_browser.h"
4869 #endif
4870
4871+#include "shared/grit/oxide_resources.h"
4872+
4873 namespace oxide {
4874
4875 content::BrowserMainParts* ContentBrowserClient::CreateBrowserMainParts(
4876@@ -316,6 +323,23 @@ ContentBrowserClient::GetDevToolsManagerDelegate() {
4877 return new DevToolsManagerDelegate();
4878 }
4879
4880+void ContentBrowserClient::RegisterRenderFrameMojoInterfaces(
4881+ service_manager::InterfaceRegistry* registry,
4882+ content::RenderFrameHost* render_frame_host) {
4883+ DCHECK(registry);
4884+ registry->AddInterface(
4885+ base::Bind(&autofill::ContentAutofillDriverFactory::BindAutofillDriver,
4886+ render_frame_host));
4887+ registry->AddInterface(
4888+ base::Bind(&password_manager::ContentPasswordManagerDriverFactory::
4889+ BindPasswordManagerDriver,
4890+ render_frame_host));
4891+ registry->AddInterface(
4892+ base::Bind(&password_manager::ContentPasswordManagerDriverFactory::
4893+ BindSensitiveInputVisibilityService,
4894+ render_frame_host));
4895+}
4896+
4897 void ContentBrowserClient::RegisterInProcessServices(
4898 StaticServiceMap* services) {
4899 content::ServiceInfo device_info;
4900@@ -330,6 +354,23 @@ void ContentBrowserClient::RegisterInProcessServices(
4901 services->insert(std::make_pair(device::mojom::kServiceName, device_info));
4902 }
4903
4904+std::unique_ptr<base::Value> ContentBrowserClient::GetServiceManifestOverlay(
4905+ base::StringPiece name) {
4906+ int id = -1;
4907+ if (name == content::mojom::kBrowserServiceName) {
4908+ id = IDR_OXIDE_BROWSER_MANIFEST_OVERLAY;
4909+ } else if (name == content::mojom::kRendererServiceName) {
4910+ id = IDR_OXIDE_RENDERER_MANIFEST_OVERLAY;
4911+ }
4912+ if (id == -1) {
4913+ return nullptr;
4914+ }
4915+
4916+ base::StringPiece manifest_contents =
4917+ ui::ResourceBundle::GetSharedInstance().GetRawDataResource(id);
4918+ return base::JSONReader::Read(manifest_contents);
4919+}
4920+
4921 void ContentBrowserClient::DidCreatePpapiPlugin(content::BrowserPpapiHost* host) {
4922 #if BUILDFLAG(ENABLE_PLUGINS)
4923 host->GetPpapiHost()->AddHostFactoryFilter(
4924diff --git a/shared/browser/oxide_content_browser_client.h b/shared/browser/oxide_content_browser_client.h
4925index a55285a..2a963dc 100644
4926--- a/shared/browser/oxide_content_browser_client.h
4927+++ b/shared/browser/oxide_content_browser_client.h
4928@@ -1,5 +1,5 @@
4929 // vim:expandtab:shiftwidth=2:tabstop=2:
4930-// Copyright (C) 2013 Canonical Ltd.
4931+// Copyright (C) 2013-2016 Canonical Ltd.
4932
4933 // This library is free software; you can redistribute it and/or
4934 // modify it under the terms of the GNU Lesser General Public
4935@@ -35,7 +35,6 @@ class ResourceDispatcherHostDelegate;
4936 namespace oxide {
4937
4938 class BrowserPlatformIntegration;
4939-class ContentMainDelegate;
4940 class ResourceDispatcherHostDelegate;
4941
4942 class ContentBrowserClient final : public content::ContentBrowserClient {
4943@@ -104,7 +103,12 @@ class ContentBrowserClient final : public content::ContentBrowserClient {
4944 void OverrideWebkitPrefs(content::RenderViewHost* render_view_host,
4945 content::WebPreferences* prefs) override;
4946 content::DevToolsManagerDelegate* GetDevToolsManagerDelegate() override;
4947+ void RegisterRenderFrameMojoInterfaces(
4948+ service_manager::InterfaceRegistry* registry,
4949+ content::RenderFrameHost* render_frame_host) override;
4950 void RegisterInProcessServices(StaticServiceMap* services) override;
4951+ std::unique_ptr<base::Value> GetServiceManifestOverlay(
4952+ base::StringPiece name) override;
4953 void DidCreatePpapiPlugin(content::BrowserPpapiHost* browser_host) override;
4954 gpu::GpuControlList::OsType GetOsTypeOverrideForGpuDataManager(
4955 std::string* os_version) override;
4956diff --git a/shared/browser/oxide_user_agent_settings.cc b/shared/browser/oxide_user_agent_settings.cc
4957index a1fe391..2a8550f 100644
4958--- a/shared/browser/oxide_user_agent_settings.cc
4959+++ b/shared/browser/oxide_user_agent_settings.cc
4960@@ -1,5 +1,5 @@
4961 // vim:expandtab:shiftwidth=2:tabstop=2:
4962-// Copyright (C) 2013-2016 Canonical Ltd.
4963+// Copyright (C) 2013-2017 Canonical Ltd.
4964
4965 // This library is free software; you can redistribute it and/or
4966 // modify it under the terms of the GNU Lesser General Public
4967@@ -22,6 +22,7 @@
4968 #include "base/logging.h"
4969 #include "base/memory/singleton.h"
4970 #include "base/strings/stringprintf.h"
4971+#include "components/autofill/core/common/autofill_pref_names.h"
4972 #include "components/keyed_service/content/browser_context_dependency_manager.h"
4973 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
4974 #include "content/public/browser/browser_thread.h"
4975@@ -151,6 +152,10 @@ bool UserAgentSettingsIOData::GetDoNotTrack() const {
4976 return do_not_track_;
4977 }
4978
4979+struct UserAgentSettings::DelayedPrefs {
4980+ bool autofill_enabled = false;
4981+};
4982+
4983 UserAgentSettings::UserAgentSettings(BrowserContext* context)
4984 : context_(context),
4985 product_(base::StringPrintf("Chrome/%s", CHROME_VERSION_STRING)),
4986@@ -159,6 +164,13 @@ UserAgentSettings::UserAgentSettings(BrowserContext* context)
4987 UserAgentSettingsIOData* io_data =
4988 context_->GetIOData()->GetUserAgentSettings();
4989 io_data->user_agent_ = content::BuildUserAgentFromProduct(product_);
4990+
4991+ PrefService* prefs = context->GetPrefs();
4992+ if (prefs->GetInitializationStatus() ==
4993+ PrefService::INITIALIZATION_STATUS_WAITING) {
4994+ prefs->AddPrefInitObserver(
4995+ base::Bind(&UserAgentSettings::OnPrefsLoaded, base::Unretained(this)));
4996+ }
4997 }
4998
4999 UserAgentSettings::~UserAgentSettings() {}
5000@@ -207,6 +219,13 @@ void UserAgentSettings::RemoveObserver(UserAgentSettingsObserver* observer) {
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches