1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/service_worker/service_worker_context_request_handler.h"
6
7#include "base/time/time.h"
8#include "content/browser/service_worker/service_worker_context_core.h"
9#include "content/browser/service_worker/service_worker_provider_host.h"
10#include "content/browser/service_worker/service_worker_read_from_cache_job.h"
11#include "content/browser/service_worker/service_worker_storage.h"
12#include "content/browser/service_worker/service_worker_version.h"
13#include "content/browser/service_worker/service_worker_write_to_cache_job.h"
14#include "net/base/load_flags.h"
15#include "net/url_request/url_request.h"
16
17namespace content {
18
19ServiceWorkerContextRequestHandler::ServiceWorkerContextRequestHandler(
20    base::WeakPtr<ServiceWorkerContextCore> context,
21    base::WeakPtr<ServiceWorkerProviderHost> provider_host,
22    base::WeakPtr<storage::BlobStorageContext> blob_storage_context,
23    ResourceType resource_type)
24    : ServiceWorkerRequestHandler(context,
25                                  provider_host,
26                                  blob_storage_context,
27                                  resource_type),
28      version_(provider_host_->running_hosted_version()) {
29  DCHECK(provider_host_->IsHostToRunningServiceWorker());
30}
31
32ServiceWorkerContextRequestHandler::~ServiceWorkerContextRequestHandler() {
33}
34
35net::URLRequestJob* ServiceWorkerContextRequestHandler::MaybeCreateJob(
36    net::URLRequest* request,
37    net::NetworkDelegate* network_delegate) {
38  if (!provider_host_ || !version_.get() || !context_)
39    return NULL;
40
41  // We currently have no use case for hijacking a redirected request.
42  if (request->url_chain().size() > 1)
43    return NULL;
44
45  // We only use the script cache for main script loading and
46  // importScripts(), even if a cached script is xhr'd, we don't
47  // retrieve it from the script cache.
48  // TODO(michaeln): Get the desired behavior clarified in the spec,
49  // and make tweak the behavior here to match.
50  if (resource_type_ != RESOURCE_TYPE_SERVICE_WORKER &&
51      resource_type_ != RESOURCE_TYPE_SCRIPT) {
52    return NULL;
53  }
54
55  if (ShouldAddToScriptCache(request->url())) {
56    ServiceWorkerRegistration* registration =
57        context_->GetLiveRegistration(version_->registration_id());
58    DCHECK(registration);  // We're registering or updating so must be there.
59
60    int64 response_id = context_->storage()->NewResourceId();
61    if (response_id == kInvalidServiceWorkerResponseId)
62      return NULL;
63
64    // Bypass the browser cache for initial installs and update
65    // checks after 24 hours have passed.
66    int extra_load_flags = 0;
67    base::TimeDelta time_since_last_check =
68        base::Time::Now() - registration->last_update_check();
69    if (time_since_last_check > base::TimeDelta::FromHours(24))
70      extra_load_flags = net::LOAD_BYPASS_CACHE;
71
72    return new ServiceWorkerWriteToCacheJob(request,
73                                            network_delegate,
74                                            resource_type_,
75                                            context_,
76                                            version_.get(),
77                                            extra_load_flags,
78                                            response_id);
79  }
80
81  int64 response_id = kInvalidServiceWorkerResponseId;
82  if (ShouldReadFromScriptCache(request->url(), &response_id)) {
83    return new ServiceWorkerReadFromCacheJob(
84        request, network_delegate, context_, response_id);
85  }
86
87  // NULL means use the network.
88  return NULL;
89}
90
91void ServiceWorkerContextRequestHandler::GetExtraResponseInfo(
92    bool* was_fetched_via_service_worker,
93    GURL* original_url_via_service_worker,
94    base::TimeTicks* fetch_start_time,
95    base::TimeTicks* fetch_ready_time,
96    base::TimeTicks* fetch_end_time) const {
97  *was_fetched_via_service_worker = false;
98  *original_url_via_service_worker = GURL();
99}
100
101bool ServiceWorkerContextRequestHandler::ShouldAddToScriptCache(
102    const GURL& url) {
103  // We only write imports that occur during the initial eval.
104  if (version_->status() != ServiceWorkerVersion::NEW &&
105      version_->status() != ServiceWorkerVersion::INSTALLING) {
106    return false;
107  }
108  return version_->script_cache_map()->Lookup(url) ==
109            kInvalidServiceWorkerResponseId;
110}
111
112bool ServiceWorkerContextRequestHandler::ShouldReadFromScriptCache(
113    const GURL& url, int64* response_id_out) {
114  // We don't read from the script cache until the version is INSTALLED.
115  if (version_->status() == ServiceWorkerVersion::NEW ||
116      version_->status() == ServiceWorkerVersion::INSTALLING)
117    return false;
118  *response_id_out = version_->script_cache_map()->Lookup(url);
119  return *response_id_out != kInvalidServiceWorkerResponseId;
120}
121
122}  // namespace content
123