summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMorten Johan Sørvig <morten.sorvig@nokia.com>2010-02-15 12:33:07 (GMT)
committerMorten Johan Sørvig <morten.sorvig@nokia.com>2010-02-23 12:37:10 (GMT)
commit526c6fd4686a8e87e5087ebcac941405f742304a (patch)
treecb16c07e1db7908e73019092705ca8543c19a566 /src
parentde4332a4728e739b37e9c7b04c021e150e096270 (diff)
downloadQt-526c6fd4686a8e87e5087ebcac941405f742304a.zip
Qt-526c6fd4686a8e87e5087ebcac941405f742304a.tar.gz
Qt-526c6fd4686a8e87e5087ebcac941405f742304a.tar.bz2
Prevent stale QWidget pointers in QCocoaView
QCocoaView has two "back" pointers ot its owning QWidget and the corresponding QWidgetPrivate. The lifetime of the QCocoaView might be longer and that of the QWidget if Cocoa itself holds references to it. In addition accessing a QWidget that is being destroyed is not safe. Set both pack pointers to NULL in ~QWidget. This prevents following stale pointers in the QCocoaView callbacks. This also means that we'll see the NULL pointers, add gurads for that.
Diffstat (limited to 'src')
-rw-r--r--src/gui/kernel/qcocoaview_mac.mm52
-rw-r--r--src/gui/kernel/qcocoaview_mac_p.h1
-rw-r--r--src/gui/kernel/qt_cocoa_helpers_mac.mm10
-rw-r--r--src/gui/kernel/qwidget.cpp9
4 files changed, 69 insertions, 3 deletions
diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm
index 455176e..4977042 100644
--- a/src/gui/kernel/qcocoaview_mac.mm
+++ b/src/gui/kernel/qcocoaview_mac.mm
@@ -419,6 +419,8 @@ extern "C" {
- (BOOL)isOpaque;
{
+ if (!qwidgetprivate)
+ return [super isOpaque];
return qwidgetprivate->isOpaque;
}
@@ -450,7 +452,7 @@ extern "C" {
}
// Make sure the opengl context is updated on resize.
- if (qwidgetprivate->isGLWidget) {
+ if (qwidgetprivate && qwidgetprivate->isGLWidget) {
qwidgetprivate->needWindowChange = true;
QEvent event(QEvent::MacGLWindowChange);
qApp->sendEvent(qwidget, &event);
@@ -459,6 +461,9 @@ extern "C" {
- (void)drawRect:(NSRect)aRect
{
+ if (!qwidget)
+ return;
+
if (QApplicationPrivate::graphicsSystem() != 0) {
if (QWidgetBackingStore *bs = qwidgetprivate->maybeBackingStore()) {
// Drawing is handled on the window level
@@ -551,12 +556,18 @@ extern "C" {
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
+ if (!qwidget)
+ return NO;
+
Q_UNUSED(theEvent);
return !qwidget->testAttribute(Qt::WA_MacNoClickThrough);
}
- (NSView *)hitTest:(NSPoint)aPoint
{
+ if (!qwidget)
+ return [super hitTest:aPoint];
+
if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents))
return nil; // You cannot hit a transparent for mouse event widget.
return [super hitTest:aPoint];
@@ -564,6 +575,9 @@ extern "C" {
- (void)updateTrackingAreas
{
+ if (!qwidget)
+ return;
+
// [NSView addTrackingArea] is slow, so bail out early if we can:
if (NSIsEmptyRect([self visibleRect]))
return;
@@ -597,6 +611,9 @@ extern "C" {
- (void)mouseEntered:(NSEvent *)event
{
+ if (!qwidget)
+ return;
+
if (qwidgetprivate->data.in_destructor)
return;
QEvent enterEvent(QEvent::Enter);
@@ -619,6 +636,9 @@ extern "C" {
- (void)mouseExited:(NSEvent *)event
{
+ if (!qwidget)
+ return;
+
QEvent leaveEvent(QEvent::Leave);
NSPoint globalPoint = [[event window] convertBaseToScreen:[event locationInWindow]];
if (!qAppInstance()->activeModalWidget() || QApplicationPrivate::tryModalHelper(qwidget, 0)) {
@@ -637,6 +657,9 @@ extern "C" {
- (void)flagsChanged:(NSEvent *)theEvent
{
+ if (!qwidget)
+ return;
+
QWidget *widgetToGetKey = qwidget;
QWidget *popup = qAppInstance()->activePopupWidget();
@@ -648,6 +671,9 @@ extern "C" {
- (void)mouseMoved:(NSEvent *)theEvent
{
+ if (!qwidget)
+ return;
+
// We always enable mouse tracking for all QCocoaView-s. In cases where we have
// child views, we will receive mouseMoved for both parent & the child (if
// mouse is over the child). We need to ignore the parent mouseMoved in such
@@ -938,6 +964,8 @@ extern "C" {
- (void)frameDidChange:(NSNotification *)note
{
Q_UNUSED(note);
+ if (!qwidget)
+ return;
if (qwidget->isWindow())
return;
NSRect newFrame = [self frame];
@@ -961,7 +989,7 @@ extern "C" {
{
QMacCocoaAutoReleasePool pool;
[super setEnabled:flag];
- if (qwidget->isEnabled() != flag)
+ if (qwidget && qwidget->isEnabled() != flag)
qwidget->setEnabled(flag);
}
@@ -972,6 +1000,8 @@ extern "C" {
- (BOOL)acceptsFirstResponder
{
+ if (!qwidget)
+ return NO;
if (qwidget->isWindow())
return YES; // Always do it, so that windows can accept key press events.
return qwidget->focusPolicy() != Qt::NoFocus;
@@ -979,6 +1009,8 @@ extern "C" {
- (BOOL)resignFirstResponder
{
+ if (!qwidget)
+ return NO;
// Seems like the following test only triggers if this
// view is inside a QMacNativeWidget:
if (qwidget == QApplication::focusWidget())
@@ -1014,6 +1046,12 @@ extern "C" {
return qwidget;
}
+- (void) qt_clearQWidget
+{
+ qwidget = 0;
+ qwidgetprivate = 0;
+}
+
- (BOOL)qt_leftButtonIsRightButton
{
return leftButtonIsRightButton;
@@ -1067,9 +1105,11 @@ extern "C" {
- (void)viewWillMoveToWindow:(NSWindow *)window
{
+ if (qwidget == 0)
+ return;
+
if (qwidget->windowFlags() & Qt::MSWindowsOwnDC
&& (window != [self window])) { // OpenGL Widget
- // Create a stupid ClearDrawable Event
QEvent event(QEvent::MacGLClearDrawable);
qApp->sendEvent(qwidget, &event);
}
@@ -1077,6 +1117,9 @@ extern "C" {
- (void)viewDidMoveToWindow
{
+ if (qwidget == 0)
+ return;
+
if (qwidget->windowFlags() & Qt::MSWindowsOwnDC && [self window]) {
// call update paint event
qwidgetprivate->needWindowChange = true;
@@ -1272,6 +1315,9 @@ extern "C" {
- (NSArray*) validAttributesForMarkedText
{
+ if (qwidget == 0)
+ return nil;
+
if (!qwidget->testAttribute(Qt::WA_InputMethodEnabled))
return nil; // Not sure if that's correct, but it's saves a malloc.
diff --git a/src/gui/kernel/qcocoaview_mac_p.h b/src/gui/kernel/qcocoaview_mac_p.h
index 4bb10c5..33aaa24 100644
--- a/src/gui/kernel/qcocoaview_mac_p.h
+++ b/src/gui/kernel/qcocoaview_mac_p.h
@@ -103,6 +103,7 @@ Q_GUI_EXPORT
- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation;
- (BOOL)isComposing;
- (QWidget *)qt_qwidget;
+- (void) qt_clearQWidget;
- (BOOL)qt_leftButtonIsRightButton;
- (void)qt_setLeftButtonIsRightButton:(BOOL)isSwapped;
+ (DnDParams*)currentMouseEvent;
diff --git a/src/gui/kernel/qt_cocoa_helpers_mac.mm b/src/gui/kernel/qt_cocoa_helpers_mac.mm
index e9fdbda..9560952 100644
--- a/src/gui/kernel/qt_cocoa_helpers_mac.mm
+++ b/src/gui/kernel/qt_cocoa_helpers_mac.mm
@@ -369,6 +369,16 @@ QMacTabletHash *qt_mac_tablet_hash()
}
#ifdef QT_MAC_USE_COCOA
+
+// Clears the QWidget pointer that each QCocoaView holds.
+void qt_mac_clearCocoaViewQWidgetPointers(QWidget *widget)
+{
+ QCocoaView *cocoaView = reinterpret_cast<QCocoaView *>(qt_mac_nativeview_for(widget));
+ if (cocoaView && [cocoaView respondsToSelector:@selector(qt_qwidget)]) {
+ [cocoaView qt_clearQWidget];
+ }
+}
+
void qt_dispatchTabletProximityEvent(void * /*NSEvent * */ tabletEvent)
{
NSEvent *proximityEvent = static_cast<NSEvent *>(tabletEvent);
diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp
index d433048..1e9b1d9 100644
--- a/src/gui/kernel/qwidget.cpp
+++ b/src/gui/kernel/qwidget.cpp
@@ -1468,6 +1468,15 @@ QWidget::~QWidget()
d->declarativeData = 0; // don't activate again in ~QObject
}
+#ifdef QT_MAC_USE_COCOA
+ // QCocoaView holds a pointer back to this widget. Clear it now
+ // to make sure it's not followed later on. The lifetime of the
+ // QCocoaView might exceed the lifetime of this widget in cases
+ // where Cocoa itself holds references to it.
+ extern void qt_mac_clearCocoaViewQWidgetPointers(QWidget *);
+ qt_mac_clearCocoaViewQWidgetPointers(this);
+#endif
+
if (!d->children.isEmpty())
d->deleteChildren();