OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2008 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 // This file replaces WebCore/platform/network/win/ResourceHandleWin.cpp with a | |
6 // platform-neutral implementation that simply defers almost entirely to | |
7 // ResouceLoaderBridge. | |
8 // | |
9 // This uses the same ResourceHandle.h header file that the rest of WebKit | |
10 // uses, allowing us to avoid complicated changes. Our specific things are | |
11 // added on ResourceHandleInternal. The ResourceHandle owns the | |
12 // ResourceHandleInternal and passes off almost all processing to it. | |
13 // | |
14 // The WebKit version of this code keeps the ResourceHandle AddRef'd when | |
15 // there are any callbacks. This prevents the callbacks from occuring into | |
16 // destroyed objects. However, our destructors should always stop callbacks | |
17 // from happening, making this (hopefully) unnecessary. | |
18 // | |
19 // We preserve this behavior for safety. A client could count on this behavior | |
20 // and fire off a request, release it, and wait for callbacks to get the data | |
21 // as long as it doesn't care about canceling the request. Although this is | |
22 // dumb, we support it. We use pending_ to indicate this extra AddRef, which | |
23 // is done in start() and released in OnCompletedRequest. | |
24 | |
25 #include "config.h" | |
26 | |
27 #pragma warning(push, 0) | |
28 #include "CString.h" | |
29 #include "Console.h" | |
30 #include "DocLoader.h" | |
31 #include "DOMWindow.h" | |
32 #include "FormData.h" | |
33 #include "FrameLoader.h" | |
34 #include "LogWin.h" | |
35 #include "Page.h" | |
36 #include "ResourceError.h" | |
37 #include "ResourceHandle.h" | |
38 #include "ResourceHandleClient.h" | |
39 #include "ResourceRequest.h" | |
40 #include "ResourceResponse.h" | |
41 #pragma warning(pop) | |
42 | |
43 #undef LOG | |
44 #include "base/logging.h" | |
45 #include "base/message_loop.h" | |
46 #include "base/process_util.h" | |
47 #include "base/time.h" | |
48 #include "base/string_util.h" | |
49 #include "base/string_tokenizer.h" | |
50 #include "webkit/glue/feed_preview.h" | |
51 #include "webkit/glue/glue_util.h" | |
52 #include "webkit/glue/multipart_response_delegate.h" | |
53 #include "webkit/glue/resource_loader_bridge.h" | |
54 #include "webkit/glue/webframe_impl.h" | |
55 #include "net/base/data_url.h" | |
56 #include "net/base/net_errors.h" | |
57 #include "net/base/net_util.h" | |
58 #include "net/base/load_flags.h" | |
59 | |
60 using webkit_glue::ResourceLoaderBridge; | |
61 using net::HttpResponseHeaders; | |
62 | |
63 namespace { | |
64 | |
65 // Extracts the information from a data: url. | |
66 bool GetInfoFromDataUrl(const GURL& url, | |
67 ResourceLoaderBridge::ResponseInfo* info, | |
68 std::string* data, URLRequestStatus* status) { | |
69 std::string mime_type; | |
70 std::string charset; | |
71 if (net::DataURL::Parse(url, &mime_type, &charset, data)) { | |
72 info->request_time = Time::Now(); | |
73 info->response_time = Time::Now(); | |
74 info->mime_type.swap(mime_type); | |
75 info->charset.swap(charset); | |
76 *status = URLRequestStatus(URLRequestStatus::SUCCESS, 0); | |
77 return true; | |
78 } | |
79 | |
80 *status = URLRequestStatus(URLRequestStatus::FAILED, net::ERR_INVALID_URL); | |
81 return false; | |
82 } | |
83 | |
84 } // namespace | |
85 | |
86 namespace WebCore { | |
87 | |
88 static void ExtractInfoFromHeaders(const HttpResponseHeaders* headers, | |
89 HTTPHeaderMap* header_map, | |
90 int* status_code, | |
91 String* status_text, | |
92 long long* expected_content_length) { | |
93 *status_code = headers->response_code(); | |
94 | |
95 // Set the status text | |
96 *status_text = webkit_glue::StdStringToString(headers->GetStatusText()); | |
97 | |
98 // Set the content length. | |
99 std::string length_val; | |
100 if (headers->EnumerateHeader(NULL, "content-length", &length_val)) | |
101 *expected_content_length = StringToInt64(length_val); | |
102 | |
103 // Build up the header map. Take care with duplicate headers. | |
104 void* iter = NULL; | |
105 std::string name, value; | |
106 while (headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
107 String name_str = webkit_glue::StdStringToString(name); | |
108 String value_str = webkit_glue::StdStringToString(value); | |
109 | |
110 pair<HTTPHeaderMap::iterator, bool> result = | |
111 header_map->add(name_str, value_str); | |
112 if (!result.second) | |
113 result.first->second += ", " + value_str; | |
114 } | |
115 } | |
116 | |
117 static ResourceResponse MakeResourceResponse( | |
118 const KURL& kurl, | |
119 const ResourceLoaderBridge::ResponseInfo& info) { | |
120 int status_code = 0; | |
121 long long expected_content_length = info.content_length; | |
122 String status_text; | |
123 HTTPHeaderMap header_map; | |
124 | |
125 // It's okay if there are no headers | |
126 if (info.headers) | |
127 ExtractInfoFromHeaders(info.headers, | |
128 &header_map, | |
129 &status_code, | |
130 &status_text, | |
131 &expected_content_length); | |
132 | |
133 // TODO(darin): We should leverage HttpResponseHeaders for this, and this | |
134 // should be using the same code as ResourceDispatcherHost. | |
135 std::wstring suggested_filename; | |
136 if (info.headers) { | |
137 std::string disp_val; | |
138 if (info.headers->EnumerateHeader(NULL, "content-disposition", &disp_val)) { | |
139 suggested_filename = net::GetSuggestedFilename( | |
140 webkit_glue::KURLToGURL(kurl), disp_val, std::wstring()); | |
141 } | |
142 } | |
143 | |
144 ResourceResponse response(kurl, | |
145 webkit_glue::StdStringToString(info.mime_type), | |
146 expected_content_length, | |
147 webkit_glue::StdStringToString(info.charset), | |
148 webkit_glue::StdWStringToString(suggested_filename)); | |
149 | |
150 if (info.headers) { | |
151 Time time_val; | |
152 if (info.headers->GetLastModifiedValue(&time_val)) | |
153 response.setLastModifiedDate(time_val.ToTimeT()); | |
154 | |
155 // Compute expiration date | |
156 TimeDelta freshness_lifetime = | |
157 info.headers->GetFreshnessLifetime(info.response_time); | |
158 if (freshness_lifetime != TimeDelta()) { | |
159 Time now = Time::Now(); | |
160 TimeDelta current_age = | |
161 info.headers->GetCurrentAge(info.request_time, info.response_time, | |
162 now); | |
163 time_val = now + freshness_lifetime - current_age; | |
164 | |
165 response.setExpirationDate(time_val.ToTimeT()); | |
166 } else { | |
167 // WebKit uses 0 as a special expiration date that means never expire. | |
168 // 1 is a small enough value to let it always expire. | |
169 response.setExpirationDate(1); | |
170 } | |
171 } | |
172 | |
173 response.setHTTPStatusCode(status_code); | |
174 response.setHTTPStatusText(status_text); | |
175 response.setSecurityInfo(webkit_glue::StdStringToCString(info.security_info)); | |
176 | |
177 // WebKit doesn't provide a way for us to set expected content length after | |
178 // calling the constructor, so we parse the headers first and then swap in | |
179 // our HTTP header map. Ideally we would like a setter for expected content | |
180 // length (perhaps by abstracting ResourceResponse interface into | |
181 // ResourceResponseBase) but that would require forking. | |
182 const_cast<HTTPHeaderMap*>(&response.httpHeaderFields())->swap(header_map); | |
183 | |
184 return response; | |
185 } | |
186 | |
187 class ResourceHandleInternal : public ResourceLoaderBridge::Peer { | |
188 public: | |
189 ResourceHandleInternal(ResourceHandle* job, const ResourceRequest& r, | |
190 ResourceHandleClient* c); | |
191 ~ResourceHandleInternal(); | |
192 | |
193 // If the response parameter is null, then an asynchronous load is started. | |
194 bool Start(ResourceLoaderBridge::SyncLoadResponse* response); | |
195 | |
196 // Used to cancel an asynchronous load. | |
197 void Cancel(); | |
198 | |
199 // Used to suspend/resume an asynchronous load. | |
200 void SetDefersLoading(bool value); | |
201 | |
202 // ResourceLoaderBridge::Peer implementation | |
203 virtual void OnReceivedRedirect(const GURL& new_url); | |
204 virtual void OnReceivedResponse( | |
205 const ResourceLoaderBridge::ResponseInfo& info, | |
206 bool content_filtered); | |
207 virtual void OnReceivedData(const char* data, int len); | |
208 virtual void OnCompletedRequest(const URLRequestStatus& status); | |
209 virtual std::string GetURLForDebugging(); | |
210 | |
211 // Handles a data: url internally instead of calling the bridge. | |
212 void HandleDataUrl(); | |
213 | |
214 // This is the bridge implemented by the embedder. | |
215 // The bridge is kept alive as long as the request is valid and we | |
216 // are ready for callbacks. | |
217 scoped_ptr<ResourceLoaderBridge> bridge_; | |
218 | |
219 // The resource loader that owns us | |
220 ResourceHandle* job_; | |
221 | |
222 // This is the object that receives various status messages (such as when the | |
223 // loader has received data). See definition for the exact messages that are | |
224 // sent to it. | |
225 ResourceHandleClient* client_; | |
226 | |
227 ResourceRequest request_; | |
228 | |
229 // Runnable Method Factory used to invoke later HandleDataUrl(). | |
230 ScopedRunnableMethodFactory<ResourceHandleInternal> data_url_factory_; | |
231 | |
232 int load_flags_; | |
233 | |
234 private: | |
235 // Set to true when we're waiting for data from the bridge, also indicating | |
236 // we have addrefed our job. | |
237 bool pending_; | |
238 | |
239 // Expected content length of the response | |
240 long long expected_content_length_; | |
241 | |
242 // NULL unless we are handling a multipart/x-mixed-replace request | |
243 scoped_ptr<MultipartResponseDelegate> multipart_delegate_; | |
244 | |
245 // NULL unless we are handling a feed:// request. | |
246 scoped_ptr<FeedClientProxy> feed_client_proxy_; | |
247 }; | |
248 | |
249 ResourceHandleInternal::ResourceHandleInternal(ResourceHandle* job, | |
250 const ResourceRequest& r, | |
251 ResourceHandleClient* c) | |
252 : job_(job), | |
253 client_(c), | |
254 request_(r), | |
255 MSVC_SUPPRESS_WARNING(4355) // can use this | |
256 data_url_factory_(this), | |
257 load_flags_(net::LOAD_NORMAL), | |
258 pending_(false), | |
259 expected_content_length_(-1), | |
260 multipart_delegate_(NULL) { | |
261 } | |
262 | |
263 ResourceHandleInternal::~ResourceHandleInternal() { | |
264 DCHECK(!pending_); | |
265 } | |
266 | |
267 void ResourceHandleInternal::HandleDataUrl() { | |
268 ResourceLoaderBridge::ResponseInfo info; | |
269 URLRequestStatus status; | |
270 std::string data; | |
271 | |
272 if (GetInfoFromDataUrl(webkit_glue::KURLToGURL(request_.url()), &info, &data, | |
273 &status)) { | |
274 OnReceivedResponse(info, false); | |
275 | |
276 if (data.size()) | |
277 OnReceivedData(data.c_str(), data.size()); | |
278 } | |
279 | |
280 OnCompletedRequest(status); | |
281 | |
282 // We are done using the object. ResourceHandle and ResourceHandleInternal | |
283 // might be destroyed now. | |
284 job_->deref(); | |
285 } | |
286 | |
287 bool ResourceHandleInternal::Start( | |
288 ResourceLoaderBridge::SyncLoadResponse* sync_load_response) { | |
289 DCHECK(!bridge_.get()); | |
290 | |
291 // The WebFrame is the Frame's FrameWinClient | |
292 WebFrameImpl* webframe = | |
293 request_.frame() ? WebFrameImpl::FromFrame(request_.frame()) : NULL; | |
294 | |
295 CString method = request_.httpMethod().latin1(); | |
296 GURL referrer(webkit_glue::StringToStdString(request_.httpReferrer())); | |
297 | |
298 // Compute the URL of the load. | |
299 GURL url = webkit_glue::KURLToGURL(request_.url()); | |
300 if (url.SchemeIs("feed:")) { | |
301 // Feed URLs are special, they actually mean "http". | |
302 url_canon::Replacements<char> replacements; | |
303 replacements.SetScheme("http", url_parse::Component(0, 4)); | |
304 url = url.ReplaceComponents(replacements); | |
305 | |
306 // Replace our client with a client that understands previewing feeds | |
307 // and forwards the feeds along to the original client. | |
308 feed_client_proxy_.reset(new FeedClientProxy(client_)); | |
309 client_ = feed_client_proxy_.get(); | |
310 } | |
311 | |
312 // Inherit the policy URL from the request's frame. However, if the request | |
313 // is for a main frame, the current document's policyBaseURL is the old | |
314 // document, so we leave policyURL empty to indicate that the request is a | |
315 // first-party request. | |
316 GURL policy_url; | |
317 if (request_.resourceType() != ResourceType::MAIN_FRAME && | |
318 request_.frame() && request_.frame()->document()) { | |
319 policy_url = GURL(webkit_glue::StringToStdString( | |
320 request_.frame()->document()->policyBaseURL())); | |
321 } | |
322 | |
323 // Translate the table of request headers to a formatted string blob | |
324 String headerBuf; | |
325 const HTTPHeaderMap& headerMap = request_.httpHeaderFields(); | |
326 | |
327 // In some cases, WebCore doesn't add an Accept header, but not having the | |
328 // header confuses some web servers. See bug 808613. | |
329 // Note: headerMap uses case-insenstive keys, so this will find Accept as | |
330 // as well. | |
331 if (!headerMap.contains("accept")) | |
332 request_.addHTTPHeaderField("Accept", "*/*"); | |
333 | |
334 const String crlf("\r\n"); | |
335 const String sep(": "); | |
336 for (HTTPHeaderMap::const_iterator it = headerMap.begin(); | |
337 it != headerMap.end(); ++it) { | |
338 // Skip over referrer headers found in the header map because we already | |
339 // pulled it out as a separate parameter. We likewise prune the UA since | |
340 // that will be added back by the network layer. | |
341 if (equalIgnoringCase((*it).first, "referer") || | |
342 equalIgnoringCase((*it).first, "user-agent")) | |
343 continue; | |
344 // WinInet dies if blank headers are set. TODO(darin): Is this still an | |
345 // issue now that we are using WinHTTP? | |
346 if ((*it).first.isEmpty()) { | |
347 webframe->frame()->domWindow()->console()->addMessage( | |
348 JSMessageSource, | |
349 ErrorMessageLevel, | |
350 "Refused to set blank header", | |
351 1, | |
352 String()); | |
353 continue; | |
354 } | |
355 if (!headerBuf.isEmpty()) | |
356 headerBuf.append(crlf); | |
357 headerBuf.append((*it).first + sep + (*it).second); | |
358 } | |
359 | |
360 switch (request_.cachePolicy()) { | |
361 case ReloadIgnoringCacheData: | |
362 // Required by LayoutTests/http/tests/misc/refresh-headers.php | |
363 load_flags_ |= net::LOAD_VALIDATE_CACHE; | |
364 break; | |
365 case ReturnCacheDataElseLoad: | |
366 load_flags_ |= net::LOAD_PREFERRING_CACHE; | |
367 break; | |
368 case ReturnCacheDataDontLoad: | |
369 load_flags_ |= net::LOAD_ONLY_FROM_CACHE; | |
370 break; | |
371 case UseProtocolCachePolicy: | |
372 break; | |
373 } | |
374 | |
375 // TODO(jcampan): in the non out-of-process plugin case the request does not | |
376 // have a origin_pid. Find a better place to set this. | |
377 int origin_pid = request_.originPid(); | |
378 if (origin_pid == 0) | |
379 origin_pid = process_util::GetCurrentProcId(); | |
380 | |
381 bool mixed_content = | |
382 webkit_glue::KURLToGURL(request_.mainDocumentURL()).SchemeIsSecure() && | |
383 !url.SchemeIsSecure(); | |
384 | |
385 if (url.SchemeIs("data")) { | |
386 if (sync_load_response) { | |
387 // This is a sync load. Do the work now. | |
388 sync_load_response->url = url; | |
389 std::string data; | |
390 GetInfoFromDataUrl(sync_load_response->url, sync_load_response, | |
391 &sync_load_response->data, | |
392 &sync_load_response->status); | |
393 } else { | |
394 pending_ = true; | |
395 job_->ref(); // to be released when we get a OnCompletedRequest. | |
396 job_->ref(); // to be released when HandleDataUrl is completed. | |
397 MessageLoop::current()->PostTask(FROM_HERE, | |
398 data_url_factory_.NewRunnableMethod( | |
399 &ResourceHandleInternal::HandleDataUrl)); | |
400 } | |
401 return true; | |
402 } | |
403 | |
404 // TODO(darin): is latin1 really correct here? It is if the strings are | |
405 // already ASCII (i.e., if they are already escaped properly). | |
406 // TODO(brettw) this should take parameter encoding into account when | |
407 // creating the GURLs. | |
408 bridge_.reset(ResourceLoaderBridge::Create( | |
409 webframe, | |
410 webkit_glue::CStringToStdString(method), | |
411 url, | |
412 policy_url, | |
413 referrer, | |
414 webkit_glue::CStringToStdString(headerBuf.latin1()), | |
415 load_flags_, | |
416 origin_pid, | |
417 request_.resourceType(), | |
418 mixed_content)); | |
419 if (!bridge_.get()) | |
420 return false; | |
421 | |
422 if (request_.httpBody()) { | |
423 // GET and HEAD requests shouldn't have http bodies. | |
424 DCHECK(method != "GET" && method != "HEAD"); | |
425 const Vector<FormDataElement>& elements = request_.httpBody()->elements(); | |
426 size_t n = elements.size(); | |
427 for (size_t i = 0; i < n; ++i) { | |
428 const FormDataElement& e = elements[static_cast<unsigned>(i)]; | |
429 if (e.m_type == FormDataElement::data) { | |
430 if (e.m_data.size() > 0) { | |
431 // WebKit sometimes gives up empty data to append. These aren't | |
432 // necessary so we just optimize those out here. | |
433 bridge_->AppendDataToUpload(e.m_data.data(), | |
434 static_cast<int>(e.m_data.size())); | |
435 } | |
436 } else { | |
437 bridge_->AppendFileToUpload( | |
438 webkit_glue::StringToStdWString(e.m_filename)); | |
439 } | |
440 } | |
441 } | |
442 | |
443 if (sync_load_response) { | |
444 bridge_->SyncLoad(sync_load_response); | |
445 return true; | |
446 } | |
447 | |
448 bool rv = bridge_->Start(this); | |
449 if (rv) { | |
450 pending_ = true; | |
451 job_->ref(); // to be released when we get a OnCompletedRequest. | |
452 } else { | |
453 bridge_.reset(); | |
454 } | |
455 | |
456 return rv; | |
457 } | |
458 | |
459 void ResourceHandleInternal::Cancel() { | |
460 // The bridge will still send OnCompletedRequest, which will deref() us, | |
461 // so we don't do that here. | |
462 if (bridge_.get()) | |
463 bridge_->Cancel(); | |
464 | |
465 // Ensure that we do not notify the multipart delegate anymore as it has | |
466 // its own pointer to the client. | |
467 multipart_delegate_.reset(); | |
468 | |
469 // Do not make any further calls to the client. | |
470 client_ = NULL; | |
471 } | |
472 | |
473 void ResourceHandleInternal::SetDefersLoading(bool value) { | |
474 if (bridge_.get()) | |
475 bridge_->SetDefersLoading(value); | |
476 } | |
477 | |
478 // ResourceLoaderBridge::Peer impl -------------------------------------------- | |
479 | |
480 void ResourceHandleInternal::OnReceivedRedirect(const GURL& new_url) { | |
481 DCHECK(pending_); | |
482 | |
483 KURL url = webkit_glue::GURLToKURL(new_url); | |
484 | |
485 // TODO(darin): need a way to properly initialize a ResourceResponse | |
486 ResourceResponse response(request_.url(), String(), -1, String(), String()); | |
487 | |
488 ResourceRequest new_request(url); | |
489 | |
490 // TODO(darin): we need to setup new_request to reflect the fact that we | |
491 // for example drop the httpBody when following a POST request that is | |
492 // redirected to a GET request. | |
493 | |
494 if (client_) | |
495 client_->willSendRequest(job_, new_request, response); | |
496 | |
497 // | |
498 // TODO(darin): since new_request is sent as a mutable reference, it is | |
499 // possible that willSendRequest may expect to be able to modify it. | |
500 // | |
501 // andresca on #webkit confirms that that is intentional, so we'll need | |
502 // to rework the ResourceLoaderBridge to give us control over what URL | |
503 // is really loaded (and with what headers) when a redirect is encountered. | |
504 // | |
505 | |
506 request_ = new_request; | |
507 } | |
508 | |
509 void ResourceHandleInternal::OnReceivedResponse( | |
510 const ResourceLoaderBridge::ResponseInfo& info, | |
511 bool content_filtered) { | |
512 DCHECK(pending_); | |
513 | |
514 // TODO(darin): need a way to properly initialize a ResourceResponse | |
515 ResourceResponse response = MakeResourceResponse(request_.url(), info); | |
516 response.setIsContentFiltered(content_filtered); | |
517 | |
518 expected_content_length_ = response.expectedContentLength(); | |
519 | |
520 if (client_) | |
521 client_->didReceiveResponse(job_, response); | |
522 | |
523 // we may have been cancelled after didReceiveResponse, which would leave us | |
524 // without a client and therefore without much need to do multipart handling. | |
525 | |
526 DCHECK(!multipart_delegate_.get()); | |
527 if (client_ && info.headers && response.isMultipart()) { | |
528 std::string content_type; | |
529 info.headers->EnumerateHeader(NULL, "content-type", &content_type); | |
530 | |
531 std::string boundary = net::GetHeaderParamValue(content_type, "boundary"); | |
532 TrimString(boundary, " \"", &boundary); | |
533 // If there's no boundary, just handle the request normally. In the gecko | |
534 // code, nsMultiMixedConv::OnStartRequest throws an exception. | |
535 if (!boundary.empty()) { | |
536 multipart_delegate_.reset(new MultipartResponseDelegate(client_, job_, | |
537 response, boundary)); | |
538 } | |
539 } | |
540 | |
541 // TODO(darin): generate willCacheResponse callback. debug mac webkit to | |
542 // determine when it should be called. | |
543 } | |
544 | |
545 void ResourceHandleInternal::OnReceivedData(const char* data, int data_len) { | |
546 DCHECK(pending_); | |
547 | |
548 if (client_) { | |
549 // TODO(darin): figure out what to pass for lengthReceived. from reading | |
550 // the loader code, it looks like this is supposed to be the content-length | |
551 // value, but it seems really wacky to include that here! we have to debug | |
552 // webkit on mac to figure out what this should be. | |
553 | |
554 // TODO(jackson): didReceiveData expects an int, but an expected content | |
555 // length is an int64, so we do our best to fit it inside an int. The only | |
556 // code that cares currently about this value is the Inspector, so beware | |
557 // that the Inspector's network panel might under-represent the size of | |
558 // some resources if they're larger than a gigabyte. | |
559 int lengthReceived = static_cast<int>(expected_content_length_); | |
560 if (lengthReceived != expected_content_length_) // overflow occurred | |
561 lengthReceived = -1; | |
562 | |
563 if (!multipart_delegate_.get()) { | |
564 client_->didReceiveData(job_, data, data_len, lengthReceived); | |
565 } else { | |
566 // AddData will make the appropriate calls to client_->didReceiveData | |
567 // and client_->didReceiveResponse | |
568 multipart_delegate_->OnReceivedData(data, data_len); | |
569 } | |
570 } | |
571 } | |
572 | |
573 void ResourceHandleInternal::OnCompletedRequest( | |
574 const URLRequestStatus& status) { | |
575 if (multipart_delegate_.get()) { | |
576 multipart_delegate_->OnCompletedRequest(); | |
577 multipart_delegate_.reset(NULL); | |
578 } | |
579 | |
580 pending_ = false; | |
581 | |
582 if (client_) { | |
583 if (status.status() != URLRequestStatus::SUCCESS) { | |
584 int error_code; | |
585 if (status.status() == URLRequestStatus::HANDLED_EXTERNALLY) { | |
586 // By marking this request as aborted we insure that we don't navigate | |
587 // to an error page. | |
588 error_code = net::ERR_ABORTED; | |
589 } else { | |
590 error_code = status.os_error(); | |
591 } | |
592 // TODO(tc): fill in these fields properly | |
593 ResourceError error(net::kErrorDomain, | |
594 error_code, | |
595 request_.url().string(), | |
596 String() /*localized description*/); | |
597 client_->didFail(job_, error); | |
598 } else { | |
599 client_->didFinishLoading(job_); | |
600 } | |
601 } | |
602 | |
603 job_->deref(); // may destroy our owner and hence |this| | |
604 } | |
605 | |
606 std::string ResourceHandleInternal::GetURLForDebugging() { | |
607 return webkit_glue::CStringToStdString(request_.url().string().latin1()); | |
608 } | |
609 | |
610 // ResourceHandle ------------------------------------------------------------- | |
611 | |
612 ResourceHandle::ResourceHandle(const ResourceRequest& request, | |
613 ResourceHandleClient* client, | |
614 bool defersLoading, | |
615 bool shouldContentSniff, | |
616 bool mightDownloadFromHandle) | |
617 MSVC_SUPPRESS_WARNING(4355) // it's okay to pass |this| here! | |
618 : d(new ResourceHandleInternal(this, request, client)) { | |
619 // TODO(darin): figure out what to do with the two bool params | |
620 } | |
621 | |
622 PassRefPtr<ResourceHandle> ResourceHandle::create(const ResourceRequest& request
, | |
623 ResourceHandleClient* client, | |
624 Frame* deprecated, | |
625 bool defersLoading, | |
626 bool shouldContentSniff, | |
627 bool mightDownloadFromHandle)
{ | |
628 RefPtr<ResourceHandle> newHandle = | |
629 adoptRef(new ResourceHandle(request, client, defersLoading, | |
630 shouldContentSniff, mightDownloadFromHandle)); | |
631 | |
632 if (newHandle->start(NULL)) | |
633 return newHandle.release(); | |
634 | |
635 return NULL; | |
636 } | |
637 | |
638 const ResourceRequest& ResourceHandle::request() const { | |
639 return d->request_; | |
640 } | |
641 | |
642 ResourceHandleClient* ResourceHandle::client() const { | |
643 return d->client_; | |
644 } | |
645 | |
646 void ResourceHandle::setClient(ResourceHandleClient* client) { | |
647 d->client_ = client; | |
648 } | |
649 | |
650 void ResourceHandle::setDefersLoading(bool value) { | |
651 d->SetDefersLoading(value); | |
652 } | |
653 | |
654 bool ResourceHandle::start(Frame* deprecated) { | |
655 return d->Start(NULL); | |
656 } | |
657 | |
658 void ResourceHandle::clearAuthentication() { | |
659 // TODO(darin): do something here. it looks like the ResourceLoader calls | |
660 // this method when it is canceled. i have no idea why it does this. | |
661 } | |
662 | |
663 void ResourceHandle::cancel() { | |
664 d->Cancel(); | |
665 } | |
666 | |
667 ResourceHandle::~ResourceHandle() { | |
668 } | |
669 | |
670 PassRefPtr<SharedBuffer> ResourceHandle::bufferedData() { | |
671 return NULL; | |
672 } | |
673 | |
674 /*static*/ bool ResourceHandle::loadsBlocked() { | |
675 return false; // this seems to be related to sync XMLHttpRequest... | |
676 } | |
677 | |
678 /*static*/ bool ResourceHandle::supportsBufferedData() { | |
679 return false; // the loader will buffer manually if it needs to | |
680 } | |
681 | |
682 /*static*/ void ResourceHandle::loadResourceSynchronously( | |
683 const ResourceRequest& request, ResourceError& error, | |
684 ResourceResponse& response, Vector<char>& data, Frame*) { | |
685 | |
686 RefPtr<ResourceHandle> handle = | |
687 new ResourceHandle(request, NULL, false, false, false); | |
688 | |
689 ResourceLoaderBridge::SyncLoadResponse sync_load_response; | |
690 if (!handle->d->Start(&sync_load_response)) { | |
691 response = | |
692 ResourceResponse(request.url(), String(), 0, String(), String()); | |
693 // TODO(darin): what should the error code really be? | |
694 error = ResourceError(net::kErrorDomain, | |
695 net::ERR_FAILED, | |
696 request.url().string(), | |
697 String() /* localized description */); | |
698 return; | |
699 } | |
700 | |
701 KURL kurl = webkit_glue::GURLToKURL(sync_load_response.url); | |
702 | |
703 // TODO(tc): For file loads, we may want to include a more descriptive | |
704 // status code or status text. | |
705 const URLRequestStatus::Status& status = sync_load_response.status.status(); | |
706 if (status != URLRequestStatus::SUCCESS && | |
707 status != URLRequestStatus::HANDLED_EXTERNALLY) { | |
708 response = ResourceResponse(kurl, String(), 0, String(), String()); | |
709 error = ResourceError(net::kErrorDomain, | |
710 sync_load_response.status.os_error(), | |
711 kurl.string(), | |
712 String() /* localized description */); | |
713 return; | |
714 } | |
715 | |
716 response = MakeResourceResponse(kurl, sync_load_response); | |
717 | |
718 data.clear(); | |
719 data.append(sync_load_response.data.data(), | |
720 sync_load_response.data.size()); | |
721 } | |
722 | |
723 // static | |
724 bool ResourceHandle::willLoadFromCache(ResourceRequest& request) { | |
725 // | |
726 // This method is used to determine if a POST request can be repeated from | |
727 // cache, but you cannot really know until you actually try to read from the | |
728 // cache. Even if we checked now, something else could come along and wipe | |
729 // out the cache entry by the time we fetch it. | |
730 // | |
731 // So, we always say yes here, which allows us to generate an ERR_CACHE_MISS | |
732 // if the request cannot be serviced from cache. We force the 'DontLoad' | |
733 // cache policy at this point to ensure that we never hit the network for | |
734 // this request. | |
735 // | |
736 DCHECK(request.httpMethod() == "POST"); | |
737 request.setCachePolicy(ReturnCacheDataDontLoad); | |
738 return true; | |
739 } | |
740 | |
741 } // namespace WebCore | |
742 | |
OLD | NEW |