Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(65)

Side by Side Diff: chrome/renderer/resources/extensions/event.js

Issue 9192029: Bindings layer for declarative events API (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 var chrome = chrome || {}; 5 var chrome = chrome || {};
6 (function () { 6 (function () {
7 native function GetChromeHidden(); 7 native function GetChromeHidden();
8 native function AttachEvent(eventName); 8 native function AttachEvent(eventName);
9 native function DetachEvent(eventName); 9 native function DetachEvent(eventName);
10 native function Print(); 10 native function Print();
11 //native function AddRules(rules);
12 //native function RemoveRules(rules);
11 13
12 var chromeHidden = GetChromeHidden(); 14 var chromeHidden = GetChromeHidden();
13 15
14 // Local implementation of JSON.parse & JSON.stringify that protect us 16 // Local implementation of JSON.parse & JSON.stringify that protect us
15 // from being clobbered by an extension. 17 // from being clobbered by an extension.
16 // 18 //
17 // TODO(aa): This makes me so sad. We shouldn't need it, as we can just pass 19 // TODO(aa): This makes me so sad. We shouldn't need it, as we can just pass
18 // Values directly over IPC without serializing to strings and use 20 // Values directly over IPC without serializing to strings and use
19 // JSONValueConverter. 21 // JSONValueConverter.
20 chromeHidden.JSON = new (function() { 22 chromeHidden.JSON = new (function() {
(...skipping 30 matching lines...) Expand all
51 53
52 // Event object. If opt_eventName is provided, this object represents 54 // Event object. If opt_eventName is provided, this object represents
53 // the unique instance of that named event, and dispatching an event 55 // the unique instance of that named event, and dispatching an event
54 // with that name will route through this object's listeners. 56 // with that name will route through this object's listeners.
55 // 57 //
56 // Example: 58 // Example:
57 // chrome.tabs.onChanged = new chrome.Event("tab-changed"); 59 // chrome.tabs.onChanged = new chrome.Event("tab-changed");
58 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); 60 // chrome.tabs.onChanged.addListener(function(data) { alert(data); });
59 // chromeHidden.Event.dispatch("tab-changed", "hi"); 61 // chromeHidden.Event.dispatch("tab-changed", "hi");
60 // will result in an alert dialog that says 'hi'. 62 // will result in an alert dialog that says 'hi'.
61 chrome.Event = function(opt_eventName, opt_argSchemas) { 63 //
64 // If opt_eventOptions exists, it is a dictionary that contains the boolean
65 // entries "supports_listeners" and "supports_rules".
66 chrome.Event = function(opt_eventName, opt_argSchemas, opt_eventOptions,
67 opt_typesAPI) {
62 this.eventName_ = opt_eventName; 68 this.eventName_ = opt_eventName;
63 this.listeners_ = []; 69 this.listeners_ = [];
70 this.eventOptions_ = opt_eventOptions ||
71 {'supports_listeners': true, 'supports_rules': false};
Aaron Boodman 2012/01/19 23:09:46 Nit: don't need the single quotes in the object li
Matt Perry 2012/01/20 00:07:55 also, use camelCase for dict keys
battre 2012/01/24 20:36:16 Done.
battre 2012/01/24 20:36:16 Done.
72 if (opt_typesAPI) {
73 this.sendRequest_ = opt_typesAPI.sendRequest;
74 this.apiDefinitions_ = opt_typesAPI.apiDefinitions;
75 } else {
76 this.sendRequest_ = function() {};
77 this.apiDefinitions_ = {};
78 }
64 79
65 // Validate event parameters if we are in debug. 80 // Validate event parameters if we are in debug.
66 if (opt_argSchemas && 81 if (opt_argSchemas &&
67 chromeHidden.validateCallbacks && 82 chromeHidden.validateCallbacks &&
68 chromeHidden.validate) { 83 chromeHidden.validate) {
69 84
70 this.validate_ = function(args) { 85 this.validate_ = function(args) {
71 try { 86 try {
72 chromeHidden.validate(args, opt_argSchemas); 87 chromeHidden.validate(args, opt_argSchemas);
73 } catch (exception) { 88 } catch (exception) {
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 }; 135 };
121 136
122 // Test if a named event has any listeners. 137 // Test if a named event has any listeners.
123 chromeHidden.Event.hasListener = function(name) { 138 chromeHidden.Event.hasListener = function(name) {
124 return (attachedNamedEvents[name] && 139 return (attachedNamedEvents[name] &&
125 attachedNamedEvents[name].listeners_.length > 0); 140 attachedNamedEvents[name].listeners_.length > 0);
126 }; 141 };
127 142
128 // Registers a callback to be called when this event is dispatched. 143 // Registers a callback to be called when this event is dispatched.
129 chrome.Event.prototype.addListener = function(cb) { 144 chrome.Event.prototype.addListener = function(cb) {
145 if (!this.eventOptions_.supports_listeners)
146 throw new Error("This event does not support listeners.");
130 if (this.listeners_.length == 0) { 147 if (this.listeners_.length == 0) {
131 this.attach_(); 148 this.attach_();
132 } 149 }
133 this.listeners_.push(cb); 150 this.listeners_.push(cb);
134 }; 151 };
135 152
136 // Unregisters a callback. 153 // Unregisters a callback.
137 chrome.Event.prototype.removeListener = function(cb) { 154 chrome.Event.prototype.removeListener = function(cb) {
155 if (!this.eventOptions_.supports_listeners)
156 throw new Error("This event does not support listeners.");
138 var idx = this.findListener_(cb); 157 var idx = this.findListener_(cb);
139 if (idx == -1) { 158 if (idx == -1) {
140 return; 159 return;
141 } 160 }
142 161
143 this.listeners_.splice(idx, 1); 162 this.listeners_.splice(idx, 1);
144 if (this.listeners_.length == 0) { 163 if (this.listeners_.length == 0) {
145 this.detach_(); 164 this.detach_();
146 } 165 }
147 }; 166 };
148 167
149 // Test if the given callback is registered for this event. 168 // Test if the given callback is registered for this event.
150 chrome.Event.prototype.hasListener = function(cb) { 169 chrome.Event.prototype.hasListener = function(cb) {
170 if (!this.eventOptions_.supports_listeners)
171 throw new Error("This event does not support listeners.");
151 return this.findListener_(cb) > -1; 172 return this.findListener_(cb) > -1;
152 }; 173 };
153 174
154 // Test if any callbacks are registered for this event. 175 // Test if any callbacks are registered for this event.
155 chrome.Event.prototype.hasListeners = function(cb) { 176 chrome.Event.prototype.hasListeners = function(cb) {
Aaron Boodman 2012/01/19 23:09:46 whoops: the cb param here not required.
battre 2012/01/24 20:36:16 Done.
177 if (!this.eventOptions_.supports_listeners)
178 throw new Error("This event does not support listeners.");
156 return this.listeners_.length > 0; 179 return this.listeners_.length > 0;
157 }; 180 };
158 181
159 // Returns the index of the given callback if registered, or -1 if not 182 // Returns the index of the given callback if registered, or -1 if not
160 // found. 183 // found.
161 chrome.Event.prototype.findListener_ = function(cb) { 184 chrome.Event.prototype.findListener_ = function(cb) {
162 for (var i = 0; i < this.listeners_.length; i++) { 185 for (var i = 0; i < this.listeners_.length; i++) {
163 if (this.listeners_[i] == cb) { 186 if (this.listeners_[i] == cb) {
164 return i; 187 return i;
165 } 188 }
166 } 189 }
167 190
168 return -1; 191 return -1;
169 }; 192 };
170 193
171 // Dispatches this event object to all listeners, passing all supplied 194 // Dispatches this event object to all listeners, passing all supplied
172 // arguments to this function each listener. 195 // arguments to this function each listener.
173 chrome.Event.prototype.dispatch = function(varargs) { 196 chrome.Event.prototype.dispatch = function(varargs) {
197 if (!this.eventOptions_.supports_listeners)
198 throw new Error("This event does not support listeners.");
174 var args = Array.prototype.slice.call(arguments); 199 var args = Array.prototype.slice.call(arguments);
175 if (this.validate_) { 200 if (this.validate_) {
176 var validationErrors = this.validate_(args); 201 var validationErrors = this.validate_(args);
177 if (validationErrors) { 202 if (validationErrors) {
178 return validationErrors; 203 return validationErrors;
179 } 204 }
180 } 205 }
181 for (var i = 0; i < this.listeners_.length; i++) { 206 for (var i = 0; i < this.listeners_.length; i++) {
182 try { 207 try {
183 this.listeners_[i].apply(null, args); 208 this.listeners_[i].apply(null, args);
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
220 245
221 delete attachedNamedEvents[this.eventName_]; 246 delete attachedNamedEvents[this.eventName_];
222 }; 247 };
223 248
224 chrome.Event.prototype.destroy_ = function() { 249 chrome.Event.prototype.destroy_ = function() {
225 this.listeners_ = []; 250 this.listeners_ = [];
226 this.validate_ = []; 251 this.validate_ = [];
227 this.detach_(); 252 this.detach_();
228 }; 253 };
229 254
255 chrome.Event.prototype.getFunctionDefinition_ =
256 function(namespace, functionName) {
257 var filterNamespace = function(val) {return val.namespace === namespace;};
258 var apiSchema = this.apiDefinitions_.filter(filterNamespace)[0];
259 var filterFunctionName = function (val) {return val.name === functionName;};
260 return apiSchema.functions.filter(filterFunctionName)[0];
261 }
262
263 chrome.Event.prototype.addRules = function(rules, opt_cb) {
264 if (!this.eventOptions_.supports_rules)
Aaron Boodman 2012/01/19 23:09:46 nit: maybe just put supports_rules_ on |this|?
battre 2012/01/24 20:36:16 hm... I think having this as a separate dictionary
265 throw new Error("This event does not support listeners.");
Aaron Boodman 2012/01/19 23:09:46 s/listener/rule/
battre 2012/01/24 20:36:16 Done.
266 // TODO: wrap callback in such a way that
267 // - the rules are stored
268 // - rule IDs are updated
269
270 // Validate conditions and actions against specific schemas of this
271 // event object type.
272 var conditionsSchema = this.eventOptions_.conditions;
273 var actionsSchema = this.eventOptions_.actions;
274 if (!conditionsSchema || !actionsSchema) {
275 throw new Error("Error in API specification.");
276 }
277 rules.forEach(function(rule) {
278 chromeHidden.validate([rule.conditions], [conditionsSchema]);
279 chromeHidden.validate([rule.actions], [actionsSchema]);
Aaron Boodman 2012/01/19 23:09:46 Interesting :) I hadn't considered that we could j
280 })
281
282 var functionDef =
283 this.getFunctionDefinition_("experimental.declarative", "addRules");
Aaron Boodman 2012/01/19 23:09:46 I think it might be easier to just use raw IPC her
battre 2012/01/19 23:45:48 I started off with this approach. Here is the deal
Matt Perry 2012/01/20 00:07:55 This seems reasonable to me. No sense rolling cust
battre 2012/01/24 20:36:16 We don't have access to these functions, they are
284 return this.sendRequest_.call(this,
285 "experimental.declarative.addRules",
286 [this.eventName_, rules, opt_cb],
287 functionDef.parameters);
288 }
289
290 chrome.Event.prototype.removeRules = function(cb) {
291 if (!this.eventOptions_.supports_rules)
292 throw new Error("This event does not support listeners.");
293 // TODO(battre): Remove rules.
294 }
295
296 chrome.Event.prototype.getRules = function(rule_identifiers, cb) {
297 if (!this.eventOptions_.supports_rules)
298 throw new Error("This event does not support listeners.");
299 // TODO(battre): Get rules.
300 cb.apply(this, []);
301 }
302
230 // Special load events: we don't use the DOM unload because that slows 303 // Special load events: we don't use the DOM unload because that slows
231 // down tab shutdown. On the other hand, onUnload might not always fire, 304 // down tab shutdown. On the other hand, onUnload might not always fire,
232 // since Chrome will terminate renderers on shutdown (SuddenTermination). 305 // since Chrome will terminate renderers on shutdown (SuddenTermination).
233 chromeHidden.onLoad = new chrome.Event(); 306 chromeHidden.onLoad = new chrome.Event();
234 chromeHidden.onUnload = new chrome.Event(); 307 chromeHidden.onUnload = new chrome.Event();
235 308
236 chromeHidden.dispatchOnLoad = function(extensionId, isExtensionProcess, 309 chromeHidden.dispatchOnLoad = function(extensionId, isExtensionProcess,
237 isIncognitoContext) { 310 isIncognitoContext) {
238 chromeHidden.onLoad.dispatch(extensionId, isExtensionProcess, 311 chromeHidden.onLoad.dispatch(extensionId, isExtensionProcess,
239 isIncognitoContext); 312 isIncognitoContext);
240 }; 313 };
241 314
242 chromeHidden.dispatchOnUnload = function() { 315 chromeHidden.dispatchOnUnload = function() {
243 chromeHidden.onUnload.dispatch(); 316 chromeHidden.onUnload.dispatch();
244 for (var i = 0; i < allAttachedEvents.length; ++i) { 317 for (var i = 0; i < allAttachedEvents.length; ++i) {
245 var event = allAttachedEvents[i]; 318 var event = allAttachedEvents[i];
246 if (event) 319 if (event)
247 event.detach_(); 320 event.detach_();
248 } 321 }
322 // TODO(battre): Remove all rules
249 }; 323 };
250 324
251 chromeHidden.dispatchError = function(msg) { 325 chromeHidden.dispatchError = function(msg) {
252 console.error(msg); 326 console.error(msg);
253 }; 327 };
254 })(); 328 })();
OLDNEW
« no previous file with comments | « chrome/renderer/extensions/event_bindings.cc ('k') | chrome/renderer/resources/extensions/schema_generated_bindings.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698