Bug 678607 - Work around Apple bug to avoid crashing with two-finger swipe on OS X Lion. r=bgirard
authorSteven Michaud <smichaud@pobox.com>
Thu, 18 Aug 2011 15:39:54 -0500
changeset 75479 2578bdcf32ee0727411fd5547600e0686e1d76f6
parent 75478 b6b6c8e74766b7616d9f603a63528b7501b04f2c
child 75480 6181ba4693f9face8f50d367f3de5593b500742d
push id21029
push usersmichaud@pobox.com
push dateThu, 18 Aug 2011 20:40:30 +0000
treeherdermozilla-central@2578bdcf32ee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgirard
bugs678607
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 678607 - Work around Apple bug to avoid crashing with two-finger swipe on OS X Lion. r=bgirard
widget/src/cocoa/nsChildView.mm
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -301,16 +301,25 @@ nsresult nsChildView::Create(nsIWidget *
   // we need to provide an autorelease pool to avoid leaking cocoa objects
   // (see bug 559075).
   nsAutoreleasePool localPool;
 
   // See NSView (MethodSwizzling) below.
   if (!gChildViewMethodsSwizzled) {
     nsToolkit::SwizzleMethods([NSView class], @selector(mouseDownCanMoveWindow),
                               @selector(nsChildView_NSView_mouseDownCanMoveWindow));
+#ifdef __LP64__
+    if (nsToolkit::OnLionOrLater()) {
+      nsToolkit::SwizzleMethods([NSEvent class], @selector(addLocalMonitorForEventsMatchingMask:handler:),
+                                @selector(nsChildView_NSEvent_addLocalMonitorForEventsMatchingMask:handler:),
+                                PR_TRUE);
+      nsToolkit::SwizzleMethods([NSEvent class], @selector(removeMonitor:),
+                                @selector(nsChildView_NSEvent_removeMonitor:), PR_TRUE);
+    }
+#endif
 #ifndef NP_NO_CARBON
     TextInputHandler::SwizzleMethods();
 #endif
     gChildViewMethodsSwizzled = PR_TRUE;
   }
 
   mBounds = aRect;
 
@@ -5087,8 +5096,53 @@ ChildViewMouseTracker::WindowAcceptsEven
   NSWindow *ourWindow = [self window];
   NSView *contentView = [ourWindow contentView];
   if ([ourWindow isKindOfClass:[ToolbarWindow class]] && (self == contentView))
     return NO;
   return [self nsChildView_NSView_mouseDownCanMoveWindow];
 }
 
 @end
+
+#ifdef __LP64__
+// When using blocks, at least on OS X 10.7, the OS sometimes calls
+// +[NSEvent removeMonitor:] more than once on a single event monitor, which
+// causes crashes.  See bug 678607.  We hook these methods to work around
+// the problem.
+@interface NSEvent (MethodSwizzling)
++ (id)nsChildView_NSEvent_addLocalMonitorForEventsMatchingMask:(unsigned long long)mask handler:(id)block;
++ (void)nsChildView_NSEvent_removeMonitor:(id)eventMonitor;
+@end
+
+// This is a local copy of the AppKit frameworks sEventObservers hashtable.
+// It only stores "local monitors".  We use it to ensure that +[NSEvent
+// removeMonitor:] is never called more than once on the same local monitor.
+static NSHashTable *sLocalEventObservers = nil;
+
+@implementation NSEvent (MethodSwizzling)
+
++ (id)nsChildView_NSEvent_addLocalMonitorForEventsMatchingMask:(unsigned long long)mask handler:(id)block
+{
+  if (!sLocalEventObservers) {
+    sLocalEventObservers = [[NSHashTable hashTableWithOptions:
+      NSHashTableStrongMemory | NSHashTableObjectPointerPersonality] retain];
+  }
+  id retval =
+    [self nsChildView_NSEvent_addLocalMonitorForEventsMatchingMask:mask handler:block];
+  if (sLocalEventObservers && retval && ![sLocalEventObservers containsObject:retval]) {
+    [sLocalEventObservers addObject:retval];
+  }
+  return retval;
+}
+
++ (void)nsChildView_NSEvent_removeMonitor:(id)eventMonitor
+{
+  if (sLocalEventObservers && [eventMonitor isKindOfClass: ::NSClassFromString(@"_NSLocalEventObserver")]) {
+    if (![sLocalEventObservers containsObject:eventMonitor]) {
+      return;
+    }
+    [sLocalEventObservers removeObject:eventMonitor];
+  }
+  [self nsChildView_NSEvent_removeMonitor:eventMonitor];
+}
+
+@end
+#endif // #ifdef __LP64__