[OS/2] Bug 516274 - fix plugin focus issues, r=pweilbacher

This commit is contained in:
Rich Walsh 2009-10-12 11:10:38 +02:00
parent e5e691bbe1
commit bdab68be3d
6 changed files with 161 additions and 89 deletions

View File

@ -1667,7 +1667,7 @@ nsFocusManager::RaiseWindow(nsPIDOMWindow* aWindow)
if (!aWindow || aWindow == mActiveWindow || aWindow == mWindowBeingLowered)
return;
#ifdef XP_WIN
#if defined(XP_WIN) || defined(XP_OS2)
// Windows would rather we focus the child widget, otherwise, the toplevel
// widget will always end up being focused. Fortunately, focusing the child
// widget will also have the effect of raising the window this widget is in.

View File

@ -23,6 +23,7 @@
* Andrei Volkov <av@netscape.com>
* Brian Stell <bstell@netscape.com>
* Peter Lubczynski <peterl@netscape.com>
* Rich Walsh <dragtext@e-vertise.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -180,7 +181,6 @@ private:
public:
// locals
PFNWP GetPrevWindowProc();
PFNWP GetWindowProc();
PluginWindowEvent* GetPluginWindowEvent(HWND aWnd,
ULONG aMsg,
@ -188,7 +188,6 @@ public:
MPARAM mp2);
private:
PFNWP mPrevWinProc;
PFNWP mPluginWinProc;
PluginWindowWeakRef mWeakRef;
nsRefPtr<PluginWindowEvent> mCachedPluginWindowEvent;
@ -299,15 +298,27 @@ static MRESULT EXPENTRY PluginWndProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM m
enablePopups = PR_TRUE;
break;
case WM_SETFOCUS:
case WM_FOCUSCHANGE:
case WM_FOCUSCHANGED:
case WM_ACTIVATE: {
// Make sure setfocus and focuschange get through
// even if they are eaten by the plugin
PFNWP prevWndProc = win->GetPrevWindowProc();
if (prevWndProc)
prevWndProc(hWnd, msg, mp1, mp2);
// When the child of a plugin gets the focus, nsWindow doesn't get
// a WM_FOCUSCHANGED msg, so plugin and window activation events
// don't happen. This fixes the problem by synthesizing a msg
// that makes it look like the plugin widget just got the focus.
case WM_FOCUSCHANGE: {
// Some plugins don't pass this msg on. If the default window proc
// doesn't receive it, window activation/deactivation won't happen.
WinDefWindowProc(hWnd, msg, mp1, mp2);
// If focus is being gained, and the plugin widget neither lost
// nor gained the focus, then a child just got it from some other
// window. Post a WM_FOCUSCHANGED msg that identifies the child
// as the window losing focus. After nsWindow::ActivatePlugin()
// activates the plugin, it will restore the focus to the child.
if (SHORT1FROMMP(mp2) && (HWND)mp1 != hWnd) {
HWND hFocus = WinQueryFocus(HWND_DESKTOP);
if (hFocus != hWnd) {
WinPostMsg(hWnd, WM_FOCUSCHANGED, (MPARAM)hFocus, mp2);
}
}
break;
}
}
@ -370,7 +381,6 @@ nsPluginNativeWindowOS2::nsPluginNativeWindowOS2() : nsPluginNativeWindow()
width = 0;
height = 0;
mPrevWinProc = NULL;
mPluginWinProc = NULL;
mPluginType = nsPluginType_Unknown;
@ -391,11 +401,6 @@ nsPluginNativeWindowOS2::~nsPluginNativeWindowOS2()
mWeakRef.forget();
}
PFNWP nsPluginNativeWindowOS2::GetPrevWindowProc()
{
return mPrevWinProc;
}
PFNWP nsPluginNativeWindowOS2::GetWindowProc()
{
return mPluginWinProc;
@ -468,14 +473,6 @@ nsresult nsPluginNativeWindowOS2::CallSetWindow(nsCOMPtr<nsIPluginInstance> &aPl
// not interested in subclassing business any more, undo and don't subclass
if (!aPluginInstance) {
UndoSubclassAndAssociateWindow();
mPrevWinProc = NULL;
}
// We need WndProc before plug-ins do subclass in nsPluginNativeWindow::CallSetWindow.
if (aPluginInstance) {
PFNWP currentWndProc = (PFNWP)::WinQueryWindowPtr((HWND)window, QWP_PFNWP);
if (currentWndProc != PluginWndProc)
mPrevWinProc = currentWndProc;
}
nsPluginNativeWindow::CallSetWindow(aPluginInstance);

View File

@ -70,6 +70,7 @@ nsFrameWindow::nsFrameWindow() : nsWindow()
{
fnwpDefFrame = 0;
mWindowType = eWindowType_toplevel;
mNeedActivation = PR_FALSE;
}
nsFrameWindow::~nsFrameWindow()
@ -322,6 +323,29 @@ nsresult nsFrameWindow::Show( PRBool bState)
return NS_OK;
}
// When WM_ACTIVATE is received with the "gaining activation" flag set,
// the frame's wndproc sets mNeedActivation. Later, when an nsWindow
// gets a WM_FOCUSCHANGED msg with the "gaining focus" flag set, it
// invokes this method on nsFrameWindow to fire an NS_ACTIVATE event.
void nsFrameWindow::ActivateTopLevelWidget()
{
// Don't fire event if we're minimized or else the window will
// be restored as soon as the user clicks on it. When the user
// explicitly restores it, SetSizeMode() will call this method.
if (mNeedActivation) {
PRInt32 sizeMode;
GetSizeMode(&sizeMode);
if (sizeMode != nsSizeMode_Minimized) {
mNeedActivation = PR_FALSE;
DEBUGFOCUS(NS_ACTIVATE);
DispatchFocus(NS_ACTIVATE);
}
}
return;
}
// Subclass for frame window
MRESULT EXPENTRY fnwpFrame( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
@ -468,11 +492,22 @@ MRESULT nsFrameWindow::FrameMessage( ULONG msg, MPARAM mp1, MPARAM mp2)
}
}
break;
// When the frame is activated, set a flag to be acted on after
// PM has finished changing focus. When deactivated, dispatch
// the event immediately because it doesn't affect the focus.
case WM_ACTIVATE:
DEBUGFOCUS(frame WM_ACTIVATE);
if (SHORT1FROMMP(mp1) &&
!(WinQueryWindowULong(mFrameWnd, QWL_STYLE) & WS_MINIMIZED)) {
bDone = DispatchFocus(NS_ACTIVATE);
DEBUGFOCUS(WM_ACTIVATE);
if (mp1) {
mNeedActivation = PR_TRUE;
} else {
mNeedActivation = PR_FALSE;
DEBUGFOCUS(NS_DEACTIVATE);
DispatchFocus(NS_DEACTIVATE);
// Prevent the frame from automatically focusing any window
// when it's reactivated. Let moz set the focus to avoid
// having non-widget children of plugins focused in error.
WinSetWindowULong(mFrameWnd, QWL_HWNDFOCUSSAVE, 0);
}
break;
}
@ -482,3 +517,4 @@ MRESULT nsFrameWindow::FrameMessage( ULONG msg, MPARAM mp1, MPARAM mp2)
return mresult;
}

