~ubuntu-branches/ubuntu/karmic/gears/karmic

« back to all changes in this revision

Viewing changes to gears/localserver/safari/async_task_sf.mm

  • Committer: Bazaar Package Importer
  • Author(s): Stefan Lesicnik
  • Date: 2009-04-30 19:15:25 UTC
  • Revision ID: james.westby@ubuntu.com-20090430191525-0790sb5wzg8ou0xb
Tags: upstream-0.5.21.0~svn3334+dfsg
ImportĀ upstreamĀ versionĀ 0.5.21.0~svn3334+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.
 
14
//
 
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.
 
25
 
 
26
#import <assert.h>
 
27
 
 
28
#import "gears/blob/blob_interface.h"
 
29
#import "gears/localserver/common/http_constants.h"
 
30
#import "gears/localserver/common/http_cookies.h"
 
31
#import "gears/localserver/safari/async_task_sf.h"
 
32
 
 
33
const char16 *AsyncTask::kCookieRequiredErrorMessage =
 
34
                  STRING16(L"Required cookie is not present");
 
35
 
 
36
//------------------------------------------------------------------------------
 
37
// AsyncTask
 
38
//------------------------------------------------------------------------------
 
39
AsyncTask::AsyncTask(BrowsingContext *browsing_context) :
 
40
    is_aborted_(false),
 
41
    is_initialized_(false),
 
42
    browsing_context_(browsing_context),
 
43
    is_destructing_(false),
 
44
    delete_when_done_(false),
 
45
    thread_(NULL),
 
46
    listener_(NULL),
 
47
    msg_port_(NULL) {
 
48
}
 
49
 
 
50
//------------------------------------------------------------------------------
 
51
// ~AsyncTask
 
52
//------------------------------------------------------------------------------
 
53
AsyncTask::~AsyncTask() {
 
54
  if (thread_) {
 
55
    LOG(("~AsyncTask - thread is still running: %p\n", this));
 
56
    // extra scope to lock our monitor
 
57
    {
 
58
      CritSecLock locker(lock_);
 
59
      is_destructing_ = true;
 
60
      locker.Unlock();
 
61
      Abort();
 
62
    }
 
63
    pthread_join(thread_, NULL);
 
64
    thread_ = NULL;
 
65
  }
 
66
}
 
67
 
 
68
//------------------------------------------------------------------------------
 
69
// Init
 
70
//------------------------------------------------------------------------------
 
71
bool AsyncTask::Init() {
 
72
  if (is_initialized_) {
 
73
    assert(!is_initialized_);
 
74
    return false;
 
75
  }
 
76
 
 
77
  is_aborted_ = false;
 
78
  is_initialized_ = true;
 
79
 
 
80
  // Create a mach port to communicate between the threads
 
81
  CFMachPortContext context;
 
82
  context.version = 0;
 
83
  context.info = this;
 
84
  context.retain = NULL;
 
85
  context.release = NULL;
 
86
  context.copyDescription = NULL;
 
87
  Boolean should_free = false;    // Don't free context.info on dealloc
 
88
  msg_port_.reset(CFMachPortCreate(kCFAllocatorDefault, OnAsyncCall, 
 
89
                                   &context, &should_free));
 
90
 
 
91
  if (!msg_port_.get() || should_free) {
 
92
    LOG(("Couldn't make mach port\n"));
 
93
    return false;
 
94
  }
 
95
 
 
96
  // Create a RL source and add it to the current runloop.  This is so that
 
97
  // the thread can send a message to the main thread via OnAsyncCall()
 
98
  scoped_CFRunLoopSource 
 
99
    msg_source(CFMachPortCreateRunLoopSource(kCFAllocatorDefault, 
 
100
                                             msg_port_.get(), 0));
 
101
  CFRunLoopAddSource(CFRunLoopGetCurrent(), msg_source.get(),
 
102
                     kCFRunLoopCommonModes);
 
103
 
 
104
  return true;
 
105
}
 
106
 
 
107
//------------------------------------------------------------------------------
 
108
// SetListener
 
109
//------------------------------------------------------------------------------
 
110
void AsyncTask::SetListener(Listener *listener) {
 
111
  listener_ = listener;
 
112
}
 
113
 
 
114
//------------------------------------------------------------------------------
 
115
// Start
 
116
//------------------------------------------------------------------------------
 
