diff options
Diffstat (limited to 'src/3rdparty/webkit/WebCore/loader/FrameLoader.cpp')
-rw-r--r-- | src/3rdparty/webkit/WebCore/loader/FrameLoader.cpp | 1807 |
1 files changed, 856 insertions, 951 deletions
diff --git a/src/3rdparty/webkit/WebCore/loader/FrameLoader.cpp b/src/3rdparty/webkit/WebCore/loader/FrameLoader.cpp index e36cb56..9a09e49 100644 --- a/src/3rdparty/webkit/WebCore/loader/FrameLoader.cpp +++ b/src/3rdparty/webkit/WebCore/loader/FrameLoader.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) - * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -52,10 +52,10 @@ #include "Frame.h" #include "FrameLoadRequest.h" #include "FrameLoaderClient.h" -#include "FramePrivate.h" #include "FrameTree.h" #include "FrameView.h" #include "HTMLAnchorElement.h" +#include "HTMLAppletElement.h" #include "HTMLFormElement.h" #include "HTMLFrameElement.h" #include "HTMLNames.h" @@ -81,15 +81,17 @@ #include "ResourceRequest.h" #include "ScriptController.h" #include "ScriptSourceCode.h" +#include "ScriptString.h" #include "ScriptValue.h" #include "SecurityOrigin.h" #include "SegmentedString.h" #include "Settings.h" -#include "SystemTime.h" #include "TextResourceDecoder.h" #include "WindowFeatures.h" #include "XMLHttpRequest.h" #include "XMLTokenizer.h" +#include "XSSAuditor.h" +#include <wtf/CurrentTime.h> #include <wtf/StdLibExtras.h> #if ENABLE(OFFLINE_WEB_APPLICATIONS) @@ -114,68 +116,50 @@ using namespace SVGNames; #endif using namespace HTMLNames; -#if USE(LOW_BANDWIDTH_DISPLAY) -const unsigned int cMaxPendingSourceLengthInLowBandwidthDisplay = 128 * 1024; -#endif - -typedef HashSet<String, CaseFoldingHash> LocalSchemesMap; - -struct FormSubmission { - const char* action; - String url; - RefPtr<FormData> data; - String target; - String contentType; - String boundary; - RefPtr<Event> event; - - FormSubmission(const char* a, const String& u, PassRefPtr<FormData> d, const String& t, - const String& ct, const String& b, PassRefPtr<Event> e) - : action(a) - , url(u) - , data(d) - , target(t) - , contentType(ct) - , boundary(b) - , event(e) - { - } -}; - struct ScheduledRedirection { - enum Type { redirection, locationChange, historyNavigation, locationChangeDuringLoad }; - Type type; - double delay; - String url; - String referrer; - int historySteps; - bool lockHistory; - bool wasUserGesture; - bool wasRefresh; - - ScheduledRedirection(double redirectDelay, const String& redirectURL, bool redirectLockHistory, bool userGesture, bool refresh) + enum Type { redirection, locationChange, historyNavigation, formSubmission }; + + const Type type; + const double delay; + const String url; + const String referrer; + const FrameLoadRequest frameRequest; + const RefPtr<Event> event; + const RefPtr<FormState> formState; + const int historySteps; + const bool lockHistory; + const bool lockBackForwardList; + const bool wasUserGesture; + const bool wasRefresh; + const bool wasDuringLoad; + + ScheduledRedirection(double delay, const String& url, bool lockHistory, bool lockBackForwardList, bool wasUserGesture, bool refresh) : type(redirection) - , delay(redirectDelay) - , url(redirectURL) + , delay(delay) + , url(url) , historySteps(0) - , lockHistory(redirectLockHistory) - , wasUserGesture(userGesture) + , lockHistory(lockHistory) + , lockBackForwardList(lockBackForwardList) + , wasUserGesture(wasUserGesture) , wasRefresh(refresh) + , wasDuringLoad(false) { + ASSERT(!url.isEmpty()); } - ScheduledRedirection(Type locationChangeType, - const String& locationChangeURL, const String& locationChangeReferrer, - bool locationChangeLockHistory, bool locationChangeWasUserGesture, bool refresh) - : type(locationChangeType) + ScheduledRedirection(const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool wasUserGesture, bool refresh, bool duringLoad) + : type(locationChange) , delay(0) - , url(locationChangeURL) - , referrer(locationChangeReferrer) + , url(url) + , referrer(referrer) , historySteps(0) - , lockHistory(locationChangeLockHistory) - , wasUserGesture(locationChangeWasUserGesture) + , lockHistory(lockHistory) + , lockBackForwardList(lockBackForwardList) + , wasUserGesture(wasUserGesture) , wasRefresh(refresh) + , wasDuringLoad(duringLoad) { + ASSERT(!url.isEmpty()); } explicit ScheduledRedirection(int historyNavigationSteps) @@ -183,12 +167,38 @@ struct ScheduledRedirection { , delay(0) , historySteps(historyNavigationSteps) , lockHistory(false) + , lockBackForwardList(false) , wasUserGesture(false) , wasRefresh(false) + , wasDuringLoad(false) { } -}; + ScheduledRedirection(const FrameLoadRequest& frameRequest, + bool lockHistory, bool lockBackForwardList, PassRefPtr<Event> event, PassRefPtr<FormState> formState, + bool duringLoad) + : type(formSubmission) + , delay(0) + , frameRequest(frameRequest) + , event(event) + , formState(formState) + , historySteps(0) + , lockHistory(lockHistory) + , lockBackForwardList(lockBackForwardList) + , wasUserGesture(false) + , wasRefresh(false) + , wasDuringLoad(duringLoad) + { + ASSERT(!frameRequest.isEmpty()); + ASSERT(this->formState); + } +}; + +#if ENABLE(XHTMLMP) +static const char defaultAcceptHeader[] = "application/xml,application/vnd.wap.xhtml+xml,application/xhtml+xml;profile='http://www.wapforum.org/xhtml',text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; +#else +static const char defaultAcceptHeader[] = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; +#endif static double storedTimeOfLastCompletedLoad; static FrameLoader::LocalLoadPolicy localLoadPolicy = FrameLoader::AllowLocalLoadsForLocalOnly; @@ -197,10 +207,9 @@ bool isBackForwardLoadType(FrameLoadType type) switch (type) { case FrameLoadTypeStandard: case FrameLoadTypeReload: - case FrameLoadTypeReloadAllowingStaleData: case FrameLoadTypeReloadFromOrigin: case FrameLoadTypeSame: - case FrameLoadTypeRedirectWithLockedHistory: + case FrameLoadTypeRedirectWithLockedBackForwardList: case FrameLoadTypeReplace: return false; case FrameLoadTypeBack: @@ -220,6 +229,11 @@ static int numRequests(Document* document) return document->docLoader()->requestCount(); } +static inline bool canReferToParentFrameEncoding(const Frame* frame, const Frame* parentFrame) +{ + return parentFrame && parentFrame->document()->securityOrigin()->canAccess(frame->document()->securityOrigin()); +} + FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client) : m_frame(frame) , m_client(client) @@ -233,11 +247,11 @@ FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client) , m_quickRedirectComing(false) , m_sentRedirectNotification(false) , m_inStopAllLoaders(false) - , m_navigationDuringLoad(false) , m_isExecutingJavaScriptFormAction(false) , m_isRunningScript(false) , m_didCallImplicitClose(false) , m_wasUnloadEventEmitted(false) + , m_unloadEventBeingDispatched(false) , m_isComplete(false) , m_isLoadingMainResource(false) , m_cancellingWithLoadInProgress(false) @@ -257,11 +271,6 @@ FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client) #ifndef NDEBUG , m_didDispatchDidCommitLoad(false) #endif -#if USE(LOW_BANDWIDTH_DISPLAY) - , m_useLowBandwidthDisplay(true) - , m_finishedParsingDuringLowBandwidthDisplay(false) - , m_needToSwitchOutLowBandwidthDisplay(false) -#endif #if ENABLE(WML) , m_forceReloadWmlDeck(false) #endif @@ -284,7 +293,7 @@ void FrameLoader::init() // this somewhat odd set of steps is needed to give the frame an initial empty document m_isDisplayingInitialEmptyDocument = false; m_creatingInitialEmptyDocument = true; - setPolicyDocumentLoader(m_client->createDocumentLoader(ResourceRequest(String("")), SubstituteData()).get()); + setPolicyDocumentLoader(m_client->createDocumentLoader(ResourceRequest(KURL("")), SubstituteData()).get()); setProvisionalDocumentLoader(m_policyDocumentLoader.get()); setState(FrameStateProvisional); m_provisionalDocumentLoader->setResponse(ResourceResponse(KURL(), "text/html", 0, String(), String())); @@ -314,7 +323,7 @@ Frame* FrameLoader::createWindow(FrameLoader* frameLoaderForFrameLookup, const F Frame* frame = frameLoaderForFrameLookup->frame()->tree()->find(request.frameName()); if (frame && shouldAllowNavigation(frame)) { if (!request.resourceRequest().url().isEmpty()) - frame->loader()->loadFrameRequestWithFormAndValues(request, false, 0, 0, HashMap<String, String>()); + frame->loader()->loadFrameRequest(request, false, false, 0, 0); if (Page* page = frame->page()) page->chrome()->focus(); created = false; @@ -372,13 +381,7 @@ bool FrameLoader::canHandleRequest(const ResourceRequest& request) return m_client->canHandleRequest(request); } -void FrameLoader::changeLocation(const String& url, const String& referrer, bool lockHistory, bool userGesture, bool refresh) -{ - changeLocation(completeURL(url), referrer, lockHistory, userGesture, refresh); -} - - -void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool lockHistory, bool userGesture, bool refresh) +void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool userGesture, bool refresh) { RefPtr<Frame> protect(m_frame); @@ -387,55 +390,41 @@ void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool l if (executeIfJavaScriptURL(request.url(), userGesture)) return; - urlSelected(request, "_self", 0, lockHistory, userGesture); + urlSelected(request, "_self", 0, lockHistory, lockBackForwardList, userGesture); } -void FrameLoader::urlSelected(const FrameLoadRequest& request, Event* event, bool lockHistory) -{ - FrameLoadRequest copy = request; - if (copy.resourceRequest().httpReferrer().isEmpty()) - copy.resourceRequest().setHTTPReferrer(m_outgoingReferrer); - addHTTPOriginIfNeeded(copy.resourceRequest(), outgoingOrigin()); - - loadFrameRequestWithFormAndValues(copy, lockHistory, event, 0, HashMap<String, String>()); -} - -void FrameLoader::urlSelected(const ResourceRequest& request, const String& _target, Event* triggeringEvent, bool lockHistory, bool userGesture) +void FrameLoader::urlSelected(const ResourceRequest& request, const String& passedTarget, PassRefPtr<Event> triggeringEvent, bool lockHistory, bool lockBackForwardList, bool userGesture) { if (executeIfJavaScriptURL(request.url(), userGesture, false)) return; - String target = _target; - if (target.isEmpty() && m_frame->document()) + String target = passedTarget; + if (target.isEmpty()) target = m_frame->document()->baseTarget(); FrameLoadRequest frameRequest(request, target); - urlSelected(frameRequest, triggeringEvent, lockHistory); + if (frameRequest.resourceRequest().httpReferrer().isEmpty()) + frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer); + addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin()); + + loadFrameRequest(frameRequest, lockHistory, lockBackForwardList, triggeringEvent, 0); } bool FrameLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName) { -#if USE(LOW_BANDWIDTH_DISPLAY) - // don't create sub-frame during low bandwidth display - if (frame()->document()->inLowBandwidthDisplay()) { - m_needToSwitchOutLowBandwidthDisplay = true; - return false; - } -#endif - // Support for <frame src="javascript:string"> KURL scriptURL; KURL url; - if (protocolIs(urlString, "javascript")) { - scriptURL = KURL(urlString); + if (protocolIsJavaScript(urlString)) { + scriptURL = completeURL(urlString); // completeURL() encodes the URL. url = blankURL(); } else url = completeURL(urlString); Frame* frame = ownerElement->contentFrame(); if (frame) - frame->loader()->scheduleLocationChange(url.string(), m_outgoingReferrer, true, userGestureHint()); + frame->loader()->scheduleLocationChange(url.string(), m_outgoingReferrer, true, true, userGestureHint()); else frame = loadSubframe(ownerElement, url, frameName, m_outgoingReferrer); @@ -466,8 +455,7 @@ Frame* FrameLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL } bool hideReferrer = shouldHideReferrer(url, referrer); - RefPtr<Frame> frame = m_client->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer, - allowsScrolling, marginWidth, marginHeight); + RefPtr<Frame> frame = m_client->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer, allowsScrolling, marginWidth, marginHeight); if (!frame) { checkCallImplicitClose(); @@ -497,71 +485,69 @@ Frame* FrameLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL return frame.get(); } -void FrameLoader::submitFormAgain() -{ - if (m_isRunningScript) - return; - OwnPtr<FormSubmission> form(m_deferredFormSubmission.release()); - if (form) - submitForm(form->action, form->url, form->data, form->target, - form->contentType, form->boundary, form->event.get()); -} - void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<FormData> formData, - const String& target, const String& contentType, const String& boundary, Event* event) + const String& target, const String& contentType, const String& boundary, + bool lockHistory, bool lockBackForwardList, PassRefPtr<Event> event, PassRefPtr<FormState> formState) { + ASSERT(action); + ASSERT(strcmp(action, "GET") == 0 || strcmp(action, "POST") == 0); ASSERT(formData); + ASSERT(formState); + ASSERT(formState->sourceFrame() == m_frame); if (!m_frame->page()) return; KURL u = completeURL(url.isNull() ? "" : url); - // FIXME: Do we really need to special-case an empty URL? - // Would it be better to just go on with the form submisson and let the I/O fail? if (u.isEmpty()) return; - if (u.protocolIs("javascript")) { + if (protocolIsJavaScript(u)) { m_isExecutingJavaScriptFormAction = true; executeIfJavaScriptURL(u, false, false); m_isExecutingJavaScriptFormAction = false; return; } - if (m_isRunningScript) { - if (m_deferredFormSubmission) - return; - m_deferredFormSubmission.set(new FormSubmission(action, url, formData, target, - contentType, boundary, event)); + FrameLoadRequest frameRequest; + + String targetOrBaseTarget = target.isEmpty() ? m_frame->document()->baseTarget() : target; + Frame* targetFrame = findFrameForNavigation(targetOrBaseTarget); + if (!targetFrame) { + targetFrame = m_frame; + frameRequest.setFrameName(targetOrBaseTarget); + } + if (!targetFrame->page()) return; + + // FIXME: We'd like to remove this altogether and fix the multiple form submission issue another way. + + // We do not want to submit more than one form from the same page, nor do we want to submit a single + // form more than once. This flag prevents these from happening; not sure how other browsers prevent this. + // The flag is reset in each time we start handle a new mouse or key down event, and + // also in setView since this part may get reused for a page from the back/forward cache. + // The form multi-submit logic here is only needed when we are submitting a form that affects this frame. + + // FIXME: Frame targeting is only one of the ways the submission could end up doing something other + // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly + // needed any more now that we reset m_submittedFormURL on each mouse or key down event. + + if (m_frame->tree()->isDescendantOf(targetFrame)) { + if (m_submittedFormURL == u) + return; + m_submittedFormURL = u; } formData->generateFiles(m_frame->page()->chrome()->client()); - FrameLoadRequest frameRequest; - if (!m_outgoingReferrer.isEmpty()) frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer); - frameRequest.setFrameName(target.isEmpty() ? m_frame->document()->baseTarget() : target); - - // Handle mailto: forms - bool isMailtoForm = equalIgnoringCase(u.protocol(), "mailto"); - if (isMailtoForm && strcmp(action, "GET") != 0) { - // Append body= for POST mailto, replace the whole query string for GET one. - String body = formData->flattenToString(); - String query = u.query(); - if (!query.isEmpty()) - query.append('&'); - u.setQuery(query + body); - } - - if (strcmp(action, "GET") == 0) { + if (strcmp(action, "GET") == 0) u.setQuery(formData->flattenToString()); - } else { - if (!isMailtoForm) - frameRequest.resourceRequest().setHTTPBody(formData.get()); + else { frameRequest.resourceRequest().setHTTPMethod("POST"); + frameRequest.resourceRequest().setHTTPBody(formData); // construct some user headers if necessary if (contentType.isNull() || contentType == "application/x-www-form-urlencoded") @@ -573,10 +559,23 @@ void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<F frameRequest.resourceRequest().setURL(u); addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin()); - submitForm(frameRequest, event); + // Navigation of a subframe during loading of the main frame does not create a new back/forward item. + // Strangely, we only implement this rule for form submission; time will tell if we need it for other types of navigation. + // The definition of "during load" is any time before the load event has been handled. + // See https://bugs.webkit.org/show_bug.cgi?id=14957 for the original motivation for this. + if (Page* targetPage = targetFrame->page()) { + Frame* mainFrame = targetPage->mainFrame(); + if (mainFrame != targetFrame) { + Document* document = mainFrame->document(); + if (!mainFrame->loader()->isComplete() || document && document->processingLoadEvent()) + lockBackForwardList = true; + } + } + + targetFrame->loader()->scheduleFormSubmission(frameRequest, lockHistory, lockBackForwardList, event, formState); } -void FrameLoader::stopLoading(bool sendUnload) +void FrameLoader::stopLoading(bool sendUnload, DatabasePolicy databasePolicy) { if (m_frame->document() && m_frame->document()->tokenizer()) m_frame->document()->tokenizer()->stopParsing(); @@ -587,18 +586,19 @@ void FrameLoader::stopLoading(bool sendUnload) Node* currentFocusedNode = m_frame->document()->focusedNode(); if (currentFocusedNode) currentFocusedNode->aboutToUnload(); - m_frame->document()->dispatchWindowEvent(eventNames().unloadEvent, false, false); + m_unloadEventBeingDispatched = true; + if (m_frame->domWindow()) + m_frame->domWindow()->dispatchUnloadEvent(); + m_unloadEventBeingDispatched = false; if (m_frame->document()) - m_frame->document()->updateRendering(); + m_frame->document()->updateStyleIfNeeded(); m_wasUnloadEventEmitted = true; - if (m_frame->eventHandler()->pendingFrameUnloadEventCount()) - m_frame->eventHandler()->clearPendingFrameUnloadEventCount(); - if (m_frame->eventHandler()->pendingFrameBeforeUnloadEventCount()) - m_frame->eventHandler()->clearPendingFrameBeforeUnloadEventCount(); } } + + // Dispatching the unload event could have made m_frame->document() null. if (m_frame->document() && !m_frame->document()->inPageCache()) - m_frame->document()->removeAllEventListenersFromAllNodes(); + m_frame->document()->removeAllEventListeners(); } m_isComplete = true; // to avoid calling completed() in finishedParsing() (David) @@ -617,7 +617,8 @@ void FrameLoader::stopLoading(bool sendUnload) cache()->loader()->cancelRequests(docLoader); #if ENABLE(DATABASE) - doc->stopDatabases(); + if (databasePolicy == DatabasePolicyStop) + doc->stopDatabases(); #endif } @@ -626,14 +627,6 @@ void FrameLoader::stopLoading(bool sendUnload) child->loader()->stopLoading(sendUnload); cancelRedirection(); - -#if USE(LOW_BANDWIDTH_DISPLAY) - if (m_frame->document() && m_frame->document()->inLowBandwidthDisplay()) { - // Since loading is forced to stop, reset the state without really switching. - m_needToSwitchOutLowBandwidthDisplay = false; - switchOutLowBandwidthDisplayIfReady(); - } -#endif } void FrameLoader::stop() @@ -642,16 +635,10 @@ void FrameLoader::stop() // The frame's last ref may be removed and it will be deleted by checkCompleted(). RefPtr<Frame> protector(m_frame); - if (m_frame->document()) { - if (m_frame->document()->tokenizer()) - m_frame->document()->tokenizer()->stopParsing(); - m_frame->document()->finishParsing(); - } else - // WebKit partially uses WebCore when loading non-HTML docs. In these cases doc==nil, but - // WebCore is enough involved that we need to checkCompleted() in order for m_bComplete to - // become true. An example is when a subframe is a pure text doc, and that subframe is the - // last one to complete. - checkCompleted(); + if (m_frame->document()->tokenizer()) + m_frame->document()->tokenizer()->stopParsing(); + m_frame->document()->finishParsing(); + if (m_iconLoader) m_iconLoader->stopLoading(); } @@ -680,11 +667,11 @@ KURL FrameLoader::iconURL() return KURL(); // If we have an iconURL from a Link element, return that - if (m_frame->document() && !m_frame->document()->iconURL().isEmpty()) + if (!m_frame->document()->iconURL().isEmpty()) return KURL(m_frame->document()->iconURL()); // Don't return a favicon iconURL unless we're http or https - if (!m_URL.protocolIs("http") && !m_URL.protocolIs("https")) + if (!m_URL.protocolInHTTPFamily()) return KURL(); KURL url; @@ -698,10 +685,11 @@ KURL FrameLoader::iconURL() bool FrameLoader::didOpenURL(const KURL& url) { - if (m_scheduledRedirection && m_scheduledRedirection->type == ScheduledRedirection::locationChangeDuringLoad) + if (m_scheduledRedirection && m_scheduledRedirection->wasDuringLoad) { // A redirect was scheduled before the document was created. // This can happen when one frame changes another frame's location. return false; + } cancelRedirection(); m_frame->editor()->clearLastEditCommand(); @@ -711,11 +699,15 @@ bool FrameLoader::didOpenURL(const KURL& url) m_isLoadingMainResource = true; m_didCallImplicitClose = false; - m_frame->setJSStatusBarText(String()); - m_frame->setJSDefaultStatusBarText(String()); - + // If we are still in the process of initializing an empty document then + // its frame is not in a consistent state for rendering, so avoid setJSStatusBarText + // since it may cause clients to attempt to render the frame. + if (!m_creatingInitialEmptyDocument) { + m_frame->setJSStatusBarText(String()); + m_frame->setJSDefaultStatusBarText(String()); + } m_URL = url; - if ((m_URL.protocolIs("http") || m_URL.protocolIs("https")) && !m_URL.host().isEmpty() && m_URL.path().isEmpty()) + if (m_URL.protocolInHTTPFamily() && !m_URL.host().isEmpty() && m_URL.path().isEmpty()) m_URL.setPath("/"); m_workingURL = m_URL; @@ -743,9 +735,12 @@ void FrameLoader::didExplicitOpen() bool FrameLoader::executeIfJavaScriptURL(const KURL& url, bool userGesture, bool replaceDocument) { - if (!url.protocolIs("javascript")) + if (!protocolIsJavaScript(url)) return false; + if (m_frame->page() && !m_frame->page()->javaScriptURLsAreAllowed()) + return true; + String script = decodeURLEscapeSequences(url.string().substring(strlen("javascript:"))); ScriptValue result = executeScript(script, userGesture); @@ -754,13 +749,13 @@ bool FrameLoader::executeIfJavaScriptURL(const KURL& url, bool userGesture, bool return true; SecurityOrigin* currentSecurityOrigin = 0; - if (m_frame->document()) - currentSecurityOrigin = m_frame->document()->securityOrigin(); + currentSecurityOrigin = m_frame->document()->securityOrigin(); // FIXME: We should always replace the document, but doing so // synchronously can cause crashes: // http://bugs.webkit.org/show_bug.cgi?id=16782 if (replaceDocument) { + stopAllLoaders(); begin(m_URL, true, currentSecurityOrigin); write(scriptResult); end(); @@ -786,8 +781,7 @@ ScriptValue FrameLoader::executeScript(const ScriptSourceCode& sourceCode) if (!wasRunningScript) { m_isRunningScript = false; - submitFormAgain(); - Document::updateDocumentsRendering(); + Document::updateStyleForAllDocuments(); } return result; @@ -812,7 +806,7 @@ void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects) return; m_needsClear = false; - if (m_frame->document() && !m_frame->document()->inPageCache()) { + if (!m_frame->document()->inPageCache()) { m_frame->document()->cancelParsing(); m_frame->document()->stopActiveDOMObjects(); if (m_frame->document()->attached()) { @@ -866,10 +860,12 @@ void FrameLoader::receivedFirstData() dispatchDidCommitLoad(); dispatchWindowObjectAvailable(); - String ptitle = m_documentLoader->title(); - // If we have a title let the WebView know about it. - if (!ptitle.isNull()) - m_client->dispatchDidReceiveTitle(ptitle); + if (m_documentLoader) { + String ptitle = m_documentLoader->title(); + // If we have a title let the WebView know about it. + if (!ptitle.isNull()) + m_client->dispatchDidReceiveTitle(ptitle); + } m_workingURL = KURL(); @@ -909,12 +905,18 @@ void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin) // might destroy the document that owns it. RefPtr<SecurityOrigin> forcedSecurityOrigin = origin; - bool resetScripting = !(m_isDisplayingInitialEmptyDocument && m_frame->document() && m_frame->document()->securityOrigin()->isSecureTransitionTo(url)); + RefPtr<Document> document; + + // Create a new document before clearing the frame, because it may need to inherit an aliased security context. + if (!m_isDisplayingInitialEmptyDocument && m_client->shouldUsePluginDocument(m_responseMIMEType)) + document = PluginDocument::create(m_frame); + else + document = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode()); + + bool resetScripting = !(m_isDisplayingInitialEmptyDocument && m_frame->document()->securityOrigin()->isSecureTransitionTo(url)); clear(resetScripting, resetScripting); if (resetScripting) m_frame->script()->updatePlatformScriptObjects(); - if (dispatch) - dispatchWindowObjectAvailable(); m_needsClear = true; m_isComplete = false; @@ -929,14 +931,11 @@ void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin) m_outgoingReferrer = ref.string(); m_URL = url; - RefPtr<Document> document; - - if (!m_isDisplayingInitialEmptyDocument && m_client->shouldUsePluginDocument(m_responseMIMEType)) - document = PluginDocument::create(m_frame); - else - document = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode()); m_frame->setDocument(document); + if (dispatch) + dispatchWindowObjectAvailable(); + document->setURL(m_URL); if (m_decoder) document->setDecoder(m_decoder.get()); @@ -946,7 +945,7 @@ void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin) m_frame->domWindow()->setURL(document->url()); m_frame->domWindow()->setSecurityOrigin(document->securityOrigin()); - updatePolicyBaseURL(); + updateFirstPartyForCookies(); Settings* settings = document->settings(); document->docLoader()->setAutoLoadImages(settings && settings->loadsImagesAutomatically()); @@ -966,21 +965,9 @@ void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin) restoreDocumentState(); document->implicitOpen(); - + if (m_frame->view()) m_frame->view()->setContentsSize(IntSize()); - -#if USE(LOW_BANDWIDTH_DISPLAY) - // Low bandwidth display is a first pass display without external resources - // used to give an instant visual feedback. We currently only enable it for - // HTML documents in the top frame. - if (document->isHTMLDocument() && !m_frame->tree()->parent() && m_useLowBandwidthDisplay) { - m_pendingSourceInLowBandwidthDisplay = String(); - m_finishedParsingDuringLowBandwidthDisplay = false; - m_needToSwitchOutLowBandwidthDisplay = false; - document->setLowBandwidthDisplay(true); - } -#endif } void FrameLoader::write(const char* str, int len, bool flush) @@ -999,12 +986,28 @@ void FrameLoader::write(const char* str, int len, bool flush) } if (!m_decoder) { - Settings* settings = m_frame->settings(); - m_decoder = TextResourceDecoder::create(m_responseMIMEType, settings ? settings->defaultTextEncodingName() : String()); - if (m_encoding.isEmpty()) { + if (Settings* settings = m_frame->settings()) { + m_decoder = TextResourceDecoder::create(m_responseMIMEType, + settings->defaultTextEncodingName(), + settings->usesEncodingDetector()); Frame* parentFrame = m_frame->tree()->parent(); - if (parentFrame && parentFrame->document()->securityOrigin()->canAccess(m_frame->document()->securityOrigin())) - m_decoder->setEncoding(parentFrame->document()->inputEncoding(), TextResourceDecoder::DefaultEncoding); + // Set the hint encoding to the parent frame encoding only if + // the parent and the current frames share the security origin. + // We impose this condition because somebody can make a child frame + // containing a carefully crafted html/javascript in one encoding + // that can be mistaken for hintEncoding (or related encoding) by + // an auto detector. When interpreted in the latter, it could be + // an attack vector. + // FIXME: This might be too cautious for non-7bit-encodings and + // we may consider relaxing this later after testing. + if (canReferToParentFrameEncoding(m_frame, parentFrame)) + m_decoder->setHintEncoding(parentFrame->document()->decoder()); + } else + m_decoder = TextResourceDecoder::create(m_responseMIMEType, String()); + Frame* parentFrame = m_frame->tree()->parent(); + if (m_encoding.isEmpty()) { + if (canReferToParentFrameEncoding(m_frame, parentFrame)) + m_decoder->setEncoding(parentFrame->document()->inputEncoding(), TextResourceDecoder::EncodingFromParentFrame); } else { m_decoder->setEncoding(m_encoding, m_encodingWasChosenByUser ? TextResourceDecoder::UserChosenEncoding : TextResourceDecoder::EncodingFromHTTPHeader); @@ -1018,11 +1021,6 @@ void FrameLoader::write(const char* str, int len, bool flush) if (decoded.isEmpty()) return; -#if USE(LOW_BANDWIDTH_DISPLAY) - if (m_frame->document()->inLowBandwidthDisplay()) - m_pendingSourceInLowBandwidthDisplay.append(decoded); -#endif - if (!m_receivedData) { m_receivedData = true; if (m_decoder->encoding().usesVisualOrdering()) @@ -1058,7 +1056,7 @@ void FrameLoader::end() void FrameLoader::endIfNotLoadingMainResource() { - if (m_isLoadingMainResource || !m_frame->page()) + if (m_isLoadingMainResource || !m_frame->page() || !m_frame->document()) return; // http://bugs.webkit.org/show_bug.cgi?id=10854 @@ -1067,21 +1065,8 @@ void FrameLoader::endIfNotLoadingMainResource() RefPtr<Frame> protector(m_frame); // make sure nothing's left in there - if (m_frame->document()) { - write(0, 0, true); - m_frame->document()->finishParsing(); -#if USE(LOW_BANDWIDTH_DISPLAY) - if (m_frame->document()->inLowBandwidthDisplay()) { - m_finishedParsingDuringLowBandwidthDisplay = true; - switchOutLowBandwidthDisplayIfReady(); - } -#endif - } else - // WebKit partially uses WebCore when loading non-HTML docs. In these cases doc==nil, but - // WebCore is enough involved that we need to checkCompleted() in order for m_bComplete to - // become true. An example is when a subframe is a pure text doc, and that subframe is the - // last one to complete. - checkCompleted(); + write(0, 0, true); + m_frame->document()->finishParsing(); } void FrameLoader::iconLoadDecisionAvailable() @@ -1165,23 +1150,6 @@ bool FrameLoader::allowSubstituteDataAccessToLocal() return localLoadPolicy != FrameLoader::AllowLocalLoadsForLocalOnly; } -static LocalSchemesMap& localSchemes() -{ - DEFINE_STATIC_LOCAL(LocalSchemesMap, localSchemes, ()); - - if (localSchemes.isEmpty()) { - localSchemes.add("file"); -#if PLATFORM(MAC) - localSchemes.add("applewebdata"); -#endif -#if PLATFORM(QT) - localSchemes.add("qrc"); -#endif - } - - return localSchemes; -} - void FrameLoader::commitIconURLToIconDatabase(const KURL& icon) { ASSERT(iconDatabase()); @@ -1193,14 +1161,11 @@ void FrameLoader::commitIconURLToIconDatabase(const KURL& icon) void FrameLoader::restoreDocumentState() { Document* doc = m_frame->document(); - if (!doc) - return; HistoryItem* itemToRestore = 0; switch (loadType()) { case FrameLoadTypeReload: - case FrameLoadTypeReloadAllowingStaleData: case FrameLoadTypeReloadFromOrigin: case FrameLoadTypeSame: case FrameLoadTypeReplace: @@ -1208,14 +1173,15 @@ void FrameLoader::restoreDocumentState() case FrameLoadTypeBack: case FrameLoadTypeForward: case FrameLoadTypeIndexedBackForward: - case FrameLoadTypeRedirectWithLockedHistory: + case FrameLoadTypeRedirectWithLockedBackForwardList: case FrameLoadTypeStandard: itemToRestore = m_currentHistoryItem.get(); } if (!itemToRestore) return; - + + LOG(Loading, "WebCoreLoading %s: restoring form state from %p", m_frame->tree()->name().string().utf8().data(), itemToRestore); doc->setStateForNewFormElements(itemToRestore->documentState()); } @@ -1225,7 +1191,7 @@ void FrameLoader::gotoAnchor() // OTOH If CSS target was set previously, we want to set it to 0, recalc // and possibly repaint because :target pseudo class may have been // set (see bug 11321). - if (!m_URL.hasRef() && !(m_frame->document() && m_frame->document()->getCSSTarget())) + if (!m_URL.hasRef() && !m_frame->document()->cssTarget()) return; String ref = m_URL.ref(); @@ -1263,12 +1229,14 @@ void FrameLoader::finishedParsing() void FrameLoader::loadDone() { - if (m_frame->document()) - checkCompleted(); + checkCompleted(); } void FrameLoader::checkCompleted() { + if (m_frame->view()) + m_frame->view()->checkStopDelayingDeferredRepaints(); + // Any frame that hasn't completed yet? for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) if (!child->loader()->m_isComplete) @@ -1279,19 +1247,12 @@ void FrameLoader::checkCompleted() return; // Are we still parsing? - if (m_frame->document() && m_frame->document()->parsing()) + if (m_frame->document()->parsing()) return; // Still waiting for images/scripts? - if (m_frame->document()) - if (numRequests(m_frame->document())) - return; - -#if USE(LOW_BANDWIDTH_DISPLAY) - // as switch will be called, don't complete yet - if (m_frame->document() && m_frame->document()->inLowBandwidthDisplay() && m_needToSwitchOutLowBandwidthDisplay) + if (numRequests(m_frame->document())) return; -#endif // OK, completed. m_isComplete = true; @@ -1335,7 +1296,7 @@ void FrameLoader::scheduleCheckLoadComplete() void FrameLoader::checkCallImplicitClose() { - if (m_didCallImplicitClose || !m_frame->document() || m_frame->document()->parsing()) + if (m_didCallImplicitClose || m_frame->document()->parsing()) return; for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) @@ -1344,8 +1305,7 @@ void FrameLoader::checkCallImplicitClose() m_didCallImplicitClose = true; m_wasUnloadEventEmitted = false; - if (m_frame->document()) - m_frame->document()->implicitClose(); + m_frame->document()->implicitClose(); } KURL FrameLoader::baseURL() const @@ -1354,12 +1314,6 @@ KURL FrameLoader::baseURL() const return m_frame->document()->baseURL(); } -String FrameLoader::baseTarget() const -{ - ASSERT(m_frame->document()); - return m_frame->document()->baseTarget(); -} - KURL FrameLoader::completeURL(const String& url) { ASSERT(m_frame->document()); @@ -1374,21 +1328,27 @@ void FrameLoader::scheduleHTTPRedirection(double delay, const String& url) if (!m_frame->page()) return; + if (url.isEmpty()) + return; + // We want a new history item if the refresh timeout is > 1 second. if (!m_scheduledRedirection || delay <= m_scheduledRedirection->delay) - scheduleRedirection(new ScheduledRedirection(delay, url, delay <= 1, false, false)); + scheduleRedirection(new ScheduledRedirection(delay, url, true, delay <= 1, false, false)); } -void FrameLoader::scheduleLocationChange(const String& url, const String& referrer, bool lockHistory, bool wasUserGesture) +void FrameLoader::scheduleLocationChange(const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool wasUserGesture) { if (!m_frame->page()) return; + if (url.isEmpty()) + return; + // If the URL we're going to navigate to is the same as the current one, except for the // fragment part, we don't need to schedule the location change. KURL parsedURL(url); if (parsedURL.hasRef() && equalIgnoringRef(m_URL, parsedURL)) { - changeLocation(url, referrer, lockHistory, wasUserGesture); + changeLocation(completeURL(url), referrer, lockHistory, lockBackForwardList, wasUserGesture); return; } @@ -1396,18 +1356,23 @@ void FrameLoader::scheduleLocationChange(const String& url, const String& referr // This may happen when a frame changes the location of another frame. bool duringLoad = !m_committedFirstRealDocumentLoad; - // If a redirect was scheduled during a load, then stop the current load. - // Otherwise when the current load transitions from a provisional to a - // committed state, pending redirects may be cancelled. - if (duringLoad) { - if (m_provisionalDocumentLoader) - m_provisionalDocumentLoader->stopLoading(); - stopLoading(true); - } + scheduleRedirection(new ScheduledRedirection(url, referrer, lockHistory, lockBackForwardList, wasUserGesture, false, duringLoad)); +} + +void FrameLoader::scheduleFormSubmission(const FrameLoadRequest& frameRequest, + bool lockHistory, bool lockBackForwardList, PassRefPtr<Event> event, PassRefPtr<FormState> formState) +{ + ASSERT(m_frame->page()); + ASSERT(!frameRequest.isEmpty()); + + // FIXME: Do we need special handling for form submissions where the URL is the same + // as the current one except for the fragment part? See scheduleLocationChange above. - ScheduledRedirection::Type type = duringLoad - ? ScheduledRedirection::locationChangeDuringLoad : ScheduledRedirection::locationChange; - scheduleRedirection(new ScheduledRedirection(type, url, referrer, lockHistory, wasUserGesture, false)); + // Handle a location change of a page with no document as a special case. + // This may happen when a frame changes the location of another frame. + bool duringLoad = !m_committedFirstRealDocumentLoad; + + scheduleRedirection(new ScheduledRedirection(frameRequest, lockHistory, lockBackForwardList, event, formState, duringLoad)); } void FrameLoader::scheduleRefresh(bool wasUserGesture) @@ -1415,19 +1380,10 @@ void FrameLoader::scheduleRefresh(bool wasUserGesture) if (!m_frame->page()) return; - // Handle a location change of a page with no document as a special case. - // This may happen when a frame requests a refresh of another frame. - bool duringLoad = !m_frame->document(); - - // If a refresh was scheduled during a load, then stop the current load. - // Otherwise when the current load transitions from a provisional to a - // committed state, pending redirects may be cancelled. - if (duringLoad) - stopLoading(true); + if (m_URL.isEmpty()) + return; - ScheduledRedirection::Type type = duringLoad - ? ScheduledRedirection::locationChangeDuringLoad : ScheduledRedirection::locationChange; - scheduleRedirection(new ScheduledRedirection(type, m_URL.string(), m_outgoingReferrer, true, wasUserGesture, true)); + scheduleRedirection(new ScheduledRedirection(m_URL.string(), m_outgoingReferrer, true, true, wasUserGesture, true, false)); } bool FrameLoader::isLocationChange(const ScheduledRedirection& redirection) @@ -1437,7 +1393,7 @@ bool FrameLoader::isLocationChange(const ScheduledRedirection& redirection) return false; case ScheduledRedirection::historyNavigation: case ScheduledRedirection::locationChange: - case ScheduledRedirection::locationChangeDuringLoad: + case ScheduledRedirection::formSubmission: return true; } ASSERT_NOT_REACHED(); @@ -1455,18 +1411,6 @@ void FrameLoader::scheduleHistoryNavigation(int steps) return; } - // If the steps to navigate is not zero (which needs to force a reload), and if we think the navigation is going to be a fragment load - // (when the URL we're going to navigate to is the same as the current one, except for the fragment part - but not exactly the same because that's a reload), - // then we don't need to schedule the navigation. - if (steps != 0) { - KURL destination = historyURL(steps); - // FIXME: This doesn't seem like a reliable way to tell whether or not the load will be a fragment load. - if (equalIgnoringRef(m_URL, destination) && m_URL != destination) { - goBackOrForward(steps); - return; - } - } - scheduleRedirection(new ScheduledRedirection(steps)); } @@ -1509,62 +1453,55 @@ void FrameLoader::redirectionTimerFired(Timer<FrameLoader>*) switch (redirection->type) { case ScheduledRedirection::redirection: case ScheduledRedirection::locationChange: - case ScheduledRedirection::locationChangeDuringLoad: - changeLocation(redirection->url, redirection->referrer, - redirection->lockHistory, redirection->wasUserGesture, redirection->wasRefresh); + changeLocation(KURL(redirection->url), redirection->referrer, + redirection->lockHistory, redirection->lockBackForwardList, redirection->wasUserGesture, redirection->wasRefresh); return; case ScheduledRedirection::historyNavigation: if (redirection->historySteps == 0) { // Special case for go(0) from a frame -> reload only the frame - urlSelected(m_URL, "", 0, redirection->lockHistory, redirection->wasUserGesture); + urlSelected(m_URL, "", 0, redirection->lockHistory, redirection->lockBackForwardList, redirection->wasUserGesture); return; } // go(i!=0) from a frame navigates into the history of the frame only, // in both IE and NS (but not in Mozilla). We can't easily do that. goBackOrForward(redirection->historySteps); return; + case ScheduledRedirection::formSubmission: + // The submitForm function will find a target frame before using the redirection timer. + // Now that the timer has fired, we need to repeat the security check which normally is done when + // selecting a target, in case conditions have changed. Other code paths avoid this by targeting + // without leaving a time window. If we fail the check just silently drop the form submission. + if (!redirection->formState->sourceFrame()->loader()->shouldAllowNavigation(m_frame)) + return; + loadFrameRequest(redirection->frameRequest, redirection->lockHistory, redirection->lockBackForwardList, + redirection->event, redirection->formState); + return; } ASSERT_NOT_REACHED(); } -/* - In the case of saving state about a page with frames, we store a tree of items that mirrors the frame tree. - The item that was the target of the user's navigation is designated as the "targetItem". - When this method is called with doClip=YES we're able to create the whole tree except for the target's children, - which will be loaded in the future. That part of the tree will be filled out as the child loads are committed. -*/ void FrameLoader::loadURLIntoChildFrame(const KURL& url, const String& referer, Frame* childFrame) { ASSERT(childFrame); + HistoryItem* parentItem = currentHistoryItem(); FrameLoadType loadType = this->loadType(); - FrameLoadType childLoadType = FrameLoadTypeRedirectWithLockedHistory; + FrameLoadType childLoadType = FrameLoadTypeRedirectWithLockedBackForwardList; KURL workingURL = url; - // If we're moving in the backforward list, we might want to replace the content + // If we're moving in the back/forward list, we might want to replace the content // of this child frame with whatever was there at that point. - // Reload will maintain the frame contents, LoadSame will not. - if (parentItem && parentItem->children().size() && - (isBackForwardLoadType(loadType) || loadType == FrameLoadTypeReloadAllowingStaleData)) - { - HistoryItem* childItem = parentItem->childItemWithName(childFrame->tree()->name()); + if (parentItem && parentItem->children().size() && isBackForwardLoadType(loadType)) { + HistoryItem* childItem = parentItem->childItemWithTarget(childFrame->tree()->name()); if (childItem) { // Use the original URL to ensure we get all the side-effects, such as // onLoad handlers, of any redirects that happened. An example of where // this is needed is Radar 3213556. workingURL = KURL(childItem->originalURLString()); - // These behaviors implied by these loadTypes should apply to the child frames childLoadType = loadType; - - if (isBackForwardLoadType(loadType)) { - // For back/forward, remember this item so we can traverse any child items as child frames load - childFrame->loader()->setProvisionalHistoryItem(childItem); - } else { - // For reload, just reinstall the current item, since a new child frame was created but we won't be creating a new BF item - childFrame->loader()->setCurrentHistoryItem(childItem); - } + childFrame->loader()->m_provisionalHistoryItem = childItem; } } @@ -1573,7 +1510,7 @@ void FrameLoader::loadURLIntoChildFrame(const KURL& url, const String& referer, if (subframeArchive) childFrame->loader()->loadArchive(subframeArchive.release()); else - childFrame->loader()->loadURL(workingURL, referer, String(), childLoadType, 0, 0); + childFrame->loader()->loadURL(workingURL, referer, String(), false, childLoadType, 0, 0); } void FrameLoader::loadArchive(PassRefPtr<Archive> prpArchive) @@ -1651,12 +1588,10 @@ bool FrameLoader::gotoAnchor(const String& name) // We need to update the layout before scrolling, otherwise we could // really mess things up if an anchor scroll comes at a bad moment. - if (m_frame->document()) { - m_frame->document()->updateRendering(); - // Only do a layout if changes have occurred that make it necessary. - if (m_frame->view() && m_frame->contentRenderer() && m_frame->contentRenderer()->needsLayout()) - m_frame->view()->layout(); - } + m_frame->document()->updateStyleIfNeeded(); + // Only do a layout if changes have occurred that make it necessary. + if (m_frame->view() && m_frame->contentRenderer() && m_frame->contentRenderer()->needsLayout()) + m_frame->view()->layout(); // Scroll nested layers and frames to reveal the anchor. // Align to the top and to the closest side (this matches other browsers). @@ -1668,8 +1603,11 @@ bool FrameLoader::gotoAnchor(const String& name) renderer = anchorNode->renderer(); rect = anchorNode->getRect(); } - if (renderer) - renderer->enclosingLayer()->scrollRectToVisible(rect, true, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignTopAlways); + if (renderer) { + renderer->enclosingLayer()->scrollRectToVisible(rect, true, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignTopAlways); + if (m_frame->view()) + m_frame->view()->setLockedToAnchor(true); + } return true; } @@ -1679,14 +1617,11 @@ bool FrameLoader::requestObject(RenderPart* renderer, const String& url, const A { if (url.isEmpty() && mimeType.isEmpty()) return false; - -#if USE(LOW_BANDWIDTH_DISPLAY) - // don't care object during low bandwidth display - if (frame()->document()->inLowBandwidthDisplay()) { - m_needToSwitchOutLowBandwidthDisplay = true; + + if (!m_frame->script()->xssAuditor()->canLoadObject(url)) { + // It is unsafe to honor the request for this object. return false; } -#endif KURL completedURL; if (!url.isEmpty()) @@ -1731,24 +1666,39 @@ bool FrameLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin; } +static HTMLPlugInElement* toPlugInElement(Node* node) +{ + if (!node) + return 0; + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + ASSERT(node->hasTagName(objectTag) || node->hasTagName(embedTag) + || node->hasTagName(videoTag) || node->hasTagName(audioTag) + || node->hasTagName(appletTag)); +#else + ASSERT(node->hasTagName(objectTag) || node->hasTagName(embedTag) + || node->hasTagName(appletTag)); +#endif + + return static_cast<HTMLPlugInElement*>(node); +} + bool FrameLoader::loadPlugin(RenderPart* renderer, const KURL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback) { Widget* widget = 0; if (renderer && !useFallback) { - Element* pluginElement = 0; - if (renderer->node() && renderer->node()->isElementNode()) - pluginElement = static_cast<Element*>(renderer->node()); + HTMLPlugInElement* element = toPlugInElement(renderer->node()); if (!canLoad(url, String(), frame()->document())) { FrameLoader::reportLocalLoadFailed(m_frame, url.string()); return false; } - widget = m_client->createPlugin(IntSize(renderer->contentWidth(), renderer->contentHeight()), - pluginElement, url, paramNames, paramValues, mimeType, - m_frame->document()->isPluginDocument()); + widget = m_client->createPlugin(IntSize(renderer->contentWidth(), renderer->contentHeight()), + element, url, paramNames, paramValues, mimeType, + m_frame->document()->isPluginDocument() && !m_containsPlugIns); if (widget) { renderer->setWidget(widget); m_containsPlugIns = true; @@ -1758,22 +1708,6 @@ bool FrameLoader::loadPlugin(RenderPart* renderer, const KURL& url, const String return widget != 0; } -void FrameLoader::clearRecordedFormValues() -{ - m_formAboutToBeSubmitted = 0; - m_formValuesAboutToBeSubmitted.clear(); -} - -void FrameLoader::setFormAboutToBeSubmitted(PassRefPtr<HTMLFormElement> element) -{ - m_formAboutToBeSubmitted = element; -} - -void FrameLoader::recordFormValue(const String& name, const String& value) -{ - m_formValuesAboutToBeSubmitted.set(name, value); -} - void FrameLoader::parentCompleted() { if (m_scheduledRedirection && !m_redirectionTimer.isActive()) @@ -1787,10 +1721,7 @@ String FrameLoader::outgoingReferrer() const String FrameLoader::outgoingOrigin() const { - if (m_frame->document()) - return m_frame->document()->securityOrigin()->toString(); - - return SecurityOrigin::createEmpty()->toString(); + return m_frame->document()->securityOrigin()->toString(); } Frame* FrameLoader::opener() @@ -1831,17 +1762,7 @@ void FrameLoader::handleFallbackContent() } void FrameLoader::provisionalLoadStarted() -{ - Page* page = m_frame->page(); - - // this is used to update the current history item - // in the event of a navigation aytime during loading - m_navigationDuringLoad = false; - if (page) { - Document *document = page->mainFrame()->document(); - m_navigationDuringLoad = !page->mainFrame()->loader()->isComplete() || (document && document->processingLoadEvent()); - } - +{ m_firstLayoutDone = false; cancelRedirection(true); m_client->provisionalLoadStarted(); @@ -1849,20 +1770,10 @@ void FrameLoader::provisionalLoadStarted() bool FrameLoader::userGestureHint() { - Frame* rootFrame = m_frame; - while (rootFrame->tree()->parent()) - rootFrame = rootFrame->tree()->parent(); - - if (rootFrame->script()->isEnabled()) - return rootFrame->script()->processingUserGesture(); - - return true; // If JavaScript is disabled, a user gesture must have initiated the navigation -} - -void FrameLoader::didNotOpenURL(const KURL& url) -{ - if (m_submittedFormURL == url) - m_submittedFormURL = KURL(); + Frame* frame = m_frame->tree()->top(); + if (!frame->script()->isEnabled()) + return true; // If JavaScript is disabled, a user gesture must have initiated the navigation. + return frame->script()->processingUserGesture(); // FIXME: Use pageIsProcessingUserGesture. } void FrameLoader::resetMultipleFormSubmissionProtection() @@ -1886,70 +1797,212 @@ void FrameLoader::addData(const char* bytes, int length) write(bytes, length); } -bool FrameLoader::canCachePage() -{ - // Cache the page, if possible. - // Don't write to the cache if in the middle of a redirect, since we will want to - // store the final page we end up on. - // No point writing to the cache on a reload or loadSame, since we will just write - // over it again when we leave that page. - // FIXME: <rdar://problem/4886592> - We should work out the complexities of caching pages with frames as they - // are the most interesting pages on the web, and often those that would benefit the most from caching! - FrameLoadType loadType = this->loadType(); - +bool FrameLoader::canCachePageContainingThisFrame() +{ return m_documentLoader && m_documentLoader->mainDocumentError().isNull() && !m_frame->tree()->childCount() - && !m_frame->tree()->parent() - // FIXME: If we ever change this so that pages with plug-ins will be cached, - // we need to make sure that we don't cache pages that have outstanding NPObjects + // FIXME: If we ever change this so that frames with plug-ins will be cached, + // we need to make sure that we don't cache frames that have outstanding NPObjects // (objects created by the plug-in). Since there is no way to pause/resume a Netscape plug-in, // they would need to be destroyed and then recreated, and there is no way that we can recreate // the right NPObjects. See <rdar://problem/5197041> for more information. && !m_containsPlugIns && !m_URL.protocolIs("https") - && m_frame->document() - && !m_frame->document()->hasWindowEventListener(eventNames().unloadEvent) + && (!m_frame->domWindow() || !m_frame->domWindow()->hasEventListener(eventNames().unloadEvent)) #if ENABLE(DATABASE) && !m_frame->document()->hasOpenDatabases() #endif && !m_frame->document()->usingGeolocation() - && m_frame->page() - && m_frame->page()->backForwardList()->enabled() - && m_frame->page()->backForwardList()->capacity() > 0 - && m_frame->page()->settings()->usesPageCache() && m_currentHistoryItem - && !isQuickRedirectComing() - && loadType != FrameLoadTypeReload - && loadType != FrameLoadTypeReloadAllowingStaleData - && loadType != FrameLoadTypeReloadFromOrigin - && loadType != FrameLoadTypeSame + && !m_quickRedirectComing && !m_documentLoader->isLoadingInAPISense() && !m_documentLoader->isStopping() && m_frame->document()->canSuspendActiveDOMObjects() #if ENABLE(OFFLINE_WEB_APPLICATIONS) - // FIXME: We should investigating caching pages that have an associated + // FIXME: We should investigating caching frames that have an associated // application cache. <rdar://problem/5917899> tracks that work. && !m_documentLoader->applicationCache() && !m_documentLoader->candidateApplicationCacheGroup() #endif + && m_client->canCachePage() + ; +} + +bool FrameLoader::canCachePage() +{ +#ifndef NDEBUG + logCanCachePageDecision(); +#endif + + // Cache the page, if possible. + // Don't write to the cache if in the middle of a redirect, since we will want to + // store the final page we end up on. + // No point writing to the cache on a reload or loadSame, since we will just write + // over it again when we leave that page. + // FIXME: <rdar://problem/4886592> - We should work out the complexities of caching pages with frames as they + // are the most interesting pages on the web, and often those that would benefit the most from caching! + FrameLoadType loadType = this->loadType(); + + return !m_frame->tree()->parent() + && canCachePageContainingThisFrame() + && m_frame->page() + && m_frame->page()->backForwardList()->enabled() + && m_frame->page()->backForwardList()->capacity() > 0 + && m_frame->page()->settings()->usesPageCache() + && loadType != FrameLoadTypeReload + && loadType != FrameLoadTypeReloadFromOrigin + && loadType != FrameLoadTypeSame ; } -void FrameLoader::updatePolicyBaseURL() +#ifndef NDEBUG +static String& pageCacheLogPrefix(int indentLevel) { - if (m_frame->tree()->parent() && m_frame->tree()->parent()->document()) - setPolicyBaseURL(m_frame->tree()->parent()->document()->policyBaseURL()); + static int previousIndent = -1; + DEFINE_STATIC_LOCAL(String, prefix, ()); + + if (indentLevel != previousIndent) { + previousIndent = indentLevel; + prefix.truncate(0); + for (int i = 0; i < previousIndent; ++i) + prefix += " "; + } + + return prefix; +} + +static void pageCacheLog(const String& prefix, const String& message) +{ + LOG(PageCache, "%s%s", prefix.utf8().data(), message.utf8().data()); +} + +#define PCLOG(...) pageCacheLog(pageCacheLogPrefix(indentLevel), String::format(__VA_ARGS__)) + +void FrameLoader::logCanCachePageDecision() +{ + // Only bother logging for main frames that have actually loaded and have content. + if (m_creatingInitialEmptyDocument) + return; + KURL currentURL = m_documentLoader ? m_documentLoader->url() : KURL(); + if (currentURL.isEmpty()) + return; + + int indentLevel = 0; + PCLOG("--------\n Determining if page can be cached:"); + + bool cannotCache = !logCanCacheFrameDecision(1); + + FrameLoadType loadType = this->loadType(); + do { + if (m_frame->tree()->parent()) + { PCLOG(" -Frame has a parent frame"); cannotCache = true; } + if (!m_frame->page()) { + PCLOG(" -There is no Page object"); + cannotCache = true; + break; + } + if (!m_frame->page()->backForwardList()->enabled()) + { PCLOG(" -The back/forward list is disabled"); cannotCache = true; } + if (!(m_frame->page()->backForwardList()->capacity() > 0)) + { PCLOG(" -The back/forward list has a 0 capacity"); cannotCache = true; } + if (!m_frame->page()->settings()->usesPageCache()) + { PCLOG(" -Page settings says b/f cache disabled"); cannotCache = true; } + if (loadType == FrameLoadTypeReload) + { PCLOG(" -Load type is: Reload"); cannotCache = true; } + if (loadType == FrameLoadTypeReloadFromOrigin) + { PCLOG(" -Load type is: Reload from origin"); cannotCache = true; } + if (loadType == FrameLoadTypeSame) + { PCLOG(" -Load type is: Same"); cannotCache = true; } + } while (false); + + PCLOG(cannotCache ? " Page CANNOT be cached\n--------" : " Page CAN be cached\n--------"); +} + +bool FrameLoader::logCanCacheFrameDecision(int indentLevel) +{ + // Only bother logging for frames that have actually loaded and have content. + if (m_creatingInitialEmptyDocument) + return false; + KURL currentURL = m_documentLoader ? m_documentLoader->url() : KURL(); + if (currentURL.isEmpty()) + return false; + + PCLOG("+---"); + KURL newURL = m_provisionalDocumentLoader ? m_provisionalDocumentLoader->url() : KURL(); + if (!newURL.isEmpty()) + PCLOG(" Determining if frame can be cached navigating from (%s) to (%s):", currentURL.string().utf8().data(), newURL.string().utf8().data()); + else + PCLOG(" Determining if subframe with URL (%s) can be cached:", currentURL.string().utf8().data()); + + bool cannotCache = false; + + do { + if (!m_documentLoader) { + PCLOG(" -There is no DocumentLoader object"); + cannotCache = true; + break; + } + if (!m_documentLoader->mainDocumentError().isNull()) + { PCLOG(" -Main document has an error"); cannotCache = true; } + if (m_frame->tree()->childCount()) + { PCLOG(" -Frame has child frames"); cannotCache = true; } + if (m_containsPlugIns) + { PCLOG(" -Frame contains plugins"); cannotCache = true; } + if (m_URL.protocolIs("https")) + { PCLOG(" -Frame is HTTPS"); cannotCache = true; } + if (m_frame->domWindow() && m_frame->domWindow()->hasEventListener(eventNames().unloadEvent)) + { PCLOG(" -Frame has an unload event listener"); cannotCache = true; } +#if ENABLE(DATABASE) + if (m_frame->document()->hasOpenDatabases()) + { PCLOG(" -Frame has open database handles"); cannotCache = true; } +#endif + if (m_frame->document()->usingGeolocation()) + { PCLOG(" -Frame uses Geolocation"); cannotCache = true; } + if (!m_currentHistoryItem) + { PCLOG(" -No current history item"); cannotCache = true; } + if (m_quickRedirectComing) + { PCLOG(" -Quick redirect is coming"); cannotCache = true; } + if (m_documentLoader->isLoadingInAPISense()) + { PCLOG(" -DocumentLoader is still loading in API sense"); cannotCache = true; } + if (m_documentLoader->isStopping()) + { PCLOG(" -DocumentLoader is in the middle of stopping"); cannotCache = true; } + if (!m_frame->document()->canSuspendActiveDOMObjects()) + { PCLOG(" -The document cannot suspect its active DOM Objects"); cannotCache = true; } +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (m_documentLoader->applicationCache()) + { PCLOG(" -The DocumentLoader has an active application cache"); cannotCache = true; } + if (m_documentLoader->candidateApplicationCacheGroup()) + { PCLOG(" -The DocumentLoader has a candidateApplicationCacheGroup"); cannotCache = true; } +#endif + if (!m_client->canCachePage()) + { PCLOG(" -The client says this frame cannot be cached"); cannotCache = true; } + } while (false); + + for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) + if (!child->loader()->logCanCacheFrameDecision(indentLevel + 1)) + cannotCache = true; + + PCLOG(cannotCache ? " Frame CANNOT be cached" : " Frame CAN be cached"); + PCLOG("+---"); + + return !cannotCache; +} +#endif + +void FrameLoader::updateFirstPartyForCookies() +{ + if (m_frame->tree()->parent()) + setFirstPartyForCookies(m_frame->tree()->parent()->document()->firstPartyForCookies()); else - setPolicyBaseURL(m_URL); + setFirstPartyForCookies(m_URL); } -void FrameLoader::setPolicyBaseURL(const KURL& url) +void FrameLoader::setFirstPartyForCookies(const KURL& url) { - if (m_frame->document()) - m_frame->document()->setPolicyBaseURL(url); + m_frame->document()->setFirstPartyForCookies(url); for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) - child->loader()->setPolicyBaseURL(url); + child->loader()->setFirstPartyForCookies(url); } // This does the same kind of work that didOpenURL does, except it relies on the fact @@ -1979,6 +2032,15 @@ void FrameLoader::scheduleRedirection(ScheduledRedirection* redirection) { ASSERT(m_frame->page()); + // If a redirect was scheduled during a load, then stop the current load. + // Otherwise when the current load transitions from a provisional to a + // committed state, pending redirects may be cancelled. + if (redirection->wasDuringLoad) { + if (m_provisionalDocumentLoader) + m_provisionalDocumentLoader->stopLoading(); + stopLoading(true); + } + stopRedirectionTimer(); m_scheduledRedirection.set(redirection); if (!m_isComplete && redirection->type != ScheduledRedirection::redirection) @@ -1996,14 +2058,17 @@ void FrameLoader::startRedirectionTimer() m_redirectionTimer.startOneShot(m_scheduledRedirection->delay); switch (m_scheduledRedirection->type) { - case ScheduledRedirection::redirection: case ScheduledRedirection::locationChange: - case ScheduledRedirection::locationChangeDuringLoad: + case ScheduledRedirection::redirection: clientRedirected(KURL(m_scheduledRedirection->url), m_scheduledRedirection->delay, currentTime() + m_redirectionTimer.nextFireInterval(), - m_scheduledRedirection->lockHistory, - m_isExecutingJavaScriptFormAction); + m_scheduledRedirection->lockBackForwardList); + return; + case ScheduledRedirection::formSubmission: + // FIXME: It would make sense to report form submissions as client redirects too. + // But we didn't do that in the past when form submission used a separate delay + // mechanism, so doing it will be a behavior change. return; case ScheduledRedirection::historyNavigation: // Don't report history navigations. @@ -2021,11 +2086,15 @@ void FrameLoader::stopRedirectionTimer() if (m_scheduledRedirection) { switch (m_scheduledRedirection->type) { - case ScheduledRedirection::redirection: case ScheduledRedirection::locationChange: - case ScheduledRedirection::locationChangeDuringLoad: + case ScheduledRedirection::redirection: clientRedirectCancelledOrFinished(m_cancellingWithLoadInProgress); return; + case ScheduledRedirection::formSubmission: + // FIXME: It would make sense to report form submissions as client redirects too. + // But we didn't do that in the past when form submission used a separate delay + // mechanism, so doing it will be a behavior change. + return; case ScheduledRedirection::historyNavigation: // Don't report history navigations. return; @@ -2041,7 +2110,8 @@ void FrameLoader::completed() child->loader()->parentCompleted(); if (Frame* parent = m_frame->tree()->parent()) parent->loader()->checkCompleted(); - submitFormAgain(); + if (m_frame->view()) + m_frame->view()->setLockedToAnchor(false); } void FrameLoader::started() @@ -2075,13 +2145,24 @@ void FrameLoader::setupForReplaceByMIMEType(const String& newMIMEType) activeDocumentLoader()->setupForReplaceByMIMEType(newMIMEType); } -void FrameLoader::loadFrameRequestWithFormAndValues(const FrameLoadRequest& request, bool lockHistory, Event* event, - HTMLFormElement* submitForm, const HashMap<String, String>& formValues) +// This is a hack to allow keep navigation to http/https feeds working. To remove this +// we need to introduce new API akin to registerURLSchemeAsLocal, that registers a +// protocols navigation policy. +static bool isFeedWithNestedProtocolInHTTPFamily(const KURL& url) { - RefPtr<FormState> formState; - if (submitForm) - formState = FormState::create(submitForm, formValues, m_frame); - + const String& urlString = url.string(); + if (!urlString.startsWith("feed", false)) + return false; + + return urlString.startsWith("feed://", false) + || urlString.startsWith("feed:http:", false) || urlString.startsWith("feed:https:", false) + || urlString.startsWith("feeds:http:", false) || urlString.startsWith("feeds:https:", false) + || urlString.startsWith("feedsearch:http:", false) || urlString.startsWith("feedsearch:https:", false); +} + +void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, bool lockHistory, bool lockBackForwardList, + PassRefPtr<Event> event, PassRefPtr<FormState> formState) +{ KURL url = request.resourceRequest().url(); String referrer; @@ -2092,7 +2173,7 @@ void FrameLoader::loadFrameRequestWithFormAndValues(const FrameLoadRequest& requ referrer = m_outgoingReferrer; ASSERT(frame()->document()); - if (url.protocolIs("file")) { + if (SecurityOrigin::shouldTreatURLAsLocal(url.string()) && !isFeedWithNestedProtocolInHTTPFamily(url)) { if (!canLoad(url, String(), frame()->document()) && !canLoad(url, referrer)) { FrameLoader::reportLocalLoadFailed(m_frame, url.string()); return; @@ -2102,29 +2183,31 @@ void FrameLoader::loadFrameRequestWithFormAndValues(const FrameLoadRequest& requ if (shouldHideReferrer(url, referrer)) referrer = String(); - Frame* targetFrame = findFrameForNavigation(request.frameName()); - - if (request.resourceRequest().httpMethod() != "POST") { - FrameLoadType loadType; - if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData) - loadType = FrameLoadTypeReload; - else if (lockHistory) - loadType = FrameLoadTypeRedirectWithLockedHistory; - else - loadType = FrameLoadTypeStandard; - - loadURL(request.resourceRequest().url(), referrer, request.frameName(), loadType, - event, formState.release()); - } else - loadPostRequest(request.resourceRequest(), referrer, request.frameName(), event, formState.release()); + FrameLoadType loadType; + if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData) + loadType = FrameLoadTypeReload; + else if (lockBackForwardList) + loadType = FrameLoadTypeRedirectWithLockedBackForwardList; + else + loadType = FrameLoadTypeStandard; - if (targetFrame && targetFrame != m_frame) + if (request.resourceRequest().httpMethod() == "POST") + loadPostRequest(request.resourceRequest(), referrer, request.frameName(), lockHistory, loadType, event, formState.get()); + else + loadURL(request.resourceRequest().url(), referrer, request.frameName(), lockHistory, loadType, event, formState.get()); + + // FIXME: It's possible this targetFrame will not be the same frame that was targeted by the actual + // load if frame names have changed. + Frame* sourceFrame = formState ? formState->sourceFrame() : m_frame; + Frame* targetFrame = sourceFrame->loader()->findFrameForNavigation(request.frameName()); + if (targetFrame && targetFrame != sourceFrame) { if (Page* page = targetFrame->page()) page->chrome()->focus(); + } } -void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, FrameLoadType newLoadType, - Event* event, PassRefPtr<FormState> prpFormState) +void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType newLoadType, + PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState) { RefPtr<FormState> formState = prpFormState; bool isFormSubmission = formState; @@ -2141,13 +2224,17 @@ void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const Stri ASSERT(newLoadType != FrameLoadTypeSame); + // The search for a target frame is done earlier in the case of form submission. + Frame* targetFrame = isFormSubmission ? 0 : findFrameForNavigation(frameName); + if (targetFrame && targetFrame != m_frame) { + targetFrame->loader()->loadURL(newURL, referrer, String(), lockHistory, newLoadType, event, formState.release()); + return; + } + NavigationAction action(newURL, newLoadType, isFormSubmission, event); - if (!frameName.isEmpty()) { - if (Frame* targetFrame = findFrameForNavigation(frameName)) - targetFrame->loader()->loadURL(newURL, referrer, String(), newLoadType, event, formState); - else - checkNewWindowPolicy(action, request, formState, frameName); + if (!targetFrame && !frameName.isEmpty()) { + checkNewWindowPolicy(action, request, formState.release(), frameName); return; } @@ -2161,12 +2248,12 @@ void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const Stri if (shouldScrollToAnchor(isFormSubmission, newLoadType, newURL)) { oldDocumentLoader->setTriggeringAction(action); stopPolicyCheck(); - checkNavigationPolicy(request, oldDocumentLoader.get(), formState, + checkNavigationPolicy(request, oldDocumentLoader.get(), formState.release(), callContinueFragmentScrollAfterNavigationPolicy, this); } else { // must grab this now, since this load may stop the previous load and clear this flag bool isRedirect = m_quickRedirectComing; - loadWithNavigationAction(request, action, newLoadType, formState); + loadWithNavigationAction(request, action, lockHistory, newLoadType, formState.release()); if (isRedirect) { m_quickRedirectComing = false; if (m_provisionalDocumentLoader) @@ -2179,40 +2266,45 @@ void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const Stri } } -void FrameLoader::load(const ResourceRequest& request) +void FrameLoader::load(const ResourceRequest& request, bool lockHistory) { - load(request, SubstituteData()); + load(request, SubstituteData(), lockHistory); } -void FrameLoader::load(const ResourceRequest& request, const SubstituteData& substituteData) +void FrameLoader::load(const ResourceRequest& request, const SubstituteData& substituteData, bool lockHistory) { if (m_inStopAllLoaders) return; // FIXME: is this the right place to reset loadType? Perhaps this should be done after loading is finished or aborted. m_loadType = FrameLoadTypeStandard; - load(m_client->createDocumentLoader(request, substituteData).get()); + RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, substituteData); + if (lockHistory && m_documentLoader) + loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory() : m_documentLoader->clientRedirectSourceForHistory()); + load(loader.get()); } -void FrameLoader::load(const ResourceRequest& request, const String& frameName) +void FrameLoader::load(const ResourceRequest& request, const String& frameName, bool lockHistory) { if (frameName.isEmpty()) { - load(request); + load(request, lockHistory); return; } Frame* frame = findFrameForNavigation(frameName); if (frame) { - frame->loader()->load(request); + frame->loader()->load(request, lockHistory); return; } checkNewWindowPolicy(NavigationAction(request.url(), NavigationTypeOther), request, 0, frameName); } -void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, FrameLoadType type, PassRefPtr<FormState> formState) +void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, bool lockHistory, FrameLoadType type, PassRefPtr<FormState> formState) { RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData()); + if (lockHistory && m_documentLoader) + loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory() : m_documentLoader->clientRedirectSourceForHistory()); loader->setTriggeringAction(action); if (m_documentLoader) @@ -2280,6 +2372,8 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t stopPolicyCheck(); setPolicyDocumentLoader(loader); + if (loader->triggeringAction().isEmpty()) + loader->setTriggeringAction(NavigationAction(newURL, m_policyLoadType, isFormSubmission)); checkNavigationPolicy(loader->request(), loader, formState, callContinueLoadAfterNavigationPolicy, this); @@ -2288,18 +2382,22 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t bool FrameLoader::canLoad(const KURL& url, const String& referrer, const Document* doc) { - // We can always load any URL that isn't considered local (e.g. http URLs) - if (!shouldTreatURLAsLocal(url.string())) + return canLoad(url, referrer, doc ? doc->securityOrigin() : 0); +} + +bool FrameLoader::canLoad(const KURL& url, const String& referrer, const SecurityOrigin* securityOrigin) +{ + // We can always load any URL that isn't considered local (e.g. http URLs). + if (!SecurityOrigin::shouldTreatURLAsLocal(url.string())) return true; // If we were provided a document, we let its local file policy dictate the result, // otherwise we allow local loads only if the supplied referrer is also local. - if (doc) - return doc->securityOrigin()->canLoadLocalResources(); - else if (!referrer.isEmpty()) - return shouldTreatURLAsLocal(referrer); - else - return false; + if (securityOrigin) + return securityOrigin->canLoadLocalResources(); + if (!referrer.isEmpty()) + return SecurityOrigin::shouldTreatURLAsLocal(referrer); + return false; } void FrameLoader::reportLocalLoadFailed(Frame* frame, const String& url) @@ -2407,7 +2505,7 @@ bool FrameLoader::shouldReloadToHandleUnreachableURL(DocumentLoader* docLoader) return compareDocumentLoader && unreachableURL == compareDocumentLoader->request().url(); } -void FrameLoader::reloadAllowingStaleData(const String& encoding) +void FrameLoader::reloadWithOverrideEncoding(const String& encoding) { if (!m_documentLoader) return; @@ -2424,7 +2522,7 @@ void FrameLoader::reloadAllowingStaleData(const String& encoding) loader->setOverrideEncoding(encoding); - loadWithDocumentLoader(loader.get(), FrameLoadTypeReloadAllowingStaleData, 0); + loadWithDocumentLoader(loader.get(), FrameLoadTypeReload, 0); } void FrameLoader::reload(bool endToEndReload) @@ -2537,8 +2635,11 @@ void FrameLoader::stopLoadingSubframes() child->loader()->stopAllLoaders(); } -void FrameLoader::stopAllLoaders() +void FrameLoader::stopAllLoaders(DatabasePolicy databasePolicy) { + if (m_unloadEventBeingDispatched) + return; + // If this method is called from within this method, infinite recursion can occur (3442218). Avoid this. if (m_inStopAllLoaders) return; @@ -2549,9 +2650,9 @@ void FrameLoader::stopAllLoaders() stopLoadingSubframes(); if (m_provisionalDocumentLoader) - m_provisionalDocumentLoader->stopLoading(); + m_provisionalDocumentLoader->stopLoading(databasePolicy); if (m_documentLoader) - m_documentLoader->stopLoading(); + m_documentLoader->stopLoading(databasePolicy); setProvisionalDocumentLoader(0); @@ -2607,11 +2708,6 @@ void FrameLoader::setDocumentLoader(DocumentLoader* loader) m_documentLoader = loader; } -DocumentLoader* FrameLoader::documentLoader() const -{ - return m_documentLoader.get(); -} - void FrameLoader::setPolicyDocumentLoader(DocumentLoader* loader) { if (m_policyDocumentLoader == loader) @@ -2628,16 +2724,6 @@ void FrameLoader::setPolicyDocumentLoader(DocumentLoader* loader) m_policyDocumentLoader = loader; } -DocumentLoader* FrameLoader::policyDocumentLoader() const -{ - return m_policyDocumentLoader.get(); -} - -DocumentLoader* FrameLoader::provisionalDocumentLoader() const -{ - return m_provisionalDocumentLoader.get(); -} - void FrameLoader::setProvisionalDocumentLoader(DocumentLoader* loader) { ASSERT(!loader || !m_provisionalDocumentLoader); @@ -2649,11 +2735,6 @@ void FrameLoader::setProvisionalDocumentLoader(DocumentLoader* loader) m_provisionalDocumentLoader = loader; } -FrameState FrameLoader::state() const -{ - return m_state; -} - double FrameLoader::timeOfLastCompletedLoad() { return storedTimeOfLastCompletedLoad; @@ -2690,14 +2771,12 @@ void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage) { RefPtr<CachedPage> cachedPage = prpCachedPage; RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader; - + + LOG(Loading, "WebCoreLoading %s: About to commit provisional load from previous URL %s", m_frame->tree()->name().string().utf8().data(), m_URL.string().utf8().data()); + // Check to see if we need to cache the page we are navigating away from into the back/forward cache. // We are doing this here because we know for sure that a new page is about to be loaded. - if (canCachePage() && m_client->canCachePage() && !m_currentHistoryItem->isInPageCache()) { - if (Document* document = m_frame->document()) - document->suspendActiveDOMObjects(); - cachePageForHistoryItem(m_currentHistoryItem.get()); - } + cachePageForHistoryItem(m_currentHistoryItem.get()); if (m_loadType != FrameLoadTypeReplace) closeOldDataSources(); @@ -2728,7 +2807,40 @@ void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage) didOpenURL(url); } - opened(); + + LOG(Loading, "WebCoreLoading %s: Finished committing provisional load to URL %s", m_frame->tree()->name().string().utf8().data(), m_URL.string().utf8().data()); + + if (m_loadType == FrameLoadTypeStandard && m_documentLoader->isClientRedirect()) + updateHistoryForClientRedirect(); + + if (m_documentLoader->isLoadingFromCachedPage()) { + m_frame->document()->documentDidBecomeActive(); + + // Force a layout to update view size and thereby update scrollbars. + m_client->forceLayout(); + + const ResponseVector& responses = m_documentLoader->responses(); + size_t count = responses.size(); + for (size_t i = 0; i < count; i++) { + const ResourceResponse& response = responses[i]; + // FIXME: If the WebKit client changes or cancels the request, this is not respected. + ResourceError error; + unsigned long identifier; + ResourceRequest request(response.url()); + requestFromDelegate(request, identifier, error); + // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing. + // However, with today's computers and networking speeds, this won't happen in practice. + // Could be an issue with a giant local file. + sendRemainingDelegateMessages(identifier, response, static_cast<int>(response.expectedContentLength()), error); + } + + pageCache()->remove(m_currentHistoryItem.get()); + + m_documentLoader->setPrimaryLoadComplete(true); + + // FIXME: Why only this frame and not parent frames? + checkLoadCompleteForThisFrame(); + } } void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) @@ -2778,7 +2890,7 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) DocumentLoader* cachedDocumentLoader = cachedPage->documentLoader(); ASSERT(cachedDocumentLoader); cachedDocumentLoader->setFrame(m_frame); - m_client->transitionToCommittedFromCachedPage(cachedPage.get()); + m_client->transitionToCommittedFromCachedFrame(cachedPage->cachedMainFrame()); } else m_client->transitionToCommittedForNewPage(); @@ -2793,11 +2905,6 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) m_client->transitionToCommittedForNewPage(); break; - // FIXME - just get rid of this case, and merge FrameLoadTypeReloadAllowingStaleData with the above case - case FrameLoadTypeReloadAllowingStaleData: - m_client->transitionToCommittedForNewPage(); - break; - case FrameLoadTypeStandard: updateHistoryForStandardLoad(); #ifndef BUILDING_ON_TIGER @@ -2809,8 +2916,8 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) m_client->transitionToCommittedForNewPage(); break; - case FrameLoadTypeRedirectWithLockedHistory: - updateHistoryForRedirectWithLockedHistory(); + case FrameLoadTypeRedirectWithLockedBackForwardList: + updateHistoryForRedirectWithLockedBackForwardList(); m_client->transitionToCommittedForNewPage(); break; @@ -2827,9 +2934,9 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) if (m_creatingInitialEmptyDocument) return; - - m_committedFirstRealDocumentLoad = true; + m_committedFirstRealDocumentLoad = true; + // For non-cached HTML pages, these methods are called in FrameLoader::begin. if (cachedPage || !m_client->hasHTMLView()) { dispatchDidCommitLoad(); @@ -2853,7 +2960,7 @@ void FrameLoader::clientRedirectCancelledOrFinished(bool cancelWithLoadInProgres m_sentRedirectNotification = false; } -void FrameLoader::clientRedirected(const KURL& url, double seconds, double fireDate, bool lockHistory, bool isJavaScriptFormAction) +void FrameLoader::clientRedirected(const KURL& url, double seconds, double fireDate, bool lockBackForwardList) { m_client->dispatchWillPerformClientRedirect(url, seconds, fireDate); @@ -2861,10 +2968,11 @@ void FrameLoader::clientRedirected(const KURL& url, double seconds, double fireD // the next provisional load, we can send a corresponding -webView:didCancelClientRedirectForFrame: m_sentRedirectNotification = true; - // If a "quick" redirect comes in an, we set a special mode so we treat the next - // load as part of the same navigation. If we don't have a document loader, we have + // If a "quick" redirect comes in, we set a special mode so we treat the next + // load as part of the original navigation. If we don't have a document loader, we have // no "original" load on which to base a redirect, so we treat the redirect as a normal load. - m_quickRedirectComing = lockHistory && m_documentLoader && !isJavaScriptFormAction; + // Loads triggered by JavaScript form submissions never count as quick redirects. + m_quickRedirectComing = lockBackForwardList && m_documentLoader && !m_isExecutingJavaScriptFormAction; } #if ENABLE(WML) @@ -2906,6 +3014,7 @@ void FrameLoader::closeOldDataSources() void FrameLoader::open(CachedPage& cachedPage) { + ASSERT(!m_frame->tree()->parent()); ASSERT(m_frame->page()); ASSERT(m_frame->page()->mainFrame() == m_frame); @@ -2913,11 +3022,6 @@ void FrameLoader::open(CachedPage& cachedPage) // We still have to close the previous part page. closeURL(); - - m_isComplete = false; - - // Don't re-emit the load event. - m_didCallImplicitClose = true; // Delete old status bar messages (if it _was_ activated on last URL). if (m_frame->script()->isEnabled()) { @@ -2925,9 +3029,22 @@ void FrameLoader::open(CachedPage& cachedPage) m_frame->setJSDefaultStatusBarText(String()); } - KURL url = cachedPage.url(); + open(*cachedPage.cachedMainFrame()); + cachedPage.restore(m_frame->page()); - if ((url.protocolIs("http") || url.protocolIs("https")) && !url.host().isEmpty() && url.path().isEmpty()) + checkCompleted(); +} + +void FrameLoader::open(CachedFrame& cachedFrame) +{ + m_isComplete = false; + + // Don't re-emit the load event. + m_didCallImplicitClose = true; + + KURL url = cachedFrame.url(); + + if (url.protocolInHTTPFamily() && !url.host().isEmpty() && url.path().isEmpty()) url.setPath("/"); m_URL = url; @@ -2937,7 +3054,7 @@ void FrameLoader::open(CachedPage& cachedPage) clear(); - Document* document = cachedPage.document(); + Document* document = cachedFrame.document(); ASSERT(document); document->setInPageCache(false); @@ -2946,28 +3063,24 @@ void FrameLoader::open(CachedPage& cachedPage) m_didCallImplicitClose = false; m_outgoingReferrer = url.string(); - FrameView* view = cachedPage.view(); + FrameView* view = cachedFrame.view(); + + // When navigating to a CachedFrame its FrameView should never be null. If it is we'll crash in creative ways downstream. + ASSERT(view); if (view) view->setWasScrolledByUser(false); m_frame->setView(view); m_frame->setDocument(document); - m_frame->setDOMWindow(cachedPage.domWindow()); + m_frame->setDOMWindow(cachedFrame.domWindow()); m_frame->domWindow()->setURL(document->url()); m_frame->domWindow()->setSecurityOrigin(document->securityOrigin()); m_decoder = document->decoder(); - updatePolicyBaseURL(); + updateFirstPartyForCookies(); - cachedPage.restore(m_frame->page()); - document->resumeActiveDOMObjects(); - - // It is necessary to update any platform script objects after restoring the - // cached page. - m_frame->script()->updatePlatformScriptObjects(); - - checkCompleted(); + cachedFrame.restore(); } bool FrameLoader::isStopping() const @@ -3060,7 +3173,17 @@ void FrameLoader::finishedLoadingDocument(DocumentLoader* loader) ArchiveResource* mainResource = archive->mainResource(); loader->setParsedArchiveData(mainResource->data()); - continueLoadWithData(mainResource->data(), mainResource->mimeType(), mainResource->textEncoding(), mainResource->url()); + + m_responseMIMEType = mainResource->mimeType(); + didOpenURL(mainResource->url()); + + String userChosenEncoding = documentLoader()->overrideEncoding(); + bool encodingIsUserChosen = !userChosenEncoding.isNull(); + setEncoding(encodingIsUserChosen ? userChosenEncoding : mainResource->textEncoding(), encodingIsUserChosen); + + ASSERT(m_frame->document()); + + addData(mainResource->data()->data(), mainResource->data()->size()); } bool FrameLoader::isReplacing() const @@ -3103,20 +3226,28 @@ FrameLoadType FrameLoader::loadType() const return m_loadType; } -CachePolicy FrameLoader::cachePolicy() const +CachePolicy FrameLoader::subresourceCachePolicy() const { if (m_isComplete) return CachePolicyVerify; - + if (m_loadType == FrameLoadTypeReloadFromOrigin) return CachePolicyReload; - + if (Frame* parentFrame = m_frame->tree()->parent()) { - CachePolicy parentCachePolicy = parentFrame->loader()->cachePolicy(); + CachePolicy parentCachePolicy = parentFrame->loader()->subresourceCachePolicy(); if (parentCachePolicy != CachePolicyVerify) return parentCachePolicy; } + // FIXME: POST documents are always Reloads, but their subresources should still be Revalidate. + // If we bring the CachePolicy.h and ResourceRequest cache policy enums in sync with each other and + // remember "Revalidate" in ResourceRequests, we can remove this "POST" check and return either "Reload" + // or "Revalidate" if the DocumentLoader was requested with either. + const ResourceRequest& request(documentLoader()->request()); + if (request.cachePolicy() == ReloadIgnoringCacheData && !equalIgnoringCase(request.httpMethod(), "post")) + return CachePolicyRevalidate; + if (m_loadType == FrameLoadTypeReload) return CachePolicyRevalidate; @@ -3156,7 +3287,7 @@ void FrameLoader::checkLoadCompleteForThisFrame() item = m_currentHistoryItem; bool shouldReset = true; - if (!pdl->isLoadingInAPISense()) { + if (!(pdl->isLoadingInAPISense() && !pdl->isStopping())) { m_delegateIsHandlingProvisionalLoadError = true; m_client->dispatchDidFailProvisionalLoad(error); m_delegateIsHandlingProvisionalLoadError = false; @@ -3188,7 +3319,7 @@ void FrameLoader::checkLoadCompleteForThisFrame() case FrameStateCommittedPage: { DocumentLoader* dl = m_documentLoader.get(); - if (!dl || dl->isLoadingInAPISense()) + if (!dl || (dl->isLoadingInAPISense() && !dl->isStopping())) return; markLoadComplete(); @@ -3222,9 +3353,7 @@ void FrameLoader::checkLoadCompleteForThisFrame() } case FrameStateComplete: - // Even if already complete, we might have set a previous item on a frame that - // didn't do any data loading on the past transaction. Make sure to clear these out. - m_client->frameLoadCompleted(); + frameLoadCompleted(); return; } @@ -3285,8 +3414,14 @@ void FrameLoader::didFirstVisuallyNonEmptyLayout() void FrameLoader::frameLoadCompleted() { + // Note: Can be called multiple times. + m_client->frameLoadCompleted(); + // Even if already complete, we might have set a previous item on a frame that + // didn't do any data loading on the past transaction. Make sure to clear these out. + m_previousHistoryItem = 0; + // After a canceled provisional load, firstLayoutDone is false. // Reset it to true if we're displaying a page. if (m_documentLoader) @@ -3298,11 +3433,6 @@ bool FrameLoader::firstLayoutDone() const return m_firstLayoutDone; } -bool FrameLoader::isQuickRedirectComing() const -{ - return m_quickRedirectComing; -} - void FrameLoader::detachChildren() { // FIXME: Is it really necessary to do this in reverse order? @@ -3313,6 +3443,18 @@ void FrameLoader::detachChildren() } } +void FrameLoader::closeAndRemoveChild(Frame* child) +{ + child->tree()->detachFromParent(); + + child->setView(0); + if (child->ownerElement()) + child->page()->decrementFrameCount(); + child->pageDestroyed(); + + m_frame->tree()->removeChild(child); +} + void FrameLoader::recursiveCheckLoadComplete() { Vector<RefPtr<Frame>, 10> frames; @@ -3349,36 +3491,6 @@ int FrameLoader::numPendingOrLoadingRequests(bool recurse) const return count; } -FrameLoaderClient* FrameLoader::client() const -{ - return m_client; -} - -void FrameLoader::submitForm(const FrameLoadRequest& request, Event* event) -{ - // FIXME: We'd like to remove this altogether and fix the multiple form submission issue another way. - // We do not want to submit more than one form from the same page, - // nor do we want to submit a single form more than once. - // This flag prevents these from happening; not sure how other browsers prevent this. - // The flag is reset in each time we start handle a new mouse or key down event, and - // also in setView since this part may get reused for a page from the back/forward cache. - // The form multi-submit logic here is only needed when we are submitting a form that affects this frame. - // FIXME: Frame targeting is only one of the ways the submission could end up doing something other - // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly - // needed any more now that we reset m_submittedFormURL on each mouse or key down event. - Frame* target = m_frame->tree()->find(request.frameName()); - if (m_frame->tree()->isDescendantOf(target)) { - if (m_submittedFormURL == request.resourceRequest().url()) - return; - m_submittedFormURL = request.resourceRequest().url(); - } - - // FIXME: We should probably call userGestureHint() to tell whether this form submission was the result of a user gesture. - loadFrameRequestWithFormAndValues(request, false, event, m_formAboutToBeSubmitted.get(), m_formValuesAboutToBeSubmitted); - - clearRecordedFormValues(); -} - String FrameLoader::userAgent(const KURL& url) const { return m_client->userAgent(url); @@ -3386,9 +3498,6 @@ String FrameLoader::userAgent(const KURL& url) const void FrameLoader::tokenizerProcessedData() { -// ASSERT(m_frame->page()); -// ASSERT(m_frame->document()); - checkCompleted(); } @@ -3400,8 +3509,7 @@ void FrameLoader::handledOnloadEvents() void FrameLoader::frameDetached() { stopAllLoaders(); - if (Document* document = m_frame->document()) - document->stopActiveDOMObjects(); + m_frame->document()->stopActiveDOMObjects(); detachFromParent(); } @@ -3421,7 +3529,7 @@ void FrameLoader::detachFromParent() setDocumentLoader(0); m_client->detachedFromParent3(); if (Frame* parent = m_frame->tree()->parent()) { - parent->tree()->removeChild(m_frame); + parent->loader()->closeAndRemoveChild(m_frame); parent->loader()->scheduleCheckCompleted(); } else { m_frame->setView(0); @@ -3441,6 +3549,19 @@ void FrameLoader::addExtraFieldsToMainResourceRequest(ResourceRequest& request) void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadType loadType, bool mainResource, bool cookiePolicyURLFromRequest) { + // Don't set the cookie policy URL if it's already been set. + // But make sure to set it on all requests, as it has significance beyond the cookie policy for all protocols (<rdar://problem/6616664>). + if (request.firstPartyForCookies().isEmpty()) { + if (mainResource && (isLoadingMainFrame() || cookiePolicyURLFromRequest)) + request.setFirstPartyForCookies(request.url()); + else if (Document* document = m_frame->document()) + request.setFirstPartyForCookies(document->firstPartyForCookies()); + } + + // The remaining modifications are only necessary for HTTP and HTTPS. + if (!request.url().isEmpty() && !request.url().protocolInHTTPFamily()) + return; + applyUserAgent(request); if (loadType == FrameLoadTypeReload) { @@ -3452,16 +3573,8 @@ void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadTyp request.setHTTPHeaderField("Pragma", "no-cache"); } - // Don't set the cookie policy URL if it's already been set. - if (request.mainDocumentURL().isEmpty()) { - if (mainResource && (isLoadingMainFrame() || cookiePolicyURLFromRequest)) - request.setMainDocumentURL(request.url()); - else if (Page* page = m_frame->page()) - request.setMainDocumentURL(page->mainFrame()->loader()->url()); - } - if (mainResource) - request.setHTTPAccept("application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"); + request.setHTTPAccept(defaultAcceptHeader); // Make sure we send the Origin header. addHTTPOriginIfNeeded(request, String()); @@ -3505,8 +3618,7 @@ void FrameLoader::committedLoad(DocumentLoader* loader, const char* data, int le m_client->committedLoad(loader, data, length); } -void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, - Event* event, PassRefPtr<FormState> prpFormState) +void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType loadType, PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState) { RefPtr<FormState> formState = prpFormState; @@ -3533,29 +3645,22 @@ void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String workingResourceRequest.setHTTPMethod("POST"); workingResourceRequest.setHTTPBody(formData); workingResourceRequest.setHTTPContentType(contentType); - addExtraFieldsToRequest(workingResourceRequest, FrameLoadTypeStandard, true, true); + addExtraFieldsToRequest(workingResourceRequest, loadType, true, true); - NavigationAction action(url, FrameLoadTypeStandard, true, event); + NavigationAction action(url, loadType, true, event); if (!frameName.isEmpty()) { - if (Frame* targetFrame = findFrameForNavigation(frameName)) - targetFrame->loader()->loadWithNavigationAction(workingResourceRequest, action, FrameLoadTypeStandard, formState.release()); + // The search for a target frame is done earlier in the case of form submission. + if (Frame* targetFrame = formState ? 0 : findFrameForNavigation(frameName)) + targetFrame->loader()->loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release()); else checkNewWindowPolicy(action, workingResourceRequest, formState.release(), frameName); } else - loadWithNavigationAction(workingResourceRequest, action, FrameLoadTypeStandard, formState.release()); -} - -void FrameLoader::loadEmptyDocumentSynchronously() -{ - ResourceRequest request(KURL("")); - load(request); + loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release()); } -unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) +unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data) { - // Since this is a subresource, we can load any URL (we ignore the return value). - // But we still want to know whether we should hide the referrer or not, so we call the canLoad method. String referrer = m_outgoingReferrer; if (shouldHideReferrer(request.url(), referrer)) referrer = String(); @@ -3573,7 +3678,7 @@ unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& requ addHTTPOriginIfNeeded(initialRequest, outgoingOrigin()); if (Page* page = m_frame->page()) - initialRequest.setMainDocumentURL(page->mainFrame()->loader()->documentLoader()->request().url()); + initialRequest.setFirstPartyForCookies(page->mainFrame()->loader()->documentLoader()->request().url()); initialRequest.setHTTPUserAgent(client()->userAgent(request.url())); unsigned long identifier = 0; @@ -3593,7 +3698,7 @@ unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& requ error = cannotShowURLError(newRequest); } else { #endif - ResourceHandle::loadResourceSynchronously(newRequest, error, response, data, m_frame); + ResourceHandle::loadResourceSynchronously(newRequest, storedCredentials, error, response, data, m_frame); #if ENABLE(OFFLINE_WEB_APPLICATIONS) // If normal loading results in a redirect to a resource with another origin (indicative of a captive portal), or a 4xx or 5xx status code or equivalent, @@ -3651,6 +3756,11 @@ void FrameLoader::didFailToLoad(ResourceLoader* loader, const ResourceError& err m_client->dispatchDidFailLoading(loader->documentLoader(), loader->identifier(), error); } +void FrameLoader::didLoadResourceByXMLHttpRequest(unsigned long identifier, const ScriptString& sourceString) +{ + m_client->dispatchDidLoadResourceByXMLHttpRequest(identifier, sourceString); +} + const ResourceRequest& FrameLoader::originalRequest() const { return activeDocumentLoader()->originalRequestCopy(); @@ -3672,8 +3782,8 @@ void FrameLoader::receivedMainResourceError(const ResourceError& error, bool isC } if (m_state == FrameStateProvisional && m_provisionalDocumentLoader) { - KURL failedURL = m_provisionalDocumentLoader->originalRequestCopy().url(); - didNotOpenURL(failedURL); + if (m_submittedFormURL == m_provisionalDocumentLoader->originalRequestCopy().url()) + m_submittedFormURL = KURL(); // We might have made a page cache item, but now we're bailing out due to an error before we ever // transitioned to the new page (before WebFrameState == commit). The goal here is to restore any state @@ -3702,10 +3812,7 @@ void FrameLoader::callContinueFragmentScrollAfterNavigationPolicy(void* argument void FrameLoader::continueFragmentScrollAfterNavigationPolicy(const ResourceRequest& request, bool shouldContinue) { - // FIXME: - // some functions check m_quickRedirectComing, and others check for - // FrameLoadTypeRedirectWithLockedHistory. - bool isRedirect = m_quickRedirectComing || m_policyLoadType == FrameLoadTypeRedirectWithLockedHistory; + bool isRedirect = m_quickRedirectComing || m_policyLoadType == FrameLoadTypeRedirectWithLockedBackForwardList; m_quickRedirectComing = false; if (!shouldContinue) @@ -3758,42 +3865,7 @@ bool FrameLoader::shouldScrollToAnchor(bool isFormSubmission, FrameLoadType load && !shouldReload(this->url(), url) // We don't want to just scroll if a link from within a // frameset is trying to reload the frameset into _top. - && !m_frame->isFrameSet(); -} - -void FrameLoader::opened() -{ - if (m_loadType == FrameLoadTypeStandard && m_documentLoader->isClientRedirect()) - updateHistoryForClientRedirect(); - - if (m_documentLoader->isLoadingFromCachedPage()) { - m_frame->document()->documentDidBecomeActive(); - - // Force a layout to update view size and thereby update scrollbars. - m_client->forceLayout(); - - const ResponseVector& responses = m_documentLoader->responses(); - size_t count = responses.size(); - for (size_t i = 0; i < count; i++) { - const ResourceResponse& response = responses[i]; - // FIXME: If the WebKit client changes or cancels the request, this is not respected. - ResourceError error; - unsigned long identifier; - ResourceRequest request(response.url()); - requestFromDelegate(request, identifier, error); - // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing. - // However, with today's computers and networking speeds, this won't happen in practice. - // Could be an issue with a giant local file. - sendRemainingDelegateMessages(identifier, response, static_cast<int>(response.expectedContentLength()), error); - } - - pageCache()->remove(m_currentHistoryItem.get()); - - m_documentLoader->setPrimaryLoadComplete(true); - - // FIXME: Why only this frame and not parent frames? - checkLoadCompleteForThisFrame(); - } + && !m_frame->document()->isFrameSet(); } void FrameLoader::checkNewWindowPolicy(const NavigationAction& action, const ResourceRequest& request, @@ -3944,7 +4016,14 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, Pass // might detach the current FrameLoader, in which case we should bail on this newly defunct load. if (!m_frame->page()) return; - + +#if ENABLE(JAVASCRIPT_DEBUGGER) + if (Page* page = m_frame->page()) { + if (page->mainFrame() == m_frame) + page->inspectorController()->resumeDebugger(); + } +#endif + setProvisionalDocumentLoader(m_policyDocumentLoader.get()); m_loadType = type; setState(FrameStateProvisional); @@ -3985,7 +4064,7 @@ void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& reques mainFrame->loader()->setOpenedByDOM(); mainFrame->loader()->m_client->dispatchShow(); mainFrame->loader()->setOpener(frame.get()); - mainFrame->loader()->loadWithNavigationAction(request, NavigationAction(), FrameLoadTypeStandard, formState); + mainFrame->loader()->loadWithNavigationAction(request, NavigationAction(), false, FrameLoadTypeStandard, formState); } void FrameLoader::sendRemainingDelegateMessages(unsigned long identifier, const ResourceResponse& response, int length, const ResourceError& error) @@ -4059,6 +4138,24 @@ void FrameLoader::applyUserAgent(ResourceRequest& request) request.setHTTPUserAgent(userAgent); } +bool FrameLoader::shouldInterruptLoadForXFrameOptions(const String& content, const KURL& url) +{ + Frame* topFrame = m_frame->tree()->top(); + if (m_frame == topFrame) + return false; + + if (equalIgnoringCase(content, "deny")) + return true; + + if (equalIgnoringCase(content, "sameorigin")) { + RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url); + if (!origin->isSameSchemeHostPort(topFrame->document()->securityOrigin())) + return true; + } + + return false; +} + bool FrameLoader::canGoBackOrForward(int distance) const { if (Page* page = m_frame->page()) { @@ -4079,28 +4176,6 @@ int FrameLoader::getHistoryLength() return 0; } -KURL FrameLoader::historyURL(int distance) -{ - if (Page* page = m_frame->page()) { - BackForwardList* list = page->backForwardList(); - HistoryItem* item = list->itemAtIndex(distance); - if (!item) { - if (distance > 0) { - int forwardListCount = list->forwardListCount(); - if (forwardListCount > 0) - item = list->itemAtIndex(forwardListCount); - } else { - int backListCount = list->backListCount(); - if (backListCount > 0) - item = list->itemAtIndex(-backListCount); - } - } - if (item) - return item->url(); - } - return KURL(); -} - void FrameLoader::addHistoryItemForFragmentScroll() { addBackForwardItemClippedAtTarget(false); @@ -4117,12 +4192,11 @@ bool FrameLoader::loadProvisionalItemFromCachedPage() void FrameLoader::cachePageForHistoryItem(HistoryItem* item) { + if (!canCachePage() || item->isInPageCache()) + return; + if (Page* page = m_frame->page()) { RefPtr<CachedPage> cachedPage = CachedPage::create(page); - cachedPage->setTimeStampToNow(); - cachedPage->setDocumentLoader(documentLoader()); - m_client->savePlatformDataToCachedPage(cachedPage.get()); - pageCache()->add(item, cachedPage.release()); } } @@ -4193,6 +4267,11 @@ PassRefPtr<HistoryItem> FrameLoader::createHistoryItem(bool useOriginal) void FrameLoader::addBackForwardItemClippedAtTarget(bool doClip) { + // In the case of saving state about a page with frames, we store a tree of items that mirrors the frame tree. + // The item that was the target of the user's navigation is designated as the "targetItem". + // When this function is called with doClip=true we're able to create the whole tree except for the target's children, + // which will be loaded in the future. That part of the tree will be filled out as the child loads are committed. + Page* page = m_frame->page(); if (!page) return; @@ -4242,9 +4321,9 @@ PassRefPtr<HistoryItem> FrameLoader::createHistoryItemTree(Frame* targetFrame, b Frame* FrameLoader::findFrameForNavigation(const AtomicString& name) { Frame* frame = m_frame->tree()->find(name); - if (shouldAllowNavigation(frame)) - return frame; - return 0; + if (!shouldAllowNavigation(frame)) + return 0; + return frame; } void FrameLoader::saveScrollPositionAndViewStateToItem(HistoryItem* item) @@ -4334,7 +4413,7 @@ void FrameLoader::saveDocumentState() Document* document = m_frame->document(); ASSERT(document); - if (document && item->isCurrentDocument(document)) { + if (item->isCurrentDocument(document)) { LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree()->name().string().utf8().data(), item); item->setDocumentState(document->formElementsState()); } @@ -4357,13 +4436,16 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) // Note if we have child frames we do a real reload, since the child frames might not // match our current frame structure, or they might not have the right content. We could // check for all that as an additional optimization. - // We also do not do anchor-style navigation if we're posting a form. - + // We also do not do anchor-style navigation if we're posting a form or navigating from + // a page that was resulted from a form post. + bool shouldScroll = !formData && !(m_currentHistoryItem && m_currentHistoryItem->formData()) && urlsMatchItem(item); + #if ENABLE(WML) - if (!formData && urlsMatchItem(item) && !m_frame->document()->isWMLDocument()) { -#else - if (!formData && urlsMatchItem(item)) { + if (m_frame->document()->isWMLDocument()) + shouldScroll = false; #endif + + if (shouldScroll) { // Must do this maintenance here, since we don't go through a real page reload saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get()); @@ -4417,6 +4499,9 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) bool addedExtraFields = false; ResourceRequest request(itemURL); + if (!item->referrer().isNull()) + request.setHTTPReferrer(item->referrer()); + // If this was a repost that failed the page cache, we might try to repost the form. NavigationAction action; if (formData) { @@ -4424,10 +4509,9 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) formData->generateFiles(m_frame->page()->chrome()->client()); request.setHTTPMethod("POST"); - request.setHTTPReferrer(item->formReferrer()); request.setHTTPBody(formData); request.setHTTPContentType(item->formContentType()); - RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::createFromString(item->formReferrer()); + RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::createFromString(item->referrer()); addHTTPOriginIfNeeded(request, securityOrigin->toString()); // Make sure to add extra fields to the request after the Origin header is added for the FormData case. @@ -4462,12 +4546,9 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) request.setCachePolicy(ReturnCacheDataElseLoad); break; case FrameLoadTypeStandard: - case FrameLoadTypeRedirectWithLockedHistory: - // no-op: leave as protocol default - // FIXME: I wonder if we ever hit this case + case FrameLoadTypeRedirectWithLockedBackForwardList: break; case FrameLoadTypeSame: - case FrameLoadTypeReloadAllowingStaleData: default: ASSERT_NOT_REACHED(); } @@ -4478,7 +4559,7 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) if (!addedExtraFields) addExtraFieldsToRequest(request, m_loadType, true, formData); - loadWithNavigationAction(request, action, loadType, 0); + loadWithNavigationAction(request, action, false, loadType, 0); } } } @@ -4575,10 +4656,10 @@ void FrameLoader::recursiveGoToItem(HistoryItem* item, HistoryItem* fromItem, Fr int size = childItems.size(); for (int i = 0; i < size; ++i) { - String childName = childItems[i]->target(); - HistoryItem* fromChildItem = fromItem->childItemWithName(childName); + String childFrameName = childItems[i]->target(); + HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFrameName); ASSERT(fromChildItem || fromItem->isTargetItem()); - Frame* childFrame = m_frame->tree()->child(childName); + Frame* childFrame = m_frame->tree()->child(childFrameName); ASSERT(childFrame); childFrame->loader()->recursiveGoToItem(childItems[i].get(), fromChildItem, type); } @@ -4596,9 +4677,10 @@ bool FrameLoader::childFramesMatchItem(HistoryItem* item) const return false; unsigned size = childItems.size(); - for (unsigned i = 0; i < size; ++i) + for (unsigned i = 0; i < size; ++i) { if (!m_frame->tree()->child(childItems[i]->target())) return false; + } // Found matches for all item targets return true; @@ -4618,21 +4700,15 @@ void FrameLoader::updateHistoryForStandardLoad() bool needPrivacy = !settings || settings->privateBrowsingEnabled(); const KURL& historyURL = documentLoader()->urlForHistory(); - // If the navigation occured during load and this is a subframe, update the current - // back/forward item rather than adding a new one and don't add the new URL to global - // history at all. But do add it to visited links. <rdar://problem/5333496> - bool frameNavigationDuringLoad = false; - if (m_navigationDuringLoad) { - HTMLFrameOwnerElement* owner = m_frame->ownerElement(); - frameNavigationDuringLoad = owner && !owner->createdByParser(); - m_navigationDuringLoad = false; - } - - if (!frameNavigationDuringLoad && !documentLoader()->isClientRedirect()) { + if (!documentLoader()->isClientRedirect()) { if (!historyURL.isEmpty()) { addBackForwardItemClippedAtTarget(true); - if (!needPrivacy) + if (!needPrivacy) { m_client->updateGlobalHistory(); + m_documentLoader->setDidCreateGlobalHistoryEntry(true); + if (m_documentLoader->unreachableURL().isEmpty()) + m_client->updateGlobalHistoryRedirectLinks(); + } if (Page* page = m_frame->page()) page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForwardList()->currentItem()); } @@ -4644,6 +4720,9 @@ void FrameLoader::updateHistoryForStandardLoad() if (!historyURL.isEmpty() && !needPrivacy) { if (Page* page = m_frame->page()) page->group().addVisitedLink(historyURL); + + if (!m_documentLoader->didCreateGlobalHistoryEntry() && documentLoader()->unreachableURL().isEmpty() && !url().isEmpty()) + m_client->updateGlobalHistoryRedirectLinks(); } } @@ -4701,11 +4780,11 @@ void FrameLoader::updateHistoryForReload() } } -void FrameLoader::updateHistoryForRedirectWithLockedHistory() +void FrameLoader::updateHistoryForRedirectWithLockedBackForwardList() { #if !LOG_DISABLED if (documentLoader()) - LOG(History, "WebCoreHistory: Updating History for internal load in frame %s", documentLoader()->title().utf8().data()); + LOG(History, "WebCoreHistory: Updating History for redirect load in frame %s", documentLoader()->title().utf8().data()); #endif Settings* settings = m_frame->settings(); @@ -4714,10 +4793,14 @@ void FrameLoader::updateHistoryForRedirectWithLockedHistory() if (documentLoader()->isClientRedirect()) { if (!m_currentHistoryItem && !m_frame->tree()->parent()) { - addBackForwardItemClippedAtTarget(true); if (!historyURL.isEmpty()) { - if (!needPrivacy) + addBackForwardItemClippedAtTarget(true); + if (!needPrivacy) { m_client->updateGlobalHistory(); + m_documentLoader->setDidCreateGlobalHistoryEntry(true); + if (m_documentLoader->unreachableURL().isEmpty()) + m_client->updateGlobalHistoryRedirectLinks(); + } if (Page* page = m_frame->page()) page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForwardList()->currentItem()); } @@ -4729,12 +4812,15 @@ void FrameLoader::updateHistoryForRedirectWithLockedHistory() } else { Frame* parentFrame = m_frame->tree()->parent(); if (parentFrame && parentFrame->loader()->m_currentHistoryItem) - parentFrame->loader()->m_currentHistoryItem->addChildItem(createHistoryItem(true)); + parentFrame->loader()->m_currentHistoryItem->setChildItem(createHistoryItem(true)); } if (!historyURL.isEmpty() && !needPrivacy) { if (Page* page = m_frame->page()) page->group().addVisitedLink(historyURL); + + if (!m_documentLoader->didCreateGlobalHistoryEntry() && documentLoader()->unreachableURL().isEmpty() && !url().isEmpty()) + m_client->updateGlobalHistoryRedirectLinks(); } } @@ -4784,38 +4870,16 @@ void FrameLoader::saveDocumentAndScrollState() } } -// FIXME: These 6 setter/getters are here for a dwindling number of users in WebKit, WebFrame -// being the primary one. After they're no longer needed there, they can be removed! HistoryItem* FrameLoader::currentHistoryItem() { return m_currentHistoryItem.get(); } -HistoryItem* FrameLoader::previousHistoryItem() -{ - return m_previousHistoryItem.get(); -} - -HistoryItem* FrameLoader::provisionalHistoryItem() -{ - return m_provisionalHistoryItem.get(); -} - void FrameLoader::setCurrentHistoryItem(PassRefPtr<HistoryItem> item) { m_currentHistoryItem = item; } -void FrameLoader::setPreviousHistoryItem(PassRefPtr<HistoryItem> item) -{ - m_previousHistoryItem = item; -} - -void FrameLoader::setProvisionalHistoryItem(PassRefPtr<HistoryItem> item) -{ - m_provisionalHistoryItem = item; -} - void FrameLoader::setMainDocumentError(DocumentLoader* loader, const ResourceError& error) { m_client->setMainDocumentError(loader, error); @@ -4982,6 +5046,11 @@ String FrameLoader::referrer() const return documentLoader()->request().httpReferrer(); } +void FrameLoader::dispatchDocumentElementAvailable() +{ + m_client->documentElementAvailable(); +} + void FrameLoader::dispatchWindowObjectAvailable() { if (!m_frame->script()->isEnabled() || !m_frame->script()->haveWindowShell()) @@ -4997,27 +5066,39 @@ void FrameLoader::dispatchWindowObjectAvailable() } } -Widget* FrameLoader::createJavaAppletWidget(const IntSize& size, Element* element, const HashMap<String, String>& args) +Widget* FrameLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement* element, const HashMap<String, String>& args) { String baseURLString; + String codeBaseURLString; Vector<String> paramNames; Vector<String> paramValues; HashMap<String, String>::const_iterator end = args.end(); for (HashMap<String, String>::const_iterator it = args.begin(); it != end; ++it) { if (equalIgnoringCase(it->first, "baseurl")) baseURLString = it->second; + else if (equalIgnoringCase(it->first, "codebase")) + codeBaseURLString = it->second; paramNames.append(it->first); paramValues.append(it->second); } - + + if (!codeBaseURLString.isEmpty()) { + KURL codeBaseURL = completeURL(codeBaseURLString); + if (!canLoad(codeBaseURL, String(), element->document())) { + FrameLoader::reportLocalLoadFailed(m_frame, codeBaseURL.string()); + return 0; + } + } + if (baseURLString.isEmpty()) baseURLString = m_frame->document()->baseURL().string(); KURL baseURL = completeURL(baseURLString); Widget* widget = m_client->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues); - if (widget) - m_containsPlugIns = true; - + if (!widget) + return 0; + + m_containsPlugIns = true; return widget; } @@ -5037,67 +5118,6 @@ void FrameLoader::didChangeTitle(DocumentLoader* loader) } } -void FrameLoader::continueLoadWithData(SharedBuffer* buffer, const String& mimeType, const String& textEncoding, const KURL& url) -{ - m_responseMIMEType = mimeType; - didOpenURL(url); - - String encoding; - if (m_frame) - encoding = documentLoader()->overrideEncoding(); - bool userChosen = !encoding.isNull(); - if (encoding.isNull()) - encoding = textEncoding; - setEncoding(encoding, userChosen); - - ASSERT(m_frame->document()); - - addData(buffer->data(), buffer->size()); -} - -void FrameLoader::registerURLSchemeAsLocal(const String& scheme) -{ - localSchemes().add(scheme); -} - -bool FrameLoader::shouldTreatURLAsLocal(const String& url) -{ - // This avoids an allocation of another String and the HashSet contains() - // call for the file: and http: schemes. - if (url.length() >= 5) { - const UChar* s = url.characters(); - if (s[0] == 'h' && s[1] == 't' && s[2] == 't' && s[3] == 'p' && s[4] == ':') - return false; - if (s[0] == 'f' && s[1] == 'i' && s[2] == 'l' && s[3] == 'e' && s[4] == ':') - return true; - } - - int loc = url.find(':'); - if (loc == -1) - return false; - - String scheme = url.left(loc); - return localSchemes().contains(scheme); -} - -bool FrameLoader::shouldTreatSchemeAsLocal(const String& scheme) -{ - // This avoids an allocation of another String and the HashSet contains() - // call for the file: and http: schemes. - if (scheme.length() == 4) { - const UChar* s = scheme.characters(); - if (s[0] == 'h' && s[1] == 't' && s[2] == 't' && s[3] == 'p') - return false; - if (s[0] == 'f' && s[1] == 'i' && s[2] == 'l' && s[3] == 'e') - return true; - } - - if (scheme.isEmpty()) - return false; - - return localSchemes().contains(scheme); -} - void FrameLoader::dispatchDidCommitLoad() { if (m_creatingInitialEmptyDocument) @@ -5129,7 +5149,7 @@ void FrameLoader::dispatchWillSendRequest(DocumentLoader* loader, unsigned long m_client->dispatchWillSendRequest(loader, identifier, request, redirectResponse); // If the URL changed, then we want to put that new URL in the "did tell client" set too. - if (oldRequestURL != request.url().string().impl()) + if (!request.isNull() && oldRequestURL != request.url().string().impl()) m_documentLoader->didTellClientAboutLoad(request.url()); if (Page* page = m_frame->page()) @@ -5187,124 +5207,9 @@ void FrameLoader::tellClientAboutPastMemoryCacheLoads() } } -#if USE(LOW_BANDWIDTH_DISPLAY) - -bool FrameLoader::addLowBandwidthDisplayRequest(CachedResource* cache) -{ - if (m_frame->document()->inLowBandwidthDisplay() == false) - return false; - - // if cache is loaded, don't add to the list, where notifyFinished() is expected. - if (cache->isLoaded()) - return false; - - switch (cache->type()) { - case CachedResource::CSSStyleSheet: - case CachedResource::Script: - m_needToSwitchOutLowBandwidthDisplay = true; - m_externalRequestsInLowBandwidthDisplay.add(cache); - cache->addClient(this); - return true; - case CachedResource::ImageResource: - case CachedResource::FontResource: -#if ENABLE(XSLT) - case CachedResource::XSLStyleSheet: -#endif -#if ENABLE(XBL) - case CachedResource::XBLStyleSheet: -#endif - return false; - } - - ASSERT_NOT_REACHED(); - return false; -} - -void FrameLoader::removeAllLowBandwidthDisplayRequests() -{ - HashSet<CachedResource*>::iterator end = m_externalRequestsInLowBandwidthDisplay.end(); - for (HashSet<CachedResource*>::iterator it = m_externalRequestsInLowBandwidthDisplay.begin(); it != end; ++it) - (*it)->removeClient(this); - m_externalRequestsInLowBandwidthDisplay.clear(); -} - -void FrameLoader::notifyFinished(CachedResource* script) -{ - HashSet<CachedResource*>::iterator it = m_externalRequestsInLowBandwidthDisplay.find(script); - if (it != m_externalRequestsInLowBandwidthDisplay.end()) { - (*it)->removeClient(this); - m_externalRequestsInLowBandwidthDisplay.remove(it); - switchOutLowBandwidthDisplayIfReady(); - } -} - -void FrameLoader::switchOutLowBandwidthDisplayIfReady() +bool FrameLoaderClient::hasHTMLView() const { - RefPtr<Document> oldDoc = m_frame->document(); - if (oldDoc->inLowBandwidthDisplay()) { - if (!m_needToSwitchOutLowBandwidthDisplay) { - // no need to switch, just reset state - oldDoc->setLowBandwidthDisplay(false); - removeAllLowBandwidthDisplayRequests(); - m_pendingSourceInLowBandwidthDisplay = String(); - m_finishedParsingDuringLowBandwidthDisplay = false; - return; - } else if (m_externalRequestsInLowBandwidthDisplay.isEmpty() || - m_pendingSourceInLowBandwidthDisplay.length() > cMaxPendingSourceLengthInLowBandwidthDisplay) { - // clear the flag first - oldDoc->setLowBandwidthDisplay(false); - - // similar to clear(), should be refactored to share more code - oldDoc->cancelParsing(); - oldDoc->detach(); - if (m_frame->script()->isEnabled()) - m_frame->script()->clearWindowShell(); - if (m_frame->view()) - m_frame->view()->clear(); - - // similar to begin(), should be refactored to share more code - RefPtr<Document> newDoc = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode()); - m_frame->setDocument(newDoc); - newDoc->setURL(m_URL); - if (m_decoder) - newDoc->setDecoder(m_decoder.get()); - restoreDocumentState(); - dispatchWindowObjectAvailable(); - newDoc->implicitOpen(); - - // swap DocLoader ownership - DocLoader* docLoader = newDoc->docLoader(); - newDoc->setDocLoader(oldDoc->docLoader()); - newDoc->docLoader()->replaceDocument(newDoc.get()); - docLoader->replaceDocument(oldDoc.get()); - oldDoc->setDocLoader(docLoader); - - // drop the old doc - oldDoc = 0; - - // write decoded data to the new doc, similar to write() - if (m_pendingSourceInLowBandwidthDisplay.length()) { - if (m_decoder->encoding().usesVisualOrdering()) - newDoc->setVisuallyOrdered(); - newDoc->recalcStyle(Node::Force); - newDoc->tokenizer()->write(m_pendingSourceInLowBandwidthDisplay, true); - - if (m_finishedParsingDuringLowBandwidthDisplay) - newDoc->finishParsing(); - } - - // update rendering - newDoc->updateRendering(); - - // reset states - removeAllLowBandwidthDisplayRequests(); - m_pendingSourceInLowBandwidthDisplay = String(); - m_finishedParsingDuringLowBandwidthDisplay = false; - m_needToSwitchOutLowBandwidthDisplay = false; - } - } + return true; } -#endif - } // namespace WebCore |