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

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

Powered by Google App Engine
This is Rietveld 408576698