Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit e9e9ece

Browse files
codymikolmmalerba
authored andcommitted
fix($mdInteraction): clean up events on $rootScope destroy (#11641)
this prevents unit tests from leaking memory Fixes: #11493 <!-- Filling out this template is required! Do not delete it when submitting a Pull Request! Without this information, your Pull Request may be auto-closed. --> ## PR Checklist Please check that your PR fulfills the following requirements: - [x] The commit message follows [our guidelines](https://github.com/angular/material/blob/master/.github/CONTRIBUTING.md#-commit-message-format) - [ ] Tests for the changes have been added or this is not a bug fix / enhancement - [ ] Docs have been added, updated, or were not required ## PR Type What kind of change does this PR introduce? <!-- Please check the one that applies to this PR using "x". --> ``` [x] Bugfix [ ] Enhancement [ ] Documentation content changes [ ] Code style update (formatting, local variables) [ ] Refactoring (no functional changes, no api changes) [ ] Build related changes [ ] CI related changes [ ] Infrastructure changes [ ] Other... Please describe: ``` ## What is the current behavior? Currently the $mdInteraction service registers when invoked. This causes a memory leak in unit tests because nothing is in place to clean up the events. <!-- Please describe the current behavior that you are modifying and link to one or more relevant issues. --> Issue Number: #11493 ## What is the new behavior? Now on the $destroy event of rootScope the events registered will be unregistered. ## Does this PR introduce a breaking change? ``` [ ] Yes [x] No ``` <!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. --> <!-- Note that breaking changes are highly unlikely to get merged to master unless the validation is clear and the use case is critical. --> ## Other information
1 parent ec9aa25 commit e9e9ece

File tree

2 files changed

+68
-8
lines changed

2 files changed

+68
-8
lines changed

src/core/services/interaction/interaction.js

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,20 @@ angular
3333
* </hljs>
3434
*
3535
*/
36-
function MdInteractionService($timeout, $mdUtil) {
36+
function MdInteractionService($timeout, $mdUtil, $rootScope) {
3737
this.$timeout = $timeout;
3838
this.$mdUtil = $mdUtil;
39+
this.$rootScope = $rootScope;
3940

41+
// IE browsers can also trigger pointer events, which also leads to an interaction.
42+
this.pointerEvent = 'MSPointerEvent' in window ? 'MSPointerDown' : 'PointerEvent' in window ? 'pointerdown' : null;
4043
this.bodyElement = angular.element(document.body);
4144
this.isBuffering = false;
4245
this.bufferTimeout = null;
4346
this.lastInteractionType = null;
4447
this.lastInteractionTime = null;
48+
this.inputHandler = this.onInputEvent.bind(this);
49+
this.bufferedInputHandler = this.onBufferInputEvent.bind(this);
4550

4651
// Type Mappings for the different events
4752
// There will be three three interaction types
@@ -65,24 +70,41 @@ function MdInteractionService($timeout, $mdUtil) {
6570
};
6671

6772
this.initializeEvents();
73+
this.$rootScope.$on('$destroy', this.deregister.bind(this));
6874
}
6975

76+
/**
77+
* Removes all event listeners created by $mdInteration on the
78+
* body element.
79+
*/
80+
MdInteractionService.prototype.deregister = function() {
81+
82+
this.bodyElement.off('keydown mousedown', this.inputHandler);
83+
84+
if ('ontouchstart' in document.documentElement) {
85+
this.bodyElement.off('touchstart', this.bufferedInputHandler);
86+
}
87+
88+
if (this.pointerEvent) {
89+
this.bodyElement.off(this.pointerEvent, this.inputHandler);
90+
}
91+
92+
};
93+
7094
/**
7195
* Initializes the interaction service, by registering all interaction events to the
7296
* body element.
7397
*/
7498
MdInteractionService.prototype.initializeEvents = function() {
75-
// IE browsers can also trigger pointer events, which also leads to an interaction.
76-
var pointerEvent = 'MSPointerEvent' in window ? 'MSPointerDown' : 'PointerEvent' in window ? 'pointerdown' : null;
7799

78-
this.bodyElement.on('keydown mousedown', this.onInputEvent.bind(this));
100+
this.bodyElement.on('keydown mousedown', this.inputHandler);
79101

80102
if ('ontouchstart' in document.documentElement) {
81-
this.bodyElement.on('touchstart', this.onBufferInputEvent.bind(this));
103+
this.bodyElement.on('touchstart', this.bufferedInputHandler);
82104
}
83105

84-
if (pointerEvent) {
85-
this.bodyElement.on(pointerEvent, this.onInputEvent.bind(this));
106+
if (this.pointerEvent) {
107+
this.bodyElement.on(this.pointerEvent, this.inputHandler);
86108
}
87109

88110
};

src/core/services/interaction/interaction.spec.js

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
describe("$mdInteraction service", function() {
22

33
var $mdInteraction = null;
4+
var $rootScope = null;
45
var bodyElement = null;
6+
var $timeout = null;
57

68
beforeEach(module('material.core'));
79

810
beforeEach(inject(function($injector) {
911
$mdInteraction = $injector.get('$mdInteraction');
12+
$rootScope = $injector.get('$rootScope');
13+
$timeout = $injector.get('$timeout');
1014

1115
bodyElement = angular.element(document.body);
1216
}));
@@ -75,4 +79,38 @@ describe("$mdInteraction service", function() {
7579

7680
});
7781

78-
});
82+
describe('when $rootScope is destroyed', function () {
83+
84+
var _initialTouchStartEvent = document.documentElement.ontouchstart;
85+
86+
beforeAll(function () {
87+
document.documentElement.ontouchstart = function () {};
88+
});
89+
90+
beforeEach(function () {
91+
$mdInteraction.lastInteractionType = 'initial';
92+
$rootScope.$destroy();
93+
});
94+
95+
afterAll(function () {
96+
document.documentElement.ontouchstart = _initialTouchStartEvent;
97+
});
98+
99+
it('should remove mousedown events', function () {
100+
bodyElement.triggerHandler('mousedown');
101+
expect($mdInteraction.getLastInteractionType()).toEqual('initial');
102+
});
103+
104+
it('should remove keydown events', function () {
105+
bodyElement.triggerHandler('keydown');
106+
expect($mdInteraction.getLastInteractionType()).toEqual('initial');
107+
});
108+
109+
it('should remove touchstart events', function () {
110+
bodyElement.triggerHandler('touchstart');
111+
expect($mdInteraction.getLastInteractionType()).toEqual('initial');
112+
});
113+
114+
});
115+
116+
});

0 commit comments

Comments
 (0)