Matteo Ferretti
Bug 886329 - Fix use of <select> elements in Jetpack panels. r=gozala, a=bajaj
--- a/addon-sdk/source/lib/sdk/panel/utils.js
+++ b/addon-sdk/source/lib/sdk/panel/utils.js
@@ -231,17 +231,22 @@ function make(document) {
};
let backgroundFrame = createFrame(addonWindow, frameOptions);
setupPanelFrame(backgroundFrame);
let viewFrame = createFrame(panel, frameOptions);
setupPanelFrame(viewFrame);
- function onDisplayChange({type}) {
+ function onDisplayChange({type, target}) {
+ // Events from child element like <select /> may propagate (dropdowns are
+ // popups too), in which case frame loader shouldn't be swapped.
+ // See Bug 886329
+ if (target !== this) return;
+
try { swapFrameLoaders(backgroundFrame, viewFrame); }
catch(error) { console.exception(error); }
events.emit(type, { subject: panel });
}
function onContentReady({target, type}) {
if (target === getContentDocument(panel)) {
style(panel);
--- a/addon-sdk/source/lib/sdk/window/helpers.js
+++ b/addon-sdk/source/lib/sdk/window/helpers.js
@@ -1,16 +1,17 @@
/* 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/. */
'use strict';
const { defer } = require('../core/promise');
const events = require('../system/events');
-const { open: openWindow, onFocus, getToplevelWindow } = require('./utils');
+const { open: openWindow, onFocus, getToplevelWindow,
+ isInteractive } = require('./utils');
function open(uri, options) {
return promise(openWindow.apply(null, arguments), 'load');
}
exports.open = open;
function close(window) {
// We shouldn't wait for unload, as it is dispatched
@@ -31,20 +32,32 @@ exports.close = close;
function focus(window) {
let p = onFocus(window);
window.focus();
return p;
}
exports.focus = focus;
+function ready(window) {
+ let { promise: result, resolve } = defer();
+
+ if (isInteractive(window))
+ resolve(window);
+ else
+ resolve(promise(window, 'DOMContentLoaded'));
+
+ return result;
+}
+exports.ready = ready;
+
function promise(target, evt, capture) {
let deferred = defer();
capture = !!capture;
target.addEventListener(evt, function eventHandler() {
target.removeEventListener(evt, eventHandler, capture);
deferred.resolve(target);
}, capture);
return deferred.promise;
}
-exports.promise = promise;
\ No newline at end of file
+exports.promise = promise;
--- a/addon-sdk/source/test/test-panel.js
+++ b/addon-sdk/source/test/test-panel.js
@@ -9,17 +9,17 @@ module.metadata = {
}
};
const { Cc, Ci } = require("chrome");
const { Loader } = require('sdk/test/loader');
const { LoaderWithHookedConsole } = require("sdk/test/loader");
const timer = require("sdk/timers");
const self = require('sdk/self');
-const { open, close, focus } = require('sdk/window/helpers');
+const { open, close, focus, ready } = require('sdk/window/helpers');
const { isPrivate } = require('sdk/private-browsing');
const { isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils');
const { defer, all } = require('sdk/core/promise');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const { getWindow } = require('sdk/panel/window');
const { pb } = require('./private-browsing/helper');
const { URL } = require('sdk/url');
@@ -891,16 +891,53 @@ exports['test passing DOM node as first
all(warned.promise, shown.promise).
then(loader.unload).
then(done, assert.fail)
panel.show(widgetNode);
};
+// This test is checking that `onpupshowing` events emitted by panel's children
+// are not considered.
+// See Bug 886329
+exports['test nested popups'] = function (assert, done) {
+ let loader = Loader(module);
+ let { Panel } = loader.require('sdk/panel');
+ let { getActiveView } = loader.require('sdk/view/core');
+ let url = '<select><option>1<option>2<option>3</select>';
+
+ let getContentWindow = panel => {
+ return getActiveView(panel).querySelector('iframe').contentWindow;
+ }
+
+ let panel = Panel({
+ contentURL: 'data:text/html;charset=utf-8,' + encodeURIComponent(url),
+ onShow: () => {
+ ready(getContentWindow(panel)).then(({ window, document }) => {
+ let select = document.querySelector('select');
+ let event = document.createEvent('UIEvent');
+
+ event.initUIEvent('popupshowing', true, true, window, null);
+ select.dispatchEvent(event);
+
+ assert.equal(
+ select,
+ getContentWindow(panel).document.querySelector('select'),
+ 'select is still loaded in panel'
+ );
+
+ done();
+ });
+ }
+ });
+
+ panel.show();
+};
+
if (isWindowPBSupported) {
exports.testGetWindow = function(assert, done) {
let activeWindow = getMostRecentBrowserWindow();
open(null, { features: {
toolbar: true,
chrome: true,
private: true
} }).then(function(window) {