1 // Copyright 2007, Google Inc.
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
6 // 1. Redistributions of source code must retain the above copyright notice,
7 // this list of conditions and the following disclaimer.
8 // 2. Redistributions in binary form must reproduce the above copyright notice,
9 // this list of conditions and the following disclaimer in the documentation
10 // and/or other materials provided with the distribution.
11 // 3. Neither the name of Google Inc. nor the names of its contributors may be
12 // used to endorse or promote products derived from this software without
13 // specific prior written permission.
15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include <windows.h> // must manually #include before nsIEventQueueService.h
29 #include <gecko_internal/nsIDOMClassInfo.h>
30 #include <gecko_internal/nsIEventQueueService.h>
32 #include "gears/httprequest/firefox/httprequest_ff.h"
34 #include "gears/base/common/js_runner.h"
35 #include "gears/base/common/string_utils.h"
36 #include "gears/base/common/url_utils.h"
37 #include "gears/base/firefox/dom_utils.h"
38 #ifndef OFFICIAL_BUILD
39 // The blob API has not been finalized for official builds
40 #include "gears/blob/blob_ff.h"
41 #include "gears/blob/buffer_blob.h"
43 #include "gears/localserver/common/http_constants.h"
45 // Returns true if the currently executing thread is the main UI thread,
46 // firefox/mozila has one such very special thread
47 // See cache_intercept.cc for implementation
50 // Boilerplate. == NS_IMPL_ISUPPORTS + ..._MAP_ENTRY_EXTERNAL_DOM_CLASSINFO
51 NS_IMPL_THREADSAFE_ADDREF(GearsHttpRequest
)
52 NS_IMPL_THREADSAFE_RELEASE(GearsHttpRequest
)
53 NS_INTERFACE_MAP_BEGIN(GearsHttpRequest
)
54 NS_INTERFACE_MAP_ENTRY(GearsBaseClassInterface
)
55 NS_INTERFACE_MAP_ENTRY(GearsHttpRequestInterface
)
56 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, GearsHttpRequest
)
57 NS_INTERFACE_MAP_ENTRY_EXTERNAL_DOM_CLASSINFO(GearsHttpRequest
)
61 const char *kGearsHttpRequestClassName
= "GearsHttpRequest";
62 const nsCID kGearsHttpRequestClassId
= // {033D8D37-95A2-478e-91A3-B0FA60A9CA8D}
63 { 0x33d8d37, 0x95a2, 0x478e,
64 { 0x91, 0xa3, 0xb0, 0xfa, 0x60, 0xa9, 0xca, 0x8d } };
67 static const char16
*kRequestFailedError
= STRING16(L
"The request failed.");
68 static const char16
*kInternalError
= STRING16(L
"Internal error.");
69 static const char16
*kAlreadyOpenError
= STRING16(L
"Request is already open.");
70 static const char16
*kNotOpenError
= STRING16(L
"Request is not open.");
71 static const char16
*kNotInteractiveError
=
72 STRING16(L
"Request is not loading or done.");
74 static const char16
*kEmptyString
= STRING16(L
"");
76 GearsHttpRequest::GearsHttpRequest()
77 : request_(NULL
), apartment_thread_(PR_GetCurrentThread()),
78 content_type_header_was_set_(false), page_is_unloaded_(false) {
81 GearsHttpRequest::~GearsHttpRequest() {
85 //------------------------------------------------------------------------------
86 // attribute ReadyStateChangeEventListener onreadystatechange
87 //------------------------------------------------------------------------------
88 NS_IMETHODIMP
GearsHttpRequest::SetOnreadystatechange(
89 ReadyStateChangeEventListener
* onreadystatechange
) {
90 assert(IsApartmentThread());
92 JsParamFetcher
js_params(this);
93 if (js_params
.GetCount(false) < 1) {
94 RETURN_EXCEPTION(STRING16(L
"Value is required."));
97 JsRootedCallback
*callback
;
98 if (!js_params
.GetAsNewRootedCallback(0, &callback
)) {
100 STRING16(L
"Invalid value for onreadystatechange property."));
103 onreadystatechange_
.reset(callback
);
109 NS_IMETHODIMP
GearsHttpRequest::GetOnreadystatechange(
110 ReadyStateChangeEventListener
**onreadystatechange
) {
111 assert(IsApartmentThread());
112 return NS_ERROR_NOT_IMPLEMENTED
;
115 //------------------------------------------------------------------------------
116 // void open(string method, string url, bool async)
117 //------------------------------------------------------------------------------
118 NS_IMETHODIMP
GearsHttpRequest::Open() {
119 assert(IsApartmentThread());
120 { // Scoped to unlock prior to invoking the readystatechange script callback
121 MutexLock
locker(&lock_
);
124 request_info_
.reset(NULL
);
125 response_info_
.reset(NULL
);
129 if (!IsUninitialized()) {
130 RETURN_EXCEPTION(kAlreadyOpenError
);
133 JsParamFetcher
js_params(this);
134 if (js_params
.GetCount(false) < 2) {
136 STRING16(L
"The method and url parameters are required."));
139 std::string16 method
;
140 if (!js_params
.GetAsString(0, &method
) || method
.empty()) {
141 RETURN_EXCEPTION(STRING16(L
"The method parameter must be a string."));
145 if (!js_params
.GetAsString(1, &url
) || url
.empty()) {
146 RETURN_EXCEPTION(STRING16(L
"The url parameter must be a string."));
149 scoped_ptr
<RequestInfo
> scoped_info(new RequestInfo());
150 scoped_info
->method
= MakeUpperString(method
);
152 std::string16 exception_message
;
153 if (!ResolveUrl(url
.c_str(), &scoped_info
->full_url
, &exception_message
)) {
154 RETURN_EXCEPTION(exception_message
.c_str());
157 request_info_
.swap(scoped_info
);
158 content_type_header_was_set_
= false;
160 response_info_
.reset(new ResponseInfo());
161 response_info_
->pending_ready_state
= HttpRequest::OPEN
;
162 response_info_
->ready_state
= HttpRequest::UNINITIALIZED
;
163 #ifndef OFFICIAL_BUILD
164 // The blob API has not been finalized for official builds
165 response_info_
->response_blob
= NULL
;
169 OnReadyStateChangedCall();
174 //------------------------------------------------------------------------------
175 // void setRequestHeader(name, value)
176 //------------------------------------------------------------------------------
178 static bool IsDisallowedHeader(const char16
*header
) {
179 // Headers which cannot be set according to the w3c spec
180 static const char16
* kDisallowedHeaders
[] = {
181 STRING16(L
"Accept-Charset"),
182 STRING16(L
"Accept-Encoding"),
183 STRING16(L
"Connection"),
184 STRING16(L
"Content-Length"),
185 STRING16(L
"Content-Transfer-Encoding"),
189 STRING16(L
"Keep-Alive"),
190 STRING16(L
"Referer"),
192 STRING16(L
"Trailer"),
193 STRING16(L
"Transfer-Encoding"),
194 STRING16(L
"Upgrade"),
196 for (int i
= 0; i
< static_cast<int>(ARRAYSIZE(kDisallowedHeaders
)); ++i
) {
197 if (StringCompareIgnoreCase(header
, kDisallowedHeaders
[i
]) == 0)
203 NS_IMETHODIMP
GearsHttpRequest::SetRequestHeader() {
204 assert(IsApartmentThread());
205 MutexLock
locker(&lock_
);
207 RETURN_EXCEPTION(kNotOpenError
);
210 JsParamFetcher
js_params(this);
211 if (js_params
.GetCount(false) < 2) {
213 STRING16(L
"The name and value parameters are required."));
217 if (!js_params
.GetAsString(0, &name
)) {
218 RETURN_EXCEPTION(STRING16(L
"The name parameter must be a string."));
222 if (!js_params
.GetAsString(1, &value
)) {
223 RETURN_EXCEPTION(STRING16(L
"The value parameter must be a string."));
226 if (IsDisallowedHeader(name
.c_str())) {
227 RETURN_EXCEPTION(STRING16(L
"This header may not be set."));
230 request_info_
->headers
.push_back(std::make_pair(name
, value
));
232 if (StringCompareIgnoreCase(name
.c_str(),
233 HttpConstants::kContentTypeHeader
) == 0) {
234 content_type_header_was_set_
= true;
240 //------------------------------------------------------------------------------
241 // void send(opt_data)
242 //------------------------------------------------------------------------------
243 NS_IMETHODIMP
GearsHttpRequest::Send() {
244 assert(IsApartmentThread());
245 MutexLock
locker(&lock_
);
247 RETURN_EXCEPTION(kNotOpenError
);
250 if (!InitEventQueues()) {
251 RETURN_EXCEPTION(kInternalError
);
254 if (request_info_
->method
== HttpConstants::kHttpPOST
||
255 request_info_
->method
== HttpConstants::kHttpPUT
) {
256 JsParamFetcher
js_params(this);
257 if (js_params
.GetCount(false) > 0) {
258 if (!js_params
.GetAsString(0, &request_info_
->post_data
)) {
259 RETURN_EXCEPTION(STRING16(L
"Data parameter must be a string."));
261 request_info_
->has_post_data
= !request_info_
->post_data
.empty();
265 if (page_is_unloaded_
) {
266 // We silently fail for this particular error condition to prevent callers
267 // from detecting errors and making noises after the page has been unloaded
273 if (!CallSendOnUiThread()) {
274 response_info_
.reset(NULL
);
275 RETURN_EXCEPTION(kInternalError
);
281 bool GearsHttpRequest::CallSendOnUiThread() {
282 return CallAsync(ui_event_queue_
, kSend
) == NS_OK
;
285 void GearsHttpRequest::OnSendCall() {
286 assert(IsUiThread());
288 assert(request_info_
.get());
289 assert(response_info_
.get());
291 MutexLock
locker(&lock_
);
293 bool ok
= request_
->Open(request_info_
->method
.c_str(),
294 request_info_
->full_url
.c_str(),
297 response_info_
->pending_ready_state
= HttpRequest::COMPLETE
;
299 CallReadyStateChangedOnApartmentThread();
303 // We defer setting up the listener to skip the OPEN callback
304 request_
->SetOnReadyStateChange(this);
306 for (size_t i
= 0; i
< request_info_
->headers
.size(); ++i
) {
307 request_
->SetRequestHeader(request_info_
->headers
[i
].first
.c_str(),
308 request_info_
->headers
[i
].second
.c_str());
311 if (request_info_
->has_post_data
) {
312 if (!content_type_header_was_set_
) {
313 request_
->SetRequestHeader(HttpConstants::kContentTypeHeader
,
314 HttpConstants::kMimeTextPlain
);
316 ok
= request_
->SendString(request_info_
->post_data
.c_str());
318 ok
= request_
->Send();
322 response_info_
->pending_ready_state
= HttpRequest::COMPLETE
;
324 CallReadyStateChangedOnApartmentThread();
330 //------------------------------------------------------------------------------
332 //------------------------------------------------------------------------------
333 NS_IMETHODIMP
GearsHttpRequest::Abort() {
334 assert(IsApartmentThread());
335 CallAbortOnUiThread();
339 bool GearsHttpRequest::CallAbortOnUiThread() {
343 } else if (ui_event_queue_
) {
344 return CallAsync(ui_event_queue_
, kAbort
) == NS_OK
;
350 void GearsHttpRequest::OnAbortCall() {
351 assert(IsUiThread());
352 nsCOMPtr
<GearsHttpRequest
> reference(this);
354 // The extra scope is to ensure we unlock prior to reference.Release
355 MutexLock
locker(&lock_
);
357 request_info_
.reset(NULL
);
358 response_info_
.reset(NULL
);
359 request_
->SetOnReadyStateChange(NULL
);
366 //------------------------------------------------------------------------------
367 // readonly attribute long readyState
368 //------------------------------------------------------------------------------
369 NS_IMETHODIMP
GearsHttpRequest::GetReadyState(PRInt32
*retval
) {
370 assert(IsApartmentThread());
371 MutexLock
locker(&lock_
);
372 if (IsUninitialized()) {
373 *retval
= HttpRequest::UNINITIALIZED
; // 0
374 } else if (IsOpen()) {
375 *retval
= HttpRequest::OPEN
; // 1
376 } else if (IsSent()) {
377 *retval
= HttpRequest::SENT
; // 2
378 } else if (IsInteractive()) {
379 *retval
= HttpRequest::INTERACTIVE
; // 3
380 } else if (IsComplete()) {
381 *retval
= HttpRequest::COMPLETE
; // 4
384 RETURN_EXCEPTION(kInternalError
);
389 bool GearsHttpRequest::IsValidResponse() {
390 assert(IsInteractive() || IsComplete());
391 return ::IsValidResponseCode(response_info_
->status
);
394 //------------------------------------------------------------------------------
395 // AString getAllResponseHeaders()
396 //------------------------------------------------------------------------------
397 NS_IMETHODIMP
GearsHttpRequest::GetAllResponseHeaders(nsAString
&retval
) {
398 assert(IsApartmentThread());
399 MutexLock
locker(&lock_
);
400 if (!(IsInteractive() || IsComplete())) {
401 RETURN_EXCEPTION(kNotInteractiveError
);
403 if (!IsValidResponse()) {
404 retval
.Assign(kEmptyString
);
407 retval
.Assign(response_info_
->headers
.c_str());
412 //------------------------------------------------------------------------------
413 // AString getResponseHeader()
414 //------------------------------------------------------------------------------
415 NS_IMETHODIMP
GearsHttpRequest::GetResponseHeader(nsAString
&retval
) {
416 assert(IsApartmentThread());
417 MutexLock
locker(&lock_
);
418 if (!(IsInteractive() || IsComplete())) {
419 RETURN_EXCEPTION(kNotInteractiveError
);
421 if (!IsValidResponse()) {
422 retval
.Assign(kEmptyString
);
425 if (!response_info_
->parsed_headers
.get()) {
426 scoped_ptr
<HTTPHeaders
> parsed_headers(new HTTPHeaders
);
427 std::string headers_utf8
;
428 String16ToUTF8(response_info_
->headers
.c_str(),
429 response_info_
->headers
.length(),
431 const char *body
= headers_utf8
.c_str();
432 uint32 body_len
= headers_utf8
.length();
433 if (!HTTPUtils::ParseHTTPHeaders(&body
, &body_len
, parsed_headers
.get(),
434 true /* allow_const_cast */)) {
437 response_info_
->parsed_headers
.swap(parsed_headers
);
440 JsParamFetcher
js_params(this);
441 if (js_params
.GetCount(false) < 1) {
442 RETURN_EXCEPTION(STRING16(L
"The name parameter is required."));
445 if (!js_params
.GetAsString(0, &name
)) {
446 RETURN_EXCEPTION(STRING16(L
"The name parameter must be a string."));
449 std::string name_utf8
;
450 String16ToUTF8(name
.c_str(), &name_utf8
);
451 const char *value_utf8
=
452 response_info_
->parsed_headers
->GetHeader(name_utf8
.c_str());
454 UTF8ToString16(value_utf8
? value_utf8
: "", &value
);
455 retval
.Assign(value
.c_str());
460 //------------------------------------------------------------------------------
461 // readonly attribute AString responseText
462 //------------------------------------------------------------------------------
463 NS_IMETHODIMP
GearsHttpRequest::GetResponseText(nsAString
&retval
) {
464 assert(IsApartmentThread());
465 MutexLock
locker(&lock_
);
466 if (!(IsInteractive() || IsComplete())) {
467 RETURN_EXCEPTION(kNotInteractiveError
);
469 if (!IsValidResponse()) {
470 retval
.Assign(kEmptyString
);
473 retval
.Assign(response_info_
->response_text
.c_str());
477 #ifdef OFFICIAL_BUILD
478 // Blob support is not ready for prime time yet
480 //------------------------------------------------------------------------------
481 // readonly attribute Blob responseBlob
482 //------------------------------------------------------------------------------
483 NS_IMETHODIMP
GearsHttpRequest::GetResponseBlob(nsISupports
**retval
) {
484 assert(IsApartmentThread());
485 MutexLock
locker(&lock_
);
487 RETURN_EXCEPTION(STRING16(L
"responseBlob is not supported before request "
491 if (response_info_
->response_blob
== NULL
) {
492 // Not already cached - make a new blob and copy the contents in
493 scoped_ptr
<GearsBlob
> blob(new GearsBlob());
494 if (IsValidResponse()) {
495 std::vector
<uint8
> *body
= response_info_
->response_body
.release();
497 blob
->Reset(new BufferBlob(body
));
500 // else blob stays empty
502 if (!blob
->InitBaseFromSibling(this)) {
503 RETURN_EXCEPTION(STRING16(L
"Initializing base class failed."));
505 response_info_
->response_blob
= blob
.release();
508 *retval
= response_info_
->response_blob
;
512 #endif // not OFFICIAL_BUILD
515 //------------------------------------------------------------------------------
516 // readonly attribute long status
517 //------------------------------------------------------------------------------
518 NS_IMETHODIMP
GearsHttpRequest::GetStatus(PRInt32
*retval
) {
519 assert(IsApartmentThread());
520 MutexLock
locker(&lock_
);
521 if (!(IsInteractive() || IsComplete())) {
522 RETURN_EXCEPTION(kNotInteractiveError
);
524 if (!IsValidResponse()) {
525 RETURN_EXCEPTION(kRequestFailedError
);
527 *retval
= response_info_
->status
;
532 //------------------------------------------------------------------------------
533 // readonly attribute AString statusText
534 //------------------------------------------------------------------------------
535 NS_IMETHODIMP
GearsHttpRequest::GetStatusText(nsAString
&retval
) {
536 assert(IsApartmentThread());
537 MutexLock
locker(&lock_
);
538 if (!(IsInteractive() || IsComplete())) {
539 RETURN_EXCEPTION(kNotInteractiveError
);
541 if (!IsValidResponse()) {
542 RETURN_EXCEPTION(kRequestFailedError
);
544 retval
.Assign(response_info_
->status_text
.c_str());
548 //------------------------------------------------------------------------------
549 // DataAvailable called by our lower level HttpRequest class
550 //------------------------------------------------------------------------------
551 void GearsHttpRequest::DataAvailable(HttpRequest
*source
) {
552 assert(IsUiThread());
553 assert(source
== request_
);
555 nsCOMPtr
<GearsHttpRequest
> reference(this);
557 // The extra scope is to ensure we unlock prior to reference.Release
558 MutexLock
locker(&lock_
);
559 source
->GetResponseBodyAsText(&response_info_
->response_text
);
560 CallDataAvailableOnApartmentThread();
564 bool GearsHttpRequest::CallDataAvailableOnApartmentThread() {
565 return CallAsync(apartment_event_queue_
, kDataAvailable
) == NS_OK
;
568 void GearsHttpRequest::OnDataAvailableCall() {
569 assert(IsApartmentThread());
570 OnReadyStateChangedCall();
573 //------------------------------------------------------------------------------
574 // ReadyStateChanged called by our lower level HttpRequest class
575 //------------------------------------------------------------------------------
576 void GearsHttpRequest::ReadyStateChanged(HttpRequest
*source
) {
577 assert(IsUiThread());
578 assert(source
== request_
);
580 nsCOMPtr
<GearsHttpRequest
> reference(this);
582 // The extra scope is to ensure we unlock prior to reference.Release
583 MutexLock
locker(&lock_
);
585 HttpRequest::ReadyState previous_state
=
586 response_info_
->pending_ready_state
;
587 HttpRequest::ReadyState state
;
588 source
->GetReadyState(&state
);
589 if (state
> previous_state
) {
590 response_info_
->pending_ready_state
= state
;
591 if (state
>= HttpRequest::INTERACTIVE
&&
592 previous_state
< HttpRequest::INTERACTIVE
) {
593 // For HEAD requests, we skip INTERACTIVE and jump straight to COMPLETE
594 source
->GetAllResponseHeaders(&response_info_
->headers
);
595 source
->GetStatus(&response_info_
->status
);
596 source
->GetStatusText(&response_info_
->status_text
);
598 if (state
== HttpRequest::COMPLETE
) {
599 source
->GetResponseBodyAsText(&response_info_
->response_text
);
600 response_info_
->response_body
.reset(source
->GetResponseBody());
603 CallReadyStateChangedOnApartmentThread();
608 bool GearsHttpRequest::CallReadyStateChangedOnApartmentThread() {
609 return CallAsync(apartment_event_queue_
, kReadyStateChanged
) == NS_OK
;
612 void GearsHttpRequest::OnReadyStateChangedCall() {
613 assert(IsApartmentThread());
615 response_info_
->ready_state
= response_info_
->pending_ready_state
;
616 bool is_complete
= IsComplete();
619 // To remove cyclic dependencies we drop our reference to the
620 // callback when the request is complete.
621 JsRootedCallback
*handler
= is_complete
? onreadystatechange_
.release()
622 : onreadystatechange_
.get();
624 JsRunnerInterface
*runner
= GetJsRunner();
627 runner
->InvokeCallback(handler
, 0, NULL
, NULL
);
635 //------------------------------------------------------------------------------
636 // CreateRequest, RemoveRequest
637 //------------------------------------------------------------------------------
639 void GearsHttpRequest::CreateRequest() {
640 assert(IsUiThread());
642 request_
= HttpRequest::Create();
643 request_
->SetCachingBehavior(HttpRequest::USE_ALL_CACHES
);
644 request_
->SetRedirectBehavior(HttpRequest::FOLLOW_WITHIN_ORIGIN
);
648 void GearsHttpRequest::RemoveRequest() {
649 assert(IsUiThread());
651 request_
->SetOnReadyStateChange(NULL
);
652 request_
->ReleaseReference();
659 //------------------------------------------------------------------------------
660 // This helper does several things:
661 // - resolve relative urls based on the page location, the 'url' may also
662 // be an absolute url to start with, if so this step does not modify it
663 // - normalizes the resulting absolute url, ie. removes path navigation
664 // - removes the fragment part of the url, ie. truncates at the '#' character
665 // - ensures the the resulting url is from the same-origin
666 // - ensures the requested url is HTTP or HTTPS
667 //------------------------------------------------------------------------------
668 bool GearsHttpRequest::ResolveUrl(const char16
*url
,
669 std::string16
*resolved_url
,
670 std::string16
*exception_message
) {
671 assert(url
&& resolved_url
&& exception_message
);
672 if (!ResolveAndNormalize(EnvPageLocationUrl().c_str(), url
, resolved_url
)) {
673 *exception_message
= STRING16(L
"Failed to resolve URL.");
677 SecurityOrigin url_origin
;
678 if (!url_origin
.InitFromUrl(resolved_url
->c_str()) ||
679 !url_origin
.IsSameOrigin(EnvPageSecurityOrigin())) {
680 *exception_message
= STRING16(L
"URL is not from the same origin.");
684 if (!HttpRequest::IsSchemeSupported(url_origin
.scheme().c_str())) {
685 *exception_message
= STRING16(L
"URL scheme '");
686 *exception_message
+= url_origin
.scheme();
687 *exception_message
+= STRING16(L
"' is not supported.");
695 //------------------------------------------------------------------------------
696 // IsUninitialized, IsOpen, IsSent, IsInteractive, and IsComplete.
697 // The caller is responsible for acquiring the lock prior to calling these
699 //------------------------------------------------------------------------------
701 bool GearsHttpRequest::IsUninitialized() {
702 assert(IsApartmentThread());
703 return !response_info_
.get() ||
704 response_info_
->ready_state
== HttpRequest::UNINITIALIZED
;
707 bool GearsHttpRequest::IsOpen() {
708 assert(IsApartmentThread());
709 return response_info_
.get() &&
710 response_info_
->ready_state
== HttpRequest::OPEN
;
713 bool GearsHttpRequest::IsSent() {
714 assert(IsApartmentThread());
715 return response_info_
.get() &&
716 response_info_
->ready_state
== HttpRequest::SENT
;
719 bool GearsHttpRequest::IsInteractive() {
720 assert(IsApartmentThread());
721 return response_info_
.get() &&
722 response_info_
->ready_state
== HttpRequest::INTERACTIVE
;
725 bool GearsHttpRequest::IsComplete() {
726 assert(IsApartmentThread());
727 return response_info_
.get() &&
728 response_info_
->ready_state
== HttpRequest::COMPLETE
;
732 //------------------------------------------------------------------------------
734 //------------------------------------------------------------------------------
735 bool GearsHttpRequest::InitEventQueues() {
736 assert(IsApartmentThread());
739 nsCOMPtr
<nsIEventQueueService
> event_queue_service
=
740 do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID
, &rv
);
741 if (NS_FAILED(rv
) || !event_queue_service
) {
745 if (!apartment_event_queue_
) {
746 rv
= event_queue_service
->GetThreadEventQueue(
747 NS_CURRENT_THREAD
, getter_AddRefs(apartment_event_queue_
));
748 if (NS_FAILED(rv
) || !apartment_event_queue_
) {
753 if (!ui_event_queue_
) {
754 rv
= event_queue_service
->GetThreadEventQueue(
755 NS_UI_THREAD
, getter_AddRefs(ui_event_queue_
));
756 if (NS_FAILED(rv
) || !ui_event_queue_
) {
765 //------------------------------------------------------------------------------
767 //------------------------------------------------------------------------------
768 void GearsHttpRequest::InitUnloadMonitor() {
769 // Create an event monitor to alert us when the page unloads.
770 if (unload_monitor_
== NULL
) {
771 unload_monitor_
.reset(new JsEventMonitor(GetJsRunner(), JSEVENT_UNLOAD
,
777 //------------------------------------------------------------------------------
778 // OnAsyncCall - Called when a message sent via CallAsync is delivered to us
779 // on the target thread of control.
780 //------------------------------------------------------------------------------
782 void GearsHttpRequest::OnAsyncCall(AsyncCallType call_type
) {
785 OnDataAvailableCall();
787 case kReadyStateChanged
:
788 OnReadyStateChangedCall();
800 // AsyncCallEvent - Custom event class used to post messages across threads
801 struct GearsHttpRequest::AsyncCallEvent
: PLEvent
{
802 AsyncCallEvent(GearsHttpRequest
*request
, AsyncCallType call_type
)
803 : request(request
), call_type(call_type
) {}
804 nsCOMPtr
<GearsHttpRequest
> request
;
805 AsyncCallType call_type
;
809 // CallAsync - Posts a message to another thead's event queue. The message will
810 // be delivered to this AsyncTask instance on that thread via OnAsyncCall.
811 nsresult
GearsHttpRequest::CallAsync(nsIEventQueue
*event_queue
,
812 AsyncCallType call_type
) {
813 AsyncCallEvent
*event
= new AsyncCallEvent(this, call_type
);
815 return NS_ERROR_OUT_OF_MEMORY
;
817 nsresult rv
= event_queue
->InitEvent(event
, this,
818 reinterpret_cast<PLHandleEventProc
>(AsyncCall_EventHandlerFunc
),
819 reinterpret_cast<PLDestroyEventProc
>(AsyncCall_EventCleanupFunc
));
825 rv
= event_queue
->PostEvent(event
);
827 delete event
; // TODO(michaeln): should call PL_DestroyEvent(event) here?
832 void GearsHttpRequest::HandleEvent(JsEventType event_type
) {
833 assert(IsApartmentThread());
834 assert(event_type
== JSEVENT_UNLOAD
);
836 onreadystatechange_
.reset(); // drop reference, js context is going away
838 // The object can live past the life of js_runner, so remove the monitor
840 unload_monitor_
.reset(NULL
);
842 page_is_unloaded_
= true;
848 GearsHttpRequest::AsyncCall_EventHandlerFunc(AsyncCallEvent
*event
) {
849 event
->request
->OnAsyncCall(event
->call_type
);
856 GearsHttpRequest::AsyncCall_EventCleanupFunc(AsyncCallEvent
*event
) {