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

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

Issue 1915753002: Sanitize inheritance in callers of utils.expose (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: webview: Set proto on prototype. Created 4 years, 8 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
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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 // TODO(robwu): Fix indentation.
6
5 var exceptionHandler = require('uncaught_exception_handler'); 7 var exceptionHandler = require('uncaught_exception_handler');
6 var eventNatives = requireNative('event_natives'); 8 var eventNatives = requireNative('event_natives');
7 var logging = requireNative('logging'); 9 var logging = requireNative('logging');
8 var schemaRegistry = requireNative('schema_registry'); 10 var schemaRegistry = requireNative('schema_registry');
9 var sendRequest = require('sendRequest').sendRequest; 11 var sendRequest = require('sendRequest').sendRequest;
10 var utils = require('utils'); 12 var utils = require('utils');
11 var validate = require('schemaUtils').validate; 13 var validate = require('schemaUtils').validate;
12 14
13 // Schemas for the rule-style functions on the events API that 15 // Schemas for the rule-style functions on the events API that
14 // only need to be generated occasionally, so populate them lazily. 16 // only need to be generated occasionally, so populate them lazily.
15 var ruleFunctionSchemas = { 17 var ruleFunctionSchemas = {
18 __proto__: null,
16 // These values are set lazily: 19 // These values are set lazily:
17 // addRules: {}, 20 // addRules: {},
18 // getRules: {}, 21 // getRules: {},
19 // removeRules: {} 22 // removeRules: {}
20 }; 23 };
21 24
22 // This function ensures that |ruleFunctionSchemas| is populated. 25 // This function ensures that |ruleFunctionSchemas| is populated.
23 function ensureRuleSchemasLoaded() { 26 function ensureRuleSchemasLoaded() {
24 if (ruleFunctionSchemas.addRules) 27 if (ruleFunctionSchemas.addRules)
25 return; 28 return;
26 var eventsSchema = schemaRegistry.GetSchema("events"); 29 var eventsSchema = schemaRegistry.GetSchema("events");
27 var eventType = utils.lookup(eventsSchema.types, 'id', 'events.Event'); 30 var eventType = utils.lookup(eventsSchema.types, 'id', 'events.Event');
28 31
29 ruleFunctionSchemas.addRules = 32 ruleFunctionSchemas.addRules =
30 utils.lookup(eventType.functions, 'name', 'addRules'); 33 utils.lookup(eventType.functions, 'name', 'addRules');
31 ruleFunctionSchemas.getRules = 34 ruleFunctionSchemas.getRules =
32 utils.lookup(eventType.functions, 'name', 'getRules'); 35 utils.lookup(eventType.functions, 'name', 'getRules');
33 ruleFunctionSchemas.removeRules = 36 ruleFunctionSchemas.removeRules =
34 utils.lookup(eventType.functions, 'name', 'removeRules'); 37 utils.lookup(eventType.functions, 'name', 'removeRules');
35 } 38 }
36 39
37 // A map of event names to the event object that is registered to that name. 40 // A map of event names to the event object that is registered to that name.
38 var attachedNamedEvents = {}; 41 var attachedNamedEvents = {__proto__: null};
39 42
40 // A map of functions that massage event arguments before they are dispatched. 43 // A map of functions that massage event arguments before they are dispatched.
41 // Key is event name, value is function. 44 // Key is event name, value is function.
42 var eventArgumentMassagers = {}; 45 var eventArgumentMassagers = {__proto__: null};
43 46
44 // An attachment strategy for events that aren't attached to the browser. 47 // An attachment strategy for events that aren't attached to the browser.
45 // This applies to events with the "unmanaged" option and events without 48 // This applies to events with the "unmanaged" option and events without
46 // names. 49 // names.
47 var NullAttachmentStrategy = function(event) { 50 function NullAttachmentStrategy(event) {
48 this.event_ = event; 51 this.event_ = event;
49 }; 52 }
53 $Object.setPrototypeOf(NullAttachmentStrategy, null);
54 $Object.setPrototypeOf(NullAttachmentStrategy.prototype, null);
50 NullAttachmentStrategy.prototype.onAddedListener = 55 NullAttachmentStrategy.prototype.onAddedListener =
51 function(listener) { 56 function(listener) {
52 }; 57 };
53 NullAttachmentStrategy.prototype.onRemovedListener = 58 NullAttachmentStrategy.prototype.onRemovedListener =
54 function(listener) { 59 function(listener) {
55 }; 60 };
56 NullAttachmentStrategy.prototype.detach = function(manual) { 61 NullAttachmentStrategy.prototype.detach = function(manual) {
57 }; 62 };
58 NullAttachmentStrategy.prototype.getListenersByIDs = function(ids) { 63 NullAttachmentStrategy.prototype.getListenersByIDs = function(ids) {
59 // |ids| is for filtered events only. 64 // |ids| is for filtered events only.
60 return this.event_.listeners; 65 return this.event_.listeners;
61 }; 66 };
62 67
63 // Handles adding/removing/dispatching listeners for unfiltered events. 68 // Handles adding/removing/dispatching listeners for unfiltered events.
64 var UnfilteredAttachmentStrategy = function(event) { 69 function UnfilteredAttachmentStrategy(event) {
65 this.event_ = event; 70 this.event_ = event;
66 }; 71 }
72 $Object.setPrototypeOf(UnfilteredAttachmentStrategy, null);
73 $Object.setPrototypeOf(UnfilteredAttachmentStrategy.prototype, null);
67 74
68 UnfilteredAttachmentStrategy.prototype.onAddedListener = 75 UnfilteredAttachmentStrategy.prototype.onAddedListener =
69 function(listener) { 76 function(listener) {
70 // Only attach / detach on the first / last listener removed. 77 // Only attach / detach on the first / last listener removed.
71 if (this.event_.listeners.length == 0) 78 if (this.event_.listeners.length == 0)
72 eventNatives.AttachEvent(this.event_.eventName); 79 eventNatives.AttachEvent(this.event_.eventName);
73 }; 80 };
74 81
75 UnfilteredAttachmentStrategy.prototype.onRemovedListener = 82 UnfilteredAttachmentStrategy.prototype.onRemovedListener =
76 function(listener) { 83 function(listener) {
77 if (this.event_.listeners.length == 0) 84 if (this.event_.listeners.length == 0)
78 this.detach(true); 85 this.detach(true);
79 }; 86 };
80 87
81 UnfilteredAttachmentStrategy.prototype.detach = function(manual) { 88 UnfilteredAttachmentStrategy.prototype.detach = function(manual) {
82 eventNatives.DetachEvent(this.event_.eventName, manual); 89 eventNatives.DetachEvent(this.event_.eventName, manual);
83 }; 90 };
84 91
85 UnfilteredAttachmentStrategy.prototype.getListenersByIDs = function(ids) { 92 UnfilteredAttachmentStrategy.prototype.getListenersByIDs = function(ids) {
86 // |ids| is for filtered events only. 93 // |ids| is for filtered events only.
87 return this.event_.listeners; 94 return this.event_.listeners;
88 }; 95 };
89 96
90 var FilteredAttachmentStrategy = function(event) { 97 function FilteredAttachmentStrategy(event) {
91 this.event_ = event; 98 this.event_ = event;
92 this.listenerMap_ = {}; 99 this.listenerMap_ = {__proto__: null};
93 }; 100 }
101 $Object.setPrototypeOf(FilteredAttachmentStrategy, null);
102 $Object.setPrototypeOf(FilteredAttachmentStrategy.prototype, null);
94 103
95 FilteredAttachmentStrategy.idToEventMap = {}; 104 FilteredAttachmentStrategy.idToEventMap = {__proto__: null};
96 105
97 FilteredAttachmentStrategy.prototype.onAddedListener = function(listener) { 106 FilteredAttachmentStrategy.prototype.onAddedListener = function(listener) {
98 var id = eventNatives.AttachFilteredEvent(this.event_.eventName, 107 var id = eventNatives.AttachFilteredEvent(this.event_.eventName,
99 listener.filters || {}); 108 listener.filters || {});
100 if (id == -1) 109 if (id == -1)
101 throw new Error("Can't add listener"); 110 throw new Error("Can't add listener");
102 listener.id = id; 111 listener.id = id;
103 this.listenerMap_[id] = listener; 112 this.listenerMap_[id] = listener;
104 FilteredAttachmentStrategy.idToEventMap[id] = this.event_; 113 FilteredAttachmentStrategy.idToEventMap[id] = this.event_;
105 }; 114 };
(...skipping 18 matching lines...) Expand all
124 }; 133 };
125 134
126 FilteredAttachmentStrategy.prototype.getListenersByIDs = function(ids) { 135 FilteredAttachmentStrategy.prototype.getListenersByIDs = function(ids) {
127 var result = []; 136 var result = [];
128 for (var i = 0; i < ids.length; i++) 137 for (var i = 0; i < ids.length; i++)
129 $Array.push(result, this.listenerMap_[ids[i]]); 138 $Array.push(result, this.listenerMap_[ids[i]]);
130 return result; 139 return result;
131 }; 140 };
132 141
133 function parseEventOptions(opt_eventOptions) { 142 function parseEventOptions(opt_eventOptions) {
134 function merge(dest, src) { 143 return $Object.assign({
135 for (var k in src) { 144 __proto__: null,
136 if (!$Object.hasOwnProperty(dest, k)) { 145 }, {
137 dest[k] = src[k];
138 }
139 }
140 }
141
142 var options = $Object.assign({}, opt_eventOptions || {});
143 merge(options, {
144 // Event supports adding listeners with filters ("filtered events"), for 146 // Event supports adding listeners with filters ("filtered events"), for
145 // example as used in the webNavigation API. 147 // example as used in the webNavigation API.
146 // 148 //
147 // event.addListener(listener, [filter1, filter2]); 149 // event.addListener(listener, [filter1, filter2]);
148 supportsFilters: false, 150 supportsFilters: false,
149 151
150 // Events supports vanilla events. Most APIs use these. 152 // Events supports vanilla events. Most APIs use these.
151 // 153 //
152 // event.addListener(listener); 154 // event.addListener(listener);
153 supportsListeners: true, 155 supportsListeners: true,
154 156
155 // Event supports adding rules ("declarative events") rather than 157 // Event supports adding rules ("declarative events") rather than
156 // listeners, for example as used in the declarativeWebRequest API. 158 // listeners, for example as used in the declarativeWebRequest API.
157 // 159 //
158 // event.addRules([rule1, rule2]); 160 // event.addRules([rule1, rule2]);
159 supportsRules: false, 161 supportsRules: false,
160 162
161 // Event is unmanaged in that the browser has no knowledge of its 163 // Event is unmanaged in that the browser has no knowledge of its
162 // existence; it's never invoked, doesn't keep the renderer alive, and 164 // existence; it's never invoked, doesn't keep the renderer alive, and
163 // the bindings system has no knowledge of it. 165 // the bindings system has no knowledge of it.
164 // 166 //
165 // Both events created by user code (new chrome.Event()) and messaging 167 // Both events created by user code (new chrome.Event()) and messaging
166 // events are unmanaged, though in the latter case the browser *does* 168 // events are unmanaged, though in the latter case the browser *does*
167 // interact indirectly with them via IPCs written by hand. 169 // interact indirectly with them via IPCs written by hand.
168 unmanaged: false, 170 unmanaged: false,
169 }); 171 }, opt_eventOptions);
170 return options; 172 }
171 };
172 173
173 // Event object. If opt_eventName is provided, this object represents 174 // Event object. If opt_eventName is provided, this object represents
174 // the unique instance of that named event, and dispatching an event 175 // the unique instance of that named event, and dispatching an event
175 // with that name will route through this object's listeners. Note that 176 // with that name will route through this object's listeners. Note that
176 // opt_eventName is required for events that support rules. 177 // opt_eventName is required for events that support rules.
177 // 178 //
178 // Example: 179 // Example:
179 // var Event = require('event_bindings').Event; 180 // var Event = require('event_bindings').Event;
180 // chrome.tabs.onChanged = new Event("tab-changed"); 181 // chrome.tabs.onChanged = new Event("tab-changed");
181 // chrome.tabs.onChanged.addListener(function(data) { alert(data); }); 182 // chrome.tabs.onChanged.addListener(function(data) { alert(data); });
182 // Event.dispatch("tab-changed", "hi"); 183 // Event.dispatch("tab-changed", "hi");
183 // will result in an alert dialog that says 'hi'. 184 // will result in an alert dialog that says 'hi'.
184 // 185 //
185 // If opt_eventOptions exists, it is a dictionary that contains the boolean 186 // If opt_eventOptions exists, it is a dictionary that contains the boolean
186 // entries "supportsListeners" and "supportsRules". 187 // entries "supportsListeners" and "supportsRules".
187 // If opt_webViewInstanceId exists, it is an integer uniquely identifying a 188 // If opt_webViewInstanceId exists, it is an integer uniquely identifying a
188 // <webview> tag within the embedder. If it does not exist, then this is an 189 // <webview> tag within the embedder. If it does not exist, then this is an
189 // extension event rather than a <webview> event. 190 // extension event rather than a <webview> event.
190 var EventImpl = function(opt_eventName, opt_argSchemas, opt_eventOptions, 191 function EventImpl(opt_eventName, opt_argSchemas, opt_eventOptions,
191 opt_webViewInstanceId) { 192 opt_webViewInstanceId) {
192 this.eventName = opt_eventName; 193 this.eventName = opt_eventName;
193 this.argSchemas = opt_argSchemas; 194 this.argSchemas = opt_argSchemas;
194 this.listeners = []; 195 this.listeners = [];
195 this.eventOptions = parseEventOptions(opt_eventOptions); 196 this.eventOptions = parseEventOptions(opt_eventOptions);
196 this.webViewInstanceId = opt_webViewInstanceId || 0; 197 this.webViewInstanceId = opt_webViewInstanceId || 0;
197 198
198 if (!this.eventName) { 199 if (!this.eventName) {
199 if (this.eventOptions.supportsRules) 200 if (this.eventOptions.supportsRules)
200 throw new Error("Events that support rules require an event name."); 201 throw new Error("Events that support rules require an event name.");
201 // Events without names cannot be managed by the browser by definition 202 // Events without names cannot be managed by the browser by definition
202 // (the browser has no way of identifying them). 203 // (the browser has no way of identifying them).
203 this.eventOptions.unmanaged = true; 204 this.eventOptions.unmanaged = true;
204 } 205 }
205 206
206 // Track whether the event has been destroyed to help track down the cause 207 // Track whether the event has been destroyed to help track down the cause
207 // of http://crbug.com/258526. 208 // of http://crbug.com/258526.
208 // This variable will eventually hold the stack trace of the destroy call. 209 // This variable will eventually hold the stack trace of the destroy call.
209 // TODO(kalman): Delete this and replace with more sound logic that catches 210 // TODO(kalman): Delete this and replace with more sound logic that catches
210 // when events are used without being *attached*. 211 // when events are used without being *attached*.
211 this.destroyed = null; 212 this.destroyed = null;
212 213
213 if (this.eventOptions.unmanaged) 214 if (this.eventOptions.unmanaged)
214 this.attachmentStrategy = new NullAttachmentStrategy(this); 215 this.attachmentStrategy = new NullAttachmentStrategy(this);
215 else if (this.eventOptions.supportsFilters) 216 else if (this.eventOptions.supportsFilters)
216 this.attachmentStrategy = new FilteredAttachmentStrategy(this); 217 this.attachmentStrategy = new FilteredAttachmentStrategy(this);
217 else 218 else
218 this.attachmentStrategy = new UnfilteredAttachmentStrategy(this); 219 this.attachmentStrategy = new UnfilteredAttachmentStrategy(this);
219 }; 220 }
221 $Object.setPrototypeOf(EventImpl, null);
222 $Object.setPrototypeOf(EventImpl.prototype, null);
220 223
221 // callback is a function(args, dispatch). args are the args we receive from 224 // callback is a function(args, dispatch). args are the args we receive from
222 // dispatchEvent(), and dispatch is a function(args) that dispatches args to 225 // dispatchEvent(), and dispatch is a function(args) that dispatches args to
223 // its listeners. 226 // its listeners.
224 function registerArgumentMassager(name, callback) { 227 function registerArgumentMassager(name, callback) {
225 if (eventArgumentMassagers[name]) 228 if (eventArgumentMassagers[name])
226 throw new Error("Massager already registered for event: " + name); 229 throw new Error("Massager already registered for event: " + name);
227 eventArgumentMassagers[name] = callback; 230 eventArgumentMassagers[name] = callback;
228 } 231 }
229 232
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after
406 409
407 EventImpl.prototype.addRules = function(rules, opt_cb) { 410 EventImpl.prototype.addRules = function(rules, opt_cb) {
408 if (!this.eventOptions.supportsRules) 411 if (!this.eventOptions.supportsRules)
409 throw new Error("This event does not support rules."); 412 throw new Error("This event does not support rules.");
410 413
411 // Takes a list of JSON datatype identifiers and returns a schema fragment 414 // Takes a list of JSON datatype identifiers and returns a schema fragment
412 // that verifies that a JSON object corresponds to an array of only these 415 // that verifies that a JSON object corresponds to an array of only these
413 // data types. 416 // data types.
414 function buildArrayOfChoicesSchema(typesList) { 417 function buildArrayOfChoicesSchema(typesList) {
415 return { 418 return {
419 __proto__: null,
416 'type': 'array', 420 'type': 'array',
417 'items': { 421 'items': {
418 'choices': $Array.map(typesList, function(el) {return {'$ref': el};}) 422 __proto__: null,
423 'choices': $Array.map(typesList, function(el) {
424 return {
425 __proto__: null,
426 '$ref': el,
427 };
428 }),
419 } 429 }
420 }; 430 };
421 }; 431 }
422 432
423 // Validate conditions and actions against specific schemas of this 433 // Validate conditions and actions against specific schemas of this
424 // event object type. 434 // event object type.
425 // |rules| is an array of JSON objects that follow the Rule type of the 435 // |rules| is an array of JSON objects that follow the Rule type of the
426 // declarative extension APIs. |conditions| is an array of JSON type 436 // declarative extension APIs. |conditions| is an array of JSON type
427 // identifiers that are allowed to occur in the conditions attribute of each 437 // identifiers that are allowed to occur in the conditions attribute of each
428 // rule. Likewise, |actions| is an array of JSON type identifiers that are 438 // rule. Likewise, |actions| is an array of JSON type identifiers that are
429 // allowed to occur in the actions attribute of each rule. 439 // allowed to occur in the actions attribute of each rule.
430 function validateRules(rules, conditions, actions) { 440 function validateRules(rules, conditions, actions) {
431 var conditionsSchema = buildArrayOfChoicesSchema(conditions); 441 var conditionsSchema = buildArrayOfChoicesSchema(conditions);
(...skipping 10 matching lines...) Expand all
442 } 452 }
443 453
444 validateRules(rules, 454 validateRules(rules,
445 this.eventOptions.conditions, 455 this.eventOptions.conditions,
446 this.eventOptions.actions); 456 this.eventOptions.actions);
447 457
448 ensureRuleSchemasLoaded(); 458 ensureRuleSchemasLoaded();
449 // We remove the first parameter from the validation to give the user more 459 // We remove the first parameter from the validation to give the user more
450 // meaningful error messages. 460 // meaningful error messages.
451 validate([this.webViewInstanceId, rules, opt_cb], 461 validate([this.webViewInstanceId, rules, opt_cb],
452 $Array.splice( 462 $Array.slice(ruleFunctionSchemas.addRules.parameters, 1));
453 $Array.slice(ruleFunctionSchemas.addRules.parameters), 1));
454 sendRequest( 463 sendRequest(
455 "events.addRules", 464 "events.addRules",
456 [this.eventName, this.webViewInstanceId, rules, opt_cb], 465 [this.eventName, this.webViewInstanceId, rules, opt_cb],
457 ruleFunctionSchemas.addRules.parameters); 466 ruleFunctionSchemas.addRules.parameters);
458 } 467 }
459 468
460 EventImpl.prototype.removeRules = function(ruleIdentifiers, opt_cb) { 469 EventImpl.prototype.removeRules = function(ruleIdentifiers, opt_cb) {
461 if (!this.eventOptions.supportsRules) 470 if (!this.eventOptions.supportsRules)
462 throw new Error("This event does not support rules."); 471 throw new Error("This event does not support rules.");
463 ensureRuleSchemasLoaded(); 472 ensureRuleSchemasLoaded();
464 // We remove the first parameter from the validation to give the user more 473 // We remove the first parameter from the validation to give the user more
465 // meaningful error messages. 474 // meaningful error messages.
466 validate([this.webViewInstanceId, ruleIdentifiers, opt_cb], 475 validate([this.webViewInstanceId, ruleIdentifiers, opt_cb],
467 $Array.splice( 476 $Array.slice(ruleFunctionSchemas.removeRules.parameters, 1));
468 $Array.slice(ruleFunctionSchemas.removeRules.parameters), 1));
469 sendRequest("events.removeRules", 477 sendRequest("events.removeRules",
470 [this.eventName, 478 [this.eventName,
471 this.webViewInstanceId, 479 this.webViewInstanceId,
472 ruleIdentifiers, 480 ruleIdentifiers,
473 opt_cb], 481 opt_cb],
474 ruleFunctionSchemas.removeRules.parameters); 482 ruleFunctionSchemas.removeRules.parameters);
475 } 483 }
476 484
477 EventImpl.prototype.getRules = function(ruleIdentifiers, cb) { 485 EventImpl.prototype.getRules = function(ruleIdentifiers, cb) {
478 if (!this.eventOptions.supportsRules) 486 if (!this.eventOptions.supportsRules)
479 throw new Error("This event does not support rules."); 487 throw new Error("This event does not support rules.");
480 ensureRuleSchemasLoaded(); 488 ensureRuleSchemasLoaded();
481 // We remove the first parameter from the validation to give the user more 489 // We remove the first parameter from the validation to give the user more
482 // meaningful error messages. 490 // meaningful error messages.
483 validate([this.webViewInstanceId, ruleIdentifiers, cb], 491 validate([this.webViewInstanceId, ruleIdentifiers, cb],
484 $Array.splice( 492 $Array.slice(ruleFunctionSchemas.getRules.parameters, 1));
485 $Array.slice(ruleFunctionSchemas.getRules.parameters), 1));
486 493
487 sendRequest( 494 sendRequest(
488 "events.getRules", 495 "events.getRules",
489 [this.eventName, this.webViewInstanceId, ruleIdentifiers, cb], 496 [this.eventName, this.webViewInstanceId, ruleIdentifiers, cb],
490 ruleFunctionSchemas.getRules.parameters); 497 ruleFunctionSchemas.getRules.parameters);
491 } 498 }
492 499
493 function Event() { 500 function Event() {
494 privates(Event).constructPrivate(this, arguments); 501 privates(Event).constructPrivate(this, arguments);
495 } 502 }
(...skipping 10 matching lines...) Expand all
506 'getRules', 513 'getRules',
507 ], 514 ],
508 }); 515 });
509 516
510 // NOTE: Event is (lazily) exposed as chrome.Event from dispatcher.cc. 517 // NOTE: Event is (lazily) exposed as chrome.Event from dispatcher.cc.
511 exports.$set('Event', Event); 518 exports.$set('Event', Event);
512 519
513 exports.$set('dispatchEvent', dispatchEvent); 520 exports.$set('dispatchEvent', dispatchEvent);
514 exports.$set('parseEventOptions', parseEventOptions); 521 exports.$set('parseEventOptions', parseEventOptions);
515 exports.$set('registerArgumentMassager', registerArgumentMassager); 522 exports.$set('registerArgumentMassager', registerArgumentMassager);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698