OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 // Custom binding for the webRequestInternal API. | |
6 | |
7 var binding = require('binding').Binding.create('webRequestInternal'); | |
8 var eventBindings = require('event_bindings'); | |
9 var sendRequest = require('sendRequest').sendRequest; | |
10 var validate = require('schemaUtils').validate; | |
11 var utils = require('utils'); | |
12 var idGeneratorNatives = requireNative('id_generator'); | |
13 | |
14 var webRequestInternal; | |
15 | |
16 function GetUniqueSubEventName(eventName) { | |
17 return eventName + "/" + idGeneratorNatives.GetNextId(); | |
18 } | |
19 | |
20 // WebRequestEventImpl object. This is used for special webRequest events | |
21 // with extra parameters. Each invocation of addListener creates a new named | |
22 // sub-event. That sub-event is associated with the extra parameters in the | |
23 // browser process, so that only it is dispatched when the main event occurs | |
24 // matching the extra parameters. | |
25 // | |
26 // Example: | |
27 // chrome.webRequest.onBeforeRequest.addListener( | |
28 // callback, {urls: 'http://*.google.com/*'}); | |
29 // ^ callback will only be called for onBeforeRequests matching the filter. | |
30 function WebRequestEventImpl(eventName, opt_argSchemas, opt_extraArgSchemas, | |
31 opt_eventOptions, opt_webViewInstanceId) { | |
32 if (typeof eventName != 'string') | |
33 throw new Error('chrome.WebRequestEvent requires an event name.'); | |
34 | |
35 this.eventName = eventName; | |
36 this.argSchemas = opt_argSchemas; | |
37 this.extraArgSchemas = opt_extraArgSchemas; | |
38 this.webViewInstanceId = opt_webViewInstanceId || 0; | |
39 this.subEvents = []; | |
40 this.eventOptions = eventBindings.parseEventOptions(opt_eventOptions); | |
41 if (this.eventOptions.supportsRules) { | |
42 this.eventForRules = | |
43 new eventBindings.Event(eventName, opt_argSchemas, opt_eventOptions, | |
44 opt_webViewInstanceId); | |
45 } | |
46 } | |
47 | |
48 // Test if the given callback is registered for this event. | |
49 WebRequestEventImpl.prototype.hasListener = function(cb) { | |
50 if (!this.eventOptions.supportsListeners) | |
51 throw new Error('This event does not support listeners.'); | |
52 return this.findListener_(cb) > -1; | |
53 }; | |
54 | |
55 // Test if any callbacks are registered fur thus event. | |
56 WebRequestEventImpl.prototype.hasListeners = function() { | |
57 if (!this.eventOptions.supportsListeners) | |
58 throw new Error('This event does not support listeners.'); | |
59 return this.subEvents.length > 0; | |
60 }; | |
61 | |
62 // Registers a callback to be called when this event is dispatched. If | |
63 // opt_filter is specified, then the callback is only called for events that | |
64 // match the given filters. If opt_extraInfo is specified, the given optional | |
65 // info is sent to the callback. | |
66 WebRequestEventImpl.prototype.addListener = | |
67 function(cb, opt_filter, opt_extraInfo) { | |
68 if (!this.eventOptions.supportsListeners) | |
69 throw new Error('This event does not support listeners.'); | |
70 // NOTE(benjhayden) New APIs should not use this subEventName trick! It does | |
71 // not play well with event pages. See downloads.onDeterminingFilename and | |
72 // ExtensionDownloadsEventRouter for an alternative approach. | |
73 var subEventName = GetUniqueSubEventName(this.eventName); | |
74 // Note: this could fail to validate, in which case we would not add the | |
75 // subEvent listener. | |
76 validate($Array.slice(arguments, 1), this.extraArgSchemas); | |
77 webRequestInternal.addEventListener( | |
78 cb, opt_filter, opt_extraInfo, this.eventName, subEventName, | |
79 this.webViewInstanceId); | |
80 | |
81 var subEvent = new eventBindings.Event(subEventName, this.argSchemas); | |
82 var subEventCallback = cb; | |
83 if (opt_extraInfo && opt_extraInfo.indexOf('blocking') >= 0) { | |
84 var eventName = this.eventName; | |
85 subEventCallback = function() { | |
86 var requestId = arguments[0].requestId; | |
87 try { | |
88 var result = $Function.apply(cb, null, arguments); | |
89 webRequestInternal.eventHandled( | |
90 eventName, subEventName, requestId, result); | |
91 } catch (e) { | |
92 webRequestInternal.eventHandled( | |
93 eventName, subEventName, requestId); | |
94 throw e; | |
95 } | |
96 }; | |
97 } else if (opt_extraInfo && opt_extraInfo.indexOf('asyncBlocking') >= 0) { | |
98 var eventName = this.eventName; | |
99 subEventCallback = function() { | |
100 var details = arguments[0]; | |
101 var requestId = details.requestId; | |
102 var handledCallback = function(response) { | |
103 webRequestInternal.eventHandled( | |
104 eventName, subEventName, requestId, response); | |
105 }; | |
106 $Function.apply(cb, null, [details, handledCallback]); | |
107 }; | |
108 } | |
109 $Array.push(this.subEvents, | |
110 {subEvent: subEvent, callback: cb, subEventCallback: subEventCallback}); | |
111 subEvent.addListener(subEventCallback); | |
112 }; | |
113 | |
114 // Unregisters a callback. | |
115 WebRequestEventImpl.prototype.removeListener = function(cb) { | |
116 if (!this.eventOptions.supportsListeners) | |
117 throw new Error('This event does not support listeners.'); | |
118 var idx; | |
119 while ((idx = this.findListener_(cb)) >= 0) { | |
120 var e = this.subEvents[idx]; | |
121 e.subEvent.removeListener(e.subEventCallback); | |
122 if (e.subEvent.hasListeners()) { | |
123 console.error( | |
124 'Internal error: webRequest subEvent has orphaned listeners.'); | |
125 } | |
126 $Array.splice(this.subEvents, idx, 1); | |
127 } | |
128 }; | |
129 | |
130 WebRequestEventImpl.prototype.findListener_ = function(cb) { | |
131 for (var i in this.subEvents) { | |
132 var e = this.subEvents[i]; | |
133 if (e.callback === cb) { | |
134 if (e.subEvent.hasListener(e.subEventCallback)) | |
135 return i; | |
136 console.error('Internal error: webRequest subEvent has no callback.'); | |
137 } | |
138 } | |
139 | |
140 return -1; | |
141 }; | |
142 | |
143 WebRequestEventImpl.prototype.addRules = function(rules, opt_cb) { | |
144 if (!this.eventOptions.supportsRules) | |
145 throw new Error('This event does not support rules.'); | |
146 this.eventForRules.addRules(rules, opt_cb); | |
147 }; | |
148 | |
149 WebRequestEventImpl.prototype.removeRules = | |
150 function(ruleIdentifiers, opt_cb) { | |
151 if (!this.eventOptions.supportsRules) | |
152 throw new Error('This event does not support rules.'); | |
153 this.eventForRules.removeRules(ruleIdentifiers, opt_cb); | |
154 }; | |
155 | |
156 WebRequestEventImpl.prototype.getRules = function(ruleIdentifiers, cb) { | |
157 if (!this.eventOptions.supportsRules) | |
158 throw new Error('This event does not support rules.'); | |
159 this.eventForRules.getRules(ruleIdentifiers, cb); | |
160 }; | |
161 | |
162 binding.registerCustomHook(function(api) { | |
163 var apiFunctions = api.apiFunctions; | |
164 | |
165 apiFunctions.setHandleRequest('addEventListener', function() { | |
166 var args = $Array.slice(arguments); | |
167 sendRequest(this.name, args, this.definition.parameters, | |
168 {forIOThread: true}); | |
169 }); | |
170 | |
171 apiFunctions.setHandleRequest('eventHandled', function() { | |
172 var args = $Array.slice(arguments); | |
173 sendRequest(this.name, args, this.definition.parameters, | |
174 {forIOThread: true}); | |
175 }); | |
176 }); | |
177 | |
178 var WebRequestEvent = utils.expose('WebRequestEvent', | |
179 WebRequestEventImpl, | |
180 { functions: [ | |
181 'hasListener', | |
182 'hasListeners', | |
183 'addListener', | |
184 'removeListener', | |
185 'addRules', | |
186 'removeRules', | |
187 'getRules' | |
188 ] }); | |
189 | |
190 webRequestInternal = binding.generate(); | |
191 exports.binding = webRequestInternal; | |
192 exports.WebRequestEvent = WebRequestEvent; | |
OLD | NEW |