2 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include "FrameLoader.h"
35 #include "DOMImplementation.h"
36 #include "DocLoader.h"
38 #include "DocumentLoader.h"
39 #include "EditCommand.h"
41 #include "EditorClient.h"
44 #include "EventNames.h"
45 #include "FloatRect.h"
46 #include "FormState.h"
48 #include "FrameLoadRequest.h"
49 #include "FrameLoaderClient.h"
50 #include "FramePrivate.h"
51 #include "FrameTree.h"
52 #include "FrameView.h"
53 #include "HistoryItem.h"
54 #include "HTMLFormElement.h"
55 #include "HTMLNames.h"
56 #include "HTMLObjectElement.h"
57 #include "HTTPParsers.h"
58 #include "IconDatabase.h"
59 #include "IconLoader.h"
61 #include "MainResourceLoader.h"
63 #include "PageCache.h"
64 #include "PageState.h"
65 #include "ProgressTracker.h"
66 #include "RenderPart.h"
67 #include "RenderWidget.h"
68 #include "ResourceHandle.h"
69 #include "ResourceRequest.h"
70 #include "SegmentedString.h"
72 #include "SystemTime.h"
73 #include "TextResourceDecoder.h"
74 #include "WindowFeatures.h"
75 #include "XMLTokenizer.h"
76 #include "kjs_binding.h"
77 #include "kjs_proxy.h"
78 #include "kjs_window.h"
79 #include "xmlhttprequest.h"
80 #include <kjs/JSLock.h>
81 #include <kjs/object.h>
85 #import "WebDataProtocol.h"
92 using namespace HTMLNames;
93 using namespace EventNames;
95 struct FormSubmission {
98 RefPtr<FormData> data;
104 FormSubmission(const char* a, const String& u, PassRefPtr<FormData> d, const String& t,
105 const String& ct, const String& b, PassRefPtr<Event> e)
117 struct ScheduledRedirection {
118 enum Type { redirection, locationChange, historyNavigation, locationChangeDuringLoad };
127 ScheduledRedirection(double redirectDelay, const String& redirectURL, bool redirectLockHistory)
129 , delay(redirectDelay)
132 , lockHistory(redirectLockHistory)
133 , wasUserGesture(false)
137 ScheduledRedirection(Type locationChangeType,
138 const String& locationChangeURL, const String& locationChangeReferrer,
139 bool locationChangeLockHistory, bool locationChangeWasUserGesture)
140 : type(locationChangeType)
142 , URL(locationChangeURL)
143 , referrer(locationChangeReferrer)
145 , lockHistory(locationChangeLockHistory)
146 , wasUserGesture(locationChangeWasUserGesture)
150 explicit ScheduledRedirection(int historyNavigationSteps)
151 : type(historyNavigation)
153 , historySteps(historyNavigationSteps)
155 , wasUserGesture(false)
160 static double storedTimeOfLastCompletedLoad;
162 static void cancelAll(const ResourceLoaderSet& loaders)
164 const ResourceLoaderSet copy = loaders;
165 ResourceLoaderSet::const_iterator end = copy.end();
166 for (ResourceLoaderSet::const_iterator it = copy.begin(); it != end; ++it)
170 static bool getString(JSValue* result, String& string)
176 if (!result->getString(ustring))
182 bool isBackForwardLoadType(FrameLoadType type)
185 case FrameLoadTypeStandard:
186 case FrameLoadTypeReload:
187 case FrameLoadTypeReloadAllowingStaleData:
188 case FrameLoadTypeSame:
189 case FrameLoadTypeInternal:
190 case FrameLoadTypeReplace:
192 case FrameLoadTypeBack:
193 case FrameLoadTypeForward:
194 case FrameLoadTypeIndexedBackForward:
197 ASSERT_NOT_REACHED();
201 static int numRequests(Document* document)
204 return cache()->loader()->numRequests(document->docLoader());
208 FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client)
211 , m_state(FrameStateCommittedPage)
212 , m_loadType(FrameLoadTypeStandard)
213 , m_policyLoadType(FrameLoadTypeStandard)
214 , m_delegateIsHandlingProvisionalLoadError(false)
215 , m_delegateIsDecidingNavigationPolicy(false)
216 , m_delegateIsHandlingUnimplementablePolicy(false)
217 , m_firstLayoutDone(false)
218 , m_quickRedirectComing(false)
219 , m_sentRedirectNotification(false)
220 , m_inStopAllLoaders(false)
221 , m_cachePolicy(CachePolicyVerify)
222 , m_isExecutingJavaScriptFormAction(false)
223 , m_isRunningScript(false)
224 , m_wasLoadEventEmitted(false)
225 , m_wasUnloadEventEmitted(false)
226 , m_isComplete(false)
227 , m_isLoadingMainResource(false)
228 , m_cancellingWithLoadInProgress(false)
229 , m_needsClear(false)
230 , m_receivedData(false)
231 , m_encodingWasChosenByUser(false)
232 , m_containsPlugIns(false)
233 , m_redirectionTimer(this, &FrameLoader::redirectionTimerFired)
235 , m_openedByJavaScript(false)
239 FrameLoader::~FrameLoader()
243 HashSet<Frame*>::iterator end = m_openedFrames.end();
244 for (HashSet<Frame*>::iterator it = m_openedFrames.begin(); it != end; ++it)
245 (*it)->loader()->m_opener = 0;
247 m_client->frameLoaderDestroyed();
250 static void setAllDefersLoading(const ResourceLoaderSet& loaders, bool defers)
252 const ResourceLoaderSet copy = loaders;
253 ResourceLoaderSet::const_iterator end = copy.end();
254 for (ResourceLoaderSet::const_iterator it = copy.begin(); it != end; ++it)
255 (*it)->setDefersLoading(defers);
258 void FrameLoader::setDefersLoading(bool defers)
260 if (m_mainResourceLoader)
261 m_mainResourceLoader->setDefersLoading(defers);
262 setAllDefersLoading(m_subresourceLoaders, defers);
263 setAllDefersLoading(m_plugInStreamLoaders, defers);
264 m_client->setDefersLoading(defers);
267 Frame* FrameLoader::createWindow(const FrameLoadRequest& request, const WindowFeatures& features)
269 ASSERT(!features.dialog || request.frameName().isEmpty());
271 if (!request.frameName().isEmpty())
272 if (Frame* frame = m_frame->tree()->find(request.frameName())) {
273 if (!request.resourceRequest().url().isEmpty())
274 frame->loader()->load(request, true, 0, 0, HashMap<String, String>());
275 frame->page()->chrome()->focus();
279 // FIXME: Setting the referrer should be the caller's responsibility.
280 FrameLoadRequest requestWithReferrer = request;
281 requestWithReferrer.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
285 page = m_frame->page()->chrome()->createModalDialog(requestWithReferrer);
287 page = m_frame->page()->chrome()->createWindow(requestWithReferrer);
291 Frame* frame = page->mainFrame();
292 frame->tree()->setName(request.frameName());
294 page->chrome()->setToolbarsVisible(features.toolBarVisible || features.locationBarVisible);
295 page->chrome()->setStatusbarVisible(features.statusBarVisible);
296 page->chrome()->setScrollbarsVisible(features.scrollbarsVisible);
297 page->chrome()->setMenubarVisible(features.menuBarVisible);
298 page->chrome()->setResizable(features.resizable);
300 // 'x' and 'y' specify the location of the window, while 'width' and 'height'
301 // specify the size of the page. We can only resize the window, so
302 // adjust for the difference between the window size and the page size.
304 FloatRect windowRect = page->chrome()->windowRect();
305 FloatSize pageSize = page->chrome()->pageRect().size();
307 windowRect.setX(features.x);
309 windowRect.setY(features.y);
310 if (features.widthSet)
311 windowRect.setWidth(features.width + (windowRect.width() - pageSize.width()));
312 if (features.heightSet)
313 windowRect.setHeight(features.height + (windowRect.height() - pageSize.height()));
314 page->chrome()->setWindowRect(windowRect);
316 page->chrome()->show();
321 bool FrameLoader::canHandleRequest(const ResourceRequest& request)
323 return m_client->canHandleRequest(request);
326 void FrameLoader::changeLocation(const String& URL, const String& referrer, bool lockHistory, bool userGesture)
328 if (URL.find("javascript:", 0, false) == 0) {
329 String script = KURL::decode_string(URL.substring(strlen("javascript:")).deprecatedString());
330 JSValue* result = executeScript(0, script, userGesture);
332 if (getString(result, scriptResult)) {
340 ResourceRequestCachePolicy policy = (m_cachePolicy == CachePolicyReload) || (m_cachePolicy == CachePolicyRefresh)
341 ? ReloadIgnoringCacheData : UseProtocolCachePolicy;
342 ResourceRequest request(completeURL(URL), referrer, policy);
344 urlSelected(request, "_self", 0, lockHistory);
347 void FrameLoader::urlSelected(const ResourceRequest& request, const String& _target, Event* triggeringEvent, bool lockHistory)
349 String target = _target;
350 if (target.isEmpty() && m_frame->document())
351 target = m_frame->document()->baseTarget();
353 const KURL& url = request.url();
354 if (url.url().startsWith("javascript:", false)) {
355 executeScript(0, KURL::decode_string(url.url().mid(strlen("javascript:"))), true);
362 FrameLoadRequest frameRequest(request, target);
364 if (frameRequest.resourceRequest().httpReferrer().isEmpty())
365 frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
367 urlSelected(frameRequest, triggeringEvent);
370 bool FrameLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName)
372 // Support for <frame src="javascript:string">
375 if (urlString.startsWith("javascript:", false)) {
376 scriptURL = urlString.deprecatedString();
379 url = completeURL(urlString);
381 Frame* frame = m_frame->tree()->child(frameName);
383 ResourceRequestCachePolicy policy = (m_cachePolicy == CachePolicyReload) || (m_cachePolicy == CachePolicyRefresh)
384 ? ReloadIgnoringCacheData : UseProtocolCachePolicy;
385 frame->loader()->urlSelected(ResourceRequest(url, m_outgoingReferrer, policy), 0);
387 frame = loadSubframe(ownerElement, url, frameName, m_outgoingReferrer);
392 if (!scriptURL.isEmpty())
393 frame->loader()->replaceContentsWithScriptResult(scriptURL);
398 Frame* FrameLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer)
400 Frame* frame = createFrame(url, name, ownerElement, referrer);
402 checkEmitLoadEvent();
406 frame->loader()->m_isComplete = false;
408 if (ownerElement->renderer() && frame->view())
409 static_cast<RenderWidget*>(ownerElement->renderer())->setWidget(frame->view());
411 checkEmitLoadEvent();
413 // In these cases, the synchronous load would have finished
414 // before we could connect the signals, so make sure to send the
415 // completed() signal for the child by hand
416 // FIXME: In this case the Frame will have finished loading before
417 // it's being added to the child list. It would be a good idea to
418 // create the child first, then invoke the loader separately.
419 if (url.isEmpty() || url == "about:blank") {
420 frame->loader()->completed();
421 frame->loader()->checkCompleted();
427 void FrameLoader::submitFormAgain()
429 if (m_isRunningScript)
431 OwnPtr<FormSubmission> form(m_deferredFormSubmission.release());
433 submitForm(form->action, form->URL, form->data, form->target,
434 form->contentType, form->boundary, form->event.get());
437 void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<FormData> formData,
438 const String& target, const String& contentType, const String& boundary, Event* event)
440 ASSERT(formData.get());
442 KURL u = completeURL(url.isNull() ? "" : url);
446 DeprecatedString urlString = u.url();
447 if (urlString.startsWith("javascript:", false)) {
448 m_isExecutingJavaScriptFormAction = true;
449 executeScript(0, KURL::decode_string(urlString.mid(strlen("javascript:"))));
450 m_isExecutingJavaScriptFormAction = false;
454 if (m_isRunningScript) {
455 if (m_deferredFormSubmission)
457 m_deferredFormSubmission.set(new FormSubmission(action, url, formData, target,
458 contentType, boundary, event));
462 FrameLoadRequest frameRequest;
464 if (!m_outgoingReferrer.isEmpty())
465 frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
467 frameRequest.setFrameName(target.isEmpty() ? m_frame->document()->baseTarget() : target);
469 // Handle mailto: forms
470 bool mailtoForm = u.protocol() == "mailto";
474 if (equalIgnoringCase(contentType, "multipart/form-data"))
475 // FIXME: is this correct? I suspect not, but what site can we test this on?
476 body = formData->flattenToString();
477 else if (equalIgnoringCase(contentType, "text/plain"))
478 // Convention seems to be to decode, and s/&/\n/
479 body = KURL::decode_string(
480 formData->flattenToString().replace('&', '\n')
481 .replace('+', ' ').deprecatedString()); // Recode for the URL
483 body = formData->flattenToString();
485 String query = u.query();
486 if (!query.isEmpty())
488 u.setQuery((query + "body=" + KURL::encode_string(body.deprecatedString())).deprecatedString());
491 if (strcmp(action, "GET") == 0) {
493 u.setQuery(formData->flattenToString().deprecatedString());
495 frameRequest.resourceRequest().setHTTPBody(formData.get());
496 frameRequest.resourceRequest().setHTTPMethod("POST");
498 // construct some user headers if necessary
499 if (contentType.isNull() || contentType == "application/x-www-form-urlencoded")
500 frameRequest.resourceRequest().setHTTPContentType(contentType);
501 else // contentType must be "multipart/form-data"
502 frameRequest.resourceRequest().setHTTPContentType(contentType + "; boundary=" + boundary);
505 frameRequest.resourceRequest().setURL(u);
507 submitForm(frameRequest, event);
510 void FrameLoader::stopLoading(bool sendUnload)
512 if (m_frame->document() && m_frame->document()->tokenizer())
513 m_frame->document()->tokenizer()->stopParsing();
515 m_responseRefreshHeader = String();
516 m_responseModifiedHeader = String();
519 if (m_frame->document()) {
520 if (m_wasLoadEventEmitted && !m_wasUnloadEventEmitted) {
521 Node* currentFocusedNode = m_frame->document()->focusedNode();
522 if (currentFocusedNode)
523 currentFocusedNode->aboutToUnload();
524 m_frame->document()->dispatchWindowEvent(unloadEvent, false, false);
525 if (m_frame->document())
526 m_frame->document()->updateRendering();
527 m_wasUnloadEventEmitted = true;
530 if (m_frame->document() && !m_frame->document()->inPageCache())
531 m_frame->document()->removeAllEventListenersFromAllNodes();
534 m_isComplete = true; // to avoid calling completed() in finishedParsing() (David)
535 m_isLoadingMainResource = false;
536 m_wasLoadEventEmitted = true; // don't want that one either
537 m_cachePolicy = CachePolicyVerify; // Why here?
539 if (m_frame->document() && m_frame->document()->parsing()) {
541 m_frame->document()->setParsing(false);
544 m_workingURL = KURL();
546 if (Document* doc = m_frame->document()) {
547 if (DocLoader* docLoader = doc->docLoader())
548 cache()->loader()->cancelRequests(docLoader);
549 XMLHttpRequest::cancelRequests(doc);
552 // tell all subframes to stop as well
553 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
554 child->loader()->stopLoading(sendUnload);
559 void FrameLoader::stop()
561 // http://bugs.webkit.org/show_bug.cgi?id=10854
562 // The frame's last ref may be removed and it will be deleted by checkCompleted().
563 RefPtr<Frame> protector(m_frame);
565 if (m_frame->document()) {
566 if (m_frame->document()->tokenizer())
567 m_frame->document()->tokenizer()->stopParsing();
568 m_frame->document()->finishParsing();
570 // WebKit partially uses WebCore when loading non-HTML docs. In these cases doc==nil, but
571 // WebCore is enough involved that we need to checkCompleted() in order for m_bComplete to
572 // become true. An example is when a subframe is a pure text doc, and that subframe is the
573 // last one to complete.
576 m_iconLoader->stopLoading();
579 bool FrameLoader::closeURL()
583 m_frame->editor()->clearUndoRedoOperations();
587 void FrameLoader::cancelRedirection(bool cancelWithLoadInProgress)
589 m_cancellingWithLoadInProgress = cancelWithLoadInProgress;
591 stopRedirectionTimer();
593 m_scheduledRedirection.clear();
596 KURL FrameLoader::iconURL()
598 // If this isn't a top level frame, return nothing
599 if (m_frame->tree() && m_frame->tree()->parent())
602 // If we have an iconURL from a Link element, return that
603 if (m_frame->document() && !m_frame->document()->iconURL().isEmpty())
604 return m_frame->document()->iconURL().deprecatedString();
606 // Don't return a favicon iconURL unless we're http or https
607 if (m_URL.protocol() != "http" && m_URL.protocol() != "https")
611 url.setProtocol(m_URL.protocol());
612 url.setHost(m_URL.host());
613 url.setPath("/favicon.ico");
617 bool FrameLoader::didOpenURL(const KURL& url)
619 if (m_scheduledRedirection && m_scheduledRedirection->type == ScheduledRedirection::locationChangeDuringLoad)
620 // A redirect was scheduled before the document was created.
621 // This can happen when one frame changes another frame's location.
625 m_frame->editor()->setLastEditCommand(0);
628 m_isComplete = false;
629 m_isLoadingMainResource = true;
630 m_wasLoadEventEmitted = false;
632 m_frame->d->m_kjsStatusBarText = String();
633 m_frame->d->m_kjsDefaultStatusBarText = String();
636 if (m_URL.protocol().startsWith("http") && !m_URL.host().isEmpty() && m_URL.path().isEmpty())
638 m_workingURL = m_URL;
645 void FrameLoader::didExplicitOpen()
647 m_isComplete = false;
648 m_wasLoadEventEmitted = false;
650 // Prevent window.open(url) -- eg window.open("about:blank") -- from blowing away results
651 // from a subsequent window.document.open / window.document.write call.
652 // Cancelling redirection here works for all cases because document.open
653 // implicitly precedes document.write.
655 if (m_frame->document()->URL() != "about:blank")
656 m_URL = m_frame->document()->URL();
659 void FrameLoader::replaceContentsWithScriptResult(const KURL& url)
661 JSValue* result = executeScript(0, KURL::decode_string(url.url().mid(strlen("javascript:"))));
663 if (!getString(result, scriptResult))
670 JSValue* FrameLoader::executeScript(Node* node, const String& script, bool forceUserGesture)
672 return executeScript(forceUserGesture ? String() : String(m_URL.url()), 0, node, script);
675 JSValue* FrameLoader::executeScript(const String& URL, int baseLine, Node* node, const String& script)
677 KJSProxy* proxy = m_frame->scriptProxy();
681 bool wasRunningScript = m_isRunningScript;
682 m_isRunningScript = true;
684 JSValue* result = proxy->evaluate(URL, baseLine, script, node);
686 if (!wasRunningScript) {
687 m_isRunningScript = false;
689 Document::updateDocumentsRendering();
695 void FrameLoader::cancelAndClear()
705 void FrameLoader::clear(bool clearWindowProperties)
707 // FIXME: Commenting out the below line causes <http://bugs.webkit.org/show_bug.cgi?id=11212>, but putting it
708 // back causes a measurable performance regression which we will need to fix to restore the correct behavior
709 // urlsBridgeKnowsAbout.clear();
712 Mac(m_frame)->setMarkedTextRange(0, nil, nil);
717 m_needsClear = false;
719 if (m_frame->document()) {
720 m_frame->document()->cancelParsing();
721 m_frame->document()->willRemove();
722 m_frame->document()->detach();
725 // Do this after detaching the document so that the unload event works.
726 if (clearWindowProperties && m_frame->scriptProxy())
727 m_frame->scriptProxy()->clear();
729 m_frame->selectionController()->clear();
730 m_frame->eventHandler()->clear();
732 m_frame->view()->clear();
734 // Do not drop the document before the script proxy and view are cleared, as some destructors
735 // might still try to access the document.
736 m_frame->d->m_doc = 0;
739 m_containsPlugIns = false;
740 m_frame->cleanupPluginObjects();
742 m_redirectionTimer.stop();
743 m_scheduledRedirection.clear();
745 m_receivedData = false;
747 if (!m_encodingWasChosenByUser)
748 m_encoding = String();
751 void FrameLoader::receivedFirstData()
755 m_frame->document()->docLoader()->setCachePolicy(m_cachePolicy);
756 m_workingURL = KURL();
758 const String& refresh = m_responseRefreshHeader;
763 if (!parseHTTPRefresh(refresh, delay, URL))
769 URL = m_frame->document()->completeURL(URL);
771 // We want a new history item if the refresh timeout > 1 second
772 scheduleRedirection(delay, URL, delay <= 1);
775 const String& FrameLoader::responseMIMEType() const
777 return m_responseMIMEType;
780 void FrameLoader::setResponseMIMEType(const String& type)
782 m_responseMIMEType = type;
785 void FrameLoader::begin()
790 void FrameLoader::begin(const KURL& url)
792 if (m_workingURL.isEmpty())
793 createEmptyDocument(); // Creates an empty document if we don't have one already
796 partClearedInBegin();
799 m_isComplete = false;
800 m_wasLoadEventEmitted = false;
801 m_isLoadingMainResource = true;
804 ref.setUser(DeprecatedString());
805 ref.setPass(DeprecatedString());
806 ref.setRef(DeprecatedString());
807 m_outgoingReferrer = ref.url();
811 if (!m_URL.isEmpty())
814 RefPtr<Document> document = DOMImplementation::instance()->
815 createDocument(m_responseMIMEType, m_frame->view(), m_frame->inViewSourceMode());
816 m_frame->d->m_doc = document;
818 if (!document->attached())
820 document->setURL(m_URL.url());
821 // We prefer m_baseURL over m_URL because m_URL changes when we are
822 // about to load a new page.
823 document->setBaseURL(baseurl.url());
825 document->setDecoder(m_decoder.get());
827 updatePolicyBaseURL();
829 document->docLoader()->setAutoLoadImages(m_frame->settings()->loadsImagesAutomatically());
831 const KURL& userStyleSheet = m_frame->settings()->userStyleSheetLocation();
832 if (!userStyleSheet.isEmpty())
833 m_frame->setUserStyleSheetLocation(KURL(userStyleSheet));
835 restoreDocumentState();
837 document->implicitOpen();
840 m_frame->view()->resizeContents(0, 0);
843 void FrameLoader::write(const char* str, int len, bool flush)
845 if (len == 0 && !flush)
851 Tokenizer* tokenizer = m_frame->document()->tokenizer();
852 if (tokenizer && tokenizer->wantsRawData()) {
853 tokenizer->writeRawData(str, len);
858 m_decoder = new TextResourceDecoder(m_responseMIMEType, m_frame->settings()->defaultTextEncodingName());
859 if (!m_encoding.isNull())
860 m_decoder->setEncoding(m_encoding,
861 m_encodingWasChosenByUser ? TextResourceDecoder::UserChosenEncoding : TextResourceDecoder::EncodingFromHTTPHeader);
862 if (m_frame->document())
863 m_frame->document()->setDecoder(m_decoder.get());
866 String decoded = m_decoder->decode(str, len);
868 decoded += m_decoder->flush();
869 if (decoded.isEmpty())
872 if (!m_receivedData) {
873 m_receivedData = true;
874 m_frame->document()->determineParseMode(decoded);
875 if (m_decoder->encoding().usesVisualOrdering())
876 m_frame->document()->setVisuallyOrdered();
877 m_frame->document()->recalcStyle(Node::Force);
881 ASSERT(!tokenizer->wantsRawData());
882 tokenizer->write(decoded, true);
886 void FrameLoader::write(const String& str)
891 if (!m_receivedData) {
892 m_receivedData = true;
893 m_frame->document()->setParseMode(Document::Strict);
896 if (Tokenizer* tokenizer = m_frame->document()->tokenizer())
897 tokenizer->write(str, true);
900 void FrameLoader::end()
902 m_isLoadingMainResource = false;
906 void FrameLoader::endIfNotLoading()
908 // http://bugs.webkit.org/show_bug.cgi?id=10854
909 // The frame's last ref may be removed and it can be deleted by checkCompleted(),
910 // so we'll add a protective refcount
911 RefPtr<Frame> protector(m_frame);
913 if (m_isLoadingMainResource)
916 // make sure nothing's left in there
917 if (m_frame->document()) {
919 m_frame->document()->finishParsing();
921 // WebKit partially uses WebCore when loading non-HTML docs. In these cases doc==nil, but
922 // WebCore is enough involved that we need to checkCompleted() in order for m_bComplete to
923 // become true. An example is when a subframe is a pure text doc, and that subframe is the
924 // last one to complete.
930 void FrameLoader::startIconLoader()
932 // FIXME: We kick off the icon loader when the frame is done receiving its main resource.
933 // But we should instead do it when we're done parsing the head element.
934 if (!isLoadingMainFrame())
937 IconDatabase* iconDB = IconDatabase::sharedIconDatabase();
940 if (!iconDB->enabled())
944 String urlString(url.url());
945 if (urlString.isEmpty())
948 // If we already have an unexpired icon, we won't kick off a load but we *will* map the appropriate URLs to it
949 if (iconDB->hasEntryForIconURL(urlString) && loadType() != FrameLoadTypeReload && !iconDB->isIconExpiredForIconURL(urlString)) {
950 commitIconURLToIconDatabase(url);
955 m_iconLoader.set(IconLoader::create(m_frame).release());
956 m_iconLoader->startLoading();
959 void FrameLoader::commitIconURLToIconDatabase(const KURL& icon)
961 IconDatabase* iconDB = IconDatabase::sharedIconDatabase();
963 iconDB->setIconURLForPageURL(icon.url(), m_URL.url());
964 iconDB->setIconURLForPageURL(icon.url(), originalRequestURL().url());
967 void FrameLoader::restoreDocumentState()
969 Document* doc = m_frame->document();
973 HistoryItem* itemToRestore = 0;
975 switch (loadType()) {
976 case FrameLoadTypeReload:
977 case FrameLoadTypeReloadAllowingStaleData:
978 case FrameLoadTypeSame:
979 case FrameLoadTypeReplace:
981 case FrameLoadTypeBack:
982 case FrameLoadTypeForward:
983 case FrameLoadTypeIndexedBackForward:
984 case FrameLoadTypeInternal:
985 case FrameLoadTypeStandard:
986 itemToRestore = m_currentHistoryItem.get();
992 doc->setStateForNewFormElements(itemToRestore->documentState());
995 void FrameLoader::gotoAnchor()
997 // If our URL has no ref, then we have no place we need to jump to.
998 // OTOH if css target was set previously, we want to set it to 0, recalc
999 // and possibly repaint because :target pseudo class may have been
1000 // set(See bug 11321)
1001 if (!m_URL.hasRef() &&
1002 !(m_frame->document() && m_frame->document()->getCSSTarget()))
1005 DeprecatedString ref = m_URL.encodedHtmlRef();
1006 if (!gotoAnchor(ref)) {
1007 // Can't use htmlRef() here because it doesn't know which encoding to use to decode.
1008 // Decoding here has to match encoding in completeURL, which means it has to use the
1009 // page's encoding rather than UTF-8.
1011 gotoAnchor(KURL::decode_string(ref, m_decoder->encoding()));
1015 void FrameLoader::finishedParsing()
1017 // This can be called from the Frame's destructor, in which case we shouldn't protect ourselves
1018 // because doing so will cause us to re-enter the destructor when protector goes out of scope.
1019 RefPtr<Frame> protector = m_frame->refCount() > 0 ? m_frame : 0;
1023 if (!m_frame->view())
1024 return; // We are being destroyed by something checkCompleted called.
1026 // Check if the scrollbars are really needed for the content.
1027 // If not, remove them, relayout, and repaint.
1028 m_frame->view()->restoreScrollbar();
1030 m_client->dispatchDidFinishDocumentLoad();
1035 void FrameLoader::loadDone()
1037 if (m_frame->document())
1041 void FrameLoader::checkCompleted()
1043 // Any frame that hasn't completed yet?
1044 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
1045 if (!child->loader()->m_isComplete)
1048 // Have we completed before?
1052 // Are we still parsing?
1053 if (m_frame->document() && m_frame->document()->parsing())
1056 // Still waiting for images/scripts?
1057 if (m_frame->document() && m_frame->document()->docLoader())
1058 if (cache()->loader()->numRequests(m_frame->document()->docLoader()))
1062 m_isComplete = true;
1064 checkEmitLoadEvent(); // if we didn't do it before
1066 // Do not start a redirection timer for subframes here.
1067 // That is deferred until the parent is completed.
1068 if (m_scheduledRedirection && !m_frame->tree()->parent())
1069 startRedirectionTimer();
1074 void FrameLoader::checkEmitLoadEvent()
1076 if (m_wasLoadEventEmitted || !m_frame->document() || m_frame->document()->parsing())
1079 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
1080 if (!child->loader()->m_isComplete) // still got a frame running -> too early
1083 // All frames completed -> set their domain to the frameset's domain
1084 // This must only be done when loading the frameset initially (#22039),
1085 // not when following a link in a frame (#44162).
1086 if (m_frame->document()) {
1087 String domain = m_frame->document()->domain();
1088 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
1089 if (child->document())
1090 child->document()->setDomain(domain);
1093 m_wasLoadEventEmitted = true;
1094 m_wasUnloadEventEmitted = false;
1095 if (m_frame->document())
1096 m_frame->document()->implicitClose();
1099 KURL FrameLoader::baseURL() const
1101 if (!m_frame->document())
1103 return m_frame->document()->baseURL();
1106 String FrameLoader::baseTarget() const
1108 if (!m_frame->document())
1110 return m_frame->document()->baseTarget();
1113 KURL FrameLoader::completeURL(const String& url)
1115 if (!m_frame->document())
1116 return url.deprecatedString();
1117 return m_frame->document()->completeURL(url).deprecatedString();
1120 void FrameLoader::scheduleRedirection(double delay, const String& url, bool doLockHistory)
1122 if (delay < 0 || delay > INT_MAX / 1000)
1124 if (!m_scheduledRedirection || delay <= m_scheduledRedirection->delay)
1125 scheduleRedirection(new ScheduledRedirection(delay, url, doLockHistory));
1128 void FrameLoader::scheduleLocationChange(const String& url, const String& referrer, bool lockHistory, bool wasUserGesture)
1130 // If the URL we're going to navigate to is the same as the current one, except for the
1131 // fragment part, we don't need to schedule the location change.
1132 KURL u(url.deprecatedString());
1133 if (u.hasRef() && equalIgnoringRef(m_URL, u)) {
1134 changeLocation(url, referrer, lockHistory, wasUserGesture);
1138 // Handle a location change of a page with no document as a special case.
1139 // This may happen when a frame changes the location of another frame.
1140 bool duringLoad = !m_frame->document();
1142 // If a redirect was scheduled during a load, then stop the current load.
1143 // Otherwise when the current load transitions from a provisional to a
1144 // committed state, pending redirects may be cancelled.
1148 ScheduledRedirection::Type type = duringLoad
1149 ? ScheduledRedirection::locationChangeDuringLoad : ScheduledRedirection::locationChange;
1150 scheduleRedirection(new ScheduledRedirection(type, url, referrer, lockHistory, wasUserGesture));
1153 void FrameLoader::scheduleRefresh(bool wasUserGesture)
1155 // Handle a location change of a page with no document as a special case.
1156 // This may happen when a frame requests a refresh of another frame.
1157 bool duringLoad = !m_frame->document();
1159 // If a refresh was scheduled during a load, then stop the current load.
1160 // Otherwise when the current load transitions from a provisional to a
1161 // committed state, pending redirects may be cancelled.
1165 ScheduledRedirection::Type type = duringLoad
1166 ? ScheduledRedirection::locationChangeDuringLoad : ScheduledRedirection::locationChange;
1167 scheduleRedirection(new ScheduledRedirection(type, m_URL.url(), m_outgoingReferrer, true, wasUserGesture));
1168 m_cachePolicy = CachePolicyRefresh;
1171 bool FrameLoader::isScheduledLocationChangePending() const
1173 if (!m_scheduledRedirection)
1175 switch (m_scheduledRedirection->type) {
1176 case ScheduledRedirection::redirection:
1178 case ScheduledRedirection::historyNavigation:
1179 case ScheduledRedirection::locationChange:
1180 case ScheduledRedirection::locationChangeDuringLoad:
1183 ASSERT_NOT_REACHED();
1187 void FrameLoader::scheduleHistoryNavigation(int steps)
1189 // navigation will always be allowed in the 0 steps case, which is OK because
1190 // that's supposed to force a reload.
1191 if (!canGoBackOrForward(steps)) {
1192 cancelRedirection();
1196 // If the URL we're going to navigate to is the same as the current one, except for the
1197 // fragment part, we don't need to schedule the navigation.
1198 if (equalIgnoringRef(m_URL, historyURL(steps))) {
1199 goBackOrForward(steps);
1203 scheduleRedirection(new ScheduledRedirection(steps));
1206 void FrameLoader::goBackOrForward(int distance)
1211 Page* page = m_frame->page();
1214 BackForwardList* list = page->backForwardList();
1218 HistoryItem* item = list->itemAtIndex(distance);
1221 int forwardListCount = list->forwardListCount();
1222 if (forwardListCount > 0)
1223 item = list->itemAtIndex(forwardListCount);
1225 int backListCount = list->forwardListCount();
1226 if (backListCount > 0)
1227 item = list->itemAtIndex(-backListCount);
1231 page->goToItem(item, FrameLoadTypeIndexedBackForward);
1234 void FrameLoader::redirectionTimerFired(Timer<FrameLoader>*)
1236 OwnPtr<ScheduledRedirection> redirection(m_scheduledRedirection.release());
1238 switch (redirection->type) {
1239 case ScheduledRedirection::redirection:
1240 case ScheduledRedirection::locationChange:
1241 case ScheduledRedirection::locationChangeDuringLoad:
1242 changeLocation(redirection->URL, redirection->referrer,
1243 redirection->lockHistory, redirection->wasUserGesture);
1245 case ScheduledRedirection::historyNavigation:
1246 if (redirection->historySteps == 0) {
1247 // Special case for go(0) from a frame -> reload only the frame
1248 urlSelected(m_URL, "", 0);
1251 // go(i!=0) from a frame navigates into the history of the frame only,
1252 // in both IE and NS (but not in Mozilla). We can't easily do that.
1253 goBackOrForward(redirection->historySteps);
1256 ASSERT_NOT_REACHED();
1259 String FrameLoader::encoding() const
1261 if (m_encodingWasChosenByUser && !m_encoding.isEmpty())
1263 if (m_decoder && m_decoder->encoding().isValid())
1264 return m_decoder->encoding().name();
1265 return m_frame->settings()->defaultTextEncodingName();
1268 bool FrameLoader::gotoAnchor(const String& name)
1270 if (!m_frame->document())
1273 Node* anchorNode = m_frame->document()->getElementById(AtomicString(name));
1275 anchorNode = m_frame->document()->anchors()->namedItem(name, !m_frame->document()->inCompatMode());
1277 m_frame->document()->setCSSTarget(anchorNode); // Setting to null will clear the current target.
1279 // Implement the rule that "" and "top" both mean top of page as in other browsers.
1280 if (!anchorNode && !(name.isEmpty() || equalIgnoringCase(name, "top")))
1283 // We need to update the layout before scrolling, otherwise we could
1284 // really mess things up if an anchor scroll comes at a bad moment.
1285 if (m_frame->document()) {
1286 m_frame->document()->updateRendering();
1287 // Only do a layout if changes have occurred that make it necessary.
1288 if (m_frame->view() && m_frame->document()->renderer() && m_frame->document()->renderer()->needsLayout())
1289 m_frame->view()->layout();
1292 // Scroll nested layers and frames to reveal the anchor.
1293 // Align to the top and to the closest side (this matches other browsers).
1294 RenderObject* renderer;
1297 renderer = m_frame->document()->renderer(); // top of document
1299 renderer = anchorNode->renderer();
1300 rect = anchorNode->getRect();
1303 renderer->enclosingLayer()->scrollRectToVisible(rect, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignTopAlways);
1308 bool FrameLoader::requestObject(RenderPart* renderer, const String& url, const AtomicString& frameName,
1309 const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
1311 if (url.isEmpty() && mimeType.isEmpty())
1316 completedURL = completeURL(url);
1319 if (shouldUsePlugin(completedURL, mimeType, renderer->hasFallbackContent(), useFallback)) {
1320 if (!m_frame->settings()->arePluginsEnabled())
1322 return loadPlugin(renderer, completedURL, mimeType, paramNames, paramValues, useFallback);
1325 ASSERT(renderer->node()->hasTagName(objectTag) || renderer->node()->hasTagName(embedTag));
1326 HTMLPlugInElement* element = static_cast<HTMLPlugInElement*>(renderer->node());
1328 AtomicString uniqueFrameName = m_frame->tree()->uniqueChildName(frameName);
1329 element->setFrameName(uniqueFrameName);
1331 // FIXME: OK to always make a new frame? When does the old frame get removed?
1332 return loadSubframe(element, completedURL, uniqueFrameName, m_outgoingReferrer);
1335 bool FrameLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool hasFallback, bool& useFallback)
1337 ObjectContentType objectType = objectContentType(url, mimeType);
1338 // If an object's content can't be handled and it has no fallback, let
1339 // it be handled as a plugin to show the broken plugin icon.
1340 useFallback = objectType == ObjectContentNone && hasFallback;
1341 return objectType == ObjectContentNone || objectType == ObjectContentPlugin;
1344 bool FrameLoader::loadPlugin(RenderPart* renderer, const KURL& url, const String& mimeType,
1345 const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
1349 if (renderer && !useFallback) {
1350 Element* pluginElement = 0;
1351 if (renderer->node() && renderer->node()->isElementNode())
1352 pluginElement = static_cast<Element*>(renderer->node());
1354 widget = createPlugin(pluginElement, url, paramNames, paramValues, mimeType);
1356 renderer->setWidget(widget);
1357 m_containsPlugIns = true;
1361 checkEmitLoadEvent();
1365 void FrameLoader::clearRecordedFormValues()
1367 m_formAboutToBeSubmitted = 0;
1368 m_formValuesAboutToBeSubmitted.clear();
1371 void FrameLoader::recordFormValue(const String& name, const String& value, PassRefPtr<HTMLFormElement> element)
1373 m_formAboutToBeSubmitted = element;
1374 m_formValuesAboutToBeSubmitted.set(name, value);
1377 void FrameLoader::parentCompleted()
1379 if (m_scheduledRedirection && !m_redirectionTimer.isActive())
1380 startRedirectionTimer();
1383 String FrameLoader::outgoingReferrer() const
1385 return m_outgoingReferrer;
1388 String FrameLoader::lastModified() const
1390 return m_responseModifiedHeader;
1393 Frame* FrameLoader::opener()
1398 void FrameLoader::setOpener(Frame* opener)
1401 m_opener->loader()->m_openedFrames.remove(m_frame);
1403 opener->loader()->m_openedFrames.add(m_frame);
1407 bool FrameLoader::openedByJavaScript()
1409 return m_openedByJavaScript;
1412 void FrameLoader::setOpenedByJavaScript()
1414 m_openedByJavaScript = true;
1417 void FrameLoader::handleFallbackContent()
1419 HTMLFrameOwnerElement* owner = m_frame->ownerElement();
1420 if (!owner || !owner->hasTagName(objectTag))
1422 static_cast<HTMLObjectElement*>(owner)->renderFallbackContent();
1425 void FrameLoader::provisionalLoadStarted()
1427 m_firstLayoutDone = false;
1428 cancelRedirection(true);
1429 FrameLoadType loadType = this->loadType();
1430 m_client->provisionalLoadStarted();
1432 // Cache the page, if possible.
1433 // Don't write to the cache if in the middle of a redirect, since we will want to
1434 // store the final page we end up on.
1435 // No point writing to the cache on a reload or loadSame, since we will just write
1436 // over it again when we leave that page.
1437 // FIXME: <rdar://problem/4886592> - We should work out the complexities of caching pages with frames as they
1438 // are the most interesting pages on the web, and often those that would benefit the most from caching!
1439 if (m_frame->page() && m_frame->page()->backForwardList()->usesPageCache()
1441 && m_currentHistoryItem
1442 && !isQuickRedirectComing()
1443 && loadType != FrameLoadTypeReload
1444 && loadType != FrameLoadTypeReloadAllowingStaleData
1445 && loadType != FrameLoadTypeSame
1446 && !documentLoader()->isLoading()
1447 && !documentLoader()->isStopping()) {
1449 if (!m_currentHistoryItem->pageCache()) {
1450 // Add the items to this page's cache.
1451 if (createPageCache(m_currentHistoryItem.get())) {
1452 // See if any page caches need to be purged after the addition of this new page cache.
1459 bool FrameLoader::userGestureHint()
1461 Frame* rootFrame = m_frame;
1462 while (rootFrame->tree()->parent())
1463 rootFrame = rootFrame->tree()->parent();
1465 if (rootFrame->scriptProxy())
1466 return rootFrame->scriptProxy()->interpreter()->wasRunByUserGesture();
1468 return true; // If JavaScript is disabled, a user gesture must have initiated the navigation
1471 #ifdef MULTIPLE_FORM_SUBMISSION_PROTECTION
1472 void FrameLoader::didNotOpenURL(const KURL& URL)
1474 if (m_submittedFormURL == URL)
1475 m_submittedFormURL = KURL();
1478 void FrameLoader::resetMultipleFormSubmissionProtection()
1480 m_submittedFormURL = KURL();
1484 void FrameLoader::setEncoding(const String& name, bool userChosen)
1486 if (!m_workingURL.isEmpty())
1487 receivedFirstData();
1489 m_encodingWasChosenByUser = userChosen;
1492 void FrameLoader::addData(const char* bytes, int length)
1494 ASSERT(m_workingURL.isEmpty());
1495 ASSERT(m_frame->document());
1496 ASSERT(m_frame->document()->parsing());
1497 write(bytes, length);
1500 bool FrameLoader::canCachePage()
1502 if (!m_client->canCachePage())
1505 return m_frame->document()
1506 && !m_frame->tree()->childCount()
1507 && !m_frame->tree()->parent()
1508 && !m_containsPlugIns
1509 && !m_URL.protocol().startsWith("https")
1510 && !m_frame->document()->applets()->length()
1511 && !m_frame->document()->hasWindowEventListener(unloadEvent)
1512 && !m_frame->document()->hasPasswordField();
1515 void FrameLoader::updatePolicyBaseURL()
1517 if (m_frame->tree()->parent() && m_frame->tree()->parent()->document())
1518 setPolicyBaseURL(m_frame->tree()->parent()->document()->policyBaseURL());
1520 setPolicyBaseURL(m_URL.url());
1523 void FrameLoader::setPolicyBaseURL(const String& s)
1525 if (m_frame->document())
1526 m_frame->document()->setPolicyBaseURL(s);
1527 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
1528 child->loader()->setPolicyBaseURL(s);
1531 // This does the same kind of work that FrameLoader::openURL does, except it relies on the fact
1532 // that a higher level already checked that the URLs match and the scrolling is the right thing to do.
1533 void FrameLoader::scrollToAnchor(const KURL& URL)
1540 // It's important to model this as a load that starts and immediately finishes.
1541 // Otherwise, the parent frame may think we never finished loading.
1542 m_isComplete = false;
1546 bool FrameLoader::isComplete() const
1548 return m_isComplete;
1551 bool FrameLoader::isLoadingMainResource() const
1553 return m_isLoadingMainResource;
1556 KURL FrameLoader::url() const
1561 void FrameLoader::scheduleRedirection(ScheduledRedirection* redirection)
1563 stopRedirectionTimer();
1564 m_scheduledRedirection.set(redirection);
1566 startRedirectionTimer();
1569 void FrameLoader::startRedirectionTimer()
1571 ASSERT(m_scheduledRedirection);
1573 m_redirectionTimer.stop();
1574 m_redirectionTimer.startOneShot(m_scheduledRedirection->delay);
1576 switch (m_scheduledRedirection->type) {
1577 case ScheduledRedirection::redirection:
1578 case ScheduledRedirection::locationChange:
1579 case ScheduledRedirection::locationChangeDuringLoad:
1580 clientRedirected(m_scheduledRedirection->URL.deprecatedString(),
1581 m_scheduledRedirection->delay,
1582 currentTime() + m_redirectionTimer.nextFireInterval(),
1583 m_scheduledRedirection->lockHistory,
1584 m_isExecutingJavaScriptFormAction);
1586 case ScheduledRedirection::historyNavigation:
1587 // Don't report history navigations.
1590 ASSERT_NOT_REACHED();
1593 void FrameLoader::stopRedirectionTimer()
1595 if (!m_redirectionTimer.isActive())
1598 m_redirectionTimer.stop();
1600 if (m_scheduledRedirection) {
1601 switch (m_scheduledRedirection->type) {
1602 case ScheduledRedirection::redirection:
1603 case ScheduledRedirection::locationChange:
1604 case ScheduledRedirection::locationChangeDuringLoad:
1605 clientRedirectCancelledOrFinished(m_cancellingWithLoadInProgress);
1607 case ScheduledRedirection::historyNavigation:
1608 // Don't report history navigations.
1611 ASSERT_NOT_REACHED();
1615 void FrameLoader::updateBaseURLForEmptyDocument()
1617 HTMLFrameOwnerElement* owner = m_frame->ownerElement();
1618 // FIXME: Should embed be included?
1619 if (owner && (owner->hasTagName(iframeTag) || owner->hasTagName(objectTag) || owner->hasTagName(embedTag)))
1620 m_frame->document()->setBaseURL(m_frame->tree()->parent()->document()->baseURL());
1623 void FrameLoader::completed()
1625 RefPtr<Frame> protect(m_frame);
1626 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
1627 child->loader()->parentCompleted();
1628 if (Frame* parent = m_frame->tree()->parent())
1629 parent->loader()->checkCompleted();
1633 void FrameLoader::started()
1635 for (Frame* frame = m_frame; frame; frame = frame->tree()->parent())
1636 frame->loader()->m_isComplete = false;
1639 bool FrameLoader::containsPlugins() const
1641 return m_containsPlugIns;
1644 void FrameLoader::prepareForLoadStart()
1646 if (m_frame->page())
1647 m_frame->page()->progress()->progressStarted(m_frame);
1648 m_client->dispatchDidStartProvisionalLoad();
1651 void FrameLoader::setupForReplace()
1653 setState(FrameStateProvisional);
1654 m_provisionalDocumentLoader = m_documentLoader;
1655 m_documentLoader = 0;
1659 void FrameLoader::setupForReplaceByMIMEType(const String& newMIMEType)
1661 activeDocumentLoader()->setupForReplaceByMIMEType(newMIMEType);
1664 void FrameLoader::finalSetupForReplace(DocumentLoader* loader)
1666 m_client->clearUnarchivingState(loader);
1669 void FrameLoader::load(const KURL& URL, Event* event)
1671 load(ResourceRequest(URL), true, event, 0, HashMap<String, String>());
1674 void FrameLoader::load(const FrameLoadRequest& request, bool userGesture, Event* event,
1675 HTMLFormElement* submitForm, const HashMap<String, String>& formValues)
1678 String argsReferrer = request.resourceRequest().httpReferrer();
1679 if (!argsReferrer.isEmpty())
1680 referrer = argsReferrer;
1682 referrer = m_outgoingReferrer;
1685 if (!canLoad(request.resourceRequest().url(), referrer, hideReferrer))
1688 referrer = String();
1690 Frame* targetFrame = m_frame->tree()->find(request.frameName());
1691 if (!canTarget(targetFrame))
1694 if (request.resourceRequest().httpMethod() != "POST") {
1695 FrameLoadType loadType;
1696 if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData)
1697 loadType = FrameLoadTypeReload;
1698 else if (!userGesture)
1699 loadType = FrameLoadTypeInternal;
1701 loadType = FrameLoadTypeStandard;
1703 load(request.resourceRequest().url(), referrer, loadType,
1704 request.frameName(), event, submitForm, formValues);
1706 post(request.resourceRequest().url(), referrer, request.frameName(),
1707 request.resourceRequest().httpBody(), request.resourceRequest().httpContentType(), event, submitForm, formValues);
1709 if (targetFrame && targetFrame != m_frame)
1710 targetFrame->page()->chrome()->focus();
1713 void FrameLoader::load(const KURL& URL, const String& referrer, FrameLoadType newLoadType,
1714 const String& frameName, Event* event, HTMLFormElement* form, const HashMap<String, String>& values)
1716 bool isFormSubmission = !values.isEmpty();
1718 ResourceRequest request(URL);
1719 if (!referrer.isEmpty())
1720 request.setHTTPReferrer(referrer);
1721 addExtraFieldsToRequest(request, true, event || isFormSubmission);
1722 if (newLoadType == FrameLoadTypeReload)
1723 request.setCachePolicy(ReloadIgnoringCacheData);
1725 ASSERT(newLoadType != FrameLoadTypeSame);
1727 NavigationAction action(URL, newLoadType, isFormSubmission, event);
1729 RefPtr<FormState> formState;
1730 if (form && !values.isEmpty())
1731 formState = FormState::create(form, values, m_frame);
1733 if (!frameName.isEmpty()) {
1734 if (Frame* targetFrame = m_frame->tree()->find(frameName))
1735 targetFrame->loader()->load(URL, referrer, newLoadType, String(), event, form, values);
1737 checkNewWindowPolicy(action, request, formState.release(), frameName);
1741 RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader;
1743 bool sameURL = shouldTreatURLAsSameAsCurrent(URL);
1745 // Make sure to do scroll to anchor processing even if the URL is
1746 // exactly the same so pages with '#' links and DHTML side effects
1748 if (!isFormSubmission
1749 && newLoadType != FrameLoadTypeReload
1750 && newLoadType != FrameLoadTypeSame
1751 && !shouldReload(URL, url())
1752 // We don't want to just scroll if a link from within a
1753 // frameset is trying to reload the frameset into _top.
1754 && !m_frame->isFrameSet()) {
1756 // Just do anchor navigation within the existing content.
1758 // We don't do this if we are submitting a form, explicitly reloading,
1759 // currently displaying a frameset, or if the new URL does not have a fragment.
1760 // These rules are based on what KHTML was doing in KHTMLPart::openURL.
1762 // FIXME: What about load types other than Standard and Reload?
1764 oldDocumentLoader->setTriggeringAction(action);
1766 checkNavigationPolicy(request, oldDocumentLoader.get(), formState.release(),
1767 callContinueFragmentScrollAfterNavigationPolicy, this);
1769 // must grab this now, since this load may stop the previous load and clear this flag
1770 bool isRedirect = m_quickRedirectComing;
1771 load(request, action, newLoadType, formState.release());
1773 m_quickRedirectComing = false;
1774 if (m_provisionalDocumentLoader)
1775 m_provisionalDocumentLoader->setIsClientRedirect(true);
1777 // Example of this case are sites that reload the same URL with a different cookie
1778 // driving the generated content, or a master frame with links that drive a target
1779 // frame, where the user has clicked on the same link repeatedly.
1780 m_loadType = FrameLoadTypeSame;
1784 void FrameLoader::load(const ResourceRequest& request)
1786 // FIXME: is this the right place to reset loadType? Perhaps this should be done after loading is finished or aborted.
1787 m_loadType = FrameLoadTypeStandard;
1788 load(m_client->createDocumentLoader(request).get());
1791 void FrameLoader::load(const ResourceRequest& request, const String& frameName)
1793 if (frameName.isEmpty()) {
1798 Frame* frame = m_frame->tree()->find(frameName);
1800 frame->loader()->load(request);
1804 checkNewWindowPolicy(NavigationAction(request.url(), NavigationTypeOther), request, 0, frameName);
1807 void FrameLoader::load(const ResourceRequest& request, const NavigationAction& action, FrameLoadType type, PassRefPtr<FormState> formState)
1809 RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request);
1810 setPolicyDocumentLoader(loader.get());
1812 loader->setTriggeringAction(action);
1813 if (m_documentLoader)
1814 loader->setOverrideEncoding(m_documentLoader->overrideEncoding());
1816 load(loader.get(), type, formState);
1819 void FrameLoader::load(DocumentLoader* newDocumentLoader)
1822 setPolicyDocumentLoader(newDocumentLoader);
1824 ResourceRequest& r = newDocumentLoader->request();
1825 addExtraFieldsToRequest(r, true, false);
1828 if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->originalRequest().url())) {
1829 r.setCachePolicy(ReloadIgnoringCacheData);
1830 type = FrameLoadTypeSame;
1832 type = FrameLoadTypeStandard;
1834 if (m_documentLoader)
1835 newDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding());
1837 // When we loading alternate content for an unreachable URL that we're
1838 // visiting in the b/f list, we treat it as a reload so the b/f list
1839 // is appropriately maintained.
1840 if (shouldReloadToHandleUnreachableURL(newDocumentLoader->originalRequest())) {
1841 ASSERT(type == FrameLoadTypeStandard);
1842 type = FrameLoadTypeReload;
1845 load(newDocumentLoader, type, 0);
1848 void FrameLoader::load(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> formState)
1850 ASSERT(m_client->hasWebView());
1852 // Unfortunately the view must be non-nil, this is ultimately due
1853 // to parser requiring a FrameView. We should fix this dependency.
1855 ASSERT(m_client->hasFrameView());
1857 m_policyLoadType = type;
1859 if (Frame* parent = m_frame->tree()->parent())
1860 loader->setOverrideEncoding(parent->loader()->documentLoader()->overrideEncoding());
1863 setPolicyDocumentLoader(loader);
1865 checkNavigationPolicy(loader->request(), loader, formState,
1866 callContinueLoadAfterNavigationPolicy, this);
1869 bool FrameLoader::canLoad(const KURL& url, const String& referrer, bool& hideReferrer)
1871 bool referrerIsWebURL = referrer.startsWith("http:", false) || referrer.startsWith("https:", false);
1872 bool referrerIsLocalURL = referrer.startsWith("file:", false) || referrer.startsWith("applewebdata:");
1873 bool URLIsFileURL = url.protocol().startsWith("file", false);
1874 bool referrerIsSecureURL = referrer.startsWith("https:", false);
1875 bool URLIsSecureURL = url.protocol().startsWith("https", false);
1877 hideReferrer = !referrerIsWebURL || (referrerIsSecureURL && !URLIsSecureURL);
1878 return !URLIsFileURL || referrerIsLocalURL;
1881 const ResourceRequest& FrameLoader::initialRequest() const
1883 return activeDocumentLoader()->initialRequest();
1886 void FrameLoader::receivedData(const char* data, int length)
1888 activeDocumentLoader()->receivedData(data, length);
1891 void FrameLoader::setRequest(const ResourceRequest& request)
1893 activeDocumentLoader()->setRequest(request);
1896 void FrameLoader::setResponse(const ResourceResponse& response)
1898 activeDocumentLoader()->setResponse(response);
1901 bool FrameLoader::willUseArchive(ResourceLoader* loader, const ResourceRequest& request, const KURL& originalURL) const
1903 return m_client->willUseArchive(loader, request, originalURL);
1906 void FrameLoader::handleUnimplementablePolicy(const ResourceError& error)
1908 m_delegateIsHandlingUnimplementablePolicy = true;
1909 m_client->dispatchUnableToImplementPolicy(error);
1910 m_delegateIsHandlingUnimplementablePolicy = false;
1913 void FrameLoader::cannotShowMIMEType(const ResourceResponse& response)
1915 handleUnimplementablePolicy(m_client->cannotShowMIMETypeError(response));
1918 ResourceError FrameLoader::interruptionForPolicyChangeError(const ResourceRequest& request)
1920 return m_client->interruptForPolicyChangeError(request);
1923 void FrameLoader::checkNavigationPolicy(const ResourceRequest& newRequest, NavigationPolicyDecisionFunction function, void* argument)
1925 checkNavigationPolicy(newRequest, activeDocumentLoader(), 0, function, argument);
1928 void FrameLoader::checkContentPolicy(const String& MIMEType, ContentPolicyDecisionFunction function, void* argument)
1930 m_policyCheck.set(function, argument);
1931 m_client->dispatchDecidePolicyForMIMEType(&FrameLoader::continueAfterContentPolicy,
1932 MIMEType, activeDocumentLoader()->request());
1935 bool FrameLoader::shouldReloadToHandleUnreachableURL(const ResourceRequest& request)
1937 KURL unreachableURL;
1939 unreachableURL = [request.nsURLRequest() _webDataRequestUnreachableURL];
1942 if (unreachableURL.isEmpty())
1945 if (!isBackForwardLoadType(m_policyLoadType))
1948 // We only treat unreachableURLs specially during the delegate callbacks
1949 // for provisional load errors and navigation policy decisions. The former
1950 // case handles well-formed URLs that can't be loaded, and the latter
1951 // case handles malformed URLs and unknown schemes. Loading alternate content
1952 // at other times behaves like a standard load.
1953 DocumentLoader* compareDocumentLoader = 0;
1954 if (m_delegateIsDecidingNavigationPolicy || m_delegateIsHandlingUnimplementablePolicy)
1955 compareDocumentLoader = m_policyDocumentLoader.get();
1956 else if (m_delegateIsHandlingProvisionalLoadError)
1957 compareDocumentLoader = m_provisionalDocumentLoader.get();
1959 return compareDocumentLoader && unreachableURL != compareDocumentLoader->request().url();
1962 void FrameLoader::reloadAllowingStaleData(const String& encoding)
1964 if (!m_documentLoader)
1967 ResourceRequest request = m_documentLoader->request();
1968 KURL unreachableURL = m_documentLoader->unreachableURL();
1969 if (!unreachableURL.isEmpty())
1970 request.setURL(unreachableURL);
1972 request.setCachePolicy(ReturnCacheDataElseLoad);
1974 RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request);
1975 setPolicyDocumentLoader(loader.get());
1977 loader->setOverrideEncoding(encoding);
1979 load(loader.get(), FrameLoadTypeReloadAllowingStaleData, 0);
1982 void FrameLoader::reload()
1984 if (!m_documentLoader)
1987 ResourceRequest& initialRequest = m_documentLoader->request();
1989 // If a window is created by javascript, its main frame can have an empty but non-nil URL.
1990 // Reloading in this case will lose the current contents (see 4151001).
1991 if (initialRequest.url().isEmpty())
1994 // Replace error-page URL with the URL we were trying to reach.
1995 KURL unreachableURL;
1997 unreachableURL = [initialRequest.nsURLRequest() _webDataRequestUnreachableURL];
1999 if (!unreachableURL.isEmpty())
2000 initialRequest = ResourceRequest(unreachableURL);
2002 RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(initialRequest);
2003 setPolicyDocumentLoader(loader.get());
2005 ResourceRequest& request = loader->request();
2007 request.setCachePolicy(ReloadIgnoringCacheData);
2009 // If we're about to re-post, set up action so the application can warn the user.
2010 if (request.httpMethod() == "POST")
2011 loader->setTriggeringAction(NavigationAction(request.url(), NavigationTypeFormResubmitted));
2013 loader->setOverrideEncoding(m_documentLoader->overrideEncoding());
2015 load(loader.get(), FrameLoadTypeReload, 0);
2018 bool FrameLoader::canTarget(Frame* target) const
2020 // This method prevents this exploit:
2021 // <rdar://problem/3715785> multiple frame injection vulnerability reported by Secunia, affects almost all browsers
2026 // Allow with navigation within the same page/frameset.
2027 if (m_frame->page() == target->page())
2031 if (Document* document = m_frame->document())
2032 domain = document->domain();
2033 // Allow if the request is made from a local file.
2034 if (domain.isEmpty())
2037 Frame* parent = target->tree()->parent();
2038 // Allow if target is an entire window.
2042 String parentDomain;
2043 if (Document* parentDocument = parent->document())
2044 domain = parentDocument->domain();
2045 // Allow if the domain of the parent of the targeted frame equals this domain.
2046 return equalIgnoringCase(parentDomain, domain);
2049 void FrameLoader::stopLoadingPlugIns()
2051 cancelAll(m_plugInStreamLoaders);
2054 void FrameLoader::stopLoadingSubresources()
2056 cancelAll(m_subresourceLoaders);
2059 void FrameLoader::stopLoadingSubframes()
2061 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
2062 child->loader()->stopAllLoaders();
2065 void FrameLoader::stopAllLoaders()
2067 // If this method is called from within this method, infinite recursion can occur (3442218). Avoid this.
2068 if (m_inStopAllLoaders)
2071 m_inStopAllLoaders = true;
2075 stopLoadingSubframes();
2076 if (m_provisionalDocumentLoader)
2077 m_provisionalDocumentLoader->stopLoading();
2078 if (m_documentLoader)
2079 m_documentLoader->stopLoading();
2080 setProvisionalDocumentLoader(0);
2081 m_client->clearArchivedResources();
2083 m_inStopAllLoaders = false;
2086 void FrameLoader::cancelMainResourceLoad()
2088 if (m_mainResourceLoader)
2089 m_mainResourceLoader->cancel();
2092 void FrameLoader::cancelPendingArchiveLoad(ResourceLoader* loader)
2094 m_client->cancelPendingArchiveLoad(loader);
2097 DocumentLoader* FrameLoader::activeDocumentLoader() const
2099 if (m_state == FrameStateProvisional)
2100 return m_provisionalDocumentLoader.get();
2101 return m_documentLoader.get();
2104 void FrameLoader::addPlugInStreamLoader(ResourceLoader* loader)
2106 m_plugInStreamLoaders.add(loader);
2107 activeDocumentLoader()->setLoading(true);
2110 void FrameLoader::removePlugInStreamLoader(ResourceLoader* loader)
2112 m_plugInStreamLoaders.remove(loader);
2113 activeDocumentLoader()->updateLoading();
2116 bool FrameLoader::hasMainResourceLoader() const
2118 return m_mainResourceLoader != 0;
2121 bool FrameLoader::isLoadingSubresources() const
2123 return !m_subresourceLoaders.isEmpty();
2126 bool FrameLoader::isLoadingPlugIns() const
2128 return !m_plugInStreamLoaders.isEmpty();
2131 bool FrameLoader::isLoading() const
2133 return isLoadingMainResource() || isLoadingSubresources() || isLoadingPlugIns();
2136 void FrameLoader::addSubresourceLoader(ResourceLoader* loader)
2138 ASSERT(!m_provisionalDocumentLoader);
2139 m_subresourceLoaders.add(loader);
2140 activeDocumentLoader()->setLoading(true);
2143 void FrameLoader::removeSubresourceLoader(ResourceLoader* loader)
2145 m_subresourceLoaders.remove(loader);
2146 activeDocumentLoader()->updateLoading();
2147 checkLoadComplete();
2150 void FrameLoader::releaseMainResourceLoader()
2152 m_mainResourceLoader = 0;
2155 void FrameLoader::setDocumentLoader(DocumentLoader* loader)
2157 if (!loader && !m_documentLoader)
2160 ASSERT(loader != m_documentLoader);
2161 ASSERT(!loader || loader->frameLoader() == this);
2163 m_client->prepareForDataSourceReplacement();
2164 if (m_documentLoader)
2165 m_documentLoader->detachFromFrame();
2167 m_documentLoader = loader;
2170 DocumentLoader* FrameLoader::documentLoader() const
2172 return m_documentLoader.get();
2175 void FrameLoader::setPolicyDocumentLoader(DocumentLoader* loader)
2177 if (m_policyDocumentLoader == loader)
2182 loader->setFrame(m_frame);
2183 if (m_policyDocumentLoader
2184 && m_policyDocumentLoader != m_provisionalDocumentLoader
2185 && m_policyDocumentLoader != m_documentLoader)
2186 m_policyDocumentLoader->detachFromFrame();
2188 m_policyDocumentLoader = loader;
2191 DocumentLoader* FrameLoader::provisionalDocumentLoader()
2193 return m_provisionalDocumentLoader.get();
2196 void FrameLoader::setProvisionalDocumentLoader(DocumentLoader* loader)
2198 ASSERT(!loader || !m_provisionalDocumentLoader);
2199 ASSERT(!loader || loader->frameLoader() == this);
2201 if (m_provisionalDocumentLoader && m_provisionalDocumentLoader != m_documentLoader)
2202 m_provisionalDocumentLoader->detachFromFrame();
2204 m_provisionalDocumentLoader = loader;
2207 FrameState FrameLoader::state() const
2212 double FrameLoader::timeOfLastCompletedLoad()
2214 return storedTimeOfLastCompletedLoad;
2217 void FrameLoader::setState(FrameState newState)
2221 if (newState == FrameStateProvisional)
2222 provisionalLoadStarted();
2223 else if (newState == FrameStateComplete) {
2224 frameLoadCompleted();
2225 storedTimeOfLastCompletedLoad = currentTime();
2226 if (m_documentLoader)
2227 m_documentLoader->stopRecordingResponses();
2231 void FrameLoader::clearProvisionalLoad()
2233 setProvisionalDocumentLoader(0);
2234 if (m_frame->page())
2235 m_frame->page()->progress()->progressCompleted(m_frame);
2236 setState(FrameStateComplete);
2239 void FrameLoader::markLoadComplete()
2241 setState(FrameStateComplete);
2244 void FrameLoader::commitProvisionalLoad()
2246 stopLoadingSubresources();
2247 stopLoadingPlugIns();
2249 setDocumentLoader(m_provisionalDocumentLoader.get());
2250 setProvisionalDocumentLoader(0);
2251 setState(FrameStateCommittedPage);
2254 void FrameLoader::commitProvisionalLoad(PassRefPtr<PageCache> prpPageCache)
2256 RefPtr<PageCache> pageCache = prpPageCache;
2257 RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader;
2259 if (m_loadType != FrameLoadTypeReplace)
2260 closeOldDataSources();
2263 m_client->makeRepresentation(pdl.get());
2265 transitionToCommitted(pageCache);
2267 // Call -_clientRedirectCancelledOrFinished: here so that the frame load delegate is notified that the redirect's
2268 // status has changed, if there was a redirect. The frame load delegate may have saved some state about
2269 // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:. Since we are
2270 // just about to commit a new page, there cannot possibly be a pending redirect at this point.
2271 if (m_sentRedirectNotification)
2272 clientRedirectCancelledOrFinished(false);
2274 RefPtr<PageState> pageState;
2276 pageState = pageCache->pageState();
2281 KURL url = pdl->URL();
2282 KURL dataURLBase = dataURLBaseFromRequest(pdl->request());
2283 if (!dataURLBase.isEmpty())
2287 url = pdl->responseURL();
2289 url = "about:blank";
2291 m_responseMIMEType = pdl->responseMIMEType();
2293 if (didOpenURL(url)) {
2294 m_responseRefreshHeader = pdl->response().httpHeaderField("Refresh");
2295 if (!pdl->getResponseModifiedHeader(m_responseModifiedHeader))
2296 m_responseModifiedHeader = "";
2302 void FrameLoader::transitionToCommitted(PassRefPtr<PageCache> pageCache)
2304 ASSERT(m_client->hasWebView());
2305 ASSERT(m_state == FrameStateProvisional);
2307 if (m_state != FrameStateProvisional)
2310 m_client->setCopiesOnScroll();
2311 updateHistoryForCommit();
2313 // The call to closeURL() invokes the unload event handler, which can execute arbitrary
2314 // JavaScript. If the script initiates a new load, we need to abandon the current load,
2315 // or the two will stomp each other.
2316 DocumentLoader* pdl = m_provisionalDocumentLoader.get();
2318 if (pdl != m_provisionalDocumentLoader)
2321 commitProvisionalLoad();
2323 // Handle adding the URL to the back/forward list.
2324 DocumentLoader* dl = m_documentLoader.get();
2325 String ptitle = dl->title();
2327 switch (m_loadType) {
2328 case FrameLoadTypeForward:
2329 case FrameLoadTypeBack:
2330 case FrameLoadTypeIndexedBackForward:
2331 if (m_frame->page()->backForwardList()) {
2332 updateHistoryForBackForwardNavigation();
2334 // Create a document view for this document, or used the cached view.
2336 m_client->setDocumentViewFromPageCache(pageCache.get());
2338 m_client->makeDocumentView();
2342 case FrameLoadTypeReload:
2343 case FrameLoadTypeSame:
2344 case FrameLoadTypeReplace:
2345 updateHistoryForReload();
2346 m_client->makeDocumentView();
2349 // FIXME - just get rid of this case, and merge FrameLoadTypeReloadAllowingStaleData with the above case
2350 case FrameLoadTypeReloadAllowingStaleData:
2351 m_client->makeDocumentView();
2354 case FrameLoadTypeStandard:
2355 updateHistoryForStandardLoad();
2356 m_client->makeDocumentView();
2359 case FrameLoadTypeInternal:
2360 updateHistoryForInternalLoad();
2361 m_client->makeDocumentView();
2364 // FIXME Remove this check when dummy ds is removed (whatever "dummy ds" is).
2365 // An exception should be thrown if we're in the FrameLoadTypeUninitialized state.
2367 ASSERT_NOT_REACHED();
2370 // Tell the client we've committed this URL.
2371 ASSERT(m_client->hasFrameView());
2372 m_client->dispatchDidCommitLoad();
2374 // If we have a title let the WebView know about it.
2375 if (!ptitle.isNull())
2376 m_client->dispatchDidReceiveTitle(ptitle);
2379 bool FrameLoader::privateBrowsingEnabled() const
2381 return m_client->privateBrowsingEnabled();
2384 void FrameLoader::clientRedirectCancelledOrFinished(bool cancelWithLoadInProgress)
2386 // Note that -webView:didCancelClientRedirectForFrame: is called on the frame load delegate even if
2387 // the redirect succeeded. We should either rename this API, or add a new method, like
2388 // -webView:didFinishClientRedirectForFrame:
2389 m_client->dispatchDidCancelClientRedirect();
2391 if (!cancelWithLoadInProgress)
2392 m_quickRedirectComing = false;
2394 m_sentRedirectNotification = false;
2397 void FrameLoader::clientRedirected(const KURL& URL, double seconds, double fireDate, bool lockHistory, bool isJavaScriptFormAction)
2399 m_client->dispatchWillPerformClientRedirect(URL, seconds, fireDate);
2401 // Remember that we sent a redirect notification to the frame load delegate so that when we commit
2402 // the next provisional load, we can send a corresponding -webView:didCancelClientRedirectForFrame:
2403 m_sentRedirectNotification = true;
2405 // If a "quick" redirect comes in an, we set a special mode so we treat the next
2406 // load as part of the same navigation. If we don't have a document loader, we have
2407 // no "original" load on which to base a redirect, so we treat the redirect as a normal load.
2408 m_quickRedirectComing = lockHistory && m_documentLoader && !isJavaScriptFormAction;
2411 bool FrameLoader::shouldReload(const KURL& currentURL, const KURL& destinationURL)
2413 // This function implements the rule: "Don't reload if navigating by fragment within
2414 // the same URL, but do reload if going to a new URL or to the same URL with no
2415 // fragment identifier at all."
2416 if (!currentURL.hasRef() && !destinationURL.hasRef())
2418 return !equalIgnoringRef(currentURL, destinationURL);
2421 void FrameLoader::closeOldDataSources()
2423 // FIXME: Is it important for this traversal to be postorder instead of preorder?
2424 // If so, add helpers for postorder traversal, and use them. If not, then lets not
2425 // use a recursive algorithm here.
2426 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
2427 child->loader()->closeOldDataSources();
2429 if (m_documentLoader)
2430 m_client->dispatchWillClose();
2432 m_client->setMainFrameDocumentReady(false); // stop giving out the actual DOMDocument to observers
2435 void FrameLoader::open(PageState& state)
2437 ASSERT(m_frame->page()->mainFrame() == m_frame);
2439 cancelRedirection();
2441 // We still have to close the previous part page.
2444 m_isComplete = false;
2446 // Don't re-emit the load event.
2447 m_wasLoadEventEmitted = true;
2449 // Delete old status bar messages (if it _was_ activated on last URL).
2450 if (m_frame->settings()->isJavaScriptEnabled()) {
2451 m_frame->d->m_kjsStatusBarText = String();
2452 m_frame->d->m_kjsDefaultStatusBarText = String();
2455 KURL URL = state.URL();
2457 if (URL.protocol().startsWith("http") && !URL.host().isEmpty() && URL.path().isEmpty())
2467 Document* document = state.document();
2469 document->setInPageCache(false);
2471 m_needsClear = true;
2472 m_isComplete = false;
2473 m_wasLoadEventEmitted = false;
2474 m_outgoingReferrer = URL.url();
2476 m_frame->setView(document->view());
2478 m_frame->d->m_doc = document;
2479 m_decoder = document->decoder();
2481 updatePolicyBaseURL();
2483 state.restore(m_frame->page());
2488 bool FrameLoader::isStopping() const
2490 return activeDocumentLoader()->isStopping();
2493 void FrameLoader::finishedLoading()
2495 // Retain because the stop may release the last reference to it.
2496 RefPtr<Frame> protect(m_frame);
2498 RefPtr<DocumentLoader> dl = activeDocumentLoader();
2499 dl->finishedLoading();
2500 if (!dl->mainDocumentError().isNull() || !dl->frameLoader())
2502 dl->setPrimaryLoadComplete(true);
2503 m_client->dispatchDidLoadMainResource(dl.get());
2504 checkLoadComplete();
2507 KURL FrameLoader::URL() const
2509 return activeDocumentLoader()->URL();
2512 bool FrameLoader::isArchiveLoadPending(ResourceLoader* loader) const
2514 return m_client->isArchiveLoadPending(loader);
2517 bool FrameLoader::isHostedByObjectElement() const
2519 HTMLFrameOwnerElement* owner = m_frame->ownerElement();
2520 return owner && owner->hasTagName(objectTag);
2523 bool FrameLoader::isLoadingMainFrame() const
2525 Page* page = m_frame->page();
2526 return page && m_frame == page->mainFrame();
2529 bool FrameLoader::canShowMIMEType(const String& MIMEType) const
2531 return m_client->canShowMIMEType(MIMEType);
2534 bool FrameLoader::representationExistsForURLScheme(const String& URLScheme)
2536 return m_client->representationExistsForURLScheme(URLScheme);
2539 String FrameLoader::generatedMIMETypeForURLScheme(const String& URLScheme)
2541 return m_client->generatedMIMETypeForURLScheme(URLScheme);
2544 void FrameLoader::cancelContentPolicyCheck()
2546 m_client->cancelPolicyCheck();
2547 m_policyCheck.clear();
2550 void FrameLoader::didReceiveServerRedirectForProvisionalLoadForFrame()
2552 m_client->dispatchDidReceiveServerRedirectForProvisionalLoad();
2555 void FrameLoader::finishedLoadingDocument(DocumentLoader* loader)
2557 m_client->finishedLoading(loader);
2560 bool FrameLoader::isReplacing() const
2562 return m_loadType == FrameLoadTypeReplace;
2565 void FrameLoader::setReplacing()
2567 m_loadType = FrameLoadTypeReplace;
2570 void FrameLoader::revertToProvisional(DocumentLoader* loader)
2572 m_client->revertToProvisionalState(loader);
2575 bool FrameLoader::subframeIsLoading() const
2577 // It's most likely that the last added frame is the last to load so we walk backwards.
2578 for (Frame* child = m_frame->tree()->lastChild(); child; child = child->tree()->previousSibling()) {
2579 FrameLoader* childLoader = child->loader();
2580 DocumentLoader* documentLoader = childLoader->documentLoader();
2581 if (documentLoader && documentLoader->isLoadingInAPISense())
2583 documentLoader = childLoader->provisionalDocumentLoader();
2584 if (documentLoader && documentLoader->isLoadingInAPISense())
2590 void FrameLoader::willChangeTitle(DocumentLoader* loader)
2592 m_client->willChangeTitle(loader);
2595 FrameLoadType FrameLoader::loadType() const
2600 void FrameLoader::stopPolicyCheck()
2602 m_client->cancelPolicyCheck();
2603 PolicyCheck check = m_policyCheck;
2604 m_policyCheck.clear();
2605 check.clearRequest();
2609 void FrameLoader::checkLoadCompleteForThisFrame()
2611 ASSERT(m_client->hasWebView());
2614 case FrameStateProvisional: {
2615 if (m_delegateIsHandlingProvisionalLoadError)
2618 RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader;
2622 // If we've received any errors we may be stuck in the provisional state and actually complete.
2623 const ResourceError& error = pdl->mainDocumentError();
2627 // Check all children first.
2628 RefPtr<HistoryItem> item;
2629 if (isBackForwardLoadType(loadType()) && m_frame == m_frame->page()->mainFrame())
2630 item = m_currentHistoryItem;
2632 bool shouldReset = true;
2633 if (!pdl->isLoadingInAPISense()) {
2634 m_delegateIsHandlingProvisionalLoadError = true;
2635 m_client->dispatchDidFailProvisionalLoad(error);
2636 m_delegateIsHandlingProvisionalLoadError = false;
2638 // FIXME: can stopping loading here possibly have any effect, if isLoading is false,
2639 // which it must be to be in this branch of the if? And is it OK to just do a full-on
2640 // stopAllLoaders instead of stopLoadingSubframes?
2641 stopLoadingSubframes();
2644 // Finish resetting the load state, but only if another load hasn't been started by the
2645 // delegate callback.
2646 if (pdl == m_provisionalDocumentLoader)
2647 clearProvisionalLoad();
2648 else if (m_documentLoader) {
2649 KURL unreachableURL = m_documentLoader->unreachableURL();
2650 if (!unreachableURL.isEmpty() && unreachableURL == pdl->request().url())
2651 shouldReset = false;
2654 if (shouldReset && item && m_frame->page())
2655 m_frame->page()->backForwardList()->goToItem(item.get());
2660 case FrameStateCommittedPage: {
2661 DocumentLoader* dl = m_documentLoader.get();
2662 if (dl->isLoadingInAPISense())
2667 // FIXME: Is this subsequent work important if we already navigated away?
2668 // Maybe there are bugs because of that, or extra work we can skip because
2669 // the new page is ready.
2671 m_client->forceLayoutForNonHTML();
2673 // If the user had a scroll point, scroll to it, overriding the anchor point if any.
2674 if ((isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadTypeReload)
2675 && m_frame->page() && m_frame->page()->backForwardList())
2676 restoreScrollPositionAndViewState();
2678 const ResourceError& error = dl->mainDocumentError();
2679 if (!error.isNull())
2680 m_client->dispatchDidFailLoad(error);
2682 m_client->dispatchDidFinishLoad();
2684 if (m_frame->page())
2685 m_frame->page()->progress()->progressCompleted(m_frame);
2689 case FrameStateComplete:
2690 // Even if already complete, we might have set a previous item on a frame that
2691 // didn't do any data loading on the past transaction. Make sure to clear these out.
2692 m_client->frameLoadCompleted();
2696 ASSERT_NOT_REACHED();
2698 void FrameLoader::continueAfterContentPolicy(PolicyAction policy)
2700 PolicyCheck check = m_policyCheck;
2701 m_policyCheck.clear();
2705 void FrameLoader::continueAfterWillSubmitForm(PolicyAction)
2710 void FrameLoader::didFirstLayout()
2712 if (isBackForwardLoadType(m_loadType) && m_frame->page() && m_frame->page()->backForwardList())
2713 restoreScrollPositionAndViewState();
2715 m_firstLayoutDone = true;
2716 m_client->dispatchDidFirstLayout();
2719 void FrameLoader::frameLoadCompleted()
2721 m_client->frameLoadCompleted();
2723 // After a canceled provisional load, firstLayoutDone is false.
2724 // Reset it to true if we're displaying a page.
2725 if (m_documentLoader)
2726 m_firstLayoutDone = true;
2729 bool FrameLoader::firstLayoutDone() const
2731 return m_firstLayoutDone;
2734 bool FrameLoader::isQuickRedirectComing() const
2736 return m_quickRedirectComing;
2739 void FrameLoader::detachChildren()
2741 // FIXME: Is it really necessary to do this in reverse order?
2743 for (Frame* child = m_frame->tree()->lastChild(); child; child = previous) {
2744 previous = child->tree()->previousSibling();
2745 child->loader()->detachFromParent();
2749 // Called every time a resource is completely loaded, or an error is received.
2750 void FrameLoader::checkLoadComplete()
2752 ASSERT(m_client->hasWebView());
2753 for (RefPtr<Frame> frame = m_frame; frame; frame = frame->tree()->parent())
2754 frame->loader()->checkLoadCompleteForThisFrame();
2757 int FrameLoader::numPendingOrLoadingRequests(bool recurse) const
2760 return numRequests(m_frame->document());
2763 for (Frame* frame = m_frame; frame; frame = frame->tree()->traverseNext(m_frame))
2764 count += numRequests(frame->document());
2768 FrameLoaderClient* FrameLoader::client() const
2773 void FrameLoader::submitForm(const FrameLoadRequest& request, Event* event)
2775 #ifdef MULTIPLE_FORM_SUBMISSION_PROTECTION
2776 // FIXME: We'd like to remove this altogether and fix the multiple form submission issue another way.
2777 // We do not want to submit more than one form from the same page,
2778 // nor do we want to submit a single form more than once.
2779 // This flag prevents these from happening; not sure how other browsers prevent this.
2780 // The flag is reset in each time we start handle a new mouse or key down event, and
2781 // also in setView since this part may get reused for a page from the back/forward cache.
2782 // The form multi-submit logic here is only needed when we are submitting a form that affects this frame.
2783 // FIXME: Frame targeting is only one of the ways the submission could end up doing something other
2784 // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly
2785 // needed any more now that we reset m_submittedFormURL on each mouse or key down event.
2786 Frame* target = m_frame->tree()->find(request.frameName());
2787 if (m_frame->tree()->isDescendantOf(target)) {
2788 if (m_submittedFormURL == request.resourceRequest().url())
2790 m_submittedFormURL = request.resourceRequest().url();
2794 // FIXME: Why do we always pass true for userGesture?
2795 load(request, true, event, m_formAboutToBeSubmitted.get(), m_formValuesAboutToBeSubmitted);
2797 clearRecordedFormValues();
2800 void FrameLoader::urlSelected(const FrameLoadRequest& request, Event* event)
2802 FrameLoadRequest copy = request;
2803 if (copy.resourceRequest().httpReferrer().isEmpty())
2804 copy.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
2806 // FIXME: Why do we always pass true for userGesture?
2807 load(copy, true, event, 0, HashMap<String, String>());
2810 String FrameLoader::userAgent() const
2812 return m_client->userAgent();
2815 void FrameLoader::createEmptyDocument()
2817 // Although it's not completely clear from the name of this function,
2818 // it does nothing if we already have a document, and just creates an
2819 // empty one if we have no document at all.
2820 if (!m_frame->document()) {
2821 loadEmptyDocumentSynchronously();
2822 updateBaseURLForEmptyDocument();
2826 void FrameLoader::tokenizerProcessedData()
2828 if (m_frame->document())
2830 checkLoadComplete();
2833 void FrameLoader::didTellBridgeAboutLoad(const String& URL)
2835 m_urlsBridgeKnowsAbout.add(URL);
2838 bool FrameLoader::haveToldBridgeAboutLoad(const String& URL)
2840 return m_urlsBridgeKnowsAbout.contains(URL);
2843 void FrameLoader::handledOnloadEvents()
2845 m_client->dispatchDidHandleOnloadEvents();
2848 void FrameLoader::frameDetached()
2854 void FrameLoader::detachFromParent()
2856 RefPtr<Frame> protect(m_frame);
2860 m_client->detachedFromParent1();
2862 m_client->detachedFromParent2();
2863 setDocumentLoader(0);
2864 m_client->detachedFromParent3();
2865 if (Frame* parent = m_frame->tree()->parent())
2866 parent->tree()->removeChild(m_frame);
2868 m_frame->setView(0);
2869 m_frame->pageDestroyed();
2874 m_client->detachedFromParent4();
2877 void FrameLoader::dispatchDidChangeLocationWithinPage()
2879 m_client->dispatchDidChangeLocationWithinPage();
2882 void FrameLoader::dispatchDidFinishLoadToClient()
2884 m_client->didFinishLoad();
2887 void FrameLoader::updateGlobalHistoryForStandardLoad(const KURL& url)
2889 m_client->updateGlobalHistoryForStandardLoad(url);
2892 void FrameLoader::updateGlobalHistoryForReload(const KURL& url)
2894 m_client->updateGlobalHistoryForReload(url);
2897 bool FrameLoader::shouldGoToHistoryItem(HistoryItem* item) const
2899 return m_client->shouldGoToHistoryItem(item);
2902 void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, bool mainResource, bool alwaysFromRequest)
2904 applyUserAgent(request);
2906 if (m_loadType == FrameLoadTypeReload)
2907 request.setHTTPHeaderField("Cache-Control", "max-age=0");
2909 // Don't set the cookie policy URL if it's already been set.
2910 if (request.mainDocumentURL().isEmpty()) {
2911 if (mainResource && (isLoadingMainFrame() || alwaysFromRequest))
2912 request.setMainDocumentURL(request.url());
2914 request.setMainDocumentURL(m_frame->page()->mainFrame()->loader()->url());
2918 request.setHTTPAccept("text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5");
2921 void FrameLoader::committedLoad(DocumentLoader* loader, const char* data, int length)
2923 m_client->committedLoad(loader, data, length);
2926 void FrameLoader::post(const KURL& URL, const String& referrer, const String& frameName, PassRefPtr<FormData> formData,
2927 const String& contentType, Event* event, HTMLFormElement* form, const HashMap<String, String>& formValues)
2929 // When posting, use the NSURLRequestReloadIgnoringCacheData load flag.
2930 // This prevents a potential bug which may cause a page with a form that uses itself
2931 // as an action to be returned from the cache without submitting.
2933 // FIXME: Where's the code that implements what the comment above says?
2935 ResourceRequest request(URL);
2936 addExtraFieldsToRequest(request, true, true);
2938 if (!referrer.isEmpty())
2939 request.setHTTPReferrer(referrer);
2940 request.setHTTPMethod("POST");
2941 request.setHTTPBody(formData);
2942 request.setHTTPContentType(contentType);
2944 NavigationAction action(URL, FrameLoadTypeStandard, true, event);
2946 RefPtr<FormState> formState;
2947 if (form && !formValues.isEmpty())
2948 formState = FormState::create(form, formValues, m_frame);
2950 if (!frameName.isEmpty()) {
2951 if (Frame* targetFrame = m_frame->tree()->find(frameName))
2952 targetFrame->loader()->load(request, action, FrameLoadTypeStandard, formState.release());
2954 checkNewWindowPolicy(action, request, formState.release(), frameName);
2956 load(request, action, FrameLoadTypeStandard, formState.release());
2959 bool FrameLoader::isReloading() const
2961 return documentLoader()->request().cachePolicy() == ReloadIgnoringCacheData;
2964 void FrameLoader::loadEmptyDocumentSynchronously()
2966 ResourceRequest request(KURL(""));
2970 void FrameLoader::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data)
2972 // Since this is a subresource, we can load any URL (we ignore the return value).
2973 // But we still want to know whether we should hide the referrer or not, so we call the canLoad method.
2974 String referrer = m_outgoingReferrer;
2976 canLoad(request.url(), referrer, hideReferrer);
2978 referrer = String();
2980 ResourceRequest initialRequest = request;
2981 initialRequest.setTimeoutInterval(10);
2983 if (initialRequest.isConditional())
2984 initialRequest.setCachePolicy(ReloadIgnoringCacheData);
2986 initialRequest.setCachePolicy(documentLoader()->request().cachePolicy());
2988 if (!referrer.isEmpty())
2989 initialRequest.setHTTPReferrer(referrer);
2991 initialRequest.setMainDocumentURL(m_frame->page()->mainFrame()->loader()->documentLoader()->request().url());
2992 initialRequest.setHTTPUserAgent(client()->userAgent());
2994 unsigned long identifier = 0;
2995 ResourceRequest newRequest(initialRequest);
2996 requestFromDelegate(newRequest, identifier, error);
2998 if (error.isNull()) {
2999 ASSERT(!newRequest.isNull());
3000 didTellBridgeAboutLoad(newRequest.url().url());
3001 ResourceHandle::loadResourceSynchronously(newRequest, error, response, data);
3004 sendRemainingDelegateMessages(identifier, response, data.size(), error);
3007 bool FrameLoader::startLoadingMainResource(ResourceRequest& request, unsigned long identifier)
3009 ASSERT(!m_mainResourceLoader);
3010 m_mainResourceLoader = MainResourceLoader::create(m_frame);
3011 m_mainResourceLoader->setIdentifier(identifier);
3013 // FIXME: is there any way the extra fields could have not been added by now?
3014 addExtraFieldsToRequest(request, true, false);
3015 if (!m_mainResourceLoader->load(request)) {
3016 // FIXME: If this should really be caught, we should just ASSERT this doesn't happen;
3017 // should it be caught by other parts of WebKit or other parts of the app?
3019 LOG_ERROR("could not create WebResourceHandle for URL %@ -- should be caught by policy handler level", request.url().getNSURL());
3021 m_mainResourceLoader = 0;
3027 // FIXME: Poor method name; also, why is this not part of startProvisionalLoad:?
3028 void FrameLoader::startLoading()
3030 if (!m_provisionalDocumentLoader)
3033 m_provisionalDocumentLoader->prepareForLoadStart();
3035 if (m_mainResourceLoader)
3038 m_provisionalDocumentLoader->setLoadingFromPageCache(false);
3040 unsigned long identifier = m_frame->page()->progress()->createUniqueIdentifier();
3041 m_client->assignIdentifierToInitialRequest(identifier, m_provisionalDocumentLoader.get(), m_provisionalDocumentLoader->originalRequest());
3043 if (!startLoadingMainResource(m_provisionalDocumentLoader->actualRequest(), identifier))
3044 m_provisionalDocumentLoader->updateLoading();
3047 void FrameLoader::cancelMainResourceLoad(const ResourceError& error)
3049 m_mainResourceLoader->cancel(error);
3052 void FrameLoader::assignIdentifierToInitialRequest(unsigned long identifier, const ResourceRequest& clientRequest)
3054 return m_client->assignIdentifierToInitialRequest(identifier, activeDocumentLoader(), clientRequest);
3057 void FrameLoader::willSendRequest(ResourceLoader* loader, ResourceRequest& clientRequest, const ResourceResponse& redirectResponse)
3059 applyUserAgent(clientRequest);
3060 m_client->dispatchWillSendRequest(activeDocumentLoader(), loader->identifier(), clientRequest, redirectResponse);
3063 void FrameLoader::didReceiveResponse(ResourceLoader* loader, const ResourceResponse& r)
3065 activeDocumentLoader()->addResponse(r);
3067 if (m_frame->page())
3068 m_frame->page()->progress()->incrementProgress(loader->identifier(), r);
3069 m_client->dispatchDidReceiveResponse(activeDocumentLoader(), loader->identifier(), r);
3072 void FrameLoader::didReceiveData(ResourceLoader* loader, const char* data, int length, int lengthReceived)
3074 if (m_frame->page())
3075 m_frame->page()->progress()->incrementProgress(loader->identifier(), data, length);
3076 m_client->dispatchDidReceiveContentLength(activeDocumentLoader(), loader->identifier(), lengthReceived);
3079 void FrameLoader::didFailToLoad(ResourceLoader* loader, const ResourceError& error)
3081 if (m_frame->page())
3082 m_frame->page()->progress()->completeProgress(loader->identifier());
3083 if (!error.isNull())
3084 m_client->dispatchDidFailLoading(activeDocumentLoader(), loader->identifier(), error);
3087 const ResourceRequest& FrameLoader::originalRequest() const
3089 return activeDocumentLoader()->originalRequestCopy();
3092 void FrameLoader::receivedMainResourceError(const ResourceError& error, bool isComplete)
3094 // Retain because the stop may release the last reference to it.
3095 RefPtr<Frame> protect(m_frame);
3097 RefPtr<DocumentLoader> loader = activeDocumentLoader();
3100 // FIXME: Don't want to do this if an entirely new load is going, so should check
3101 // that both data sources on the frame are either this or nil.
3103 if (m_client->shouldFallBack(error))
3104 handleFallbackContent();
3107 if (m_state == FrameStateProvisional) {
3108 #ifdef MULTIPLE_FORM_SUBMISSION_PROTECTION
3109 KURL failedURL = m_provisionalDocumentLoader->originalRequestCopy().url();
3110 didNotOpenURL(failedURL);
3112 // We might have made a page cache item, but now we're bailing out due to an error before we ever
3113 // transitioned to the new page (before WebFrameState == commit). The goal here is to restore any state
3114 // so that the existing view (that wenever got far enough to replace) can continue being used.
3115 Document* document = m_frame->document();
3117 document->setInPageCache(false);
3118 invalidateCurrentItemPageCache();
3120 // Call clientRedirectCancelledOrFinished here so that the frame load delegate is notified that the redirect's
3121 // status has changed, if there was a redirect. The frame load delegate may have saved some state about
3122 // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:. Since we are definitely
3123 // not going to use this provisional resource, as it was cancelled, notify the frame load delegate that the redirect
3125 if (m_sentRedirectNotification)
3126 clientRedirectCancelledOrFinished(false);
3130 loader->mainReceivedError(error, isComplete);
3133 void FrameLoader::callContinueFragmentScrollAfterNavigationPolicy(void* argument,
3134 const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue)
3136 FrameLoader* loader = static_cast<FrameLoader*>(argument);
3137 loader->continueFragmentScrollAfterNavigationPolicy(request, shouldContinue);
3140 void FrameLoader::continueFragmentScrollAfterNavigationPolicy(const ResourceRequest& request, bool shouldContinue)
3142 bool isRedirect = m_quickRedirectComing;
3143 m_quickRedirectComing = false;
3145 if (!shouldContinue)
3148 KURL URL = request.url();
3150 m_documentLoader->replaceRequestURLForAnchorScroll(URL);
3151 if (!isRedirect && !shouldTreatURLAsSameAsCurrent(URL)) {
3152 // NB: must happen after _setURL, since we add based on the current request.
3153 // Must also happen before we openURL and displace the scroll position, since
3154 // adding the BF item will save away scroll state.
3156 // NB2: If we were loading a long, slow doc, and the user anchor nav'ed before
3157 // it was done, currItem is now set the that slow doc, and prevItem is whatever was
3158 // before it. Adding the b/f item will bump the slow doc down to prevItem, even
3159 // though its load is not yet done. I think this all works out OK, for one because
3160 // we have already saved away the scroll and doc state for the long slow load,
3161 // but it's not an obvious case.
3163 addHistoryItemForFragmentScroll();
3166 scrollToAnchor(URL);
3169 // This will clear previousItem from the rest of the frame tree that didn't
3170 // doing any loading. We need to make a pass on this now, since for anchor nav
3171 // we'll not go through a real load and reach Completed state.
3172 checkLoadComplete();
3174 dispatchDidChangeLocationWithinPage();
3175 m_client->didFinishLoad();
3178 void FrameLoader::opened()
3180 if (m_loadType == FrameLoadTypeStandard && m_documentLoader->isClientRedirect())
3181 updateHistoryForClientRedirect();
3183 if (m_documentLoader->isLoadingFromPageCache()) {
3184 // Force a layout to update view size and thereby update scrollbars.
3185 m_client->forceLayout();
3187 const ResponseVector& responses = m_documentLoader->responses();
3188 size_t count = responses.size();
3189 for (size_t i = 0; i < count; i++) {
3190 const ResourceResponse& response = responses[i];
3191 // FIXME: If the WebKit client changes or cancels the request, this is not respected.
3192 ResourceError error;
3193 unsigned long identifier;
3194 ResourceRequest request(response.url());
3195 requestFromDelegate(request, identifier, error);
3196 sendRemainingDelegateMessages(identifier, response, response.expectedContentLength(), error);
3199 m_client->loadedFromPageCache();
3201 m_documentLoader->setPrimaryLoadComplete(true);
3203 // FIXME: Why only this frame and not parent frames?
3204 checkLoadCompleteForThisFrame();
3208 KURL FrameLoader::dataURLBaseFromRequest(const ResourceRequest& request) const
3211 if (WebDataRequestParameters* params = [request.nsURLRequest() _webDataRequestParametersForReading])
3212 return params->baseURL;
3217 void FrameLoader::checkNewWindowPolicy(const NavigationAction& action, const ResourceRequest& request,
3218 PassRefPtr<FormState> formState, const String& frameName)
3220 m_policyCheck.set(request, formState, frameName,
3221 callContinueLoadAfterNewWindowPolicy, this);
3222 m_client->dispatchDecidePolicyForNewWindowAction(&FrameLoader::continueAfterNewWindowPolicy,
3223 action, request, frameName);
3226 void FrameLoader::continueAfterNewWindowPolicy(PolicyAction policy)
3228 PolicyCheck check = m_policyCheck;
3229 m_policyCheck.clear();
3233 check.clearRequest();
3235 case PolicyDownload:
3236 m_client->startDownload(check.request());
3237 check.clearRequest();
3243 check.call(policy == PolicyUse);
3246 void FrameLoader::checkNavigationPolicy(const ResourceRequest& request, DocumentLoader* loader,
3247 PassRefPtr<FormState> formState, NavigationPolicyDecisionFunction function, void* argument)
3249 NavigationAction action = loader->triggeringAction();
3250 if (action.isEmpty()) {
3251 action = NavigationAction(request.url(), NavigationTypeOther);
3252 loader->setTriggeringAction(action);
3255 // Don't ask more than once for the same request or if we are loading an empty URL.
3256 // This avoids confusion on the part of the client.
3257 if (request == loader->lastCheckedRequest() || request.url().isEmpty()) {
3258 function(argument, request, 0, true);
3262 // We are always willing to show alternate content for unreachable URLs;
3263 // treat it like a reload so it maintains the right state for b/f list.
3265 if ([request.nsURLRequest() _webDataRequestUnreachableURL]) {
3266 if (isBackForwardLoadType(m_policyLoadType))
3267 m_policyLoadType = FrameLoadTypeReload;
3268 function(argument, request, 0, true);
3273 loader->setLastCheckedRequest(request);
3275 m_policyCheck.set(request, formState, function, argument);
3277 m_delegateIsDecidingNavigationPolicy = true;
3278 m_client->dispatchDecidePolicyForNavigationAction(&FrameLoader::continueAfterNavigationPolicy,
3280 m_delegateIsDecidingNavigationPolicy = false;
3283 void FrameLoader::continueAfterNavigationPolicy(PolicyAction policy)
3285 PolicyCheck check = m_policyCheck;
3286 m_policyCheck.clear();
3290 check.clearRequest();
3292 case PolicyDownload:
3293 m_client->startDownload(check.request());
3294 check.clearRequest();
3297 ResourceRequest request(check.request());
3298 if (!m_client->canHandleRequest(request)) {
3299 handleUnimplementablePolicy(m_client->cannotShowURLError(check.request()));
3300 check.clearRequest();
3306 check.call(policy == PolicyUse);
3309 void FrameLoader::callContinueLoadAfterNavigationPolicy(void* argument,
3310 const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue)
3312 FrameLoader* loader = static_cast<FrameLoader*>(argument);
3313 loader->continueLoadAfterNavigationPolicy(request, formState, shouldContinue);
3316 void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue)
3318 // If we loaded an alternate page to replace an unreachableURL, we'll get in here with a
3319 // nil policyDataSource because loading the alternate page will have passed
3320 // through this method already, nested; otherwise, policyDataSource should still be set.
3321 ASSERT(m_policyDocumentLoader || !m_provisionalDocumentLoader->unreachableURL().isEmpty());
3323 bool isTargetItem = m_provisionalHistoryItem ? m_provisionalHistoryItem->isTargetItem() : false;
3325 // Two reasons we can't continue:
3326 // 1) Navigation policy delegate said we can't so request is nil. A primary case of this
3327 // is the user responding Cancel to the form repost nag sheet.
3328 // 2) User responded Cancel to an alert popped up by the before unload event handler.
3329 // The "before unload" event handler runs only for the main frame.
3330 bool canContinue = shouldContinue && (!isLoadingMainFrame() || m_frame->shouldClose());
3333 // If we were waiting for a quick redirect, but the policy delegate decided to ignore it, then we
3334 // need to report that the client redirect was cancelled.
3335 if (m_quickRedirectComing)
3336 clientRedirectCancelledOrFinished(false);
3338 setPolicyDocumentLoader(0);
3340 // If the navigation request came from the back/forward menu, and we punt on it, we have the
3341 // problem that we have optimistically moved the b/f cursor already, so move it back. For sanity,
3342 // we only do this when punting a navigation for the target frame or top-level frame.
3343 if ((isTargetItem || isLoadingMainFrame()) && isBackForwardLoadType(m_policyLoadType) && m_frame->page()) {
3344 Frame* mainFrame = m_frame->page()->mainFrame();
3345 if (HistoryItem* resetItem = mainFrame->loader()->m_currentHistoryItem.get())
3346 m_frame->page()->backForwardList()->goToItem(resetItem);
3351 FrameLoadType type = m_policyLoadType;
3353 setProvisionalDocumentLoader(m_policyDocumentLoader.get());
3355 setState(FrameStateProvisional);
3357 setPolicyDocumentLoader(0);
3359 if (isBackForwardLoadType(type) && loadProvisionalItemFromPageCache())
3363 m_client->dispatchWillSubmitForm(&FrameLoader::continueAfterWillSubmitForm, formState);
3365 continueAfterWillSubmitForm();
3369 void FrameLoader::callContinueLoadAfterNewWindowPolicy(void* argument,
3370 const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, bool shouldContinue)
3372 FrameLoader* loader = static_cast<FrameLoader*>(argument);
3373 loader->continueLoadAfterNewWindowPolicy(request, formState, frameName, shouldContinue);
3376 void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& request,
3377 PassRefPtr<FormState> formState, const String& frameName, bool shouldContinue)
3379 if (!shouldContinue)
3382 RefPtr<Frame> frame = m_frame;
3383 RefPtr<Frame> mainFrame = m_client->dispatchCreatePage();
3387 mainFrame->tree()->setName(frameName);
3388 mainFrame->loader()->m_client->dispatchShow();
3389 mainFrame->loader()->setOpener(frame.get());
3390 mainFrame->loader()->load(request, NavigationAction(), FrameLoadTypeStandard, formState);
3393 void FrameLoader::sendRemainingDelegateMessages(unsigned long identifier, const ResourceResponse& response, unsigned length, const ResourceError& error)
3395 if (!response.isNull())
3396 m_client->dispatchDidReceiveResponse(m_documentLoader.get(), identifier, response);
3399 m_client->dispatchDidReceiveContentLength(m_documentLoader.get(), identifier, length);
3402 m_client->dispatchDidFinishLoading(m_documentLoader.get(), identifier);
3404 m_client->dispatchDidFailLoading(m_documentLoader.get(), identifier, error);
3407 void FrameLoader::requestFromDelegate(ResourceRequest& request, unsigned long& identifier, ResourceError& error)
3409 ASSERT(!request.isNull());
3411 identifier = m_frame->page()->progress()->createUniqueIdentifier();
3412 m_client->assignIdentifierToInitialRequest(identifier, m_documentLoader.get(), request);
3414 ResourceRequest newRequest(request);
3415 m_client->dispatchWillSendRequest(m_documentLoader.get(), identifier, newRequest, ResourceResponse());
3417 if (newRequest.isNull())
3418 error = m_client->cancelledError(request);
3420 error = ResourceError();
3422 request = newRequest;
3425 void FrameLoader::loadedResourceFromMemoryCache(const ResourceRequest& request, const ResourceResponse& response, int length)
3427 if (m_client->dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), request, response, length))
3430 unsigned long identifier;
3431 ResourceError error;
3432 ResourceRequest r(request);
3433 requestFromDelegate(r, identifier, error);
3434 sendRemainingDelegateMessages(identifier, response, length, error);
3437 void FrameLoader::applyUserAgent(ResourceRequest& request)
3439 String userAgent = client()->userAgent();
3440 ASSERT(!userAgent.isNull());
3441 request.setHTTPUserAgent(userAgent);
3444 bool FrameLoader::canGoBackOrForward(int distance) const
3448 if (distance > 0 && distance <= m_frame->page()->backForwardList()->forwardListCount())
3450 if (distance < 0 && -distance <= m_frame->page()->backForwardList()->backListCount())
3455 int FrameLoader::getHistoryLength()
3457 return m_frame->page()->backForwardList()->backListCount() + 1;
3460 KURL FrameLoader::historyURL(int distance)
3462 BackForwardList *list = m_frame->page()->backForwardList();
3463 HistoryItem* item = list->itemAtIndex(distance);
3466 int forwardListCount = list->forwardListCount();
3467 if (forwardListCount > 0)
3468 item = list->itemAtIndex(forwardListCount);
3470 int backListCount = list->backListCount();
3471 if (backListCount > 0)
3472 item = list->itemAtIndex(-backListCount);
3481 void FrameLoader::addHistoryItemForFragmentScroll()
3483 addBackForwardItemClippedAtTarget(false);
3486 bool FrameLoader::loadProvisionalItemFromPageCache()
3488 if (!m_provisionalHistoryItem || !m_provisionalHistoryItem->hasPageCache())
3491 RefPtr<PageState> state = m_provisionalHistoryItem->pageCache()->pageState();
3495 provisionalDocumentLoader()->loadFromPageCache(m_provisionalHistoryItem->pageCache());
3499 bool FrameLoader::createPageCache(HistoryItem* item)
3501 RefPtr<PageState> pageState = PageState::create(m_frame->page());
3504 item->setHasPageCache(false);
3508 item->setHasPageCache(true);
3509 RefPtr<PageCache> pageCache = item->pageCache();
3511 pageCache->setPageState(pageState.release());
3512 pageCache->setTimeStampToNow();
3513 pageCache->setDocumentLoader(documentLoader());
3514 m_client->saveDocumentViewToPageCache(pageCache.get());
3519 bool FrameLoader::shouldTreatURLAsSameAsCurrent(const KURL& URL) const
3521 if (!m_currentHistoryItem)
3523 return URL == m_currentHistoryItem->url() || URL == m_currentHistoryItem->originalURL();
3526 PassRefPtr<HistoryItem> FrameLoader::createHistoryItem(bool useOriginal)
3528 DocumentLoader* docLoader = documentLoader();
3530 KURL unreachableURL = docLoader ? docLoader->unreachableURL() : KURL();
3535 if (!unreachableURL.isEmpty()) {
3536 url = unreachableURL;
3537 originalURL = unreachableURL;
3539 originalURL = docLoader ? docLoader->originalURL() : KURL();
3543 url = docLoader->requestURL();
3546 LOG (History, "WebCoreHistory - Creating item for %s", url.url().ascii());
3548 // Frames that have never successfully loaded any content
3549 // may have no URL at all. Currently our history code can't
3550 // deal with such things, so we nip that in the bud here.
3551 // Later we may want to learn to live with nil for URL.
3552 // See bug 3368236 and related bugs for more information.
3554 url = KURL("about:blank");
3555 if (originalURL.isEmpty())
3556 originalURL = KURL("about:blank");
3558 RefPtr<HistoryItem> item = new HistoryItem(url, m_frame->tree()->name(), m_frame->tree()->parent() ? m_frame->tree()->parent()->tree()->name() : "", docLoader ? docLoader->title() : "");
3559 item->setOriginalURLString(originalURL.url());
3561 // Save form state if this is a POST
3564 item->setFormInfoFromRequest(docLoader->originalRequest());
3566 item->setFormInfoFromRequest(docLoader->request());
3569 // Set the item for which we will save document state
3570 m_previousHistoryItem = m_currentHistoryItem;
3571 m_currentHistoryItem = item;
3573 return item.release();
3576 void FrameLoader::addBackForwardItemClippedAtTarget(bool doClip)
3578 if (!documentLoader()->urlForHistory().isEmpty()) {
3579 Frame* mainFrame = m_frame->page()->mainFrame();
3581 RefPtr<HistoryItem> item = mainFrame->loader()->createHistoryItemTree(m_frame, doClip);
3582 LOG(BackForward, "WebCoreBackForward - Adding backforward item %p for frame %s", item.get(), documentLoader()->URL().url().ascii());
3583 ASSERT(m_frame->page());
3584 m_frame->page()->backForwardList()->addItem(item);
3588 PassRefPtr<HistoryItem> FrameLoader::createHistoryItemTree(Frame* targetFrame, bool clipAtTarget)
3590 RefPtr<HistoryItem> bfItem = createHistoryItem(m_frame->tree()->parent() ? true : false);
3591 if (m_previousHistoryItem)
3592 saveScrollPositionAndViewStateToItem(m_previousHistoryItem.get());
3593 if (!(clipAtTarget && m_frame == targetFrame)) {
3594 // save frame state for items that aren't loading (khtml doesn't save those)
3595 saveDocumentState();
3596 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
3597 bfItem->addChildItem(child->loader()->createHistoryItemTree(targetFrame, clipAtTarget));
3599 if (m_frame == targetFrame)
3600 bfItem->setIsTargetItem(true);
3604 void FrameLoader::saveScrollPositionAndViewStateToItem(HistoryItem* item)
3606 // FIXME: It would be great to work out a way to put this code in WebCore instead of calling through to the client.
3607 m_client->saveScrollPositionAndViewStateToItem(item);
3610 void FrameLoader::restoreScrollPositionAndViewState()
3612 // FIXME: It would be great to work out a way to put this code in WebCore instead of calling through to the client.
3613 m_client->restoreScrollPositionAndViewState();
3616 void FrameLoader::purgePageCache()
3618 // This method implements the rule for purging the page cache.
3619 if (!m_frame->page())
3622 BackForwardList* bfList = m_frame->page()->backForwardList();
3623 unsigned sizeLimit = bfList->pageCacheSize();
3624 unsigned pagesCached = 0;
3626 HistoryItemVector items;
3627 bfList->backListWithLimit(INT_MAX, items);
3628 RefPtr<HistoryItem> oldestNonSnapbackItem;
3632 for (; i < items.size(); ++i) {
3633 if (items[i]->hasPageCache()) {
3634 if (!oldestNonSnapbackItem && !items[i]->alwaysAttemptToUsePageCache())
3635 oldestNonSnapbackItem = items[i];
3640 // Snapback items are never directly purged here.
3641 if (pagesCached >= sizeLimit && oldestNonSnapbackItem) {
3642 LOG(PageCache, "Purging back/forward cache, %s\n", oldestNonSnapbackItem->url().url().ascii());
3643 oldestNonSnapbackItem->setHasPageCache(false);
3647 void FrameLoader::invalidateCurrentItemPageCache()
3649 // When we are pre-commit, the currentItem is where the pageCache data resides
3650 PageCache* pageCache = m_currentHistoryItem ? m_currentHistoryItem->pageCache() : 0;
3651 PageState* pageState = pageCache ? pageCache->pageState() : 0;
3653 // FIXME: This is a grotesque hack to fix <rdar://problem/4059059> Crash in RenderFlow::detach
3654 // Somehow the PageState object is not properly updated, and is holding onto a stale document.
3655 // Both Xcode and FileMaker see this crash, Safari does not.
3657 ASSERT(!pageState || pageState->document() == m_frame->document());
3658 if (pageState && pageState->document() == m_frame->document())
3661 if (m_currentHistoryItem)
3662 m_currentHistoryItem->setHasPageCache(false);
3665 void FrameLoader::saveDocumentState()
3667 // Do not save doc state if the page has a password field and a form that would be submitted via https.
3668 Document* document = m_frame->document();
3669 if (document && document->hasPasswordField() && document->hasSecureForm())
3672 // For a standard page load, we will have a previous item set, which will be used to
3673 // store the form state. However, in some cases we will have no previous item, and
3674 // the current item is the right place to save the state. One example is when we
3675 // detach a bunch of frames because we are navigating from a site with frames to
3676 // another site. Another is when saving the frame state of a frame that is not the
3677 // target of the current navigation (if we even decide to save with that granularity).
3679 // Because of previousItem's "masking" of currentItem for this purpose, it's important
3680 // that previousItem be cleared at the end of a page transition. We leverage the
3681 // checkLoadComplete recursion to achieve this goal.
3683 HistoryItem* item = m_previousHistoryItem ? m_previousHistoryItem.get() : m_currentHistoryItem.get();
3688 LOG(Loading, "WebCoreLoading %s: saving form state to %p", ((String&)m_frame->tree()->name()).ascii().data(), item);
3689 item->setDocumentState(document->formElementsState());
3693 // Loads content into this frame, as specified by history item
3694 void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType)
3696 KURL itemURL = item->url();
3697 KURL itemOriginalURL = item->originalURL();
3698 KURL currentURL = documentLoader()->URL();
3699 RefPtr<FormData> formData = item->formData();
3701 // Are we navigating to an anchor within the page?
3702 // Note if we have child frames we do a real reload, since the child frames might not
3703 // match our current frame structure, or they might not have the right content. We could
3704 // check for all that as an additional optimization.
3705 // We also do not do anchor-style navigation if we're posting a form.
3707 // FIXME: These checks don't match the ones in _loadURL:referrer:loadType:target:triggeringEvent:isFormSubmission:
3708 // Perhaps they should.
3709 if (!formData && !shouldReload(itemURL, currentURL) && urlsMatchItem(item)) {
3711 // FIXME: We need to normalize the code paths for anchor navigation. Something
3712 // like the following line of code should be done, but also accounting for correct
3713 // updates to the back/forward list and scroll position.
3714 // rjw 4/9/03 See 3223929.
3715 [self _loadURL:itemURL referrer:[[[self dataSource] request] HTTPReferrer] loadType:loadType target:nil triggeringEvent:nil form:nil formValues:nil];
3718 // Must do this maintenance here, since we don't go through a real page reload
3719 saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get());
3721 // FIXME: form state might want to be saved here too
3723 // We always call scrollToAnchor here, even if the URL doesn't have an
3724 // anchor fragment. This is so we'll keep the WebCore Frame's URL up-to-date.
3725 scrollToAnchor(item->url());
3727 // must do this maintenance here, since we don't go through a real page reload
3728 m_currentHistoryItem = item;
3729 restoreScrollPositionAndViewState();
3731 // Fake the URL change by updating the data source's request. This will no longer
3732 // be necessary if we do the better fix described above.
3733 documentLoader()->replaceRequestURLForAnchorScroll(itemURL);
3735 dispatchDidChangeLocationWithinPage();
3737 // FrameLoaderClient::didFinishLoad() tells the internal load delegate the load finished with no error
3738 dispatchDidFinishLoadToClient();
3740 // Remember this item so we can traverse any child items as child frames load
3741 m_provisionalHistoryItem = item;
3743 bool inPageCache = false;
3745 // Check if we'll be using the page cache. We only use the page cache
3746 // if one exists and it is less than _backForwardCacheExpirationInterval
3747 // seconds old. If the cache is expired it gets flushed here.
3748 if (item->hasPageCache()) {
3749 RefPtr<PageCache> pageCache = item->pageCache();
3750 double interval = currentTime() - pageCache->timeStamp();
3752 // FIXME: 1800 is the current backforwardcache expiration time, but we actually store as a pref -
3753 // previously, this code was -
3754 //if (interval <= [[getWebView(self) preferences] _backForwardCacheExpirationInterval]) {
3755 if (interval <= 1800) {
3756 load(pageCache->documentLoader(), loadType, 0);
3759 LOG (PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", m_provisionalHistoryItem->url().url().ascii());
3760 item->setHasPageCache(false);
3765 ResourceRequest request(itemURL);
3767 addExtraFieldsToRequest(request, true, formData);
3769 // If this was a repost that failed the page cache, we might try to repost the form.
3770 NavigationAction action;
3772 request.setHTTPMethod("POST");
3773 request.setHTTPReferrer(item->formReferrer());
3774 request.setHTTPBody(formData);
3775 request.setHTTPContentType(item->formContentType());
3777 // FIXME: Slight hack to test if the NSURL cache contains the page we're going to.
3778 // We want to know this before talking to the policy delegate, since it affects whether
3779 // we show the DoYouReallyWantToRepost nag.
3781 // This trick has a small bug (3123893) where we might find a cache hit, but then
3782 // have the item vanish when we try to use it in the ensuing nav. This should be
3783 // extremely rare, but in that case the user will get an error on the navigation.
3785 if (ResourceHandle::willLoadFromCache(request))
3786 action = NavigationAction(itemURL, loadType, false);
3788 request.setCachePolicy(ReloadIgnoringCacheData);
3789 action = NavigationAction(itemURL, NavigationTypeFormResubmitted);
3793 case FrameLoadTypeReload:
3794 request.setCachePolicy(ReloadIgnoringCacheData);
3796 case FrameLoadTypeBack:
3797 case FrameLoadTypeForward:
3798 case FrameLoadTypeIndexedBackForward:
3799 if (itemURL.protocol() == "https")
3800 request.setCachePolicy(ReturnCacheDataElseLoad);
3802 case FrameLoadTypeStandard:
3803 case FrameLoadTypeInternal:
3804 // no-op: leave as protocol default
3805 // FIXME: I wonder if we ever hit this case
3807 case FrameLoadTypeSame:
3808 case FrameLoadTypeReloadAllowingStaleData:
3810 ASSERT_NOT_REACHED();
3813 action = NavigationAction(itemOriginalURL, loadType, false);
3816 load(request, action, loadType, 0);
3821 // Walk the frame tree and ensure that the URLs match the URLs in the item.
3822 bool FrameLoader::urlsMatchItem(HistoryItem* item) const
3824 KURL currentURL = documentLoader()->URL();
3826 if (!equalIgnoringRef(currentURL, item->url()))
3829 const HistoryItemVector& childItems = item->children();
3831 unsigned size = childItems.size();
3832 for (unsigned i = 0; i < size; ++i) {
3833 Frame* childFrame = m_frame->tree()->child(childItems[i]->target());
3834 if (childFrame && !childFrame->loader()->urlsMatchItem(childItems[i].get()))
3841 // Main funnel for navigating to a previous location (back/forward, non-search snap-back)
3842 // This includes recursion to handle loading into framesets properly
3843 void FrameLoader::goToItem(HistoryItem* targetItem, FrameLoadType type)
3845 ASSERT(!m_frame->tree()->parent());
3847 // shouldGoToHistoryItem is a private delegate method. This is needed to fix:
3848 // <rdar://problem/3951283> can view pages from the back/forward cache that should be disallowed by Parental Controls
3849 // Ultimately, history item navigations should go through the policy delegate. That's covered in:
3850 // <rdar://problem/3979539> back/forward cache navigations should consult policy delegate
3851 if (shouldGoToHistoryItem(targetItem)) {
3852 BackForwardList* bfList = m_frame->page()->backForwardList();
3853 HistoryItem* currentItem = bfList->currentItem();
3855 // Set the BF cursor before commit, which lets the user quickly click back/forward again.
3856 // - plus, it only makes sense for the top level of the operation through the frametree,
3857 // as opposed to happening for some/one of the page commits that might happen soon
3858 bfList->goToItem(targetItem);
3859 recursiveGoToItem(targetItem, currentItem, type);
3863 // The general idea here is to traverse the frame tree and the item tree in parallel,
3864 // tracking whether each frame already has the content the item requests. If there is
3865 // a match (by URL), we just restore scroll position and recurse. Otherwise we must
3866 // reload that frame, and all its kids.
3867 void FrameLoader::recursiveGoToItem(HistoryItem* item, HistoryItem* fromItem, FrameLoadType type)
3872 KURL itemURL = item->url();
3873 KURL currentURL = documentLoader()->URL();
3875 // Always reload the target frame of the item we're going to. This ensures that we will
3876 // do -some- load for the transition, which means a proper notification will be posted
3878 // The exact URL has to match, including fragment. We want to go through the _load
3879 // method, even if to do a within-page navigation.
3880 // The current frame tree and the frame tree snapshot in the item have to match.
3881 if (!item->isTargetItem() &&
3882 itemURL == currentURL &&
3883 ((m_frame->tree()->name().isEmpty() && item->target().isEmpty()) || m_frame->tree()->name() == item->target()) &&
3884 childFramesMatchItem(item))
3886 // This content is good, so leave it alone and look for children that need reloading
3887 // Save form state (works from currentItem, since prevItem is nil)
3888 ASSERT(!m_previousHistoryItem);
3889 saveDocumentState();
3890 saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get());
3891 m_currentHistoryItem = item;
3893 // Restore form state (works from currentItem)
3894 restoreDocumentState();
3896 // Restore the scroll position (taken in favor of going back to the anchor)
3897 restoreScrollPositionAndViewState();
3899 const HistoryItemVector& childItems = item->children();
3901 int size = childItems.size();
3902 for (int i = 0; i < size; ++i) {
3903 String childName = childItems[i]->target();
3904 HistoryItem* fromChildItem = fromItem->childItemWithName(childName);
3905 ASSERT(fromChildItem || fromItem->isTargetItem());
3906 Frame* childFrame = m_frame->tree()->child(childName);
3908 childFrame->loader()->recursiveGoToItem(childItems[i].get(), fromChildItem, type);
3911 loadItem(item, type);
3915 // helper method that determines whether the subframes described by the item's subitems
3916 // match our own current frameset
3917 bool FrameLoader::childFramesMatchItem(HistoryItem* item) const
3919 const HistoryItemVector& childItems = item->children();
3920 if (childItems.size() != m_frame->tree()->childCount())
3923 unsigned size = childItems.size();
3924 for (unsigned i = 0; i < size; ++i)
3925 if (!m_frame->tree()->child(childItems[i]->target()))
3928 // Found matches for all item targets
3932 void FrameLoader::updateHistoryForStandardLoad()
3934 LOG(History, "WebCoreHistory - Updating History for Standard Load in frame %s", documentLoader()->URL().url().ascii());
3936 if (!documentLoader()->isClientRedirect()) {
3937 KURL url = documentLoader()->urlForHistory();
3938 if (!url.isEmpty()) {
3939 if (!privateBrowsingEnabled()) {
3940 // FIXME: <rdar://problem/4880065> - This will be a hook into the WebCore global history, and this loader/client call will be removed
3941 updateGlobalHistoryForStandardLoad(url);
3943 addBackForwardItemClippedAtTarget(true);
3945 } else if (documentLoader()->unreachableURL().isEmpty() && m_currentHistoryItem) {
3946 m_currentHistoryItem->setURL(documentLoader()->URL());
3947 m_currentHistoryItem->setFormInfoFromRequest(documentLoader()->request());
3951 void FrameLoader::updateHistoryForClientRedirect()
3954 if (documentLoader())
3955 LOG(History, "WebCoreHistory - Updating History for client redirect in frame %s", documentLoader()->title().utf8().data());
3958 // Clear out form data so we don't try to restore it into the incoming page. Must happen after
3959 // webcore has closed the URL and saved away the form state.
3960 if (m_currentHistoryItem) {
3961 m_currentHistoryItem->clearDocumentState();
3962 m_currentHistoryItem->clearScrollPoint();
3966 void FrameLoader::updateHistoryForBackForwardNavigation()
3969 if (documentLoader())
3970 LOG(History, "WebCoreHistory - Updating History for back/forward navigation in frame %s", documentLoader()->title().utf8().data());
3973 // Must grab the current scroll position before disturbing it
3974 saveScrollPositionAndViewStateToItem(m_previousHistoryItem.get());
3977 void FrameLoader::updateHistoryForReload()
3980 if (documentLoader())
3981 LOG(History, "WebCoreHistory - Updating History for reload in frame %s", documentLoader()->title().utf8().data());
3984 if (m_previousHistoryItem) {
3985 m_previousHistoryItem->setHasPageCache(false);
3987 if (loadType() == FrameLoadTypeReload)
3988 saveScrollPositionAndViewStateToItem(m_previousHistoryItem.get());
3990 // Sometimes loading a page again leads to a different result because of cookies. Bugzilla 4072
3991 if (documentLoader()->unreachableURL().isEmpty())
3992 m_previousHistoryItem->setURL(documentLoader()->requestURL());
3995 // FIXME: <rdar://problem/4880065> - This will be a hook into the WebCore global history, and this loader/client call will be removed
3996 // Update the last visited time. Mostly interesting for URL autocompletion statistics.
3997 updateGlobalHistoryForReload(documentLoader()->originalURL());
4000 void FrameLoader::updateHistoryForInternalLoad()
4003 if (documentLoader())
4004 LOG(History, "WebCoreHistory - Updating History for internal load in frame %s", documentLoader()->title().utf8().data());
4007 // Add an item to the item tree for this frame
4008 ASSERT(!documentLoader()->isClientRedirect());
4009 Frame* parentFrame = m_frame->tree()->parent();
4010 // The only case where parentItem is NULL should be when a parent frame loaded an
4011 // empty URL, which doesn't set up a current item in that parent.
4013 if (parentFrame->loader()->m_currentHistoryItem)
4014 parentFrame->loader()->m_currentHistoryItem->addChildItem(createHistoryItem(true));
4016 // See 3556159. It's not clear if it's valid to be in FrameLoadTypeOnLoadEvent
4017 // for a top-level frame, but that was a likely explanation for those crashes,
4018 // so let's guard against it.
4019 // ...and all FrameLoadTypeOnLoadEvent uses were folded to WebFrameLoadTypeInternal
4020 LOG_ERROR("No parent frame in transitionToCommitted:, FrameLoadTypeInternal");
4024 void FrameLoader::updateHistoryForCommit()
4027 if (documentLoader())
4028 LOG(History, "WebCoreHistory - Updating History for commit in frame %s", documentLoader()->title().utf8().data());
4030 FrameLoadType type = loadType();
4031 if (isBackForwardLoadType(type) ||
4032 (type == FrameLoadTypeReload && documentLoader() && !documentLoader()->unreachableURL().isEmpty())) {
4033 // Once committed, we want to use current item for saving DocState, and
4034 // the provisional item for restoring state.
4035 // Note previousItem must be set before we close the URL, which will
4036 // happen when the data source is made non-provisional below
4037 m_previousHistoryItem = m_currentHistoryItem;
4038 ASSERT(m_provisionalHistoryItem);
4039 m_currentHistoryItem = m_provisionalHistoryItem;
4040 m_provisionalHistoryItem = 0;
4044 // Walk the frame tree, telling all frames to save their form state into their current
4046 void FrameLoader::saveDocumentAndScrollState()
4048 for (Frame* frame = m_frame; frame; frame = frame->tree()->traverseNext(m_frame)) {
4049 frame->loader()->saveDocumentState();
4050 frame->loader()->saveScrollPositionAndViewStateToItem(frame->loader()->currentHistoryItem());
4054 // FIXME: These 6 setter/getters are here for a dwindling number of users in WebKit, WebFrame
4055 // being the primary one. After they're no longer needed there, they can be removed!
4056 HistoryItem* FrameLoader::currentHistoryItem()
4058 return m_currentHistoryItem.get();
4061 HistoryItem* FrameLoader::previousHistoryItem()
4063 return m_previousHistoryItem.get();
4066 HistoryItem* FrameLoader::provisionalHistoryItem()
4068 return m_provisionalHistoryItem.get();
4071 void FrameLoader::setCurrentHistoryItem(PassRefPtr<HistoryItem> item)
4073 m_currentHistoryItem = item;
4076 void FrameLoader::setPreviousHistoryItem(PassRefPtr<HistoryItem> item)
4078 m_previousHistoryItem = item;
4081 void FrameLoader::setProvisionalHistoryItem(PassRefPtr<HistoryItem> item)
4083 m_provisionalHistoryItem = item;
4086 void FrameLoader::setMainDocumentError(DocumentLoader* loader, const ResourceError& error)
4088 m_client->setMainDocumentError(loader, error);
4091 void FrameLoader::mainReceivedCompleteError(DocumentLoader* loader, const ResourceError& error)
4093 loader->setPrimaryLoadComplete(true);
4094 m_client->dispatchDidLoadMainResource(activeDocumentLoader());
4095 checkLoadComplete();
4098 void FrameLoader::mainReceivedError(const ResourceError& error, bool isComplete)
4100 activeDocumentLoader()->mainReceivedError(error, isComplete);
4103 ResourceError FrameLoader::cancelledError(const ResourceRequest& request) const
4105 return m_client->cancelledError(request);
4108 ResourceError FrameLoader::fileDoesNotExistError(const ResourceResponse& response) const
4110 return m_client->fileDoesNotExistError(response);
4113 void FrameLoader::didFinishLoad(ResourceLoader* loader)
4115 if (m_frame->page())
4116 m_frame->page()->progress()->completeProgress(loader->identifier());
4117 m_client->dispatchDidFinishLoading(activeDocumentLoader(), loader->identifier());
4120 PassRefPtr<SharedBuffer> FrameLoader::mainResourceData() const
4122 if (!m_mainResourceLoader)
4124 return m_mainResourceLoader->resourceData();
4127 void FrameLoader::didReceiveAuthenticationChallenge(ResourceLoader* loader, const AuthenticationChallenge& currentWebChallenge)
4129 m_client->dispatchDidReceiveAuthenticationChallenge(activeDocumentLoader(), loader->identifier(), currentWebChallenge);
4132 void FrameLoader::didCancelAuthenticationChallenge(ResourceLoader* loader, const AuthenticationChallenge& currentWebChallenge)
4134 m_client->dispatchDidCancelAuthenticationChallenge(activeDocumentLoader(), loader->identifier(), currentWebChallenge);
4137 PolicyCheck::PolicyCheck()
4138 : m_navigationFunction(0)
4139 , m_newWindowFunction(0)
4140 , m_contentFunction(0)
4144 void PolicyCheck::clear()
4147 m_navigationFunction = 0;
4148 m_newWindowFunction = 0;
4149 m_contentFunction = 0;
4152 void PolicyCheck::set(const ResourceRequest& request, PassRefPtr<FormState> formState,
4153 NavigationPolicyDecisionFunction function, void* argument)
4155 m_request = request;
4156 m_formState = formState;
4157 m_frameName = String();
4159 m_navigationFunction = function;
4160 m_newWindowFunction = 0;
4161 m_contentFunction = 0;
4162 m_argument = argument;
4165 void PolicyCheck::set(const ResourceRequest& request, PassRefPtr<FormState> formState,
4166 const String& frameName, NewWindowPolicyDecisionFunction function, void* argument)
4168 m_request = request;
4169 m_formState = formState;
4170 m_frameName = frameName;
4172 m_navigationFunction = 0;
4173 m_newWindowFunction = function;
4174 m_contentFunction = 0;
4175 m_argument = argument;
4178 void PolicyCheck::set(ContentPolicyDecisionFunction function, void* argument)
4180 m_request = ResourceRequest();
4182 m_frameName = String();
4184 m_navigationFunction = 0;
4185 m_newWindowFunction = 0;
4186 m_contentFunction = function;
4187 m_argument = argument;
4190 void PolicyCheck::call(bool shouldContinue)
4192 if (m_navigationFunction)
4193 m_navigationFunction(m_argument, m_request, m_formState.get(), shouldContinue);
4194 if (m_newWindowFunction)
4195 m_newWindowFunction(m_argument, m_request, m_formState.get(), m_frameName, shouldContinue);
4196 ASSERT(!m_contentFunction);
4199 void PolicyCheck::call(PolicyAction action)
4201 ASSERT(!m_navigationFunction);
4202 ASSERT(!m_newWindowFunction);
4203 ASSERT(m_contentFunction);
4204 m_contentFunction(m_argument, action);
4207 void PolicyCheck::clearRequest()
4209 m_request = ResourceRequest();
4211 m_frameName = String();
4214 void FrameLoader::setTitle(const String& title)
4216 documentLoader()->setTitle(title);
4219 KURL FrameLoader::originalRequestURL() const
4221 return activeDocumentLoader()->initialRequest().url();
4224 String FrameLoader::referrer() const
4226 return documentLoader()->request().httpReferrer();
4230 } // namespace WebCore