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

« back to all changes in this revision

Viewing changes to gears/localserver/resource_store_module.cc

  • 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 2005, 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
#include "gears/localserver/resource_store_module.h"
 
27
 
 
28
#if BROWSER_IE || BROWSER_IEMOBILE
 
29
#include <windows.h>
 
30
#endif
 
31
 
 
32
#include "gears/base/common/js_dom_element.h"
 
33
#include "gears/base/common/mime_detect.h"
 
34
#include "gears/base/common/url_utils.h"
 
35
#include "gears/blob/blob.h"
 
36
#include "gears/blob/buffer_blob.h"
 
37
#include "gears/blob/file_blob.h"
 
38
#include "gears/localserver/common/http_constants.h"
 
39
#include "gears/localserver/file_submitter.h"
 
40
 
 
41
 
 
42
//-----------------------------------------------------------------------------
 
43
// GearsResourceStoreMessageHwnd
 
44
//-----------------------------------------------------------------------------
 
45
#if BROWSER_IE || BROWSER_IEMOBILE
 
46
class GearsResourceStoreMessageHwnd
 
47
    : public CWindowImpl<GearsResourceStoreMessageHwnd> {
 
48
 public:
 
49
  static const int kCaptureTaskMessageBase = WM_USER;
 
50
  static const int
 
51
      WM_CAPTURE_TASK_COMPLETE = CaptureTask::CAPTURE_TASK_COMPLETE
 
52
                                 + kCaptureTaskMessageBase;
 
53
  static const int
 
54
      WM_CAPTURE_URL_SUCCEEDED = CaptureTask::CAPTURE_URL_SUCCEEDED
 
55
                                 + kCaptureTaskMessageBase;
 
56
  static const int
 
57
      WM_CAPTURE_URL_FAILED = CaptureTask::CAPTURE_URL_FAILED
 
58
                              + kCaptureTaskMessageBase;
 
59
 
 
60
  BEGIN_MSG_MAP(GearsResourceStoreMessageHwnd)
 
61
    MESSAGE_HANDLER(WM_CAPTURE_TASK_COMPLETE, OnCaptureTaskComplete)
 
62
    MESSAGE_HANDLER(WM_CAPTURE_URL_SUCCEEDED, OnCaptureUrlComplete)
 
63
    MESSAGE_HANDLER(WM_CAPTURE_URL_FAILED, OnCaptureUrlComplete)
 
64
  END_MSG_MAP()
 
65
 
 
66
  GearsResourceStoreMessageHwnd(GearsResourceStore *resource_store)
 
67
      : resource_store_(resource_store) {}
 
68
 
 
69
  void Initialize() {
 
70
    // Make sure we have an HWND
 
71
    if (!IsWindow()) {
 
72
      if (!Create(kMessageOnlyWindowParent,    // parent
 
73
                  NULL,                        // position
 
74
                  NULL,                        // name
 
75
                  kMessageOnlyWindowStyle)) {  // style
 
76
        assert(false);
 
77
      }
 
78
    }
 
79
  }
 
80
 
 
81
  LRESULT OnCaptureTaskComplete(UINT uMsg,
 
82
                                WPARAM wParam,
 
83
                                LPARAM lParam,
 
84
                                BOOL& bHandled) {
 
85
    CaptureTask* task = reinterpret_cast<CaptureTask*>(lParam);
 
86
    if (task && (task == resource_store_->capture_task_.get())) {
 
87
      resource_store_->OnCaptureTaskComplete();
 
88
    }
 
89
    bHandled = TRUE;
 
90
    return 0;
 
91
  }
 
92
 
 
93
  LRESULT OnCaptureUrlComplete(UINT uMsg,
 
94
                               WPARAM wParam,
 
95
                               LPARAM lParam,
 
96
                               BOOL& bHandled) {
 
97
    CaptureTask* task = reinterpret_cast<CaptureTask*>(lParam);
 
98
    if (task && (task == resource_store_->capture_task_.get())) {
 
99
      int index = wParam;
 
100
      bool success = (uMsg == WM_CAPTURE_URL_SUCCEEDED);
 
101
      resource_store_->OnCaptureUrlComplete(index, success);
 
102
    }
 
103
    bHandled = TRUE;
 
104
    return 0;
 
105
  }
 
106
 
 
107
  void OnFinalMessage(HWND hwnd) {
 
108
    delete this;
 
109
  }
 
110
 
 
111
 private:
 
112
  GearsResourceStore *resource_store_;
 
113
  DISALLOW_EVIL_CONSTRUCTORS(GearsResourceStoreMessageHwnd);
 
114
};
 
115
#endif
 
116
 
 
117
 
 
118
//------------------------------------------------------------------------------
 
119
// GearsResourceStore
 
120
//------------------------------------------------------------------------------
 
121
DECLARE_DISPATCHER(GearsResourceStore);
 
122
 
 
123
// static
 
124
template<>
 
