content/base/src/nsXMLHttpRequest.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "nsXMLHttpRequest.h"
michael@0 8
michael@0 9 #include "mozilla/ArrayUtils.h"
michael@0 10 #include "mozilla/dom/XMLHttpRequestUploadBinding.h"
michael@0 11 #include "mozilla/EventDispatcher.h"
michael@0 12 #include "mozilla/EventListenerManager.h"
michael@0 13 #include "mozilla/MemoryReporting.h"
michael@0 14 #include "nsDOMBlobBuilder.h"
michael@0 15 #include "nsIDOMDocument.h"
michael@0 16 #include "nsIDOMProgressEvent.h"
michael@0 17 #include "nsIJARChannel.h"
michael@0 18 #include "nsLayoutCID.h"
michael@0 19 #include "nsReadableUtils.h"
michael@0 20
michael@0 21 #include "nsIURI.h"
michael@0 22 #include "nsILoadGroup.h"
michael@0 23 #include "nsNetUtil.h"
michael@0 24 #include "nsStreamUtils.h"
michael@0 25 #include "nsThreadUtils.h"
michael@0 26 #include "nsIUploadChannel.h"
michael@0 27 #include "nsIUploadChannel2.h"
michael@0 28 #include "nsIDOMSerializer.h"
michael@0 29 #include "nsXPCOM.h"
michael@0 30 #include "nsIDOMEventListener.h"
michael@0 31 #include "nsIScriptSecurityManager.h"
michael@0 32 #include "nsIDOMWindow.h"
michael@0 33 #include "nsIVariant.h"
michael@0 34 #include "nsVariant.h"
michael@0 35 #include "nsIScriptError.h"
michael@0 36 #include "nsIStreamConverterService.h"
michael@0 37 #include "nsICachingChannel.h"
michael@0 38 #include "nsContentUtils.h"
michael@0 39 #include "nsCxPusher.h"
michael@0 40 #include "nsCycleCollectionParticipant.h"
michael@0 41 #include "nsIContentPolicy.h"
michael@0 42 #include "nsContentPolicyUtils.h"
michael@0 43 #include "nsError.h"
michael@0 44 #include "nsCrossSiteListenerProxy.h"
michael@0 45 #include "nsIHTMLDocument.h"
michael@0 46 #include "nsIStorageStream.h"
michael@0 47 #include "nsIPromptFactory.h"
michael@0 48 #include "nsIWindowWatcher.h"
michael@0 49 #include "nsIConsoleService.h"
michael@0 50 #include "nsIChannelPolicy.h"
michael@0 51 #include "nsChannelPolicy.h"
michael@0 52 #include "nsIContentSecurityPolicy.h"
michael@0 53 #include "nsAsyncRedirectVerifyHelper.h"
michael@0 54 #include "nsStringBuffer.h"
michael@0 55 #include "nsDOMFile.h"
michael@0 56 #include "nsIFileChannel.h"
michael@0 57 #include "mozilla/Telemetry.h"
michael@0 58 #include "jsfriendapi.h"
michael@0 59 #include "GeckoProfiler.h"
michael@0 60 #include "mozilla/dom/EncodingUtils.h"
michael@0 61 #include "nsIUnicodeDecoder.h"
michael@0 62 #include "mozilla/dom/XMLHttpRequestBinding.h"
michael@0 63 #include "mozilla/Attributes.h"
michael@0 64 #include "nsIPermissionManager.h"
michael@0 65 #include "nsMimeTypes.h"
michael@0 66 #include "nsIHttpChannelInternal.h"
michael@0 67 #include "nsCharSeparatedTokenizer.h"
michael@0 68 #include "nsFormData.h"
michael@0 69 #include "nsStreamListenerWrapper.h"
michael@0 70 #include "xpcjsid.h"
michael@0 71 #include "nsITimedChannel.h"
michael@0 72
michael@0 73 #include "nsWrapperCacheInlines.h"
michael@0 74
michael@0 75 using namespace mozilla;
michael@0 76 using namespace mozilla::dom;
michael@0 77
michael@0 78 // Maximum size that we'll grow an ArrayBuffer instead of doubling,
michael@0 79 // once doubling reaches this threshold
michael@0 80 #define XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH (32*1024*1024)
michael@0 81 // start at 32k to avoid lots of doubling right at the start
michael@0 82 #define XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE (32*1024)
michael@0 83 // the maximum Content-Length that we'll preallocate. 1GB. Must fit
michael@0 84 // in an int32_t!
michael@0 85 #define XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE (1*1024*1024*1024LL)
michael@0 86
michael@0 87 #define LOAD_STR "load"
michael@0 88 #define ERROR_STR "error"
michael@0 89 #define ABORT_STR "abort"
michael@0 90 #define TIMEOUT_STR "timeout"
michael@0 91 #define LOADSTART_STR "loadstart"
michael@0 92 #define PROGRESS_STR "progress"
michael@0 93 #define READYSTATE_STR "readystatechange"
michael@0 94 #define LOADEND_STR "loadend"
michael@0 95
michael@0 96 // CIDs
michael@0 97
michael@0 98 // State
michael@0 99 #define XML_HTTP_REQUEST_UNSENT (1 << 0) // 0 UNSENT
michael@0 100 #define XML_HTTP_REQUEST_OPENED (1 << 1) // 1 OPENED
michael@0 101 #define XML_HTTP_REQUEST_HEADERS_RECEIVED (1 << 2) // 2 HEADERS_RECEIVED
michael@0 102 #define XML_HTTP_REQUEST_LOADING (1 << 3) // 3 LOADING
michael@0 103 #define XML_HTTP_REQUEST_DONE (1 << 4) // 4 DONE
michael@0 104 #define XML_HTTP_REQUEST_SENT (1 << 5) // Internal, OPENED in IE and external view
michael@0 105 // The above states are mutually exclusive, change with ChangeState() only.
michael@0 106 // The states below can be combined.
michael@0 107 #define XML_HTTP_REQUEST_ABORTED (1 << 7) // Internal
michael@0 108 #define XML_HTTP_REQUEST_ASYNC (1 << 8) // Internal
michael@0 109 #define XML_HTTP_REQUEST_PARSEBODY (1 << 9) // Internal
michael@0 110 #define XML_HTTP_REQUEST_SYNCLOOPING (1 << 10) // Internal
michael@0 111 #define XML_HTTP_REQUEST_BACKGROUND (1 << 13) // Internal
michael@0 112 #define XML_HTTP_REQUEST_USE_XSITE_AC (1 << 14) // Internal
michael@0 113 #define XML_HTTP_REQUEST_NEED_AC_PREFLIGHT (1 << 15) // Internal
michael@0 114 #define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 16) // Internal
michael@0 115 #define XML_HTTP_REQUEST_TIMED_OUT (1 << 17) // Internal
michael@0 116 #define XML_HTTP_REQUEST_DELETED (1 << 18) // Internal
michael@0 117
michael@0 118 #define XML_HTTP_REQUEST_LOADSTATES \
michael@0 119 (XML_HTTP_REQUEST_UNSENT | \
michael@0 120 XML_HTTP_REQUEST_OPENED | \
michael@0 121 XML_HTTP_REQUEST_HEADERS_RECEIVED | \
michael@0 122 XML_HTTP_REQUEST_LOADING | \
michael@0 123 XML_HTTP_REQUEST_DONE | \
michael@0 124 XML_HTTP_REQUEST_SENT)
michael@0 125
michael@0 126 #define NS_BADCERTHANDLER_CONTRACTID \
michael@0 127 "@mozilla.org/content/xmlhttprequest-bad-cert-handler;1"
michael@0 128
michael@0 129 #define NS_PROGRESS_EVENT_INTERVAL 50
michael@0 130
michael@0 131 #define IMPL_CSTRING_GETTER(_name) \
michael@0 132 NS_IMETHODIMP \
michael@0 133 nsXMLHttpRequest::_name(nsACString& aOut) \
michael@0 134 { \
michael@0 135 nsCString tmp; \
michael@0 136 _name(tmp); \
michael@0 137 aOut = tmp; \
michael@0 138 return NS_OK; \
michael@0 139 }
michael@0 140
michael@0 141 NS_IMPL_ISUPPORTS(nsXHRParseEndListener, nsIDOMEventListener)
michael@0 142
michael@0 143 class nsResumeTimeoutsEvent : public nsRunnable
michael@0 144 {
michael@0 145 public:
michael@0 146 nsResumeTimeoutsEvent(nsPIDOMWindow* aWindow) : mWindow(aWindow) {}
michael@0 147
michael@0 148 NS_IMETHOD Run()
michael@0 149 {
michael@0 150 mWindow->ResumeTimeouts(false);
michael@0 151 return NS_OK;
michael@0 152 }
michael@0 153
michael@0 154 private:
michael@0 155 nsCOMPtr<nsPIDOMWindow> mWindow;
michael@0 156 };
michael@0 157
michael@0 158
michael@0 159 // This helper function adds the given load flags to the request's existing
michael@0 160 // load flags.
michael@0 161 static void AddLoadFlags(nsIRequest *request, nsLoadFlags newFlags)
michael@0 162 {
michael@0 163 nsLoadFlags flags;
michael@0 164 request->GetLoadFlags(&flags);
michael@0 165 flags |= newFlags;
michael@0 166 request->SetLoadFlags(flags);
michael@0 167 }
michael@0 168
michael@0 169 //-----------------------------------------------------------------------------
michael@0 170 // XMLHttpRequestAuthPrompt
michael@0 171 //-----------------------------------------------------------------------------
michael@0 172
michael@0 173 class XMLHttpRequestAuthPrompt : public nsIAuthPrompt
michael@0 174 {
michael@0 175 public:
michael@0 176 NS_DECL_ISUPPORTS
michael@0 177 NS_DECL_NSIAUTHPROMPT
michael@0 178
michael@0 179 XMLHttpRequestAuthPrompt();
michael@0 180 virtual ~XMLHttpRequestAuthPrompt();
michael@0 181 };
michael@0 182
michael@0 183 NS_IMPL_ISUPPORTS(XMLHttpRequestAuthPrompt, nsIAuthPrompt)
michael@0 184
michael@0 185 XMLHttpRequestAuthPrompt::XMLHttpRequestAuthPrompt()
michael@0 186 {
michael@0 187 MOZ_COUNT_CTOR(XMLHttpRequestAuthPrompt);
michael@0 188 }
michael@0 189
michael@0 190 XMLHttpRequestAuthPrompt::~XMLHttpRequestAuthPrompt()
michael@0 191 {
michael@0 192 MOZ_COUNT_DTOR(XMLHttpRequestAuthPrompt);
michael@0 193 }
michael@0 194
michael@0 195 NS_IMETHODIMP
michael@0 196 XMLHttpRequestAuthPrompt::Prompt(const char16_t* aDialogTitle,
michael@0 197 const char16_t* aText,
michael@0 198 const char16_t* aPasswordRealm,
michael@0 199 uint32_t aSavePassword,
michael@0 200 const char16_t* aDefaultText,
michael@0 201 char16_t** aResult,
michael@0 202 bool* aRetval)
michael@0 203 {
michael@0 204 *aRetval = false;
michael@0 205 return NS_OK;
michael@0 206 }
michael@0 207
michael@0 208 NS_IMETHODIMP
michael@0 209 XMLHttpRequestAuthPrompt::PromptUsernameAndPassword(const char16_t* aDialogTitle,
michael@0 210 const char16_t* aDialogText,
michael@0 211 const char16_t* aPasswordRealm,
michael@0 212 uint32_t aSavePassword,
michael@0 213 char16_t** aUser,
michael@0 214 char16_t** aPwd,
michael@0 215 bool* aRetval)
michael@0 216 {
michael@0 217 *aRetval = false;
michael@0 218 return NS_OK;
michael@0 219 }
michael@0 220
michael@0 221 NS_IMETHODIMP
michael@0 222 XMLHttpRequestAuthPrompt::PromptPassword(const char16_t* aDialogTitle,
michael@0 223 const char16_t* aText,
michael@0 224 const char16_t* aPasswordRealm,
michael@0 225 uint32_t aSavePassword,
michael@0 226 char16_t** aPwd,
michael@0 227 bool* aRetval)
michael@0 228 {
michael@0 229 *aRetval = false;
michael@0 230 return NS_OK;
michael@0 231 }
michael@0 232
michael@0 233 /////////////////////////////////////////////
michael@0 234
michael@0 235 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXHREventTarget)
michael@0 236
michael@0 237 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXHREventTarget,
michael@0 238 DOMEventTargetHelper)
michael@0 239 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 240
michael@0 241 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXHREventTarget,
michael@0 242 DOMEventTargetHelper)
michael@0 243 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 244
michael@0 245 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXHREventTarget)
michael@0 246 NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequestEventTarget)
michael@0 247 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
michael@0 248
michael@0 249 NS_IMPL_ADDREF_INHERITED(nsXHREventTarget, DOMEventTargetHelper)
michael@0 250 NS_IMPL_RELEASE_INHERITED(nsXHREventTarget, DOMEventTargetHelper)
michael@0 251
michael@0 252 void
michael@0 253 nsXHREventTarget::DisconnectFromOwner()
michael@0 254 {
michael@0 255 DOMEventTargetHelper::DisconnectFromOwner();
michael@0 256 }
michael@0 257
michael@0 258 /////////////////////////////////////////////
michael@0 259
michael@0 260 NS_INTERFACE_MAP_BEGIN(nsXMLHttpRequestUpload)
michael@0 261 NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequestUpload)
michael@0 262 NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget)
michael@0 263
michael@0 264 NS_IMPL_ADDREF_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
michael@0 265 NS_IMPL_RELEASE_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
michael@0 266
michael@0 267 /* virtual */ JSObject*
michael@0 268 nsXMLHttpRequestUpload::WrapObject(JSContext* aCx)
michael@0 269 {
michael@0 270 return XMLHttpRequestUploadBinding::Wrap(aCx, this);
michael@0 271 }
michael@0 272
michael@0 273 /////////////////////////////////////////////
michael@0 274 //
michael@0 275 //
michael@0 276 /////////////////////////////////////////////
michael@0 277
michael@0 278 bool
michael@0 279 nsXMLHttpRequest::sDontWarnAboutSyncXHR = false;
michael@0 280
michael@0 281 nsXMLHttpRequest::nsXMLHttpRequest()
michael@0 282 : mResponseBodyDecodedPos(0),
michael@0 283 mResponseType(XML_HTTP_RESPONSE_TYPE_DEFAULT),
michael@0 284 mRequestObserver(nullptr), mState(XML_HTTP_REQUEST_UNSENT),
michael@0 285 mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
michael@0 286 mProgressSinceLastProgressEvent(false),
michael@0 287 mRequestSentTime(0), mTimeoutMilliseconds(0),
michael@0 288 mErrorLoad(false), mWaitingForOnStopRequest(false),
michael@0 289 mProgressTimerIsActive(false),
michael@0 290 mIsHtml(false),
michael@0 291 mWarnAboutSyncHtml(false),
michael@0 292 mLoadLengthComputable(false), mLoadTotal(0),
michael@0 293 mIsSystem(false),
michael@0 294 mIsAnon(false),
michael@0 295 mFirstStartRequestSeen(false),
michael@0 296 mInLoadProgressEvent(false),
michael@0 297 mResultJSON(JSVAL_VOID),
michael@0 298 mResultArrayBuffer(nullptr),
michael@0 299 mXPCOMifier(nullptr)
michael@0 300 {
michael@0 301 SetIsDOMBinding();
michael@0 302 #ifdef DEBUG
michael@0 303 StaticAssertions();
michael@0 304 #endif
michael@0 305 }
michael@0 306
michael@0 307 nsXMLHttpRequest::~nsXMLHttpRequest()
michael@0 308 {
michael@0 309 mState |= XML_HTTP_REQUEST_DELETED;
michael@0 310
michael@0 311 if (mState & (XML_HTTP_REQUEST_SENT |
michael@0 312 XML_HTTP_REQUEST_LOADING)) {
michael@0 313 Abort();
michael@0 314 }
michael@0 315
michael@0 316 NS_ABORT_IF_FALSE(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
michael@0 317 mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
michael@0 318
michael@0 319 mResultJSON = JSVAL_VOID;
michael@0 320 mResultArrayBuffer = nullptr;
michael@0 321 mozilla::DropJSObjects(this);
michael@0 322 }
michael@0 323
michael@0 324 void
michael@0 325 nsXMLHttpRequest::RootJSResultObjects()
michael@0 326 {
michael@0 327 mozilla::HoldJSObjects(this);
michael@0 328 }
michael@0 329
michael@0 330 /**
michael@0 331 * This Init method is called from the factory constructor.
michael@0 332 */
michael@0 333 nsresult
michael@0 334 nsXMLHttpRequest::Init()
michael@0 335 {
michael@0 336 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
michael@0 337 nsCOMPtr<nsIPrincipal> subjectPrincipal;
michael@0 338 if (secMan) {
michael@0 339 secMan->GetSystemPrincipal(getter_AddRefs(subjectPrincipal));
michael@0 340 }
michael@0 341 NS_ENSURE_STATE(subjectPrincipal);
michael@0 342
michael@0 343 // Instead of grabbing some random global from the context stack,
michael@0 344 // let's use the default one (junk scope) for now.
michael@0 345 // We should move away from this Init...
michael@0 346 nsCOMPtr<nsIGlobalObject> global = xpc::GetJunkScopeGlobal();
michael@0 347 NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
michael@0 348 Construct(subjectPrincipal, global);
michael@0 349 return NS_OK;
michael@0 350 }
michael@0 351
michael@0 352 /**
michael@0 353 * This Init method should only be called by C++ consumers.
michael@0 354 */
michael@0 355 NS_IMETHODIMP
michael@0 356 nsXMLHttpRequest::Init(nsIPrincipal* aPrincipal,
michael@0 357 nsIScriptContext* aScriptContext,
michael@0 358 nsIGlobalObject* aGlobalObject,
michael@0 359 nsIURI* aBaseURI)
michael@0 360 {
michael@0 361 NS_ENSURE_ARG_POINTER(aPrincipal);
michael@0 362
michael@0 363 if (nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aGlobalObject)) {
michael@0 364 if (win->IsOuterWindow()) {
michael@0 365 // Must be bound to inner window, innerize if necessary.
michael@0 366 nsCOMPtr<nsIGlobalObject> inner = do_QueryInterface(
michael@0 367 win->GetCurrentInnerWindow());
michael@0 368 aGlobalObject = inner.get();
michael@0 369 }
michael@0 370 }
michael@0 371
michael@0 372 Construct(aPrincipal, aGlobalObject, aBaseURI);
michael@0 373 return NS_OK;
michael@0 374 }
michael@0 375
michael@0 376 void
michael@0 377 nsXMLHttpRequest::InitParameters(bool aAnon, bool aSystem)
michael@0 378 {
michael@0 379 if (!aAnon && !aSystem) {
michael@0 380 return;
michael@0 381 }
michael@0 382
michael@0 383 // Check for permissions.
michael@0 384 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetOwner());
michael@0 385 if (!window || !window->GetDocShell()) {
michael@0 386 return;
michael@0 387 }
michael@0 388
michael@0 389 // Chrome is always allowed access, so do the permission check only
michael@0 390 // for non-chrome pages.
michael@0 391 if (!IsSystemXHR() && aSystem) {
michael@0 392 nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
michael@0 393 if (!doc) {
michael@0 394 return;
michael@0 395 }
michael@0 396
michael@0 397 nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
michael@0 398 nsCOMPtr<nsIPermissionManager> permMgr =
michael@0 399 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
michael@0 400 if (!permMgr)
michael@0 401 return;
michael@0 402
michael@0 403 uint32_t permission;
michael@0 404 nsresult rv =
michael@0 405 permMgr->TestPermissionFromPrincipal(principal, "systemXHR", &permission);
michael@0 406 if (NS_FAILED(rv) || permission != nsIPermissionManager::ALLOW_ACTION) {
michael@0 407 return;
michael@0 408 }
michael@0 409 }
michael@0 410
michael@0 411 SetParameters(aAnon, aSystem);
michael@0 412 }
michael@0 413
michael@0 414 void
michael@0 415 nsXMLHttpRequest::ResetResponse()
michael@0 416 {
michael@0 417 mResponseXML = nullptr;
michael@0 418 mResponseBody.Truncate();
michael@0 419 mResponseText.Truncate();
michael@0 420 mResponseBlob = nullptr;
michael@0 421 mDOMFile = nullptr;
michael@0 422 mBlobSet = nullptr;
michael@0 423 mResultArrayBuffer = nullptr;
michael@0 424 mArrayBufferBuilder.reset();
michael@0 425 mResultJSON = JSVAL_VOID;
michael@0 426 mLoadTransferred = 0;
michael@0 427 mResponseBodyDecodedPos = 0;
michael@0 428 }
michael@0 429
michael@0 430 void
michael@0 431 nsXMLHttpRequest::SetRequestObserver(nsIRequestObserver* aObserver)
michael@0 432 {
michael@0 433 mRequestObserver = aObserver;
michael@0 434 }
michael@0 435
michael@0 436 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequest)
michael@0 437
michael@0 438 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXMLHttpRequest)
michael@0 439 bool isBlack = tmp->IsBlack();
michael@0 440 if (isBlack || tmp->mWaitingForOnStopRequest) {
michael@0 441 if (tmp->mListenerManager) {
michael@0 442 tmp->mListenerManager->MarkForCC();
michael@0 443 }
michael@0 444 if (!isBlack && tmp->PreservingWrapper()) {
michael@0 445 // This marks the wrapper black.
michael@0 446 tmp->GetWrapper();
michael@0 447 }
michael@0 448 return true;
michael@0 449 }
michael@0 450 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
michael@0 451
michael@0 452 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXMLHttpRequest)
michael@0 453 return tmp->
michael@0 454 IsBlackAndDoesNotNeedTracing(static_cast<DOMEventTargetHelper*>(tmp));
michael@0 455 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
michael@0 456
michael@0 457 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXMLHttpRequest)
michael@0 458 return tmp->IsBlack();
michael@0 459 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
michael@0 460
michael@0 461 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLHttpRequest,
michael@0 462 nsXHREventTarget)
michael@0 463 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
michael@0 464 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
michael@0 465 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseXML)
michael@0 466 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCORSPreflightChannel)
michael@0 467
michael@0 468 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXMLParserStreamListener)
michael@0 469
michael@0 470 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink)
michael@0 471 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressEventSink)
michael@0 472
michael@0 473 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
michael@0 474 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 475
michael@0 476 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXMLHttpRequest,
michael@0 477 nsXHREventTarget)
michael@0 478 tmp->mResultArrayBuffer = nullptr;
michael@0 479 tmp->mArrayBufferBuilder.reset();
michael@0 480 tmp->mResultJSON = JSVAL_VOID;
michael@0 481 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
michael@0 482 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
michael@0 483 NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML)
michael@0 484 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCORSPreflightChannel)
michael@0 485
michael@0 486 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener)
michael@0 487
michael@0 488 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannelEventSink)
michael@0 489 NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressEventSink)
michael@0 490
michael@0 491 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
michael@0 492 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 493
michael@0 494 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsXMLHttpRequest,
michael@0 495 nsXHREventTarget)
michael@0 496 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
michael@0 497 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResultJSON)
michael@0 498 NS_IMPL_CYCLE_COLLECTION_TRACE_END
michael@0 499
michael@0 500 // QueryInterface implementation for nsXMLHttpRequest
michael@0 501 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXMLHttpRequest)
michael@0 502 NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequest)
michael@0 503 NS_INTERFACE_MAP_ENTRY(nsIJSXMLHttpRequest)
michael@0 504 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
michael@0 505 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
michael@0 506 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
michael@0 507 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
michael@0 508 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
michael@0 509 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
michael@0 510 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
michael@0 511 NS_INTERFACE_MAP_ENTRY(nsISizeOfEventTarget)
michael@0 512 NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget)
michael@0 513
michael@0 514 NS_IMPL_ADDREF_INHERITED(nsXMLHttpRequest, nsXHREventTarget)
michael@0 515 NS_IMPL_RELEASE_INHERITED(nsXMLHttpRequest, nsXHREventTarget)
michael@0 516
michael@0 517 NS_IMPL_EVENT_HANDLER(nsXMLHttpRequest, readystatechange)
michael@0 518
michael@0 519 void
michael@0 520 nsXMLHttpRequest::DisconnectFromOwner()
michael@0 521 {
michael@0 522 nsXHREventTarget::DisconnectFromOwner();
michael@0 523 Abort();
michael@0 524 }
michael@0 525
michael@0 526 size_t
michael@0 527 nsXMLHttpRequest::SizeOfEventTargetIncludingThis(
michael@0 528 MallocSizeOf aMallocSizeOf) const
michael@0 529 {
michael@0 530 size_t n = aMallocSizeOf(this);
michael@0 531 n += mResponseBody.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
michael@0 532
michael@0 533 // Why is this safe? Because no-one else will report this string. The
michael@0 534 // other possible sharers of this string are as follows.
michael@0 535 //
michael@0 536 // - The JS engine could hold copies if the JS code holds references, e.g.
michael@0 537 // |var text = XHR.responseText|. However, those references will be via JS
michael@0 538 // external strings, for which the JS memory reporter does *not* report the
michael@0 539 // chars.
michael@0 540 //
michael@0 541 // - Binary extensions, but they're *extremely* unlikely to do any memory
michael@0 542 // reporting.
michael@0 543 //
michael@0 544 n += mResponseText.SizeOfExcludingThisEvenIfShared(aMallocSizeOf);
michael@0 545
michael@0 546 return n;
michael@0 547
michael@0 548 // Measurement of the following members may be added later if DMD finds it is
michael@0 549 // worthwhile:
michael@0 550 // - lots
michael@0 551 }
michael@0 552
michael@0 553 /* readonly attribute nsIChannel channel; */
michael@0 554 NS_IMETHODIMP
michael@0 555 nsXMLHttpRequest::GetChannel(nsIChannel **aChannel)
michael@0 556 {
michael@0 557 NS_ENSURE_ARG_POINTER(aChannel);
michael@0 558 NS_IF_ADDREF(*aChannel = mChannel);
michael@0 559
michael@0 560 return NS_OK;
michael@0 561 }
michael@0 562
michael@0 563 static void LogMessage(const char* aWarning, nsPIDOMWindow* aWindow)
michael@0 564 {
michael@0 565 nsCOMPtr<nsIDocument> doc;
michael@0 566 if (aWindow) {
michael@0 567 doc = aWindow->GetExtantDoc();
michael@0 568 }
michael@0 569 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
michael@0 570 NS_LITERAL_CSTRING("DOM"), doc,
michael@0 571 nsContentUtils::eDOM_PROPERTIES,
michael@0 572 aWarning);
michael@0 573 }
michael@0 574
michael@0 575 /* readonly attribute nsIDOMDocument responseXML; */
michael@0 576 NS_IMETHODIMP
michael@0 577 nsXMLHttpRequest::GetResponseXML(nsIDOMDocument **aResponseXML)
michael@0 578 {
michael@0 579 ErrorResult rv;
michael@0 580 nsIDocument* responseXML = GetResponseXML(rv);
michael@0 581 if (rv.Failed()) {
michael@0 582 return rv.ErrorCode();
michael@0 583 }
michael@0 584
michael@0 585 if (!responseXML) {
michael@0 586 *aResponseXML = nullptr;
michael@0 587 return NS_OK;
michael@0 588 }
michael@0 589
michael@0 590 return CallQueryInterface(responseXML, aResponseXML);
michael@0 591 }
michael@0 592
michael@0 593 nsIDocument*
michael@0 594 nsXMLHttpRequest::GetResponseXML(ErrorResult& aRv)
michael@0 595 {
michael@0 596 if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT &&
michael@0 597 mResponseType != XML_HTTP_RESPONSE_TYPE_DOCUMENT) {
michael@0 598 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 599 return nullptr;
michael@0 600 }
michael@0 601 if (mWarnAboutSyncHtml) {
michael@0 602 mWarnAboutSyncHtml = false;
michael@0 603 LogMessage("HTMLSyncXHRWarning", GetOwner());
michael@0 604 }
michael@0 605 return (XML_HTTP_REQUEST_DONE & mState) ? mResponseXML : nullptr;
michael@0 606 }
michael@0 607
michael@0 608 /*
michael@0 609 * This piece copied from XMLDocument, we try to get the charset
michael@0 610 * from HTTP headers.
michael@0 611 */
michael@0 612 nsresult
michael@0 613 nsXMLHttpRequest::DetectCharset()
michael@0 614 {
michael@0 615 mResponseCharset.Truncate();
michael@0 616 mDecoder = nullptr;
michael@0 617
michael@0 618 if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT &&
michael@0 619 mResponseType != XML_HTTP_RESPONSE_TYPE_TEXT &&
michael@0 620 mResponseType != XML_HTTP_RESPONSE_TYPE_JSON &&
michael@0 621 mResponseType != XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT) {
michael@0 622 return NS_OK;
michael@0 623 }
michael@0 624
michael@0 625 nsAutoCString charsetVal;
michael@0 626 bool ok = mChannel &&
michael@0 627 NS_SUCCEEDED(mChannel->GetContentCharset(charsetVal)) &&
michael@0 628 EncodingUtils::FindEncodingForLabel(charsetVal, mResponseCharset);
michael@0 629 if (!ok || mResponseCharset.IsEmpty()) {
michael@0 630 // MS documentation states UTF-8 is default for responseText
michael@0 631 mResponseCharset.AssignLiteral("UTF-8");
michael@0 632 }
michael@0 633
michael@0 634 if (mResponseType == XML_HTTP_RESPONSE_TYPE_JSON &&
michael@0 635 !mResponseCharset.EqualsLiteral("UTF-8")) {
michael@0 636 // The XHR spec says only UTF-8 is supported for responseType == "json"
michael@0 637 LogMessage("JSONCharsetWarning", GetOwner());
michael@0 638 mResponseCharset.AssignLiteral("UTF-8");
michael@0 639 }
michael@0 640
michael@0 641 mDecoder = EncodingUtils::DecoderForEncoding(mResponseCharset);
michael@0 642
michael@0 643 return NS_OK;
michael@0 644 }
michael@0 645
michael@0 646 nsresult
michael@0 647 nsXMLHttpRequest::AppendToResponseText(const char * aSrcBuffer,
michael@0 648 uint32_t aSrcBufferLen)
michael@0 649 {
michael@0 650 NS_ENSURE_STATE(mDecoder);
michael@0 651
michael@0 652 int32_t destBufferLen;
michael@0 653 nsresult rv = mDecoder->GetMaxLength(aSrcBuffer, aSrcBufferLen,
michael@0 654 &destBufferLen);
michael@0 655 NS_ENSURE_SUCCESS(rv, rv);
michael@0 656
michael@0 657 if (!mResponseText.SetCapacity(mResponseText.Length() + destBufferLen, fallible_t())) {
michael@0 658 return NS_ERROR_OUT_OF_MEMORY;
michael@0 659 }
michael@0 660
michael@0 661 char16_t* destBuffer = mResponseText.BeginWriting() + mResponseText.Length();
michael@0 662
michael@0 663 int32_t totalChars = mResponseText.Length();
michael@0 664
michael@0 665 // This code here is basically a copy of a similar thing in
michael@0 666 // nsScanner::Append(const char* aBuffer, uint32_t aLen).
michael@0 667 int32_t srclen = (int32_t)aSrcBufferLen;
michael@0 668 int32_t destlen = (int32_t)destBufferLen;
michael@0 669 rv = mDecoder->Convert(aSrcBuffer,
michael@0 670 &srclen,
michael@0 671 destBuffer,
michael@0 672 &destlen);
michael@0 673 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 674
michael@0 675 totalChars += destlen;
michael@0 676
michael@0 677 mResponseText.SetLength(totalChars);
michael@0 678
michael@0 679 return NS_OK;
michael@0 680 }
michael@0 681
michael@0 682 /* readonly attribute AString responseText; */
michael@0 683 NS_IMETHODIMP
michael@0 684 nsXMLHttpRequest::GetResponseText(nsAString& aResponseText)
michael@0 685 {
michael@0 686 ErrorResult rv;
michael@0 687 nsString responseText;
michael@0 688 GetResponseText(responseText, rv);
michael@0 689 aResponseText = responseText;
michael@0 690 return rv.ErrorCode();
michael@0 691 }
michael@0 692
michael@0 693 void
michael@0 694 nsXMLHttpRequest::GetResponseText(nsString& aResponseText, ErrorResult& aRv)
michael@0 695 {
michael@0 696 aResponseText.Truncate();
michael@0 697
michael@0 698 if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT &&
michael@0 699 mResponseType != XML_HTTP_RESPONSE_TYPE_TEXT &&
michael@0 700 mResponseType != XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT) {
michael@0 701 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 702 return;
michael@0 703 }
michael@0 704
michael@0 705 if (mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT &&
michael@0 706 !mInLoadProgressEvent) {
michael@0 707 aResponseText.SetIsVoid(true);
michael@0 708 return;
michael@0 709 }
michael@0 710
michael@0 711 if (!(mState & (XML_HTTP_REQUEST_DONE | XML_HTTP_REQUEST_LOADING))) {
michael@0 712 return;
michael@0 713 }
michael@0 714
michael@0 715 // We only decode text lazily if we're also parsing to a doc.
michael@0 716 // Also, if we've decoded all current data already, then no need to decode
michael@0 717 // more.
michael@0 718 if (!mResponseXML ||
michael@0 719 mResponseBodyDecodedPos == mResponseBody.Length()) {
michael@0 720 aResponseText = mResponseText;
michael@0 721 return;
michael@0 722 }
michael@0 723
michael@0 724 if (mResponseCharset != mResponseXML->GetDocumentCharacterSet()) {
michael@0 725 mResponseCharset = mResponseXML->GetDocumentCharacterSet();
michael@0 726 mResponseText.Truncate();
michael@0 727 mResponseBodyDecodedPos = 0;
michael@0 728 mDecoder = EncodingUtils::DecoderForEncoding(mResponseCharset);
michael@0 729 }
michael@0 730
michael@0 731 NS_ASSERTION(mResponseBodyDecodedPos < mResponseBody.Length(),
michael@0 732 "Unexpected mResponseBodyDecodedPos");
michael@0 733 aRv = AppendToResponseText(mResponseBody.get() + mResponseBodyDecodedPos,
michael@0 734 mResponseBody.Length() - mResponseBodyDecodedPos);
michael@0 735 if (aRv.Failed()) {
michael@0 736 return;
michael@0 737 }
michael@0 738
michael@0 739 mResponseBodyDecodedPos = mResponseBody.Length();
michael@0 740
michael@0 741 if (mState & XML_HTTP_REQUEST_DONE) {
michael@0 742 // Free memory buffer which we no longer need
michael@0 743 mResponseBody.Truncate();
michael@0 744 mResponseBodyDecodedPos = 0;
michael@0 745 }
michael@0 746
michael@0 747 aResponseText = mResponseText;
michael@0 748 }
michael@0 749
michael@0 750 nsresult
michael@0 751 nsXMLHttpRequest::CreateResponseParsedJSON(JSContext* aCx)
michael@0 752 {
michael@0 753 if (!aCx) {
michael@0 754 return NS_ERROR_FAILURE;
michael@0 755 }
michael@0 756 RootJSResultObjects();
michael@0 757
michael@0 758 // The Unicode converter has already zapped the BOM if there was one
michael@0 759 JS::Rooted<JS::Value> value(aCx);
michael@0 760 if (!JS_ParseJSON(aCx,
michael@0 761 static_cast<const jschar*>(mResponseText.get()), mResponseText.Length(),
michael@0 762 &value)) {
michael@0 763 return NS_ERROR_FAILURE;
michael@0 764 }
michael@0 765
michael@0 766 mResultJSON = value;
michael@0 767 return NS_OK;
michael@0 768 }
michael@0 769
michael@0 770 void
michael@0 771 nsXMLHttpRequest::CreatePartialBlob()
michael@0 772 {
michael@0 773 if (mDOMFile) {
michael@0 774 if (mLoadTotal == mLoadTransferred) {
michael@0 775 mResponseBlob = mDOMFile;
michael@0 776 } else {
michael@0 777 mResponseBlob =
michael@0 778 mDOMFile->CreateSlice(0, mLoadTransferred, EmptyString());
michael@0 779 }
michael@0 780 return;
michael@0 781 }
michael@0 782
michael@0 783 // mBlobSet can be null if the request has been canceled
michael@0 784 if (!mBlobSet) {
michael@0 785 return;
michael@0 786 }
michael@0 787
michael@0 788 nsAutoCString contentType;
michael@0 789 if (mLoadTotal == mLoadTransferred) {
michael@0 790 mChannel->GetContentType(contentType);
michael@0 791 }
michael@0 792
michael@0 793 mResponseBlob = mBlobSet->GetBlobInternal(contentType);
michael@0 794 }
michael@0 795
michael@0 796 /* attribute AString responseType; */
michael@0 797 NS_IMETHODIMP nsXMLHttpRequest::GetResponseType(nsAString& aResponseType)
michael@0 798 {
michael@0 799 switch (mResponseType) {
michael@0 800 case XML_HTTP_RESPONSE_TYPE_DEFAULT:
michael@0 801 aResponseType.Truncate();
michael@0 802 break;
michael@0 803 case XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER:
michael@0 804 aResponseType.AssignLiteral("arraybuffer");
michael@0 805 break;
michael@0 806 case XML_HTTP_RESPONSE_TYPE_BLOB:
michael@0 807 aResponseType.AssignLiteral("blob");
michael@0 808 break;
michael@0 809 case XML_HTTP_RESPONSE_TYPE_DOCUMENT:
michael@0 810 aResponseType.AssignLiteral("document");
michael@0 811 break;
michael@0 812 case XML_HTTP_RESPONSE_TYPE_TEXT:
michael@0 813 aResponseType.AssignLiteral("text");
michael@0 814 break;
michael@0 815 case XML_HTTP_RESPONSE_TYPE_JSON:
michael@0 816 aResponseType.AssignLiteral("json");
michael@0 817 break;
michael@0 818 case XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT:
michael@0 819 aResponseType.AssignLiteral("moz-chunked-text");
michael@0 820 break;
michael@0 821 case XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER:
michael@0 822 aResponseType.AssignLiteral("moz-chunked-arraybuffer");
michael@0 823 break;
michael@0 824 case XML_HTTP_RESPONSE_TYPE_MOZ_BLOB:
michael@0 825 aResponseType.AssignLiteral("moz-blob");
michael@0 826 break;
michael@0 827 default:
michael@0 828 NS_ERROR("Should not happen");
michael@0 829 }
michael@0 830
michael@0 831 return NS_OK;
michael@0 832 }
michael@0 833
michael@0 834 #ifdef DEBUG
michael@0 835 void
michael@0 836 nsXMLHttpRequest::StaticAssertions()
michael@0 837 {
michael@0 838 #define ASSERT_ENUM_EQUAL(_lc, _uc) \
michael@0 839 static_assert(\
michael@0 840 static_cast<int>(XMLHttpRequestResponseType::_lc) \
michael@0 841 == XML_HTTP_RESPONSE_TYPE_ ## _uc, \
michael@0 842 #_uc " should match")
michael@0 843
michael@0 844 ASSERT_ENUM_EQUAL(_empty, DEFAULT);
michael@0 845 ASSERT_ENUM_EQUAL(Arraybuffer, ARRAYBUFFER);
michael@0 846 ASSERT_ENUM_EQUAL(Blob, BLOB);
michael@0 847 ASSERT_ENUM_EQUAL(Document, DOCUMENT);
michael@0 848 ASSERT_ENUM_EQUAL(Json, JSON);
michael@0 849 ASSERT_ENUM_EQUAL(Text, TEXT);
michael@0 850 ASSERT_ENUM_EQUAL(Moz_chunked_text, CHUNKED_TEXT);
michael@0 851 ASSERT_ENUM_EQUAL(Moz_chunked_arraybuffer, CHUNKED_ARRAYBUFFER);
michael@0 852 ASSERT_ENUM_EQUAL(Moz_blob, MOZ_BLOB);
michael@0 853 #undef ASSERT_ENUM_EQUAL
michael@0 854 }
michael@0 855 #endif
michael@0 856
michael@0 857 /* attribute AString responseType; */
michael@0 858 NS_IMETHODIMP nsXMLHttpRequest::SetResponseType(const nsAString& aResponseType)
michael@0 859 {
michael@0 860 nsXMLHttpRequest::ResponseTypeEnum responseType;
michael@0 861 if (aResponseType.IsEmpty()) {
michael@0 862 responseType = XML_HTTP_RESPONSE_TYPE_DEFAULT;
michael@0 863 } else if (aResponseType.EqualsLiteral("arraybuffer")) {
michael@0 864 responseType = XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER;
michael@0 865 } else if (aResponseType.EqualsLiteral("blob")) {
michael@0 866 responseType = XML_HTTP_RESPONSE_TYPE_BLOB;
michael@0 867 } else if (aResponseType.EqualsLiteral("document")) {
michael@0 868 responseType = XML_HTTP_RESPONSE_TYPE_DOCUMENT;
michael@0 869 } else if (aResponseType.EqualsLiteral("text")) {
michael@0 870 responseType = XML_HTTP_RESPONSE_TYPE_TEXT;
michael@0 871 } else if (aResponseType.EqualsLiteral("json")) {
michael@0 872 responseType = XML_HTTP_RESPONSE_TYPE_JSON;
michael@0 873 } else if (aResponseType.EqualsLiteral("moz-chunked-text")) {
michael@0 874 responseType = XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT;
michael@0 875 } else if (aResponseType.EqualsLiteral("moz-chunked-arraybuffer")) {
michael@0 876 responseType = XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER;
michael@0 877 } else if (aResponseType.EqualsLiteral("moz-blob")) {
michael@0 878 responseType = XML_HTTP_RESPONSE_TYPE_MOZ_BLOB;
michael@0 879 } else {
michael@0 880 return NS_OK;
michael@0 881 }
michael@0 882
michael@0 883 ErrorResult rv;
michael@0 884 SetResponseType(responseType, rv);
michael@0 885 return rv.ErrorCode();
michael@0 886 }
michael@0 887
michael@0 888 void
michael@0 889 nsXMLHttpRequest::SetResponseType(XMLHttpRequestResponseType aType,
michael@0 890 ErrorResult& aRv)
michael@0 891 {
michael@0 892 SetResponseType(ResponseTypeEnum(static_cast<int>(aType)), aRv);
michael@0 893 }
michael@0 894
michael@0 895 void
michael@0 896 nsXMLHttpRequest::SetResponseType(nsXMLHttpRequest::ResponseTypeEnum aResponseType,
michael@0 897 ErrorResult& aRv)
michael@0 898 {
michael@0 899 // If the state is not OPENED or HEADERS_RECEIVED raise an
michael@0 900 // INVALID_STATE_ERR exception and terminate these steps.
michael@0 901 if (!(mState & (XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT |
michael@0 902 XML_HTTP_REQUEST_HEADERS_RECEIVED))) {
michael@0 903 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 904 return;
michael@0 905 }
michael@0 906
michael@0 907 // sync request is not allowed setting responseType in window context
michael@0 908 if (HasOrHasHadOwner() &&
michael@0 909 !(mState & (XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC))) {
michael@0 910 LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
michael@0 911 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
michael@0 912 return;
michael@0 913 }
michael@0 914
michael@0 915 if (!(mState & XML_HTTP_REQUEST_ASYNC) &&
michael@0 916 (aResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT ||
michael@0 917 aResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER)) {
michael@0 918 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 919 return;
michael@0 920 }
michael@0 921
michael@0 922 // Set the responseType attribute's value to the given value.
michael@0 923 mResponseType = aResponseType;
michael@0 924
michael@0 925 }
michael@0 926
michael@0 927 /* readonly attribute jsval response; */
michael@0 928 NS_IMETHODIMP
michael@0 929 nsXMLHttpRequest::GetResponse(JSContext *aCx, JS::MutableHandle<JS::Value> aResult)
michael@0 930 {
michael@0 931 ErrorResult rv;
michael@0 932 GetResponse(aCx, aResult, rv);
michael@0 933 return rv.ErrorCode();
michael@0 934 }
michael@0 935
michael@0 936 void
michael@0 937 nsXMLHttpRequest::GetResponse(JSContext* aCx,
michael@0 938 JS::MutableHandle<JS::Value> aResponse,
michael@0 939 ErrorResult& aRv)
michael@0 940 {
michael@0 941 switch (mResponseType) {
michael@0 942 case XML_HTTP_RESPONSE_TYPE_DEFAULT:
michael@0 943 case XML_HTTP_RESPONSE_TYPE_TEXT:
michael@0 944 case XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT:
michael@0 945 {
michael@0 946 nsString str;
michael@0 947 aRv = GetResponseText(str);
michael@0 948 if (aRv.Failed()) {
michael@0 949 return;
michael@0 950 }
michael@0 951 if (!xpc::StringToJsval(aCx, str, aResponse)) {
michael@0 952 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
michael@0 953 }
michael@0 954 return;
michael@0 955 }
michael@0 956
michael@0 957 case XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER:
michael@0 958 case XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER:
michael@0 959 {
michael@0 960 if (!(mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
michael@0 961 mState & XML_HTTP_REQUEST_DONE) &&
michael@0 962 !(mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER &&
michael@0 963 mInLoadProgressEvent)) {
michael@0 964 aResponse.setNull();
michael@0 965 return;
michael@0 966 }
michael@0 967
michael@0 968 if (!mResultArrayBuffer) {
michael@0 969 RootJSResultObjects();
michael@0 970
michael@0 971 mResultArrayBuffer = mArrayBufferBuilder.getArrayBuffer(aCx);
michael@0 972 if (!mResultArrayBuffer) {
michael@0 973 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
michael@0 974 return;
michael@0 975 }
michael@0 976 }
michael@0 977 JS::ExposeObjectToActiveJS(mResultArrayBuffer);
michael@0 978 aResponse.setObject(*mResultArrayBuffer);
michael@0 979 return;
michael@0 980 }
michael@0 981 case XML_HTTP_RESPONSE_TYPE_BLOB:
michael@0 982 case XML_HTTP_RESPONSE_TYPE_MOZ_BLOB:
michael@0 983 {
michael@0 984 if (!(mState & XML_HTTP_REQUEST_DONE)) {
michael@0 985 if (mResponseType != XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
michael@0 986 aResponse.setNull();
michael@0 987 return;
michael@0 988 }
michael@0 989
michael@0 990 if (!mResponseBlob) {
michael@0 991 CreatePartialBlob();
michael@0 992 }
michael@0 993 }
michael@0 994
michael@0 995 if (!mResponseBlob) {
michael@0 996 aResponse.setNull();
michael@0 997 return;
michael@0 998 }
michael@0 999
michael@0 1000 aRv = nsContentUtils::WrapNative(aCx, mResponseBlob, aResponse);
michael@0 1001 return;
michael@0 1002 }
michael@0 1003 case XML_HTTP_RESPONSE_TYPE_DOCUMENT:
michael@0 1004 {
michael@0 1005 if (!(mState & XML_HTTP_REQUEST_DONE) || !mResponseXML) {
michael@0 1006 aResponse.setNull();
michael@0 1007 return;
michael@0 1008 }
michael@0 1009
michael@0 1010 aRv = nsContentUtils::WrapNative(aCx, mResponseXML, aResponse);
michael@0 1011 return;
michael@0 1012 }
michael@0 1013 case XML_HTTP_RESPONSE_TYPE_JSON:
michael@0 1014 {
michael@0 1015 if (!(mState & XML_HTTP_REQUEST_DONE)) {
michael@0 1016 aResponse.setNull();
michael@0 1017 return;
michael@0 1018 }
michael@0 1019
michael@0 1020 if (mResultJSON.isUndefined()) {
michael@0 1021 aRv = CreateResponseParsedJSON(aCx);
michael@0 1022 mResponseText.Truncate();
michael@0 1023 if (aRv.Failed()) {
michael@0 1024 // Per spec, errors aren't propagated. null is returned instead.
michael@0 1025 aRv = NS_OK;
michael@0 1026 // It would be nice to log the error to the console. That's hard to
michael@0 1027 // do without calling window.onerror as a side effect, though.
michael@0 1028 JS_ClearPendingException(aCx);
michael@0 1029 mResultJSON.setNull();
michael@0 1030 }
michael@0 1031 }
michael@0 1032 JS::ExposeValueToActiveJS(mResultJSON);
michael@0 1033 aResponse.set(mResultJSON);
michael@0 1034 return;
michael@0 1035 }
michael@0 1036 default:
michael@0 1037 NS_ERROR("Should not happen");
michael@0 1038 }
michael@0 1039
michael@0 1040 aResponse.setNull();
michael@0 1041 }
michael@0 1042
michael@0 1043 /* readonly attribute unsigned long status; */
michael@0 1044 NS_IMETHODIMP
michael@0 1045 nsXMLHttpRequest::GetStatus(uint32_t *aStatus)
michael@0 1046 {
michael@0 1047 *aStatus = Status();
michael@0 1048 return NS_OK;
michael@0 1049 }
michael@0 1050
michael@0 1051 uint32_t
michael@0 1052 nsXMLHttpRequest::Status()
michael@0 1053 {
michael@0 1054 if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
michael@0 1055 // Make sure we don't leak status information from denied cross-site
michael@0 1056 // requests.
michael@0 1057 if (mChannel) {
michael@0 1058 nsresult status;
michael@0 1059 mChannel->GetStatus(&status);
michael@0 1060 if (NS_FAILED(status)) {
michael@0 1061 return 0;
michael@0 1062 }
michael@0 1063 }
michael@0 1064 }
michael@0 1065
michael@0 1066 uint16_t readyState;
michael@0 1067 GetReadyState(&readyState);
michael@0 1068 if (readyState == UNSENT || readyState == OPENED) {
michael@0 1069 return 0;
michael@0 1070 }
michael@0 1071
michael@0 1072 if (mErrorLoad) {
michael@0 1073 // Let's simulate the http protocol for jar/app requests:
michael@0 1074 nsCOMPtr<nsIJARChannel> jarChannel = GetCurrentJARChannel();
michael@0 1075 if (jarChannel) {
michael@0 1076 nsresult status;
michael@0 1077 mChannel->GetStatus(&status);
michael@0 1078
michael@0 1079 if (status == NS_ERROR_FILE_NOT_FOUND) {
michael@0 1080 return 404; // Not Found
michael@0 1081 } else {
michael@0 1082 return 500; // Internal Error
michael@0 1083 }
michael@0 1084 }
michael@0 1085
michael@0 1086 return 0;
michael@0 1087 }
michael@0 1088
michael@0 1089 nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
michael@0 1090 if (!httpChannel) {
michael@0 1091
michael@0 1092 // Let's simulate the http protocol for jar/app requests:
michael@0 1093 nsCOMPtr<nsIJARChannel> jarChannel = GetCurrentJARChannel();
michael@0 1094 if (jarChannel) {
michael@0 1095 return 200; // Ok
michael@0 1096 }
michael@0 1097
michael@0 1098 return 0;
michael@0 1099 }
michael@0 1100
michael@0 1101 uint32_t status;
michael@0 1102 nsresult rv = httpChannel->GetResponseStatus(&status);
michael@0 1103 if (NS_FAILED(rv)) {
michael@0 1104 status = 0;
michael@0 1105 }
michael@0 1106
michael@0 1107 return status;
michael@0 1108 }
michael@0 1109
michael@0 1110 IMPL_CSTRING_GETTER(GetStatusText)
michael@0 1111 void
michael@0 1112 nsXMLHttpRequest::GetStatusText(nsCString& aStatusText)
michael@0 1113 {
michael@0 1114 nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
michael@0 1115
michael@0 1116 aStatusText.Truncate();
michael@0 1117
michael@0 1118 if (!httpChannel) {
michael@0 1119 return;
michael@0 1120 }
michael@0 1121
michael@0 1122 if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
michael@0 1123 // Make sure we don't leak status information from denied cross-site
michael@0 1124 // requests.
michael@0 1125 if (mChannel) {
michael@0 1126 nsresult status;
michael@0 1127 mChannel->GetStatus(&status);
michael@0 1128 if (NS_FAILED(status)) {
michael@0 1129 return;
michael@0 1130 }
michael@0 1131 }
michael@0 1132 }
michael@0 1133
michael@0 1134 httpChannel->GetResponseStatusText(aStatusText);
michael@0 1135
michael@0 1136 }
michael@0 1137
michael@0 1138 void
michael@0 1139 nsXMLHttpRequest::CloseRequestWithError(const nsAString& aType,
michael@0 1140 const uint32_t aFlag)
michael@0 1141 {
michael@0 1142 if (mChannel) {
michael@0 1143 mChannel->Cancel(NS_BINDING_ABORTED);
michael@0 1144 }
michael@0 1145 if (mCORSPreflightChannel) {
michael@0 1146 mCORSPreflightChannel->Cancel(NS_BINDING_ABORTED);
michael@0 1147 }
michael@0 1148 if (mTimeoutTimer) {
michael@0 1149 mTimeoutTimer->Cancel();
michael@0 1150 }
michael@0 1151 uint32_t responseLength = mResponseBody.Length();
michael@0 1152 ResetResponse();
michael@0 1153 mState |= aFlag;
michael@0 1154
michael@0 1155 // If we're in the destructor, don't risk dispatching an event.
michael@0 1156 if (mState & XML_HTTP_REQUEST_DELETED) {
michael@0 1157 mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
michael@0 1158 return;
michael@0 1159 }
michael@0 1160
michael@0 1161 if (!(mState & (XML_HTTP_REQUEST_UNSENT |
michael@0 1162 XML_HTTP_REQUEST_OPENED |
michael@0 1163 XML_HTTP_REQUEST_DONE))) {
michael@0 1164 ChangeState(XML_HTTP_REQUEST_DONE, true);
michael@0 1165
michael@0 1166 if (!(mState & XML_HTTP_REQUEST_SYNCLOOPING)) {
michael@0 1167 DispatchProgressEvent(this, aType, mLoadLengthComputable, responseLength,
michael@0 1168 mLoadTotal);
michael@0 1169 if (mUpload && !mUploadComplete) {
michael@0 1170 mUploadComplete = true;
michael@0 1171 DispatchProgressEvent(mUpload, aType, true, mUploadTransferred,
michael@0 1172 mUploadTotal);
michael@0 1173 }
michael@0 1174 }
michael@0 1175 }
michael@0 1176
michael@0 1177 // The ChangeState call above calls onreadystatechange handlers which
michael@0 1178 // if they load a new url will cause nsXMLHttpRequest::Open to clear
michael@0 1179 // the abort state bit. If this occurs we're not uninitialized (bug 361773).
michael@0 1180 if (mState & XML_HTTP_REQUEST_ABORTED) {
michael@0 1181 ChangeState(XML_HTTP_REQUEST_UNSENT, false); // IE seems to do it
michael@0 1182 }
michael@0 1183
michael@0 1184 mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
michael@0 1185 }
michael@0 1186
michael@0 1187 /* void abort (); */
michael@0 1188 void
michael@0 1189 nsXMLHttpRequest::Abort()
michael@0 1190 {
michael@0 1191 CloseRequestWithError(NS_LITERAL_STRING(ABORT_STR), XML_HTTP_REQUEST_ABORTED);
michael@0 1192 }
michael@0 1193
michael@0 1194 NS_IMETHODIMP
michael@0 1195 nsXMLHttpRequest::SlowAbort()
michael@0 1196 {
michael@0 1197 Abort();
michael@0 1198 return NS_OK;
michael@0 1199 }
michael@0 1200
michael@0 1201 /*Method that checks if it is safe to expose a header value to the client.
michael@0 1202 It is used to check what headers are exposed for CORS requests.*/
michael@0 1203 bool
michael@0 1204 nsXMLHttpRequest::IsSafeHeader(const nsACString& header, nsIHttpChannel* httpChannel)
michael@0 1205 {
michael@0 1206 // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
michael@0 1207 if (!IsSystemXHR() &&
michael@0 1208 (header.LowerCaseEqualsASCII("set-cookie") ||
michael@0 1209 header.LowerCaseEqualsASCII("set-cookie2"))) {
michael@0 1210 NS_WARNING("blocked access to response header");
michael@0 1211 return false;
michael@0 1212 }
michael@0 1213 // if this is not a CORS call all headers are safe
michael@0 1214 if (!(mState & XML_HTTP_REQUEST_USE_XSITE_AC)){
michael@0 1215 return true;
michael@0 1216 }
michael@0 1217 // Check for dangerous headers
michael@0 1218 // Make sure we don't leak header information from denied cross-site
michael@0 1219 // requests.
michael@0 1220 if (mChannel) {
michael@0 1221 nsresult status;
michael@0 1222 mChannel->GetStatus(&status);
michael@0 1223 if (NS_FAILED(status)) {
michael@0 1224 return false;
michael@0 1225 }
michael@0 1226 }
michael@0 1227 const char* kCrossOriginSafeHeaders[] = {
michael@0 1228 "cache-control", "content-language", "content-type", "expires",
michael@0 1229 "last-modified", "pragma"
michael@0 1230 };
michael@0 1231 for (uint32_t i = 0; i < ArrayLength(kCrossOriginSafeHeaders); ++i) {
michael@0 1232 if (header.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
michael@0 1233 return true;
michael@0 1234 }
michael@0 1235 }
michael@0 1236 nsAutoCString headerVal;
michael@0 1237 // The "Access-Control-Expose-Headers" header contains a comma separated
michael@0 1238 // list of method names.
michael@0 1239 httpChannel->
michael@0 1240 GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"),
michael@0 1241 headerVal);
michael@0 1242 nsCCharSeparatedTokenizer exposeTokens(headerVal, ',');
michael@0 1243 bool isSafe = false;
michael@0 1244 while (exposeTokens.hasMoreTokens()) {
michael@0 1245 const nsDependentCSubstring& token = exposeTokens.nextToken();
michael@0 1246 if (token.IsEmpty()) {
michael@0 1247 continue;
michael@0 1248 }
michael@0 1249 if (!IsValidHTTPToken(token)) {
michael@0 1250 return false;
michael@0 1251 }
michael@0 1252 if (header.Equals(token, nsCaseInsensitiveCStringComparator())) {
michael@0 1253 isSafe = true;
michael@0 1254 }
michael@0 1255 }
michael@0 1256 return isSafe;
michael@0 1257 }
michael@0 1258
michael@0 1259 /* ByteString getAllResponseHeaders(); */
michael@0 1260 IMPL_CSTRING_GETTER(GetAllResponseHeaders)
michael@0 1261 void
michael@0 1262 nsXMLHttpRequest::GetAllResponseHeaders(nsCString& aResponseHeaders)
michael@0 1263 {
michael@0 1264 aResponseHeaders.Truncate();
michael@0 1265
michael@0 1266 // If the state is UNSENT or OPENED,
michael@0 1267 // return the empty string and terminate these steps.
michael@0 1268 if (mState & (XML_HTTP_REQUEST_UNSENT |
michael@0 1269 XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
michael@0 1270 return;
michael@0 1271 }
michael@0 1272
michael@0 1273 if (nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel()) {
michael@0 1274 nsRefPtr<nsHeaderVisitor> visitor = new nsHeaderVisitor(this, httpChannel);
michael@0 1275 if (NS_SUCCEEDED(httpChannel->VisitResponseHeaders(visitor))) {
michael@0 1276 aResponseHeaders = visitor->Headers();
michael@0 1277 }
michael@0 1278 return;
michael@0 1279 }
michael@0 1280
michael@0 1281 if (!mChannel) {
michael@0 1282 return;
michael@0 1283 }
michael@0 1284
michael@0 1285 // Even non-http channels supply content type.
michael@0 1286 nsAutoCString value;
michael@0 1287 if (NS_SUCCEEDED(mChannel->GetContentType(value))) {
michael@0 1288 aResponseHeaders.AppendLiteral("Content-Type: ");
michael@0 1289 aResponseHeaders.Append(value);
michael@0 1290 if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) && !value.IsEmpty()) {
michael@0 1291 aResponseHeaders.AppendLiteral(";charset=");
michael@0 1292 aResponseHeaders.Append(value);
michael@0 1293 }
michael@0 1294 aResponseHeaders.AppendLiteral("\r\n");
michael@0 1295 }
michael@0 1296
michael@0 1297 int64_t length;
michael@0 1298 if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
michael@0 1299 aResponseHeaders.AppendLiteral("Content-Length: ");
michael@0 1300 aResponseHeaders.AppendInt(length);
michael@0 1301 aResponseHeaders.AppendLiteral("\r\n");
michael@0 1302 }
michael@0 1303 }
michael@0 1304
michael@0 1305 NS_IMETHODIMP
michael@0 1306 nsXMLHttpRequest::GetResponseHeader(const nsACString& aHeader,
michael@0 1307 nsACString& aResult)
michael@0 1308 {
michael@0 1309 ErrorResult rv;
michael@0 1310 GetResponseHeader(aHeader, aResult, rv);
michael@0 1311 return rv.ErrorCode();
michael@0 1312 }
michael@0 1313
michael@0 1314 void
michael@0 1315 nsXMLHttpRequest::GetResponseHeader(const nsACString& header,
michael@0 1316 nsACString& _retval, ErrorResult& aRv)
michael@0 1317 {
michael@0 1318 _retval.SetIsVoid(true);
michael@0 1319
michael@0 1320 nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
michael@0 1321
michael@0 1322 if (!httpChannel) {
michael@0 1323 // If the state is UNSENT or OPENED,
michael@0 1324 // return null and terminate these steps.
michael@0 1325 if (mState & (XML_HTTP_REQUEST_UNSENT |
michael@0 1326 XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
michael@0 1327 return;
michael@0 1328 }
michael@0 1329
michael@0 1330 // Even non-http channels supply content type and content length.
michael@0 1331 // Remember we don't leak header information from denied cross-site
michael@0 1332 // requests.
michael@0 1333 nsresult status;
michael@0 1334 if (!mChannel ||
michael@0 1335 NS_FAILED(mChannel->GetStatus(&status)) ||
michael@0 1336 NS_FAILED(status)) {
michael@0 1337 return;
michael@0 1338 }
michael@0 1339
michael@0 1340 // Content Type:
michael@0 1341 if (header.LowerCaseEqualsASCII("content-type")) {
michael@0 1342 if (NS_FAILED(mChannel->GetContentType(_retval))) {
michael@0 1343 // Means no content type
michael@0 1344 _retval.SetIsVoid(true);
michael@0 1345 return;
michael@0 1346 }
michael@0 1347
michael@0 1348 nsCString value;
michael@0 1349 if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) &&
michael@0 1350 !value.IsEmpty()) {
michael@0 1351 _retval.Append(";charset=");
michael@0 1352 _retval.Append(value);
michael@0 1353 }
michael@0 1354 }
michael@0 1355
michael@0 1356 // Content Length:
michael@0 1357 else if (header.LowerCaseEqualsASCII("content-length")) {
michael@0 1358 int64_t length;
michael@0 1359 if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
michael@0 1360 _retval.AppendInt(length);
michael@0 1361 }
michael@0 1362 }
michael@0 1363
michael@0 1364 return;
michael@0 1365 }
michael@0 1366
michael@0 1367 // Check for dangerous headers
michael@0 1368 if (!IsSafeHeader(header, httpChannel)) {
michael@0 1369 return;
michael@0 1370 }
michael@0 1371
michael@0 1372 aRv = httpChannel->GetResponseHeader(header, _retval);
michael@0 1373 if (aRv.ErrorCode() == NS_ERROR_NOT_AVAILABLE) {
michael@0 1374 // Means no header
michael@0 1375 _retval.SetIsVoid(true);
michael@0 1376 aRv = NS_OK;
michael@0 1377 }
michael@0 1378 }
michael@0 1379
michael@0 1380 already_AddRefed<nsILoadGroup>
michael@0 1381 nsXMLHttpRequest::GetLoadGroup() const
michael@0 1382 {
michael@0 1383 if (mState & XML_HTTP_REQUEST_BACKGROUND) {
michael@0 1384 return nullptr;
michael@0 1385 }
michael@0 1386
michael@0 1387 nsresult rv = NS_ERROR_FAILURE;
michael@0 1388 nsIScriptContext* sc =
michael@0 1389 const_cast<nsXMLHttpRequest*>(this)->GetContextForEventHandlers(&rv);
michael@0 1390 nsCOMPtr<nsIDocument> doc =
michael@0 1391 nsContentUtils::GetDocumentFromScriptContext(sc);
michael@0 1392 if (doc) {
michael@0 1393 return doc->GetDocumentLoadGroup();
michael@0 1394 }
michael@0 1395
michael@0 1396 return nullptr;
michael@0 1397 }
michael@0 1398
michael@0 1399 nsresult
michael@0 1400 nsXMLHttpRequest::CreateReadystatechangeEvent(nsIDOMEvent** aDOMEvent)
michael@0 1401 {
michael@0 1402 nsresult rv = EventDispatcher::CreateEvent(this, nullptr, nullptr,
michael@0 1403 NS_LITERAL_STRING("Events"),
michael@0 1404 aDOMEvent);
michael@0 1405 if (NS_FAILED(rv)) {
michael@0 1406 return rv;
michael@0 1407 }
michael@0 1408
michael@0 1409 (*aDOMEvent)->InitEvent(NS_LITERAL_STRING(READYSTATE_STR),
michael@0 1410 false, false);
michael@0 1411
michael@0 1412 // We assume anyone who managed to call CreateReadystatechangeEvent is trusted
michael@0 1413 (*aDOMEvent)->SetTrusted(true);
michael@0 1414
michael@0 1415 return NS_OK;
michael@0 1416 }
michael@0 1417
michael@0 1418 void
michael@0 1419 nsXMLHttpRequest::DispatchProgressEvent(DOMEventTargetHelper* aTarget,
michael@0 1420 const nsAString& aType,
michael@0 1421 bool aLengthComputable,
michael@0 1422 uint64_t aLoaded, uint64_t aTotal)
michael@0 1423 {
michael@0 1424 NS_ASSERTION(aTarget, "null target");
michael@0 1425 NS_ASSERTION(!aType.IsEmpty(), "missing event type");
michael@0 1426
michael@0 1427 if (NS_FAILED(CheckInnerWindowCorrectness()) ||
michael@0 1428 (!AllowUploadProgress() && aTarget == mUpload)) {
michael@0 1429 return;
michael@0 1430 }
michael@0 1431
michael@0 1432 bool dispatchLoadend = aType.EqualsLiteral(LOAD_STR) ||
michael@0 1433 aType.EqualsLiteral(ERROR_STR) ||
michael@0 1434 aType.EqualsLiteral(TIMEOUT_STR) ||
michael@0 1435 aType.EqualsLiteral(ABORT_STR);
michael@0 1436
michael@0 1437 nsCOMPtr<nsIDOMEvent> event;
michael@0 1438 nsresult rv = NS_NewDOMProgressEvent(getter_AddRefs(event), this,
michael@0 1439 nullptr, nullptr);
michael@0 1440 if (NS_FAILED(rv)) {
michael@0 1441 return;
michael@0 1442 }
michael@0 1443
michael@0 1444 nsCOMPtr<nsIDOMProgressEvent> progress = do_QueryInterface(event);
michael@0 1445 if (!progress) {
michael@0 1446 return;
michael@0 1447 }
michael@0 1448
michael@0 1449 progress->InitProgressEvent(aType, false, false, aLengthComputable,
michael@0 1450 aLoaded, (aTotal == UINT64_MAX) ? 0 : aTotal);
michael@0 1451
michael@0 1452 event->SetTrusted(true);
michael@0 1453
michael@0 1454 aTarget->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
michael@0 1455
michael@0 1456 if (dispatchLoadend) {
michael@0 1457 DispatchProgressEvent(aTarget, NS_LITERAL_STRING(LOADEND_STR),
michael@0 1458 aLengthComputable, aLoaded, aTotal);
michael@0 1459 }
michael@0 1460 }
michael@0 1461
michael@0 1462 already_AddRefed<nsIHttpChannel>
michael@0 1463 nsXMLHttpRequest::GetCurrentHttpChannel()
michael@0 1464 {
michael@0 1465 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
michael@0 1466 return httpChannel.forget();
michael@0 1467 }
michael@0 1468
michael@0 1469 already_AddRefed<nsIJARChannel>
michael@0 1470 nsXMLHttpRequest::GetCurrentJARChannel()
michael@0 1471 {
michael@0 1472 nsCOMPtr<nsIJARChannel> appChannel = do_QueryInterface(mChannel);
michael@0 1473 return appChannel.forget();
michael@0 1474 }
michael@0 1475
michael@0 1476 bool
michael@0 1477 nsXMLHttpRequest::IsSystemXHR()
michael@0 1478 {
michael@0 1479 return mIsSystem || nsContentUtils::IsSystemPrincipal(mPrincipal);
michael@0 1480 }
michael@0 1481
michael@0 1482 nsresult
michael@0 1483 nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel)
michael@0 1484 {
michael@0 1485 // A system XHR (chrome code or a web app with the right permission) can
michael@0 1486 // always perform cross-site requests. In the web app case, however, we
michael@0 1487 // must still check for protected URIs like file:///.
michael@0 1488 if (IsSystemXHR()) {
michael@0 1489 if (!nsContentUtils::IsSystemPrincipal(mPrincipal)) {
michael@0 1490 nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
michael@0 1491 nsCOMPtr<nsIURI> uri;
michael@0 1492 aChannel->GetOriginalURI(getter_AddRefs(uri));
michael@0 1493 return secMan->CheckLoadURIWithPrincipal(
michael@0 1494 mPrincipal, uri, nsIScriptSecurityManager::STANDARD);
michael@0 1495 }
michael@0 1496 return NS_OK;
michael@0 1497 }
michael@0 1498
michael@0 1499 // If this is a same-origin request or the channel's URI inherits
michael@0 1500 // its principal, it's allowed.
michael@0 1501 if (nsContentUtils::CheckMayLoad(mPrincipal, aChannel, true)) {
michael@0 1502 return NS_OK;
michael@0 1503 }
michael@0 1504
michael@0 1505 // This is a cross-site request
michael@0 1506 mState |= XML_HTTP_REQUEST_USE_XSITE_AC;
michael@0 1507
michael@0 1508 // Check if we need to do a preflight request.
michael@0 1509 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
michael@0 1510 NS_ENSURE_TRUE(httpChannel, NS_ERROR_DOM_BAD_URI);
michael@0 1511
michael@0 1512 nsAutoCString method;
michael@0 1513 httpChannel->GetRequestMethod(method);
michael@0 1514 if (!mCORSUnsafeHeaders.IsEmpty() ||
michael@0 1515 (mUpload && mUpload->HasListeners()) ||
michael@0 1516 (!method.LowerCaseEqualsLiteral("get") &&
michael@0 1517 !method.LowerCaseEqualsLiteral("post") &&
michael@0 1518 !method.LowerCaseEqualsLiteral("head"))) {
michael@0 1519 mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT;
michael@0 1520 }
michael@0 1521
michael@0 1522 return NS_OK;
michael@0 1523 }
michael@0 1524
michael@0 1525 NS_IMETHODIMP
michael@0 1526 nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url,
michael@0 1527 bool async, const nsAString& user,
michael@0 1528 const nsAString& password, uint8_t optional_argc)
michael@0 1529 {
michael@0 1530 if (!optional_argc) {
michael@0 1531 // No optional arguments were passed in. Default async to true.
michael@0 1532 async = true;
michael@0 1533 }
michael@0 1534 Optional<nsAString> realUser;
michael@0 1535 if (optional_argc > 1) {
michael@0 1536 realUser = &user;
michael@0 1537 }
michael@0 1538 Optional<nsAString> realPassword;
michael@0 1539 if (optional_argc > 2) {
michael@0 1540 realPassword = &password;
michael@0 1541 }
michael@0 1542 return Open(method, url, async, realUser, realPassword);
michael@0 1543 }
michael@0 1544
michael@0 1545 nsresult
michael@0 1546 nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,
michael@0 1547 bool async, const Optional<nsAString>& user,
michael@0 1548 const Optional<nsAString>& password)
michael@0 1549 {
michael@0 1550 NS_ENSURE_ARG(!inMethod.IsEmpty());
michael@0 1551
michael@0 1552 if (!async && !DontWarnAboutSyncXHR() && GetOwner() &&
michael@0 1553 GetOwner()->GetExtantDoc()) {
michael@0 1554 GetOwner()->GetExtantDoc()->WarnOnceAbout(nsIDocument::eSyncXMLHttpRequest);
michael@0 1555 }
michael@0 1556
michael@0 1557 Telemetry::Accumulate(Telemetry::XMLHTTPREQUEST_ASYNC_OR_SYNC,
michael@0 1558 async ? 0 : 1);
michael@0 1559
michael@0 1560 NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
michael@0 1561
michael@0 1562 // Disallow HTTP/1.1 TRACE method (see bug 302489)
michael@0 1563 // and MS IIS equivalent TRACK (see bug 381264)
michael@0 1564 // and CONNECT
michael@0 1565 if (inMethod.LowerCaseEqualsLiteral("trace") ||
michael@0 1566 inMethod.LowerCaseEqualsLiteral("connect") ||
michael@0 1567 inMethod.LowerCaseEqualsLiteral("track")) {
michael@0 1568 return NS_ERROR_DOM_SECURITY_ERR;
michael@0 1569 }
michael@0 1570
michael@0 1571 nsAutoCString method;
michael@0 1572 // GET, POST, DELETE, HEAD, OPTIONS, PUT methods normalized to upper case
michael@0 1573 if (inMethod.LowerCaseEqualsLiteral("get")) {
michael@0 1574 method.Assign(NS_LITERAL_CSTRING("GET"));
michael@0 1575 } else if (inMethod.LowerCaseEqualsLiteral("post")) {
michael@0 1576 method.Assign(NS_LITERAL_CSTRING("POST"));
michael@0 1577 } else if (inMethod.LowerCaseEqualsLiteral("delete")) {
michael@0 1578 method.Assign(NS_LITERAL_CSTRING("DELETE"));
michael@0 1579 } else if (inMethod.LowerCaseEqualsLiteral("head")) {
michael@0 1580 method.Assign(NS_LITERAL_CSTRING("HEAD"));
michael@0 1581 } else if (inMethod.LowerCaseEqualsLiteral("options")) {
michael@0 1582 method.Assign(NS_LITERAL_CSTRING("OPTIONS"));
michael@0 1583 } else if (inMethod.LowerCaseEqualsLiteral("put")) {
michael@0 1584 method.Assign(NS_LITERAL_CSTRING("PUT"));
michael@0 1585 } else {
michael@0 1586 method = inMethod; // other methods are not normalized
michael@0 1587 }
michael@0 1588
michael@0 1589 // sync request is not allowed using withCredential or responseType
michael@0 1590 // in window context
michael@0 1591 if (!async && HasOrHasHadOwner() &&
michael@0 1592 (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS ||
michael@0 1593 mTimeoutMilliseconds ||
michael@0 1594 mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT)) {
michael@0 1595 if (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS) {
michael@0 1596 LogMessage("WithCredentialsSyncXHRWarning", GetOwner());
michael@0 1597 }
michael@0 1598 if (mTimeoutMilliseconds) {
michael@0 1599 LogMessage("TimeoutSyncXHRWarning", GetOwner());
michael@0 1600 }
michael@0 1601 if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT) {
michael@0 1602 LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
michael@0 1603 }
michael@0 1604 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
michael@0 1605 }
michael@0 1606
michael@0 1607 nsresult rv;
michael@0 1608 nsCOMPtr<nsIURI> uri;
michael@0 1609
michael@0 1610 if (mState & (XML_HTTP_REQUEST_OPENED |
michael@0 1611 XML_HTTP_REQUEST_HEADERS_RECEIVED |
michael@0 1612 XML_HTTP_REQUEST_LOADING |
michael@0 1613 XML_HTTP_REQUEST_SENT)) {
michael@0 1614 // IE aborts as well
michael@0 1615 Abort();
michael@0 1616
michael@0 1617 // XXX We should probably send a warning to the JS console
michael@0 1618 // that load was aborted and event listeners were cleared
michael@0 1619 // since this looks like a situation that could happen
michael@0 1620 // by accident and you could spend a lot of time wondering
michael@0 1621 // why things didn't work.
michael@0 1622 }
michael@0 1623
michael@0 1624 // Unset any pre-existing aborted and timed-out states.
michael@0 1625 mState &= ~XML_HTTP_REQUEST_ABORTED & ~XML_HTTP_REQUEST_TIMED_OUT;
michael@0 1626
michael@0 1627 if (async) {
michael@0 1628 mState |= XML_HTTP_REQUEST_ASYNC;
michael@0 1629 } else {
michael@0 1630 mState &= ~XML_HTTP_REQUEST_ASYNC;
michael@0 1631 }
michael@0 1632
michael@0 1633 nsIScriptContext* sc = GetContextForEventHandlers(&rv);
michael@0 1634 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1635 nsCOMPtr<nsIDocument> doc =
michael@0 1636 nsContentUtils::GetDocumentFromScriptContext(sc);
michael@0 1637
michael@0 1638 nsCOMPtr<nsIURI> baseURI;
michael@0 1639 if (mBaseURI) {
michael@0 1640 baseURI = mBaseURI;
michael@0 1641 }
michael@0 1642 else if (doc) {
michael@0 1643 baseURI = doc->GetBaseURI();
michael@0 1644 }
michael@0 1645
michael@0 1646 rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, baseURI);
michael@0 1647 if (NS_FAILED(rv)) return rv;
michael@0 1648
michael@0 1649 rv = CheckInnerWindowCorrectness();
michael@0 1650 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1651 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
michael@0 1652 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_XMLHTTPREQUEST,
michael@0 1653 uri,
michael@0 1654 mPrincipal,
michael@0 1655 doc,
michael@0 1656 EmptyCString(), //mime guess
michael@0 1657 nullptr, //extra
michael@0 1658 &shouldLoad,
michael@0 1659 nsContentUtils::GetContentPolicy(),
michael@0 1660 nsContentUtils::GetSecurityManager());
michael@0 1661 if (NS_FAILED(rv)) return rv;
michael@0 1662 if (NS_CP_REJECTED(shouldLoad)) {
michael@0 1663 // Disallowed by content policy
michael@0 1664 return NS_ERROR_CONTENT_BLOCKED;
michael@0 1665 }
michael@0 1666
michael@0 1667 // XXXbz this is wrong: we should only be looking at whether
michael@0 1668 // user/password were passed, not at the values! See bug 759624.
michael@0 1669 if (user.WasPassed() && !user.Value().IsEmpty()) {
michael@0 1670 nsAutoCString userpass;
michael@0 1671 CopyUTF16toUTF8(user.Value(), userpass);
michael@0 1672 if (password.WasPassed() && !password.Value().IsEmpty()) {
michael@0 1673 userpass.Append(':');
michael@0 1674 AppendUTF16toUTF8(password.Value(), userpass);
michael@0 1675 }
michael@0 1676 uri->SetUserPass(userpass);
michael@0 1677 }
michael@0 1678
michael@0 1679 // Clear our record of previously set headers so future header set
michael@0 1680 // operations will merge/override correctly.
michael@0 1681 mAlreadySetHeaders.Clear();
michael@0 1682
michael@0 1683 // When we are called from JS we can find the load group for the page,
michael@0 1684 // and add ourselves to it. This way any pending requests
michael@0 1685 // will be automatically aborted if the user leaves the page.
michael@0 1686 nsCOMPtr<nsILoadGroup> loadGroup = GetLoadGroup();
michael@0 1687
michael@0 1688 // get Content Security Policy from principal to pass into channel
michael@0 1689 nsCOMPtr<nsIChannelPolicy> channelPolicy;
michael@0 1690 nsCOMPtr<nsIContentSecurityPolicy> csp;
michael@0 1691 rv = mPrincipal->GetCsp(getter_AddRefs(csp));
michael@0 1692 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1693 if (csp) {
michael@0 1694 channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
michael@0 1695 channelPolicy->SetContentSecurityPolicy(csp);
michael@0 1696 channelPolicy->SetLoadType(nsIContentPolicy::TYPE_XMLHTTPREQUEST);
michael@0 1697 }
michael@0 1698 rv = NS_NewChannel(getter_AddRefs(mChannel),
michael@0 1699 uri,
michael@0 1700 nullptr, // ioService
michael@0 1701 loadGroup,
michael@0 1702 nullptr, // callbacks
michael@0 1703 nsIRequest::LOAD_BACKGROUND,
michael@0 1704 channelPolicy);
michael@0 1705 if (NS_FAILED(rv)) return rv;
michael@0 1706
michael@0 1707 mState &= ~(XML_HTTP_REQUEST_USE_XSITE_AC |
michael@0 1708 XML_HTTP_REQUEST_NEED_AC_PREFLIGHT);
michael@0 1709
michael@0 1710 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
michael@0 1711 if (httpChannel) {
michael@0 1712 rv = httpChannel->SetRequestMethod(method);
michael@0 1713 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1714
michael@0 1715 // Set the initiator type
michael@0 1716 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
michael@0 1717 if (timedChannel) {
michael@0 1718 timedChannel->SetInitiatorType(NS_LITERAL_STRING("xmlhttprequest"));
michael@0 1719 }
michael@0 1720 }
michael@0 1721
michael@0 1722 ChangeState(XML_HTTP_REQUEST_OPENED);
michael@0 1723
michael@0 1724 return rv;
michael@0 1725 }
michael@0 1726
michael@0 1727 /*
michael@0 1728 * "Copy" from a stream.
michael@0 1729 */
michael@0 1730 NS_METHOD
michael@0 1731 nsXMLHttpRequest::StreamReaderFunc(nsIInputStream* in,
michael@0 1732 void* closure,
michael@0 1733 const char* fromRawSegment,
michael@0 1734 uint32_t toOffset,
michael@0 1735 uint32_t count,
michael@0 1736 uint32_t *writeCount)
michael@0 1737 {
michael@0 1738 nsXMLHttpRequest* xmlHttpRequest = static_cast<nsXMLHttpRequest*>(closure);
michael@0 1739 if (!xmlHttpRequest || !writeCount) {
michael@0 1740 NS_WARNING("XMLHttpRequest cannot read from stream: no closure or writeCount");
michael@0 1741 return NS_ERROR_FAILURE;
michael@0 1742 }
michael@0 1743
michael@0 1744 nsresult rv = NS_OK;
michael@0 1745
michael@0 1746 if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
michael@0 1747 xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
michael@0 1748 if (!xmlHttpRequest->mDOMFile) {
michael@0 1749 if (!xmlHttpRequest->mBlobSet) {
michael@0 1750 xmlHttpRequest->mBlobSet = new BlobSet();
michael@0 1751 }
michael@0 1752 rv = xmlHttpRequest->mBlobSet->AppendVoidPtr(fromRawSegment, count);
michael@0 1753 }
michael@0 1754 // Clear the cache so that the blob size is updated.
michael@0 1755 if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
michael@0 1756 xmlHttpRequest->mResponseBlob = nullptr;
michael@0 1757 }
michael@0 1758 } else if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER ||
michael@0 1759 xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER) {
michael@0 1760 // get the initial capacity to something reasonable to avoid a bunch of reallocs right
michael@0 1761 // at the start
michael@0 1762 if (xmlHttpRequest->mArrayBufferBuilder.capacity() == 0)
michael@0 1763 xmlHttpRequest->mArrayBufferBuilder.setCapacity(PR_MAX(count, XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE));
michael@0 1764
michael@0 1765 xmlHttpRequest->mArrayBufferBuilder.append(reinterpret_cast<const uint8_t*>(fromRawSegment), count,
michael@0 1766 XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH);
michael@0 1767 } else if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT &&
michael@0 1768 xmlHttpRequest->mResponseXML) {
michael@0 1769 // Copy for our own use
michael@0 1770 uint32_t previousLength = xmlHttpRequest->mResponseBody.Length();
michael@0 1771 xmlHttpRequest->mResponseBody.Append(fromRawSegment,count);
michael@0 1772 if (count > 0 && xmlHttpRequest->mResponseBody.Length() == previousLength) {
michael@0 1773 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1774 }
michael@0 1775 } else if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT ||
michael@0 1776 xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_TEXT ||
michael@0 1777 xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_JSON ||
michael@0 1778 xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT) {
michael@0 1779 NS_ASSERTION(!xmlHttpRequest->mResponseXML,
michael@0 1780 "We shouldn't be parsing a doc here");
michael@0 1781 xmlHttpRequest->AppendToResponseText(fromRawSegment, count);
michael@0 1782 }
michael@0 1783
michael@0 1784 if (xmlHttpRequest->mState & XML_HTTP_REQUEST_PARSEBODY) {
michael@0 1785 // Give the same data to the parser.
michael@0 1786
michael@0 1787 // We need to wrap the data in a new lightweight stream and pass that
michael@0 1788 // to the parser, because calling ReadSegments() recursively on the same
michael@0 1789 // stream is not supported.
michael@0 1790 nsCOMPtr<nsIInputStream> copyStream;
michael@0 1791 rv = NS_NewByteInputStream(getter_AddRefs(copyStream), fromRawSegment, count);
michael@0 1792
michael@0 1793 if (NS_SUCCEEDED(rv) && xmlHttpRequest->mXMLParserStreamListener) {
michael@0 1794 NS_ASSERTION(copyStream, "NS_NewByteInputStream lied");
michael@0 1795 nsresult parsingResult = xmlHttpRequest->mXMLParserStreamListener
michael@0 1796 ->OnDataAvailable(xmlHttpRequest->mChannel,
michael@0 1797 xmlHttpRequest->mContext,
michael@0 1798 copyStream, toOffset, count);
michael@0 1799
michael@0 1800 // No use to continue parsing if we failed here, but we
michael@0 1801 // should still finish reading the stream
michael@0 1802 if (NS_FAILED(parsingResult)) {
michael@0 1803 xmlHttpRequest->mState &= ~XML_HTTP_REQUEST_PARSEBODY;
michael@0 1804 }
michael@0 1805 }
michael@0 1806 }
michael@0 1807
michael@0 1808 if (NS_SUCCEEDED(rv)) {
michael@0 1809 *writeCount = count;
michael@0 1810 } else {
michael@0 1811 *writeCount = 0;
michael@0 1812 }
michael@0 1813
michael@0 1814 return rv;
michael@0 1815 }
michael@0 1816
michael@0 1817 bool nsXMLHttpRequest::CreateDOMFile(nsIRequest *request)
michael@0 1818 {
michael@0 1819 nsCOMPtr<nsIFile> file;
michael@0 1820 nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(request);
michael@0 1821 if (fc) {
michael@0 1822 fc->GetFile(getter_AddRefs(file));
michael@0 1823 }
michael@0 1824
michael@0 1825 if (!file)
michael@0 1826 return false;
michael@0 1827
michael@0 1828 nsAutoCString contentType;
michael@0 1829 mChannel->GetContentType(contentType);
michael@0 1830
michael@0 1831 mDOMFile =
michael@0 1832 new nsDOMFileFile(file, EmptyString(), NS_ConvertASCIItoUTF16(contentType));
michael@0 1833 mBlobSet = nullptr;
michael@0 1834 NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
michael@0 1835 return true;
michael@0 1836 }
michael@0 1837
michael@0 1838 NS_IMETHODIMP
michael@0 1839 nsXMLHttpRequest::OnDataAvailable(nsIRequest *request,
michael@0 1840 nsISupports *ctxt,
michael@0 1841 nsIInputStream *inStr,
michael@0 1842 uint64_t sourceOffset,
michael@0 1843 uint32_t count)
michael@0 1844 {
michael@0 1845 NS_ENSURE_ARG_POINTER(inStr);
michael@0 1846
michael@0 1847 NS_ABORT_IF_FALSE(mContext.get() == ctxt,"start context different from OnDataAvailable context");
michael@0 1848
michael@0 1849 mProgressSinceLastProgressEvent = true;
michael@0 1850
michael@0 1851 bool cancelable = false;
michael@0 1852 if ((mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
michael@0 1853 mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) && !mDOMFile) {
michael@0 1854 cancelable = CreateDOMFile(request);
michael@0 1855 // The nsIStreamListener contract mandates us
michael@0 1856 // to read from the stream before returning.
michael@0 1857 }
michael@0 1858
michael@0 1859 uint32_t totalRead;
michael@0 1860 nsresult rv = inStr->ReadSegments(nsXMLHttpRequest::StreamReaderFunc,
michael@0 1861 (void*)this, count, &totalRead);
michael@0 1862 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1863
michael@0 1864 if (cancelable) {
michael@0 1865 // We don't have to read from the local file for the blob response
michael@0 1866 mDOMFile->GetSize(&mLoadTransferred);
michael@0 1867 ChangeState(XML_HTTP_REQUEST_LOADING);
michael@0 1868 return request->Cancel(NS_OK);
michael@0 1869 }
michael@0 1870
michael@0 1871 mLoadTransferred += totalRead;
michael@0 1872
michael@0 1873 ChangeState(XML_HTTP_REQUEST_LOADING);
michael@0 1874
michael@0 1875 MaybeDispatchProgressEvents(false);
michael@0 1876
michael@0 1877 return NS_OK;
michael@0 1878 }
michael@0 1879
michael@0 1880 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
michael@0 1881 NS_IMETHODIMP
michael@0 1882 nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
michael@0 1883 {
michael@0 1884 PROFILER_LABEL("nsXMLHttpRequest", "OnStartRequest");
michael@0 1885 nsresult rv = NS_OK;
michael@0 1886 if (!mFirstStartRequestSeen && mRequestObserver) {
michael@0 1887 mFirstStartRequestSeen = true;
michael@0 1888 mRequestObserver->OnStartRequest(request, ctxt);
michael@0 1889 }
michael@0 1890
michael@0 1891 if (request != mChannel) {
michael@0 1892 // Can this still happen?
michael@0 1893 return NS_OK;
michael@0 1894 }
michael@0 1895
michael@0 1896 // Don't do anything if we have been aborted
michael@0 1897 if (mState & XML_HTTP_REQUEST_UNSENT)
michael@0 1898 return NS_OK;
michael@0 1899
michael@0 1900 /* Apparently, Abort() should set XML_HTTP_REQUEST_UNSENT. See bug 361773.
michael@0 1901 XHR2 spec says this is correct. */
michael@0 1902 if (mState & XML_HTTP_REQUEST_ABORTED) {
michael@0 1903 NS_ERROR("Ugh, still getting data on an aborted XMLHttpRequest!");
michael@0 1904
michael@0 1905 return NS_ERROR_UNEXPECTED;
michael@0 1906 }
michael@0 1907
michael@0 1908 // Don't do anything if we have timed out.
michael@0 1909 if (mState & XML_HTTP_REQUEST_TIMED_OUT) {
michael@0 1910 return NS_OK;
michael@0 1911 }
michael@0 1912
michael@0 1913 nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
michael@0 1914 NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
michael@0 1915
michael@0 1916 nsCOMPtr<nsIPrincipal> documentPrincipal;
michael@0 1917 if (IsSystemXHR()) {
michael@0 1918 // Don't give this document the system principal. We need to keep track of
michael@0 1919 // mPrincipal being system because we use it for various security checks
michael@0 1920 // that should be passing, but the document data shouldn't get a system
michael@0 1921 // principal.
michael@0 1922 nsresult rv;
michael@0 1923 documentPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
michael@0 1924 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1925 } else {
michael@0 1926 documentPrincipal = mPrincipal;
michael@0 1927 }
michael@0 1928
michael@0 1929 channel->SetOwner(documentPrincipal);
michael@0 1930
michael@0 1931 nsresult status;
michael@0 1932 request->GetStatus(&status);
michael@0 1933 mErrorLoad = mErrorLoad || NS_FAILED(status);
michael@0 1934
michael@0 1935 if (mUpload && !mUploadComplete && !mErrorLoad &&
michael@0 1936 (mState & XML_HTTP_REQUEST_ASYNC)) {
michael@0 1937 if (mProgressTimerIsActive) {
michael@0 1938 mProgressTimerIsActive = false;
michael@0 1939 mProgressNotifier->Cancel();
michael@0 1940 }
michael@0 1941 if (mUploadTransferred < mUploadTotal) {
michael@0 1942 mUploadTransferred = mUploadTotal;
michael@0 1943 mProgressSinceLastProgressEvent = true;
michael@0 1944 mUploadLengthComputable = true;
michael@0 1945 MaybeDispatchProgressEvents(true);
michael@0 1946 }
michael@0 1947 mUploadComplete = true;
michael@0 1948 DispatchProgressEvent(mUpload, NS_LITERAL_STRING(LOAD_STR),
michael@0 1949 true, mUploadTotal, mUploadTotal);
michael@0 1950 }
michael@0 1951
michael@0 1952 mContext = ctxt;
michael@0 1953 mState |= XML_HTTP_REQUEST_PARSEBODY;
michael@0 1954 ChangeState(XML_HTTP_REQUEST_HEADERS_RECEIVED);
michael@0 1955
michael@0 1956 ResetResponse();
michael@0 1957
michael@0 1958 if (!mOverrideMimeType.IsEmpty()) {
michael@0 1959 channel->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType));
michael@0 1960 }
michael@0 1961
michael@0 1962 DetectCharset();
michael@0 1963
michael@0 1964 // Set up arraybuffer
michael@0 1965 if (mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER && NS_SUCCEEDED(status)) {
michael@0 1966 int64_t contentLength;
michael@0 1967 rv = channel->GetContentLength(&contentLength);
michael@0 1968 if (NS_SUCCEEDED(rv) &&
michael@0 1969 contentLength > 0 &&
michael@0 1970 contentLength < XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE) {
michael@0 1971 mArrayBufferBuilder.setCapacity(static_cast<int32_t>(contentLength));
michael@0 1972 }
michael@0 1973 }
michael@0 1974
michael@0 1975 // Set up responseXML
michael@0 1976 bool parseBody = mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT ||
michael@0 1977 mResponseType == XML_HTTP_RESPONSE_TYPE_DOCUMENT;
michael@0 1978 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
michael@0 1979 if (parseBody && httpChannel) {
michael@0 1980 nsAutoCString method;
michael@0 1981 httpChannel->GetRequestMethod(method);
michael@0 1982 parseBody = !method.EqualsLiteral("HEAD");
michael@0 1983 }
michael@0 1984
michael@0 1985 mIsHtml = false;
michael@0 1986 mWarnAboutSyncHtml = false;
michael@0 1987 if (parseBody && NS_SUCCEEDED(status)) {
michael@0 1988 // We can gain a huge performance win by not even trying to
michael@0 1989 // parse non-XML data. This also protects us from the situation
michael@0 1990 // where we have an XML document and sink, but HTML (or other)
michael@0 1991 // parser, which can produce unreliable results.
michael@0 1992 nsAutoCString type;
michael@0 1993 channel->GetContentType(type);
michael@0 1994
michael@0 1995 if ((mResponseType == XML_HTTP_RESPONSE_TYPE_DOCUMENT) &&
michael@0 1996 type.EqualsLiteral("text/html")) {
michael@0 1997 // HTML parsing is only supported for responseType == "document" to
michael@0 1998 // avoid running the parser and, worse, populating responseXML for
michael@0 1999 // legacy users of XHR who use responseType == "" for retrieving the
michael@0 2000 // responseText of text/html resources. This legacy case is so common
michael@0 2001 // that it's not useful to emit a warning about it.
michael@0 2002 if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
michael@0 2003 // We don't make cool new features available in the bad synchronous
michael@0 2004 // mode. The synchronous mode is for legacy only.
michael@0 2005 mWarnAboutSyncHtml = true;
michael@0 2006 mState &= ~XML_HTTP_REQUEST_PARSEBODY;
michael@0 2007 } else {
michael@0 2008 mIsHtml = true;
michael@0 2009 }
michael@0 2010 } else if (type.Find("xml") == kNotFound) {
michael@0 2011 mState &= ~XML_HTTP_REQUEST_PARSEBODY;
michael@0 2012 }
michael@0 2013 } else {
michael@0 2014 // The request failed, so we shouldn't be parsing anyway
michael@0 2015 mState &= ~XML_HTTP_REQUEST_PARSEBODY;
michael@0 2016 }
michael@0 2017
michael@0 2018 if (mState & XML_HTTP_REQUEST_PARSEBODY) {
michael@0 2019 nsCOMPtr<nsIURI> baseURI, docURI;
michael@0 2020 rv = mChannel->GetURI(getter_AddRefs(docURI));
michael@0 2021 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2022 baseURI = docURI;
michael@0 2023
michael@0 2024 nsIScriptContext* sc = GetContextForEventHandlers(&rv);
michael@0 2025 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2026 nsCOMPtr<nsIDocument> doc =
michael@0 2027 nsContentUtils::GetDocumentFromScriptContext(sc);
michael@0 2028 nsCOMPtr<nsIURI> chromeXHRDocURI, chromeXHRDocBaseURI;
michael@0 2029 if (doc) {
michael@0 2030 chromeXHRDocURI = doc->GetDocumentURI();
michael@0 2031 chromeXHRDocBaseURI = doc->GetBaseURI();
michael@0 2032 }
michael@0 2033
michael@0 2034 // Create an empty document from it. Here we have to cheat a little bit...
michael@0 2035 // Setting the base URI to |baseURI| won't work if the document has a null
michael@0 2036 // principal, so use mPrincipal when creating the document, then reset the
michael@0 2037 // principal.
michael@0 2038 const nsAString& emptyStr = EmptyString();
michael@0 2039 nsCOMPtr<nsIDOMDocument> responseDoc;
michael@0 2040 nsIGlobalObject* global = DOMEventTargetHelper::GetParentObject();
michael@0 2041 rv = NS_NewDOMDocument(getter_AddRefs(responseDoc),
michael@0 2042 emptyStr, emptyStr, nullptr, docURI,
michael@0 2043 baseURI, mPrincipal, true, global,
michael@0 2044 mIsHtml ? DocumentFlavorHTML :
michael@0 2045 DocumentFlavorLegacyGuess);
michael@0 2046 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2047 mResponseXML = do_QueryInterface(responseDoc);
michael@0 2048 mResponseXML->SetPrincipal(documentPrincipal);
michael@0 2049 mResponseXML->SetChromeXHRDocURI(chromeXHRDocURI);
michael@0 2050 mResponseXML->SetChromeXHRDocBaseURI(chromeXHRDocBaseURI);
michael@0 2051
michael@0 2052 if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
michael@0 2053 mResponseXML->ForceEnableXULXBL();
michael@0 2054 }
michael@0 2055
michael@0 2056 if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
michael@0 2057 nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mResponseXML);
michael@0 2058 if (htmlDoc) {
michael@0 2059 htmlDoc->DisableCookieAccess();
michael@0 2060 }
michael@0 2061 }
michael@0 2062
michael@0 2063 nsCOMPtr<nsIStreamListener> listener;
michael@0 2064 nsCOMPtr<nsILoadGroup> loadGroup;
michael@0 2065 channel->GetLoadGroup(getter_AddRefs(loadGroup));
michael@0 2066
michael@0 2067 rv = mResponseXML->StartDocumentLoad(kLoadAsData, channel, loadGroup,
michael@0 2068 nullptr, getter_AddRefs(listener),
michael@0 2069 !(mState & XML_HTTP_REQUEST_USE_XSITE_AC));
michael@0 2070 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2071
michael@0 2072 mXMLParserStreamListener = listener;
michael@0 2073 rv = mXMLParserStreamListener->OnStartRequest(request, ctxt);
michael@0 2074 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2075 }
michael@0 2076
michael@0 2077 // We won't get any progress events anyway if we didn't have progress
michael@0 2078 // events when starting the request - so maybe no need to start timer here.
michael@0 2079 if (NS_SUCCEEDED(rv) &&
michael@0 2080 (mState & XML_HTTP_REQUEST_ASYNC) &&
michael@0 2081 HasListenersFor(nsGkAtoms::onprogress)) {
michael@0 2082 StartProgressEventTimer();
michael@0 2083 }
michael@0 2084
michael@0 2085 return NS_OK;
michael@0 2086 }
michael@0 2087
michael@0 2088 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status, in wstring statusArg); */
michael@0 2089 NS_IMETHODIMP
michael@0 2090 nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
michael@0 2091 {
michael@0 2092 PROFILER_LABEL("content", "nsXMLHttpRequest::OnStopRequest");
michael@0 2093 if (request != mChannel) {
michael@0 2094 // Can this still happen?
michael@0 2095 return NS_OK;
michael@0 2096 }
michael@0 2097
michael@0 2098 mWaitingForOnStopRequest = false;
michael@0 2099
michael@0 2100 if (mRequestObserver) {
michael@0 2101 NS_ASSERTION(mFirstStartRequestSeen, "Inconsistent state!");
michael@0 2102 mFirstStartRequestSeen = false;
michael@0 2103 mRequestObserver->OnStopRequest(request, ctxt, status);
michael@0 2104 }
michael@0 2105
michael@0 2106 // make sure to notify the listener if we were aborted
michael@0 2107 // XXX in fact, why don't we do the cleanup below in this case??
michael@0 2108 // XML_HTTP_REQUEST_UNSENT is for abort calls. See OnStartRequest above.
michael@0 2109 if ((mState & XML_HTTP_REQUEST_UNSENT) ||
michael@0 2110 (mState & XML_HTTP_REQUEST_TIMED_OUT)) {
michael@0 2111 if (mXMLParserStreamListener)
michael@0 2112 (void) mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
michael@0 2113 return NS_OK;
michael@0 2114 }
michael@0 2115
michael@0 2116 // Is this good enough here?
michael@0 2117 if (mState & XML_HTTP_REQUEST_PARSEBODY && mXMLParserStreamListener) {
michael@0 2118 mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
michael@0 2119 }
michael@0 2120
michael@0 2121 mXMLParserStreamListener = nullptr;
michael@0 2122 mContext = nullptr;
michael@0 2123
michael@0 2124 // If we're received data since the last progress event, make sure to fire
michael@0 2125 // an event for it, except in the HTML case, defer the last progress event
michael@0 2126 // until the parser is done.
michael@0 2127 if (!mIsHtml) {
michael@0 2128 MaybeDispatchProgressEvents(true);
michael@0 2129 }
michael@0 2130
michael@0 2131 if (NS_SUCCEEDED(status) &&
michael@0 2132 (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
michael@0 2133 mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB)) {
michael@0 2134 if (!mDOMFile) {
michael@0 2135 CreateDOMFile(request);
michael@0 2136 }
michael@0 2137 if (mDOMFile) {
michael@0 2138 mResponseBlob = mDOMFile;
michael@0 2139 mDOMFile = nullptr;
michael@0 2140 } else {
michael@0 2141 // mBlobSet can be null if the channel is non-file non-cacheable
michael@0 2142 // and if the response length is zero.
michael@0 2143 if (!mBlobSet) {
michael@0 2144 mBlobSet = new BlobSet();
michael@0 2145 }
michael@0 2146 // Smaller files may be written in cache map instead of separate files.
michael@0 2147 // Also, no-store response cannot be written in persistent cache.
michael@0 2148 nsAutoCString contentType;
michael@0 2149 mChannel->GetContentType(contentType);
michael@0 2150 mResponseBlob = mBlobSet->GetBlobInternal(contentType);
michael@0 2151 mBlobSet = nullptr;
michael@0 2152 }
michael@0 2153 NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
michael@0 2154 NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
michael@0 2155 } else if (NS_SUCCEEDED(status) &&
michael@0 2156 (mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER ||
michael@0 2157 mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER)) {
michael@0 2158 // set the capacity down to the actual length, to realloc back
michael@0 2159 // down to the actual size
michael@0 2160 if (!mArrayBufferBuilder.setCapacity(mArrayBufferBuilder.length())) {
michael@0 2161 // this should never happen!
michael@0 2162 status = NS_ERROR_UNEXPECTED;
michael@0 2163 }
michael@0 2164 }
michael@0 2165
michael@0 2166 nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
michael@0 2167 NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
michael@0 2168
michael@0 2169 channel->SetNotificationCallbacks(nullptr);
michael@0 2170 mNotificationCallbacks = nullptr;
michael@0 2171 mChannelEventSink = nullptr;
michael@0 2172 mProgressEventSink = nullptr;
michael@0 2173
michael@0 2174 mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
michael@0 2175
michael@0 2176 if (NS_FAILED(status)) {
michael@0 2177 // This can happen if the server is unreachable. Other possible
michael@0 2178 // reasons are that the user leaves the page or hits the ESC key.
michael@0 2179
michael@0 2180 mErrorLoad = true;
michael@0 2181 mResponseXML = nullptr;
michael@0 2182 }
michael@0 2183
michael@0 2184 // If we're uninitialized at this point, we encountered an error
michael@0 2185 // earlier and listeners have already been notified. Also we do
michael@0 2186 // not want to do this if we already completed.
michael@0 2187 if (mState & (XML_HTTP_REQUEST_UNSENT |
michael@0 2188 XML_HTTP_REQUEST_DONE)) {
michael@0 2189 return NS_OK;
michael@0 2190 }
michael@0 2191
michael@0 2192 if (!mResponseXML) {
michael@0 2193 ChangeStateToDone();
michael@0 2194 return NS_OK;
michael@0 2195 }
michael@0 2196 if (mIsHtml) {
michael@0 2197 NS_ASSERTION(!(mState & XML_HTTP_REQUEST_SYNCLOOPING),
michael@0 2198 "We weren't supposed to support HTML parsing with XHR!");
michael@0 2199 nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(mResponseXML);
michael@0 2200 EventListenerManager* manager =
michael@0 2201 eventTarget->GetOrCreateListenerManager();
michael@0 2202 manager->AddEventListenerByType(new nsXHRParseEndListener(this),
michael@0 2203 NS_LITERAL_STRING("DOMContentLoaded"),
michael@0 2204 TrustedEventsAtSystemGroupBubble());
michael@0 2205 return NS_OK;
michael@0 2206 }
michael@0 2207 // We might have been sent non-XML data. If that was the case,
michael@0 2208 // we should null out the document member. The idea in this
michael@0 2209 // check here is that if there is no document element it is not
michael@0 2210 // an XML document. We might need a fancier check...
michael@0 2211 if (!mResponseXML->GetRootElement()) {
michael@0 2212 mResponseXML = nullptr;
michael@0 2213 }
michael@0 2214 ChangeStateToDone();
michael@0 2215 return NS_OK;
michael@0 2216 }
michael@0 2217
michael@0 2218 void
michael@0 2219 nsXMLHttpRequest::ChangeStateToDone()
michael@0 2220 {
michael@0 2221 if (mIsHtml) {
michael@0 2222 // In the HTML case, this has to be deferred, because the parser doesn't
michael@0 2223 // do it's job synchronously.
michael@0 2224 MaybeDispatchProgressEvents(true);
michael@0 2225 }
michael@0 2226
michael@0 2227 ChangeState(XML_HTTP_REQUEST_DONE, true);
michael@0 2228 if (mTimeoutTimer) {
michael@0 2229 mTimeoutTimer->Cancel();
michael@0 2230 }
michael@0 2231
michael@0 2232 NS_NAMED_LITERAL_STRING(errorStr, ERROR_STR);
michael@0 2233 NS_NAMED_LITERAL_STRING(loadStr, LOAD_STR);
michael@0 2234 DispatchProgressEvent(this,
michael@0 2235 mErrorLoad ? errorStr : loadStr,
michael@0 2236 !mErrorLoad,
michael@0 2237 mLoadTransferred,
michael@0 2238 mErrorLoad ? 0 : mLoadTransferred);
michael@0 2239 if (mErrorLoad && mUpload && !mUploadComplete) {
michael@0 2240 DispatchProgressEvent(mUpload, errorStr, true,
michael@0 2241 mUploadTransferred, mUploadTotal);
michael@0 2242 }
michael@0 2243
michael@0 2244 if (mErrorLoad) {
michael@0 2245 // By nulling out channel here we make it so that Send() can test
michael@0 2246 // for that and throw. Also calling the various status
michael@0 2247 // methods/members will not throw.
michael@0 2248 // This matches what IE does.
michael@0 2249 mChannel = nullptr;
michael@0 2250 mCORSPreflightChannel = nullptr;
michael@0 2251 }
michael@0 2252 }
michael@0 2253
michael@0 2254 NS_IMETHODIMP
michael@0 2255 nsXMLHttpRequest::SendAsBinary(const nsAString &aBody)
michael@0 2256 {
michael@0 2257 ErrorResult rv;
michael@0 2258 SendAsBinary(aBody, rv);
michael@0 2259 return rv.ErrorCode();
michael@0 2260 }
michael@0 2261
michael@0 2262 void
michael@0 2263 nsXMLHttpRequest::SendAsBinary(const nsAString &aBody,
michael@0 2264 ErrorResult& aRv)
michael@0 2265 {
michael@0 2266 char *data = static_cast<char*>(NS_Alloc(aBody.Length() + 1));
michael@0 2267 if (!data) {
michael@0 2268 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
michael@0 2269 return;
michael@0 2270 }
michael@0 2271
michael@0 2272 if (GetOwner() && GetOwner()->GetExtantDoc()) {
michael@0 2273 GetOwner()->GetExtantDoc()->WarnOnceAbout(nsIDocument::eSendAsBinary);
michael@0 2274 }
michael@0 2275
michael@0 2276 nsAString::const_iterator iter, end;
michael@0 2277 aBody.BeginReading(iter);
michael@0 2278 aBody.EndReading(end);
michael@0 2279 char *p = data;
michael@0 2280 while (iter != end) {
michael@0 2281 if (*iter & 0xFF00) {
michael@0 2282 NS_Free(data);
michael@0 2283 aRv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
michael@0 2284 return;
michael@0 2285 }
michael@0 2286 *p++ = static_cast<char>(*iter++);
michael@0 2287 }
michael@0 2288 *p = '\0';
michael@0 2289
michael@0 2290 nsCOMPtr<nsIInputStream> stream;
michael@0 2291 aRv = NS_NewByteInputStream(getter_AddRefs(stream), data, aBody.Length(),
michael@0 2292 NS_ASSIGNMENT_ADOPT);
michael@0 2293 if (aRv.Failed()) {
michael@0 2294 NS_Free(data);
michael@0 2295 return;
michael@0 2296 }
michael@0 2297
michael@0 2298 nsCOMPtr<nsIWritableVariant> variant = new nsVariant();
michael@0 2299
michael@0 2300 aRv = variant->SetAsISupports(stream);
michael@0 2301 if (aRv.Failed()) {
michael@0 2302 return;
michael@0 2303 }
michael@0 2304
michael@0 2305 aRv = Send(variant);
michael@0 2306 }
michael@0 2307
michael@0 2308 static nsresult
michael@0 2309 GetRequestBody(nsIDOMDocument* aDoc, nsIInputStream** aResult,
michael@0 2310 uint64_t* aContentLength, nsACString& aContentType,
michael@0 2311 nsACString& aCharset)
michael@0 2312 {
michael@0 2313 aContentType.AssignLiteral("application/xml");
michael@0 2314 nsAutoString inputEncoding;
michael@0 2315 aDoc->GetInputEncoding(inputEncoding);
michael@0 2316 if (!DOMStringIsNull(inputEncoding)) {
michael@0 2317 CopyUTF16toUTF8(inputEncoding, aCharset);
michael@0 2318 }
michael@0 2319 else {
michael@0 2320 aCharset.AssignLiteral("UTF-8");
michael@0 2321 }
michael@0 2322
michael@0 2323 // Serialize to a stream so that the encoding used will
michael@0 2324 // match the document's.
michael@0 2325 nsresult rv;
michael@0 2326 nsCOMPtr<nsIDOMSerializer> serializer =
michael@0 2327 do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rv);
michael@0 2328 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2329
michael@0 2330 nsCOMPtr<nsIStorageStream> storStream;
michael@0 2331 rv = NS_NewStorageStream(4096, UINT32_MAX, getter_AddRefs(storStream));
michael@0 2332 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2333
michael@0 2334 nsCOMPtr<nsIOutputStream> output;
michael@0 2335 rv = storStream->GetOutputStream(0, getter_AddRefs(output));
michael@0 2336 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2337
michael@0 2338 // Make sure to use the encoding we'll send
michael@0 2339 rv = serializer->SerializeToStream(aDoc, output, aCharset);
michael@0 2340 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2341
michael@0 2342 output->Close();
michael@0 2343
michael@0 2344 uint32_t length;
michael@0 2345 rv = storStream->GetLength(&length);
michael@0 2346 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2347 *aContentLength = length;
michael@0 2348
michael@0 2349 return storStream->NewInputStream(0, aResult);
michael@0 2350 }
michael@0 2351
michael@0 2352 static nsresult
michael@0 2353 GetRequestBody(const nsAString& aString, nsIInputStream** aResult,
michael@0 2354 uint64_t* aContentLength, nsACString& aContentType,
michael@0 2355 nsACString& aCharset)
michael@0 2356 {
michael@0 2357 aContentType.AssignLiteral("text/plain");
michael@0 2358 aCharset.AssignLiteral("UTF-8");
michael@0 2359
michael@0 2360 nsCString converted = NS_ConvertUTF16toUTF8(aString);
michael@0 2361 *aContentLength = converted.Length();
michael@0 2362 return NS_NewCStringInputStream(aResult, converted);
michael@0 2363 }
michael@0 2364
michael@0 2365 static nsresult
michael@0 2366 GetRequestBody(nsIInputStream* aStream, nsIInputStream** aResult,
michael@0 2367 uint64_t* aContentLength, nsACString& aContentType,
michael@0 2368 nsACString& aCharset)
michael@0 2369 {
michael@0 2370 aContentType.AssignLiteral("text/plain");
michael@0 2371 aCharset.Truncate();
michael@0 2372
michael@0 2373 nsresult rv = aStream->Available(aContentLength);
michael@0 2374 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2375
michael@0 2376 NS_ADDREF(*aResult = aStream);
michael@0 2377
michael@0 2378 return NS_OK;
michael@0 2379 }
michael@0 2380
michael@0 2381 static nsresult
michael@0 2382 GetRequestBody(nsIXHRSendable* aSendable, nsIInputStream** aResult, uint64_t* aContentLength,
michael@0 2383 nsACString& aContentType, nsACString& aCharset)
michael@0 2384 {
michael@0 2385 return aSendable->GetSendInfo(aResult, aContentLength, aContentType, aCharset);
michael@0 2386 }
michael@0 2387
michael@0 2388 // Used for array buffers and array buffer views
michael@0 2389 static nsresult
michael@0 2390 GetRequestBody(const uint8_t* aData, uint32_t aDataLength,
michael@0 2391 nsIInputStream** aResult, uint64_t* aContentLength,
michael@0 2392 nsACString& aContentType, nsACString& aCharset)
michael@0 2393 {
michael@0 2394 aContentType.SetIsVoid(true);
michael@0 2395 aCharset.Truncate();
michael@0 2396
michael@0 2397 *aContentLength = aDataLength;
michael@0 2398 const char* data = reinterpret_cast<const char*>(aData);
michael@0 2399
michael@0 2400 nsCOMPtr<nsIInputStream> stream;
michael@0 2401 nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), data, aDataLength,
michael@0 2402 NS_ASSIGNMENT_COPY);
michael@0 2403 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2404
michael@0 2405 stream.forget(aResult);
michael@0 2406
michael@0 2407 return NS_OK;
michael@0 2408 }
michael@0 2409
michael@0 2410 static nsresult
michael@0 2411 GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult, uint64_t* aContentLength,
michael@0 2412 nsACString& aContentType, nsACString& aCharset)
michael@0 2413 {
michael@0 2414 *aResult = nullptr;
michael@0 2415
michael@0 2416 uint16_t dataType;
michael@0 2417 nsresult rv = aBody->GetDataType(&dataType);
michael@0 2418 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2419
michael@0 2420 if (dataType == nsIDataType::VTYPE_INTERFACE ||
michael@0 2421 dataType == nsIDataType::VTYPE_INTERFACE_IS) {
michael@0 2422 nsCOMPtr<nsISupports> supports;
michael@0 2423 nsID *iid;
michael@0 2424 rv = aBody->GetAsInterface(&iid, getter_AddRefs(supports));
michael@0 2425 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2426
michael@0 2427 nsMemory::Free(iid);
michael@0 2428
michael@0 2429 // document?
michael@0 2430 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(supports);
michael@0 2431 if (doc) {
michael@0 2432 return GetRequestBody(doc, aResult, aContentLength, aContentType, aCharset);
michael@0 2433 }
michael@0 2434
michael@0 2435 // nsISupportsString?
michael@0 2436 nsCOMPtr<nsISupportsString> wstr = do_QueryInterface(supports);
michael@0 2437 if (wstr) {
michael@0 2438 nsAutoString string;
michael@0 2439 wstr->GetData(string);
michael@0 2440
michael@0 2441 return GetRequestBody(string, aResult, aContentLength, aContentType, aCharset);
michael@0 2442 }
michael@0 2443
michael@0 2444 // nsIInputStream?
michael@0 2445 nsCOMPtr<nsIInputStream> stream = do_QueryInterface(supports);
michael@0 2446 if (stream) {
michael@0 2447 return GetRequestBody(stream, aResult, aContentLength, aContentType, aCharset);
michael@0 2448 }
michael@0 2449
michael@0 2450 // nsIXHRSendable?
michael@0 2451 nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(supports);
michael@0 2452 if (sendable) {
michael@0 2453 return GetRequestBody(sendable, aResult, aContentLength, aContentType, aCharset);
michael@0 2454 }
michael@0 2455
michael@0 2456 // ArrayBuffer?
michael@0 2457 AutoSafeJSContext cx;
michael@0 2458 JS::Rooted<JS::Value> realVal(cx);
michael@0 2459
michael@0 2460 nsresult rv = aBody->GetAsJSVal(&realVal);
michael@0 2461 if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(realVal)) {
michael@0 2462 JS::Rooted<JSObject*> obj(cx, JSVAL_TO_OBJECT(realVal));
michael@0 2463 if (JS_IsArrayBufferObject(obj)) {
michael@0 2464 ArrayBuffer buf(obj);
michael@0 2465 buf.ComputeLengthAndData();
michael@0 2466 return GetRequestBody(buf.Data(), buf.Length(), aResult,
michael@0 2467 aContentLength, aContentType, aCharset);
michael@0 2468 }
michael@0 2469 }
michael@0 2470 }
michael@0 2471 else if (dataType == nsIDataType::VTYPE_VOID ||
michael@0 2472 dataType == nsIDataType::VTYPE_EMPTY) {
michael@0 2473 // Makes us act as if !aBody, don't upload anything
michael@0 2474 aContentType.AssignLiteral("text/plain");
michael@0 2475 aCharset.AssignLiteral("UTF-8");
michael@0 2476 *aContentLength = 0;
michael@0 2477
michael@0 2478 return NS_OK;
michael@0 2479 }
michael@0 2480
michael@0 2481 char16_t* data = nullptr;
michael@0 2482 uint32_t len = 0;
michael@0 2483 rv = aBody->GetAsWStringWithSize(&len, &data);
michael@0 2484 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2485
michael@0 2486 nsString string;
michael@0 2487 string.Adopt(data, len);
michael@0 2488
michael@0 2489 return GetRequestBody(string, aResult, aContentLength, aContentType, aCharset);
michael@0 2490 }
michael@0 2491
michael@0 2492 /* static */
michael@0 2493 nsresult
michael@0 2494 nsXMLHttpRequest::GetRequestBody(nsIVariant* aVariant,
michael@0 2495 const Nullable<RequestBody>& aBody,
michael@0 2496 nsIInputStream** aResult,
michael@0 2497 uint64_t* aContentLength,
michael@0 2498 nsACString& aContentType, nsACString& aCharset)
michael@0 2499 {
michael@0 2500 if (aVariant) {
michael@0 2501 return ::GetRequestBody(aVariant, aResult, aContentLength, aContentType, aCharset);
michael@0 2502 }
michael@0 2503
michael@0 2504 const RequestBody& body = aBody.Value();
michael@0 2505 RequestBody::Value value = body.GetValue();
michael@0 2506 switch (body.GetType()) {
michael@0 2507 case nsXMLHttpRequest::RequestBody::ArrayBuffer:
michael@0 2508 {
michael@0 2509 const ArrayBuffer* buffer = value.mArrayBuffer;
michael@0 2510 buffer->ComputeLengthAndData();
michael@0 2511 return ::GetRequestBody(buffer->Data(), buffer->Length(), aResult,
michael@0 2512 aContentLength, aContentType, aCharset);
michael@0 2513 }
michael@0 2514 case nsXMLHttpRequest::RequestBody::ArrayBufferView:
michael@0 2515 {
michael@0 2516 const ArrayBufferView* view = value.mArrayBufferView;
michael@0 2517 view->ComputeLengthAndData();
michael@0 2518 return ::GetRequestBody(view->Data(), view->Length(), aResult,
michael@0 2519 aContentLength, aContentType, aCharset);
michael@0 2520 }
michael@0 2521 case nsXMLHttpRequest::RequestBody::Blob:
michael@0 2522 {
michael@0 2523 nsresult rv;
michael@0 2524 nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(value.mBlob, &rv);
michael@0 2525 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2526
michael@0 2527 return ::GetRequestBody(sendable, aResult, aContentLength, aContentType, aCharset);
michael@0 2528 }
michael@0 2529 case nsXMLHttpRequest::RequestBody::Document:
michael@0 2530 {
michael@0 2531 nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(value.mDocument);
michael@0 2532 return ::GetRequestBody(document, aResult, aContentLength, aContentType, aCharset);
michael@0 2533 }
michael@0 2534 case nsXMLHttpRequest::RequestBody::DOMString:
michael@0 2535 {
michael@0 2536 return ::GetRequestBody(*value.mString, aResult, aContentLength,
michael@0 2537 aContentType, aCharset);
michael@0 2538 }
michael@0 2539 case nsXMLHttpRequest::RequestBody::FormData:
michael@0 2540 {
michael@0 2541 MOZ_ASSERT(value.mFormData);
michael@0 2542 return ::GetRequestBody(value.mFormData, aResult, aContentLength,
michael@0 2543 aContentType, aCharset);
michael@0 2544 }
michael@0 2545 case nsXMLHttpRequest::RequestBody::InputStream:
michael@0 2546 {
michael@0 2547 return ::GetRequestBody(value.mStream, aResult, aContentLength,
michael@0 2548 aContentType, aCharset);
michael@0 2549 }
michael@0 2550 default:
michael@0 2551 {
michael@0 2552 return NS_ERROR_FAILURE;
michael@0 2553 }
michael@0 2554 }
michael@0 2555
michael@0 2556 NS_NOTREACHED("Default cases exist for a reason");
michael@0 2557 return NS_OK;
michael@0 2558 }
michael@0 2559
michael@0 2560 /* void send (in nsIVariant aBody); */
michael@0 2561 NS_IMETHODIMP
michael@0 2562 nsXMLHttpRequest::Send(nsIVariant *aBody)
michael@0 2563 {
michael@0 2564 return Send(aBody, Nullable<RequestBody>());
michael@0 2565 }
michael@0 2566
michael@0 2567 nsresult
michael@0 2568 nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
michael@0 2569 {
michael@0 2570 NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
michael@0 2571
michael@0 2572 nsresult rv = CheckInnerWindowCorrectness();
michael@0 2573 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2574
michael@0 2575 // Return error if we're already processing a request
michael@0 2576 if (XML_HTTP_REQUEST_SENT & mState) {
michael@0 2577 return NS_ERROR_FAILURE;
michael@0 2578 }
michael@0 2579
michael@0 2580 // Make sure we've been opened
michael@0 2581 if (!mChannel || !(XML_HTTP_REQUEST_OPENED & mState)) {
michael@0 2582 return NS_ERROR_NOT_INITIALIZED;
michael@0 2583 }
michael@0 2584
michael@0 2585
michael@0 2586 // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
michael@0 2587 // in turn keeps STOP button from becoming active. If the consumer passed in
michael@0 2588 // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
michael@0 2589 // necko won't generate any progress notifications.
michael@0 2590 if (HasListenersFor(nsGkAtoms::onprogress) ||
michael@0 2591 (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress))) {
michael@0 2592 nsLoadFlags loadFlags;
michael@0 2593 mChannel->GetLoadFlags(&loadFlags);
michael@0 2594 loadFlags &= ~nsIRequest::LOAD_BACKGROUND;
michael@0 2595 loadFlags |= nsIRequest::LOAD_NORMAL;
michael@0 2596 mChannel->SetLoadFlags(loadFlags);
michael@0 2597 }
michael@0 2598
michael@0 2599 // XXX We should probably send a warning to the JS console
michael@0 2600 // if there are no event listeners set and we are doing
michael@0 2601 // an asynchronous call.
michael@0 2602
michael@0 2603 // Ignore argument if method is GET, there is no point in trying to
michael@0 2604 // upload anything
michael@0 2605 nsAutoCString method;
michael@0 2606 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
michael@0 2607
michael@0 2608 if (httpChannel) {
michael@0 2609 httpChannel->GetRequestMethod(method); // If GET, method name will be uppercase
michael@0 2610
michael@0 2611 if (!IsSystemXHR()) {
michael@0 2612 // Get the referrer for the request.
michael@0 2613 //
michael@0 2614 // If it weren't for history.push/replaceState, we could just use the
michael@0 2615 // principal's URI here. But since we want changes to the URI effected
michael@0 2616 // by push/replaceState to be reflected in the XHR referrer, we have to
michael@0 2617 // be more clever.
michael@0 2618 //
michael@0 2619 // If the document's original URI (before any push/replaceStates) matches
michael@0 2620 // our principal, then we use the document's current URI (after
michael@0 2621 // push/replaceStates). Otherwise (if the document is, say, a data:
michael@0 2622 // URI), we just use the principal's URI.
michael@0 2623
michael@0 2624 nsCOMPtr<nsIURI> principalURI;
michael@0 2625 mPrincipal->GetURI(getter_AddRefs(principalURI));
michael@0 2626
michael@0 2627 nsIScriptContext* sc = GetContextForEventHandlers(&rv);
michael@0 2628 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2629 nsCOMPtr<nsIDocument> doc =
michael@0 2630 nsContentUtils::GetDocumentFromScriptContext(sc);
michael@0 2631
michael@0 2632 nsCOMPtr<nsIURI> docCurURI;
michael@0 2633 nsCOMPtr<nsIURI> docOrigURI;
michael@0 2634 if (doc) {
michael@0 2635 docCurURI = doc->GetDocumentURI();
michael@0 2636 docOrigURI = doc->GetOriginalURI();
michael@0 2637 }
michael@0 2638
michael@0 2639 nsCOMPtr<nsIURI> referrerURI;
michael@0 2640
michael@0 2641 if (principalURI && docCurURI && docOrigURI) {
michael@0 2642 bool equal = false;
michael@0 2643 principalURI->Equals(docOrigURI, &equal);
michael@0 2644 if (equal) {
michael@0 2645 referrerURI = docCurURI;
michael@0 2646 }
michael@0 2647 }
michael@0 2648
michael@0 2649 if (!referrerURI)
michael@0 2650 referrerURI = principalURI;
michael@0 2651
michael@0 2652 httpChannel->SetReferrer(referrerURI);
michael@0 2653 }
michael@0 2654
michael@0 2655 // Some extensions override the http protocol handler and provide their own
michael@0 2656 // implementation. The channels returned from that implementation doesn't
michael@0 2657 // seem to always implement the nsIUploadChannel2 interface, presumably
michael@0 2658 // because it's a new interface.
michael@0 2659 // Eventually we should remove this and simply require that http channels
michael@0 2660 // implement the new interface.
michael@0 2661 // See bug 529041
michael@0 2662 nsCOMPtr<nsIUploadChannel2> uploadChannel2 =
michael@0 2663 do_QueryInterface(httpChannel);
michael@0 2664 if (!uploadChannel2) {
michael@0 2665 nsCOMPtr<nsIConsoleService> consoleService =
michael@0 2666 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
michael@0 2667 if (consoleService) {
michael@0 2668 consoleService->LogStringMessage(NS_LITERAL_STRING(
michael@0 2669 "Http channel implementation doesn't support nsIUploadChannel2. An extension has supplied a non-functional http protocol handler. This will break behavior and in future releases not work at all."
michael@0 2670 ).get());
michael@0 2671 }
michael@0 2672 }
michael@0 2673 }
michael@0 2674
michael@0 2675 mUploadTransferred = 0;
michael@0 2676 mUploadTotal = 0;
michael@0 2677 // By default we don't have any upload, so mark upload complete.
michael@0 2678 mUploadComplete = true;
michael@0 2679 mErrorLoad = false;
michael@0 2680 mLoadLengthComputable = false;
michael@0 2681 mLoadTotal = 0;
michael@0 2682 if ((aVariant || !aBody.IsNull()) && httpChannel &&
michael@0 2683 !method.LowerCaseEqualsLiteral("get") &&
michael@0 2684 !method.LowerCaseEqualsLiteral("head")) {
michael@0 2685
michael@0 2686 nsAutoCString charset;
michael@0 2687 nsAutoCString defaultContentType;
michael@0 2688 nsCOMPtr<nsIInputStream> postDataStream;
michael@0 2689
michael@0 2690 rv = GetRequestBody(aVariant, aBody, getter_AddRefs(postDataStream),
michael@0 2691 &mUploadTotal, defaultContentType, charset);
michael@0 2692 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2693
michael@0 2694 if (postDataStream) {
michael@0 2695 // If no content type header was set by the client, we set it to
michael@0 2696 // application/xml.
michael@0 2697 nsAutoCString contentType;
michael@0 2698 if (NS_FAILED(httpChannel->
michael@0 2699 GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
michael@0 2700 contentType)) ||
michael@0 2701 contentType.IsEmpty()) {
michael@0 2702 contentType = defaultContentType;
michael@0 2703 }
michael@0 2704
michael@0 2705 // We don't want to set a charset for streams.
michael@0 2706 if (!charset.IsEmpty()) {
michael@0 2707 nsAutoCString specifiedCharset;
michael@0 2708 bool haveCharset;
michael@0 2709 int32_t charsetStart, charsetEnd;
michael@0 2710 rv = NS_ExtractCharsetFromContentType(contentType, specifiedCharset,
michael@0 2711 &haveCharset, &charsetStart,
michael@0 2712 &charsetEnd);
michael@0 2713 if (NS_SUCCEEDED(rv)) {
michael@0 2714 // special case: the extracted charset is quoted with single quotes
michael@0 2715 // -- for the purpose of preserving what was set we want to handle
michael@0 2716 // them as delimiters (although they aren't really)
michael@0 2717 if (specifiedCharset.Length() >= 2 &&
michael@0 2718 specifiedCharset.First() == '\'' &&
michael@0 2719 specifiedCharset.Last() == '\'') {
michael@0 2720 specifiedCharset = Substring(specifiedCharset, 1,
michael@0 2721 specifiedCharset.Length() - 2);
michael@0 2722 }
michael@0 2723
michael@0 2724 // If the content-type the page set already has a charset parameter,
michael@0 2725 // and it's the same charset, up to case, as |charset|, just send the
michael@0 2726 // page-set content-type header. Apparently at least
michael@0 2727 // google-web-toolkit is broken and relies on the exact case of its
michael@0 2728 // charset parameter, which makes things break if we use |charset|
michael@0 2729 // (which is always a fully resolved charset per our charset alias
michael@0 2730 // table, hence might be differently cased).
michael@0 2731 if (!specifiedCharset.Equals(charset,
michael@0 2732 nsCaseInsensitiveCStringComparator())) {
michael@0 2733 nsAutoCString newCharset("; charset=");
michael@0 2734 newCharset.Append(charset);
michael@0 2735 contentType.Replace(charsetStart, charsetEnd - charsetStart,
michael@0 2736 newCharset);
michael@0 2737 }
michael@0 2738 }
michael@0 2739 }
michael@0 2740
michael@0 2741 // If necessary, wrap the stream in a buffered stream so as to guarantee
michael@0 2742 // support for our upload when calling ExplicitSetUploadStream.
michael@0 2743 if (!NS_InputStreamIsBuffered(postDataStream)) {
michael@0 2744 nsCOMPtr<nsIInputStream> bufferedStream;
michael@0 2745 rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
michael@0 2746 postDataStream,
michael@0 2747 4096);
michael@0 2748 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2749
michael@0 2750 postDataStream = bufferedStream;
michael@0 2751 }
michael@0 2752
michael@0 2753 mUploadComplete = false;
michael@0 2754
michael@0 2755 // We want to use a newer version of the upload channel that won't
michael@0 2756 // ignore the necessary headers for an empty Content-Type.
michael@0 2757 nsCOMPtr<nsIUploadChannel2> uploadChannel2(do_QueryInterface(httpChannel));
michael@0 2758 // This assertion will fire if buggy extensions are installed
michael@0 2759 NS_ASSERTION(uploadChannel2, "http must support nsIUploadChannel2");
michael@0 2760 if (uploadChannel2) {
michael@0 2761 uploadChannel2->ExplicitSetUploadStream(postDataStream, contentType,
michael@0 2762 mUploadTotal, method, false);
michael@0 2763 }
michael@0 2764 else {
michael@0 2765 // http channel doesn't support the new nsIUploadChannel2. Emulate
michael@0 2766 // as best we can using nsIUploadChannel
michael@0 2767 if (contentType.IsEmpty()) {
michael@0 2768 contentType.AssignLiteral("application/octet-stream");
michael@0 2769 }
michael@0 2770 nsCOMPtr<nsIUploadChannel> uploadChannel =
michael@0 2771 do_QueryInterface(httpChannel);
michael@0 2772 uploadChannel->SetUploadStream(postDataStream, contentType, mUploadTotal);
michael@0 2773 // Reset the method to its original value
michael@0 2774 httpChannel->SetRequestMethod(method);
michael@0 2775 }
michael@0 2776 }
michael@0 2777 }
michael@0 2778
michael@0 2779 if (httpChannel) {
michael@0 2780 nsAutoCString contentTypeHeader;
michael@0 2781 rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
michael@0 2782 contentTypeHeader);
michael@0 2783 if (NS_SUCCEEDED(rv)) {
michael@0 2784 nsAutoCString contentType, charset;
michael@0 2785 rv = NS_ParseContentType(contentTypeHeader, contentType, charset);
michael@0 2786 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2787
michael@0 2788 if (!contentType.LowerCaseEqualsLiteral("text/plain") &&
michael@0 2789 !contentType.LowerCaseEqualsLiteral("application/x-www-form-urlencoded") &&
michael@0 2790 !contentType.LowerCaseEqualsLiteral("multipart/form-data")) {
michael@0 2791 mCORSUnsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type"));
michael@0 2792 }
michael@0 2793 }
michael@0 2794 }
michael@0 2795
michael@0 2796 ResetResponse();
michael@0 2797
michael@0 2798 rv = CheckChannelForCrossSiteRequest(mChannel);
michael@0 2799 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2800
michael@0 2801 bool withCredentials = !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
michael@0 2802
michael@0 2803 // Hook us up to listen to redirects and the like
michael@0 2804 mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
michael@0 2805 mChannel->SetNotificationCallbacks(this);
michael@0 2806
michael@0 2807 // Blocking gets are common enough out of XHR that we should mark
michael@0 2808 // the channel slow by default for pipeline purposes
michael@0 2809 AddLoadFlags(mChannel, nsIRequest::INHIBIT_PIPELINE);
michael@0 2810
michael@0 2811 nsCOMPtr<nsIHttpChannelInternal>
michael@0 2812 internalHttpChannel(do_QueryInterface(mChannel));
michael@0 2813 if (internalHttpChannel) {
michael@0 2814 // we never let XHR be blocked by head CSS/JS loads to avoid
michael@0 2815 // potential deadlock where server generation of CSS/JS requires
michael@0 2816 // an XHR signal.
michael@0 2817 internalHttpChannel->SetLoadUnblocked(true);
michael@0 2818
michael@0 2819 // Disable Necko-internal response timeouts.
michael@0 2820 internalHttpChannel->SetResponseTimeoutEnabled(false);
michael@0 2821 }
michael@0 2822
michael@0 2823 nsCOMPtr<nsIStreamListener> listener = this;
michael@0 2824 if (!IsSystemXHR()) {
michael@0 2825 // Always create a nsCORSListenerProxy here even if it's
michael@0 2826 // a same-origin request right now, since it could be redirected.
michael@0 2827 nsRefPtr<nsCORSListenerProxy> corsListener =
michael@0 2828 new nsCORSListenerProxy(listener, mPrincipal, withCredentials);
michael@0 2829 rv = corsListener->Init(mChannel, true);
michael@0 2830 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2831 listener = corsListener;
michael@0 2832 }
michael@0 2833 else {
michael@0 2834 // Because of bug 682305, we can't let listener be the XHR object itself
michael@0 2835 // because JS wouldn't be able to use it. So if we haven't otherwise
michael@0 2836 // created a listener around 'this', do so now.
michael@0 2837
michael@0 2838 listener = new nsStreamListenerWrapper(listener);
michael@0 2839 }
michael@0 2840
michael@0 2841 if (mIsAnon) {
michael@0 2842 AddLoadFlags(mChannel, nsIRequest::LOAD_ANONYMOUS);
michael@0 2843 }
michael@0 2844 else {
michael@0 2845 AddLoadFlags(mChannel, nsIChannel::LOAD_EXPLICIT_CREDENTIALS);
michael@0 2846 }
michael@0 2847
michael@0 2848 NS_ASSERTION(listener != this,
michael@0 2849 "Using an object as a listener that can't be exposed to JS");
michael@0 2850
michael@0 2851 // Bypass the network cache in cases where it makes no sense:
michael@0 2852 // POST responses are always unique, and we provide no API that would
michael@0 2853 // allow our consumers to specify a "cache key" to access old POST
michael@0 2854 // responses, so they are not worth caching.
michael@0 2855 if (method.EqualsLiteral("POST")) {
michael@0 2856 AddLoadFlags(mChannel,
michael@0 2857 nsIRequest::LOAD_BYPASS_CACHE | nsIRequest::INHIBIT_CACHING);
michael@0 2858 }
michael@0 2859 // When we are sync loading, we need to bypass the local cache when it would
michael@0 2860 // otherwise block us waiting for exclusive access to the cache. If we don't
michael@0 2861 // do this, then we could dead lock in some cases (see bug 309424).
michael@0 2862 else if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
michael@0 2863 AddLoadFlags(mChannel,
michael@0 2864 nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
michael@0 2865 }
michael@0 2866
michael@0 2867 // Since we expect XML data, set the type hint accordingly
michael@0 2868 // if the channel doesn't know any content type.
michael@0 2869 // This means that we always try to parse local files as XML
michael@0 2870 // ignoring return value, as this is not critical
michael@0 2871 nsAutoCString contentType;
michael@0 2872 if (NS_FAILED(mChannel->GetContentType(contentType)) ||
michael@0 2873 contentType.IsEmpty() ||
michael@0 2874 contentType.Equals(UNKNOWN_CONTENT_TYPE)) {
michael@0 2875 mChannel->SetContentType(NS_LITERAL_CSTRING("application/xml"));
michael@0 2876 }
michael@0 2877
michael@0 2878 // We're about to send the request. Start our timeout.
michael@0 2879 mRequestSentTime = PR_Now();
michael@0 2880 StartTimeoutTimer();
michael@0 2881
michael@0 2882 // Set up the preflight if needed
michael@0 2883 if (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT) {
michael@0 2884 // Check to see if this initial OPTIONS request has already been cached
michael@0 2885 // in our special Access Control Cache.
michael@0 2886
michael@0 2887 rv = NS_StartCORSPreflight(mChannel, listener,
michael@0 2888 mPrincipal, withCredentials,
michael@0 2889 mCORSUnsafeHeaders,
michael@0 2890 getter_AddRefs(mCORSPreflightChannel));
michael@0 2891 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2892 }
michael@0 2893 else {
michael@0 2894 // Start reading from the channel
michael@0 2895 rv = mChannel->AsyncOpen(listener, nullptr);
michael@0 2896 }
michael@0 2897
michael@0 2898 if (NS_FAILED(rv)) {
michael@0 2899 // Drop our ref to the channel to avoid cycles
michael@0 2900 mChannel = nullptr;
michael@0 2901 mCORSPreflightChannel = nullptr;
michael@0 2902 return rv;
michael@0 2903 }
michael@0 2904
michael@0 2905 // Either AsyncOpen was called, or CORS will open the channel later.
michael@0 2906 mWaitingForOnStopRequest = true;
michael@0 2907
michael@0 2908 // If we're synchronous, spin an event loop here and wait
michael@0 2909 if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
michael@0 2910 mState |= XML_HTTP_REQUEST_SYNCLOOPING;
michael@0 2911
michael@0 2912 nsCOMPtr<nsIDocument> suspendedDoc;
michael@0 2913 nsCOMPtr<nsIRunnable> resumeTimeoutRunnable;
michael@0 2914 if (GetOwner()) {
michael@0 2915 nsCOMPtr<nsIDOMWindow> topWindow;
michael@0 2916 if (NS_SUCCEEDED(GetOwner()->GetTop(getter_AddRefs(topWindow)))) {
michael@0 2917 nsCOMPtr<nsPIDOMWindow> suspendedWindow(do_QueryInterface(topWindow));
michael@0 2918 if (suspendedWindow &&
michael@0 2919 (suspendedWindow = suspendedWindow->GetCurrentInnerWindow())) {
michael@0 2920 suspendedDoc = suspendedWindow->GetExtantDoc();
michael@0 2921 if (suspendedDoc) {
michael@0 2922 suspendedDoc->SuppressEventHandling(nsIDocument::eEvents);
michael@0 2923 }
michael@0 2924 suspendedWindow->SuspendTimeouts(1, false);
michael@0 2925 resumeTimeoutRunnable = new nsResumeTimeoutsEvent(suspendedWindow);
michael@0 2926 }
michael@0 2927 }
michael@0 2928 }
michael@0 2929
michael@0 2930 ChangeState(XML_HTTP_REQUEST_SENT);
michael@0 2931
michael@0 2932 {
michael@0 2933 nsAutoSyncOperation sync(suspendedDoc);
michael@0 2934 // Note, calling ChangeState may have cleared
michael@0 2935 // XML_HTTP_REQUEST_SYNCLOOPING flag.
michael@0 2936 nsIThread *thread = NS_GetCurrentThread();
michael@0 2937 while (mState & XML_HTTP_REQUEST_SYNCLOOPING) {
michael@0 2938 if (!NS_ProcessNextEvent(thread)) {
michael@0 2939 rv = NS_ERROR_UNEXPECTED;
michael@0 2940 break;
michael@0 2941 }
michael@0 2942 }
michael@0 2943 }
michael@0 2944
michael@0 2945 if (suspendedDoc) {
michael@0 2946 suspendedDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents,
michael@0 2947 true);
michael@0 2948 }
michael@0 2949
michael@0 2950 if (resumeTimeoutRunnable) {
michael@0 2951 NS_DispatchToCurrentThread(resumeTimeoutRunnable);
michael@0 2952 }
michael@0 2953 } else {
michael@0 2954 // Now that we've successfully opened the channel, we can change state. Note
michael@0 2955 // that this needs to come after the AsyncOpen() and rv check, because this
michael@0 2956 // can run script that would try to restart this request, and that could end
michael@0 2957 // up doing our AsyncOpen on a null channel if the reentered AsyncOpen fails.
michael@0 2958 ChangeState(XML_HTTP_REQUEST_SENT);
michael@0 2959 if (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress)) {
michael@0 2960 StartProgressEventTimer();
michael@0 2961 }
michael@0 2962 DispatchProgressEvent(this, NS_LITERAL_STRING(LOADSTART_STR), false,
michael@0 2963 0, 0);
michael@0 2964 if (mUpload && !mUploadComplete) {
michael@0 2965 DispatchProgressEvent(mUpload, NS_LITERAL_STRING(LOADSTART_STR), true,
michael@0 2966 0, mUploadTotal);
michael@0 2967 }
michael@0 2968 }
michael@0 2969
michael@0 2970 if (!mChannel) {
michael@0 2971 return NS_ERROR_FAILURE;
michael@0 2972 }
michael@0 2973
michael@0 2974 return rv;
michael@0 2975 }
michael@0 2976
michael@0 2977 /* void setRequestHeader (in ByteString header, in ByteString value); */
michael@0 2978 // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader
michael@0 2979 NS_IMETHODIMP
michael@0 2980 nsXMLHttpRequest::SetRequestHeader(const nsACString& header,
michael@0 2981 const nsACString& value)
michael@0 2982 {
michael@0 2983 // Step 1 and 2
michael@0 2984 if (!(mState & XML_HTTP_REQUEST_OPENED)) {
michael@0 2985 return NS_ERROR_DOM_INVALID_STATE_ERR;
michael@0 2986 }
michael@0 2987 NS_ASSERTION(mChannel, "mChannel must be valid if we're OPENED.");
michael@0 2988
michael@0 2989 // Step 3
michael@0 2990 // Make sure we don't store an invalid header name in mCORSUnsafeHeaders
michael@0 2991 if (!IsValidHTTPToken(header)) { // XXX nsHttp::IsValidToken?
michael@0 2992 return NS_ERROR_DOM_SYNTAX_ERR;
michael@0 2993 }
michael@0 2994
michael@0 2995 // Check that we haven't already opened the channel. We can't rely on
michael@0 2996 // the channel throwing from mChannel->SetRequestHeader since we might
michael@0 2997 // still be waiting for mCORSPreflightChannel to actually open mChannel
michael@0 2998 if (mCORSPreflightChannel) {
michael@0 2999 bool pending;
michael@0 3000 nsresult rv = mCORSPreflightChannel->IsPending(&pending);
michael@0 3001 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3002
michael@0 3003 if (pending) {
michael@0 3004 return NS_ERROR_IN_PROGRESS;
michael@0 3005 }
michael@0 3006 }
michael@0 3007
michael@0 3008 if (!mChannel) // open() initializes mChannel, and open()
michael@0 3009 return NS_ERROR_FAILURE; // must be called before first setRequestHeader()
michael@0 3010
michael@0 3011 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
michael@0 3012 if (!httpChannel) {
michael@0 3013 return NS_OK;
michael@0 3014 }
michael@0 3015
michael@0 3016 // We will merge XHR headers, per the spec (secion 4.6.2) unless:
michael@0 3017 // 1 - The caller is privileged and setting an invalid header,
michael@0 3018 // or
michael@0 3019 // 2 - we have not yet explicitly set that header; this allows web
michael@0 3020 // content to override default headers the first time they set them.
michael@0 3021 bool mergeHeaders = true;
michael@0 3022
michael@0 3023 // Prevent modification to certain HTTP headers (see bug 302263), unless
michael@0 3024 // the executing script is privileged.
michael@0 3025 bool isInvalidHeader = false;
michael@0 3026 static const char *kInvalidHeaders[] = {
michael@0 3027 "accept-charset", "accept-encoding", "access-control-request-headers",
michael@0 3028 "access-control-request-method", "connection", "content-length",
michael@0 3029 "cookie", "cookie2", "content-transfer-encoding", "date", "dnt",
michael@0 3030 "expect", "host", "keep-alive", "origin", "referer", "te", "trailer",
michael@0 3031 "transfer-encoding", "upgrade", "user-agent", "via"
michael@0 3032 };
michael@0 3033 uint32_t i;
michael@0 3034 for (i = 0; i < ArrayLength(kInvalidHeaders); ++i) {
michael@0 3035 if (header.LowerCaseEqualsASCII(kInvalidHeaders[i])) {
michael@0 3036 isInvalidHeader = true;
michael@0 3037 break;
michael@0 3038 }
michael@0 3039 }
michael@0 3040
michael@0 3041 if (!IsSystemXHR()) {
michael@0 3042 // Step 5: Check for dangerous headers.
michael@0 3043 if (isInvalidHeader) {
michael@0 3044 NS_WARNING("refusing to set request header");
michael@0 3045 return NS_OK;
michael@0 3046 }
michael@0 3047 if (StringBeginsWith(header, NS_LITERAL_CSTRING("proxy-"),
michael@0 3048 nsCaseInsensitiveCStringComparator()) ||
michael@0 3049 StringBeginsWith(header, NS_LITERAL_CSTRING("sec-"),
michael@0 3050 nsCaseInsensitiveCStringComparator())) {
michael@0 3051 NS_WARNING("refusing to set request header");
michael@0 3052 return NS_OK;
michael@0 3053 }
michael@0 3054
michael@0 3055 // Check for dangerous cross-site headers
michael@0 3056 bool safeHeader = IsSystemXHR();
michael@0 3057 if (!safeHeader) {
michael@0 3058 // Content-Type isn't always safe, but we'll deal with it in Send()
michael@0 3059 const char *kCrossOriginSafeHeaders[] = {
michael@0 3060 "accept", "accept-language", "content-language", "content-type",
michael@0 3061 "last-event-id"
michael@0 3062 };
michael@0 3063 for (i = 0; i < ArrayLength(kCrossOriginSafeHeaders); ++i) {
michael@0 3064 if (header.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
michael@0 3065 safeHeader = true;
michael@0 3066 break;
michael@0 3067 }
michael@0 3068 }
michael@0 3069 }
michael@0 3070
michael@0 3071 if (!safeHeader) {
michael@0 3072 if (!mCORSUnsafeHeaders.Contains(header)) {
michael@0 3073 mCORSUnsafeHeaders.AppendElement(header);
michael@0 3074 }
michael@0 3075 }
michael@0 3076 } else {
michael@0 3077 // Case 1 above
michael@0 3078 if (isInvalidHeader) {
michael@0 3079 mergeHeaders = false;
michael@0 3080 }
michael@0 3081 }
michael@0 3082
michael@0 3083 if (!mAlreadySetHeaders.Contains(header)) {
michael@0 3084 // Case 2 above
michael@0 3085 mergeHeaders = false;
michael@0 3086 }
michael@0 3087
michael@0 3088 // Merge headers depending on what we decided above.
michael@0 3089 nsresult rv = httpChannel->SetRequestHeader(header, value, mergeHeaders);
michael@0 3090 if (rv == NS_ERROR_INVALID_ARG) {
michael@0 3091 return NS_ERROR_DOM_SYNTAX_ERR;
michael@0 3092 }
michael@0 3093 if (NS_SUCCEEDED(rv)) {
michael@0 3094 // Remember that we've set this header, so subsequent set operations will merge values.
michael@0 3095 mAlreadySetHeaders.PutEntry(nsCString(header));
michael@0 3096
michael@0 3097 // We'll want to duplicate this header for any replacement channels (eg. on redirect)
michael@0 3098 RequestHeader reqHeader = {
michael@0 3099 nsCString(header), nsCString(value)
michael@0 3100 };
michael@0 3101 mModifiedRequestHeaders.AppendElement(reqHeader);
michael@0 3102 }
michael@0 3103 return rv;
michael@0 3104 }
michael@0 3105
michael@0 3106 /* attribute unsigned long timeout; */
michael@0 3107 NS_IMETHODIMP
michael@0 3108 nsXMLHttpRequest::GetTimeout(uint32_t *aTimeout)
michael@0 3109 {
michael@0 3110 *aTimeout = Timeout();
michael@0 3111 return NS_OK;
michael@0 3112 }
michael@0 3113
michael@0 3114 NS_IMETHODIMP
michael@0 3115 nsXMLHttpRequest::SetTimeout(uint32_t aTimeout)
michael@0 3116 {
michael@0 3117 ErrorResult rv;
michael@0 3118 SetTimeout(aTimeout, rv);
michael@0 3119 return rv.ErrorCode();
michael@0 3120 }
michael@0 3121
michael@0 3122 void
michael@0 3123 nsXMLHttpRequest::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
michael@0 3124 {
michael@0 3125 if (!(mState & (XML_HTTP_REQUEST_ASYNC | XML_HTTP_REQUEST_UNSENT)) &&
michael@0 3126 HasOrHasHadOwner()) {
michael@0 3127 /* Timeout is not supported for synchronous requests with an owning window,
michael@0 3128 per XHR2 spec. */
michael@0 3129 LogMessage("TimeoutSyncXHRWarning", GetOwner());
michael@0 3130 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
michael@0 3131 return;
michael@0 3132 }
michael@0 3133
michael@0 3134 mTimeoutMilliseconds = aTimeout;
michael@0 3135 if (mRequestSentTime) {
michael@0 3136 StartTimeoutTimer();
michael@0 3137 }
michael@0 3138 }
michael@0 3139
michael@0 3140 void
michael@0 3141 nsXMLHttpRequest::StartTimeoutTimer()
michael@0 3142 {
michael@0 3143 NS_ABORT_IF_FALSE(mRequestSentTime,
michael@0 3144 "StartTimeoutTimer mustn't be called before the request was sent!");
michael@0 3145 if (mState & XML_HTTP_REQUEST_DONE) {
michael@0 3146 // do nothing!
michael@0 3147 return;
michael@0 3148 }
michael@0 3149
michael@0 3150 if (mTimeoutTimer) {
michael@0 3151 mTimeoutTimer->Cancel();
michael@0 3152 }
michael@0 3153
michael@0 3154 if (!mTimeoutMilliseconds) {
michael@0 3155 return;
michael@0 3156 }
michael@0 3157
michael@0 3158 if (!mTimeoutTimer) {
michael@0 3159 mTimeoutTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
michael@0 3160 }
michael@0 3161 uint32_t elapsed =
michael@0 3162 (uint32_t)((PR_Now() - mRequestSentTime) / PR_USEC_PER_MSEC);
michael@0 3163 mTimeoutTimer->InitWithCallback(
michael@0 3164 this,
michael@0 3165 mTimeoutMilliseconds > elapsed ? mTimeoutMilliseconds - elapsed : 0,
michael@0 3166 nsITimer::TYPE_ONE_SHOT
michael@0 3167 );
michael@0 3168 }
michael@0 3169
michael@0 3170 /* readonly attribute unsigned short readyState; */
michael@0 3171 NS_IMETHODIMP
michael@0 3172 nsXMLHttpRequest::GetReadyState(uint16_t *aState)
michael@0 3173 {
michael@0 3174 *aState = ReadyState();
michael@0 3175 return NS_OK;
michael@0 3176 }
michael@0 3177
michael@0 3178 uint16_t
michael@0 3179 nsXMLHttpRequest::ReadyState()
michael@0 3180 {
michael@0 3181 // Translate some of our internal states for external consumers
michael@0 3182 if (mState & XML_HTTP_REQUEST_UNSENT) {
michael@0 3183 return UNSENT;
michael@0 3184 }
michael@0 3185 if (mState & (XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
michael@0 3186 return OPENED;
michael@0 3187 }
michael@0 3188 if (mState & XML_HTTP_REQUEST_HEADERS_RECEIVED) {
michael@0 3189 return HEADERS_RECEIVED;
michael@0 3190 }
michael@0 3191 if (mState & XML_HTTP_REQUEST_LOADING) {
michael@0 3192 return LOADING;
michael@0 3193 }
michael@0 3194 MOZ_ASSERT(mState & XML_HTTP_REQUEST_DONE);
michael@0 3195 return DONE;
michael@0 3196 }
michael@0 3197
michael@0 3198 /* void overrideMimeType(in DOMString mimetype); */
michael@0 3199 NS_IMETHODIMP
michael@0 3200 nsXMLHttpRequest::SlowOverrideMimeType(const nsAString& aMimeType)
michael@0 3201 {
michael@0 3202 OverrideMimeType(aMimeType);
michael@0 3203 return NS_OK;
michael@0 3204 }
michael@0 3205
michael@0 3206 /* attribute boolean mozBackgroundRequest; */
michael@0 3207 NS_IMETHODIMP
michael@0 3208 nsXMLHttpRequest::GetMozBackgroundRequest(bool *_retval)
michael@0 3209 {
michael@0 3210 *_retval = MozBackgroundRequest();
michael@0 3211 return NS_OK;
michael@0 3212 }
michael@0 3213
michael@0 3214 bool
michael@0 3215 nsXMLHttpRequest::MozBackgroundRequest()
michael@0 3216 {
michael@0 3217 return !!(mState & XML_HTTP_REQUEST_BACKGROUND);
michael@0 3218 }
michael@0 3219
michael@0 3220 NS_IMETHODIMP
michael@0 3221 nsXMLHttpRequest::SetMozBackgroundRequest(bool aMozBackgroundRequest)
michael@0 3222 {
michael@0 3223 nsresult rv = NS_OK;
michael@0 3224 SetMozBackgroundRequest(aMozBackgroundRequest, rv);
michael@0 3225 return rv;
michael@0 3226 }
michael@0 3227
michael@0 3228 void
michael@0 3229 nsXMLHttpRequest::SetMozBackgroundRequest(bool aMozBackgroundRequest, nsresult& aRv)
michael@0 3230 {
michael@0 3231 if (!IsSystemXHR()) {
michael@0 3232 aRv = NS_ERROR_DOM_SECURITY_ERR;
michael@0 3233 return;
michael@0 3234 }
michael@0 3235
michael@0 3236 if (!(mState & XML_HTTP_REQUEST_UNSENT)) {
michael@0 3237 // Can't change this while we're in the middle of something.
michael@0 3238 aRv = NS_ERROR_IN_PROGRESS;
michael@0 3239 return;
michael@0 3240 }
michael@0 3241
michael@0 3242 if (aMozBackgroundRequest) {
michael@0 3243 mState |= XML_HTTP_REQUEST_BACKGROUND;
michael@0 3244 } else {
michael@0 3245 mState &= ~XML_HTTP_REQUEST_BACKGROUND;
michael@0 3246 }
michael@0 3247 }
michael@0 3248
michael@0 3249 /* attribute boolean withCredentials; */
michael@0 3250 NS_IMETHODIMP
michael@0 3251 nsXMLHttpRequest::GetWithCredentials(bool *_retval)
michael@0 3252 {
michael@0 3253 *_retval = WithCredentials();
michael@0 3254 return NS_OK;
michael@0 3255 }
michael@0 3256
michael@0 3257 bool
michael@0 3258 nsXMLHttpRequest::WithCredentials()
michael@0 3259 {
michael@0 3260 return !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
michael@0 3261 }
michael@0 3262
michael@0 3263 NS_IMETHODIMP
michael@0 3264 nsXMLHttpRequest::SetWithCredentials(bool aWithCredentials)
michael@0 3265 {
michael@0 3266 ErrorResult rv;
michael@0 3267 SetWithCredentials(aWithCredentials, rv);
michael@0 3268 return rv.ErrorCode();
michael@0 3269 }
michael@0 3270
michael@0 3271 void
michael@0 3272 nsXMLHttpRequest::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
michael@0 3273 {
michael@0 3274 // Return error if we're already processing a request
michael@0 3275 if (XML_HTTP_REQUEST_SENT & mState) {
michael@0 3276 aRv = NS_ERROR_FAILURE;
michael@0 3277 return;
michael@0 3278 }
michael@0 3279
michael@0 3280 // sync request is not allowed setting withCredentials in window context
michael@0 3281 if (HasOrHasHadOwner() &&
michael@0 3282 !(mState & (XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC))) {
michael@0 3283 LogMessage("WithCredentialsSyncXHRWarning", GetOwner());
michael@0 3284 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
michael@0 3285 return;
michael@0 3286 }
michael@0 3287
michael@0 3288 if (aWithCredentials) {
michael@0 3289 mState |= XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
michael@0 3290 } else {
michael@0 3291 mState &= ~XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
michael@0 3292 }
michael@0 3293 }
michael@0 3294
michael@0 3295 nsresult
michael@0 3296 nsXMLHttpRequest::ChangeState(uint32_t aState, bool aBroadcast)
michael@0 3297 {
michael@0 3298 // If we are setting one of the mutually exclusive states,
michael@0 3299 // unset those state bits first.
michael@0 3300 if (aState & XML_HTTP_REQUEST_LOADSTATES) {
michael@0 3301 mState &= ~XML_HTTP_REQUEST_LOADSTATES;
michael@0 3302 }
michael@0 3303 mState |= aState;
michael@0 3304 nsresult rv = NS_OK;
michael@0 3305
michael@0 3306 if (mProgressNotifier &&
michael@0 3307 !(aState & (XML_HTTP_REQUEST_HEADERS_RECEIVED | XML_HTTP_REQUEST_LOADING))) {
michael@0 3308 mProgressTimerIsActive = false;
michael@0 3309 mProgressNotifier->Cancel();
michael@0 3310 }
michael@0 3311
michael@0 3312 if ((aState & XML_HTTP_REQUEST_LOADSTATES) && // Broadcast load states only
michael@0 3313 aState != XML_HTTP_REQUEST_SENT && // And not internal ones
michael@0 3314 aBroadcast &&
michael@0 3315 (mState & XML_HTTP_REQUEST_ASYNC ||
michael@0 3316 aState & XML_HTTP_REQUEST_OPENED ||
michael@0 3317 aState & XML_HTTP_REQUEST_DONE)) {
michael@0 3318 nsCOMPtr<nsIDOMEvent> event;
michael@0 3319 rv = CreateReadystatechangeEvent(getter_AddRefs(event));
michael@0 3320 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3321
michael@0 3322 DispatchDOMEvent(nullptr, event, nullptr, nullptr);
michael@0 3323 }
michael@0 3324
michael@0 3325 return rv;
michael@0 3326 }
michael@0 3327
michael@0 3328 /*
michael@0 3329 * Simple helper class that just forwards the redirect callback back
michael@0 3330 * to the nsXMLHttpRequest.
michael@0 3331 */
michael@0 3332 class AsyncVerifyRedirectCallbackForwarder MOZ_FINAL : public nsIAsyncVerifyRedirectCallback
michael@0 3333 {
michael@0 3334 public:
michael@0 3335 AsyncVerifyRedirectCallbackForwarder(nsXMLHttpRequest *xhr)
michael@0 3336 : mXHR(xhr)
michael@0 3337 {
michael@0 3338 }
michael@0 3339
michael@0 3340 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
michael@0 3341 NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackForwarder)
michael@0 3342
michael@0 3343 // nsIAsyncVerifyRedirectCallback implementation
michael@0 3344 NS_IMETHOD OnRedirectVerifyCallback(nsresult result)
michael@0 3345 {
michael@0 3346 mXHR->OnRedirectVerifyCallback(result);
michael@0 3347
michael@0 3348 return NS_OK;
michael@0 3349 }
michael@0 3350
michael@0 3351 private:
michael@0 3352 nsRefPtr<nsXMLHttpRequest> mXHR;
michael@0 3353 };
michael@0 3354
michael@0 3355 NS_IMPL_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder, mXHR)
michael@0 3356
michael@0 3357 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder)
michael@0 3358 NS_INTERFACE_MAP_ENTRY(nsISupports)
michael@0 3359 NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
michael@0 3360 NS_INTERFACE_MAP_END
michael@0 3361
michael@0 3362 NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackForwarder)
michael@0 3363 NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackForwarder)
michael@0 3364
michael@0 3365
michael@0 3366 /////////////////////////////////////////////////////
michael@0 3367 // nsIChannelEventSink methods:
michael@0 3368 //
michael@0 3369 NS_IMETHODIMP
michael@0 3370 nsXMLHttpRequest::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
michael@0 3371 nsIChannel *aNewChannel,
michael@0 3372 uint32_t aFlags,
michael@0 3373 nsIAsyncVerifyRedirectCallback *callback)
michael@0 3374 {
michael@0 3375 NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
michael@0 3376
michael@0 3377 nsresult rv;
michael@0 3378
michael@0 3379 if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
michael@0 3380 rv = CheckChannelForCrossSiteRequest(aNewChannel);
michael@0 3381 if (NS_FAILED(rv)) {
michael@0 3382 NS_WARNING("nsXMLHttpRequest::OnChannelRedirect: "
michael@0 3383 "CheckChannelForCrossSiteRequest returned failure");
michael@0 3384 return rv;
michael@0 3385 }
michael@0 3386
michael@0 3387 // Disable redirects for preflighted cross-site requests entirely for now
michael@0 3388 // Note, do this after the call to CheckChannelForCrossSiteRequest
michael@0 3389 // to make sure that XML_HTTP_REQUEST_USE_XSITE_AC is up-to-date
michael@0 3390 if ((mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT)) {
michael@0 3391 return NS_ERROR_DOM_BAD_URI;
michael@0 3392 }
michael@0 3393 }
michael@0 3394
michael@0 3395 // Prepare to receive callback
michael@0 3396 mRedirectCallback = callback;
michael@0 3397 mNewRedirectChannel = aNewChannel;
michael@0 3398
michael@0 3399 if (mChannelEventSink) {
michael@0 3400 nsRefPtr<AsyncVerifyRedirectCallbackForwarder> fwd =
michael@0 3401 new AsyncVerifyRedirectCallbackForwarder(this);
michael@0 3402
michael@0 3403 rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
michael@0 3404 aNewChannel,
michael@0 3405 aFlags, fwd);
michael@0 3406 if (NS_FAILED(rv)) {
michael@0 3407 mRedirectCallback = nullptr;
michael@0 3408 mNewRedirectChannel = nullptr;
michael@0 3409 }
michael@0 3410 return rv;
michael@0 3411 }
michael@0 3412 OnRedirectVerifyCallback(NS_OK);
michael@0 3413 return NS_OK;
michael@0 3414 }
michael@0 3415
michael@0 3416 void
michael@0 3417 nsXMLHttpRequest::OnRedirectVerifyCallback(nsresult result)
michael@0 3418 {
michael@0 3419 NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
michael@0 3420 NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
michael@0 3421
michael@0 3422 if (NS_SUCCEEDED(result)) {
michael@0 3423 mChannel = mNewRedirectChannel;
michael@0 3424
michael@0 3425 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
michael@0 3426 if (httpChannel) {
michael@0 3427 // Ensure all original headers are duplicated for the new channel (bug #553888)
michael@0 3428 for (uint32_t i = mModifiedRequestHeaders.Length(); i > 0; ) {
michael@0 3429 --i;
michael@0 3430 httpChannel->SetRequestHeader(mModifiedRequestHeaders[i].header,
michael@0 3431 mModifiedRequestHeaders[i].value,
michael@0 3432 false);
michael@0 3433 }
michael@0 3434 }
michael@0 3435 } else {
michael@0 3436 mErrorLoad = true;
michael@0 3437 }
michael@0 3438
michael@0 3439 mNewRedirectChannel = nullptr;
michael@0 3440
michael@0 3441 mRedirectCallback->OnRedirectVerifyCallback(result);
michael@0 3442 mRedirectCallback = nullptr;
michael@0 3443 }
michael@0 3444
michael@0 3445 /////////////////////////////////////////////////////
michael@0 3446 // nsIProgressEventSink methods:
michael@0 3447 //
michael@0 3448
michael@0 3449 void
michael@0 3450 nsXMLHttpRequest::MaybeDispatchProgressEvents(bool aFinalProgress)
michael@0 3451 {
michael@0 3452 if (aFinalProgress && mProgressTimerIsActive) {
michael@0 3453 mProgressTimerIsActive = false;
michael@0 3454 mProgressNotifier->Cancel();
michael@0 3455 }
michael@0 3456
michael@0 3457 if (mProgressTimerIsActive ||
michael@0 3458 !mProgressSinceLastProgressEvent ||
michael@0 3459 mErrorLoad ||
michael@0 3460 !(mState & XML_HTTP_REQUEST_ASYNC)) {
michael@0 3461 return;
michael@0 3462 }
michael@0 3463
michael@0 3464 if (!aFinalProgress) {
michael@0 3465 StartProgressEventTimer();
michael@0 3466 }
michael@0 3467
michael@0 3468 // We're uploading if our state is XML_HTTP_REQUEST_OPENED or
michael@0 3469 // XML_HTTP_REQUEST_SENT
michael@0 3470 if ((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState) {
michael@0 3471 if (mUpload && !mUploadComplete) {
michael@0 3472 DispatchProgressEvent(mUpload, NS_LITERAL_STRING(PROGRESS_STR),
michael@0 3473 mUploadLengthComputable, mUploadTransferred,
michael@0 3474 mUploadTotal);
michael@0 3475 }
michael@0 3476 } else {
michael@0 3477 if (aFinalProgress) {
michael@0 3478 mLoadTotal = mLoadTransferred;
michael@0 3479 }
michael@0 3480 mInLoadProgressEvent = true;
michael@0 3481 DispatchProgressEvent(this, NS_LITERAL_STRING(PROGRESS_STR),
michael@0 3482 mLoadLengthComputable, mLoadTransferred,
michael@0 3483 mLoadTotal);
michael@0 3484 mInLoadProgressEvent = false;
michael@0 3485 if (mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT ||
michael@0 3486 mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER) {
michael@0 3487 mResponseBody.Truncate();
michael@0 3488 mResponseText.Truncate();
michael@0 3489 mResultArrayBuffer = nullptr;
michael@0 3490 mArrayBufferBuilder.reset();
michael@0 3491 }
michael@0 3492 }
michael@0 3493
michael@0 3494 mProgressSinceLastProgressEvent = false;
michael@0 3495 }
michael@0 3496
michael@0 3497 NS_IMETHODIMP
michael@0 3498 nsXMLHttpRequest::OnProgress(nsIRequest *aRequest, nsISupports *aContext, uint64_t aProgress, uint64_t aProgressMax)
michael@0 3499 {
michael@0 3500 // We're uploading if our state is XML_HTTP_REQUEST_OPENED or
michael@0 3501 // XML_HTTP_REQUEST_SENT
michael@0 3502 bool upload = !!((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState);
michael@0 3503 // When uploading, OnProgress reports also headers in aProgress and aProgressMax.
michael@0 3504 // So, try to remove the headers, if possible.
michael@0 3505 bool lengthComputable = (aProgressMax != UINT64_MAX);
michael@0 3506 if (upload) {
michael@0 3507 uint64_t loaded = aProgress;
michael@0 3508 uint64_t total = aProgressMax;
michael@0 3509 if (lengthComputable) {
michael@0 3510 uint64_t headerSize = aProgressMax - mUploadTotal;
michael@0 3511 loaded -= headerSize;
michael@0 3512 total -= headerSize;
michael@0 3513 }
michael@0 3514 mUploadLengthComputable = lengthComputable;
michael@0 3515 mUploadTransferred = loaded;
michael@0 3516 mProgressSinceLastProgressEvent = true;
michael@0 3517
michael@0 3518 MaybeDispatchProgressEvents(false);
michael@0 3519 } else {
michael@0 3520 mLoadLengthComputable = lengthComputable;
michael@0 3521 mLoadTotal = lengthComputable ? aProgressMax : 0;
michael@0 3522
michael@0 3523 // Don't dispatch progress events here. OnDataAvailable will take care
michael@0 3524 // of that.
michael@0 3525 }
michael@0 3526
michael@0 3527 if (mProgressEventSink) {
michael@0 3528 mProgressEventSink->OnProgress(aRequest, aContext, aProgress,
michael@0 3529 aProgressMax);
michael@0 3530 }
michael@0 3531
michael@0 3532 return NS_OK;
michael@0 3533 }
michael@0 3534
michael@0 3535 NS_IMETHODIMP
michael@0 3536 nsXMLHttpRequest::OnStatus(nsIRequest *aRequest, nsISupports *aContext, nsresult aStatus, const char16_t *aStatusArg)
michael@0 3537 {
michael@0 3538 if (mProgressEventSink) {
michael@0 3539 mProgressEventSink->OnStatus(aRequest, aContext, aStatus, aStatusArg);
michael@0 3540 }
michael@0 3541
michael@0 3542 return NS_OK;
michael@0 3543 }
michael@0 3544
michael@0 3545 bool
michael@0 3546 nsXMLHttpRequest::AllowUploadProgress()
michael@0 3547 {
michael@0 3548 return !(mState & XML_HTTP_REQUEST_USE_XSITE_AC) ||
michael@0 3549 (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT);
michael@0 3550 }
michael@0 3551
michael@0 3552 /////////////////////////////////////////////////////
michael@0 3553 // nsIInterfaceRequestor methods:
michael@0 3554 //
michael@0 3555 NS_IMETHODIMP
michael@0 3556 nsXMLHttpRequest::GetInterface(const nsIID & aIID, void **aResult)
michael@0 3557 {
michael@0 3558 nsresult rv;
michael@0 3559
michael@0 3560 // Make sure to return ourselves for the channel event sink interface and
michael@0 3561 // progress event sink interface, no matter what. We can forward these to
michael@0 3562 // mNotificationCallbacks if it wants to get notifications for them. But we
michael@0 3563 // need to see these notifications for proper functioning.
michael@0 3564 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
michael@0 3565 mChannelEventSink = do_GetInterface(mNotificationCallbacks);
michael@0 3566 *aResult = static_cast<nsIChannelEventSink*>(EnsureXPCOMifier().take());
michael@0 3567 return NS_OK;
michael@0 3568 } else if (aIID.Equals(NS_GET_IID(nsIProgressEventSink))) {
michael@0 3569 mProgressEventSink = do_GetInterface(mNotificationCallbacks);
michael@0 3570 *aResult = static_cast<nsIProgressEventSink*>(EnsureXPCOMifier().take());
michael@0 3571 return NS_OK;
michael@0 3572 }
michael@0 3573
michael@0 3574 // Now give mNotificationCallbacks (if non-null) a chance to return the
michael@0 3575 // desired interface.
michael@0 3576 if (mNotificationCallbacks) {
michael@0 3577 rv = mNotificationCallbacks->GetInterface(aIID, aResult);
michael@0 3578 if (NS_SUCCEEDED(rv)) {
michael@0 3579 NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!");
michael@0 3580 return rv;
michael@0 3581 }
michael@0 3582 }
michael@0 3583
michael@0 3584 if (mState & XML_HTTP_REQUEST_BACKGROUND) {
michael@0 3585 nsCOMPtr<nsIInterfaceRequestor> badCertHandler(do_CreateInstance(NS_BADCERTHANDLER_CONTRACTID, &rv));
michael@0 3586
michael@0 3587 // Ignore failure to get component, we may not have all its dependencies
michael@0 3588 // available
michael@0 3589 if (NS_SUCCEEDED(rv)) {
michael@0 3590 rv = badCertHandler->GetInterface(aIID, aResult);
michael@0 3591 if (NS_SUCCEEDED(rv))
michael@0 3592 return rv;
michael@0 3593 }
michael@0 3594 }
michael@0 3595 else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
michael@0 3596 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
michael@0 3597
michael@0 3598 nsCOMPtr<nsIURI> uri;
michael@0 3599 rv = mChannel->GetURI(getter_AddRefs(uri));
michael@0 3600 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3601
michael@0 3602 // Verify that it's ok to prompt for credentials here, per spec
michael@0 3603 // http://xhr.spec.whatwg.org/#the-send%28%29-method
michael@0 3604 bool showPrompt = true;
michael@0 3605
michael@0 3606 // If authentication fails, XMLHttpRequest origin and
michael@0 3607 // the request URL are same origin, ...
michael@0 3608 /* Disabled - bug: 799540
michael@0 3609 if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
michael@0 3610 showPrompt = false;
michael@0 3611 }
michael@0 3612 */
michael@0 3613
michael@0 3614 // ... Authorization is not in the list of author request headers, ...
michael@0 3615 if (showPrompt) {
michael@0 3616 for (uint32_t i = 0, len = mModifiedRequestHeaders.Length(); i < len; ++i) {
michael@0 3617 if (mModifiedRequestHeaders[i].header.
michael@0 3618 LowerCaseEqualsLiteral("authorization")) {
michael@0 3619 showPrompt = false;
michael@0 3620 break;
michael@0 3621 }
michael@0 3622 }
michael@0 3623 }
michael@0 3624
michael@0 3625 // ... request username is null, and request password is null,
michael@0 3626 if (showPrompt) {
michael@0 3627
michael@0 3628 nsCString username;
michael@0 3629 rv = uri->GetUsername(username);
michael@0 3630 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3631
michael@0 3632 nsCString password;
michael@0 3633 rv = uri->GetPassword(password);
michael@0 3634 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3635
michael@0 3636 if (!username.IsEmpty() || !password.IsEmpty()) {
michael@0 3637 showPrompt = false;
michael@0 3638 }
michael@0 3639 }
michael@0 3640
michael@0 3641 // ... user agents should prompt the end user for their username and password.
michael@0 3642 if (!showPrompt) {
michael@0 3643 nsRefPtr<XMLHttpRequestAuthPrompt> prompt = new XMLHttpRequestAuthPrompt();
michael@0 3644 if (!prompt)
michael@0 3645 return NS_ERROR_OUT_OF_MEMORY;
michael@0 3646
michael@0 3647 return prompt->QueryInterface(aIID, aResult);
michael@0 3648 }
michael@0 3649
michael@0 3650 nsCOMPtr<nsIPromptFactory> wwatch =
michael@0 3651 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
michael@0 3652 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3653
michael@0 3654 // Get the an auth prompter for our window so that the parenting
michael@0 3655 // of the dialogs works as it should when using tabs.
michael@0 3656
michael@0 3657 nsCOMPtr<nsIDOMWindow> window;
michael@0 3658 if (GetOwner()) {
michael@0 3659 window = GetOwner()->GetOuterWindow();
michael@0 3660 }
michael@0 3661
michael@0 3662 return wwatch->GetPrompt(window, aIID,
michael@0 3663 reinterpret_cast<void**>(aResult));
michael@0 3664 }
michael@0 3665 // Now check for the various XHR non-DOM interfaces, except
michael@0 3666 // nsIProgressEventSink and nsIChannelEventSink which we already
michael@0 3667 // handled above.
michael@0 3668 else if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
michael@0 3669 *aResult = static_cast<nsIStreamListener*>(EnsureXPCOMifier().take());
michael@0 3670 return NS_OK;
michael@0 3671 }
michael@0 3672 else if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
michael@0 3673 *aResult = static_cast<nsIRequestObserver*>(EnsureXPCOMifier().take());
michael@0 3674 return NS_OK;
michael@0 3675 }
michael@0 3676 else if (aIID.Equals(NS_GET_IID(nsITimerCallback))) {
michael@0 3677 *aResult = static_cast<nsITimerCallback*>(EnsureXPCOMifier().take());
michael@0 3678 return NS_OK;
michael@0 3679 }
michael@0 3680
michael@0 3681 return QueryInterface(aIID, aResult);
michael@0 3682 }
michael@0 3683
michael@0 3684 void
michael@0 3685 nsXMLHttpRequest::GetInterface(JSContext* aCx, nsIJSID* aIID,
michael@0 3686 JS::MutableHandle<JS::Value> aRetval,
michael@0 3687 ErrorResult& aRv)
michael@0 3688 {
michael@0 3689 dom::GetInterface(aCx, this, aIID, aRetval, aRv);
michael@0 3690 }
michael@0 3691
michael@0 3692 nsXMLHttpRequestUpload*
michael@0 3693 nsXMLHttpRequest::Upload()
michael@0 3694 {
michael@0 3695 if (!mUpload) {
michael@0 3696 mUpload = new nsXMLHttpRequestUpload(this);
michael@0 3697 }
michael@0 3698 return mUpload;
michael@0 3699 }
michael@0 3700
michael@0 3701 NS_IMETHODIMP
michael@0 3702 nsXMLHttpRequest::GetUpload(nsIXMLHttpRequestUpload** aUpload)
michael@0 3703 {
michael@0 3704 nsRefPtr<nsXMLHttpRequestUpload> upload = Upload();
michael@0 3705 upload.forget(aUpload);
michael@0 3706 return NS_OK;
michael@0 3707 }
michael@0 3708
michael@0 3709 bool
michael@0 3710 nsXMLHttpRequest::MozAnon()
michael@0 3711 {
michael@0 3712 return mIsAnon;
michael@0 3713 }
michael@0 3714
michael@0 3715 NS_IMETHODIMP
michael@0 3716 nsXMLHttpRequest::GetMozAnon(bool* aAnon)
michael@0 3717 {
michael@0 3718 *aAnon = MozAnon();
michael@0 3719 return NS_OK;
michael@0 3720 }
michael@0 3721
michael@0 3722 bool
michael@0 3723 nsXMLHttpRequest::MozSystem()
michael@0 3724 {
michael@0 3725 return IsSystemXHR();
michael@0 3726 }
michael@0 3727
michael@0 3728 NS_IMETHODIMP
michael@0 3729 nsXMLHttpRequest::GetMozSystem(bool* aSystem)
michael@0 3730 {
michael@0 3731 *aSystem = MozSystem();
michael@0 3732 return NS_OK;
michael@0 3733 }
michael@0 3734
michael@0 3735 void
michael@0 3736 nsXMLHttpRequest::HandleTimeoutCallback()
michael@0 3737 {
michael@0 3738 if (mState & XML_HTTP_REQUEST_DONE) {
michael@0 3739 NS_NOTREACHED("nsXMLHttpRequest::HandleTimeoutCallback with completed request");
michael@0 3740 // do nothing!
michael@0 3741 return;
michael@0 3742 }
michael@0 3743
michael@0 3744 CloseRequestWithError(NS_LITERAL_STRING(TIMEOUT_STR),
michael@0 3745 XML_HTTP_REQUEST_TIMED_OUT);
michael@0 3746 }
michael@0 3747
michael@0 3748 NS_IMETHODIMP
michael@0 3749 nsXMLHttpRequest::Notify(nsITimer* aTimer)
michael@0 3750 {
michael@0 3751 if (mProgressNotifier == aTimer) {
michael@0 3752 HandleProgressTimerCallback();
michael@0 3753 return NS_OK;
michael@0 3754 }
michael@0 3755
michael@0 3756 if (mTimeoutTimer == aTimer) {
michael@0 3757 HandleTimeoutCallback();
michael@0 3758 return NS_OK;
michael@0 3759 }
michael@0 3760
michael@0 3761 // Just in case some JS user wants to QI to nsITimerCallback and play with us...
michael@0 3762 NS_WARNING("Unexpected timer!");
michael@0 3763 return NS_ERROR_INVALID_POINTER;
michael@0 3764 }
michael@0 3765
michael@0 3766 void
michael@0 3767 nsXMLHttpRequest::HandleProgressTimerCallback()
michael@0 3768 {
michael@0 3769 mProgressTimerIsActive = false;
michael@0 3770 MaybeDispatchProgressEvents(false);
michael@0 3771 }
michael@0 3772
michael@0 3773 void
michael@0 3774 nsXMLHttpRequest::StartProgressEventTimer()
michael@0 3775 {
michael@0 3776 if (!mProgressNotifier) {
michael@0 3777 mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID);
michael@0 3778 }
michael@0 3779 if (mProgressNotifier) {
michael@0 3780 mProgressTimerIsActive = true;
michael@0 3781 mProgressNotifier->Cancel();
michael@0 3782 mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
michael@0 3783 nsITimer::TYPE_ONE_SHOT);
michael@0 3784 }
michael@0 3785 }
michael@0 3786
michael@0 3787 already_AddRefed<nsXMLHttpRequestXPCOMifier>
michael@0 3788 nsXMLHttpRequest::EnsureXPCOMifier()
michael@0 3789 {
michael@0 3790 if (!mXPCOMifier) {
michael@0 3791 mXPCOMifier = new nsXMLHttpRequestXPCOMifier(this);
michael@0 3792 }
michael@0 3793 nsRefPtr<nsXMLHttpRequestXPCOMifier> newRef(mXPCOMifier);
michael@0 3794 return newRef.forget();
michael@0 3795 }
michael@0 3796
michael@0 3797 NS_IMPL_ISUPPORTS(nsXMLHttpRequest::nsHeaderVisitor, nsIHttpHeaderVisitor)
michael@0 3798
michael@0 3799 NS_IMETHODIMP nsXMLHttpRequest::
michael@0 3800 nsHeaderVisitor::VisitHeader(const nsACString &header, const nsACString &value)
michael@0 3801 {
michael@0 3802 if (mXHR->IsSafeHeader(header, mHttpChannel)) {
michael@0 3803 mHeaders.Append(header);
michael@0 3804 mHeaders.Append(": ");
michael@0 3805 mHeaders.Append(value);
michael@0 3806 mHeaders.Append("\r\n");
michael@0 3807 }
michael@0 3808 return NS_OK;
michael@0 3809 }
michael@0 3810
michael@0 3811 // nsXMLHttpRequestXPCOMifier implementation
michael@0 3812 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier)
michael@0 3813 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
michael@0 3814 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
michael@0 3815 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
michael@0 3816 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
michael@0 3817 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
michael@0 3818 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
michael@0 3819 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
michael@0 3820 NS_INTERFACE_MAP_END
michael@0 3821
michael@0 3822 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpRequestXPCOMifier)
michael@0 3823 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpRequestXPCOMifier)
michael@0 3824
michael@0 3825 // Can't NS_IMPL_CYCLE_COLLECTION( because mXHR has ambiguous
michael@0 3826 // inheritance from nsISupports.
michael@0 3827 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequestXPCOMifier)
michael@0 3828
michael@0 3829 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXMLHttpRequestXPCOMifier)
michael@0 3830 if (tmp->mXHR) {
michael@0 3831 tmp->mXHR->mXPCOMifier = nullptr;
michael@0 3832 }
michael@0 3833 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXHR)
michael@0 3834 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 3835
michael@0 3836 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXMLHttpRequestXPCOMifier)
michael@0 3837 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXHR)
michael@0 3838 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 3839
michael@0 3840 NS_IMETHODIMP
michael@0 3841 nsXMLHttpRequestXPCOMifier::GetInterface(const nsIID & aIID, void **aResult)
michael@0 3842 {
michael@0 3843 // Return ourselves for the things we implement (except
michael@0 3844 // nsIInterfaceRequestor) and the XHR for the rest.
michael@0 3845 if (!aIID.Equals(NS_GET_IID(nsIInterfaceRequestor))) {
michael@0 3846 nsresult rv = QueryInterface(aIID, aResult);
michael@0 3847 if (NS_SUCCEEDED(rv)) {
michael@0 3848 return rv;
michael@0 3849 }
michael@0 3850 }
michael@0 3851
michael@0 3852 return mXHR->GetInterface(aIID, aResult);
michael@0 3853 }
michael@0 3854
michael@0 3855 namespace mozilla {
michael@0 3856
michael@0 3857 ArrayBufferBuilder::ArrayBufferBuilder()
michael@0 3858 : mDataPtr(nullptr),
michael@0 3859 mCapacity(0),
michael@0 3860 mLength(0)
michael@0 3861 {
michael@0 3862 }
michael@0 3863
michael@0 3864 ArrayBufferBuilder::~ArrayBufferBuilder()
michael@0 3865 {
michael@0 3866 reset();
michael@0 3867 }
michael@0 3868
michael@0 3869 void
michael@0 3870 ArrayBufferBuilder::reset()
michael@0 3871 {
michael@0 3872 if (mDataPtr) {
michael@0 3873 JS_free(nullptr, mDataPtr);
michael@0 3874 }
michael@0 3875 mDataPtr = nullptr;
michael@0 3876 mCapacity = mLength = 0;
michael@0 3877 }
michael@0 3878
michael@0 3879 bool
michael@0 3880 ArrayBufferBuilder::setCapacity(uint32_t aNewCap)
michael@0 3881 {
michael@0 3882 uint8_t *newdata = (uint8_t *) JS_ReallocateArrayBufferContents(nullptr, aNewCap, mDataPtr, mCapacity);
michael@0 3883 if (!newdata) {
michael@0 3884 return false;
michael@0 3885 }
michael@0 3886
michael@0 3887 mDataPtr = newdata;
michael@0 3888 mCapacity = aNewCap;
michael@0 3889 if (mLength > aNewCap) {
michael@0 3890 mLength = aNewCap;
michael@0 3891 }
michael@0 3892
michael@0 3893 return true;
michael@0 3894 }
michael@0 3895
michael@0 3896 bool
michael@0 3897 ArrayBufferBuilder::append(const uint8_t *aNewData, uint32_t aDataLen,
michael@0 3898 uint32_t aMaxGrowth)
michael@0 3899 {
michael@0 3900 if (mLength + aDataLen > mCapacity) {
michael@0 3901 uint32_t newcap;
michael@0 3902 // Double while under aMaxGrowth or if not specified.
michael@0 3903 if (!aMaxGrowth || mCapacity < aMaxGrowth) {
michael@0 3904 newcap = mCapacity * 2;
michael@0 3905 } else {
michael@0 3906 newcap = mCapacity + aMaxGrowth;
michael@0 3907 }
michael@0 3908
michael@0 3909 // But make sure there's always enough to satisfy our request.
michael@0 3910 if (newcap < mLength + aDataLen) {
michael@0 3911 newcap = mLength + aDataLen;
michael@0 3912 }
michael@0 3913
michael@0 3914 // Did we overflow?
michael@0 3915 if (newcap < mCapacity) {
michael@0 3916 return false;
michael@0 3917 }
michael@0 3918
michael@0 3919 if (!setCapacity(newcap)) {
michael@0 3920 return false;
michael@0 3921 }
michael@0 3922 }
michael@0 3923
michael@0 3924 // Assert that the region isn't overlapping so we can memcpy.
michael@0 3925 MOZ_ASSERT(!areOverlappingRegions(aNewData, aDataLen, mDataPtr + mLength,
michael@0 3926 aDataLen));
michael@0 3927
michael@0 3928 memcpy(mDataPtr + mLength, aNewData, aDataLen);
michael@0 3929 mLength += aDataLen;
michael@0 3930
michael@0 3931 return true;
michael@0 3932 }
michael@0 3933
michael@0 3934 JSObject*
michael@0 3935 ArrayBufferBuilder::getArrayBuffer(JSContext* aCx)
michael@0 3936 {
michael@0 3937 // we need to check for mLength == 0, because nothing may have been
michael@0 3938 // added
michael@0 3939 if (mCapacity > mLength || mLength == 0) {
michael@0 3940 if (!setCapacity(mLength)) {
michael@0 3941 return nullptr;
michael@0 3942 }
michael@0 3943 }
michael@0 3944
michael@0 3945 JSObject* obj = JS_NewArrayBufferWithContents(aCx, mLength, mDataPtr);
michael@0 3946 mDataPtr = nullptr;
michael@0 3947 mLength = mCapacity = 0;
michael@0 3948 if (!obj) {
michael@0 3949 js_free(mDataPtr);
michael@0 3950 return nullptr;
michael@0 3951 }
michael@0 3952 return obj;
michael@0 3953 }
michael@0 3954
michael@0 3955 /* static */ bool
michael@0 3956 ArrayBufferBuilder::areOverlappingRegions(const uint8_t* aStart1,
michael@0 3957 uint32_t aLength1,
michael@0 3958 const uint8_t* aStart2,
michael@0 3959 uint32_t aLength2)
michael@0 3960 {
michael@0 3961 const uint8_t* end1 = aStart1 + aLength1;
michael@0 3962 const uint8_t* end2 = aStart2 + aLength2;
michael@0 3963
michael@0 3964 const uint8_t* max_start = aStart1 > aStart2 ? aStart1 : aStart2;
michael@0 3965 const uint8_t* min_end = end1 < end2 ? end1 : end2;
michael@0 3966
michael@0 3967 return max_start < min_end;
michael@0 3968 }
michael@0 3969
michael@0 3970 } // namespace mozilla

mercurial