Bug 435848, don't allow xul popups in minimized windows, fix window.windowState property and window.restore() on Mac, r=josh,sr=roc
authorNeil Deakin <neil@mozilla.com>
Wed, 10 Sep 2008 12:57:57 -0400
changeset 19112 de4f208c9413c7e98f878b96a8df67b9cafe92d4
parent 19111 78f592a0a109ef81b802dc5e8c3372420df650c4
child 19113 d25eff99e4e5e561dc1031eceff9e9ffb7047a32
push id1943
push userneil@mozilla.com
push dateWed, 10 Sep 2008 16:59:20 +0000
treeherderautoland@8a3c07d864ba [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjosh, roc
bugs435848
milestone1.9.1b1pre
Bug 435848, don't allow xul popups in minimized windows, fix window.windowState property and window.restore() on Mac, r=josh,sr=roc
layout/xul/base/src/nsXULPopupManager.cpp
toolkit/content/tests/chrome/window_popup_preventdefault_chrome.xul
widget/src/cocoa/nsCocoaWindow.h
widget/src/cocoa/nsCocoaWindow.mm
--- a/layout/xul/base/src/nsXULPopupManager.cpp
+++ b/layout/xul/base/src/nsXULPopupManager.cpp
@@ -1205,48 +1205,56 @@ nsXULPopupManager::MayShowPopup(nsMenuPo
   // if the popup was just rolled up, don't reopen it
   nsCOMPtr<nsIWidget> widget;
   aPopup->GetWidget(getter_AddRefs(widget));
   if (widget && widget->GetLastRollup() == aPopup->GetContent())
       return PR_FALSE;
 
   nsCOMPtr<nsISupports> cont = aPopup->PresContext()->GetContainer();
   nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(cont);
-  if (!dsti)
+  nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(dsti);
+  if (!baseWin)
     return PR_FALSE;
 
-  // chrome shells can always open popups, but other types of shells can only
-  // open popups when they are focused
   PRInt32 type = -1;
   if (NS_FAILED(dsti->GetItemType(&type)))
     return PR_FALSE;
 
+  // chrome shells can always open popups, but other types of shells can only
+  // open popups when they are focused and visible
   if (type != nsIDocShellTreeItem::typeChrome) {
+    // only allow popups in active windows
     nsCOMPtr<nsPIDOMWindow> win = do_GetInterface(dsti);
     if (!win)
       return PR_FALSE;
 
-    // only allow popups in active windows
     PRBool active;
     nsIFocusController* focusController = win->GetRootFocusController();
     focusController->GetActive(&active);
     if (!active)
       return PR_FALSE;
 
-    nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(dsti);
-    if (!baseWin)
-      return PR_FALSE;
-
     // only allow popups in visible frames
     PRBool visible;
     baseWin->GetVisibility(&visible);
     if (!visible)
       return PR_FALSE;
   }
 
+  // platforms respond differently when an popup is opened in a minimized
+  // window, so this is always disabled.
+  nsCOMPtr<nsIWidget> mainWidget;
+  baseWin->GetMainWidget(getter_AddRefs(mainWidget));
+  if (mainWidget) {
+    PRInt32 sizeMode;
+    mainWidget->GetSizeMode(&sizeMode);
+    if (sizeMode == nsSizeMode_Minimized)
+      return PR_FALSE;
+  }
+
   // cannot open a popup that is a submenu of a menupopup that isn't open.
   nsIFrame* parent = aPopup->GetParent();
   if (parent && parent->GetType() == nsGkAtoms::menuFrame) {
     nsMenuFrame* menuFrame = static_cast<nsMenuFrame *>(parent);
     nsIMenuParent* parentPopup = menuFrame->GetMenuParent();
     if (parentPopup && !parentPopup->IsOpen())
       return PR_FALSE;
   }
--- a/toolkit/content/tests/chrome/window_popup_preventdefault_chrome.xul
+++ b/toolkit/content/tests/chrome/window_popup_preventdefault_chrome.xul
@@ -1,32 +1,52 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 
 <window title="Popup Prevent Default Tests"
-  onload="setTimeout(runTest, 0);"
+  onfocus="if (!gDone) { gDone = true; setTimeout(runTest, 0); }"
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <title>Popup Prevent Default Tests</title>
 
 <!--
   This tests checks that preventDefault can be called on a popupshowing
   event or popuphiding event to prevent the default behaviour.
   -->
 
 <script>
 
+var gDone = false;
 var gBlockShowing = true;
 var gBlockHiding = true;
 var gShownNotAllowed = true;
 var gHiddenNotAllowed = true;
 
+var is = function(l, r, v) { window.opener.wrappedJSObject.SimpleTest.is(l, r, v); }
+
 function runTest()
 {
+  is(window.windowState, window.STATE_NORMAL, "window is normal");
+  window.minimize();
+  is(window.windowState, window.STATE_MINIMIZED, "window is minimized");
+
   document.getElementById("menu").open = true;
+
+  setTimeout(runTestAfterMinimize, 0);
+}
+
+function runTestAfterMinimize()
+{
+  var menu = document.getElementById("menu");
+  is(menu.firstChild.state, "closed", "popup not opened when window minimized");
+
+  window.restore();
+  is(window.windowState, window.STATE_NORMAL, "window is restored");
+
+  menu.open = true;
 }
 
 function popupShowing(event)
 {
   if (gBlockShowing) {
     event.preventDefault();
     gBlockShowing = false;
     setTimeout(function() {
--- a/widget/src/cocoa/nsCocoaWindow.h
+++ b/widget/src/cocoa/nsCocoaWindow.h
@@ -245,16 +245,19 @@ public:
     NS_IMETHOD SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight) { return NS_OK; }
     NS_IMETHOD DispatchEvent(nsGUIEvent* event, nsEventStatus & aStatus) ;
     NS_IMETHOD CaptureRollupEvents(nsIRollupListener * aListener, PRBool aDoCapture, PRBool aConsumeRollupEvent);
     NS_IMETHOD GetAttention(PRInt32 aCycleCount);
     virtual nsTransparencyMode GetTransparencyMode();
     virtual void SetTransparencyMode(nsTransparencyMode aMode);
     NS_IMETHOD SetWindowTitlebarColor(nscolor aColor, PRBool aActive);
 
+    // dispatch an NS_SIZEMODE event on miniaturize or deminiaturize
+    void DispatchSizeModeEvent(nsSizeMode aSizeMode);
+
     virtual gfxASurface* GetThebesSurface();
 
     // be notified that a some form of drag event needs to go into Gecko
     virtual PRBool DragEvent(unsigned int aMessage, Point aMouseGlobal, UInt16 aKeyModifiers);
 
     // Helpers to prevent recursive resizing during live-resize
     PRBool IsResizing () const { return mIsResizing; }
     void StartResizing () { mIsResizing = PR_TRUE; }
--- a/widget/src/cocoa/nsCocoaWindow.mm
+++ b/widget/src/cocoa/nsCocoaWindow.mm
@@ -904,17 +904,19 @@ NS_METHOD nsCocoaWindow::SetSizeMode(PRI
 
   PRInt32 previousMode;
   nsBaseWidget::GetSizeMode(&previousMode);
 
   nsresult rv = nsBaseWidget::SetSizeMode(aMode);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aMode == nsSizeMode_Normal) {
-    if (previousMode == nsSizeMode_Maximized && [mWindow isZoomed])
+    if ([mWindow isMiniaturized])
+      [mWindow deminiaturize:nil];
+    else if (previousMode == nsSizeMode_Maximized && [mWindow isZoomed])
       [mWindow zoom:nil];
   }
   else if (aMode == nsSizeMode_Minimized) {
     if (![mWindow isMiniaturized])
       [mWindow miniaturize:nil];
   }
   else if (aMode == nsSizeMode_Maximized) {
     if ([mWindow isMiniaturized])
@@ -1152,16 +1154,27 @@ nsCocoaWindow::DispatchEvent(nsGUIEvent*
 
   NS_IF_RELEASE(aWidget);
 
   return NS_OK;
 }
 
 
 void
+nsCocoaWindow::DispatchSizeModeEvent(nsSizeMode aSizeMode)
+{
+  nsSizeModeEvent event(PR_TRUE, NS_SIZEMODE, this);
+  event.mSizeMode = aSizeMode;
+  event.time = PR_IntervalNow();
+
+  nsEventStatus status = nsEventStatus_eIgnore;
+  DispatchEvent(&event, status);
+}
+
+void
 nsCocoaWindow::ReportSizeEvent(NSRect *r)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   NSRect windowFrame;
   if (r)
     windowFrame = [mWindow contentRectForFrameRect:(*r)];
   else
@@ -1553,16 +1566,30 @@ NS_IMETHODIMP nsCocoaWindow::EndSecureKe
 
 
 - (void)windowWillMiniaturize:(NSNotification *)aNotification
 {
   RollUpPopups();
 }
 
 
+- (void)windowDidMiniaturize:(NSNotification *)aNotification
+{
+  if (mGeckoWindow)
+    mGeckoWindow->DispatchSizeModeEvent(nsSizeMode_Minimized);
+}
+
+
+- (void)windowDidDeminiaturize:(NSNotification *)aNotification
+{
+  if (mGeckoWindow)
+    mGeckoWindow->DispatchSizeModeEvent(nsSizeMode_Normal);
+}
+
+
 - (void)sendFocusEvent:(PRUint32)eventType
 {
   if (!mGeckoWindow)
     return;
 
   nsEventStatus status = nsEventStatus_eIgnore;
   nsGUIEvent focusGuiEvent(PR_TRUE, eventType, mGeckoWindow);
   focusGuiEvent.time = PR_IntervalNow();