mirror of https://github.com/gorhill/uBlock.git
Imrpove `prevent-xhr` scriptlet; add `trusted-prevent-xhr` scriptlet
Add support for synchronous `send()` calls. `trusted-prevent-xhr` is essentially the same as `prevent-xhr` except that if the `directive` argument is not a known token, it will be used as is as the response text of the xhr request, whereas `prevent-xhr` returns an empty string when the directive is unknown.
This commit is contained in:
parent
bcb31db176
commit
fe49ced2ac
|
@ -321,6 +321,9 @@ function runAtHtmlElementFn(fn) {
|
||||||
|
|
||||||
// Reference:
|
// Reference:
|
||||||
// https://github.com/AdguardTeam/Scriptlets/blob/master/wiki/about-scriptlets.md#prevent-xhr
|
// https://github.com/AdguardTeam/Scriptlets/blob/master/wiki/about-scriptlets.md#prevent-xhr
|
||||||
|
//
|
||||||
|
// Added `trusted` argument to allow for returning arbitrary text. Can only
|
||||||
|
// be used through scriptlets requiring trusted source.
|
||||||
|
|
||||||
builtinScriptlets.push({
|
builtinScriptlets.push({
|
||||||
name: 'generate-content.fn',
|
name: 'generate-content.fn',
|
||||||
|
@ -329,7 +332,7 @@ builtinScriptlets.push({
|
||||||
'safe-self.fn',
|
'safe-self.fn',
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
function generateContentFn(directive) {
|
function generateContentFn(trusted, directive) {
|
||||||
const safe = safeSelf();
|
const safe = safeSelf();
|
||||||
const randomize = len => {
|
const randomize = len => {
|
||||||
const chunks = [];
|
const chunks = [];
|
||||||
|
@ -343,27 +346,27 @@ function generateContentFn(directive) {
|
||||||
return chunks.join(' ').slice(0, len);
|
return chunks.join(' ').slice(0, len);
|
||||||
};
|
};
|
||||||
if ( directive === 'true' ) {
|
if ( directive === 'true' ) {
|
||||||
return Promise.resolve(randomize(10));
|
return randomize(10);
|
||||||
}
|
}
|
||||||
if ( directive === 'emptyObj' ) {
|
if ( directive === 'emptyObj' ) {
|
||||||
return Promise.resolve('{}');
|
return '{}';
|
||||||
}
|
}
|
||||||
if ( directive === 'emptyArr' ) {
|
if ( directive === 'emptyArr' ) {
|
||||||
return Promise.resolve('[]');
|
return '[]';
|
||||||
}
|
}
|
||||||
if ( directive === 'emptyStr' ) {
|
if ( directive === 'emptyStr' ) {
|
||||||
return Promise.resolve('');
|
return '';
|
||||||
}
|
}
|
||||||
if ( directive.startsWith('length:') ) {
|
if ( directive.startsWith('length:') ) {
|
||||||
const match = /^length:(\d+)(?:-(\d+))?$/.exec(directive);
|
const match = /^length:(\d+)(?:-(\d+))?$/.exec(directive);
|
||||||
if ( match ) {
|
if ( match === null ) { return ''; }
|
||||||
const min = parseInt(match[1], 10);
|
const min = parseInt(match[1], 10);
|
||||||
const extent = safe.Math_max(parseInt(match[2], 10) || 0, min) - min;
|
const extent = safe.Math_max(parseInt(match[2], 10) || 0, min) - min;
|
||||||
const len = safe.Math_min(min + extent * safe.Math_random(), 500000);
|
const len = safe.Math_min(min + extent * safe.Math_random(), 500000);
|
||||||
return Promise.resolve(randomize(len | 0));
|
return randomize(len | 0);
|
||||||
}
|
}
|
||||||
}
|
if ( directive.startsWith('war:') ) {
|
||||||
if ( directive.startsWith('war:') && scriptletGlobals.warOrigin ) {
|
if ( scriptletGlobals.warOrigin === undefined ) { return ''; }
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const warOrigin = scriptletGlobals.warOrigin;
|
const warOrigin = scriptletGlobals.warOrigin;
|
||||||
const warName = directive.slice(4);
|
const warName = directive.slice(4);
|
||||||
|
@ -379,9 +382,12 @@ function generateContentFn(directive) {
|
||||||
};
|
};
|
||||||
warXHR.open('GET', fullpath.join(''));
|
warXHR.open('GET', fullpath.join(''));
|
||||||
warXHR.send();
|
warXHR.send();
|
||||||
});
|
}).catch(( ) => '');
|
||||||
}
|
}
|
||||||
return Promise.resolve('');
|
if ( trusted ) {
|
||||||
|
return directive;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -1565,6 +1571,196 @@ function proxyApplyFn(
|
||||||
context[prop] = new Proxy(fn, proxyDetails);
|
context[prop] = new Proxy(fn, proxyDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
builtinScriptlets.push({
|
||||||
|
name: 'prevent-xhr.fn',
|
||||||
|
fn: preventXhrFn,
|
||||||
|
dependencies: [
|
||||||
|
'generate-content.fn',
|
||||||
|
'match-object-properties.fn',
|
||||||
|
'parse-properties-to-match.fn',
|
||||||
|
'safe-self.fn',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
function preventXhrFn(
|
||||||
|
trusted = false,
|
||||||
|
propsToMatch = '',
|
||||||
|
directive = ''
|
||||||
|
) {
|
||||||
|
if ( typeof propsToMatch !== 'string' ) { return; }
|
||||||
|
const safe = safeSelf();
|
||||||
|
const scriptletName = trusted ? 'trusted-prevent-xhr' : 'prevent-xhr';
|
||||||
|
const logPrefix = safe.makeLogPrefix(scriptletName, propsToMatch, directive);
|
||||||
|
const xhrInstances = new WeakMap();
|
||||||
|
const propNeedles = parsePropertiesToMatch(propsToMatch, 'url');
|
||||||
|
const warOrigin = scriptletGlobals.warOrigin;
|
||||||
|
const safeDispatchEvent = (xhr, type) => {
|
||||||
|
try {
|
||||||
|
xhr.dispatchEvent(new Event(type));
|
||||||
|
} catch(_) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const XHRBefore = XMLHttpRequest.prototype;
|
||||||
|
self.XMLHttpRequest = class extends self.XMLHttpRequest {
|
||||||
|
open(method, url, defer, ...args) {
|
||||||
|
xhrInstances.delete(this);
|
||||||
|
if ( warOrigin !== undefined && url.startsWith(warOrigin) ) {
|
||||||
|
return super.open(method, url, defer, ...args);
|
||||||
|
}
|
||||||
|
const haystack = { method, url };
|
||||||
|
if ( propsToMatch === '' && directive === '' ) {
|
||||||
|
safe.uboLog(logPrefix, `Called: ${safe.JSON_stringify(haystack, null, 2)}`);
|
||||||
|
return super.open(method, url, defer, ...args);
|
||||||
|
}
|
||||||
|
if ( matchObjectProperties(propNeedles, haystack) ) {
|
||||||
|
const xhrDetails = Object.assign(haystack, {
|
||||||
|
xhr: this,
|
||||||
|
defer,
|
||||||
|
directive,
|
||||||
|
headers: {
|
||||||
|
'date': '',
|
||||||
|
'content-type': '',
|
||||||
|
'content-length': '',
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
response: { value: '' },
|
||||||
|
responseText: { value: '' },
|
||||||
|
responseXML: { value: null },
|
||||||
|
responseURL: { value: haystack.url },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
xhrInstances.set(this, xhrDetails);
|
||||||
|
}
|
||||||
|
return super.open(method, url, defer, ...args);
|
||||||
|
}
|
||||||
|
send(...args) {
|
||||||
|
const xhrDetails = xhrInstances.get(this);
|
||||||
|
if ( xhrDetails === undefined ) {
|
||||||
|
return super.send(...args);
|
||||||
|
}
|
||||||
|
xhrDetails.headers['date'] = (new Date()).toUTCString();
|
||||||
|
let xhrText = '';
|
||||||
|
switch ( this.responseType ) {
|
||||||
|
case 'arraybuffer':
|
||||||
|
xhrDetails.props.response.value = new ArrayBuffer(0);
|
||||||
|
xhrDetails.headers['content-type'] = 'application/octet-stream';
|
||||||
|
break;
|
||||||
|
case 'blob':
|
||||||
|
xhrDetails.props.response.value = new Blob([]);
|
||||||
|
xhrDetails.headers['content-type'] = 'application/octet-stream';
|
||||||
|
break;
|
||||||
|
case 'document': {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString('', 'text/html');
|
||||||
|
xhrDetails.props.response.value = doc;
|
||||||
|
xhrDetails.props.responseXML.value = doc;
|
||||||
|
xhrDetails.headers['content-type'] = 'text/html';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'json':
|
||||||
|
xhrDetails.props.response.value = {};
|
||||||
|
xhrDetails.props.responseText.value = '{}';
|
||||||
|
xhrDetails.headers['content-type'] = 'application/json';
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
if ( directive === '' ) { break; }
|
||||||
|
xhrText = generateContentFn(trusted, xhrDetails.directive);
|
||||||
|
if ( xhrText instanceof Promise ) {
|
||||||
|
xhrText = xhrText.then(text => {
|
||||||
|
xhrDetails.props.response.value = text;
|
||||||
|
xhrDetails.props.responseText.value = text;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
xhrDetails.props.response.value = xhrText;
|
||||||
|
xhrDetails.props.responseText.value = xhrText;
|
||||||
|
}
|
||||||
|
xhrDetails.headers['content-type'] = 'text/plain';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( xhrDetails.defer === false ) {
|
||||||
|
xhrDetails.headers['content-length'] = `${xhrDetails.props.response.value}`.length;
|
||||||
|
Object.defineProperties(xhrDetails.xhr, {
|
||||||
|
readyState: { value: 4 },
|
||||||
|
status: { value: 200 },
|
||||||
|
statusText: { value: 'OK' },
|
||||||
|
});
|
||||||
|
Object.defineProperties(xhrDetails.xhr, xhrDetails.props);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Promise.resolve(xhrText).then(( ) => xhrDetails).then(details => {
|
||||||
|
Object.defineProperties(details.xhr, {
|
||||||
|
readyState: { value: 1, configurable: true },
|
||||||
|
});
|
||||||
|
safeDispatchEvent(details.xhr, 'readystatechange');
|
||||||
|
return details;
|
||||||
|
}).then(details => {
|
||||||
|
xhrDetails.headers['content-length'] = `${details.props.response.value}`.length;
|
||||||
|
Object.defineProperties(details.xhr, {
|
||||||
|
readyState: { value: 2, configurable: true },
|
||||||
|
status: { value: 200 },
|
||||||
|
statusText: { value: 'OK' },
|
||||||
|
});
|
||||||
|
safeDispatchEvent(details.xhr, 'readystatechange');
|
||||||
|
return details;
|
||||||
|
}).then(details => {
|
||||||
|
Object.defineProperties(details.xhr, {
|
||||||
|
readyState: { value: 3, configurable: true },
|
||||||
|
});
|
||||||
|
Object.defineProperties(details.xhr, details.props);
|
||||||
|
safeDispatchEvent(details.xhr, 'readystatechange');
|
||||||
|
return details;
|
||||||
|
}).then(details => {
|
||||||
|
Object.defineProperties(details.xhr, {
|
||||||
|
readyState: { value: 4 },
|
||||||
|
});
|
||||||
|
safeDispatchEvent(details.xhr, 'readystatechange');
|
||||||
|
safeDispatchEvent(details.xhr, 'load');
|
||||||
|
safeDispatchEvent(details.xhr, 'loadend');
|
||||||
|
safe.uboLog(logPrefix, `Prevented with response:\n${details.xhr.response}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getResponseHeader(headerName) {
|
||||||
|
const xhrDetails = xhrInstances.get(this);
|
||||||
|
if ( xhrDetails === undefined || this.readyState < this.HEADERS_RECEIVED ) {
|
||||||
|
return super.getResponseHeader(headerName);
|
||||||
|
}
|
||||||
|
const value = xhrDetails.headers[headerName.toLowerCase()];
|
||||||
|
if ( value !== undefined && value !== '' ) { return value; }
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
getAllResponseHeaders() {
|
||||||
|
const xhrDetails = xhrInstances.get(this);
|
||||||
|
if ( xhrDetails === undefined || this.readyState < this.HEADERS_RECEIVED ) {
|
||||||
|
return super.getAllResponseHeaders();
|
||||||
|
}
|
||||||
|
const out = [];
|
||||||
|
for ( const [ name, value ] of Object.entries(xhrDetails.headers) ) {
|
||||||
|
if ( !value ) { continue; }
|
||||||
|
out.push(`${name}: ${value}`);
|
||||||
|
}
|
||||||
|
if ( out.length !== 0 ) { out.push(''); }
|
||||||
|
return out.join('\r\n');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.XMLHttpRequest.prototype.open.toString = function() {
|
||||||
|
return XHRBefore.open.toString();
|
||||||
|
};
|
||||||
|
self.XMLHttpRequest.prototype.send.toString = function() {
|
||||||
|
return XHRBefore.send.toString();
|
||||||
|
};
|
||||||
|
self.XMLHttpRequest.prototype.getResponseHeader.toString = function() {
|
||||||
|
return XHRBefore.getResponseHeader.toString();
|
||||||
|
};
|
||||||
|
self.XMLHttpRequest.prototype.getAllResponseHeaders.toString = function() {
|
||||||
|
return XHRBefore.getAllResponseHeaders.toString();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
|
||||||
Injectable scriptlets
|
Injectable scriptlets
|
||||||
|
@ -2271,7 +2467,7 @@ function noFetchIf(
|
||||||
if ( proceed ) {
|
if ( proceed ) {
|
||||||
return context.reflect();
|
return context.reflect();
|
||||||
}
|
}
|
||||||
return generateContentFn(responseBody).then(text => {
|
return Promise.resolve(generateContentFn(false, responseBody)).then(text => {
|
||||||
safe.uboLog(logPrefix, `Prevented with response "${text}"`);
|
safe.uboLog(logPrefix, `Prevented with response "${text}"`);
|
||||||
const response = new Response(text, {
|
const response = new Response(text, {
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -2730,192 +2926,17 @@ function webrtcIf(
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
builtinScriptlets.push({
|
builtinScriptlets.push({
|
||||||
name: 'no-xhr-if.js',
|
name: 'prevent-xhr.js',
|
||||||
aliases: [
|
aliases: [
|
||||||
'prevent-xhr.js',
|
'no-xhr-if.js',
|
||||||
],
|
],
|
||||||
fn: noXhrIf,
|
fn: preventXhr,
|
||||||
dependencies: [
|
dependencies: [
|
||||||
'generate-content.fn',
|
'prevent-xhr.fn',
|
||||||
'match-object-properties.fn',
|
|
||||||
'parse-properties-to-match.fn',
|
|
||||||
'safe-self.fn',
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
function noXhrIf(
|
function preventXhr(...args) {
|
||||||
propsToMatch = '',
|
return preventXhrFn(false, ...args);
|
||||||
directive = ''
|
|
||||||
) {
|
|
||||||
if ( typeof propsToMatch !== 'string' ) { return; }
|
|
||||||
const safe = safeSelf();
|
|
||||||
const logPrefix = safe.makeLogPrefix('prevent-xhr', propsToMatch, directive);
|
|
||||||
const xhrInstances = new WeakMap();
|
|
||||||
const propNeedles = parsePropertiesToMatch(propsToMatch, 'url');
|
|
||||||
const warOrigin = scriptletGlobals.warOrigin;
|
|
||||||
const headers = {
|
|
||||||
'date': '',
|
|
||||||
'content-type': '',
|
|
||||||
'content-length': '',
|
|
||||||
};
|
|
||||||
const safeDispatchEvent = (xhr, type) => {
|
|
||||||
try {
|
|
||||||
xhr.dispatchEvent(new Event(type));
|
|
||||||
} catch(_) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const XHRBefore = XMLHttpRequest.prototype;
|
|
||||||
self.XMLHttpRequest = class extends self.XMLHttpRequest {
|
|
||||||
open(method, url, ...args) {
|
|
||||||
xhrInstances.delete(this);
|
|
||||||
if ( warOrigin !== undefined && url.startsWith(warOrigin) ) {
|
|
||||||
return super.open(method, url, ...args);
|
|
||||||
}
|
|
||||||
const haystack = { method, url };
|
|
||||||
if ( propsToMatch === '' && directive === '' ) {
|
|
||||||
safe.uboLog(logPrefix, `Called: ${safe.JSON_stringify(haystack, null, 2)}`);
|
|
||||||
return super.open(method, url, ...args);
|
|
||||||
}
|
|
||||||
if ( matchObjectProperties(propNeedles, haystack) ) {
|
|
||||||
xhrInstances.set(this, haystack);
|
|
||||||
}
|
|
||||||
haystack.headers = Object.assign({}, headers);
|
|
||||||
return super.open(method, url, ...args);
|
|
||||||
}
|
|
||||||
send(...args) {
|
|
||||||
const haystack = xhrInstances.get(this);
|
|
||||||
if ( haystack === undefined ) {
|
|
||||||
return super.send(...args);
|
|
||||||
}
|
|
||||||
haystack.headers['date'] = (new Date()).toUTCString();
|
|
||||||
let promise = Promise.resolve({
|
|
||||||
xhr: this,
|
|
||||||
directive,
|
|
||||||
response: {
|
|
||||||
response: { value: '' },
|
|
||||||
responseText: { value: '' },
|
|
||||||
responseXML: { value: null },
|
|
||||||
responseURL: { value: haystack.url },
|
|
||||||
}
|
|
||||||
});
|
|
||||||
switch ( this.responseType ) {
|
|
||||||
case 'arraybuffer':
|
|
||||||
promise = promise.then(details => {
|
|
||||||
const response = details.response;
|
|
||||||
response.response.value = new ArrayBuffer(0);
|
|
||||||
return details;
|
|
||||||
});
|
|
||||||
haystack.headers['content-type'] = 'application/octet-stream';
|
|
||||||
break;
|
|
||||||
case 'blob':
|
|
||||||
promise = promise.then(details => {
|
|
||||||
const response = details.response;
|
|
||||||
response.response.value = new Blob([]);
|
|
||||||
return details;
|
|
||||||
});
|
|
||||||
haystack.headers['content-type'] = 'application/octet-stream';
|
|
||||||
break;
|
|
||||||
case 'document': {
|
|
||||||
promise = promise.then(details => {
|
|
||||||
const parser = new DOMParser();
|
|
||||||
const doc = parser.parseFromString('', 'text/html');
|
|
||||||
const response = details.response;
|
|
||||||
response.response.value = doc;
|
|
||||||
response.responseXML.value = doc;
|
|
||||||
return details;
|
|
||||||
});
|
|
||||||
haystack.headers['content-type'] = 'text/html';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'json':
|
|
||||||
promise = promise.then(details => {
|
|
||||||
const response = details.response;
|
|
||||||
response.response.value = {};
|
|
||||||
response.responseText.value = '{}';
|
|
||||||
return details;
|
|
||||||
});
|
|
||||||
haystack.headers['content-type'] = 'application/json';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if ( directive === '' ) { break; }
|
|
||||||
promise = promise.then(details => {
|
|
||||||
return generateContentFn(details.directive).then(text => {
|
|
||||||
const response = details.response;
|
|
||||||
response.response.value = text;
|
|
||||||
response.responseText.value = text;
|
|
||||||
return details;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
haystack.headers['content-type'] = 'text/plain';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
promise.then(details => {
|
|
||||||
Object.defineProperties(details.xhr, {
|
|
||||||
readyState: { value: 1, configurable: true },
|
|
||||||
});
|
|
||||||
safeDispatchEvent(details.xhr, 'readystatechange');
|
|
||||||
return details;
|
|
||||||
}).then(details => {
|
|
||||||
const response = details.response;
|
|
||||||
haystack.headers['content-length'] = `${response.response.value}`.length;
|
|
||||||
Object.defineProperties(details.xhr, {
|
|
||||||
readyState: { value: 2, configurable: true },
|
|
||||||
status: { value: 200 },
|
|
||||||
statusText: { value: 'OK' },
|
|
||||||
});
|
|
||||||
safeDispatchEvent(details.xhr, 'readystatechange');
|
|
||||||
return details;
|
|
||||||
}).then(details => {
|
|
||||||
Object.defineProperties(details.xhr, {
|
|
||||||
readyState: { value: 3, configurable: true },
|
|
||||||
});
|
|
||||||
Object.defineProperties(details.xhr, details.response);
|
|
||||||
safeDispatchEvent(details.xhr, 'readystatechange');
|
|
||||||
return details;
|
|
||||||
}).then(details => {
|
|
||||||
Object.defineProperties(details.xhr, {
|
|
||||||
readyState: { value: 4 },
|
|
||||||
});
|
|
||||||
safeDispatchEvent(details.xhr, 'readystatechange');
|
|
||||||
safeDispatchEvent(details.xhr, 'load');
|
|
||||||
safeDispatchEvent(details.xhr, 'loadend');
|
|
||||||
safe.uboLog(logPrefix, `Prevented with response:\n${details.xhr.response}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
getResponseHeader(headerName) {
|
|
||||||
const haystack = xhrInstances.get(this);
|
|
||||||
if ( haystack === undefined || this.readyState < this.HEADERS_RECEIVED ) {
|
|
||||||
return super.getResponseHeader(headerName);
|
|
||||||
}
|
|
||||||
const value = haystack.headers[headerName.toLowerCase()];
|
|
||||||
if ( value !== undefined && value !== '' ) { return value; }
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
getAllResponseHeaders() {
|
|
||||||
const haystack = xhrInstances.get(this);
|
|
||||||
if ( haystack === undefined || this.readyState < this.HEADERS_RECEIVED ) {
|
|
||||||
return super.getAllResponseHeaders();
|
|
||||||
}
|
|
||||||
const out = [];
|
|
||||||
for ( const [ name, value ] of Object.entries(haystack.headers) ) {
|
|
||||||
if ( !value ) { continue; }
|
|
||||||
out.push(`${name}: ${value}`);
|
|
||||||
}
|
|
||||||
if ( out.length !== 0 ) { out.push(''); }
|
|
||||||
return out.join('\r\n');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.XMLHttpRequest.prototype.open.toString = function() {
|
|
||||||
return XHRBefore.open.toString();
|
|
||||||
};
|
|
||||||
self.XMLHttpRequest.prototype.send.toString = function() {
|
|
||||||
return XHRBefore.send.toString();
|
|
||||||
};
|
|
||||||
self.XMLHttpRequest.prototype.getResponseHeader.toString = function() {
|
|
||||||
return XHRBefore.getResponseHeader.toString();
|
|
||||||
};
|
|
||||||
self.XMLHttpRequest.prototype.getAllResponseHeaders.toString = function() {
|
|
||||||
return XHRBefore.getAllResponseHeaders.toString();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -5103,4 +5124,23 @@ function trustedSuppressNativeMethod(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
*
|
||||||
|
* Trusted version of prevent-xhr(), which allows the use of an arbitrary
|
||||||
|
* string as response text.
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
|
||||||
|
builtinScriptlets.push({
|
||||||
|
name: 'trusted-prevent-xhr.js',
|
||||||
|
requiresTrust: true,
|
||||||
|
fn: trustedPreventXhr,
|
||||||
|
dependencies: [
|
||||||
|
'prevent-xhr.fn',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
function trustedPreventXhr(...args) {
|
||||||
|
return preventXhrFn(true, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
Loading…
Reference in New Issue