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.
26 #include "gears/localserver/npapi/async_task_np.h"
28 #include "gears/base/common/atomic_ops.h"
29 // TODO(mpcomplete): remove these next 2 dependencies
30 #include "gears/base/ie/activex_utils.h"
31 #include "gears/localserver/common/critical_section.h"
32 #include "gears/localserver/common/http_constants.h"
33 #include "gears/localserver/common/http_cookies.h"
35 const char16
*AsyncTask::kCookieRequiredErrorMessage
=
36 STRING16(L
"Required cookie is not present");
38 const char16
*kIsOfflineErrorMessage
=
39 STRING16(L
"The browser is offline");
41 static const int kAsyncTaskMessageType
= 2;
43 //------------------------------------------------------------------------------
45 //------------------------------------------------------------------------------
46 struct AsyncTaskMessage
: public MessageData
{
48 AsyncTaskMessage(int code
, int param
, AsyncTask
*target
)
49 : code(code
), param(param
), target(target
) {
50 target
->AddReference();
53 target
->RemoveReference();
61 //------------------------------------------------------------------------------
62 // AsyncTaskMessageRouter
63 // This class receives messages from the ThreadMessageQueue and routes them
64 // to the target AsyncTask.
65 //------------------------------------------------------------------------------
66 class AsyncTaskMessageRouter
: public ThreadMessageQueue::HandlerInterface
{
69 virtual void HandleThreadMessage(int message_type
, MessageData
*message_data
);
72 void AsyncTaskMessageRouter::Init() {
73 static AsyncTaskMessageRouter instance
;
74 static bool initialized
= false;
77 ThreadMessageQueue::GetInstance()->RegisterHandler(kAsyncTaskMessageType
,
82 void AsyncTaskMessageRouter::HandleThreadMessage(int message_type
,
83 MessageData
*message_data
) {
84 assert(message_type
== kAsyncTaskMessageType
);
86 AsyncTaskMessage
*message
= static_cast<AsyncTaskMessage
*>(message_data
);
87 message
->target
->HandleAsync(message
->code
, message
->param
);
91 //------------------------------------------------------------------------------
93 //------------------------------------------------------------------------------
94 AsyncTask::AsyncTask()
95 : is_initialized_(false),
98 delete_when_done_(false),
99 ready_state_changed_signalled_(false),
100 abort_signalled_(false),
101 listener_thread_id_(0),
107 //------------------------------------------------------------------------------
109 //------------------------------------------------------------------------------
110 AsyncTask::~AsyncTask() {
112 assert(refcount_
== 0 ||
113 (refcount_
== 1 && !delete_when_done_
));
116 void AsyncTask::AddReference() {
117 AtomicIncrement(&refcount_
, 1);
120 void AsyncTask::RemoveReference() {
121 if (AtomicIncrement(&refcount_
, -1) == 0) {
126 //------------------------------------------------------------------------------
128 //------------------------------------------------------------------------------
129 bool AsyncTask::Init() {
130 if (is_initialized_
) {
131 assert(!is_initialized_
);
135 AsyncTaskMessageRouter::Init();
136 ThreadMessageQueue::GetInstance()->InitThreadMessageQueue();
139 is_initialized_
= true;
140 listener_thread_id_
= ThreadMessageQueue::GetInstance()->GetCurrentThreadId();
144 //------------------------------------------------------------------------------
146 //------------------------------------------------------------------------------
147 void AsyncTask::SetListener(Listener
*listener
) {
148 CritSecLock
locker(lock_
);
149 assert(!delete_when_done_
);
150 assert(IsListenerThread());
151 listener_
= listener
;
154 //------------------------------------------------------------------------------
156 //------------------------------------------------------------------------------
157 bool AsyncTask::Start() {
158 if (!is_initialized_
|| thread_
) {
162 assert(IsListenerThread());
164 CritSecLock
locker(lock_
);
166 ready_state_changed_signalled_
= false;
167 abort_signalled_
= false;
168 unsigned int thread_id
;
169 thread_
= reinterpret_cast<HANDLE
>(_beginthreadex(NULL
, 0, &ThreadMain
,
170 this, 0, &thread_id
));
171 task_thread_id_
= thread_id
;
173 if (thread_
== NULL
) return false;
175 AddReference(); // reference is removed upon worker thread exit
179 //------------------------------------------------------------------------------
181 //------------------------------------------------------------------------------
182 void AsyncTask::Abort() {
183 CritSecLock
locker(lock_
);
184 if (thread_
&& !is_aborted_
) {
185 LOG16((L
"AsyncTask::Abort\n"));
187 CallAsync(task_thread_id_
, kAbortMessageCode
, 0);
191 //------------------------------------------------------------------------------
193 //------------------------------------------------------------------------------
194 void AsyncTask::DeleteWhenDone() {
195 assert(!delete_when_done_
);
196 assert(IsListenerThread());
198 LOG(("AsyncTask::DeleteWhenDone\n"));
199 CritSecLock
locker(lock_
);
201 delete_when_done_
= true;
203 // We have to call unlock prior to calling RemoveReference
204 // otherwise the locker would try to access deleted memory, &lock_,
205 // after it's been freed.
207 RemoveReference(); // remove the reference added by the constructor
211 //------------------------------------------------------------------------------
213 //------------------------------------------------------------------------------
214 unsigned int __stdcall
AsyncTask::ThreadMain(void *task
) {
215 // If initialization fails, don't return immediately. Let the thread continue
216 // to run to let signaling around startup/shutdown performed by clients work
217 // properly. Down the road, COM objects will fail to be created or function
218 // and the thread will unwind normally.
219 if (FAILED(CoInitializeEx(NULL
, GEARS_COINIT_THREAD_MODEL
))) {
220 LOG(("AsyncTask::ThreadMain - failed to initialize new thread.\n"));
223 AsyncTask
*self
= reinterpret_cast<AsyncTask
*>(task
);
225 // Don't run until we're sure all state setup by the Start method initialized.
227 CritSecLock
locker(self
->lock_
);
228 assert(self
->IsTaskThread());
231 ThreadMessageQueue::GetInstance()->InitThreadMessageQueue();
235 CloseHandle(self
->thread_
);
236 self
->thread_
= NULL
;
237 self
->RemoveReference(); // remove the reference added by the Start
243 void AsyncTask::CallAsync(ThreadId thread_id
, int code
, int param
) {
244 ThreadMessageQueue::GetInstance()->Send(
246 kAsyncTaskMessageType
,
247 new AsyncTaskMessage(code
, param
, this));
250 void AsyncTask::HandleAsync(int code
, int param
) {
252 case kAbortMessageCode
:
253 assert(IsTaskThread());
254 abort_signalled_
= true;
257 assert(IsListenerThread());
259 listener_
->HandleEvent(code
, param
, this);
265 //------------------------------------------------------------------------------
267 //------------------------------------------------------------------------------
268 void AsyncTask::NotifyListener(int code
, int param
) {
269 CallAsync(listener_thread_id_
, code
, param
);
272 //------------------------------------------------------------------------------
274 //------------------------------------------------------------------------------
275 bool AsyncTask::HttpGet(const char16
*full_url
,
277 const char16
*if_mod_since_date
,
278 const char16
*required_cookie
,
279 WebCacheDB::PayloadInfo
*payload
,
280 bool *was_redirected
,
281 std::string16
*full_redirect_url
,
282 std::string16
*error_message
) {
283 if (was_redirected
) {
284 *was_redirected
= false;
286 if (full_redirect_url
) {
287 full_redirect_url
->clear();
290 error_message
->clear();
293 if (!ActiveXUtils::IsOnline()) {
295 *error_message
= kIsOfflineErrorMessage
;
300 if (required_cookie
&& required_cookie
[0]) {
301 std::string16
required_cookie_str(required_cookie
);
302 std::string16 name
, value
;
303 ParseCookieNameAndValue(required_cookie_str
, &name
, &value
);
304 if (value
!= kNegatedRequiredCookieValue
) {
305 CookieMap cookie_map
;
306 cookie_map
.LoadMapForUrl(full_url
);
307 if (!cookie_map
.HasLocalServerRequiredCookie(required_cookie_str
)) {
309 *(error_message
) = kCookieRequiredErrorMessage
;
316 ScopedHttpRequestPtr
scoped_http_request(HttpRequest::Create());
317 HttpRequest
*http_request
= scoped_http_request
.get();
322 http_request
->SetCachingBehavior(HttpRequest::BYPASS_ALL_CACHES
);
324 if (!http_request
->Open(HttpConstants::kHttpGET
, full_url
, true)) {
329 http_request
->SetRedirectBehavior(HttpRequest::FOLLOW_NONE
);
330 if (!http_request
->SetRequestHeader(HttpConstants::kXGoogleGearsHeader
,
336 if (!http_request
->SetRequestHeader(HttpConstants::kCacheControlHeader
,
337 HttpConstants::kNoCache
)) {
341 if (!http_request
->SetRequestHeader(HttpConstants::kPragmaHeader
,
342 HttpConstants::kNoCache
)) {
346 if (if_mod_since_date
&& if_mod_since_date
[0]) {
347 if (!http_request
->SetRequestHeader(HttpConstants::kIfModifiedSinceHeader
,
348 if_mod_since_date
)) {
353 payload
->data
.reset();
354 http_request
->SetOnReadyStateChange(this);
355 ready_state_changed_signalled_
= false;
357 if (is_aborted_
|| !http_request
->Send()) {
358 http_request
->SetOnReadyStateChange(NULL
);
364 while (!done
&& GetMessage(&msg
, NULL
, 0, 0)) {
365 TranslateMessage(&msg
);
366 DispatchMessage(&msg
);
368 if (ready_state_changed_signalled_
) {
369 ready_state_changed_signalled_
= false;
370 // TODO(michaeln): perhaps simplify the HttpRequest interface to
371 // include a getResponse(&payload) method?
372 HttpRequest::ReadyState state
;
373 if (http_request
->GetReadyState(&state
)) {
374 if (state
== HttpRequest::COMPLETE
) {
378 if (http_request
->GetStatus(&status
)) {
379 payload
->status_code
= status
;
380 if (http_request
->GetStatusLine(&payload
->status_line
)) {
381 if (http_request
->GetAllResponseHeaders(&payload
->headers
)) {
382 payload
->data
.reset(http_request
->GetResponseBody());
389 LOG16((L
"AsyncTask - getReadyState failed, aborting request\n"));
390 http_request
->Abort();
393 } else if (abort_signalled_
) {
394 abort_signalled_
= false;
395 LOG16((L
"AsyncTask - abort event signalled, aborting request\n"));
396 // We abort the request but continue the loop waiting for it to complete
397 // TODO(michaeln): paranoia, what if it never does complete, timer?
398 http_request
->Abort();
402 http_request
->SetOnReadyStateChange(NULL
);
404 if (http_request
->WasRedirected()) {
405 if (was_redirected
) {
406 *was_redirected
= true;
408 if (full_redirect_url
) {
409 http_request
->GetFinalUrl(full_redirect_url
);
413 return !is_aborted_
&& payload
->data
.get();
416 //------------------------------------------------------------------------------
417 // HttpRequest::ReadyStateListener::ReadyStateChanged
418 //------------------------------------------------------------------------------
419 void AsyncTask::ReadyStateChanged(HttpRequest
*source
) {
420 assert(IsTaskThread());
421 ready_state_changed_signalled_
= true;