2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Trolltech ASA
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include "FrameLoader.h"
35 #include "CachedPage.h"
37 #include "DOMImplementation.h"
38 #include "DocLoader.h"
40 #include "DocumentLoader.h"
41 #include "EditCommand.h"
43 #include "EditorClient.h"
46 #include "EventNames.h"
47 #include "FloatRect.h"
48 #include "FormState.h"
50 #include "FrameLoadRequest.h"
51 #include "FrameLoaderClient.h"
52 #include "FramePrivate.h"
53 #include "FrameTree.h"
54 #include "FrameView.h"
55 #include "HTMLFormElement.h"
56 #include "HTMLFrameElement.h"
57 #include "HTMLNames.h"
58 #include "HTMLObjectElement.h"
59 #include "HTTPParsers.h"
60 #include "HistoryItem.h"
61 #include "IconDatabase.h"
62 #include "IconLoader.h"
63 #include "InspectorController.h"
65 #include "MIMETypeRegistry.h"
66 #include "MainResourceLoader.h"
68 #include "PageCache.h"
69 #include "ProgressTracker.h"
70 #include "RenderPart.h"
71 #include "RenderWidget.h"
72 #include "ResourceHandle.h"
73 #include "ResourceRequest.h"
74 #include "SecurityOrigin.h"
75 #include "SegmentedString.h"
77 #include "SystemTime.h"
78 #include "TextResourceDecoder.h"
79 #include "WindowFeatures.h"
80 #include "XMLHttpRequest.h"
81 #include "XMLTokenizer.h"
82 #include "kjs_binding.h"
83 #include "kjs_proxy.h"
84 #include "kjs_window.h"
85 #include <kjs/JSLock.h>
86 #include <kjs/object.h>
89 #include "SVGDocument.h"
90 #include "SVGLocatable.h"
92 #include "SVGPreserveAspectRatio.h"
93 #include "SVGSVGElement.h"
94 #include "SVGViewElement.h"
95 #include "SVGViewSpec.h"
105 using namespace SVGNames;
107 using namespace HTMLNames;
108 using namespace EventNames;
110 #if USE(LOW_BANDWIDTH_DISPLAY)
111 const unsigned int cMaxPendingSourceLengthInLowBandwidthDisplay = 128 * 1024;
114 struct FormSubmission {
117 RefPtr<FormData> data;
123 FormSubmission(const char* a, const String& u, PassRefPtr<FormData> d, const String& t,
124 const String& ct, const String& b, PassRefPtr<Event> e)
136 struct ScheduledRedirection {
137 enum Type { redirection, locationChange, historyNavigation, locationChangeDuringLoad };
146 ScheduledRedirection(double redirectDelay, const String& redirectURL, bool redirectLockHistory, bool userGesture)
148 , delay(redirectDelay)
151 , lockHistory(redirectLockHistory)
152 , wasUserGesture(userGesture)
156 ScheduledRedirection(Type locationChangeType,
157 const String& locationChangeURL, const String& locationChangeReferrer,
158 bool locationChangeLockHistory, bool locationChangeWasUserGesture)
159 : type(locationChangeType)
161 , URL(locationChangeURL)
162 , referrer(locationChangeReferrer)
164 , lockHistory(locationChangeLockHistory)
165 , wasUserGesture(locationChangeWasUserGesture)
169 explicit ScheduledRedirection(int historyNavigationSteps)
170 : type(historyNavigation)
172 , historySteps(historyNavigationSteps)
174 , wasUserGesture(false)
179 static double storedTimeOfLastCompletedLoad;
180 static bool m_restrictAccessToLocal = false;
182 static bool getString(JSValue* result, String& string)
188 if (!result->getString(ustring))
194 bool isBackForwardLoadType(FrameLoadType type)
197 case FrameLoadTypeStandard:
198 case FrameLoadTypeReload:
199 case FrameLoadTypeReloadAllowingStaleData:
200 case FrameLoadTypeSame:
201 case FrameLoadTypeRedirectWithLockedHistory:
202 case FrameLoadTypeReplace:
204 case FrameLoadTypeBack:
205 case FrameLoadTypeForward:
206 case FrameLoadTypeIndexedBackForward:
209 ASSERT_NOT_REACHED();
213 static int numRequests(Document* document)
218 return document->docLoader()->requestCount();
221 FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client)
224 , m_state(FrameStateCommittedPage)
225 , m_loadType(FrameLoadTypeStandard)
226 , m_policyLoadType(FrameLoadTypeStandard)
227 , m_delegateIsHandlingProvisionalLoadError(false)
228 , m_delegateIsDecidingNavigationPolicy(false)
229 , m_delegateIsHandlingUnimplementablePolicy(false)
230 , m_firstLayoutDone(false)
231 , m_quickRedirectComing(false)
232 , m_sentRedirectNotification(false)
233 , m_inStopAllLoaders(false)
234 , m_navigationDuringLoad(false)
235 , m_cachePolicy(CachePolicyVerify)
236 , m_isExecutingJavaScriptFormAction(false)
237 , m_isRunningScript(false)
238 , m_didCallImplicitClose(false)
239 , m_wasUnloadEventEmitted(false)
240 , m_isComplete(false)
241 , m_isLoadingMainResource(false)
242 , m_cancellingWithLoadInProgress(false)
243 , m_needsClear(false)
244 , m_receivedData(false)
245 , m_encodingWasChosenByUser(false)
246 , m_containsPlugIns(false)
247 , m_redirectionTimer(this, &FrameLoader::redirectionTimerFired)
248 , m_checkCompletedTimer(this, &FrameLoader::checkCompletedTimerFired)
249 , m_checkLoadCompleteTimer(this, &FrameLoader::checkLoadCompleteTimerFired)
251 , m_openedByDOM(false)
252 , m_creatingInitialEmptyDocument(false)
253 , m_isDisplayingInitialEmptyDocument(false)
254 , m_committedFirstRealDocumentLoad(false)
255 , m_didPerformFirstNavigation(false)
256 #if USE(LOW_BANDWIDTH_DISPLAY)
257 , m_useLowBandwidthDisplay(true)
258 , m_finishedParsingDuringLowBandwidthDisplay(false)
259 , m_needToSwitchOutLowBandwidthDisplay(false)
264 FrameLoader::~FrameLoader()
268 HashSet<Frame*>::iterator end = m_openedFrames.end();
269 for (HashSet<Frame*>::iterator it = m_openedFrames.begin(); it != end; ++it)
270 (*it)->loader()->m_opener = 0;
272 m_client->frameLoaderDestroyed();
275 void FrameLoader::init()
277 // this somewhat odd set of steps is needed to give the frame an initial empty document
278 m_isDisplayingInitialEmptyDocument = false;
279 m_creatingInitialEmptyDocument = true;
280 setPolicyDocumentLoader(m_client->createDocumentLoader(ResourceRequest(String("")), SubstituteData()).get());
281 setProvisionalDocumentLoader(m_policyDocumentLoader.get());
282 setState(FrameStateProvisional);
283 m_provisionalDocumentLoader->setResponse(ResourceResponse(KURL(), "text/html", 0, String(), String()));
284 m_provisionalDocumentLoader->finishedLoading();
285 begin(KURL(), false);
287 m_frame->document()->cancelParsing();
288 m_creatingInitialEmptyDocument = false;
289 m_didCallImplicitClose = true;
292 void FrameLoader::setDefersLoading(bool defers)
294 if (m_documentLoader)
295 m_documentLoader->setDefersLoading(defers);
296 if (m_provisionalDocumentLoader)
297 m_provisionalDocumentLoader->setDefersLoading(defers);
298 if (m_policyDocumentLoader)
299 m_policyDocumentLoader->setDefersLoading(defers);
300 m_client->setDefersLoading(defers);
303 Frame* FrameLoader::createWindow(const FrameLoadRequest& request, const WindowFeatures& features, bool& created)
305 ASSERT(!features.dialog || request.frameName().isEmpty());
307 if (!request.frameName().isEmpty() && request.frameName() != "_blank")
308 if (Frame* frame = m_frame->tree()->find(request.frameName())) {
309 if (!shouldAllowNavigation(frame))
311 if (!request.resourceRequest().url().isEmpty())
312 frame->loader()->load(request, false, true, 0, 0, HashMap<String, String>());
313 if (Page* page = frame->page())
314 page->chrome()->focus();
319 // FIXME: Setting the referrer should be the caller's responsibility.
320 FrameLoadRequest requestWithReferrer = request;
321 requestWithReferrer.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
323 Page* page = m_frame->page();
325 page = page->chrome()->createWindow(m_frame, requestWithReferrer, features);
329 Frame* frame = page->mainFrame();
330 if (request.frameName() != "_blank")
331 frame->tree()->setName(request.frameName());
333 page->chrome()->setToolbarsVisible(features.toolBarVisible || features.locationBarVisible);
334 page->chrome()->setStatusbarVisible(features.statusBarVisible);
335 page->chrome()->setScrollbarsVisible(features.scrollbarsVisible);
336 page->chrome()->setMenubarVisible(features.menuBarVisible);
337 page->chrome()->setResizable(features.resizable);
339 // 'x' and 'y' specify the location of the window, while 'width' and 'height'
340 // specify the size of the page. We can only resize the window, so
341 // adjust for the difference between the window size and the page size.
343 FloatRect windowRect = page->chrome()->windowRect();
344 FloatSize pageSize = page->chrome()->pageRect().size();
346 windowRect.setX(features.x);
348 windowRect.setY(features.y);
349 if (features.widthSet)
350 windowRect.setWidth(features.width + (windowRect.width() - pageSize.width()));
351 if (features.heightSet)
352 windowRect.setHeight(features.height + (windowRect.height() - pageSize.height()));
353 page->chrome()->setWindowRect(windowRect);
355 page->chrome()->show();
361 bool FrameLoader::canHandleRequest(const ResourceRequest& request)
363 return m_client->canHandleRequest(request);
366 void FrameLoader::changeLocation(const String& URL, const String& referrer, bool lockHistory, bool userGesture)
368 changeLocation(completeURL(URL), referrer, lockHistory, userGesture);
371 void FrameLoader::changeLocation(const KURL& URL, const String& referrer, bool lockHistory, bool userGesture)
373 if (URL.url().find("javascript:", 0, false) == 0) {
374 String script = KURL::decode_string(URL.url().mid(strlen("javascript:")));
375 JSValue* result = executeScript(script, userGesture);
377 if (getString(result, scriptResult)) {
385 ResourceRequestCachePolicy policy = (m_cachePolicy == CachePolicyReload) || (m_cachePolicy == CachePolicyRefresh)
386 ? ReloadIgnoringCacheData : UseProtocolCachePolicy;
387 ResourceRequest request(URL, referrer, policy);
389 urlSelected(request, "_self", 0, lockHistory, userGesture);
392 void FrameLoader::urlSelected(const ResourceRequest& request, const String& _target, Event* triggeringEvent, bool lockHistory, bool userGesture)
394 String target = _target;
395 if (target.isEmpty() && m_frame->document())
396 target = m_frame->document()->baseTarget();
398 const KURL& url = request.url();
399 if (url.url().startsWith("javascript:", false)) {
400 executeScript(KURL::decode_string(url.url().mid(strlen("javascript:"))), true);
404 FrameLoadRequest frameRequest(request, target);
406 if (frameRequest.resourceRequest().httpReferrer().isEmpty())
407 frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
409 urlSelected(frameRequest, triggeringEvent, lockHistory, userGesture);
412 bool FrameLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName)
414 #if USE(LOW_BANDWIDTH_DISPLAY)
415 // don't create sub-frame during low bandwidth display
416 if (frame()->document()->inLowBandwidthDisplay()) {
417 m_needToSwitchOutLowBandwidthDisplay = true;
422 // Support for <frame src="javascript:string">
425 if (urlString.startsWith("javascript:", false)) {
426 scriptURL = urlString.deprecatedString();
429 url = completeURL(urlString);
431 Frame* frame = ownerElement->contentFrame();
433 frame->loader()->scheduleLocationChange(url.url(), m_outgoingReferrer, true, userGestureHint());
435 frame = loadSubframe(ownerElement, url, frameName, m_outgoingReferrer);
440 if (!scriptURL.isEmpty())
441 frame->loader()->replaceContentsWithScriptResult(scriptURL);
446 Frame* FrameLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer)
448 bool allowsScrolling = true;
449 int marginWidth = -1;
450 int marginHeight = -1;
451 if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) {
452 HTMLFrameElementBase* o = static_cast<HTMLFrameElementBase*>(ownerElement);
453 allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff;
454 marginWidth = o->getMarginWidth();
455 marginHeight = o->getMarginHeight();
458 if (!canLoad(url, referrer)) {
459 FrameLoader::reportLocalLoadFailed(m_frame->page(), url.url());
463 bool hideReferrer = shouldHideReferrer(url, referrer);
464 RefPtr<Frame> frame = m_client->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer,
465 allowsScrolling, marginWidth, marginHeight);
468 checkCallImplicitClose();
472 frame->loader()->m_isComplete = false;
474 if (ownerElement->renderer() && frame->view())
475 static_cast<RenderWidget*>(ownerElement->renderer())->setWidget(frame->view());
477 checkCallImplicitClose();
479 // In these cases, the synchronous load would have finished
480 // before we could connect the signals, so make sure to send the
481 // completed() signal for the child by hand
482 // FIXME: In this case the Frame will have finished loading before
483 // it's being added to the child list. It would be a good idea to
484 // create the child first, then invoke the loader separately.
485 if (url.isEmpty() || url == "about:blank") {
486 frame->loader()->completed();
487 frame->loader()->checkCompleted();
493 void FrameLoader::submitFormAgain()
495 if (m_isRunningScript)
497 OwnPtr<FormSubmission> form(m_deferredFormSubmission.release());
499 submitForm(form->action, form->URL, form->data, form->target,
500 form->contentType, form->boundary, form->event.get());
503 void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<FormData> formData,
504 const String& target, const String& contentType, const String& boundary, Event* event)
506 ASSERT(formData.get());
508 KURL u = completeURL(url.isNull() ? "" : url);
509 // FIXME: Do we really need to special-case an empty URL?
510 // Would it be better to just go on with the form submisson and let the I/O fail?
514 DeprecatedString urlString = u.url();
515 if (urlString.startsWith("javascript:", false)) {
516 m_isExecutingJavaScriptFormAction = true;
517 executeScript(KURL::decode_string(urlString.mid(strlen("javascript:"))));
518 m_isExecutingJavaScriptFormAction = false;
522 if (m_isRunningScript) {
523 if (m_deferredFormSubmission)
525 m_deferredFormSubmission.set(new FormSubmission(action, url, formData, target,
526 contentType, boundary, event));
530 FrameLoadRequest frameRequest;
532 if (!m_outgoingReferrer.isEmpty())
533 frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
535 frameRequest.setFrameName(target.isEmpty() ? m_frame->document()->baseTarget() : target);
537 // Handle mailto: forms
538 bool mailtoForm = u.protocol() == "mailto";
542 if (equalIgnoringCase(contentType, "multipart/form-data"))
543 // FIXME: is this correct? I suspect not, but what site can we test this on?
544 body = formData->flattenToString();
545 else if (equalIgnoringCase(contentType, "text/plain"))
546 // Convention seems to be to decode, and s/&/\n/
547 body = KURL::decode_string(
548 formData->flattenToString().replace('&', '\n')
549 .replace('+', ' ').deprecatedString()); // Recode for the URL
551 body = formData->flattenToString();
553 String query = u.query();
554 if (!query.isEmpty())
556 u.setQuery((query + "body=" + KURL::encode_string(body.deprecatedString())).deprecatedString());
559 if (strcmp(action, "GET") == 0) {
561 u.setQuery(formData->flattenToString().deprecatedString());
563 frameRequest.resourceRequest().setHTTPBody(formData.get());
564 frameRequest.resourceRequest().setHTTPMethod("POST");
566 // construct some user headers if necessary
567 if (contentType.isNull() || contentType == "application/x-www-form-urlencoded")
568 frameRequest.resourceRequest().setHTTPContentType(contentType);
569 else // contentType must be "multipart/form-data"
570 frameRequest.resourceRequest().setHTTPContentType(contentType + "; boundary=" + boundary);
573 frameRequest.resourceRequest().setURL(u);
575 submitForm(frameRequest, event);
578 void FrameLoader::stopLoading(bool sendUnload)
580 if (m_frame->document() && m_frame->document()->tokenizer())
581 m_frame->document()->tokenizer()->stopParsing();
584 if (m_frame->document()) {
585 if (m_didCallImplicitClose && !m_wasUnloadEventEmitted) {
586 Node* currentFocusedNode = m_frame->document()->focusedNode();
587 if (currentFocusedNode)
588 currentFocusedNode->aboutToUnload();
589 m_frame->document()->dispatchWindowEvent(unloadEvent, false, false);
590 if (m_frame->document())
591 m_frame->document()->updateRendering();
592 m_wasUnloadEventEmitted = true;
595 if (m_frame->document() && !m_frame->document()->inPageCache())
596 m_frame->document()->removeAllEventListenersFromAllNodes();
599 m_isComplete = true; // to avoid calling completed() in finishedParsing() (David)
600 m_isLoadingMainResource = false;
601 m_didCallImplicitClose = true; // don't want that one either
602 m_cachePolicy = CachePolicyVerify; // Why here?
604 if (m_frame->document() && m_frame->document()->parsing()) {
606 m_frame->document()->setParsing(false);
609 m_workingURL = KURL();
611 if (Document* doc = m_frame->document()) {
612 if (DocLoader* docLoader = doc->docLoader())
613 cache()->loader()->cancelRequests(docLoader);
614 XMLHttpRequest::cancelRequests(doc);
617 // tell all subframes to stop as well
618 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
619 child->loader()->stopLoading(sendUnload);
623 #if USE(LOW_BANDWIDTH_DISPLAY)
624 if (m_frame->document() && m_frame->document()->inLowBandwidthDisplay()) {
625 // Since loading is forced to stop, reset the state without really switching.
626 m_needToSwitchOutLowBandwidthDisplay = false;
627 switchOutLowBandwidthDisplayIfReady();
632 void FrameLoader::stop()
634 // http://bugs.webkit.org/show_bug.cgi?id=10854
635 // The frame's last ref may be removed and it will be deleted by checkCompleted().
636 RefPtr<Frame> protector(m_frame);
638 if (m_frame->document()) {
639 if (m_frame->document()->tokenizer())
640 m_frame->document()->tokenizer()->stopParsing();
641 m_frame->document()->finishParsing();
643 // WebKit partially uses WebCore when loading non-HTML docs. In these cases doc==nil, but
644 // WebCore is enough involved that we need to checkCompleted() in order for m_bComplete to
645 // become true. An example is when a subframe is a pure text doc, and that subframe is the
646 // last one to complete.
649 m_iconLoader->stopLoading();
652 bool FrameLoader::closeURL()
656 m_frame->editor()->clearUndoRedoOperations();
660 void FrameLoader::cancelRedirection(bool cancelWithLoadInProgress)
662 m_cancellingWithLoadInProgress = cancelWithLoadInProgress;
664 stopRedirectionTimer();
666 m_scheduledRedirection.clear();
669 KURL FrameLoader::iconURL()
671 // If this isn't a top level frame, return nothing
672 if (m_frame->tree() && m_frame->tree()->parent())
675 // If we have an iconURL from a Link element, return that
676 if (m_frame->document() && !m_frame->document()->iconURL().isEmpty())
677 return m_frame->document()->iconURL().deprecatedString();
679 // Don't return a favicon iconURL unless we're http or https
680 if (m_URL.protocol() != "http" && m_URL.protocol() != "https")
684 url.setProtocol(m_URL.protocol());
685 url.setHost(m_URL.host());
686 if (int port = m_URL.port())
688 url.setPath("/favicon.ico");
692 bool FrameLoader::didOpenURL(const KURL& url)
694 if (m_scheduledRedirection && m_scheduledRedirection->type == ScheduledRedirection::locationChangeDuringLoad)
695 // A redirect was scheduled before the document was created.
696 // This can happen when one frame changes another frame's location.
700 m_frame->editor()->setLastEditCommand(0);
703 m_isComplete = false;
704 m_isLoadingMainResource = true;
705 m_didCallImplicitClose = false;
707 m_frame->setJSStatusBarText(String());
708 m_frame->setJSDefaultStatusBarText(String());
711 if (m_URL.protocol().startsWith("http") && !m_URL.host().isEmpty() && m_URL.path().isEmpty())
713 m_workingURL = m_URL;
720 void FrameLoader::didExplicitOpen()
722 m_isComplete = false;
723 m_didCallImplicitClose = false;
725 // Prevent window.open(url) -- eg window.open("about:blank") -- from blowing away results
726 // from a subsequent window.document.open / window.document.write call.
727 // Cancelling redirection here works for all cases because document.open
728 // implicitly precedes document.write.
730 if (m_frame->document()->URL() != "about:blank")
731 m_URL = m_frame->document()->URL();
734 void FrameLoader::replaceContentsWithScriptResult(const KURL& url)
736 JSValue* result = executeScript(KURL::decode_string(url.url().mid(strlen("javascript:"))));
738 if (!getString(result, scriptResult))
745 JSValue* FrameLoader::executeScript(const String& script, bool forceUserGesture)
747 return executeScript(forceUserGesture ? String() : String(m_URL.url()), 0, script);
750 JSValue* FrameLoader::executeScript(const String& URL, int baseLine, const String& script)
752 KJSProxy* proxy = m_frame->scriptProxy();
756 bool wasRunningScript = m_isRunningScript;
757 m_isRunningScript = true;
759 JSValue* result = proxy->evaluate(URL, baseLine, script);
761 if (!wasRunningScript) {
762 m_isRunningScript = false;
764 Document::updateDocumentsRendering();
770 void FrameLoader::cancelAndClear()
780 void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects)
782 // FIXME: Commenting out the below line causes <http://bugs.webkit.org/show_bug.cgi?id=11212>, but putting it
783 // back causes a measurable performance regression which we will need to fix to restore the correct behavior
784 // urlsBridgeKnowsAbout.clear();
786 m_frame->editor()->clear();
790 m_needsClear = false;
792 if (m_frame->document() && !m_frame->document()->inPageCache()) {
793 m_frame->document()->cancelParsing();
794 if (m_frame->document()->attached()) {
795 m_frame->document()->willRemove();
796 m_frame->document()->detach();
800 // Do this after detaching the document so that the unload event works.
801 if (clearWindowProperties) {
802 m_frame->clearScriptProxy();
803 m_frame->clearDOMWindow();
806 m_frame->selectionController()->clear();
807 m_frame->eventHandler()->clear();
809 m_frame->view()->clear();
811 // Do not drop the document before the script proxy and view are cleared, as some destructors
812 // might still try to access the document.
813 m_frame->setDocument(0);
816 m_containsPlugIns = false;
818 if (clearScriptObjects)
819 m_frame->clearScriptObjects();
821 m_redirectionTimer.stop();
822 m_scheduledRedirection.clear();
824 m_checkCompletedTimer.stop();
825 m_checkLoadCompleteTimer.stop();
827 m_receivedData = false;
828 m_isDisplayingInitialEmptyDocument = false;
830 if (!m_encodingWasChosenByUser)
831 m_encoding = String();
834 void FrameLoader::receivedFirstData()
836 begin(m_workingURL, false);
838 dispatchDidCommitLoad();
839 dispatchWindowObjectAvailable();
841 String ptitle = m_documentLoader->title();
842 // If we have a title let the WebView know about it.
843 if (!ptitle.isNull())
844 m_client->dispatchDidReceiveTitle(ptitle);
846 m_frame->document()->docLoader()->setCachePolicy(m_cachePolicy);
847 m_workingURL = KURL();
851 if (!m_documentLoader)
853 if (!parseHTTPRefresh(m_documentLoader->response().httpHeaderField("Refresh"), false, delay, URL))
859 URL = m_frame->document()->completeURL(URL);
861 scheduleHTTPRedirection(delay, URL);
864 const String& FrameLoader::responseMIMEType() const
866 return m_responseMIMEType;
869 void FrameLoader::setResponseMIMEType(const String& type)
871 m_responseMIMEType = type;
874 void FrameLoader::begin()
879 void FrameLoader::begin(const KURL& url, bool dispatch)
881 bool resetScripting = !(m_isDisplayingInitialEmptyDocument && m_frame->document() && m_frame->document()->securityOrigin().isSecureTransitionTo(url));
882 clear(resetScripting, resetScripting);
884 dispatchWindowObjectAvailable();
887 m_isComplete = false;
888 m_didCallImplicitClose = false;
889 m_isLoadingMainResource = true;
890 m_isDisplayingInitialEmptyDocument = m_creatingInitialEmptyDocument;
893 ref.setUser(DeprecatedString());
894 ref.setPass(DeprecatedString());
895 ref.setRef(DeprecatedString());
896 m_outgoingReferrer = ref.url();
900 if (!m_URL.isEmpty())
903 RefPtr<Document> document = DOMImplementation::instance()->createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode());
904 m_frame->setDocument(document);
906 document->setURL(m_URL.url());
907 // We prefer m_baseURL over m_URL because m_URL changes when we are
908 // about to load a new page.
909 document->setBaseURL(baseurl.url());
911 document->setDecoder(m_decoder.get());
913 updatePolicyBaseURL();
915 Settings* settings = document->settings();
916 document->docLoader()->setAutoLoadImages(settings && settings->loadsImagesAutomatically());
917 KURL userStyleSheet = settings ? settings->userStyleSheetLocation() : KURL();
918 if (!userStyleSheet.isEmpty())
919 m_frame->setUserStyleSheetLocation(userStyleSheet);
921 restoreDocumentState();
923 document->implicitOpen();
926 m_frame->view()->resizeContents(0, 0);
928 #if USE(LOW_BANDWIDTH_DISPLAY)
929 // Low bandwidth display is a first pass display without external resources
930 // used to give an instant visual feedback. We currently only enable it for
931 // HTML documents in the top frame.
932 if (document->isHTMLDocument() && !m_frame->tree()->parent() && m_useLowBandwidthDisplay) {
933 m_pendingSourceInLowBandwidthDisplay = String();
934 m_finishedParsingDuringLowBandwidthDisplay = false;
935 m_needToSwitchOutLowBandwidthDisplay = false;
936 document->setLowBandwidthDisplay(true);
941 void FrameLoader::write(const char* str, int len, bool flush)
943 if (len == 0 && !flush)
949 Tokenizer* tokenizer = m_frame->document()->tokenizer();
950 if (tokenizer && tokenizer->wantsRawData()) {
952 tokenizer->writeRawData(str, len);
957 Settings* settings = m_frame->settings();
958 m_decoder = new TextResourceDecoder(m_responseMIMEType, settings ? settings->defaultTextEncodingName() : String());
959 if (!m_encoding.isNull())
960 m_decoder->setEncoding(m_encoding,
961 m_encodingWasChosenByUser ? TextResourceDecoder::UserChosenEncoding : TextResourceDecoder::EncodingFromHTTPHeader);
962 if (m_frame->document())
963 m_frame->document()->setDecoder(m_decoder.get());
966 String decoded = m_decoder->decode(str, len);
968 decoded += m_decoder->flush();
969 if (decoded.isEmpty())
972 #if USE(LOW_BANDWIDTH_DISPLAY)
973 if (m_frame->document()->inLowBandwidthDisplay())
974 m_pendingSourceInLowBandwidthDisplay.append(decoded);
975 else // reset policy which is changed in switchOutLowBandwidthDisplayIfReady()
976 m_frame->document()->docLoader()->setCachePolicy(m_cachePolicy);
979 if (!m_receivedData) {
980 m_receivedData = true;
981 m_frame->document()->determineParseMode(decoded);
982 if (m_decoder->encoding().usesVisualOrdering())
983 m_frame->document()->setVisuallyOrdered();
984 m_frame->document()->recalcStyle(Node::Force);
988 ASSERT(!tokenizer->wantsRawData());
989 tokenizer->write(decoded, true);
993 void FrameLoader::write(const String& str)
998 if (!m_receivedData) {
999 m_receivedData = true;
1000 m_frame->document()->setParseMode(Document::Strict);
1003 if (Tokenizer* tokenizer = m_frame->document()->tokenizer())
1004 tokenizer->write(str, true);
1007 void FrameLoader::end()
1009 m_isLoadingMainResource = false;
1010 endIfNotLoadingMainResource();
1013 void FrameLoader::endIfNotLoadingMainResource()
1015 if (m_isLoadingMainResource)
1018 // http://bugs.webkit.org/show_bug.cgi?id=10854
1019 // The frame's last ref may be removed and it can be deleted by checkCompleted(),
1020 // so we'll add a protective refcount
1021 RefPtr<Frame> protector(m_frame);
1023 // make sure nothing's left in there
1024 if (m_frame->document()) {
1026 m_frame->document()->finishParsing();
1027 #if USE(LOW_BANDWIDTH_DISPLAY)
1028 if (m_frame->document()->inLowBandwidthDisplay()) {
1029 m_finishedParsingDuringLowBandwidthDisplay = true;
1030 switchOutLowBandwidthDisplayIfReady();
1034 // WebKit partially uses WebCore when loading non-HTML docs. In these cases doc==nil, but
1035 // WebCore is enough involved that we need to checkCompleted() in order for m_bComplete to
1036 // become true. An example is when a subframe is a pure text doc, and that subframe is the
1037 // last one to complete.
1040 if (m_documentLoader && !m_documentLoader->isLoadingFromCachedPage())
1044 void FrameLoader::iconLoadDecisionAvailable()
1046 if (!m_mayLoadIconLater)
1048 LOG(IconDatabase, "FrameLoader %p was told a load decision is available for its icon", this);
1050 m_mayLoadIconLater = false;
1053 void FrameLoader::startIconLoader()
1055 // FIXME: We kick off the icon loader when the frame is done receiving its main resource.
1056 // But we should instead do it when we're done parsing the head element.
1057 if (!isLoadingMainFrame())
1060 if (!iconDatabase() || !iconDatabase()->isEnabled())
1063 KURL url(iconURL());
1064 String urlString(url.url());
1065 if (urlString.isEmpty())
1068 // If we're not reloading and the icon database doesn't say to load now then bail before we actually start the load
1069 if (loadType() != FrameLoadTypeReload) {
1070 IconLoadDecision decision = iconDatabase()->loadDecisionForIconURL(urlString, m_documentLoader.get());
1071 if (decision == IconLoadNo) {
1072 LOG(IconDatabase, "FrameLoader::startIconLoader() - Told not to load this icon, committing iconURL %s to database for pageURL mapping", urlString.ascii().data());
1073 commitIconURLToIconDatabase(url);
1075 // We were told not to load this icon - that means this icon is already known by the database
1076 // 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
1077 // has done it. This is after registering for the notification so the WebView can call the appropriate delegate method.
1078 // Otherwise if the icon data *is* available, notify the delegate
1079 if (!iconDatabase()->iconDataKnownForIconURL(urlString)) {
1080 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());
1081 m_client->registerForIconNotification();
1082 iconDatabase()->iconForPageURL(m_URL.url(), IntSize(0, 0));
1083 iconDatabase()->iconForPageURL(originalRequestURL().url(), IntSize(0, 0));
1085 m_client->dispatchDidReceiveIcon();
1090 if (decision == IconLoadUnknown) {
1091 // In this case, we may end up loading the icon later, but we still want to commit the icon url mapping to the database
1092 // 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
1093 // We also tell the client to register for the notification that the icon is received now so it isn't missed in case the
1094 // icon is later read in from disk
1095 LOG(IconDatabase, "FrameLoader %p might load icon %s later", this, urlString.ascii().data());
1096 m_mayLoadIconLater = true;
1097 m_client->registerForIconNotification();
1098 commitIconURLToIconDatabase(url);
1103 // This is either a reload or the icon database said "yes, load the icon", so kick off the load!
1105 m_iconLoader.set(IconLoader::create(m_frame).release());
1107 m_iconLoader->startLoading();
1110 bool FrameLoader::restrictAccessToLocal()
1112 return m_restrictAccessToLocal;
1115 void FrameLoader::setRestrictAccessToLocal(bool access)
1117 m_restrictAccessToLocal = access;
1120 static HashSet<String, CaseInsensitiveHash<String> >& localSchemes()
1122 static HashSet<String, CaseInsensitiveHash<String> > localSchemes;
1124 if (localSchemes.isEmpty()) {
1125 localSchemes.add("file");
1126 localSchemes.add("applewebdata");
1129 return localSchemes;
1132 void FrameLoader::commitIconURLToIconDatabase(const KURL& icon)
1134 ASSERT(iconDatabase());
1135 LOG(IconDatabase, "Committing iconURL %s to database for pageURLs %s and %s", icon.url().ascii(), m_URL.url().ascii(), originalRequestURL().url().ascii());
1136 iconDatabase()->setIconURLForPageURL(icon.url(), m_URL.url());
1137 iconDatabase()->setIconURLForPageURL(icon.url(), originalRequestURL().url());
1140 void FrameLoader::restoreDocumentState()
1142 Document* doc = m_frame->document();
1146 HistoryItem* itemToRestore = 0;
1148 switch (loadType()) {
1149 case FrameLoadTypeReload:
1150 case FrameLoadTypeReloadAllowingStaleData:
1151 case FrameLoadTypeSame:
1152 case FrameLoadTypeReplace:
1154 case FrameLoadTypeBack:
1155 case FrameLoadTypeForward:
1156 case FrameLoadTypeIndexedBackForward:
1157 case FrameLoadTypeRedirectWithLockedHistory:
1158 case FrameLoadTypeStandard:
1159 itemToRestore = m_currentHistoryItem.get();
1165 doc->setStateForNewFormElements(itemToRestore->documentState());
1168 void FrameLoader::gotoAnchor()
1170 // If our URL has no ref, then we have no place we need to jump to.
1171 // OTOH if css target was set previously, we want to set it to 0, recalc
1172 // and possibly repaint because :target pseudo class may have been
1173 // set(See bug 11321)
1174 if (!m_URL.hasRef() &&
1175 !(m_frame->document() && m_frame->document()->getCSSTarget()))
1178 DeprecatedString ref = m_URL.encodedHtmlRef();
1179 if (!gotoAnchor(ref)) {
1180 // Can't use htmlRef() here because it doesn't know which encoding to use to decode.
1181 // Decoding here has to match encoding in completeURL, which means it has to use the
1182 // page's encoding rather than UTF-8.
1184 gotoAnchor(KURL::decode_string(ref, m_decoder->encoding()));
1188 void FrameLoader::finishedParsing()
1190 if (m_creatingInitialEmptyDocument)
1193 // This can be called from the Frame's destructor, in which case we shouldn't protect ourselves
1194 // because doing so will cause us to re-enter the destructor when protector goes out of scope.
1195 RefPtr<Frame> protector = m_frame->refCount() > 0 ? m_frame : 0;
1199 if (!m_frame->view())
1200 return; // We are being destroyed by something checkCompleted called.
1202 // Check if the scrollbars are really needed for the content.
1203 // If not, remove them, relayout, and repaint.
1204 m_frame->view()->restoreScrollbar();
1206 m_client->dispatchDidFinishDocumentLoad();
1211 void FrameLoader::loadDone()
1213 if (m_frame->document())
1217 void FrameLoader::checkCompleted()
1219 // Any frame that hasn't completed yet?
1220 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
1221 if (!child->loader()->m_isComplete)
1224 // Have we completed before?
1228 // Are we still parsing?
1229 if (m_frame->document() && m_frame->document()->parsing())
1232 // Still waiting for images/scripts?
1233 if (m_frame->document())
1234 if (numRequests(m_frame->document()))
1237 #if USE(LOW_BANDWIDTH_DISPLAY)
1238 // as switch will be called, don't complete yet
1239 if (m_frame->document() && m_frame->document()->inLowBandwidthDisplay() && m_needToSwitchOutLowBandwidthDisplay)
1244 m_isComplete = true;
1246 RefPtr<Frame> protect(m_frame);
1247 checkCallImplicitClose(); // if we didn't do it before
1249 // Do not start a redirection timer for subframes here.
1250 // That is deferred until the parent is completed.
1251 if (m_scheduledRedirection && !m_frame->tree()->parent())
1252 startRedirectionTimer();
1255 if (m_frame->page())
1256 checkLoadComplete();
1259 void FrameLoader::checkCompletedTimerFired(Timer<FrameLoader>*)
1264 void FrameLoader::scheduleCheckCompleted()
1266 if (!m_checkCompletedTimer.isActive())
1267 m_checkCompletedTimer.startOneShot(0);
1270 void FrameLoader::checkLoadCompleteTimerFired(Timer<FrameLoader>*)
1272 if (m_frame->page())
1273 checkLoadComplete();
1276 void FrameLoader::scheduleCheckLoadComplete()
1278 if (!m_checkLoadCompleteTimer.isActive())
1279 m_checkLoadCompleteTimer.startOneShot(0);
1282 void FrameLoader::checkCallImplicitClose()
1284 if (m_didCallImplicitClose || !m_frame->document() || m_frame->document()->parsing())
1287 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
1288 if (!child->loader()->m_isComplete) // still got a frame running -> too early
1291 // All frames completed -> set their domain to the frameset's domain
1292 // This must only be done when loading the frameset initially (#22039),
1293 // not when following a link in a frame (#44162).
1294 if (m_frame->document()) {
1295 String domain = m_frame->document()->domain();
1296 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
1297 if (child->document())
1298 child->document()->setDomainInternal(domain);
1301 m_didCallImplicitClose = true;
1302 m_wasUnloadEventEmitted = false;
1303 if (m_frame->document())
1304 m_frame->document()->implicitClose();
1307 KURL FrameLoader::baseURL() const
1309 ASSERT(m_frame->document());
1310 return m_frame->document()->baseURL();
1313 String FrameLoader::baseTarget() const
1315 ASSERT(m_frame->document());
1316 return m_frame->document()->baseTarget();
1319 KURL FrameLoader::completeURL(const String& url)
1321 ASSERT(m_frame->document());
1322 return m_frame->document()->completeURL(url).deprecatedString();
1325 void FrameLoader::scheduleHTTPRedirection(double delay, const String& url)
1327 if (delay < 0 || delay > INT_MAX / 1000)
1330 // We want a new history item if the refresh timeout is > 1 second.
1331 if (!m_scheduledRedirection || delay <= m_scheduledRedirection->delay)
1332 scheduleRedirection(new ScheduledRedirection(delay, url, delay <= 1, false));
1335 void FrameLoader::scheduleLocationChange(const String& url, const String& referrer, bool lockHistory, bool wasUserGesture)
1337 // If the URL we're going to navigate to is the same as the current one, except for the
1338 // fragment part, we don't need to schedule the location change.
1339 KURL u(url.deprecatedString());
1340 if (u.hasRef() && equalIgnoringRef(m_URL, u)) {
1341 changeLocation(url, referrer, lockHistory, wasUserGesture);
1345 // Handle a location change of a page with no document as a special case.
1346 // This may happen when a frame changes the location of another frame.
1347 bool duringLoad = !m_committedFirstRealDocumentLoad;
1349 // If a redirect was scheduled during a load, then stop the current load.
1350 // Otherwise when the current load transitions from a provisional to a
1351 // committed state, pending redirects may be cancelled.
1353 if (m_provisionalDocumentLoader)
1354 m_provisionalDocumentLoader->stopLoading();
1358 ScheduledRedirection::Type type = duringLoad
1359 ? ScheduledRedirection::locationChangeDuringLoad : ScheduledRedirection::locationChange;
1360 scheduleRedirection(new ScheduledRedirection(type, url, referrer, lockHistory, wasUserGesture));
1363 void FrameLoader::scheduleRefresh(bool wasUserGesture)
1365 // Handle a location change of a page with no document as a special case.
1366 // This may happen when a frame requests a refresh of another frame.
1367 bool duringLoad = !m_frame->document();
1369 // If a refresh was scheduled during a load, then stop the current load.
1370 // Otherwise when the current load transitions from a provisional to a
1371 // committed state, pending redirects may be cancelled.
1375 ScheduledRedirection::Type type = duringLoad
1376 ? ScheduledRedirection::locationChangeDuringLoad : ScheduledRedirection::locationChange;
1377 scheduleRedirection(new ScheduledRedirection(type, m_URL.url(), m_outgoingReferrer, true, wasUserGesture));
1378 m_cachePolicy = CachePolicyRefresh;
1381 bool FrameLoader::isLocationChange(const ScheduledRedirection& redirection)
1383 switch (redirection.type) {
1384 case ScheduledRedirection::redirection:
1386 case ScheduledRedirection::historyNavigation:
1387 case ScheduledRedirection::locationChange:
1388 case ScheduledRedirection::locationChangeDuringLoad:
1391 ASSERT_NOT_REACHED();
1395 void FrameLoader::scheduleHistoryNavigation(int steps)
1397 // navigation will always be allowed in the 0 steps case, which is OK because
1398 // that's supposed to force a reload.
1399 if (!canGoBackOrForward(steps)) {
1400 cancelRedirection();
1404 // If the steps to navigate is not zero (which needs to force a reload), and if the URL we're going to navigate
1405 // to is the same as the current one, except for the fragment part, we don't need to schedule the navigation.
1406 if (steps != 0 && equalIgnoringRef(m_URL, historyURL(steps))) {
1407 goBackOrForward(steps);
1411 scheduleRedirection(new ScheduledRedirection(steps));
1414 void FrameLoader::goBackOrForward(int distance)
1419 Page* page = m_frame->page();
1422 BackForwardList* list = page->backForwardList();
1426 HistoryItem* item = list->itemAtIndex(distance);
1429 int forwardListCount = list->forwardListCount();
1430 if (forwardListCount > 0)
1431 item = list->itemAtIndex(forwardListCount);
1433 int backListCount = list->backListCount();
1434 if (backListCount > 0)
1435 item = list->itemAtIndex(-backListCount);
1439 ASSERT(item); // we should not reach this line with an empty back/forward list
1441 page->goToItem(item, FrameLoadTypeIndexedBackForward);
1444 void FrameLoader::redirectionTimerFired(Timer<FrameLoader>*)
1446 OwnPtr<ScheduledRedirection> redirection(m_scheduledRedirection.release());
1448 switch (redirection->type) {
1449 case ScheduledRedirection::redirection:
1450 case ScheduledRedirection::locationChange:
1451 case ScheduledRedirection::locationChangeDuringLoad:
1452 changeLocation(redirection->URL, redirection->referrer,
1453 redirection->lockHistory, redirection->wasUserGesture);
1455 case ScheduledRedirection::historyNavigation:
1456 if (redirection->historySteps == 0) {
1457 // Special case for go(0) from a frame -> reload only the frame
1458 urlSelected(m_URL, "", 0, redirection->lockHistory, redirection->wasUserGesture);
1461 // go(i!=0) from a frame navigates into the history of the frame only,
1462 // in both IE and NS (but not in Mozilla). We can't easily do that.
1463 goBackOrForward(redirection->historySteps);
1466 ASSERT_NOT_REACHED();
1469 String FrameLoader::encoding() const
1471 if (m_encodingWasChosenByUser && !m_encoding.isEmpty())
1473 if (m_decoder && m_decoder->encoding().isValid())
1474 return m_decoder->encoding().name();
1475 Settings* settings = m_frame->settings();
1476 return settings ? settings->defaultTextEncodingName() : String();
1479 bool FrameLoader::gotoAnchor(const String& name)
1481 ASSERT(m_frame->document());
1483 if (!m_frame->document()->haveStylesheetsLoaded()) {
1484 m_frame->document()->setGotoAnchorNeededAfterStylesheetsLoad(true);
1488 m_frame->document()->setGotoAnchorNeededAfterStylesheetsLoad(false);
1490 Node* anchorNode = m_frame->document()->getElementById(AtomicString(name));
1492 anchorNode = m_frame->document()->anchors()->namedItem(name, !m_frame->document()->inCompatMode());
1495 if (m_frame->document()->isSVGDocument()) {
1496 if (name.startsWith("xpointer(")) {
1497 // We need to parse the xpointer reference here
1498 } else if (name.startsWith("svgView(")) {
1499 RefPtr<SVGSVGElement> svg = static_cast<SVGDocument*>(m_frame->document())->rootElement();
1500 if (!svg->currentView()->parseViewSpec(name))
1502 svg->setUseCurrentView(true);
1504 if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) {
1505 RefPtr<SVGViewElement> viewElement = anchorNode->hasTagName(SVGNames::viewTag) ? static_cast<SVGViewElement*>(anchorNode) : 0;
1506 if (viewElement.get()) {
1507 RefPtr<SVGSVGElement> svg = static_cast<SVGSVGElement*>(SVGLocatable::nearestViewportElement(viewElement.get()));
1508 svg->inheritViewAttributes(viewElement.get());
1512 // FIXME: need to decide which <svg> to focus on, and zoom to that one
1513 // FIXME: need to actually "highlight" the viewTarget(s)
1517 m_frame->document()->setCSSTarget(anchorNode); // Setting to null will clear the current target.
1519 // Implement the rule that "" and "top" both mean top of page as in other browsers.
1520 if (!anchorNode && !(name.isEmpty() || equalIgnoringCase(name, "top")))
1523 // We need to update the layout before scrolling, otherwise we could
1524 // really mess things up if an anchor scroll comes at a bad moment.
1525 if (m_frame->document()) {
1526 m_frame->document()->updateRendering();
1527 // Only do a layout if changes have occurred that make it necessary.
1528 if (m_frame->view() && m_frame->document()->renderer() && m_frame->document()->renderer()->needsLayout())
1529 m_frame->view()->layout();
1532 // Scroll nested layers and frames to reveal the anchor.
1533 // Align to the top and to the closest side (this matches other browsers).
1534 RenderObject* renderer;
1537 renderer = m_frame->document()->renderer(); // top of document
1539 renderer = anchorNode->renderer();
1540 rect = anchorNode->getRect();
1543 renderer->enclosingLayer()->scrollRectToVisible(rect, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignTopAlways);
1548 bool FrameLoader::requestObject(RenderPart* renderer, const String& url, const AtomicString& frameName,
1549 const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
1551 if (url.isEmpty() && mimeType.isEmpty())
1554 #if USE(LOW_BANDWIDTH_DISPLAY)
1555 // don't care object during low bandwidth display
1556 if (frame()->document()->inLowBandwidthDisplay()) {
1557 m_needToSwitchOutLowBandwidthDisplay = true;
1564 completedURL = completeURL(url);
1567 if (shouldUsePlugin(completedURL, mimeType, renderer->hasFallbackContent(), useFallback)) {
1568 Settings* settings = m_frame->settings();
1569 if (!settings || !settings->arePluginsEnabled() ||
1570 (!settings->isJavaEnabled() && MIMETypeRegistry::isJavaAppletMIMEType(mimeType)))
1572 return loadPlugin(renderer, completedURL, mimeType, paramNames, paramValues, useFallback);
1575 ASSERT(renderer->node()->hasTagName(objectTag) || renderer->node()->hasTagName(embedTag));
1576 HTMLPlugInElement* element = static_cast<HTMLPlugInElement*>(renderer->node());
1578 // FIXME: OK to always make a new frame? When does the old frame get removed?
1579 return loadSubframe(element, completedURL, frameName, m_outgoingReferrer);
1582 bool FrameLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool hasFallback, bool& useFallback)
1584 ObjectContentType objectType = m_client->objectContentType(url, mimeType);
1585 // If an object's content can't be handled and it has no fallback, let
1586 // it be handled as a plugin to show the broken plugin icon.
1587 useFallback = objectType == ObjectContentNone && hasFallback;
1588 return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin;
1591 bool FrameLoader::loadPlugin(RenderPart* renderer, const KURL& url, const String& mimeType,
1592 const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
1596 if (renderer && !useFallback) {
1597 Element* pluginElement = 0;
1598 if (renderer->node() && renderer->node()->isElementNode())
1599 pluginElement = static_cast<Element*>(renderer->node());
1601 if (!canLoad(url, frame()->document())) {
1602 FrameLoader::reportLocalLoadFailed(m_frame->page(), url.url());
1606 widget = m_client->createPlugin(IntSize(renderer->contentWidth(), renderer->contentHeight()),
1607 pluginElement, url, paramNames, paramValues, mimeType,
1608 m_frame->document()->isPluginDocument());
1610 renderer->setWidget(widget);
1611 m_containsPlugIns = true;
1618 void FrameLoader::clearRecordedFormValues()
1620 m_formAboutToBeSubmitted = 0;
1621 m_formValuesAboutToBeSubmitted.clear();
1624 void FrameLoader::recordFormValue(const String& name, const String& value, PassRefPtr<HTMLFormElement> element)
1626 m_formAboutToBeSubmitted = element;
1627 m_formValuesAboutToBeSubmitted.set(name, value);
1630 void FrameLoader::parentCompleted()
1632 if (m_scheduledRedirection && !m_redirectionTimer.isActive())
1633 startRedirectionTimer();
1636 String FrameLoader::outgoingReferrer() const
1638 return m_outgoingReferrer;
1641 Frame* FrameLoader::opener()
1646 void FrameLoader::setOpener(Frame* opener)
1649 m_opener->loader()->m_openedFrames.remove(m_frame);
1651 opener->loader()->m_openedFrames.add(m_frame);
1654 if (m_frame->document())
1655 m_frame->document()->initSecurityOrigin();
1658 bool FrameLoader::openedByDOM() const
1660 return m_openedByDOM;
1663 void FrameLoader::setOpenedByDOM()
1665 m_openedByDOM = true;
1668 void FrameLoader::handleFallbackContent()
1670 HTMLFrameOwnerElement* owner = m_frame->ownerElement();
1671 if (!owner || !owner->hasTagName(objectTag))
1673 static_cast<HTMLObjectElement*>(owner)->renderFallbackContent();
1676 void FrameLoader::provisionalLoadStarted()
1678 Page* page = m_frame->page();
1680 // this is used to update the current history item
1681 // in the event of a navigation aytime during loading
1682 m_navigationDuringLoad = false;
1684 Document *document = page->mainFrame()->document();
1685 m_navigationDuringLoad = !page->mainFrame()->loader()->isComplete() || (document && document->processingLoadEvent());
1688 m_firstLayoutDone = false;
1689 cancelRedirection(true);
1690 m_client->provisionalLoadStarted();
1692 if (canCachePage() && m_client->canCachePage() && !m_currentHistoryItem->isInPageCache())
1693 cachePageForHistoryItem(m_currentHistoryItem.get());
1696 bool FrameLoader::userGestureHint()
1698 Frame* rootFrame = m_frame;
1699 while (rootFrame->tree()->parent())
1700 rootFrame = rootFrame->tree()->parent();
1702 if (rootFrame->scriptProxy())
1703 return rootFrame->scriptProxy()->interpreter()->wasRunByUserGesture();
1705 return true; // If JavaScript is disabled, a user gesture must have initiated the navigation
1708 void FrameLoader::didNotOpenURL(const KURL& URL)
1710 if (m_submittedFormURL == URL)
1711 m_submittedFormURL = KURL();
1714 void FrameLoader::resetMultipleFormSubmissionProtection()
1716 m_submittedFormURL = KURL();
1719 void FrameLoader::setEncoding(const String& name, bool userChosen)
1721 if (!m_workingURL.isEmpty())
1722 receivedFirstData();
1724 m_encodingWasChosenByUser = userChosen;
1727 void FrameLoader::addData(const char* bytes, int length)
1729 ASSERT(m_workingURL.isEmpty());
1730 ASSERT(m_frame->document());
1731 ASSERT(m_frame->document()->parsing());
1732 write(bytes, length);
1735 bool FrameLoader::canCachePage()
1737 // Cache the page, if possible.
1738 // Don't write to the cache if in the middle of a redirect, since we will want to
1739 // store the final page we end up on.
1740 // No point writing to the cache on a reload or loadSame, since we will just write
1741 // over it again when we leave that page.
1742 // FIXME: <rdar://problem/4886592> - We should work out the complexities of caching pages with frames as they
1743 // are the most interesting pages on the web, and often those that would benefit the most from caching!
1744 FrameLoadType loadType = this->loadType();
1746 return m_documentLoader
1747 && m_documentLoader->mainDocumentError().isNull()
1748 && !m_frame->tree()->childCount()
1749 && !m_frame->tree()->parent()
1750 // FIXME: If we ever change this so that pages with plug-ins will be cached,
1751 // we need to make sure that we don't cache pages that have outstanding NPObjects
1752 // (objects created by the plug-in). Since there is no way to pause/resume a Netscape plug-in,
1753 // they would need to be destroyed and then recreated, and there is no way that we can recreate
1754 // the right NPObjects. See <rdar://problem/5197041> for more information.
1755 && !m_containsPlugIns
1756 && !m_URL.protocol().startsWith("https")
1757 && m_frame->document()
1758 && !m_frame->document()->applets()->length()
1759 && !m_frame->document()->hasWindowEventListener(unloadEvent)
1761 && m_frame->page()->backForwardList()->enabled()
1762 && m_frame->page()->backForwardList()->capacity() > 0
1763 && m_frame->page()->settings()->usesPageCache()
1764 && m_currentHistoryItem
1765 && !isQuickRedirectComing()
1766 && loadType != FrameLoadTypeReload
1767 && loadType != FrameLoadTypeReloadAllowingStaleData
1768 && loadType != FrameLoadTypeSame
1769 && !m_documentLoader->isLoadingInAPISense()
1770 && !m_documentLoader->isStopping();
1773 void FrameLoader::updatePolicyBaseURL()
1775 if (m_frame->tree()->parent() && m_frame->tree()->parent()->document())
1776 setPolicyBaseURL(m_frame->tree()->parent()->document()->policyBaseURL());
1778 setPolicyBaseURL(m_URL.url());
1781 void FrameLoader::setPolicyBaseURL(const String& s)
1783 if (m_frame->document())
1784 m_frame->document()->setPolicyBaseURL(s);
1785 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
1786 child->loader()->setPolicyBaseURL(s);
1789 // This does the same kind of work that FrameLoader::openURL does, except it relies on the fact
1790 // that a higher level already checked that the URLs match and the scrolling is the right thing to do.
1791 void FrameLoader::scrollToAnchor(const KURL& URL)
1798 // It's important to model this as a load that starts and immediately finishes.
1799 // Otherwise, the parent frame may think we never finished loading.
1800 m_isComplete = false;
1804 bool FrameLoader::isComplete() const
1806 return m_isComplete;
1809 void FrameLoader::scheduleRedirection(ScheduledRedirection* redirection)
1811 stopRedirectionTimer();
1812 m_scheduledRedirection.set(redirection);
1813 if (!m_isComplete && redirection->type != ScheduledRedirection::redirection)
1815 if (m_isComplete || redirection->type != ScheduledRedirection::redirection)
1816 startRedirectionTimer();
1819 void FrameLoader::startRedirectionTimer()
1821 ASSERT(m_scheduledRedirection);
1823 m_redirectionTimer.stop();
1824 m_redirectionTimer.startOneShot(m_scheduledRedirection->delay);
1826 switch (m_scheduledRedirection->type) {
1827 case ScheduledRedirection::redirection:
1828 case ScheduledRedirection::locationChange:
1829 case ScheduledRedirection::locationChangeDuringLoad:
1830 clientRedirected(m_scheduledRedirection->URL.deprecatedString(),
1831 m_scheduledRedirection->delay,
1832 currentTime() + m_redirectionTimer.nextFireInterval(),
1833 m_scheduledRedirection->lockHistory,
1834 m_isExecutingJavaScriptFormAction);
1836 case ScheduledRedirection::historyNavigation:
1837 // Don't report history navigations.
1840 ASSERT_NOT_REACHED();
1843 void FrameLoader::stopRedirectionTimer()
1845 if (!m_redirectionTimer.isActive())
1848 m_redirectionTimer.stop();
1850 if (m_scheduledRedirection) {
1851 switch (m_scheduledRedirection->type) {
1852 case ScheduledRedirection::redirection:
1853 case ScheduledRedirection::locationChange:
1854 case ScheduledRedirection::locationChangeDuringLoad:
1855 clientRedirectCancelledOrFinished(m_cancellingWithLoadInProgress);
1857 case ScheduledRedirection::historyNavigation:
1858 // Don't report history navigations.
1861 ASSERT_NOT_REACHED();
1865 void FrameLoader::completed()
1867 RefPtr<Frame> protect(m_frame);
1868 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
1869 child->loader()->parentCompleted();
1870 if (Frame* parent = m_frame->tree()->parent())
1871 parent->loader()->checkCompleted();
1875 void FrameLoader::started()
1877 for (Frame* frame = m_frame; frame; frame = frame->tree()->parent())
1878 frame->loader()->m_isComplete = false;
1881 bool FrameLoader::containsPlugins() const
1883 return m_containsPlugIns;
1886 void FrameLoader::prepareForLoadStart()
1888 if (Page* page = m_frame->page())
1889 page->progress()->progressStarted(m_frame);
1890 m_client->dispatchDidStartProvisionalLoad();
1893 void FrameLoader::setupForReplace()
1895 setState(FrameStateProvisional);
1896 m_provisionalDocumentLoader = m_documentLoader;
1897 m_documentLoader = 0;
1901 void FrameLoader::setupForReplaceByMIMEType(const String& newMIMEType)
1903 activeDocumentLoader()->setupForReplaceByMIMEType(newMIMEType);
1906 void FrameLoader::finalSetupForReplace(DocumentLoader* loader)
1908 m_client->clearUnarchivingState(loader);
1911 void FrameLoader::load(const KURL& URL, Event* event)
1913 load(ResourceRequest(URL), false, true, event, 0, HashMap<String, String>());
1916 void FrameLoader::load(const FrameLoadRequest& request, bool lockHistory, bool userGesture, Event* event,
1917 HTMLFormElement* submitForm, const HashMap<String, String>& formValues)
1919 KURL url = request.resourceRequest().url();
1922 String argsReferrer = request.resourceRequest().httpReferrer();
1923 if (!argsReferrer.isEmpty())
1924 referrer = argsReferrer;
1926 referrer = m_outgoingReferrer;
1928 ASSERT(frame()->document());
1929 if (url.url().startsWith("file:", false)) {
1930 if (!canLoad(url, frame()->document()) && !canLoad(url, referrer)) {
1931 FrameLoader::reportLocalLoadFailed(m_frame->page(), url.url());
1936 if (shouldHideReferrer(url, referrer))
1937 referrer = String();
1939 Frame* targetFrame = m_frame->tree()->find(request.frameName());
1940 if (!shouldAllowNavigation(targetFrame))
1943 if (request.resourceRequest().httpMethod() != "POST") {
1944 FrameLoadType loadType;
1945 if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData)
1946 loadType = FrameLoadTypeReload;
1947 else if (lockHistory)
1948 loadType = FrameLoadTypeRedirectWithLockedHistory;
1950 loadType = FrameLoadTypeStandard;
1952 RefPtr<FormState> formState;
1953 if (submitForm && !formValues.isEmpty())
1954 formState = FormState::create(submitForm, formValues, m_frame);
1956 load(request.resourceRequest().url(), referrer, loadType,
1957 request.frameName(), event, formState.release());
1959 post(request.resourceRequest().url(), referrer, request.frameName(),
1960 request.resourceRequest().httpBody(), request.resourceRequest().httpContentType(), event, submitForm, formValues);
1962 if (targetFrame && targetFrame != m_frame)
1963 if (Page* page = targetFrame->page())
1964 page->chrome()->focus();
1967 void FrameLoader::load(const KURL& URL, const String& referrer, FrameLoadType newLoadType,
1968 const String& frameName, Event* event, PassRefPtr<FormState> formState)
1970 bool isFormSubmission = formState;
1972 ResourceRequest request(URL);
1973 if (!referrer.isEmpty())
1974 request.setHTTPReferrer(referrer);
1975 addExtraFieldsToRequest(request, true, event || isFormSubmission);
1976 if (newLoadType == FrameLoadTypeReload)
1977 request.setCachePolicy(ReloadIgnoringCacheData);
1979 ASSERT(newLoadType != FrameLoadTypeSame);
1981 NavigationAction action(URL, newLoadType, isFormSubmission, event);
1983 if (!frameName.isEmpty()) {
1984 if (Frame* targetFrame = m_frame->tree()->find(frameName))
1985 targetFrame->loader()->load(URL, referrer, newLoadType, String(), event, formState);
1987 checkNewWindowPolicy(action, request, formState, frameName);
1991 RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader;
1993 bool sameURL = shouldTreatURLAsSameAsCurrent(URL);
1995 // Make sure to do scroll to anchor processing even if the URL is
1996 // exactly the same so pages with '#' links and DHTML side effects
1998 if (!isFormSubmission
1999 && newLoadType != FrameLoadTypeReload
2000 && newLoadType != FrameLoadTypeSame
2001 && !shouldReload(URL, url())
2002 // We don't want to just scroll if a link from within a
2003 // frameset is trying to reload the frameset into _top.
2004 && !m_frame->isFrameSet()) {
2006 // Just do anchor navigation within the existing content.
2008 // We don't do this if we are submitting a form, explicitly reloading,
2009 // currently displaying a frameset, or if the new URL does not have a fragment.
2010 // These rules are based on what KHTML was doing in KHTMLPart::openURL.
2012 // FIXME: What about load types other than Standard and Reload?
2014 oldDocumentLoader->setTriggeringAction(action);
2016 checkNavigationPolicy(request, oldDocumentLoader.get(), formState,
2017 callContinueFragmentScrollAfterNavigationPolicy, this);
2019 // must grab this now, since this load may stop the previous load and clear this flag
2020 bool isRedirect = m_quickRedirectComing;
2021 load(request, action, newLoadType, formState);
2023 m_quickRedirectComing = false;
2024 if (m_provisionalDocumentLoader)
2025 m_provisionalDocumentLoader->setIsClientRedirect(true);
2027 // Example of this case are sites that reload the same URL with a different cookie
2028 // driving the generated content, or a master frame with links that drive a target
2029 // frame, where the user has clicked on the same link repeatedly.
2030 m_loadType = FrameLoadTypeSame;
2034 void FrameLoader::load(const ResourceRequest& request)
2036 load(request, SubstituteData());
2039 void FrameLoader::load(const ResourceRequest& request, const SubstituteData& substituteData)
2041 if (m_inStopAllLoaders)
2044 // FIXME: is this the right place to reset loadType? Perhaps this should be done after loading is finished or aborted.
2045 m_loadType = FrameLoadTypeStandard;
2046 load(m_client->createDocumentLoader(request, substituteData).get());
2049 void FrameLoader::load(const ResourceRequest& request, const String& frameName)
2051 if (frameName.isEmpty()) {
2056 Frame* frame = m_frame->tree()->find(frameName);
2058 frame->loader()->load(request);
2062 checkNewWindowPolicy(NavigationAction(request.url(), NavigationTypeOther), request, 0, frameName);
2065 void FrameLoader::load(const ResourceRequest& request, const NavigationAction& action, FrameLoadType type, PassRefPtr<FormState> formState)
2067 RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData());
2069 loader->setTriggeringAction(action);
2070 if (m_documentLoader)
2071 loader->setOverrideEncoding(m_documentLoader->overrideEncoding());
2073 load(loader.get(), type, formState);
2076 void FrameLoader::load(DocumentLoader* newDocumentLoader)
2078 ResourceRequest& r = newDocumentLoader->request();
2079 addExtraFieldsToRequest(r, true, false);
2082 if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->originalRequest().url())) {
2083 r.setCachePolicy(ReloadIgnoringCacheData);
2084 type = FrameLoadTypeSame;
2086 type = FrameLoadTypeStandard;
2088 if (m_documentLoader)
2089 newDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding());
2091 // When we loading alternate content for an unreachable URL that we're
2092 // visiting in the b/f list, we treat it as a reload so the b/f list
2093 // is appropriately maintained.
2094 if (shouldReloadToHandleUnreachableURL(newDocumentLoader)) {
2095 ASSERT(type == FrameLoadTypeStandard);
2096 type = FrameLoadTypeReload;
2099 load(newDocumentLoader, type, 0);
2102 void FrameLoader::load(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> formState)
2104 ASSERT(m_client->hasWebView());
2106 // Unfortunately the view must be non-nil, this is ultimately due
2107 // to parser requiring a FrameView. We should fix this dependency.
2109 ASSERT(m_client->hasFrameView());
2111 m_policyLoadType = type;
2113 if (Frame* parent = m_frame->tree()->parent())
2114 loader->setOverrideEncoding(parent->loader()->documentLoader()->overrideEncoding());
2117 setPolicyDocumentLoader(loader);
2119 checkNavigationPolicy(loader->request(), loader, formState,
2120 callContinueLoadAfterNavigationPolicy, this);
2123 // FIXME: It would be nice if we could collapse these into one or two functions.
2124 bool FrameLoader::canLoad(const KURL& url, const String& referrer)
2126 if (!shouldTreatURLAsLocal(url.url()))
2129 return shouldTreatURLAsLocal(referrer);
2132 bool FrameLoader::canLoad(const KURL& url, const Document* doc)
2134 if (!shouldTreatURLAsLocal(url.url()))
2137 return doc && doc->isAllowedToLoadLocalResources();
2140 bool FrameLoader::canLoad(const CachedResource& resource, const Document* doc)
2142 if (!resource.treatAsLocal())
2145 return doc && doc->isAllowedToLoadLocalResources();
2148 void FrameLoader::reportLocalLoadFailed(const Page* page, const String& url)
2150 ASSERT(!url.isEmpty());
2152 page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, "Not allowed to load local resource: " + url, 0, String());
2155 bool FrameLoader::shouldHideReferrer(const KURL& url, const String& referrer)
2157 bool referrerIsSecureURL = referrer.startsWith("https:", false);
2158 bool referrerIsWebURL = referrerIsSecureURL || referrer.startsWith("http:", false);
2160 if (!referrerIsWebURL)
2163 if (!referrerIsSecureURL)
2166 bool URLIsSecureURL = url.url().startsWith("https:", false);
2168 return !URLIsSecureURL;
2171 const ResourceRequest& FrameLoader::initialRequest() const
2173 return activeDocumentLoader()->initialRequest();
2176 void FrameLoader::receivedData(const char* data, int length)
2178 activeDocumentLoader()->receivedData(data, length);
2181 bool FrameLoader::willUseArchive(ResourceLoader* loader, const ResourceRequest& request, const KURL& originalURL) const
2183 return m_client->willUseArchive(loader, request, originalURL);
2186 void FrameLoader::handleUnimplementablePolicy(const ResourceError& error)
2188 m_delegateIsHandlingUnimplementablePolicy = true;
2189 m_client->dispatchUnableToImplementPolicy(error);
2190 m_delegateIsHandlingUnimplementablePolicy = false;
2193 void FrameLoader::cannotShowMIMEType(const ResourceResponse& response)
2195 handleUnimplementablePolicy(m_client->cannotShowMIMETypeError(response));
2198 ResourceError FrameLoader::interruptionForPolicyChangeError(const ResourceRequest& request)
2200 return m_client->interruptForPolicyChangeError(request);
2203 void FrameLoader::checkNavigationPolicy(const ResourceRequest& newRequest, NavigationPolicyDecisionFunction function, void* argument)
2205 checkNavigationPolicy(newRequest, activeDocumentLoader(), 0, function, argument);
2208 void FrameLoader::checkContentPolicy(const String& MIMEType, ContentPolicyDecisionFunction function, void* argument)
2210 ASSERT(activeDocumentLoader());
2212 // Always show content with valid substitute data.
2213 if (activeDocumentLoader()->substituteData().isValid()) {
2214 function(argument, PolicyUse);
2219 // Respect the hidden FTP Directory Listing pref so it can be tested even if the policy delegate might otherwise disallow it
2220 Settings* settings = m_frame->settings();
2221 if (settings && settings->forceFTPDirectoryListings() && MIMEType == "application/x-ftp-directory") {
2222 function(argument, PolicyUse);
2227 m_policyCheck.set(function, argument);
2228 m_client->dispatchDecidePolicyForMIMEType(&FrameLoader::continueAfterContentPolicy,
2229 MIMEType, activeDocumentLoader()->request());
2232 bool FrameLoader::shouldReloadToHandleUnreachableURL(DocumentLoader* docLoader)
2234 KURL unreachableURL = docLoader->unreachableURL();
2236 if (unreachableURL.isEmpty())
2239 if (!isBackForwardLoadType(m_policyLoadType))
2242 // We only treat unreachableURLs specially during the delegate callbacks
2243 // for provisional load errors and navigation policy decisions. The former
2244 // case handles well-formed URLs that can't be loaded, and the latter
2245 // case handles malformed URLs and unknown schemes. Loading alternate content
2246 // at other times behaves like a standard load.
2247 DocumentLoader* compareDocumentLoader = 0;
2248 if (m_delegateIsDecidingNavigationPolicy || m_delegateIsHandlingUnimplementablePolicy)
2249 compareDocumentLoader = m_policyDocumentLoader.get();
2250 else if (m_delegateIsHandlingProvisionalLoadError)
2251 compareDocumentLoader = m_provisionalDocumentLoader.get();
2253 return compareDocumentLoader && unreachableURL == compareDocumentLoader->request().url();
2256 void FrameLoader::reloadAllowingStaleData(const String& encoding)
2258 if (!m_documentLoader)
2261 ResourceRequest request = m_documentLoader->request();
2262 KURL unreachableURL = m_documentLoader->unreachableURL();
2263 if (!unreachableURL.isEmpty())
2264 request.setURL(unreachableURL);
2266 request.setCachePolicy(ReturnCacheDataElseLoad);
2268 RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData());
2269 setPolicyDocumentLoader(loader.get());
2271 loader->setOverrideEncoding(encoding);
2273 load(loader.get(), FrameLoadTypeReloadAllowingStaleData, 0);
2276 void FrameLoader::reload()
2278 if (!m_documentLoader)
2281 ResourceRequest& initialRequest = m_documentLoader->request();
2283 // If a window is created by javascript, its main frame can have an empty but non-nil URL.
2284 // Reloading in this case will lose the current contents (see 4151001).
2285 if (initialRequest.url().isEmpty())
2288 // Replace error-page URL with the URL we were trying to reach.
2289 KURL unreachableURL = m_documentLoader->unreachableURL();
2290 if (!unreachableURL.isEmpty())
2291 initialRequest = ResourceRequest(unreachableURL);
2293 RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(initialRequest, SubstituteData());
2295 ResourceRequest& request = loader->request();
2297 request.setCachePolicy(ReloadIgnoringCacheData);
2298 request.setHTTPHeaderField("Cache-Control", "max-age=0");
2300 // If we're about to re-post, set up action so the application can warn the user.
2301 if (request.httpMethod() == "POST")
2302 loader->setTriggeringAction(NavigationAction(request.url(), NavigationTypeFormResubmitted));
2304 loader->setOverrideEncoding(m_documentLoader->overrideEncoding());
2306 load(loader.get(), FrameLoadTypeReload, 0);
2309 bool FrameLoader::shouldAllowNavigation(Frame* targetFrame) const
2311 // The navigation change is safe if the active frame is:
2312 // - in the same security origin as the target or one of the target's ancestors
2313 // Or the target frame is:
2314 // - a top-level frame in the frame hierarchy
2319 if (m_frame == targetFrame)
2322 if (!targetFrame->tree()->parent())
2325 Document* activeDocument = m_frame->document();
2326 ASSERT(activeDocument);
2327 const SecurityOrigin& activeSecurityOrigin = activeDocument->securityOrigin();
2328 for (Frame* ancestorFrame = targetFrame; ancestorFrame; ancestorFrame = ancestorFrame->tree()->parent()) {
2329 Document* ancestorDocument = ancestorFrame->document();
2330 if (!ancestorDocument)
2333 const SecurityOrigin& ancestorSecurityOrigin = ancestorDocument->securityOrigin();
2334 if (activeSecurityOrigin.canAccess(ancestorSecurityOrigin))
2338 if (!targetFrame->settings()->privateBrowsingEnabled()) {
2339 Document* targetDocument = targetFrame->document();
2340 // FIXME: this error message should contain more specifics of why the navigation change is not allowed.
2341 String message = String::format("Unsafe JavaScript attempt to initiate a navigation change for frame with URL %s from frame with URL %s.\n",
2342 targetDocument->URL().utf8().data(), activeDocument->URL().utf8().data());
2344 if (KJS::Interpreter::shouldPrintExceptions())
2345 printf("%s", message.utf8().data());
2347 // FIXME: should we print to the console of the activeFrame as well?
2348 if (Page* page = targetFrame->page())
2349 page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 1, String());
2355 void FrameLoader::stopLoadingSubframes()
2357 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
2358 child->loader()->stopAllLoaders();
2361 void FrameLoader::stopAllLoaders()
2363 // If this method is called from within this method, infinite recursion can occur (3442218). Avoid this.
2364 if (m_inStopAllLoaders)
2367 m_inStopAllLoaders = true;
2371 stopLoadingSubframes();
2372 if (m_provisionalDocumentLoader)
2373 m_provisionalDocumentLoader->stopLoading();
2374 if (m_documentLoader)
2375 m_documentLoader->stopLoading();
2376 setProvisionalDocumentLoader(0);
2377 m_client->clearArchivedResources();
2379 m_inStopAllLoaders = false;
2382 void FrameLoader::stopForUserCancel(bool deferCheckLoadComplete)
2386 if (deferCheckLoadComplete)
2387 scheduleCheckLoadComplete();
2388 else if (m_frame->page())
2389 checkLoadComplete();
2392 void FrameLoader::cancelPendingArchiveLoad(ResourceLoader* loader)
2394 m_client->cancelPendingArchiveLoad(loader);
2397 DocumentLoader* FrameLoader::activeDocumentLoader() const
2399 if (m_state == FrameStateProvisional)
2400 return m_provisionalDocumentLoader.get();
2401 return m_documentLoader.get();
2404 bool FrameLoader::isLoading() const
2406 DocumentLoader* docLoader = activeDocumentLoader();
2409 return docLoader->isLoadingMainResource() || docLoader->isLoadingSubresources() || docLoader->isLoadingPlugIns();
2412 bool FrameLoader::frameHasLoaded() const
2414 return m_committedFirstRealDocumentLoad || (m_provisionalDocumentLoader && !m_creatingInitialEmptyDocument);
2417 void FrameLoader::setDocumentLoader(DocumentLoader* loader)
2419 if (!loader && !m_documentLoader)
2422 ASSERT(loader != m_documentLoader);
2423 ASSERT(!loader || loader->frameLoader() == this);
2425 m_client->prepareForDataSourceReplacement();
2427 if (m_documentLoader)
2428 m_documentLoader->detachFromFrame();
2430 m_documentLoader = loader;
2433 DocumentLoader* FrameLoader::documentLoader() const
2435 return m_documentLoader.get();
2438 void FrameLoader::setPolicyDocumentLoader(DocumentLoader* loader)
2440 if (m_policyDocumentLoader == loader)
2445 loader->setFrame(m_frame);
2446 if (m_policyDocumentLoader
2447 && m_policyDocumentLoader != m_provisionalDocumentLoader
2448 && m_policyDocumentLoader != m_documentLoader)
2449 m_policyDocumentLoader->detachFromFrame();
2451 m_policyDocumentLoader = loader;
2454 DocumentLoader* FrameLoader::provisionalDocumentLoader()
2456 return m_provisionalDocumentLoader.get();
2459 void FrameLoader::setProvisionalDocumentLoader(DocumentLoader* loader)
2461 ASSERT(!loader || !m_provisionalDocumentLoader);
2462 ASSERT(!loader || loader->frameLoader() == this);
2464 if (m_provisionalDocumentLoader && m_provisionalDocumentLoader != m_documentLoader)
2465 m_provisionalDocumentLoader->detachFromFrame();
2467 m_provisionalDocumentLoader = loader;
2470 FrameState FrameLoader::state() const
2475 double FrameLoader::timeOfLastCompletedLoad()
2477 return storedTimeOfLastCompletedLoad;
2480 void FrameLoader::setState(FrameState newState)
2484 if (newState == FrameStateProvisional)
2485 provisionalLoadStarted();
2486 else if (newState == FrameStateComplete) {
2487 frameLoadCompleted();
2488 storedTimeOfLastCompletedLoad = currentTime();
2489 if (m_documentLoader)
2490 m_documentLoader->stopRecordingResponses();
2494 void FrameLoader::clearProvisionalLoad()
2496 setProvisionalDocumentLoader(0);
2497 if (Page* page = m_frame->page())
2498 page->progress()->progressCompleted(m_frame);
2499 setState(FrameStateComplete);
2502 void FrameLoader::markLoadComplete()
2504 setState(FrameStateComplete);
2507 void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage)
2509 RefPtr<CachedPage> cachedPage = prpCachedPage;
2510 RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader;
2512 if (m_loadType != FrameLoadTypeReplace)
2513 closeOldDataSources();
2515 if (!cachedPage && !m_creatingInitialEmptyDocument)
2516 m_client->makeRepresentation(pdl.get());
2518 transitionToCommitted(cachedPage);
2520 // Call clientRedirectCancelledOrFinished() here so that the frame load delegate is notified that the redirect's
2521 // status has changed, if there was a redirect. The frame load delegate may have saved some state about
2522 // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:. Since we are
2523 // just about to commit a new page, there cannot possibly be a pending redirect at this point.
2524 if (m_sentRedirectNotification)
2525 clientRedirectCancelledOrFinished(false);
2527 if (cachedPage && cachedPage->document()) {
2529 cachedPage->clear();
2531 KURL url = pdl->substituteData().responseURL();
2535 url = pdl->responseURL();
2537 url = "about:blank";
2544 void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage)
2546 ASSERT(m_client->hasWebView());
2547 ASSERT(m_state == FrameStateProvisional);
2549 if (m_state != FrameStateProvisional)
2552 m_client->setCopiesOnScroll();
2553 updateHistoryForCommit();
2555 // The call to closeURL() invokes the unload event handler, which can execute arbitrary
2556 // JavaScript. If the script initiates a new load, we need to abandon the current load,
2557 // or the two will stomp each other.
2558 DocumentLoader* pdl = m_provisionalDocumentLoader.get();
2559 if (m_documentLoader)
2561 if (pdl != m_provisionalDocumentLoader)
2564 // Nothing else can interupt this commit - set the Provisional->Committed transition in stone
2565 if (m_documentLoader)
2566 m_documentLoader->stopLoadingSubresources();
2567 if (m_documentLoader)
2568 m_documentLoader->stopLoadingPlugIns();
2570 setDocumentLoader(m_provisionalDocumentLoader.get());
2571 setProvisionalDocumentLoader(0);
2572 setState(FrameStateCommittedPage);
2574 // Handle adding the URL to the back/forward list.
2575 DocumentLoader* dl = m_documentLoader.get();
2576 String ptitle = dl->title();
2578 switch (m_loadType) {
2579 case FrameLoadTypeForward:
2580 case FrameLoadTypeBack:
2581 case FrameLoadTypeIndexedBackForward:
2582 if (Page* page = m_frame->page())
2583 if (page->backForwardList()) {
2584 updateHistoryForBackForwardNavigation();
2586 // Create a document view for this document, or used the cached view.
2588 m_client->setDocumentViewFromCachedPage(cachedPage.get());
2590 m_client->makeDocumentView();
2594 case FrameLoadTypeReload:
2595 case FrameLoadTypeSame:
2596 case FrameLoadTypeReplace:
2597 updateHistoryForReload();
2598 m_client->makeDocumentView();
2601 // FIXME - just get rid of this case, and merge FrameLoadTypeReloadAllowingStaleData with the above case
2602 case FrameLoadTypeReloadAllowingStaleData:
2603 m_client->makeDocumentView();
2606 case FrameLoadTypeStandard:
2607 updateHistoryForStandardLoad();
2608 #ifndef BUILDING_ON_TIGER
2609 // This code was originally added for a Leopard performance imporvement. We decided to
2610 // ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>.
2611 if (m_frame->view())
2612 m_frame->view()->suppressScrollbars(true);
2614 m_client->makeDocumentView();
2617 case FrameLoadTypeRedirectWithLockedHistory:
2618 updateHistoryForRedirectWithLockedHistory();
2619 m_client->makeDocumentView();
2622 // FIXME Remove this check when dummy ds is removed (whatever "dummy ds" is).
2623 // An exception should be thrown if we're in the FrameLoadTypeUninitialized state.
2625 ASSERT_NOT_REACHED();
2628 m_responseMIMEType = dl->responseMIMEType();
2630 // Tell the client we've committed this URL.
2631 ASSERT(m_client->hasFrameView());
2633 if (m_creatingInitialEmptyDocument)
2636 m_committedFirstRealDocumentLoad = true;
2638 // For non-cached HTML pages, these methods are called in FrameLoader::begin.
2639 if (cachedPage || !m_client->hasHTMLView()) {
2640 dispatchDidCommitLoad();
2642 // If we have a title let the WebView know about it.
2643 if (!ptitle.isNull())
2644 m_client->dispatchDidReceiveTitle(ptitle);
2648 bool FrameLoader::privateBrowsingEnabled() const
2650 return m_client->privateBrowsingEnabled();
2653 void FrameLoader::clientRedirectCancelledOrFinished(bool cancelWithLoadInProgress)
2655 // Note that -webView:didCancelClientRedirectForFrame: is called on the frame load delegate even if
2656 // the redirect succeeded. We should either rename this API, or add a new method, like
2657 // -webView:didFinishClientRedirectForFrame:
2658 m_client->dispatchDidCancelClientRedirect();
2660 if (!cancelWithLoadInProgress)
2661 m_quickRedirectComing = false;
2663 m_sentRedirectNotification = false;
2666 void FrameLoader::clientRedirected(const KURL& URL, double seconds, double fireDate, bool lockHistory, bool isJavaScriptFormAction)
2668 m_client->dispatchWillPerformClientRedirect(URL, seconds, fireDate);
2670 // Remember that we sent a redirect notification to the frame load delegate so that when we commit
2671 // the next provisional load, we can send a corresponding -webView:didCancelClientRedirectForFrame:
2672 m_sentRedirectNotification = true;
2674 // If a "quick" redirect comes in an, we set a special mode so we treat the next
2675 // load as part of the same navigation. If we don't have a document loader, we have
2676 // no "original" load on which to base a redirect, so we treat the redirect as a normal load.
2677 m_quickRedirectComing = lockHistory && m_documentLoader && !isJavaScriptFormAction;
2680 bool FrameLoader::shouldReload(const KURL& currentURL, const KURL& destinationURL)
2682 // This function implements the rule: "Don't reload if navigating by fragment within
2683 // the same URL, but do reload if going to a new URL or to the same URL with no
2684 // fragment identifier at all."
2685 if (!currentURL.hasRef() && !destinationURL.hasRef())
2687 return !equalIgnoringRef(currentURL, destinationURL);
2690 void FrameLoader::closeOldDataSources()
2692 // FIXME: Is it important for this traversal to be postorder instead of preorder?
2693 // If so, add helpers for postorder traversal, and use them. If not, then lets not
2694 // use a recursive algorithm here.
2695 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
2696 child->loader()->closeOldDataSources();
2698 if (m_documentLoader)
2699 m_client->dispatchWillClose();
2701 m_client->setMainFrameDocumentReady(false); // stop giving out the actual DOMDocument to observers
2704 void FrameLoader::open(CachedPage& cachedPage)
2706 ASSERT(m_frame->page());
2707 ASSERT(m_frame->page()->mainFrame() == m_frame);
2709 cancelRedirection();
2711 // We still have to close the previous part page.
2714 m_isComplete = false;
2716 // Don't re-emit the load event.
2717 m_didCallImplicitClose = true;
2719 // Delete old status bar messages (if it _was_ activated on last URL).
2720 Settings* settings = m_frame->settings();
2721 if (settings && settings->isJavaScriptEnabled()) {
2722 m_frame->setJSStatusBarText(String());
2723 m_frame->setJSDefaultStatusBarText(String());
2726 KURL URL = cachedPage.URL();
2728 if (URL.protocol().startsWith("http") && !URL.host().isEmpty() && URL.path().isEmpty())
2738 Document* document = cachedPage.document();
2740 document->setInPageCache(false);
2742 m_needsClear = true;
2743 m_isComplete = false;
2744 m_didCallImplicitClose = false;
2745 m_outgoingReferrer = URL.url();
2747 FrameView* view = cachedPage.view();
2749 view->setWasScrolledByUser(false);
2750 m_frame->setView(view);
2752 m_frame->setDocument(document);
2753 m_decoder = document->decoder();
2755 updatePolicyBaseURL();
2757 cachedPage.restore(m_frame->page());
2762 bool FrameLoader::isStopping() const
2764 return activeDocumentLoader()->isStopping();
2767 void FrameLoader::finishedLoading()
2769 // Retain because the stop may release the last reference to it.
2770 RefPtr<Frame> protect(m_frame);
2772 RefPtr<DocumentLoader> dl = activeDocumentLoader();
2773 dl->finishedLoading();
2774 if (!dl->mainDocumentError().isNull() || !dl->frameLoader())
2776 dl->setPrimaryLoadComplete(true);
2777 m_client->dispatchDidLoadMainResource(dl.get());
2778 checkLoadComplete();
2781 // FIXME: Which one of these URL methods is right?
2783 KURL FrameLoader::url() const
2788 KURL FrameLoader::URL() const
2790 return activeDocumentLoader()->URL();
2793 bool FrameLoader::isArchiveLoadPending(ResourceLoader* loader) const
2795 return m_client->isArchiveLoadPending(loader);
2798 bool FrameLoader::isHostedByObjectElement() const
2800 HTMLFrameOwnerElement* owner = m_frame->ownerElement();
2801 return owner && owner->hasTagName(objectTag);
2804 bool FrameLoader::isLoadingMainFrame() const
2806 Page* page = m_frame->page();
2807 return page && m_frame == page->mainFrame();
2810 bool FrameLoader::canShowMIMEType(const String& MIMEType) const
2812 return m_client->canShowMIMEType(MIMEType);
2815 bool FrameLoader::representationExistsForURLScheme(const String& URLScheme)
2817 return m_client->representationExistsForURLScheme(URLScheme);
2820 String FrameLoader::generatedMIMETypeForURLScheme(const String& URLScheme)
2822 return m_client->generatedMIMETypeForURLScheme(URLScheme);
2825 void FrameLoader::cancelContentPolicyCheck()
2827 m_client->cancelPolicyCheck();
2828 m_policyCheck.clear();
2831 void FrameLoader::didReceiveServerRedirectForProvisionalLoadForFrame()
2833 m_client->dispatchDidReceiveServerRedirectForProvisionalLoad();
2836 void FrameLoader::finishedLoadingDocument(DocumentLoader* loader)
2839 if (!m_creatingInitialEmptyDocument)
2841 m_client->finishedLoading(loader);
2844 bool FrameLoader::isReplacing() const
2846 return m_loadType == FrameLoadTypeReplace;
2849 void FrameLoader::setReplacing()
2851 m_loadType = FrameLoadTypeReplace;
2854 void FrameLoader::revertToProvisional(DocumentLoader* loader)
2856 m_client->revertToProvisionalState(loader);
2859 bool FrameLoader::subframeIsLoading() const
2861 // It's most likely that the last added frame is the last to load so we walk backwards.
2862 for (Frame* child = m_frame->tree()->lastChild(); child; child = child->tree()->previousSibling()) {
2863 FrameLoader* childLoader = child->loader();
2864 DocumentLoader* documentLoader = childLoader->documentLoader();
2865 if (documentLoader && documentLoader->isLoadingInAPISense())
2867 documentLoader = childLoader->provisionalDocumentLoader();
2868 if (documentLoader && documentLoader->isLoadingInAPISense())
2874 void FrameLoader::willChangeTitle(DocumentLoader* loader)
2876 m_client->willChangeTitle(loader);
2879 FrameLoadType FrameLoader::loadType() const
2884 void FrameLoader::stopPolicyCheck()
2886 m_client->cancelPolicyCheck();
2887 PolicyCheck check = m_policyCheck;
2888 m_policyCheck.clear();
2892 void FrameLoader::checkLoadCompleteForThisFrame()
2894 ASSERT(m_client->hasWebView());
2897 case FrameStateProvisional: {
2898 if (m_delegateIsHandlingProvisionalLoadError)
2901 RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader;
2905 // If we've received any errors we may be stuck in the provisional state and actually complete.
2906 const ResourceError& error = pdl->mainDocumentError();
2910 // Check all children first.
2911 RefPtr<HistoryItem> item;
2912 if (Page* page = m_frame->page())
2913 if (isBackForwardLoadType(loadType()) && m_frame == page->mainFrame())
2914 item = m_currentHistoryItem;
2916 bool shouldReset = true;
2917 if (!pdl->isLoadingInAPISense()) {
2918 m_delegateIsHandlingProvisionalLoadError = true;
2919 m_client->dispatchDidFailProvisionalLoad(error);
2920 m_delegateIsHandlingProvisionalLoadError = false;
2922 // FIXME: can stopping loading here possibly have any effect, if isLoading is false,
2923 // which it must be to be in this branch of the if? And is it OK to just do a full-on
2924 // stopAllLoaders instead of stopLoadingSubframes?
2925 stopLoadingSubframes();
2928 // Finish resetting the load state, but only if another load hasn't been started by the
2929 // delegate callback.
2930 if (pdl == m_provisionalDocumentLoader)
2931 clearProvisionalLoad();
2932 else if (m_provisionalDocumentLoader) {
2933 KURL unreachableURL = m_provisionalDocumentLoader->unreachableURL();
2934 if (!unreachableURL.isEmpty() && unreachableURL == pdl->request().url())
2935 shouldReset = false;
2938 if (shouldReset && item)
2939 if (Page* page = m_frame->page())
2940 page->backForwardList()->goToItem(item.get());
2944 case FrameStateCommittedPage: {
2945 DocumentLoader* dl = m_documentLoader.get();
2946 if (!dl || dl->isLoadingInAPISense())
2951 // FIXME: Is this subsequent work important if we already navigated away?
2952 // Maybe there are bugs because of that, or extra work we can skip because
2953 // the new page is ready.
2955 m_client->forceLayoutForNonHTML();
2957 // If the user had a scroll point, scroll to it, overriding the anchor point if any.
2958 if (Page* page = m_frame->page())
2959 if ((isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadTypeReload) && page->backForwardList())
2960 restoreScrollPositionAndViewState();
2962 if (m_creatingInitialEmptyDocument || !m_committedFirstRealDocumentLoad)
2965 const ResourceError& error = dl->mainDocumentError();
2966 if (!error.isNull())
2967 m_client->dispatchDidFailLoad(error);
2969 m_client->dispatchDidFinishLoad();
2971 if (Page* page = m_frame->page())
2972 page->progress()->progressCompleted(m_frame);
2976 case FrameStateComplete:
2977 // Even if already complete, we might have set a previous item on a frame that
2978 // didn't do any data loading on the past transaction. Make sure to clear these out.
2979 m_client->frameLoadCompleted();
2983 ASSERT_NOT_REACHED();
2986 void FrameLoader::continueAfterContentPolicy(PolicyAction policy)
2988 PolicyCheck check = m_policyCheck;
2989 m_policyCheck.clear();
2993 void FrameLoader::continueLoadAfterWillSubmitForm(PolicyAction)
2995 if (!m_provisionalDocumentLoader)
2998 m_provisionalDocumentLoader->prepareForLoadStart();
3000 DocumentLoader* activeDocLoader = activeDocumentLoader();
3001 if (activeDocLoader && activeDocLoader->isLoadingMainResource())
3004 m_provisionalDocumentLoader->setLoadingFromCachedPage(false);
3006 unsigned long identifier = 0;
3008 if (Page* page = m_frame->page()) {
3009 identifier = page->progress()->createUniqueIdentifier();
3010 dispatchAssignIdentifierToInitialRequest(identifier, m_provisionalDocumentLoader.get(), m_provisionalDocumentLoader->originalRequest());
3013 if (!m_provisionalDocumentLoader->startLoadingMainResource(identifier))
3014 m_provisionalDocumentLoader->updateLoading();
3017 void FrameLoader::didFirstLayout()
3019 if (Page* page = m_frame->page())
3020 if (isBackForwardLoadType(m_loadType) && page->backForwardList())
3021 restoreScrollPositionAndViewState();
3023 m_firstLayoutDone = true;
3024 m_client->dispatchDidFirstLayout();
3027 void FrameLoader::frameLoadCompleted()
3029 m_client->frameLoadCompleted();
3031 // After a canceled provisional load, firstLayoutDone is false.
3032 // Reset it to true if we're displaying a page.
3033 if (m_documentLoader)
3034 m_firstLayoutDone = true;
3037 bool FrameLoader::firstLayoutDone() const
3039 return m_firstLayoutDone;
3042 bool FrameLoader::isQuickRedirectComing() const
3044 return m_quickRedirectComing;
3047 void FrameLoader::detachChildren()
3049 // FIXME: Is it really necessary to do this in reverse order?
3051 for (Frame* child = m_frame->tree()->lastChild(); child; child = previous) {
3052 previous = child->tree()->previousSibling();
3053 child->loader()->detachFromParent();
3057 void FrameLoader::recursiveCheckLoadComplete()
3059 Vector<RefPtr<Frame>, 10> frames;
3061 for (RefPtr<Frame> frame = m_frame->tree()->firstChild(); frame; frame = frame->tree()->nextSibling())
3062 frames.append(frame);
3064 unsigned size = frames.size();
3065 for (unsigned i = 0; i < size; i++)
3066 frames[i]->loader()->recursiveCheckLoadComplete();
3068 checkLoadCompleteForThisFrame();
3071 // Called every time a resource is completely loaded, or an error is received.
3072 void FrameLoader::checkLoadComplete()
3074 ASSERT(m_client->hasWebView());
3076 // FIXME: Always traversing the entire frame tree is a bit inefficient, but
3077 // is currently needed in order to null out the previous history item for all frames.
3078 if (Page* page = m_frame->page())
3079 page->mainFrame()->loader()->recursiveCheckLoadComplete();
3082 int FrameLoader::numPendingOrLoadingRequests(bool recurse) const
3085 return numRequests(m_frame->document());
3088 for (Frame* frame = m_frame; frame; frame = frame->tree()->traverseNext(m_frame))
3089 count += numRequests(frame->document());
3093 FrameLoaderClient* FrameLoader::client() const
3098 void FrameLoader::submitForm(const FrameLoadRequest& request, Event* event)
3100 // FIXME: We'd like to remove this altogether and fix the multiple form submission issue another way.
3101 // We do not want to submit more than one form from the same page,
3102 // nor do we want to submit a single form more than once.
3103 // This flag prevents these from happening; not sure how other browsers prevent this.
3104 // The flag is reset in each time we start handle a new mouse or key down event, and
3105 // also in setView since this part may get reused for a page from the back/forward cache.
3106 // The form multi-submit logic here is only needed when we are submitting a form that affects this frame.
3107 // FIXME: Frame targeting is only one of the ways the submission could end up doing something other
3108 // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly
3109 // needed any more now that we reset m_submittedFormURL on each mouse or key down event.
3110 Frame* target = m_frame->tree()->find(request.frameName());
3111 if (m_frame->tree()->isDescendantOf(target)) {
3112 if (m_submittedFormURL == request.resourceRequest().url())
3114 m_submittedFormURL = request.resourceRequest().url();
3117 // FIXME: We should probably call userGestureHint() to tell whether this form submission was the result of a user gesture.
3118 load(request, false, true, event, m_formAboutToBeSubmitted.get(), m_formValuesAboutToBeSubmitted);
3120 clearRecordedFormValues();
3123 void FrameLoader::urlSelected(const FrameLoadRequest& request, Event* event, bool lockHistory, bool userGesture)
3125 FrameLoadRequest copy = request;
3126 if (copy.resourceRequest().httpReferrer().isEmpty())
3127 copy.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
3129 load(copy, lockHistory, userGesture, event, 0, HashMap<String, String>());
3132 String FrameLoader::userAgent(const KURL& url) const
3134 return m_client->userAgent(url);
3137 void FrameLoader::tokenizerProcessedData()
3139 ASSERT(m_frame->page());
3140 ASSERT(m_frame->document());
3145 void FrameLoader::didTellBridgeAboutLoad(const String& URL)
3147 m_urlsBridgeKnowsAbout.add(URL);
3150 bool FrameLoader::haveToldBridgeAboutLoad(const String& URL)
3152 return m_urlsBridgeKnowsAbout.contains(URL);
3155 void FrameLoader::handledOnloadEvents()
3157 m_client->dispatchDidHandleOnloadEvents();
3160 void FrameLoader::frameDetached()
3166 void FrameLoader::detachFromParent()
3168 RefPtr<Frame> protect(m_frame);
3172 saveScrollPositionAndViewStateToItem(currentHistoryItem());
3175 if (Page* page = m_frame->page())
3176 page->inspectorController()->frameDetachedFromParent(m_frame);
3178 m_client->detachedFromParent2();
3179 setDocumentLoader(0);
3180 m_client->detachedFromParent3();
3181 if (Frame* parent = m_frame->tree()->parent()) {
3182 parent->tree()->removeChild(m_frame);
3183 parent->loader()->scheduleCheckCompleted();
3185 m_frame->setView(0);
3186 m_frame->pageDestroyed();
3189 [m_frame->bridge() close];
3191 m_client->detachedFromParent4();
3194 void FrameLoader::dispatchDidChangeLocationWithinPage()
3196 m_client->dispatchDidChangeLocationWithinPage();
3199 void FrameLoader::dispatchDidFinishLoadToClient()
3201 m_client->didFinishLoad();
3204 void FrameLoader::updateGlobalHistoryForStandardLoad(const KURL& url)
3206 m_client->updateGlobalHistoryForStandardLoad(url);
3209 void FrameLoader::updateGlobalHistoryForReload(const KURL& url)
3211 m_client->updateGlobalHistoryForReload(url);
3214 bool FrameLoader::shouldGoToHistoryItem(HistoryItem* item) const
3216 return m_client->shouldGoToHistoryItem(item);
3219 void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, bool mainResource, bool alwaysFromRequest)
3221 applyUserAgent(request);
3223 if (m_loadType == FrameLoadTypeReload) {
3224 request.setCachePolicy(ReloadIgnoringCacheData);
3225 request.setHTTPHeaderField("Cache-Control", "max-age=0");
3228 // Don't set the cookie policy URL if it's already been set.
3229 if (request.mainDocumentURL().isEmpty()) {
3230 if (mainResource && (isLoadingMainFrame() || alwaysFromRequest))
3231 request.setMainDocumentURL(request.url());
3232 else if (Page* page = m_frame->page())
3233 request.setMainDocumentURL(page->mainFrame()->loader()->url());
3237 request.setHTTPAccept("text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5");
3240 void FrameLoader::committedLoad(DocumentLoader* loader, const char* data, int length)
3242 m_client->committedLoad(loader, data, length);
3245 void FrameLoader::post(const KURL& URL, const String& referrer, const String& frameName, PassRefPtr<FormData> formData,
3246 const String& contentType, Event* event, HTMLFormElement* form, const HashMap<String, String>& formValues)
3248 // When posting, use the NSURLRequestReloadIgnoringCacheData load flag.
3249 // This prevents a potential bug which may cause a page with a form that uses itself
3250 // as an action to be returned from the cache without submitting.
3252 // FIXME: Where's the code that implements what the comment above says?
3254 ResourceRequest request(URL);
3255 addExtraFieldsToRequest(request, true, true);
3257 if (!referrer.isEmpty())
3258 request.setHTTPReferrer(referrer);
3259 request.setHTTPMethod("POST");
3260 request.setHTTPBody(formData);
3261 request.setHTTPContentType(contentType);
3263 NavigationAction action(URL, FrameLoadTypeStandard, true, event);
3265 RefPtr<FormState> formState;
3266 if (form && !formValues.isEmpty())
3267 formState = FormState::create(form, formValues, m_frame);
3269 if (!frameName.isEmpty()) {
3270 if (Frame* targetFrame = m_frame->tree()->find(frameName))
3271 targetFrame->loader()->load(request, action, FrameLoadTypeStandard, formState.release());
3273 checkNewWindowPolicy(action, request, formState.release(), frameName);
3275 load(request, action, FrameLoadTypeStandard, formState.release());
3278 bool FrameLoader::isReloading() const
3280 return documentLoader()->request().cachePolicy() == ReloadIgnoringCacheData;
3283 void FrameLoader::loadEmptyDocumentSynchronously()
3285 ResourceRequest request(KURL(""));
3289 void FrameLoader::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data)
3291 // Since this is a subresource, we can load any URL (we ignore the return value).
3292 // But we still want to know whether we should hide the referrer or not, so we call the canLoad method.
3293 String referrer = m_outgoingReferrer;
3294 if (shouldHideReferrer(request.url(), referrer))
3295 referrer = String();
3297 ResourceRequest initialRequest = request;
3298 initialRequest.setTimeoutInterval(10);
3300 if (initialRequest.isConditional())
3301 initialRequest.setCachePolicy(ReloadIgnoringCacheData);
3303 initialRequest.setCachePolicy(documentLoader()->request().cachePolicy());
3305 if (!referrer.isEmpty())
3306 initialRequest.setHTTPReferrer(referrer);
3308 if (Page* page = m_frame->page())
3309 initialRequest.setMainDocumentURL(page->mainFrame()->loader()->documentLoader()->request().url());
3310 initialRequest.setHTTPUserAgent(client()->userAgent(request.url()));
3312 unsigned long identifier = 0;
3313 ResourceRequest newRequest(initialRequest);
3314 requestFromDelegate(newRequest, identifier, error);
3316 if (error.isNull()) {
3317 ASSERT(!newRequest.isNull());
3318 didTellBridgeAboutLoad(newRequest.url().url());
3319 ResourceHandle::loadResourceSynchronously(newRequest, error, response, data);
3322 sendRemainingDelegateMessages(identifier, response, data.size(), error);
3325 void FrameLoader::assignIdentifierToInitialRequest(unsigned long identifier, const ResourceRequest& clientRequest)
3327 return dispatchAssignIdentifierToInitialRequest(identifier, activeDocumentLoader(), clientRequest);
3330 void FrameLoader::willSendRequest(ResourceLoader* loader, ResourceRequest& clientRequest, const ResourceResponse& redirectResponse)
3332 applyUserAgent(clientRequest);
3333 dispatchWillSendRequest(loader->documentLoader(), loader->identifier(), clientRequest, redirectResponse);
3336 void FrameLoader::didReceiveResponse(ResourceLoader* loader, const ResourceResponse& r)
3338 activeDocumentLoader()->addResponse(r);
3340 if (Page* page = m_frame->page())
3341 page->progress()->incrementProgress(loader->identifier(), r);
3342 dispatchDidReceiveResponse(loader->documentLoader(), loader->identifier(), r);
3345 void FrameLoader::didReceiveData(ResourceLoader* loader, const char* data, int length, int lengthReceived)
3347 if (Page* page = m_frame->page())
3348 page->progress()->incrementProgress(loader->identifier(), data, length);
3349 dispatchDidReceiveContentLength(loader->documentLoader(), loader->identifier(), lengthReceived);
3352 void FrameLoader::didFailToLoad(ResourceLoader* loader, const ResourceError& error)
3354 if (Page* page = m_frame->page())
3355 page->progress()->completeProgress(loader->identifier());
3356 if (!error.isNull())
3357 m_client->dispatchDidFailLoading(loader->documentLoader(), loader->identifier(), error);
3360 const ResourceRequest& FrameLoader::originalRequest() const
3362 return activeDocumentLoader()->originalRequestCopy();
3365 void FrameLoader::receivedMainResourceError(const ResourceError& error, bool isComplete)
3367 // Retain because the stop may release the last reference to it.
3368 RefPtr<Frame> protect(m_frame);
3370 RefPtr<DocumentLoader> loader = activeDocumentLoader();
3373 // FIXME: Don't want to do this if an entirely new load is going, so should check
3374 // that both data sources on the frame are either this or nil.
3376 if (m_client->shouldFallBack(error))
3377 handleFallbackContent();
3380 if (m_state == FrameStateProvisional && m_provisionalDocumentLoader) {
3381 KURL failedURL = m_provisionalDocumentLoader->originalRequestCopy().url();
3382 didNotOpenURL(failedURL);
3384 // We might have made a page cache item, but now we're bailing out due to an error before we ever
3385 // transitioned to the new page (before WebFrameState == commit). The goal here is to restore any state
3386 // so that the existing view (that wenever got far enough to replace) can continue being used.
3387 invalidateCurrentItemCachedPage();
3389 // Call clientRedirectCancelledOrFinished here so that the frame load delegate is notified that the redirect's
3390 // status has changed, if there was a redirect. The frame load delegate may have saved some state about
3391 // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:. Since we are definitely
3392 // not going to use this provisional resource, as it was cancelled, notify the frame load delegate that the redirect
3394 if (m_sentRedirectNotification)
3395 clientRedirectCancelledOrFinished(false);
3399 loader->mainReceivedError(error, isComplete);
3402 void FrameLoader::callContinueFragmentScrollAfterNavigationPolicy(void* argument,
3403 const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue)
3405 FrameLoader* loader = static_cast<FrameLoader*>(argument);
3406 loader->continueFragmentScrollAfterNavigationPolicy(request, shouldContinue);
3409 void FrameLoader::continueFragmentScrollAfterNavigationPolicy(const ResourceRequest& request, bool shouldContinue)
3412 // some functions check m_quickRedirectComing, and others check for
3413 // FrameLoadTypeRedirectWithLockedHistory.
3414 bool isRedirect = m_quickRedirectComing || m_policyLoadType == FrameLoadTypeRedirectWithLockedHistory;
3415 m_quickRedirectComing = false;
3417 if (!shouldContinue)
3420 KURL URL = request.url();
3422 m_documentLoader->replaceRequestURLForAnchorScroll(URL);
3423 if (!isRedirect && !shouldTreatURLAsSameAsCurrent(URL)) {
3424 // NB: must happen after _setURL, since we add based on the current request.
3425 // Must also happen before we openURL and displace the scroll position, since
3426 // adding the BF item will save away scroll state.
3428 // NB2: If we were loading a long, slow doc, and the user anchor nav'ed before
3429 // it was done, currItem is now set the that slow doc, and prevItem is whatever was
3430 // before it. Adding the b/f item will bump the slow doc down to prevItem, even
3431 // though its load is not yet done. I think this all works out OK, for one because
3432 // we have already saved away the scroll and doc state for the long slow load,
3433 // but it's not an obvious case.
3435 addHistoryItemForFragmentScroll();
3438 scrollToAnchor(URL);
3441 // This will clear previousItem from the rest of the frame tree that didn't
3442 // doing any loading. We need to make a pass on this now, since for anchor nav
3443 // we'll not go through a real load and reach Completed state.
3444 checkLoadComplete();
3446 dispatchDidChangeLocationWithinPage();
3447 m_client->didFinishLoad();
3450 void FrameLoader::opened()
3452 if (m_loadType == FrameLoadTypeStandard && m_documentLoader->isClientRedirect())
3453 updateHistoryForClientRedirect();
3455 if (m_documentLoader->isLoadingFromCachedPage()) {
3456 m_frame->document()->didRestoreFromCache();
3458 // Force a layout to update view size and thereby update scrollbars.
3459 m_client->forceLayout();
3461 const ResponseVector& responses = m_documentLoader->responses();
3462 size_t count = responses.size();
3463 for (size_t i = 0; i < count; i++) {
3464 const ResourceResponse& response = responses[i];
3465 // FIXME: If the WebKit client changes or cancels the request, this is not respected.
3466 ResourceError error;
3467 unsigned long identifier;
3468 ResourceRequest request(response.url());
3469 requestFromDelegate(request, identifier, error);
3470 // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
3471 // However, with today's computers and networking speeds, this won't happen in practice.
3472 // Could be an issue with a giant local file.
3473 sendRemainingDelegateMessages(identifier, response, static_cast<int>(response.expectedContentLength()), error);
3476 pageCache()->remove(m_currentHistoryItem.get());
3478 m_documentLoader->setPrimaryLoadComplete(true);
3480 // FIXME: Why only this frame and not parent frames?
3481 checkLoadCompleteForThisFrame();
3485 void FrameLoader::checkNewWindowPolicy(const NavigationAction& action, const ResourceRequest& request,
3486 PassRefPtr<FormState> formState, const String& frameName)
3488 m_policyCheck.set(request, formState, frameName,
3489 callContinueLoadAfterNewWindowPolicy, this);
3490 m_client->dispatchDecidePolicyForNewWindowAction(&FrameLoader::continueAfterNewWindowPolicy,
3491 action, request, frameName);
3494 void FrameLoader::continueAfterNewWindowPolicy(PolicyAction policy)
3496 PolicyCheck check = m_policyCheck;
3497 m_policyCheck.clear();
3501 check.clearRequest();
3503 case PolicyDownload:
3504 m_client->startDownload(check.request());
3505 check.clearRequest();
3511 check.call(policy == PolicyUse);
3514 void FrameLoader::checkNavigationPolicy(const ResourceRequest& request, DocumentLoader* loader,
3515 PassRefPtr<FormState> formState, NavigationPolicyDecisionFunction function, void* argument)
3517 NavigationAction action = loader->triggeringAction();
3518 if (action.isEmpty()) {
3519 action = NavigationAction(request.url(), NavigationTypeOther);
3520 loader->setTriggeringAction(action);
3523 // Don't ask more than once for the same request or if we are loading an empty URL.
3524 // This avoids confusion on the part of the client.
3525 if (equalIgnoringHeaderFields(request, loader->lastCheckedRequest()) || (!request.isNull() && request.url().isEmpty())) {
3526 function(argument, request, 0, true);
3527 loader->setLastCheckedRequest(request);
3531 // We are always willing to show alternate content for unreachable URLs;
3532 // treat it like a reload so it maintains the right state for b/f list.
3533 if (loader->substituteData().isValid() && !loader->substituteData().failingURL().isEmpty()) {
3534 if (isBackForwardLoadType(m_policyLoadType))
3535 m_policyLoadType = FrameLoadTypeReload;
3536 function(argument, request, 0, true);
3540 loader->setLastCheckedRequest(request);
3542 m_policyCheck.set(request, formState, function, argument);
3544 m_delegateIsDecidingNavigationPolicy = true;
3545 m_client->dispatchDecidePolicyForNavigationAction(&FrameLoader::continueAfterNavigationPolicy,
3547 m_delegateIsDecidingNavigationPolicy = false;
3550 void FrameLoader::continueAfterNavigationPolicy(PolicyAction policy)
3552 PolicyCheck check = m_policyCheck;
3553 m_policyCheck.clear();
3555 bool shouldContinue = policy == PolicyUse;
3559 check.clearRequest();
3561 case PolicyDownload:
3562 m_client->startDownload(check.request());
3563 check.clearRequest();
3566 ResourceRequest request(check.request());
3568 if (!m_client->canHandleRequest(request)) {
3569 handleUnimplementablePolicy(m_client->cannotShowURLError(check.request()));
3570 check.clearRequest();
3571 shouldContinue = false;
3577 check.call(shouldContinue);
3580 void FrameLoader::callContinueLoadAfterNavigationPolicy(void* argument,
3581 const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue)
3583 FrameLoader* loader = static_cast<FrameLoader*>(argument);
3584 loader->continueLoadAfterNavigationPolicy(request, formState, shouldContinue);
3587 void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue)
3589 // If we loaded an alternate page to replace an unreachableURL, we'll get in here with a
3590 // nil policyDataSource because loading the alternate page will have passed
3591 // through this method already, nested; otherwise, policyDataSource should still be set.
3592 ASSERT(m_policyDocumentLoader || !m_provisionalDocumentLoader->unreachableURL().isEmpty());
3594 bool isTargetItem = m_provisionalHistoryItem ? m_provisionalHistoryItem->isTargetItem() : false;
3596 // Two reasons we can't continue:
3597 // 1) Navigation policy delegate said we can't so request is nil. A primary case of this
3598 // is the user responding Cancel to the form repost nag sheet.
3599 // 2) User responded Cancel to an alert popped up by the before unload event handler.
3600 // The "before unload" event handler runs only for the main frame.
3601 bool canContinue = shouldContinue && (!isLoadingMainFrame() || m_frame->shouldClose());
3604 // If we were waiting for a quick redirect, but the policy delegate decided to ignore it, then we
3605 // need to report that the client redirect was cancelled.
3606 if (m_quickRedirectComing)
3607 clientRedirectCancelledOrFinished(false);
3609 setPolicyDocumentLoader(0);
3611 // If the navigation request came from the back/forward menu, and we punt on it, we have the
3612 // problem that we have optimistically moved the b/f cursor already, so move it back. For sanity,
3613 // we only do this when punting a navigation for the target frame or top-level frame.
3614 if ((isTargetItem || isLoadingMainFrame()) && isBackForwardLoadType(m_policyLoadType))
3615 if (Page* page = m_frame->page()) {
3616 Frame* mainFrame = page->mainFrame();
3617 if (HistoryItem* resetItem = mainFrame->loader()->m_currentHistoryItem.get())
3618 page->backForwardList()->goToItem(resetItem);
3623 FrameLoadType type = m_policyLoadType;
3625 setProvisionalDocumentLoader(m_policyDocumentLoader.get());
3627 setState(FrameStateProvisional);
3629 setPolicyDocumentLoader(0);
3631 if (isBackForwardLoadType(type) && loadProvisionalItemFromCachedPage())
3635 m_client->dispatchWillSubmitForm(&FrameLoader::continueLoadAfterWillSubmitForm, formState);
3637 continueLoadAfterWillSubmitForm();
3641 void FrameLoader::callContinueLoadAfterNewWindowPolicy(void* argument,
3642 const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, bool shouldContinue)
3644 FrameLoader* loader = static_cast<FrameLoader*>(argument);
3645 loader->continueLoadAfterNewWindowPolicy(request, formState, frameName, shouldContinue);
3648 void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& request,
3649 PassRefPtr<FormState> formState, const String& frameName, bool shouldContinue)
3651 if (!shouldContinue)
3654 RefPtr<Frame> frame = m_frame;
3655 RefPtr<Frame> mainFrame = m_client->dispatchCreatePage();
3659 if (frameName != "_blank")
3660 mainFrame->tree()->setName(frameName);
3662 mainFrame->loader()->setOpenedByDOM();
3663 mainFrame->loader()->m_client->dispatchShow();
3664 mainFrame->loader()->setOpener(frame.get());
3665 mainFrame->loader()->load(request, NavigationAction(), FrameLoadTypeStandard, formState);
3668 void FrameLoader::sendRemainingDelegateMessages(unsigned long identifier, const ResourceResponse& response, int length, const ResourceError& error)
3670 if (!response.isNull())
3671 dispatchDidReceiveResponse(m_documentLoader.get(), identifier, response);
3674 dispatchDidReceiveContentLength(m_documentLoader.get(), identifier, length);
3677 dispatchDidFinishLoading(m_documentLoader.get(), identifier);
3679 m_client->dispatchDidFailLoading(m_documentLoader.get(), identifier, error);
3682 void FrameLoader::requestFromDelegate(ResourceRequest& request, unsigned long& identifier, ResourceError& error)
3684 ASSERT(!request.isNull());
3687 if (Page* page = m_frame->page()) {
3688 identifier = page->progress()->createUniqueIdentifier();
3689 dispatchAssignIdentifierToInitialRequest(identifier, m_documentLoader.get(), request);
3692 ResourceRequest newRequest(request);
3693 dispatchWillSendRequest(m_documentLoader.get(), identifier, newRequest, ResourceResponse());
3695 if (newRequest.isNull())
3696 error = m_client->cancelledError(request);
3698 error = ResourceError();
3700 request = newRequest;
3703 void FrameLoader::loadedResourceFromMemoryCache(const ResourceRequest& request, const ResourceResponse& response, int length)
3705 if (dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), request, response, length))
3708 unsigned long identifier;
3709 ResourceError error;
3710 ResourceRequest r(request);
3711 requestFromDelegate(r, identifier, error);
3712 sendRemainingDelegateMessages(identifier, response, length, error);
3715 void FrameLoader::applyUserAgent(ResourceRequest& request)
3717 String userAgent = client()->userAgent(request.url());
3718 ASSERT(!userAgent.isNull());
3719 request.setHTTPUserAgent(userAgent);
3722 bool FrameLoader::canGoBackOrForward(int distance) const
3724 if (Page* page = m_frame->page()) {
3727 if (distance > 0 && distance <= page->backForwardList()->forwardListCount())
3729 if (distance < 0 && -distance <= page->backForwardList()->backListCount())
3735 int FrameLoader::getHistoryLength()
3737 if (Page* page = m_frame->page())
3738 return page->backForwardList()->backListCount() + 1;
3742 KURL FrameLoader::historyURL(int distance)
3744 if (Page* page = m_frame->page()) {
3745 BackForwardList* list = page->backForwardList();
3746 HistoryItem* item = list->itemAtIndex(distance);
3749 int forwardListCount = list->forwardListCount();
3750 if (forwardListCount > 0)
3751 item = list->itemAtIndex(forwardListCount);
3753 int backListCount = list->backListCount();
3754 if (backListCount > 0)
3755 item = list->itemAtIndex(-backListCount);
3764 void FrameLoader::addHistoryItemForFragmentScroll()
3766 addBackForwardItemClippedAtTarget(false);
3769 bool FrameLoader::loadProvisionalItemFromCachedPage()
3771 RefPtr<CachedPage> cachedPage = pageCache()->get(m_provisionalHistoryItem.get());
3772 if (!cachedPage || !cachedPage->document())
3774 provisionalDocumentLoader()->loadFromCachedPage(cachedPage.release());
3778 void FrameLoader::cachePageForHistoryItem(HistoryItem* item)
3780 if (Page* page = m_frame->page()) {
3781 RefPtr<CachedPage> cachedPage = CachedPage::create(page);
3782 cachedPage->setTimeStampToNow();
3783 cachedPage->setDocumentLoader(documentLoader());
3784 m_client->saveDocumentViewToCachedPage(cachedPage.get());
3786 pageCache()->add(item, cachedPage.release());
3790 bool FrameLoader::shouldTreatURLAsSameAsCurrent(const KURL& URL) const
3792 if (!m_currentHistoryItem)
3794 return URL == m_currentHistoryItem->url() || URL == m_currentHistoryItem->originalURL();
3797 PassRefPtr<HistoryItem> FrameLoader::createHistoryItem(bool useOriginal)
3799 DocumentLoader* docLoader = documentLoader();
3801 KURL unreachableURL = docLoader ? docLoader->unreachableURL() : KURL();
3806 if (!unreachableURL.isEmpty()) {
3807 url = unreachableURL;
3808 originalURL = unreachableURL;
3810 originalURL = docLoader ? docLoader->originalURL() : KURL();
3814 url = docLoader->requestURL();
3817 LOG(History, "WebCoreHistory: Creating item for %s", url.url().ascii());
3819 // Frames that have never successfully loaded any content
3820 // may have no URL at all. Currently our history code can't
3821 // deal with such things, so we nip that in the bud here.
3822 // Later we may want to learn to live with nil for URL.
3823 // See bug 3368236 and related bugs for more information.
3825 url = KURL("about:blank");
3826 if (originalURL.isEmpty())
3827 originalURL = KURL("about:blank");
3829 RefPtr<HistoryItem> item = new HistoryItem(url, m_frame->tree()->name(), m_frame->tree()->parent() ? m_frame->tree()->parent()->tree()->name() : "", docLoader ? docLoader->title() : "");
3830 item->setOriginalURLString(originalURL.url());
3832 // Save form state if this is a POST
3835 item->setFormInfoFromRequest(docLoader->originalRequest());
3837 item->setFormInfoFromRequest(docLoader->request());
3840 // Set the item for which we will save document state
3841 m_previousHistoryItem = m_currentHistoryItem;
3842 m_currentHistoryItem = item;
3844 return item.release();
3847 void FrameLoader::addBackForwardItemClippedAtTarget(bool doClip)
3849 if (Page* page = m_frame->page())
3850 if (!documentLoader()->urlForHistory().isEmpty()) {
3851 Frame* mainFrame = page->mainFrame();
3853 FrameLoader* frameLoader = mainFrame->loader();
3855 if (!frameLoader->m_didPerformFirstNavigation && page->backForwardList()->entries().size() == 1) {
3856 frameLoader->m_didPerformFirstNavigation = true;
3857 m_client->didPerformFirstNavigation();
3860 RefPtr<HistoryItem> item = frameLoader->createHistoryItemTree(m_frame, doClip);
3861 LOG(BackForward, "WebCoreBackForward - Adding backforward item %p for frame %s", item.get(), documentLoader()->URL().url().ascii());
3862 page->backForwardList()->addItem(item);
3866 PassRefPtr<HistoryItem> FrameLoader::createHistoryItemTree(Frame* targetFrame, bool clipAtTarget)
3868 RefPtr<HistoryItem> bfItem = createHistoryItem(m_frame->tree()->parent() ? true : false);
3869 if (m_previousHistoryItem)
3870 saveScrollPositionAndViewStateToItem(m_previousHistoryItem.get());
3871 if (!(clipAtTarget && m_frame == targetFrame)) {
3872 // save frame state for items that aren't loading (khtml doesn't save those)
3873 saveDocumentState();
3874 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
3875 bfItem->addChildItem(child->loader()->createHistoryItemTree(targetFrame, clipAtTarget));
3877 if (m_frame == targetFrame)
3878 bfItem->setIsTargetItem(true);
3882 void FrameLoader::saveScrollPositionAndViewStateToItem(HistoryItem* item)
3884 if (!item || !m_frame->view())
3887 item->setScrollPoint(IntPoint(m_frame->view()->contentsX(), m_frame->view()->contentsY()));
3888 // FIXME: It would be great to work out a way to put this code in WebCore instead of calling through to the client.
3889 m_client->saveViewStateToItem(item);
3893 There is a race condition between the layout and load completion that affects restoring the scroll position.
3894 We try to restore the scroll position at both the first layout and upon load completion.
3896 1) If first layout happens before the load completes, we want to restore the scroll position then so that the
3897 first time we draw the page is already scrolled to the right place, instead of starting at the top and later
3898 jumping down. It is possible that the old scroll position is past the part of the doc laid out so far, in
3899 which case the restore silent fails and we will fix it in when we try to restore on doc completion.
3900 2) If the layout happens after the load completes, the attempt to restore at load completion time silently
3901 fails. We then successfully restore it when the layout happens.
3903 void FrameLoader::restoreScrollPositionAndViewState()
3905 if (!m_committedFirstRealDocumentLoad)
3908 ASSERT(m_currentHistoryItem);
3910 // FIXME: As the ASSERT attests, it seems we should always have a currentItem here.
3911 // One counterexample is <rdar://problem/4917290>
3912 // For now, to cover this issue in release builds, there is no technical harm to returning
3913 // early and from a user standpoint - as in the above radar - the previous page load failed
3914 // so there *is* no scroll or view state to restore!
3915 if (!m_currentHistoryItem)
3918 // FIXME: It would be great to work out a way to put this code in WebCore instead of calling
3919 // through to the client. It's currently used only for the PDF view on Mac.
3920 m_client->restoreViewState();
3922 if (FrameView* view = m_frame->view())
3923 if (!view->wasScrolledByUser()) {
3924 const IntPoint& scrollPoint = m_currentHistoryItem->scrollPoint();
3925 view->setContentsPos(scrollPoint.x(), scrollPoint.y());
3929 void FrameLoader::invalidateCurrentItemCachedPage()
3931 // When we are pre-commit, the currentItem is where the pageCache data resides
3932 CachedPage* cachedPage = pageCache()->get(m_currentHistoryItem.get());
3934 // FIXME: This is a grotesque hack to fix <rdar://problem/4059059> Crash in RenderFlow::detach
3935 // Somehow the PageState object is not properly updated, and is holding onto a stale document.
3936 // Both Xcode and FileMaker see this crash, Safari does not.
3938 ASSERT(!cachedPage || cachedPage->document() == m_frame->document());
3939 if (cachedPage && cachedPage->document() == m_frame->document()) {
3940 cachedPage->document()->setInPageCache(false);
3941 cachedPage->clear();
3945 pageCache()->remove(m_currentHistoryItem.get());
3948 void FrameLoader::saveDocumentState()
3950 if (m_creatingInitialEmptyDocument)
3953 // For a standard page load, we will have a previous item set, which will be used to
3954 // store the form state. However, in some cases we will have no previous item, and
3955 // the current item is the right place to save the state. One example is when we
3956 // detach a bunch of frames because we are navigating from a site with frames to
3957 // another site. Another is when saving the frame state of a frame that is not the
3958 // target of the current navigation (if we even decide to save with that granularity).
3960 // Because of previousItem's "masking" of currentItem for this purpose, it's important
3961 // that previousItem be cleared at the end of a page transition. We leverage the
3962 // checkLoadComplete recursion to achieve this goal.
3964 HistoryItem* item = m_previousHistoryItem ? m_previousHistoryItem.get() : m_currentHistoryItem.get();
3968 Document* document = m_frame->document();
3971 if (document && item->isCurrentDocument(document)) {
3972 LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree()->name().domString().utf8().data(), item);
3973 item->setDocumentState(document->formElementsState());
3977 // Loads content into this frame, as specified by history item
3978 void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType)
3980 KURL itemURL = item->url();
3981 KURL itemOriginalURL = item->originalURL();
3983 if (documentLoader())
3984 currentURL = documentLoader()->URL();
3985 RefPtr<FormData> formData = item->formData();
3987 // Are we navigating to an anchor within the page?
3988 // Note if we have child frames we do a real reload, since the child frames might not
3989 // match our current frame structure, or they might not have the right content. We could
3990 // check for all that as an additional optimization.
3991 // We also do not do anchor-style navigation if we're posting a form.
3993 if (!formData && !shouldReload(itemURL, currentURL) && urlsMatchItem(item)) {
3994 // Must do this maintenance here, since we don't go through a real page reload
3995 saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get());
3997 if (FrameView* view = m_frame->view())
3998 view->setWasScrolledByUser(false);
4000 m_currentHistoryItem = item;
4002 // FIXME: Form state might need to be saved here too.
4004 // We always call scrollToAnchor here, even if the URL doesn't have an
4005 // anchor fragment. This is so we'll keep the WebCore Frame's URL up-to-date.
4006 scrollToAnchor(item->url());
4008 // must do this maintenance here, since we don't go through a real page reload
4009 restoreScrollPositionAndViewState();
4011 // Fake the URL change by updating the data source's request. This will no longer
4012 // be necessary if we do the better fix described above.
4013 documentLoader()->replaceRequestURLForAnchorScroll(itemURL);
4015 dispatchDidChangeLocationWithinPage();
4017 // FrameLoaderClient::didFinishLoad() tells the internal load delegate the load finished with no error
4018 dispatchDidFinishLoadToClient();
4020 // Remember this item so we can traverse any child items as child frames load
4021 m_provisionalHistoryItem = item;
4023 bool inPageCache = false;
4025 // Check if we'll be using the page cache. We only use the page cache
4026 // if one exists and it is less than _backForwardCacheExpirationInterval
4027 // seconds old. If the cache is expired it gets flushed here.
4028 if (RefPtr<CachedPage> cachedPage = pageCache()->get(item)) {
4029 double interval = currentTime() - cachedPage->timeStamp();
4031 // FIXME: 1800 should not be hardcoded, it should come from
4032 // WebKitBackForwardCacheExpirationIntervalKey in WebKit.
4033 // Or we should remove WebKitBackForwardCacheExpirationIntervalKey.
4034 if (interval <= 1800) {
4035 load(cachedPage->documentLoader(), loadType, 0);
4038 LOG(PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", m_provisionalHistoryItem->url().url().ascii());
4039 pageCache()->remove(item);
4044 ResourceRequest request(itemURL);
4046 addExtraFieldsToRequest(request, true, formData);
4048 // If this was a repost that failed the page cache, we might try to repost the form.
4049 NavigationAction action;
4051 request.setHTTPMethod("POST");
4052 request.setHTTPReferrer(item->formReferrer());
4053 request.setHTTPBody(formData);
4054 request.setHTTPContentType(item->formContentType());
4056 // FIXME: Slight hack to test if the NSURL cache contains the page we're going to.
4057 // We want to know this before talking to the policy delegate, since it affects whether
4058 // we show the DoYouReallyWantToRepost nag.
4060 // This trick has a small bug (3123893) where we might find a cache hit, but then
4061 // have the item vanish when we try to use it in the ensuing nav. This should be
4062 // extremely rare, but in that case the user will get an error on the navigation.
4064 if (ResourceHandle::willLoadFromCache(request))
4065 action = NavigationAction(itemURL, loadType, false);
4067 request.setCachePolicy(ReloadIgnoringCacheData);
4068 action = NavigationAction(itemURL, NavigationTypeFormResubmitted);
4072 case FrameLoadTypeReload:
4073 request.setCachePolicy(ReloadIgnoringCacheData);
4075 case FrameLoadTypeBack:
4076 case FrameLoadTypeForward:
4077 case FrameLoadTypeIndexedBackForward:
4078 if (itemURL.protocol() == "https")
4079 request.setCachePolicy(ReturnCacheDataElseLoad);
4081 case FrameLoadTypeStandard:
4082 case FrameLoadTypeRedirectWithLockedHistory:
4083 // no-op: leave as protocol default
4084 // FIXME: I wonder if we ever hit this case
4086 case FrameLoadTypeSame:
4087 case FrameLoadTypeReloadAllowingStaleData:
4089 ASSERT_NOT_REACHED();
4092 action = NavigationAction(itemOriginalURL, loadType, false);
4095 load(request, action, loadType, 0);
4100 // Walk the frame tree and ensure that the URLs match the URLs in the item.
4101 bool FrameLoader::urlsMatchItem(HistoryItem* item) const
4103 KURL currentURL = documentLoader()->URL();
4105 if (!equalIgnoringRef(currentURL, item->url()))
4108 const HistoryItemVector& childItems = item->children();
4110 unsigned size = childItems.size();
4111 for (unsigned i = 0; i < size; ++i) {
4112 Frame* childFrame = m_frame->tree()->child(childItems[i]->target());
4113 if (childFrame && !childFrame->loader()->urlsMatchItem(childItems[i].get()))
4120 // Main funnel for navigating to a previous location (back/forward, non-search snap-back)
4121 // This includes recursion to handle loading into framesets properly
4122 void FrameLoader::goToItem(HistoryItem* targetItem, FrameLoadType type)
4124 ASSERT(!m_frame->tree()->parent());
4126 // shouldGoToHistoryItem is a private delegate method. This is needed to fix:
4127 // <rdar://problem/3951283> can view pages from the back/forward cache that should be disallowed by Parental Controls
4128 // Ultimately, history item navigations should go through the policy delegate. That's covered in:
4129 // <rdar://problem/3979539> back/forward cache navigations should consult policy delegate
4130 if (Page* page = m_frame->page())
4131 if (shouldGoToHistoryItem(targetItem)) {
4132 BackForwardList* bfList = page->backForwardList();
4133 HistoryItem* currentItem = bfList->currentItem();
4135 // Set the BF cursor before commit, which lets the user quickly click back/forward again.
4136 // - plus, it only makes sense for the top level of the operation through the frametree,
4137 // as opposed to happening for some/one of the page commits that might happen soon
4138 bfList->goToItem(targetItem);
4139 recursiveGoToItem(targetItem, currentItem, type);
4143 // The general idea here is to traverse the frame tree and the item tree in parallel,
4144 // tracking whether each frame already has the content the item requests. If there is
4145 // a match (by URL), we just restore scroll position and recurse. Otherwise we must
4146 // reload that frame, and all its kids.
4147 void FrameLoader::recursiveGoToItem(HistoryItem* item, HistoryItem* fromItem, FrameLoadType type)
4152 KURL itemURL = item->url();
4154 if (documentLoader())
4155 currentURL = documentLoader()->URL();
4157 // Always reload the target frame of the item we're going to. This ensures that we will
4158 // do -some- load for the transition, which means a proper notification will be posted
4160 // The exact URL has to match, including fragment. We want to go through the _load
4161 // method, even if to do a within-page navigation.
4162 // The current frame tree and the frame tree snapshot in the item have to match.
4163 if (!item->isTargetItem() &&
4164 itemURL == currentURL &&
4165 ((m_frame->tree()->name().isEmpty() && item->target().isEmpty()) || m_frame->tree()->name() == item->target()) &&
4166 childFramesMatchItem(item))
4168 // This content is good, so leave it alone and look for children that need reloading
4169 // Save form state (works from currentItem, since prevItem is nil)
4170 ASSERT(!m_previousHistoryItem);
4171 saveDocumentState();
4172 saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get());
4174 if (FrameView* view = m_frame->view())
4175 view->setWasScrolledByUser(false);
4177 m_currentHistoryItem = item;
4179 // Restore form state (works from currentItem)
4180 restoreDocumentState();
4182 // Restore the scroll position (we choose to do this rather than going back to the anchor point)
4183 restoreScrollPositionAndViewState();
4185 const HistoryItemVector& childItems = item->children();
4187 int size = childItems.size();
4188 for (int i = 0; i < size; ++i) {
4189 String childName = childItems[i]->target();
4190 HistoryItem* fromChildItem = fromItem->childItemWithName(childName);
4191 ASSERT(fromChildItem || fromItem->isTargetItem());
4192 Frame* childFrame = m_frame->tree()->child(childName);
4194 childFrame->loader()->recursiveGoToItem(childItems[i].get(), fromChildItem, type);
4197 loadItem(item, type);
4201 // helper method that determines whether the subframes described by the item's subitems
4202 // match our own current frameset
4203 bool FrameLoader::childFramesMatchItem(HistoryItem* item) const
4205 const HistoryItemVector& childItems = item->children();
4206 if (childItems.size() != m_frame->tree()->childCount())
4209 unsigned size = childItems.size();
4210 for (unsigned i = 0; i < size; ++i)
4211 if (!m_frame->tree()->child(childItems[i]->target()))
4214 // Found matches for all item targets
4218 void FrameLoader::addHistoryForCurrentLocation()