Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "mozilla/BasicEvents.h" |
michael@0 | 7 | #include "mozilla/DebugOnly.h" |
michael@0 | 8 | |
michael@0 | 9 | #include "windows.h" |
michael@0 | 10 | #include "windowsx.h" |
michael@0 | 11 | |
michael@0 | 12 | // XXXbz windowsx.h defines GetFirstChild, GetNextSibling, |
michael@0 | 13 | // GetPrevSibling are macros, apparently... Eeevil. We have functions |
michael@0 | 14 | // called that on some classes, so undef them. |
michael@0 | 15 | #undef GetFirstChild |
michael@0 | 16 | #undef GetNextSibling |
michael@0 | 17 | #undef GetPrevSibling |
michael@0 | 18 | |
michael@0 | 19 | #include "nsDebug.h" |
michael@0 | 20 | |
michael@0 | 21 | #include "nsWindowsDllInterceptor.h" |
michael@0 | 22 | #include "nsPluginNativeWindow.h" |
michael@0 | 23 | #include "nsThreadUtils.h" |
michael@0 | 24 | #include "nsAutoPtr.h" |
michael@0 | 25 | #include "nsTWeakRef.h" |
michael@0 | 26 | #include "nsCrashOnException.h" |
michael@0 | 27 | |
michael@0 | 28 | using namespace mozilla; |
michael@0 | 29 | |
michael@0 | 30 | #define NP_POPUP_API_VERSION 16 |
michael@0 | 31 | |
michael@0 | 32 | #define nsMajorVersion(v) (((int32_t)(v) >> 16) & 0xffff) |
michael@0 | 33 | #define nsMinorVersion(v) ((int32_t)(v) & 0xffff) |
michael@0 | 34 | #define versionOK(suppliedV, requiredV) \ |
michael@0 | 35 | (nsMajorVersion(suppliedV) == nsMajorVersion(requiredV) \ |
michael@0 | 36 | && nsMinorVersion(suppliedV) >= nsMinorVersion(requiredV)) |
michael@0 | 37 | |
michael@0 | 38 | |
michael@0 | 39 | #define NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION TEXT("MozillaPluginWindowPropertyAssociation") |
michael@0 | 40 | #define NS_PLUGIN_CUSTOM_MSG_ID TEXT("MozFlashUserRelay") |
michael@0 | 41 | #define WM_USER_FLASH WM_USER+1 |
michael@0 | 42 | static UINT sWM_FLASHBOUNCEMSG = 0; |
michael@0 | 43 | |
michael@0 | 44 | typedef nsTWeakRef<class nsPluginNativeWindowWin> PluginWindowWeakRef; |
michael@0 | 45 | |
michael@0 | 46 | /** |
michael@0 | 47 | * PLEvent handling code |
michael@0 | 48 | */ |
michael@0 | 49 | class PluginWindowEvent : public nsRunnable { |
michael@0 | 50 | public: |
michael@0 | 51 | PluginWindowEvent(); |
michael@0 | 52 | void Init(const PluginWindowWeakRef &ref, HWND hWnd, UINT msg, WPARAM wParam, |
michael@0 | 53 | LPARAM lParam); |
michael@0 | 54 | void Clear(); |
michael@0 | 55 | HWND GetWnd() { return mWnd; }; |
michael@0 | 56 | UINT GetMsg() { return mMsg; }; |
michael@0 | 57 | WPARAM GetWParam() { return mWParam; }; |
michael@0 | 58 | LPARAM GetLParam() { return mLParam; }; |
michael@0 | 59 | bool InUse() { return mWnd != nullptr; }; |
michael@0 | 60 | |
michael@0 | 61 | NS_DECL_NSIRUNNABLE |
michael@0 | 62 | |
michael@0 | 63 | protected: |
michael@0 | 64 | PluginWindowWeakRef mPluginWindowRef; |
michael@0 | 65 | HWND mWnd; |
michael@0 | 66 | UINT mMsg; |
michael@0 | 67 | WPARAM mWParam; |
michael@0 | 68 | LPARAM mLParam; |
michael@0 | 69 | }; |
michael@0 | 70 | |
michael@0 | 71 | PluginWindowEvent::PluginWindowEvent() |
michael@0 | 72 | { |
michael@0 | 73 | Clear(); |
michael@0 | 74 | } |
michael@0 | 75 | |
michael@0 | 76 | void PluginWindowEvent::Clear() |
michael@0 | 77 | { |
michael@0 | 78 | mWnd = nullptr; |
michael@0 | 79 | mMsg = 0; |
michael@0 | 80 | mWParam = 0; |
michael@0 | 81 | mLParam = 0; |
michael@0 | 82 | } |
michael@0 | 83 | |
michael@0 | 84 | void PluginWindowEvent::Init(const PluginWindowWeakRef &ref, HWND aWnd, |
michael@0 | 85 | UINT aMsg, WPARAM aWParam, LPARAM aLParam) |
michael@0 | 86 | { |
michael@0 | 87 | NS_ASSERTION(aWnd != nullptr, "invalid plugin event value"); |
michael@0 | 88 | NS_ASSERTION(mWnd == nullptr, "event already in use"); |
michael@0 | 89 | mPluginWindowRef = ref; |
michael@0 | 90 | mWnd = aWnd; |
michael@0 | 91 | mMsg = aMsg; |
michael@0 | 92 | mWParam = aWParam; |
michael@0 | 93 | mLParam = aLParam; |
michael@0 | 94 | } |
michael@0 | 95 | |
michael@0 | 96 | /** |
michael@0 | 97 | * nsPluginNativeWindow Windows specific class declaration |
michael@0 | 98 | */ |
michael@0 | 99 | |
michael@0 | 100 | typedef enum { |
michael@0 | 101 | nsPluginType_Unknown = 0, |
michael@0 | 102 | nsPluginType_Flash, |
michael@0 | 103 | nsPluginType_Real, |
michael@0 | 104 | nsPluginType_PDF, |
michael@0 | 105 | nsPluginType_Other |
michael@0 | 106 | } nsPluginType; |
michael@0 | 107 | |
michael@0 | 108 | class nsPluginNativeWindowWin : public nsPluginNativeWindow { |
michael@0 | 109 | public: |
michael@0 | 110 | nsPluginNativeWindowWin(); |
michael@0 | 111 | virtual ~nsPluginNativeWindowWin(); |
michael@0 | 112 | |
michael@0 | 113 | virtual nsresult CallSetWindow(nsRefPtr<nsNPAPIPluginInstance> &aPluginInstance); |
michael@0 | 114 | |
michael@0 | 115 | private: |
michael@0 | 116 | nsresult SubclassAndAssociateWindow(); |
michael@0 | 117 | nsresult UndoSubclassAndAssociateWindow(); |
michael@0 | 118 | |
michael@0 | 119 | public: |
michael@0 | 120 | // locals |
michael@0 | 121 | WNDPROC GetPrevWindowProc(); |
michael@0 | 122 | void SetPrevWindowProc(WNDPROC proc) { mPluginWinProc = proc; } |
michael@0 | 123 | WNDPROC GetWindowProc(); |
michael@0 | 124 | PluginWindowEvent * GetPluginWindowEvent(HWND aWnd, |
michael@0 | 125 | UINT aMsg, |
michael@0 | 126 | WPARAM aWParam, |
michael@0 | 127 | LPARAM aLParam); |
michael@0 | 128 | |
michael@0 | 129 | private: |
michael@0 | 130 | WNDPROC mPluginWinProc; |
michael@0 | 131 | WNDPROC mPrevWinProc; |
michael@0 | 132 | PluginWindowWeakRef mWeakRef; |
michael@0 | 133 | nsRefPtr<PluginWindowEvent> mCachedPluginWindowEvent; |
michael@0 | 134 | |
michael@0 | 135 | HWND mParentWnd; |
michael@0 | 136 | LONG_PTR mParentProc; |
michael@0 | 137 | public: |
michael@0 | 138 | nsPluginType mPluginType; |
michael@0 | 139 | }; |
michael@0 | 140 | |
michael@0 | 141 | static bool sInMessageDispatch = false; |
michael@0 | 142 | static bool sInPreviousMessageDispatch = false; |
michael@0 | 143 | static UINT sLastMsg = 0; |
michael@0 | 144 | |
michael@0 | 145 | static bool ProcessFlashMessageDelayed(nsPluginNativeWindowWin * aWin, nsNPAPIPluginInstance * aInst, |
michael@0 | 146 | HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) |
michael@0 | 147 | { |
michael@0 | 148 | NS_ENSURE_TRUE(aWin, false); |
michael@0 | 149 | NS_ENSURE_TRUE(aInst, false); |
michael@0 | 150 | |
michael@0 | 151 | if (msg == sWM_FLASHBOUNCEMSG) { |
michael@0 | 152 | // See PluginWindowEvent::Run() below. |
michael@0 | 153 | NS_ASSERTION((sWM_FLASHBOUNCEMSG != 0), "RegisterWindowMessage failed in flash plugin WM_USER message handling!"); |
michael@0 | 154 | ::CallWindowProc((WNDPROC)aWin->GetWindowProc(), hWnd, WM_USER_FLASH, wParam, lParam); |
michael@0 | 155 | return true; |
michael@0 | 156 | } |
michael@0 | 157 | |
michael@0 | 158 | if (msg != WM_USER_FLASH) |
michael@0 | 159 | return false; // no need to delay |
michael@0 | 160 | |
michael@0 | 161 | // do stuff |
michael@0 | 162 | nsCOMPtr<nsIRunnable> pwe = aWin->GetPluginWindowEvent(hWnd, msg, wParam, lParam); |
michael@0 | 163 | if (pwe) { |
michael@0 | 164 | NS_DispatchToCurrentThread(pwe); |
michael@0 | 165 | return true; |
michael@0 | 166 | } |
michael@0 | 167 | return false; |
michael@0 | 168 | } |
michael@0 | 169 | |
michael@0 | 170 | class nsDelayedPopupsEnabledEvent : public nsRunnable |
michael@0 | 171 | { |
michael@0 | 172 | public: |
michael@0 | 173 | nsDelayedPopupsEnabledEvent(nsNPAPIPluginInstance *inst) |
michael@0 | 174 | : mInst(inst) |
michael@0 | 175 | {} |
michael@0 | 176 | |
michael@0 | 177 | NS_DECL_NSIRUNNABLE |
michael@0 | 178 | |
michael@0 | 179 | private: |
michael@0 | 180 | nsRefPtr<nsNPAPIPluginInstance> mInst; |
michael@0 | 181 | }; |
michael@0 | 182 | |
michael@0 | 183 | NS_IMETHODIMP nsDelayedPopupsEnabledEvent::Run() |
michael@0 | 184 | { |
michael@0 | 185 | mInst->PushPopupsEnabledState(false); |
michael@0 | 186 | return NS_OK; |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | static LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); |
michael@0 | 190 | |
michael@0 | 191 | /** |
michael@0 | 192 | * New plugin window procedure |
michael@0 | 193 | */ |
michael@0 | 194 | static LRESULT CALLBACK PluginWndProcInternal(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) |
michael@0 | 195 | { |
michael@0 | 196 | nsPluginNativeWindowWin * win = (nsPluginNativeWindowWin *)::GetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION); |
michael@0 | 197 | if (!win) |
michael@0 | 198 | return TRUE; |
michael@0 | 199 | |
michael@0 | 200 | // The DispatchEvent(NS_PLUGIN_ACTIVATE) below can trigger a reentrant focus |
michael@0 | 201 | // event which might destroy us. Hold a strong ref on the plugin instance |
michael@0 | 202 | // to prevent that, bug 374229. |
michael@0 | 203 | nsRefPtr<nsNPAPIPluginInstance> inst; |
michael@0 | 204 | win->GetPluginInstance(inst); |
michael@0 | 205 | |
michael@0 | 206 | // Real may go into a state where it recursivly dispatches the same event |
michael@0 | 207 | // when subclassed. If this is Real, lets examine the event and drop it |
michael@0 | 208 | // on the floor if we get into this recursive situation. See bug 192914. |
michael@0 | 209 | if (win->mPluginType == nsPluginType_Real) { |
michael@0 | 210 | if (sInMessageDispatch && msg == sLastMsg) |
michael@0 | 211 | return true; |
michael@0 | 212 | // Cache the last message sent |
michael@0 | 213 | sLastMsg = msg; |
michael@0 | 214 | } |
michael@0 | 215 | |
michael@0 | 216 | bool enablePopups = false; |
michael@0 | 217 | |
michael@0 | 218 | // Activate/deactivate mouse capture on the plugin widget |
michael@0 | 219 | // here, before we pass the Windows event to the plugin |
michael@0 | 220 | // because its possible our widget won't get paired events |
michael@0 | 221 | // (see bug 131007) and we'll look frozen. Note that this |
michael@0 | 222 | // is also done in ChildWindow::DispatchMouseEvent. |
michael@0 | 223 | switch (msg) { |
michael@0 | 224 | case WM_LBUTTONDOWN: |
michael@0 | 225 | case WM_MBUTTONDOWN: |
michael@0 | 226 | case WM_RBUTTONDOWN: { |
michael@0 | 227 | nsCOMPtr<nsIWidget> widget; |
michael@0 | 228 | win->GetPluginWidget(getter_AddRefs(widget)); |
michael@0 | 229 | if (widget) |
michael@0 | 230 | widget->CaptureMouse(true); |
michael@0 | 231 | break; |
michael@0 | 232 | } |
michael@0 | 233 | case WM_LBUTTONUP: |
michael@0 | 234 | enablePopups = true; |
michael@0 | 235 | |
michael@0 | 236 | // fall through |
michael@0 | 237 | case WM_MBUTTONUP: |
michael@0 | 238 | case WM_RBUTTONUP: { |
michael@0 | 239 | nsCOMPtr<nsIWidget> widget; |
michael@0 | 240 | win->GetPluginWidget(getter_AddRefs(widget)); |
michael@0 | 241 | if (widget) |
michael@0 | 242 | widget->CaptureMouse(false); |
michael@0 | 243 | break; |
michael@0 | 244 | } |
michael@0 | 245 | case WM_KEYDOWN: |
michael@0 | 246 | // Ignore repeating keydown messages... |
michael@0 | 247 | if ((lParam & 0x40000000) != 0) { |
michael@0 | 248 | break; |
michael@0 | 249 | } |
michael@0 | 250 | |
michael@0 | 251 | // fall through |
michael@0 | 252 | case WM_KEYUP: |
michael@0 | 253 | enablePopups = true; |
michael@0 | 254 | |
michael@0 | 255 | break; |
michael@0 | 256 | |
michael@0 | 257 | case WM_MOUSEACTIVATE: { |
michael@0 | 258 | // If a child window of this plug-in is already focused, |
michael@0 | 259 | // don't focus the parent to avoid focus dance. We'll |
michael@0 | 260 | // receive a follow up WM_SETFOCUS which will notify |
michael@0 | 261 | // the appropriate window anyway. |
michael@0 | 262 | HWND focusedWnd = ::GetFocus(); |
michael@0 | 263 | if (!::IsChild((HWND)win->window, focusedWnd)) { |
michael@0 | 264 | // Notify the dom / focus manager the plugin has focus when one of |
michael@0 | 265 | // it's child windows receives it. OOPP specific - this code is |
michael@0 | 266 | // critical in notifying the dom of focus changes when the plugin |
michael@0 | 267 | // window in the child process receives focus via a mouse click. |
michael@0 | 268 | // WM_MOUSEACTIVATE is sent by nsWindow via a custom window event |
michael@0 | 269 | // sent from PluginInstanceParent in response to focus events sent |
michael@0 | 270 | // from the child. (bug 540052) Note, this gui event could also be |
michael@0 | 271 | // sent directly from widget. |
michael@0 | 272 | nsCOMPtr<nsIWidget> widget; |
michael@0 | 273 | win->GetPluginWidget(getter_AddRefs(widget)); |
michael@0 | 274 | if (widget) { |
michael@0 | 275 | WidgetGUIEvent event(true, NS_PLUGIN_ACTIVATE, widget); |
michael@0 | 276 | nsEventStatus status; |
michael@0 | 277 | widget->DispatchEvent(&event, status); |
michael@0 | 278 | } |
michael@0 | 279 | } |
michael@0 | 280 | } |
michael@0 | 281 | break; |
michael@0 | 282 | |
michael@0 | 283 | case WM_SETFOCUS: |
michael@0 | 284 | case WM_KILLFOCUS: { |
michael@0 | 285 | // RealPlayer can crash, don't process the message for those, |
michael@0 | 286 | // see bug 328675. |
michael@0 | 287 | if (win->mPluginType == nsPluginType_Real && msg == sLastMsg) |
michael@0 | 288 | return TRUE; |
michael@0 | 289 | // Make sure setfocus and killfocus get through to the widget procedure |
michael@0 | 290 | // even if they are eaten by the plugin. Also make sure we aren't calling |
michael@0 | 291 | // recursively. |
michael@0 | 292 | WNDPROC prevWndProc = win->GetPrevWindowProc(); |
michael@0 | 293 | if (prevWndProc && !sInPreviousMessageDispatch) { |
michael@0 | 294 | sInPreviousMessageDispatch = true; |
michael@0 | 295 | ::CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam); |
michael@0 | 296 | sInPreviousMessageDispatch = false; |
michael@0 | 297 | } |
michael@0 | 298 | break; |
michael@0 | 299 | } |
michael@0 | 300 | } |
michael@0 | 301 | |
michael@0 | 302 | // Macromedia Flash plugin may flood the message queue with some special messages |
michael@0 | 303 | // (WM_USER+1) causing 100% CPU consumption and GUI freeze, see mozilla bug 132759; |
michael@0 | 304 | // we can prevent this from happening by delaying the processing such messages; |
michael@0 | 305 | if (win->mPluginType == nsPluginType_Flash) { |
michael@0 | 306 | if (ProcessFlashMessageDelayed(win, inst, hWnd, msg, wParam, lParam)) |
michael@0 | 307 | return TRUE; |
michael@0 | 308 | } |
michael@0 | 309 | |
michael@0 | 310 | if (enablePopups && inst) { |
michael@0 | 311 | uint16_t apiVersion; |
michael@0 | 312 | if (NS_SUCCEEDED(inst->GetPluginAPIVersion(&apiVersion)) && |
michael@0 | 313 | !versionOK(apiVersion, NP_POPUP_API_VERSION)) { |
michael@0 | 314 | inst->PushPopupsEnabledState(true); |
michael@0 | 315 | } |
michael@0 | 316 | } |
michael@0 | 317 | |
michael@0 | 318 | sInMessageDispatch = true; |
michael@0 | 319 | LRESULT res; |
michael@0 | 320 | WNDPROC proc = (WNDPROC)win->GetWindowProc(); |
michael@0 | 321 | if (PluginWndProc == proc) { |
michael@0 | 322 | NS_WARNING("Previous plugin window procedure references PluginWndProc! " |
michael@0 | 323 | "Report this bug!"); |
michael@0 | 324 | res = CallWindowProc(DefWindowProc, hWnd, msg, wParam, lParam); |
michael@0 | 325 | } else { |
michael@0 | 326 | res = CallWindowProc(proc, hWnd, msg, wParam, lParam); |
michael@0 | 327 | } |
michael@0 | 328 | sInMessageDispatch = false; |
michael@0 | 329 | |
michael@0 | 330 | if (inst) { |
michael@0 | 331 | // Popups are enabled (were enabled before the call to |
michael@0 | 332 | // CallWindowProc()). Some plugins (at least the flash player) |
michael@0 | 333 | // post messages from their key handlers etc that delay the actual |
michael@0 | 334 | // processing, so we need to delay the disabling of popups so that |
michael@0 | 335 | // popups remain enabled when the flash player ends up processing |
michael@0 | 336 | // the actual key handlers. We do this by posting an event that |
michael@0 | 337 | // does the disabling, this way our disabling will happen after |
michael@0 | 338 | // the handlers in the plugin are done. |
michael@0 | 339 | |
michael@0 | 340 | // Note that it's not fatal if any of this fails (which won't |
michael@0 | 341 | // happen unless we're out of memory anyways) since the plugin |
michael@0 | 342 | // code will pop any popup state pushed by this plugin on |
michael@0 | 343 | // destruction. |
michael@0 | 344 | |
michael@0 | 345 | nsCOMPtr<nsIRunnable> event = new nsDelayedPopupsEnabledEvent(inst); |
michael@0 | 346 | if (event) |
michael@0 | 347 | NS_DispatchToCurrentThread(event); |
michael@0 | 348 | } |
michael@0 | 349 | |
michael@0 | 350 | return res; |
michael@0 | 351 | } |
michael@0 | 352 | |
michael@0 | 353 | static LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) |
michael@0 | 354 | { |
michael@0 | 355 | return mozilla::CallWindowProcCrashProtected(PluginWndProcInternal, hWnd, msg, wParam, lParam); |
michael@0 | 356 | } |
michael@0 | 357 | |
michael@0 | 358 | /* |
michael@0 | 359 | * Flash will reset the subclass of our widget at various times. |
michael@0 | 360 | * (Notably when entering and exiting full screen mode.) This |
michael@0 | 361 | * occurs independent of the main plugin window event procedure. |
michael@0 | 362 | * We trap these subclass calls to prevent our subclass hook from |
michael@0 | 363 | * getting dropped. |
michael@0 | 364 | * Note, ascii versions can be nixed once flash versions < 10.1 |
michael@0 | 365 | * are considered obsolete. |
michael@0 | 366 | */ |
michael@0 | 367 | static WindowsDllInterceptor sUser32Intercept; |
michael@0 | 368 | |
michael@0 | 369 | #ifdef _WIN64 |
michael@0 | 370 | typedef LONG_PTR |
michael@0 | 371 | (WINAPI *User32SetWindowLongPtrA)(HWND hWnd, |
michael@0 | 372 | int nIndex, |
michael@0 | 373 | LONG_PTR dwNewLong); |
michael@0 | 374 | typedef LONG_PTR |
michael@0 | 375 | (WINAPI *User32SetWindowLongPtrW)(HWND hWnd, |
michael@0 | 376 | int nIndex, |
michael@0 | 377 | LONG_PTR dwNewLong); |
michael@0 | 378 | static User32SetWindowLongPtrA sUser32SetWindowLongAHookStub = nullptr; |
michael@0 | 379 | static User32SetWindowLongPtrW sUser32SetWindowLongWHookStub = nullptr; |
michael@0 | 380 | #else |
michael@0 | 381 | typedef LONG |
michael@0 | 382 | (WINAPI *User32SetWindowLongA)(HWND hWnd, |
michael@0 | 383 | int nIndex, |
michael@0 | 384 | LONG dwNewLong); |
michael@0 | 385 | typedef LONG |
michael@0 | 386 | (WINAPI *User32SetWindowLongW)(HWND hWnd, |
michael@0 | 387 | int nIndex, |
michael@0 | 388 | LONG dwNewLong); |
michael@0 | 389 | static User32SetWindowLongA sUser32SetWindowLongAHookStub = nullptr; |
michael@0 | 390 | static User32SetWindowLongW sUser32SetWindowLongWHookStub = nullptr; |
michael@0 | 391 | #endif |
michael@0 | 392 | static inline bool |
michael@0 | 393 | SetWindowLongHookCheck(HWND hWnd, |
michael@0 | 394 | int nIndex, |
michael@0 | 395 | LONG_PTR newLong) |
michael@0 | 396 | { |
michael@0 | 397 | nsPluginNativeWindowWin * win = |
michael@0 | 398 | (nsPluginNativeWindowWin *)GetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION); |
michael@0 | 399 | if (!win || (win && win->mPluginType != nsPluginType_Flash) || |
michael@0 | 400 | (nIndex == GWLP_WNDPROC && |
michael@0 | 401 | newLong == reinterpret_cast<LONG_PTR>(PluginWndProc))) |
michael@0 | 402 | return true; |
michael@0 | 403 | return false; |
michael@0 | 404 | } |
michael@0 | 405 | |
michael@0 | 406 | #ifdef _WIN64 |
michael@0 | 407 | LONG_PTR WINAPI |
michael@0 | 408 | SetWindowLongPtrAHook(HWND hWnd, |
michael@0 | 409 | int nIndex, |
michael@0 | 410 | LONG_PTR newLong) |
michael@0 | 411 | #else |
michael@0 | 412 | LONG WINAPI |
michael@0 | 413 | SetWindowLongAHook(HWND hWnd, |
michael@0 | 414 | int nIndex, |
michael@0 | 415 | LONG newLong) |
michael@0 | 416 | #endif |
michael@0 | 417 | { |
michael@0 | 418 | if (SetWindowLongHookCheck(hWnd, nIndex, newLong)) |
michael@0 | 419 | return sUser32SetWindowLongAHookStub(hWnd, nIndex, newLong); |
michael@0 | 420 | |
michael@0 | 421 | // Set flash's new subclass to get the result. |
michael@0 | 422 | LONG_PTR proc = sUser32SetWindowLongAHookStub(hWnd, nIndex, newLong); |
michael@0 | 423 | |
michael@0 | 424 | // We already checked this in SetWindowLongHookCheck |
michael@0 | 425 | nsPluginNativeWindowWin * win = |
michael@0 | 426 | (nsPluginNativeWindowWin *)GetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION); |
michael@0 | 427 | |
michael@0 | 428 | // Hook our subclass back up, just like we do on setwindow. |
michael@0 | 429 | win->SetPrevWindowProc( |
michael@0 | 430 | reinterpret_cast<WNDPROC>(sUser32SetWindowLongWHookStub(hWnd, nIndex, |
michael@0 | 431 | reinterpret_cast<LONG_PTR>(PluginWndProc)))); |
michael@0 | 432 | return proc; |
michael@0 | 433 | } |
michael@0 | 434 | |
michael@0 | 435 | #ifdef _WIN64 |
michael@0 | 436 | LONG_PTR WINAPI |
michael@0 | 437 | SetWindowLongPtrWHook(HWND hWnd, |
michael@0 | 438 | int nIndex, |
michael@0 | 439 | LONG_PTR newLong) |
michael@0 | 440 | #else |
michael@0 | 441 | LONG WINAPI |
michael@0 | 442 | SetWindowLongWHook(HWND hWnd, |
michael@0 | 443 | int nIndex, |
michael@0 | 444 | LONG newLong) |
michael@0 | 445 | #endif |
michael@0 | 446 | { |
michael@0 | 447 | if (SetWindowLongHookCheck(hWnd, nIndex, newLong)) |
michael@0 | 448 | return sUser32SetWindowLongWHookStub(hWnd, nIndex, newLong); |
michael@0 | 449 | |
michael@0 | 450 | // Set flash's new subclass to get the result. |
michael@0 | 451 | LONG_PTR proc = sUser32SetWindowLongWHookStub(hWnd, nIndex, newLong); |
michael@0 | 452 | |
michael@0 | 453 | // We already checked this in SetWindowLongHookCheck |
michael@0 | 454 | nsPluginNativeWindowWin * win = |
michael@0 | 455 | (nsPluginNativeWindowWin *)GetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION); |
michael@0 | 456 | |
michael@0 | 457 | // Hook our subclass back up, just like we do on setwindow. |
michael@0 | 458 | win->SetPrevWindowProc( |
michael@0 | 459 | reinterpret_cast<WNDPROC>(sUser32SetWindowLongWHookStub(hWnd, nIndex, |
michael@0 | 460 | reinterpret_cast<LONG_PTR>(PluginWndProc)))); |
michael@0 | 461 | return proc; |
michael@0 | 462 | } |
michael@0 | 463 | |
michael@0 | 464 | static void |
michael@0 | 465 | HookSetWindowLongPtr() |
michael@0 | 466 | { |
michael@0 | 467 | sUser32Intercept.Init("user32.dll"); |
michael@0 | 468 | #ifdef _WIN64 |
michael@0 | 469 | if (!sUser32SetWindowLongAHookStub) |
michael@0 | 470 | sUser32Intercept.AddHook("SetWindowLongPtrA", |
michael@0 | 471 | reinterpret_cast<intptr_t>(SetWindowLongPtrAHook), |
michael@0 | 472 | (void**) &sUser32SetWindowLongAHookStub); |
michael@0 | 473 | if (!sUser32SetWindowLongWHookStub) |
michael@0 | 474 | sUser32Intercept.AddHook("SetWindowLongPtrW", |
michael@0 | 475 | reinterpret_cast<intptr_t>(SetWindowLongPtrWHook), |
michael@0 | 476 | (void**) &sUser32SetWindowLongWHookStub); |
michael@0 | 477 | #else |
michael@0 | 478 | if (!sUser32SetWindowLongAHookStub) |
michael@0 | 479 | sUser32Intercept.AddHook("SetWindowLongA", |
michael@0 | 480 | reinterpret_cast<intptr_t>(SetWindowLongAHook), |
michael@0 | 481 | (void**) &sUser32SetWindowLongAHookStub); |
michael@0 | 482 | if (!sUser32SetWindowLongWHookStub) |
michael@0 | 483 | sUser32Intercept.AddHook("SetWindowLongW", |
michael@0 | 484 | reinterpret_cast<intptr_t>(SetWindowLongWHook), |
michael@0 | 485 | (void**) &sUser32SetWindowLongWHookStub); |
michael@0 | 486 | #endif |
michael@0 | 487 | } |
michael@0 | 488 | |
michael@0 | 489 | /** |
michael@0 | 490 | * nsPluginNativeWindowWin implementation |
michael@0 | 491 | */ |
michael@0 | 492 | nsPluginNativeWindowWin::nsPluginNativeWindowWin() : nsPluginNativeWindow() |
michael@0 | 493 | { |
michael@0 | 494 | // initialize the struct fields |
michael@0 | 495 | window = nullptr; |
michael@0 | 496 | x = 0; |
michael@0 | 497 | y = 0; |
michael@0 | 498 | width = 0; |
michael@0 | 499 | height = 0; |
michael@0 | 500 | |
michael@0 | 501 | mPrevWinProc = nullptr; |
michael@0 | 502 | mPluginWinProc = nullptr; |
michael@0 | 503 | mPluginType = nsPluginType_Unknown; |
michael@0 | 504 | |
michael@0 | 505 | mParentWnd = nullptr; |
michael@0 | 506 | mParentProc = 0; |
michael@0 | 507 | |
michael@0 | 508 | if (!sWM_FLASHBOUNCEMSG) { |
michael@0 | 509 | sWM_FLASHBOUNCEMSG = ::RegisterWindowMessage(NS_PLUGIN_CUSTOM_MSG_ID); |
michael@0 | 510 | } |
michael@0 | 511 | } |
michael@0 | 512 | |
michael@0 | 513 | nsPluginNativeWindowWin::~nsPluginNativeWindowWin() |
michael@0 | 514 | { |
michael@0 | 515 | // clear weak reference to self to prevent any pending events from |
michael@0 | 516 | // dereferencing this. |
michael@0 | 517 | mWeakRef.forget(); |
michael@0 | 518 | } |
michael@0 | 519 | |
michael@0 | 520 | WNDPROC nsPluginNativeWindowWin::GetPrevWindowProc() |
michael@0 | 521 | { |
michael@0 | 522 | return mPrevWinProc; |
michael@0 | 523 | } |
michael@0 | 524 | |
michael@0 | 525 | WNDPROC nsPluginNativeWindowWin::GetWindowProc() |
michael@0 | 526 | { |
michael@0 | 527 | return mPluginWinProc; |
michael@0 | 528 | } |
michael@0 | 529 | |
michael@0 | 530 | NS_IMETHODIMP PluginWindowEvent::Run() |
michael@0 | 531 | { |
michael@0 | 532 | nsPluginNativeWindowWin *win = mPluginWindowRef.get(); |
michael@0 | 533 | if (!win) |
michael@0 | 534 | return NS_OK; |
michael@0 | 535 | |
michael@0 | 536 | HWND hWnd = GetWnd(); |
michael@0 | 537 | if (!hWnd) |
michael@0 | 538 | return NS_OK; |
michael@0 | 539 | |
michael@0 | 540 | nsRefPtr<nsNPAPIPluginInstance> inst; |
michael@0 | 541 | win->GetPluginInstance(inst); |
michael@0 | 542 | |
michael@0 | 543 | if (GetMsg() == WM_USER_FLASH) { |
michael@0 | 544 | // XXX Unwind issues related to runnable event callback depth for this |
michael@0 | 545 | // event and destruction of the plugin. (Bug 493601) |
michael@0 | 546 | ::PostMessage(hWnd, sWM_FLASHBOUNCEMSG, GetWParam(), GetLParam()); |
michael@0 | 547 | } |
michael@0 | 548 | else { |
michael@0 | 549 | // Currently not used, but added so that processing events here |
michael@0 | 550 | // is more generic. |
michael@0 | 551 | ::CallWindowProc(win->GetWindowProc(), |
michael@0 | 552 | hWnd, |
michael@0 | 553 | GetMsg(), |
michael@0 | 554 | GetWParam(), |
michael@0 | 555 | GetLParam()); |
michael@0 | 556 | } |
michael@0 | 557 | |
michael@0 | 558 | Clear(); |
michael@0 | 559 | return NS_OK; |
michael@0 | 560 | } |
michael@0 | 561 | |
michael@0 | 562 | PluginWindowEvent * |
michael@0 | 563 | nsPluginNativeWindowWin::GetPluginWindowEvent(HWND aWnd, UINT aMsg, WPARAM aWParam, LPARAM aLParam) |
michael@0 | 564 | { |
michael@0 | 565 | if (!mWeakRef) { |
michael@0 | 566 | mWeakRef = this; |
michael@0 | 567 | if (!mWeakRef) |
michael@0 | 568 | return nullptr; |
michael@0 | 569 | } |
michael@0 | 570 | |
michael@0 | 571 | PluginWindowEvent *event; |
michael@0 | 572 | |
michael@0 | 573 | // We have the ability to alloc if needed in case in the future some plugin |
michael@0 | 574 | // should post multiple PostMessages. However, this could lead to many |
michael@0 | 575 | // alloc's per second which could become a performance issue. See bug 169247. |
michael@0 | 576 | if (!mCachedPluginWindowEvent) |
michael@0 | 577 | { |
michael@0 | 578 | event = new PluginWindowEvent(); |
michael@0 | 579 | if (!event) return nullptr; |
michael@0 | 580 | mCachedPluginWindowEvent = event; |
michael@0 | 581 | } |
michael@0 | 582 | else if (mCachedPluginWindowEvent->InUse()) |
michael@0 | 583 | { |
michael@0 | 584 | event = new PluginWindowEvent(); |
michael@0 | 585 | if (!event) return nullptr; |
michael@0 | 586 | } |
michael@0 | 587 | else |
michael@0 | 588 | { |
michael@0 | 589 | event = mCachedPluginWindowEvent; |
michael@0 | 590 | } |
michael@0 | 591 | |
michael@0 | 592 | event->Init(mWeakRef, aWnd, aMsg, aWParam, aLParam); |
michael@0 | 593 | return event; |
michael@0 | 594 | } |
michael@0 | 595 | |
michael@0 | 596 | nsresult nsPluginNativeWindowWin::CallSetWindow(nsRefPtr<nsNPAPIPluginInstance> &aPluginInstance) |
michael@0 | 597 | { |
michael@0 | 598 | // Note, 'window' can be null |
michael@0 | 599 | |
michael@0 | 600 | // check the incoming instance, null indicates that window is going away and we are |
michael@0 | 601 | // not interested in subclassing business any more, undo and don't subclass |
michael@0 | 602 | if (!aPluginInstance) { |
michael@0 | 603 | UndoSubclassAndAssociateWindow(); |
michael@0 | 604 | nsPluginNativeWindow::CallSetWindow(aPluginInstance); |
michael@0 | 605 | return NS_OK; |
michael@0 | 606 | } |
michael@0 | 607 | |
michael@0 | 608 | // check plugin mime type and cache it if it will need special treatment later |
michael@0 | 609 | if (mPluginType == nsPluginType_Unknown) { |
michael@0 | 610 | const char* mimetype = nullptr; |
michael@0 | 611 | aPluginInstance->GetMIMEType(&mimetype); |
michael@0 | 612 | if (mimetype) { |
michael@0 | 613 | if (!strcmp(mimetype, "application/x-shockwave-flash")) |
michael@0 | 614 | mPluginType = nsPluginType_Flash; |
michael@0 | 615 | else if (!strcmp(mimetype, "audio/x-pn-realaudio-plugin")) |
michael@0 | 616 | mPluginType = nsPluginType_Real; |
michael@0 | 617 | else if (!strcmp(mimetype, "application/pdf")) |
michael@0 | 618 | mPluginType = nsPluginType_PDF; |
michael@0 | 619 | else |
michael@0 | 620 | mPluginType = nsPluginType_Other; |
michael@0 | 621 | } |
michael@0 | 622 | } |
michael@0 | 623 | |
michael@0 | 624 | if (window) { |
michael@0 | 625 | // grab the widget procedure before the plug-in does a subclass in |
michael@0 | 626 | // setwindow. We'll use this in PluginWndProc for forwarding focus |
michael@0 | 627 | // events to the widget. |
michael@0 | 628 | WNDPROC currentWndProc = |
michael@0 | 629 | (WNDPROC)::GetWindowLongPtr((HWND)window, GWLP_WNDPROC); |
michael@0 | 630 | if (!mPrevWinProc && currentWndProc != PluginWndProc) |
michael@0 | 631 | mPrevWinProc = currentWndProc; |
michael@0 | 632 | |
michael@0 | 633 | // PDF plugin v7.0.9, v8.1.3, and v9.0 subclass parent window, bug 531551 |
michael@0 | 634 | // V8.2.2 and V9.1 don't have such problem. |
michael@0 | 635 | if (mPluginType == nsPluginType_PDF) { |
michael@0 | 636 | HWND parent = ::GetParent((HWND)window); |
michael@0 | 637 | if (mParentWnd != parent) { |
michael@0 | 638 | NS_ASSERTION(!mParentWnd, "Plugin's parent window changed"); |
michael@0 | 639 | mParentWnd = parent; |
michael@0 | 640 | mParentProc = ::GetWindowLongPtr(mParentWnd, GWLP_WNDPROC); |
michael@0 | 641 | } |
michael@0 | 642 | } |
michael@0 | 643 | } |
michael@0 | 644 | |
michael@0 | 645 | nsPluginNativeWindow::CallSetWindow(aPluginInstance); |
michael@0 | 646 | |
michael@0 | 647 | SubclassAndAssociateWindow(); |
michael@0 | 648 | |
michael@0 | 649 | if (window && mPluginType == nsPluginType_Flash && |
michael@0 | 650 | !GetPropW((HWND)window, L"PluginInstanceParentProperty")) { |
michael@0 | 651 | HookSetWindowLongPtr(); |
michael@0 | 652 | } |
michael@0 | 653 | |
michael@0 | 654 | return NS_OK; |
michael@0 | 655 | } |
michael@0 | 656 | |
michael@0 | 657 | nsresult nsPluginNativeWindowWin::SubclassAndAssociateWindow() |
michael@0 | 658 | { |
michael@0 | 659 | if (type != NPWindowTypeWindow || !window) |
michael@0 | 660 | return NS_ERROR_FAILURE; |
michael@0 | 661 | |
michael@0 | 662 | HWND hWnd = (HWND)window; |
michael@0 | 663 | |
michael@0 | 664 | // check if we need to subclass |
michael@0 | 665 | WNDPROC currentWndProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC); |
michael@0 | 666 | if (currentWndProc == PluginWndProc) |
michael@0 | 667 | return NS_OK; |
michael@0 | 668 | |
michael@0 | 669 | // If the plugin reset the subclass, set it back. |
michael@0 | 670 | if (mPluginWinProc) { |
michael@0 | 671 | #ifdef DEBUG |
michael@0 | 672 | NS_WARNING("A plugin cleared our subclass - resetting."); |
michael@0 | 673 | if (currentWndProc != mPluginWinProc) { |
michael@0 | 674 | NS_WARNING("Procedures do not match up, discarding old subclass value."); |
michael@0 | 675 | } |
michael@0 | 676 | if (mPrevWinProc && currentWndProc == mPrevWinProc) { |
michael@0 | 677 | NS_WARNING("The new procedure is our widget procedure?"); |
michael@0 | 678 | } |
michael@0 | 679 | #endif |
michael@0 | 680 | SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)PluginWndProc); |
michael@0 | 681 | return NS_OK; |
michael@0 | 682 | } |
michael@0 | 683 | |
michael@0 | 684 | LONG_PTR style = GetWindowLongPtr(hWnd, GWL_STYLE); |
michael@0 | 685 | // Out of process plugins must not have the WS_CLIPCHILDREN style set on their |
michael@0 | 686 | // parent windows or else synchronous paints (via UpdateWindow() and others) |
michael@0 | 687 | // will cause deadlocks. |
michael@0 | 688 | if (::GetPropW(hWnd, L"PluginInstanceParentProperty")) |
michael@0 | 689 | style &= ~WS_CLIPCHILDREN; |
michael@0 | 690 | else |
michael@0 | 691 | style |= WS_CLIPCHILDREN; |
michael@0 | 692 | SetWindowLongPtr(hWnd, GWL_STYLE, style); |
michael@0 | 693 | |
michael@0 | 694 | mPluginWinProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)PluginWndProc); |
michael@0 | 695 | if (!mPluginWinProc) |
michael@0 | 696 | return NS_ERROR_FAILURE; |
michael@0 | 697 | |
michael@0 | 698 | DebugOnly<nsPluginNativeWindowWin *> win = (nsPluginNativeWindowWin *)::GetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION); |
michael@0 | 699 | NS_ASSERTION(!win || (win == this), "plugin window already has property and this is not us"); |
michael@0 | 700 | |
michael@0 | 701 | if (!::SetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION, (HANDLE)this)) |
michael@0 | 702 | return NS_ERROR_FAILURE; |
michael@0 | 703 | |
michael@0 | 704 | return NS_OK; |
michael@0 | 705 | } |
michael@0 | 706 | |
michael@0 | 707 | nsresult nsPluginNativeWindowWin::UndoSubclassAndAssociateWindow() |
michael@0 | 708 | { |
michael@0 | 709 | // release plugin instance |
michael@0 | 710 | SetPluginInstance(nullptr); |
michael@0 | 711 | |
michael@0 | 712 | // remove window property |
michael@0 | 713 | HWND hWnd = (HWND)window; |
michael@0 | 714 | if (IsWindow(hWnd)) |
michael@0 | 715 | ::RemoveProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION); |
michael@0 | 716 | |
michael@0 | 717 | // restore the original win proc |
michael@0 | 718 | // but only do this if this were us last time |
michael@0 | 719 | if (mPluginWinProc) { |
michael@0 | 720 | WNDPROC currentWndProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC); |
michael@0 | 721 | if (currentWndProc == PluginWndProc) |
michael@0 | 722 | SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)mPluginWinProc); |
michael@0 | 723 | mPluginWinProc = nullptr; |
michael@0 | 724 | |
michael@0 | 725 | LONG_PTR style = GetWindowLongPtr(hWnd, GWL_STYLE); |
michael@0 | 726 | style &= ~WS_CLIPCHILDREN; |
michael@0 | 727 | SetWindowLongPtr(hWnd, GWL_STYLE, style); |
michael@0 | 728 | } |
michael@0 | 729 | |
michael@0 | 730 | if (mPluginType == nsPluginType_PDF && mParentWnd) { |
michael@0 | 731 | ::SetWindowLongPtr(mParentWnd, GWLP_WNDPROC, mParentProc); |
michael@0 | 732 | mParentWnd = nullptr; |
michael@0 | 733 | mParentProc = 0; |
michael@0 | 734 | } |
michael@0 | 735 | |
michael@0 | 736 | return NS_OK; |
michael@0 | 737 | } |
michael@0 | 738 | |
michael@0 | 739 | nsresult PLUG_NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow) |
michael@0 | 740 | { |
michael@0 | 741 | NS_ENSURE_ARG_POINTER(aPluginNativeWindow); |
michael@0 | 742 | |
michael@0 | 743 | *aPluginNativeWindow = new nsPluginNativeWindowWin(); |
michael@0 | 744 | |
michael@0 | 745 | return *aPluginNativeWindow ? NS_OK : NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 746 | } |
michael@0 | 747 | |
michael@0 | 748 | nsresult PLUG_DeletePluginNativeWindow(nsPluginNativeWindow * aPluginNativeWindow) |
michael@0 | 749 | { |
michael@0 | 750 | NS_ENSURE_ARG_POINTER(aPluginNativeWindow); |
michael@0 | 751 | nsPluginNativeWindowWin *p = (nsPluginNativeWindowWin *)aPluginNativeWindow; |
michael@0 | 752 | delete p; |
michael@0 | 753 | return NS_OK; |
michael@0 | 754 | } |