1 /*
2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
4 * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 * its contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include "FrameLoader.h"
33
34 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
35 #include "Archive.h"
36 #include "ArchiveFactory.h"
37 #endif
38 #include "CString.h"
39 #include "Cache.h"
40 #include "CachedPage.h"
41 #include "Chrome.h"
42 #include "DOMImplementation.h"
43 #include "DOMWindow.h"
44 #include "DocLoader.h"
45 #include "Document.h"
46 #include "DocumentLoader.h"
47 #include "Editor.h"
48 #include "EditorClient.h"
49 #include "Element.h"
50 #include "Event.h"
51 #include "EventNames.h"
52 #include "FloatRect.h"
53 #include "FormState.h"
54 #include "Frame.h"
55 #include "FrameLoadRequest.h"
56 #include "FrameLoaderClient.h"
57 #include "FrameTree.h"
58 #include "FrameView.h"
59 #include "HTMLAnchorElement.h"
60 #include "HTMLFormElement.h"
61 #include "HTMLFrameElement.h"
62 #include "HTMLNames.h"
63 #include "HTMLObjectElement.h"
64 #include "HTTPParsers.h"
65 #include "HistoryItem.h"
66 #include "IconDatabase.h"
67 #include "IconLoader.h"
68 #include "InspectorController.h"
69 #include "Logging.h"
70 #include "MIMETypeRegistry.h"
71 #include "MainResourceLoader.h"
72 #include "Page.h"
73 #include "PageCache.h"
74 #include "PageGroup.h"
75 #include "PluginData.h"
76 #include "PluginDocument.h"
77 #include "ProgressTracker.h"
78 #include "RenderPart.h"
79 #include "RenderView.h"
80 #include "RenderWidget.h"
81 #include "ResourceHandle.h"
82 #include "ResourceRequest.h"
83 #include "ScriptController.h"
84 #include "ScriptSourceCode.h"
85 #include "ScriptValue.h"
86 #include "SecurityOrigin.h"
87 #include "SegmentedString.h"
88 #include "Settings.h"
89 #include "TextResourceDecoder.h"
90 #include "WindowFeatures.h"
91 #include "XMLHttpRequest.h"
92 #include "XMLTokenizer.h"
93 #include <wtf/CurrentTime.h>
94 #include <wtf/StdLibExtras.h>
95
96 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
97 #include "ApplicationCache.h"
98 #include "ApplicationCacheResource.h"
99 #endif
100
101 #if ENABLE(SVG)
102 #include "SVGDocument.h"
103 #include "SVGLocatable.h"
104 #include "SVGNames.h"
105 #include "SVGPreserveAspectRatio.h"
106 #include "SVGSVGElement.h"
107 #include "SVGViewElement.h"
108 #include "SVGViewSpec.h"
109 #endif
110
111 #ifdef ANDROID_INSTRUMENT
112 #include "TimeCounter.h"
113 #include "RenderArena.h"
114 #endif
115
116 #if PLATFORM(ANDROID)
117 #include "WebCoreFrameBridge.h"
118 #endif
119
120 namespace WebCore {
121
122 #if ENABLE(SVG)
123 using namespace SVGNames;
124 #endif
125 using namespace HTMLNames;
126
127 #if USE(LOW_BANDWIDTH_DISPLAY)
128 const unsigned int cMaxPendingSourceLengthInLowBandwidthDisplay = 128 * 1024;
129 #endif
130
131 typedef HashSet<String, CaseFoldingHash> LocalSchemesMap;
132
133 struct FormSubmission {
FormSubmissionWebCore::FormSubmission134 FormSubmission(const char* action, const String& url, PassRefPtr<FormData> formData,
135 const String& target, const String& contentType, const String& boundary,
136 PassRefPtr<Event> event, bool lockHistory, bool lockBackForwardList)
137 : action(action)
138 , url(url)
139 , formData(formData)
140 , target(target)
141 , contentType(contentType)
142 , boundary(boundary)
143 , event(event)
144 , lockHistory(lockHistory)
145 , lockBackForwardList(lockBackForwardList)
146 {
147 }
148
149 const char* action;
150 String url;
151 RefPtr<FormData> formData;
152 String target;
153 String contentType;
154 String boundary;
155 RefPtr<Event> event;
156 bool lockHistory;
157 bool lockBackForwardList;
158 };
159
160 struct ScheduledRedirection {
161 enum Type { redirection, locationChange, historyNavigation, locationChangeDuringLoad };
162 Type type;
163 double delay;
164 String url;
165 String referrer;
166 int historySteps;
167 bool lockHistory;
168 bool lockBackForwardList;
169 bool wasUserGesture;
170 bool wasRefresh;
171
ScheduledRedirectionWebCore::ScheduledRedirection172 ScheduledRedirection(double delay, const String& url, bool lockHistory, bool lockBackForwardList, bool wasUserGesture, bool refresh)
173 : type(redirection)
174 , delay(delay)
175 , url(url)
176 , historySteps(0)
177 , lockHistory(lockHistory)
178 , lockBackForwardList(lockBackForwardList)
179 , wasUserGesture(wasUserGesture)
180 , wasRefresh(refresh)
181 {
182 }
183
ScheduledRedirectionWebCore::ScheduledRedirection184 ScheduledRedirection(Type locationChangeType, const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool wasUserGesture, bool refresh)
185 : type(locationChangeType)
186 , delay(0)
187 , url(url)
188 , referrer(referrer)
189 , historySteps(0)
190 , lockHistory(lockHistory)
191 , lockBackForwardList(lockBackForwardList)
192 , wasUserGesture(wasUserGesture)
193 , wasRefresh(refresh)
194 {
195 }
196
ScheduledRedirectionWebCore::ScheduledRedirection197 explicit ScheduledRedirection(int historyNavigationSteps)
198 : type(historyNavigation)
199 , delay(0)
200 , historySteps(historyNavigationSteps)
201 , lockHistory(false)
202 , wasUserGesture(false)
203 , wasRefresh(false)
204 {
205 }
206 };
207
208 static double storedTimeOfLastCompletedLoad;
209 static FrameLoader::LocalLoadPolicy localLoadPolicy = FrameLoader::AllowLocalLoadsForLocalOnly;
210
isBackForwardLoadType(FrameLoadType type)211 bool isBackForwardLoadType(FrameLoadType type)
212 {
213 switch (type) {
214 case FrameLoadTypeStandard:
215 case FrameLoadTypeReload:
216 case FrameLoadTypeReloadFromOrigin:
217 case FrameLoadTypeSame:
218 case FrameLoadTypeRedirectWithLockedBackForwardList:
219 case FrameLoadTypeReplace:
220 return false;
221 case FrameLoadTypeBack:
222 case FrameLoadTypeForward:
223 case FrameLoadTypeIndexedBackForward:
224 return true;
225 }
226 ASSERT_NOT_REACHED();
227 return false;
228 }
229
numRequests(Document * document)230 static int numRequests(Document* document)
231 {
232 if (!document)
233 return 0;
234
235 return document->docLoader()->requestCount();
236 }
237
FrameLoader(Frame * frame,FrameLoaderClient * client)238 FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client)
239 : m_frame(frame)
240 , m_client(client)
241 , m_state(FrameStateCommittedPage)
242 , m_loadType(FrameLoadTypeStandard)
243 , m_policyLoadType(FrameLoadTypeStandard)
244 , m_delegateIsHandlingProvisionalLoadError(false)
245 , m_delegateIsDecidingNavigationPolicy(false)
246 , m_delegateIsHandlingUnimplementablePolicy(false)
247 , m_firstLayoutDone(false)
248 , m_quickRedirectComing(false)
249 , m_sentRedirectNotification(false)
250 , m_inStopAllLoaders(false)
251 , m_navigationDuringLoad(false)
252 , m_isExecutingJavaScriptFormAction(false)
253 , m_isRunningScript(false)
254 , m_didCallImplicitClose(false)
255 , m_wasUnloadEventEmitted(false)
256 , m_isComplete(false)
257 , m_isLoadingMainResource(false)
258 , m_cancellingWithLoadInProgress(false)
259 , m_needsClear(false)
260 , m_receivedData(false)
261 , m_encodingWasChosenByUser(false)
262 , m_containsPlugIns(false)
263 , m_redirectionTimer(this, &FrameLoader::redirectionTimerFired)
264 , m_checkCompletedTimer(this, &FrameLoader::checkCompletedTimerFired)
265 , m_checkLoadCompleteTimer(this, &FrameLoader::checkLoadCompleteTimerFired)
266 , m_opener(0)
267 , m_openedByDOM(false)
268 , m_creatingInitialEmptyDocument(false)
269 , m_isDisplayingInitialEmptyDocument(false)
270 , m_committedFirstRealDocumentLoad(false)
271 , m_didPerformFirstNavigation(false)
272 #ifndef NDEBUG
273 , m_didDispatchDidCommitLoad(false)
274 #endif
275 #if USE(LOW_BANDWIDTH_DISPLAY)
276 , m_useLowBandwidthDisplay(true)
277 , m_finishedParsingDuringLowBandwidthDisplay(false)
278 , m_needToSwitchOutLowBandwidthDisplay(false)
279 #endif
280 #if ENABLE(WML)
281 , m_forceReloadWmlDeck(false)
282 #endif
283 {
284 }
285
~FrameLoader()286 FrameLoader::~FrameLoader()
287 {
288 setOpener(0);
289
290 HashSet<Frame*>::iterator end = m_openedFrames.end();
291 for (HashSet<Frame*>::iterator it = m_openedFrames.begin(); it != end; ++it)
292 (*it)->loader()->m_opener = 0;
293
294 m_client->frameLoaderDestroyed();
295 }
296
init()297 void FrameLoader::init()
298 {
299 // this somewhat odd set of steps is needed to give the frame an initial empty document
300 m_isDisplayingInitialEmptyDocument = false;
301 m_creatingInitialEmptyDocument = true;
302 setPolicyDocumentLoader(m_client->createDocumentLoader(ResourceRequest(KURL("")), SubstituteData()).get());
303 setProvisionalDocumentLoader(m_policyDocumentLoader.get());
304 setState(FrameStateProvisional);
305 m_provisionalDocumentLoader->setResponse(ResourceResponse(KURL(), "text/html", 0, String(), String()));
306 m_provisionalDocumentLoader->finishedLoading();
307 begin(KURL(), false);
308 end();
309 m_frame->document()->cancelParsing();
310 m_creatingInitialEmptyDocument = false;
311 m_didCallImplicitClose = true;
312 }
313
setDefersLoading(bool defers)314 void FrameLoader::setDefersLoading(bool defers)
315 {
316 if (m_documentLoader)
317 m_documentLoader->setDefersLoading(defers);
318 if (m_provisionalDocumentLoader)
319 m_provisionalDocumentLoader->setDefersLoading(defers);
320 if (m_policyDocumentLoader)
321 m_policyDocumentLoader->setDefersLoading(defers);
322 }
323
createWindow(FrameLoader * frameLoaderForFrameLookup,const FrameLoadRequest & request,const WindowFeatures & features,bool & created)324 Frame* FrameLoader::createWindow(FrameLoader* frameLoaderForFrameLookup, const FrameLoadRequest& request, const WindowFeatures& features, bool& created)
325 {
326 ASSERT(!features.dialog || request.frameName().isEmpty());
327
328 if (!request.frameName().isEmpty() && request.frameName() != "_blank") {
329 Frame* frame = frameLoaderForFrameLookup->frame()->tree()->find(request.frameName());
330 if (frame && shouldAllowNavigation(frame)) {
331 if (!request.resourceRequest().url().isEmpty())
332 frame->loader()->loadFrameRequestWithFormAndValues(request, false, false, 0, 0, HashMap<String, String>());
333 if (Page* page = frame->page())
334 page->chrome()->focus();
335 created = false;
336 return frame;
337 }
338 }
339
340 // FIXME: Setting the referrer should be the caller's responsibility.
341 FrameLoadRequest requestWithReferrer = request;
342 requestWithReferrer.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
343 addHTTPOriginIfNeeded(requestWithReferrer.resourceRequest(), outgoingOrigin());
344
345 Page* oldPage = m_frame->page();
346 if (!oldPage)
347 return 0;
348
349 Page* page = oldPage->chrome()->createWindow(m_frame, requestWithReferrer, features);
350 if (!page)
351 return 0;
352
353 Frame* frame = page->mainFrame();
354 if (request.frameName() != "_blank")
355 frame->tree()->setName(request.frameName());
356
357 page->chrome()->setToolbarsVisible(features.toolBarVisible || features.locationBarVisible);
358 page->chrome()->setStatusbarVisible(features.statusBarVisible);
359 page->chrome()->setScrollbarsVisible(features.scrollbarsVisible);
360 page->chrome()->setMenubarVisible(features.menuBarVisible);
361 page->chrome()->setResizable(features.resizable);
362
363 // 'x' and 'y' specify the location of the window, while 'width' and 'height'
364 // specify the size of the page. We can only resize the window, so
365 // adjust for the difference between the window size and the page size.
366
367 FloatRect windowRect = page->chrome()->windowRect();
368 FloatSize pageSize = page->chrome()->pageRect().size();
369 if (features.xSet)
370 windowRect.setX(features.x);
371 if (features.ySet)
372 windowRect.setY(features.y);
373 if (features.widthSet)
374 windowRect.setWidth(features.width + (windowRect.width() - pageSize.width()));
375 if (features.heightSet)
376 windowRect.setHeight(features.height + (windowRect.height() - pageSize.height()));
377 page->chrome()->setWindowRect(windowRect);
378
379 page->chrome()->show();
380
381 created = true;
382 return frame;
383 }
384
canHandleRequest(const ResourceRequest & request)385 bool FrameLoader::canHandleRequest(const ResourceRequest& request)
386 {
387 return m_client->canHandleRequest(request);
388 }
389
changeLocation(const String & url,const String & referrer,bool lockHistory,bool lockBackForwardList,bool userGesture,bool refresh)390 void FrameLoader::changeLocation(const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool userGesture, bool refresh)
391 {
392 changeLocation(completeURL(url), referrer, lockHistory, lockBackForwardList, userGesture, refresh);
393 }
394
395
changeLocation(const KURL & url,const String & referrer,bool lockHistory,bool lockBackForwardList,bool userGesture,bool refresh)396 void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool userGesture, bool refresh)
397 {
398 RefPtr<Frame> protect(m_frame);
399
400 ResourceRequest request(url, referrer, refresh ? ReloadIgnoringCacheData : UseProtocolCachePolicy);
401 #ifdef ANDROID_USER_GESTURE
402 request.setUserGesture(userGesture);
403 #endif
404
405 if (executeIfJavaScriptURL(request.url(), userGesture))
406 return;
407
408 urlSelected(request, "_self", 0, lockHistory, lockBackForwardList, userGesture);
409 }
410
urlSelected(const FrameLoadRequest & request,Event * event,bool lockHistory,bool lockBackForwardList)411 void FrameLoader::urlSelected(const FrameLoadRequest& request, Event* event, bool lockHistory, bool lockBackForwardList)
412 {
413 FrameLoadRequest copy = request;
414 if (copy.resourceRequest().httpReferrer().isEmpty())
415 copy.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
416 addHTTPOriginIfNeeded(copy.resourceRequest(), outgoingOrigin());
417
418 loadFrameRequestWithFormAndValues(copy, lockHistory, lockBackForwardList, event, 0, HashMap<String, String>());
419 }
420
urlSelected(const ResourceRequest & request,const String & _target,Event * triggeringEvent,bool lockHistory,bool lockBackForwardList,bool userGesture)421 void FrameLoader::urlSelected(const ResourceRequest& request, const String& _target, Event* triggeringEvent, bool lockHistory, bool lockBackForwardList, bool userGesture)
422 {
423 if (executeIfJavaScriptURL(request.url(), userGesture, false))
424 return;
425
426 String target = _target;
427 if (target.isEmpty() && m_frame->document())
428 target = m_frame->document()->baseTarget();
429
430 FrameLoadRequest frameRequest(request, target);
431 #ifdef ANDROID_USER_GESTURE
432 frameRequest.setWasUserGesture(userGesture);
433 #endif
434
435 urlSelected(frameRequest, triggeringEvent, lockHistory, lockBackForwardList);
436 }
437
requestFrame(HTMLFrameOwnerElement * ownerElement,const String & urlString,const AtomicString & frameName)438 bool FrameLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName)
439 {
440 #if USE(LOW_BANDWIDTH_DISPLAY)
441 // don't create sub-frame during low bandwidth display
442 if (frame()->document()->inLowBandwidthDisplay()) {
443 m_needToSwitchOutLowBandwidthDisplay = true;
444 return false;
445 }
446 #endif
447
448 // Support for <frame src="javascript:string">
449 KURL scriptURL;
450 KURL url;
451 if (protocolIs(urlString, "javascript")) {
452 scriptURL = completeURL(urlString); // completeURL() encodes the URL.
453 url = blankURL();
454 } else
455 url = completeURL(urlString);
456
457 Frame* frame = ownerElement->contentFrame();
458 if (frame)
459 frame->loader()->scheduleLocationChange(url.string(), m_outgoingReferrer, true, true, userGestureHint());
460 else
461 frame = loadSubframe(ownerElement, url, frameName, m_outgoingReferrer);
462
463 if (!frame)
464 return false;
465
466 if (!scriptURL.isEmpty())
467 frame->loader()->executeIfJavaScriptURL(scriptURL);
468
469 return true;
470 }
471
loadSubframe(HTMLFrameOwnerElement * ownerElement,const KURL & url,const String & name,const String & referrer)472 Frame* FrameLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer)
473 {
474 bool allowsScrolling = true;
475 int marginWidth = -1;
476 int marginHeight = -1;
477 if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) {
478 HTMLFrameElementBase* o = static_cast<HTMLFrameElementBase*>(ownerElement);
479 allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff;
480 marginWidth = o->getMarginWidth();
481 marginHeight = o->getMarginHeight();
482 }
483
484 if (!canLoad(url, referrer)) {
485 FrameLoader::reportLocalLoadFailed(m_frame, url.string());
486 return 0;
487 }
488
489 bool hideReferrer = shouldHideReferrer(url, referrer);
490 RefPtr<Frame> frame = m_client->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer,
491 allowsScrolling, marginWidth, marginHeight);
492
493 if (!frame) {
494 checkCallImplicitClose();
495 return 0;
496 }
497
498 frame->loader()->m_isComplete = false;
499
500 RenderObject* renderer = ownerElement->renderer();
501 FrameView* view = frame->view();
502 if (renderer && renderer->isWidget() && view)
503 static_cast<RenderWidget*>(renderer)->setWidget(view);
504
505 checkCallImplicitClose();
506
507 // In these cases, the synchronous load would have finished
508 // before we could connect the signals, so make sure to send the
509 // completed() signal for the child by hand
510 // FIXME: In this case the Frame will have finished loading before
511 // it's being added to the child list. It would be a good idea to
512 // create the child first, then invoke the loader separately.
513 if (url.isEmpty() || url == blankURL()) {
514 frame->loader()->completed();
515 frame->loader()->checkCompleted();
516 }
517
518 return frame.get();
519 }
520
submitFormAgain()521 void FrameLoader::submitFormAgain()
522 {
523 if (m_isRunningScript)
524 return;
525 OwnPtr<FormSubmission> form(m_deferredFormSubmission.release());
526 if (!form)
527 return;
528 submitForm(form->action, form->url, form->formData, form->target, form->contentType, form->boundary, form->event.get(), form->lockHistory, form->lockBackForwardList);
529 }
530
submitForm(const char * action,const String & url,PassRefPtr<FormData> formData,const String & target,const String & contentType,const String & boundary,Event * event,bool lockHistory,bool lockBackForwardList)531 void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<FormData> formData,
532 const String& target, const String& contentType, const String& boundary, Event* event, bool lockHistory, bool lockBackForwardList)
533 {
534 ASSERT(formData);
535
536 if (!m_frame->page())
537 return;
538
539 KURL u = completeURL(url.isNull() ? "" : url);
540 // FIXME: Do we really need to special-case an empty URL?
541 // Would it be better to just go on with the form submisson and let the I/O fail?
542 if (u.isEmpty())
543 return;
544
545 if (u.protocolIs("javascript")) {
546 m_isExecutingJavaScriptFormAction = true;
547 executeIfJavaScriptURL(u, false, false);
548 m_isExecutingJavaScriptFormAction = false;
549 return;
550 }
551
552 if (m_isRunningScript) {
553 if (m_deferredFormSubmission)
554 return;
555 m_deferredFormSubmission.set(new FormSubmission(action, url, formData, target, contentType, boundary, event, lockHistory, lockBackForwardList));
556 return;
557 }
558
559 formData->generateFiles(m_frame->page()->chrome()->client());
560
561 FrameLoadRequest frameRequest;
562 #ifdef ANDROID_USER_GESTURE
563 frameRequest.setWasUserGesture(userGestureHint());
564 #endif
565
566 if (!m_outgoingReferrer.isEmpty())
567 frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
568
569 frameRequest.setFrameName(target.isEmpty() ? m_frame->document()->baseTarget() : target);
570
571 // Handle mailto: forms
572 bool isMailtoForm = equalIgnoringCase(u.protocol(), "mailto");
573 if (isMailtoForm && strcmp(action, "GET") != 0) {
574 // Append body= for POST mailto, replace the whole query string for GET one.
575 String body = formData->flattenToString();
576 String query = u.query();
577 if (!query.isEmpty())
578 query.append('&');
579 u.setQuery(query + body);
580 }
581
582 if (strcmp(action, "GET") == 0) {
583 u.setQuery(formData->flattenToString());
584 } else {
585 if (!isMailtoForm)
586 frameRequest.resourceRequest().setHTTPBody(formData.get());
587 frameRequest.resourceRequest().setHTTPMethod("POST");
588
589 // construct some user headers if necessary
590 if (contentType.isNull() || contentType == "application/x-www-form-urlencoded")
591 frameRequest.resourceRequest().setHTTPContentType(contentType);
592 else // contentType must be "multipart/form-data"
593 frameRequest.resourceRequest().setHTTPContentType(contentType + "; boundary=" + boundary);
594 }
595
596 frameRequest.resourceRequest().setURL(u);
597 addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin());
598
599 submitForm(frameRequest, event, lockHistory, lockBackForwardList);
600 }
601
stopLoading(bool sendUnload)602 void FrameLoader::stopLoading(bool sendUnload)
603 {
604 if (m_frame->document() && m_frame->document()->tokenizer())
605 m_frame->document()->tokenizer()->stopParsing();
606
607 if (sendUnload) {
608 if (m_frame->document()) {
609 if (m_didCallImplicitClose && !m_wasUnloadEventEmitted) {
610 Node* currentFocusedNode = m_frame->document()->focusedNode();
611 if (currentFocusedNode)
612 currentFocusedNode->aboutToUnload();
613 m_frame->document()->dispatchWindowEvent(eventNames().unloadEvent, false, false);
614 if (m_frame->document())
615 m_frame->document()->updateRendering();
616 m_wasUnloadEventEmitted = true;
617 if (m_frame->eventHandler()->pendingFrameUnloadEventCount())
618 m_frame->eventHandler()->clearPendingFrameUnloadEventCount();
619 if (m_frame->eventHandler()->pendingFrameBeforeUnloadEventCount())
620 m_frame->eventHandler()->clearPendingFrameBeforeUnloadEventCount();
621 }
622 }
623 if (m_frame->document() && !m_frame->document()->inPageCache())
624 m_frame->document()->removeAllEventListenersFromAllNodes();
625 }
626
627 m_isComplete = true; // to avoid calling completed() in finishedParsing() (David)
628 m_isLoadingMainResource = false;
629 m_didCallImplicitClose = true; // don't want that one either
630
631 if (m_frame->document() && m_frame->document()->parsing()) {
632 finishedParsing();
633 m_frame->document()->setParsing(false);
634 }
635
636 m_workingURL = KURL();
637
638 if (Document* doc = m_frame->document()) {
639 if (DocLoader* docLoader = doc->docLoader())
640 cache()->loader()->cancelRequests(docLoader);
641
642 #if ENABLE(DATABASE)
643 doc->stopDatabases();
644 #endif
645 }
646
647 // tell all subframes to stop as well
648 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
649 child->loader()->stopLoading(sendUnload);
650
651 cancelRedirection();
652
653 #if USE(LOW_BANDWIDTH_DISPLAY)
654 if (m_frame->document() && m_frame->document()->inLowBandwidthDisplay()) {
655 // Since loading is forced to stop, reset the state without really switching.
656 m_needToSwitchOutLowBandwidthDisplay = false;
657 switchOutLowBandwidthDisplayIfReady();
658 }
659 #endif
660 }
661
stop()662 void FrameLoader::stop()
663 {
664 // http://bugs.webkit.org/show_bug.cgi?id=10854
665 // The frame's last ref may be removed and it will be deleted by checkCompleted().
666 RefPtr<Frame> protector(m_frame);
667
668 if (m_frame->document()) {
669 if (m_frame->document()->tokenizer())
670 m_frame->document()->tokenizer()->stopParsing();
671 m_frame->document()->finishParsing();
672 } else
673 // WebKit partially uses WebCore when loading non-HTML docs. In these cases doc==nil, but
674 // WebCore is enough involved that we need to checkCompleted() in order for m_bComplete to
675 // become true. An example is when a subframe is a pure text doc, and that subframe is the
676 // last one to complete.
677 checkCompleted();
678 if (m_iconLoader)
679 m_iconLoader->stopLoading();
680 }
681
closeURL()682 bool FrameLoader::closeURL()
683 {
684 saveDocumentState();
685 stopLoading(true);
686 m_frame->editor()->clearUndoRedoOperations();
687 return true;
688 }
689
cancelRedirection(bool cancelWithLoadInProgress)690 void FrameLoader::cancelRedirection(bool cancelWithLoadInProgress)
691 {
692 m_cancellingWithLoadInProgress = cancelWithLoadInProgress;
693
694 stopRedirectionTimer();
695
696 m_scheduledRedirection.clear();
697 }
698
iconURL()699 KURL FrameLoader::iconURL()
700 {
701 // If this isn't a top level frame, return nothing
702 if (m_frame->tree() && m_frame->tree()->parent())
703 return KURL();
704
705 // If we have an iconURL from a Link element, return that
706 if (m_frame->document() && !m_frame->document()->iconURL().isEmpty())
707 return KURL(m_frame->document()->iconURL());
708
709 // Don't return a favicon iconURL unless we're http or https
710 if (!m_URL.protocolIs("http") && !m_URL.protocolIs("https"))
711 return KURL();
712
713 KURL url;
714 url.setProtocol(m_URL.protocol());
715 url.setHost(m_URL.host());
716 if (int port = m_URL.port())
717 url.setPort(port);
718 url.setPath("/favicon.ico");
719 return url;
720 }
721
didOpenURL(const KURL & url)722 bool FrameLoader::didOpenURL(const KURL& url)
723 {
724 if (m_scheduledRedirection && m_scheduledRedirection->type == ScheduledRedirection::locationChangeDuringLoad)
725 // A redirect was scheduled before the document was created.
726 // This can happen when one frame changes another frame's location.
727 return false;
728
729 cancelRedirection();
730 m_frame->editor()->clearLastEditCommand();
731 closeURL();
732
733 m_isComplete = false;
734 m_isLoadingMainResource = true;
735 m_didCallImplicitClose = false;
736
737 m_frame->setJSStatusBarText(String());
738 m_frame->setJSDefaultStatusBarText(String());
739
740 m_URL = url;
741 if ((m_URL.protocolIs("http") || m_URL.protocolIs("https")) && !m_URL.host().isEmpty() && m_URL.path().isEmpty())
742 m_URL.setPath("/");
743 m_workingURL = m_URL;
744
745 started();
746
747 return true;
748 }
749
didExplicitOpen()750 void FrameLoader::didExplicitOpen()
751 {
752 m_isComplete = false;
753 m_didCallImplicitClose = false;
754
755 // Calling document.open counts as committing the first real document load.
756 m_committedFirstRealDocumentLoad = true;
757
758 // Prevent window.open(url) -- eg window.open("about:blank") -- from blowing away results
759 // from a subsequent window.document.open / window.document.write call.
760 // Cancelling redirection here works for all cases because document.open
761 // implicitly precedes document.write.
762 cancelRedirection();
763 if (m_frame->document()->url() != blankURL())
764 m_URL = m_frame->document()->url();
765 }
766
executeIfJavaScriptURL(const KURL & url,bool userGesture,bool replaceDocument)767 bool FrameLoader::executeIfJavaScriptURL(const KURL& url, bool userGesture, bool replaceDocument)
768 {
769 if (!url.protocolIs("javascript"))
770 return false;
771
772 String script = decodeURLEscapeSequences(url.string().substring(strlen("javascript:")));
773 ScriptValue result = executeScript(script, userGesture);
774
775 String scriptResult;
776 if (!result.getString(scriptResult))
777 return true;
778
779 SecurityOrigin* currentSecurityOrigin = 0;
780 if (m_frame->document())
781 currentSecurityOrigin = m_frame->document()->securityOrigin();
782
783 // FIXME: We should always replace the document, but doing so
784 // synchronously can cause crashes:
785 // http://bugs.webkit.org/show_bug.cgi?id=16782
786 if (replaceDocument) {
787 begin(m_URL, true, currentSecurityOrigin);
788 write(scriptResult);
789 end();
790 }
791
792 return true;
793 }
794
executeScript(const String & script,bool forceUserGesture)795 ScriptValue FrameLoader::executeScript(const String& script, bool forceUserGesture)
796 {
797 return executeScript(ScriptSourceCode(script, forceUserGesture ? KURL() : m_URL));
798 }
799
executeScript(const ScriptSourceCode & sourceCode)800 ScriptValue FrameLoader::executeScript(const ScriptSourceCode& sourceCode)
801 {
802 if (!m_frame->script()->isEnabled() || m_frame->script()->isPaused())
803 return ScriptValue();
804
805 bool wasRunningScript = m_isRunningScript;
806 m_isRunningScript = true;
807
808 ScriptValue result = m_frame->script()->evaluate(sourceCode);
809
810 if (!wasRunningScript) {
811 m_isRunningScript = false;
812 submitFormAgain();
813 Document::updateDocumentsRendering();
814 }
815
816 return result;
817 }
818
cancelAndClear()819 void FrameLoader::cancelAndClear()
820 {
821 cancelRedirection();
822
823 if (!m_isComplete)
824 closeURL();
825
826 clear(false);
827 m_frame->script()->updatePlatformScriptObjects();
828 }
829
clear(bool clearWindowProperties,bool clearScriptObjects)830 void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects)
831 {
832 m_frame->editor()->clear();
833
834 if (!m_needsClear)
835 return;
836 m_needsClear = false;
837
838 if (m_frame->document() && !m_frame->document()->inPageCache()) {
839 m_frame->document()->cancelParsing();
840 m_frame->document()->stopActiveDOMObjects();
841 if (m_frame->document()->attached()) {
842 m_frame->document()->willRemove();
843 m_frame->document()->detach();
844
845 m_frame->document()->removeFocusedNodeOfSubtree(m_frame->document());
846 }
847 }
848
849 // Do this after detaching the document so that the unload event works.
850 if (clearWindowProperties) {
851 m_frame->clearDOMWindow();
852 m_frame->script()->clearWindowShell();
853 }
854
855 m_frame->selection()->clear();
856 m_frame->eventHandler()->clear();
857 if (m_frame->view())
858 m_frame->view()->clear();
859
860 m_frame->setSelectionGranularity(CharacterGranularity);
861
862 // Do not drop the document before the ScriptController and view are cleared
863 // as some destructors might still try to access the document.
864 m_frame->setDocument(0);
865 m_decoder = 0;
866
867 m_containsPlugIns = false;
868
869 if (clearScriptObjects)
870 m_frame->script()->clearScriptObjects();
871
872 m_redirectionTimer.stop();
873 m_scheduledRedirection.clear();
874
875 m_checkCompletedTimer.stop();
876 m_checkLoadCompleteTimer.stop();
877
878 m_receivedData = false;
879 m_isDisplayingInitialEmptyDocument = false;
880
881 if (!m_encodingWasChosenByUser)
882 m_encoding = String();
883 }
884
receivedFirstData()885 void FrameLoader::receivedFirstData()
886 {
887 begin(m_workingURL, false);
888
889 dispatchDidCommitLoad();
890 dispatchWindowObjectAvailable();
891
892 String ptitle = m_documentLoader->title();
893 // If we have a title let the WebView know about it.
894 if (!ptitle.isNull())
895 m_client->dispatchDidReceiveTitle(ptitle);
896
897 m_workingURL = KURL();
898
899 double delay;
900 String url;
901 if (!m_documentLoader)
902 return;
903 if (!parseHTTPRefresh(m_documentLoader->response().httpHeaderField("Refresh"), false, delay, url))
904 return;
905
906 if (url.isEmpty())
907 url = m_URL.string();
908 else
909 url = m_frame->document()->completeURL(url).string();
910
911 scheduleHTTPRedirection(delay, url);
912 }
913
responseMIMEType() const914 const String& FrameLoader::responseMIMEType() const
915 {
916 return m_responseMIMEType;
917 }
918
setResponseMIMEType(const String & type)919 void FrameLoader::setResponseMIMEType(const String& type)
920 {
921 m_responseMIMEType = type;
922 }
923
begin()924 void FrameLoader::begin()
925 {
926 begin(KURL());
927 }
928
begin(const KURL & url,bool dispatch,SecurityOrigin * origin)929 void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin)
930 {
931 // We need to take a reference to the security origin because |clear|
932 // might destroy the document that owns it.
933 RefPtr<SecurityOrigin> forcedSecurityOrigin = origin;
934
935 bool resetScripting = !(m_isDisplayingInitialEmptyDocument && m_frame->document() && m_frame->document()->securityOrigin()->isSecureTransitionTo(url));
936 clear(resetScripting, resetScripting);
937 if (resetScripting)
938 m_frame->script()->updatePlatformScriptObjects();
939 if (dispatch)
940 dispatchWindowObjectAvailable();
941
942 m_needsClear = true;
943 m_isComplete = false;
944 m_didCallImplicitClose = false;
945 m_isLoadingMainResource = true;
946 m_isDisplayingInitialEmptyDocument = m_creatingInitialEmptyDocument;
947
948 KURL ref(url);
949 ref.setUser(String());
950 ref.setPass(String());
951 ref.setRef(String());
952 m_outgoingReferrer = ref.string();
953 m_URL = url;
954
955 RefPtr<Document> document;
956
957 if (!m_isDisplayingInitialEmptyDocument && m_client->shouldUsePluginDocument(m_responseMIMEType))
958 document = PluginDocument::create(m_frame);
959 else
960 document = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode());
961 m_frame->setDocument(document);
962
963 document->setURL(m_URL);
964 if (m_decoder)
965 document->setDecoder(m_decoder.get());
966 if (forcedSecurityOrigin)
967 document->setSecurityOrigin(forcedSecurityOrigin.get());
968
969 m_frame->domWindow()->setURL(document->url());
970 m_frame->domWindow()->setSecurityOrigin(document->securityOrigin());
971
972 updatePolicyBaseURL();
973
974 Settings* settings = document->settings();
975 document->docLoader()->setAutoLoadImages(settings && settings->loadsImagesAutomatically());
976 #ifdef ANDROID_BLOCK_NETWORK_IMAGE
977 document->docLoader()->setBlockNetworkImage(settings && settings->blockNetworkImage());
978 #endif
979
980 if (m_documentLoader) {
981 String dnsPrefetchControl = m_documentLoader->response().httpHeaderField("X-DNS-Prefetch-Control");
982 if (!dnsPrefetchControl.isEmpty())
983 document->parseDNSPrefetchControlHeader(dnsPrefetchControl);
984 }
985
986 #if FRAME_LOADS_USER_STYLESHEET
987 KURL userStyleSheet = settings ? settings->userStyleSheetLocation() : KURL();
988 if (!userStyleSheet.isEmpty())
989 m_frame->setUserStyleSheetLocation(userStyleSheet);
990 #endif
991
992 restoreDocumentState();
993
994 document->implicitOpen();
995
996 if (m_frame->view())
997 m_frame->view()->setContentsSize(IntSize());
998
999 #if USE(LOW_BANDWIDTH_DISPLAY)
1000 // Low bandwidth display is a first pass display without external resources
1001 // used to give an instant visual feedback. We currently only enable it for
1002 // HTML documents in the top frame.
1003 if (document->isHTMLDocument() && !m_frame->tree()->parent() && m_useLowBandwidthDisplay) {
1004 m_pendingSourceInLowBandwidthDisplay = String();
1005 m_finishedParsingDuringLowBandwidthDisplay = false;
1006 m_needToSwitchOutLowBandwidthDisplay = false;
1007 document->setLowBandwidthDisplay(true);
1008 }
1009 #endif
1010 }
1011
write(const char * str,int len,bool flush)1012 void FrameLoader::write(const char* str, int len, bool flush)
1013 {
1014 if (len == 0 && !flush)
1015 return;
1016
1017 if (len == -1)
1018 len = strlen(str);
1019
1020 Tokenizer* tokenizer = m_frame->document()->tokenizer();
1021 if (tokenizer && tokenizer->wantsRawData()) {
1022 if (len > 0)
1023 tokenizer->writeRawData(str, len);
1024 return;
1025 }
1026
1027 if (!m_decoder) {
1028 Settings* settings = m_frame->settings();
1029 m_decoder = TextResourceDecoder::create(m_responseMIMEType, settings ? settings->defaultTextEncodingName() : String());
1030 if (m_encoding.isEmpty()) {
1031 Frame* parentFrame = m_frame->tree()->parent();
1032 if (parentFrame && parentFrame->document()->securityOrigin()->canAccess(m_frame->document()->securityOrigin()))
1033 m_decoder->setEncoding(parentFrame->document()->inputEncoding(), TextResourceDecoder::DefaultEncoding);
1034 } else {
1035 m_decoder->setEncoding(m_encoding,
1036 m_encodingWasChosenByUser ? TextResourceDecoder::UserChosenEncoding : TextResourceDecoder::EncodingFromHTTPHeader);
1037 }
1038 m_frame->document()->setDecoder(m_decoder.get());
1039 }
1040
1041 String decoded = m_decoder->decode(str, len);
1042 if (flush)
1043 decoded += m_decoder->flush();
1044 if (decoded.isEmpty())
1045 return;
1046
1047 #if USE(LOW_BANDWIDTH_DISPLAY)
1048 if (m_frame->document()->inLowBandwidthDisplay())
1049 m_pendingSourceInLowBandwidthDisplay.append(decoded);
1050 #endif
1051
1052 if (!m_receivedData) {
1053 m_receivedData = true;
1054 if (m_decoder->encoding().usesVisualOrdering())
1055 m_frame->document()->setVisuallyOrdered();
1056 m_frame->document()->recalcStyle(Node::Force);
1057 }
1058
1059 if (tokenizer) {
1060 ASSERT(!tokenizer->wantsRawData());
1061 tokenizer->write(decoded, true);
1062 }
1063 }
1064
write(const String & str)1065 void FrameLoader::write(const String& str)
1066 {
1067 if (str.isNull())
1068 return;
1069
1070 if (!m_receivedData) {
1071 m_receivedData = true;
1072 m_frame->document()->setParseMode(Document::Strict);
1073 }
1074
1075 if (Tokenizer* tokenizer = m_frame->document()->tokenizer())
1076 tokenizer->write(str, true);
1077 }
1078
end()1079 void FrameLoader::end()
1080 {
1081 m_isLoadingMainResource = false;
1082 endIfNotLoadingMainResource();
1083 }
1084
endIfNotLoadingMainResource()1085 void FrameLoader::endIfNotLoadingMainResource()
1086 {
1087 if (m_isLoadingMainResource || !m_frame->page())
1088 return;
1089
1090 // http://bugs.webkit.org/show_bug.cgi?id=10854
1091 // The frame's last ref may be removed and it can be deleted by checkCompleted(),
1092 // so we'll add a protective refcount
1093 RefPtr<Frame> protector(m_frame);
1094
1095 // make sure nothing's left in there
1096 if (m_frame->document()) {
1097 write(0, 0, true);
1098 m_frame->document()->finishParsing();
1099 #if USE(LOW_BANDWIDTH_DISPLAY)
1100 if (m_frame->document()->inLowBandwidthDisplay()) {
1101 m_finishedParsingDuringLowBandwidthDisplay = true;
1102 switchOutLowBandwidthDisplayIfReady();
1103 }
1104 #endif
1105 } else
1106 // WebKit partially uses WebCore when loading non-HTML docs. In these cases doc==nil, but
1107 // WebCore is enough involved that we need to checkCompleted() in order for m_bComplete to
1108 // become true. An example is when a subframe is a pure text doc, and that subframe is the
1109 // last one to complete.
1110 checkCompleted();
1111 }
1112
iconLoadDecisionAvailable()1113 void FrameLoader::iconLoadDecisionAvailable()
1114 {
1115 if (!m_mayLoadIconLater)
1116 return;
1117 LOG(IconDatabase, "FrameLoader %p was told a load decision is available for its icon", this);
1118 startIconLoader();
1119 m_mayLoadIconLater = false;
1120 }
1121
startIconLoader()1122 void FrameLoader::startIconLoader()
1123 {
1124 // FIXME: We kick off the icon loader when the frame is done receiving its main resource.
1125 // But we should instead do it when we're done parsing the head element.
1126 if (!isLoadingMainFrame())
1127 return;
1128
1129 if (!iconDatabase() || !iconDatabase()->isEnabled())
1130 return;
1131
1132 KURL url(iconURL());
1133 String urlString(url.string());
1134 if (urlString.isEmpty())
1135 return;
1136
1137 // If we're not reloading and the icon database doesn't say to load now then bail before we actually start the load
1138 if (loadType() != FrameLoadTypeReload && loadType() != FrameLoadTypeReloadFromOrigin) {
1139 IconLoadDecision decision = iconDatabase()->loadDecisionForIconURL(urlString, m_documentLoader.get());
1140 if (decision == IconLoadNo) {
1141 LOG(IconDatabase, "FrameLoader::startIconLoader() - Told not to load this icon, committing iconURL %s to database for pageURL mapping", urlString.ascii().data());
1142 commitIconURLToIconDatabase(url);
1143
1144 // We were told not to load this icon - that means this icon is already known by the database
1145 // If the icon data hasn't been read in from disk yet, kick off the read of the icon from the database to make sure someone
1146 // has done it. This is after registering for the notification so the WebView can call the appropriate delegate method.
1147 // Otherwise if the icon data *is* available, notify the delegate
1148 if (!iconDatabase()->iconDataKnownForIconURL(urlString)) {
1149 LOG(IconDatabase, "Told not to load icon %s but icon data is not yet available - registering for notification and requesting load from disk", urlString.ascii().data());
1150 m_client->registerForIconNotification();
1151 iconDatabase()->iconForPageURL(m_URL.string(), IntSize(0, 0));
1152 iconDatabase()->iconForPageURL(originalRequestURL().string(), IntSize(0, 0));
1153 } else
1154 m_client->dispatchDidReceiveIcon();
1155
1156 return;
1157 }
1158
1159 if (decision == IconLoadUnknown) {
1160 // In this case, we may end up loading the icon later, but we still want to commit the icon url mapping to the database
1161 // just in case we don't end up loading later - if we commit the mapping a second time after the load, that's no big deal
1162 // We also tell the client to register for the notification that the icon is received now so it isn't missed in case the
1163 // icon is later read in from disk
1164 LOG(IconDatabase, "FrameLoader %p might load icon %s later", this, urlString.ascii().data());
1165 m_mayLoadIconLater = true;
1166 m_client->registerForIconNotification();
1167 commitIconURLToIconDatabase(url);
1168 return;
1169 }
1170 }
1171
1172 // This is either a reload or the icon database said "yes, load the icon", so kick off the load!
1173 if (!m_iconLoader)
1174 m_iconLoader.set(IconLoader::create(m_frame).release());
1175
1176 m_iconLoader->startLoading();
1177 }
1178
setLocalLoadPolicy(LocalLoadPolicy policy)1179 void FrameLoader::setLocalLoadPolicy(LocalLoadPolicy policy)
1180 {
1181 localLoadPolicy = policy;
1182 }
1183
restrictAccessToLocal()1184 bool FrameLoader::restrictAccessToLocal()
1185 {
1186 return localLoadPolicy != FrameLoader::AllowLocalLoadsForAll;
1187 }
1188
allowSubstituteDataAccessToLocal()1189 bool FrameLoader::allowSubstituteDataAccessToLocal()
1190 {
1191 return localLoadPolicy != FrameLoader::AllowLocalLoadsForLocalOnly;
1192 }
1193
localSchemes()1194 static LocalSchemesMap& localSchemes()
1195 {
1196 DEFINE_STATIC_LOCAL(LocalSchemesMap, localSchemes, ());
1197
1198 if (localSchemes.isEmpty()) {
1199 localSchemes.add("file");
1200 #if PLATFORM(MAC)
1201 localSchemes.add("applewebdata");
1202 #endif
1203 #if PLATFORM(QT)
1204 localSchemes.add("qrc");
1205 #endif
1206 }
1207
1208 return localSchemes;
1209 }
1210
commitIconURLToIconDatabase(const KURL & icon)1211 void FrameLoader::commitIconURLToIconDatabase(const KURL& icon)
1212 {
1213 ASSERT(iconDatabase());
1214 LOG(IconDatabase, "Committing iconURL %s to database for pageURLs %s and %s", icon.string().ascii().data(), m_URL.string().ascii().data(), originalRequestURL().string().ascii().data());
1215 iconDatabase()->setIconURLForPageURL(icon.string(), m_URL.string());
1216 iconDatabase()->setIconURLForPageURL(icon.string(), originalRequestURL().string());
1217 }
1218
restoreDocumentState()1219 void FrameLoader::restoreDocumentState()
1220 {
1221 Document* doc = m_frame->document();
1222 if (!doc)
1223 return;
1224
1225 HistoryItem* itemToRestore = 0;
1226
1227 switch (loadType()) {
1228 case FrameLoadTypeReload:
1229 case FrameLoadTypeReloadFromOrigin:
1230 case FrameLoadTypeSame:
1231 case FrameLoadTypeReplace:
1232 break;
1233 case FrameLoadTypeBack:
1234 case FrameLoadTypeForward:
1235 case FrameLoadTypeIndexedBackForward:
1236 case FrameLoadTypeRedirectWithLockedBackForwardList:
1237 case FrameLoadTypeStandard:
1238 itemToRestore = m_currentHistoryItem.get();
1239 }
1240
1241 if (!itemToRestore)
1242 return;
1243
1244 LOG(Loading, "WebCoreLoading %s: restoring form state from %p", m_frame->tree()->name().string().utf8().data(), itemToRestore);
1245 doc->setStateForNewFormElements(itemToRestore->documentState());
1246 }
1247
gotoAnchor()1248 void FrameLoader::gotoAnchor()
1249 {
1250 // If our URL has no ref, then we have no place we need to jump to.
1251 // OTOH If CSS target was set previously, we want to set it to 0, recalc
1252 // and possibly repaint because :target pseudo class may have been
1253 // set (see bug 11321).
1254 if (!m_URL.hasRef() && !(m_frame->document() && m_frame->document()->getCSSTarget()))
1255 return;
1256
1257 String ref = m_URL.ref();
1258 if (gotoAnchor(ref))
1259 return;
1260
1261 // Try again after decoding the ref, based on the document's encoding.
1262 if (m_decoder)
1263 gotoAnchor(decodeURLEscapeSequences(ref, m_decoder->encoding()));
1264 }
1265
finishedParsing()1266 void FrameLoader::finishedParsing()
1267 {
1268 if (m_creatingInitialEmptyDocument)
1269 return;
1270
1271 // This can be called from the Frame's destructor, in which case we shouldn't protect ourselves
1272 // because doing so will cause us to re-enter the destructor when protector goes out of scope.
1273 // Null-checking the FrameView indicates whether or not we're in the destructor.
1274 RefPtr<Frame> protector = m_frame->view() ? m_frame : 0;
1275
1276 m_client->dispatchDidFinishDocumentLoad();
1277
1278 checkCompleted();
1279
1280 if (!m_frame->view())
1281 return; // We are being destroyed by something checkCompleted called.
1282
1283 // Check if the scrollbars are really needed for the content.
1284 // If not, remove them, relayout, and repaint.
1285 m_frame->view()->restoreScrollbar();
1286
1287 gotoAnchor();
1288 }
1289
loadDone()1290 void FrameLoader::loadDone()
1291 {
1292 if (m_frame->document())
1293 checkCompleted();
1294 }
1295
checkCompleted()1296 void FrameLoader::checkCompleted()
1297 {
1298 // Any frame that hasn't completed yet?
1299 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
1300 if (!child->loader()->m_isComplete)
1301 return;
1302
1303 // Have we completed before?
1304 if (m_isComplete)
1305 return;
1306
1307 // Are we still parsing?
1308 if (m_frame->document() && m_frame->document()->parsing())
1309 return;
1310
1311 // Still waiting for images/scripts?
1312 if (m_frame->document())
1313 if (numRequests(m_frame->document()))
1314 return;
1315
1316 #if USE(LOW_BANDWIDTH_DISPLAY)
1317 // as switch will be called, don't complete yet
1318 if (m_frame->document() && m_frame->document()->inLowBandwidthDisplay() && m_needToSwitchOutLowBandwidthDisplay)
1319 return;
1320 #endif
1321
1322 // OK, completed.
1323 m_isComplete = true;
1324
1325 RefPtr<Frame> protect(m_frame);
1326 checkCallImplicitClose(); // if we didn't do it before
1327
1328 // Do not start a redirection timer for subframes here.
1329 // That is deferred until the parent is completed.
1330 if (m_scheduledRedirection && !m_frame->tree()->parent())
1331 startRedirectionTimer();
1332
1333 completed();
1334 if (m_frame->page())
1335 checkLoadComplete();
1336 }
1337
checkCompletedTimerFired(Timer<FrameLoader> *)1338 void FrameLoader::checkCompletedTimerFired(Timer<FrameLoader>*)
1339 {
1340 checkCompleted();
1341 }
1342
scheduleCheckCompleted()1343 void FrameLoader::scheduleCheckCompleted()
1344 {
1345 if (!m_checkCompletedTimer.isActive())
1346 m_checkCompletedTimer.startOneShot(0);
1347 }
1348
checkLoadCompleteTimerFired(Timer<FrameLoader> *)1349 void FrameLoader::checkLoadCompleteTimerFired(Timer<FrameLoader>*)
1350 {
1351 if (!m_frame->page())
1352 return;
1353 checkLoadComplete();
1354 }
1355
scheduleCheckLoadComplete()1356 void FrameLoader::scheduleCheckLoadComplete()
1357 {
1358 if (!m_checkLoadCompleteTimer.isActive())
1359 m_checkLoadCompleteTimer.startOneShot(0);
1360 }
1361
checkCallImplicitClose()1362 void FrameLoader::checkCallImplicitClose()
1363 {
1364 if (m_didCallImplicitClose || !m_frame->document() || m_frame->document()->parsing())
1365 return;
1366
1367 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
1368 if (!child->loader()->m_isComplete) // still got a frame running -> too early
1369 return;
1370
1371 m_didCallImplicitClose = true;
1372 m_wasUnloadEventEmitted = false;
1373 if (m_frame->document())
1374 m_frame->document()->implicitClose();
1375 }
1376
baseURL() const1377 KURL FrameLoader::baseURL() const
1378 {
1379 ASSERT(m_frame->document());
1380 return m_frame->document()->baseURL();
1381 }
1382
baseTarget() const1383 String FrameLoader::baseTarget() const
1384 {
1385 ASSERT(m_frame->document());
1386 return m_frame->document()->baseTarget();
1387 }
1388
completeURL(const String & url)1389 KURL FrameLoader::completeURL(const String& url)
1390 {
1391 ASSERT(m_frame->document());
1392 return m_frame->document()->completeURL(url);
1393 }
1394
scheduleHTTPRedirection(double delay,const String & url)1395 void FrameLoader::scheduleHTTPRedirection(double delay, const String& url)
1396 {
1397 if (delay < 0 || delay > INT_MAX / 1000)
1398 return;
1399
1400 if (!m_frame->page())
1401 return;
1402
1403 // We want a new history item if the refresh timeout is > 1 second.
1404 if (!m_scheduledRedirection || delay <= m_scheduledRedirection->delay)
1405 #ifdef ANDROID_USER_GESTURE
1406 {
1407 bool wasUserGesture = false;
1408 DocumentLoader* docLoader = activeDocumentLoader();
1409 if (docLoader)
1410 wasUserGesture = docLoader->request().userGesture();
1411 scheduleRedirection(new ScheduledRedirection(delay, url, true, delay <= 1, wasUserGesture, false));
1412 }
1413 #else
1414 scheduleRedirection(new ScheduledRedirection(delay, url, true, delay <= 1, false, false));
1415 #endif
1416 }
1417
scheduleLocationChange(const String & url,const String & referrer,bool lockHistory,bool lockBackForwardList,bool wasUserGesture)1418 void FrameLoader::scheduleLocationChange(const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool wasUserGesture)
1419 {
1420 if (!m_frame->page())
1421 return;
1422
1423 // If the URL we're going to navigate to is the same as the current one, except for the
1424 // fragment part, we don't need to schedule the location change.
1425 KURL parsedURL(url);
1426 if (parsedURL.hasRef() && equalIgnoringRef(m_URL, parsedURL)) {
1427 changeLocation(url, referrer, lockHistory, lockBackForwardList, wasUserGesture);
1428 return;
1429 }
1430
1431 // Handle a location change of a page with no document as a special case.
1432 // This may happen when a frame changes the location of another frame.
1433 bool duringLoad = !m_committedFirstRealDocumentLoad;
1434
1435 // If a redirect was scheduled during a load, then stop the current load.
1436 // Otherwise when the current load transitions from a provisional to a
1437 // committed state, pending redirects may be cancelled.
1438 if (duringLoad) {
1439 if (m_provisionalDocumentLoader)
1440 m_provisionalDocumentLoader->stopLoading();
1441 stopLoading(true);
1442 }
1443
1444 ScheduledRedirection::Type type = duringLoad
1445 ? ScheduledRedirection::locationChangeDuringLoad : ScheduledRedirection::locationChange;
1446 scheduleRedirection(new ScheduledRedirection(type, url, referrer, lockHistory, lockBackForwardList, wasUserGesture, false));
1447 }
1448
scheduleRefresh(bool wasUserGesture)1449 void FrameLoader::scheduleRefresh(bool wasUserGesture)
1450 {
1451 if (!m_frame->page())
1452 return;
1453
1454 // Handle a location change of a page with no document as a special case.
1455 // This may happen when a frame requests a refresh of another frame.
1456 bool duringLoad = !m_frame->document();
1457
1458 // If a refresh was scheduled during a load, then stop the current load.
1459 // Otherwise when the current load transitions from a provisional to a
1460 // committed state, pending redirects may be cancelled.
1461 if (duringLoad)
1462 stopLoading(true);
1463
1464 ScheduledRedirection::Type type = duringLoad
1465 ? ScheduledRedirection::locationChangeDuringLoad : ScheduledRedirection::locationChange;
1466 scheduleRedirection(new ScheduledRedirection(type, m_URL.string(), m_outgoingReferrer, true, true, wasUserGesture, true));
1467 }
1468
isLocationChange(const ScheduledRedirection & redirection)1469 bool FrameLoader::isLocationChange(const ScheduledRedirection& redirection)
1470 {
1471 switch (redirection.type) {
1472 case ScheduledRedirection::redirection:
1473 return false;
1474 case ScheduledRedirection::historyNavigation:
1475 case ScheduledRedirection::locationChange:
1476 case ScheduledRedirection::locationChangeDuringLoad:
1477 return true;
1478 }
1479 ASSERT_NOT_REACHED();
1480 return false;
1481 }
1482
scheduleHistoryNavigation(int steps)1483 void FrameLoader::scheduleHistoryNavigation(int steps)
1484 {
1485 if (!m_frame->page())
1486 return;
1487
1488 // navigation will always be allowed in the 0 steps case, which is OK because that's supposed to force a reload.
1489 if (!canGoBackOrForward(steps)) {
1490 cancelRedirection();
1491 return;
1492 }
1493
1494 // If the steps to navigate is not zero (which needs to force a reload), and if we think the navigation is going to be a fragment load
1495 // (when the URL we're going to navigate to is the same as the current one, except for the fragment part - but not exactly the same because that's a reload),
1496 // then we don't need to schedule the navigation.
1497 if (steps != 0) {
1498 KURL destination = historyURL(steps);
1499 // FIXME: This doesn't seem like a reliable way to tell whether or not the load will be a fragment load.
1500 if (equalIgnoringRef(m_URL, destination) && m_URL != destination) {
1501 goBackOrForward(steps);
1502 return;
1503 }
1504 }
1505
1506 scheduleRedirection(new ScheduledRedirection(steps));
1507 }
1508
goBackOrForward(int distance)1509 void FrameLoader::goBackOrForward(int distance)
1510 {
1511 if (distance == 0)
1512 return;
1513
1514 Page* page = m_frame->page();
1515 if (!page)
1516 return;
1517 BackForwardList* list = page->backForwardList();
1518 if (!list)
1519 return;
1520
1521 HistoryItem* item = list->itemAtIndex(distance);
1522 if (!item) {
1523 if (distance > 0) {
1524 int forwardListCount = list->forwardListCount();
1525 if (forwardListCount > 0)
1526 item = list->itemAtIndex(forwardListCount);
1527 } else {
1528 int backListCount = list->backListCount();
1529 if (backListCount > 0)
1530 item = list->itemAtIndex(-backListCount);
1531 }
1532 }
1533
1534 ASSERT(item); // we should not reach this line with an empty back/forward list
1535 if (item)
1536 page->goToItem(item, FrameLoadTypeIndexedBackForward);
1537 }
1538
redirectionTimerFired(Timer<FrameLoader> *)1539 void FrameLoader::redirectionTimerFired(Timer<FrameLoader>*)
1540 {
1541 ASSERT(m_frame->page());
1542
1543 OwnPtr<ScheduledRedirection> redirection(m_scheduledRedirection.release());
1544
1545 switch (redirection->type) {
1546 case ScheduledRedirection::redirection:
1547 case ScheduledRedirection::locationChange:
1548 case ScheduledRedirection::locationChangeDuringLoad:
1549 changeLocation(redirection->url, redirection->referrer,
1550 redirection->lockHistory, redirection->lockBackForwardList, redirection->wasUserGesture, redirection->wasRefresh);
1551 return;
1552 case ScheduledRedirection::historyNavigation:
1553 if (redirection->historySteps == 0) {
1554 // Special case for go(0) from a frame -> reload only the frame
1555 urlSelected(m_URL, "", 0, redirection->lockHistory, redirection->lockBackForwardList, redirection->wasUserGesture);
1556 return;
1557 }
1558 // go(i!=0) from a frame navigates into the history of the frame only,
1559 // in both IE and NS (but not in Mozilla). We can't easily do that.
1560 goBackOrForward(redirection->historySteps);
1561 return;
1562 }
1563
1564 ASSERT_NOT_REACHED();
1565 }
1566
1567 /*
1568 In the case of saving state about a page with frames, we store a tree of items that mirrors the frame tree.
1569 The item that was the target of the user's navigation is designated as the "targetItem".
1570 When this method is called with doClip=YES we're able to create the whole tree except for the target's children,
1571 which will be loaded in the future. That part of the tree will be filled out as the child loads are committed.
1572 */
loadURLIntoChildFrame(const KURL & url,const String & referer,Frame * childFrame)1573 void FrameLoader::loadURLIntoChildFrame(const KURL& url, const String& referer, Frame* childFrame)
1574 {
1575 ASSERT(childFrame);
1576 HistoryItem* parentItem = currentHistoryItem();
1577 FrameLoadType loadType = this->loadType();
1578 FrameLoadType childLoadType = FrameLoadTypeRedirectWithLockedBackForwardList;
1579
1580 KURL workingURL = url;
1581
1582 // If we're moving in the backforward list, we might want to replace the content
1583 // of this child frame with whatever was there at that point.
1584 if (parentItem && parentItem->children().size() && isBackForwardLoadType(loadType)) {
1585 HistoryItem* childItem = parentItem->childItemWithName(childFrame->tree()->name());
1586 if (childItem) {
1587 // Use the original URL to ensure we get all the side-effects, such as
1588 // onLoad handlers, of any redirects that happened. An example of where
1589 // this is needed is Radar 3213556.
1590 workingURL = KURL(childItem->originalURLString());
1591 childLoadType = loadType;
1592 childFrame->loader()->setProvisionalHistoryItem(childItem);
1593 }
1594 }
1595
1596 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
1597 RefPtr<Archive> subframeArchive = activeDocumentLoader()->popArchiveForSubframe(childFrame->tree()->name());
1598
1599 if (subframeArchive)
1600 childFrame->loader()->loadArchive(subframeArchive.release());
1601 else
1602 #endif
1603 #ifdef ANDROID_USER_GESTURE
1604 childFrame->loader()->loadURL(workingURL, referer, String(), false, childLoadType, 0, 0, false);
1605 #else
1606 childFrame->loader()->loadURL(workingURL, referer, String(), false, childLoadType, 0, 0);
1607 #endif
1608 }
1609
1610 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
loadArchive(PassRefPtr<Archive> prpArchive)1611 void FrameLoader::loadArchive(PassRefPtr<Archive> prpArchive)
1612 {
1613 RefPtr<Archive> archive = prpArchive;
1614
1615 ArchiveResource* mainResource = archive->mainResource();
1616 ASSERT(mainResource);
1617 if (!mainResource)
1618 return;
1619
1620 SubstituteData substituteData(mainResource->data(), mainResource->mimeType(), mainResource->textEncoding(), KURL());
1621
1622 ResourceRequest request(mainResource->url());
1623 #if PLATFORM(MAC)
1624 request.applyWebArchiveHackForMail();
1625 #endif
1626
1627 RefPtr<DocumentLoader> documentLoader = m_client->createDocumentLoader(request, substituteData);
1628 documentLoader->addAllArchiveResources(archive.get());
1629 load(documentLoader.get());
1630 }
1631 #endif
1632
encoding() const1633 String FrameLoader::encoding() const
1634 {
1635 if (m_encodingWasChosenByUser && !m_encoding.isEmpty())
1636 return m_encoding;
1637 if (m_decoder && m_decoder->encoding().isValid())
1638 return m_decoder->encoding().name();
1639 Settings* settings = m_frame->settings();
1640 return settings ? settings->defaultTextEncodingName() : String();
1641 }
1642
gotoAnchor(const String & name)1643 bool FrameLoader::gotoAnchor(const String& name)
1644 {
1645 ASSERT(m_frame->document());
1646
1647 if (!m_frame->document()->haveStylesheetsLoaded()) {
1648 m_frame->document()->setGotoAnchorNeededAfterStylesheetsLoad(true);
1649 return false;
1650 }
1651
1652 m_frame->document()->setGotoAnchorNeededAfterStylesheetsLoad(false);
1653
1654 Element* anchorNode = m_frame->document()->findAnchor(name);
1655
1656 #if ENABLE(SVG)
1657 if (m_frame->document()->isSVGDocument()) {
1658 if (name.startsWith("xpointer(")) {
1659 // We need to parse the xpointer reference here
1660 } else if (name.startsWith("svgView(")) {
1661 RefPtr<SVGSVGElement> svg = static_cast<SVGDocument*>(m_frame->document())->rootElement();
1662 if (!svg->currentView()->parseViewSpec(name))
1663 return false;
1664 svg->setUseCurrentView(true);
1665 } else {
1666 if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) {
1667 RefPtr<SVGViewElement> viewElement = anchorNode->hasTagName(SVGNames::viewTag) ? static_cast<SVGViewElement*>(anchorNode) : 0;
1668 if (viewElement.get()) {
1669 RefPtr<SVGSVGElement> svg = static_cast<SVGSVGElement*>(SVGLocatable::nearestViewportElement(viewElement.get()));
1670 svg->inheritViewAttributes(viewElement.get());
1671 }
1672 }
1673 }
1674 // FIXME: need to decide which <svg> to focus on, and zoom to that one
1675 // FIXME: need to actually "highlight" the viewTarget(s)
1676 }
1677 #endif
1678
1679 m_frame->document()->setCSSTarget(anchorNode); // Setting to null will clear the current target.
1680
1681 // Implement the rule that "" and "top" both mean top of page as in other browsers.
1682 if (!anchorNode && !(name.isEmpty() || equalIgnoringCase(name, "top")))
1683 return false;
1684
1685 // We need to update the layout before scrolling, otherwise we could
1686 // really mess things up if an anchor scroll comes at a bad moment.
1687 if (m_frame->document()) {
1688 m_frame->document()->updateRendering();
1689 // Only do a layout if changes have occurred that make it necessary.
1690 if (m_frame->view() && m_frame->contentRenderer() && m_frame->contentRenderer()->needsLayout())
1691 m_frame->view()->layout();
1692 }
1693
1694 // Scroll nested layers and frames to reveal the anchor.
1695 // Align to the top and to the closest side (this matches other browsers).
1696 RenderObject* renderer;
1697 IntRect rect;
1698 if (!anchorNode)
1699 renderer = m_frame->document()->renderer(); // top of document
1700 else {
1701 renderer = anchorNode->renderer();
1702 rect = anchorNode->getRect();
1703 }
1704 #ifdef ANDROID_SCROLL_ON_GOTO_ANCHOR
1705 android::WebFrame::getWebFrame(m_frame)->setUserInitiatedClick(true);
1706 #endif
1707 if (renderer)
1708 renderer->enclosingLayer()->scrollRectToVisible(rect, true, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignTopAlways);
1709 #ifdef ANDROID_SCROLL_ON_GOTO_ANCHOR
1710 android::WebFrame::getWebFrame(m_frame)->setUserInitiatedClick(false);
1711 #endif
1712 return true;
1713 }
1714
requestObject(RenderPart * renderer,const String & url,const AtomicString & frameName,const String & mimeType,const Vector<String> & paramNames,const Vector<String> & paramValues)1715 bool FrameLoader::requestObject(RenderPart* renderer, const String& url, const AtomicString& frameName,
1716 const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
1717 {
1718 if (url.isEmpty() && mimeType.isEmpty())
1719 return false;
1720
1721 #if USE(LOW_BANDWIDTH_DISPLAY)
1722 // don't care object during low bandwidth display
1723 if (frame()->document()->inLowBandwidthDisplay()) {
1724 m_needToSwitchOutLowBandwidthDisplay = true;
1725 return false;
1726 }
1727 #endif
1728
1729 KURL completedURL;
1730 if (!url.isEmpty())
1731 completedURL = completeURL(url);
1732
1733 bool useFallback;
1734 if (shouldUsePlugin(completedURL, mimeType, renderer->hasFallbackContent(), useFallback)) {
1735 Settings* settings = m_frame->settings();
1736 if (!settings || !settings->arePluginsEnabled() ||
1737 (!settings->isJavaEnabled() && MIMETypeRegistry::isJavaAppletMIMEType(mimeType)))
1738 return false;
1739 return loadPlugin(renderer, completedURL, mimeType, paramNames, paramValues, useFallback);
1740 }
1741
1742 ASSERT(renderer->node()->hasTagName(objectTag) || renderer->node()->hasTagName(embedTag));
1743 HTMLPlugInElement* element = static_cast<HTMLPlugInElement*>(renderer->node());
1744
1745 // FIXME: OK to always make a new frame? When does the old frame get removed?
1746 return loadSubframe(element, completedURL, frameName, m_outgoingReferrer);
1747 }
1748
shouldUsePlugin(const KURL & url,const String & mimeType,bool hasFallback,bool & useFallback)1749 bool FrameLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool hasFallback, bool& useFallback)
1750 {
1751 if (m_client->shouldUsePluginDocument(mimeType)) {
1752 useFallback = false;
1753 return true;
1754 }
1755
1756 // Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that
1757 // can handle TIFF (which QuickTime can also handle) they probably intended to override QT.
1758 if (m_frame->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) {
1759 const PluginData* pluginData = m_frame->page()->pluginData();
1760 String pluginName = pluginData ? pluginData->pluginNameForMimeType(mimeType) : String();
1761 if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false))
1762 return true;
1763 }
1764
1765 ObjectContentType objectType = m_client->objectContentType(url, mimeType);
1766 // If an object's content can't be handled and it has no fallback, let
1767 // it be handled as a plugin to show the broken plugin icon.
1768 useFallback = objectType == ObjectContentNone && hasFallback;
1769 return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin;
1770 }
1771
loadPlugin(RenderPart * renderer,const KURL & url,const String & mimeType,const Vector<String> & paramNames,const Vector<String> & paramValues,bool useFallback)1772 bool FrameLoader::loadPlugin(RenderPart* renderer, const KURL& url, const String& mimeType,
1773 const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
1774 {
1775 Widget* widget = 0;
1776
1777 if (renderer && !useFallback) {
1778 Element* pluginElement = 0;
1779 if (renderer->node() && renderer->node()->isElementNode())
1780 pluginElement = static_cast<Element*>(renderer->node());
1781
1782 if (!canLoad(url, String(), frame()->document())) {
1783 FrameLoader::reportLocalLoadFailed(m_frame, url.string());
1784 return false;
1785 }
1786
1787 widget = m_client->createPlugin(IntSize(renderer->contentWidth(), renderer->contentHeight()),
1788 pluginElement, url, paramNames, paramValues, mimeType,
1789 m_frame->document()->isPluginDocument());
1790 if (widget) {
1791 renderer->setWidget(widget);
1792 m_containsPlugIns = true;
1793 }
1794 }
1795
1796 return widget != 0;
1797 }
1798
clearRecordedFormValues()1799 void FrameLoader::clearRecordedFormValues()
1800 {
1801 m_formAboutToBeSubmitted = 0;
1802 m_formValuesAboutToBeSubmitted.clear();
1803 }
1804
setFormAboutToBeSubmitted(PassRefPtr<HTMLFormElement> element)1805 void FrameLoader::setFormAboutToBeSubmitted(PassRefPtr<HTMLFormElement> element)
1806 {
1807 m_formAboutToBeSubmitted = element;
1808 }
1809
recordFormValue(const String & name,const String & value)1810 void FrameLoader::recordFormValue(const String& name, const String& value)
1811 {
1812 m_formValuesAboutToBeSubmitted.set(name, value);
1813 }
1814
parentCompleted()1815 void FrameLoader::parentCompleted()
1816 {
1817 if (m_scheduledRedirection && !m_redirectionTimer.isActive())
1818 startRedirectionTimer();
1819 }
1820
outgoingReferrer() const1821 String FrameLoader::outgoingReferrer() const
1822 {
1823 return m_outgoingReferrer;
1824 }
1825
outgoingOrigin() const1826 String FrameLoader::outgoingOrigin() const
1827 {
1828 if (m_frame->document())
1829 return m_frame->document()->securityOrigin()->toString();
1830
1831 return SecurityOrigin::createEmpty()->toString();
1832 }
1833
opener()1834 Frame* FrameLoader::opener()
1835 {
1836 return m_opener;
1837 }
1838
setOpener(Frame * opener)1839 void FrameLoader::setOpener(Frame* opener)
1840 {
1841 if (m_opener)
1842 m_opener->loader()->m_openedFrames.remove(m_frame);
1843 if (opener)
1844 opener->loader()->m_openedFrames.add(m_frame);
1845 m_opener = opener;
1846
1847 if (m_frame->document()) {
1848 m_frame->document()->initSecurityContext();
1849 m_frame->domWindow()->setSecurityOrigin(m_frame->document()->securityOrigin());
1850 }
1851 }
1852
openedByDOM() const1853 bool FrameLoader::openedByDOM() const
1854 {
1855 return m_openedByDOM;
1856 }
1857
setOpenedByDOM()1858 void FrameLoader::setOpenedByDOM()
1859 {
1860 m_openedByDOM = true;
1861 }
1862
handleFallbackContent()1863 void FrameLoader::handleFallbackContent()
1864 {
1865 HTMLFrameOwnerElement* owner = m_frame->ownerElement();
1866 if (!owner || !owner->hasTagName(objectTag))
1867 return;
1868 static_cast<HTMLObjectElement*>(owner)->renderFallbackContent();
1869 }
1870
provisionalLoadStarted()1871 void FrameLoader::provisionalLoadStarted()
1872 {
1873 #ifdef ANDROID_INSTRUMENT
1874 if (!m_frame->tree()->parent())
1875 android::TimeCounter::reset();
1876 #endif
1877
1878 Page* page = m_frame->page();
1879
1880 // this is used to update the current history item
1881 // in the event of a navigation aytime during loading
1882 m_navigationDuringLoad = false;
1883 if (page) {
1884 Document *document = page->mainFrame()->document();
1885 m_navigationDuringLoad = !page->mainFrame()->loader()->isComplete() || (document && document->processingLoadEvent());
1886 }
1887
1888 m_firstLayoutDone = false;
1889 cancelRedirection(true);
1890 m_client->provisionalLoadStarted();
1891 }
1892
userGestureHint()1893 bool FrameLoader::userGestureHint()
1894 {
1895 Frame* frame = m_frame->tree()->top();
1896 if (!frame->script()->isEnabled())
1897 return true; // If JavaScript is disabled, a user gesture must have initiated the navigation.
1898 return frame->script()->processingUserGesture(); // FIXME: Use pageIsProcessingUserGesture.
1899 }
1900
didNotOpenURL(const KURL & url)1901 void FrameLoader::didNotOpenURL(const KURL& url)
1902 {
1903 if (m_submittedFormURL == url)
1904 m_submittedFormURL = KURL();
1905 }
1906
resetMultipleFormSubmissionProtection()1907 void FrameLoader::resetMultipleFormSubmissionProtection()
1908 {
1909 m_submittedFormURL = KURL();
1910 }
1911
setEncoding(const String & name,bool userChosen)1912 void FrameLoader::setEncoding(const String& name, bool userChosen)
1913 {
1914 if (!m_workingURL.isEmpty())
1915 receivedFirstData();
1916 m_encoding = name;
1917 m_encodingWasChosenByUser = userChosen;
1918 }
1919
addData(const char * bytes,int length)1920 void FrameLoader::addData(const char* bytes, int length)
1921 {
1922 ASSERT(m_workingURL.isEmpty());
1923 ASSERT(m_frame->document());
1924 ASSERT(m_frame->document()->parsing());
1925 write(bytes, length);
1926 }
1927
canCachePageContainingThisFrame()1928 bool FrameLoader::canCachePageContainingThisFrame()
1929 {
1930 return m_documentLoader
1931 && m_documentLoader->mainDocumentError().isNull()
1932 && !m_frame->tree()->childCount()
1933 // FIXME: If we ever change this so that frames with plug-ins will be cached,
1934 // we need to make sure that we don't cache frames that have outstanding NPObjects
1935 // (objects created by the plug-in). Since there is no way to pause/resume a Netscape plug-in,
1936 // they would need to be destroyed and then recreated, and there is no way that we can recreate
1937 // the right NPObjects. See <rdar://problem/5197041> for more information.
1938 && !m_containsPlugIns
1939 && !m_URL.protocolIs("https")
1940 && m_frame->document()
1941 && !m_frame->document()->hasWindowEventListener(eventNames().unloadEvent)
1942 #if ENABLE(DATABASE)
1943 && !m_frame->document()->hasOpenDatabases()
1944 #endif
1945 && !m_frame->document()->usingGeolocation()
1946 && m_currentHistoryItem
1947 && !isQuickRedirectComing()
1948 && !m_documentLoader->isLoadingInAPISense()
1949 && !m_documentLoader->isStopping()
1950 && m_frame->document()->canSuspendActiveDOMObjects()
1951 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
1952 // FIXME: We should investigating caching frames that have an associated
1953 // application cache. <rdar://problem/5917899> tracks that work.
1954 && !m_documentLoader->applicationCache()
1955 && !m_documentLoader->candidateApplicationCacheGroup()
1956 #endif
1957 && m_client->canCachePage()
1958 ;
1959 }
1960
canCachePage()1961 bool FrameLoader::canCachePage()
1962 {
1963 #ifndef NDEBUG
1964 logCanCachePageDecision();
1965 #endif
1966
1967 // Cache the page, if possible.
1968 // Don't write to the cache if in the middle of a redirect, since we will want to
1969 // store the final page we end up on.
1970 // No point writing to the cache on a reload or loadSame, since we will just write
1971 // over it again when we leave that page.
1972 // FIXME: <rdar://problem/4886592> - We should work out the complexities of caching pages with frames as they
1973 // are the most interesting pages on the web, and often those that would benefit the most from caching!
1974 FrameLoadType loadType = this->loadType();
1975
1976 return !m_frame->tree()->parent()
1977 && canCachePageContainingThisFrame()
1978 && m_frame->page()
1979 && m_frame->page()->backForwardList()->enabled()
1980 && m_frame->page()->backForwardList()->capacity() > 0
1981 && m_frame->page()->settings()->usesPageCache()
1982 && loadType != FrameLoadTypeReload
1983 && loadType != FrameLoadTypeReloadFromOrigin
1984 && loadType != FrameLoadTypeSame
1985 ;
1986 }
1987
1988 #ifndef NDEBUG
pageCacheLogPrefix(int indentLevel)1989 static String& pageCacheLogPrefix(int indentLevel)
1990 {
1991 static int previousIndent = -1;
1992 DEFINE_STATIC_LOCAL(String, prefix, ());
1993
1994 if (indentLevel != previousIndent) {
1995 previousIndent = indentLevel;
1996 prefix.truncate(0);
1997 for (int i = 0; i < previousIndent; ++i)
1998 prefix += " ";
1999 }
2000
2001 return prefix;
2002 }
2003
pageCacheLog(const String & prefix,const String & message)2004 static void pageCacheLog(const String& prefix, const String& message)
2005 {
2006 LOG(PageCache, "%s%s", prefix.utf8().data(), message.utf8().data());
2007 }
2008
2009 #define PCLOG(...) pageCacheLog(pageCacheLogPrefix(indentLevel), String::format(__VA_ARGS__))
2010
logCanCachePageDecision()2011 void FrameLoader::logCanCachePageDecision()
2012 {
2013 // Only bother logging for main frames that have actually loaded and have content.
2014 if (m_creatingInitialEmptyDocument)
2015 return;
2016 KURL currentURL = m_documentLoader ? m_documentLoader->url() : KURL();
2017 if (currentURL.isEmpty())
2018 return;
2019
2020 int indentLevel = 0;
2021 PCLOG("--------\n Determining if page can be cached:");
2022
2023 bool cannotCache = !logCanCacheFrameDecision(1);
2024
2025 FrameLoadType loadType = this->loadType();
2026 do {
2027 if (m_frame->tree()->parent())
2028 { PCLOG(" -Frame has a parent frame"); cannotCache = true; }
2029 if (!m_frame->page()) {
2030 PCLOG(" -There is no Page object");
2031 cannotCache = true;
2032 break;
2033 }
2034 if (!m_frame->page()->backForwardList()->enabled())
2035 { PCLOG(" -The back/forward list is disabled"); cannotCache = true; }
2036 if (!(m_frame->page()->backForwardList()->capacity() > 0))
2037 { PCLOG(" -The back/forward list has a 0 capacity"); cannotCache = true; }
2038 if (!m_frame->page()->settings()->usesPageCache())
2039 { PCLOG(" -Page settings says b/f cache disabled"); cannotCache = true; }
2040 if (loadType == FrameLoadTypeReload)
2041 { PCLOG(" -Load type is: Reload"); cannotCache = true; }
2042 if (loadType == FrameLoadTypeReloadFromOrigin)
2043 { PCLOG(" -Load type is: Reload from origin"); cannotCache = true; }
2044 if (loadType == FrameLoadTypeSame)
2045 { PCLOG(" -Load type is: Same"); cannotCache = true; }
2046 } while (false);
2047
2048 PCLOG(cannotCache ? " Page CANNOT be cached\n--------" : " Page CAN be cached\n--------");
2049 }
2050
logCanCacheFrameDecision(int indentLevel)2051 bool FrameLoader::logCanCacheFrameDecision(int indentLevel)
2052 {
2053 // Only bother logging for frames that have actually loaded and have content.
2054 if (m_creatingInitialEmptyDocument)
2055 return false;
2056 KURL currentURL = m_documentLoader ? m_documentLoader->url() : KURL();
2057 if (currentURL.isEmpty())
2058 return false;
2059
2060 PCLOG("+---");
2061 KURL newURL = m_provisionalDocumentLoader ? m_provisionalDocumentLoader->url() : KURL();
2062 if (!newURL.isEmpty())
2063 PCLOG(" Determining if frame can be cached navigating from (%s) to (%s):", currentURL.string().utf8().data(), newURL.string().utf8().data());
2064 else
2065 PCLOG(" Determining if subframe with URL (%s) can be cached:", currentURL.string().utf8().data());
2066
2067 bool cannotCache = false;
2068
2069 do {
2070 if (!m_documentLoader) {
2071 PCLOG(" -There is no DocumentLoader object");
2072 cannotCache = true;
2073 break;
2074 }
2075 if (!m_documentLoader->mainDocumentError().isNull())
2076 { PCLOG(" -Main document has an error"); cannotCache = true; }
2077 if (m_frame->tree()->childCount())
2078 { PCLOG(" -Frame has child frames"); cannotCache = true; }
2079 if (m_containsPlugIns)
2080 { PCLOG(" -Frame contains plugins"); cannotCache = true; }
2081 if (m_URL.protocolIs("https"))
2082 { PCLOG(" -Frame is HTTPS"); cannotCache = true; }
2083 if (!m_frame->document()) {
2084 PCLOG(" -There is no Document object");
2085 cannotCache = true;
2086 break;
2087 }
2088 if (m_frame->document()->hasWindowEventListener(eventNames().unloadEvent))
2089 { PCLOG(" -Frame has an unload event listener"); cannotCache = true; }
2090 #if ENABLE(DATABASE)
2091 if (m_frame->document()->hasOpenDatabases())
2092 { PCLOG(" -Frame has open database handles"); cannotCache = true; }
2093 #endif
2094 if (m_frame->document()->usingGeolocation())
2095 { PCLOG(" -Frame uses Geolocation"); cannotCache = true; }
2096 if (!m_currentHistoryItem)
2097 { PCLOG(" -No current history item"); cannotCache = true; }
2098 if (isQuickRedirectComing())
2099 { PCLOG(" -Quick redirect is coming"); cannotCache = true; }
2100 if (m_documentLoader->isLoadingInAPISense())
2101 { PCLOG(" -DocumentLoader is still loading in API sense"); cannotCache = true; }
2102 if (m_documentLoader->isStopping())
2103 { PCLOG(" -DocumentLoader is in the middle of stopping"); cannotCache = true; }
2104 if (!m_frame->document()->canSuspendActiveDOMObjects())
2105 { PCLOG(" -The document cannot suspect its active DOM Objects"); cannotCache = true; }
2106 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
2107 if (m_documentLoader->applicationCache())
2108 { PCLOG(" -The DocumentLoader has an active application cache"); cannotCache = true; }
2109 if (m_documentLoader->candidateApplicationCacheGroup())
2110 { PCLOG(" -The DocumentLoader has a candidateApplicationCacheGroup"); cannotCache = true; }
2111 #endif
2112 if (!m_client->canCachePage())
2113 { PCLOG(" -The client says this frame cannot be cached"); cannotCache = true; }
2114 } while (false);
2115
2116 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
2117 if (!child->loader()->logCanCacheFrameDecision(indentLevel + 1))
2118 cannotCache = true;
2119
2120 PCLOG(cannotCache ? " Frame CANNOT be cached" : " Frame CAN be cached");
2121 PCLOG("+---");
2122
2123 return !cannotCache;
2124 }
2125 #endif
2126
updatePolicyBaseURL()2127 void FrameLoader::updatePolicyBaseURL()
2128 {
2129 if (m_frame->tree()->parent() && m_frame->tree()->parent()->document())
2130 setPolicyBaseURL(m_frame->tree()->parent()->document()->policyBaseURL());
2131 else
2132 setPolicyBaseURL(m_URL);
2133 }
2134
setPolicyBaseURL(const KURL & url)2135 void FrameLoader::setPolicyBaseURL(const KURL& url)
2136 {
2137 if (m_frame->document())
2138 m_frame->document()->setPolicyBaseURL(url);
2139 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
2140 child->loader()->setPolicyBaseURL(url);
2141 }
2142
2143 // This does the same kind of work that didOpenURL does, except it relies on the fact
2144 // that a higher level already checked that the URLs match and the scrolling is the right thing to do.
scrollToAnchor(const KURL & url)2145 void FrameLoader::scrollToAnchor(const KURL& url)
2146 {
2147 m_URL = url;
2148 updateHistoryForAnchorScroll();
2149
2150 // If we were in the autoscroll/panScroll mode we want to stop it before following the link to the anchor
2151 m_frame->eventHandler()->stopAutoscrollTimer();
2152 started();
2153 gotoAnchor();
2154
2155 // It's important to model this as a load that starts and immediately finishes.
2156 // Otherwise, the parent frame may think we never finished loading.
2157 m_isComplete = false;
2158 checkCompleted();
2159 }
2160
isComplete() const2161 bool FrameLoader::isComplete() const
2162 {
2163 return m_isComplete;
2164 }
2165
scheduleRedirection(ScheduledRedirection * redirection)2166 void FrameLoader::scheduleRedirection(ScheduledRedirection* redirection)
2167 {
2168 ASSERT(m_frame->page());
2169
2170 stopRedirectionTimer();
2171 m_scheduledRedirection.set(redirection);
2172 if (!m_isComplete && redirection->type != ScheduledRedirection::redirection)
2173 completed();
2174 if (m_isComplete || redirection->type != ScheduledRedirection::redirection)
2175 startRedirectionTimer();
2176 }
2177
startRedirectionTimer()2178 void FrameLoader::startRedirectionTimer()
2179 {
2180 ASSERT(m_frame->page());
2181 ASSERT(m_scheduledRedirection);
2182
2183 m_redirectionTimer.stop();
2184 m_redirectionTimer.startOneShot(m_scheduledRedirection->delay);
2185
2186 switch (m_scheduledRedirection->type) {
2187 case ScheduledRedirection::redirection:
2188 case ScheduledRedirection::locationChange:
2189 case ScheduledRedirection::locationChangeDuringLoad:
2190 clientRedirected(KURL(m_scheduledRedirection->url),
2191 m_scheduledRedirection->delay,
2192 currentTime() + m_redirectionTimer.nextFireInterval(),
2193 m_scheduledRedirection->lockBackForwardList,
2194 m_isExecutingJavaScriptFormAction);
2195 return;
2196 case ScheduledRedirection::historyNavigation:
2197 // Don't report history navigations.
2198 return;
2199 }
2200 ASSERT_NOT_REACHED();
2201 }
2202
stopRedirectionTimer()2203 void FrameLoader::stopRedirectionTimer()
2204 {
2205 if (!m_redirectionTimer.isActive())
2206 return;
2207
2208 m_redirectionTimer.stop();
2209
2210 if (m_scheduledRedirection) {
2211 switch (m_scheduledRedirection->type) {
2212 case ScheduledRedirection::redirection:
2213 case ScheduledRedirection::locationChange:
2214 case ScheduledRedirection::locationChangeDuringLoad:
2215 clientRedirectCancelledOrFinished(m_cancellingWithLoadInProgress);
2216 return;
2217 case ScheduledRedirection::historyNavigation:
2218 // Don't report history navigations.
2219 return;
2220 }
2221 ASSERT_NOT_REACHED();
2222 }
2223 }
2224
completed()2225 void FrameLoader::completed()
2226 {
2227 RefPtr<Frame> protect(m_frame);
2228 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
2229 child->loader()->parentCompleted();
2230 if (Frame* parent = m_frame->tree()->parent())
2231 parent->loader()->checkCompleted();
2232 submitFormAgain();
2233 }
2234
started()2235 void FrameLoader::started()
2236 {
2237 for (Frame* frame = m_frame; frame; frame = frame->tree()->parent())
2238 frame->loader()->m_isComplete = false;
2239 }
2240
containsPlugins() const2241 bool FrameLoader::containsPlugins() const
2242 {
2243 return m_containsPlugIns;
2244 }
2245
prepareForLoadStart()2246 void FrameLoader::prepareForLoadStart()
2247 {
2248 if (Page* page = m_frame->page())
2249 page->progress()->progressStarted(m_frame);
2250 m_client->dispatchDidStartProvisionalLoad();
2251 }
2252
setupForReplace()2253 void FrameLoader::setupForReplace()
2254 {
2255 setState(FrameStateProvisional);
2256 m_provisionalDocumentLoader = m_documentLoader;
2257 m_documentLoader = 0;
2258 detachChildren();
2259 }
2260
setupForReplaceByMIMEType(const String & newMIMEType)2261 void FrameLoader::setupForReplaceByMIMEType(const String& newMIMEType)
2262 {
2263 activeDocumentLoader()->setupForReplaceByMIMEType(newMIMEType);
2264 }
2265
loadFrameRequestWithFormAndValues(const FrameLoadRequest & request,bool lockHistory,bool lockBackForwardList,Event * event,HTMLFormElement * submitForm,const HashMap<String,String> & formValues)2266 void FrameLoader::loadFrameRequestWithFormAndValues(const FrameLoadRequest& request, bool lockHistory, bool lockBackForwardList, Event* event,
2267 HTMLFormElement* submitForm, const HashMap<String, String>& formValues)
2268 {
2269 RefPtr<FormState> formState;
2270 if (submitForm)
2271 formState = FormState::create(submitForm, formValues, m_frame);
2272
2273 KURL url = request.resourceRequest().url();
2274
2275 String referrer;
2276 String argsReferrer = request.resourceRequest().httpReferrer();
2277 if (!argsReferrer.isEmpty())
2278 referrer = argsReferrer;
2279 else
2280 referrer = m_outgoingReferrer;
2281
2282 ASSERT(frame()->document());
2283 if (url.protocolIs("file")) {
2284 if (!canLoad(url, String(), frame()->document()) && !canLoad(url, referrer)) {
2285 FrameLoader::reportLocalLoadFailed(m_frame, url.string());
2286 return;
2287 }
2288 }
2289
2290 if (shouldHideReferrer(url, referrer))
2291 referrer = String();
2292
2293 FrameLoadType loadType;
2294 if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData)
2295 loadType = FrameLoadTypeReload;
2296 else if (lockBackForwardList)
2297 loadType = FrameLoadTypeRedirectWithLockedBackForwardList;
2298 else
2299 loadType = FrameLoadTypeStandard;
2300
2301 if (request.resourceRequest().httpMethod() == "POST")
2302 #ifdef ANDROID_USER_GESTURE
2303 loadPostRequest(request.resourceRequest(), referrer, request.frameName(), lockHistory, loadType, event, formState.release(), request.wasUserGesture());
2304 #else
2305 loadPostRequest(request.resourceRequest(), referrer, request.frameName(), lockHistory, loadType, event, formState.release());
2306 #endif
2307 else
2308 #ifdef ANDROID_USER_GESTURE
2309 loadURL(request.resourceRequest().url(), referrer, request.frameName(), lockHistory, loadType, event, formState.release(), request.wasUserGesture());
2310 #else
2311 loadURL(request.resourceRequest().url(), referrer, request.frameName(), lockHistory, loadType, event, formState.release());
2312 #endif
2313
2314 Frame* targetFrame = findFrameForNavigation(request.frameName());
2315 if (targetFrame && targetFrame != m_frame)
2316 if (Page* page = targetFrame->page())
2317 page->chrome()->focus();
2318 }
2319
2320 #ifdef ANDROID_USER_GESTURE
loadURL(const KURL & newURL,const String & referrer,const String & frameName,bool lockHistory,FrameLoadType newLoadType,Event * event,PassRefPtr<FormState> prpFormState,bool userGesture)2321 void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType newLoadType,
2322 Event* event, PassRefPtr<FormState> prpFormState, bool userGesture)
2323 #else
2324 void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType newLoadType,
2325 Event* event, PassRefPtr<FormState> prpFormState)
2326 #endif
2327 {
2328 RefPtr<FormState> formState = prpFormState;
2329 bool isFormSubmission = formState;
2330
2331 ResourceRequest request(newURL);
2332 #ifdef ANDROID_USER_GESTURE
2333 request.setUserGesture(userGesture);
2334 #endif
2335 if (!referrer.isEmpty()) {
2336 request.setHTTPReferrer(referrer);
2337 RefPtr<SecurityOrigin> referrerOrigin = SecurityOrigin::createFromString(referrer);
2338 addHTTPOriginIfNeeded(request, referrerOrigin->toString());
2339 }
2340 addExtraFieldsToRequest(request, newLoadType, true, event || isFormSubmission);
2341 if (newLoadType == FrameLoadTypeReload || newLoadType == FrameLoadTypeReloadFromOrigin)
2342 request.setCachePolicy(ReloadIgnoringCacheData);
2343
2344 ASSERT(newLoadType != FrameLoadTypeSame);
2345
2346 NavigationAction action(newURL, newLoadType, isFormSubmission, event);
2347
2348 if (!frameName.isEmpty()) {
2349 if (Frame* targetFrame = findFrameForNavigation(frameName))
2350 #ifdef ANDROID_USER_GESTURE
2351 targetFrame->loader()->loadURL(newURL, referrer, String(), lockHistory, newLoadType, event, formState, userGesture);
2352 #else
2353 targetFrame->loader()->loadURL(newURL, referrer, String(), lockHistory, newLoadType, event, formState);
2354 #endif
2355 else
2356 checkNewWindowPolicy(action, request, formState, frameName);
2357 return;
2358 }
2359
2360 RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader;
2361
2362 bool sameURL = shouldTreatURLAsSameAsCurrent(newURL);
2363
2364 // Make sure to do scroll to anchor processing even if the URL is
2365 // exactly the same so pages with '#' links and DHTML side effects
2366 // work properly.
2367 if (shouldScrollToAnchor(isFormSubmission, newLoadType, newURL)) {
2368 oldDocumentLoader->setTriggeringAction(action);
2369 stopPolicyCheck();
2370 checkNavigationPolicy(request, oldDocumentLoader.get(), formState,
2371 callContinueFragmentScrollAfterNavigationPolicy, this);
2372 } else {
2373 // must grab this now, since this load may stop the previous load and clear this flag
2374 bool isRedirect = m_quickRedirectComing;
2375 loadWithNavigationAction(request, action, lockHistory, newLoadType, formState);
2376 if (isRedirect) {
2377 m_quickRedirectComing = false;
2378 if (m_provisionalDocumentLoader)
2379 m_provisionalDocumentLoader->setIsClientRedirect(true);
2380 } else if (sameURL)
2381 // Example of this case are sites that reload the same URL with a different cookie
2382 // driving the generated content, or a master frame with links that drive a target
2383 // frame, where the user has clicked on the same link repeatedly.
2384 m_loadType = FrameLoadTypeSame;
2385 }
2386 }
2387
load(const ResourceRequest & request,bool lockHistory)2388 void FrameLoader::load(const ResourceRequest& request, bool lockHistory)
2389 {
2390 load(request, SubstituteData(), lockHistory);
2391 }
2392
load(const ResourceRequest & request,const SubstituteData & substituteData,bool lockHistory)2393 void FrameLoader::load(const ResourceRequest& request, const SubstituteData& substituteData, bool lockHistory)
2394 {
2395 if (m_inStopAllLoaders)
2396 return;
2397
2398 // FIXME: is this the right place to reset loadType? Perhaps this should be done after loading is finished or aborted.
2399 m_loadType = FrameLoadTypeStandard;
2400 RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, substituteData);
2401 loader->setURLForHistoryReflectsClientRedirect(lockHistory);
2402 load(loader.get());
2403 }
2404
load(const ResourceRequest & request,const String & frameName,bool lockHistory)2405 void FrameLoader::load(const ResourceRequest& request, const String& frameName, bool lockHistory)
2406 {
2407 if (frameName.isEmpty()) {
2408 load(request, lockHistory);
2409 return;
2410 }
2411
2412 Frame* frame = findFrameForNavigation(frameName);
2413 if (frame) {
2414 frame->loader()->load(request, lockHistory);
2415 return;
2416 }
2417
2418 checkNewWindowPolicy(NavigationAction(request.url(), NavigationTypeOther), request, 0, frameName);
2419 }
2420
loadWithNavigationAction(const ResourceRequest & request,const NavigationAction & action,bool lockHistory,FrameLoadType type,PassRefPtr<FormState> formState)2421 void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, bool lockHistory, FrameLoadType type, PassRefPtr<FormState> formState)
2422 {
2423 RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData());
2424 loader->setURLForHistoryReflectsClientRedirect(lockHistory);
2425
2426 loader->setTriggeringAction(action);
2427 if (m_documentLoader)
2428 loader->setOverrideEncoding(m_documentLoader->overrideEncoding());
2429
2430 loadWithDocumentLoader(loader.get(), type, formState);
2431 }
2432
load(DocumentLoader * newDocumentLoader)2433 void FrameLoader::load(DocumentLoader* newDocumentLoader)
2434 {
2435 ResourceRequest& r = newDocumentLoader->request();
2436 addExtraFieldsToMainResourceRequest(r);
2437 FrameLoadType type;
2438
2439 if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->originalRequest().url())) {
2440 r.setCachePolicy(ReloadIgnoringCacheData);
2441 type = FrameLoadTypeSame;
2442 } else
2443 type = FrameLoadTypeStandard;
2444
2445 if (m_documentLoader)
2446 newDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding());
2447
2448 // When we loading alternate content for an unreachable URL that we're
2449 // visiting in the history list, we treat it as a reload so the history list
2450 // is appropriately maintained.
2451 //
2452 // FIXME: This seems like a dangerous overloading of the meaning of "FrameLoadTypeReload" ...
2453 // shouldn't a more explicit type of reload be defined, that means roughly
2454 // "load without affecting history" ?
2455 if (shouldReloadToHandleUnreachableURL(newDocumentLoader)) {
2456 ASSERT(type == FrameLoadTypeStandard);
2457 type = FrameLoadTypeReload;
2458 }
2459
2460 loadWithDocumentLoader(newDocumentLoader, type, 0);
2461 }
2462
loadWithDocumentLoader(DocumentLoader * loader,FrameLoadType type,PassRefPtr<FormState> prpFormState)2463 void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState)
2464 {
2465 ASSERT(m_client->hasWebView());
2466
2467 // Unfortunately the view must be non-nil, this is ultimately due
2468 // to parser requiring a FrameView. We should fix this dependency.
2469
2470 ASSERT(m_frame->view());
2471
2472 m_policyLoadType = type;
2473 RefPtr<FormState> formState = prpFormState;
2474 bool isFormSubmission = formState;
2475
2476 const KURL& newURL = loader->request().url();
2477
2478 if (shouldScrollToAnchor(isFormSubmission, m_policyLoadType, newURL)) {
2479 RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader;
2480 NavigationAction action(newURL, m_policyLoadType, isFormSubmission);
2481
2482 oldDocumentLoader->setTriggeringAction(action);
2483 stopPolicyCheck();
2484 checkNavigationPolicy(loader->request(), oldDocumentLoader.get(), formState,
2485 callContinueFragmentScrollAfterNavigationPolicy, this);
2486 } else {
2487 if (Frame* parent = m_frame->tree()->parent())
2488 loader->setOverrideEncoding(parent->loader()->documentLoader()->overrideEncoding());
2489
2490 stopPolicyCheck();
2491 setPolicyDocumentLoader(loader);
2492
2493 checkNavigationPolicy(loader->request(), loader, formState,
2494 callContinueLoadAfterNavigationPolicy, this);
2495 }
2496 }
2497
canLoad(const KURL & url,const String & referrer,const Document * doc)2498 bool FrameLoader::canLoad(const KURL& url, const String& referrer, const Document* doc)
2499 {
2500 // We can always load any URL that isn't considered local (e.g. http URLs)
2501 if (!shouldTreatURLAsLocal(url.string()))
2502 return true;
2503
2504 // If we were provided a document, we let its local file policy dictate the result,
2505 // otherwise we allow local loads only if the supplied referrer is also local.
2506 if (doc)
2507 return doc->securityOrigin()->canLoadLocalResources();
2508 else if (!referrer.isEmpty())
2509 return shouldTreatURLAsLocal(referrer);
2510 else
2511 return false;
2512 }
2513
reportLocalLoadFailed(Frame * frame,const String & url)2514 void FrameLoader::reportLocalLoadFailed(Frame* frame, const String& url)
2515 {
2516 ASSERT(!url.isEmpty());
2517 if (!frame)
2518 return;
2519
2520 frame->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, "Not allowed to load local resource: " + url, 0, String());
2521 }
2522
shouldHideReferrer(const KURL & url,const String & referrer)2523 bool FrameLoader::shouldHideReferrer(const KURL& url, const String& referrer)
2524 {
2525 bool referrerIsSecureURL = protocolIs(referrer, "https");
2526 bool referrerIsWebURL = referrerIsSecureURL || protocolIs(referrer, "http");
2527
2528 if (!referrerIsWebURL)
2529 return true;
2530
2531 if (!referrerIsSecureURL)
2532 return false;
2533
2534 bool URLIsSecureURL = url.protocolIs("https");
2535
2536 return !URLIsSecureURL;
2537 }
2538
initialRequest() const2539 const ResourceRequest& FrameLoader::initialRequest() const
2540 {
2541 return activeDocumentLoader()->originalRequest();
2542 }
2543
receivedData(const char * data,int length)2544 void FrameLoader::receivedData(const char* data, int length)
2545 {
2546 activeDocumentLoader()->receivedData(data, length);
2547 }
2548
handleUnimplementablePolicy(const ResourceError & error)2549 void FrameLoader::handleUnimplementablePolicy(const ResourceError& error)
2550 {
2551 m_delegateIsHandlingUnimplementablePolicy = true;
2552 m_client->dispatchUnableToImplementPolicy(error);
2553 m_delegateIsHandlingUnimplementablePolicy = false;
2554 }
2555
cannotShowMIMEType(const ResourceResponse & response)2556 void FrameLoader::cannotShowMIMEType(const ResourceResponse& response)
2557 {
2558 handleUnimplementablePolicy(m_client->cannotShowMIMETypeError(response));
2559 }
2560
interruptionForPolicyChangeError(const ResourceRequest & request)2561 ResourceError FrameLoader::interruptionForPolicyChangeError(const ResourceRequest& request)
2562 {
2563 return m_client->interruptForPolicyChangeError(request);
2564 }
2565
checkNavigationPolicy(const ResourceRequest & newRequest,NavigationPolicyDecisionFunction function,void * argument)2566 void FrameLoader::checkNavigationPolicy(const ResourceRequest& newRequest, NavigationPolicyDecisionFunction function, void* argument)
2567 {
2568 checkNavigationPolicy(newRequest, activeDocumentLoader(), 0, function, argument);
2569 }
2570
checkContentPolicy(const String & MIMEType,ContentPolicyDecisionFunction function,void * argument)2571 void FrameLoader::checkContentPolicy(const String& MIMEType, ContentPolicyDecisionFunction function, void* argument)
2572 {
2573 ASSERT(activeDocumentLoader());
2574
2575 // Always show content with valid substitute data.
2576 if (activeDocumentLoader()->substituteData().isValid()) {
2577 function(argument, PolicyUse);
2578 return;
2579 }
2580
2581 #if ENABLE(FTPDIR)
2582 // Respect the hidden FTP Directory Listing pref so it can be tested even if the policy delegate might otherwise disallow it
2583 Settings* settings = m_frame->settings();
2584 if (settings && settings->forceFTPDirectoryListings() && MIMEType == "application/x-ftp-directory") {
2585 function(argument, PolicyUse);
2586 return;
2587 }
2588 #endif
2589
2590 m_policyCheck.set(function, argument);
2591 m_client->dispatchDecidePolicyForMIMEType(&FrameLoader::continueAfterContentPolicy,
2592 MIMEType, activeDocumentLoader()->request());
2593 }
2594
shouldReloadToHandleUnreachableURL(DocumentLoader * docLoader)2595 bool FrameLoader::shouldReloadToHandleUnreachableURL(DocumentLoader* docLoader)
2596 {
2597 KURL unreachableURL = docLoader->unreachableURL();
2598
2599 if (unreachableURL.isEmpty())
2600 return false;
2601
2602 if (!isBackForwardLoadType(m_policyLoadType))
2603 return false;
2604
2605 // We only treat unreachableURLs specially during the delegate callbacks
2606 // for provisional load errors and navigation policy decisions. The former
2607 // case handles well-formed URLs that can't be loaded, and the latter
2608 // case handles malformed URLs and unknown schemes. Loading alternate content
2609 // at other times behaves like a standard load.
2610 DocumentLoader* compareDocumentLoader = 0;
2611 if (m_delegateIsDecidingNavigationPolicy || m_delegateIsHandlingUnimplementablePolicy)
2612 compareDocumentLoader = m_policyDocumentLoader.get();
2613 else if (m_delegateIsHandlingProvisionalLoadError)
2614 compareDocumentLoader = m_provisionalDocumentLoader.get();
2615
2616 return compareDocumentLoader && unreachableURL == compareDocumentLoader->request().url();
2617 }
2618
reloadWithOverrideEncoding(const String & encoding)2619 void FrameLoader::reloadWithOverrideEncoding(const String& encoding)
2620 {
2621 if (!m_documentLoader)
2622 return;
2623
2624 ResourceRequest request = m_documentLoader->request();
2625 KURL unreachableURL = m_documentLoader->unreachableURL();
2626 if (!unreachableURL.isEmpty())
2627 request.setURL(unreachableURL);
2628
2629 request.setCachePolicy(ReturnCacheDataElseLoad);
2630
2631 RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData());
2632 setPolicyDocumentLoader(loader.get());
2633
2634 loader->setOverrideEncoding(encoding);
2635
2636 loadWithDocumentLoader(loader.get(), FrameLoadTypeReload, 0);
2637 }
2638
reload(bool endToEndReload)2639 void FrameLoader::reload(bool endToEndReload)
2640 {
2641 if (!m_documentLoader)
2642 return;
2643
2644 // If a window is created by javascript, its main frame can have an empty but non-nil URL.
2645 // Reloading in this case will lose the current contents (see 4151001).
2646 if (m_documentLoader->request().url().isEmpty())
2647 return;
2648
2649 ResourceRequest initialRequest = m_documentLoader->request();
2650
2651 // Replace error-page URL with the URL we were trying to reach.
2652 KURL unreachableURL = m_documentLoader->unreachableURL();
2653 if (!unreachableURL.isEmpty())
2654 initialRequest.setURL(unreachableURL);
2655
2656 // Create a new document loader for the reload, this will become m_documentLoader eventually,
2657 // but first it has to be the "policy" document loader, and then the "provisional" document loader.
2658 RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(initialRequest, SubstituteData());
2659
2660 ResourceRequest& request = loader->request();
2661
2662 // FIXME: We don't have a mechanism to revalidate the main resource without reloading at the moment.
2663 request.setCachePolicy(ReloadIgnoringCacheData);
2664
2665 // If we're about to re-post, set up action so the application can warn the user.
2666 if (request.httpMethod() == "POST")
2667 loader->setTriggeringAction(NavigationAction(request.url(), NavigationTypeFormResubmitted));
2668
2669 loader->setOverrideEncoding(m_documentLoader->overrideEncoding());
2670
2671 loadWithDocumentLoader(loader.get(), endToEndReload ? FrameLoadTypeReloadFromOrigin : FrameLoadTypeReload, 0);
2672 }
2673
canAccessAncestor(const SecurityOrigin * activeSecurityOrigin,Frame * targetFrame)2674 static bool canAccessAncestor(const SecurityOrigin* activeSecurityOrigin, Frame* targetFrame)
2675 {
2676 // targetFrame can be NULL when we're trying to navigate a top-level frame
2677 // that has a NULL opener.
2678 if (!targetFrame)
2679 return false;
2680
2681 for (Frame* ancestorFrame = targetFrame; ancestorFrame; ancestorFrame = ancestorFrame->tree()->parent()) {
2682 Document* ancestorDocument = ancestorFrame->document();
2683 if (!ancestorDocument)
2684 return true;
2685
2686 const SecurityOrigin* ancestorSecurityOrigin = ancestorDocument->securityOrigin();
2687 if (activeSecurityOrigin->canAccess(ancestorSecurityOrigin))
2688 return true;
2689 }
2690
2691 return false;
2692 }
2693
shouldAllowNavigation(Frame * targetFrame) const2694 bool FrameLoader::shouldAllowNavigation(Frame* targetFrame) const
2695 {
2696 // The navigation change is safe if the active frame is:
2697 // - in the same security origin as the target or one of the target's
2698 // ancestors.
2699 //
2700 // Or the target frame is:
2701 // - a top-level frame in the frame hierarchy and the active frame can
2702 // navigate the target frame's opener per above.
2703
2704 if (!targetFrame)
2705 return true;
2706
2707 // Performance optimization.
2708 if (m_frame == targetFrame)
2709 return true;
2710
2711 // Let a frame navigate the top-level window that contains it. This is
2712 // important to allow because it lets a site "frame-bust" (escape from a
2713 // frame created by another web site).
2714 if (targetFrame == m_frame->tree()->top())
2715 return true;
2716
2717 Document* activeDocument = m_frame->document();
2718 ASSERT(activeDocument);
2719 const SecurityOrigin* activeSecurityOrigin = activeDocument->securityOrigin();
2720
2721 // For top-level windows, check the opener.
2722 if (!targetFrame->tree()->parent() && canAccessAncestor(activeSecurityOrigin, targetFrame->loader()->opener()))
2723 return true;
2724
2725 // In general, check the frame's ancestors.
2726 if (canAccessAncestor(activeSecurityOrigin, targetFrame))
2727 return true;
2728
2729 Settings* settings = targetFrame->settings();
2730 if (settings && !settings->privateBrowsingEnabled()) {
2731 Document* targetDocument = targetFrame->document();
2732 // FIXME: this error message should contain more specifics of why the navigation change is not allowed.
2733 String message = String::format("Unsafe JavaScript attempt to initiate a navigation change for frame with URL %s from frame with URL %s.\n",
2734 targetDocument->url().string().utf8().data(), activeDocument->url().string().utf8().data());
2735
2736 // FIXME: should we print to the console of the activeFrame as well?
2737 targetFrame->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, message, 1, String());
2738 }
2739
2740 return false;
2741 }
2742
stopLoadingSubframes()2743 void FrameLoader::stopLoadingSubframes()
2744 {
2745 for (RefPtr<Frame> child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
2746 child->loader()->stopAllLoaders();
2747 }
2748
stopAllLoaders()2749 void FrameLoader::stopAllLoaders()
2750 {
2751 // If this method is called from within this method, infinite recursion can occur (3442218). Avoid this.
2752 if (m_inStopAllLoaders)
2753 return;
2754
2755 m_inStopAllLoaders = true;
2756
2757 stopPolicyCheck();
2758
2759 stopLoadingSubframes();
2760 if (m_provisionalDocumentLoader)
2761 m_provisionalDocumentLoader->stopLoading();
2762 if (m_documentLoader)
2763 m_documentLoader->stopLoading();
2764
2765 setProvisionalDocumentLoader(0);
2766
2767 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
2768 if (m_documentLoader)
2769 m_documentLoader->clearArchiveResources();
2770 #endif
2771
2772 m_inStopAllLoaders = false;
2773 }
2774
stopForUserCancel(bool deferCheckLoadComplete)2775 void FrameLoader::stopForUserCancel(bool deferCheckLoadComplete)
2776 {
2777 stopAllLoaders();
2778
2779 if (deferCheckLoadComplete)
2780 scheduleCheckLoadComplete();
2781 else if (m_frame->page())
2782 checkLoadComplete();
2783 }
2784
activeDocumentLoader() const2785 DocumentLoader* FrameLoader::activeDocumentLoader() const
2786 {
2787 if (m_state == FrameStateProvisional)
2788 return m_provisionalDocumentLoader.get();
2789 return m_documentLoader.get();
2790 }
2791
isLoading() const2792 bool FrameLoader::isLoading() const
2793 {
2794 DocumentLoader* docLoader = activeDocumentLoader();
2795 if (!docLoader)
2796 return false;
2797 return docLoader->isLoadingMainResource() || docLoader->isLoadingSubresources() || docLoader->isLoadingPlugIns();
2798 }
2799
frameHasLoaded() const2800 bool FrameLoader::frameHasLoaded() const
2801 {
2802 return m_committedFirstRealDocumentLoad || (m_provisionalDocumentLoader && !m_creatingInitialEmptyDocument);
2803 }
2804
setDocumentLoader(DocumentLoader * loader)2805 void FrameLoader::setDocumentLoader(DocumentLoader* loader)
2806 {
2807 if (!loader && !m_documentLoader)
2808 return;
2809
2810 ASSERT(loader != m_documentLoader);
2811 ASSERT(!loader || loader->frameLoader() == this);
2812
2813 m_client->prepareForDataSourceReplacement();
2814 detachChildren();
2815 if (m_documentLoader)
2816 m_documentLoader->detachFromFrame();
2817
2818 m_documentLoader = loader;
2819 }
2820
documentLoader() const2821 DocumentLoader* FrameLoader::documentLoader() const
2822 {
2823 return m_documentLoader.get();
2824 }
2825
setPolicyDocumentLoader(DocumentLoader * loader)2826 void FrameLoader::setPolicyDocumentLoader(DocumentLoader* loader)
2827 {
2828 if (m_policyDocumentLoader == loader)
2829 return;
2830
2831 ASSERT(m_frame);
2832 if (loader)
2833 loader->setFrame(m_frame);
2834 if (m_policyDocumentLoader
2835 && m_policyDocumentLoader != m_provisionalDocumentLoader
2836 && m_policyDocumentLoader != m_documentLoader)
2837 m_policyDocumentLoader->detachFromFrame();
2838
2839 m_policyDocumentLoader = loader;
2840 }
2841
policyDocumentLoader() const2842 DocumentLoader* FrameLoader::policyDocumentLoader() const
2843 {
2844 return m_policyDocumentLoader.get();
2845 }
2846
provisionalDocumentLoader() const2847 DocumentLoader* FrameLoader::provisionalDocumentLoader() const
2848 {
2849 return m_provisionalDocumentLoader.get();
2850 }
2851
setProvisionalDocumentLoader(DocumentLoader * loader)2852 void FrameLoader::setProvisionalDocumentLoader(DocumentLoader* loader)
2853 {
2854 ASSERT(!loader || !m_provisionalDocumentLoader);
2855 ASSERT(!loader || loader->frameLoader() == this);
2856
2857 if (m_provisionalDocumentLoader && m_provisionalDocumentLoader != m_documentLoader)
2858 m_provisionalDocumentLoader->detachFromFrame();
2859
2860 m_provisionalDocumentLoader = loader;
2861 }
2862
state() const2863 FrameState FrameLoader::state() const
2864 {
2865 return m_state;
2866 }
2867
timeOfLastCompletedLoad()2868 double FrameLoader::timeOfLastCompletedLoad()
2869 {
2870 return storedTimeOfLastCompletedLoad;
2871 }
2872
setState(FrameState newState)2873 void FrameLoader::setState(FrameState newState)
2874 {
2875 m_state = newState;
2876
2877 if (newState == FrameStateProvisional)
2878 provisionalLoadStarted();
2879 else if (newState == FrameStateComplete) {
2880 frameLoadCompleted();
2881 storedTimeOfLastCompletedLoad = currentTime();
2882 if (m_documentLoader)
2883 m_documentLoader->stopRecordingResponses();
2884 }
2885 }
2886
clearProvisionalLoad()2887 void FrameLoader::clearProvisionalLoad()
2888 {
2889 setProvisionalDocumentLoader(0);
2890 if (Page* page = m_frame->page())
2891 page->progress()->progressCompleted(m_frame);
2892 setState(FrameStateComplete);
2893 }
2894
markLoadComplete()2895 void FrameLoader::markLoadComplete()
2896 {
2897 setState(FrameStateComplete);
2898 }
2899
commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage)2900 void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage)
2901 {
2902 RefPtr<CachedPage> cachedPage = prpCachedPage;
2903 RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader;
2904
2905 LOG(Loading, "WebCoreLoading %s: About to commit provisional load from previous URL %s", m_frame->tree()->name().string().utf8().data(), m_URL.string().utf8().data());
2906
2907 // Check to see if we need to cache the page we are navigating away from into the back/forward cache.
2908 // We are doing this here because we know for sure that a new page is about to be loaded.
2909 if (canCachePage() && !m_currentHistoryItem->isInPageCache()) {
2910 if (Document* document = m_frame->document())
2911 document->suspendActiveDOMObjects();
2912 cachePageForHistoryItem(m_currentHistoryItem.get());
2913 }
2914
2915 if (m_loadType != FrameLoadTypeReplace)
2916 closeOldDataSources();
2917
2918 if (!cachedPage && !m_creatingInitialEmptyDocument)
2919 m_client->makeRepresentation(pdl.get());
2920
2921 transitionToCommitted(cachedPage);
2922
2923 // Call clientRedirectCancelledOrFinished() here so that the frame load delegate is notified that the redirect's
2924 // status has changed, if there was a redirect. The frame load delegate may have saved some state about
2925 // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:. Since we are
2926 // just about to commit a new page, there cannot possibly be a pending redirect at this point.
2927 if (m_sentRedirectNotification)
2928 clientRedirectCancelledOrFinished(false);
2929
2930 if (cachedPage && cachedPage->document()) {
2931 open(*cachedPage);
2932 cachedPage->clear();
2933 } else {
2934 KURL url = pdl->substituteData().responseURL();
2935 if (url.isEmpty())
2936 url = pdl->url();
2937 if (url.isEmpty())
2938 url = pdl->responseURL();
2939 if (url.isEmpty())
2940 url = blankURL();
2941
2942 didOpenURL(url);
2943 }
2944
2945 LOG(Loading, "WebCoreLoading %s: Finished committing provisional load to URL %s", m_frame->tree()->name().string().utf8().data(), m_URL.string().utf8().data());
2946
2947 opened();
2948 }
2949
transitionToCommitted(PassRefPtr<CachedPage> cachedPage)2950 void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage)
2951 {
2952 ASSERT(m_client->hasWebView());
2953 ASSERT(m_state == FrameStateProvisional);
2954
2955 if (m_state != FrameStateProvisional)
2956 return;
2957
2958 m_client->setCopiesOnScroll();
2959 updateHistoryForCommit();
2960
2961 // The call to closeURL() invokes the unload event handler, which can execute arbitrary
2962 // JavaScript. If the script initiates a new load, we need to abandon the current load,
2963 // or the two will stomp each other.
2964 DocumentLoader* pdl = m_provisionalDocumentLoader.get();
2965 if (m_documentLoader)
2966 closeURL();
2967 if (pdl != m_provisionalDocumentLoader)
2968 return;
2969
2970 // Nothing else can interupt this commit - set the Provisional->Committed transition in stone
2971 if (m_documentLoader)
2972 m_documentLoader->stopLoadingSubresources();
2973 if (m_documentLoader)
2974 m_documentLoader->stopLoadingPlugIns();
2975
2976 setDocumentLoader(m_provisionalDocumentLoader.get());
2977 setProvisionalDocumentLoader(0);
2978 setState(FrameStateCommittedPage);
2979
2980 // Handle adding the URL to the back/forward list.
2981 DocumentLoader* dl = m_documentLoader.get();
2982 String ptitle = dl->title();
2983
2984 switch (m_loadType) {
2985 case FrameLoadTypeForward:
2986 case FrameLoadTypeBack:
2987 case FrameLoadTypeIndexedBackForward:
2988 if (Page* page = m_frame->page())
2989 if (page->backForwardList()) {
2990 updateHistoryForBackForwardNavigation();
2991
2992 // Create a document view for this document, or used the cached view.
2993 if (cachedPage) {
2994 DocumentLoader* cachedDocumentLoader = cachedPage->documentLoader();
2995 ASSERT(cachedDocumentLoader);
2996 cachedDocumentLoader->setFrame(m_frame);
2997 m_client->transitionToCommittedFromCachedFrame(cachedPage->cachedMainFrame());
2998
2999 } else
3000 m_client->transitionToCommittedForNewPage();
3001 }
3002 break;
3003
3004 case FrameLoadTypeReload:
3005 case FrameLoadTypeReloadFromOrigin:
3006 case FrameLoadTypeSame:
3007 case FrameLoadTypeReplace:
3008 updateHistoryForReload();
3009 m_client->transitionToCommittedForNewPage();
3010 break;
3011
3012 case FrameLoadTypeStandard:
3013 updateHistoryForStandardLoad();
3014 #ifndef BUILDING_ON_TIGER
3015 // This code was originally added for a Leopard performance imporvement. We decided to
3016 // ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>.
3017 if (m_frame->view())
3018 m_frame->view()->setScrollbarsSuppressed(true);
3019 #endif
3020 m_client->transitionToCommittedForNewPage();
3021 break;
3022
3023 case FrameLoadTypeRedirectWithLockedBackForwardList:
3024 updateHistoryForRedirectWithLockedBackForwardList();
3025 m_client->transitionToCommittedForNewPage();
3026 break;
3027
3028 // FIXME Remove this check when dummy ds is removed (whatever "dummy ds" is).
3029 // An exception should be thrown if we're in the FrameLoadTypeUninitialized state.
3030 default:
3031 ASSERT_NOT_REACHED();
3032 }
3033
3034 m_responseMIMEType = dl->responseMIMEType();
3035
3036 // Tell the client we've committed this URL.
3037 ASSERT(m_frame->view());
3038
3039 if (m_creatingInitialEmptyDocument)
3040 return;
3041
3042 m_committedFirstRealDocumentLoad = true;
3043
3044 // For non-cached HTML pages, these methods are called in FrameLoader::begin.
3045 if (cachedPage || !m_client->hasHTMLView()) {
3046 dispatchDidCommitLoad();
3047
3048 // If we have a title let the WebView know about it.
3049 if (!ptitle.isNull())
3050 m_client->dispatchDidReceiveTitle(ptitle);
3051 }
3052 }
3053
clientRedirectCancelledOrFinished(bool cancelWithLoadInProgress)3054 void FrameLoader::clientRedirectCancelledOrFinished(bool cancelWithLoadInProgress)
3055 {
3056 // Note that -webView:didCancelClientRedirectForFrame: is called on the frame load delegate even if
3057 // the redirect succeeded. We should either rename this API, or add a new method, like
3058 // -webView:didFinishClientRedirectForFrame:
3059 m_client->dispatchDidCancelClientRedirect();
3060
3061 if (!cancelWithLoadInProgress)
3062 m_quickRedirectComing = false;
3063
3064 m_sentRedirectNotification = false;
3065 }
3066
clientRedirected(const KURL & url,double seconds,double fireDate,bool lockBackForwardList,bool isJavaScriptFormAction)3067 void FrameLoader::clientRedirected(const KURL& url, double seconds, double fireDate, bool lockBackForwardList, bool isJavaScriptFormAction)
3068 {
3069 m_client->dispatchWillPerformClientRedirect(url, seconds, fireDate);
3070
3071 // Remember that we sent a redirect notification to the frame load delegate so that when we commit
3072 // the next provisional load, we can send a corresponding -webView:didCancelClientRedirectForFrame:
3073 m_sentRedirectNotification = true;
3074
3075 // If a "quick" redirect comes in an, we set a special mode so we treat the next
3076 // load as part of the same navigation. If we don't have a document loader, we have
3077 // no "original" load on which to base a redirect, so we treat the redirect as a normal load.
3078 m_quickRedirectComing = lockBackForwardList && m_documentLoader && !isJavaScriptFormAction;
3079 }
3080
3081 #if ENABLE(WML)
setForceReloadWmlDeck(bool reload)3082 void FrameLoader::setForceReloadWmlDeck(bool reload)
3083 {
3084 m_forceReloadWmlDeck = reload;
3085 }
3086 #endif
3087
shouldReload(const KURL & currentURL,const KURL & destinationURL)3088 bool FrameLoader::shouldReload(const KURL& currentURL, const KURL& destinationURL)
3089 {
3090 #if ENABLE(WML)
3091 // As for WML deck, sometimes it's supposed to be reloaded even if the same URL with fragment
3092 if (m_forceReloadWmlDeck)
3093 return true;
3094 #endif
3095
3096 // This function implements the rule: "Don't reload if navigating by fragment within
3097 // the same URL, but do reload if going to a new URL or to the same URL with no
3098 // fragment identifier at all."
3099 if (!destinationURL.hasRef())
3100 return true;
3101 return !equalIgnoringRef(currentURL, destinationURL);
3102 }
3103
closeOldDataSources()3104 void FrameLoader::closeOldDataSources()
3105 {
3106 // FIXME: Is it important for this traversal to be postorder instead of preorder?
3107 // If so, add helpers for postorder traversal, and use them. If not, then lets not
3108 // use a recursive algorithm here.
3109 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
3110 child->loader()->closeOldDataSources();
3111
3112 if (m_documentLoader)
3113 m_client->dispatchWillClose();
3114
3115 m_client->setMainFrameDocumentReady(false); // stop giving out the actual DOMDocument to observers
3116 }
3117
open(CachedPage & cachedPage)3118 void FrameLoader::open(CachedPage& cachedPage)
3119 {
3120 ASSERT(m_frame->page());
3121 ASSERT(m_frame->page()->mainFrame() == m_frame);
3122
3123 cancelRedirection();
3124
3125 // We still have to close the previous part page.
3126 closeURL();
3127
3128 m_isComplete = false;
3129
3130 // Don't re-emit the load event.
3131 m_didCallImplicitClose = true;
3132
3133 // Delete old status bar messages (if it _was_ activated on last URL).
3134 if (m_frame->script()->isEnabled()) {
3135 m_frame->setJSStatusBarText(String());
3136 m_frame->setJSDefaultStatusBarText(String());
3137 }
3138
3139 KURL url = cachedPage.url();
3140
3141 if ((url.protocolIs("http") || url.protocolIs("https")) && !url.host().isEmpty() && url.path().isEmpty())
3142 url.setPath("/");
3143
3144 m_URL = url;
3145 m_workingURL = url;
3146
3147 started();
3148
3149 clear();
3150
3151 Document* document = cachedPage.document();
3152 ASSERT(document);
3153 document->setInPageCache(false);
3154
3155 m_needsClear = true;
3156 m_isComplete = false;
3157 m_didCallImplicitClose = false;
3158 m_outgoingReferrer = url.string();
3159
3160 FrameView* view = cachedPage.view();
3161 if (view)
3162 view->setWasScrolledByUser(false);
3163 m_frame->setView(view);
3164
3165 m_frame->setDocument(document);
3166 m_frame->setDOMWindow(cachedPage.domWindow());
3167 m_frame->domWindow()->setURL(document->url());
3168 m_frame->domWindow()->setSecurityOrigin(document->securityOrigin());
3169
3170 m_decoder = document->decoder();
3171
3172 updatePolicyBaseURL();
3173
3174 cachedPage.restore(m_frame->page());
3175 document->resumeActiveDOMObjects();
3176
3177 // It is necessary to update any platform script objects after restoring the
3178 // cached page.
3179 m_frame->script()->updatePlatformScriptObjects();
3180
3181 checkCompleted();
3182 }
3183
isStopping() const3184 bool FrameLoader::isStopping() const
3185 {
3186 return activeDocumentLoader()->isStopping();
3187 }
3188
finishedLoading()3189 void FrameLoader::finishedLoading()
3190 {
3191 // Retain because the stop may release the last reference to it.
3192 RefPtr<Frame> protect(m_frame);
3193
3194 RefPtr<DocumentLoader> dl = activeDocumentLoader();
3195 dl->finishedLoading();
3196 if (!dl->mainDocumentError().isNull() || !dl->frameLoader())
3197 return;
3198 dl->setPrimaryLoadComplete(true);
3199 m_client->dispatchDidLoadMainResource(dl.get());
3200 checkLoadComplete();
3201 }
3202
isHostedByObjectElement() const3203 bool FrameLoader::isHostedByObjectElement() const
3204 {
3205 HTMLFrameOwnerElement* owner = m_frame->ownerElement();
3206 return owner && owner->hasTagName(objectTag);
3207 }
3208
isLoadingMainFrame() const3209 bool FrameLoader::isLoadingMainFrame() const
3210 {
3211 Page* page = m_frame->page();
3212 return page && m_frame == page->mainFrame();
3213 }
3214
canShowMIMEType(const String & MIMEType) const3215 bool FrameLoader::canShowMIMEType(const String& MIMEType) const
3216 {
3217 return m_client->canShowMIMEType(MIMEType);
3218 }
3219
representationExistsForURLScheme(const String & URLScheme)3220 bool FrameLoader::representationExistsForURLScheme(const String& URLScheme)
3221 {
3222 return m_client->representationExistsForURLScheme(URLScheme);
3223 }
3224
generatedMIMETypeForURLScheme(const String & URLScheme)3225 String FrameLoader::generatedMIMETypeForURLScheme(const String& URLScheme)
3226 {
3227 return m_client->generatedMIMETypeForURLScheme(URLScheme);
3228 }
3229
cancelContentPolicyCheck()3230 void FrameLoader::cancelContentPolicyCheck()
3231 {
3232 m_client->cancelPolicyCheck();
3233 m_policyCheck.clear();
3234 }
3235
didReceiveServerRedirectForProvisionalLoadForFrame()3236 void FrameLoader::didReceiveServerRedirectForProvisionalLoadForFrame()
3237 {
3238 m_client->dispatchDidReceiveServerRedirectForProvisionalLoad();
3239 }
3240
finishedLoadingDocument(DocumentLoader * loader)3241 void FrameLoader::finishedLoadingDocument(DocumentLoader* loader)
3242 {
3243 // FIXME: Platforms shouldn't differ here!
3244 #if PLATFORM(WIN) || PLATFORM(CHROMIUM) || defined(ANDROID)
3245 if (m_creatingInitialEmptyDocument)
3246 return;
3247 #endif
3248
3249 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
3250 // If loading a webarchive, run through webarchive machinery
3251 const String& responseMIMEType = loader->responseMIMEType();
3252
3253 // FIXME: Mac's FrameLoaderClient::finishedLoading() method does work that is required even with Archive loads
3254 // so we still need to call it. Other platforms should only call finishLoading for non-archive loads
3255 // That work should be factored out so this #ifdef can be removed
3256 #if PLATFORM(MAC)
3257 m_client->finishedLoading(loader);
3258 if (!ArchiveFactory::isArchiveMimeType(responseMIMEType))
3259 return;
3260 #else
3261 if (!ArchiveFactory::isArchiveMimeType(responseMIMEType)) {
3262 m_client->finishedLoading(loader);
3263 return;
3264 }
3265 #endif
3266
3267 RefPtr<Archive> archive(ArchiveFactory::create(loader->mainResourceData().get(), responseMIMEType));
3268 if (!archive)
3269 return;
3270
3271 loader->addAllArchiveResources(archive.get());
3272
3273 ArchiveResource* mainResource = archive->mainResource();
3274 loader->setParsedArchiveData(mainResource->data());
3275 continueLoadWithData(mainResource->data(), mainResource->mimeType(), mainResource->textEncoding(), mainResource->url());
3276 #else
3277 m_client->finishedLoading(loader);
3278 #endif
3279 }
3280
isReplacing() const3281 bool FrameLoader::isReplacing() const
3282 {
3283 return m_loadType == FrameLoadTypeReplace;
3284 }
3285
setReplacing()3286 void FrameLoader::setReplacing()
3287 {
3288 m_loadType = FrameLoadTypeReplace;
3289 }
3290
revertToProvisional(DocumentLoader * loader)3291 void FrameLoader::revertToProvisional(DocumentLoader* loader)
3292 {
3293 m_client->revertToProvisionalState(loader);
3294 }
3295
subframeIsLoading() const3296 bool FrameLoader::subframeIsLoading() const
3297 {
3298 // It's most likely that the last added frame is the last to load so we walk backwards.
3299 for (Frame* child = m_frame->tree()->lastChild(); child; child = child->tree()->previousSibling()) {
3300 FrameLoader* childLoader = child->loader();
3301 DocumentLoader* documentLoader = childLoader->documentLoader();
3302 if (documentLoader && documentLoader->isLoadingInAPISense())
3303 return true;
3304 documentLoader = childLoader->provisionalDocumentLoader();
3305 if (documentLoader && documentLoader->isLoadingInAPISense())
3306 return true;
3307 }
3308 return false;
3309 }
3310
willChangeTitle(DocumentLoader * loader)3311 void FrameLoader::willChangeTitle(DocumentLoader* loader)
3312 {
3313 m_client->willChangeTitle(loader);
3314 }
3315
loadType() const3316 FrameLoadType FrameLoader::loadType() const
3317 {
3318 return m_loadType;
3319 }
3320
cachePolicy() const3321 CachePolicy FrameLoader::cachePolicy() const
3322 {
3323 if (m_isComplete)
3324 return CachePolicyVerify;
3325
3326 if (m_loadType == FrameLoadTypeReloadFromOrigin)
3327 return CachePolicyReload;
3328
3329 if (Frame* parentFrame = m_frame->tree()->parent()) {
3330 CachePolicy parentCachePolicy = parentFrame->loader()->cachePolicy();
3331 if (parentCachePolicy != CachePolicyVerify)
3332 return parentCachePolicy;
3333 }
3334
3335 if (m_loadType == FrameLoadTypeReload)
3336 return CachePolicyRevalidate;
3337
3338 return CachePolicyVerify;
3339 }
3340
stopPolicyCheck()3341 void FrameLoader::stopPolicyCheck()
3342 {
3343 m_client->cancelPolicyCheck();
3344 PolicyCheck check = m_policyCheck;
3345 m_policyCheck.clear();
3346 check.cancel();
3347 }
3348
checkLoadCompleteForThisFrame()3349 void FrameLoader::checkLoadCompleteForThisFrame()
3350 {
3351 ASSERT(m_client->hasWebView());
3352
3353 switch (m_state) {
3354 case FrameStateProvisional: {
3355 if (m_delegateIsHandlingProvisionalLoadError)
3356 return;
3357
3358 RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader;
3359 if (!pdl)
3360 return;
3361
3362 // If we've received any errors we may be stuck in the provisional state and actually complete.
3363 const ResourceError& error = pdl->mainDocumentError();
3364 if (error.isNull())
3365 return;
3366
3367 // Check all children first.
3368 RefPtr<HistoryItem> item;
3369 if (Page* page = m_frame->page())
3370 if (isBackForwardLoadType(loadType()) && m_frame == page->mainFrame())
3371 item = m_currentHistoryItem;
3372
3373 bool shouldReset = true;
3374 if (!pdl->isLoadingInAPISense()) {
3375 m_delegateIsHandlingProvisionalLoadError = true;
3376 m_client->dispatchDidFailProvisionalLoad(error);
3377 m_delegateIsHandlingProvisionalLoadError = false;
3378
3379 // FIXME: can stopping loading here possibly have any effect, if isLoading is false,
3380 // which it must be to be in this branch of the if? And is it OK to just do a full-on
3381 // stopAllLoaders instead of stopLoadingSubframes?
3382 stopLoadingSubframes();
3383 pdl->stopLoading();
3384
3385 // Finish resetting the load state, but only if another load hasn't been started by the
3386 // delegate callback.
3387 if (pdl == m_provisionalDocumentLoader)
3388 clearProvisionalLoad();
3389 else if (m_provisionalDocumentLoader) {
3390 KURL unreachableURL = m_provisionalDocumentLoader->unreachableURL();
3391 if (!unreachableURL.isEmpty() && unreachableURL == pdl->request().url())
3392 shouldReset = false;
3393 }
3394 }
3395 if (shouldReset && item)
3396 if (Page* page = m_frame->page()) {
3397 page->backForwardList()->goToItem(item.get());
3398 Settings* settings = m_frame->settings();
3399 page->setGlobalHistoryItem((!settings || settings->privateBrowsingEnabled()) ? 0 : item.get());
3400 }
3401 return;
3402 }
3403
3404 case FrameStateCommittedPage: {
3405 DocumentLoader* dl = m_documentLoader.get();
3406 if (!dl || dl->isLoadingInAPISense())
3407 return;
3408
3409 markLoadComplete();
3410
3411 // FIXME: Is this subsequent work important if we already navigated away?
3412 // Maybe there are bugs because of that, or extra work we can skip because
3413 // the new page is ready.
3414
3415 m_client->forceLayoutForNonHTML();
3416
3417 // If the user had a scroll point, scroll to it, overriding the anchor point if any.
3418 if (Page* page = m_frame->page())
3419 if ((isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadTypeReload || m_loadType == FrameLoadTypeReloadFromOrigin) && page->backForwardList())
3420 restoreScrollPositionAndViewState();
3421
3422 if (m_creatingInitialEmptyDocument || !m_committedFirstRealDocumentLoad)
3423 return;
3424
3425 const ResourceError& error = dl->mainDocumentError();
3426 #ifndef NDEBUG
3427 m_didDispatchDidCommitLoad = false;
3428 #endif
3429 if (!error.isNull())
3430 m_client->dispatchDidFailLoad(error);
3431 else
3432 m_client->dispatchDidFinishLoad();
3433
3434 if (Page* page = m_frame->page())
3435 page->progress()->progressCompleted(m_frame);
3436
3437 #ifdef ANDROID_INSTRUMENT
3438 if (!m_frame->tree()->parent() && m_frame->document()->renderArena())
3439 android::TimeCounter::report(m_URL, cache()->getLiveSize(), cache()->getDeadSize(),
3440 m_frame->document()->renderArena()->reportPoolSize());
3441 #endif
3442 return;
3443 }
3444
3445 case FrameStateComplete:
3446 // Even if already complete, we might have set a previous item on a frame that
3447 // didn't do any data loading on the past transaction. Make sure to clear these out.
3448 m_client->frameLoadCompleted();
3449 return;
3450 }
3451
3452 ASSERT_NOT_REACHED();
3453 }
3454
continueAfterContentPolicy(PolicyAction policy)3455 void FrameLoader::continueAfterContentPolicy(PolicyAction policy)
3456 {
3457 PolicyCheck check = m_policyCheck;
3458 m_policyCheck.clear();
3459 check.call(policy);
3460 }
3461
continueLoadAfterWillSubmitForm(PolicyAction)3462 void FrameLoader::continueLoadAfterWillSubmitForm(PolicyAction)
3463 {
3464 if (!m_provisionalDocumentLoader)
3465 return;
3466
3467 // DocumentLoader calls back to our prepareForLoadStart
3468 m_provisionalDocumentLoader->prepareForLoadStart();
3469
3470 // The load might be cancelled inside of prepareForLoadStart(), nulling out the m_provisionalDocumentLoader,
3471 // so we need to null check it again.
3472 if (!m_provisionalDocumentLoader)
3473 return;
3474
3475 DocumentLoader* activeDocLoader = activeDocumentLoader();
3476 if (activeDocLoader && activeDocLoader->isLoadingMainResource())
3477 return;
3478
3479 m_provisionalDocumentLoader->setLoadingFromCachedPage(false);
3480
3481 unsigned long identifier = 0;
3482
3483 if (Page* page = m_frame->page()) {
3484 identifier = page->progress()->createUniqueIdentifier();
3485 dispatchAssignIdentifierToInitialRequest(identifier, m_provisionalDocumentLoader.get(), m_provisionalDocumentLoader->originalRequest());
3486 }
3487
3488 if (!m_provisionalDocumentLoader->startLoadingMainResource(identifier))
3489 m_provisionalDocumentLoader->updateLoading();
3490 }
3491
didFirstLayout()3492 void FrameLoader::didFirstLayout()
3493 {
3494 if (Page* page = m_frame->page())
3495 #ifdef ANDROID_HISTORY_CLIENT
3496 // this should match the logic in FrameStateCommittedPage, so that we
3497 // can restore the scroll position and view state as early as possible
3498 if ((isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadTypeReload) && page->backForwardList())
3499 #else
3500 if (isBackForwardLoadType(m_loadType) && page->backForwardList())
3501 #endif
3502 restoreScrollPositionAndViewState();
3503
3504 m_firstLayoutDone = true;
3505 m_client->dispatchDidFirstLayout();
3506 }
3507
didFirstVisuallyNonEmptyLayout()3508 void FrameLoader::didFirstVisuallyNonEmptyLayout()
3509 {
3510 m_client->dispatchDidFirstVisuallyNonEmptyLayout();
3511 }
3512
frameLoadCompleted()3513 void FrameLoader::frameLoadCompleted()
3514 {
3515 m_client->frameLoadCompleted();
3516
3517 // After a canceled provisional load, firstLayoutDone is false.
3518 // Reset it to true if we're displaying a page.
3519 if (m_documentLoader)
3520 m_firstLayoutDone = true;
3521 }
3522
firstLayoutDone() const3523 bool FrameLoader::firstLayoutDone() const
3524 {
3525 return m_firstLayoutDone;
3526 }
3527
isQuickRedirectComing() const3528 bool FrameLoader::isQuickRedirectComing() const
3529 {
3530 return m_quickRedirectComing;
3531 }
3532
detachChildren()3533 void FrameLoader::detachChildren()
3534 {
3535 // FIXME: Is it really necessary to do this in reverse order?
3536 Frame* previous;
3537 for (Frame* child = m_frame->tree()->lastChild(); child; child = previous) {
3538 previous = child->tree()->previousSibling();
3539 child->loader()->detachFromParent();
3540 }
3541 }
3542
recursiveCheckLoadComplete()3543 void FrameLoader::recursiveCheckLoadComplete()
3544 {
3545 Vector<RefPtr<Frame>, 10> frames;
3546
3547 for (RefPtr<Frame> frame = m_frame->tree()->firstChild(); frame; frame = frame->tree()->nextSibling())
3548 frames.append(frame);
3549
3550 unsigned size = frames.size();
3551 for (unsigned i = 0; i < size; i++)
3552 frames[i]->loader()->recursiveCheckLoadComplete();
3553
3554 checkLoadCompleteForThisFrame();
3555 }
3556
3557 // Called every time a resource is completely loaded, or an error is received.
checkLoadComplete()3558 void FrameLoader::checkLoadComplete()
3559 {
3560 ASSERT(m_client->hasWebView());
3561
3562 // FIXME: Always traversing the entire frame tree is a bit inefficient, but
3563 // is currently needed in order to null out the previous history item for all frames.
3564 if (Page* page = m_frame->page())
3565 page->mainFrame()->loader()->recursiveCheckLoadComplete();
3566 }
3567
numPendingOrLoadingRequests(bool recurse) const3568 int FrameLoader::numPendingOrLoadingRequests(bool recurse) const
3569 {
3570 if (!recurse)
3571 return numRequests(m_frame->document());
3572
3573 int count = 0;
3574 for (Frame* frame = m_frame; frame; frame = frame->tree()->traverseNext(m_frame))
3575 count += numRequests(frame->document());
3576 return count;
3577 }
3578
submitForm(const FrameLoadRequest & request,Event * event,bool lockHistory,bool lockBackForwardList)3579 void FrameLoader::submitForm(const FrameLoadRequest& request, Event* event, bool lockHistory, bool lockBackForwardList)
3580 {
3581 // FIXME: We'd like to remove this altogether and fix the multiple form submission issue another way.
3582 // We do not want to submit more than one form from the same page,
3583 // nor do we want to submit a single form more than once.
3584 // This flag prevents these from happening; not sure how other browsers prevent this.
3585 // The flag is reset in each time we start handle a new mouse or key down event, and
3586 // also in setView since this part may get reused for a page from the back/forward cache.
3587 // The form multi-submit logic here is only needed when we are submitting a form that affects this frame.
3588 // FIXME: Frame targeting is only one of the ways the submission could end up doing something other
3589 // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly
3590 // needed any more now that we reset m_submittedFormURL on each mouse or key down event.
3591 Frame* target = m_frame->tree()->find(request.frameName());
3592 if (m_frame->tree()->isDescendantOf(target)) {
3593 if (m_submittedFormURL == request.resourceRequest().url())
3594 return;
3595 m_submittedFormURL = request.resourceRequest().url();
3596 }
3597
3598 loadFrameRequestWithFormAndValues(request, lockHistory, lockBackForwardList, event, m_formAboutToBeSubmitted.get(), m_formValuesAboutToBeSubmitted);
3599
3600 clearRecordedFormValues();
3601 }
3602
userAgent(const KURL & url) const3603 String FrameLoader::userAgent(const KURL& url) const
3604 {
3605 return m_client->userAgent(url);
3606 }
3607
tokenizerProcessedData()3608 void FrameLoader::tokenizerProcessedData()
3609 {
3610 // ASSERT(m_frame->page());
3611 // ASSERT(m_frame->document());
3612
3613 checkCompleted();
3614 }
3615
handledOnloadEvents()3616 void FrameLoader::handledOnloadEvents()
3617 {
3618 m_client->dispatchDidHandleOnloadEvents();
3619 }
3620
frameDetached()3621 void FrameLoader::frameDetached()
3622 {
3623 stopAllLoaders();
3624 if (Document* document = m_frame->document())
3625 document->stopActiveDOMObjects();
3626 detachFromParent();
3627 }
3628
detachFromParent()3629 void FrameLoader::detachFromParent()
3630 {
3631 RefPtr<Frame> protect(m_frame);
3632
3633 closeURL();
3634 stopAllLoaders();
3635 saveScrollPositionAndViewStateToItem(currentHistoryItem());
3636 detachChildren();
3637
3638 if (Page* page = m_frame->page())
3639 page->inspectorController()->frameDetachedFromParent(m_frame);
3640
3641 m_client->detachedFromParent2();
3642 setDocumentLoader(0);
3643 m_client->detachedFromParent3();
3644 if (Frame* parent = m_frame->tree()->parent()) {
3645 parent->tree()->removeChild(m_frame);
3646 parent->loader()->scheduleCheckCompleted();
3647 } else {
3648 m_frame->setView(0);
3649 m_frame->pageDestroyed();
3650 }
3651 }
3652
addExtraFieldsToSubresourceRequest(ResourceRequest & request)3653 void FrameLoader::addExtraFieldsToSubresourceRequest(ResourceRequest& request)
3654 {
3655 addExtraFieldsToRequest(request, m_loadType, false, false);
3656 }
3657
addExtraFieldsToMainResourceRequest(ResourceRequest & request)3658 void FrameLoader::addExtraFieldsToMainResourceRequest(ResourceRequest& request)
3659 {
3660 addExtraFieldsToRequest(request, m_loadType, true, false);
3661 }
3662
addExtraFieldsToRequest(ResourceRequest & request,FrameLoadType loadType,bool mainResource,bool cookiePolicyURLFromRequest)3663 void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadType loadType, bool mainResource, bool cookiePolicyURLFromRequest)
3664 {
3665 applyUserAgent(request);
3666
3667 if (loadType == FrameLoadTypeReload) {
3668 request.setCachePolicy(ReloadIgnoringCacheData);
3669 request.setHTTPHeaderField("Cache-Control", "max-age=0");
3670 } else if (loadType == FrameLoadTypeReloadFromOrigin) {
3671 request.setCachePolicy(ReloadIgnoringCacheData);
3672 request.setHTTPHeaderField("Cache-Control", "no-cache");
3673 request.setHTTPHeaderField("Pragma", "no-cache");
3674 }
3675
3676 // Don't set the cookie policy URL if it's already been set.
3677 if (request.mainDocumentURL().isEmpty()) {
3678 if (mainResource && (isLoadingMainFrame() || cookiePolicyURLFromRequest))
3679 request.setMainDocumentURL(request.url());
3680 else if (Page* page = m_frame->page())
3681 request.setMainDocumentURL(page->mainFrame()->loader()->url());
3682 }
3683
3684 if (mainResource)
3685 request.setHTTPAccept("application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5");
3686
3687 // Make sure we send the Origin header.
3688 addHTTPOriginIfNeeded(request, String());
3689
3690 // Always try UTF-8. If that fails, try frame encoding (if any) and then the default.
3691 // For a newly opened frame with an empty URL, encoding() should not be used, because this methods asks decoder, which uses ISO-8859-1.
3692 Settings* settings = m_frame->settings();
3693 request.setResponseContentDispositionEncodingFallbackArray("UTF-8", m_URL.isEmpty() ? m_encoding : encoding(), settings ? settings->defaultTextEncodingName() : String());
3694 }
3695
addHTTPOriginIfNeeded(ResourceRequest & request,String origin)3696 void FrameLoader::addHTTPOriginIfNeeded(ResourceRequest& request, String origin)
3697 {
3698 if (!request.httpOrigin().isEmpty())
3699 return; // Request already has an Origin header.
3700
3701 // Don't send an Origin header for GET or HEAD to avoid privacy issues.
3702 // For example, if an intranet page has a hyperlink to an external web
3703 // site, we don't want to include the Origin of the request because it
3704 // will leak the internal host name. Similar privacy concerns have lead
3705 // to the widespread suppression of the Referer header at the network
3706 // layer.
3707 if (request.httpMethod() == "GET" || request.httpMethod() == "HEAD")
3708 return;
3709
3710 // For non-GET and non-HEAD methods, always send an Origin header so the
3711 // server knows we support this feature.
3712
3713 if (origin.isEmpty()) {
3714 // If we don't know what origin header to attach, we attach the value
3715 // for an empty origin.
3716 origin = SecurityOrigin::createEmpty()->toString();
3717 }
3718
3719 request.setHTTPOrigin(origin);
3720 }
3721
committedLoad(DocumentLoader * loader,const char * data,int length)3722 void FrameLoader::committedLoad(DocumentLoader* loader, const char* data, int length)
3723 {
3724 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
3725 if (ArchiveFactory::isArchiveMimeType(loader->response().mimeType()))
3726 return;
3727 #endif
3728 m_client->committedLoad(loader, data, length);
3729 }
3730
3731 #ifdef ANDROID_USER_GESTURE
loadPostRequest(const ResourceRequest & inRequest,const String & referrer,const String & frameName,bool lockHistory,FrameLoadType loadType,Event * event,PassRefPtr<FormState> prpFormState,bool userGesture)3732 void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType loadType, Event* event, PassRefPtr<FormState> prpFormState, bool userGesture)
3733 #else
3734 void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType loadType, Event* event, PassRefPtr<FormState> prpFormState)
3735 #endif
3736 {
3737 RefPtr<FormState> formState = prpFormState;
3738
3739 // When posting, use the NSURLRequestReloadIgnoringCacheData load flag.
3740 // This prevents a potential bug which may cause a page with a form that uses itself
3741 // as an action to be returned from the cache without submitting.
3742
3743 // FIXME: Where's the code that implements what the comment above says?
3744
3745 // Previously when this method was reached, the original FrameLoadRequest had been deconstructed to build a
3746 // bunch of parameters that would come in here and then be built back up to a ResourceRequest. In case
3747 // any caller depends on the immutability of the original ResourceRequest, I'm rebuilding a ResourceRequest
3748 // from scratch as it did all along.
3749 const KURL& url = inRequest.url();
3750 RefPtr<FormData> formData = inRequest.httpBody();
3751 const String& contentType = inRequest.httpContentType();
3752 String origin = inRequest.httpOrigin();
3753
3754 ResourceRequest workingResourceRequest(url);
3755 #ifdef ANDROID_USER_GESTURE
3756 workingResourceRequest.setUserGesture(userGesture);
3757 #endif
3758
3759 if (!referrer.isEmpty())
3760 workingResourceRequest.setHTTPReferrer(referrer);
3761 workingResourceRequest.setHTTPOrigin(origin);
3762 workingResourceRequest.setHTTPMethod("POST");
3763 workingResourceRequest.setHTTPBody(formData);
3764 workingResourceRequest.setHTTPContentType(contentType);
3765 addExtraFieldsToRequest(workingResourceRequest, loadType, true, true);
3766
3767 NavigationAction action(url, loadType, true, event);
3768
3769 if (!frameName.isEmpty()) {
3770 if (Frame* targetFrame = findFrameForNavigation(frameName))
3771 targetFrame->loader()->loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release());
3772 else
3773 checkNewWindowPolicy(action, workingResourceRequest, formState.release(), frameName);
3774 } else
3775 loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release());
3776 }
3777
loadEmptyDocumentSynchronously()3778 void FrameLoader::loadEmptyDocumentSynchronously()
3779 {
3780 ResourceRequest request(KURL(""));
3781 load(request, false);
3782 }
3783
loadResourceSynchronously(const ResourceRequest & request,ResourceError & error,ResourceResponse & response,Vector<char> & data)3784 unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data)
3785 {
3786 // Since this is a subresource, we can load any URL (we ignore the return value).
3787 // But we still want to know whether we should hide the referrer or not, so we call the canLoad method.
3788 String referrer = m_outgoingReferrer;
3789 if (shouldHideReferrer(request.url(), referrer))
3790 referrer = String();
3791
3792 ResourceRequest initialRequest = request;
3793 initialRequest.setTimeoutInterval(10);
3794
3795 if (initialRequest.isConditional())
3796 initialRequest.setCachePolicy(ReloadIgnoringCacheData);
3797 else
3798 initialRequest.setCachePolicy(documentLoader()->request().cachePolicy());
3799
3800 if (!referrer.isEmpty())
3801 initialRequest.setHTTPReferrer(referrer);
3802 addHTTPOriginIfNeeded(initialRequest, outgoingOrigin());
3803
3804 if (Page* page = m_frame->page())
3805 initialRequest.setMainDocumentURL(page->mainFrame()->loader()->documentLoader()->request().url());
3806 initialRequest.setHTTPUserAgent(client()->userAgent(request.url()));
3807
3808 unsigned long identifier = 0;
3809 ResourceRequest newRequest(initialRequest);
3810 requestFromDelegate(newRequest, identifier, error);
3811
3812 if (error.isNull()) {
3813 ASSERT(!newRequest.isNull());
3814
3815 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
3816 ApplicationCacheResource* resource;
3817 if (documentLoader()->shouldLoadResourceFromApplicationCache(newRequest, resource)) {
3818 if (resource) {
3819 response = resource->response();
3820 data.append(resource->data()->data(), resource->data()->size());
3821 } else
3822 error = cannotShowURLError(newRequest);
3823 } else {
3824 #endif
3825 ResourceHandle::loadResourceSynchronously(newRequest, error, response, data, m_frame);
3826
3827 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
3828 // If normal loading results in a redirect to a resource with another origin (indicative of a captive portal), or a 4xx or 5xx status code or equivalent,
3829 // or if there were network errors (but not if the user canceled the download), then instead get, from the cache, the resource of the fallback entry
3830 // corresponding to the matched namespace.
3831 if ((!error.isNull() && !error.isCancellation())
3832 || response.httpStatusCode() / 100 == 4 || response.httpStatusCode() / 100 == 5
3833 || !protocolHostAndPortAreEqual(newRequest.url(), response.url())) {
3834 if (documentLoader()->getApplicationCacheFallbackResource(newRequest, resource)) {
3835 response = resource->response();
3836 data.clear();
3837 data.append(resource->data()->data(), resource->data()->size());
3838 }
3839 }
3840 }
3841 #endif
3842 }
3843
3844 sendRemainingDelegateMessages(identifier, response, data.size(), error);
3845 return identifier;
3846 }
3847
assignIdentifierToInitialRequest(unsigned long identifier,const ResourceRequest & clientRequest)3848 void FrameLoader::assignIdentifierToInitialRequest(unsigned long identifier, const ResourceRequest& clientRequest)
3849 {
3850 return dispatchAssignIdentifierToInitialRequest(identifier, activeDocumentLoader(), clientRequest);
3851 }
3852
willSendRequest(ResourceLoader * loader,ResourceRequest & clientRequest,const ResourceResponse & redirectResponse)3853 void FrameLoader::willSendRequest(ResourceLoader* loader, ResourceRequest& clientRequest, const ResourceResponse& redirectResponse)
3854 {
3855 applyUserAgent(clientRequest);
3856 dispatchWillSendRequest(loader->documentLoader(), loader->identifier(), clientRequest, redirectResponse);
3857 }
3858
didReceiveResponse(ResourceLoader * loader,const ResourceResponse & r)3859 void FrameLoader::didReceiveResponse(ResourceLoader* loader, const ResourceResponse& r)
3860 {
3861 activeDocumentLoader()->addResponse(r);
3862
3863 if (Page* page = m_frame->page())
3864 page->progress()->incrementProgress(loader->identifier(), r);
3865 dispatchDidReceiveResponse(loader->documentLoader(), loader->identifier(), r);
3866 }
3867
didReceiveData(ResourceLoader * loader,const char * data,int length,int lengthReceived)3868 void FrameLoader::didReceiveData(ResourceLoader* loader, const char* data, int length, int lengthReceived)
3869 {
3870 if (Page* page = m_frame->page())
3871 page->progress()->incrementProgress(loader->identifier(), data, length);
3872 dispatchDidReceiveContentLength(loader->documentLoader(), loader->identifier(), lengthReceived);
3873 }
3874
didFailToLoad(ResourceLoader * loader,const ResourceError & error)3875 void FrameLoader::didFailToLoad(ResourceLoader* loader, const ResourceError& error)
3876 {
3877 if (Page* page = m_frame->page())
3878 page->progress()->completeProgress(loader->identifier());
3879 if (!error.isNull())
3880 m_client->dispatchDidFailLoading(loader->documentLoader(), loader->identifier(), error);
3881 }
3882
originalRequest() const3883 const ResourceRequest& FrameLoader::originalRequest() const
3884 {
3885 return activeDocumentLoader()->originalRequestCopy();
3886 }
3887
receivedMainResourceError(const ResourceError & error,bool isComplete)3888 void FrameLoader::receivedMainResourceError(const ResourceError& error, bool isComplete)
3889 {
3890 // Retain because the stop may release the last reference to it.
3891 RefPtr<Frame> protect(m_frame);
3892
3893 RefPtr<DocumentLoader> loader = activeDocumentLoader();
3894
3895 if (isComplete) {
3896 // FIXME: Don't want to do this if an entirely new load is going, so should check
3897 // that both data sources on the frame are either this or nil.
3898 stop();
3899 if (m_client->shouldFallBack(error))
3900 handleFallbackContent();
3901 }
3902
3903 if (m_state == FrameStateProvisional && m_provisionalDocumentLoader) {
3904 KURL failedURL = m_provisionalDocumentLoader->originalRequestCopy().url();
3905 didNotOpenURL(failedURL);
3906
3907 // We might have made a page cache item, but now we're bailing out due to an error before we ever
3908 // transitioned to the new page (before WebFrameState == commit). The goal here is to restore any state
3909 // so that the existing view (that wenever got far enough to replace) can continue being used.
3910 invalidateCurrentItemCachedPage();
3911
3912 // Call clientRedirectCancelledOrFinished here so that the frame load delegate is notified that the redirect's
3913 // status has changed, if there was a redirect. The frame load delegate may have saved some state about
3914 // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:. Since we are definitely
3915 // not going to use this provisional resource, as it was cancelled, notify the frame load delegate that the redirect
3916 // has ended.
3917 if (m_sentRedirectNotification)
3918 clientRedirectCancelledOrFinished(false);
3919 }
3920
3921
3922 loader->mainReceivedError(error, isComplete);
3923 }
3924
callContinueFragmentScrollAfterNavigationPolicy(void * argument,const ResourceRequest & request,PassRefPtr<FormState>,bool shouldContinue)3925 void FrameLoader::callContinueFragmentScrollAfterNavigationPolicy(void* argument,
3926 const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue)
3927 {
3928 FrameLoader* loader = static_cast<FrameLoader*>(argument);
3929 loader->continueFragmentScrollAfterNavigationPolicy(request, shouldContinue);
3930 }
3931
continueFragmentScrollAfterNavigationPolicy(const ResourceRequest & request,bool shouldContinue)3932 void FrameLoader::continueFragmentScrollAfterNavigationPolicy(const ResourceRequest& request, bool shouldContinue)
3933 {
3934 bool isRedirect = m_quickRedirectComing || m_policyLoadType == FrameLoadTypeRedirectWithLockedBackForwardList;
3935 m_quickRedirectComing = false;
3936
3937 if (!shouldContinue)
3938 return;
3939
3940 KURL url = request.url();
3941
3942 m_documentLoader->replaceRequestURLForAnchorScroll(url);
3943 if (!isRedirect && !shouldTreatURLAsSameAsCurrent(url)) {
3944 // NB: must happen after _setURL, since we add based on the current request.
3945 // Must also happen before we openURL and displace the scroll position, since
3946 // adding the BF item will save away scroll state.
3947
3948 // NB2: If we were loading a long, slow doc, and the user anchor nav'ed before
3949 // it was done, currItem is now set the that slow doc, and prevItem is whatever was
3950 // before it. Adding the b/f item will bump the slow doc down to prevItem, even
3951 // though its load is not yet done. I think this all works out OK, for one because
3952 // we have already saved away the scroll and doc state for the long slow load,
3953 // but it's not an obvious case.
3954
3955 addHistoryItemForFragmentScroll();
3956 }
3957
3958 scrollToAnchor(url);
3959
3960 if (!isRedirect)
3961 // This will clear previousItem from the rest of the frame tree that didn't
3962 // doing any loading. We need to make a pass on this now, since for anchor nav
3963 // we'll not go through a real load and reach Completed state.
3964 checkLoadComplete();
3965
3966 m_client->dispatchDidChangeLocationWithinPage();
3967 m_client->didFinishLoad();
3968 }
3969
shouldScrollToAnchor(bool isFormSubmission,FrameLoadType loadType,const KURL & url)3970 bool FrameLoader::shouldScrollToAnchor(bool isFormSubmission, FrameLoadType loadType, const KURL& url)
3971 {
3972 // Should we do anchor navigation within the existing content?
3973
3974 // We don't do this if we are submitting a form, explicitly reloading,
3975 // currently displaying a frameset, or if the URL does not have a fragment.
3976 // These rules were originally based on what KHTML was doing in KHTMLPart::openURL.
3977
3978 // FIXME: What about load types other than Standard and Reload?
3979
3980 return !isFormSubmission
3981 && loadType != FrameLoadTypeReload
3982 && loadType != FrameLoadTypeReloadFromOrigin
3983 && loadType != FrameLoadTypeSame
3984 && !shouldReload(this->url(), url)
3985 // We don't want to just scroll if a link from within a
3986 // frameset is trying to reload the frameset into _top.
3987 && !m_frame->isFrameSet();
3988 }
3989
opened()3990 void FrameLoader::opened()
3991 {
3992 if (m_loadType == FrameLoadTypeStandard && m_documentLoader->isClientRedirect())
3993 updateHistoryForClientRedirect();
3994
3995 if (m_documentLoader->isLoadingFromCachedPage()) {
3996 m_frame->document()->documentDidBecomeActive();
3997
3998 // Force a layout to update view size and thereby update scrollbars.
3999 m_client->forceLayout();
4000
4001 const ResponseVector& responses = m_documentLoader->responses();
4002 size_t count = responses.size();
4003 for (size_t i = 0; i < count; i++) {
4004 const ResourceResponse& response = responses[i];
4005 // FIXME: If the WebKit client changes or cancels the request, this is not respected.
4006 ResourceError error;
4007 unsigned long identifier;
4008 ResourceRequest request(response.url());
4009 requestFromDelegate(request, identifier, error);
4010 // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
4011 // However, with today's computers and networking speeds, this won't happen in practice.
4012 // Could be an issue with a giant local file.
4013 sendRemainingDelegateMessages(identifier, response, static_cast<int>(response.expectedContentLength()), error);
4014 }
4015
4016 pageCache()->remove(m_currentHistoryItem.get());
4017
4018 m_documentLoader->setPrimaryLoadComplete(true);
4019
4020 // FIXME: Why only this frame and not parent frames?
4021 checkLoadCompleteForThisFrame();
4022 }
4023 }
4024
checkNewWindowPolicy(const NavigationAction & action,const ResourceRequest & request,PassRefPtr<FormState> formState,const String & frameName)4025 void FrameLoader::checkNewWindowPolicy(const NavigationAction& action, const ResourceRequest& request,
4026 PassRefPtr<FormState> formState, const String& frameName)
4027 {
4028 m_policyCheck.set(request, formState, frameName,
4029 callContinueLoadAfterNewWindowPolicy, this);
4030 m_client->dispatchDecidePolicyForNewWindowAction(&FrameLoader::continueAfterNewWindowPolicy,
4031 action, request, formState, frameName);
4032 }
4033
continueAfterNewWindowPolicy(PolicyAction policy)4034 void FrameLoader::continueAfterNewWindowPolicy(PolicyAction policy)
4035 {
4036 PolicyCheck check = m_policyCheck;
4037 m_policyCheck.clear();
4038
4039 switch (policy) {
4040 case PolicyIgnore:
4041 check.clearRequest();
4042 break;
4043 case PolicyDownload:
4044 m_client->startDownload(check.request());
4045 check.clearRequest();
4046 break;
4047 case PolicyUse:
4048 break;
4049 }
4050
4051 check.call(policy == PolicyUse);
4052 }
4053
checkNavigationPolicy(const ResourceRequest & request,DocumentLoader * loader,PassRefPtr<FormState> formState,NavigationPolicyDecisionFunction function,void * argument)4054 void FrameLoader::checkNavigationPolicy(const ResourceRequest& request, DocumentLoader* loader,
4055 PassRefPtr<FormState> formState, NavigationPolicyDecisionFunction function, void* argument)
4056 {
4057 NavigationAction action = loader->triggeringAction();
4058 if (action.isEmpty()) {
4059 action = NavigationAction(request.url(), NavigationTypeOther);
4060 loader->setTriggeringAction(action);
4061 }
4062
4063 // Don't ask more than once for the same request or if we are loading an empty URL.
4064 // This avoids confusion on the part of the client.
4065 if (equalIgnoringHeaderFields(request, loader->lastCheckedRequest()) || (!request.isNull() && request.url().isEmpty())) {
4066 function(argument, request, 0, true);
4067 loader->setLastCheckedRequest(request);
4068 return;
4069 }
4070
4071 // We are always willing to show alternate content for unreachable URLs;
4072 // treat it like a reload so it maintains the right state for b/f list.
4073 if (loader->substituteData().isValid() && !loader->substituteData().failingURL().isEmpty()) {
4074 if (isBackForwardLoadType(m_policyLoadType))
4075 m_policyLoadType = FrameLoadTypeReload;
4076 function(argument, request, 0, true);
4077 return;
4078 }
4079
4080 loader->setLastCheckedRequest(request);
4081
4082 m_policyCheck.set(request, formState.get(), function, argument);
4083
4084 m_delegateIsDecidingNavigationPolicy = true;
4085 m_client->dispatchDecidePolicyForNavigationAction(&FrameLoader::continueAfterNavigationPolicy,
4086 action, request, formState);
4087 m_delegateIsDecidingNavigationPolicy = false;
4088 }
4089
continueAfterNavigationPolicy(PolicyAction policy)4090 void FrameLoader::continueAfterNavigationPolicy(PolicyAction policy)
4091 {
4092 PolicyCheck check = m_policyCheck;
4093 m_policyCheck.clear();
4094
4095 bool shouldContinue = policy == PolicyUse;
4096
4097 switch (policy) {
4098 case PolicyIgnore:
4099 check.clearRequest();
4100 break;
4101 case PolicyDownload:
4102 m_client->startDownload(check.request());
4103 check.clearRequest();
4104 break;
4105 case PolicyUse: {
4106 ResourceRequest request(check.request());
4107
4108 if (!m_client->canHandleRequest(request)) {
4109 handleUnimplementablePolicy(m_client->cannotShowURLError(check.request()));
4110 check.clearRequest();
4111 shouldContinue = false;
4112 }
4113 break;
4114 }
4115 }
4116
4117 check.call(shouldContinue);
4118 }
4119
callContinueLoadAfterNavigationPolicy(void * argument,const ResourceRequest & request,PassRefPtr<FormState> formState,bool shouldContinue)4120 void FrameLoader::callContinueLoadAfterNavigationPolicy(void* argument,
4121 const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue)
4122 {
4123 FrameLoader* loader = static_cast<FrameLoader*>(argument);
4124 loader->continueLoadAfterNavigationPolicy(request, formState, shouldContinue);
4125 }
4126
continueLoadAfterNavigationPolicy(const ResourceRequest &,PassRefPtr<FormState> formState,bool shouldContinue)4127 void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState> formState, bool shouldContinue)
4128 {
4129 // If we loaded an alternate page to replace an unreachableURL, we'll get in here with a
4130 // nil policyDataSource because loading the alternate page will have passed
4131 // through this method already, nested; otherwise, policyDataSource should still be set.
4132 ASSERT(m_policyDocumentLoader || !m_provisionalDocumentLoader->unreachableURL().isEmpty());
4133
4134 bool isTargetItem = m_provisionalHistoryItem ? m_provisionalHistoryItem->isTargetItem() : false;
4135
4136 // Two reasons we can't continue:
4137 // 1) Navigation policy delegate said we can't so request is nil. A primary case of this
4138 // is the user responding Cancel to the form repost nag sheet.
4139 // 2) User responded Cancel to an alert popped up by the before unload event handler.
4140 // The "before unload" event handler runs only for the main frame.
4141 bool canContinue = shouldContinue && (!isLoadingMainFrame() || m_frame->shouldClose());
4142
4143 if (!canContinue) {
4144 // If we were waiting for a quick redirect, but the policy delegate decided to ignore it, then we
4145 // need to report that the client redirect was cancelled.
4146 if (m_quickRedirectComing)
4147 clientRedirectCancelledOrFinished(false);
4148
4149 setPolicyDocumentLoader(0);
4150
4151 // If the navigation request came from the back/forward menu, and we punt on it, we have the
4152 // problem that we have optimistically moved the b/f cursor already, so move it back. For sanity,
4153 // we only do this when punting a navigation for the target frame or top-level frame.
4154 if ((isTargetItem || isLoadingMainFrame()) && isBackForwardLoadType(m_policyLoadType))
4155 if (Page* page = m_frame->page()) {
4156 Frame* mainFrame = page->mainFrame();
4157 if (HistoryItem* resetItem = mainFrame->loader()->m_currentHistoryItem.get()) {
4158 page->backForwardList()->goToItem(resetItem);
4159 Settings* settings = m_frame->settings();
4160 page->setGlobalHistoryItem((!settings || settings->privateBrowsingEnabled()) ? 0 : resetItem);
4161 }
4162 }
4163 return;
4164 }
4165
4166 FrameLoadType type = m_policyLoadType;
4167 stopAllLoaders();
4168
4169 // <rdar://problem/6250856> - In certain circumstances on pages with multiple frames, stopAllLoaders()
4170 // might detach the current FrameLoader, in which case we should bail on this newly defunct load.
4171 if (!m_frame->page())
4172 return;
4173
4174 setProvisionalDocumentLoader(m_policyDocumentLoader.get());
4175 m_loadType = type;
4176 setState(FrameStateProvisional);
4177
4178 setPolicyDocumentLoader(0);
4179
4180 if (isBackForwardLoadType(type) && loadProvisionalItemFromCachedPage())
4181 return;
4182
4183 if (formState)
4184 m_client->dispatchWillSubmitForm(&FrameLoader::continueLoadAfterWillSubmitForm, formState);
4185 else
4186 continueLoadAfterWillSubmitForm();
4187 }
4188
4189
callContinueLoadAfterNewWindowPolicy(void * argument,const ResourceRequest & request,PassRefPtr<FormState> formState,const String & frameName,bool shouldContinue)4190 void FrameLoader::callContinueLoadAfterNewWindowPolicy(void* argument,
4191 const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, bool shouldContinue)
4192 {
4193 FrameLoader* loader = static_cast<FrameLoader*>(argument);
4194 loader->continueLoadAfterNewWindowPolicy(request, formState, frameName, shouldContinue);
4195 }
4196
continueLoadAfterNewWindowPolicy(const ResourceRequest & request,PassRefPtr<FormState> formState,const String & frameName,bool shouldContinue)4197 void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& request,
4198 PassRefPtr<FormState> formState, const String& frameName, bool shouldContinue)
4199 {
4200 if (!shouldContinue)
4201 return;
4202
4203 RefPtr<Frame> frame = m_frame;
4204 RefPtr<Frame> mainFrame = m_client->dispatchCreatePage();
4205 if (!mainFrame)
4206 return;
4207
4208 if (frameName != "_blank")
4209 mainFrame->tree()->setName(frameName);
4210
4211 mainFrame->loader()->setOpenedByDOM();
4212 mainFrame->loader()->m_client->dispatchShow();
4213 mainFrame->loader()->setOpener(frame.get());
4214 mainFrame->loader()->loadWithNavigationAction(request, NavigationAction(), false, FrameLoadTypeStandard, formState);
4215 }
4216
sendRemainingDelegateMessages(unsigned long identifier,const ResourceResponse & response,int length,const ResourceError & error)4217 void FrameLoader::sendRemainingDelegateMessages(unsigned long identifier, const ResourceResponse& response, int length, const ResourceError& error)
4218 {
4219 if (!response.isNull())
4220 dispatchDidReceiveResponse(m_documentLoader.get(), identifier, response);
4221
4222 if (length > 0)
4223 dispatchDidReceiveContentLength(m_documentLoader.get(), identifier, length);
4224
4225 if (error.isNull())
4226 dispatchDidFinishLoading(m_documentLoader.get(), identifier);
4227 else
4228 m_client->dispatchDidFailLoading(m_documentLoader.get(), identifier, error);
4229 }
4230
requestFromDelegate(ResourceRequest & request,unsigned long & identifier,ResourceError & error)4231 void FrameLoader::requestFromDelegate(ResourceRequest& request, unsigned long& identifier, ResourceError& error)
4232 {
4233 ASSERT(!request.isNull());
4234
4235 identifier = 0;
4236 if (Page* page = m_frame->page()) {
4237 identifier = page->progress()->createUniqueIdentifier();
4238 dispatchAssignIdentifierToInitialRequest(identifier, m_documentLoader.get(), request);
4239 }
4240
4241 ResourceRequest newRequest(request);
4242 dispatchWillSendRequest(m_documentLoader.get(), identifier, newRequest, ResourceResponse());
4243
4244 if (newRequest.isNull())
4245 error = cancelledError(request);
4246 else
4247 error = ResourceError();
4248
4249 request = newRequest;
4250 }
4251
loadedResourceFromMemoryCache(const CachedResource * resource)4252 void FrameLoader::loadedResourceFromMemoryCache(const CachedResource* resource)
4253 {
4254 Page* page = m_frame->page();
4255 if (!page)
4256 return;
4257
4258 page->inspectorController()->didLoadResourceFromMemoryCache(m_documentLoader.get(), resource);
4259
4260 if (!resource->sendResourceLoadCallbacks() || m_documentLoader->haveToldClientAboutLoad(resource->url()))
4261 return;
4262
4263 if (!page->areMemoryCacheClientCallsEnabled()) {
4264 m_documentLoader->recordMemoryCacheLoadForFutureClientNotification(resource->url());
4265 m_documentLoader->didTellClientAboutLoad(resource->url());
4266 return;
4267 }
4268
4269 ResourceRequest request(resource->url());
4270 if (m_client->dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), request, resource->response(), resource->encodedSize())) {
4271 m_documentLoader->didTellClientAboutLoad(resource->url());
4272 return;
4273 }
4274
4275 unsigned long identifier;
4276 ResourceError error;
4277 requestFromDelegate(request, identifier, error);
4278 sendRemainingDelegateMessages(identifier, resource->response(), resource->encodedSize(), error);
4279 }
4280
applyUserAgent(ResourceRequest & request)4281 void FrameLoader::applyUserAgent(ResourceRequest& request)
4282 {
4283 String userAgent = client()->userAgent(request.url());
4284 ASSERT(!userAgent.isNull());
4285 request.setHTTPUserAgent(userAgent);
4286 }
4287
canGoBackOrForward(int distance) const4288 bool FrameLoader::canGoBackOrForward(int distance) const
4289 {
4290 if (Page* page = m_frame->page()) {
4291 if (distance == 0)
4292 return true;
4293 if (distance > 0 && distance <= page->backForwardList()->forwardListCount())
4294 return true;
4295 if (distance < 0 && -distance <= page->backForwardList()->backListCount())
4296 return true;
4297 }
4298 return false;
4299 }
4300
getHistoryLength()4301 int FrameLoader::getHistoryLength()
4302 {
4303 if (Page* page = m_frame->page())
4304 return page->backForwardList()->backListCount() + 1;
4305 return 0;
4306 }
4307
historyURL(int distance)4308 KURL FrameLoader::historyURL(int distance)
4309 {
4310 if (Page* page = m_frame->page()) {
4311 BackForwardList* list = page->backForwardList();
4312 HistoryItem* item = list->itemAtIndex(distance);
4313 if (!item) {
4314 if (distance > 0) {
4315 int forwardListCount = list->forwardListCount();
4316 if (forwardListCount > 0)
4317 item = list->itemAtIndex(forwardListCount);
4318 } else {
4319 int backListCount = list->backListCount();
4320 if (backListCount > 0)
4321 item = list->itemAtIndex(-backListCount);
4322 }
4323 }
4324 if (item)
4325 return item->url();
4326 }
4327 return KURL();
4328 }
4329
addHistoryItemForFragmentScroll()4330 void FrameLoader::addHistoryItemForFragmentScroll()
4331 {
4332 addBackForwardItemClippedAtTarget(false);
4333 }
4334
loadProvisionalItemFromCachedPage()4335 bool FrameLoader::loadProvisionalItemFromCachedPage()
4336 {
4337 RefPtr<CachedPage> cachedPage = pageCache()->get(m_provisionalHistoryItem.get());
4338 if (!cachedPage || !cachedPage->document())
4339 return false;
4340 provisionalDocumentLoader()->loadFromCachedPage(cachedPage.release());
4341 return true;
4342 }
4343
cachePageForHistoryItem(HistoryItem * item)4344 void FrameLoader::cachePageForHistoryItem(HistoryItem* item)
4345 {
4346 if (Page* page = m_frame->page()) {
4347 RefPtr<CachedPage> cachedPage = CachedPage::create(page);
4348 m_client->savePlatformDataToCachedFrame(cachedPage->cachedMainFrame());
4349
4350 pageCache()->add(item, cachedPage.release());
4351 }
4352 }
4353
shouldTreatURLAsSameAsCurrent(const KURL & url) const4354 bool FrameLoader::shouldTreatURLAsSameAsCurrent(const KURL& url) const
4355 {
4356 if (!m_currentHistoryItem)
4357 return false;
4358 return url == m_currentHistoryItem->url() || url == m_currentHistoryItem->originalURL();
4359 }
4360
createHistoryItem(bool useOriginal)4361 PassRefPtr<HistoryItem> FrameLoader::createHistoryItem(bool useOriginal)
4362 {
4363 DocumentLoader* docLoader = documentLoader();
4364
4365 KURL unreachableURL = docLoader ? docLoader->unreachableURL() : KURL();
4366
4367 KURL url;
4368 KURL originalURL;
4369
4370 if (!unreachableURL.isEmpty()) {
4371 url = unreachableURL;
4372 originalURL = unreachableURL;
4373 } else {
4374 originalURL = docLoader ? docLoader->originalURL() : KURL();
4375 if (useOriginal)
4376 url = originalURL;
4377 else if (docLoader)
4378 url = docLoader->requestURL();
4379 }
4380
4381 LOG(History, "WebCoreHistory: Creating item for %s", url.string().ascii().data());
4382
4383 // Frames that have never successfully loaded any content
4384 // may have no URL at all. Currently our history code can't
4385 // deal with such things, so we nip that in the bud here.
4386 // Later we may want to learn to live with nil for URL.
4387 // See bug 3368236 and related bugs for more information.
4388 if (url.isEmpty())
4389 url = blankURL();
4390 if (originalURL.isEmpty())
4391 originalURL = blankURL();
4392
4393 Frame* parentFrame = m_frame->tree()->parent();
4394 String parent = parentFrame ? parentFrame->tree()->name() : "";
4395 String title = docLoader ? docLoader->title() : "";
4396
4397 RefPtr<HistoryItem> item = HistoryItem::create(url, m_frame->tree()->name(), parent, title);
4398 item->setOriginalURLString(originalURL.string());
4399
4400 if (!unreachableURL.isEmpty() || !docLoader || docLoader->response().httpStatusCode() >= 400)
4401 item->setLastVisitWasFailure(true);
4402
4403 // Save form state if this is a POST
4404 if (docLoader) {
4405 if (useOriginal)
4406 item->setFormInfoFromRequest(docLoader->originalRequest());
4407 else
4408 item->setFormInfoFromRequest(docLoader->request());
4409 }
4410
4411 // Set the item for which we will save document state
4412 m_previousHistoryItem = m_currentHistoryItem;
4413 m_currentHistoryItem = item;
4414
4415 return item.release();
4416 }
4417
addBackForwardItemClippedAtTarget(bool doClip)4418 void FrameLoader::addBackForwardItemClippedAtTarget(bool doClip)
4419 {
4420 Page* page = m_frame->page();
4421 if (!page)
4422 return;
4423
4424 if (documentLoader()->urlForHistory().isEmpty())
4425 return;
4426
4427 Frame* mainFrame = page->mainFrame();
4428 ASSERT(mainFrame);
4429 FrameLoader* frameLoader = mainFrame->loader();
4430
4431 if (!frameLoader->m_didPerformFirstNavigation && page->backForwardList()->entries().size() == 1) {
4432 frameLoader->m_didPerformFirstNavigation = true;
4433 m_client->didPerformFirstNavigation();
4434 }
4435
4436 RefPtr<HistoryItem> item = frameLoader->createHistoryItemTree(m_frame, doClip);
4437 LOG(BackForward, "WebCoreBackForward - Adding backforward item %p for frame %s", item.get(), documentLoader()->url().string().ascii().data());
4438 page->backForwardList()->addItem(item);
4439 }
4440
createHistoryItemTree(Frame * targetFrame,bool clipAtTarget)4441 PassRefPtr<HistoryItem> FrameLoader::createHistoryItemTree(Frame* targetFrame, bool clipAtTarget)
4442 {
4443 RefPtr<HistoryItem> bfItem = createHistoryItem(m_frame->tree()->parent() ? true : false);
4444 if (m_previousHistoryItem)
4445 saveScrollPositionAndViewStateToItem(m_previousHistoryItem.get());
4446 if (!(clipAtTarget && m_frame == targetFrame)) {
4447 // save frame state for items that aren't loading (khtml doesn't save those)
4448 saveDocumentState();
4449 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) {
4450 FrameLoader* childLoader = child->loader();
4451 bool hasChildLoaded = childLoader->frameHasLoaded();
4452
4453 // If the child is a frame corresponding to an <object> element that never loaded,
4454 // we don't want to create a history item, because that causes fallback content
4455 // to be ignored on reload.
4456
4457 if (!(!hasChildLoaded && childLoader->isHostedByObjectElement()))
4458 bfItem->addChildItem(childLoader->createHistoryItemTree(targetFrame, clipAtTarget));
4459 }
4460 }
4461 if (m_frame == targetFrame)
4462 bfItem->setIsTargetItem(true);
4463 return bfItem;
4464 }
4465
findFrameForNavigation(const AtomicString & name)4466 Frame* FrameLoader::findFrameForNavigation(const AtomicString& name)
4467 {
4468 Frame* frame = m_frame->tree()->find(name);
4469 if (shouldAllowNavigation(frame))
4470 return frame;
4471 return 0;
4472 }
4473
saveScrollPositionAndViewStateToItem(HistoryItem * item)4474 void FrameLoader::saveScrollPositionAndViewStateToItem(HistoryItem* item)
4475 {
4476 if (!item || !m_frame->view())
4477 return;
4478
4479 item->setScrollPoint(m_frame->view()->scrollPosition());
4480 // FIXME: It would be great to work out a way to put this code in WebCore instead of calling through to the client.
4481 m_client->saveViewStateToItem(item);
4482 }
4483
4484 /*
4485 There is a race condition between the layout and load completion that affects restoring the scroll position.
4486 We try to restore the scroll position at both the first layout and upon load completion.
4487
4488 1) If first layout happens before the load completes, we want to restore the scroll position then so that the
4489 first time we draw the page is already scrolled to the right place, instead of starting at the top and later
4490 jumping down. It is possible that the old scroll position is past the part of the doc laid out so far, in
4491 which case the restore silent fails and we will fix it in when we try to restore on doc completion.
4492 2) If the layout happens after the load completes, the attempt to restore at load completion time silently
4493 fails. We then successfully restore it when the layout happens.
4494 */
restoreScrollPositionAndViewState()4495 void FrameLoader::restoreScrollPositionAndViewState()
4496 {
4497 if (!m_committedFirstRealDocumentLoad)
4498 return;
4499
4500 ASSERT(m_currentHistoryItem);
4501
4502 // FIXME: As the ASSERT attests, it seems we should always have a currentItem here.
4503 // One counterexample is <rdar://problem/4917290>
4504 // For now, to cover this issue in release builds, there is no technical harm to returning
4505 // early and from a user standpoint - as in the above radar - the previous page load failed
4506 // so there *is* no scroll or view state to restore!
4507 if (!m_currentHistoryItem)
4508 return;
4509
4510 // FIXME: It would be great to work out a way to put this code in WebCore instead of calling
4511 // through to the client. It's currently used only for the PDF view on Mac.
4512 m_client->restoreViewState();
4513
4514 if (FrameView* view = m_frame->view())
4515 if (!view->wasScrolledByUser())
4516 view->setScrollPosition(m_currentHistoryItem->scrollPoint());
4517 }
4518
invalidateCurrentItemCachedPage()4519 void FrameLoader::invalidateCurrentItemCachedPage()
4520 {
4521 // When we are pre-commit, the currentItem is where the pageCache data resides
4522 CachedPage* cachedPage = pageCache()->get(m_currentHistoryItem.get());
4523
4524 // FIXME: This is a grotesque hack to fix <rdar://problem/4059059> Crash in RenderFlow::detach
4525 // Somehow the PageState object is not properly updated, and is holding onto a stale document.
4526 // Both Xcode and FileMaker see this crash, Safari does not.
4527
4528 ASSERT(!cachedPage || cachedPage->document() == m_frame->document());
4529 if (cachedPage && cachedPage->document() == m_frame->document()) {
4530 cachedPage->document()->setInPageCache(false);
4531 cachedPage->clear();
4532 }
4533
4534 if (cachedPage)
4535 pageCache()->remove(m_currentHistoryItem.get());
4536 }
4537
saveDocumentState()4538 void FrameLoader::saveDocumentState()
4539 {
4540 if (m_creatingInitialEmptyDocument)
4541 return;
4542
4543 // For a standard page load, we will have a previous item set, which will be used to
4544 // store the form state. However, in some cases we will have no previous item, and
4545 // the current item is the right place to save the state. One example is when we
4546 // detach a bunch of frames because we are navigating from a site with frames to
4547 // another site. Another is when saving the frame state of a frame that is not the
4548 // target of the current navigation (if we even decide to save with that granularity).
4549
4550 // Because of previousItem's "masking" of currentItem for this purpose, it's important
4551 // that previousItem be cleared at the end of a page transition. We leverage the
4552 // checkLoadComplete recursion to achieve this goal.
4553
4554 HistoryItem* item = m_previousHistoryItem ? m_previousHistoryItem.get() : m_currentHistoryItem.get();
4555 if (!item)
4556 return;
4557
4558 Document* document = m_frame->document();
4559 ASSERT(document);
4560
4561 if (document && item->isCurrentDocument(document)) {
4562 LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree()->name().string().utf8().data(), item);
4563 item->setDocumentState(document->formElementsState());
4564 }
4565 }
4566
4567 // Loads content into this frame, as specified by history item
loadItem(HistoryItem * item,FrameLoadType loadType)4568 void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType)
4569 {
4570 if (!m_frame->page())
4571 return;
4572
4573 KURL itemURL = item->url();
4574 KURL itemOriginalURL = item->originalURL();
4575 KURL currentURL;
4576 if (documentLoader())
4577 currentURL = documentLoader()->url();
4578 RefPtr<FormData> formData = item->formData();
4579
4580 // Are we navigating to an anchor within the page?
4581 // Note if we have child frames we do a real reload, since the child frames might not
4582 // match our current frame structure, or they might not have the right content. We could
4583 // check for all that as an additional optimization.
4584 // We also do not do anchor-style navigation if we're posting a form.
4585
4586 #if ENABLE(WML)
4587 if (!formData && urlsMatchItem(item) && !m_frame->document()->isWMLDocument()) {
4588 #else
4589 if (!formData && urlsMatchItem(item)) {
4590 #endif
4591 // Must do this maintenance here, since we don't go through a real page reload
4592 saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get());
4593
4594 if (FrameView* view = m_frame->view())
4595 view->setWasScrolledByUser(false);
4596
4597 m_currentHistoryItem = item;
4598
4599 // FIXME: Form state might need to be saved here too.
4600
4601 // We always call scrollToAnchor here, even if the URL doesn't have an
4602 // anchor fragment. This is so we'll keep the WebCore Frame's URL up-to-date.
4603 scrollToAnchor(item->url());
4604
4605 // must do this maintenance here, since we don't go through a real page reload
4606 restoreScrollPositionAndViewState();
4607
4608 // Fake the URL change by updating the data source's request. This will no longer
4609 // be necessary if we do the better fix described above.
4610 documentLoader()->replaceRequestURLForAnchorScroll(itemURL);
4611
4612 m_client->dispatchDidChangeLocationWithinPage();
4613
4614 // FrameLoaderClient::didFinishLoad() tells the internal load delegate the load finished with no error
4615 m_client->didFinishLoad();
4616 } else {
4617 // Remember this item so we can traverse any child items as child frames load
4618 m_provisionalHistoryItem = item;
4619
4620 bool inPageCache = false;
4621
4622 // Check if we'll be using the page cache. We only use the page cache
4623 // if one exists and it is less than _backForwardCacheExpirationInterval
4624 // seconds old. If the cache is expired it gets flushed here.
4625 if (RefPtr<CachedPage> cachedPage = pageCache()->get(item)) {
4626 double interval = currentTime() - cachedPage->timeStamp();
4627
4628 // FIXME: 1800 should not be hardcoded, it should come from
4629 // WebKitBackForwardCacheExpirationIntervalKey in WebKit.
4630 // Or we should remove WebKitBackForwardCacheExpirationIntervalKey.
4631 if (interval <= 1800) {
4632 loadWithDocumentLoader(cachedPage->documentLoader(), loadType, 0);
4633 inPageCache = true;
4634 } else {
4635 LOG(PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", m_provisionalHistoryItem->url().string().ascii().data());
4636 pageCache()->remove(item);
4637 }
4638 }
4639
4640 if (!inPageCache) {
4641 bool addedExtraFields = false;
4642 ResourceRequest request(itemURL);
4643
4644 if (!item->referrer().isNull())
4645 request.setHTTPReferrer(item->referrer());
4646
4647 // If this was a repost that failed the page cache, we might try to repost the form.
4648 NavigationAction action;
4649 if (formData) {
4650
4651 formData->generateFiles(m_frame->page()->chrome()->client());
4652
4653 request.setHTTPMethod("POST");
4654 request.setHTTPBody(formData);
4655 request.setHTTPContentType(item->formContentType());
4656 RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::createFromString(item->referrer());
4657 addHTTPOriginIfNeeded(request, securityOrigin->toString());
4658
4659 // Make sure to add extra fields to the request after the Origin header is added for the FormData case.
4660 // See https://bugs.webkit.org/show_bug.cgi?id=22194 for more discussion.
4661 addExtraFieldsToRequest(request, m_loadType, true, formData);
4662 addedExtraFields = true;
4663
4664 // FIXME: Slight hack to test if the NSURL cache contains the page we're going to.
4665 // We want to know this before talking to the policy delegate, since it affects whether
4666 // we show the DoYouReallyWantToRepost nag.
4667 //
4668 // This trick has a small bug (3123893) where we might find a cache hit, but then
4669 // have the item vanish when we try to use it in the ensuing nav. This should be
4670 // extremely rare, but in that case the user will get an error on the navigation.
4671
4672 if (ResourceHandle::willLoadFromCache(request))
4673 action = NavigationAction(itemURL, loadType, false);
4674 else {
4675 request.setCachePolicy(ReloadIgnoringCacheData);
4676 action = NavigationAction(itemURL, NavigationTypeFormResubmitted);
4677 }
4678 } else {
4679 switch (loadType) {
4680 case FrameLoadTypeReload:
4681 case FrameLoadTypeReloadFromOrigin:
4682 request.setCachePolicy(ReloadIgnoringCacheData);
4683 break;
4684 case FrameLoadTypeBack:
4685 case FrameLoadTypeForward:
4686 case FrameLoadTypeIndexedBackForward:
4687 if (itemURL.protocol() != "https")
4688 request.setCachePolicy(ReturnCacheDataElseLoad);
4689 break;
4690 case FrameLoadTypeStandard:
4691 case FrameLoadTypeRedirectWithLockedBackForwardList:
4692 break;
4693 case FrameLoadTypeSame:
4694 default:
4695 ASSERT_NOT_REACHED();
4696 }
4697
4698 action = NavigationAction(itemOriginalURL, loadType, false);
4699 }
4700
4701 if (!addedExtraFields)
4702 addExtraFieldsToRequest(request, m_loadType, true, formData);
4703
4704 loadWithNavigationAction(request, action, false, loadType, 0);
4705 }
4706 }
4707 }
4708
4709 // Walk the frame tree and ensure that the URLs match the URLs in the item.
4710 bool FrameLoader::urlsMatchItem(HistoryItem* item) const
4711 {
4712 const KURL& currentURL = documentLoader()->url();
4713 if (!equalIgnoringRef(currentURL, item->url()))
4714 return false;
4715
4716 const HistoryItemVector& childItems = item->children();
4717
4718 unsigned size = childItems.size();
4719 for (unsigned i = 0; i < size; ++i) {
4720 Frame* childFrame = m_frame->tree()->child(childItems[i]->target());
4721 if (childFrame && !childFrame->loader()->urlsMatchItem(childItems[i].get()))
4722 return false;
4723 }
4724
4725 return true;
4726 }
4727
4728 // Main funnel for navigating to a previous location (back/forward, non-search snap-back)
4729 // This includes recursion to handle loading into framesets properly
4730 void FrameLoader::goToItem(HistoryItem* targetItem, FrameLoadType type)
4731 {
4732 ASSERT(!m_frame->tree()->parent());
4733
4734 // shouldGoToHistoryItem is a private delegate method. This is needed to fix:
4735 // <rdar://problem/3951283> can view pages from the back/forward cache that should be disallowed by Parental Controls
4736 // Ultimately, history item navigations should go through the policy delegate. That's covered in:
4737 // <rdar://problem/3979539> back/forward cache navigations should consult policy delegate
4738 Page* page = m_frame->page();
4739 if (!page)
4740 return;
4741 if (!m_client->shouldGoToHistoryItem(targetItem))
4742 return;
4743
4744 // Set the BF cursor before commit, which lets the user quickly click back/forward again.
4745 // - plus, it only makes sense for the top level of the operation through the frametree,
4746 // as opposed to happening for some/one of the page commits that might happen soon
4747 BackForwardList* bfList = page->backForwardList();
4748 HistoryItem* currentItem = bfList->currentItem();
4749 bfList->goToItem(targetItem);
4750 Settings* settings = m_frame->settings();
4751 page->setGlobalHistoryItem((!settings || settings->privateBrowsingEnabled()) ? 0 : targetItem);
4752 recursiveGoToItem(targetItem, currentItem, type);
4753 }
4754
4755 // The general idea here is to traverse the frame tree and the item tree in parallel,
4756 // tracking whether each frame already has the content the item requests. If there is
4757 // a match (by URL), we just restore scroll position and recurse. Otherwise we must
4758 // reload that frame, and all its kids.
4759 void FrameLoader::recursiveGoToItem(HistoryItem* item, HistoryItem* fromItem, FrameLoadType type)
4760 {
4761 ASSERT(item);
4762 ASSERT(fromItem);
4763
4764 KURL itemURL = item->url();
4765 KURL currentURL;
4766 if (documentLoader())
4767 currentURL = documentLoader()->url();
4768
4769 // Always reload the target frame of the item we're going to. This ensures that we will
4770 // do -some- load for the transition, which means a proper notification will be posted
4771 // to the app.
4772 // The exact URL has to match, including fragment. We want to go through the _load
4773 // method, even if to do a within-page navigation.
4774 // The current frame tree and the frame tree snapshot in the item have to match.
4775 if (!item->isTargetItem() &&
4776 itemURL == currentURL &&
4777 ((m_frame->tree()->name().isEmpty() && item->target().isEmpty()) || m_frame->tree()->name() == item->target()) &&
4778 childFramesMatchItem(item))
4779 {
4780 // This content is good, so leave it alone and look for children that need reloading
4781 // Save form state (works from currentItem, since prevItem is nil)
4782 ASSERT(!m_previousHistoryItem);
4783 saveDocumentState();
4784 saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get());
4785
4786 if (FrameView* view = m_frame->view())
4787 view->setWasScrolledByUser(false);
4788
4789 m_currentHistoryItem = item;
4790
4791 // Restore form state (works from currentItem)
4792 restoreDocumentState();
4793
4794 // Restore the scroll position (we choose to do this rather than going back to the anchor point)
4795 restoreScrollPositionAndViewState();
4796
4797 const HistoryItemVector& childItems = item->children();
4798
4799 int size = childItems.size();
4800 for (int i = 0; i < size; ++i) {
4801 String childName = childItems[i]->target();
4802 HistoryItem* fromChildItem = fromItem->childItemWithName(childName);
4803 ASSERT(fromChildItem || fromItem->isTargetItem());
4804 Frame* childFrame = m_frame->tree()->child(childName);
4805 ASSERT(childFrame);
4806 childFrame->loader()->recursiveGoToItem(childItems[i].get(), fromChildItem, type);
4807 }
4808 } else {
4809 loadItem(item, type);
4810 }
4811 }
4812
4813 // helper method that determines whether the subframes described by the item's subitems
4814 // match our own current frameset
4815 bool FrameLoader::childFramesMatchItem(HistoryItem* item) const
4816 {
4817 const HistoryItemVector& childItems = item->children();
4818 if (childItems.size() != m_frame->tree()->childCount())
4819 return false;
4820
4821 unsigned size = childItems.size();
4822 for (unsigned i = 0; i < size; ++i)
4823 if (!m_frame->tree()->child(childItems[i]->target()))
4824 return false;
4825
4826 // Found matches for all item targets
4827 return true;
4828 }
4829
4830 // There are 3 things you might think of as "history", all of which are handled by these functions.
4831 //
4832 // 1) Back/forward: The m_currentHistoryItem is part of this mechanism.
4833 // 2) Global history: Handled by the client.
4834 // 3) Visited links: Handled by the PageGroup.
4835
4836 void FrameLoader::updateHistoryForStandardLoad()
4837 {
4838 LOG(History, "WebCoreHistory: Updating History for Standard Load in frame %s", documentLoader()->url().string().ascii().data());
4839
4840 Settings* settings = m_frame->settings();
4841 bool needPrivacy = !settings || settings->privateBrowsingEnabled();
4842 const KURL& historyURL = documentLoader()->urlForHistory();
4843
4844 // If the navigation occured during load and this is a subframe, update the current
4845 // back/forward item rather than adding a new one and don't add the new URL to global
4846 // history at all. But do add it to visited links. <rdar://problem/5333496>
4847 bool frameNavigationDuringLoad = false;
4848 if (m_navigationDuringLoad) {
4849 HTMLFrameOwnerElement* owner = m_frame->ownerElement();
4850 frameNavigationDuringLoad = owner && !owner->createdByParser();
4851 m_navigationDuringLoad = false;
4852 }
4853
4854 bool didUpdateGlobalHistory = false;
4855 if (!frameNavigationDuringLoad && !documentLoader()->isClientRedirect()) {
4856 if (!historyURL.isEmpty()) {
4857 addBackForwardItemClippedAtTarget(true);
4858 if (!needPrivacy) {
4859 m_client->updateGlobalHistory();
4860 didUpdateGlobalHistory = true;
4861 }
4862 if (Page* page = m_frame->page())
4863 page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForwardList()->currentItem());
4864 }
4865 } else if (documentLoader()->unreachableURL().isEmpty() && m_currentHistoryItem) {
4866 m_currentHistoryItem->setURL(documentLoader()->url());
4867 m_currentHistoryItem->setFormInfoFromRequest(documentLoader()->request());
4868 }
4869
4870 if (!historyURL.isEmpty() && !needPrivacy) {
4871 if (Page* page = m_frame->page())
4872 page->group().addVisitedLink(historyURL);
4873
4874 if (!didUpdateGlobalHistory && !url().isEmpty())
4875 m_client->updateGlobalHistoryForRedirectWithoutHistoryItem();
4876 }
4877 }
4878
4879 void FrameLoader::updateHistoryForClientRedirect()
4880 {
4881 #if !LOG_DISABLED
4882 if (documentLoader())
4883 LOG(History, "WebCoreHistory: Updating History for client redirect in frame %s", documentLoader()->title().utf8().data());
4884 #endif
4885
4886 // Clear out form data so we don't try to restore it into the incoming page. Must happen after
4887 // webcore has closed the URL and saved away the form state.
4888 if (m_currentHistoryItem) {
4889 m_currentHistoryItem->clearDocumentState();
4890 m_currentHistoryItem->clearScrollPoint();
4891 }
4892
4893 Settings* settings = m_frame->settings();
4894 bool needPrivacy = !settings || settings->privateBrowsingEnabled();
4895 const KURL& historyURL = documentLoader()->urlForHistory();
4896
4897 if (!historyURL.isEmpty() && !needPrivacy) {
4898 if (Page* page = m_frame->page())
4899 page->group().addVisitedLink(historyURL);
4900 }
4901 }
4902
4903 void FrameLoader::updateHistoryForBackForwardNavigation()
4904 {
4905 #if !LOG_DISABLED
4906 if (documentLoader())
4907 LOG(History, "WebCoreHistory: Updating History for back/forward navigation in frame %s", documentLoader()->title().utf8().data());
4908 #endif
4909
4910 // Must grab the current scroll position before disturbing it
4911 saveScrollPositionAndViewStateToItem(m_previousHistoryItem.get());
4912 }
4913
4914 void FrameLoader::updateHistoryForReload()
4915 {
4916 #if !LOG_DISABLED
4917 if (documentLoader())
4918 LOG(History, "WebCoreHistory: Updating History for reload in frame %s", documentLoader()->title().utf8().data());
4919 #endif
4920
4921 if (m_currentHistoryItem) {
4922 pageCache()->remove(m_currentHistoryItem.get());
4923
4924 if (loadType() == FrameLoadTypeReload || loadType() == FrameLoadTypeReloadFromOrigin)
4925 saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get());
4926
4927 // Sometimes loading a page again leads to a different result because of cookies. Bugzilla 4072
4928 if (documentLoader()->unreachableURL().isEmpty())
4929 m_currentHistoryItem->setURL(documentLoader()->requestURL());
4930 }
4931 }
4932
4933 void FrameLoader::updateHistoryForRedirectWithLockedBackForwardList()
4934 {
4935 #if !LOG_DISABLED
4936 if (documentLoader())
4937 LOG(History, "WebCoreHistory: Updating History for redirect load in frame %s", documentLoader()->title().utf8().data());
4938 #endif
4939
4940 Settings* settings = m_frame->settings();
4941 bool needPrivacy = !settings || settings->privateBrowsingEnabled();
4942 const KURL& historyURL = documentLoader()->urlForHistory();
4943
4944 bool didUpdateGlobalHistory = false;
4945 if (documentLoader()->isClientRedirect()) {
4946 if (!m_currentHistoryItem && !m_frame->tree()->parent()) {
4947 if (!historyURL.isEmpty()) {
4948 addBackForwardItemClippedAtTarget(true);
4949 if (!needPrivacy) {
4950 m_client->updateGlobalHistory();
4951 didUpdateGlobalHistory = true;
4952 }
4953 if (Page* page = m_frame->page())
4954 page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForwardList()->currentItem());
4955 }
4956 }
4957 if (m_currentHistoryItem) {
4958 m_currentHistoryItem->setURL(documentLoader()->url());
4959 m_currentHistoryItem->setFormInfoFromRequest(documentLoader()->request());
4960 }
4961 } else {
4962 Frame* parentFrame = m_frame->tree()->parent();
4963 if (parentFrame && parentFrame->loader()->m_currentHistoryItem)
4964 parentFrame->loader()->m_currentHistoryItem->addChildItem(createHistoryItem(true));
4965 }
4966
4967 if (!historyURL.isEmpty() && !needPrivacy) {
4968 if (Page* page = m_frame->page())
4969 page->group().addVisitedLink(historyURL);
4970
4971 if (!didUpdateGlobalHistory && !url().isEmpty())
4972 m_client->updateGlobalHistoryForRedirectWithoutHistoryItem();
4973 }
4974 }
4975
4976 void FrameLoader::updateHistoryForCommit()
4977 {
4978 #if !LOG_DISABLED
4979 if (documentLoader())
4980 LOG(History, "WebCoreHistory: Updating History for commit in frame %s", documentLoader()->title().utf8().data());
4981 #endif
4982 FrameLoadType type = loadType();
4983 if (isBackForwardLoadType(type) ||
4984 ((type == FrameLoadTypeReload || type == FrameLoadTypeReloadFromOrigin) && !provisionalDocumentLoader()->unreachableURL().isEmpty())) {
4985 // Once committed, we want to use current item for saving DocState, and
4986 // the provisional item for restoring state.
4987 // Note previousItem must be set before we close the URL, which will
4988 // happen when the data source is made non-provisional below
4989 m_previousHistoryItem = m_currentHistoryItem;
4990 ASSERT(m_provisionalHistoryItem);
4991 m_currentHistoryItem = m_provisionalHistoryItem;
4992 m_provisionalHistoryItem = 0;
4993 }
4994 }
4995
4996 void FrameLoader::updateHistoryForAnchorScroll()
4997 {
4998 if (m_URL.isEmpty())
4999 return;
5000
5001 Settings* settings = m_frame->settings();
5002 if (!settings || settings->privateBrowsingEnabled())
5003 return;
5004
5005 Page* page = m_frame->page();
5006 if (!page)
5007 return;
5008
5009 page->group().addVisitedLink(m_URL);
5010 }
5011
5012 // Walk the frame tree, telling all frames to save their form state into their current
5013 // history item.
5014 void FrameLoader::saveDocumentAndScrollState()
5015 {
5016 for (Frame* frame = m_frame; frame; frame = frame->tree()->traverseNext(m_frame)) {
5017 frame->loader()->saveDocumentState();
5018 frame->loader()->saveScrollPositionAndViewStateToItem(frame->loader()->currentHistoryItem());
5019 }
5020 }
5021
5022 // FIXME: These 6 setter/getters are here for a dwindling number of users in WebKit, WebFrame
5023 // being the primary one. After they're no longer needed there, they can be removed!
5024 HistoryItem* FrameLoader::currentHistoryItem()
5025 {
5026 return m_currentHistoryItem.get();
5027 }
5028
5029 HistoryItem* FrameLoader::previousHistoryItem()
5030 {
5031 return m_previousHistoryItem.get();
5032 }
5033
5034 HistoryItem* FrameLoader::provisionalHistoryItem()
5035 {
5036 return m_provisionalHistoryItem.get();
5037 }
5038
5039 void FrameLoader::setCurrentHistoryItem(PassRefPtr<HistoryItem> item)
5040 {
5041 m_currentHistoryItem = item;
5042 }
5043
5044 void FrameLoader::setPreviousHistoryItem(PassRefPtr<HistoryItem> item)
5045 {
5046 m_previousHistoryItem = item;
5047 }
5048
5049 void FrameLoader::setProvisionalHistoryItem(PassRefPtr<HistoryItem> item)
5050 {
5051 m_provisionalHistoryItem = item;
5052 }
5053
5054 void FrameLoader::setMainDocumentError(DocumentLoader* loader, const ResourceError& error)
5055 {
5056 m_client->setMainDocumentError(loader, error);
5057 }
5058
5059 void FrameLoader::mainReceivedCompleteError(DocumentLoader* loader, const ResourceError&)
5060 {
5061 loader->setPrimaryLoadComplete(true);
5062 m_client->dispatchDidLoadMainResource(activeDocumentLoader());
5063 checkCompleted();
5064 if (m_frame->page())
5065 checkLoadComplete();
5066 }
5067
5068 void FrameLoader::mainReceivedError(const ResourceError& error, bool isComplete)
5069 {
5070 activeDocumentLoader()->mainReceivedError(error, isComplete);
5071 }
5072
5073 ResourceError FrameLoader::cancelledError(const ResourceRequest& request) const
5074 {
5075 ResourceError error = m_client->cancelledError(request);
5076 error.setIsCancellation(true);
5077 return error;
5078 }
5079
5080 ResourceError FrameLoader::blockedError(const ResourceRequest& request) const
5081 {
5082 return m_client->blockedError(request);
5083 }
5084
5085 ResourceError FrameLoader::cannotShowURLError(const ResourceRequest& request) const
5086 {
5087 return m_client->cannotShowURLError(request);
5088 }
5089
5090 ResourceError FrameLoader::fileDoesNotExistError(const ResourceResponse& response) const
5091 {
5092 return m_client->fileDoesNotExistError(response);
5093 }
5094
5095 void FrameLoader::didFinishLoad(ResourceLoader* loader)
5096 {
5097 if (Page* page = m_frame->page())
5098 page->progress()->completeProgress(loader->identifier());
5099 dispatchDidFinishLoading(loader->documentLoader(), loader->identifier());
5100 }
5101
5102 bool FrameLoader::shouldUseCredentialStorage(ResourceLoader* loader)
5103 {
5104 return m_client->shouldUseCredentialStorage(loader->documentLoader(), loader->identifier());
5105 }
5106
5107 void FrameLoader::didReceiveAuthenticationChallenge(ResourceLoader* loader, const AuthenticationChallenge& currentWebChallenge)
5108 {
5109 m_client->dispatchDidReceiveAuthenticationChallenge(loader->documentLoader(), loader->identifier(), currentWebChallenge);
5110 }
5111
5112 void FrameLoader::didCancelAuthenticationChallenge(ResourceLoader* loader, const AuthenticationChallenge& currentWebChallenge)
5113 {
5114 m_client->dispatchDidCancelAuthenticationChallenge(loader->documentLoader(), loader->identifier(), currentWebChallenge);
5115 }
5116
5117 PolicyCheck::PolicyCheck()
5118 : m_navigationFunction(0)
5119 , m_newWindowFunction(0)
5120 , m_contentFunction(0)
5121 {
5122 }
5123
5124 void PolicyCheck::clear()
5125 {
5126 clearRequest();
5127 m_navigationFunction = 0;
5128 m_newWindowFunction = 0;
5129 m_contentFunction = 0;
5130 }
5131
5132 void PolicyCheck::set(const ResourceRequest& request, PassRefPtr<FormState> formState,
5133 NavigationPolicyDecisionFunction function, void* argument)
5134 {
5135 m_request = request;
5136 m_formState = formState;
5137 m_frameName = String();
5138
5139 m_navigationFunction = function;
5140 m_newWindowFunction = 0;
5141 m_contentFunction = 0;
5142 m_argument = argument;
5143 }
5144
5145 void PolicyCheck::set(const ResourceRequest& request, PassRefPtr<FormState> formState,
5146 const String& frameName, NewWindowPolicyDecisionFunction function, void* argument)
5147 {
5148 m_request = request;
5149 m_formState = formState;
5150 m_frameName = frameName;
5151
5152 m_navigationFunction = 0;
5153 m_newWindowFunction = function;
5154 m_contentFunction = 0;
5155 m_argument = argument;
5156 }
5157
5158 void PolicyCheck::set(ContentPolicyDecisionFunction function, void* argument)
5159 {
5160 m_request = ResourceRequest();
5161 m_formState = 0;
5162 m_frameName = String();
5163
5164 m_navigationFunction = 0;
5165 m_newWindowFunction = 0;
5166 m_contentFunction = function;
5167 m_argument = argument;
5168 }
5169
5170 void PolicyCheck::call(bool shouldContinue)
5171 {
5172 if (m_navigationFunction)
5173 m_navigationFunction(m_argument, m_request, m_formState.get(), shouldContinue);
5174 if (m_newWindowFunction)
5175 m_newWindowFunction(m_argument, m_request, m_formState.get(), m_frameName, shouldContinue);
5176 ASSERT(!m_contentFunction);
5177 }
5178
5179 void PolicyCheck::call(PolicyAction action)
5180 {
5181 ASSERT(!m_navigationFunction);
5182 ASSERT(!m_newWindowFunction);
5183 ASSERT(m_contentFunction);
5184 m_contentFunction(m_argument, action);
5185 }
5186
5187 void PolicyCheck::clearRequest()
5188 {
5189 m_request = ResourceRequest();
5190 m_formState = 0;
5191 m_frameName = String();
5192 }
5193
5194 void PolicyCheck::cancel()
5195 {
5196 clearRequest();
5197 if (m_navigationFunction)
5198 m_navigationFunction(m_argument, m_request, m_formState.get(), false);
5199 if (m_newWindowFunction)
5200 m_newWindowFunction(m_argument, m_request, m_formState.get(), m_frameName, false);
5201 if (m_contentFunction)
5202 m_contentFunction(m_argument, PolicyIgnore);
5203 }
5204
5205 void FrameLoader::setTitle(const String& title)
5206 {
5207 documentLoader()->setTitle(title);
5208 }
5209
5210 KURL FrameLoader::originalRequestURL() const
5211 {
5212 return activeDocumentLoader()->originalRequest().url();
5213 }
5214
5215 String FrameLoader::referrer() const
5216 {
5217 return documentLoader()->request().httpReferrer();
5218 }
5219
5220 void FrameLoader::dispatchWindowObjectAvailable()
5221 {
5222 if (!m_frame->script()->isEnabled() || !m_frame->script()->haveWindowShell())
5223 return;
5224
5225 m_client->windowObjectCleared();
5226
5227 if (Page* page = m_frame->page()) {
5228 if (InspectorController* inspector = page->inspectorController())
5229 inspector->inspectedWindowScriptObjectCleared(m_frame);
5230 if (InspectorController* inspector = page->parentInspectorController())
5231 inspector->windowScriptObjectAvailable();
5232 }
5233 }
5234
5235 Widget* FrameLoader::createJavaAppletWidget(const IntSize& size, Element* element, const HashMap<String, String>& args)
5236 {
5237 String baseURLString;
5238 Vector<String> paramNames;
5239 Vector<String> paramValues;
5240 HashMap<String, String>::const_iterator end = args.end();
5241 for (HashMap<String, String>::const_iterator it = args.begin(); it != end; ++it) {
5242 if (equalIgnoringCase(it->first, "baseurl"))
5243 baseURLString = it->second;
5244 paramNames.append(it->first);
5245 paramValues.append(it->second);
5246 }
5247
5248 if (baseURLString.isEmpty())
5249 baseURLString = m_frame->document()->baseURL().string();
5250 KURL baseURL = completeURL(baseURLString);
5251
5252 Widget* widget = m_client->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues);
5253 if (widget)
5254 m_containsPlugIns = true;
5255
5256 return widget;
5257 }
5258
5259 void FrameLoader::didChangeTitle(DocumentLoader* loader)
5260 {
5261 m_client->didChangeTitle(loader);
5262
5263 // The title doesn't get communicated to the WebView until we are committed.
5264 if (loader->isCommitted()) {
5265 // Must update the entries in the back-forward list too.
5266 if (m_currentHistoryItem)
5267 m_currentHistoryItem->setTitle(loader->title());
5268 // This must go through the WebFrame because it has the right notion of the current b/f item.
5269 m_client->setTitle(loader->title(), loader->urlForHistory());
5270 m_client->setMainFrameDocumentReady(true); // update observers with new DOMDocument
5271 m_client->dispatchDidReceiveTitle(loader->title());
5272 }
5273 }
5274
5275 void FrameLoader::continueLoadWithData(SharedBuffer* buffer, const String& mimeType, const String& textEncoding, const KURL& url)
5276 {
5277 m_responseMIMEType = mimeType;
5278 didOpenURL(url);
5279
5280 String encoding;
5281 if (m_frame)
5282 encoding = documentLoader()->overrideEncoding();
5283 bool userChosen = !encoding.isNull();
5284 if (encoding.isNull())
5285 encoding = textEncoding;
5286 setEncoding(encoding, userChosen);
5287
5288 ASSERT(m_frame->document());
5289
5290 addData(buffer->data(), buffer->size());
5291 }
5292
5293 void FrameLoader::registerURLSchemeAsLocal(const String& scheme)
5294 {
5295 localSchemes().add(scheme);
5296 }
5297
5298 bool FrameLoader::shouldTreatURLAsLocal(const String& url)
5299 {
5300 // This avoids an allocation of another String and the HashSet contains()
5301 // call for the file: and http: schemes.
5302 if (url.length() >= 5) {
5303 const UChar* s = url.characters();
5304 if (s[0] == 'h' && s[1] == 't' && s[2] == 't' && s[3] == 'p' && s[4] == ':')
5305 return false;
5306 if (s[0] == 'f' && s[1] == 'i' && s[2] == 'l' && s[3] == 'e' && s[4] == ':')
5307 return true;
5308 }
5309
5310 int loc = url.find(':');
5311 if (loc == -1)
5312 return false;
5313
5314 String scheme = url.left(loc);
5315 return localSchemes().contains(scheme);
5316 }
5317
5318 bool FrameLoader::shouldTreatSchemeAsLocal(const String& scheme)
5319 {
5320 // This avoids an allocation of another String and the HashSet contains()
5321 // call for the file: and http: schemes.
5322 if (scheme.length() == 4) {
5323 const UChar* s = scheme.characters();
5324 if (s[0] == 'h' && s[1] == 't' && s[2] == 't' && s[3] == 'p')
5325 return false;
5326 if (s[0] == 'f' && s[1] == 'i' && s[2] == 'l' && s[3] == 'e')
5327 return true;
5328 }
5329
5330 if (scheme.isEmpty())
5331 return false;
5332
5333 return localSchemes().contains(scheme);
5334 }
5335
5336 void FrameLoader::dispatchDidCommitLoad()
5337 {
5338 if (m_creatingInitialEmptyDocument)
5339 return;
5340
5341 #ifndef NDEBUG
5342 m_didDispatchDidCommitLoad = true;
5343 #endif
5344
5345 m_client->dispatchDidCommitLoad();
5346
5347 if (Page* page = m_frame->page())
5348 page->inspectorController()->didCommitLoad(m_documentLoader.get());
5349 }
5350
5351 void FrameLoader::dispatchAssignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request)
5352 {
5353 m_client->assignIdentifierToInitialRequest(identifier, loader, request);
5354
5355 if (Page* page = m_frame->page())
5356 page->inspectorController()->identifierForInitialRequest(identifier, loader, request);
5357 }
5358
5359 void FrameLoader::dispatchWillSendRequest(DocumentLoader* loader, unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse)
5360 {
5361 StringImpl* oldRequestURL = request.url().string().impl();
5362 m_documentLoader->didTellClientAboutLoad(request.url());
5363
5364 m_client->dispatchWillSendRequest(loader, identifier, request, redirectResponse);
5365
5366 // If the URL changed, then we want to put that new URL in the "did tell client" set too.
5367 if (!request.isNull() && oldRequestURL != request.url().string().impl())
5368 m_documentLoader->didTellClientAboutLoad(request.url());
5369
5370 if (Page* page = m_frame->page())
5371 page->inspectorController()->willSendRequest(loader, identifier, request, redirectResponse);
5372 }
5373
5374 void FrameLoader::dispatchDidReceiveResponse(DocumentLoader* loader, unsigned long identifier, const ResourceResponse& r)
5375 {
5376 m_client->dispatchDidReceiveResponse(loader, identifier, r);
5377
5378 if (Page* page = m_frame->page())
5379 page->inspectorController()->didReceiveResponse(loader, identifier, r);
5380 }
5381
5382 void FrameLoader::dispatchDidReceiveContentLength(DocumentLoader* loader, unsigned long identifier, int length)
5383 {
5384 m_client->dispatchDidReceiveContentLength(loader, identifier, length);
5385
5386 if (Page* page = m_frame->page())
5387 page->inspectorController()->didReceiveContentLength(loader, identifier, length);
5388 }
5389
5390 void FrameLoader::dispatchDidFinishLoading(DocumentLoader* loader, unsigned long identifier)
5391 {
5392 m_client->dispatchDidFinishLoading(loader, identifier);
5393
5394 if (Page* page = m_frame->page())
5395 page->inspectorController()->didFinishLoading(loader, identifier);
5396 }
5397
5398 void FrameLoader::tellClientAboutPastMemoryCacheLoads()
5399 {
5400 ASSERT(m_frame->page());
5401 ASSERT(m_frame->page()->areMemoryCacheClientCallsEnabled());
5402
5403 if (!m_documentLoader)
5404 return;
5405
5406 Vector<String> pastLoads;
5407 m_documentLoader->takeMemoryCacheLoadsForClientNotification(pastLoads);
5408
5409 size_t size = pastLoads.size();
5410 for (size_t i = 0; i < size; ++i) {
5411 CachedResource* resource = cache()->resourceForURL(pastLoads[i]);
5412
5413 // FIXME: These loads, loaded from cache, but now gone from the cache by the time
5414 // Page::setMemoryCacheClientCallsEnabled(true) is called, will not be seen by the client.
5415 // Consider if there's some efficient way of remembering enough to deliver this client call.
5416 // We have the URL, but not the rest of the response or the length.
5417 if (!resource)
5418 continue;
5419
5420 ResourceRequest request(resource->url());
5421 m_client->dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), request, resource->response(), resource->encodedSize());
5422 }
5423 }
5424
5425 #if USE(LOW_BANDWIDTH_DISPLAY)
5426
5427 bool FrameLoader::addLowBandwidthDisplayRequest(CachedResource* cache)
5428 {
5429 if (m_frame->document()->inLowBandwidthDisplay() == false)
5430 return false;
5431
5432 // if cache is loaded, don't add to the list, where notifyFinished() is expected.
5433 if (cache->isLoaded())
5434 return false;
5435
5436 switch (cache->type()) {
5437 case CachedResource::CSSStyleSheet:
5438 case CachedResource::Script:
5439 m_needToSwitchOutLowBandwidthDisplay = true;
5440 m_externalRequestsInLowBandwidthDisplay.add(cache);
5441 cache->addClient(this);
5442 return true;
5443 case CachedResource::ImageResource:
5444 case CachedResource::FontResource:
5445 #if ENABLE(XSLT)
5446 case CachedResource::XSLStyleSheet:
5447 #endif
5448 #if ENABLE(XBL)
5449 case CachedResource::XBLStyleSheet:
5450 #endif
5451 return false;
5452 }
5453
5454 ASSERT_NOT_REACHED();
5455 return false;
5456 }
5457
5458 void FrameLoader::removeAllLowBandwidthDisplayRequests()
5459 {
5460 HashSet<CachedResource*>::iterator end = m_externalRequestsInLowBandwidthDisplay.end();
5461 for (HashSet<CachedResource*>::iterator it = m_externalRequestsInLowBandwidthDisplay.begin(); it != end; ++it)
5462 (*it)->removeClient(this);
5463 m_externalRequestsInLowBandwidthDisplay.clear();
5464 }
5465
5466 void FrameLoader::notifyFinished(CachedResource* script)
5467 {
5468 HashSet<CachedResource*>::iterator it = m_externalRequestsInLowBandwidthDisplay.find(script);
5469 if (it != m_externalRequestsInLowBandwidthDisplay.end()) {
5470 (*it)->removeClient(this);
5471 m_externalRequestsInLowBandwidthDisplay.remove(it);
5472 switchOutLowBandwidthDisplayIfReady();
5473 }
5474 }
5475
5476 void FrameLoader::switchOutLowBandwidthDisplayIfReady()
5477 {
5478 RefPtr<Document> oldDoc = m_frame->document();
5479 if (oldDoc->inLowBandwidthDisplay()) {
5480 if (!m_needToSwitchOutLowBandwidthDisplay) {
5481 // no need to switch, just reset state
5482 oldDoc->setLowBandwidthDisplay(false);
5483 removeAllLowBandwidthDisplayRequests();
5484 m_pendingSourceInLowBandwidthDisplay = String();
5485 m_finishedParsingDuringLowBandwidthDisplay = false;
5486 return;
5487 } else if (m_externalRequestsInLowBandwidthDisplay.isEmpty() ||
5488 m_pendingSourceInLowBandwidthDisplay.length() > cMaxPendingSourceLengthInLowBandwidthDisplay) {
5489 // clear the flag first
5490 oldDoc->setLowBandwidthDisplay(false);
5491
5492 // similar to clear(), should be refactored to share more code
5493 oldDoc->cancelParsing();
5494 oldDoc->detach();
5495 if (m_frame->script()->isEnabled())
5496 m_frame->script()->clearWindowShell();
5497 if (m_frame->view())
5498 m_frame->view()->clear();
5499
5500 // similar to begin(), should be refactored to share more code
5501 RefPtr<Document> newDoc = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode());
5502 m_frame->setDocument(newDoc);
5503 newDoc->setURL(m_URL);
5504 if (m_decoder)
5505 newDoc->setDecoder(m_decoder.get());
5506 restoreDocumentState();
5507 dispatchWindowObjectAvailable();
5508 newDoc->implicitOpen();
5509
5510 // swap DocLoader ownership
5511 DocLoader* docLoader = newDoc->docLoader();
5512 newDoc->setDocLoader(oldDoc->docLoader());
5513 newDoc->docLoader()->replaceDocument(newDoc.get());
5514 docLoader->replaceDocument(oldDoc.get());
5515 oldDoc->setDocLoader(docLoader);
5516
5517 // drop the old doc
5518 oldDoc = 0;
5519
5520 // write decoded data to the new doc, similar to write()
5521 if (m_pendingSourceInLowBandwidthDisplay.length()) {
5522 if (m_decoder->encoding().usesVisualOrdering())
5523 newDoc->setVisuallyOrdered();
5524 newDoc->recalcStyle(Node::Force);
5525 newDoc->tokenizer()->write(m_pendingSourceInLowBandwidthDisplay, true);
5526
5527 if (m_finishedParsingDuringLowBandwidthDisplay)
5528 newDoc->finishParsing();
5529 }
5530
5531 // update rendering
5532 newDoc->updateRendering();
5533
5534 // reset states
5535 removeAllLowBandwidthDisplayRequests();
5536 m_pendingSourceInLowBandwidthDisplay = String();
5537 m_finishedParsingDuringLowBandwidthDisplay = false;
5538 m_needToSwitchOutLowBandwidthDisplay = false;
5539 }
5540 }
5541 }
5542
5543 #endif
5544
5545 } // namespace WebCore
5546