OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/appcache/appcache_request_handler.h" | 5 #include "content/browser/appcache/appcache_request_handler.h" |
6 | 6 |
7 #include <utility> | 7 #include <utility> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "content/browser/appcache/appcache.h" | 10 #include "content/browser/appcache/appcache.h" |
11 #include "content/browser/appcache/appcache_backend_impl.h" | 11 #include "content/browser/appcache/appcache_backend_impl.h" |
12 #include "content/browser/appcache/appcache_policy.h" | 12 #include "content/browser/appcache/appcache_policy.h" |
13 #include "content/browser/appcache/appcache_request.h" | |
13 #include "content/browser/appcache/appcache_url_request_job.h" | 14 #include "content/browser/appcache/appcache_url_request_job.h" |
14 #include "content/browser/service_worker/service_worker_request_handler.h" | 15 #include "content/browser/service_worker/service_worker_request_handler.h" |
15 #include "net/url_request/url_request.h" | 16 #include "net/url_request/url_request.h" |
16 #include "net/url_request/url_request_job.h" | 17 #include "net/url_request/url_request_job.h" |
17 | 18 |
18 namespace content { | 19 namespace content { |
19 | 20 |
20 AppCacheRequestHandler::AppCacheRequestHandler(AppCacheHost* host, | 21 AppCacheRequestHandler::AppCacheRequestHandler(AppCacheHost* host, |
21 ResourceType resource_type, | 22 ResourceType resource_type, |
22 bool should_reset_appcache) | 23 bool should_reset_appcache, |
24 AppCacheRequest* request) | |
23 : host_(host), | 25 : host_(host), |
24 resource_type_(resource_type), | 26 resource_type_(resource_type), |
25 should_reset_appcache_(should_reset_appcache), | 27 should_reset_appcache_(should_reset_appcache), |
26 is_waiting_for_cache_selection_(false), | 28 is_waiting_for_cache_selection_(false), |
27 found_group_id_(0), | 29 found_group_id_(0), |
28 found_cache_id_(0), | 30 found_cache_id_(0), |
29 found_network_namespace_(false), | 31 found_network_namespace_(false), |
30 cache_entry_not_found_(false), | 32 cache_entry_not_found_(false), |
31 is_delivering_network_response_(false), | 33 is_delivering_network_response_(false), |
32 maybe_load_resource_executed_(false), | 34 maybe_load_resource_executed_(false), |
33 old_process_id_(0), | 35 old_process_id_(0), |
34 old_host_id_(kAppCacheNoHostId), | 36 old_host_id_(kAppCacheNoHostId), |
35 cache_id_(kAppCacheNoCacheId), | 37 cache_id_(kAppCacheNoCacheId), |
36 service_(host_->service()) { | 38 service_(host_->service()), |
39 request_(request) { | |
37 DCHECK(host_); | 40 DCHECK(host_); |
38 DCHECK(service_); | 41 DCHECK(service_); |
39 host_->AddObserver(this); | 42 host_->AddObserver(this); |
40 service_->AddObserver(this); | 43 service_->AddObserver(this); |
41 } | 44 } |
42 | 45 |
43 AppCacheRequestHandler::~AppCacheRequestHandler() { | 46 AppCacheRequestHandler::~AppCacheRequestHandler() { |
44 if (host_) { | 47 if (host_) { |
45 storage()->CancelDelegateCallbacks(this); | 48 storage()->CancelDelegateCallbacks(this); |
46 host_->RemoveObserver(this); | 49 host_->RemoveObserver(this); |
47 } | 50 } |
48 if (service_) | 51 if (service_) |
49 service_->RemoveObserver(this); | 52 service_->RemoveObserver(this); |
50 } | 53 } |
51 | 54 |
52 AppCacheStorage* AppCacheRequestHandler::storage() const { | 55 AppCacheStorage* AppCacheRequestHandler::storage() const { |
53 DCHECK(host_); | 56 DCHECK(host_); |
54 return host_->storage(); | 57 return host_->storage(); |
55 } | 58 } |
56 | 59 |
57 AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadResource( | 60 AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadResource( |
58 net::URLRequest* request, | |
59 net::NetworkDelegate* network_delegate) { | 61 net::NetworkDelegate* network_delegate) { |
60 maybe_load_resource_executed_ = true; | 62 maybe_load_resource_executed_ = true; |
61 if (!host_ || !IsSchemeAndMethodSupportedForAppCache(request) || | 63 if (!host_ || |
64 !AppCacheRequest::IsSchemeAndMethodSupportedForAppCache(request_.get()) || | |
62 cache_entry_not_found_) { | 65 cache_entry_not_found_) { |
63 return NULL; | 66 return NULL; |
64 } | 67 } |
65 | 68 |
66 // This method can get called multiple times over the life | 69 // This method can get called multiple times over the life |
67 // of a request. The case we detect here is having scheduled | 70 // of a request. The case we detect here is having scheduled |
68 // delivery of a "network response" using a job set up on an | 71 // delivery of a "network response" using a job set up on an |
69 // earlier call through this method. To send the request through | 72 // earlier call through this method. To send the request through |
70 // to the network involves restarting the request altogether, | 73 // to the network involves restarting the request altogether, |
71 // which will call through to our interception layer again. | 74 // which will call through to our interception layer again. |
72 // This time through, we return NULL so the request hits the wire. | 75 // This time through, we return NULL so the request hits the wire. |
73 if (is_delivering_network_response_) { | 76 if (is_delivering_network_response_) { |
74 is_delivering_network_response_ = false; | 77 is_delivering_network_response_ = false; |
75 return NULL; | 78 return NULL; |
76 } | 79 } |
77 | 80 |
78 // Clear out our 'found' fields since we're starting a request for a | 81 // Clear out our 'found' fields since we're starting a request for a |
79 // new resource, any values in those fields are no longer valid. | 82 // new resource, any values in those fields are no longer valid. |
80 found_entry_ = AppCacheEntry(); | 83 found_entry_ = AppCacheEntry(); |
81 found_fallback_entry_ = AppCacheEntry(); | 84 found_fallback_entry_ = AppCacheEntry(); |
82 found_cache_id_ = kAppCacheNoCacheId; | 85 found_cache_id_ = kAppCacheNoCacheId; |
83 found_manifest_url_ = GURL(); | 86 found_manifest_url_ = GURL(); |
84 found_network_namespace_ = false; | 87 found_network_namespace_ = false; |
85 | 88 |
86 std::unique_ptr<AppCacheURLRequestJob> job; | 89 std::unique_ptr<AppCacheURLRequestJob> job; |
87 if (is_main_resource()) | 90 if (is_main_resource()) |
88 job = MaybeLoadMainResource(request, network_delegate); | 91 job = MaybeLoadMainResource(network_delegate); |
89 else | 92 else |
90 job = MaybeLoadSubResource(request, network_delegate); | 93 job = MaybeLoadSubResource(network_delegate); |
91 | 94 |
92 // If its been setup to deliver a network response, we can just delete | 95 // If its been setup to deliver a network response, we can just delete |
93 // it now and return NULL instead to achieve that since it couldn't | 96 // it now and return NULL instead to achieve that since it couldn't |
94 // have been started yet. | 97 // have been started yet. |
95 if (job && job->is_delivering_network_response()) { | 98 if (job && job->is_delivering_network_response()) { |
96 DCHECK(!job->has_been_started()); | 99 DCHECK(!job->has_been_started()); |
97 job.reset(); | 100 job.reset(); |
98 } | 101 } |
99 | 102 |
100 return job.release(); | 103 return job.release(); |
101 } | 104 } |
102 | 105 |
103 AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadFallbackForRedirect( | 106 AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadFallbackForRedirect( |
104 net::URLRequest* request, | |
105 net::NetworkDelegate* network_delegate, | 107 net::NetworkDelegate* network_delegate, |
106 const GURL& location) { | 108 const GURL& location) { |
107 if (!host_ || !IsSchemeAndMethodSupportedForAppCache(request) || | 109 if (!host_ || |
110 !AppCacheRequest::IsSchemeAndMethodSupportedForAppCache(request_.get()) || | |
108 cache_entry_not_found_) | 111 cache_entry_not_found_) |
109 return NULL; | 112 return NULL; |
110 if (is_main_resource()) | 113 if (is_main_resource()) |
111 return NULL; | 114 return NULL; |
112 // TODO(vabr) This is a temporary fix (see crbug/141114). We should get rid of | 115 // TODO(vabr) This is a temporary fix (see crbug/141114). We should get rid of |
113 // it once a more general solution to crbug/121325 is in place. | 116 // it once a more general solution to crbug/121325 is in place. |
114 if (!maybe_load_resource_executed_) | 117 if (!maybe_load_resource_executed_) |
115 return NULL; | 118 return NULL; |
116 if (request->url().GetOrigin() == location.GetOrigin()) | 119 if (request_->GetURL().GetOrigin() == location.GetOrigin()) |
117 return NULL; | 120 return NULL; |
118 | 121 |
119 DCHECK(!job_.get()); // our jobs never generate redirects | 122 DCHECK(!job_.get()); // our jobs never generate redirects |
120 | 123 |
121 std::unique_ptr<AppCacheURLRequestJob> job; | 124 std::unique_ptr<AppCacheURLRequestJob> job; |
122 if (found_fallback_entry_.has_response_id()) { | 125 if (found_fallback_entry_.has_response_id()) { |
123 // 6.9.6, step 4: If this results in a redirect to another origin, | 126 // 6.9.6, step 4: If this results in a redirect to another origin, |
124 // get the resource of the fallback entry. | 127 // get the resource of the fallback entry. |
125 job = CreateJob(request, network_delegate); | 128 job = CreateJob(network_delegate); |
126 DeliverAppCachedResponse(found_fallback_entry_, found_cache_id_, | 129 DeliverAppCachedResponse(found_fallback_entry_, found_cache_id_, |
127 found_manifest_url_, true, | 130 found_manifest_url_, true, |
128 found_namespace_entry_url_); | 131 found_namespace_entry_url_); |
129 } else if (!found_network_namespace_) { | 132 } else if (!found_network_namespace_) { |
130 // 6.9.6, step 6: Fail the resource load. | 133 // 6.9.6, step 6: Fail the resource load. |
131 job = CreateJob(request, network_delegate); | 134 job = CreateJob(network_delegate); |
132 DeliverErrorResponse(); | 135 DeliverErrorResponse(); |
133 } else { | 136 } else { |
134 // 6.9.6 step 3 and 5: Fetch the resource normally. | 137 // 6.9.6 step 3 and 5: Fetch the resource normally. |
135 } | 138 } |
136 | 139 |
137 return job.release(); | 140 return job.release(); |
138 } | 141 } |
139 | 142 |
140 AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadFallbackForResponse( | 143 AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadFallbackForResponse( |
141 net::URLRequest* request, | |
142 net::NetworkDelegate* network_delegate) { | 144 net::NetworkDelegate* network_delegate) { |
143 if (!host_ || !IsSchemeAndMethodSupportedForAppCache(request) || | 145 if (!host_ || |
146 !AppCacheRequest::IsSchemeAndMethodSupportedForAppCache(request_.get()) || | |
144 cache_entry_not_found_) | 147 cache_entry_not_found_) |
145 return NULL; | 148 return NULL; |
146 if (!found_fallback_entry_.has_response_id()) | 149 if (!found_fallback_entry_.has_response_id()) |
147 return NULL; | 150 return NULL; |
148 | 151 |
149 if (request->status().status() == net::URLRequestStatus::CANCELED) { | 152 if (request_->IsCancelled()) { |
150 // 6.9.6, step 4: But not if the user canceled the download. | 153 // 6.9.6, step 4: But not if the user canceled the download. |
151 return NULL; | 154 return NULL; |
152 } | 155 } |
153 | 156 |
154 // We don't fallback for responses that we delivered. | 157 // We don't fallback for responses that we delivered. |
155 if (job_.get()) { | 158 if (job_.get()) { |
156 DCHECK(!job_->is_delivering_network_response()); | 159 DCHECK(!job_->is_delivering_network_response()); |
157 return NULL; | 160 return NULL; |
158 } | 161 } |
159 | 162 |
160 if (request->status().is_success()) { | 163 if (request_->IsSuccess()) { |
161 int code_major = request->GetResponseCode() / 100; | 164 int code_major = request_->GetResponseCode() / 100; |
162 if (code_major !=4 && code_major != 5) | 165 if (code_major !=4 && code_major != 5) |
163 return NULL; | 166 return NULL; |
164 | 167 |
165 // Servers can override the fallback behavior with a response header. | 168 // Servers can override the fallback behavior with a response header. |
166 const std::string kFallbackOverrideHeader( | 169 const std::string kFallbackOverrideHeader( |
167 "x-chromium-appcache-fallback-override"); | 170 "x-chromium-appcache-fallback-override"); |
168 const std::string kFallbackOverrideValue( | 171 const std::string kFallbackOverrideValue( |
169 "disallow-fallback"); | 172 "disallow-fallback"); |
170 std::string header_value; | 173 std::string header_value; |
171 request->GetResponseHeaderByName(kFallbackOverrideHeader, &header_value); | 174 header_value = request_->GetResponseHeaderByName(kFallbackOverrideHeader); |
172 if (header_value == kFallbackOverrideValue) | 175 if (header_value == kFallbackOverrideValue) |
173 return NULL; | 176 return NULL; |
174 } | 177 } |
175 | 178 |
176 // 6.9.6, step 4: If this results in a 4xx or 5xx status code | 179 // 6.9.6, step 4: If this results in a 4xx or 5xx status code |
177 // or there were network errors, get the resource of the fallback entry. | 180 // or there were network errors, get the resource of the fallback entry. |
178 std::unique_ptr<AppCacheURLRequestJob> job = | 181 std::unique_ptr<AppCacheURLRequestJob> job = CreateJob(network_delegate); |
179 CreateJob(request, network_delegate); | |
180 DeliverAppCachedResponse(found_fallback_entry_, found_cache_id_, | 182 DeliverAppCachedResponse(found_fallback_entry_, found_cache_id_, |
181 found_manifest_url_, true, | 183 found_manifest_url_, true, |
182 found_namespace_entry_url_); | 184 found_namespace_entry_url_); |
183 return job.release(); | 185 return job.release(); |
michaeln
2017/05/03 20:05:47
The 'request' and the 'job' objects should be two
ananta
2017/05/03 20:38:40
Yes
| |
184 } | 186 } |
185 | 187 |
186 void AppCacheRequestHandler::GetExtraResponseInfo(int64_t* cache_id, | 188 void AppCacheRequestHandler::GetExtraResponseInfo(int64_t* cache_id, |
187 GURL* manifest_url) { | 189 GURL* manifest_url) { |
188 *cache_id = cache_id_; | 190 *cache_id = cache_id_; |
189 *manifest_url = manifest_url_; | 191 *manifest_url = manifest_url_; |
190 } | 192 } |
191 | 193 |
192 void AppCacheRequestHandler::PrepareForCrossSiteTransfer(int old_process_id) { | 194 void AppCacheRequestHandler::PrepareForCrossSiteTransfer(int old_process_id) { |
193 if (!host_) | 195 if (!host_) |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
288 | 290 |
289 cache_entry_not_found_ = job_->cache_entry_not_found(); | 291 cache_entry_not_found_ = job_->cache_entry_not_found(); |
290 is_delivering_network_response_ = job_->is_delivering_network_response(); | 292 is_delivering_network_response_ = job_->is_delivering_network_response(); |
291 | 293 |
292 storage()->CancelDelegateCallbacks(this); | 294 storage()->CancelDelegateCallbacks(this); |
293 | 295 |
294 job_.reset(); | 296 job_.reset(); |
295 } | 297 } |
296 | 298 |
297 std::unique_ptr<AppCacheURLRequestJob> AppCacheRequestHandler::CreateJob( | 299 std::unique_ptr<AppCacheURLRequestJob> AppCacheRequestHandler::CreateJob( |
298 net::URLRequest* request, | |
299 net::NetworkDelegate* network_delegate) { | 300 net::NetworkDelegate* network_delegate) { |
300 std::unique_ptr<AppCacheURLRequestJob> job(new AppCacheURLRequestJob( | 301 std::unique_ptr<AppCacheURLRequestJob> job(new AppCacheURLRequestJob( |
301 request, network_delegate, storage(), host_, is_main_resource(), | 302 request_->GetURLRequest(), network_delegate, storage(), host_, |
303 is_main_resource(), | |
302 base::Bind(&AppCacheRequestHandler::OnPrepareToRestart, | 304 base::Bind(&AppCacheRequestHandler::OnPrepareToRestart, |
303 base::Unretained(this)))); | 305 base::Unretained(this)))); |
304 job_ = job->GetWeakPtr(); | 306 job_ = job->GetWeakPtr(); |
305 return job; | 307 return job; |
306 } | 308 } |
307 | 309 |
308 // Main-resource handling ---------------------------------------------- | 310 // Main-resource handling ---------------------------------------------- |
309 | 311 |
310 std::unique_ptr<AppCacheURLRequestJob> | 312 std::unique_ptr<AppCacheURLRequestJob> |
311 AppCacheRequestHandler::MaybeLoadMainResource( | 313 AppCacheRequestHandler::MaybeLoadMainResource( |
312 net::URLRequest* request, | |
313 net::NetworkDelegate* network_delegate) { | 314 net::NetworkDelegate* network_delegate) { |
314 DCHECK(!job_.get()); | 315 DCHECK(!job_.get()); |
315 DCHECK(host_); | 316 DCHECK(host_); |
316 | 317 |
317 // If a page falls into the scope of a ServiceWorker, any matching AppCaches | 318 // If a page falls into the scope of a ServiceWorker, any matching AppCaches |
318 // should be ignored. This depends on the ServiceWorker handler being invoked | 319 // should be ignored. This depends on the ServiceWorker handler being invoked |
319 // prior to the AppCache handler. | 320 // prior to the AppCache handler. |
320 if (ServiceWorkerRequestHandler::IsControlledByServiceWorker(request)) { | 321 if (ServiceWorkerRequestHandler::IsControlledByServiceWorker( |
322 request_->GetURLRequest())) { | |
321 host_->enable_cache_selection(false); | 323 host_->enable_cache_selection(false); |
322 return nullptr; | 324 return nullptr; |
323 } | 325 } |
324 | 326 |
325 host_->enable_cache_selection(true); | 327 host_->enable_cache_selection(true); |
326 | 328 |
327 const AppCacheHost* spawning_host = | 329 const AppCacheHost* spawning_host = |
328 (resource_type_ == RESOURCE_TYPE_SHARED_WORKER) ? | 330 (resource_type_ == RESOURCE_TYPE_SHARED_WORKER) ? |
329 host_ : host_->GetSpawningHost(); | 331 host_ : host_->GetSpawningHost(); |
330 GURL preferred_manifest_url = spawning_host ? | 332 GURL preferred_manifest_url = spawning_host ? |
331 spawning_host->preferred_manifest_url() : GURL(); | 333 spawning_host->preferred_manifest_url() : GURL(); |
332 | 334 |
333 // We may have to wait for our storage query to complete, but | 335 // We may have to wait for our storage query to complete, but |
334 // this query can also complete syncrhonously. | 336 // this query can also complete syncrhonously. |
335 std::unique_ptr<AppCacheURLRequestJob> job = | 337 std::unique_ptr<AppCacheURLRequestJob> job = CreateJob(network_delegate); |
336 CreateJob(request, network_delegate); | 338 storage()->FindResponseForMainRequest(request_->GetURL(), |
337 storage()->FindResponseForMainRequest( | 339 preferred_manifest_url, this); |
338 request->url(), preferred_manifest_url, this); | |
339 return job; | 340 return job; |
340 } | 341 } |
341 | 342 |
342 void AppCacheRequestHandler::OnMainResponseFound( | 343 void AppCacheRequestHandler::OnMainResponseFound( |
343 const GURL& url, | 344 const GURL& url, |
344 const AppCacheEntry& entry, | 345 const AppCacheEntry& entry, |
345 const GURL& namespace_entry_url, | 346 const GURL& namespace_entry_url, |
346 const AppCacheEntry& fallback_entry, | 347 const AppCacheEntry& fallback_entry, |
347 int64_t cache_id, | 348 int64_t cache_id, |
348 int64_t group_id, | 349 int64_t group_id, |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
405 false, found_namespace_entry_url_); | 406 false, found_namespace_entry_url_); |
406 } else { | 407 } else { |
407 DeliverNetworkResponse(); | 408 DeliverNetworkResponse(); |
408 } | 409 } |
409 } | 410 } |
410 | 411 |
411 // Sub-resource handling ---------------------------------------------- | 412 // Sub-resource handling ---------------------------------------------- |
412 | 413 |
413 std::unique_ptr<AppCacheURLRequestJob> | 414 std::unique_ptr<AppCacheURLRequestJob> |
414 AppCacheRequestHandler::MaybeLoadSubResource( | 415 AppCacheRequestHandler::MaybeLoadSubResource( |
415 net::URLRequest* request, | |
416 net::NetworkDelegate* network_delegate) { | 416 net::NetworkDelegate* network_delegate) { |
417 DCHECK(!job_.get()); | 417 DCHECK(!job_.get()); |
418 | 418 |
419 if (host_->is_selection_pending()) { | 419 if (host_->is_selection_pending()) { |
420 // We have to wait until cache selection is complete and the | 420 // We have to wait until cache selection is complete and the |
421 // selected cache is loaded. | 421 // selected cache is loaded. |
422 is_waiting_for_cache_selection_ = true; | 422 is_waiting_for_cache_selection_ = true; |
423 return CreateJob(request, network_delegate); | 423 return CreateJob(network_delegate); |
424 } | 424 } |
425 | 425 |
426 if (!host_->associated_cache() || | 426 if (!host_->associated_cache() || |
427 !host_->associated_cache()->is_complete() || | 427 !host_->associated_cache()->is_complete() || |
428 host_->associated_cache()->owning_group()->is_being_deleted()) { | 428 host_->associated_cache()->owning_group()->is_being_deleted()) { |
429 return nullptr; | 429 return nullptr; |
430 } | 430 } |
431 | 431 |
432 std::unique_ptr<AppCacheURLRequestJob> job = | 432 std::unique_ptr<AppCacheURLRequestJob> job = CreateJob(network_delegate); |
433 CreateJob(request, network_delegate); | |
434 ContinueMaybeLoadSubResource(); | 433 ContinueMaybeLoadSubResource(); |
435 return job; | 434 return job; |
436 } | 435 } |
437 | 436 |
438 void AppCacheRequestHandler::ContinueMaybeLoadSubResource() { | 437 void AppCacheRequestHandler::ContinueMaybeLoadSubResource() { |
439 // 6.9.6 Changes to the networking model | 438 // 6.9.6 Changes to the networking model |
440 // If the resource is not to be fetched using the HTTP GET mechanism or | 439 // If the resource is not to be fetched using the HTTP GET mechanism or |
441 // equivalent ... then fetch the resource normally. | 440 // equivalent ... then fetch the resource normally. |
442 DCHECK(job_.get()); | 441 DCHECK(job_.get()); |
443 DCHECK(host_->associated_cache() && host_->associated_cache()->is_complete()); | 442 DCHECK(host_->associated_cache() && host_->associated_cache()->is_complete()); |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
501 if (!host_->associated_cache() || | 500 if (!host_->associated_cache() || |
502 !host_->associated_cache()->is_complete()) { | 501 !host_->associated_cache()->is_complete()) { |
503 DeliverNetworkResponse(); | 502 DeliverNetworkResponse(); |
504 return; | 503 return; |
505 } | 504 } |
506 | 505 |
507 ContinueMaybeLoadSubResource(); | 506 ContinueMaybeLoadSubResource(); |
508 } | 507 } |
509 | 508 |
510 } // namespace content | 509 } // namespace content |
OLD | NEW |