зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1039846 - Request implementation. r=baku
--HG-- extra : transplant_source : %E6%A1%EB%5C3%95%8A%B4%3F%5C%D8%28%B2%98%5E%A1%C2%E5%1C%EE
This commit is contained in:
Родитель
1d8716d7ba
Коммит
68bb3e1d0c
|
@ -60,3 +60,5 @@ MSG_DEF(MSG_INVALID_HEADER_VALUE, 1, "{0} is an invalid header value.")
|
|||
MSG_DEF(MSG_INVALID_HEADER_SEQUENCE, 0, "Headers require name/value tuples when being initialized by a sequence.")
|
||||
MSG_DEF(MSG_PERMISSION_DENIED_TO_PASS_ARG, 1, "Permission denied to pass cross-origin object as {0}.")
|
||||
MSG_DEF(MSG_MISSING_REQUIRED_DICTIONARY_MEMBER, 1, "Missing required {0}.")
|
||||
MSG_DEF(MSG_INVALID_REQUEST_METHOD, 1, "Invalid request method {0}.")
|
||||
MSG_DEF(MSG_REQUEST_BODY_CONSUMED_ERROR, 0, "Request body has already been consumed.")
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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 "Fetch.h"
|
||||
|
||||
#include "nsIStringStream.h"
|
||||
#include "nsIUnicodeEncoder.h"
|
||||
|
||||
#include "nsStringStream.h"
|
||||
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
#include "mozilla/dom/URLSearchParams.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
nsresult
|
||||
ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams& aBodyInit,
|
||||
nsIInputStream** aStream,
|
||||
nsCString& aContentType)
|
||||
{
|
||||
MOZ_ASSERT(aStream);
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIInputStream> byteStream;
|
||||
if (aBodyInit.IsArrayBuffer()) {
|
||||
const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer();
|
||||
buf.ComputeLengthAndData();
|
||||
//XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
|
||||
rv = NS_NewByteInputStream(getter_AddRefs(byteStream),
|
||||
reinterpret_cast<char*>(buf.Data()),
|
||||
buf.Length(), NS_ASSIGNMENT_COPY);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
} else if (aBodyInit.IsArrayBufferView()) {
|
||||
const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView();
|
||||
buf.ComputeLengthAndData();
|
||||
//XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
|
||||
rv = NS_NewByteInputStream(getter_AddRefs(byteStream),
|
||||
reinterpret_cast<char*>(buf.Data()),
|
||||
buf.Length(), NS_ASSIGNMENT_COPY);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
} else if (aBodyInit.IsScalarValueString()) {
|
||||
nsString str = aBodyInit.GetAsScalarValueString();
|
||||
|
||||
nsCOMPtr<nsIUnicodeEncoder> encoder = EncodingUtils::EncoderForEncoding("UTF-8");
|
||||
if (!encoder) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
int32_t destBufferLen;
|
||||
rv = encoder->GetMaxLength(str.get(), str.Length(), &destBufferLen);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCString encoded;
|
||||
if (!encoded.SetCapacity(destBufferLen, fallible_t())) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
char* destBuffer = encoded.BeginWriting();
|
||||
int32_t srcLen = (int32_t) str.Length();
|
||||
int32_t outLen = destBufferLen;
|
||||
rv = encoder->Convert(str.get(), &srcLen, destBuffer, &outLen);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(outLen <= destBufferLen);
|
||||
encoded.SetLength(outLen);
|
||||
rv = NS_NewCStringInputStream(getter_AddRefs(byteStream), encoded);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
aContentType = NS_LITERAL_CSTRING("text/plain;charset=UTF-8");
|
||||
} else if (aBodyInit.IsURLSearchParams()) {
|
||||
URLSearchParams& params = aBodyInit.GetAsURLSearchParams();
|
||||
nsString serialized;
|
||||
params.Stringify(serialized);
|
||||
rv = NS_NewStringInputStream(getter_AddRefs(byteStream), serialized);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
aContentType = NS_LITERAL_CSTRING("application/x-www-form-urlencoded;charset=UTF-8");
|
||||
}
|
||||
|
||||
MOZ_ASSERT(byteStream);
|
||||
byteStream.forget(aStream);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,29 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_Fetch_h
|
||||
#define mozilla_dom_Fetch_h
|
||||
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
|
||||
class nsIInputStream;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/*
|
||||
* Creates an nsIInputStream based on the fetch specifications 'extract a byte
|
||||
* stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
|
||||
* Stores content type in out param aContentType.
|
||||
*/
|
||||
nsresult
|
||||
ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams& aBodyInit,
|
||||
nsIInputStream** aStream,
|
||||
nsCString& aContentType);
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_Fetch_h
|
|
@ -7,7 +7,6 @@
|
|||
#include "mozilla/dom/Headers.h"
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
|
@ -83,6 +82,39 @@ Headers::Constructor(const GlobalObject& aGlobal,
|
|||
return headers.forget();
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<Headers>
|
||||
Headers::Constructor(const GlobalObject& aGlobal,
|
||||
const OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap& aInit,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsRefPtr<Headers> headers = new Headers(aGlobal.GetAsSupports());
|
||||
|
||||
if (aInit.IsHeaders()) {
|
||||
headers->Fill(aInit.GetAsHeaders(), aRv);
|
||||
} else if (aInit.IsByteStringSequenceSequence()) {
|
||||
headers->Fill(aInit.GetAsByteStringSequenceSequence(), aRv);
|
||||
} else if (aInit.IsByteStringMozMap()) {
|
||||
headers->Fill(aInit.GetAsByteStringMozMap(), aRv);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return headers.forget();
|
||||
}
|
||||
|
||||
Headers::Headers(const Headers& aOther)
|
||||
: mOwner(aOther.mOwner)
|
||||
, mGuard(aOther.mGuard)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
ErrorResult result;
|
||||
Fill(aOther, result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
}
|
||||
|
||||
void
|
||||
Headers::Append(const nsACString& aName, const nsACString& aValue,
|
||||
ErrorResult& aRv)
|
||||
|
@ -202,6 +234,12 @@ Headers::Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Headers::Clear()
|
||||
{
|
||||
mList.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
Headers::SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv)
|
||||
{
|
||||
|
@ -328,6 +366,5 @@ Headers::Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv)
|
|||
Append(NS_ConvertUTF16toUTF8(keys[i]), aInit.Get(keys[i]), aRv);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#define mozilla_dom_Headers_h
|
||||
|
||||
#include "mozilla/dom/HeadersBinding.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
|
@ -42,7 +44,7 @@ private:
|
|||
nsCString mValue;
|
||||
};
|
||||
|
||||
nsRefPtr<nsISupports> mOwner;
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
HeadersGuardEnum mGuard;
|
||||
nsTArray<Entry> mList;
|
||||
|
||||
|
@ -54,6 +56,8 @@ public:
|
|||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
explicit Headers(const Headers& aOther);
|
||||
|
||||
static bool PrefEnabled(JSContext* cx, JSObject* obj);
|
||||
|
||||
static already_AddRefed<Headers>
|
||||
|
@ -61,6 +65,11 @@ public:
|
|||
const Optional<HeadersOrByteStringSequenceSequenceOrByteStringMozMap>& aInit,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<Headers>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
const OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap& aInit,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void Append(const nsACString& aName, const nsACString& aValue,
|
||||
ErrorResult& aRv);
|
||||
void Delete(const nsACString& aName, ErrorResult& aRv);
|
||||
|
@ -70,6 +79,8 @@ public:
|
|||
bool Has(const nsACString& aName, ErrorResult& aRv) const;
|
||||
void Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv);
|
||||
|
||||
void Clear();
|
||||
|
||||
// ChromeOnly
|
||||
HeadersGuardEnum Guard() const { return mGuard; }
|
||||
void SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv);
|
||||
|
@ -77,8 +88,13 @@ public:
|
|||
virtual JSObject* WrapObject(JSContext* aCx);
|
||||
nsISupports* GetParentObject() const { return mOwner; }
|
||||
|
||||
void Fill(const Headers& aInit, ErrorResult& aRv);
|
||||
private:
|
||||
Headers(const Headers& aOther) MOZ_DELETE;
|
||||
// Since Headers is also an nsISupports, the above constructor can
|
||||
// accidentally be invoked as new Headers(Headers*[, implied None guard]) when
|
||||
// the intention is to use the copy constructor. Explicitly disallow it.
|
||||
Headers(Headers* aOther) MOZ_DELETE;
|
||||
|
||||
virtual ~Headers();
|
||||
|
||||
static bool IsSimpleHeader(const nsACString& aName,
|
||||
|
@ -103,7 +119,6 @@ private:
|
|||
IsForbiddenResponseHeader(aName);
|
||||
}
|
||||
|
||||
void Fill(const Headers& aInit, ErrorResult& aRv);
|
||||
void Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv);
|
||||
void Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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 "InternalRequest.h"
|
||||
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsIDocument.h"
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/workers/Workers.h"
|
||||
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// The global is used to extract the principal.
|
||||
already_AddRefed<InternalRequest>
|
||||
InternalRequest::GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult& aRv) const
|
||||
{
|
||||
nsRefPtr<InternalRequest> copy = new InternalRequest();
|
||||
copy->mURL.Assign(mURL);
|
||||
copy->SetMethod(mMethod);
|
||||
copy->mHeaders = new Headers(*mHeaders);
|
||||
|
||||
copy->mBodyStream = mBodyStream;
|
||||
copy->mPreserveContentCodings = true;
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
nsIPrincipal* principal = aGlobal->PrincipalOrNull();
|
||||
MOZ_ASSERT(principal);
|
||||
aRv = nsContentUtils::GetASCIIOrigin(principal, copy->mOrigin);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
worker->AssertIsOnWorkerThread();
|
||||
|
||||
workers::WorkerPrivate::LocationInfo& location = worker->GetLocationInfo();
|
||||
copy->mOrigin = NS_ConvertUTF16toUTF8(location.mOrigin);
|
||||
}
|
||||
|
||||
copy->mMode = mMode;
|
||||
copy->mCredentialsMode = mCredentialsMode;
|
||||
// FIXME(nsm): Add ContentType fetch to nsIContentPolicy and friends.
|
||||
// Then set copy's mContext to that.
|
||||
return copy.forget();
|
||||
}
|
||||
|
||||
InternalRequest::~InternalRequest()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,264 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_InternalRequest_h
|
||||
#define mozilla_dom_InternalRequest_h
|
||||
|
||||
#include "mozilla/dom/Headers.h"
|
||||
#include "mozilla/dom/RequestBinding.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
class nsIDocument;
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FetchBodyStream;
|
||||
class Request;
|
||||
|
||||
class InternalRequest MOZ_FINAL
|
||||
{
|
||||
friend class Request;
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InternalRequest)
|
||||
|
||||
enum ContextFrameType
|
||||
{
|
||||
FRAMETYPE_AUXILIARY = 0,
|
||||
FRAMETYPE_TOP_LEVEL,
|
||||
FRAMETYPE_NESTED,
|
||||
FRAMETYPE_NONE,
|
||||
};
|
||||
|
||||
// Since referrer type can be none, client or a URL.
|
||||
enum ReferrerType
|
||||
{
|
||||
REFERRER_NONE = 0,
|
||||
REFERRER_CLIENT,
|
||||
REFERRER_URL,
|
||||
};
|
||||
|
||||
enum ResponseTainting
|
||||
{
|
||||
RESPONSETAINT_BASIC,
|
||||
RESPONSETAINT_CORS,
|
||||
RESPONSETAINT_OPAQUE,
|
||||
};
|
||||
|
||||
explicit InternalRequest()
|
||||
: mMethod("GET")
|
||||
, mHeaders(new Headers(nullptr, HeadersGuardEnum::None))
|
||||
, mContextFrameType(FRAMETYPE_NONE)
|
||||
, mReferrerType(REFERRER_CLIENT)
|
||||
, mMode(RequestMode::No_cors)
|
||||
, mCredentialsMode(RequestCredentials::Omit)
|
||||
, mResponseTainting(RESPONSETAINT_BASIC)
|
||||
, mRedirectCount(0)
|
||||
, mAuthenticationFlag(false)
|
||||
, mForceOriginHeader(false)
|
||||
, mManualRedirect(false)
|
||||
, mPreserveContentCodings(false)
|
||||
, mSameOriginDataURL(false)
|
||||
, mSkipServiceWorker(false)
|
||||
, mSynchronous(false)
|
||||
, mUnsafeRequest(false)
|
||||
, mUseURLCredentials(false)
|
||||
{
|
||||
}
|
||||
|
||||
explicit InternalRequest(const InternalRequest& aOther)
|
||||
: mMethod(aOther.mMethod)
|
||||
, mURL(aOther.mURL)
|
||||
, mHeaders(aOther.mHeaders)
|
||||
, mBodyStream(aOther.mBodyStream)
|
||||
, mContext(aOther.mContext)
|
||||
, mOrigin(aOther.mOrigin)
|
||||
, mContextFrameType(aOther.mContextFrameType)
|
||||
, mReferrerType(aOther.mReferrerType)
|
||||
, mReferrerURL(aOther.mReferrerURL)
|
||||
, mMode(aOther.mMode)
|
||||
, mCredentialsMode(aOther.mCredentialsMode)
|
||||
, mResponseTainting(aOther.mResponseTainting)
|
||||
, mRedirectCount(aOther.mRedirectCount)
|
||||
, mAuthenticationFlag(aOther.mAuthenticationFlag)
|
||||
, mForceOriginHeader(aOther.mForceOriginHeader)
|
||||
, mManualRedirect(aOther.mManualRedirect)
|
||||
, mPreserveContentCodings(aOther.mPreserveContentCodings)
|
||||
, mSameOriginDataURL(aOther.mSameOriginDataURL)
|
||||
, mSandboxedStorageAreaURLs(aOther.mSandboxedStorageAreaURLs)
|
||||
, mSkipServiceWorker(aOther.mSkipServiceWorker)
|
||||
, mSynchronous(aOther.mSynchronous)
|
||||
, mUnsafeRequest(aOther.mUnsafeRequest)
|
||||
, mUseURLCredentials(aOther.mUseURLCredentials)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
GetMethod(nsCString& aMethod) const
|
||||
{
|
||||
aMethod.Assign(mMethod);
|
||||
}
|
||||
|
||||
void
|
||||
SetMethod(const nsACString& aMethod)
|
||||
{
|
||||
mMethod.Assign(aMethod);
|
||||
}
|
||||
|
||||
void
|
||||
GetURL(nsCString& aURL) const
|
||||
{
|
||||
aURL.Assign(mURL);
|
||||
}
|
||||
|
||||
bool
|
||||
ReferrerIsNone() const
|
||||
{
|
||||
return mReferrerType == REFERRER_NONE;
|
||||
}
|
||||
|
||||
bool
|
||||
ReferrerIsURL() const
|
||||
{
|
||||
return mReferrerType == REFERRER_URL;
|
||||
}
|
||||
|
||||
bool
|
||||
ReferrerIsClient() const
|
||||
{
|
||||
return mReferrerType == REFERRER_CLIENT;
|
||||
}
|
||||
|
||||
nsCString
|
||||
ReferrerAsURL() const
|
||||
{
|
||||
MOZ_ASSERT(ReferrerIsURL());
|
||||
return mReferrerURL;
|
||||
}
|
||||
|
||||
void
|
||||
SetReferrer(const nsACString& aReferrer)
|
||||
{
|
||||
// May be removed later.
|
||||
MOZ_ASSERT(!ReferrerIsNone());
|
||||
mReferrerType = REFERRER_URL;
|
||||
mReferrerURL.Assign(aReferrer);
|
||||
}
|
||||
|
||||
bool
|
||||
IsSynchronous() const
|
||||
{
|
||||
return mSynchronous;
|
||||
}
|
||||
|
||||
void
|
||||
SetMode(RequestMode aMode)
|
||||
{
|
||||
mMode = aMode;
|
||||
}
|
||||
|
||||
void
|
||||
SetCredentialsMode(RequestCredentials aCredentialsMode)
|
||||
{
|
||||
mCredentialsMode = aCredentialsMode;
|
||||
}
|
||||
|
||||
nsContentPolicyType
|
||||
GetContext() const
|
||||
{
|
||||
return mContext;
|
||||
}
|
||||
|
||||
Headers*
|
||||
Headers_()
|
||||
{
|
||||
return mHeaders;
|
||||
}
|
||||
|
||||
bool
|
||||
ForceOriginHeader()
|
||||
{
|
||||
return mForceOriginHeader;
|
||||
}
|
||||
|
||||
void
|
||||
GetOrigin(nsCString& aOrigin) const
|
||||
{
|
||||
aOrigin.Assign(mOrigin);
|
||||
}
|
||||
|
||||
void
|
||||
SetBody(nsIInputStream* aStream)
|
||||
{
|
||||
mBodyStream = aStream;
|
||||
}
|
||||
|
||||
// Will return the original stream!
|
||||
// Use a tee or copy if you don't want to erase the original.
|
||||
void
|
||||
GetBody(nsIInputStream** aStream)
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> s = mBodyStream;
|
||||
s.forget(aStream);
|
||||
}
|
||||
|
||||
// The global is used as the client for the new object.
|
||||
already_AddRefed<InternalRequest>
|
||||
GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult& aRv) const;
|
||||
|
||||
private:
|
||||
~InternalRequest();
|
||||
|
||||
void
|
||||
SetURL(const nsACString& aURL)
|
||||
{
|
||||
mURL.Assign(aURL);
|
||||
}
|
||||
|
||||
nsCString mMethod;
|
||||
nsCString mURL;
|
||||
nsRefPtr<Headers> mHeaders;
|
||||
nsCOMPtr<nsIInputStream> mBodyStream;
|
||||
|
||||
// nsContentPolicyType does not cover the complete set defined in the spec,
|
||||
// but it is a good start.
|
||||
nsContentPolicyType mContext;
|
||||
|
||||
nsCString mOrigin;
|
||||
|
||||
ContextFrameType mContextFrameType;
|
||||
ReferrerType mReferrerType;
|
||||
|
||||
// When mReferrerType is REFERRER_URL.
|
||||
nsCString mReferrerURL;
|
||||
|
||||
RequestMode mMode;
|
||||
RequestCredentials mCredentialsMode;
|
||||
ResponseTainting mResponseTainting;
|
||||
|
||||
uint32_t mRedirectCount;
|
||||
|
||||
bool mAuthenticationFlag;
|
||||
bool mForceOriginHeader;
|
||||
bool mManualRedirect;
|
||||
bool mPreserveContentCodings;
|
||||
bool mSameOriginDataURL;
|
||||
bool mSandboxedStorageAreaURLs;
|
||||
bool mSkipServiceWorker;
|
||||
bool mSynchronous;
|
||||
bool mUnsafeRequest;
|
||||
bool mUseURLCredentials;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_InternalRequest_h
|
|
@ -5,30 +5,44 @@
|
|||
|
||||
#include "Request.h"
|
||||
|
||||
#include "nsDOMString.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsIUnicodeDecoder.h"
|
||||
#include "nsIURI.h"
|
||||
|
||||
#include "nsDOMFile.h"
|
||||
#include "nsDOMString.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsStringStream.h"
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
#include "mozilla/dom/Headers.h"
|
||||
#include "mozilla/dom/Fetch.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/URL.h"
|
||||
#include "mozilla/dom/workers/bindings/URL.h"
|
||||
|
||||
// dom/workers
|
||||
#include "File.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Request)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Request)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Request, mOwner, mHeaders)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Request, mOwner)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Request)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
Request::Request(nsISupports* aOwner)
|
||||
Request::Request(nsIGlobalObject* aOwner, InternalRequest* aRequest)
|
||||
: mOwner(aOwner)
|
||||
, mHeaders(new Headers(aOwner))
|
||||
, mRequest(aRequest)
|
||||
, mBodyUsed(false)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
@ -37,82 +51,393 @@ Request::~Request()
|
|||
{
|
||||
}
|
||||
|
||||
/*static*/ already_AddRefed<Request>
|
||||
Request::Constructor(const GlobalObject& global,
|
||||
const RequestOrScalarValueString& aInput,
|
||||
const RequestInit& aInit, ErrorResult& rv)
|
||||
already_AddRefed<InternalRequest>
|
||||
Request::GetInternalRequest()
|
||||
{
|
||||
nsRefPtr<Request> request = new Request(global.GetAsSupports());
|
||||
return request.forget();
|
||||
nsRefPtr<InternalRequest> r = mRequest;
|
||||
return r.forget();
|
||||
}
|
||||
|
||||
/*static*/ already_AddRefed<Request>
|
||||
Request::Constructor(const GlobalObject& aGlobal,
|
||||
const RequestOrScalarValueString& aInput,
|
||||
const RequestInit& aInit, ErrorResult& aRv)
|
||||
{
|
||||
nsRefPtr<InternalRequest> request;
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
|
||||
if (aInput.IsRequest()) {
|
||||
nsRefPtr<Request> inputReq = &aInput.GetAsRequest();
|
||||
if (inputReq->BodyUsed()) {
|
||||
aRv.ThrowTypeError(MSG_REQUEST_BODY_CONSUMED_ERROR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inputReq->SetBodyUsed();
|
||||
request = inputReq->GetInternalRequest();
|
||||
} else {
|
||||
request = new InternalRequest();
|
||||
}
|
||||
|
||||
request = request->GetRequestConstructorCopy(global, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RequestMode fallbackMode = RequestMode::EndGuard_;
|
||||
RequestCredentials fallbackCredentials = RequestCredentials::EndGuard_;
|
||||
if (aInput.IsScalarValueString()) {
|
||||
nsString input;
|
||||
input.Assign(aInput.GetAsScalarValueString());
|
||||
|
||||
nsString requestURL;
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global);
|
||||
MOZ_ASSERT(window);
|
||||
nsCOMPtr<nsIURI> docURI = window->GetDocumentURI();
|
||||
nsCString spec;
|
||||
aRv = docURI->GetSpec(spec);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<mozilla::dom::URL> url =
|
||||
dom::URL::Constructor(aGlobal, input, NS_ConvertUTF8toUTF16(spec), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
url->Stringify(requestURL, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
worker->AssertIsOnWorkerThread();
|
||||
|
||||
nsString baseURL = NS_ConvertUTF8toUTF16(worker->GetLocationInfo().mHref);
|
||||
nsRefPtr<workers::URL> url =
|
||||
workers::URL::Constructor(aGlobal, input, baseURL, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
url->Stringify(requestURL, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
request->SetURL(NS_ConvertUTF16toUTF8(requestURL));
|
||||
fallbackMode = RequestMode::Cors;
|
||||
fallbackCredentials = RequestCredentials::Omit;
|
||||
}
|
||||
|
||||
RequestMode mode = aInit.mMode.WasPassed() ? aInit.mMode.Value() : fallbackMode;
|
||||
RequestCredentials credentials =
|
||||
aInit.mCredentials.WasPassed() ? aInit.mCredentials.Value()
|
||||
: fallbackCredentials;
|
||||
|
||||
if (mode != RequestMode::EndGuard_) {
|
||||
request->SetMode(mode);
|
||||
}
|
||||
|
||||
if (credentials != RequestCredentials::EndGuard_) {
|
||||
request->SetCredentialsMode(credentials);
|
||||
}
|
||||
|
||||
if (aInit.mMethod.WasPassed()) {
|
||||
nsCString method = aInit.mMethod.Value();
|
||||
ToLowerCase(method);
|
||||
|
||||
if (!method.EqualsASCII("options") &&
|
||||
!method.EqualsASCII("get") &&
|
||||
!method.EqualsASCII("head") &&
|
||||
!method.EqualsASCII("post") &&
|
||||
!method.EqualsASCII("put") &&
|
||||
!method.EqualsASCII("delete")) {
|
||||
NS_ConvertUTF8toUTF16 label(method);
|
||||
aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ToUpperCase(method);
|
||||
request->SetMethod(method);
|
||||
}
|
||||
|
||||
nsRefPtr<Request> domRequest = new Request(global, request);
|
||||
nsRefPtr<Headers> domRequestHeaders = domRequest->Headers_();
|
||||
|
||||
nsRefPtr<Headers> headers;
|
||||
if (aInit.mHeaders.WasPassed()) {
|
||||
headers = Headers::Constructor(aGlobal, aInit.mHeaders.Value(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
headers = new Headers(*domRequestHeaders);
|
||||
}
|
||||
|
||||
domRequestHeaders->Clear();
|
||||
|
||||
if (domRequest->Mode() == RequestMode::No_cors) {
|
||||
nsCString method;
|
||||
domRequest->GetMethod(method);
|
||||
ToLowerCase(method);
|
||||
if (!method.EqualsASCII("get") &&
|
||||
!method.EqualsASCII("head") &&
|
||||
!method.EqualsASCII("post")) {
|
||||
NS_ConvertUTF8toUTF16 label(method);
|
||||
aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
domRequestHeaders->SetGuard(HeadersGuardEnum::Request_no_cors, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
domRequestHeaders->Fill(*headers, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aInit.mBody.WasPassed()) {
|
||||
const OwningArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams& bodyInit = aInit.mBody.Value();
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
nsCString contentType;
|
||||
aRv = ExtractByteStreamFromBody(bodyInit,
|
||||
getter_AddRefs(stream), contentType);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
request->SetBody(stream);
|
||||
|
||||
if (!contentType.IsVoid() &&
|
||||
!domRequestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
|
||||
domRequestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"),
|
||||
contentType, aRv);
|
||||
}
|
||||
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract mime type.
|
||||
nsTArray<nsCString> contentTypeValues;
|
||||
domRequestHeaders->GetAll(NS_LITERAL_CSTRING("Content-Type"),
|
||||
contentTypeValues, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// HTTP ABNF states Content-Type may have only one value.
|
||||
// This is from the "parse a header value" of the fetch spec.
|
||||
if (contentTypeValues.Length() == 1) {
|
||||
domRequest->mMimeType = contentTypeValues[0];
|
||||
ToLowerCase(domRequest->mMimeType);
|
||||
}
|
||||
|
||||
return domRequest.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Request>
|
||||
Request::Clone() const
|
||||
{
|
||||
nsRefPtr<Request> request = new Request(mOwner);
|
||||
// FIXME(nsm): Bug 1073231. This is incorrect, but the clone method isn't
|
||||
// well defined yet.
|
||||
nsRefPtr<Request> request = new Request(mOwner,
|
||||
new InternalRequest(*mRequest));
|
||||
return request.forget();
|
||||
}
|
||||
|
||||
namespace {
|
||||
nsresult
|
||||
DecodeUTF8(const nsCString& aBuffer, nsString& aDecoded)
|
||||
{
|
||||
nsCOMPtr<nsIUnicodeDecoder> decoder =
|
||||
EncodingUtils::DecoderForEncoding("UTF-8");
|
||||
if (!decoder) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
int32_t destBufferLen;
|
||||
nsresult rv =
|
||||
decoder->GetMaxLength(aBuffer.get(), aBuffer.Length(), &destBufferLen);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!aDecoded.SetCapacity(destBufferLen, fallible_t())) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
char16_t* destBuffer = aDecoded.BeginWriting();
|
||||
int32_t srcLen = (int32_t) aBuffer.Length();
|
||||
int32_t outLen = destBufferLen;
|
||||
rv = decoder->Convert(aBuffer.get(), &srcLen, destBuffer, &outLen);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(outLen <= destBufferLen);
|
||||
aDecoded.SetLength(outLen);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Request::ConsumeBody(ConsumeType aType, ErrorResult& aRv)
|
||||
{
|
||||
nsRefPtr<Promise> promise = Promise::Create(mOwner, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (BodyUsed()) {
|
||||
aRv.ThrowTypeError(MSG_REQUEST_BODY_CONSUMED_ERROR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SetBodyUsed();
|
||||
|
||||
// While the spec says to do this asynchronously, all the body constructors
|
||||
// right now only accept bodies whose streams are backed by an in-memory
|
||||
// buffer that can be read without blocking. So I think this is fine.
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
mRequest->GetBody(getter_AddRefs(stream));
|
||||
|
||||
if (!stream) {
|
||||
aRv = NS_NewByteInputStream(getter_AddRefs(stream), "", 0,
|
||||
NS_ASSIGNMENT_COPY);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
AutoJSAPI api;
|
||||
api.Init(mOwner);
|
||||
JSContext* cx = api.cx();
|
||||
|
||||
// We can make this assertion because for now we only support memory backed
|
||||
// structures for the body argument for a Request.
|
||||
MOZ_ASSERT(NS_InputStreamIsBuffered(stream));
|
||||
nsCString buffer;
|
||||
uint64_t len;
|
||||
aRv = stream->Available(&len);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
aRv = NS_ReadInputStreamToString(stream, buffer, len);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
buffer.SetLength(len);
|
||||
|
||||
switch (aType) {
|
||||
case CONSUME_ARRAYBUFFER: {
|
||||
JS::Rooted<JSObject*> arrayBuffer(cx);
|
||||
arrayBuffer =
|
||||
ArrayBuffer::Create(cx, buffer.Length(),
|
||||
reinterpret_cast<const uint8_t*>(buffer.get()));
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
val.setObjectOrNull(arrayBuffer);
|
||||
promise->MaybeResolve(cx, val);
|
||||
return promise.forget();
|
||||
}
|
||||
case CONSUME_BLOB: {
|
||||
// XXXnsm it is actually possible to avoid these duplicate allocations
|
||||
// for the Blob case by having the Blob adopt the stream's memory
|
||||
// directly, but I've not added a special case for now.
|
||||
//
|
||||
// This is similar to nsContentUtils::CreateBlobBuffer, but also deals
|
||||
// with worker wrapping.
|
||||
uint32_t blobLen = buffer.Length();
|
||||
void* blobData = moz_malloc(blobLen);
|
||||
nsCOMPtr<nsIDOMBlob> blob;
|
||||
if (blobData) {
|
||||
memcpy(blobData, buffer.BeginReading(), blobLen);
|
||||
blob = DOMFile::CreateMemoryFile(blobData, blobLen,
|
||||
NS_ConvertUTF8toUTF16(mMimeType));
|
||||
} else {
|
||||
aRv = NS_ERROR_OUT_OF_MEMORY;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> jsBlob(cx);
|
||||
if (NS_IsMainThread()) {
|
||||
aRv = nsContentUtils::WrapNative(cx, blob, &jsBlob);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
jsBlob.setObject(*workers::file::CreateBlob(cx, blob));
|
||||
}
|
||||
promise->MaybeResolve(cx, jsBlob);
|
||||
return promise.forget();
|
||||
}
|
||||
case CONSUME_JSON: {
|
||||
nsString decoded;
|
||||
aRv = DecodeUTF8(buffer, decoded);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> json(cx);
|
||||
if (!JS_ParseJSON(cx, decoded.get(), decoded.Length(), &json)) {
|
||||
JS::Rooted<JS::Value> exn(cx);
|
||||
if (JS_GetPendingException(cx, &exn)) {
|
||||
JS_ClearPendingException(cx);
|
||||
promise->MaybeReject(cx, exn);
|
||||
}
|
||||
}
|
||||
promise->MaybeResolve(cx, json);
|
||||
return promise.forget();
|
||||
}
|
||||
case CONSUME_TEXT: {
|
||||
nsString decoded;
|
||||
aRv = DecodeUTF8(buffer, decoded);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
promise->MaybeResolve(decoded);
|
||||
return promise.forget();
|
||||
}
|
||||
}
|
||||
|
||||
NS_NOTREACHED("Unexpected consume body type");
|
||||
// Silence warnings.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Request::ArrayBuffer(ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
|
||||
MOZ_ASSERT(global);
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
|
||||
return promise.forget();
|
||||
return ConsumeBody(CONSUME_ARRAYBUFFER, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Request::Blob(ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
|
||||
MOZ_ASSERT(global);
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
|
||||
return promise.forget();
|
||||
return ConsumeBody(CONSUME_BLOB, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Request::Json(ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
|
||||
MOZ_ASSERT(global);
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
|
||||
return promise.forget();
|
||||
return ConsumeBody(CONSUME_JSON, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Request::Text(ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
|
||||
MOZ_ASSERT(global);
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
Request::BodyUsed()
|
||||
{
|
||||
return false;
|
||||
return ConsumeBody(CONSUME_TEXT, aRv);
|
||||
}
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -9,10 +9,12 @@
|
|||
#include "nsISupportsImpl.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
#include "mozilla/dom/InternalRequest.h"
|
||||
// Required here due to certain WebIDL enums/classes being declared in both
|
||||
// files.
|
||||
#include "mozilla/dom/RequestBinding.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -28,7 +30,7 @@ class Request MOZ_FINAL : public nsISupports
|
|||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Request)
|
||||
|
||||
public:
|
||||
Request(nsISupports* aOwner);
|
||||
Request(nsIGlobalObject* aOwner, InternalRequest* aRequest);
|
||||
|
||||
JSObject*
|
||||
WrapObject(JSContext* aCx)
|
||||
|
@ -39,34 +41,40 @@ public:
|
|||
void
|
||||
GetUrl(DOMString& aUrl) const
|
||||
{
|
||||
aUrl.AsAString() = EmptyString();
|
||||
aUrl.AsAString() = NS_ConvertUTF8toUTF16(mRequest->mURL);
|
||||
}
|
||||
|
||||
void
|
||||
GetMethod(nsCString& aMethod) const
|
||||
{
|
||||
aMethod = EmptyCString();
|
||||
aMethod = mRequest->mMethod;
|
||||
}
|
||||
|
||||
RequestMode
|
||||
Mode() const
|
||||
{
|
||||
return RequestMode::Same_origin;
|
||||
return mRequest->mMode;
|
||||
}
|
||||
|
||||
RequestCredentials
|
||||
Credentials() const
|
||||
{
|
||||
return RequestCredentials::Omit;
|
||||
return mRequest->mCredentialsMode;
|
||||
}
|
||||
|
||||
void
|
||||
GetReferrer(DOMString& aReferrer) const
|
||||
{
|
||||
aReferrer.AsAString() = EmptyString();
|
||||
if (mRequest->ReferrerIsNone()) {
|
||||
aReferrer.AsAString() = EmptyString();
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME(nsm): Spec doesn't say what to do if referrer is client.
|
||||
aReferrer.AsAString() = NS_ConvertUTF8toUTF16(mRequest->mReferrerURL);
|
||||
}
|
||||
|
||||
Headers* Headers_() const { return mHeaders; }
|
||||
Headers* Headers_() const { return mRequest->Headers_(); }
|
||||
|
||||
static already_AddRefed<Request>
|
||||
Constructor(const GlobalObject& aGlobal, const RequestOrScalarValueString& aInput,
|
||||
|
@ -93,12 +101,38 @@ public:
|
|||
Text(ErrorResult& aRv);
|
||||
|
||||
bool
|
||||
BodyUsed();
|
||||
BodyUsed() const
|
||||
{
|
||||
return mBodyUsed;
|
||||
}
|
||||
|
||||
already_AddRefed<InternalRequest>
|
||||
GetInternalRequest();
|
||||
private:
|
||||
enum ConsumeType
|
||||
{
|
||||
CONSUME_ARRAYBUFFER,
|
||||
CONSUME_BLOB,
|
||||
// FormData not supported right now,
|
||||
CONSUME_JSON,
|
||||
CONSUME_TEXT,
|
||||
};
|
||||
|
||||
~Request();
|
||||
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
nsRefPtr<Headers> mHeaders;
|
||||
already_AddRefed<Promise>
|
||||
ConsumeBody(ConsumeType aType, ErrorResult& aRv);
|
||||
|
||||
void
|
||||
SetBodyUsed()
|
||||
{
|
||||
mBodyUsed = true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mOwner;
|
||||
nsRefPtr<InternalRequest> mRequest;
|
||||
bool mBodyUsed;
|
||||
nsCString mMimeType;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -5,13 +5,17 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'Fetch.h',
|
||||
'Headers.h',
|
||||
'InternalRequest.h',
|
||||
'Request.h',
|
||||
'Response.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'Fetch.cpp',
|
||||
'Headers.cpp',
|
||||
'InternalRequest.cpp',
|
||||
'Request.cpp',
|
||||
'Response.cpp',
|
||||
]
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "StructuredCloneTags.h"
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
|
||||
#include "Queue.h"
|
||||
#include "WorkerFeature.h"
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
worker_interfaces.js
|
||||
worker_test_request.js
|
||||
|
||||
[test_interfaces.html]
|
||||
[test_request.html]
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug XXXXXX - Test Request object in worker</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function checkEnabled() {
|
||||
var worker = new Worker("worker_test_request.js");
|
||||
worker.onmessage = function(event) {
|
||||
|
||||
if (event.data.type == 'finish') {
|
||||
SimpleTest.finish();
|
||||
} else if (event.data.type == 'status') {
|
||||
ok(event.data.status, event.data.msg);
|
||||
}
|
||||
}
|
||||
|
||||
worker.onerror = function(event) {
|
||||
ok(false, "Worker had an error: " + event.message + " at " + event.lineno);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
worker.postMessage(true);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.fetch.enabled", true]
|
||||
]}, function() {
|
||||
checkEnabled();
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
function ok(a, msg) {
|
||||
dump("OK: " + !!a + " => " + a + " " + msg + "\n");
|
||||
postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
|
||||
}
|
||||
|
||||
function is(a, b, msg) {
|
||||
dump("IS: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n");
|
||||
postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
|
||||
}
|
||||
|
||||
function testDefaultCtor() {
|
||||
var req = new Request("");
|
||||
is(req.method, "GET", "Default Request method is GET");
|
||||
ok(req.headers instanceof Headers, "Request should have non-null Headers object");
|
||||
is(req.url, self.location.href, "URL should be resolved with entry settings object's API base URL");
|
||||
is(req.referrer, "", "Default referrer is `client` which serializes to empty string.");
|
||||
is(req.mode, "cors", "Request mode for string input is cors");
|
||||
is(req.credentials, "omit", "Default Request credentials is omit");
|
||||
|
||||
var req = new Request(req);
|
||||
is(req.method, "GET", "Default Request method is GET");
|
||||
ok(req.headers instanceof Headers, "Request should have non-null Headers object");
|
||||
is(req.url, self.location.href, "URL should be resolved with entry settings object's API base URL");
|
||||
is(req.referrer, "", "Default referrer is `client` which serializes to empty string.");
|
||||
is(req.mode, "cors", "Request mode string input is cors");
|
||||
is(req.credentials, "omit", "Default Request credentials is omit");
|
||||
}
|
||||
|
||||
function testClone() {
|
||||
var req = (new Request("./cloned_request.txt", {
|
||||
method: 'POST',
|
||||
headers: { "Content-Length": 5 },
|
||||
body: "Sample body",
|
||||
mode: "same-origin",
|
||||
credentials: "same-origin",
|
||||
})).clone();
|
||||
ok(req.method === "POST", "Request method is POST");
|
||||
ok(req.headers instanceof Headers, "Request should have non-null Headers object");
|
||||
is(req.headers.get('content-length'), "5", "Request content-length should be 5.");
|
||||
ok(req.url === (new URL("./cloned_request.txt", self.location.href)).href,
|
||||
"URL should be resolved with entry settings object's API base URL");
|
||||
ok(req.referrer === "", "Default referrer is `client` which serializes to empty string.");
|
||||
ok(req.mode === "same-origin", "Request mode is same-origin");
|
||||
ok(req.credentials === "same-origin", "Default credentials is same-origin");
|
||||
}
|
||||
|
||||
function testUsedRequest() {
|
||||
// Passing a used request should fail.
|
||||
var req = new Request("", { body: "This is foo" });
|
||||
var p1 = req.text().then(function(v) {
|
||||
try {
|
||||
var req2 = new Request(req);
|
||||
ok(false, "Used Request cannot be passed to new Request");
|
||||
} catch(e) {
|
||||
ok(true, "Used Request cannot be passed to new Request");
|
||||
}
|
||||
});
|
||||
|
||||
// Passing a request should set the request as used.
|
||||
var reqA = new Request("", { body: "This is foo" });
|
||||
var reqB = new Request(reqA);
|
||||
is(reqA.bodyUsed, true, "Passing a Request to another Request should set the former as used");
|
||||
return p1;
|
||||
}
|
||||
|
||||
function testSimpleUrlParse() {
|
||||
// Just checks that the URL parser is actually being used.
|
||||
var req = new Request("/file.html");
|
||||
is(req.url, (new URL("/file.html", self.location.href)).href, "URL parser should be used to resolve Request URL");
|
||||
}
|
||||
|
||||
function testMethod() {
|
||||
var allowed = ["delete", "get", "head", "options", "post", "put"];
|
||||
for (var i = 0; i < allowed.length; ++i) {
|
||||
try {
|
||||
var r = new Request("", { method: allowed[i] });
|
||||
ok(true, "Method " + allowed[i] + " should be allowed");
|
||||
} catch(e) {
|
||||
ok(false, "Method " + allowed[i] + " should be allowed");
|
||||
}
|
||||
}
|
||||
|
||||
var forbidden = ["aardvark", "connect", "trace", "track"];
|
||||
for (var i = 0; i < forbidden.length; ++i) {
|
||||
try {
|
||||
var r = new Request("", { method: forbidden[i] });
|
||||
ok(false, "Method " + forbidden[i] + " should be forbidden");
|
||||
} catch(e) {
|
||||
ok(true, "Method " + forbidden[i] + " should be forbidden");
|
||||
}
|
||||
}
|
||||
|
||||
var allowedNoCors = ["get", "head", "post"];
|
||||
for (var i = 0; i < allowedNoCors.length; ++i) {
|
||||
try {
|
||||
var r = new Request("", { method: allowedNoCors[i], mode: "no-cors" });
|
||||
ok(true, "Method " + allowedNoCors[i] + " should be allowed in no-cors mode");
|
||||
} catch(e) {
|
||||
ok(false, "Method " + allowedNoCors[i] + " should be allowed in no-cors mode");
|
||||
}
|
||||
}
|
||||
|
||||
var forbiddenNoCors = ["aardvark", "delete", "options", "put"];
|
||||
for (var i = 0; i < forbiddenNoCors.length; ++i) {
|
||||
try {
|
||||
var r = new Request("", { method: forbiddenNoCors[i], mode: "no-cors" });
|
||||
ok(false, "Method " + forbiddenNoCors[i] + " should be forbidden in no-cors mode");
|
||||
} catch(e) {
|
||||
ok(true, "Method " + forbiddenNoCors[i] + " should be forbidden in no-cors mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function testUrlFragment() {
|
||||
var req = new Request("./request#withfragment");
|
||||
ok(req.url, (new URL("./request", self.location.href)).href, "request.url should be serialized with exclude fragment flag set");
|
||||
}
|
||||
|
||||
function testBodyUsed() {
|
||||
var req = new Request("./bodyused", { body: "Sample body" });
|
||||
is(req.bodyUsed, false, "bodyUsed is initially false.");
|
||||
return req.text().then((v) => {
|
||||
is(v, "Sample body", "Body should match");
|
||||
is(req.bodyUsed, true, "After reading body, bodyUsed should be true.");
|
||||
}).then((v) => {
|
||||
return req.blob().then((v) => {
|
||||
ok(false, "Attempting to read body again should fail.");
|
||||
}, (e) => {
|
||||
ok(true, "Attempting to read body again should fail.");
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// FIXME(nsm): Bug 1071290: We can't use Blobs as the body yet.
|
||||
function testBodyCreation() {
|
||||
var text = "κόσμε";
|
||||
var req1 = new Request("", { body: text });
|
||||
var p1 = req1.text().then(function(v) {
|
||||
ok(typeof v === "string", "Should resolve to string");
|
||||
is(text, v, "Extracted string should match");
|
||||
});
|
||||
|
||||
var req2 = new Request("", { body: new Uint8Array([72, 101, 108, 108, 111]) });
|
||||
var p2 = req2.text().then(function(v) {
|
||||
is("Hello", v, "Extracted string should match");
|
||||
});
|
||||
|
||||
var req2b = new Request("", { body: (new Uint8Array([72, 101, 108, 108, 111])).buffer });
|
||||
var p2b = req2b.text().then(function(v) {
|
||||
is("Hello", v, "Extracted string should match");
|
||||
});
|
||||
|
||||
var params = new URLSearchParams();
|
||||
params.append("item", "Geckos");
|
||||
params.append("feature", "stickyfeet");
|
||||
params.append("quantity", "700");
|
||||
var req3 = new Request("", { body: params });
|
||||
var p3 = req3.text().then(function(v) {
|
||||
var extracted = new URLSearchParams(v);
|
||||
is(extracted.get("item"), "Geckos", "Param should match");
|
||||
is(extracted.get("feature"), "stickyfeet", "Param should match");
|
||||
is(extracted.get("quantity"), "700", "Param should match");
|
||||
});
|
||||
|
||||
return Promise.all([p1, p2, p2b, p3]);
|
||||
}
|
||||
|
||||
function testBodyExtraction() {
|
||||
var text = "κόσμε";
|
||||
var newReq = function() { return new Request("", { body: text }); }
|
||||
return newReq().text().then(function(v) {
|
||||
ok(typeof v === "string", "Should resolve to string");
|
||||
is(text, v, "Extracted string should match");
|
||||
}).then(function() {
|
||||
return newReq().blob().then(function(v) {
|
||||
ok(v instanceof Blob, "Should resolve to Blob");
|
||||
var fs = new FileReaderSync();
|
||||
is(fs.readAsText(v), text, "Decoded Blob should match original");
|
||||
});
|
||||
}).then(function() {
|
||||
return newReq().json().then(function(v) {
|
||||
ok(false, "Invalid json should reject");
|
||||
}, function(e) {
|
||||
ok(true, "Invalid json should reject");
|
||||
})
|
||||
}).then(function() {
|
||||
return newReq().arrayBuffer().then(function(v) {
|
||||
ok(v instanceof ArrayBuffer, "Should resolve to ArrayBuffer");
|
||||
var dec = new TextDecoder();
|
||||
is(dec.decode(new Uint8Array(v)), text, "UTF-8 decoded ArrayBuffer should match original");
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
onmessage = function() {
|
||||
var done = function() { postMessage({ type: 'finish' }) }
|
||||
|
||||
testDefaultCtor();
|
||||
testClone();
|
||||
testSimpleUrlParse();
|
||||
testUrlFragment();
|
||||
testMethod();
|
||||
|
||||
Promise.resolve()
|
||||
.then(testBodyCreation)
|
||||
.then(testBodyUsed)
|
||||
.then(testBodyExtraction)
|
||||
.then(testUsedRequest)
|
||||
// Put more promise based tests here.
|
||||
.then(done)
|
||||
.catch(function(e) {
|
||||
ok(false, "Some Request tests failed " + e);
|
||||
done();
|
||||
})
|
||||
}
|
Загрузка…
Ссылка в новой задаче