Matteo Ferretti
authorMatteo Ferretti <zer0@mozilla.com>
Fri, 30 Aug 2013 17:24:15 -0400
changeset 153923 b8295c37fe7b830843562217a6d440d2b60f81ba
parent 153922 5b70f87c3ab8ce2e4cced0c444f4334163b06254
child 153924 9dd5f0e7f716995180065dc1b1404aa83cd77693
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs886329
milestone25.0a2
Matteo Ferretti Bug 886329 - Fix use of <select> elements in Jetpack panels. r=gozala, a=bajaj
addon-sdk/source/lib/sdk/panel/utils.js
addon-sdk/source/lib/sdk/window/helpers.js
addon-sdk/source/test/test-panel.js
--- 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) {