From 027a317291848217efb4539479f5b803e9bae110 Mon Sep 17 00:00:00 2001 From: Antoine Mercadal Date: Thu, 5 Mar 2015 16:59:37 -0800 Subject: [PATCH] FIXED: Memory Leak with tooltips Only populate the tooltips handler functions when necessary (when there is a tooltip and when the view is in a window) or clear them otherwise. Tests in /Tests/AppKit/CPViewTest.j --- AppKit/CPView.j | 29 +++++++++------ Tests/AppKit/CPViewTest.j | 74 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 91 insertions(+), 12 deletions(-) diff --git a/AppKit/CPView.j b/AppKit/CPView.j index 12f207bd6..6ebd0f2b5 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -312,13 +312,6 @@ var CPViewHighDPIDrawingEnabled = YES; _viewClassFlags = CPViewFlags[classUID]; } -- (void)_setupToolTipHandlers -{ - _toolTipInstalled = NO; - _toolTipFunctionIn = function(e) { [_CPToolTip scheduleToolTipForView:self]; } - _toolTipFunctionOut = function(e) { [_CPToolTip invalidateCurrentToolTipIfNeeded]; }; -} - + (CPSet)keyPathsForValuesAffectingFrame { return [CPSet setWithObjects:@"frameOrigin", @"frameSize"]; @@ -387,7 +380,6 @@ var CPViewHighDPIDrawingEnabled = YES; _DOMImageSizes = []; #endif - [self _setupToolTipHandlers]; [self _setupViewFlags]; [self _loadThemeAttributes]; @@ -412,12 +404,16 @@ var CPViewHighDPIDrawingEnabled = YES; _toolTip = aToolTip; - if (_toolTip) + [self _manageToolTipInstallation]; +} + +- (void)_manageToolTipInstallation +{ + if ([self window] && _toolTip) [self _installToolTipEventHandlers]; else [self _uninstallToolTipEventHandlers]; } - /*! @ignore Install the handlers for the tooltip @@ -427,6 +423,12 @@ var CPViewHighDPIDrawingEnabled = YES; if (_toolTipInstalled) return; + if (!_toolTipFunctionIn) + _toolTipFunctionIn = function(e) { [_CPToolTip scheduleToolTipForView:self]; } + + if (!_toolTipFunctionOut) + _toolTipFunctionOut = function(e) { [_CPToolTip invalidateCurrentToolTipIfNeeded]; }; + #if PLATFORM(DOM) if (_DOMElement.addEventListener) { @@ -469,6 +471,9 @@ var CPViewHighDPIDrawingEnabled = YES; } #endif + _toolTipFunctionIn = nil; + _toolTipFunctionOut = nil; + _toolTipInstalled = NO; } @@ -807,6 +812,8 @@ var CPViewHighDPIDrawingEnabled = YES; [self viewDidMoveToWindow]; + [self _manageToolTipInstallation]; + [[self window] _dirtyKeyViewLoop]; } @@ -834,6 +841,7 @@ var CPViewHighDPIDrawingEnabled = YES; { // if (_graphicsContext) [self setNeedsDisplay:YES]; + } /*! @@ -3555,7 +3563,6 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", _hitTests = ![aCoder containsValueForKey:CPViewHitTestsKey] || [aCoder decodeBoolForKey:CPViewHitTestsKey]; - [self _setupToolTipHandlers]; _toolTip = [aCoder decodeObjectForKey:CPViewToolTipKey]; if (_toolTip) diff --git a/Tests/AppKit/CPViewTest.j b/Tests/AppKit/CPViewTest.j index e6eb4d091..549773b26 100644 --- a/Tests/AppKit/CPViewTest.j +++ b/Tests/AppKit/CPViewTest.j @@ -699,6 +699,78 @@ var methodCalled; [self assert:expectedRestult equals:methodCalled]; } +- (void)testToolTipInitialEmpty +{ + [self assert:nil equals:view._toolTip]; + [self assert:nil equals:view._toolTipInstalled]; + [self assert:nil equals:view._toolTipFunctionIn]; + [self assert:nil equals:view._toolTipFunctionOut]; +} + +- (void)testToolTipWithToolTipAndNoWindow +{ + [view setToolTip:@"tooltip"]; + + [self assert:@"tooltip" equals:view._toolTip]; + [self assert:nil equals:view._toolTipInstalled]; + [self assert:nil equals:view._toolTipFunctionIn]; + [self assert:nil equals:view._toolTipFunctionOut]; +} + +- (void)testToolTipWithToolTipAndWindow +{ + [view setToolTip:@"tooltip"]; + + [[window contentView] addSubview:view] + + [self assert:@"tooltip" equals:view._toolTip]; + [self assertTrue:view._toolTipInstalled]; + [self assertTrue:!!view._toolTipFunctionIn]; + [self assertTrue:!!view._toolTipFunctionOut]; +} + +- (void)testToolTipWithToolTipAndWindowThenNoWindow +{ + [view setToolTip:@"tooltip"]; + + [[window contentView] addSubview:view] + [view removeFromSuperview]; + + [self assert:@"tooltip" equals:view._toolTip]; + [self assert:NO equals:view._toolTipInstalled]; + [self assert:nil equals:view._toolTipFunctionIn]; + [self assert:nil equals:view._toolTipFunctionOut]; +} + +- (void)testToolTipWithNoToolTipAndWindowThenNoWindowThenToolTip +{ + [self assert:nil equals:view._toolTip]; + [self assert:nil equals:view._toolTipInstalled]; + [self assert:nil equals:view._toolTipFunctionIn]; + [self assert:nil equals:view._toolTipFunctionOut]; + + [[window contentView] addSubview:view]; + + [self assert:nil equals:view._toolTip]; + [self assert:nil equals:view._toolTipInstalled]; + [self assert:nil equals:view._toolTipFunctionIn]; + [self assert:nil equals:view._toolTipFunctionOut]; + + [view setToolTip:@"tooltip"]; + + [self assert:@"tooltip" equals:view._toolTip]; + [self assertTrue:view._toolTipInstalled]; + [self assertTrue:!!view._toolTipFunctionIn]; + [self assertTrue:!!view._toolTipFunctionOut]; + + [view removeFromSuperview]; + + [self assert:@"tooltip" equals:view._toolTip]; + [self assert:NO equals:view._toolTipInstalled]; + [self assert:nil equals:view._toolTipFunctionIn]; + [self assert:nil equals:view._toolTipFunctionOut]; +} + @end @implementation CPLayoutView : CPView @@ -747,4 +819,4 @@ var methodCalled; return YES; } -@end \ No newline at end of file +@end