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

Commit 09571b6

Browse files
committed
fix(Options): fix memory leak due to detached DOM tree
1 parent fdb0360 commit 09571b6

File tree

13 files changed

+254
-172
lines changed

13 files changed

+254
-172
lines changed

src/cache.js

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,32 @@ define('cache', function (require, _exports, module) {
3030
var BaseView = cache.BaseView = Backbone.View.extend({
3131
initialize: function () {
3232
var _this = this;
33-
if (_this.templateUrl)
34-
_this.__gotTemplate = cache.get(_this.templateUrl)
35-
.then(function (fn) {
36-
_this.templateFn = fn;
37-
});
33+
_this.subviews = {data: {}};
34+
_this.childViews = [];
35+
if (_this.templateUrl) {
36+
_this.__gotTemplate = cache.get(_this.templateUrl)
37+
.then(function (fn) {
38+
_this.templateFn = fn;
39+
});
40+
}
3841
_.bindAll(_this, 'render', 'postrender');
3942
_this.render();
4043
},
44+
clear: function () {
45+
var _this = this;
46+
if (_this.childViews.length) {
47+
_this.childViews.forEach(function (view) {
48+
view.remove();
49+
});
50+
_this.childViews = [];
51+
}
52+
},
53+
remove: function () {
54+
var _this = this;
55+
_this.clear();
56+
_this.undelegateEvents();
57+
Backbone.View.prototype.remove.call(_this);
58+
},
4159
_render: function () {
4260
this.$el.html(this.templateFn());
4361
},
@@ -55,6 +73,27 @@ define('cache', function (require, _exports, module) {
5573
_.features.isHit(node.dataset.feature) || node.classList.add('feature');
5674
});
5775
},
76+
loadSubview: function (name, factory, selector) {
77+
var _this = this;
78+
var view;
79+
var subviews = _this.subviews;
80+
view = subviews.data[name];
81+
if (!view) {
82+
view = factory();
83+
}
84+
var current = subviews.current;
85+
if (name !== current) {
86+
var currentView = subviews.data[current];
87+
if (currentView) {
88+
currentView.remove();
89+
subviews.data[current] = null;
90+
}
91+
}
92+
subviews.data[subviews.current = name] = view;
93+
var $el = selector ? _this.$(selector) : _this.$el;
94+
$el.html(view.render().el);
95+
return view;
96+
},
5897
getValue: function (target) {
5998
var key = target.dataset.id;
6099
var value;
@@ -79,6 +118,30 @@ define('cache', function (require, _exports, module) {
79118

80119
BaseView.prototype.postrender.call(window);
81120

121+
cache.BaseRouter = Backbone.Router.extend({
122+
initialize: function (selector) {
123+
var _this = this;
124+
_this.views = {data: {}};
125+
_this.$root = $(selector);
126+
},
127+
loadView: function (name, factory) {
128+
var _this = this;
129+
var views = _this.views;
130+
var view = views.data[name];
131+
if (!view) view = views.data[name] = factory();
132+
if (name !== views.current) {
133+
var currentView = views.data[views.current];
134+
if (currentView) {
135+
currentView.remove();
136+
views.data[views.current] = null;
137+
}
138+
views.data[views.current = name] = view;
139+
}
140+
_this.$root.html(view.el);
141+
return view;
142+
},
143+
});
144+
82145
!function () {
83146
var xhr = new XMLHttpRequest;
84147
xhr.open('GET', '/icons/sprite.svg', true);

src/options/app.js

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,36 @@
11
define('app', function (require, exports, _module) {
22
var MainView = require('views/Main');
33
var ConfirmView = require('views/Confirm');
4-
var EditView = require('views/Edit');
54
var MessageView = require('views/Message');
65
var models = require('models');
6+
var cache = require('cache');
77
zip.workerScriptsPath = '/lib/zip.js/';
88

99
_.sendMessage = _.getMessenger({});
1010
_.showMessage = function (options) {
1111
new MessageView(options);
1212
};
1313

14-
var App = Backbone.Router.extend({
14+
var App = cache.BaseRouter.extend({
1515
routes: {
1616
'': 'renderMain',
1717
'main/:tab': 'renderMain',
1818
'confirm/:url': 'renderConfirm',
1919
'confirm/:url/:from': 'renderConfirm',
2020
},
2121
renderMain: function (tab) {
22-
exports.scriptList || initMain();
23-
this.view = new MainView(tab);
22+
this.loadView('main', function () {
23+
initMain();
24+
return new MainView;
25+
}).loadTab(tab);
2426
},
25-
renderConfirm: function (url, _from) {
26-
this.view = new ConfirmView(url, _from);
27-
},
28-
renderEdit: function (id) {
29-
this.view = new EditView(id);
27+
renderConfirm: function (url, referer) {
28+
this.loadView('confirm', function () {
29+
return new ConfirmView;
30+
}).initData(url, referer);
3031
},
3132
});
32-
var app = new App();
33+
var app = new App('#app');
3334
Backbone.history.start() || app.navigate('', {trigger: true, replace: true});
3435

3536
$(document).on('click', '[data-feature]', function (e) {

src/options/style.css

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ aside img {
9191
border-right: 1px solid darkgray;
9292
overflow-y: hidden;
9393
}
94-
#tab > header {
94+
.content > header {
9595
height: 30px;
9696
line-height: 30px;
9797
padding: 0 .5rem;
@@ -147,6 +147,9 @@ input[disabled] ~ * {
147147
padding: 20px;
148148
overflow-y: auto;
149149
}
150+
.no-pad {
151+
padding: 0;
152+
}
150153
fieldset.title {
151154
margin-top: 20px;
152155
padding: 10px;

src/options/templates/confirm.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<button id=btnClose data-i18n=buttonClose></button>
99
</div>
1010
<h1><span data-i18n=labelInstall></span> - <span data-i18n=extName></span></h1>
11-
<div id=url class=ellipsis title="<%- it.url %>"><%- it.url %></div>
11+
<div id=url class=ellipsis></div>
1212
<div id=msg class=ellipsis></div>
1313
</div>
1414
<div class=frame-body>

src/options/templates/main.html

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
<div class="main">
2-
<aside>
3-
<img src="/icons/icon_64.png">
4-
<h2 data-i18n=extName></h2>
5-
<div class="line">2013-2016</div>
6-
<hr>
7-
<div class=sidemenu>
8-
<a href="#main/installed" <%= it.tab == 'main' ? 'class="active"' : '' %> data-i18n=sideMenuInstalled></a>
9-
<a href="#main/settings" <%= it.tab == 'settings' ? 'class="active"' : '' %> data-feature="settings">
10-
<span data-i18n=sideMenuSettings class="feature-text"></span>
11-
</a>
12-
<a href="#main/about" <%= it.tab == 'about' ? 'class="active"' : '' %> data-i18n=sideMenuAbout></a>
13-
</div>
14-
</aside>
15-
<div id="tab"></div>
16-
</div>
1+
<aside>
2+
<img src="/icons/icon_64.png">
3+
<h2 data-i18n=extName></h2>
4+
<div class="line">2013-2016</div>
5+
<hr>
6+
<div class=sidemenu>
7+
<a href="#main/installed" <%= it.tab == 'main' ? 'class="active"' : '' %> data-i18n=sideMenuInstalled></a>
8+
<a href="#main/settings" <%= it.tab == 'settings' ? 'class="active"' : '' %> data-feature="settings">
9+
<span data-i18n=sideMenuSettings class="feature-text"></span>
10+
</a>
11+
<a href="#main/about" <%= it.tab == 'about' ? 'class="active"' : '' %> data-i18n=sideMenuAbout></a>
12+
</div>
13+
</aside>
14+
<div id="tab"></div>
Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,26 @@
1-
<div class="content">
2-
<h1>
3-
<span data-i18n=labelAbout></span>
4-
<small>v<%= it.version %></small>
5-
</h1>
6-
<div class=line data-i18n=extDescription></div>
7-
<div class=line>
8-
<label data-i18n=labelRelated></label>
9-
<span data-i18n=anchorSupportPage></span> |
10-
<a href=http://gerald.top/donate target=_blank data-i18n=labelDonate></a> |
11-
<a href=https://github.com/Violentmonkey/Violentmonkey-mx/issues target=_blank data-i18n=labelFeedback></a>
12-
</div>
13-
<div class=line>
14-
<label data-i18n=labelAuthor></label>
15-
<span data-i18n=anchorAuthor></span>
16-
</div>
17-
<div class=line>
18-
<label data-i18n=labelTranslator></label>
19-
<span data-i18n=anchorTranslator></span>
20-
</div>
21-
<div class=line>
22-
<label data-i18n=labelCurrentLang></label>
23-
<span id="currentLang"><%= navigator.language %></span> |
24-
<a href=http://cotrans.geraldl.net target=_blank>
25-
Help with translation
26-
</a>
27-
</div>
1+
<h1>
2+
<span data-i18n=labelAbout></span>
3+
<small>v<%= it.version %></small>
4+
</h1>
5+
<div class=line data-i18n=extDescription></div>
6+
<div class=line>
7+
<label data-i18n=labelRelated></label>
8+
<span data-i18n=anchorSupportPage></span> |
9+
<a href=http://gerald.top/donate target=_blank data-i18n=labelDonate></a> |
10+
<a href=https://github.com/Violentmonkey/Violentmonkey-mx/issues target=_blank data-i18n=labelFeedback></a>
11+
</div>
12+
<div class=line>
13+
<label data-i18n=labelAuthor></label>
14+
<span data-i18n=anchorAuthor></span>
15+
</div>
16+
<div class=line>
17+
<label data-i18n=labelTranslator></label>
18+
<span data-i18n=anchorTranslator></span>
19+
</div>
20+
<div class=line>
21+
<label data-i18n=labelCurrentLang></label>
22+
<span id="currentLang"><%= navigator.language %></span> |
23+
<a href=http://cotrans.geraldl.net target=_blank>
24+
Help with translation
25+
</a>
2826
</div>
Lines changed: 51 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,57 @@
1-
<div class="content">
2-
<h1 data-i18n=labelSettings></h1>
1+
<h1 data-i18n=labelSettings></h1>
2+
<label class="line">
3+
<input id=cUpdate type=checkbox data-check=autoUpdate <%= it.autoUpdate ? 'checked' : '' %>>
4+
<span data-i18n=labelAutoUpdate></span>
5+
</label>
6+
<label class="line">
7+
<input id=cBadge type=checkbox data-check=showBadge <%= it.showBadge ? 'checked' : '' %>>
8+
<span data-i18n=labelShowBadge></span>
9+
</label>
10+
<label class="line">
11+
<input type=checkbox data-check=ignoreGrant <%= it.ignoreGrant ? 'checked' : '' %>>
12+
<span data-i18n=labelIgnoreGrant></span>
13+
</label>
14+
<label class="line">
15+
<input type=checkbox data-check=autoReload <%= it.autoReload ? 'checked' : '' %>>
16+
<span data-i18n=labelAutoReloadCurrentTab></span>
17+
</label>
18+
<div class="option-group">
319
<label class="line">
4-
<input id=cUpdate type=checkbox data-check=autoUpdate <%= it.autoUpdate ? 'checked' : '' %>>
5-
<span data-i18n=labelAutoUpdate></span>
20+
<input type="checkbox" data-check=startReload <%= it.startReload ? 'checked' : '' %>>
21+
<span data-i18n=labelStartReload></span>
622
</label>
7-
<label class="line">
8-
<input id=cBadge type=checkbox data-check=showBadge <%= it.showBadge ? 'checked' : '' %>>
9-
<span data-i18n=labelShowBadge></span>
10-
</label>
11-
<label class="line">
12-
<input type=checkbox data-check=ignoreGrant <%= it.ignoreGrant ? 'checked' : '' %>>
13-
<span data-i18n=labelIgnoreGrant></span>
14-
</label>
15-
<label class="line">
16-
<input type=checkbox data-check=autoReload <%= it.autoReload ? 'checked' : '' %>>
17-
<span data-i18n=labelAutoReloadCurrentTab></span>
18-
</label>
19-
<div class="option-group">
23+
<div class="suboption">
2024
<label class="line">
21-
<input type="checkbox" data-check=startReload <%= it.startReload ? 'checked' : '' %>>
22-
<span data-i18n=labelStartReload></span>
25+
<input type="checkbox" data-check=reloadHTTPS <%= it.reloadHTTPS ? 'checked' : '' %>>
26+
<span data-i18n=labelReloadHTTPS></span>
2327
</label>
24-
<div class="suboption">
25-
<label class="line">
26-
<input type="checkbox" data-check=reloadHTTPS <%= it.reloadHTTPS ? 'checked' : '' %>>
27-
<span data-i18n=labelReloadHTTPS></span>
28-
</label>
29-
</div>
3028
</div>
31-
<!--<label class="line">
32-
<span data-i18n=labelInjectMode></span>
33-
<select id=sInjectMode>
34-
<option value=0 data-i18n=injectModeNormal></option>
35-
<option value=1 data-i18n=injectModeAdvanced></option>
36-
</select>
37-
<span></span>
38-
</label>-->
39-
<fieldset class=title>
40-
<legend data-i18n=labelDataImport></legend>
41-
<button id=bImport data-i18n=buttonImportData></button>
42-
<button id=bVacuum data-i18n=buttonVacuum title="<%- _.i18n('hintVacuum') %>"></button>
43-
</fieldset>
44-
<fieldset class=title>
45-
<legend data-i18n=labelDataExport></legend>
46-
<b data-i18n=labelScriptsToExport></b>
47-
<label>
48-
<input id=cbValues type=checkbox data-check=exportValues <%= it.exportValues ? 'checked' : '' %>>
49-
<span data-i18n=labelExportScriptData></span>
50-
</label>
51-
<select class=export-list multiple></select>
52-
<button id=bSelect data-i18n=buttonAllNone></button>
53-
<button id=bExport data-i18n=buttonExportData></button>
54-
</fieldset>
55-
<fieldset class=title data-feature="sync">
56-
<legend data-i18n=labelSync class="feature-text"></legend>
57-
<div class="sync-services"></div>
58-
</fieldset>
5929
</div>
30+
<!--<label class="line">
31+
<span data-i18n=labelInjectMode></span>
32+
<select id=sInjectMode>
33+
<option value=0 data-i18n=injectModeNormal></option>
34+
<option value=1 data-i18n=injectModeAdvanced></option>
35+
</select>
36+
<span></span>
37+
</label>-->
38+
<fieldset class=title>
39+
<legend data-i18n=labelDataImport></legend>
40+
<button id=bImport data-i18n=buttonImportData></button>
41+
<button id=bVacuum data-i18n=buttonVacuum title="<%- _.i18n('hintVacuum') %>"></button>
42+
</fieldset>
43+
<fieldset class=title>
44+
<legend data-i18n=labelDataExport></legend>
45+
<b data-i18n=labelScriptsToExport></b>
46+
<label>
47+
<input id=cbValues type=checkbox data-check=exportValues <%= it.exportValues ? 'checked' : '' %>>
48+
<span data-i18n=labelExportScriptData></span>
49+
</label>
50+
<select class=export-list multiple></select>
51+
<button id=bSelect data-i18n=buttonAllNone></button>
52+
<button id=bExport data-i18n=buttonExportData></button>
53+
</fieldset>
54+
<fieldset class=title data-feature="sync">
55+
<legend data-i18n=labelSync class="feature-text"></legend>
56+
<div class="sync-services"></div>
57+
</fieldset>

0 commit comments

Comments
 (0)