View File

@ -61,6 +61,10 @@ class nsFrameWindow : public nsWindow
PFNWP fnwpDefFrame;
nsSize mSizeClient;
nsSize mSizeBorder;
PRBool mNeedActivation;
// Fires NS_ACTIVATE is mNeedActivation is set
virtual void ActivateTopLevelWidget();
// So we can create the frame, parent the client & position it right
virtual void RealDoCreate( HWND hwndP, nsWindow *aParent,

View File

@ -124,9 +124,6 @@ nsIWidget * gRollupWidget = nsnull;
PRBool gRollupConsumeRollupEvent = PR_FALSE;
////////////////////////////////////////////////////
PRBool gJustGotActivate = PR_FALSE;
PRBool gJustGotDeactivate = PR_FALSE;
////////////////////////////////////////////////////
// Mouse Clicks - static variable defintions
// for figuring out 1 - 3 Clicks
@ -185,6 +182,9 @@ static PRUint32 gDragStatus = 0;
#define FAPPCOMMAND_MASK 0xF000
#define GET_APPCOMMAND_LPARAM(lParam) ((USHORT)(HIUSHORT(lParam) & ~FAPPCOMMAND_MASK))
// used to identify plugin widgets (copied from nsPluginNativeWindowOS2.cpp)
#define NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION "MozillaPluginWindowPropertyAssociation"
//-------------------------------------------------------------------------
//
// nsWindow constructor
@ -1141,19 +1141,18 @@ NS_METHOD nsWindow::SetZIndex(PRInt32 aZIndex)
NS_IMETHODIMP nsWindow::SetSizeMode(PRInt32 aMode)
{
nsresult rv;
PRInt32 previousMode;
GetSizeMode(&previousMode);
// save the requested state
rv = nsBaseWidget::SetSizeMode(aMode);
// save the new state
nsresult rv = nsBaseWidget::SetSizeMode(aMode);
// this is part of a kludge to keep minimized windows from getting
// restored when they get the focus - we defer the activation event
// until the window has actually been restored; see WM_FOCUSCHANGED
if (gJustGotActivate) {
// Minimized windows would get restored involuntarily if we fired an
// NS_ACTIVATE when the user clicks on them. Instead, we defer the
// activation event until the window has explicitly been restored.
if (previousMode == nsSizeMode_Minimized && previousMode != aMode) {
DEBUGFOCUS(deferred NS_ACTIVATE);
gJustGotActivate = PR_FALSE;
gJustGotDeactivate = PR_FALSE;
DispatchFocus(NS_ACTIVATE);
ActivateTopLevelWidget();
}
// nothing to do in these cases
@ -2384,6 +2383,7 @@ void nsWindow::ConstrainZLevel(HWND *aAfter) {
NS_IF_RELEASE(event.mActualBelow);
}
//-----------------------------------------------------------------------
// 'Window procedure'
PRBool nsWindow::ProcessMessage( ULONG msg, MPARAM mp1, MPARAM mp2, MRESULT &rc)
@ -2499,12 +2499,10 @@ PRBool nsWindow::ProcessMessage( ULONG msg, MPARAM mp1, MPARAM mp2, MRESULT &rc)
case WM_BUTTON1DOWN:
if (!mIsScrollBar)
WinSetCapture( HWND_DESKTOP, mWnd);
result = DispatchMouseEvent( NS_MOUSE_BUTTON_DOWN, mp1, mp2);
DispatchMouseEvent( NS_MOUSE_BUTTON_DOWN, mp1, mp2);
// there's no need to clear this on button-up
gLastButton1Down.x = XFROMMP(mp1);
gLastButton1Down.y = YFROMMP(mp1);
WinSetActiveWindow(HWND_DESKTOP, mWnd);
result = PR_TRUE;
break;
case WM_BUTTON1UP:
if (!mIsScrollBar)
@ -2662,58 +2660,25 @@ PRBool nsWindow::ProcessMessage( ULONG msg, MPARAM mp1, MPARAM mp2, MRESULT &rc)
result = OnScroll( msg, mp1, mp2);
break;
case WM_ACTIVATE:
DEBUGFOCUS(WM_ACTIVATE);
if (mp1)
gJustGotActivate = PR_TRUE;
else
gJustGotDeactivate = PR_TRUE;
break;
// Do not act on WM_ACTIVATE - it is handled by nsFrameWindow.
// case WM_ACTIVATE:
// break;
// This msg is used to activate top-level and plugin widgets
// after PM is done changing the focus. We're only interested
// in windows gaining focus, not in those losing it.
case WM_FOCUSCHANGED:
{
DEBUGFOCUS(WM_FOCUSCHANGED);
// If the frame was activated earlier or mp1 is 0, dispatch
// focus & activation events. However, if the frame is minimized,
// defer activation and let SetSizeMode() dispatch it after the
// window has been restored by the user - otherwise, Show() will
// restore it involuntarily.
if (SHORT1FROMMP(mp2)) {
if (gJustGotActivate || mp1 == 0) {
HWND hActive = WinQueryActiveWindow( HWND_DESKTOP);
if (!(WinQueryWindowULong( hActive, QWL_STYLE) & WS_MINIMIZED)) {
DEBUGFOCUS(NS_ACTIVATE);
gJustGotActivate = PR_FALSE;
gJustGotDeactivate = PR_FALSE;
result = DispatchFocus(NS_ACTIVATE);
}
}
if ( WinIsChild( mWnd, HWNDFROMMP(mp1)) && mNextID == 1) {
DEBUGFOCUS(NS_PLUGIN_ACTIVATE);
result = DispatchFocus(NS_PLUGIN_ACTIVATE);
WinSetFocus(HWND_DESKTOP, mWnd);
}
ActivateTopLevelWidget();
ActivatePlugin(HWNDFROMMP(mp1));
}
// We are losing focus
else {
if (gJustGotDeactivate) {
DEBUGFOCUS(NS_DEACTIVATE);
gJustGotDeactivate = PR_FALSE;
result = DispatchFocus(NS_DEACTIVATE);
}
}
break;
}
case WM_WINDOWPOSCHANGED:
result = OnReposition( (PSWP) mp1);
break;
case WM_PRESPARAMCHANGED:
// This is really for font-change notifies. Do that first.
rc = GetPrevWP()( mWnd, msg, mp1, mp2);
@ -2735,6 +2700,73 @@ PRBool nsWindow::ProcessMessage( ULONG msg, MPARAM mp1, MPARAM mp2, MRESULT &rc)
return result;
}
//-------------------------------------------------------------------------
// When a window gets the focus, call nsFrameWindow's version of this
// method. It will fire an NS_ACTIVATE event on the top-level widget
// if appropriate.
void nsWindow::ActivateTopLevelWidget()
{
nsWindow * top = static_cast<nsWindow*>(GetTopLevelWidget());
if (top) {
top->ActivateTopLevelWidget();
}
return;
}
//-------------------------------------------------------------------------
// Fire an NS_PLUGIN_ACTIVATE event whenever a window associated with
// a plugin widget get the focus.
void nsWindow::ActivatePlugin(HWND aWnd)
{
// avoid acting on recursive WM_FOCUSCHANGED msgs
static PRBool inPluginActivate = FALSE;
if (inPluginActivate) {
return;
}
// This property is used by the plugin window to store a pointer
// to its plugin object. We just use it as a convenient marker.
if (!WinQueryProperty(mWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION)) {
return;
}
// Fire a plugin activation event on the plugin widget.
inPluginActivate = TRUE;
DEBUGFOCUS(NS_PLUGIN_ACTIVATE);
DispatchFocus(NS_PLUGIN_ACTIVATE);
// Activating the plugin moves the focus off the child that had it,
// so try to restore it. If the WM_FOCUSCHANGED msg was synthesized
// by the plugin, then mp1 contains the child window that lost focus.
// Otherwise, just move it to the plugin's first child unless this
// is the mplayer plugin - doing so will put us into an endless loop.
// Since its children belong to another process, use the PID as a test.
HWND hFocus = 0;
if (WinIsChild(aWnd, mWnd)) {
hFocus = aWnd;
} else {
hFocus = WinQueryWindow(mWnd, QW_TOP);
if (hFocus) {
PID pidFocus, pidThis;
TID tid;
WinQueryWindowProcess(hFocus, &pidFocus, &tid);
WinQueryWindowProcess(mWnd, &pidThis, &tid);
if (pidFocus != pidThis) {
hFocus = 0;
}
}
}
if (hFocus) {
WinSetFocus(HWND_DESKTOP, hFocus);
}
inPluginActivate = FALSE;
return;
}
// -----------------------------------------------------------------------
//

View File

@ -65,7 +65,7 @@ class imgIContainer;
//#define DEBUG_FOCUS
#ifdef DEBUG_FOCUS
#define DEBUGFOCUS(what) printf("[%x] "#what" (%d)\n", (int)this, mWindowIdentifier)
#define DEBUGFOCUS(what) fprintf(stderr, "[%8x] %8lx (%02d) "#what"\n", (int)this, mWnd, mWindowIdentifier)
#else
#define DEBUGFOCUS(what)
#endif
@ -206,6 +206,9 @@ protected:
// Return whether message has been processed.
virtual PRBool ProcessMessage( ULONG m, MPARAM p1, MPARAM p2, MRESULT &r);
void ActivatePlugin(HWND aWnd);
virtual void ActivateTopLevelWidget();
virtual PRBool OnPaint();
virtual void OnDestroy();
virtual PRBool OnReposition( PSWP pNewSwp);