Back to index

lightning-sunbird  0.9+nobinonly
nsPluginNativeWindowWin.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Andrei Volkov <av@netscape.com>
00024  *   Brian Stell <bstell@netscape.com>
00025  *   Peter Lubczynski <peterl@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "windows.h"
00042 #include "windowsx.h"
00043 
00044 // XXXbz windowsx.h defines GetFirstChild, GetNextSibling,
00045 // GetPrevSibling are macros, apparently... Eeevil.  We have functions
00046 // called that on some classes, so undef them.
00047 #undef GetFirstChild
00048 #undef GetNextSibling
00049 #undef GetPrevSibling
00050 
00051 #include "nsDebug.h"
00052 
00053 #include "plevent.h"
00054 #include "nsIEventQueueService.h"
00055 
00056 #include "nsIPluginInstancePeer.h"
00057 #include "nsIPluginInstanceInternal.h"
00058 #include "nsPluginSafety.h"
00059 #include "nsPluginNativeWindow.h"
00060 
00061 static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
00062 static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID); // needed for NS_TRY_SAFE_CALL
00063 
00064 #define NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION "MozillaPluginWindowPropertyAssociation"
00065 
00069 class PluginWindowEvent : public PLEvent {
00070 public:
00071   PluginWindowEvent();
00072   void Init(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
00073   void Clear();
00074   HWND   GetWnd()    { return mWnd; };
00075   UINT   GetMsg()    { return mMsg; };
00076   WPARAM GetWParam() { return mWParam; };
00077   LPARAM GetLParam() { return mLParam; };
00078   PRBool GetIsAlloced() { return mIsAlloced; };
00079   void   SetIsAlloced(PRBool aIsAlloced) { mIsAlloced = aIsAlloced; };
00080   PRBool InUse() { return (mWnd!=NULL || mMsg!=0); };
00081 
00082 protected:
00083   HWND   mWnd;
00084   UINT   mMsg;
00085   WPARAM mWParam;
00086   LPARAM mLParam;
00087   PRBool mIsAlloced;
00088 };
00089 
00090 PluginWindowEvent::PluginWindowEvent()
00091 {
00092   Clear();
00093 }
00094 
00095 void PluginWindowEvent::Clear()
00096 {
00097   mWnd    = NULL;
00098   mMsg    = 0;
00099   mWParam = 0;
00100   mLParam = 0;
00101 }
00102 
00103 void PluginWindowEvent::Init(HWND aWnd, UINT aMsg, WPARAM aWParam, LPARAM aLParam)
00104 {
00105   NS_ASSERTION(aWnd!=NULL && aMsg!=0, "invalid plugin event value");
00106   NS_ASSERTION(mWnd==NULL && mMsg==0 && mWParam==0 && mLParam==0,"event already in use");
00107   mWnd    = aWnd;
00108   mMsg    = aMsg;
00109   mWParam = aWParam;
00110   mLParam = aLParam;
00111 }
00112 
00117 typedef enum {
00118   nsPluginType_Unknown = 0,
00119   nsPluginType_Flash,
00120   nsPluginType_Real,
00121   nsPluginType_Other
00122 } nsPluginType;
00123 
00124 class nsPluginNativeWindowWin : public nsPluginNativeWindow {
00125 public: 
00126   nsPluginNativeWindowWin();
00127   virtual ~nsPluginNativeWindowWin();
00128 
00129   virtual nsresult CallSetWindow(nsCOMPtr<nsIPluginInstance> &aPluginInstance);
00130 
00131 private:
00132 #ifndef WINCE
00133   nsresult SubclassAndAssociateWindow();
00134   nsresult UndoSubclassAndAssociateWindow();
00135 #endif
00136 
00137 public:
00138   // locals
00139   WNDPROC GetWindowProc();
00140   nsIEventQueueService *GetEventService();
00141   PluginWindowEvent * GetPluginWindowEvent(HWND aWnd, UINT aMsg, WPARAM aWParam, LPARAM aLParam);
00142 
00143 private:
00144   WNDPROC mPluginWinProc;
00145   nsCOMPtr<nsIEventQueueService> mEventService;
00146   PluginWindowEvent mPluginWindowEvent;
00147 
00148 public:
00149   nsPluginType mPluginType;
00150 };
00151 
00152 static PRBool sInMessageDispatch = PR_FALSE;
00153 static UINT sLastMsg = 0;
00154 
00155 static PRBool ProcessFlashMessageDelayed(nsPluginNativeWindowWin * aWin, 
00156                                          HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
00157 {
00158   NS_ENSURE_TRUE(aWin, NS_ERROR_NULL_POINTER);
00159 
00160   if (msg != WM_USER+1)
00161     return PR_FALSE; // no need to delay
00162 
00163   // do stuff
00164   nsIEventQueueService *eventService = aWin->GetEventService();
00165   if (eventService) {
00166     nsCOMPtr<nsIEventQueue> eventQueue;  
00167     eventService->GetThreadEventQueue(PR_GetCurrentThread(),
00168                                       getter_AddRefs(eventQueue));
00169     if (eventQueue) {
00170       PluginWindowEvent *pwe = aWin->GetPluginWindowEvent(hWnd, msg, wParam, lParam);
00171       if (pwe) {
00172         eventQueue->PostEvent(pwe);
00173         return PR_TRUE;  
00174       }
00175     }
00176   }
00177   return PR_FALSE;
00178 }
00179 
00180 PR_STATIC_CALLBACK(void*)
00181 DelayedPopupsEnabledEvent_Handle(PLEvent *event)
00182 {
00183   nsIPluginInstanceInternal *instInternal =
00184     (nsIPluginInstanceInternal *)event->owner;
00185 
00186   instInternal->PushPopupsEnabledState(PR_FALSE);
00187 
00188   return nsnull;
00189 }
00190 
00191 PR_STATIC_CALLBACK(void)
00192 DelayedPopupsEnabledEvent_Destroy(PLEvent *event)
00193 {
00194   nsIPluginInstanceInternal *instInternal =
00195     (nsIPluginInstanceInternal *)event->owner;
00196 
00197   NS_RELEASE(instInternal);
00198 
00199   delete event;
00200 }
00201 
00205 static LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
00206 {
00207   nsPluginNativeWindowWin * win = (nsPluginNativeWindowWin *)::GetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
00208   if (!win)
00209     return TRUE;
00210 
00211   // check plugin myme type and cache whether it is Flash or not
00212   // Flash will need special treatment later
00213   if (win->mPluginType == nsPluginType_Unknown) {
00214     nsCOMPtr<nsIPluginInstance> inst;
00215     win->GetPluginInstance(inst);
00216     if (inst) {
00217       nsCOMPtr<nsIPluginInstancePeer> pip;
00218       inst->GetPeer(getter_AddRefs(pip));
00219       if (pip) {
00220         nsMIMEType mimetype = nsnull;
00221         pip->GetMIMEType(&mimetype);
00222         if (mimetype) { 
00223           if (!strcmp(mimetype, "application/x-shockwave-flash"))
00224             win->mPluginType = nsPluginType_Flash;
00225           else if (!strcmp(mimetype, "audio/x-pn-realaudio-plugin"))
00226             win->mPluginType = nsPluginType_Real;
00227           else
00228             win->mPluginType = nsPluginType_Other;
00229         }
00230       }
00231     }
00232   }
00233 
00234   // Real may go into a state where it recursivly dispatches the same event
00235   // when subclassed. If this is Real, lets examine the event and drop it
00236   // on the floor if we get into this recursive situation. See bug 192914.
00237   if (win->mPluginType == nsPluginType_Real) {
00238     
00239     if (sInMessageDispatch && (msg == sLastMsg)) {
00240 #ifdef DEBUG
00241       printf("Dropping event %d for Real on the floor\n", msg);
00242 #endif
00243       return PR_TRUE;  // prevents event dispatch
00244     } else {
00245       sLastMsg = msg;  // no need to prevent dispatch
00246     }
00247   }
00248 
00249   PRBool enablePopups = PR_FALSE;
00250 
00251   // Activate/deactivate mouse capture on the plugin widget
00252   // here, before we pass the Windows event to the plugin
00253   // because its possible our widget won't get paired events
00254   // (see bug 131007) and we'll look frozen. Note that this
00255   // is also done in ChildWindow::DispatchMouseEvent.
00256   switch (msg) {
00257     case WM_LBUTTONDOWN:
00258     case WM_MBUTTONDOWN:
00259     case WM_RBUTTONDOWN: {
00260       nsCOMPtr<nsIWidget> widget;
00261       win->GetPluginWidget(getter_AddRefs(widget));
00262       if (widget)
00263         widget->CaptureMouse(PR_TRUE);
00264       break;
00265     }
00266     case WM_LBUTTONUP:
00267       enablePopups = PR_TRUE;
00268 
00269       // fall through
00270     case WM_MBUTTONUP:
00271     case WM_RBUTTONUP: {
00272       nsCOMPtr<nsIWidget> widget;
00273       win->GetPluginWidget(getter_AddRefs(widget));
00274       if (widget)
00275         widget->CaptureMouse(PR_FALSE);
00276       break;
00277     }
00278     case WM_KEYDOWN:
00279       // Ignore repeating keydown messages...
00280       if ((lParam & 0x40000000) != 0) {
00281         break;
00282       }
00283 
00284       // fall through
00285     case WM_KEYUP:
00286       enablePopups = PR_TRUE;
00287 
00288       break;
00289   }
00290 
00291   // Macromedia Flash plugin may flood the message queue with some special messages
00292   // (WM_USER+1) causing 100% CPU consumption and GUI freeze, see mozilla bug 132759;
00293   // we can prevent this from happening by delaying the processing such messages;
00294   if (win->mPluginType == nsPluginType_Flash) {
00295     if (ProcessFlashMessageDelayed(win, hWnd, msg, wParam, lParam))
00296       return TRUE;
00297   }
00298 
00299   LRESULT res = TRUE;
00300 
00301   nsCOMPtr<nsIPluginInstanceInternal> instInternal;
00302   nsCOMPtr<nsIPluginInstance> inst;
00303   win->GetPluginInstance(inst);
00304 
00305   if (enablePopups) {
00306     nsCOMPtr<nsIPluginInstanceInternal> tmp = do_QueryInterface(inst);
00307 
00308     if (tmp && !nsVersionOK(tmp->GetPluginAPIVersion(),
00309                             NP_POPUP_API_VERSION)) {
00310       tmp.swap(instInternal);
00311 
00312       instInternal->PushPopupsEnabledState(PR_TRUE);
00313     }
00314   }
00315 
00316   sInMessageDispatch = PR_TRUE;
00317 
00318   NS_TRY_SAFE_CALL_RETURN(res, 
00319                           ::CallWindowProc((WNDPROC)win->GetWindowProc(), hWnd, msg, wParam, lParam),
00320                           nsnull, inst);
00321 
00322   sInMessageDispatch = PR_FALSE;
00323 
00324   if (instInternal) {
00325     // Popups are enabled (were enabled before the call to
00326     // CallWindowProc()). Some plugins (at least the flash player)
00327     // post messages from their key handlers etc that delay the actual
00328     // processing, so we need to delay the disabling of popups so that
00329     // popups remain enabled when the flash player ends up processing
00330     // the actual key handlers. We do this by posting an event that
00331     // does the disabling, this way our disabling will happen after
00332     // the handlers in the plugin are done.
00333 
00334     // Note that it's not fatal if any of this fails (which won't
00335     // happen unless we're out of memory anyways) since the plugin
00336     // code will pop any popup state pushed by this plugin on
00337     // destruction.
00338 
00339     nsIEventQueueService *eventService = win->GetEventService();
00340     if (eventService) {
00341       nsCOMPtr<nsIEventQueue> eventQueue;  
00342       eventService->GetThreadEventQueue(PR_GetCurrentThread(),
00343                                         getter_AddRefs(eventQueue));
00344       if (eventQueue) {
00345         PLEvent *event = new PLEvent;
00346 
00347         if (event) {
00348           nsIPluginInstanceInternal *eventInst = instInternal;
00349 
00350           // Make the event own the plugin instance.
00351           NS_ADDREF(eventInst);
00352 
00353           PL_InitEvent(event, eventInst, DelayedPopupsEnabledEvent_Handle,
00354                        DelayedPopupsEnabledEvent_Destroy);
00355 
00356           eventQueue->PostEvent(event);
00357         }
00358       }
00359     }
00360   }
00361 
00362   return res;
00363 }
00364 
00368 nsPluginNativeWindowWin::nsPluginNativeWindowWin() : nsPluginNativeWindow()
00369 {
00370   // initialize the struct fields
00371   window = nsnull; 
00372   x = 0; 
00373   y = 0; 
00374   width = 0; 
00375   height = 0; 
00376 
00377   mPluginWinProc = NULL;
00378   mPluginWindowEvent.SetIsAlloced(PR_FALSE);
00379   mPluginType = nsPluginType_Unknown;
00380 }
00381 
00382 nsPluginNativeWindowWin::~nsPluginNativeWindowWin()
00383 {
00384   // clear any pending events to avoid dangling pointers
00385   nsCOMPtr<nsIEventQueueService> eventService(do_GetService(kEventQueueServiceCID));
00386   if (eventService) {
00387     nsCOMPtr<nsIEventQueue> eventQueue;  
00388     eventService->GetThreadEventQueue(PR_GetCurrentThread(), getter_AddRefs(eventQueue));
00389     if (eventQueue) {
00390       eventQueue->RevokeEvents(this);
00391     }
00392   }
00393 }
00394 
00395 WNDPROC nsPluginNativeWindowWin::GetWindowProc()
00396 {
00397   return mPluginWinProc;
00398 }
00399 
00400 PR_STATIC_CALLBACK(void*)
00401 PluginWindowEvent_Handle(PLEvent* self)
00402 {
00403   if (!self)
00404     return nsnull;
00405 
00406   PluginWindowEvent *event = NS_STATIC_CAST(PluginWindowEvent*, self);
00407   
00408   HWND hWnd = event->GetWnd();
00409   if (!hWnd)
00410     return nsnull;
00411 
00412   nsPluginNativeWindowWin * win = (nsPluginNativeWindowWin *)::GetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
00413   if (win) {
00414     nsCOMPtr<nsIPluginInstance> inst;
00415     win->GetPluginInstance(inst);
00416     NS_TRY_SAFE_CALL_VOID(::CallWindowProc(win->GetWindowProc(), 
00417                           hWnd, 
00418                           event->GetMsg(), 
00419                           event->GetWParam(), 
00420                           event->GetLParam()),
00421                           nsnull, inst);
00422   }
00423 
00424   return nsnull;
00425 }
00426 
00427 PR_STATIC_CALLBACK(void)
00428 PluginWindowEvent_Destroy(PLEvent* self)
00429 {
00430   if (!self)
00431     return;
00432 
00433   PluginWindowEvent *event = NS_STATIC_CAST(PluginWindowEvent*, self);
00434   if (event->GetIsAlloced()) {
00435     delete event;
00436   }
00437   else
00438     event->Clear();
00439 }
00440 
00441 nsIEventQueueService *nsPluginNativeWindowWin::GetEventService()
00442 {
00443   if (!mEventService) {
00444     mEventService = do_GetService(kEventQueueServiceCID);
00445   }
00446 
00447   return mEventService;
00448 }
00449 
00450 PluginWindowEvent*
00451 nsPluginNativeWindowWin::GetPluginWindowEvent(HWND aWnd, UINT aMsg, WPARAM aWParam, LPARAM aLParam)
00452 {
00453   PluginWindowEvent *event;
00454   if (mPluginWindowEvent.InUse()) {
00455     // We have the ability to alloc if needed in case in the future some plugin
00456     // should post multiple PostMessages. However, this could lead to many
00457     // alloc's per second which could become a performance issue. If/when this
00458     // is asserting then this needs to be studied. See bug 169247
00459     NS_ASSERTION(1, "possible plugin performance issue");
00460     event = new PluginWindowEvent();
00461     if (!event)
00462       return nsnull;
00463 
00464     event->SetIsAlloced(PR_TRUE);
00465   }
00466   else {
00467     event = &mPluginWindowEvent;
00468   }
00469 
00470   event->Init(aWnd, aMsg, aWParam, aLParam);
00471   PL_InitEvent(event, (void *)this, &PluginWindowEvent_Handle, PluginWindowEvent_Destroy);
00472   return event;
00473 }
00474 
00475 nsresult nsPluginNativeWindowWin::CallSetWindow(nsCOMPtr<nsIPluginInstance> &aPluginInstance)
00476 {
00477   // check the incoming instance, null indicates that window is going away and we are
00478   // not interested in subclassing business any more, undo and don't subclass
00479 
00480   // WINCE does not subclass windows.  See bug 300011 for the details.
00481 #ifndef WINCE
00482   if (!aPluginInstance)
00483     UndoSubclassAndAssociateWindow();
00484 #endif
00485 
00486   nsPluginNativeWindow::CallSetWindow(aPluginInstance);
00487 
00488 #ifndef WINCE
00489   if (aPluginInstance)
00490     SubclassAndAssociateWindow();
00491 #endif
00492 
00493   return NS_OK;
00494 }
00495 
00496 #ifndef WINCE
00497 
00498 nsresult nsPluginNativeWindowWin::SubclassAndAssociateWindow()
00499 {
00500   if (type != nsPluginWindowType_Window)
00501     return NS_ERROR_FAILURE;
00502 
00503   HWND hWnd = (HWND)window;
00504   if (!hWnd)
00505     return NS_ERROR_FAILURE;
00506 
00507   // check if we need to re-subclass
00508   WNDPROC currentWndProc = (WNDPROC)::GetWindowLong(hWnd, GWL_WNDPROC);
00509   if (PluginWndProc == currentWndProc)
00510     return NS_OK;
00511 
00512   mPluginWinProc = SubclassWindow(hWnd, (LONG)PluginWndProc);
00513   if (!mPluginWinProc)
00514     return NS_ERROR_FAILURE;
00515 
00516   nsPluginNativeWindowWin * win = (nsPluginNativeWindowWin *)::GetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
00517   NS_ASSERTION(!win || (win == this), "plugin window already has property and this is not us");
00518   
00519   if (!::SetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION, (HANDLE)this))
00520     return NS_ERROR_FAILURE;
00521 
00522   return NS_OK;
00523 }
00524 
00525 nsresult nsPluginNativeWindowWin::UndoSubclassAndAssociateWindow()
00526 {
00527   // release plugin instance
00528   SetPluginInstance(nsnull);
00529 
00530   // remove window property
00531   HWND hWnd = (HWND)window;
00532   if (IsWindow(hWnd))
00533     ::RemoveProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
00534 
00535   // restore the original win proc
00536   // but only do this if this were us last time
00537   if (mPluginWinProc) {
00538     WNDPROC currentWndProc = (WNDPROC)::GetWindowLong(hWnd, GWL_WNDPROC);
00539     if (currentWndProc == PluginWndProc)
00540       SubclassWindow(hWnd, (LONG)mPluginWinProc);
00541   }
00542 
00543   return NS_OK;
00544 }
00545 #endif // WINCE
00546 
00547 nsresult PLUG_NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow)
00548 {
00549   NS_ENSURE_ARG_POINTER(aPluginNativeWindow);
00550 
00551   *aPluginNativeWindow = new nsPluginNativeWindowWin();
00552 
00553   return *aPluginNativeWindow ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
00554 }
00555 
00556 nsresult PLUG_DeletePluginNativeWindow(nsPluginNativeWindow * aPluginNativeWindow)
00557 {
00558   NS_ENSURE_ARG_POINTER(aPluginNativeWindow);
00559   nsPluginNativeWindowWin *p = (nsPluginNativeWindowWin *)aPluginNativeWindow;
00560   delete p;
00561   return NS_OK;
00562 }