[Author: aa]
[google-gears.git] / gears / localserver / npapi / async_task_np.cc
blob9b2c83d792d2f38203fbde6b99bad2e970c4e202
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 #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 //------------------------------------------------------------------------------
44 // AsyncTaskMessage
45 //------------------------------------------------------------------------------
46 struct AsyncTaskMessage : public MessageData {
47 public:
48 AsyncTaskMessage(int code, int param, AsyncTask *target)
49 : code(code), param(param), target(target) {
50 target->AddReference();
52 ~AsyncTaskMessage() {
53 target->RemoveReference();
56 int code;
57 int param;
58 AsyncTask *target;
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 {
67 public:
68 static void Init();
69 virtual void HandleThreadMessage(int message_type, MessageData *message_data);
72 void AsyncTaskMessageRouter::Init() {
73 static AsyncTaskMessageRouter instance;
74 static bool initialized = false;
75 if (!initialized) {
76 initialized = true;
77 ThreadMessageQueue::GetInstance()->RegisterHandler(kAsyncTaskMessageType,
78 &instance);
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 //------------------------------------------------------------------------------
92 // AsyncTask
93 //------------------------------------------------------------------------------
94 AsyncTask::AsyncTask()
95 : is_initialized_(false),
96 is_aborted_(false),
97 refcount_(1),
98 delete_when_done_(false),
99 ready_state_changed_signalled_(false),
100 abort_signalled_(false),
101 listener_thread_id_(0),
102 task_thread_id_(0),
103 listener_(NULL),
104 thread_(NULL) {
107 //------------------------------------------------------------------------------
108 // ~AsyncTask
109 //------------------------------------------------------------------------------
110 AsyncTask::~AsyncTask() {
111 assert(!thread_);
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) {
122 delete this;
126 //------------------------------------------------------------------------------
127 // Init
128 //------------------------------------------------------------------------------
129 bool AsyncTask::Init() {
130 if (is_initialized_) {
131 assert(!is_initialized_);
132 return false;
135 AsyncTaskMessageRouter::Init();
136 ThreadMessageQueue::GetInstance()->InitThreadMessageQueue();
138 is_aborted_ = false;
139 is_initialized_ = true;
140 listener_thread_id_ = ThreadMessageQueue::GetInstance()->GetCurrentThreadId();
141 return true;
144 //------------------------------------------------------------------------------
145 // SetListener
146 //------------------------------------------------------------------------------
147 void AsyncTask::SetListener(Listener *listener) {
148 CritSecLock locker(lock_);
149 assert(!delete_when_done_);
150 assert(IsListenerThread());
151 listener_ = listener;
154 //------------------------------------------------------------------------------
155 // Start
156 //------------------------------------------------------------------------------
157 bool AsyncTask::Start() {
158 if (!is_initialized_ || thread_) {
159 return false;
162 assert(IsListenerThread());
164 CritSecLock locker(lock_);
165 is_aborted_ = false;
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
176 return true;
179 //------------------------------------------------------------------------------
180 // Abort
181 //------------------------------------------------------------------------------
182 void AsyncTask::Abort() {
183 CritSecLock locker(lock_);
184 if (thread_ && !is_aborted_) {
185 LOG16((L"AsyncTask::Abort\n"));
186 is_aborted_ = true;
187 CallAsync(task_thread_id_, kAbortMessageCode, 0);
191 //------------------------------------------------------------------------------
192 // DeleteWhenDone
193 //------------------------------------------------------------------------------
194 void AsyncTask::DeleteWhenDone() {
195 assert(!delete_when_done_);
196 assert(IsListenerThread());
198 LOG(("AsyncTask::DeleteWhenDone\n"));
199 CritSecLock locker(lock_);
200 SetListener(NULL);
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.
206 locker.Unlock();
207 RemoveReference(); // remove the reference added by the constructor
211 //------------------------------------------------------------------------------
212 // ThreadMain
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();
233 self->Run();
235 CloseHandle(self->thread_);
236 self->thread_ = NULL;
237 self->RemoveReference(); // remove the reference added by the Start
239 CoUninitialize();
240 return 0;
243 void AsyncTask::CallAsync(ThreadId thread_id, int code, int param) {
244 ThreadMessageQueue::GetInstance()->Send(
245 thread_id,
246 kAsyncTaskMessageType,
247 new AsyncTaskMessage(code, param, this));
250 void AsyncTask::HandleAsync(int code, int param) {
251 switch (code) {
252 case kAbortMessageCode:
253 assert(IsTaskThread());
254 abort_signalled_ = true;
255 break;
256 default:
257 assert(IsListenerThread());
258 if (listener_) {
259 listener_->HandleEvent(code, param, this);
261 break;
265 //------------------------------------------------------------------------------
266 // NotifyListener
267 //------------------------------------------------------------------------------
268 void AsyncTask::NotifyListener(int code, int param) {
269 CallAsync(listener_thread_id_, code, param);
272 //------------------------------------------------------------------------------
273 // HttpGet
274 //------------------------------------------------------------------------------
275 bool AsyncTask::HttpGet(const char16 *full_url,
276 bool is_capturing,
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();
289 if (error_message) {
290 error_message->clear();
293 if (!ActiveXUtils::IsOnline()) {
294 if (error_message) {
295 *error_message = kIsOfflineErrorMessage;
297 return false;
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)) {
308 if (error_message) {
309 *(error_message) = kCookieRequiredErrorMessage;
311 return false;
316 ScopedHttpRequestPtr scoped_http_request(HttpRequest::Create());
317 HttpRequest *http_request = scoped_http_request.get();
318 if (!http_request) {
319 return false;
322 http_request->SetCachingBehavior(HttpRequest::BYPASS_ALL_CACHES);
324 if (!http_request->Open(HttpConstants::kHttpGET, full_url, true)) {
325 return false;
328 if (is_capturing) {
329 http_request->SetRedirectBehavior(HttpRequest::FOLLOW_NONE);
330 if (!http_request->SetRequestHeader(HttpConstants::kXGoogleGearsHeader,
331 STRING16(L"1"))) {
332 return false;
336 if (!http_request->SetRequestHeader(HttpConstants::kCacheControlHeader,
337 HttpConstants::kNoCache)) {
338 return false;
341 if (!http_request->SetRequestHeader(HttpConstants::kPragmaHeader,
342 HttpConstants::kNoCache)) {
343 return false;
346 if (if_mod_since_date && if_mod_since_date[0]) {
347 if (!http_request->SetRequestHeader(HttpConstants::kIfModifiedSinceHeader,
348 if_mod_since_date)) {
349 return false;
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);
359 return false;
362 bool done = false;
363 MSG msg;
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) {
375 done = true;
376 if (!is_aborted_) {
377 int status;
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());
388 } else {
389 LOG16((L"AsyncTask - getReadyState failed, aborting request\n"));
390 http_request->Abort();
391 done = true;
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;