Bug 1020865 - Implement the dialog displayed upon form.requestAutocomplete(). r=MattN
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Mon, 14 Jul 2014 13:51:56 +0100
changeset 215711 a599e3c677bfc328f3dffa7921847bd8be05c36e
parent 215710 3d74395f47e0465ed343500aec153c519e232990
child 215712 4b08f0ea3bb68b3763d39078b50aaf78a7942e97
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN
bugs1020865
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1020865 - Implement the dialog displayed upon form.requestAutocomplete(). r=MattN
b2g/installer/package-manifest.in
browser/installer/package-manifest.in
mobile/android/installer/package-manifest.in
toolkit/components/formautofill/FormAutofillContentService.js
toolkit/components/formautofill/FormAutofillIntegration.jsm
toolkit/components/formautofill/content/RequestAutocompleteUI.jsm
toolkit/components/formautofill/content/requestAutocomplete.js
toolkit/components/formautofill/content/requestAutocomplete.xhtml
toolkit/components/formautofill/jar.mn
toolkit/components/formautofill/moz.build
toolkit/components/formautofill/test/browser/browser.ini
toolkit/components/formautofill/test/browser/browser_ui_requestAutocomplete.js
toolkit/components/formautofill/test/chrome/chrome.ini
toolkit/components/formautofill/test/chrome/test_requestAutocomplete_cancel.html
toolkit/components/formautofill/test/chrome/test_requestAutocomplete_disabled.html
toolkit/components/formautofill/test/head_common.js
toolkit/components/formautofill/test/xpcshell/test_integration.js
toolkit/locales/en-US/chrome/formautofill/requestAutocomplete.dtd
toolkit/locales/jar.mn
toolkit/themes/osx/mozapps/jar.mn
toolkit/themes/shared/formautofill/requestAutocomplete.css
toolkit/themes/windows/mozapps/jar.mn
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -288,16 +288,17 @@
 @BINPATH@/components/captivedetect.xpt
 #endif
 @BINPATH@/components/shellservice.xpt
 @BINPATH@/components/shistory.xpt
 @BINPATH@/components/spellchecker.xpt
 @BINPATH@/components/storage.xpt
 @BINPATH@/components/telemetry.xpt
 @BINPATH@/components/toolkit_finalizationwitness.xpt
+@BINPATH@/components/toolkit_formautofill.xpt
 @BINPATH@/components/toolkit_osfile.xpt
 @BINPATH@/components/toolkitprofile.xpt
 #ifdef MOZ_ENABLE_XREMOTE
 @BINPATH@/components/toolkitremote.xpt
 #endif
 @BINPATH@/components/txtsvc.xpt
 @BINPATH@/components/txmgr.xpt
 #ifdef MOZ_USE_NATIVE_UCONV
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -295,16 +295,17 @@
 #ifdef MOZ_CAPTIVEDETECT
 @BINPATH@/components/captivedetect.xpt
 #endif
 @BINPATH@/browser/components/shellservice.xpt
 @BINPATH@/components/shistory.xpt
 @BINPATH@/components/spellchecker.xpt
 @BINPATH@/components/storage.xpt
 @BINPATH@/components/toolkit_finalizationwitness.xpt
+@BINPATH@/components/toolkit_formautofill.xpt
 @BINPATH@/components/toolkit_osfile.xpt
 @BINPATH@/components/toolkitprofile.xpt
 #ifdef MOZ_ENABLE_XREMOTE
 @BINPATH@/components/toolkitremote.xpt
 #endif
 @BINPATH@/components/txtsvc.xpt
 @BINPATH@/components/txmgr.xpt
 @BINPATH@/components/uconv.xpt
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -242,16 +242,17 @@
 @BINPATH@/components/captivedetect.xpt
 #endif
 @BINPATH@/components/shellservice.xpt
 @BINPATH@/components/shistory.xpt
 @BINPATH@/components/spellchecker.xpt
 @BINPATH@/components/storage.xpt
 @BINPATH@/components/telemetry.xpt
 @BINPATH@/components/toolkit_finalizationwitness.xpt
