Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / modules / plugin / base / src / nsPluginNativeWindowWin.cpp
blob27b74b160d01f3cfd97ae2b3e8c758c2184b0d71
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
13 * License.
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.
22 * Contributor(s):
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 ***** */
41 #include "windows.h"
42 #include "windowsx.h"
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.
47 #undef GetFirstChild
48 #undef GetNextSibling
49 #undef GetPrevSibling
51 #include "nsDebug.h"
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;
69 /**
70 * PLEvent handling code
72 class PluginWindowEvent : public nsRunnable {
73 public:
74 PluginWindowEvent();
75 void Init(const PluginWindowWeakRef &ref, HWND hWnd, UINT msg, WPARAM wParam,
76 LPARAM lParam);
77 void Clear();
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); };
84 NS_DECL_NSIRUNNABLE
86 protected:
87 PluginWindowWeakRef mPluginWindowRef;
88 HWND mWnd;
89 UINT mMsg;
90 WPARAM mWParam;
91 LPARAM mLParam;
94 PluginWindowEvent::PluginWindowEvent()
96 Clear();
99 void PluginWindowEvent::Clear()
101 mWnd = NULL;
102 mMsg = 0;
103 mWParam = 0;
104 mLParam = 0;
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;
113 mWnd = aWnd;
114 mMsg = aMsg;
115 mWParam = aWParam;
116 mLParam = aLParam;
120 * nsPluginNativeWindow Windows specific class declaration
123 typedef enum {
124 nsPluginType_Unknown = 0,
125 nsPluginType_Flash,
126 nsPluginType_Real,
127 nsPluginType_Other
128 } nsPluginType;
130 class nsPluginNativeWindowWin : public nsPluginNativeWindow {
131 public:
132 nsPluginNativeWindowWin();
133 virtual ~nsPluginNativeWindowWin();
135 virtual nsresult CallSetWindow(nsCOMPtr<nsIPluginInstance> &aPluginInstance);
137 private:
138 #ifndef WINCE
139 nsresult SubclassAndAssociateWindow();
140 nsresult UndoSubclassAndAssociateWindow();
141 #endif
143 public:
144 // locals
145 WNDPROC GetPrevWindowProc();
146 WNDPROC GetWindowProc();
147 PluginWindowEvent * GetPluginWindowEvent(HWND aWnd,
148 UINT aMsg,
149 WPARAM aWParam,
150 LPARAM aLParam);
152 private:
153 WNDPROC mPrevWinProc;
154 WNDPROC mPluginWinProc;
155 PluginWindowWeakRef mWeakRef;
156 nsRefPtr<PluginWindowEvent> mCachedPluginWindowEvent;
158 public:
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
173 // do stuff
174 nsCOMPtr<nsIRunnable> pwe = aWin->GetPluginWindowEvent(hWnd, msg, wParam, lParam);
175 if (pwe) {
176 NS_DispatchToCurrentThread(pwe);
177 return PR_TRUE;
179 return PR_FALSE;
182 class nsDelayedPopupsEnabledEvent : public nsRunnable
184 public:
185 nsDelayedPopupsEnabledEvent(nsIPluginInstanceInternal *inst)
186 : mInst(inst)
189 NS_DECL_NSIRUNNABLE
191 private:
192 nsCOMPtr<nsIPluginInstanceInternal> mInst;
195 NS_IMETHODIMP nsDelayedPopupsEnabledEvent::Run()
197 mInst->PushPopupsEnabledState(PR_FALSE);
198 return NS_OK;
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);
207 if (!win)
208 return TRUE;
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) {
219 if (inst) {
220 nsCOMPtr<nsIPluginInstancePeer> pip;
221 inst->GetPeer(getter_AddRefs(pip));
222 if (pip) {
223 nsMIMEType mimetype = nsnull;
224 pip->GetMIMEType(&mimetype);
225 if (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;
230 else
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)) {
243 #ifdef DEBUG
244 printf("Dropping event %d for Real on the floor\n", msg);
245 #endif
246 return PR_TRUE; // prevents event dispatch
247 } else {
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.
259 switch (msg) {
260 case WM_LBUTTONDOWN:
261 case WM_MBUTTONDOWN:
262 case WM_RBUTTONDOWN: {
263 nsCOMPtr<nsIWidget> widget;
264 win->GetPluginWidget(getter_AddRefs(widget));
265 if (widget)
266 widget->CaptureMouse(PR_TRUE);
267 break;
269 case WM_LBUTTONUP:
270 enablePopups = PR_TRUE;
272 // fall through
273 case WM_MBUTTONUP:
274 case WM_RBUTTONUP: {
275 nsCOMPtr<nsIWidget> widget;
276 win->GetPluginWidget(getter_AddRefs(widget));
277 if (widget)
278 widget->CaptureMouse(PR_FALSE);
279 break;
281 case WM_KEYDOWN:
282 // Ignore repeating keydown messages...
283 if ((lParam & 0x40000000) != 0) {
284 break;
287 // fall through
288 case WM_KEYUP:
289 enablePopups = PR_TRUE;
291 break;
293 #ifndef WINCE
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));
309 if (widget) {
310 nsFocusEvent event(PR_TRUE, NS_PLUGIN_ACTIVATE, widget);
311 nsEventStatus status;
312 widget->DispatchEvent(&event, status);
316 break;
318 case WM_SETFOCUS:
319 case WM_KILLFOCUS: {
320 // RealPlayer can crash, don't process the message for those, see bug 328675
321 if (win->mPluginType == nsPluginType_Real && msg == sLastMsg)
322 return TRUE;
323 // Make sure setfocus and killfocus get through
324 // even if they are eaten by the plugin
325 WNDPROC prevWndProc = win->GetPrevWindowProc();
326 if (prevWndProc)
327 ::CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam);
328 break;
330 #endif
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))
338 return TRUE;
341 LRESULT res = TRUE;
343 nsCOMPtr<nsIPluginInstanceInternal> instInternal;
345 if (enablePopups) {
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),
360 nsnull, inst);
362 sInMessageDispatch = PR_FALSE;
364 if (instInternal) {
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
377 // destruction.
379 nsCOMPtr<nsIRunnable> event =
380 new nsDelayedPopupsEnabledEvent(instInternal);
381 if (event) {
382 NS_DispatchToCurrentThread(event);
386 return res;
390 * nsPluginNativeWindowWin implementation
392 nsPluginNativeWindowWin::nsPluginNativeWindowWin() : nsPluginNativeWindow()
394 // initialize the struct fields
395 window = nsnull;
396 x = 0;
397 y = 0;
398 width = 0;
399 height = 0;
401 mPrevWinProc = NULL;
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.
410 mWeakRef.forget();
413 WNDPROC nsPluginNativeWindowWin::GetPrevWindowProc()
415 return mPrevWinProc;
418 WNDPROC nsPluginNativeWindowWin::GetWindowProc()
420 return mPluginWinProc;
423 NS_IMETHODIMP PluginWindowEvent::Run()
425 nsPluginNativeWindowWin *win = mPluginWindowRef.get();
426 if (!win)
427 return NS_OK;
429 HWND hWnd = GetWnd();
430 if (!hWnd)
431 return NS_OK;
433 nsCOMPtr<nsIPluginInstance> inst;
434 win->GetPluginInstance(inst);
435 NS_TRY_SAFE_CALL_VOID(::CallWindowProc(win->GetWindowProc(),
436 hWnd,
437 GetMsg(),
438 GetWParam(),
439 GetLParam()),
440 nsnull, inst);
441 Clear();
442 return NS_OK;
445 PluginWindowEvent *
446 nsPluginNativeWindowWin::GetPluginWindowEvent(HWND aWnd, UINT aMsg, WPARAM aWParam, LPARAM aLParam)
448 if (!mWeakRef) {
449 mWeakRef = this;
450 if (!mWeakRef)
451 return nsnull;
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;
470 else
472 event = mCachedPluginWindowEvent;
475 event->Init(mWeakRef, aWnd, aMsg, aWParam, aLParam);
476 return event;
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.
485 #ifndef WINCE
486 if (!aPluginInstance) {
487 UndoSubclassAndAssociateWindow();
488 mPrevWinProc = NULL;
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;
497 #endif
499 nsPluginNativeWindow::CallSetWindow(aPluginInstance);
501 #ifndef WINCE
502 if (aPluginInstance)
503 SubclassAndAssociateWindow();
504 #endif
506 return NS_OK;
509 #ifndef WINCE
511 nsresult nsPluginNativeWindowWin::SubclassAndAssociateWindow()
513 if (type != nsPluginWindowType_Window)
514 return NS_ERROR_FAILURE;
516 HWND hWnd = (HWND)window;
517 if (!hWnd)
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)
523 return NS_OK;
525 mPluginWinProc = SubclassWindow(hWnd, (LONG)PluginWndProc);
526 if (!mPluginWinProc)
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;
535 return NS_OK;
538 nsresult nsPluginNativeWindowWin::UndoSubclassAndAssociateWindow()
540 // release plugin instance
541 SetPluginInstance(nsnull);
543 // remove window property
544 HWND hWnd = (HWND)window;
545 if (IsWindow(hWnd))
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);
556 return NS_OK;
558 #endif // WINCE
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;
573 delete p;
574 return NS_OK;