117
bool AsyncTask::Start() {
 
118
  if (!is_initialized_) {
 
119
    assert(is_initialized_);
 
120
    return false;
 
121
  }
 
122
 
 
123
  is_aborted_ = false;
 
124
  if (pthread_create(&thread_, NULL, ThreadEntry, this))
 
125
    return false;
 
126
 
 
127
  return (thread_ != NULL);
 
128
}
 
129
 
 
130
//------------------------------------------------------------------------------
 
131
// Abort
 
132
//------------------------------------------------------------------------------
 
133
void AsyncTask::Abort() {
 
134
  CritSecLock locker(lock_);
 
135
  if (thread_ && !is_aborted_) {
 
136
    is_aborted_ = true;
 
137
  }
 
138
}
 
139
 
 
140
//------------------------------------------------------------------------------
 
141
// DeleteWhenDone
 
142
//------------------------------------------------------------------------------
 
143
void AsyncTask::DeleteWhenDone() {
 
144
  CritSecLock locker(lock_);
 
145
  assert(!delete_when_done_);
 
146
  if (!delete_when_done_) {
 
147
    if (!thread_) {
 
148
      // In this particular code path, we have to call unlock prior to delete
 
149
      // otherwise the locker would try to access deleted memory, &lock_,
 
150
      // after it's been freed.
 
151
      locker.Unlock();
 
152
      delete this;
 
153
    } else {
 
154
      delete_when_done_ = true;
 
155
    }
 
156
  }
 
157
}
 
158
 
 
159
//------------------------------------------------------------------------------
 
160
// ThreadEntry - Our worker thread's entry procedure
 
161
//------------------------------------------------------------------------------
 
162
void *AsyncTask::ThreadEntry(void *task) {
 
163
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
164
  
 
165
  AsyncTask *asyncTask = reinterpret_cast<AsyncTask*>(task);
 
166
  asyncTask->Run();
 
167
  asyncTask->NotifyListener(kThreadDoneMessageCode, 0);
 
168
  
 
169
  [pool release];
 
170
 
 
171
  return NULL;
 
172
}
 
173
 
 
174
//------------------------------------------------------------------------------
 
175
// NotifyListener
 
176
//------------------------------------------------------------------------------
 
177
void AsyncTask::NotifyListener(int code, int param) {
 
178
  ThreadMessage msg = {0};
 
179
  
 
180
  // Post a Mach message to be recieved by the main thread via OnAsyncCall()
 
181
  msg.header.msgh_size = sizeof(ThreadMessage);
 
182
  msg.header.msgh_remote_port = CFMachPortGetPort(msg_port_.get());
 
183
  msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
 
184
                                        MACH_MSG_TYPE_MAKE_SEND_ONCE);
 
185
  msg.code = code;
 
186
  msg.param = param;
 
187
  mach_msg(&(msg.header),
 
188
           MACH_SEND_MSG | MACH_SEND_TIMEOUT,
 
189
           msg.header.msgh_size, 0, 0, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
 
190
}
 
191
 
 
192
//------------------------------------------------------------------------------
 
193
// OnThreadDone
 
194
//------------------------------------------------------------------------------
 
195
void AsyncTask::OnThreadDone() {
 
196
  CritSecLock locker(lock_);
 
197
  if (delete_when_done_) {
 
198
    thread_ = NULL;
 
199
    // In this particular code path, we have to call unlock prior to delete
 
200
    // otherwise the locker would try to access deleted memory, &lock_,
 
201
    // after it's been freed.
 
202
    locker.Unlock();
 
203
    delete this;
 
204
  }
 
205
}
 
206
 
 
207
//------------------------------------------------------------------------------
 
208
// HttpGet
 
209
//------------------------------------------------------------------------------
 
210
bool AsyncTask::HttpGet(const char16 *full_url,
 
211
                        bool is_capturing,
 
212
                        const char16 *reason_header_value,
 
213
                        const char16 *if_mod_since_date,
 
214
                        const char16 *required_cookie,
 
215
                        WebCacheDB::PayloadInfo *payload,
 
216
                        scoped_refptr<BlobInterface> *payload_data,
 
217
                        bool *was_redirected,
 
218
                        std::string16 *full_redirect_url,
 
219
                        std::string16 *error_message) {
 
220
  return MakeHttpRequest(HttpConstants::kHttpGET,
 
221
                         full_url,
 
222
                         is_capturing,
 
223
                         reason_header_value,
 
224
                         NULL,  // content_type_header_value
 
225
                         if_mod_since_date,
 
226
                         required_cookie,
 
227
                         false,
 
228
                         NULL,
 
229
                         payload,
 
230
                         payload_data,
 
231
                         was_redirected,
 
232
                         full_redirect_url,
 
233
                         error_message);
 
234
}
 