+@BINPATH@/components/toolkit_formautofill.xpt
 @BINPATH@/components/toolkit_osfile.xpt
 @BINPATH@/components/toolkitprofile.xpt
 #ifdef MOZ_ENABLE_XREMOTE
 @BINPATH@/components/toolkitremote.xpt
 #endif
 @BINPATH@/components/txtsvc.xpt
 @BINPATH@/components/txmgr.xpt
 @BINPATH@/components/uconv.xpt
--- a/toolkit/components/formautofill/FormAutofillContentService.js
+++ b/toolkit/components/formautofill/FormAutofillContentService.js
@@ -11,31 +11,53 @@
 
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "FormAutofill",
+                                  "resource://gre/modules/FormAutofill.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+                                  "resource://gre/modules/Task.jsm");
+
 function FormAutofillContentService() {
 }
 
 FormAutofillContentService.prototype = {
   classID: Components.ID("{ed9c2c3c-3f86-4ae5-8e31-10f71b0f19e6}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormAutofillContentService]),
 
   // nsIFormAutofillContentService
   requestAutocomplete: function (aForm, aWindow) {
-    Services.console.logStringMessage("requestAutocomplete not implemented.");
+    Task.spawn(function* () {
+      // Start processing the request asynchronously.  At the end, the "reason"
+      // variable will contain the outcome of the operation, where an empty
+      // string indicates that an unexpected exception occurred.
+      let reason = "";
+      try {
+        let ui = yield FormAutofill.integration.createRequestAutocompleteUI({});
+        let result = yield ui.show();
 
-    // We will return "disabled" for now.
-    let event = new aWindow.AutocompleteErrorEvent("autocompleteerror",
-                                                   { bubbles: true,
-                                                     reason: "disabled" });
+        // At present, we only have cancellation and success cases, since we
+        // don't do any validation or precondition check.
+        reason = result.canceled ? "cancel" : "success";
+      } catch (ex) {
+        Cu.reportError(ex);
+      }
 
-    // Ensure the event is always dispatched on the next tick.
-    Services.tm.currentThread.dispatch(() => aForm.dispatchEvent(event),
-                                       Ci.nsIThread.DISPATCH_NORMAL);
+      // The type of event depends on whether this is a success condition.
+      let event = (reason == "success")
+                  ? new aWindow.Event("autocomplete", { bubbles: true })
+                  : new aWindow.AutocompleteErrorEvent("autocompleteerror",
+                                                       { bubbles: true,
+                                                         reason: reason });
+
+      // Ensure the event is always dispatched on the next tick.
+      Services.tm.currentThread.dispatch(() => aForm.dispatchEvent(event),
+                                         Ci.nsIThread.DISPATCH_NORMAL);
+    }.bind(this)).catch(Cu.reportError);
   },
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FormAutofillContentService]);
--- a/toolkit/components/formautofill/FormAutofillIntegration.jsm
+++ b/toolkit/components/formautofill/FormAutofillIntegration.jsm
@@ -19,16 +19,18 @@ this.EXPORTED_SYMBOLS = [
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "RequestAutocompleteUI",
+                                  "resource://gre/modules/RequestAutocompleteUI.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
 
 /**
  * This module defines the default implementation of platform-specific functions
  * that can be overridden by the host application and by add-ons.
  */
 this.FormAutofillIntegration = {
@@ -38,11 +40,11 @@ this.FormAutofillIntegration = {
    * @param aProperties
    *        Provides the initial properties for the newly created object.
    *
    * @return {Promise}
    * @resolves The newly created RequestAutocompleteUI object.
    * @rejects JavaScript exception.
    */
   createRequestAutocompleteUI: Task.async(function* (aProperties) {
-    return {};
+    return new RequestAutocompleteUI(aProperties);
   }),
 };
new file mode 100644
--- /dev/null
+++ b/toolkit/components/formautofill/content/RequestAutocompleteUI.jsm
@@ -0,0 +1,52 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Handles the requestAutocomplete user interface.
+ */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = [
+  "RequestAutocompleteUI",
+];
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+                                  "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+                                  "resource://gre/modules/Task.jsm");
+
+/**
+ * Handles the requestAutocomplete user interface.
+ */
+this.RequestAutocompleteUI = function (aProperties) {
+}
+
+this.RequestAutocompleteUI.prototype = {
+  show: Task.async(function* () {
+    // Create a new promise and store the function that will resolve it.  This
+    // will be called by the UI once the selection has been made.
+    let resolveFn;
+    let uiPromise = new Promise(resolve => resolveFn = resolve);
+
+    // Wrap the callback function so that it survives XPCOM.
+    let args = { resolveFn: resolveFn };
+    args.wrappedJSObject = args;
+
+    // Open the window providing the function to call when it closes.
+    Services.ww.openWindow(null,
+                           "chrome://formautofill/content/requestAutocomplete.xhtml",
+                           "Toolkit:RequestAutocomplete",
+                           "chrome,dialog=no,resizable",
+                           args);
+
+    // Wait for the window to be closed and the operation confirmed.
+    return yield uiPromise;
+  }),
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/components/formautofill/content/requestAutocomplete.js
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Implementation of "requestAutocomplete.xhtml".
+ */
+
+"use strict";
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+                                  "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+                                  "resource://gre/modules/Task.jsm");
+
+const RequestAutocompleteDialog = {
+  resolveFn: null,
+
+  onLoad: function () {
+    Task.spawn(function* () {
+      this.resolveFn = window.arguments[0].wrappedJSObject.resolveFn;
+
+      window.sizeToContent();
+
+      Services.obs.notifyObservers(window,
+                                   "formautofill-window-initialized", "");
+    }.bind(this)).catch(Cu.reportError);
+  },
+
+  onAccept: function () {
+    window.close();
+    this.resolveFn({ email: "email@example.org" });
+  },
+
+  onCancel: function () {
+    window.close();
+    this.resolveFn({ canceled: true });
+  },
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/components/formautofill/content/requestAutocomplete.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!DOCTYPE html [
+  <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
+  %htmlDTD;
+  <!ENTITY % requestAutocompleteDTD SYSTEM "chrome://formautofill/locale/requestAutocomplete.dtd">
+  %requestAutocompleteDTD;
+  <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" >
+  %globalDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>requestAutocomplete demo window</title>
+    <link rel="stylesheet"
+          href="chrome://mozapps/skin/formautofill/requestAutocomplete.css" />
+    <script type="application/javascript;version=1.7"
+            src="chrome://formautofill/content/requestAutocomplete.js" />
+  </head>
+  <body dir="&locale.dir;" onload="RequestAutocompleteDialog.onLoad();">
+    <h1>requestAutocomplete</h1>
+    <p>This is a demo window.</p>
+    <input id="accept" type="button" value="(OK)"
+           onclick="RequestAutocompleteDialog.onAccept();" />
+    <input id="cancel" type="button" value="(Cancel)"
+           onclick="RequestAutocompleteDialog.onCancel();" />
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/formautofill/jar.mn
@@ -0,0 +1,8 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+toolkit.jar:
+% content formautofill %content/formautofill/
+  content/formautofill/requestAutocomplete.js      (content/requestAutocomplete.js)
+  content/formautofill/requestAutocomplete.xhtml   (content/requestAutocomplete.xhtml)
--- a/toolkit/components/formautofill/moz.build
+++ b/toolkit/components/formautofill/moz.build
@@ -24,11 +24,16 @@ XPIDL_SOURCES += [
 XPIDL_MODULE = 'toolkit_formautofill'
 
 EXTRA_COMPONENTS += [
     'formautofill.manifest',
     'FormAutofillContentService.js',
 ]
 
 EXTRA_JS_MODULES += [
+    'content/RequestAutocompleteUI.jsm',
     'FormAutofill.jsm',
     'FormAutofillIntegration.jsm',
 ]
+
+JAR_MANIFESTS += [
+    'jar.mn',
+]
--- a/toolkit/components/formautofill/test/browser/browser.ini
+++ b/toolkit/components/formautofill/test/browser/browser.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
 support-files =
   ../head_common.js
   head.js
 
 [browser_infrastructure.js]
+[browser_ui_requestAutocomplete.js]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/formautofill/test/browser/browser_ui_requestAutocomplete.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests the requestAutocomplete user interface.
+ */
+
+"use strict";
+
+/**
+ * Open the requestAutocomplete UI and test that selecting a profile results in
+ * the correct data being sent back to the opener.
+ */
+add_task(function* test_select_profile() {
+  // Request an e-mail address.
+  let data = { "": { "": { "email": null } } };
+  let { uiWindow, promiseResult } = yield FormAutofillTest.showUI(data);
+
+  // Accept the dialog.
+  let acceptButton = uiWindow.document.getElementById("accept");
+  EventUtils.synthesizeMouseAtCenter(acceptButton, {}, uiWindow);
+
+  let result = yield promiseResult;
+  Assert.equal(result.email, "email@example.org");
+});
+
+/**
+ * Open the requestAutocomplete UI and cancel the dialog.
+ */
+add_task(function* test_cancel() {
+  // Request an e-mail address.
+  let data = { "": { "": { "email": null } } };
+  let { uiWindow, promiseResult } = yield FormAutofillTest.showUI(data);
+
+  // Cancel the dialog.
+  let acceptButton = uiWindow.document.getElementById("cancel");
+  EventUtils.synthesizeMouseAtCenter(acceptButton, {}, uiWindow);
+
+  let result = yield promiseResult;
+  Assert.ok(result.canceled);
+});
+
+add_task(terminationTaskFn);
--- a/toolkit/components/formautofill/test/chrome/chrome.ini
+++ b/toolkit/components/formautofill/test/chrome/chrome.ini
@@ -1,7 +1,7 @@
 [DEFAULT]
 support-files =
   ../head_common.js
   head.js
 
 [test_infrastructure.html]
-[test_requestAutocomplete_disabled.html]
+[test_requestAutocomplete_cancel.html]
rename from toolkit/components/formautofill/test/chrome/test_requestAutocomplete_disabled.html
rename to toolkit/components/formautofill/test/chrome/test_requestAutocomplete_cancel.html
--- a/toolkit/components/formautofill/test/chrome/test_requestAutocomplete_disabled.html
+++ b/toolkit/components/formautofill/test/chrome/test_requestAutocomplete_cancel.html
@@ -4,28 +4,35 @@
 
 <form id="form">
 </form>
 
 <script type="application/javascript;version=1.7" src="head.js"></script>
 <script type="application/javascript;version=1.7">
 
 /*
- * Tests the cases where the requestAutocomplete method returns "disabled".
+ * Tests the response sent when requestAutocomplete is canceled by the user.
  */
 
 "use strict";
 
 /**
- * Tests the case where the feature is disabled globally.
+ * The requestAutocomplete UI will not be displayed during these tests.
  */
-add_task(function* test_disabled_globally() {
+add_task(function* test_initialize() {
+  FormAutofillTest.requestAutocompleteResponse = { canceled: true };
+});
+
+/**
+ * Tests the case where the feature is canceled.
+ */
+add_task(function* test_cancel() {
   let promise = TestUtils.waitForEvent($("form"), "autocompleteerror");
   $("form").requestAutocomplete();
   let errorEvent = yield promise;
 
-  Assert.equal(errorEvent.reason, "disabled");
+  Assert.equal(errorEvent.reason, "cancel");
 });
 
 add_task(terminationTaskFn);
 
 </script>
 </body></html>
--- a/toolkit/components/formautofill/test/head_common.js
+++ b/toolkit/components/formautofill/test/head_common.js
@@ -158,19 +158,82 @@ let TestUtils = {
     add_termination_task(function* () {
       if (yield OS.File.exists(path)) {
         yield OS.File.remove(path);
       }
     });
 
     return path;
   }),
-}
+};
+
+/* --- Local helpers --- */
+
+let FormAutofillTest = {
+  /**
+   * Stores the response that the next call to the mock requestAutocomplete UI
+   * will return to the requester, or null to enable displaying the default UI.
+   */
+  requestAutocompleteResponse: null,
+
+  /**
+   * Displays the requestAutocomplete user interface using the specified data.
+   *
+   * @param aFormAutofillData
+   *        Serializable object containing the set of requested fields.
+   *
+   * @return {Promise}
+   * @resolves An object with the following properties:
+   *           {
+   *             uiWindow: Reference to the initialized window.
+   *             promiseResult: Promise resolved by the UI when it closes.
+   *           }
+   */
+  showUI: Task.async(function* (aFormAutofillData) {
+    Output.print("Opening UI with data: " + JSON.stringify(aFormAutofillData));
+
+    // Wait for the initialization event before opening the window.
+    let promiseUIWindow =
+        TestUtils.waitForNotification("formautofill-window-initialized");
+    let ui = yield FormAutofill.integration.createRequestAutocompleteUI({});
+    let promiseResult = ui.show();
+
+    // The window is the subject of the observer notification.
+    return {
+      uiWindow: (yield promiseUIWindow)[0],
+      promiseResult: promiseResult,
+    };
+  }),
+};
 
 /* --- Initialization and termination functions common to all tests --- */
 
 add_task(function* test_common_initialize() {
   // We must manually enable the feature while testing.
   Services.prefs.setBoolPref("dom.forms.requestAutocomplete", true);
   add_termination_task(function* () {
     Services.prefs.clearUserPref("dom.forms.requestAutocomplete");
   });
+
+  // If required, we return a mock response instead of displaying the UI.
+  let mockIntegrationFn = base => ({
+    createRequestAutocompleteUI: Task.async(function* () {
+      // Call the base method to display the UI if override is not requested.
+      if (FormAutofillTest.requestAutocompleteResponse === null) {
+        return yield base.createRequestAutocompleteUI.apply(this, arguments);
+      }
+
+      // Return a mock RequestAutocompleteUI object.
+      return {
+        show: Task.async(function* () {
+          let response = FormAutofillTest.requestAutocompleteResponse;
+          Output.print("Mock UI response: " + JSON.stringify(response));
+          return response;
+        }),
+      };
+    }),
+  });
+
+  FormAutofill.registerIntegration(mockIntegrationFn);
+  add_termination_task(function* () {
+    FormAutofill.unregisterIntegration(mockIntegrationFn);
+  });
 });
--- a/toolkit/components/formautofill/test/xpcshell/test_integration.js
+++ b/toolkit/components/formautofill/test/xpcshell/test_integration.js
@@ -3,31 +3,40 @@
 
 /*
  * Tests overriding the FormAutofillIntegration module functions.
  */
 
 "use strict";
 
 /**
+ * The requestAutocomplete UI will not be displayed during these tests.
+ */
+add_task(function* test_initialize() {
+  FormAutofillTest.requestAutocompleteResponse = { canceled: true };
+});
+
+/**
  * Registers and unregisters an integration override function.
  */
 add_task(function* test_integration_override() {
   let overrideCalled = false;
 
   let newIntegrationFn = base => ({
     createRequestAutocompleteUI: Task.async(function* () {
-      yield base.createRequestAutocompleteUI.apply(this, arguments);
       overrideCalled = true;
+      return yield base.createRequestAutocompleteUI.apply(this, arguments);
     }),
   });
 
   FormAutofill.registerIntegration(newIntegrationFn);
   try {
-    yield FormAutofill.integration.createRequestAutocompleteUI({});
+    let ui = yield FormAutofill.integration.createRequestAutocompleteUI({});
+    let result = yield ui.show();
+    Assert.ok(result.canceled);
   } finally {
     FormAutofill.unregisterIntegration(newIntegrationFn);
   }
 
   Assert.ok(overrideCalled);
 });
 
 /**
@@ -36,25 +45,27 @@ add_task(function* test_integration_over
  */
 add_task(function* test_integration_override_error() {
   let overrideCalled = false;
 
   let errorIntegrationFn = base => { throw "Expected error." };
 
   let newIntegrationFn = base => ({
     createRequestAutocompleteUI: Task.async(function* () {
-      yield base.createRequestAutocompleteUI.apply(this, arguments);
       overrideCalled = true;
+      return yield base.createRequestAutocompleteUI.apply(this, arguments);
     }),
   });
 
   FormAutofill.registerIntegration(errorIntegrationFn);
   FormAutofill.registerIntegration(newIntegrationFn);
   try {
-    yield FormAutofill.integration.createRequestAutocompleteUI({});
+    let ui = yield FormAutofill.integration.createRequestAutocompleteUI({});
+    let result = yield ui.show();
+    Assert.ok(result.canceled);
   } finally {
     FormAutofill.unregisterIntegration(errorIntegrationFn);
     FormAutofill.unregisterIntegration(newIntegrationFn);
   }
 
   Assert.ok(overrideCalled);
 });
 
new file mode 100644
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/formautofill/requestAutocomplete.dtd
@@ -0,0 +1,5 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- This file has no entities because the feature is still experimental. -->
--- a/toolkit/locales/jar.mn
+++ b/toolkit/locales/jar.mn
@@ -116,16 +116,18 @@
   locale/@AB_CD@/mozapps/xpinstall/xpinstallConfirm.dtd           (%chrome/mozapps/extensions/xpinstallConfirm.dtd)
   locale/@AB_CD@/mozapps/xpinstall/xpinstallConfirm.properties    (%chrome/mozapps/extensions/xpinstallConfirm.properties)
 % locale alerts @AB_CD@ %locale/@AB_CD@/alerts/
   locale/@AB_CD@/alerts/alert.dtd                                (%chrome/alerts/alert.dtd)
   locale/@AB_CD@/alerts/notificationNames.properties             (%chrome/alerts/notificationNames.properties)
 % locale cookie @AB_CD@ %locale/@AB_CD@/cookie/
   locale/@AB_CD@/cookie/cookieAcceptDialog.dtd           (%chrome/cookie/cookieAcceptDialog.dtd)
   locale/@AB_CD@/cookie/cookieAcceptDialog.properties    (%chrome/cookie/cookieAcceptDialog.properties)
+% locale formautofill @AB_CD@ %locale/@AB_CD@/formautofill/
+  locale/@AB_CD@/formautofill/requestAutocomplete.dtd (%chrome/formautofill/requestAutocomplete.dtd)
 % locale passwordmgr @AB_CD@ %locale/@AB_CD@/passwordmgr/
   locale/@AB_CD@/passwordmgr/passwordmgr.properties (%chrome/passwordmgr/passwordmgr.properties)
   locale/@AB_CD@/passwordmgr/passwordManager.dtd    (%chrome/passwordmgr/passwordManager.dtd)
 % locale autoconfig @AB_CD@ %locale/@AB_CD@/autoconfig/
   locale/@AB_CD@/autoconfig/autoconfig.properties   (%chrome/autoconfig/autoconfig.properties)
 #ifdef MOZ_HELP_VIEWER
 % locale help @AB_CD@ %locale/@AB_CD@/help/
   locale/@AB_CD@/help/help.properties                     (%chrome/mozapps/help/help.properties)
--- a/toolkit/themes/osx/mozapps/jar.mn
+++ b/toolkit/themes/osx/mozapps/jar.mn
@@ -52,16 +52,17 @@ toolkit.jar:
   skin/classic/mozapps/extensions/update.css                      (extensions/update.css)
   skin/classic/mozapps/extensions/eula.css                        (extensions/eula.css)
   skin/classic/mozapps/extensions/blocklist.css                   (extensions/blocklist.css)
 * skin/classic/mozapps/extensions/newaddon.css                    (extensions/newaddon.css)
   skin/classic/mozapps/passwordmgr/key.png                        (passwordmgr/key.png)
   skin/classic/mozapps/passwordmgr/key-16.png                     (passwordmgr/key-16.png)
   skin/classic/mozapps/passwordmgr/key-16@2x.png                  (passwordmgr/key-16@2x.png)
   skin/classic/mozapps/passwordmgr/key-64.png                     (passwordmgr/key-64.png)
+  skin/classic/mozapps/formautofill/requestAutocomplete.css       (../../shared/formautofill/requestAutocomplete.css)
   skin/classic/mozapps/plugins/pluginProblem.css                  (../../shared/plugins/pluginProblem.css)
   skin/classic/mozapps/aboutNetworking.css                        (../../shared/aboutNetworking.css)
   skin/classic/mozapps/plugins/contentPluginActivate.png          (../../shared/plugins/contentPluginActivate.png)
   skin/classic/mozapps/plugins/contentPluginBlocked.png           (../../shared/plugins/contentPluginBlocked.png)
   skin/classic/mozapps/plugins/contentPluginClose.png             (../../shared/plugins/contentPluginClose.png)
   skin/classic/mozapps/plugins/contentPluginCrashed.png           (../../shared/plugins/contentPluginCrashed.png)
   skin/classic/mozapps/plugins/contentPluginDisabled.png          (../../shared/plugins/contentPluginDisabled.png)
   skin/classic/mozapps/plugins/contentPluginDownload.png          (../../shared/plugins/contentPluginDownload.png)
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/formautofill/requestAutocomplete.css
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+@import url(chrome://global/skin/inContentUI.css);
+
+:root {
+  height: 100%;
+  width: 100%;
+  padding: 0;
+}
--- a/toolkit/themes/windows/mozapps/jar.mn
+++ b/toolkit/themes/windows/mozapps/jar.mn
@@ -56,16 +56,17 @@ toolkit.jar:
         skin/classic/mozapps/extensions/newaddon.css               (extensions/newaddon.css)
         skin/classic/mozapps/handling/handling.css                 (handling/handling.css)
         skin/classic/mozapps/passwordmgr/key.png                   (passwordmgr/key.png)
         skin/classic/mozapps/passwordmgr/key-16.png                (passwordmgr/key-16.png)
         skin/classic/mozapps/passwordmgr/key-64.png                (passwordmgr/key-64.png)
 #ifdef MOZ_PLACES
         skin/classic/mozapps/places/defaultFavicon.png             (places/defaultFavicon.png)
 #endif
+        skin/classic/mozapps/formautofill/requestAutocomplete.css  (../../shared/formautofill/requestAutocomplete.css)
         skin/classic/mozapps/plugins/pluginProblem.css             (../../shared/plugins/pluginProblem.css)
         skin/classic/mozapps/aboutNetworking.css                   (../../shared/aboutNetworking.css)
         skin/classic/mozapps/plugins/contentPluginActivate.png     (../../shared/plugins/contentPluginActivate.png)
         skin/classic/mozapps/plugins/contentPluginBlocked.png      (../../shared/plugins/contentPluginBlocked.png)
         skin/classic/mozapps/plugins/contentPluginClose.png        (../../shared/plugins/contentPluginClose.png)
         skin/classic/mozapps/plugins/contentPluginCrashed.png      (../../shared/plugins/contentPluginCrashed.png)
         skin/classic/mozapps/plugins/contentPluginDisabled.png     (../../shared/plugins/contentPluginDisabled.png)
         skin/classic/mozapps/plugins/contentPluginDownload.png     (../../shared/plugins/contentPluginDownload.png)
@@ -139,16 +140,17 @@ toolkit.jar:
 *       skin/classic/aero/mozapps/extensions/newaddon.css                  (extensions/newaddon-aero.css)
         skin/classic/aero/mozapps/handling/handling.css                    (handling/handling.css)
         skin/classic/aero/mozapps/passwordmgr/key.png                      (passwordmgr/key.png)
         skin/classic/aero/mozapps/passwordmgr/key-16.png                   (passwordmgr/key-16.png)
         skin/classic/aero/mozapps/passwordmgr/key-64.png                   (passwordmgr/key-64.png)
 #ifdef MOZ_PLACES
         skin/classic/aero/mozapps/places/defaultFavicon.png                (places/defaultFavicon.png)
 #endif
+        skin/classic/aero/mozapps/formautofill/requestAutocomplete.css     (../../shared/formautofill/requestAutocomplete.css)
         skin/classic/aero/mozapps/plugins/pluginProblem.css                (../../shared/plugins/pluginProblem.css)
         skin/classic/aero/mozapps/aboutNetworking.css                           (../../shared/aboutNetworking.css)
         skin/classic/aero/mozapps/plugins/contentPluginActivate.png        (../../shared/plugins/contentPluginActivate.png)
         skin/classic/aero/mozapps/plugins/contentPluginBlocked.png         (../../shared/plugins/contentPluginBlocked.png)
         skin/classic/aero/mozapps/plugins/contentPluginClose.png           (../../shared/plugins/contentPluginClose.png)
         skin/classic/aero/mozapps/plugins/contentPluginCrashed.png         (../../shared/plugins/contentPluginCrashed.png)
         skin/classic/aero/mozapps/plugins/contentPluginDisabled.png        (../../shared/plugins/contentPluginDisabled.png)
         skin/classic/aero/mozapps/plugins/contentPluginDownload.png        (../../shared/plugins/contentPluginDownload.png)