зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1254204: Part 2 - Allow suspending requests by returning Promises from blocking request listeners. r=mixedpuppy
MozReview-Commit-ID: B1ekz7WJ9kU --HG-- extra : rebase_source : 376ad1771c5af3e4c73a6d83ac4a2e2fb1756ab0
This commit is contained in:
Родитель
5aed24393a
Коммит
66a8f8f544
|
@ -35,6 +35,7 @@ support-files =
|
|||
file_ext_test_api_injection.js
|
||||
file_permission_xhr.html
|
||||
file_teardown_test.js
|
||||
return_headers.sjs
|
||||
webrequest_worker.js
|
||||
tags = webextensions
|
||||
|
||||
|
@ -96,6 +97,8 @@ skip-if = (os == 'android') # Bug 1258975 on android.
|
|||
skip-if = os == 'android' # webrequest api unsupported (bug 1258975).
|
||||
[test_ext_webrequest_background_events.html]
|
||||
skip-if = os == 'android' # webrequest api unsupported (bug 1258975).
|
||||
[test_ext_webrequest_suspend.html]
|
||||
skip-if = os == 'android' # webrequest api unsupported (bug 1258975).
|
||||
[test_ext_webrequest_upload.html]
|
||||
skip-if = os == 'android' # webrequest api unsupported (bug 1258975).
|
||||
[test_ext_webnavigation.html]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
function handleRequest(aRequest, aResponse) {
|
||||
aResponse.setStatusLine(aRequest.httpVersion, 302);
|
||||
aResponse.setHeader("Location", "./dummy_page.html");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
/* exported handleRequest */
|
||||
|
||||
function handleRequest(request, response) {
|
||||
response.setHeader("Content-Type", "text/plain", false);
|
||||
|
||||
let headers = {};
|
||||
// Why on earth is this a nsISimpleEnumerator...
|
||||
let enumerator = request.headers;
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let header = enumerator.getNext().data;
|
||||
headers[header.toLowerCase()] = request.getHeader(header);
|
||||
}
|
||||
|
||||
response.write(JSON.stringify(headers));
|
||||
}
|
||||
|
|
@ -257,7 +257,6 @@ function backgroundScript() {
|
|||
}
|
||||
|
||||
let modifiedAny = false;
|
||||
browser.test.log(`HEADERS ${JSON.stringify(headers)}`);
|
||||
for (let header of headers.filter(h => h.name in modified)) {
|
||||
let {name, value} = header;
|
||||
if (name.toLowerCase() === "content-type" && skippedRequests.has(details.requestId)) {
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for simple WebExtension</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
|
||||
<script type="text/javascript" src="head.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="text/javascript">
|
||||
"use strict";
|
||||
|
||||
add_task(function* () {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
permissions: [
|
||||
"webRequest",
|
||||
"webRequestBlocking",
|
||||
],
|
||||
},
|
||||
|
||||
background() {
|
||||
browser.webRequest.onBeforeSendHeaders.addListener(details => {
|
||||
let requestHeaders = details.requestHeaders.concat({name: "Foo", value: "Bar"});
|
||||
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, 500);
|
||||
}).then(() => {
|
||||
return {requestHeaders};
|
||||
});
|
||||
},
|
||||
{urls: ["<all_urls>"]},
|
||||
["blocking", "requestHeaders"]);
|
||||
},
|
||||
});
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
let result = yield fetch(SimpleTest.getTestFileURL("return_headers.sjs"));
|
||||
|
||||
let headers = JSON.parse(yield result.text());
|
||||
|
||||
is(headers.foo, "Bar", "Request header was correctly set on suspended request");
|
||||
|
||||
yield extension.unload();
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -15,8 +15,9 @@ const Cr = Components.results;
|
|||
|
||||
const {nsIHttpActivityObserver, nsISocketTransport} = Ci;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
|
||||
"resource://gre/modules/AppConstants.jsm");
|
||||
|
@ -115,6 +116,10 @@ function mergeStatus(data, channel, event) {
|
|||
}
|
||||
}
|
||||
|
||||
function isThenable(value) {
|
||||
return value && typeof value === "object" && typeof value.then === "function";
|
||||
}
|
||||
|
||||
class HeaderChanger {
|
||||
constructor(channel) {
|
||||
this.channel = channel;
|
||||
|
@ -484,7 +489,9 @@ HttpObserverManager = {
|
|||
let channel = subject.QueryInterface(Ci.nsIHttpChannel);
|
||||
switch (topic) {
|
||||
case "http-on-modify-request":
|
||||
this.modify(channel, topic, data);
|
||||
let loadContext = this.getLoadContext(channel);
|
||||
|
||||
this.runChannelListener(channel, loadContext, "opening");
|
||||
break;
|
||||
case "http-on-examine-cached-response":
|
||||
case "http-on-examine-merged-response":
|
||||
|
@ -558,11 +565,11 @@ HttpObserverManager = {
|
|||
let channelData = getData(channel);
|
||||
if (kind === "onError") {
|
||||
if (channelData.errorNotified) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
channelData.errorNotified = true;
|
||||
} else if (this.errorCheck(channel, loadContext, channelData)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
let listeners = this.listeners[kind];
|
||||
|
@ -677,42 +684,70 @@ HttpObserverManager = {
|
|||
}
|
||||
}
|
||||
|
||||
for (let {opts, result} of handlerResults) {
|
||||
if (result.cancel) {
|
||||
channel.cancel(Cr.NS_ERROR_ABORT);
|
||||
this.errorCheck(channel, loadContext);
|
||||
return false;
|
||||
}
|
||||
this.applyChanges(kind, channel, loadContext, handlerResults, requestHeaders, responseHeaders);
|
||||
},
|
||||
|
||||
if (result.redirectUrl) {
|
||||
try {
|
||||
channel.redirectTo(BrowserUtils.makeURI(result.redirectUrl));
|
||||
return false;
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
applyChanges: Task.async(function* (kind, channel, loadContext, handlerResults, requestHeaders, responseHeaders) {
|
||||
let asyncHandlers = handlerResults.filter(({result}) => isThenable(result));
|
||||
let isAsync = asyncHandlers.length > 0;
|
||||
|
||||
try {
|
||||
if (isAsync) {
|
||||
channel.suspend();
|
||||
|
||||
for (let value of asyncHandlers) {
|
||||
try {
|
||||
value.result = yield value.result;
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
value.result = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.requestHeaders && result.requestHeaders) {
|
||||
requestHeaders.applyChanges(result.requestHeaders);
|
||||
}
|
||||
for (let {opts, result} of handlerResults) {
|
||||
if (result.cancel) {
|
||||
channel.cancel(Cr.NS_ERROR_ABORT);
|
||||
|
||||
if (opts.responseHeaders && result.responseHeaders) {
|
||||
responseHeaders.applyChanges(result.responseHeaders);
|
||||
this.errorCheck(channel, loadContext);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.redirectUrl) {
|
||||
try {
|
||||
if (isAsync) {
|
||||
channel.resume();
|
||||
}
|
||||
|
||||
channel.redirectTo(BrowserUtils.makeURI(result.redirectUrl));
|
||||
return;
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.requestHeaders && result.requestHeaders) {
|
||||
requestHeaders.applyChanges(result.requestHeaders);
|
||||
}
|
||||
|
||||
if (opts.responseHeaders && result.responseHeaders) {
|
||||
responseHeaders.applyChanges(result.responseHeaders);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
modify(channel, topic, data) {
|
||||
let loadContext = this.getLoadContext(channel);
|
||||
|
||||
if (this.runChannelListener(channel, loadContext, "opening") &&
|
||||
this.runChannelListener(channel, loadContext, "modify")) {
|
||||
this.runChannelListener(channel, loadContext, "afterModify");
|
||||
if (isAsync) {
|
||||
channel.resume();
|
||||
}
|
||||
},
|
||||
|
||||
if (kind === "opening") {
|
||||
return this.runChannelListener(channel, loadContext, "modify");
|
||||
} else if (kind === "modify") {
|
||||
return this.runChannelListener(channel, loadContext, "afterModify");
|
||||
}
|
||||
}),
|
||||
|
||||
examine(channel, topic, data) {
|
||||
let loadContext = this.getLoadContext(channel);
|
||||
|
|
Загрузка…
Ссылка в новой задаче