1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Andrei Volkov <av@netscape.com>
24 * Brian Stell <bstell@netscape.com>
25 * Peter Lubczynski <peterl@netscape.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
44 // XXXbz windowsx.h defines GetFirstChild, GetNextSibling,
45 // GetPrevSibling are macros, apparently... Eeevil. We have functions
46 // called that on some classes, so undef them.
53 #include "nsGUIEvent.h"
55 #include "nsIPluginInstancePeer.h"
56 #include "nsIPluginInstanceInternal.h"
57 #include "nsPluginSafety.h"
58 #include "nsPluginNativeWindow.h"
59 #include "nsThreadUtils.h"
60 #include "nsAutoPtr.h"
61 #include "nsTWeakRef.h"
63 static NS_DEFINE_CID(kCPluginManagerCID
, NS_PLUGINMANAGER_CID
); // needed for NS_TRY_SAFE_CALL
65 #define NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION "MozillaPluginWindowPropertyAssociation"
67 typedef nsTWeakRef
<class nsPluginNativeWindowWin
> PluginWindowWeakRef
;
70 * PLEvent handling code
72 class PluginWindowEvent
: public nsRunnable
{
75 void Init(const PluginWindowWeakRef
&ref
, HWND hWnd
, UINT msg
, WPARAM wParam
,
78 HWND
GetWnd() { return mWnd
; };
79 UINT
GetMsg() { return mMsg
; };
80 WPARAM
GetWParam() { return mWParam
; };
81 LPARAM
GetLParam() { return mLParam
; };
82 PRBool
InUse() { return (mWnd
!=NULL
); };
87 PluginWindowWeakRef mPluginWindowRef
;
94 PluginWindowEvent::PluginWindowEvent()
99 void PluginWindowEvent::Clear()
107 void PluginWindowEvent::Init(const PluginWindowWeakRef
&ref
, HWND aWnd
,
108 UINT aMsg
, WPARAM aWParam
, LPARAM aLParam
)
110 NS_ASSERTION(aWnd
!= NULL
, "invalid plugin event value");
111 NS_ASSERTION(mWnd
== NULL
, "event already in use");
112 mPluginWindowRef
= ref
;
120 * nsPluginNativeWindow Windows specific class declaration
124 nsPluginType_Unknown
= 0,
130 class nsPluginNativeWindowWin
: public nsPluginNativeWindow
{
132 nsPluginNativeWindowWin();
133 virtual ~nsPluginNativeWindowWin();
135 virtual nsresult
CallSetWindow(nsCOMPtr
<nsIPluginInstance
> &aPluginInstance
);
139 nsresult
SubclassAndAssociateWindow();
140 nsresult
UndoSubclassAndAssociateWindow();
145 WNDPROC
GetPrevWindowProc();
146 WNDPROC
GetWindowProc();
147 PluginWindowEvent
* GetPluginWindowEvent(HWND aWnd
,
153 WNDPROC mPrevWinProc
;
154 WNDPROC mPluginWinProc
;
155 PluginWindowWeakRef mWeakRef
;
156 nsRefPtr
<PluginWindowEvent
> mCachedPluginWindowEvent
;
159 nsPluginType mPluginType
;
162 static PRBool sInMessageDispatch
= PR_FALSE
;
163 static UINT sLastMsg
= 0;
165 static PRBool
ProcessFlashMessageDelayed(nsPluginNativeWindowWin
* aWin
,
166 HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
168 NS_ENSURE_TRUE(aWin
, NS_ERROR_NULL_POINTER
);
170 if (msg
!= WM_USER
+1)
171 return PR_FALSE
; // no need to delay
174 nsCOMPtr
<nsIRunnable
> pwe
= aWin
->GetPluginWindowEvent(hWnd
, msg
, wParam
, lParam
);
176 NS_DispatchToCurrentThread(pwe
);
182 class nsDelayedPopupsEnabledEvent
: public nsRunnable
185 nsDelayedPopupsEnabledEvent(nsIPluginInstanceInternal
*inst
)
192 nsCOMPtr
<nsIPluginInstanceInternal
> mInst
;
195 NS_IMETHODIMP
nsDelayedPopupsEnabledEvent::Run()
197 mInst
->PushPopupsEnabledState(PR_FALSE
);
202 * New plugin window procedure
204 static LRESULT CALLBACK
PluginWndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
206 nsPluginNativeWindowWin
* win
= (nsPluginNativeWindowWin
*)::GetProp(hWnd
, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION
);
210 // The DispatchEvent(NS_PLUGIN_ACTIVATE) below can trigger a reentrant focus
211 // event which might destroy us. Hold a strong ref on the plugin instance
212 // to prevent that, bug 374229.
213 nsCOMPtr
<nsIPluginInstance
> inst
;
214 win
->GetPluginInstance(inst
);
216 // check plugin mime type and cache whether it is Flash or not
217 // Flash will need special treatment later
218 if (win
->mPluginType
== nsPluginType_Unknown
) {
220 nsCOMPtr
<nsIPluginInstancePeer
> pip
;
221 inst
->GetPeer(getter_AddRefs(pip
));
223 nsMIMEType mimetype
= nsnull
;
224 pip
->GetMIMEType(&mimetype
);
226 if (!strcmp(mimetype
, "application/x-shockwave-flash"))
227 win
->mPluginType
= nsPluginType_Flash
;
228 else if (!strcmp(mimetype
, "audio/x-pn-realaudio-plugin"))
229 win
->mPluginType
= nsPluginType_Real
;
231 win
->mPluginType
= nsPluginType_Other
;
237 // Real may go into a state where it recursivly dispatches the same event
238 // when subclassed. If this is Real, lets examine the event and drop it
239 // on the floor if we get into this recursive situation. See bug 192914.
240 if (win
->mPluginType
== nsPluginType_Real
) {
242 if (sInMessageDispatch
&& (msg
== sLastMsg
)) {
244 printf("Dropping event %d for Real on the floor\n", msg
);
246 return PR_TRUE
; // prevents event dispatch
248 sLastMsg
= msg
; // no need to prevent dispatch
252 PRBool enablePopups
= PR_FALSE
;
254 // Activate/deactivate mouse capture on the plugin widget
255 // here, before we pass the Windows event to the plugin
256 // because its possible our widget won't get paired events
257 // (see bug 131007) and we'll look frozen. Note that this
258 // is also done in ChildWindow::DispatchMouseEvent.
262 case WM_RBUTTONDOWN
: {
263 nsCOMPtr
<nsIWidget
> widget
;
264 win
->GetPluginWidget(getter_AddRefs(widget
));
266 widget
->CaptureMouse(PR_TRUE
);
270 enablePopups
= PR_TRUE
;
275 nsCOMPtr
<nsIWidget
> widget
;
276 win
->GetPluginWidget(getter_AddRefs(widget
));
278 widget
->CaptureMouse(PR_FALSE
);
282 // Ignore repeating keydown messages...
283 if ((lParam
& 0x40000000) != 0) {
289 enablePopups
= PR_TRUE
;
294 case WM_MOUSEACTIVATE
: {
295 // If a child window of this plug-in is already focused,
296 // don't focus the parent to avoid focus dance.
297 // The following WM_SETFOCUS message will give the focus
298 // to the appropriate window anyway.
299 HWND focusedWnd
= ::GetFocus();
300 if (!::IsChild((HWND
)win
->window
, focusedWnd
)) {
301 // This seems to be the only way we're
302 // notified when a child window that doesn't have this handler proc
303 // (read as: windows created by plugins like Adobe Acrobat)
304 // has been activated via clicking.
305 // should be handled here because some plugins won't forward
306 // messages to original WinProc.
307 nsCOMPtr
<nsIWidget
> widget
;
308 win
->GetPluginWidget(getter_AddRefs(widget
));
310 nsFocusEvent
event(PR_TRUE
, NS_PLUGIN_ACTIVATE
, widget
);
311 nsEventStatus status
;
312 widget
->DispatchEvent(&event
, status
);
320 // RealPlayer can crash, don't process the message for those, see bug 328675
321 if (win
->mPluginType
== nsPluginType_Real
&& msg
== sLastMsg
)
323 // Make sure setfocus and killfocus get through
324 // even if they are eaten by the plugin
325 WNDPROC prevWndProc
= win
->GetPrevWindowProc();
327 ::CallWindowProc(prevWndProc
, hWnd
, msg
, wParam
, lParam
);
333 // Macromedia Flash plugin may flood the message queue with some special messages
334 // (WM_USER+1) causing 100% CPU consumption and GUI freeze, see mozilla bug 132759;
335 // we can prevent this from happening by delaying the processing such messages;
336 if (win
->mPluginType
== nsPluginType_Flash
) {
337 if (ProcessFlashMessageDelayed(win
, hWnd
, msg
, wParam
, lParam
))
343 nsCOMPtr
<nsIPluginInstanceInternal
> instInternal
;
346 nsCOMPtr
<nsIPluginInstanceInternal
> tmp
= do_QueryInterface(inst
);
348 if (tmp
&& !nsVersionOK(tmp
->GetPluginAPIVersion(),
349 NP_POPUP_API_VERSION
)) {
350 tmp
.swap(instInternal
);
352 instInternal
->PushPopupsEnabledState(PR_TRUE
);
356 sInMessageDispatch
= PR_TRUE
;
358 NS_TRY_SAFE_CALL_RETURN(res
,
359 ::CallWindowProc((WNDPROC
)win
->GetWindowProc(), hWnd
, msg
, wParam
, lParam
),
362 sInMessageDispatch
= PR_FALSE
;
365 // Popups are enabled (were enabled before the call to
366 // CallWindowProc()). Some plugins (at least the flash player)
367 // post messages from their key handlers etc that delay the actual
368 // processing, so we need to delay the disabling of popups so that
369 // popups remain enabled when the flash player ends up processing
370 // the actual key handlers. We do this by posting an event that
371 // does the disabling, this way our disabling will happen after
372 // the handlers in the plugin are done.
374 // Note that it's not fatal if any of this fails (which won't
375 // happen unless we're out of memory anyways) since the plugin
376 // code will pop any popup state pushed by this plugin on
379 nsCOMPtr
<nsIRunnable
> event
=
380 new nsDelayedPopupsEnabledEvent(instInternal
);
382 NS_DispatchToCurrentThread(event
);
390 * nsPluginNativeWindowWin implementation
392 nsPluginNativeWindowWin::nsPluginNativeWindowWin() : nsPluginNativeWindow()
394 // initialize the struct fields
402 mPluginWinProc
= NULL
;
403 mPluginType
= nsPluginType_Unknown
;
406 nsPluginNativeWindowWin::~nsPluginNativeWindowWin()
408 // clear weak reference to self to prevent any pending events from
409 // dereferencing this.
413 WNDPROC
nsPluginNativeWindowWin::GetPrevWindowProc()
418 WNDPROC
nsPluginNativeWindowWin::GetWindowProc()
420 return mPluginWinProc
;
423 NS_IMETHODIMP
PluginWindowEvent::Run()
425 nsPluginNativeWindowWin
*win
= mPluginWindowRef
.get();
429 HWND hWnd
= GetWnd();
433 nsCOMPtr
<nsIPluginInstance
> inst
;
434 win
->GetPluginInstance(inst
);
435 NS_TRY_SAFE_CALL_VOID(::CallWindowProc(win
->GetWindowProc(),
446 nsPluginNativeWindowWin::GetPluginWindowEvent(HWND aWnd
, UINT aMsg
, WPARAM aWParam
, LPARAM aLParam
)
454 PluginWindowEvent
*event
;
456 // We have the ability to alloc if needed in case in the future some plugin
457 // should post multiple PostMessages. However, this could lead to many
458 // alloc's per second which could become a performance issue. See bug 169247.
459 if (!mCachedPluginWindowEvent
)
461 event
= new PluginWindowEvent();
462 if (!event
) return nsnull
;
463 mCachedPluginWindowEvent
= event
;
465 else if (mCachedPluginWindowEvent
->InUse())
467 event
= new PluginWindowEvent();
468 if (!event
) return nsnull
;
472 event
= mCachedPluginWindowEvent
;
475 event
->Init(mWeakRef
, aWnd
, aMsg
, aWParam
, aLParam
);
479 nsresult
nsPluginNativeWindowWin::CallSetWindow(nsCOMPtr
<nsIPluginInstance
> &aPluginInstance
)
481 // check the incoming instance, null indicates that window is going away and we are
482 // not interested in subclassing business any more, undo and don't subclass
484 // WINCE does not subclass windows. See bug 300011 for the details.
486 if (!aPluginInstance
) {
487 UndoSubclassAndAssociateWindow();
491 // We need WndProc before plug-ins do subclass in nsPluginNativeWindow::CallSetWindow.
492 if (aPluginInstance
) {
493 WNDPROC currentWndProc
= (WNDPROC
)::GetWindowLong((HWND
)window
, GWL_WNDPROC
);
494 if (currentWndProc
!= PluginWndProc
)
495 mPrevWinProc
= currentWndProc
;
499 nsPluginNativeWindow::CallSetWindow(aPluginInstance
);
503 SubclassAndAssociateWindow();
511 nsresult
nsPluginNativeWindowWin::SubclassAndAssociateWindow()
513 if (type
!= nsPluginWindowType_Window
)
514 return NS_ERROR_FAILURE
;
516 HWND hWnd
= (HWND
)window
;
518 return NS_ERROR_FAILURE
;
520 // check if we need to re-subclass
521 WNDPROC currentWndProc
= (WNDPROC
)::GetWindowLong(hWnd
, GWL_WNDPROC
);
522 if (PluginWndProc
== currentWndProc
)
525 mPluginWinProc
= SubclassWindow(hWnd
, (LONG
)PluginWndProc
);
527 return NS_ERROR_FAILURE
;
529 nsPluginNativeWindowWin
* win
= (nsPluginNativeWindowWin
*)::GetProp(hWnd
, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION
);
530 NS_ASSERTION(!win
|| (win
== this), "plugin window already has property and this is not us");
532 if (!::SetProp(hWnd
, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION
, (HANDLE
)this))
533 return NS_ERROR_FAILURE
;
538 nsresult
nsPluginNativeWindowWin::UndoSubclassAndAssociateWindow()
540 // release plugin instance
541 SetPluginInstance(nsnull
);
543 // remove window property
544 HWND hWnd
= (HWND
)window
;
546 ::RemoveProp(hWnd
, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION
);
548 // restore the original win proc
549 // but only do this if this were us last time
550 if (mPluginWinProc
) {
551 WNDPROC currentWndProc
= (WNDPROC
)::GetWindowLong(hWnd
, GWL_WNDPROC
);
552 if (currentWndProc
== PluginWndProc
)
553 SubclassWindow(hWnd
, (LONG
)mPluginWinProc
);
560 nsresult
PLUG_NewPluginNativeWindow(nsPluginNativeWindow
** aPluginNativeWindow
)
562 NS_ENSURE_ARG_POINTER(aPluginNativeWindow
);
564 *aPluginNativeWindow
= new nsPluginNativeWindowWin();
566 return *aPluginNativeWindow
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
569 nsresult
PLUG_DeletePluginNativeWindow(nsPluginNativeWindow
* aPluginNativeWindow
)
571 NS_ENSURE_ARG_POINTER(aPluginNativeWindow
);
572 nsPluginNativeWindowWin
*p
= (nsPluginNativeWindowWin
*)aPluginNativeWindow
;