Bug 751809 - Fix OSX focus issue with click-to-play. r=josh
authorGeorg Fritzsche <georg.fritzsche@googlemail.com>
Fri, 10 Aug 2012 20:28:34 +0200
changeset 103582 31a27d47d242ebb956d10a8b0155f9071f43e5a4
parent 103581 158ffe78995bd7345f6e160d1de2b206b68d410e
child 103583 6a8b865e6225f19de7258f1ee0a88e4837bcfdc7
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjosh
bugs751809
milestone17.0a1
Bug 751809 - Fix OSX focus issue with click-to-play. r=josh
dom/plugins/base/nsPluginInstanceOwner.cpp
dom/plugins/test/mochitest/Makefile.in
dom/plugins/test/mochitest/test_bug751809.html
dom/plugins/test/testplugin/nptest.cpp
dom/plugins/test/testplugin/nptest.h
dom/plugins/test/testplugin/nptest_gtk2.cpp
dom/plugins/test/testplugin/nptest_macosx.mm
dom/plugins/test/testplugin/nptest_qt.cpp
dom/plugins/test/testplugin/nptest_windows.cpp
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -3772,16 +3772,22 @@ void nsPluginInstanceOwner::SetFrame(nsO
       for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
         nsIScrollableFrame* sf = do_QueryFrame(f);
         if (sf) {
           sf->AddScrollPositionListener(this);
         }
       }
     }
 #endif
+    
+    nsFocusManager* fm = nsFocusManager::GetFocusManager();
+    const nsIContent* content = aFrame->GetContent();
+    if (fm && content) {
+      mContentFocused = (content == fm->GetFocusedContent());
+    }
   }
 }
 
 nsObjectFrame* nsPluginInstanceOwner::GetFrame()
 {
   return mObjectFrame;
 }
 
--- a/dom/plugins/test/mochitest/Makefile.in
+++ b/dom/plugins/test/mochitest/Makefile.in
@@ -59,16 +59,17 @@ MOCHITEST_FILES = \
   crashing_subpage.html \
   test_GCrace.html \
   test_propertyAndMethod.html \
   test_bug539565-1.html \
   test_bug539565-2.html \
   test_bug771202.html \
   file_bug771202.html \
   test_bug777098.html \
+  test_bug751809.html \
   test_enumerate.html \
   test_npruntime_construct.html \
   307-xo-redirect.sjs \
   test_redirect_handling.html \
   test_zero_opacity.html \
   test_NPPVpluginWantsAllNetworkStreams.html \
   test_npruntime_npnsetexception.html \
   test_NPNVdocumentOrigin.html \
