|
1 /* |
|
2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |
|
3 * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) |
|
4 * |
|
5 * Redistribution and use in source and binary forms, with or without |
|
6 * modification, are permitted provided that the following conditions |
|
7 * are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * 2. Redistributions in binary form must reproduce the above copyright |
|
12 * notice, this list of conditions and the following disclaimer in the |
|
13 * documentation and/or other materials provided with the distribution. |
|
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
|
15 * its contributors may be used to endorse or promote products derived |
|
16 * from this software without specific prior written permission. |
|
17 * |
|
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
|
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
|
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
28 */ |
|
29 |
|
30 #include "config.h" |
|
31 #include "MainResourceLoader.h" |
|
32 |
|
33 #include "ApplicationCacheHost.h" |
|
34 #include "DocumentLoader.h" |
|
35 #include "FormState.h" |
|
36 #include "Frame.h" |
|
37 #include "FrameLoader.h" |
|
38 #include "FrameLoaderClient.h" |
|
39 #include "HTMLFormElement.h" |
|
40 #include "Page.h" |
|
41 #if PLATFORM(QT) |
|
42 #include "PluginDatabase.h" |
|
43 #endif |
|
44 #include "ResourceError.h" |
|
45 #include "ResourceHandle.h" |
|
46 #include "Settings.h" |
|
47 #include <wtf/CurrentTime.h> |
|
48 |
|
49 // FIXME: More that is in common with SubresourceLoader should move up into ResourceLoader. |
|
50 |
|
51 namespace WebCore { |
|
52 |
|
53 MainResourceLoader::MainResourceLoader(Frame* frame) |
|
54 : ResourceLoader(frame, true, true) |
|
55 , m_dataLoadTimer(this, &MainResourceLoader::handleDataLoadNow) |
|
56 , m_loadingMultipartContent(false) |
|
57 , m_waitingForContentPolicy(false) |
|
58 { |
|
59 } |
|
60 |
|
61 MainResourceLoader::~MainResourceLoader() |
|
62 { |
|
63 } |
|
64 |
|
65 PassRefPtr<MainResourceLoader> MainResourceLoader::create(Frame* frame) |
|
66 { |
|
67 return adoptRef(new MainResourceLoader(frame)); |
|
68 } |
|
69 |
|
70 void MainResourceLoader::receivedError(const ResourceError& error) |
|
71 { |
|
72 // Calling receivedMainResourceError will likely result in the last reference to this object to go away. |
|
73 RefPtr<MainResourceLoader> protect(this); |
|
74 RefPtr<Frame> protectFrame(m_frame); |
|
75 |
|
76 // It is important that we call FrameLoader::receivedMainResourceError before calling |
|
77 // FrameLoader::didFailToLoad because receivedMainResourceError clears out the relevant |
|
78 // document loaders. Also, receivedMainResourceError ends up calling a FrameLoadDelegate method |
|
79 // and didFailToLoad calls a ResourceLoadDelegate method and they need to be in the correct order. |
|
80 frameLoader()->receivedMainResourceError(error, true); |
|
81 |
|
82 if (!cancelled()) { |
|
83 ASSERT(!reachedTerminalState()); |
|
84 frameLoader()->notifier()->didFailToLoad(this, error); |
|
85 |
|
86 releaseResources(); |
|
87 } |
|
88 |
|
89 ASSERT(reachedTerminalState()); |
|
90 } |
|
91 |
|
92 void MainResourceLoader::didCancel(const ResourceError& error) |
|
93 { |
|
94 m_dataLoadTimer.stop(); |
|
95 |
|
96 // Calling receivedMainResourceError will likely result in the last reference to this object to go away. |
|
97 RefPtr<MainResourceLoader> protect(this); |
|
98 |
|
99 if (m_waitingForContentPolicy) { |
|
100 frameLoader()->policyChecker()->cancelCheck(); |
|
101 ASSERT(m_waitingForContentPolicy); |
|
102 m_waitingForContentPolicy = false; |
|
103 deref(); // balances ref in didReceiveResponse |
|
104 } |
|
105 frameLoader()->receivedMainResourceError(error, true); |
|
106 ResourceLoader::didCancel(error); |
|
107 } |
|
108 |
|
109 ResourceError MainResourceLoader::interruptionForPolicyChangeError() const |
|
110 { |
|
111 return frameLoader()->interruptionForPolicyChangeError(request()); |
|
112 } |
|
113 |
|
114 void MainResourceLoader::stopLoadingForPolicyChange() |
|
115 { |
|
116 cancel(interruptionForPolicyChangeError()); |
|
117 } |
|
118 |
|
119 void MainResourceLoader::callContinueAfterNavigationPolicy(void* argument, const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue) |
|
120 { |
|
121 static_cast<MainResourceLoader*>(argument)->continueAfterNavigationPolicy(request, shouldContinue); |
|
122 } |
|
123 |
|
124 void MainResourceLoader::continueAfterNavigationPolicy(const ResourceRequest&, bool shouldContinue) |
|
125 { |
|
126 if (!shouldContinue) |
|
127 stopLoadingForPolicyChange(); |
|
128 deref(); // balances ref in willSendRequest |
|
129 } |
|
130 |
|
131 bool MainResourceLoader::isPostOrRedirectAfterPost(const ResourceRequest& newRequest, const ResourceResponse& redirectResponse) |
|
132 { |
|
133 if (newRequest.httpMethod() == "POST") |
|
134 return true; |
|
135 |
|
136 int status = redirectResponse.httpStatusCode(); |
|
137 if (((status >= 301 && status <= 303) || status == 307) |
|
138 && frameLoader()->initialRequest().httpMethod() == "POST") |
|
139 return true; |
|
140 |
|
141 return false; |
|
142 } |
|
143 |
|
144 void MainResourceLoader::addData(const char* data, int length, bool allAtOnce) |
|
145 { |
|
146 ResourceLoader::addData(data, length, allAtOnce); |
|
147 frameLoader()->receivedData(data, length); |
|
148 } |
|
149 |
|
150 void MainResourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse) |
|
151 { |
|
152 // Note that there are no asserts here as there are for the other callbacks. This is due to the |
|
153 // fact that this "callback" is sent when starting every load, and the state of callback |
|
154 // deferrals plays less of a part in this function in preventing the bad behavior deferring |
|
155 // callbacks is meant to prevent. |
|
156 ASSERT(!newRequest.isNull()); |
|
157 |
|
158 // The additional processing can do anything including possibly removing the last |
|
159 // reference to this object; one example of this is 3266216. |
|
160 RefPtr<MainResourceLoader> protect(this); |
|
161 |
|
162 ASSERT(documentLoader()->timing()->fetchStart); |
|
163 if (!redirectResponse.isNull()) { |
|
164 DocumentLoadTiming* documentLoadTiming = documentLoader()->timing(); |
|
165 documentLoadTiming->redirectCount++; |
|
166 if (!documentLoadTiming->redirectStart) |
|
167 documentLoadTiming->redirectStart = documentLoadTiming->fetchStart; |
|
168 documentLoadTiming->redirectEnd = currentTime(); |
|
169 documentLoadTiming->fetchStart = documentLoadTiming->redirectEnd; |
|
170 } |
|
171 |
|
172 // Update cookie policy base URL as URL changes, except for subframes, which use the |
|
173 // URL of the main frame which doesn't change when we redirect. |
|
174 if (frameLoader()->isLoadingMainFrame()) |
|
175 newRequest.setFirstPartyForCookies(newRequest.url()); |
|
176 |
|
177 // If we're fielding a redirect in response to a POST, force a load from origin, since |
|
178 // this is a common site technique to return to a page viewing some data that the POST |
|
179 // just modified. |
|
180 // Also, POST requests always load from origin, but this does not affect subresources. |
|
181 if (newRequest.cachePolicy() == UseProtocolCachePolicy && isPostOrRedirectAfterPost(newRequest, redirectResponse)) |
|
182 newRequest.setCachePolicy(ReloadIgnoringCacheData); |
|
183 |
|
184 ResourceLoader::willSendRequest(newRequest, redirectResponse); |
|
185 |
|
186 // Don't set this on the first request. It is set when the main load was started. |
|
187 m_documentLoader->setRequest(newRequest); |
|
188 |
|
189 Frame* top = m_frame->tree()->top(); |
|
190 if (top != m_frame) |
|
191 frameLoader()->checkIfDisplayInsecureContent(top->document()->securityOrigin(), newRequest.url()); |
|
192 |
|
193 // FIXME: Ideally we'd stop the I/O until we hear back from the navigation policy delegate |
|
194 // listener. But there's no way to do that in practice. So instead we cancel later if the |
|
195 // listener tells us to. In practice that means the navigation policy needs to be decided |
|
196 // synchronously for these redirect cases. |
|
197 if (!redirectResponse.isNull()) { |
|
198 ref(); // balanced by deref in continueAfterNavigationPolicy |
|
199 frameLoader()->policyChecker()->checkNavigationPolicy(newRequest, callContinueAfterNavigationPolicy, this); |
|
200 } |
|
201 } |
|
202 |
|
203 static bool shouldLoadAsEmptyDocument(const KURL& url) |
|
204 { |
|
205 #if PLATFORM(TORCHMOBILE) |
|
206 return url.isEmpty() || (url.protocolIs("about") && equalIgnoringRef(url, blankURL())); |
|
207 #else |
|
208 return url.isEmpty() || url.protocolIs("about"); |
|
209 #endif |
|
210 } |
|
211 |
|
212 void MainResourceLoader::continueAfterContentPolicy(PolicyAction contentPolicy, const ResourceResponse& r) |
|
213 { |
|
214 KURL url = request().url(); |
|
215 const String& mimeType = r.mimeType(); |
|
216 |
|
217 switch (contentPolicy) { |
|
218 case PolicyUse: { |
|
219 // Prevent remote web archives from loading because they can claim to be from any domain and thus avoid cross-domain security checks (4120255). |
|
220 bool isRemoteWebArchive = equalIgnoringCase("application/x-webarchive", mimeType) && !m_substituteData.isValid() && !url.isLocalFile(); |
|
221 if (!frameLoader()->canShowMIMEType(mimeType) || isRemoteWebArchive) { |
|
222 frameLoader()->policyChecker()->cannotShowMIMEType(r); |
|
223 // Check reachedTerminalState since the load may have already been cancelled inside of _handleUnimplementablePolicyWithErrorCode::. |
|
224 if (!reachedTerminalState()) |
|
225 stopLoadingForPolicyChange(); |
|
226 return; |
|
227 } |
|
228 break; |
|
229 } |
|
230 |
|
231 case PolicyDownload: |
|
232 // m_handle can be null, e.g. when loading a substitute resource from application cache. |
|
233 if (!m_handle) { |
|
234 receivedError(cannotShowURLError()); |
|
235 return; |
|
236 } |
|
237 frameLoader()->client()->download(m_handle.get(), request(), m_handle.get()->firstRequest(), r); |
|
238 // It might have gone missing |
|
239 if (frameLoader()) |
|
240 receivedError(interruptionForPolicyChangeError()); |
|
241 return; |
|
242 |
|
243 case PolicyIgnore: |
|
244 stopLoadingForPolicyChange(); |
|
245 return; |
|
246 |
|
247 default: |
|
248 ASSERT_NOT_REACHED(); |
|
249 } |
|
250 |
|
251 RefPtr<MainResourceLoader> protect(this); |
|
252 |
|
253 if (r.isHTTP()) { |
|
254 int status = r.httpStatusCode(); |
|
255 if (status < 200 || status >= 300) { |
|
256 bool hostedByObject = frameLoader()->isHostedByObjectElement(); |
|
257 |
|
258 frameLoader()->handleFallbackContent(); |
|
259 // object elements are no longer rendered after we fallback, so don't |
|
260 // keep trying to process data from their load |
|
261 |
|
262 if (hostedByObject) |
|
263 cancel(); |
|
264 } |
|
265 } |
|
266 |
|
267 // we may have cancelled this load as part of switching to fallback content |
|
268 if (!reachedTerminalState()) |
|
269 ResourceLoader::didReceiveResponse(r); |
|
270 |
|
271 if (frameLoader() && !frameLoader()->isStopping()) { |
|
272 if (m_substituteData.isValid()) { |
|
273 if (m_substituteData.content()->size()) |
|
274 didReceiveData(m_substituteData.content()->data(), m_substituteData.content()->size(), m_substituteData.content()->size(), true); |
|
275 if (frameLoader() && !frameLoader()->isStopping()) |
|
276 didFinishLoading(); |
|
277 } else if (shouldLoadAsEmptyDocument(url) || frameLoader()->representationExistsForURLScheme(url.protocol())) |
|
278 didFinishLoading(); |
|
279 } |
|
280 } |
|
281 |
|
282 void MainResourceLoader::callContinueAfterContentPolicy(void* argument, PolicyAction policy) |
|
283 { |
|
284 static_cast<MainResourceLoader*>(argument)->continueAfterContentPolicy(policy); |
|
285 } |
|
286 |
|
287 void MainResourceLoader::continueAfterContentPolicy(PolicyAction policy) |
|
288 { |
|
289 ASSERT(m_waitingForContentPolicy); |
|
290 m_waitingForContentPolicy = false; |
|
291 if (frameLoader() && !frameLoader()->isStopping()) |
|
292 continueAfterContentPolicy(policy, m_response); |
|
293 deref(); // balances ref in didReceiveResponse |
|
294 } |
|
295 |
|
296 #if PLATFORM(QT) |
|
297 void MainResourceLoader::substituteMIMETypeFromPluginDatabase(const ResourceResponse& r) |
|
298 { |
|
299 if (!m_frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin)) |
|
300 return; |
|
301 |
|
302 String filename = r.url().lastPathComponent(); |
|
303 if (filename.endsWith("/")) |
|
304 return; |
|
305 |
|
306 int extensionPos = filename.reverseFind('.'); |
|
307 if (extensionPos == -1) |
|
308 return; |
|
309 |
|
310 String extension = filename.substring(extensionPos + 1); |
|
311 String mimeType = PluginDatabase::installedPlugins()->MIMETypeForExtension(extension); |
|
312 if (!mimeType.isEmpty()) { |
|
313 ResourceResponse* response = const_cast<ResourceResponse*>(&r); |
|
314 response->setMimeType(mimeType); |
|
315 } |
|
316 } |
|
317 #endif |
|
318 |
|
319 void MainResourceLoader::didReceiveResponse(const ResourceResponse& r) |
|
320 { |
|
321 #if ENABLE(OFFLINE_WEB_APPLICATIONS) |
|
322 if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForMainResponse(request(), r)) |
|
323 return; |
|
324 #endif |
|
325 |
|
326 HTTPHeaderMap::const_iterator it = r.httpHeaderFields().find(AtomicString("x-frame-options")); |
|
327 if (it != r.httpHeaderFields().end()) { |
|
328 String content = it->second; |
|
329 if (m_frame->loader()->shouldInterruptLoadForXFrameOptions(content, r.url())) { |
|
330 cancel(); |
|
331 return; |
|
332 } |
|
333 } |
|
334 |
|
335 // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. |
|
336 // See <rdar://problem/6304600> for more details. |
|
337 #if !PLATFORM(CF) |
|
338 ASSERT(shouldLoadAsEmptyDocument(r.url()) || !defersLoading()); |
|
339 #endif |
|
340 |
|
341 #if PLATFORM(QT) |
|
342 if (r.mimeType() == "application/octet-stream") |
|
343 substituteMIMETypeFromPluginDatabase(r); |
|
344 #endif |
|
345 |
|
346 if (m_loadingMultipartContent) { |
|
347 frameLoader()->setupForReplaceByMIMEType(r.mimeType()); |
|
348 clearResourceData(); |
|
349 } |
|
350 |
|
351 if (r.isMultipart()) |
|
352 m_loadingMultipartContent = true; |
|
353 |
|
354 // The additional processing can do anything including possibly removing the last |
|
355 // reference to this object; one example of this is 3266216. |
|
356 RefPtr<MainResourceLoader> protect(this); |
|
357 |
|
358 m_documentLoader->setResponse(r); |
|
359 |
|
360 m_response = r; |
|
361 |
|
362 ASSERT(!m_waitingForContentPolicy); |
|
363 m_waitingForContentPolicy = true; |
|
364 ref(); // balanced by deref in continueAfterContentPolicy and didCancel |
|
365 |
|
366 ASSERT(frameLoader()->activeDocumentLoader()); |
|
367 |
|
368 // Always show content with valid substitute data. |
|
369 if (frameLoader()->activeDocumentLoader()->substituteData().isValid()) { |
|
370 callContinueAfterContentPolicy(this, PolicyUse); |
|
371 return; |
|
372 } |
|
373 |
|
374 #if ENABLE(FTPDIR) |
|
375 // Respect the hidden FTP Directory Listing pref so it can be tested even if the policy delegate might otherwise disallow it |
|
376 Settings* settings = m_frame->settings(); |
|
377 if (settings && settings->forceFTPDirectoryListings() && m_response.mimeType() == "application/x-ftp-directory") { |
|
378 callContinueAfterContentPolicy(this, PolicyUse); |
|
379 return; |
|
380 } |
|
381 #endif |
|
382 |
|
383 frameLoader()->policyChecker()->checkContentPolicy(m_response.mimeType(), callContinueAfterContentPolicy, this); |
|
384 } |
|
385 |
|
386 void MainResourceLoader::didReceiveData(const char* data, int length, long long lengthReceived, bool allAtOnce) |
|
387 { |
|
388 ASSERT(data); |
|
389 ASSERT(length != 0); |
|
390 |
|
391 ASSERT(!m_response.isNull()); |
|
392 |
|
393 #if USE(CFNETWORK) || (PLATFORM(MAC) && !defined(BUILDING_ON_TIGER)) |
|
394 // Workaround for <rdar://problem/6060782> |
|
395 if (m_response.isNull()) { |
|
396 m_response = ResourceResponse(KURL(), "text/html", 0, String(), String()); |
|
397 if (DocumentLoader* documentLoader = frameLoader()->activeDocumentLoader()) |
|
398 documentLoader->setResponse(m_response); |
|
399 } |
|
400 #endif |
|
401 |
|
402 // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. |
|
403 // See <rdar://problem/6304600> for more details. |
|
404 #if !PLATFORM(CF) |
|
405 ASSERT(!defersLoading()); |
|
406 #endif |
|
407 |
|
408 #if ENABLE(OFFLINE_WEB_APPLICATIONS) |
|
409 documentLoader()->applicationCacheHost()->mainResourceDataReceived(data, length, lengthReceived, allAtOnce); |
|
410 #endif |
|
411 |
|
412 // The additional processing can do anything including possibly removing the last |
|
413 // reference to this object; one example of this is 3266216. |
|
414 RefPtr<MainResourceLoader> protect(this); |
|
415 |
|
416 m_timeOfLastDataReceived = currentTime(); |
|
417 |
|
418 ResourceLoader::didReceiveData(data, length, lengthReceived, allAtOnce); |
|
419 } |
|
420 |
|
421 void MainResourceLoader::didFinishLoading() |
|
422 { |
|
423 // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. |
|
424 // See <rdar://problem/6304600> for more details. |
|
425 #if !PLATFORM(CF) |
|
426 ASSERT(shouldLoadAsEmptyDocument(frameLoader()->activeDocumentLoader()->url()) || !defersLoading()); |
|
427 #endif |
|
428 |
|
429 // The additional processing can do anything including possibly removing the last |
|
430 // reference to this object. |
|
431 RefPtr<MainResourceLoader> protect(this); |
|
432 |
|
433 #if ENABLE(OFFLINE_WEB_APPLICATIONS) |
|
434 RefPtr<DocumentLoader> dl = documentLoader(); |
|
435 #endif |
|
436 |
|
437 ASSERT(!documentLoader()->timing()->responseEnd); |
|
438 documentLoader()->timing()->responseEnd = m_timeOfLastDataReceived; |
|
439 frameLoader()->finishedLoading(); |
|
440 ResourceLoader::didFinishLoading(); |
|
441 |
|
442 #if ENABLE(OFFLINE_WEB_APPLICATIONS) |
|
443 dl->applicationCacheHost()->finishedLoadingMainResource(); |
|
444 #endif |
|
445 } |
|
446 |
|
447 void MainResourceLoader::didFail(const ResourceError& error) |
|
448 { |
|
449 #if ENABLE(OFFLINE_WEB_APPLICATIONS) |
|
450 if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForMainError(request(), error)) |
|
451 return; |
|
452 #endif |
|
453 |
|
454 // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. |
|
455 // See <rdar://problem/6304600> for more details. |
|
456 #if !PLATFORM(CF) |
|
457 ASSERT(!defersLoading()); |
|
458 #endif |
|
459 |
|
460 receivedError(error); |
|
461 } |
|
462 |
|
463 void MainResourceLoader::handleEmptyLoad(const KURL& url, bool forURLScheme) |
|
464 { |
|
465 String mimeType; |
|
466 if (forURLScheme) |
|
467 mimeType = frameLoader()->generatedMIMETypeForURLScheme(url.protocol()); |
|
468 else |
|
469 mimeType = "text/html"; |
|
470 |
|
471 ResourceResponse response(url, mimeType, 0, String(), String()); |
|
472 didReceiveResponse(response); |
|
473 } |
|
474 |
|
475 void MainResourceLoader::handleDataLoadNow(MainResourceLoaderTimer*) |
|
476 { |
|
477 RefPtr<MainResourceLoader> protect(this); |
|
478 |
|
479 KURL url = m_substituteData.responseURL(); |
|
480 if (url.isEmpty()) |
|
481 url = m_initialRequest.url(); |
|
482 |
|
483 // Clear the initial request here so that subsequent entries into the |
|
484 // loader will not think there's still a deferred load left to do. |
|
485 m_initialRequest = ResourceRequest(); |
|
486 |
|
487 ResourceResponse response(url, m_substituteData.mimeType(), m_substituteData.content()->size(), m_substituteData.textEncoding(), ""); |
|
488 didReceiveResponse(response); |
|
489 } |
|
490 |
|
491 void MainResourceLoader::startDataLoadTimer() |
|
492 { |
|
493 m_dataLoadTimer.startOneShot(0); |
|
494 |
|
495 #if HAVE(RUNLOOP_TIMER) |
|
496 if (SchedulePairHashSet* scheduledPairs = m_frame->page()->scheduledRunLoopPairs()) |
|
497 m_dataLoadTimer.schedule(*scheduledPairs); |
|
498 #endif |
|
499 } |
|
500 |
|
501 void MainResourceLoader::handleDataLoadSoon(ResourceRequest& r) |
|
502 { |
|
503 m_initialRequest = r; |
|
504 |
|
505 if (m_documentLoader->deferMainResourceDataLoad()) |
|
506 startDataLoadTimer(); |
|
507 else |
|
508 handleDataLoadNow(0); |
|
509 } |
|
510 |
|
511 bool MainResourceLoader::loadNow(ResourceRequest& r) |
|
512 { |
|
513 bool shouldLoadEmptyBeforeRedirect = shouldLoadAsEmptyDocument(r.url()); |
|
514 |
|
515 ASSERT(!m_handle); |
|
516 ASSERT(shouldLoadEmptyBeforeRedirect || !defersLoading()); |
|
517 |
|
518 // Send this synthetic delegate callback since clients expect it, and |
|
519 // we no longer send the callback from within NSURLConnection for |
|
520 // initial requests. |
|
521 willSendRequest(r, ResourceResponse()); |
|
522 |
|
523 // <rdar://problem/4801066> |
|
524 // willSendRequest() is liable to make the call to frameLoader() return NULL, so we need to check that here |
|
525 if (!frameLoader()) |
|
526 return false; |
|
527 |
|
528 const KURL& url = r.url(); |
|
529 bool shouldLoadEmpty = shouldLoadAsEmptyDocument(url) && !m_substituteData.isValid(); |
|
530 |
|
531 if (shouldLoadEmptyBeforeRedirect && !shouldLoadEmpty && defersLoading()) |
|
532 return true; |
|
533 |
|
534 if (m_substituteData.isValid()) |
|
535 handleDataLoadSoon(r); |
|
536 else if (shouldLoadEmpty || frameLoader()->representationExistsForURLScheme(url.protocol())) |
|
537 handleEmptyLoad(url, !shouldLoadEmpty); |
|
538 else |
|
539 m_handle = ResourceHandle::create(r, this, m_frame.get(), false, true); |
|
540 |
|
541 return false; |
|
542 } |
|
543 |
|
544 bool MainResourceLoader::load(const ResourceRequest& r, const SubstituteData& substituteData) |
|
545 { |
|
546 ASSERT(!m_handle); |
|
547 |
|
548 m_substituteData = substituteData; |
|
549 |
|
550 ASSERT(documentLoader()->timing()->navigationStart); |
|
551 ASSERT(!documentLoader()->timing()->fetchStart); |
|
552 documentLoader()->timing()->fetchStart = currentTime(); |
|
553 ResourceRequest request(r); |
|
554 |
|
555 #if ENABLE(OFFLINE_WEB_APPLICATIONS) |
|
556 documentLoader()->applicationCacheHost()->maybeLoadMainResource(request, m_substituteData); |
|
557 #endif |
|
558 |
|
559 bool defer = defersLoading(); |
|
560 if (defer) { |
|
561 bool shouldLoadEmpty = shouldLoadAsEmptyDocument(request.url()); |
|
562 if (shouldLoadEmpty) |
|
563 defer = false; |
|
564 } |
|
565 if (!defer) { |
|
566 if (loadNow(request)) { |
|
567 // Started as an empty document, but was redirected to something non-empty. |
|
568 ASSERT(defersLoading()); |
|
569 defer = true; |
|
570 } |
|
571 } |
|
572 if (defer) |
|
573 m_initialRequest = request; |
|
574 |
|
575 return true; |
|
576 } |
|
577 |
|
578 void MainResourceLoader::setDefersLoading(bool defers) |
|
579 { |
|
580 ResourceLoader::setDefersLoading(defers); |
|
581 |
|
582 if (defers) { |
|
583 if (m_dataLoadTimer.isActive()) |
|
584 m_dataLoadTimer.stop(); |
|
585 } else { |
|
586 if (m_initialRequest.isNull()) |
|
587 return; |
|
588 |
|
589 if (m_substituteData.isValid() && m_documentLoader->deferMainResourceDataLoad()) |
|
590 startDataLoadTimer(); |
|
591 else { |
|
592 ResourceRequest r(m_initialRequest); |
|
593 m_initialRequest = ResourceRequest(); |
|
594 loadNow(r); |
|
595 } |
|
596 } |
|
597 } |
|
598 |
|
599 } |