235
 
 
236
//------------------------------------------------------------------------------
 
237
// HttpPost
 
238
//------------------------------------------------------------------------------
 
239
bool AsyncTask::HttpPost(const char16 *full_url,
 
240
                         bool is_capturing,
 
241
                         const char16 *reason_header_value,
 
242
                         const char16 *content_type_header_value,
 
243
                         const char16 *if_mod_since_date,
 
244
                         const char16 *required_cookie,
 
245
                         bool disable_browser_cookies,
 
246
                         BlobInterface *post_body,
 
247
                         WebCacheDB::PayloadInfo *payload,
 
248
                         scoped_refptr<BlobInterface> *payload_data,
 
249
                         bool *was_redirected,
 
250
                         std::string16 *full_redirect_url,
 
251
                         std::string16 *error_message) {
 
252
  return MakeHttpRequest(HttpConstants::kHttpPOST,
 
253
                         full_url,
 
254
                         is_capturing,
 
255
                         reason_header_value,
 
256
                         content_type_header_value,
 
257
                         if_mod_since_date,
 
258
                         required_cookie,
 
259
                         disable_browser_cookies,
 
260
                         post_body,
 
261
                         payload,
 
262
                         payload_data,
 
263
                         was_redirected,
 
264
                         full_redirect_url,
 
265
                         error_message);
 
266
}
 
267
 
 
268
//------------------------------------------------------------------------------
 
269
// MakeHttpRequest
 
270
//------------------------------------------------------------------------------
 
