diff options
Diffstat (limited to 'WebCore/loader/FrameLoader.cpp')
-rw-r--r-- | WebCore/loader/FrameLoader.cpp | 1350 |
1 files changed, 379 insertions, 971 deletions
diff --git a/WebCore/loader/FrameLoader.cpp b/WebCore/loader/FrameLoader.cpp index 514f98a..dc2c68c 100644 --- a/WebCore/loader/FrameLoader.cpp +++ b/WebCore/loader/FrameLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) 2008 Alp Toker <alp@atoker.com> @@ -39,7 +39,7 @@ #include "ArchiveFactory.h" #endif #include "BackForwardList.h" -#include "CString.h" +#include "BeforeUnloadEvent.h" #include "Cache.h" #include "CachedPage.h" #include "Chrome.h" @@ -47,6 +47,7 @@ #include "DOMWindow.h" #include "DocLoader.h" #include "Document.h" +#include "DocumentLoadTiming.h" #include "DocumentLoader.h" #include "Editor.h" #include "EditorClient.h" @@ -55,15 +56,17 @@ #include "EventNames.h" #include "FloatRect.h" #include "FormState.h" +#include "FormSubmission.h" #include "Frame.h" #include "FrameLoadRequest.h" #include "FrameLoaderClient.h" #include "FrameTree.h" #include "FrameView.h" #include "HTMLAnchorElement.h" -#include "HTMLAppletElement.h" #include "HTMLFormElement.h" -#include "HTMLFrameElement.h" +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "HTMLMediaElement.h" +#endif #include "HTMLNames.h" #include "HTMLObjectElement.h" #include "HTTPParsers.h" @@ -83,31 +86,26 @@ #include "PluginDatabase.h" #include "PluginDocument.h" #include "ProgressTracker.h" -#include "RenderPart.h" -#include "RenderView.h" -#include "RenderWidget.h" #include "ResourceHandle.h" #include "ResourceRequest.h" +#include "SchemeRegistry.h" #include "ScriptController.h" #include "ScriptSourceCode.h" #include "ScriptString.h" -#include "ScriptValue.h" #include "SecurityOrigin.h" #include "SegmentedString.h" +#include "SerializedScriptValue.h" #include "Settings.h" - -#if ENABLE(SHARED_WORKERS) -#include "SharedWorkerRepository.h" -#endif - #include "TextResourceDecoder.h" #include "WindowFeatures.h" -#include "XMLHttpRequest.h" -#include "XMLTokenizer.h" -#include "XSSAuditor.h" +#include "XMLDocumentParser.h" #include <wtf/CurrentTime.h> #include <wtf/StdLibExtras.h> +#include <wtf/text/CString.h> +#if ENABLE(SHARED_WORKERS) +#include "SharedWorkerRepository.h" +#endif #if ENABLE(SVG) #include "SVGDocument.h" @@ -126,16 +124,18 @@ namespace WebCore { +using namespace HTMLNames; + #if ENABLE(SVG) using namespace SVGNames; #endif -using namespace HTMLNames; #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; bool isBackForwardLoadType(FrameLoadType type) @@ -171,40 +171,48 @@ static inline bool canReferToParentFrameEncoding(const Frame* frame, const Frame return parentFrame && parentFrame->document()->securityOrigin()->canAccess(frame->document()->securityOrigin()); } +// This is not in the FrameLoader class to emphasize that it does not depend on +// private FrameLoader data, and to avoid increasing the number of public functions +// with access to private data. Since only this .cpp file needs it, making it +// non-member lets us exclude it from the header file, thus keeping FrameLoader.h's +// API simpler. +// +// FIXME: isDocumentSandboxed should eventually replace isSandboxed. +static bool isDocumentSandboxed(Frame* frame, SandboxFlags mask) +{ + return frame->document() && frame->document()->securityOrigin()->isSandboxed(mask); +} + FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client) : m_frame(frame) , m_client(client) , m_policyChecker(frame) , m_history(frame) , m_notifer(frame) + , m_writer(frame) + , m_subframeLoader(frame) , m_state(FrameStateCommittedPage) , m_loadType(FrameLoadTypeStandard) , m_delegateIsHandlingProvisionalLoadError(false) - , m_firstLayoutDone(false) , m_quickRedirectComing(false) , m_sentRedirectNotification(false) , m_inStopAllLoaders(false) , m_isExecutingJavaScriptFormAction(false) , m_didCallImplicitClose(false) , m_wasUnloadEventEmitted(false) - , m_unloadEventBeingDispatched(false) + , m_pageDismissalEventBeingDispatched(false) , m_isComplete(false) , m_isLoadingMainResource(false) , m_needsClear(false) - , m_receivedData(false) - , m_encodingWasChosenByUser(false) - , m_containsPlugIns(false) , m_checkTimer(this, &FrameLoader::checkTimerFired) , m_shouldCallCheckCompleted(false) , m_shouldCallCheckLoadComplete(false) , m_opener(0) - , m_creatingInitialEmptyDocument(false) - , m_isDisplayingInitialEmptyDocument(false) - , m_committedFirstRealDocumentLoad(false) , m_didPerformFirstNavigation(false) , m_loadingFromCachedPage(false) , m_suppressOpenerInNewFrame(false) , m_sandboxFlags(SandboxAll) + , m_forcedSandboxFlags(SandboxNone) #ifndef NDEBUG , m_didDispatchDidCommitLoad(false) #endif @@ -224,22 +232,22 @@ FrameLoader::~FrameLoader() void FrameLoader::init() { + // Propagate sandbox attributes to this Frameloader and its descendants. + // This needs to be done early, so that an initial document gets correct sandbox flags in its SecurityOrigin. + updateSandboxFlags(); + // this somewhat odd set of steps is needed to give the frame an initial empty document - m_isDisplayingInitialEmptyDocument = false; - m_creatingInitialEmptyDocument = true; + m_stateMachine.advanceTo(FrameLoaderStateMachine::CreatingInitialEmptyDocument); setPolicyDocumentLoader(m_client->createDocumentLoader(ResourceRequest(KURL(ParsedURLString, "")), SubstituteData()).get()); setProvisionalDocumentLoader(m_policyDocumentLoader.get()); setState(FrameStateProvisional); m_provisionalDocumentLoader->setResponse(ResourceResponse(KURL(), "text/html", 0, String(), String())); m_provisionalDocumentLoader->finishedLoading(); - begin(KURL(), false); - end(); + writer()->begin(KURL(), false); + writer()->end(); m_frame->document()->cancelParsing(); - m_creatingInitialEmptyDocument = false; + m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocument); m_didCallImplicitClose = true; - - // Propagate sandbox attributes to this Frameloader and its descendants. - updateSandboxFlags(); } void FrameLoader::setDefersLoading(bool defers) @@ -257,71 +265,6 @@ void FrameLoader::setDefersLoading(bool defers) } } -Frame* FrameLoader::createWindow(FrameLoader* frameLoaderForFrameLookup, const FrameLoadRequest& request, const WindowFeatures& features, bool& created) -{ - ASSERT(!features.dialog || request.frameName().isEmpty()); - - if (!request.frameName().isEmpty() && request.frameName() != "_blank") { - Frame* frame = frameLoaderForFrameLookup->frame()->tree()->find(request.frameName()); - if (frame && shouldAllowNavigation(frame)) { - if (!request.resourceRequest().url().isEmpty()) - frame->loader()->loadFrameRequest(request, false, false, 0, 0, SendReferrer); - if (Page* page = frame->page()) -#ifdef ANDROID_USER_GESTURE - page->chrome()->focus(isProcessingUserGesture()); -#else - page->chrome()->focus(); -#endif - created = false; - return frame; - } - } - - // FIXME: Setting the referrer should be the caller's responsibility. - FrameLoadRequest requestWithReferrer = request; - requestWithReferrer.resourceRequest().setHTTPReferrer(m_outgoingReferrer); - addHTTPOriginIfNeeded(requestWithReferrer.resourceRequest(), outgoingOrigin()); - - Page* oldPage = m_frame->page(); - if (!oldPage) - return 0; - - Page* page = oldPage->chrome()->createWindow(m_frame, requestWithReferrer, features); - if (!page) - return 0; - - Frame* frame = page->mainFrame(); - if (request.frameName() != "_blank") - frame->tree()->setName(request.frameName()); - - page->chrome()->setToolbarsVisible(features.toolBarVisible || features.locationBarVisible); - page->chrome()->setStatusbarVisible(features.statusBarVisible); - page->chrome()->setScrollbarsVisible(features.scrollbarsVisible); - page->chrome()->setMenubarVisible(features.menuBarVisible); - page->chrome()->setResizable(features.resizable); - - // 'x' and 'y' specify the location of the window, while 'width' and 'height' - // specify the size of the page. We can only resize the window, so - // adjust for the difference between the window size and the page size. - - FloatRect windowRect = page->chrome()->windowRect(); - FloatSize pageSize = page->chrome()->pageRect().size(); - if (features.xSet) - windowRect.setX(features.x); - if (features.ySet) - windowRect.setY(features.y); - if (features.widthSet) - windowRect.setWidth(features.width + (windowRect.width() - pageSize.width())); - if (features.heightSet) - windowRect.setHeight(features.height + (windowRect.height() - pageSize.height())); - page->chrome()->setWindowRect(windowRect); - - page->chrome()->show(); - - created = true; - return frame; -} - bool FrameLoader::canHandleRequest(const ResourceRequest& request) { return m_client->canHandleRequest(request); @@ -332,21 +275,22 @@ void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool l RefPtr<Frame> protect(m_frame); ResourceRequest request(url, referrer, refresh ? ReloadIgnoringCacheData : UseProtocolCachePolicy); -#ifdef ANDROID_USER_GESTURE - request.setUserGesture(userGesture); -#endif - if (m_frame->script()->executeIfJavaScriptURL(request.url(), userGesture)) - return; + urlSelected(request, "_self", 0, lockHistory, lockBackForwardList, userGesture, SendReferrer, ReplaceDocumentIfJavaScriptURL); +} - urlSelected(request, "_self", 0, lockHistory, lockBackForwardList, userGesture, SendReferrer); +void FrameLoader::urlSelected(const KURL& url, const String& passedTarget, PassRefPtr<Event> triggeringEvent, bool lockHistory, bool lockBackForwardList, bool userGesture, ReferrerPolicy referrerPolicy) +{ + urlSelected(ResourceRequest(url), passedTarget, triggeringEvent, lockHistory, lockBackForwardList, userGesture, referrerPolicy, DoNotReplaceDocumentIfJavaScriptURL); } -void FrameLoader::urlSelected(const ResourceRequest& request, const String& passedTarget, PassRefPtr<Event> triggeringEvent, bool lockHistory, bool lockBackForwardList, bool userGesture, ReferrerPolicy referrerPolicy) +// The shouldReplaceDocumentIfJavaScriptURL parameter will go away when the FIXME to eliminate the +// corresponding parameter from ScriptController::executeIfJavaScriptURL() is addressed. +void FrameLoader::urlSelected(const ResourceRequest& request, const String& passedTarget, PassRefPtr<Event> triggeringEvent, bool lockHistory, bool lockBackForwardList, bool userGesture, ReferrerPolicy referrerPolicy, ShouldReplaceDocumentIfJavaScriptURL shouldReplaceDocumentIfJavaScriptURL) { ASSERT(!m_suppressOpenerInNewFrame); - if (m_frame->script()->executeIfJavaScriptURL(request.url(), userGesture, false)) + if (m_frame->script()->executeIfJavaScriptURL(request.url(), userGesture, shouldReplaceDocumentIfJavaScriptURL)) return; String target = passedTarget; @@ -366,125 +310,42 @@ void FrameLoader::urlSelected(const ResourceRequest& request, const String& pass m_suppressOpenerInNewFrame = false; } -bool FrameLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName) +void FrameLoader::submitForm(PassRefPtr<FormSubmission> submission) { - // Support for <frame src="javascript:string"> - KURL scriptURL; - KURL url; - if (protocolIsJavaScript(urlString)) { - scriptURL = completeURL(urlString); // completeURL() encodes the URL. - url = blankURL(); - } else - url = completeURL(urlString); - - Frame* frame = ownerElement->contentFrame(); - if (frame) - frame->redirectScheduler()->scheduleLocationChange(url.string(), m_outgoingReferrer, true, true, isProcessingUserGesture()); - else - frame = loadSubframe(ownerElement, url, frameName, m_outgoingReferrer); - - if (!frame) - return false; - - if (!scriptURL.isEmpty()) - frame->script()->executeIfJavaScriptURL(scriptURL); - - return true; -} - -Frame* FrameLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer) -{ - bool allowsScrolling = true; - int marginWidth = -1; - int marginHeight = -1; - if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) { - HTMLFrameElementBase* o = static_cast<HTMLFrameElementBase*>(ownerElement); - allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff; - marginWidth = o->getMarginWidth(); - marginHeight = o->getMarginHeight(); - } - - if (!SecurityOrigin::canLoad(url, referrer, 0)) { - FrameLoader::reportLocalLoadFailed(m_frame, url.string()); - return 0; - } - - bool hideReferrer = SecurityOrigin::shouldHideReferrer(url, referrer); - RefPtr<Frame> frame = m_client->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer, allowsScrolling, marginWidth, marginHeight); + ASSERT(submission->method() == FormSubmission::PostMethod || submission->method() == FormSubmission::GetMethod); - if (!frame) { - checkCallImplicitClose(); - return 0; - } - - // All new frames will have m_isComplete set to true at this point due to synchronously loading - // an empty document in FrameLoader::init(). But many frames will now be starting an - // asynchronous load of url, so we set m_isComplete to false and then check if the load is - // actually completed below. (Note that we set m_isComplete to false even for synchronous - // loads, so that checkCompleted() below won't bail early.) - // FIXME: Can we remove this entirely? m_isComplete normally gets set to false when a load is committed. - frame->loader()->m_isComplete = false; - - RenderObject* renderer = ownerElement->renderer(); - FrameView* view = frame->view(); - if (renderer && renderer->isWidget() && view) - toRenderWidget(renderer)->setWidget(view); - - checkCallImplicitClose(); - - // Some loads are performed synchronously (e.g., about:blank and loads - // cancelled by returning a null ResourceRequest from requestFromDelegate). - // In these cases, the synchronous load would have finished - // before we could connect the signals, so make sure to send the - // completed() signal for the child by hand and mark the load as being - // complete. - // FIXME: In this case the Frame will have finished loading before - // it's being added to the child list. It would be a good idea to - // create the child first, then invoke the loader separately. - if (frame->loader()->state() == FrameStateComplete) - frame->loader()->checkCompleted(); - - return frame.get(); -} - -void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<FormData> formData, - const String& target, const String& contentType, const String& boundary, - bool lockHistory, 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); + // FIXME: Find a good spot for these. + ASSERT(submission->data()); + ASSERT(submission->state()); + ASSERT(submission->state()->sourceFrame() == m_frame); if (!m_frame->page()) return; - KURL u = completeURL(url.isNull() ? "" : url); - if (u.isEmpty()) + if (submission->action().isEmpty()) return; - if (isDocumentSandboxed(SandboxForms)) + if (isDocumentSandboxed(m_frame, SandboxForms)) return; - if (protocolIsJavaScript(u)) { + if (protocolIsJavaScript(submission->action())) { m_isExecutingJavaScriptFormAction = true; - m_frame->script()->executeIfJavaScriptURL(u, false, false); + m_frame->script()->executeIfJavaScriptURL(submission->action(), false, DoNotReplaceDocumentIfJavaScriptURL); m_isExecutingJavaScriptFormAction = false; return; } - FrameLoadRequest frameRequest; -#ifdef ANDROID_USER_GESTURE - frameRequest.resourceRequest().setUserGesture(isProcessingUserGesture()); -#endif - - String targetOrBaseTarget = target.isEmpty() ? m_frame->document()->baseTarget() : target; - Frame* targetFrame = findFrameForNavigation(targetOrBaseTarget); + Frame* targetFrame = m_frame->tree()->find(submission->target()); + if (!shouldAllowNavigation(targetFrame)) + return; if (!targetFrame) { + if (!DOMWindow::allowPopUp(m_frame) && !isProcessingUserGesture()) + return; + targetFrame = m_frame; - frameRequest.setFrameName(targetOrBaseTarget); - } + } else + submission->clearTarget(); + if (!targetFrame->page()) return; @@ -501,39 +362,22 @@ void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<F // 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) + if (m_submittedFormURL == submission->action()) return; - m_submittedFormURL = u; - } - - formData->generateFiles(m_frame->page()->chrome()->client()); - - if (!m_outgoingReferrer.isEmpty()) - frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer); - - if (strcmp(action, "GET") == 0) - u.setQuery(formData->flattenToString()); - else { - frameRequest.resourceRequest().setHTTPMethod("POST"); - frameRequest.resourceRequest().setHTTPBody(formData); - - // construct some user headers if necessary - if (contentType.isNull() || contentType == "application/x-www-form-urlencoded") - frameRequest.resourceRequest().setHTTPContentType(contentType); - else // contentType must be "multipart/form-data" - frameRequest.resourceRequest().setHTTPContentType(contentType + "; boundary=" + boundary); + m_submittedFormURL = submission->action(); } - frameRequest.resourceRequest().setURL(u); - addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin()); + submission->data()->generateFiles(m_frame->document()); + submission->setReferrer(m_outgoingReferrer); + submission->setOrigin(outgoingOrigin()); - targetFrame->redirectScheduler()->scheduleFormSubmission(frameRequest, lockHistory, event, formState); + targetFrame->redirectScheduler()->scheduleFormSubmission(submission); } void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy, DatabasePolicy databasePolicy) { - if (m_frame->document() && m_frame->document()->tokenizer()) - m_frame->document()->tokenizer()->stopParsing(); + if (m_frame->document() && m_frame->document()->parser()) + m_frame->document()->parser()->stopParsing(); if (unloadEventPolicy != UnloadEventPolicyNone) { if (m_frame->document()) { @@ -541,14 +385,23 @@ void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy, DatabasePolic Node* currentFocusedNode = m_frame->document()->focusedNode(); if (currentFocusedNode) currentFocusedNode->aboutToUnload(); - m_unloadEventBeingDispatched = true; + m_pageDismissalEventBeingDispatched = true; if (m_frame->domWindow()) { if (unloadEventPolicy == UnloadEventPolicyUnloadAndPageHide) m_frame->domWindow()->dispatchEvent(PageTransitionEvent::create(eventNames().pagehideEvent, m_frame->document()->inPageCache()), m_frame->document()); - if (!m_frame->document()->inPageCache()) + if (!m_frame->document()->inPageCache()) { m_frame->domWindow()->dispatchEvent(Event::create(eventNames().unloadEvent, false, false), m_frame->domWindow()->document()); + + if (m_provisionalDocumentLoader) { + DocumentLoadTiming* timing = m_provisionalDocumentLoader->timing(); + ASSERT(timing->navigationStart); + ASSERT(!timing->unloadEventEnd); + timing->unloadEventEnd = currentTime(); + ASSERT(timing->unloadEventEnd >= timing->navigationStart); + } + } } - m_unloadEventBeingDispatched = false; + m_pageDismissalEventBeingDispatched = false; if (m_frame->document()) m_frame->document()->updateStyleIfNeeded(); m_wasUnloadEventEmitted = true; @@ -558,7 +411,7 @@ void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy, DatabasePolic // Dispatching the unload event could have made m_frame->document() null. if (m_frame->document() && !m_frame->document()->inPageCache()) { // Don't remove event listeners from a transitional empty document (see bug 28716 for more information). - bool keepEventListeners = m_isDisplayingInitialEmptyDocument && m_provisionalDocumentLoader + bool keepEventListeners = m_stateMachine.isDisplayingInitialEmptyDocument() && m_provisionalDocumentLoader && m_frame->document()->securityOrigin()->isSecureTransitionTo(m_provisionalDocumentLoader->url()); if (!keepEventListeners) @@ -589,10 +442,7 @@ void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy, DatabasePolic #endif } - // tell all subframes to stop as well - for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) - child->loader()->stopLoading(unloadEventPolicy); - + // FIXME: This will cancel redirection timer, which really needs to be restarted when restoring the frame from b/f cache. m_frame->redirectScheduler()->cancel(); } @@ -602,8 +452,8 @@ 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()->tokenizer()) - m_frame->document()->tokenizer()->stopParsing(); + if (m_frame->document()->parser()) + m_frame->document()->parser()->stopParsing(); m_frame->document()->finishParsing(); if (m_iconLoader) @@ -664,9 +514,11 @@ bool FrameLoader::didOpenURL(const KURL& url) // 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()); + if (!m_stateMachine.creatingInitialEmptyDocument()) { + if (DOMWindow* window = m_frame->existingDOMWindow()) { + window->setStatus(String()); + window->setDefaultStatus(String()); + } } m_URL = url; if (m_URL.protocolInHTTPFamily() && !m_URL.host().isEmpty() && m_URL.path().isEmpty()) @@ -684,7 +536,8 @@ void FrameLoader::didExplicitOpen() m_didCallImplicitClose = false; // Calling document.open counts as committing the first real document load. - m_committedFirstRealDocumentLoad = true; + if (!m_stateMachine.committedFirstRealDocumentLoad()) + m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocumentPostCommit); // Prevent window.open(url) -- eg window.open("about:blank") -- from blowing away results // from a subsequent window.document.open / window.document.write call. @@ -707,14 +560,6 @@ void FrameLoader::cancelAndClear() m_frame->script()->updatePlatformScriptObjects(); } -void FrameLoader::replaceDocument(const String& html) -{ - stopAllLoaders(); - begin(m_URL, true, m_frame->document()->securityOrigin()); - write(html); - end(); -} - void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects, bool clearFrameView) { m_frame->editor()->clear(); @@ -737,7 +582,7 @@ void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects, boo // Do this after detaching the document so that the unload event works. if (clearWindowProperties) { m_frame->clearDOMWindow(); - m_frame->script()->clearWindowShell(); + m_frame->script()->clearWindowShell(m_frame->document()->inPageCache()); } m_frame->selection()->clear(); @@ -745,14 +590,12 @@ void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects, boo if (clearFrameView && m_frame->view()) m_frame->view()->clear(); - m_frame->setSelectionGranularity(CharacterGranularity); - // Do not drop the document before the ScriptController and view are cleared // as some destructors might still try to access the document. m_frame->setDocument(0); - m_decoder = 0; + writer()->clear(); - m_containsPlugIns = false; + m_subframeLoader.clear(); if (clearScriptObjects) m_frame->script()->clearScriptObjects(); @@ -763,16 +606,13 @@ void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects, boo m_shouldCallCheckCompleted = false; m_shouldCallCheckLoadComplete = false; - m_receivedData = false; - m_isDisplayingInitialEmptyDocument = false; - - if (!m_encodingWasChosenByUser) - m_encoding = String(); + if (m_stateMachine.isDisplayingInitialEmptyDocument() && m_stateMachine.committedFirstRealDocumentLoad()) + m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad); } void FrameLoader::receivedFirstData() { - begin(m_workingURL, false); + writer()->begin(m_workingURL, false); dispatchDidCommitLoad(); dispatchDidClearWindowObjectsInAllWorlds(); @@ -803,193 +643,51 @@ void FrameLoader::receivedFirstData() m_frame->redirectScheduler()->scheduleRedirect(delay, url); } -const String& FrameLoader::responseMIMEType() const -{ - return m_responseMIMEType; -} - -void FrameLoader::setResponseMIMEType(const String& type) -{ - m_responseMIMEType = type; -} - -void FrameLoader::begin() -{ - begin(KURL()); -} - -void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin) +void FrameLoader::setURL(const KURL& url) { - // We need to take a reference to the security origin because |clear| - // might destroy the document that owns it. - RefPtr<SecurityOrigin> forcedSecurityOrigin = origin; - - 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 if (!m_client->hasHTMLView()) - document = PlaceholderDocument::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(); - - m_needsClear = true; - m_isComplete = false; - m_didCallImplicitClose = false; - m_isLoadingMainResource = true; - m_isDisplayingInitialEmptyDocument = m_creatingInitialEmptyDocument; - KURL ref(url); ref.setUser(String()); ref.setPass(String()); ref.removeFragmentIdentifier(); m_outgoingReferrer = ref.string(); m_URL = url; +} - document->setURL(m_URL); - m_frame->setDocument(document); +void FrameLoader::didBeginDocument(bool dispatch) +{ + m_needsClear = true; + m_isComplete = false; + m_didCallImplicitClose = false; + m_isLoadingMainResource = true; if (m_pendingStateObject) { - document->statePopped(m_pendingStateObject.get()); + m_frame->document()->statePopped(m_pendingStateObject.get()); m_pendingStateObject.clear(); } - - if (m_decoder) - document->setDecoder(m_decoder.get()); - if (forcedSecurityOrigin) - document->setSecurityOrigin(forcedSecurityOrigin.get()); - - m_frame->domWindow()->setURL(document->url()); - m_frame->domWindow()->setSecurityOrigin(document->securityOrigin()); if (dispatch) dispatchDidClearWindowObjectsInAllWorlds(); - + updateFirstPartyForCookies(); - Settings* settings = document->settings(); - document->docLoader()->setAutoLoadImages(settings && settings->loadsImagesAutomatically()); + Settings* settings = m_frame->document()->settings(); + m_frame->document()->docLoader()->setAutoLoadImages(settings && settings->loadsImagesAutomatically()); #ifdef ANDROID_BLOCK_NETWORK_IMAGE - document->docLoader()->setBlockNetworkImage(settings && settings->blockNetworkImage()); + m_frame->document()->docLoader()->setBlockNetworkImage(settings && settings->blockNetworkImage()); #endif if (m_documentLoader) { String dnsPrefetchControl = m_documentLoader->response().httpHeaderField("X-DNS-Prefetch-Control"); if (!dnsPrefetchControl.isEmpty()) - document->parseDNSPrefetchControlHeader(dnsPrefetchControl); + m_frame->document()->parseDNSPrefetchControlHeader(dnsPrefetchControl); } history()->restoreDocumentState(); - - document->implicitOpen(); - - if (m_frame->view() && m_client->hasHTMLView()) - m_frame->view()->setContentsSize(IntSize()); -} - -void FrameLoader::write(const char* str, int len, bool flush) -{ - if (len == 0 && !flush) - return; - - if (len == -1) - len = strlen(str); - - Tokenizer* tokenizer = m_frame->document()->tokenizer(); - if (tokenizer && tokenizer->wantsRawData()) { - if (len > 0) - tokenizer->writeRawData(str, len); - return; - } - - if (!m_decoder) { - if (Settings* settings = m_frame->settings()) { - m_decoder = TextResourceDecoder::create(m_responseMIMEType, - settings->defaultTextEncodingName(), - settings->usesEncodingDetector()); - Frame* parentFrame = m_frame->tree()->parent(); - // 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); - } - m_frame->document()->setDecoder(m_decoder.get()); - } - - String decoded = m_decoder->decode(str, len); - if (flush) - decoded += m_decoder->flush(); - if (decoded.isEmpty()) - return; - - if (!m_receivedData) { - m_receivedData = true; - if (m_decoder->encoding().usesVisualOrdering()) - m_frame->document()->setVisuallyOrdered(); - m_frame->document()->recalcStyle(Node::Force); - } - - if (tokenizer) { - ASSERT(!tokenizer->wantsRawData()); - tokenizer->write(decoded, true); - } -} - -void FrameLoader::write(const String& str) -{ - if (str.isNull()) - return; - - if (!m_receivedData) { - m_receivedData = true; - m_frame->document()->setParseMode(Document::Strict); - } - - if (Tokenizer* tokenizer = m_frame->document()->tokenizer()) - tokenizer->write(str, true); } -void FrameLoader::end() +void FrameLoader::didEndDocument() { m_isLoadingMainResource = false; - endIfNotLoadingMainResource(); -} - -void FrameLoader::endIfNotLoadingMainResource() -{ - if (m_isLoadingMainResource || !m_frame->page() || !m_frame->document()) - return; - - // http://bugs.webkit.org/show_bug.cgi?id=10854 - // The frame's last ref may be removed and it can be deleted by checkCompleted(), - // so we'll add a protective refcount - RefPtr<Frame> protector(m_frame); - - // make sure nothing's left in there - write(0, 0, true); - m_frame->document()->finishParsing(); } void FrameLoader::iconLoadDecisionAvailable() @@ -1059,7 +757,7 @@ void FrameLoader::startIconLoader() // This is either a reload or the icon database said "yes, load the icon", so kick off the load! if (!m_iconLoader) - m_iconLoader.set(IconLoader::create(m_frame).release()); + m_iconLoader = IconLoader::create(m_frame); m_iconLoader->startLoading(); } @@ -1074,7 +772,7 @@ void FrameLoader::commitIconURLToIconDatabase(const KURL& icon) void FrameLoader::finishedParsing() { - if (m_creatingInitialEmptyDocument) + if (m_stateMachine.creatingInitialEmptyDocument()) return; m_frame->injectUserScripts(InjectAtDocumentEnd); @@ -1245,11 +943,7 @@ void FrameLoader::loadURLIntoChildFrame(const KURL& url, const String& referer, childFrame->loader()->loadArchive(subframeArchive.release()); else #endif -#ifdef ANDROID_USER_GESTURE - childFrame->loader()->loadURL(workingURL, referer, String(), false, childLoadType, 0, 0, false); -#else childFrame->loader()->loadURL(workingURL, referer, String(), false, childLoadType, 0, 0); -#endif } #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size @@ -1275,74 +969,6 @@ void FrameLoader::loadArchive(PassRefPtr<Archive> prpArchive) } #endif -String FrameLoader::encoding() const -{ - if (m_encodingWasChosenByUser && !m_encoding.isEmpty()) - return m_encoding; - if (m_decoder && m_decoder->encoding().isValid()) - return m_decoder->encoding().name(); - Settings* settings = m_frame->settings(); - return settings ? settings->defaultTextEncodingName() : String(); -} - -bool FrameLoader::requestObject(RenderPart* renderer, const String& url, const AtomicString& frameName, - const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues) -{ - if (url.isEmpty() && mimeType.isEmpty()) - return false; - - if (!m_frame->script()->xssAuditor()->canLoadObject(url)) { - // It is unsafe to honor the request for this object. - return false; - } - - KURL completedURL; - if (!url.isEmpty()) - completedURL = completeURL(url); - - bool useFallback; - if (shouldUsePlugin(completedURL, mimeType, renderer->hasFallbackContent(), useFallback)) { - Settings* settings = m_frame->settings(); - if (!m_client->allowPlugins(settings && settings->arePluginsEnabled()) - || (!settings->isJavaEnabled() && MIMETypeRegistry::isJavaAppletMIMEType(mimeType))) - return false; - if (isDocumentSandboxed(SandboxPlugins)) - return false; - return loadPlugin(renderer, completedURL, mimeType, paramNames, paramValues, useFallback); - } - - ASSERT(renderer->node()->hasTagName(objectTag) || renderer->node()->hasTagName(embedTag)); - HTMLPlugInElement* element = static_cast<HTMLPlugInElement*>(renderer->node()); - - // If the plug-in element already contains a subframe, requestFrame will re-use it. Otherwise, - // it will create a new frame and set it as the RenderPart's widget, causing what was previously - // in the widget to be torn down. - return requestFrame(element, completedURL, frameName); -} - -bool FrameLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool hasFallback, bool& useFallback) -{ - if (m_client->shouldUsePluginDocument(mimeType)) { - useFallback = false; - return true; - } - - // Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that - // can handle TIFF (which QuickTime can also handle) they probably intended to override QT. - if (m_frame->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) { - const PluginData* pluginData = m_frame->page()->pluginData(); - String pluginName = pluginData ? pluginData->pluginNameForMimeType(mimeType) : String(); - if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false)) - return true; - } - - ObjectContentType objectType = m_client->objectContentType(url, mimeType); - // If an object's content can't be handled and it has no fallback, let - // it be handled as a plugin to show the broken plugin icon. - useFallback = objectType == ObjectContentNone && hasFallback; - return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin; -} - ObjectContentType FrameLoader::defaultObjectContentType(const KURL& url, const String& mimeTypeIn) { String mimeType = mimeTypeIn; @@ -1356,7 +982,7 @@ ObjectContentType FrameLoader::defaultObjectContentType(const KURL& url, const S if (MIMETypeRegistry::isSupportedImageMIMEType(mimeType)) return WebCore::ObjectContentImage; -#if !PLATFORM(MAC) && !PLATFORM(CHROMIUM) // Mac has no PluginDatabase, nor does Chromium +#if !PLATFORM(MAC) && !PLATFORM(CHROMIUM) && !PLATFORM(EFL) // Mac has no PluginDatabase, nor does Chromium or EFL if (PluginDatabase::installedPlugins()->isMIMETypeRegistered(mimeType)) return WebCore::ObjectContentNetscapePlugin; #endif @@ -1367,50 +993,6 @@ ObjectContentType FrameLoader::defaultObjectContentType(const KURL& url, const S return WebCore::ObjectContentNone; } -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) -{ - RefPtr<Widget> widget; - - if (renderer && !useFallback) { - HTMLPlugInElement* element = toPlugInElement(renderer->node()); - - if (!SecurityOrigin::canLoad(url, String(), frame()->document())) { - FrameLoader::reportLocalLoadFailed(m_frame, url.string()); - return false; - } - - checkIfRunInsecureContent(m_frame->document()->securityOrigin(), url); - - 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; - } - } - - return widget != 0; -} - String FrameLoader::outgoingReferrer() const { return m_outgoingReferrer; @@ -1426,7 +1008,7 @@ bool FrameLoader::isMixedContent(SecurityOrigin* context, const KURL& url) if (context->protocol() != "https") return false; // We only care about HTTPS security origins. - if (!url.isValid() || url.protocolIs("https") || url.protocolIs("about") || url.protocolIs("data")) + if (!url.isValid() || SchemeRegistry::shouldTreatURLSchemeAsSecure(url.protocol())) return false; // Loading these protocols is secure. return true; @@ -1489,7 +1071,8 @@ void FrameLoader::provisionalLoadStarted() if (!m_frame->tree()->parent()) android::TimeCounter::reset(); #endif - m_firstLayoutDone = false; + if (m_stateMachine.firstLayoutDone()) + m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad); m_frame->redirectScheduler()->cancel(true); m_client->provisionalLoadStarted(); } @@ -1497,9 +1080,9 @@ void FrameLoader::provisionalLoadStarted() bool FrameLoader::isProcessingUserGesture() { Frame* frame = m_frame->tree()->top(); - if (!frame->script()->canExecuteScripts()) + if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript)) return true; // If JavaScript is disabled, a user gesture must have initiated the navigation. - return frame->script()->processingUserGesture(mainThreadNormalWorld()); // FIXME: Use pageIsProcessingUserGesture. + return ScriptController::processingUserGesture(); // FIXME: Use pageIsProcessingUserGesture. } void FrameLoader::resetMultipleFormSubmissionProtection() @@ -1507,12 +1090,10 @@ void FrameLoader::resetMultipleFormSubmissionProtection() m_submittedFormURL = KURL(); } -void FrameLoader::setEncoding(const String& name, bool userChosen) +void FrameLoader::willSetEncoding() { if (!m_workingURL.isEmpty()) receivedFirstData(); - m_encoding = name; - m_encodingWasChosenByUser = userChosen; } void FrameLoader::addData(const char* bytes, int length) @@ -1520,7 +1101,7 @@ void FrameLoader::addData(const char* bytes, int length) ASSERT(m_workingURL.isEmpty()); ASSERT(m_frame->document()); ASSERT(m_frame->document()->parsing()); - write(bytes, length); + writer()->addData(bytes, length); } #if ENABLE(WML) @@ -1534,208 +1115,6 @@ static inline bool frameContainsWMLContent(Frame* frame) } #endif -bool FrameLoader::canCachePageContainingThisFrame() -{ - for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) { - if (!child->loader()->canCachePageContainingThisFrame()) - return false; - } - - return m_documentLoader - && m_documentLoader->mainDocumentError().isNull() - // 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->domWindow() || !m_frame->domWindow()->hasEventListeners(eventNames().unloadEvent)) -#if ENABLE(DATABASE) - && !m_frame->document()->hasOpenDatabases() -#endif -#if ENABLE(SHARED_WORKERS) - && !SharedWorkerRepository::hasSharedWorkers(m_frame->document()) -#endif - && !m_frame->document()->usingGeolocation() - && history()->currentItem() - && !m_quickRedirectComing - && !m_documentLoader->isLoadingInAPISense() - && !m_documentLoader->isStopping() - && m_frame->document()->canSuspendActiveDOMObjects() -#if ENABLE(OFFLINE_WEB_APPLICATIONS) - // FIXME: We should investigating caching frames that have an associated - // application cache. <rdar://problem/5917899> tracks that work. - && m_documentLoader->applicationCacheHost()->canCacheInPageCache() -#endif -#if ENABLE(WML) - && !frameContainsWMLContent(m_frame) -#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 - ; -} - -#ifndef NDEBUG -static String& pageCacheLogPrefix(int indentLevel) -{ - 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_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()->hasEventListeners(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 ENABLE(SHARED_WORKERS) - if (SharedWorkerRepository::hasSharedWorkers(m_frame->document())) - { PCLOG(" -Frame has associated SharedWorkers"); cannotCache = true; } -#endif - if (m_frame->document()->usingGeolocation()) - { PCLOG(" -Frame uses Geolocation"); cannotCache = true; } - if (!history()->currentItem()) - { 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->applicationCacheHost()->canCacheInPageCache()) - { PCLOG(" -The DocumentLoader uses an application cache"); 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()) @@ -1776,7 +1155,10 @@ void FrameLoader::loadInSameDocument(const KURL& url, SerializedScriptValue* sta history()->updateBackForwardListForFragmentScroll(); } + String oldURL; bool hashChange = equalIgnoringFragmentIdentifier(url, m_URL) && url.fragmentIdentifier() != m_URL.fragmentIdentifier(); + oldURL = m_URL; + m_URL = url; history()->updateForSameDocumentNavigation(); @@ -1787,11 +1169,11 @@ void FrameLoader::loadInSameDocument(const KURL& url, SerializedScriptValue* sta // It's important to model this as a load that starts and immediately finishes. // Otherwise, the parent frame may think we never finished loading. started(); - - if (hashChange) { - if (FrameView* view = m_frame->view()) - view->scrollToFragment(m_URL); - } + + // We need to scroll to the fragment whether or not a hash change occurred, since + // the user might have scrolled since the previous navigation. + if (FrameView* view = m_frame->view()) + view->scrollToFragment(m_URL); m_isComplete = false; checkCompleted(); @@ -1803,13 +1185,15 @@ void FrameLoader::loadInSameDocument(const KURL& url, SerializedScriptValue* sta checkLoadComplete(); } + m_client->dispatchDidNavigateWithinPage(); + if (stateObject) { m_frame->document()->statePopped(stateObject); m_client->dispatchDidPopStateWithinPage(); } if (hashChange) { - m_frame->document()->dispatchWindowEvent(Event::create(eventNames().hashchangeEvent, false, false)); + m_frame->document()->enqueueHashchangeEvent(oldURL, url); m_client->dispatchDidChangeLocationWithinPage(); } @@ -1842,11 +1226,6 @@ void FrameLoader::started() frame->loader()->m_isComplete = false; } -bool FrameLoader::containsPlugins() const -{ - return m_containsPlugIns; -} - void FrameLoader::prepareForLoadStart() { if (Page* page = m_frame->page()) @@ -1895,7 +1274,7 @@ void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, bool lockHis referrer = m_outgoingReferrer; ASSERT(frame()->document()); - if (SecurityOrigin::shouldTreatURLAsLocal(url.string()) && !isFeedWithNestedProtocolInHTTPFamily(url)) { + if (SchemeRegistry::shouldTreatURLAsLocal(url.string()) && !isFeedWithNestedProtocolInHTTPFamily(url)) { if (!SecurityOrigin::canLoad(url, String(), frame()->document()) && !SecurityOrigin::canLoad(url, referrer, 0)) { FrameLoader::reportLocalLoadFailed(m_frame, url.string()); return; @@ -1913,17 +1292,10 @@ void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, bool lockHis else loadType = FrameLoadTypeStandard; -#ifdef ANDROID_USER_GESTURE - if (request.resourceRequest().httpMethod() == "POST") - loadPostRequest(request.resourceRequest(), referrer, request.frameName(), lockHistory, loadType, event, formState.get(), request.resourceRequest().getUserGesture()); - else - loadURL(request.resourceRequest().url(), referrer, request.frameName(), lockHistory, loadType, event, formState.get(), request.resourceRequest().getUserGesture()); -#else 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()); -#endif // FIXME: It's possible this targetFrame will not be the same frame that was targeted by the actual // load if frame names have changed. @@ -1931,29 +1303,17 @@ void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, bool lockHis Frame* targetFrame = sourceFrame->loader()->findFrameForNavigation(request.frameName()); if (targetFrame && targetFrame != sourceFrame) { if (Page* page = targetFrame->page()) -#ifdef ANDROID_USER_GESTURE - page->chrome()->focus(request.resourceRequest().getUserGesture()); -#else page->chrome()->focus(); -#endif } } -#ifdef ANDROID_USER_GESTURE -void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType newLoadType, - PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState, bool userGesture) -#else void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType newLoadType, PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState) -#endif { RefPtr<FormState> formState = prpFormState; bool isFormSubmission = formState; ResourceRequest request(newURL); -#ifdef ANDROID_USER_GESTURE - request.setUserGesture(userGesture); -#endif if (!referrer.isEmpty()) { request.setHTTPReferrer(referrer); RefPtr<SecurityOrigin> referrerOrigin = SecurityOrigin::createFromString(referrer); @@ -1968,15 +1328,11 @@ void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const Stri // 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) { -#ifdef ANDROID_USER_GESTURE - targetFrame->loader()->loadURL(newURL, referrer, String(), lockHistory, newLoadType, event, formState.release(), userGesture); -#else targetFrame->loader()->loadURL(newURL, referrer, String(), lockHistory, newLoadType, event, formState.release()); -#endif return; } - if (m_unloadEventBeingDispatched) + if (m_pageDismissalEventBeingDispatched) return; NavigationAction action(newURL, newLoadType, isFormSubmission, event); @@ -2102,7 +1458,7 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t ASSERT(m_frame->view()); - if (m_unloadEventBeingDispatched) + if (m_pageDismissalEventBeingDispatched) return; policyChecker()->setLoadType(type); @@ -2264,6 +1620,7 @@ static bool canAccessAncestor(const SecurityOrigin* activeSecurityOrigin, Frame* if (!targetFrame) return false; + const bool isLocalActiveOrigin = activeSecurityOrigin->isLocal(); for (Frame* ancestorFrame = targetFrame; ancestorFrame; ancestorFrame = ancestorFrame->tree()->parent()) { Document* ancestorDocument = ancestorFrame->document(); if (!ancestorDocument) @@ -2272,6 +1629,10 @@ static bool canAccessAncestor(const SecurityOrigin* activeSecurityOrigin, Frame* const SecurityOrigin* ancestorSecurityOrigin = ancestorDocument->securityOrigin(); if (activeSecurityOrigin->canAccess(ancestorSecurityOrigin)) return true; + + // Allow file URL descendant navigation even when allowFileAccessFromFileURLs is false. + if (isLocalActiveOrigin && ancestorSecurityOrigin->isLocal()) + return true; } return false; @@ -2295,16 +1656,16 @@ bool FrameLoader::shouldAllowNavigation(Frame* targetFrame) const if (m_frame == targetFrame) return true; - // A sandboxed frame can only navigate itself and its descendants. - if (isDocumentSandboxed(SandboxNavigation) && !targetFrame->tree()->isDescendantOf(m_frame)) - return false; - // Let a frame navigate the top-level window that contains it. This is // important to allow because it lets a site "frame-bust" (escape from a // frame created by another web site). - if (targetFrame == m_frame->tree()->top()) + if (!isDocumentSandboxed(m_frame, SandboxTopNavigation) && targetFrame == m_frame->tree()->top()) return true; + // A sandboxed frame can only navigate itself and its descendants. + if (isDocumentSandboxed(m_frame, SandboxNavigation) && !targetFrame->tree()->isDescendantOf(m_frame)) + return false; + // Let a frame navigate its opener if the opener is a top-level window. if (!targetFrame->tree()->parent() && m_frame->loader()->opener() == targetFrame) return true; @@ -2344,7 +1705,7 @@ void FrameLoader::stopLoadingSubframes() void FrameLoader::stopAllLoaders(DatabasePolicy databasePolicy) { ASSERT(!m_frame->document() || !m_frame->document()->inPageCache()); - if (m_unloadEventBeingDispatched) + if (m_pageDismissalEventBeingDispatched) return; // If this method is called from within this method, infinite recursion can occur (3442218). Avoid this. @@ -2400,7 +1761,7 @@ bool FrameLoader::isLoading() const bool FrameLoader::frameHasLoaded() const { - return m_committedFirstRealDocumentLoad || (m_provisionalDocumentLoader && !m_creatingInitialEmptyDocument); + return m_stateMachine.committedFirstRealDocumentLoad() || (m_provisionalDocumentLoader && !m_stateMachine.creatingInitialEmptyDocument()); } void FrameLoader::setDocumentLoader(DocumentLoader* loader) @@ -2478,9 +1839,9 @@ void FrameLoader::markLoadComplete() setState(FrameStateComplete); } -void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage) +void FrameLoader::commitProvisionalLoad() { - RefPtr<CachedPage> cachedPage = prpCachedPage; + RefPtr<CachedPage> cachedPage = m_loadingFromCachedPage ? pageCache()->get(history()->provisionalItem()) : 0; RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader; LOG(PageCache, "WebCoreLoading %s: About to commit provisional load from previous URL '%s' to new URL '%s'", m_frame->tree()->name().string().utf8().data(), m_URL.string().utf8().data(), @@ -2488,12 +1849,14 @@ void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage) // 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. - cachePageForHistoryItem(history()->currentItem()); + HistoryItem* item = history()->currentItem(); + if (!m_frame->tree()->parent() && PageCache::canCache(m_frame->page()) && !item->isInPageCache()) + pageCache()->add(item, m_frame->page()); if (m_loadType != FrameLoadTypeReplace) closeOldDataSources(); - if (!cachedPage && !m_creatingInitialEmptyDocument) + if (!cachedPage && !m_stateMachine.creatingInitialEmptyDocument()) m_client->makeRepresentation(pdl.get()); transitionToCommitted(cachedPage); @@ -2505,9 +1868,11 @@ void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage) if (m_sentRedirectNotification) clientRedirectCancelledOrFinished(false); - if (cachedPage && cachedPage->document()) - open(*cachedPage); - else { + if (cachedPage && cachedPage->document()) { + prepareForCachedPageRestore(); + cachedPage->restore(m_frame->page()); + checkCompleted(); + } else { KURL url = pdl->substituteData().responseURL(); if (url.isEmpty()) url = pdl->url(); @@ -2595,18 +1960,24 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) case FrameLoadTypeIndexedBackForward: if (Page* page = m_frame->page()) { if (page->backForwardList()) { + // If the first load within a frame is a navigation within a back/forward list that was attached + // without any of the items being loaded then we need to update the history in a similar manner as + // for a standard load with the exception of updating the back/forward list (<rdar://problem/8091103>). + if (!m_stateMachine.committedFirstRealDocumentLoad()) + history()->updateForStandardLoad(HistoryController::UpdateAllExceptBackForwardList); + history()->updateForBackForwardNavigation(); if (history()->currentItem()) m_pendingStateObject = history()->currentItem()->stateObject(); - + // Create a document view for this document, or used the cached view. if (cachedPage) { DocumentLoader* cachedDocumentLoader = cachedPage->documentLoader(); ASSERT(cachedDocumentLoader); cachedDocumentLoader->setFrame(m_frame); m_client->transitionToCommittedFromCachedFrame(cachedPage->cachedMainFrame()); - + } else m_client->transitionToCommittedForNewPage(); } @@ -2643,15 +2014,16 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) ASSERT_NOT_REACHED(); } - m_responseMIMEType = dl->responseMIMEType(); + writer()->setMIMEType(dl->responseMIMEType()); // Tell the client we've committed this URL. ASSERT(m_frame->view()); - if (m_creatingInitialEmptyDocument) + if (m_stateMachine.creatingInitialEmptyDocument()) return; - - m_committedFirstRealDocumentLoad = true; + + if (!m_stateMachine.committedFirstRealDocumentLoad()) + m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocumentPostCommit); if (!m_client->hasHTMLView()) receivedFirstData(); @@ -2690,7 +2062,7 @@ void FrameLoader::clientRedirected(const KURL& url, double seconds, double fireD // 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. // Loads triggered by JavaScript form submissions never count as quick redirects. - m_quickRedirectComing = lockBackForwardList && m_documentLoader && !m_isExecutingJavaScriptFormAction; + m_quickRedirectComing = (lockBackForwardList || history()->currentItemShouldBeReplaced()) && m_documentLoader && !m_isExecutingJavaScriptFormAction; } bool FrameLoader::shouldReload(const KURL& currentURL, const KURL& destinationURL) @@ -2723,7 +2095,7 @@ void FrameLoader::closeOldDataSources() m_client->setMainFrameDocumentReady(false); // stop giving out the actual DOMDocument to observers } -void FrameLoader::open(CachedPage& cachedPage) +void FrameLoader::prepareForCachedPageRestore() { ASSERT(!m_frame->tree()->parent()); ASSERT(m_frame->page()); @@ -2735,14 +2107,12 @@ void FrameLoader::open(CachedPage& cachedPage) closeURL(); // Delete old status bar messages (if it _was_ activated on last URL). - if (m_frame->script()->canExecuteScripts()) { - m_frame->setJSStatusBarText(String()); - m_frame->setJSDefaultStatusBarText(String()); + if (m_frame->script()->canExecuteScripts(NotAboutToExecuteScript)) { + if (DOMWindow* window = m_frame->existingDOMWindow()) { + window->setStatus(String()); + window->setDefaultStatus(String()); + } } - - cachedPage.restore(m_frame->page()); - - checkCompleted(); } void FrameLoader::open(CachedFrameBase& cachedFrame) @@ -2788,7 +2158,7 @@ void FrameLoader::open(CachedFrameBase& cachedFrame) m_frame->domWindow()->setURL(document->url()); m_frame->domWindow()->setSecurityOrigin(document->securityOrigin()); - m_decoder = document->decoder(); + writer()->setDecoder(document->decoder()); updateFirstPartyForCookies(); @@ -2850,7 +2220,7 @@ void FrameLoader::finishedLoadingDocument(DocumentLoader* loader) { // FIXME: Platforms shouldn't differ here! #if PLATFORM(WIN) || PLATFORM(CHROMIUM) || defined(ANDROID) - if (m_creatingInitialEmptyDocument) + if (m_stateMachine.creatingInitialEmptyDocument()) return; #endif @@ -2881,14 +2251,14 @@ void FrameLoader::finishedLoadingDocument(DocumentLoader* loader) ArchiveResource* mainResource = archive->mainResource(); loader->setParsedArchiveData(mainResource->data()); - m_responseMIMEType = mainResource->mimeType(); + writer()->setMIMEType(mainResource->mimeType()); closeURL(); didOpenURL(mainResource->url()); String userChosenEncoding = documentLoader()->overrideEncoding(); bool encodingIsUserChosen = !userChosenEncoding.isNull(); - setEncoding(encodingIsUserChosen ? userChosenEncoding : mainResource->textEncoding(), encodingIsUserChosen); + writer()->setEncoding(encodingIsUserChosen ? userChosenEncoding : mainResource->textEncoding(), encodingIsUserChosen); ASSERT(m_frame->document()); @@ -2924,6 +2294,9 @@ bool FrameLoader::subframeIsLoading() const documentLoader = childLoader->provisionalDocumentLoader(); if (documentLoader && documentLoader->isLoadingInAPISense()) return true; + documentLoader = childLoader->policyDocumentLoader(); + if (documentLoader) + return true; } return false; } @@ -3046,7 +2419,7 @@ void FrameLoader::checkLoadCompleteForThisFrame() if ((isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadTypeReload || m_loadType == FrameLoadTypeReloadFromOrigin) && page->backForwardList()) history()->restoreScrollPositionAndViewState(); - if (m_creatingInitialEmptyDocument || !m_committedFirstRealDocumentLoad) + if (m_stateMachine.creatingInitialEmptyDocument() || !m_stateMachine.committedFirstRealDocumentLoad()) return; const ResourceError& error = dl->mainDocumentError(); @@ -3103,6 +2476,9 @@ void FrameLoader::continueLoadAfterWillSubmitForm() notifier()->assignIdentifierToInitialRequest(identifier, m_provisionalDocumentLoader.get(), m_provisionalDocumentLoader->originalRequest()); } + ASSERT(!m_provisionalDocumentLoader->timing()->navigationStart); + m_provisionalDocumentLoader->timing()->navigationStart = currentTime(); + if (!m_provisionalDocumentLoader->startLoadingMainResource(identifier)) m_provisionalDocumentLoader->updateLoading(); } @@ -3113,7 +2489,8 @@ void FrameLoader::didFirstLayout() if (isBackForwardLoadType(m_loadType) && page->backForwardList()) history()->restoreScrollPositionAndViewState(); - m_firstLayoutDone = true; + if (m_stateMachine.committedFirstRealDocumentLoad() && !m_stateMachine.isDisplayingInitialEmptyDocument() && !m_stateMachine.firstLayoutDone()) + m_stateMachine.advanceTo(FrameLoaderStateMachine::FirstLayoutDone); m_client->dispatchDidFirstLayout(); } @@ -3132,13 +2509,8 @@ void FrameLoader::frameLoadCompleted() // After a canceled provisional load, firstLayoutDone is false. // Reset it to true if we're displaying a page. - if (m_documentLoader) - m_firstLayoutDone = true; -} - -bool FrameLoader::firstLayoutDone() const -{ - return m_firstLayoutDone; + if (m_documentLoader && m_stateMachine.committedFirstRealDocumentLoad() && !m_stateMachine.isDisplayingInitialEmptyDocument() && !m_stateMachine.firstLayoutDone()) + m_stateMachine.advanceTo(FrameLoaderStateMachine::FirstLayoutDone); } void FrameLoader::detachChildren() @@ -3206,18 +2578,16 @@ String FrameLoader::userAgent(const KURL& url) const return m_client->userAgent(url); } -void FrameLoader::tokenizerProcessedData() -{ - checkCompleted(); -} - void FrameLoader::handledOnloadEvents() { m_client->dispatchDidHandleOnloadEvents(); + + if (documentLoader()) { + documentLoader()->handledOnloadEvents(); #if ENABLE(OFFLINE_WEB_APPLICATIONS) - if (documentLoader()) documentLoader()->applicationCacheHost()->stopDeferringEvents(); #endif + } } void FrameLoader::frameDetached() @@ -3286,14 +2656,30 @@ void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadTyp applyUserAgent(request); - if (loadType == FrameLoadTypeReload) { + // If we inherit cache policy from a main resource, we use the DocumentLoader's + // original request cache policy for two reasons: + // 1. For POST requests, we mutate the cache policy for the main resource, + // but we do not want this to apply to subresources + // 2. Delegates that modify the cache policy using willSendRequest: should + // not affect any other resources. Such changes need to be done + // per request. + if (!mainResource) { + if (request.isConditional()) + request.setCachePolicy(ReloadIgnoringCacheData); + else if (documentLoader()->isLoadingInAPISense()) + request.setCachePolicy(documentLoader()->originalRequest().cachePolicy()); + else + request.setCachePolicy(UseProtocolCachePolicy); + } else if (loadType == FrameLoadTypeReload) { request.setCachePolicy(ReloadIgnoringCacheData); request.setHTTPHeaderField("Cache-Control", "max-age=0"); } else if (loadType == FrameLoadTypeReloadFromOrigin) { request.setCachePolicy(ReloadIgnoringCacheData); request.setHTTPHeaderField("Cache-Control", "no-cache"); request.setHTTPHeaderField("Pragma", "no-cache"); - } else if (isBackForwardLoadType(loadType) && !request.url().protocolIs("https")) + } else if (request.isConditional()) + request.setCachePolicy(ReloadIgnoringCacheData); + else if (isBackForwardLoadType(loadType) && m_stateMachine.committedFirstRealDocumentLoad() && !request.url().protocolIs("https")) request.setCachePolicy(ReturnCacheDataElseLoad); if (mainResource) @@ -3305,7 +2691,7 @@ void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadTyp // Always try UTF-8. If that fails, try frame encoding (if any) and then the default. // For a newly opened frame with an empty URL, encoding() should not be used, because this methods asks decoder, which uses ISO-8859-1. Settings* settings = m_frame->settings(); - request.setResponseContentDispositionEncodingFallbackArray("UTF-8", m_URL.isEmpty() ? m_encoding : encoding(), settings ? settings->defaultTextEncodingName() : String()); + request.setResponseContentDispositionEncodingFallbackArray("UTF-8", writer()->deprecatedFrameEncoding(), settings ? settings->defaultTextEncodingName() : String()); } void FrameLoader::addHTTPOriginIfNeeded(ResourceRequest& request, String origin) @@ -3343,11 +2729,7 @@ void FrameLoader::committedLoad(DocumentLoader* loader, const char* data, int le m_client->committedLoad(loader, data, length); } -#ifdef ANDROID_USER_GESTURE -void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType loadType, PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState, bool userGesture) -#else void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType loadType, PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState) -#endif { RefPtr<FormState> formState = prpFormState; @@ -3367,9 +2749,6 @@ void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String String origin = inRequest.httpOrigin(); ResourceRequest workingResourceRequest(url); -#ifdef ANDROID_USER_GESTURE - workingResourceRequest.setUserGesture(userGesture); -#endif if (!referrer.isEmpty()) workingResourceRequest.setHTTPReferrer(referrer); @@ -3400,17 +2779,6 @@ unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& requ ResourceRequest initialRequest = request; initialRequest.setTimeoutInterval(10); - // Use the original request's cache policy for two reasons: - // 1. For POST requests, we mutate the cache policy for the main resource, - // but we do not want this to apply to subresources - // 2. Delegates that modify the cache policy using willSendRequest: should - // not affect any other resources. Such changes need to be done - // per request. - if (initialRequest.isConditional()) - initialRequest.setCachePolicy(ReloadIgnoringCacheData); - else - initialRequest.setCachePolicy(originalRequest().cachePolicy()); - if (!referrer.isEmpty()) initialRequest.setHTTPReferrer(referrer); addHTTPOriginIfNeeded(initialRequest, outgoingOrigin()); @@ -3418,6 +2786,8 @@ unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& requ if (Page* page = m_frame->page()) initialRequest.setFirstPartyForCookies(page->mainFrame()->loader()->documentLoader()->request().url()); initialRequest.setHTTPUserAgent(client()->userAgent(request.url())); + + addExtraFieldsToSubresourceRequest(initialRequest); unsigned long identifier = 0; ResourceRequest newRequest(initialRequest); @@ -3526,6 +2896,36 @@ void FrameLoader::callContinueLoadAfterNavigationPolicy(void* argument, loader->continueLoadAfterNavigationPolicy(request, formState, shouldContinue); } +bool FrameLoader::shouldClose() +{ + Page* page = m_frame->page(); + Chrome* chrome = page ? page->chrome() : 0; + if (!chrome || !chrome->canRunBeforeUnloadConfirmPanel()) + return true; + + DOMWindow* domWindow = m_frame->existingDOMWindow(); + if (!domWindow) + return true; + + RefPtr<Document> document = m_frame->document(); + HTMLElement* body = document->body(); + if (!body) + return true; + + RefPtr<BeforeUnloadEvent> beforeUnloadEvent = BeforeUnloadEvent::create(); + m_pageDismissalEventBeingDispatched = true; + domWindow->dispatchEvent(beforeUnloadEvent.get(), domWindow->document()); + m_pageDismissalEventBeingDispatched = false; + + if (!beforeUnloadEvent->defaultPrevented()) + document->defaultEventHandler(beforeUnloadEvent.get()); + if (beforeUnloadEvent->result().isNull()) + return true; + + String text = document->displayStringModifiedByEncoding(beforeUnloadEvent->result()); + return chrome->runBeforeUnloadConfirmPanel(text, m_frame); +} + void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState> formState, bool shouldContinue) { // If we loaded an alternate page to replace an unreachableURL, we'll get in here with a @@ -3540,7 +2940,7 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, Pass // is the user responding Cancel to the form repost nag sheet. // 2) User responded Cancel to an alert popped up by the before unload event handler. // The "before unload" event handler runs only for the main frame. - bool canContinue = shouldContinue && (!isLoadingMainFrame() || m_frame->shouldClose()); + bool canContinue = shouldContinue && (!isLoadingMainFrame() || shouldClose()); if (!canContinue) { // If we were waiting for a quick redirect, but the policy delegate decided to ignore it, then we @@ -3576,7 +2976,7 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, Pass #if ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR) && USE(JSC) if (Page* page = m_frame->page()) { if (page->mainFrame() == m_frame) - page->inspectorController()->resumeDebugger(); + page->inspectorController()->resume(); } #endif @@ -3586,8 +2986,10 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, Pass setPolicyDocumentLoader(0); - if (isBackForwardLoadType(type) && loadProvisionalItemFromCachedPage()) + if (isBackForwardLoadType(type) && history()->provisionalItem()->isInPageCache()) { + loadProvisionalItemFromCachedPage(); return; + } if (formState) m_client->dispatchWillSubmitForm(&PolicyChecker::continueLoadAfterWillSubmitForm, formState); @@ -3650,14 +3052,13 @@ void FrameLoader::loadedResourceFromMemoryCache(const CachedResource* resource) if (!page) return; -#if ENABLE(INSPECTOR) - page->inspectorController()->didLoadResourceFromMemoryCache(m_documentLoader.get(), resource); -#endif - if (!resource->sendResourceLoadCallbacks() || m_documentLoader->haveToldClientAboutLoad(resource->url())) return; if (!page->areMemoryCacheClientCallsEnabled()) { +#if ENABLE(INSPECTOR) + page->inspectorController()->didLoadResourceFromMemoryCache(m_documentLoader.get(), resource); +#endif m_documentLoader->recordMemoryCacheLoadForFutureClientNotification(resource->url()); m_documentLoader->didTellClientAboutLoad(resource->url()); return; @@ -3665,6 +3066,9 @@ void FrameLoader::loadedResourceFromMemoryCache(const CachedResource* resource) ResourceRequest request(resource->url()); if (m_client->dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), request, resource->response(), resource->encodedSize())) { +#if ENABLE(INSPECTOR) + page->inspectorController()->didLoadResourceFromMemoryCache(m_documentLoader.get(), resource); +#endif m_documentLoader->didTellClientAboutLoad(resource->url()); return; } @@ -3700,48 +3104,22 @@ bool FrameLoader::shouldInterruptLoadForXFrameOptions(const String& content, con return false; } -bool FrameLoader::loadProvisionalItemFromCachedPage() +void FrameLoader::loadProvisionalItemFromCachedPage() { - RefPtr<CachedPage> cachedPage = pageCache()->get(history()->provisionalItem()); - if (!cachedPage || !cachedPage->document()) - return false; + DocumentLoader* provisionalLoader = provisionalDocumentLoader(); + LOG(PageCache, "WebCorePageCache: Loading provisional DocumentLoader %p with URL '%s' from CachedPage", provisionalDocumentLoader(), provisionalDocumentLoader()->url().string().utf8().data()); - DocumentLoader *provisionalLoader = provisionalDocumentLoader(); - LOG(PageCache, "WebCorePageCache: FrameLoader %p loading provisional DocumentLoader %p with URL '%s' from CachedPage %p", this, provisionalLoader, provisionalLoader->url().string().utf8().data(), cachedPage.get()); - provisionalLoader->prepareForLoadStart(); m_loadingFromCachedPage = true; - - provisionalLoader->setCommitted(true); - commitProvisionalLoad(cachedPage); - - return true; -} - -void FrameLoader::cachePageForHistoryItem(HistoryItem* item) -{ - if (!canCachePage() || item->isInPageCache()) - return; - - pageHidden(); - if (Page* page = m_frame->page()) { - RefPtr<CachedPage> cachedPage = CachedPage::create(page); - pageCache()->add(item, cachedPage.release()); - } -} - -void FrameLoader::pageHidden() -{ - m_unloadEventBeingDispatched = true; - if (m_frame->domWindow()) - m_frame->domWindow()->dispatchEvent(PageTransitionEvent::create(eventNames().pagehideEvent, true), m_frame->document()); - m_unloadEventBeingDispatched = false; + // Should have timing data from previous time(s) the page was shown. + ASSERT(provisionalLoader->timing()->navigationStart); + provisionalLoader->resetTiming(); + provisionalLoader->timing()->navigationStart = currentTime(); - // Send pagehide event for subframes as well - for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) - child->loader()->pageHidden(); + provisionalLoader->setCommitted(true); + commitProvisionalLoad(); } bool FrameLoader::shouldTreatURLAsSameAsCurrent(const KURL& url) const @@ -3767,7 +3145,7 @@ Frame* FrameLoader::findFrameForNavigation(const AtomicString& name) { Frame* frame = m_frame->tree()->find(name); if (!shouldAllowNavigation(frame)) - return 0; + return 0; return frame; } @@ -3797,21 +3175,10 @@ void FrameLoader::navigateToDifferentDocument(HistoryItem* item, FrameLoadType l { // Remember this item so we can traverse any child items as child frames load history()->setProvisionalItem(item); - - // Check if we'll be using the page cache. We only use the page cache - // if one exists and it is less than _backForwardCacheExpirationInterval - // seconds old. If the cache is expired it gets flushed here. - if (RefPtr<CachedPage> cachedPage = pageCache()->get(item)) { - // FIXME: 1800 should not be hardcoded, it should come from - // WebKitBackForwardCacheExpirationIntervalKey in WebKit. - // Or we should remove WebKitBackForwardCacheExpirationIntervalKey. - if (currentTime() - cachedPage->timeStamp() <= 1800) { - loadWithDocumentLoader(cachedPage->documentLoader(), loadType, 0); - return; - } - - LOG(PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", history()->provisionalItem()->url().string().ascii().data()); - pageCache()->remove(item); + + if (CachedPage* cachedPage = pageCache()->get(item)) { + loadWithDocumentLoader(cachedPage->documentLoader(), loadType, 0); + return; } KURL itemURL = item->url(); @@ -3830,7 +3197,7 @@ void FrameLoader::navigateToDifferentDocument(HistoryItem* item, FrameLoadType l // If this was a repost that failed the page cache, we might try to repost the form. NavigationAction action; if (formData) { - formData->generateFiles(m_frame->page()->chrome()->client()); + formData->generateFiles(m_frame->document()); request.setHTTPMethod("POST"); request.setHTTPBody(formData); @@ -3867,7 +3234,9 @@ void FrameLoader::navigateToDifferentDocument(HistoryItem* item, FrameLoadType l case FrameLoadTypeBackWMLDeckNotAccessible: case FrameLoadTypeForward: case FrameLoadTypeIndexedBackForward: - if (!itemURL.protocolIs("https")) + // If the first load within a frame is a navigation within a back/forward list that was attached + // without any of the items being loaded then we should use the default caching policy (<rdar://problem/8131355>). + if (m_stateMachine.committedFirstRealDocumentLoad() && !itemURL.protocolIs("https")) request.setCachePolicy(ReturnCacheDataElseLoad); break; case FrameLoadTypeStandard: @@ -3891,12 +3260,11 @@ void FrameLoader::navigateToDifferentDocument(HistoryItem* item, FrameLoadType l void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) { // We do same-document navigation in the following cases: - // - The HistoryItem has a history state object - // - Navigating to an anchor within the page, with no form data stored on the target item or the current history entry, - // and the URLs in the frame tree match the history item for fragment scrolling. + // - The HistoryItem corresponds to the same document. + // - The HistoryItem is not the same as the current item. HistoryItem* currentItem = history()->currentItem(); - bool sameDocumentNavigation = (!item->formData() && !(currentItem && currentItem->formData()) && history()->urlsMatchItem(item)) - || (currentItem && item->documentSequenceNumber() == currentItem->documentSequenceNumber()); + bool sameDocumentNavigation = currentItem && item != currentItem + && item->documentSequenceNumber() == currentItem->documentSequenceNumber(); #if ENABLE(WML) // All WML decks should go through the real load mechanism, not the scroll-to-anchor code @@ -3961,11 +3329,23 @@ bool FrameLoader::shouldUseCredentialStorage(ResourceLoader* loader) return m_client->shouldUseCredentialStorage(loader->documentLoader(), loader->identifier()); } +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) +bool FrameLoader::canAuthenticateAgainstProtectionSpace(ResourceLoader* loader, const ProtectionSpace& protectionSpace) +{ + return m_client->canAuthenticateAgainstProtectionSpace(loader->documentLoader(), loader->identifier(), protectionSpace); +} +#endif + void FrameLoader::setTitle(const String& title) { documentLoader()->setTitle(title); } +void FrameLoader::setIconURL(const String& iconURL) +{ + documentLoader()->setIconURL(iconURL); +} + KURL FrameLoader::originalRequestURL() const { return activeDocumentLoader()->originalRequest().url(); @@ -3973,7 +3353,7 @@ KURL FrameLoader::originalRequestURL() const String FrameLoader::referrer() const { - return documentLoader()->request().httpReferrer(); + return m_documentLoader ? m_documentLoader->request().httpReferrer() : ""; } void FrameLoader::dispatchDocumentElementAvailable() @@ -3984,7 +3364,7 @@ void FrameLoader::dispatchDocumentElementAvailable() void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds() { - if (!m_frame->script()->canExecuteScripts()) + if (!m_frame->script()->canExecuteScripts(NotAboutToExecuteScript)) return; Vector<DOMWrapperWorld*> worlds; @@ -3995,7 +3375,7 @@ void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds() void FrameLoader::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld* world) { - if (!m_frame->script()->canExecuteScripts() || !m_frame->script()->existingWindowShell(world)) + if (!m_frame->script()->canExecuteScripts(NotAboutToExecuteScript) || !m_frame->script()->existingWindowShell(world)) return; m_client->dispatchDidClearWindowObjectInWorld(world); @@ -4007,15 +3387,13 @@ void FrameLoader::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld* world) if (Page* page = m_frame->page()) { if (InspectorController* inspector = page->inspectorController()) inspector->inspectedWindowScriptObjectCleared(m_frame); - if (InspectorController* inspector = page->parentInspectorController()) - inspector->windowScriptObjectAvailable(); } #endif } void FrameLoader::updateSandboxFlags() { - SandboxFlags flags = SandboxNone; + SandboxFlags flags = m_forcedSandboxFlags; if (Frame* parentFrame = m_frame->tree()->parent()) flags |= parentFrame->loader()->sandboxFlags(); if (HTMLFrameOwnerElement* ownerElement = m_frame->ownerElement()) @@ -4030,47 +3408,6 @@ void FrameLoader::updateSandboxFlags() child->loader()->updateSandboxFlags(); } -bool FrameLoader::isDocumentSandboxed(SandboxFlags mask) const -{ - return m_frame->document() && m_frame->document()->securityOrigin()->isSandboxed(mask); -} - -PassRefPtr<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 (!SecurityOrigin::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); - - RefPtr<Widget> widget = m_client->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues); - if (!widget) - return 0; - - m_containsPlugIns = true; - return widget; -} - void FrameLoader::didChangeTitle(DocumentLoader* loader) { m_client->didChangeTitle(loader); @@ -4085,9 +3422,15 @@ void FrameLoader::didChangeTitle(DocumentLoader* loader) } } +void FrameLoader::didChangeIcons(DocumentLoader* loader) +{ + if (loader == m_documentLoader) + m_client->dispatchDidChangeIcons(); +} + void FrameLoader::dispatchDidCommitLoad() { - if (m_creatingInitialEmptyDocument) + if (m_stateMachine.creatingInitialEmptyDocument()) return; #ifndef NDEBUG @@ -4134,4 +3477,69 @@ bool FrameLoaderClient::hasHTMLView() const return true; } +Frame* createWindow(Frame* openerFrame, Frame* lookupFrame, const FrameLoadRequest& request, const WindowFeatures& features, bool& created) +{ + ASSERT(!features.dialog || request.frameName().isEmpty()); + + if (!request.frameName().isEmpty() && request.frameName() != "_blank") { + Frame* frame = lookupFrame->tree()->find(request.frameName()); + if (frame && openerFrame->loader()->shouldAllowNavigation(frame)) { + if (!request.resourceRequest().url().isEmpty()) + frame->loader()->loadFrameRequest(request, false, false, 0, 0, SendReferrer); + if (Page* page = frame->page()) + page->chrome()->focus(); + created = false; + return frame; + } + } + + // Sandboxed frames cannot open new auxiliary browsing contexts. + if (isDocumentSandboxed(openerFrame, SandboxNavigation)) + return 0; + + // FIXME: Setting the referrer should be the caller's responsibility. + FrameLoadRequest requestWithReferrer = request; + requestWithReferrer.resourceRequest().setHTTPReferrer(openerFrame->loader()->outgoingReferrer()); + FrameLoader::addHTTPOriginIfNeeded(requestWithReferrer.resourceRequest(), openerFrame->loader()->outgoingOrigin()); + + Page* oldPage = openerFrame->page(); + if (!oldPage) + return 0; + + Page* page = oldPage->chrome()->createWindow(openerFrame, requestWithReferrer, features); + if (!page) + return 0; + + Frame* frame = page->mainFrame(); + if (request.frameName() != "_blank") + frame->tree()->setName(request.frameName()); + + page->chrome()->setToolbarsVisible(features.toolBarVisible || features.locationBarVisible); + page->chrome()->setStatusbarVisible(features.statusBarVisible); + page->chrome()->setScrollbarsVisible(features.scrollbarsVisible); + page->chrome()->setMenubarVisible(features.menuBarVisible); + page->chrome()->setResizable(features.resizable); + + // 'x' and 'y' specify the location of the window, while 'width' and 'height' + // specify the size of the page. We can only resize the window, so + // adjust for the difference between the window size and the page size. + + FloatRect windowRect = page->chrome()->windowRect(); + FloatSize pageSize = page->chrome()->pageRect().size(); + if (features.xSet) + windowRect.setX(features.x); + if (features.ySet) + windowRect.setY(features.y); + if (features.widthSet) + windowRect.setWidth(features.width + (windowRect.width() - pageSize.width())); + if (features.heightSet) + windowRect.setHeight(features.height + (windowRect.height() - pageSize.height())); + page->chrome()->setWindowRect(windowRect); + + page->chrome()->show(); + + created = true; + return frame; +} + } // namespace WebCore |