Backed out 5 changesets (bug 1305162) for permatimeout in e10s browser_action_keyword.js

Backed out changeset c633650caea6 (bug 1305162)
Backed out changeset 1a23ce5a17c3 (bug 1305162)
Backed out changeset d1095e03dfc6 (bug 1305162)
Backed out changeset 1202edf6008d (bug 1305162)
Backed out changeset de131f7c1fc1 (bug 1305162)
This commit is contained in:
Phil Ringnalda 2017-01-21 09:41:03 -08:00
Родитель 8b31ae6749
Коммит c7973f5461
12 изменённых файлов: 286 добавлений и 282 удалений

Просмотреть файл

@ -41,15 +41,10 @@ add_task(function* () {
data = yield createCurlData(requests.multipart, gNetwork);
testIsMultipartRequest(data);
testGetMultipartBoundary(data);
testMultiPartHeaders(data);
testRemoveBinaryDataFromMultipartText(data);
data = yield createCurlData(requests.multipartForm, gNetwork);
testMultiPartHeaders(data);
testGetHeadersFromMultipartText({
postDataText: "Content-Type: text/plain\r\n\r\n",
});
testGetHeadersFromMultipartText(data);
if (Services.appinfo.OS != "WINNT") {
testEscapeStringPosix();
@ -84,14 +79,6 @@ function testFindHeader(data) {
"Should return null when a header is not found.");
}
function testMultiPartHeaders(data) {
let headers = data.headers;
let contentType = CurlUtils.findHeader(headers, "Content-Type");
ok(contentType.startsWith("multipart/form-data; boundary="),
"Multi-part content type header is present in headers array");
}
function testWritePostDataTextParams(data) {
let params = CurlUtils.writePostDataTextParams(data.postDataText);
is(params, "param1=value1&param2=value2&param3=value3",

Просмотреть файл

@ -118,19 +118,14 @@ add_task(function* testFormSubmission() {
let client = hud.ui.webConsoleClient;
const postData = yield client.getRequestPostData(request.actor);
const requestHeaders = yield client.getRequestHeaders(request.actor);
const responseContent = yield client.getResponseContent(request.actor);
let getHeader = name => {
let header = requestHeaders.headers.find(h => h.name == name);
return header && header.value;
};
is(request.request.method, "POST", "Method is correct");
is(getHeader("Content-Type"), "application/x-www-form-urlencoded",
"Content-Type is correct");
is(getHeader("Content-Length"), "20",
"Content-length is correct");
isnot(postData.postData.text
.indexOf("Content-Type: application/x-www-form-urlencoded"), -1,
"Content-Type is correct");
isnot(postData.postData.text
.indexOf("Content-Length: 20"), -1, "Content-length is correct");
isnot(postData.postData.text
.indexOf("name=foo+bar&age=144"), -1, "Form data is correct");
is(responseContent.content.text.indexOf("<!DOCTYPE HTML>"), 0,

Просмотреть файл

@ -11,12 +11,6 @@ using struct mozilla::void_t
namespace mozilla {
namespace ipc {
struct HeaderEntry
{
nsCString name;
nsCString value;
};
struct StringInputStreamParams
{
nsCString data;
@ -92,8 +86,10 @@ struct BufferedInputStreamParams
struct MIMEInputStreamParams
{
OptionalInputStreamParams optionalStream;
HeaderEntry[] headers;
nsCString headers;
nsCString contentLength;
bool startedReading;
bool addContentLength;
};
} // namespace ipc

Просмотреть файл

@ -3,7 +3,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIHttpHeaderVisitor.idl"
#include "nsIInputStream.idl"
/**
@ -20,10 +19,6 @@ interface nsIMIMEInputStream : nsIInputStream
* using the available() method on the data stream. The value is
* recalculated every time the stream is rewinded to the start.
* Not allowed to be changed once the stream has been started to be read.
*
* @deprecated A Content-Length header is automatically added when
* attaching the stream to a channel, so this setting no longer has any
* effect, and may not be set to false.
*/
attribute boolean addContentLength;
@ -35,15 +30,6 @@ interface nsIMIMEInputStream : nsIInputStream
*/
void addHeader(in string name, in string value);
/**
* Visits all headers which have been added via addHeader. Calling
* addHeader while visiting request headers has undefined behavior.
*
* @param aVisitor
* The header visitor instance.
*/
void visitHeaders(in nsIHttpHeaderVisitor visitor);
/**
* Sets data-stream. May not be called once the stream has been started
* to be read.

Просмотреть файл

@ -12,19 +12,18 @@
#include "nsCOMPtr.h"
#include "nsComponentManagerUtils.h"
#include "nsIHttpHeaderVisitor.h"
#include "nsIMultiplexInputStream.h"
#include "nsIMIMEInputStream.h"
#include "nsISeekableStream.h"
#include "nsIStringStream.h"
#include "nsString.h"
#include "nsMIMEInputStream.h"
#include "nsIClassInfoImpl.h"
#include "nsIIPCSerializableInputStream.h"
#include "mozilla/Move.h"
#include "mozilla/ipc/InputStreamUtils.h"
using namespace mozilla::ipc;
using mozilla::Maybe;
using mozilla::Move;
class nsMIMEInputStream : public nsIMIMEInputStream,
public nsISeekableStream,
@ -41,6 +40,8 @@ public:
NS_DECL_NSISEEKABLESTREAM
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
nsresult Init();
private:
void InitStreams();
@ -54,9 +55,15 @@ private:
const char* aFromRawSegment, uint32_t aToOffset,
uint32_t aCount, uint32_t *aWriteCount);
nsTArray<HeaderEntry> mHeaders;
nsCOMPtr<nsIInputStream> mStream;
nsCString mHeaders;
nsCOMPtr<nsIStringInputStream> mHeaderStream;
nsCString mContentLength;
nsCOMPtr<nsIStringInputStream> mCLStream;
nsCOMPtr<nsIInputStream> mData;
nsCOMPtr<nsIMultiplexInputStream> mStream;
bool mAddContentLength;
bool mStartedReading;
};
@ -76,7 +83,8 @@ NS_IMPL_CI_INTERFACE_GETTER(nsMIMEInputStream,
nsIInputStream,
nsISeekableStream)
nsMIMEInputStream::nsMIMEInputStream() : mStartedReading(false)
nsMIMEInputStream::nsMIMEInputStream() : mAddContentLength(false),
mStartedReading(false)
{
}
@ -84,21 +92,40 @@ nsMIMEInputStream::~nsMIMEInputStream()
{
}
nsresult nsMIMEInputStream::Init()
{
nsresult rv = NS_OK;
mStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1",
&rv);
NS_ENSURE_SUCCESS(rv, rv);
mHeaderStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1",
&rv);
NS_ENSURE_SUCCESS(rv, rv);
mCLStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = mStream->AppendStream(mHeaderStream);
NS_ENSURE_SUCCESS(rv, rv);
rv = mStream->AppendStream(mCLStream);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInputStream::GetAddContentLength(bool *aAddContentLength)
{
*aAddContentLength = true;
*aAddContentLength = mAddContentLength;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInputStream::SetAddContentLength(bool aAddContentLength)
{
NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
if (!aAddContentLength) {
// Content-Length is automatically added by the channel when setting the
// upload stream, so setting this to false has no practical effect.
return NS_ERROR_FAILURE;
}
mAddContentLength = aAddContentLength;
return NS_OK;
}
@ -106,39 +133,30 @@ NS_IMETHODIMP
nsMIMEInputStream::AddHeader(const char *aName, const char *aValue)
{
NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
mHeaders.Append(aName);
mHeaders.AppendLiteral(": ");
mHeaders.Append(aValue);
mHeaders.AppendLiteral("\r\n");
HeaderEntry* entry = mHeaders.AppendElement();
entry->name().Append(aName);
entry->value().Append(aValue);
// Just in case someone somehow uses our stream, lets at least
// let the stream have a valid pointer. The stream will be properly
// initialized in nsMIMEInputStream::InitStreams
mHeaderStream->ShareData(mHeaders.get(), 0);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInputStream::VisitHeaders(nsIHttpHeaderVisitor *visitor)
{
nsresult rv;
for (auto& header : mHeaders) {
rv = visitor->VisitHeader(header.name(), header.value());
if (NS_FAILED(rv)) {
return rv;
}
}
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInputStream::SetData(nsIInputStream *aStream)
{
NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
// Remove the old stream if there is one
if (mData)
mStream->RemoveStream(2);
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aStream);
if (!seekable) {
return NS_ERROR_INVALID_ARG;
}
mStream = aStream;
mData = aStream;
if (aStream)
mStream->AppendStream(mData);
return NS_OK;
}
@ -146,7 +164,7 @@ NS_IMETHODIMP
nsMIMEInputStream::GetData(nsIInputStream **aStream)
{
NS_ENSURE_ARG_POINTER(aStream);
*aStream = mStream;
*aStream = mData;
NS_IF_ADDREF(*aStream);
return NS_OK;
}
@ -158,13 +176,28 @@ void nsMIMEInputStream::InitStreams()
"Don't call initStreams twice without rewinding");
mStartedReading = true;
// We'll use the content-length stream to add the final \r\n
if (mAddContentLength) {
uint64_t cl = 0;
if (mData) {
mData->Available(&cl);
}
mContentLength.AssignLiteral("Content-Length: ");
mContentLength.AppendInt(cl);
mContentLength.AppendLiteral("\r\n\r\n");
}
else {
mContentLength.AssignLiteral("\r\n");
}
mCLStream->ShareData(mContentLength.get(), -1);
mHeaderStream->ShareData(mHeaders.get(), -1);
}
#define INITSTREAMS \
if (!mStartedReading) { \
NS_ENSURE_TRUE(mStream, NS_ERROR_UNEXPECTED); \
InitStreams(); \
}
@ -172,11 +205,8 @@ if (!mStartedReading) { \
NS_IMETHODIMP
nsMIMEInputStream::Seek(int32_t whence, int64_t offset)
{
NS_ENSURE_TRUE(mStream, NS_ERROR_UNEXPECTED);
nsresult rv;
nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
if (whence == NS_SEEK_SET && offset == 0) {
rv = stream->Seek(whence, offset);
if (NS_SUCCEEDED(rv))
@ -254,11 +284,22 @@ nsMIMEInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result)
if (outer)
return NS_ERROR_NO_AGGREGATION;
RefPtr<nsMIMEInputStream> inst = new nsMIMEInputStream();
nsMIMEInputStream *inst = new nsMIMEInputStream();
if (!inst)
return NS_ERROR_OUT_OF_MEMORY;
return inst->QueryInterface(iid, result);
NS_ADDREF(inst);
nsresult rv = inst->Init();
if (NS_FAILED(rv)) {
NS_RELEASE(inst);
return rv;
}
rv = inst->QueryInterface(iid, result);
NS_RELEASE(inst);
return rv;
}
void
@ -267,9 +308,12 @@ nsMIMEInputStream::Serialize(InputStreamParams& aParams,
{
MIMEInputStreamParams params;
if (mStream) {
if (mData) {
nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mData);
MOZ_ASSERT(stream);
InputStreamParams wrappedParams;
SerializeInputStream(mStream, wrappedParams, aFileDescriptors);
SerializeInputStream(stream, wrappedParams, aFileDescriptors);
NS_ASSERTION(wrappedParams.type() != InputStreamParams::T__None,
"Wrapped stream failed to serialize!");
@ -281,7 +325,9 @@ nsMIMEInputStream::Serialize(InputStreamParams& aParams,
}
params.headers() = mHeaders;
params.contentLength() = mContentLength;
params.startedReading() = mStartedReading;
params.addContentLength() = mAddContentLength;
aParams = params;
}
@ -300,10 +346,17 @@ nsMIMEInputStream::Deserialize(const InputStreamParams& aParams,
const OptionalInputStreamParams& wrappedParams = params.optionalStream();
mHeaders = params.headers();
mContentLength = params.contentLength();
mStartedReading = params.startedReading();
// nsMIMEInputStream::Init() already appended mHeaderStream & mCLStream
mHeaderStream->ShareData(mHeaders.get(),
mStartedReading ? mHeaders.Length() : 0);
mCLStream->ShareData(mContentLength.get(),
mStartedReading ? mContentLength.Length() : 0);
nsCOMPtr<nsIInputStream> stream;
if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) {
nsCOMPtr<nsIInputStream> stream;
stream = DeserializeInputStream(wrappedParams.get_InputStreamParams(),
aFileDescriptors);
if (!stream) {
@ -311,13 +364,20 @@ nsMIMEInputStream::Deserialize(const InputStreamParams& aParams,
return false;
}
mStream = stream;
mData = stream;
if (NS_FAILED(mStream->AppendStream(mData))) {
NS_WARNING("Failed to append stream!");
return false;
}
}
else {
NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t,
"Unknown type for OptionalInputStreamParams!");
}
mAddContentLength = params.addContentLength();
return true;
}

Просмотреть файл

@ -52,9 +52,7 @@
#include "nsIURL.h"
#include "nsIConsoleService.h"
#include "mozilla/BinarySearch.h"
#include "mozilla/DebugOnly.h"
#include "nsIHttpHeaderVisitor.h"
#include "nsIMIMEInputStream.h"
#include "nsIXULRuntime.h"
#include "nsICacheInfoChannel.h"
#include "nsIDOMWindowUtils.h"
@ -65,85 +63,6 @@
namespace mozilla {
namespace net {
static
bool IsHeaderBlacklistedForRedirectCopy(nsHttpAtom const& aHeader)
{
// IMPORTANT: keep this list ASCII-code sorted
static nsHttpAtom const* blackList[] = {
&nsHttp::Accept,
&nsHttp::Accept_Encoding,
&nsHttp::Accept_Language,
&nsHttp::Authentication,
&nsHttp::Authorization,
&nsHttp::Connection,
&nsHttp::Content_Length,
&nsHttp::Cookie,
&nsHttp::Host,
&nsHttp::If,
&nsHttp::If_Match,
&nsHttp::If_Modified_Since,
&nsHttp::If_None_Match,
&nsHttp::If_None_Match_Any,
&nsHttp::If_Range,
&nsHttp::If_Unmodified_Since,
&nsHttp::Proxy_Authenticate,
&nsHttp::Proxy_Authorization,
&nsHttp::Range,
&nsHttp::TE,
&nsHttp::Transfer_Encoding,
&nsHttp::Upgrade,
&nsHttp::User_Agent,
&nsHttp::WWW_Authenticate
};
class HttpAtomComparator
{
nsHttpAtom const& mTarget;
public:
explicit HttpAtomComparator(nsHttpAtom const& aTarget)
: mTarget(aTarget) {}
int operator()(nsHttpAtom const* aVal) const {
if (mTarget == *aVal) {
return 0;
}
return strcmp(mTarget._val, aVal->_val);
}
};
size_t unused;
return BinarySearchIf(blackList, 0, ArrayLength(blackList),
HttpAtomComparator(aHeader), &unused);
}
class AddHeadersToChannelVisitor final : public nsIHttpHeaderVisitor
{
public:
NS_DECL_ISUPPORTS
explicit AddHeadersToChannelVisitor(nsIHttpChannel *aChannel)
: mChannel(aChannel)
{
}
NS_IMETHOD VisitHeader(const nsACString& aHeader,
const nsACString& aValue) override
{
nsHttpAtom atom = nsHttp::ResolveAtom(aHeader);
if (!IsHeaderBlacklistedForRedirectCopy(atom)) {
mChannel->SetRequestHeader(aHeader, aValue, false);
}
return NS_OK;
}
private:
~AddHeadersToChannelVisitor()
{
}
nsCOMPtr<nsIHttpChannel> mChannel;
};
NS_IMPL_ISUPPORTS(AddHeadersToChannelVisitor, nsIHttpHeaderVisitor)
HttpBaseChannel::HttpBaseChannel()
: mStartPos(UINT64_MAX)
, mStatus(NS_OK)
@ -734,38 +653,22 @@ HttpBaseChannel::SetUploadStream(nsIInputStream *stream,
if (stream) {
nsAutoCString method;
bool hasHeaders = false;
bool hasHeaders;
// This method and ExplicitSetUploadStream mean different things by "empty
// content type string". This method means "no header", but
// ExplicitSetUploadStream means "header with empty value". So we have to
// massage the contentType argument into the form ExplicitSetUploadStream
// expects.
nsCOMPtr<nsIMIMEInputStream> mimeStream;
nsCString contentType(contentTypeArg);
if (contentType.IsEmpty()) {
contentType.SetIsVoid(true);
nsAutoCString contentType;
if (contentTypeArg.IsEmpty()) {
method = NS_LITERAL_CSTRING("POST");
// MIME streams are a special case, and include headers which need to be
// copied to the channel.
mimeStream = do_QueryInterface(stream);
if (mimeStream) {
// Copy non-origin related headers to the channel.
nsCOMPtr<nsIHttpHeaderVisitor> visitor =
new AddHeadersToChannelVisitor(this);
mimeStream->VisitHeaders(visitor);
return ExplicitSetUploadStream(stream, contentType, contentLength,
method, hasHeaders);
}
hasHeaders = true;
contentType.SetIsVoid(true);
} else {
method = NS_LITERAL_CSTRING("PUT");
MOZ_ASSERT(NS_FAILED(CallQueryInterface(stream, getter_AddRefs(mimeStream))),
"nsIMIMEInputStream should not be set with an explicit content type");
hasHeaders = false;
contentType = contentTypeArg;
}
return ExplicitSetUploadStream(stream, contentType, contentLength,
method, hasHeaders);
@ -907,13 +810,6 @@ HttpBaseChannel::ExplicitSetUploadStream(nsIInputStream *aStream,
// Ensure stream is set and method is valid
NS_ENSURE_TRUE(aStream, NS_ERROR_FAILURE);
{
DebugOnly<nsCOMPtr<nsIMIMEInputStream>> mimeStream;
MOZ_ASSERT(!aStreamHasHeaders ||
NS_FAILED(CallQueryInterface(aStream, getter_AddRefs(mimeStream.value))),
"nsIMIMEInputStream should not include headers");
}
if (aContentLength < 0 && !aStreamHasHeaders) {
nsresult rv = aStream->Available(reinterpret_cast<uint64_t*>(&aContentLength));
if (NS_FAILED(rv) || aContentLength < 0) {
@ -2997,6 +2893,85 @@ HttpBaseChannel::ShouldRewriteRedirectToGET(uint32_t httpStatus,
return false;
}
static
bool IsHeaderBlacklistedForRedirectCopy(nsHttpAtom const& aHeader)
{
// IMPORTANT: keep this list ASCII-code sorted
static nsHttpAtom const* blackList[] = {
&nsHttp::Accept,
&nsHttp::Accept_Encoding,
&nsHttp::Accept_Language,
&nsHttp::Authentication,
&nsHttp::Authorization,
&nsHttp::Connection,
&nsHttp::Content_Length,
&nsHttp::Cookie,
&nsHttp::Host,
&nsHttp::If,
&nsHttp::If_Match,
&nsHttp::If_Modified_Since,
&nsHttp::If_None_Match,
&nsHttp::If_None_Match_Any,
&nsHttp::If_Range,
&nsHttp::If_Unmodified_Since,
&nsHttp::Proxy_Authenticate,
&nsHttp::Proxy_Authorization,
&nsHttp::Range,
&nsHttp::TE,
&nsHttp::Transfer_Encoding,
&nsHttp::Upgrade,
&nsHttp::User_Agent,
&nsHttp::WWW_Authenticate
};
class HttpAtomComparator
{
nsHttpAtom const& mTarget;
public:
explicit HttpAtomComparator(nsHttpAtom const& aTarget)
: mTarget(aTarget) {}
int operator()(nsHttpAtom const* aVal) const {
if (mTarget == *aVal) {
return 0;
}
return strcmp(mTarget._val, aVal->_val);
}
};
size_t unused;
return BinarySearchIf(blackList, 0, ArrayLength(blackList),
HttpAtomComparator(aHeader), &unused);
}
class SetupReplacementChannelHeaderVisitor final : public nsIHttpHeaderVisitor
{
public:
NS_DECL_ISUPPORTS
explicit SetupReplacementChannelHeaderVisitor(nsIHttpChannel *aChannel)
: mChannel(aChannel)
{
}
NS_IMETHOD VisitHeader(const nsACString& aHeader,
const nsACString& aValue) override
{
nsHttpAtom atom = nsHttp::ResolveAtom(aHeader);
if (!IsHeaderBlacklistedForRedirectCopy(atom)) {
mChannel->SetRequestHeader(aHeader, aValue, false);
}
return NS_OK;
}
private:
~SetupReplacementChannelHeaderVisitor()
{
}
nsCOMPtr<nsIHttpChannel> mChannel;
};
NS_IMPL_ISUPPORTS(SetupReplacementChannelHeaderVisitor, nsIHttpHeaderVisitor)
nsresult
HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
nsIChannel *newChannel,
@ -3292,7 +3267,7 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
nsIChannelEventSink::REDIRECT_STS_UPGRADE)) {
// Copy non-origin related headers to the new channel.
nsCOMPtr<nsIHttpHeaderVisitor> visitor =
new AddHeadersToChannelVisitor(httpChannel);
new SetupReplacementChannelHeaderVisitor(httpChannel);
mRequestHead.VisitHeaders(visitor);
}

Просмотреть файл

@ -149,23 +149,15 @@ document.getElementById('form').submit();
`;
} else if (this.uri.spec.startsWith(ACTION_BASE)) {
var postData = "";
var headers = {};
if (this._uploadStream) {
var bstream = Cc["@mozilla.org/binaryinputstream;1"]
.createInstance(Ci.nsIBinaryInputStream);
bstream.setInputStream(this._uploadStream);
postData = bstream.readBytes(bstream.available());
if (this._uploadStream instanceof Ci.nsIMIMEInputStream) {
this._uploadStream.visitHeaders((name, value) => {
headers[name] = value;
});
}
}
data += `
<input id="upload_stream" value="${this._uploadStream ? "yes" : "no"}">
<input id="post_data" value="${btoa(postData)}">
<input id="upload_headers" value='${JSON.stringify(headers)}'>
`;
}
@ -222,9 +214,8 @@ function frameScript() {
if (frame) {
var upload_stream = frame.contentDocument.getElementById("upload_stream");
var post_data = frame.contentDocument.getElementById("post_data");
var headers = frame.contentDocument.getElementById("upload_headers");
if (upload_stream && post_data && headers) {
sendAsyncMessage("Test:IFrameLoaded", [upload_stream.value, post_data.value, headers.value]);
if (upload_stream && post_data) {
sendAsyncMessage("Test:IFrameLoaded", [upload_stream.value, post_data.value]);
return;
}
}
@ -245,9 +236,9 @@ function loadTestTab(uri) {
browser.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")();", true);
return new Promise(resolve => {
function listener({ data: [hasUploadStream, postData, headers] }) {
function listener({ data: [hasUploadStream, postData] }) {
manager.removeMessageListener("Test:IFrameLoaded", listener);
resolve([hasUploadStream, atob(postData), JSON.parse(headers)]);
resolve([hasUploadStream, atob(postData)]);
}
manager.addMessageListener("Test:IFrameLoaded", listener);
@ -281,14 +272,13 @@ add_task(function*() {
});
add_task(function*() {
var [hasUploadStream, postData, headers] = yield loadTestTab(POST_FORM_URI);
var [hasUploadStream, postData] = yield loadTestTab(POST_FORM_URI);
is(hasUploadStream, "yes", "post action should have uploadStream");
is(postData, "foo=bar\r\n",
"POST data is received correctly");
is(headers["Content-Type"], "text/plain", "Content-Type header is correct");
is(headers["Content-Length"], undefined, "Content-Length header is correct");
is(postData,
"Content-Type: text/plain\r\n" +
"Content-Length: 9\r\n" +
"\r\n" +
"foo=bar\r\n", "POST data is received correctly");
gBrowser.removeCurrentTab();
});

Просмотреть файл

@ -69,13 +69,14 @@ add_task(function* test_setup() {
function background() {
const FILTERS = {urls: ["<all_urls>"]};
let requestBodySupported = true;
function onUpload(details) {
let url = new URL(details.url);
let upload = url.searchParams.get("upload");
if (!upload) {
if (!upload || !requestBodySupported) {
return;
}
let requestBody = details.requestBody;
browser.test.log(`onBeforeRequest upload: ${details.url} ${JSON.stringify(details.requestBody)}`);
browser.test.assertTrue(!!requestBody, `Intercepted upload ${details.url} #${details.requestId} ${upload} have a requestBody`);
@ -109,8 +110,21 @@ function background() {
onUpload(details);
};
browser.webRequest.onBeforeRequest.addListener(
onBeforeRequest, FILTERS, ["requestBody"]);
try {
browser.webRequest.onBeforeRequest.addListener(
onBeforeRequest, FILTERS, ["requestBody"]);
} catch (e) {
browser.test.assertTrue(/\brequestBody\b/.test(e.message),
"Request body is unsupported");
// requestBody is disabled in release builds
if (!/\brequestBody\b/.test(e.message)) {
throw e;
}
browser.webRequest.onBeforeRequest.addListener(
onBeforeRequest, FILTERS);
}
}
add_task(function* test_xhr_forms() {

Просмотреть файл

@ -18,19 +18,8 @@ function makeURI(url) {
return Services.io.newURI(url);
}
function serializeInputStream(aStream) {
let data = {
content: NetUtil.readInputStreamToString(aStream, aStream.available()),
};
if (aStream instanceof Ci.nsIMIMEInputStream) {
data.headers = new Map();
aStream.visitHeaders((name, value) => {
data.headers.set(name, value);
});
}
return data;
function readInputStreamToString(aStream) {
return NetUtil.readInputStreamToString(aStream, aStream.available());
}
function RemoteWebNavigation() {
@ -92,8 +81,8 @@ RemoteWebNavigation.prototype = {
flags: aLoadFlags,
referrer: aReferrer ? aReferrer.spec : null,
referrerPolicy: aReferrerPolicy,
postData: aPostData ? serializeInputStream(aPostData) : null,
headers: aHeaders ? serializeInputStream(aHeaders) : null,
postData: aPostData ? readInputStreamToString(aPostData) : null,
headers: aHeaders ? readInputStreamToString(aHeaders) : null,
baseURI: aBaseURI ? aBaseURI.spec : null,
triggeringPrincipal: aTriggeringPrincipal
? Utils.serializePrincipal(aTriggeringPrincipal)

Просмотреть файл

@ -26,22 +26,10 @@ if (AppConstants.MOZ_CRASHREPORTER) {
"nsICrashReporter");
}
function makeInputStream(data) {
function makeInputStream(aString) {
let stream = Cc["@mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsISupportsCString);
stream.data = data.content;
if (data.headers) {
let mimeStream = Cc["@mozilla.org/network/mime-input-stream;1"]
.createInstance(Ci.nsIMIMEInputStream);
mimeStream.setData(stream);
for (let [name, value] of data.headers) {
mimeStream.addHeader(name, value);
}
return mimeStream;
}
stream.data = aString;
return stream; // XPConnect will QI this to nsIInputStream for us.
}

Просмотреть файл

@ -19,6 +19,8 @@ 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");
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
"resource://gre/modules/BrowserUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionUtils",
@ -855,8 +857,14 @@ HttpObserverManager = {
};
var onBeforeRequest = {
allowedOptions: ["blocking", "requestBody"],
get allowedOptions() {
delete this.allowedOptions;
this.allowedOptions = ["blocking"];
if (!AppConstants.RELEASE_OR_BETA) {
this.allowedOptions.push("requestBody");
}
return this.allowedOptions;
},
addListener(callback, filter = null, opt_extraInfoSpec = null) {
let opts = parseExtra(opt_extraInfoSpec, this.allowedOptions);
opts.filter = parseFilter(filter);

Просмотреть файл

@ -368,11 +368,31 @@ function parseFormData(stream, channel, lenient = false) {
}
try {
let headers;
if (stream instanceof Ci.nsIMIMEInputStream && stream.data) {
// MIME input streams encode additional headers as a block at the
// beginning of their stream. The actual request data comes from a
// sub-stream, which is accessible via their `data` member. The
// difference in available bytes between the outer stream and the
// inner data stream tells us the size of that header block.
//
// Since we need to know at least the value of the Content-Type
// header to properly parse the request body, we need to read and
// parse the header block in order to extract it.
headers = readString(createTextStream(stream),
stream.available() - stream.data.available());
rewind(stream);
stream = stream.data;
}
let contentType = channel.getRequestHeader("Content-Type");
let contentType;
try {
contentType = channel.getRequestHeader("Content-Type");
} catch (e) {
contentType = new Headers(headers).get("content-type");
}
switch (Headers.getParam(contentType, "")) {
case "multipart/form-data":
@ -482,33 +502,29 @@ function* getRawDataChunked(outerStream, maxRead = WebRequestUpload.MAX_RAW_BYTE
WebRequestUpload = {
createRequestBody(channel) {
if (!(channel instanceof Ci.nsIUploadChannel) || !channel.uploadStream) {
return null;
}
if (channel instanceof Ci.nsIUploadChannel && channel.uploadStream) {
try {
let stream = channel.uploadStream;
if (channel instanceof Ci.nsIUploadChannel2 && channel.uploadStreamHasHeaders) {
return {error: "Upload streams with headers are unsupported"};
}
let formData = createFormData(stream, channel);
if (formData) {
return {formData};
}
try {
let stream = channel.uploadStream;
let formData = createFormData(stream, channel);
if (formData) {
return {formData};
// If we failed to parse the stream as form data, return it as a
// sequence of raw data chunks, along with a leniently-parsed form
// data object, which ignores encoding errors.
return {
raw: Array.from(getRawDataChunked(stream)),
lenientFormData: createFormData(stream, channel, true),
};
} catch (e) {
Cu.reportError(e);
return {error: e.message || String(e)};
}
// If we failed to parse the stream as form data, return it as a
// sequence of raw data chunks, along with a leniently-parsed form
// data object, which ignores encoding errors.
return {
raw: Array.from(getRawDataChunked(stream)),
lenientFormData: createFormData(stream, channel, true),
};
} catch (e) {
Cu.reportError(e);
return {error: e.message || String(e)};
}
return null;
},
};