125
void Dispatcher<GearsResourceStore>::Init() {
 
126
  RegisterProperty("name", &GearsResourceStore::GetName, NULL);
 
127
  RegisterProperty("requiredCookie", &GearsResourceStore::GetRequiredCookie,
 
128
                   NULL);
 
129
  RegisterProperty("enabled", &GearsResourceStore::GetEnabled,
 
130
                   &GearsResourceStore::SetEnabled);
 
131
 
 
132
  RegisterMethod("capture", &GearsResourceStore::Capture);
 
133
  RegisterMethod("abortCapture", &GearsResourceStore::AbortCapture);
 
134
  RegisterMethod("isCaptured", &GearsResourceStore::IsCaptured);
 
135
  RegisterMethod("remove", &GearsResourceStore::Remove);
 
136
  RegisterMethod("rename", &GearsResourceStore::Rename);
 
137
  RegisterMethod("copy", &GearsResourceStore::Copy);
 
138
  RegisterMethod("getHeader", &GearsResourceStore::GetHeader);
 
139
  RegisterMethod("getAllHeaders", &GearsResourceStore::GetAllHeaders);
 
140
  RegisterMethod("getAsBlob", &GearsResourceStore::GetAsBlob);
 
141
  RegisterMethod("captureBlob", &GearsResourceStore::CaptureBlob);
 
142
  RegisterMethod("captureFile", &GearsResourceStore::CaptureFile);
 
143
  RegisterMethod("getCapturedFileName",
 
144
                 &GearsResourceStore::GetCapturedFileName);
 
145
  RegisterMethod("createFileSubmitter",
 
146
                 &GearsResourceStore::CreateFileSubmitter);
 
147
}
 
148
 
 
149
const std::string GearsResourceStore::kModuleName("GearsResourceStore");
 
150
 
 
151
GearsResourceStore::GearsResourceStore()
 
152
    : ModuleImplBaseClass(kModuleName),
 
153
#if BROWSER_IE || BROWSER_IEMOBILE
 
154
      message_hwnd_(new GearsResourceStoreMessageHwnd(this)),
 
155
#endif
 
156
      next_capture_id_(0), page_is_unloaded_(false) {}
 
157
 
 
158
GearsResourceStore::~GearsResourceStore() {
 
159
  AbortAllRequests();
 
160
#if BROWSER_IE || BROWSER_IEMOBILE
 
161
  if (message_hwnd_->IsWindow()) {
 
162
    message_hwnd_->DestroyWindow();
 
163
  } else {
 
164
    delete message_hwnd_;
 
165
  }
 
166
#endif
 
167
}
 
168
 
 
169
//------------------------------------------------------------------------------
 
170
// GetName
 
171
//------------------------------------------------------------------------------
 
172
void GearsResourceStore::GetName(JsCallContext *context) {
 
173
  std::string16 name(store_.GetName());
 
174
  context->SetReturnValue(JSPARAM_STRING16, &name);
 
175
}
 
176
 
 
177
//------------------------------------------------------------------------------
 
178
// GetRequiredCookie
 
179
//------------------------------------------------------------------------------
 
180
void GearsResourceStore::GetRequiredCookie(JsCallContext *context) {
 
181
  std::string16 cookie(store_.GetRequiredCookie());
 
182
  context->SetReturnValue(JSPARAM_STRING16, &cookie);
 
183
}
 
184
 
 
185
//------------------------------------------------------------------------------
 
186
// GetEnabled
 
187
//------------------------------------------------------------------------------
 
188
void GearsResourceStore::GetEnabled(JsCallContext *context) {
 
189
  bool enabled = store_.IsEnabled();
 
190
  context->SetReturnValue(JSPARAM_BOOL, &enabled);
 
191
}
 
192
 
 
193
//------------------------------------------------------------------------------
 
194
// SetEnabled
 
195
//------------------------------------------------------------------------------
 
196
void GearsResourceStore::SetEnabled(JsCallContext *context) {
 
197
  bool enabled;
 
198
  JsArgument argv[] = {
 
199
    { JSPARAM_REQUIRED, JSPARAM_BOOL, &enabled },
 
200
  };
 
201
  context->GetArguments(ARRAYSIZE(argv), argv);
 
202
  if (context->is_exception_set())
 
203
    return;
 
204
 
 
205
  if (!store_.SetEnabled(enabled)) {
 
206
    context->SetException(STRING16(L"Failed to set the enabled property."));
 
207
    return;
 
208
  }
 
209
}
 
210
 
 
211
//------------------------------------------------------------------------------
 
212
// Capture
 
213
//------------------------------------------------------------------------------
 
