forked from greasemonkey/greasemonkey
-
Notifications
You must be signed in to change notification settings - Fork 0
/
addons4.js
executable file
·248 lines (213 loc) · 8.26 KB
/
addons4.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
// This file specifically targets integration with the add-ons tab in Firefox
// 4+, thus it makes liberal use of features only available there.
//
// Derived from the SlipperyMonkey extension originally by Dave Townsend:
// http://hg.oxymoronical.com/extensions/SlipperyMonkey/
// http://www.oxymoronical.com/blog/2010/07/How-to-extend-the-new-Add-ons-Manager
// Module exported symbols.
var EXPORTED_SYMBOLS = ['addonsStartup'];
////////////////////////////////////////////////////////////////////////////////
// Module level imports / constants / globals.
////////////////////////////////////////////////////////////////////////////////
Components.utils.import('resource://gre/modules/AddonManager.jsm');
Components.utils.import('resource://gre/modules/Services.jsm');
Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
const Cc = Components.classes;
const Ci = Components.interfaces;
const NS_XHTML = 'http://www.w3.org/1999/xhtml';
const SCRIPT_ID_SUFFIX = '@greasespot.net';
// Pull this helper method into this module scope; it's not module-ized yet.
var GM_getConfig;
(function() {
var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader);
var scope = {};
loader.loadSubScript('chrome://greasemonkey/content/utils.js', scope);
GM_getConfig = scope.GM_getConfig;
})();
////////////////////////////////////////////////////////////////////////////////
// Addons API Integration
////////////////////////////////////////////////////////////////////////////////
var AddonProvider = {
getAddonByID: function(aId, aCallback) {
aCallback(ScriptAddonFactoryById(aId));
},
getAddonsByTypes: function(aTypes, aCallback) {
if (aTypes && aTypes.indexOf('user-script') < 0) {
aCallback([]);
} else {
var scriptAddons = [];
GM_getConfig().scripts.forEach(function(script) {
scriptAddons.push(ScriptAddonFactoryByScript(script));
});
aCallback(scriptAddons);
}
}
};
var ScriptAddonCache = {};
function ScriptAddonFactoryByScript(aScript) {
var id = aScript.id + SCRIPT_ID_SUFFIX;
if (!(id in ScriptAddonCache)) {
ScriptAddonCache[id] = new ScriptAddon(aScript);
}
return ScriptAddonCache[id];
}
function ScriptAddonFactoryById(aId) {
var scripts = GM_getConfig().getMatchingScripts(
function(script) {
return (script.id + SCRIPT_ID_SUFFIX) == aId;
});
if (1 == scripts.length) return ScriptAddonFactoryByScript(scripts[0]);
// TODO: throw an error instead?
return null;
}
// https://developer.mozilla.org/en/Addons/Add-on_Manager/Addon
function ScriptAddon(aScript) {
this._script = aScript;
this.id = aScript.id + SCRIPT_ID_SUFFIX;
this.name = this._script.name;
this.version = this._script.version;
this.description = this._script.description;
this.iconURL = this._script.icon.fileURL;
this.size = this._script.size;
this.updateDate = this._script.modifiedDate;
}
// Required attributes.
ScriptAddon.prototype.id = null;
ScriptAddon.prototype.version = null;
ScriptAddon.prototype.type = 'user-script';
ScriptAddon.prototype.isCompatible = true;
ScriptAddon.prototype.providesUpdatesSecurely = true;
ScriptAddon.prototype.blocklistState = 0;
ScriptAddon.prototype.appDisabled = false;
ScriptAddon.prototype.scope = AddonManager.SCOPE_PROFILE;
ScriptAddon.prototype.name = null;
ScriptAddon.prototype.creator = null;
ScriptAddon.prototype.pendingOperations = 0;
// Optional attributes
ScriptAddon.prototype.description = null;
ScriptAddon.prototype.size = null;
// Private and custom attributes.
ScriptAddon.prototype._script = null;
ScriptAddon.prototype.__defineGetter__('executionIndex',
function ScriptAddon_prototype_getter_executionIndex() {
return GM_getConfig()._scripts.indexOf(this._script);
});
// Getters/setters/functions for API attributes.
ScriptAddon.prototype.__defineGetter__('isActive',
function ScriptAddon_prototype_getter_isActive() {
return this._script.enabled;
});
ScriptAddon.prototype.__defineGetter__('userDisabled',
function ScriptAddon_prototype_getter_userDisabled() {
return !this._script.enabled;
});
ScriptAddon.prototype.__defineSetter__('userDisabled',
function ScriptAddon_prototype_setter_userDisabled(val) {
if (val == this.userDisabled) {
return val;
}
AddonManagerPrivate.callAddonListeners(
val ? 'onEnabling' : 'onDisabling', this, false);
this._script.enabled = !val;
AddonManagerPrivate.callAddonListeners(
val ? 'onEnabled' : 'onDisabled', this);
});
ScriptAddon.prototype.__defineGetter__('permissions',
function ScriptAddon_prototype_getter_permissions() {
var perms = AddonManager.PERM_CAN_UNINSTALL;
perms |= this.userDisabled
? AddonManager.PERM_CAN_ENABLE
: AddonManager.PERM_CAN_DISABLE;
return perms;
});
ScriptAddon.prototype.isCompatibleWith = function() {
return true;
};
ScriptAddon.prototype.findUpdates = function(aListener) {
if ('onNoCompatibilityUpdateAvailable' in aListener) {
aListener.onNoCompatibilityUpdateAvailable(this);
}
if ('onNoUpdateAvailable' in aListener) {
aListener.onNoUpdateAvailable(this);
}
if ('onUpdateFinished' in aListener) {
aListener.onUpdateFinished(this);
}
};
ScriptAddon.prototype.uninstall = function() {
AddonManagerPrivate.callAddonListeners("onUninstalling", this, false);
// TODO: pick an appropriate time, and act on these pending uninstalls.
this.pendingOperations |= AddonManager.PENDING_UNINSTALL;
AddonManagerPrivate.callAddonListeners("onUninstalled", this);
};
ScriptAddon.prototype.cancelUninstall = function() {
this.pendingOperations ^= AddonManager.PENDING_UNINSTALL;
AddonManagerPrivate.callAddonListeners("onOperationCancelled", this);
};
////////////////////////////////////////////////////////////////////////////////
var WindowObserver = {
// Inject the 'User Scripts' choice into the list of add-on types.
addToAddonsManager: function(aWindow) {
// This function used to handle tasks that are now done in a XUL overlay.
// Leaving the function here in case the routing to make it run at the
// right time proves useful.
// TODO: Remove once Firefox 4 is final, if it is still unused.
},
findAllAddonsManagers: function() {
var managers = [];
var windows = Services.wm.getEnumerator('navigator:browser');
while (windows.hasMoreElements()) {
var window = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
window.gBrowser.browsers.forEach(function(aBrowser) {
if (aBrowser.currentURI.spec == 'about:addons')
managers.push(aBrowser.contentWindow);
});
}
return managers;
},
addToAddonsManagers: function() {
var managers = this.findAllAddonsManagers();
managers.forEach(function(aWindow) {
this.addToAddonsManager(aWindow);
}, this);
},
/* TODO: restore when we are restartless for FF4.
removeFromAddonsManagers: function() {
var managers = this.findAllAddonsManagers();
managers.forEach(function(aWindow) {
var window = aWindow.wrappedJSObject;
var scripts = window.document.getElementById('category-scripts');
scripts.parentNode.removeChild(scripts);
var styles = window.document.getElementById('script-styles');
styles.parentNode.removeChild(styles);
window.gStrings.ext = window.gStrings.ext.basebundle;
});
},
*/
observe: function(aSubject, aTopic, aData) {
var win = aSubject;
var uri = win.document.documentURIObject;
if (uri.spec != 'about:addons') return;
// Run after DOM load, so that the window contents exist, to be altered.
win.addEventListener('DOMContentLoaded',
function() { WindowObserver.addToAddonsManager(win); },
false);
}
};
////////////////////////////////////////////////////////////////////////////////
var _addonsStartupHasRun = false;
function addonsStartup(aParams) {
if (_addonsStartupHasRun) return;
_addonsStartupHasRun = true;
Services.obs.addObserver(WindowObserver, 'chrome-document-global-created', false);
AddonManagerPrivate.registerProvider(AddonProvider);
WindowObserver.addToAddonsManagers();
}
/* TODO: restore when we are restartless for FF4.
function addonsShutdown() {
WindowObserver.removeFromAddonsManagers();
AddonManagerPrivate.unregisterProvider(AddonProvider);
Services.obs.removeObserver(WindowObserver, 'chrome-document-global-created');
}
*/