271
bool AsyncTask::MakeHttpRequest(const char16 *method,
 
272
                                const char16 *full_url,
 
273
                                bool is_capturing,
 
274
                                const char16 *reason_header_value,
 
275
                                const char16 *content_type_header_value,
 
276
                                const char16 *if_mod_since_date,
 
277
                                const char16 *required_cookie,
 
278
                                bool disable_browser_cookies,
 
279
                                BlobInterface *post_body,
 
280
                                WebCacheDB::PayloadInfo *payload,
 
281
                                scoped_refptr<BlobInterface> *payload_data,
 
282
                                bool *was_redirected,
 
283
                                std::string16 *full_redirect_url,
 
284
                                std::string16 *error_message) {
 
285
  assert(payload);
 
286
  if (was_redirected) {
 
287
    *was_redirected = false;
 
288
  }
 
289
  if (full_redirect_url) {
 
290
    full_redirect_url->clear();
 
291
  }
 
292
  if (error_message) {
 
293
    error_message->clear();
 
294
  }
 
295
  
 
296
  if (required_cookie && required_cookie[0]) {
 
297
    std::string16 required_cookie_str(required_cookie);
 
298
    std::string16 name, value;
 
299
    ParseCookieNameAndValue(required_cookie_str, &name, &value);
 
300
    if (value != kNegatedRequiredCookieValue) {
 
301
      CookieMap cookie_map;
 
302
      cookie_map.LoadMapForUrl(full_url, browsing_context_.get());
 
303
      if (!cookie_map.HasLocalServerRequiredCookie(required_cookie_str)) {
 
304
        if (error_message) {
 
305
          *(error_message) = kCookieRequiredErrorMessage;
 
306
        }
 
307
        return false;
 
308
      }
 
309
    }
 
310
  }
 
311
 
 
312
  scoped_refptr<HttpRequest> http_request;
 
313
  if (!HttpRequest::Create(&http_request))
 
314
    return false;
 
315
    
 
316
  http_request->SetCachingBehavior(HttpRequest::BYPASS_ALL_CACHES);
 
317
 
 
318
  if (!http_request->Open(method, full_url, true, browsing_context_.get())) {
 
319
    return false;
 
320
  }
 
321
 
 
322
  if (is_capturing) {
 
323
    http_request->SetRedirectBehavior(HttpRequest::FOLLOW_NONE);
 
324
    if (!http_request->SetRequestHeader(HttpConstants::kXGoogleGearsHeader,
 
325
                                        STRING16(L"1"))) {
 
326
      return false;
 
327
    }
 
328
  }
 
329
 
 
330
  if (!http_request->SetRequestHeader(HttpConstants::kCacheControlHeader,
 
331
                                      HttpConstants::kNoCache)) {
 
332
    return false;
 
333
  }
 
334
 
 
335
  if (!http_request->SetRequestHeader(HttpConstants::kPragmaHeader,
 
336
                                      HttpConstants::kNoCache)) {
 
337
    return false;
 
338
  }
 
339
  
 
340
  if (reason_header_value && reason_header_value[0]) {
 
341
    if (!http_request->SetRequestHeader(HttpConstants::kXGearsReasonHeader,
 
342
                                        reason_header_value)) {
 
343
      return false;
 
344
    }
 
345
  }
 
346
 
 
347
  if (content_type_header_value && content_type_header_value[0]) {
 
348
    if (!http_request->SetRequestHeader(HttpConstants::kContentTypeHeader,
 
349
                                        content_type_header_value)) {
 
350
      return false;
 
351
    }
 
352
  }
 
353
 
 
354
  if (if_mod_since_date && if_mod_since_date[0]) {
 
355
    if (!http_request->SetRequestHeader(HttpConstants::kIfModifiedSinceHeader,
 
356
                                        if_mod_since_date)) {
 
357
      return false;
 
358
    }
 
359
  }
 
360
 
 
361
  if (disable_browser_cookies) {
 
362
    if (!http_request->SetCookieBehavior(
 
363
        HttpRequest::DO_NOT_SEND_BROWSER_COOKIES)) {
 
364
      return false;
 
365
    }
 
366
  }
 
367
 
 
368
  payload->data.reset();
 
369
 
 
370
  // Rely on logic inside HttpRequest to check for valid combinations of
 
371
  // method and presence of body.
 
372
  bool result = http_request->Send(post_body);
 
373
  if (!result) {
 
374
    return false;
 
375
  }
 
376
 
 
377
  // Wait for the data to come back from the HTTP request
 
378
  while (1) {
 
379
    HttpRequest::ReadyState ready_state;
 
380
    http_request->GetReadyState(&ready_state);
 
381
 
 
382
    if (ready_state == HttpRequest::COMPLETE) {
 
383
      break;
 
384
    }
 
385
 
 
386
    if (is_aborted_) {
 
387
      http_request->Abort();
 
388
      break;
 
389
    }
 
390
 
 
391
    // Run for a second in the run loop
 
392
    CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, true);
 
393
  }
 
394
 
 
395
  // Extract the status & data
 
396
  int status;
 
397
  if (http_request->GetStatus(&status)) {
 
398
    payload->status_code = status;
 
399
    if (http_request->GetStatusLine(&payload->status_line)) {
 
400
      if (http_request->GetAllResponseHeaders(&payload->headers)) {
 
401
        // TODO(bgarcia): Make WebCacheDB::PayloadInfo.data a Blob.
 
402
        http_request->GetResponseBody(payload_data);
 
403
      }
 
404
    }
 
405
  }
 
406
 
 
407
  // If we were redirected during the load, setup the return variables
 
408
  if (http_request->WasRedirected()) {
 
409
    if (was_redirected)
 
410
      *was_redirected = true;
 
411
 
 
412
    if (full_redirect_url)
 
413
      http_request->GetFinalUrl(full_redirect_url);
 
414
  }
 
415
 
 
416
  return !is_aborted_ && payload_data->get();
 
417
}
 
418
 
 
419
//------------------------------------------------------------------------------
 
420
// static OnAsyncCall
 
421
//------------------------------------------------------------------------------
 
422
void AsyncTask::OnAsyncCall(CFMachPortRef port, void *mach_msg, CFIndex size,
 
423
                            void *info) {
 
424
  ThreadMessage *msg = (ThreadMessage *)mach_msg;
 
425
  AsyncTask *asyncTask = (AsyncTask *)info;
 
426
  
 
427
  if (msg->code == kThreadDoneMessageCode) {
 
428
    asyncTask->OnThreadDone();
 
429
    return;
 
430
  }
 
431
 
 
432
  if (asyncTask->listener_)
 
433
    asyncTask->listener_->HandleAsyncTaskEvent(
 
434
        msg->code, msg->param, asyncTask);
 
435
}