214
void GearsResourceStore::Capture(JsCallContext *context) {
 
215
  std::string16 url;
 
216
  scoped_ptr<JsArray> url_array;
 
217
  JsRootedCallback *callback = NULL;
 
218
 
 
219
  JsArgument argv[] = {
 
220
    { JSPARAM_REQUIRED, JSPARAM_UNKNOWN, NULL },
 
221
    { JSPARAM_OPTIONAL, JSPARAM_FUNCTION, &callback },
 
222
  };
 
223
 
 
224
  // TODO(aa): Consider coercing anything except array to string. This would
 
225
  // make it consistent with XMLHttpRequest and window.setTimeout.
 
226
  int url_arg_type = context->GetArgumentType(0);
 
227
  if (url_arg_type == JSPARAM_ARRAY) {
 
228
    argv[0].type = JSPARAM_ARRAY;
 
229
    argv[0].value_ptr = as_out_parameter(url_array);
 
230
  } else if (url_arg_type == JSPARAM_STRING16) {
 
231
    argv[0].type = JSPARAM_STRING16;
 
232
    argv[0].value_ptr = &url;
 
233
  } else {
 
234
    context->SetException(
 
235
      STRING16(L"First parameter must be an array or string."));
 
236
    return;
 
237
  }
 
238
 
 
239
  context->GetArguments(ARRAYSIZE(argv), argv);
 
240
  scoped_ptr<JsRootedCallback> scoped_callback(callback);
 
241
  if (context->is_exception_set())
 
242
    return;
 
243
 
 
244
  int capture_id = ++next_capture_id_;
 
245
  LOG(("ResourceStore::capture - id = %d\n", capture_id));
 
246
 
 
247
  scoped_ptr<CaptureRequest> request(new CaptureRequest);
 
248
  request->id = capture_id;
 
249
  request->callback.swap(scoped_callback);  // transfer ownership
 
250
 
 
251
  if (url_arg_type == JSPARAM_ARRAY) {
 
252
    // 'urls' was an array of strings
 
253
    int array_length;
 
254
    if (!url_array->GetLength(&array_length)) {
 
255
      context->SetException(GET_INTERNAL_ERROR_MESSAGE());
 
256
      return;
 
257
    }
 
258
 
 
259
    for (int i = 0; i < array_length; ++i) {
 
260
      if (!url_array->GetElementAsString(i, &url)) {
 
261
        context->SetException(STRING16(L"Invalid parameter."));
 
262
        return;
 
263
      }
 
264
 
 
265
      if (!ResolveAndAppendUrl(url.c_str(), request.get())) {
 
266
        context->SetException(exception_message_.c_str());
 
267
        return;
 
268
      }
 
269
    }
 
270
  } else {
 
271
    // 'urls' was a string
 
272
    if (!ResolveAndAppendUrl(url.c_str(), request.get())) {
 
273
      context->SetException(exception_message_.c_str());
 
274
      return;
 
275
    }
 
276
  }
 
277
 
 
278
  pending_requests_.push_back(request.release());
 
279
 
 
280
  if (!StartCaptureTaskIfNeeded(false)) {
 
281
    context->SetException(exception_message_);
 
282
    return;
 
283
  }
 
284
 
 
285
  context->SetReturnValue(JSPARAM_INT, &capture_id);
 
286
}
 
287
 
 
288
//------------------------------------------------------------------------------
 
289
// AbortCapture
 
290
//------------------------------------------------------------------------------
 
291
void GearsResourceStore::AbortCapture(JsCallContext *context) {
 
292
  int capture_id;
 
293
  JsArgument argv[] = {
 
294
    { JSPARAM_REQUIRED, JSPARAM_INT, &capture_id },
 
295
  };
 
296
  context->GetArguments(ARRAYSIZE(argv), argv);
 
297
  if (context->is_exception_set())
 
298
    return;
 
299
 
 
300
  if (current_request_.get() && (current_request_->id == capture_id)) {
 
301
    // The caller is aborting the task that we're running
 
302
    assert(capture_task_.get());
 
303
    if (capture_task_.get()) {
 
304
      capture_task_->Abort();
 
305
    }
 
306
    return;
 
307
  }
 
308
 
 
309
  // Search for capture_id in our pending queue
 
310
  std::deque<CaptureRequest*>::iterator iter;
 
311
  for (iter = pending_requests_.begin();
 
312
       iter < pending_requests_.end();
 
313
       iter++) {
 
314
    if ((*iter)->id == capture_id) {
 
315
      // Remove it from the queue and fire completion events
 
316
      CaptureRequest *request = (*iter);
 
317
      pending_requests_.erase(iter);
 
318
      FireFailedEvents(request);
 
319
      delete request;
 
320
      return;
 
321
      // Note: the deque.erase() call is safe here since we return and
 
322
      // do not continue the iteration
 
323
    }
 
324
  }
 
325
}
 
326
 
 
327
//------------------------------------------------------------------------------
 
328
// IsCaptured
 
329
//------------------------------------------------------------------------------
 
330
void GearsResourceStore::IsCaptured(JsCallContext *context) {
 
331
  std::string16 url;
 
332
  JsArgument argv[] = {
 
333
    { JSPARAM_REQUIRED, JSPARAM_STRING16, &url },
 
334
  };
 
335
  context->GetArguments(ARRAYSIZE(argv), argv);
 
336
  if (context->is_exception_set())
 
337
    return;
 
338
 
 
339
  std::string16 full_url;
 
340
  if (!ResolveUrl(url, &full_url)) {
 
341
    context->SetException(exception_message_.c_str());
 
342
    return;
 
343
  }
 
344
  bool is_captured = store_.IsCaptured(full_url.c_str());
 
345
  context->SetReturnValue(JSPARAM_BOOL, &is_captured);
 
346
}
 
347
 
 
348
//------------------------------------------------------------------------------
 
349
// Remove
 
350
//------------------------------------------------------------------------------
 
