Back to index

lightning-sunbird  0.9+nobinonly
nsPluginNativeWindowOS2.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 #define INCL_WIN
00041 #include "os2.h"
00042 
00043 #include "nsDebug.h"
00044 
00045 #include "plevent.h"
00046 #include "nsIEventQueueService.h"
00047 
00048 #include "nsIPluginInstancePeer.h"
00049 #include "nsPluginSafety.h"
00050 #include "nsPluginNativeWindow.h"
00051 
00052 static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
00053 static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID); // needed for NS_TRY_SAFE_CALL
00054 
00055 #define NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION "MozillaPluginWindowPropertyAssociation"
00056 
00057 extern "C" {
00058 PVOID APIENTRY WinQueryProperty(HWND hwnd, PCSZ  pszNameOrAtom);
00059 
00060 PVOID APIENTRY WinRemoveProperty(HWND hwnd, PCSZ  pszNameOrAtom);
00061 
00062 BOOL  APIENTRY WinSetProperty(HWND hwnd, PCSZ  pszNameOrAtom,
00063                               PVOID pvData, ULONG ulFlags);
00064 }
00065 
00069 class PluginWindowEvent : public PLEvent {
00070 public:
00071   PluginWindowEvent();
00072   void Init(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2);
00073   void Clear();
00074   HWND   GetWnd()    { return mWnd; };
00075   ULONG   GetMsg()    { return mMsg; };
00076   MPARAM GetWParam() { return mWParam; };
00077   MPARAM 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   ULONG  mMsg;
00085   MPARAM mWParam;
00086   MPARAM 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, ULONG aMsg, MPARAM mp1, MPARAM mp2)
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 = mp1;
00110   mLParam = mp2;
00111 }
00112 
00117 typedef enum {
00118   nsPluginType_Unknown = 0,
00119   nsPluginType_Flash,
00120   nsPluginType_Java_vm,
00121   nsPluginType_Other
00122 } nsPluginType;
00123 
00124 class nsPluginNativeWindowOS2 : public nsPluginNativeWindow {
00125 public: 
00126   nsPluginNativeWindowOS2();
00127   virtual ~nsPluginNativeWindowOS2();
00128 
00129   virtual nsresult CallSetWindow(nsCOMPtr<nsIPluginInstance> &aPluginInstance);
00130 
00131 private:
00132   nsresult SubclassAndAssociateWindow();
00133   nsresult UndoSubclassAndAssociateWindow();
00134 
00135 public:
00136   // locals
00137   PFNWP GetWindowProc();
00138   nsresult GetEventService(nsCOMPtr<nsIEventQueueService> &aEventService);
00139   PluginWindowEvent * GetPluginWindowEvent(HWND aWnd, ULONG aMsg, MPARAM mp1, MPARAM mp2);
00140 
00141 private:
00142   PFNWP mPluginWinProc;
00143   nsCOMPtr<nsIEventQueueService> mEventService;
00144   PluginWindowEvent mPluginWindowEvent;
00145 
00146 public:
00147   nsPluginType mPluginType;
00148 };
00149 
00150 static PRBool ProcessFlashMessageDelayed(nsPluginNativeWindowOS2 * aWin, 
00151                                          HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2)
00152 {
00153   NS_ENSURE_TRUE(aWin, NS_ERROR_NULL_POINTER);
00154 
00155   if (msg != WM_USER+1)
00156     return PR_FALSE; // no need to delay
00157 
00158   // do stuff
00159   nsCOMPtr<nsIEventQueueService> eventService;
00160   if (NS_SUCCEEDED(aWin->GetEventService(eventService))) {
00161     nsCOMPtr<nsIEventQueue> eventQueue;  
00162     eventService->GetThreadEventQueue(PR_GetCurrentThread(), getter_AddRefs(eventQueue));
00163     if (eventQueue) {
00164       PluginWindowEvent *pwe = aWin->GetPluginWindowEvent(hWnd, msg, mp1, mp2);
00165       if (pwe) {
00166         eventQueue->PostEvent(pwe);
00167         return PR_TRUE;  
00168       }
00169     }
00170   }
00171   return PR_FALSE;
00172 }
00173 
00177 MRESULT EXPENTRY PluginWndProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2)
00178 {
00179   nsPluginNativeWindowOS2 * win = (nsPluginNativeWindowOS2 *)::WinQueryProperty(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
00180   if (!win)
00181     return (MRESULT)TRUE;
00182 
00183   // check plugin mime type and cache whether it is Flash or java-vm or not
00184   // flash and java-vm will need special treatment later
00185   if (win->mPluginType == nsPluginType_Unknown) {
00186     nsCOMPtr<nsIPluginInstance> inst;
00187     win->GetPluginInstance(inst);
00188     if (inst) {
00189       nsCOMPtr<nsIPluginInstancePeer> pip;
00190       inst->GetPeer(getter_AddRefs(pip));
00191       if (pip) {
00192         nsMIMEType mimetype = nsnull;
00193         pip->GetMIMEType(&mimetype);
00194         if (mimetype) { 
00195           if (!strcmp(mimetype, "application/x-shockwave-flash"))
00196             win->mPluginType = nsPluginType_Flash;
00197           else if (!strcmp(mimetype, "application/x-java-vm"))
00198             win->mPluginType = nsPluginType_Java_vm;
00199           else
00200             win->mPluginType = nsPluginType_Other;
00201         }
00202       }
00203     }
00204   }
00205 
00206   // Macromedia Flash plugin may flood the message queue with some special messages
00207   // (WM_USER+1) causing 100% CPU consumption and GUI freeze, see mozilla bug 132759;
00208   // we can prevent this from happening by delaying the processing such messages;
00209   if (win->mPluginType == nsPluginType_Flash) {
00210     if (ProcessFlashMessageDelayed(win, hWnd, msg, mp1, mp2))
00211       return (MRESULT)TRUE;
00212   }
00213 
00214   MRESULT res = (MRESULT)TRUE;
00215 
00216   nsCOMPtr<nsIPluginInstance> inst;
00217   win->GetPluginInstance(inst);
00218 
00219   if (win->mPluginType == nsPluginType_Java_vm) {
00220     NS_TRY_SAFE_CALL_RETURN(res, WinDefWindowProc(hWnd, msg, mp1, mp2), nsnull, inst);
00221   } else {
00222     NS_TRY_SAFE_CALL_RETURN(res, (win->GetWindowProc())(hWnd, msg, mp1, mp2), nsnull, inst);
00223   }
00224 
00225   return res;
00226 }
00227 
00231 nsPluginNativeWindowOS2::nsPluginNativeWindowOS2() : nsPluginNativeWindow()
00232 {
00233   // initialize the struct fields
00234   window = nsnull; 
00235   x = 0; 
00236   y = 0; 
00237   width = 0; 
00238   height = 0; 
00239 
00240   mPluginWinProc = NULL;
00241   mPluginWindowEvent.SetIsAlloced(PR_FALSE);
00242   mPluginType = nsPluginType_Unknown;
00243 }
00244 
00245 nsPluginNativeWindowOS2::~nsPluginNativeWindowOS2()
00246 {
00247   // clear any pending events to avoid dangling pointers
00248   nsCOMPtr<nsIEventQueueService> eventService(do_GetService(kEventQueueServiceCID));
00249   if (eventService) {
00250     nsCOMPtr<nsIEventQueue> eventQueue;  
00251     eventService->GetThreadEventQueue(PR_GetCurrentThread(), getter_AddRefs(eventQueue));
00252     if (eventQueue) {
00253       eventQueue->RevokeEvents(this);
00254     }
00255   }
00256 }
00257 
00258 PFNWP nsPluginNativeWindowOS2::GetWindowProc()
00259 {
00260   return mPluginWinProc;
00261 }
00262 
00263 PR_STATIC_CALLBACK(void*)
00264 PluginWindowEvent_Handle(PLEvent* self)
00265 {
00266   if (!self)
00267     return nsnull;
00268 
00269   PluginWindowEvent *event = NS_STATIC_CAST(PluginWindowEvent*, self);
00270   
00271   HWND hWnd = event->GetWnd();
00272   if (!hWnd)
00273     return nsnull;
00274 
00275   nsPluginNativeWindowOS2 * win = (nsPluginNativeWindowOS2 *)::WinQueryProperty(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
00276   if (win) {
00277     nsCOMPtr<nsIPluginInstance> inst;
00278     win->GetPluginInstance(inst);
00279     NS_TRY_SAFE_CALL_VOID((win->GetWindowProc())
00280                          (hWnd, 
00281                           event->GetMsg(), 
00282                           event->GetWParam(), 
00283                           event->GetLParam()),
00284                           nsnull, inst);
00285   }
00286 
00287   return nsnull;
00288 }
00289 
00290 PR_STATIC_CALLBACK(void)
00291 PluginWindowEvent_Destroy(PLEvent* self)
00292 {
00293   if (!self)
00294     return;
00295 
00296   PluginWindowEvent *event = NS_STATIC_CAST(PluginWindowEvent*, self);
00297   if (event->GetIsAlloced()) {
00298     delete event;
00299   }
00300   else
00301     event->Clear();
00302 }
00303 
00304 nsresult nsPluginNativeWindowOS2::GetEventService(nsCOMPtr<nsIEventQueueService> &aEventService)
00305 {
00306   if (!mEventService) {
00307     mEventService = do_GetService(kEventQueueServiceCID);
00308     if (!mEventService)
00309       return NS_ERROR_FAILURE;
00310   }
00311   aEventService = mEventService;
00312   return NS_OK;
00313 }
00314 
00315 PluginWindowEvent*
00316 nsPluginNativeWindowOS2::GetPluginWindowEvent(HWND aWnd, ULONG aMsg, MPARAM aMp1, MPARAM aMp2)
00317 {
00318   PluginWindowEvent *event;
00319   if (mPluginWindowEvent.InUse()) {
00320     // We have the ability to alloc if needed in case in the future some plugin
00321     // should post multiple PostMessages. However, this could lead to many
00322     // alloc's per second which could become a performance issue. If/when this
00323     // is asserting then this needs to be studied. See bug 169247
00324     NS_ASSERTION(1, "possible plugin performance issue");
00325     event = new PluginWindowEvent();
00326     if (!event)
00327       return nsnull;
00328 
00329     event->SetIsAlloced(PR_TRUE);
00330   }
00331   else {
00332     event = &mPluginWindowEvent;
00333   }
00334 
00335   event->Init(aWnd, aMsg, aMp1, aMp2);
00336   PL_InitEvent(event, (void *)this, &PluginWindowEvent_Handle, PluginWindowEvent_Destroy);
00337   return event;
00338 };
00339 
00340 nsresult nsPluginNativeWindowOS2::CallSetWindow(nsCOMPtr<nsIPluginInstance> &aPluginInstance)
00341 {
00342   // check the incoming instance, null indicates that window is going away and we are
00343   // not interested in subclassing business any more, undo and don't subclass
00344   if (!aPluginInstance)
00345     UndoSubclassAndAssociateWindow();
00346 
00347   nsPluginNativeWindow::CallSetWindow(aPluginInstance);
00348 
00349   if (aPluginInstance)
00350     SubclassAndAssociateWindow();
00351 
00352   return NS_OK;
00353 }
00354 
00355 nsresult nsPluginNativeWindowOS2::SubclassAndAssociateWindow()
00356 {
00357   if (type != nsPluginWindowType_Window)
00358     return NS_ERROR_FAILURE;
00359 
00360   HWND hWnd = (HWND)window;
00361   if (!hWnd)
00362     return NS_ERROR_FAILURE;
00363 
00364   // check if we need to re-subclass
00365   PFNWP currentWndProc = (PFNWP)::WinQueryWindowPtr(hWnd, QWP_PFNWP);
00366   if (PluginWndProc == currentWndProc)
00367     return NS_OK;
00368 
00369   mPluginWinProc = WinSubclassWindow(hWnd, PluginWndProc);
00370   if (!mPluginWinProc)
00371     return NS_ERROR_FAILURE;
00372 
00373   nsPluginNativeWindowOS2 * win = (nsPluginNativeWindowOS2 *)::WinQueryProperty(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
00374   NS_ASSERTION(!win || (win == this), "plugin window already has property and this is not us");
00375   
00376   if (!::WinSetProperty(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION, (PVOID)this, 0))
00377     return NS_ERROR_FAILURE;
00378 
00379   return NS_OK;
00380 }
00381 
00382 nsresult nsPluginNativeWindowOS2::UndoSubclassAndAssociateWindow()
00383 {
00384   // release plugin instance
00385   SetPluginInstance(nsnull);
00386 
00387   // remove window property
00388   HWND hWnd = (HWND)window;
00389   if (WinIsWindow(/*HAB*/0, hWnd))
00390     ::WinRemoveProperty(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
00391 
00392   // restore the original win proc
00393   // but only do this if this were us last time
00394   if (mPluginWinProc) {
00395     PFNWP currentWndProc = (PFNWP)::WinQueryWindowPtr(hWnd, QWP_PFNWP);
00396     if (currentWndProc == PluginWndProc)
00397       WinSubclassWindow(hWnd, mPluginWinProc);
00398   }
00399 
00400   return NS_OK;
00401 }
00402 
00403 nsresult PLUG_NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow)
00404 {
00405   NS_ENSURE_ARG_POINTER(aPluginNativeWindow);
00406 
00407   *aPluginNativeWindow = new nsPluginNativeWindowOS2();
00408 
00409   return *aPluginNativeWindow ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
00410 }
00411 
00412 nsresult PLUG_DeletePluginNativeWindow(nsPluginNativeWindow * aPluginNativeWindow)
00413 {
00414   NS_ENSURE_ARG_POINTER(aPluginNativeWindow);
00415   nsPluginNativeWindowOS2 *p = (nsPluginNativeWindowOS2 *)aPluginNativeWindow;
00416   delete p;
00417   return NS_OK;
00418 }