| 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 |