351
void GearsResourceStore::Remove(JsCallContext *context) {
 
352
  std::string16 url;
 
353
  JsArgument argv[] = {
 
354
    { JSPARAM_REQUIRED, JSPARAM_STRING16, &url },
 
355
  };
 
356
  context->GetArguments(ARRAYSIZE(argv), argv);
 
357
  if (context->is_exception_set())
 
358
    return;
 
359
 
 
360
  std::string16 full_url;
 
361
  if (!ResolveUrl(url, &full_url)) {
 
362
    context->SetException(exception_message_.c_str());
 
363
    return;
 
364
  }
 
365
 
 
366
  if (!store_.Delete(full_url.c_str())) {
 
367
    context->SetException(STRING16(L"Failure removing url."));
 
368
    return;
 
369
  }
 
370
}
 
371
 
 
372
//------------------------------------------------------------------------------
 
373
// Rename
 
374
//------------------------------------------------------------------------------
 
375
void GearsResourceStore::Rename(JsCallContext *context) {
 
376
  std::string16 src_url;
 
377
  std::string16 dest_url;
 
378
  JsArgument argv[] = {
 
379
    { JSPARAM_REQUIRED, JSPARAM_STRING16, &src_url },
 
380
    { JSPARAM_REQUIRED, JSPARAM_STRING16, &dest_url },
 
381
  };
 
382
  context->GetArguments(ARRAYSIZE(argv), argv);
 
383
  if (context->is_exception_set())
 
384
    return;
 
385
 
 
386
  std::string16 full_src_url;
 
387
  if (!ResolveUrl(src_url, &full_src_url)) {
 
388
    context->SetException(exception_message_.c_str());
 
389
    return;
 
390
  }
 
391
 
 
392
  std::string16 full_dest_url;
 
393
  if (!ResolveUrl(dest_url, &full_dest_url)) {
 
394
    context->SetException(exception_message_.c_str());
 
395
    return;
 
396
  }
 
397
 
 
398
  if (!store_.Rename(full_src_url.c_str(), full_dest_url.c_str())) {
 
399
    context->SetException(STRING16(L"Failure renaming url."));
 
400
    return;
 
401
  }
 
402
}
 
403
 
 
404
//------------------------------------------------------------------------------
 
405
// Copy
 
406
//------------------------------------------------------------------------------
 
407
void GearsResourceStore::Copy(JsCallContext *context) {
 
408
  std::string16 src_url;
 
409
  std::string16 dest_url;
 
410
  JsArgument argv[] = {
 
411
    { JSPARAM_REQUIRED, JSPARAM_STRING16, &src_url },
 
412
    { JSPARAM_REQUIRED, JSPARAM_STRING16, &dest_url },
 
413
  };
 
414
  context->GetArguments(ARRAYSIZE(argv), argv);
 
415
  if (context->is_exception_set())
 
416
    return;
 
417
 
 
418
  std::string16 full_src_url;
 
419
  if (!ResolveUrl(src_url, &full_src_url)) {
 
420
    context->SetException(exception_message_.c_str());
 
421
    return;
 
422
  }
 
423
 
 
424
  std::string16 full_dest_url;
 
425
  if (!ResolveUrl(dest_url, &full_dest_url)) {
 
426
    context->SetException(exception_message_.c_str());
 
427
    return;
 
428
  }
 
429
 
 
430
  if (!store_.Copy(full_src_url.c_str(), full_dest_url.c_str())) {
 
431
    context->SetException(STRING16(L"Failure copying url."));
 
432
    return;
 
433
  }
 
434
}
 
435
 
 
436
//------------------------------------------------------------------------------
 
437
// GetAsBlob
 
438
//------------------------------------------------------------------------------
 
