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.
5 // Custom binding for the webRequestInternal API.
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');
14 var webRequestInternal
;
16 function GetUniqueSubEventName(eventName
) {
17 return eventName
+ "/" + idGeneratorNatives
.GetNextId();
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.
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.');
35 this.eventName
= eventName
;
36 this.argSchemas
= opt_argSchemas
;
37 this.extraArgSchemas
= opt_extraArgSchemas
;
38 this.webViewInstanceId
= opt_webViewInstanceId
|| 0;
40 this.eventOptions
= eventBindings
.parseEventOptions(opt_eventOptions
);
41 if (this.eventOptions
.supportsRules
) {
43 new eventBindings
.Event(eventName
, opt_argSchemas
, opt_eventOptions
,
44 opt_webViewInstanceId
);
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;
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;
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
76 validate($Array
.slice(arguments
, 1), this.extraArgSchemas
);
77 webRequestInternal
.addEventListener(
78 cb
, opt_filter
, opt_extraInfo
, this.eventName
, subEventName
,
79 this.webViewInstanceId
);
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
;
88 var result
= $Function
.apply(cb
, null, arguments
);
89 webRequestInternal
.eventHandled(
90 eventName
, subEventName
, requestId
, result
);
92 webRequestInternal
.eventHandled(
93 eventName
, subEventName
, requestId
);
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
);
106 $Function
.apply(cb
, null, [details
, handledCallback
]);
109 $Array
.push(this.subEvents
,
110 {subEvent
: subEvent
, callback
: cb
, subEventCallback
: subEventCallback
});
111 subEvent
.addListener(subEventCallback
);
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.');
119 while ((idx
= this.findListener_(cb
)) >= 0) {
120 var e
= this.subEvents
[idx
];
121 e
.subEvent
.removeListener(e
.subEventCallback
);
122 if (e
.subEvent
.hasListeners()) {
124 'Internal error: webRequest subEvent has orphaned listeners.');
126 $Array
.splice(this.subEvents
, idx
, 1);
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
))
136 console
.error('Internal error: webRequest subEvent has no callback.');
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
);
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
);
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
);
162 binding
.registerCustomHook(function(api
) {
163 var apiFunctions
= api
.apiFunctions
;
165 apiFunctions
.setHandleRequest('addEventListener', function() {
166 var args
= $Array
.slice(arguments
);
167 sendRequest(this.name
, args
, this.definition
.parameters
,
168 {forIOThread
: true});
171 apiFunctions
.setHandleRequest('eventHandled', function() {
172 var args
= $Array
.slice(arguments
);
173 sendRequest(this.name
, args
, this.definition
.parameters
,
174 {forIOThread
: true});
178 var WebRequestEvent
= utils
.expose('WebRequestEvent',
190 webRequestInternal
= binding
.generate();
191 exports
.binding
= webRequestInternal
;
192 exports
.WebRequestEvent
= WebRequestEvent
;