new file mode 100644
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug751809.html
@@ -0,0 +1,92 @@
+<html>
+<head>
+  <title>Bug 751809</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body>
+  <script type="application/javascript">
+  
+  SimpleTest.waitForExplicitFinish();
+  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+
+  const Ci = Components.interfaces;
+  const utils = window.QueryInterface(Ci.nsIInterfaceRequestor).
+                                    getInterface(Ci.nsIDOMWindowUtils);
+
+  function waitForCondition(condition, nextTest, errorMsg) {
+    var tries = 0;
+    var interval = setInterval(function() {
+      if (tries >= 30) {
+        ok(false, errorMsg);
+        moveOn();
+      }
+      if (condition()) {
+        moveOn();
+      }
+      tries++;
+    }, 100);
+    var moveOn = function() { clearInterval(interval); nextTest(); };
+  }
+    
+  function startFocusTest() {
+    var plugin = document.getElementById('plugin');
+    ok(plugin, "Got plugin element.");
+    
+    var condition = function() plugin.getBoundingClientRect().width == 400;
+    waitForCondition(condition, afterPluginInsertion, "Waited too long for plugin to show up in page");
+  }
+    
+  function afterPluginInsertion() {
+    var plugin = document.getElementById('plugin');
+    var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
+    ok(!objLoadingContent.activated, "plugin should not be activated");
+    
+    SimpleTest.waitForFocus(afterWindowFocus);
+  }
+    
+  function afterWindowFocus() {
+    var plugin = document.getElementById('plugin');
+    var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
+    
+    synthesizeMouseAtCenter(plugin, {}, window);
+    var condition = function() objLoadingContent.activated;
+    waitForCondition(condition, afterPluginActivation, "Waited too long for plugin to activate");
+  }
+    
+  function afterPluginActivation() { 
+    var plugin = document.getElementById('plugin');
+    var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
+    ok(objLoadingContent.activated, "plugin should be activated now");
+    try {
+      is(plugin.getMouseUpEventCount(), 0, "Plugin should not have received mouse events yet.");
+    } catch(e) {
+      ok(false, "plugin.getMouseUpEventCount() shouldn't throw");
+    }
+
+    synthesizeMouseAtCenter(plugin, {}, window);
+    var condition = function() plugin.getMouseUpEventCount() > 0;
+    waitForCondition(condition, afterFirstClick, "Waited too long for plugin to receive the mouse click");
+  }
+  
+  function afterFirstClick() {
+    var plugin = document.getElementById('plugin');
+    try {
+      is(plugin.getMouseUpEventCount(), 1, "Plugin should have received 1 mouse up event.");
+    } catch(e) {
+      ok(false, "plugin.getMouseUpEventCount() shouldn't throw");
+    }
+
+    SpecialPowers.clearUserPref("plugins.click_to_play");
+    SimpleTest.finish();
+  }
+
+  SpecialPowers.setBoolPref("plugins.click_to_play", true);
+  document.write('<embed id="plugin" type="application/x-test" width="400" height="400" drawmode="solid" color="FF00FFFF"></embed>');
+  SimpleTest.executeSoon(startFocusTest);
+                   
+  </script>
+</body>
+</html>
--- a/dom/plugins/test/testplugin/nptest.cpp
+++ b/dom/plugins/test/testplugin/nptest.cpp
@@ -158,16 +158,17 @@ static bool getEventModel(NPObject* npob
 static bool getReflector(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool isVisible(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool getWindowPosition(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool constructObject(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool setSitesWithData(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool setSitesWithDataCapabilities(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool getLastKeyText(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool getNPNVdocumentOrigin(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+static bool getMouseUpEventCount(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 
 static const NPUTF8* sPluginMethodIdentifierNames[] = {
   "npnEvaluateTest",
   "npnInvokeTest",
   "npnInvokeDefaultTest",
   "setUndefinedValueTest",
   "identifierToStringTest",
   "timerTest",
@@ -217,17 +218,18 @@ static const NPUTF8* sPluginMethodIdenti
   "getEventModel",
   "getReflector",
   "isVisible",
   "getWindowPosition",
   "constructObject",
   "setSitesWithData",
   "setSitesWithDataCapabilities",
   "getLastKeyText",
-  "getNPNVdocumentOrigin"
+  "getNPNVdocumentOrigin",
+  "getMouseUpEventCount"
 };
 static NPIdentifier sPluginMethodIdentifiers[ARRAY_LENGTH(sPluginMethodIdentifierNames)];
 static const ScriptableFunction sPluginMethodFunctions[] = {
   npnEvaluateTest,
   npnInvokeTest,
   npnInvokeDefaultTest,
   setUndefinedValueTest,
   identifierToStringTest,
@@ -278,17 +280,18 @@ static const ScriptableFunction sPluginM
   getEventModel,
   getReflector,
   isVisible,
   getWindowPosition,
   constructObject,
   setSitesWithData,
   setSitesWithDataCapabilities,
   getLastKeyText,
-  getNPNVdocumentOrigin
+  getNPNVdocumentOrigin,
+  getMouseUpEventCount
 };
 
 STATIC_ASSERT(ARRAY_LENGTH(sPluginMethodIdentifierNames) ==
               ARRAY_LENGTH(sPluginMethodFunctions));
 
 static const NPUTF8* sPluginPropertyIdentifierNames[] = {
   "propertyAndMethod"
 };
@@ -772,16 +775,17 @@ NPP_New(NPMIMEType pluginType, NPP insta
   instanceData->focusState = ACTIVATION_STATE_UNKNOWN;
   instanceData->focusEventCount = 0;
   instanceData->eventModel = 0;
   instanceData->closeStream = false;
   instanceData->wantsAllStreams = false;
   instanceData->asyncDrawing = AD_NONE;
   instanceData->frontBuffer = NULL;
   instanceData->backBuffer = NULL;
+  instanceData->mouseUpEventCount = 0;
   instance->pdata = instanceData;
 
   TestNPObject* scriptableObject = (TestNPObject*)NPN_CreateObject(instance, &sNPClass);
   if (!scriptableObject) {
     printf("NPN_CreateObject failed to create an object, can't create a plugin instance\n");
     free(instanceData);
     return NPERR_GENERIC_ERROR;
   }
@@ -3644,8 +3648,20 @@ bool getNPNVdocumentOrigin(NPObject* npo
   NPError err = NPN_GetValue(npp, NPNVdocumentOrigin, &origin);
   if (err != NPERR_NO_ERROR) {
     return false;
   }
 
   STRINGZ_TO_NPVARIANT(origin, *result);
   return true;
 }
+
+bool getMouseUpEventCount(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+  if (argCount != 0) {
+    return false;
+  }
+  
+  NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+  InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+  INT32_TO_NPVARIANT(id->mouseUpEventCount, *result);
+  return true;
+}
--- a/dom/plugins/test/testplugin/nptest.h
+++ b/dom/plugins/test/testplugin/nptest.h
@@ -145,13 +145,14 @@ typedef struct InstanceData {
   int32_t focusEventCount;
   int32_t eventModel;
   bool closeStream;
   std::string lastKeyText;
   bool wantsAllStreams;
   AsyncDrawing asyncDrawing;
   NPAsyncSurface *frontBuffer;
   NPAsyncSurface *backBuffer;
+  int32_t mouseUpEventCount;
 } InstanceData;
 
 void notifyDidPaint(InstanceData* instanceData);
 
 #endif // nptest_h_
--- a/dom/plugins/test/testplugin/nptest_gtk2.cpp
+++ b/dom/plugins/test/testplugin/nptest_gtk2.cpp
@@ -249,16 +249,19 @@ MotionEvent(GtkWidget* widget, GdkEventM
 
 static gboolean
 ButtonEvent(GtkWidget* widget, GdkEventButton* event,
             gpointer user_data)
 {
   InstanceData* instanceData = static_cast<InstanceData*>(user_data);
   instanceData->lastMouseX = event->x;
   instanceData->lastMouseY = event->y;
+  if (event->type == GDK_BUTTON_RELEASE) {
+    instanceData->mouseUpEventCount++;
+  }
   return TRUE;
 }
 
 static gboolean
 DeleteWidget(GtkWidget* widget, GdkEvent* event, gpointer user_data)
 {
   InstanceData* instanceData = static_cast<InstanceData*>(user_data);
   // Some plugins do not expect the plug to be removed from the socket before
@@ -413,16 +416,19 @@ pluginHandleEvent(InstanceData* instance
     instanceData->lastMouseY = motion->y;
     break;
   }
   case ButtonPress:
   case ButtonRelease: {
     XButtonEvent* button = &nsEvent->xbutton;
     instanceData->lastMouseX = button->x;
     instanceData->lastMouseY = button->y;
+    if (nsEvent->type == ButtonRelease) {
+      instanceData->mouseUpEventCount++;
+    }
     break;
   }
   default:
     break;
   }
 #endif
 
   return 0;
--- a/dom/plugins/test/testplugin/nptest_macosx.mm
+++ b/dom/plugins/test/testplugin/nptest_macosx.mm
@@ -271,16 +271,19 @@ pluginHandleEvent(InstanceData* instance
       case osEvt:
       {
         Rect globalBounds = {0};
         WindowRef nativeWindow = static_cast<WindowRef>(static_cast<NP_CGContext*>(w->window)->window);
         if (nativeWindow)
           ::GetWindowBounds(nativeWindow, kWindowStructureRgn, &globalBounds);
         instanceData->lastMouseX = carbonEvent->where.h - w->x - globalBounds.left;
         instanceData->lastMouseY = carbonEvent->where.v - w->y - globalBounds.top;
+        if (carbonEvent->what == mouseUp) {
+          instanceData->mouseUpEventCount++;
+        }
         break;
       }
       default:
         return kNPEventNotHandled;
     }
 
     return kNPEventHandled;
   }
@@ -294,16 +297,19 @@ pluginHandleEvent(InstanceData* instance
     case NPCocoaEventDrawRect:
       pluginDraw(instanceData, cocoaEvent);
       break;
     case NPCocoaEventMouseDown:
     case NPCocoaEventMouseUp:
     case NPCocoaEventMouseMoved:
       instanceData->lastMouseX = (int32_t)cocoaEvent->data.mouse.pluginX;
       instanceData->lastMouseY = (int32_t)cocoaEvent->data.mouse.pluginY;
+      if (cocoaEvent->type == NPCocoaEventMouseUp) {
+        instanceData->mouseUpEventCount++;
+      }
       break;
     case NPCocoaEventWindowFocusChanged:
       instanceData->topLevelWindowActivationState = cocoaEvent->data.focus.hasFocus ?
         ACTIVATION_STATE_ACTIVATED : ACTIVATION_STATE_DEACTIVATED;
       instanceData->topLevelWindowActivationEventCount = instanceData->topLevelWindowActivationEventCount + 1;
       break;
     case NPCocoaEventFocusChanged:
       instanceData->focusState = cocoaEvent->data.focus.hasFocus ?
--- a/dom/plugins/test/testplugin/nptest_qt.cpp
+++ b/dom/plugins/test/testplugin/nptest_qt.cpp
@@ -188,16 +188,17 @@ pluginHandleEvent(InstanceData* instance
     ////printf("ButtonPress\n");
     break;
   }
   case ButtonRelease: {
     //printf("ButtonRelease\n");
     XButtonEvent* button = &nsEvent->xbutton;
     instanceData->lastMouseX = button->x;
     instanceData->lastMouseY = button->y;
+    instanceData->mouseUpEventCount++;
     break;
   }
   default:
     break;
   }
 #endif
 
   return 0;
--- a/dom/plugins/test/testplugin/nptest_windows.cpp
+++ b/dom/plugins/test/testplugin/nptest_windows.cpp
@@ -597,16 +597,19 @@ handleEventInternal(InstanceData* instan
     case WM_MBUTTONDOWN:
     case WM_MBUTTONUP:
     case WM_RBUTTONDOWN:
     case WM_RBUTTONUP: {
       int x = instanceData->hasWidget ? 0 : instanceData->winX;
       int y = instanceData->hasWidget ? 0 : instanceData->winY;
       instanceData->lastMouseX = GET_X_LPARAM(pe->lParam) - x;
       instanceData->lastMouseY = GET_Y_LPARAM(pe->lParam) - y;
+      if ((UINT)pe->event == WM_LBUTTONUP) {
+        instanceData->mouseUpEventCount++;
+      }
       return true;
     }
 
     case WM_KEYDOWN:
       instanceData->lastKeyText.erase();
       *result = 0;
       return true;