439
void GearsResourceStore::GetAsBlob(JsCallContext *context) {
 
440
  std::string16 url;
 
441
  JsArgument argv[] = {
 
442
    { JSPARAM_REQUIRED, JSPARAM_STRING16, &url }
 
443
  };
 
444
  context->GetArguments(ARRAYSIZE(argv), argv);
 
445
  if (context->is_exception_set()) {
 
446
    return;
 
447
  }
 
448
  std::string16 full_url;
 
449
  if (!ResolveUrl(url.c_str(), &full_url)) {
 
450
    context->SetException(exception_message_.c_str());
 
451
    return;
 
452
  }
 
453
 
 
454
  ResourceStore::Item item;
 
455
  scoped_refptr<BlobInterface> blob;
 
456
#ifdef USE_FILE_STORE
 
457
  // TODO(michaeln): This is not quite right, but its better than allocating
 
458
  // huge amounts of memory. Provided the store/item are not removed so long
 
459
  // as the returned blob is around, this is fine and good. Otherwise some
 
460
  // issues...
 
461
  // * By the time we are creating the file blob, the file may have been
 
462
  //   deleted if some other thread/process had removed this entry or store
 
463
  //   between the calls to GetItemInfo() and new FileBlob()
 
464
  // * Removing the entry while the GearsBlob instance is out there is
 
465
  //   problematic, the system will want to delete the underlying file and
 
466
  //   depending on the OS may or may not succeed at that. Subsequent use
 
467
  //   of the outstanding GearsBlob may or may not work depending.
 
468
  if (!store_.GetItemInfo(full_url.c_str(), &item)) {
 
469
    context->SetException(STRING16(L"Failed to get blob."));
 
470
    return;
 
471
  }
 
472
  if (!item.payload.cached_filepath.empty())
 
473
    blob.reset(new FileBlob(item.payload.cached_filepath));
 
474
  else
 
475
    blob.reset(new EmptyBlob());
 
476
#else
 
477
  if (!store_.GetItem(full_url.c_str(), &item)) {
 
478
    context->SetException(STRING16(L"Failed to get blob."));
 
479
    return;
 
480
  }
 
481
  assert(item.payload.data.get());
 
482
  blob.reset(new BufferBlob(item.payload.data.get());
 
483
#endif
 
484
 
 
485
  scoped_refptr<GearsBlob> blob_object;
 
486
  if (!CreateModule<GearsBlob>(module_environment_.get(),
 
487
                               context, &blob_object)) {
 
488
    context->SetException(GET_INTERNAL_ERROR_MESSAGE());
 
489
    return;
 
490
  }
 
491
  blob_object->Reset(blob.get());
 
492
  context->SetReturnValue(JSPARAM_MODULE, blob_object.get());
 
493
}
 
494
 
 
495
//------------------------------------------------------------------------------
 
496
// CaptureBlob
 
497
// TODO(michaeln): provide a means of setting the Content-Disposition header
 
498
// to indicate the blob should be treated as an attachment.
 
499
//------------------------------------------------------------------------------
 
500
void GearsResourceStore::CaptureBlob(JsCallContext *context) {
 
501
  ModuleImplBaseClass *other_module = NULL;
 
502
  std::string16 url, content_type;
 
503
  JsArgument argv[] = {
 
504
    { JSPARAM_REQUIRED, JSPARAM_MODULE, &other_module },
 
505
    { JSPARAM_REQUIRED, JSPARAM_STRING16, &url },
 
506
    { JSPARAM_OPTIONAL, JSPARAM_STRING16, &content_type }
 
507
  };
 
508
  context->GetArguments(ARRAYSIZE(argv), argv);
 
509
  if (context->is_exception_set()) {
 
510
    return;
 
511
  }
 
512
  std::string16 full_url;
 
513
  if (!ResolveUrl(url.c_str(), &full_url)) {
 
514
    context->SetException(exception_message_.c_str());
 
515
    return;
 
516
  }
 
517
  if (!content_type.empty() && !IsValidHttpHeaderValue(content_type)) {
 
518
    context->SetException(STRING16(L"Invalid content type."));
 
519
    return;
 
520
  }
 
521
 
 
522
  if (GearsBlob::kModuleName != other_module->get_module_name()) {
 
523
    context->SetException(STRING16(L"First argument must be a Blob."));
 
524
    return;
 
525
  }
 
526
  scoped_refptr<BlobInterface> blob;
 
527
  static_cast<GearsBlob*>(other_module)->GetContents(&blob);
 
528
  assert(blob.get());
 
529
 
 
530
  ResourceStore::Item item;
 
531
  if (!ResourceStore::BlobToItem(blob.get(), full_url.c_str(),
 
532
                                 content_type.c_str(),
 
533
                                 NULL, &item) ||
 
534
      !store_.PutItem(&item)) {
 
535
    context->SetException(STRING16(L"The blob could not be captured."));
 
536
    return;
 
537
  }
 
538
}
 
539
 
 
540
//------------------------------------------------------------------------------
 
541
// CaptureFile
 
542
//------------------------------------------------------------------------------
 
543
void GearsResourceStore::CaptureFile(JsCallContext *context) {
 
544
#if BROWSER_NPAPI
 
545
  // TODO(nigeltao): implement on NPAPI. To do this, I need to figure out how
 
546
  // a FileInputElement is represented.
 
547
  context->SetException(STRING16(L"captureFile is not implemented."));
 
548
#else
 
549
  if (EnvIsWorker()) {
 
550
    context->SetException(
 
551
        STRING16(L"captureFile is not supported in workers."));
 
552
    return;
 
553
  }
 
554
 
 
555
  JsDomElement dom_element;
 
556
  std::string16 url;
 
557
  JsArgument argv[] = {
 
558
    { JSPARAM_REQUIRED, JSPARAM_DOM_ELEMENT, &dom_element },
 
559
    { JSPARAM_REQUIRED, JSPARAM_STRING16, &url }
 
560
  };
 
561
  context->GetArguments(ARRAYSIZE(argv), argv);
 
562
  if (context->is_exception_set()) {
 
563
    return;
 
564
  }
 
565
  std::string16 full_url;
 
566
  if (!ResolveUrl(url.c_str(), &full_url)) {
 
567
    context->SetException(STRING16(L"Failed to resolve url."));
 
568
    return;
 
569
  }
 
570
 
 
571
  std::string16 file_name;
 
572
  if (!dom_element.GetFileInputElementValue(&file_name)) {
 
573
    context->SetException(
 
574
        STRING16(L"Failed to get the file name from the file input element."));
 
575
    return;
 
576
  }
 
577
  if (file_name.empty()) {
 
578
    context->SetException(STRING16(L"File path is empty."));
 
579
    return;
 
580
  }
 
581
  if (!File::Exists(file_name.c_str())) {
 
582
    context->SetException(STRING16(L"File does not exist."));
 
583
    return;
 
584
  }
 
585
  std::string16 file_base_name;
 
586
  if (!File::GetBaseName(file_name, &file_base_name)) {
 
587
    context->SetException(STRING16(L"Could not extract the file's base name."));
 
588
    return;
 
589
  }
 
590
  std::string16 mime_type = DetectMimeTypeOfFile(file_name);
 
591
  scoped_refptr<BlobInterface> blob(new FileBlob(file_name));
 
592
 
 
593
  ResourceStore::Item item;
 
594
  if (!ResourceStore::BlobToItem(blob.get(), full_url.c_str(),
 
595
                                 mime_type.c_str(), file_base_name.c_str(),
 
596
                                 &item) ||
 
597
      !store_.PutItem(&item)) {
 
598
    context->SetException(STRING16(L"The file could not be captured."));
 
599
    return;
 
600
  }
 
601
#endif
 
602
}
 
603
 
 
604
//------------------------------------------------------------------------------
 
605
// GetCapturedFileName
 
606
//------------------------------------------------------------------------------
 
607
void GearsResourceStore::GetCapturedFileName(JsCallContext *context) {
 
608
  std::string16 url;
 
609
  JsArgument argv[] = {
 
610
    { JSPARAM_REQUIRED, JSPARAM_STRING16, &url },
 
611
  };
 
612
  context->GetArguments(ARRAYSIZE(argv), argv);
 
613
  if (context->is_exception_set())
 
614
    return;
 
615
 
 
616
  std::string16 full_url;
 
617
  if (!ResolveUrl(url, &full_url)) {
 
618
    context->SetException(exception_message_.c_str());
 
619
    return;
 
620
  }
 
621
 
 
622
  std::string16 file_name;
 
623
  if (!store_.GetCapturedFileName(full_url.c_str(), &file_name)) {
 
624
    context->SetException(STRING16(L"GetCapturedFileName failed."));
 
625
    return;
 
626
  }
 
627
 
 
628
  context->SetReturnValue(JSPARAM_STRING16, &file_name);
 
629
}
 
630
 
 
631
//------------------------------------------------------------------------------
 
632
// GetHeader
 
633
//------------------------------------------------------------------------------
 
634
void GearsResourceStore::GetHeader(JsCallContext *context) {
 
635
  std::string16 url;
 
636
  std::string16 name;
 
637
  JsArgument argv[] = {
 
638
    { JSPARAM_REQUIRED, JSPARAM_STRING16, &url },
 
639
    { JSPARAM_REQUIRED, JSPARAM_STRING16, &name },
 
640
  };
 
641
  context->GetArguments(ARRAYSIZE(argv), argv);
 
642
  if (context->is_exception_set())
 
643
    return;
 
644
 
 
645
  std::string16 full_url;
 
646
  if (!ResolveUrl(url, &full_url)) {
 
647
    context->SetException(exception_message_.c_str());
 
648
    return;
 
649
  }
 
650
 
 
651
  std::string16 value;
 
652
  store_.GetHeader(full_url.c_str(), name.c_str(), &value);
 
653
  context->SetReturnValue(JSPARAM_STRING16, &value);
 
654
}
 
655
 
 
656
//------------------------------------------------------------------------------
 
657
// GetAllHeaders
 
658
//------------------------------------------------------------------------------
 
659
void GearsResourceStore::GetAllHeaders(JsCallContext *context) {
 
660
  std::string16 url;
 
661
  JsArgument argv[] = {
 
662
    { JSPARAM_REQUIRED, JSPARAM_STRING16, &url },
 
663
  };
 
664
  context->GetArguments(ARRAYSIZE(argv), argv);
 
665
  if (context->is_exception_set())
 
666
    return;
 
667
 
 
668
  std::string16 full_url;
 
669
  if (!ResolveUrl(url, &full_url)) {
 
670
    context->SetException(exception_message_.c_str());
 
671
    return;
 
672
  }
 
673
 
 
674
  std::string16 all_headers;
 
675
  if (!store_.GetAllHeaders(full_url.c_str(), &all_headers)) {
 
676
    context->SetException(STRING16(L"GetAllHeaders failed."));
 
677
    return;
 
678
  }
 
679
 
 
680
  context->SetReturnValue(JSPARAM_STRING16, &all_headers);
 
681
}
 
682
 
 
683
//------------------------------------------------------------------------------
 
684
// CreateFileSubmitter
 
685
//------------------------------------------------------------------------------
 
686
void GearsResourceStore::CreateFileSubmitter(JsCallContext *context) {
 
687
#ifdef OS_WINCE
 
688
  context->SetException(STRING16(L"createFileSubmitter is not implemented."));
 
689
  return;
 
690
#elif BROWSER_NPAPI
 
691
  // TODO(nigeltao): implement on NPAPI.
 
692
  context->SetException(STRING16(L"createFileSubmitter is not implemented."));
 
693
  return;
 
694
#else
 
695
  if (EnvIsWorker()) {
 
696
    context->SetException(
 
697
        STRING16(L"createFileSubmitter cannot be called in a worker."));
 
698
    return;
 
699
  }
 
700
 
 
701
  scoped_refptr<GearsFileSubmitter> submitter;
 
702
  if (!CreateModule<GearsFileSubmitter>(module_environment_.get(),
 
703
                                        context, &submitter)) {
 
704
    return;
 
705
  }
 
706
  if (!submitter->store_.Clone(&store_)) {
 
707
    context->SetException(STRING16(L"Error initializing base class."));
 
708
    return;
 
709
  }
 
710
 
 
711
  context->SetReturnValue(JSPARAM_MODULE, submitter.get());
 
712
#endif
 
713
}
 
714
 
 
715
// End Javascript API
 
716
 
 
717
//------------------------------------------------------------------------------
 
718
// HandleEvent
 
719
//------------------------------------------------------------------------------
 
720
void GearsResourceStore::HandleEvent(JsEventType event_type) {
 
721
  assert(event_type == JSEVENT_UNLOAD);
 
722
 
 
723
  page_is_unloaded_ = true;
 
724
  AbortAllRequests();
 
725
}
 
726
 
 
727
 
 
728
//------------------------------------------------------------------------------
 
729
// AbortAllRequests
 
730
//------------------------------------------------------------------------------
 
731
void GearsResourceStore::AbortAllRequests() {
 
732
  if (capture_task_.get()) {
 
733
#if BROWSER_IE || BROWSER_IEMOBILE
 
734
    capture_task_->SetListenerWindow(NULL, 0);
 
735
#else
 
736
    capture_task_->SetListener(NULL);
 
737
#endif
 
738
    // No need to fire failed events since the current page is being unloaded
 
739
    // or the resource store deleted for some other reason.
 
740
    need_to_fire_failed_events_ = false;
 
741
    capture_task_->Abort();
 
742
    capture_task_.release()->DeleteWhenDone();
 
743
  }
 
744
 
 
745
  if (current_request_.get()) {
 
746
    current_request_->callback.reset(NULL);
 
747
  }
 
748
 
 
749
  for (std::deque<CaptureRequest*>::iterator iter = pending_requests_.begin();
 
750
       iter != pending_requests_.end(); ++iter) {
 
751
    delete (*iter);
 
752
  }
 
753
  pending_requests_.clear();
 
754
}
 
755
 
 
756
//------------------------------------------------------------------------------
 
757
// StartCaptureTaskIfNeeded
 
758
//------------------------------------------------------------------------------
 
759
bool GearsResourceStore::StartCaptureTaskIfNeeded(bool fire_events_on_failure) {
 
760
  if (page_is_unloaded_) {
 
761
    // We silently fail for this particular error condition to prevent callers
 
762
    // from detecting errors and making noises after the page has been unloaded
 
763
    return true;
 
764
  }
 
765
 
 
766
  // Create an event monitor to alert us when the page unloads.
 
767
  if (unload_monitor_ == NULL) {
 
768
    unload_monitor_.reset(new JsEventMonitor(GetJsRunner(), JSEVENT_UNLOAD,
 
769
                                             this));
 
770
  }
 
771
 
 
772
  if (capture_task_.get()) {
 
773
    assert(current_request_.get());
 
774
    return true;
 
775
  }
 
776
 
 
777
  if (pending_requests_.empty()) {
 
778
    return true;
 
779
  }
 
780
 
 
781
  assert(!current_request_.get());
 
782
  current_request_.reset(pending_requests_.front());
 
783
  pending_requests_.pop_front();
 
784
 
 
785
  capture_task_.reset(new CaptureTask(EnvPageBrowsingContext()));
 
786
  if (!capture_task_->Init(&store_, current_request_.get())) {
 
787
    scoped_ptr<CaptureRequest> failed_request(current_request_.release());
 
788
    capture_task_.reset(NULL);
 
789
    if (fire_events_on_failure) {
 
790
      FireFailedEvents(failed_request.get());
 
791
    }
 
792
    exception_message_ = STRING16(L"Failed to initialize capture task.");
 
793
    return false;
 
794
  }
 
795
 
 
796
#if BROWSER_IE || BROWSER_IEMOBILE
 
797
  message_hwnd_->Initialize();
 
798
  capture_task_->SetListenerWindow(
 
799
      message_hwnd_->m_hWnd,
 
800
      GearsResourceStoreMessageHwnd::kCaptureTaskMessageBase);
 
801
#else
 
802
  capture_task_->SetListener(this);
 
803
#endif
 
804
  need_to_fire_failed_events_ = true;
 
805
  if (!capture_task_->Start()) {
 
806
    scoped_ptr<CaptureRequest> failed_request(current_request_.release());
 
807
    capture_task_.reset(NULL);
 
808
    if (fire_events_on_failure) {
 
809
      FireFailedEvents(failed_request.get());
 
810
    }
 
811
    exception_message_ = STRING16(L"Failed to start capture task.");
 
812
    return false;
 
813
  }
 
814
 
 
815
  return true;
 
816
}
 
817
 
 
818
//------------------------------------------------------------------------------
 
819
// HandleAsyncTaskEvent
 
820
//------------------------------------------------------------------------------
 
821
#if BROWSER_IE || BROWSER_IEMOBILE
 
822
  // On IE, AsyncTask uses a GearsResourceStoreMessageHwnd instead.
 
823
#else
 
824
void GearsResourceStore::HandleAsyncTaskEvent(int code, int param,
 
825
                                              AsyncTask *source) {
 
826
  if (source && (source == capture_task_.get())) {
 
827
    if (code == CaptureTask::CAPTURE_TASK_COMPLETE) {
 
828
      OnCaptureTaskComplete();
 
829
    } else {
 
830
      // param = the index of the url that has been processed
 
831
      bool success = (code == CaptureTask::CAPTURE_URL_SUCCEEDED);
 
832
      OnCaptureUrlComplete(param, success);
 
833
    }
 
834
  }
 
835
}
 
836
#endif
 
837
 
 
838
//------------------------------------------------------------------------------
 
839
// OnCaptureUrlComplete
 
840
//------------------------------------------------------------------------------
 
841
void GearsResourceStore::OnCaptureUrlComplete(int index, bool success) {
 
842
  if (current_request_.get()) {
 
843
    need_to_fire_failed_events_ = false;
 
844
    InvokeCompletionCallback(current_request_.get(),
 
845
                             current_request_->urls[index],
 
846
                             current_request_->id,
 
847
                             success);
 
848
  }
 
849
}
 
850
 
 
851
//------------------------------------------------------------------------------
 
852
// OnCaptureTaskComplete
 
853
//------------------------------------------------------------------------------
 
854
void GearsResourceStore::OnCaptureTaskComplete() {
 
855
#if BROWSER_IE || BROWSER_IEMOBILE
 
856
  capture_task_->SetListenerWindow(NULL, 0);
 
857
#else
 
858
  capture_task_->SetListener(NULL);
 
859
#endif
 
860
  capture_task_.release()->DeleteWhenDone();
 
861
  if (need_to_fire_failed_events_) {
 
862
    assert(current_request_.get());
 
863
    FireFailedEvents(current_request_.get());
 
864
  }
 
865
  current_request_.reset(NULL);
 
866
  StartCaptureTaskIfNeeded(true);
 
867
}
 
868
 
 
869
//------------------------------------------------------------------------------
 
870
// FireFailedEvents
 
871
//------------------------------------------------------------------------------
 
872
void GearsResourceStore::FireFailedEvents(CaptureRequest *request) {
 
873
  assert(request);
 
874
  for (size_t i = 0; i < request->urls.size(); ++i) {
 
875
    InvokeCompletionCallback(request,
 
876
                             request->urls[i],
 
877
                             request->id,
 
878
                             false);
 
879
  }
 
880
}
 
881
 
 
882
//------------------------------------------------------------------------------
 
883
// InvokeCompletionCallback
 
884
//------------------------------------------------------------------------------
 
885
void GearsResourceStore::InvokeCompletionCallback(
 
886
                             CaptureRequest *request,
 
887
                             const std::string16 &capture_url,
 
888
                             int capture_id,
 
889
                             bool succeeded) {
 
890
  // If completion callback was not set, return immediately
 
891
  if (!request->callback.get()) { return; }
 
892
 
 
893
  const int argc = 3;
 
894
  JsParamToSend argv[argc] = {
 
895
    { JSPARAM_STRING16, &capture_url },
 
896
    { JSPARAM_BOOL, &succeeded },
 
897
    { JSPARAM_INT, &capture_id }
 
898
  };
 
899
  GetJsRunner()->InvokeCallback(
 
900
      request->callback.get(), NULL, argc, argv, NULL);
 
901
}
 
902
 
 
903
//------------------------------------------------------------------------------
 
904
// ResolveAndAppendUrl
 
905
//------------------------------------------------------------------------------
 
906
bool GearsResourceStore::ResolveAndAppendUrl(const std::string16 &url,
 
907
                                             CaptureRequest *request) {
 
908
  std::string16 full_url;
 
909
  if (!ResolveUrl(url.c_str(), &full_url)) {
 
910
    return false;
 
911
  }
 
912
  request->urls.push_back(url);
 
913
  request->full_urls.push_back(full_url);
 
914
  return true;
 
915
}
 
916
 
 
917
//------------------------------------------------------------------------------
 
918
// This helper does several things:
 
919
// - resolve relative urls based on the page location, the 'url' may also
 
920
//   be an absolute url to start with, if so this step does not modify it
 
921
// - normalizes the resulting absolute url, ie. removes path navigation
 
922
// - removes the fragment part of the url, ie. truncates at the '#' character
 
923
// - ensures the the resulting url is from the same-origin
 
924
//------------------------------------------------------------------------------
 
925
bool GearsResourceStore::ResolveUrl(const std::string16 &url,
 
926
                                    std::string16 *resolved_url) {
 
927
  if (!ResolveAndNormalize(EnvPageLocationUrl().c_str(), url.c_str(),
 
928
                           resolved_url)) {
 
929
    exception_message_ = STRING16(L"Failed to resolve url.");
 
930
    return false;
 
931
  }
 
932
  if (!EnvPageSecurityOrigin().IsSameOriginAsUrl(resolved_url->c_str())) {
 
933
    exception_message_ = STRING16(L"Url is not from the same origin");
 
934
    return false;
 
935
  }
 
936
  return true;
 
937
}