Bug 1342708 fix datalist failure in webext popups, r?kmag
MozReview-Commit-ID: FbTB9h3TTdq
--- a/browser/base/content/webext-panels.js
+++ b/browser/base/content/webext-panels.js
@@ -27,16 +27,17 @@ function getBrowser(sidebar) {
browser = document.createElementNS(XUL_NS, "browser");
browser.setAttribute("id", "webext-panels-browser");
browser.setAttribute("type", "content");
browser.setAttribute("flex", "1");
browser.setAttribute("disableglobalhistory", "true");
browser.setAttribute("webextension-view-type", "sidebar");
browser.setAttribute("context", "contentAreaContextMenu");
browser.setAttribute("tooltip", "aHTMLTooltip");
+ browser.setAttribute("autocompletepopup", "PopupAutoComplete");
browser.setAttribute("onclick", "window.parent.contentAreaClick(event, true);");
let readyPromise;
if (sidebar.remote) {
browser.setAttribute("remote", "true");
browser.setAttribute("remoteType",
E10SUtils.getRemoteTypeForURI(sidebar.uri, true,
E10SUtils.EXTENSION_REMOTE_TYPE));
--- a/browser/base/content/webext-panels.xul
+++ b/browser/base/content/webext-panels.xul
@@ -45,16 +45,24 @@
oncommand="getPanelBrowser().webNavigation.goForward();"
disabled="true"/>
<command id="Browser:Stop" oncommand="PanelBrowserStop();"/>
<command id="Browser:Reload" oncommand="PanelBrowserReload();"/>
</commandset>
<popupset id="mainPopupSet">
<tooltip id="aHTMLTooltip" page="true"/>
+
+ <panel type="autocomplete-richlistbox"
+ id="PopupAutoComplete"
+ noautofocus="true"
+ hidden="true"
+ overflowpadding="4"
+ norolluponanchor="true" />
+
<menupopup id="contentAreaContextMenu" pagemenu="start"
onpopupshowing="if (event.target != this)
return true;
gContextMenu = new nsContextMenu(this, event.shiftKey);
if (gContextMenu.shouldDisplay)
document.popupNode = this.triggerNode;
return gContextMenu.shouldDisplay;"
onpopuphiding="if (event.target != this)
--- a/browser/components/extensions/ExtensionPopups.jsm
+++ b/browser/components/extensions/ExtensionPopups.jsm
@@ -223,16 +223,17 @@ class BasePopup {
let browser = document.createElementNS(XUL_NS, "browser");
browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true");
browser.setAttribute("transparent", "true");
browser.setAttribute("class", "webextension-popup-browser");
browser.setAttribute("webextension-view-type", "popup");
browser.setAttribute("tooltip", "aHTMLTooltip");
browser.setAttribute("contextmenu", "contentAreaContextMenu");
+ browser.setAttribute("autocompletepopup", "PopupAutoComplete");
if (this.extension.remote) {
browser.setAttribute("remote", "true");
browser.setAttribute("remoteType", E10SUtils.EXTENSION_REMOTE_TYPE);
}
// We only need flex sizing for the sake of the slide-in sub-views of the
// main menu panel, so that the browser occupies the full width of the view,
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -1,8 +1,9 @@
[DEFAULT]
tags = webextensions in-process-webextensions
+[browser_ext_autocompletepopup.js]
[browser_ext_legacy_extension_context_contentscript.js]
[browser_ext_windows_allowScriptsToClose.js]
[include:browser-common.ini]
[parent:browser-common.ini]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_autocompletepopup.js
@@ -0,0 +1,84 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(function* testAutocompletePopup() {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ "browser_action": {
+ "default_popup": "page.html",
+ "browser_style": false,
+ },
+ "page_action": {
+ "default_popup": "page.html",
+ "browser_style": false,
+ },
+ },
+ background: async function() {
+ let [tab] = await browser.tabs.query({active: true, currentWindow: true});
+ await browser.pageAction.show(tab.id);
+ browser.test.sendMessage("ready");
+ },
+ files: {
+ "page.html": `<!DOCTYPE html>
+ <html>
+ <head><meta charset="utf-8"></head>
+ <body>
+ <div>
+ <input placeholder="Test input" id="test-input" list="test-list" />
+ <datalist id="test-list">
+ <option value="aa">
+ <option value="ab">
+ <option value="ae">
+ <option value="af">
+ <option value="ak">
+ <option value="am">
+ <option value="an">
+ <option value="ar">
+ </datalist>
+ </div>
+ </body>
+ </html>`,
+ },
+ });
+
+ function* testDatalist(browser, doc) {
+ let autocompletePopup = doc.getElementById("PopupAutoComplete");
+ let opened = promisePopupShown(autocompletePopup);
+ info("click in test-input now");
+ // two clicks to open
+ yield BrowserTestUtils.synthesizeMouseAtCenter("#test-input", {}, browser);
+ yield BrowserTestUtils.synthesizeMouseAtCenter("#test-input", {}, browser);
+ info("wait for opened event");
+ yield opened;
+ // third to close
+ let closed = promisePopupHidden(autocompletePopup);
+ info("click in test-input now");
+ yield BrowserTestUtils.synthesizeMouseAtCenter("#test-input", {}, browser);
+ info("wait for closed event");
+ yield closed;
+ // If this didn't work, we hang. Other tests deal with testing the actual functionality of datalist.
+ ok(true, "datalist popup has been shown");
+ }
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
+ yield extension.startup();
+ yield extension.awaitMessage("ready");
+
+ clickPageAction(extension);
+ // intentional misspell so eslint is ok with browser in background script.
+ let bowser = yield awaitExtensionPanel(extension);
+ ok(!!bowser, "panel opened with browser");
+ yield testDatalist(bowser, document);
+ closePageAction(extension);
+ yield new Promise(resolve => setTimeout(resolve, 0));
+
+ clickBrowserAction(extension);
+ bowser = yield awaitExtensionPanel(extension);
+ ok(!!bowser, "panel opened with browser");
+ yield testDatalist(bowser, document);
+ closeBrowserAction(extension);
+ yield new Promise(resolve => setTimeout(resolve, 0));
+
+ yield extension.unload();
+ yield BrowserTestUtils.removeTab(tab);
+});
--- a/toolkit/components/satchel/AutoCompletePopup.jsm
+++ b/toolkit/components/satchel/AutoCompletePopup.jsm
@@ -138,19 +138,18 @@ this.AutoCompletePopup = {
if (!results.length || this.openedPopup) {
// We shouldn't ever be showing an empty popup, and if we
// already have a popup open, the old one needs to close before
// we consider opening a new one.
return;
}
let window = browser.ownerGlobal;
- let tabbrowser = window.gBrowser;
- if (Services.focus.activeWindow != window ||
- tabbrowser.selectedBrowser != browser) {
+ // Also check window top in case this is a sidebar.
+ if (Services.focus.activeWindow !== window.top) {
// We were sent a message from a window or tab that went into the
// background, so we'll ignore it for now.
return;
}
this.weakBrowser = Cu.getWeakReference(browser);
this.openedPopup = browser.autoCompletePopup;
// the layout varies according to different result type
--- a/toolkit/content/browser-content.js
+++ b/toolkit/content/browser-content.js
@@ -1447,16 +1447,19 @@ let AutoCompletePopup = {
"FormAutoComplete:PopupClosed",
"FormAutoComplete:PopupOpened",
"FormAutoComplete:RequestFocus",
],
init() {
addEventListener("unload", this);
addEventListener("DOMContentLoaded", this);
+ // WebExtension browserAction is preloaded and does not receive DCL, wait
+ // on pageshow so we can hookup the formfill controller.
+ addEventListener("pageshow", this, true);
for (let messageName of this.MESSAGES) {
addMessageListener(messageName, this);
}
this._input = null;
this._popupOpen = false;
},
@@ -1464,40 +1467,53 @@ let AutoCompletePopup = {
destroy() {
if (this._connected) {
let controller = Cc["@mozilla.org/satchel/form-fill-controller;1"]
.getService(Ci.nsIFormFillController);
controller.detachFromBrowser(docShell);
this._connected = false;
}
+ removeEventListener("pageshow", this);
removeEventListener("unload", this);
removeEventListener("DOMContentLoaded", this);
for (let messageName of this.MESSAGES) {
removeMessageListener(messageName, this);
}
},
+ connect() {
+ if (this._connected) {
+ return;
+ }
+ // We need to wait for a content viewer to be available
+ // before we can attach our AutoCompletePopup handler,
+ // since nsFormFillController assumes one will exist
+ // when we call attachToBrowser.
+
+ // Hook up the form fill autocomplete controller.
+ let controller = Cc["@mozilla.org/satchel/form-fill-controller;1"]
+ .getService(Ci.nsIFormFillController);
+ controller.attachToBrowser(docShell,
+ this.QueryInterface(Ci.nsIAutoCompletePopup));
+ this._connected = true;
+ },
+
handleEvent(event) {
switch (event.type) {
+ case "pageshow": {
+ removeEventListener("pageshow", this);
+ this.connect();
+ break;
+ }
+
case "DOMContentLoaded": {
removeEventListener("DOMContentLoaded", this);
-
- // We need to wait for a content viewer to be available
- // before we can attach our AutoCompletePopup handler,
- // since nsFormFillController assumes one will exist
- // when we call attachToBrowser.
-
- // Hook up the form fill autocomplete controller.
- let controller = Cc["@mozilla.org/satchel/form-fill-controller;1"]
- .getService(Ci.nsIFormFillController);
- controller.attachToBrowser(docShell,
- this.QueryInterface(Ci.nsIAutoCompletePopup));
- this._connected = true;
+ this.connect();
break;
}
case "unload": {
this.destroy();
break;
}
}