blob: 8685ab84fd9c48485001cb6421001fbc08a31189 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
var CHECK = requireNative('logging').CHECK;
var eventBindings = bindingUtil ? undefined : require('event_bindings');
var idGeneratorNatives = requireNative('id_generator');
var utils = require('utils');
var validate = bindingUtil ? undefined : require('schemaUtils').validate;
var webRequestInternal = getInternalApi ?
getInternalApi('webRequestInternal') :
require('binding').Binding.create('webRequestInternal').generate();
function validateListenerArguments(
eventName, extraArgSchemas, listenerArguments) {
if (bindingUtil)
bindingUtil.validateCustomSignature(eventName, listenerArguments);
else
validate(listenerArguments, extraArgSchemas);
}
function getUniqueSubEventName(eventName) {
return eventName + '/' + idGeneratorNatives.GetNextId();
}
function createSubEvent(name, argSchemas) {
if (bindingUtil) {
var supportsFilters = false;
var supportsLazyListeners = true;
return bindingUtil.createCustomEvent(name, undefined,
supportsFilters,
supportsLazyListeners);
}
return new eventBindings.Event(name, argSchemas);
}
// WebRequestEventImpl object. This is used for special webRequest events
// with extra parameters. Each invocation of addListener creates a new named
// sub-event. That sub-event is associated with the extra parameters in the
// browser process, so that only it is dispatched when the main event occurs
// matching the extra parameters.
// Note: this is not used for the onActionIgnored event.
//
// Example:
// chrome.webRequest.onBeforeRequest.addListener(
// callback, {urls: 'http://*.google.com/*'});
// ^ callback will only be called for onBeforeRequests matching the filter.
function WebRequestEventImpl(eventName, opt_argSchemas, opt_extraArgSchemas,
opt_eventOptions, opt_webViewInstanceId) {
if (typeof eventName != 'string')
throw new Error('chrome.WebRequestEvent requires an event name.');
if (bindingUtil)
bindingUtil.addCustomSignature(eventName, opt_extraArgSchemas);
this.eventName = eventName;
this.argSchemas = opt_argSchemas;
this.extraArgSchemas = opt_extraArgSchemas;
this.webViewInstanceId = opt_webViewInstanceId || 0;
this.subEvents = [];
if (eventBindings) {
var eventOptions = eventBindings.parseEventOptions(opt_eventOptions);
CHECK(!eventOptions.supportsRules, eventName + ' supports rules');
CHECK(eventOptions.supportsListeners,
eventName + ' does not support listeners');
}
}
$Object.setPrototypeOf(WebRequestEventImpl.prototype, null);
// Test if the given callback is registered for this event.
WebRequestEventImpl.prototype.hasListener = function(cb) {
return this.findListener_(cb) > -1;
};
// Test if any callbacks are registered fur thus event.
WebRequestEventImpl.prototype.hasListeners = function() {
return this.subEvents.length > 0;
};
// Registers a callback to be called when this event is dispatched. If
// opt_filter is specified, then the callback is only called for events that
// match the given filters. If opt_extraInfo is specified, the given optional
// info is sent to the callback.
WebRequestEventImpl.prototype.addListener =
function(cb, opt_filter, opt_extraInfo) {
// NOTE(benjhayden) New APIs should not use this subEventName trick! It does
// not play well with event pages. See downloads.onDeterminingFilename and
// ExtensionDownloadsEventRouter for an alternative approach.
var subEventName = getUniqueSubEventName(this.eventName);
// Note: this could fail to validate, in which case we would not add the
// subEvent listener.
validateListenerArguments(this.eventName, this.extraArgSchemas,
$Array.slice(arguments, 1));
webRequestInternal.addEventListener(
cb, opt_filter, opt_extraInfo, this.eventName, subEventName,
this.webViewInstanceId);
var subEvent = createSubEvent(subEventName, this.argSchemas);
var subEventCallback = cb;
if (opt_extraInfo && $Array.indexOf(opt_extraInfo, 'blocking') >= 0) {
var eventName = this.eventName;
subEventCallback = function() {
var requestId = arguments[0].requestId;
try {
var result = $Function.apply(cb, null, arguments);
webRequestInternal.eventHandled(
eventName, subEventName, requestId, result);
} catch (e) {
webRequestInternal.eventHandled(
eventName, subEventName, requestId);
throw e;
}
};
} else if (
opt_extraInfo && $Array.indexOf(opt_extraInfo, 'asyncBlocking') >= 0) {
var eventName = this.eventName;
subEventCallback = function() {
var details = arguments[0];
var requestId = details.requestId;
var handledCallback = function(response) {
webRequestInternal.eventHandled(
eventName, subEventName, requestId, response);
};
$Function.apply(cb, null, [details, handledCallback]);
};
}
$Array.push(this.subEvents,
{subEvent: subEvent, callback: cb, subEventCallback: subEventCallback});
subEvent.addListener(subEventCallback);
};
// Unregisters a callback.
WebRequestEventImpl.prototype.removeListener = function(cb) {
var idx;
while ((idx = this.findListener_(cb)) >= 0) {
var e = this.subEvents[idx];
e.subEvent.removeListener(e.subEventCallback);
if (e.subEvent.hasListeners()) {
console.error(
'Internal error: webRequest subEvent has orphaned listeners.');
}
$Array.splice(this.subEvents, idx, 1);
}
};
WebRequestEventImpl.prototype.findListener_ = function(cb) {
for (var i in this.subEvents) {
var e = this.subEvents[i];
if (e.callback === cb) {
if (e.subEvent.hasListener(e.subEventCallback))
return i;
console.error('Internal error: webRequest subEvent has no callback.');
}
}
return -1;
};
WebRequestEventImpl.prototype.addRules = function(rules, opt_cb) {
throw new Error('This event does not support rules.');
};
WebRequestEventImpl.prototype.removeRules =
function(ruleIdentifiers, opt_cb) {
throw new Error('This event does not support rules.');
};
WebRequestEventImpl.prototype.getRules = function(ruleIdentifiers, cb) {
throw new Error('This event does not support rules.');
};
function WebRequestEvent() {
privates(WebRequestEvent).constructPrivate(this, arguments);
}
// Our util code requires we construct a new WebRequestEvent via a call to
// 'new WebRequestEvent', which wouldn't work well with calling a v8::Function.
// Provide a wrapper for native bindings to call into.
function createWebRequestEvent(eventName, opt_argSchemas, opt_extraArgSchemas,
opt_eventOptions, opt_webViewInstanceId) {
return new WebRequestEvent(eventName, opt_argSchemas, opt_extraArgSchemas,
opt_eventOptions, opt_webViewInstanceId);
}
utils.expose(WebRequestEvent, WebRequestEventImpl, {
functions: [
'hasListener',
'hasListeners',
'addListener',
'removeListener',
'addRules',
'removeRules',
'getRules',
],
});
exports.$set('WebRequestEvent', WebRequestEvent);
exports.$set('createWebRequestEvent', createWebRequestEvent);