[Author: zork]
[google-gears.git] / gears / httprequest / firefox / httprequest_ff.cc
blob7ff0e72befb3c087ac6e061f94d8cc62223c1f8f
1 // Copyright 2007, Google Inc.
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
5 //
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.
26 #ifdef WIN32
27 #include <windows.h> // must manually #include before nsIEventQueueService.h
28 #endif
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"
42 #endif
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
48 bool IsUiThread();
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)
58 NS_INTERFACE_MAP_END
60 // Object identifiers
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 } };
66 // Error messages
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() {
82 assert(!request_);
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)) {
99 RETURN_EXCEPTION(
100 STRING16(L"Invalid value for onreadystatechange property."));
103 onreadystatechange_.reset(callback);
104 InitUnloadMonitor();
106 RETURN_NORMAL();
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_);
123 if (IsComplete()) {
124 request_info_.reset(NULL);
125 response_info_.reset(NULL);
126 assert(!request_);
129 if (!IsUninitialized()) {
130 RETURN_EXCEPTION(kAlreadyOpenError);
133 JsParamFetcher js_params(this);
134 if (js_params.GetCount(false) < 2) {
135 RETURN_EXCEPTION(
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."));
144 std::string16 url;
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;
166 #endif
169 OnReadyStateChangedCall();
171 RETURN_NORMAL();
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"),
186 STRING16(L"Date"),
187 STRING16(L"Expect"),
188 STRING16(L"Host"),
189 STRING16(L"Keep-Alive"),
190 STRING16(L"Referer"),
191 STRING16(L"TE"),
192 STRING16(L"Trailer"),
193 STRING16(L"Transfer-Encoding"),
194 STRING16(L"Upgrade"),
195 STRING16(L"Via") };
196 for (int i = 0; i < static_cast<int>(ARRAYSIZE(kDisallowedHeaders)); ++i) {
197 if (StringCompareIgnoreCase(header, kDisallowedHeaders[i]) == 0)
198 return true;
200 return false;
203 NS_IMETHODIMP GearsHttpRequest::SetRequestHeader() {
204 assert(IsApartmentThread());
205 MutexLock locker(&lock_);
206 if (!IsOpen()) {
207 RETURN_EXCEPTION(kNotOpenError);
210 JsParamFetcher js_params(this);
211 if (js_params.GetCount(false) < 2) {
212 RETURN_EXCEPTION(
213 STRING16(L"The name and value parameters are required."));
216 std::string16 name;
217 if (!js_params.GetAsString(0, &name)) {
218 RETURN_EXCEPTION(STRING16(L"The name parameter must be a string."));
221 std::string16 value;
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;
237 RETURN_NORMAL();
240 //------------------------------------------------------------------------------
241 // void send(opt_data)
242 //------------------------------------------------------------------------------
243 NS_IMETHODIMP GearsHttpRequest::Send() {
244 assert(IsApartmentThread());
245 MutexLock locker(&lock_);
246 if (!IsOpen()) {
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
268 return true;
271 InitUnloadMonitor();
273 if (!CallSendOnUiThread()) {
274 response_info_.reset(NULL);
275 RETURN_EXCEPTION(kInternalError);
278 RETURN_NORMAL();
281 bool GearsHttpRequest::CallSendOnUiThread() {
282 return CallAsync(ui_event_queue_, kSend) == NS_OK;
285 void GearsHttpRequest::OnSendCall() {
286 assert(IsUiThread());
287 assert(!request_);
288 assert(request_info_.get());
289 assert(response_info_.get());
291 MutexLock locker(&lock_);
292 CreateRequest();
293 bool ok = request_->Open(request_info_->method.c_str(),
294 request_info_->full_url.c_str(),
295 true); // async
296 if (!ok) {
297 response_info_->pending_ready_state = HttpRequest::COMPLETE;
298 RemoveRequest();
299 CallReadyStateChangedOnApartmentThread();
300 return;
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());
317 } else {
318 ok = request_->Send();
321 if (!ok) {
322 response_info_->pending_ready_state = HttpRequest::COMPLETE;
323 RemoveRequest();
324 CallReadyStateChangedOnApartmentThread();
325 return;
330 //------------------------------------------------------------------------------
331 // void abort()
332 //------------------------------------------------------------------------------
333 NS_IMETHODIMP GearsHttpRequest::Abort() {
334 assert(IsApartmentThread());
335 CallAbortOnUiThread();
336 RETURN_NORMAL();
339 bool GearsHttpRequest::CallAbortOnUiThread() {
340 if (IsUiThread()) {
341 OnAbortCall();
342 return true;
343 } else if (ui_event_queue_) {
344 return CallAsync(ui_event_queue_, kAbort) == NS_OK;
345 } else {
346 return true;
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_);
356 if (request_) {
357 request_info_.reset(NULL);
358 response_info_.reset(NULL);
359 request_->SetOnReadyStateChange(NULL);
360 request_->Abort();
361 RemoveRequest();
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
382 } else {
383 assert(false);
384 RETURN_EXCEPTION(kInternalError);
386 RETURN_NORMAL();
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);
405 RETURN_NORMAL();
407 retval.Assign(response_info_->headers.c_str());
408 RETURN_NORMAL();
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);
423 RETURN_NORMAL();
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(),
430 &headers_utf8);
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 */)) {
435 return false;
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."));
444 std::string16 name;
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());
453 std::string16 value;
454 UTF8ToString16(value_utf8 ? value_utf8 : "", &value);
455 retval.Assign(value.c_str());
456 RETURN_NORMAL();
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);
471 RETURN_NORMAL();
473 retval.Assign(response_info_->response_text.c_str());
474 RETURN_NORMAL();
477 #ifdef OFFICIAL_BUILD
478 // Blob support is not ready for prime time yet
479 #else
480 //------------------------------------------------------------------------------
481 // readonly attribute Blob responseBlob
482 //------------------------------------------------------------------------------
483 NS_IMETHODIMP GearsHttpRequest::GetResponseBlob(nsISupports **retval) {
484 assert(IsApartmentThread());
485 MutexLock locker(&lock_);
486 if (!IsComplete()) {
487 RETURN_EXCEPTION(STRING16(L"responseBlob is not supported before request "
488 L"is complete"));
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();
496 if (body) {
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;
509 (*retval)->AddRef();
510 RETURN_NORMAL();
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;
528 RETURN_NORMAL();
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());
545 RETURN_NORMAL();
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());
601 RemoveRequest();
603 CallReadyStateChangedOnApartmentThread();
608 bool GearsHttpRequest::CallReadyStateChangedOnApartmentThread() {
609 return CallAsync(apartment_event_queue_, kReadyStateChanged) == NS_OK;
612 void GearsHttpRequest::OnReadyStateChangedCall() {
613 assert(IsApartmentThread());
614 lock_.Lock();
615 response_info_->ready_state = response_info_->pending_ready_state;
616 bool is_complete = IsComplete();
617 lock_.Unlock();
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();
623 if (handler) {
624 JsRunnerInterface *runner = GetJsRunner();
625 assert(runner);
626 if (runner) {
627 runner->InvokeCallback(handler, 0, NULL, NULL);
629 if (is_complete)
630 delete handler;
635 //------------------------------------------------------------------------------
636 // CreateRequest, RemoveRequest
637 //------------------------------------------------------------------------------
639 void GearsHttpRequest::CreateRequest() {
640 assert(IsUiThread());
641 RemoveRequest();
642 request_ = HttpRequest::Create();
643 request_->SetCachingBehavior(HttpRequest::USE_ALL_CACHES);
644 request_->SetRedirectBehavior(HttpRequest::FOLLOW_WITHIN_ORIGIN);
645 this->AddRef();
648 void GearsHttpRequest::RemoveRequest() {
649 assert(IsUiThread());
650 if (request_) {
651 request_->SetOnReadyStateChange(NULL);
652 request_->ReleaseReference();
653 request_ = NULL;
654 this->Release();
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.");
674 return false;
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.");
681 return false;
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.");
688 return false;
691 return true;
695 //------------------------------------------------------------------------------
696 // IsUninitialized, IsOpen, IsSent, IsInteractive, and IsComplete.
697 // The caller is responsible for acquiring the lock prior to calling these
698 // state accessors.
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 //------------------------------------------------------------------------------
733 // InitEventQueues
734 //------------------------------------------------------------------------------
735 bool GearsHttpRequest::InitEventQueues() {
736 assert(IsApartmentThread());
737 nsresult rv = NS_OK;
739 nsCOMPtr<nsIEventQueueService> event_queue_service =
740 do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv);
741 if (NS_FAILED(rv) || !event_queue_service) {
742 return false;
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_) {
749 return false;
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_) {
757 return false;
761 return true;
765 //------------------------------------------------------------------------------
766 // InitUnloadMonitor
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,
772 this));
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) {
783 switch (call_type) {
784 case kDataAvailable:
785 OnDataAvailableCall();
786 break;
787 case kReadyStateChanged:
788 OnReadyStateChangedCall();
789 break;
790 case kSend:
791 OnSendCall();
792 break;
793 case kAbort:
794 OnAbortCall();
795 break;
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);
814 if (!event) {
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));
820 if (NS_FAILED(rv)) {
821 delete event;
822 return rv;
825 rv = event_queue->PostEvent(event);
826 if (NS_FAILED(rv)) {
827 delete event; // TODO(michaeln): should call PL_DestroyEvent(event) here?
829 return rv;
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
839 // manually.
840 unload_monitor_.reset(NULL);
842 page_is_unloaded_ = true;
843 Abort();
846 // static
847 void *PR_CALLBACK
848 GearsHttpRequest::AsyncCall_EventHandlerFunc(AsyncCallEvent *event) {
849 event->request->OnAsyncCall(event->call_type);
850 return nsnull;
854 // static
855 void PR_CALLBACK
856 GearsHttpRequest::AsyncCall_EventCleanupFunc(AsyncCallEvent *event) {
857 delete event;