diff options
Diffstat (limited to 'WebCore/loader/FrameLoader.cpp')
-rw-r--r-- | WebCore/loader/FrameLoader.cpp | 5283 |
1 files changed, 0 insertions, 5283 deletions
diff --git a/WebCore/loader/FrameLoader.cpp b/WebCore/loader/FrameLoader.cpp deleted file mode 100644 index 0285a8c..0000000 --- a/WebCore/loader/FrameLoader.cpp +++ /dev/null @@ -1,5283 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "FrameLoader.h" - -#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size -#include "Archive.h" -#include "ArchiveFactory.h" -#endif -#include "CString.h" -#include "Cache.h" -#include "CachedPage.h" -#include "Chrome.h" -#include "DOMImplementation.h" -#include "DOMWindow.h" -#include "DocLoader.h" -#include "Document.h" -#include "DocumentLoader.h" -#include "Editor.h" -#include "EditorClient.h" -#include "Element.h" -#include "Event.h" -#include "EventNames.h" -#include "FloatRect.h" -#include "FormState.h" -#include "Frame.h" -#include "FrameLoadRequest.h" -#include "FrameLoaderClient.h" -#include "FramePrivate.h" -#include "FrameTree.h" -#include "FrameView.h" -#include "HTMLFormElement.h" -#include "HTMLFrameElement.h" -#include "HTMLNames.h" -#include "HTMLObjectElement.h" -#include "HTTPParsers.h" -#include "HistoryItem.h" -#include "IconDatabase.h" -#include "IconLoader.h" -#include "InspectorController.h" -#include "Logging.h" -#include "MIMETypeRegistry.h" -#include "MainResourceLoader.h" -#include "Page.h" -#include "PageCache.h" -#include "PageGroup.h" -#include "PluginData.h" -#include "ProgressTracker.h" -#include "RenderPart.h" -#include "RenderWidget.h" -#include "RenderView.h" -#include "ResourceHandle.h" -#include "ResourceRequest.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 "JSDOMBinding.h" -#include "ScriptController.h" -#include <runtime/JSLock.h> -#include <runtime/JSObject.h> - -#if ENABLE(OFFLINE_WEB_APPLICATIONS) -#include "ApplicationCache.h" -#include "ApplicationCacheResource.h" -#endif - -#if ENABLE(SVG) -#include "SVGDocument.h" -#include "SVGLocatable.h" -#include "SVGNames.h" -#include "SVGPreserveAspectRatio.h" -#include "SVGSVGElement.h" -#include "SVGViewElement.h" -#include "SVGViewSpec.h" -#endif - -#ifdef ANDROID_INSTRUMENT -#include "TimeCounter.h" -#endif - -using namespace JSC; - -namespace WebCore { - -#if ENABLE(SVG) -using namespace SVGNames; -#endif -using namespace HTMLNames; - -#if USE(LOW_BANDWIDTH_DISPLAY) -const unsigned int cMaxPendingSourceLengthInLowBandwidthDisplay = 128 * 1024; -#endif - -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; - - ScheduledRedirection(double redirectDelay, const String& redirectURL, bool redirectLockHistory, bool userGesture) - : type(redirection) - , delay(redirectDelay) - , url(redirectURL) - , historySteps(0) - , lockHistory(redirectLockHistory) - , wasUserGesture(userGesture) - { - } - - ScheduledRedirection(Type locationChangeType, - const String& locationChangeURL, const String& locationChangeReferrer, - bool locationChangeLockHistory, bool locationChangeWasUserGesture) - : type(locationChangeType) - , delay(0) - , url(locationChangeURL) - , referrer(locationChangeReferrer) - , historySteps(0) - , lockHistory(locationChangeLockHistory) - , wasUserGesture(locationChangeWasUserGesture) - { - } - - explicit ScheduledRedirection(int historyNavigationSteps) - : type(historyNavigation) - , delay(0) - , historySteps(historyNavigationSteps) - , lockHistory(false) - , wasUserGesture(false) - { - } -}; - -static double storedTimeOfLastCompletedLoad; -static FrameLoader::LocalLoadPolicy localLoadPolicy = FrameLoader::AllowLocalLoadsForLocalOnly; - -static bool getString(JSValue* result, String& string) -{ - if (!result) - return false; - JSLock lock(false); - UString ustring; - if (!result->getString(ustring)) - return false; - string = ustring; - return true; -} - -bool isBackForwardLoadType(FrameLoadType type) -{ - switch (type) { - case FrameLoadTypeStandard: - case FrameLoadTypeReload: - case FrameLoadTypeReloadAllowingStaleData: - case FrameLoadTypeSame: - case FrameLoadTypeRedirectWithLockedHistory: - case FrameLoadTypeReplace: - return false; - case FrameLoadTypeBack: - case FrameLoadTypeForward: - case FrameLoadTypeIndexedBackForward: - return true; - } - ASSERT_NOT_REACHED(); - return false; -} - -static int numRequests(Document* document) -{ - if (!document) - return 0; - - return document->docLoader()->requestCount(); -} - -FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client) - : m_frame(frame) - , m_client(client) - , m_state(FrameStateCommittedPage) - , m_loadType(FrameLoadTypeStandard) - , m_policyLoadType(FrameLoadTypeStandard) - , m_delegateIsHandlingProvisionalLoadError(false) - , m_delegateIsDecidingNavigationPolicy(false) - , m_delegateIsHandlingUnimplementablePolicy(false) - , m_firstLayoutDone(false) - , m_quickRedirectComing(false) - , m_sentRedirectNotification(false) - , m_inStopAllLoaders(false) - , m_navigationDuringLoad(false) - , m_cachePolicy(CachePolicyVerify) - , m_isExecutingJavaScriptFormAction(false) - , m_isRunningScript(false) - , m_didCallImplicitClose(false) - , m_wasUnloadEventEmitted(false) - , m_isComplete(false) - , m_isLoadingMainResource(false) - , m_cancellingWithLoadInProgress(false) - , m_needsClear(false) - , m_receivedData(false) - , m_encodingWasChosenByUser(false) - , m_containsPlugIns(false) - , m_redirectionTimer(this, &FrameLoader::redirectionTimerFired) - , m_checkCompletedTimer(this, &FrameLoader::checkCompletedTimerFired) - , m_checkLoadCompleteTimer(this, &FrameLoader::checkLoadCompleteTimerFired) - , m_opener(0) - , m_openedByDOM(false) - , m_creatingInitialEmptyDocument(false) - , m_isDisplayingInitialEmptyDocument(false) - , m_committedFirstRealDocumentLoad(false) - , m_didPerformFirstNavigation(false) -#ifndef NDEBUG - , m_didDispatchDidCommitLoad(false) -#endif -#if USE(LOW_BANDWIDTH_DISPLAY) - , m_useLowBandwidthDisplay(true) - , m_finishedParsingDuringLowBandwidthDisplay(false) - , m_needToSwitchOutLowBandwidthDisplay(false) -#endif -{ -} - -FrameLoader::~FrameLoader() -{ - setOpener(0); - - HashSet<Frame*>::iterator end = m_openedFrames.end(); - for (HashSet<Frame*>::iterator it = m_openedFrames.begin(); it != end; ++it) - (*it)->loader()->m_opener = 0; - - m_client->frameLoaderDestroyed(); -} - -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()); - setProvisionalDocumentLoader(m_policyDocumentLoader.get()); - setState(FrameStateProvisional); - m_provisionalDocumentLoader->setResponse(ResourceResponse(KURL(), "text/html", 0, String(), String())); - m_provisionalDocumentLoader->finishedLoading(); - begin(KURL(), false); - end(); - m_frame->document()->cancelParsing(); - m_creatingInitialEmptyDocument = false; - m_didCallImplicitClose = true; -} - -void FrameLoader::setDefersLoading(bool defers) -{ - if (m_documentLoader) - m_documentLoader->setDefersLoading(defers); - if (m_provisionalDocumentLoader) - m_provisionalDocumentLoader->setDefersLoading(defers); - if (m_policyDocumentLoader) - m_policyDocumentLoader->setDefersLoading(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()->loadFrameRequestWithFormAndValues(request, false, 0, 0, HashMap<String, String>()); - if (Page* page = frame->page()) - page->chrome()->focus(); - 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); -} - -void FrameLoader::changeLocation(const String& url, const String& referrer, bool lockHistory, bool userGesture) -{ - changeLocation(completeURL(url), referrer, lockHistory, userGesture); -} - - -void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool lockHistory, bool userGesture) -{ - RefPtr<Frame> protect(m_frame); - - ResourceRequestCachePolicy policy = (m_cachePolicy == CachePolicyReload) || (m_cachePolicy == CachePolicyRefresh) - ? ReloadIgnoringCacheData : UseProtocolCachePolicy; - ResourceRequest request(url, referrer, policy); -#ifdef ANDROID_USER_GESTURE - request.setUserGesture(userGesture); -#endif - - if (executeIfJavaScriptURL(request.url(), userGesture)) - return; - - urlSelected(request, "_self", 0, lockHistory, 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) -{ - if (executeIfJavaScriptURL(request.url(), userGesture, false)) - return; - - String target = _target; - if (target.isEmpty() && m_frame->document()) - target = m_frame->document()->baseTarget(); - - FrameLoadRequest frameRequest(request, target); -#ifdef ANDROID_USER_GESTURE - frameRequest.setWasUserGesture(userGesture); -#endif - - urlSelected(frameRequest, triggeringEvent, lockHistory); -} - -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); - url = blankURL(); - } else - url = completeURL(urlString); - - Frame* frame = ownerElement->contentFrame(); - if (frame) - frame->loader()->scheduleLocationChange(url.string(), m_outgoingReferrer, true, userGestureHint()); - else - frame = loadSubframe(ownerElement, url, frameName, m_outgoingReferrer); - - if (!frame) - return false; - - if (!scriptURL.isEmpty()) - frame->loader()->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 (!canLoad(url, referrer)) { - FrameLoader::reportLocalLoadFailed(m_frame, url.string()); - return 0; - } - - bool hideReferrer = shouldHideReferrer(url, referrer); - RefPtr<Frame> frame = m_client->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer, - allowsScrolling, marginWidth, marginHeight); - - if (!frame) { - checkCallImplicitClose(); - return 0; - } - - frame->loader()->m_isComplete = false; - - RenderObject* renderer = ownerElement->renderer(); - FrameView* view = frame->view(); - if (renderer && renderer->isWidget() && view) - static_cast<RenderWidget*>(renderer)->setWidget(view); - - checkCallImplicitClose(); - - // 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 - // 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 (url.isEmpty() || url == blankURL()) { - frame->loader()->completed(); - frame->loader()->checkCompleted(); - } - - 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) -{ - ASSERT(formData); - - 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")) { - 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)); - return; - } - - formData->generateFiles(m_frame->page()->chrome()->client()); - - FrameLoadRequest frameRequest; -#ifdef ANDROID_USER_GESTURE - frameRequest.setWasUserGesture(userGestureHint()); -#endif - - 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) { - u.setQuery(formData->flattenToString()); - } else { - if (!isMailtoForm) - frameRequest.resourceRequest().setHTTPBody(formData.get()); - frameRequest.resourceRequest().setHTTPMethod("POST"); - - // 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); - } - - frameRequest.resourceRequest().setURL(u); - addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin()); - - submitForm(frameRequest, event); -} - -void FrameLoader::stopLoading(bool sendUnload) -{ - if (m_frame->document() && m_frame->document()->tokenizer()) - m_frame->document()->tokenizer()->stopParsing(); - - if (sendUnload) { - if (m_frame->document()) { - if (m_didCallImplicitClose && !m_wasUnloadEventEmitted) { - Node* currentFocusedNode = m_frame->document()->focusedNode(); - if (currentFocusedNode) - currentFocusedNode->aboutToUnload(); - m_frame->document()->dispatchWindowEvent(eventNames().unloadEvent, false, false); - if (m_frame->document()) - m_frame->document()->updateRendering(); - m_wasUnloadEventEmitted = true; - if (m_frame->eventHandler()->pendingFrameUnloadEventCount()) - m_frame->eventHandler()->clearPendingFrameUnloadEventCount(); - if (m_frame->eventHandler()->pendingFrameBeforeUnloadEventCount()) - m_frame->eventHandler()->clearPendingFrameBeforeUnloadEventCount(); - } - } - if (m_frame->document() && !m_frame->document()->inPageCache()) - m_frame->document()->removeAllEventListenersFromAllNodes(); - } - - m_isComplete = true; // to avoid calling completed() in finishedParsing() (David) - m_isLoadingMainResource = false; - m_didCallImplicitClose = true; // don't want that one either - m_cachePolicy = CachePolicyVerify; // Why here? - - if (m_frame->document() && m_frame->document()->parsing()) { - finishedParsing(); - m_frame->document()->setParsing(false); - } - - m_workingURL = KURL(); - - if (Document* doc = m_frame->document()) { - if (DocLoader* docLoader = doc->docLoader()) - cache()->loader()->cancelRequests(docLoader); - - doc->stopActiveDOMObjects(); - -#if ENABLE(DATABASE) - doc->stopDatabases(); -#endif - } - - // tell all subframes to stop as well - for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) - 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() -{ - // http://bugs.webkit.org/show_bug.cgi?id=10854 - // 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_iconLoader) - m_iconLoader->stopLoading(); -} - -bool FrameLoader::closeURL() -{ - saveDocumentState(); - stopLoading(true); - m_frame->editor()->clearUndoRedoOperations(); - return true; -} - -void FrameLoader::cancelRedirection(bool cancelWithLoadInProgress) -{ - m_cancellingWithLoadInProgress = cancelWithLoadInProgress; - - stopRedirectionTimer(); - - m_scheduledRedirection.clear(); -} - -KURL FrameLoader::iconURL() -{ - // If this isn't a top level frame, return nothing - if (m_frame->tree() && m_frame->tree()->parent()) - return KURL(); - - // If we have an iconURL from a Link element, return that - if (m_frame->document() && !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")) - return KURL(); - - KURL url; - url.setProtocol(m_URL.protocol()); - url.setHost(m_URL.host()); - if (int port = m_URL.port()) - url.setPort(port); - url.setPath("/favicon.ico"); - return url; -} - -bool FrameLoader::didOpenURL(const KURL& url) -{ - if (m_scheduledRedirection && m_scheduledRedirection->type == ScheduledRedirection::locationChangeDuringLoad) - // 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(); - closeURL(); - - m_isComplete = false; - m_isLoadingMainResource = true; - m_didCallImplicitClose = false; - - 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()) - m_URL.setPath("/"); - m_workingURL = m_URL; - - started(); - - return true; -} - -void FrameLoader::didExplicitOpen() -{ - m_isComplete = false; - m_didCallImplicitClose = false; - - // Calling document.open counts as committing the first real document load. - m_committedFirstRealDocumentLoad = true; - - // Prevent window.open(url) -- eg window.open("about:blank") -- from blowing away results - // from a subsequent window.document.open / window.document.write call. - // Cancelling redirection here works for all cases because document.open - // implicitly precedes document.write. - cancelRedirection(); - if (m_frame->document()->url() != blankURL()) - m_URL = m_frame->document()->url(); -} - -bool FrameLoader::executeIfJavaScriptURL(const KURL& url, bool userGesture, bool replaceDocument) -{ - if (!url.protocolIs("javascript")) - return false; - - String script = decodeURLEscapeSequences(url.string().substring(strlen("javascript:"))); - JSValue* result = executeScript(script, userGesture); - - String scriptResult; - if (!getString(result, scriptResult)) - return true; - - SecurityOrigin* currentSecurityOrigin = 0; - if (m_frame->document()) - 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) { - begin(m_URL, true, currentSecurityOrigin); - write(scriptResult); - end(); - } - - return true; -} - -JSValue* FrameLoader::executeScript(const String& script, bool forceUserGesture) -{ - return executeScript(forceUserGesture ? String() : m_URL.string(), 1, script); -} - -JSValue* FrameLoader::executeScript(const String& url, int baseLine, const String& script) -{ - if (!m_frame->script()->isEnabled() || m_frame->script()->isPaused()) - return noValue(); - - bool wasRunningScript = m_isRunningScript; - m_isRunningScript = true; - - JSValue* result = m_frame->script()->evaluate(url, baseLine, script); - - if (!wasRunningScript) { - m_isRunningScript = false; - submitFormAgain(); - Document::updateDocumentsRendering(); - } - - return result; -} - -void FrameLoader::cancelAndClear() -{ - cancelRedirection(); - - if (!m_isComplete) - closeURL(); - - clear(false); -} - -void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects) -{ - // FIXME: Commenting out the below line causes <http://bugs.webkit.org/show_bug.cgi?id=11212>, but putting it - // back causes a measurable performance regression which we will need to fix to restore the correct behavior - // urlsBridgeKnowsAbout.clear(); - - m_frame->editor()->clear(); - - if (!m_needsClear) - return; - m_needsClear = false; - - if (m_frame->document() && !m_frame->document()->inPageCache()) { - m_frame->document()->cancelParsing(); - if (m_frame->document()->attached()) { - m_frame->document()->willRemove(); - m_frame->document()->detach(); - - m_frame->document()->removeFocusedNodeOfSubtree(m_frame->document()); - } - } - - // Do this after detaching the document so that the unload event works. - if (clearWindowProperties) { - m_frame->clearDOMWindow(); - m_frame->script()->clearWindowShell(); - } - - m_frame->selection()->clear(); - m_frame->eventHandler()->clear(); - if (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; - - m_containsPlugIns = false; - - if (clearScriptObjects) - m_frame->script()->clearScriptObjects(); - - m_redirectionTimer.stop(); - m_scheduledRedirection.clear(); - - m_checkCompletedTimer.stop(); - m_checkLoadCompleteTimer.stop(); - - m_receivedData = false; - m_isDisplayingInitialEmptyDocument = false; - - if (!m_encodingWasChosenByUser) - m_encoding = String(); -} - -void FrameLoader::receivedFirstData() -{ - begin(m_workingURL, false); - - 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); - - m_frame->document()->docLoader()->setCachePolicy(m_cachePolicy); - m_workingURL = KURL(); - - double delay; - String url; - if (!m_documentLoader) - return; - if (!parseHTTPRefresh(m_documentLoader->response().httpHeaderField("Refresh"), false, delay, url)) - return; - - if (url.isEmpty()) - url = m_URL.string(); - else - url = m_frame->document()->completeURL(url).string(); - - scheduleHTTPRedirection(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) -{ - // We need to take a reference to the security origin because |clear| - // might destroy the document that owns it. - RefPtr<SecurityOrigin> forcedSecurityOrigin = origin; - - bool resetScripting = !(m_isDisplayingInitialEmptyDocument && m_frame->document() && m_frame->document()->securityOrigin()->isSecureTransitionTo(url)); - clear(resetScripting, resetScripting); - if (dispatch) - dispatchWindowObjectAvailable(); - - 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.setRef(String()); - m_outgoingReferrer = ref.string(); - m_URL = url; - - RefPtr<Document> document = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode()); - m_frame->setDocument(document); - - document->setURL(m_URL); - 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()); - - updatePolicyBaseURL(); - - Settings* settings = document->settings(); - document->docLoader()->setAutoLoadImages(settings && settings->loadsImagesAutomatically()); -#ifdef ANDROID_BLOCK_NETWORK_IMAGE - 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); - } - -#if FRAME_LOADS_USER_STYLESHEET - KURL userStyleSheet = settings ? settings->userStyleSheetLocation() : KURL(); - if (!userStyleSheet.isEmpty()) - m_frame->setUserStyleSheetLocation(userStyleSheet); -#endif - - 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) -{ - 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) { - Settings* settings = m_frame->settings(); - m_decoder = TextResourceDecoder::create(m_responseMIMEType, settings ? settings->defaultTextEncodingName() : String()); - if (m_encoding.isEmpty()) { - Frame* parentFrame = m_frame->tree()->parent(); - if (parentFrame && parentFrame->document()->securityOrigin()->canAccess(m_frame->document()->securityOrigin())) - m_decoder->setEncoding(parentFrame->document()->inputEncoding(), TextResourceDecoder::DefaultEncoding); - } 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 USE(LOW_BANDWIDTH_DISPLAY) - if (m_frame->document()->inLowBandwidthDisplay()) - m_pendingSourceInLowBandwidthDisplay.append(decoded); - else // reset policy which is changed in switchOutLowBandwidthDisplayIfReady() - m_frame->document()->docLoader()->setCachePolicy(m_cachePolicy); -#endif - - 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() -{ - m_isLoadingMainResource = false; - endIfNotLoadingMainResource(); -} - -void FrameLoader::endIfNotLoadingMainResource() -{ - if (m_isLoadingMainResource || !m_frame->page()) - 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 - 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(); -} - -void FrameLoader::iconLoadDecisionAvailable() -{ - if (!m_mayLoadIconLater) - return; - LOG(IconDatabase, "FrameLoader %p was told a load decision is available for its icon", this); - startIconLoader(); - m_mayLoadIconLater = false; -} - -void FrameLoader::startIconLoader() -{ - // FIXME: We kick off the icon loader when the frame is done receiving its main resource. - // But we should instead do it when we're done parsing the head element. - if (!isLoadingMainFrame()) - return; - - if (!iconDatabase() || !iconDatabase()->isEnabled()) - return; - - KURL url(iconURL()); - String urlString(url.string()); - if (urlString.isEmpty()) - return; - - // If we're not reloading and the icon database doesn't say to load now then bail before we actually start the load - if (loadType() != FrameLoadTypeReload) { - IconLoadDecision decision = iconDatabase()->loadDecisionForIconURL(urlString, m_documentLoader.get()); - if (decision == IconLoadNo) { - LOG(IconDatabase, "FrameLoader::startIconLoader() - Told not to load this icon, committing iconURL %s to database for pageURL mapping", urlString.ascii().data()); - commitIconURLToIconDatabase(url); - - // We were told not to load this icon - that means this icon is already known by the database - // If the icon data hasn't been read in from disk yet, kick off the read of the icon from the database to make sure someone - // has done it. This is after registering for the notification so the WebView can call the appropriate delegate method. - // Otherwise if the icon data *is* available, notify the delegate - if (!iconDatabase()->iconDataKnownForIconURL(urlString)) { - LOG(IconDatabase, "Told not to load icon %s but icon data is not yet available - registering for notification and requesting load from disk", urlString.ascii().data()); - m_client->registerForIconNotification(); - iconDatabase()->iconForPageURL(m_URL.string(), IntSize(0, 0)); - iconDatabase()->iconForPageURL(originalRequestURL().string(), IntSize(0, 0)); - } else - m_client->dispatchDidReceiveIcon(); - - return; - } - - if (decision == IconLoadUnknown) { - // In this case, we may end up loading the icon later, but we still want to commit the icon url mapping to the database - // just in case we don't end up loading later - if we commit the mapping a second time after the load, that's no big deal - // We also tell the client to register for the notification that the icon is received now so it isn't missed in case the - // icon is later read in from disk - LOG(IconDatabase, "FrameLoader %p might load icon %s later", this, urlString.ascii().data()); - m_mayLoadIconLater = true; - m_client->registerForIconNotification(); - commitIconURLToIconDatabase(url); - return; - } - } - - // 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->startLoading(); -} - -void FrameLoader::setLocalLoadPolicy(LocalLoadPolicy policy) -{ - localLoadPolicy = policy; -} - -bool FrameLoader::restrictAccessToLocal() -{ - return localLoadPolicy != FrameLoader::AllowLocalLoadsForAll; -} - -bool FrameLoader::allowSubstituteDataAccessToLocal() -{ - return localLoadPolicy != FrameLoader::AllowLocalLoadsForLocalOnly; -} - -static HashSet<String, CaseFoldingHash>& localSchemes() -{ - static HashSet<String, CaseFoldingHash> 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()); - LOG(IconDatabase, "Committing iconURL %s to database for pageURLs %s and %s", icon.string().ascii().data(), m_URL.string().ascii().data(), originalRequestURL().string().ascii().data()); - iconDatabase()->setIconURLForPageURL(icon.string(), m_URL.string()); - iconDatabase()->setIconURLForPageURL(icon.string(), originalRequestURL().string()); -} - -void FrameLoader::restoreDocumentState() -{ - Document* doc = m_frame->document(); - if (!doc) - return; - - HistoryItem* itemToRestore = 0; - - switch (loadType()) { - case FrameLoadTypeReload: -#ifndef ANDROID_HISTORY_CLIENT - case FrameLoadTypeReloadAllowingStaleData: -#endif - case FrameLoadTypeSame: - case FrameLoadTypeReplace: - break; - case FrameLoadTypeBack: - case FrameLoadTypeForward: - case FrameLoadTypeIndexedBackForward: - case FrameLoadTypeRedirectWithLockedHistory: - case FrameLoadTypeStandard: -#ifdef ANDROID_HISTORY_CLIENT - case FrameLoadTypeReloadAllowingStaleData: -#endif - itemToRestore = m_currentHistoryItem.get(); - } - - if (!itemToRestore) - return; - - doc->setStateForNewFormElements(itemToRestore->documentState()); -} - -void FrameLoader::gotoAnchor() -{ - // If our URL has no ref, then we have no place we need to jump to. - // 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())) - return; - - String ref = m_URL.ref(); - if (gotoAnchor(ref)) - return; - - // Try again after decoding the ref, based on the document's encoding. - if (m_decoder) - gotoAnchor(decodeURLEscapeSequences(ref, m_decoder->encoding())); -} - -void FrameLoader::finishedParsing() -{ - if (m_creatingInitialEmptyDocument) - return; - - // This can be called from the Frame's destructor, in which case we shouldn't protect ourselves - // because doing so will cause us to re-enter the destructor when protector goes out of scope. - // Null-checking the FrameView indicates whether or not we're in the destructor. - RefPtr<Frame> protector = m_frame->view() ? m_frame : 0; - - checkCompleted(); - - if (!m_frame->view()) - return; // We are being destroyed by something checkCompleted called. - - // Check if the scrollbars are really needed for the content. - // If not, remove them, relayout, and repaint. - m_frame->view()->restoreScrollbar(); - - m_client->dispatchDidFinishDocumentLoad(); - - gotoAnchor(); -} - -void FrameLoader::loadDone() -{ - if (m_frame->document()) - checkCompleted(); -} - -void FrameLoader::checkCompleted() -{ - // Any frame that hasn't completed yet? - for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) - if (!child->loader()->m_isComplete) - return; - - // Have we completed before? - if (m_isComplete) - return; - - // Are we still parsing? - if (m_frame->document() && 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) - return; -#endif - - // OK, completed. - m_isComplete = true; - - RefPtr<Frame> protect(m_frame); - checkCallImplicitClose(); // if we didn't do it before - - // Do not start a redirection timer for subframes here. - // That is deferred until the parent is completed. - if (m_scheduledRedirection && !m_frame->tree()->parent()) - startRedirectionTimer(); - - completed(); - if (m_frame->page()) - checkLoadComplete(); -} - -void FrameLoader::checkCompletedTimerFired(Timer<FrameLoader>*) -{ - checkCompleted(); -} - -void FrameLoader::scheduleCheckCompleted() -{ - if (!m_checkCompletedTimer.isActive()) - m_checkCompletedTimer.startOneShot(0); -} - -void FrameLoader::checkLoadCompleteTimerFired(Timer<FrameLoader>*) -{ - if (!m_frame->page()) - return; - checkLoadComplete(); -} - -void FrameLoader::scheduleCheckLoadComplete() -{ - if (!m_checkLoadCompleteTimer.isActive()) - m_checkLoadCompleteTimer.startOneShot(0); -} - -void FrameLoader::checkCallImplicitClose() -{ - if (m_didCallImplicitClose || !m_frame->document() || m_frame->document()->parsing()) - return; - - for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) - if (!child->loader()->m_isComplete) // still got a frame running -> too early - return; - - m_didCallImplicitClose = true; - m_wasUnloadEventEmitted = false; - if (m_frame->document()) - m_frame->document()->implicitClose(); -} - -KURL FrameLoader::baseURL() const -{ - ASSERT(m_frame->document()); - 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()); - return m_frame->document()->completeURL(url); -} - -void FrameLoader::scheduleHTTPRedirection(double delay, const String& url) -{ - if (delay < 0 || delay > INT_MAX / 1000) - return; - - if (!m_frame->page()) - return; - - // We want a new history item if the refresh timeout is > 1 second. - if (!m_scheduledRedirection || delay <= m_scheduledRedirection->delay) -#ifdef ANDROID_USER_GESTURE - { - bool wasUserGesture = false; - DocumentLoader* docLoader = activeDocumentLoader(); - if (docLoader) - wasUserGesture = docLoader->request().userGesture(); - scheduleRedirection(new ScheduledRedirection(delay, url, delay <= 1, wasUserGesture)); - } -#else - scheduleRedirection(new ScheduledRedirection(delay, url, delay <= 1, false)); -#endif -} - -void FrameLoader::scheduleLocationChange(const String& url, const String& referrer, bool lockHistory, bool wasUserGesture) -{ - if (!m_frame->page()) - 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); - return; - } - - // 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; - - // 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); - } - - ScheduledRedirection::Type type = duringLoad - ? ScheduledRedirection::locationChangeDuringLoad : ScheduledRedirection::locationChange; - scheduleRedirection(new ScheduledRedirection(type, url, referrer, lockHistory, wasUserGesture)); -} - -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); - - ScheduledRedirection::Type type = duringLoad - ? ScheduledRedirection::locationChangeDuringLoad : ScheduledRedirection::locationChange; - scheduleRedirection(new ScheduledRedirection(type, m_URL.string(), m_outgoingReferrer, true, wasUserGesture)); - m_cachePolicy = CachePolicyRefresh; -} - -bool FrameLoader::isLocationChange(const ScheduledRedirection& redirection) -{ - switch (redirection.type) { - case ScheduledRedirection::redirection: - return false; - case ScheduledRedirection::historyNavigation: - case ScheduledRedirection::locationChange: - case ScheduledRedirection::locationChangeDuringLoad: - return true; - } - ASSERT_NOT_REACHED(); - return false; -} - -void FrameLoader::scheduleHistoryNavigation(int steps) -{ - if (!m_frame->page()) - return; - - // navigation will always be allowed in the 0 steps case, which is OK because that's supposed to force a reload. - if (!canGoBackOrForward(steps)) { - cancelRedirection(); - 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)); -} - -void FrameLoader::goBackOrForward(int distance) -{ - if (distance == 0) - return; - - Page* page = m_frame->page(); - if (!page) - return; - BackForwardList* list = page->backForwardList(); - if (!list) - return; - - 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); - } - } - - ASSERT(item); // we should not reach this line with an empty back/forward list - if (item) - page->goToItem(item, FrameLoadTypeIndexedBackForward); -} - -void FrameLoader::redirectionTimerFired(Timer<FrameLoader>*) -{ - ASSERT(m_frame->page()); - - OwnPtr<ScheduledRedirection> redirection(m_scheduledRedirection.release()); - - switch (redirection->type) { - case ScheduledRedirection::redirection: - case ScheduledRedirection::locationChange: - case ScheduledRedirection::locationChangeDuringLoad: - changeLocation(redirection->url, redirection->referrer, - redirection->lockHistory, redirection->wasUserGesture); - 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); - 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; - } - - 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; - - KURL workingURL = url; - - // If we're moving in the backforward 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 (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); - } - } - } - -#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size - RefPtr<Archive> subframeArchive = activeDocumentLoader()->popArchiveForSubframe(childFrame->tree()->name()); - - if (subframeArchive) - childFrame->loader()->loadArchive(subframeArchive.release()); - else -#endif -#ifdef ANDROID_USER_GESTURE - childFrame->loader()->loadURL(workingURL, referer, String(), childLoadType, 0, 0, false); -#else - childFrame->loader()->loadURL(workingURL, referer, String(), childLoadType, 0, 0); -#endif -} - -#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size -void FrameLoader::loadArchive(PassRefPtr<Archive> prpArchive) -{ - RefPtr<Archive> archive = prpArchive; - - ArchiveResource* mainResource = archive->mainResource(); - ASSERT(mainResource); - if (!mainResource) - return; - - SubstituteData substituteData(mainResource->data(), mainResource->mimeType(), mainResource->textEncoding(), KURL()); - - ResourceRequest request(mainResource->url()); -#if PLATFORM(MAC) - request.applyWebArchiveHackForMail(); -#endif - - RefPtr<DocumentLoader> documentLoader = m_client->createDocumentLoader(request, substituteData); - documentLoader->addAllArchiveResources(archive.get()); - load(documentLoader.get()); -} -#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::gotoAnchor(const String& name) -{ - ASSERT(m_frame->document()); - - if (!m_frame->document()->haveStylesheetsLoaded()) { - m_frame->document()->setGotoAnchorNeededAfterStylesheetsLoad(true); - return false; - } - - m_frame->document()->setGotoAnchorNeededAfterStylesheetsLoad(false); - - Node* anchorNode = m_frame->document()->getElementById(AtomicString(name)); - if (!anchorNode && !name.isEmpty()) - anchorNode = m_frame->document()->anchors()->namedItem(name, !m_frame->document()->inCompatMode()); - -#if ENABLE(SVG) - if (m_frame->document()->isSVGDocument()) { - if (name.startsWith("xpointer(")) { - // We need to parse the xpointer reference here - } else if (name.startsWith("svgView(")) { - RefPtr<SVGSVGElement> svg = static_cast<SVGDocument*>(m_frame->document())->rootElement(); - if (!svg->currentView()->parseViewSpec(name)) - return false; - svg->setUseCurrentView(true); - } else { - if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) { - RefPtr<SVGViewElement> viewElement = anchorNode->hasTagName(SVGNames::viewTag) ? static_cast<SVGViewElement*>(anchorNode) : 0; - if (viewElement.get()) { - RefPtr<SVGSVGElement> svg = static_cast<SVGSVGElement*>(SVGLocatable::nearestViewportElement(viewElement.get())); - svg->inheritViewAttributes(viewElement.get()); - } - } - } - // FIXME: need to decide which <svg> to focus on, and zoom to that one - // FIXME: need to actually "highlight" the viewTarget(s) - } -#endif - - m_frame->document()->setCSSTarget(anchorNode); // Setting to null will clear the current target. - - // Implement the rule that "" and "top" both mean top of page as in other browsers. - if (!anchorNode && !(name.isEmpty() || equalIgnoringCase(name, "top"))) - return false; - - // 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(); - } - - // Scroll nested layers and frames to reveal the anchor. - // Align to the top and to the closest side (this matches other browsers). - RenderObject* renderer; - IntRect rect; - if (!anchorNode) - renderer = m_frame->document()->renderer(); // top of document - else { - renderer = anchorNode->renderer(); - rect = anchorNode->getRect(); - } - if (renderer) - renderer->enclosingLayer()->scrollRectToVisible(rect, true, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignTopAlways); - - return true; -} - -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 USE(LOW_BANDWIDTH_DISPLAY) - // don't care object during low bandwidth display - if (frame()->document()->inLowBandwidthDisplay()) { - m_needToSwitchOutLowBandwidthDisplay = true; - return false; - } -#endif - - KURL completedURL; - if (!url.isEmpty()) - completedURL = completeURL(url); - - bool useFallback; - if (shouldUsePlugin(completedURL, mimeType, renderer->hasFallbackContent(), useFallback)) { - Settings* settings = m_frame->settings(); - if (!settings || !settings->arePluginsEnabled() || - (!settings->isJavaEnabled() && MIMETypeRegistry::isJavaAppletMIMEType(mimeType))) - 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()); - - // FIXME: OK to always make a new frame? When does the old frame get removed? - return loadSubframe(element, completedURL, frameName, m_outgoingReferrer); -} - -bool FrameLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool hasFallback, bool& useFallback) -{ - // 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")) { - String pluginName = m_frame->page()->pluginData()->pluginNameForMimeType(mimeType); - 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; -} - -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()); - - 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()); - if (widget) { - renderer->setWidget(widget); - m_containsPlugIns = true; - } - } - - 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()) - startRedirectionTimer(); -} - -String FrameLoader::outgoingReferrer() const -{ - return m_outgoingReferrer; -} - -String FrameLoader::outgoingOrigin() const -{ - if (m_frame->document()) - return m_frame->document()->securityOrigin()->toString(); - - return SecurityOrigin::createEmpty()->toString(); -} - -Frame* FrameLoader::opener() -{ - return m_opener; -} - -void FrameLoader::setOpener(Frame* opener) -{ - if (m_opener) - m_opener->loader()->m_openedFrames.remove(m_frame); - if (opener) - opener->loader()->m_openedFrames.add(m_frame); - m_opener = opener; - - if (m_frame->document()) { - m_frame->document()->initSecurityContext(); - m_frame->domWindow()->setSecurityOrigin(m_frame->document()->securityOrigin()); - } -} - -bool FrameLoader::openedByDOM() const -{ - return m_openedByDOM; -} - -void FrameLoader::setOpenedByDOM() -{ - m_openedByDOM = true; -} - -void FrameLoader::handleFallbackContent() -{ - HTMLFrameOwnerElement* owner = m_frame->ownerElement(); - if (!owner || !owner->hasTagName(objectTag)) - return; - static_cast<HTMLObjectElement*>(owner)->renderFallbackContent(); -} - -void FrameLoader::provisionalLoadStarted() -{ -#ifdef ANDROID_INSTRUMENT - if (!m_frame->tree()->parent()) - android::TimeCounter::reset(); -#endif - - 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(); -} - -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(); -} - -void FrameLoader::resetMultipleFormSubmissionProtection() -{ - m_submittedFormURL = KURL(); -} - -void FrameLoader::setEncoding(const String& name, bool userChosen) -{ - if (!m_workingURL.isEmpty()) - receivedFirstData(); - m_encoding = name; - m_encodingWasChosenByUser = userChosen; -} - -void FrameLoader::addData(const char* bytes, int length) -{ - ASSERT(m_workingURL.isEmpty()); - ASSERT(m_frame->document()); - ASSERT(m_frame->document()->parsing()); - 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(); - - 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 - // (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) -#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 != FrameLoadTypeSame - && !m_documentLoader->isLoadingInAPISense() - && !m_documentLoader->isStopping() -#if ENABLE(OFFLINE_WEB_APPLICATIONS) - // FIXME: We should investigating caching pages that have an associated - // application cache. <rdar://problem/5917899> tracks that work. - && !m_documentLoader->applicationCache() - && !m_documentLoader->candidateApplicationCacheGroup() -#endif - ; -} - -void FrameLoader::updatePolicyBaseURL() -{ - if (m_frame->tree()->parent() && m_frame->tree()->parent()->document()) - setPolicyBaseURL(m_frame->tree()->parent()->document()->policyBaseURL()); - else - setPolicyBaseURL(m_URL); -} - -void FrameLoader::setPolicyBaseURL(const KURL& url) -{ - if (m_frame->document()) - m_frame->document()->setPolicyBaseURL(url); - for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) - child->loader()->setPolicyBaseURL(url); -} - -// This does the same kind of work that didOpenURL does, except it relies on the fact -// that a higher level already checked that the URLs match and the scrolling is the right thing to do. -void FrameLoader::scrollToAnchor(const KURL& url) -{ - m_URL = url; - updateHistoryForAnchorScroll(); - - // If we were in the autoscroll/panScroll mode we want to stop it before following the link to the anchor - m_frame->eventHandler()->stopAutoscrollTimer(); - started(); - gotoAnchor(); - - // It's important to model this as a load that starts and immediately finishes. - // Otherwise, the parent frame may think we never finished loading. - m_isComplete = false; - checkCompleted(); -} - -bool FrameLoader::isComplete() const -{ - return m_isComplete; -} - -void FrameLoader::scheduleRedirection(ScheduledRedirection* redirection) -{ - ASSERT(m_frame->page()); - - stopRedirectionTimer(); - m_scheduledRedirection.set(redirection); - if (!m_isComplete && redirection->type != ScheduledRedirection::redirection) - completed(); - if (m_isComplete || redirection->type != ScheduledRedirection::redirection) - startRedirectionTimer(); -} - -void FrameLoader::startRedirectionTimer() -{ - ASSERT(m_frame->page()); - ASSERT(m_scheduledRedirection); - - m_redirectionTimer.stop(); - m_redirectionTimer.startOneShot(m_scheduledRedirection->delay); - - switch (m_scheduledRedirection->type) { - case ScheduledRedirection::redirection: - case ScheduledRedirection::locationChange: - case ScheduledRedirection::locationChangeDuringLoad: - clientRedirected(KURL(m_scheduledRedirection->url), - m_scheduledRedirection->delay, - currentTime() + m_redirectionTimer.nextFireInterval(), - m_scheduledRedirection->lockHistory, - m_isExecutingJavaScriptFormAction); - return; - case ScheduledRedirection::historyNavigation: - // Don't report history navigations. - return; - } - ASSERT_NOT_REACHED(); -} - -void FrameLoader::stopRedirectionTimer() -{ - if (!m_redirectionTimer.isActive()) - return; - - m_redirectionTimer.stop(); - - if (m_scheduledRedirection) { - switch (m_scheduledRedirection->type) { - case ScheduledRedirection::redirection: - case ScheduledRedirection::locationChange: - case ScheduledRedirection::locationChangeDuringLoad: - clientRedirectCancelledOrFinished(m_cancellingWithLoadInProgress); - return; - case ScheduledRedirection::historyNavigation: - // Don't report history navigations. - return; - } - ASSERT_NOT_REACHED(); - } -} - -void FrameLoader::completed() -{ - RefPtr<Frame> protect(m_frame); - for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) - child->loader()->parentCompleted(); - if (Frame* parent = m_frame->tree()->parent()) - parent->loader()->checkCompleted(); - submitFormAgain(); -} - -void FrameLoader::started() -{ - for (Frame* frame = m_frame; frame; frame = frame->tree()->parent()) - frame->loader()->m_isComplete = false; -} - -bool FrameLoader::containsPlugins() const -{ - return m_containsPlugIns; -} - -void FrameLoader::prepareForLoadStart() -{ - if (Page* page = m_frame->page()) - page->progress()->progressStarted(m_frame); - m_client->dispatchDidStartProvisionalLoad(); -} - -void FrameLoader::setupForReplace() -{ - setState(FrameStateProvisional); - m_provisionalDocumentLoader = m_documentLoader; - m_documentLoader = 0; - detachChildren(); -} - -void FrameLoader::setupForReplaceByMIMEType(const String& newMIMEType) -{ - activeDocumentLoader()->setupForReplaceByMIMEType(newMIMEType); -} - -void FrameLoader::loadFrameRequestWithFormState(const FrameLoadRequest& request, bool lockHistory, Event* event, PassRefPtr<FormState> prpFormState) -{ - RefPtr<FormState> formState = prpFormState; - KURL url = request.resourceRequest().url(); - - String referrer; - String argsReferrer = request.resourceRequest().httpReferrer(); - if (!argsReferrer.isEmpty()) - referrer = argsReferrer; - else - referrer = m_outgoingReferrer; - - ASSERT(frame()->document()); - if (url.protocolIs("file")) { - if (!canLoad(url, String(), frame()->document()) && !canLoad(url, referrer)) { - FrameLoader::reportLocalLoadFailed(m_frame, url.string()); - return; - } - } - - 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; - -#ifdef ANDROID_USER_GESTURE - loadURL(request.resourceRequest().url(), referrer, request.frameName(), loadType, - event, formState.release(), request.wasUserGesture()); -#else - loadURL(request.resourceRequest().url(), referrer, request.frameName(), loadType, - event, formState.release()); -#endif - } else -#ifdef ANDROID_USER_GESTURE - loadPostRequest(request.resourceRequest(), referrer, request.frameName(), event, formState.release(), request.wasUserGesture()); -#else - loadPostRequest(request.resourceRequest(), referrer, request.frameName(), event, formState.release()); -#endif - - if (targetFrame && targetFrame != m_frame) - if (Page* page = targetFrame->page()) - page->chrome()->focus(); -} - -void FrameLoader::loadFrameRequestWithFormAndValues(const FrameLoadRequest& request, bool lockHistory, Event* event, - HTMLFormElement* submitForm, const HashMap<String, String>& formValues) -{ - RefPtr<FormState> formState; - if (submitForm) - formState = FormState::create(submitForm, formValues, m_frame); - - loadFrameRequestWithFormState(request, lockHistory, event, formState.release()); -} - -#ifdef ANDROID_USER_GESTURE -void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, FrameLoadType newLoadType, - Event* event, PassRefPtr<FormState> prpFormState, bool userGesture) -#else -void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, FrameLoadType newLoadType, - 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); - addHTTPOriginIfNeeded(request, referrerOrigin->toString()); - } - addExtraFieldsToRequest(request, true, event || isFormSubmission); - if (newLoadType == FrameLoadTypeReload) - request.setCachePolicy(ReloadIgnoringCacheData); - - ASSERT(newLoadType != FrameLoadTypeSame); - - NavigationAction action(newURL, newLoadType, isFormSubmission, event); - - if (!frameName.isEmpty()) { - if (Frame* targetFrame = findFrameForNavigation(frameName)) -#ifdef ANDROID_USER_GESTURE - targetFrame->loader()->loadURL(newURL, referrer, String(), newLoadType, event, formState, userGesture); -#else - targetFrame->loader()->loadURL(newURL, referrer, String(), newLoadType, event, formState); -#endif - else - checkNewWindowPolicy(action, request, formState, frameName); - return; - } - - RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader; - - bool sameURL = shouldTreatURLAsSameAsCurrent(newURL); - - // Make sure to do scroll to anchor processing even if the URL is - // exactly the same so pages with '#' links and DHTML side effects - // work properly. - if (shouldScrollToAnchor(isFormSubmission, newLoadType, newURL)) { - oldDocumentLoader->setTriggeringAction(action); - stopPolicyCheck(); - checkNavigationPolicy(request, oldDocumentLoader.get(), formState, - 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); - if (isRedirect) { - m_quickRedirectComing = false; - if (m_provisionalDocumentLoader) - m_provisionalDocumentLoader->setIsClientRedirect(true); -#ifdef ANDROID_HISTORY_CLIENT - } else if (sameURL && (newLoadType != FrameLoadTypeReloadAllowingStaleData)) -#else - } else if (sameURL) -#endif - // Example of this case are sites that reload the same URL with a different cookie - // driving the generated content, or a master frame with links that drive a target - // frame, where the user has clicked on the same link repeatedly. - m_loadType = FrameLoadTypeSame; - } -} - -void FrameLoader::load(const ResourceRequest& request) -{ - load(request, SubstituteData()); -} - -void FrameLoader::load(const ResourceRequest& request, const SubstituteData& substituteData) -{ - 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()); -} - -void FrameLoader::load(const ResourceRequest& request, const String& frameName) -{ - if (frameName.isEmpty()) { - load(request); - return; - } - - Frame* frame = findFrameForNavigation(frameName); - if (frame) { - frame->loader()->load(request); - return; - } - - checkNewWindowPolicy(NavigationAction(request.url(), NavigationTypeOther), request, 0, frameName); -} - -void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, FrameLoadType type, PassRefPtr<FormState> formState) -{ - RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData()); - - loader->setTriggeringAction(action); - if (m_documentLoader) - loader->setOverrideEncoding(m_documentLoader->overrideEncoding()); - - loadWithDocumentLoader(loader.get(), type, formState); -} - -void FrameLoader::load(DocumentLoader* newDocumentLoader) -{ - ResourceRequest& r = newDocumentLoader->request(); - addExtraFieldsToRequest(r, true, false); - FrameLoadType type; - - if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->originalRequest().url())) { - r.setCachePolicy(ReloadIgnoringCacheData); - type = FrameLoadTypeSame; - } else - type = FrameLoadTypeStandard; - - if (m_documentLoader) - newDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding()); - - // When we loading alternate content for an unreachable URL that we're - // visiting in the history list, we treat it as a reload so the history list - // is appropriately maintained. - // - // FIXME: This seems like a dangerous overloading of the meaning of "FrameLoadTypeReload" ... - // shouldn't a more explicit type of reload be defined, that means roughly - // "load without affecting history" ? - if (shouldReloadToHandleUnreachableURL(newDocumentLoader)) { - ASSERT(type == FrameLoadTypeStandard); - type = FrameLoadTypeReload; - } - - loadWithDocumentLoader(newDocumentLoader, type, 0); -} - -void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState) -{ - ASSERT(m_client->hasWebView()); - - // Unfortunately the view must be non-nil, this is ultimately due - // to parser requiring a FrameView. We should fix this dependency. - - ASSERT(m_frame->view()); - - m_policyLoadType = type; - RefPtr<FormState> formState = prpFormState; - bool isFormSubmission = formState; - - const KURL& newURL = loader->request().url(); - - if (shouldScrollToAnchor(isFormSubmission, m_policyLoadType, newURL)) { - RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader; - NavigationAction action(newURL, m_policyLoadType, isFormSubmission); - - oldDocumentLoader->setTriggeringAction(action); - stopPolicyCheck(); - checkNavigationPolicy(loader->request(), oldDocumentLoader.get(), formState, - callContinueFragmentScrollAfterNavigationPolicy, this); - } else { - if (Frame* parent = m_frame->tree()->parent()) - loader->setOverrideEncoding(parent->loader()->documentLoader()->overrideEncoding()); - - stopPolicyCheck(); - setPolicyDocumentLoader(loader); - - checkNavigationPolicy(loader->request(), loader, formState, - callContinueLoadAfterNavigationPolicy, this); - } -} - -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 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; -} - -void FrameLoader::reportLocalLoadFailed(Frame* frame, const String& url) -{ - ASSERT(!url.isEmpty()); - if (!frame) - return; - - frame->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, "Not allowed to load local resource: " + url, 0, String()); -} - -bool FrameLoader::shouldHideReferrer(const KURL& url, const String& referrer) -{ - bool referrerIsSecureURL = protocolIs(referrer, "https"); - bool referrerIsWebURL = referrerIsSecureURL || protocolIs(referrer, "http"); - - if (!referrerIsWebURL) - return true; - - if (!referrerIsSecureURL) - return false; - - bool URLIsSecureURL = url.protocolIs("https"); - - return !URLIsSecureURL; -} - -const ResourceRequest& FrameLoader::initialRequest() const -{ - return activeDocumentLoader()->originalRequest(); -} - -void FrameLoader::receivedData(const char* data, int length) -{ - activeDocumentLoader()->receivedData(data, length); -} - -void FrameLoader::handleUnimplementablePolicy(const ResourceError& error) -{ - m_delegateIsHandlingUnimplementablePolicy = true; - m_client->dispatchUnableToImplementPolicy(error); - m_delegateIsHandlingUnimplementablePolicy = false; -} - -void FrameLoader::cannotShowMIMEType(const ResourceResponse& response) -{ - handleUnimplementablePolicy(m_client->cannotShowMIMETypeError(response)); -} - -ResourceError FrameLoader::interruptionForPolicyChangeError(const ResourceRequest& request) -{ - return m_client->interruptForPolicyChangeError(request); -} - -void FrameLoader::checkNavigationPolicy(const ResourceRequest& newRequest, NavigationPolicyDecisionFunction function, void* argument) -{ - checkNavigationPolicy(newRequest, activeDocumentLoader(), 0, function, argument); -} - -void FrameLoader::checkContentPolicy(const String& MIMEType, ContentPolicyDecisionFunction function, void* argument) -{ - ASSERT(activeDocumentLoader()); - - // Always show content with valid substitute data. - if (activeDocumentLoader()->substituteData().isValid()) { - function(argument, PolicyUse); - return; - } - -#if ENABLE(FTPDIR) - // Respect the hidden FTP Directory Listing pref so it can be tested even if the policy delegate might otherwise disallow it - Settings* settings = m_frame->settings(); - if (settings && settings->forceFTPDirectoryListings() && MIMEType == "application/x-ftp-directory") { - function(argument, PolicyUse); - return; - } -#endif - - m_policyCheck.set(function, argument); - m_client->dispatchDecidePolicyForMIMEType(&FrameLoader::continueAfterContentPolicy, - MIMEType, activeDocumentLoader()->request()); -} - -bool FrameLoader::shouldReloadToHandleUnreachableURL(DocumentLoader* docLoader) -{ - KURL unreachableURL = docLoader->unreachableURL(); - - if (unreachableURL.isEmpty()) - return false; - - if (!isBackForwardLoadType(m_policyLoadType)) - return false; - - // We only treat unreachableURLs specially during the delegate callbacks - // for provisional load errors and navigation policy decisions. The former - // case handles well-formed URLs that can't be loaded, and the latter - // case handles malformed URLs and unknown schemes. Loading alternate content - // at other times behaves like a standard load. - DocumentLoader* compareDocumentLoader = 0; - if (m_delegateIsDecidingNavigationPolicy || m_delegateIsHandlingUnimplementablePolicy) - compareDocumentLoader = m_policyDocumentLoader.get(); - else if (m_delegateIsHandlingProvisionalLoadError) - compareDocumentLoader = m_provisionalDocumentLoader.get(); - - return compareDocumentLoader && unreachableURL == compareDocumentLoader->request().url(); -} - -void FrameLoader::reloadAllowingStaleData(const String& encoding) -{ - if (!m_documentLoader) - return; - - ResourceRequest request = m_documentLoader->request(); - KURL unreachableURL = m_documentLoader->unreachableURL(); - if (!unreachableURL.isEmpty()) - request.setURL(unreachableURL); - - request.setCachePolicy(ReturnCacheDataElseLoad); - - RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData()); - setPolicyDocumentLoader(loader.get()); - - loader->setOverrideEncoding(encoding); - - loadWithDocumentLoader(loader.get(), FrameLoadTypeReloadAllowingStaleData, 0); -} - -void FrameLoader::reload() -{ - if (!m_documentLoader) - return; - - ResourceRequest& initialRequest = m_documentLoader->request(); - - // If a window is created by javascript, its main frame can have an empty but non-nil URL. - // Reloading in this case will lose the current contents (see 4151001). - if (initialRequest.url().isEmpty()) - return; - - // Replace error-page URL with the URL we were trying to reach. - KURL unreachableURL = m_documentLoader->unreachableURL(); - if (!unreachableURL.isEmpty()) - initialRequest = ResourceRequest(unreachableURL); - - // Create a new document loader for the reload, this will become m_documentLoader eventually, - // but first it has to be the "policy" document loader, and then the "provisional" document loader. - RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(initialRequest, SubstituteData()); - - ResourceRequest& request = loader->request(); - - request.setCachePolicy(ReloadIgnoringCacheData); - request.setHTTPHeaderField("Cache-Control", "max-age=0"); - - // If we're about to re-post, set up action so the application can warn the user. - if (request.httpMethod() == "POST") - loader->setTriggeringAction(NavigationAction(request.url(), NavigationTypeFormResubmitted)); - - loader->setOverrideEncoding(m_documentLoader->overrideEncoding()); - - loadWithDocumentLoader(loader.get(), FrameLoadTypeReload, 0); -} - -static bool canAccessAncestor(const SecurityOrigin* activeSecurityOrigin, Frame* targetFrame) -{ - // targetFrame can be NULL when we're trying to navigate a top-level frame - // that has a NULL opener. - if (!targetFrame) - return false; - - for (Frame* ancestorFrame = targetFrame; ancestorFrame; ancestorFrame = ancestorFrame->tree()->parent()) { - Document* ancestorDocument = ancestorFrame->document(); - if (!ancestorDocument) - return true; - - const SecurityOrigin* ancestorSecurityOrigin = ancestorDocument->securityOrigin(); - if (activeSecurityOrigin->canAccess(ancestorSecurityOrigin)) - return true; - } - - return false; -} - -bool FrameLoader::shouldAllowNavigation(Frame* targetFrame) const -{ - // The navigation change is safe if the active frame is: - // - in the same security origin as the target or one of the target's - // ancestors. - // - // Or the target frame is: - // - a top-level frame in the frame hierarchy and the active frame can - // navigate the target frame's opener per above. - - if (!targetFrame) - return true; - - // Performance optimization. - if (m_frame == targetFrame) - return true; - - // 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()) - return true; - - Document* activeDocument = m_frame->document(); - ASSERT(activeDocument); - const SecurityOrigin* activeSecurityOrigin = activeDocument->securityOrigin(); - - // For top-level windows, check the opener. - if (!targetFrame->tree()->parent() && canAccessAncestor(activeSecurityOrigin, targetFrame->loader()->opener())) - return true; - - // In general, check the frame's ancestors. - if (canAccessAncestor(activeSecurityOrigin, targetFrame)) - return true; - - Settings* settings = targetFrame->settings(); - if (settings && !settings->privateBrowsingEnabled()) { - Document* targetDocument = targetFrame->document(); - // FIXME: this error message should contain more specifics of why the navigation change is not allowed. - String message = String::format("Unsafe JavaScript attempt to initiate a navigation change for frame with URL %s from frame with URL %s.\n", - targetDocument->url().string().utf8().data(), activeDocument->url().string().utf8().data()); - - // FIXME: should we print to the console of the activeFrame as well? - targetFrame->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, message, 1, String()); - } - - return false; -} - -void FrameLoader::stopLoadingSubframes() -{ - for (RefPtr<Frame> child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) - child->loader()->stopAllLoaders(); -} - -void FrameLoader::stopAllLoaders() -{ - // If this method is called from within this method, infinite recursion can occur (3442218). Avoid this. - if (m_inStopAllLoaders) - return; - - m_inStopAllLoaders = true; - - stopPolicyCheck(); - - stopLoadingSubframes(); - if (m_provisionalDocumentLoader) - m_provisionalDocumentLoader->stopLoading(); - if (m_documentLoader) - m_documentLoader->stopLoading(); - - setProvisionalDocumentLoader(0); - -#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size - if (m_documentLoader) - m_documentLoader->clearArchiveResources(); -#endif - - m_inStopAllLoaders = false; -} - -void FrameLoader::stopForUserCancel(bool deferCheckLoadComplete) -{ - stopAllLoaders(); - - if (deferCheckLoadComplete) - scheduleCheckLoadComplete(); - else if (m_frame->page()) - checkLoadComplete(); -} - -DocumentLoader* FrameLoader::activeDocumentLoader() const -{ - if (m_state == FrameStateProvisional) - return m_provisionalDocumentLoader.get(); - return m_documentLoader.get(); -} - -bool FrameLoader::isLoading() const -{ - DocumentLoader* docLoader = activeDocumentLoader(); - if (!docLoader) - return false; - return docLoader->isLoadingMainResource() || docLoader->isLoadingSubresources() || docLoader->isLoadingPlugIns(); -} - -bool FrameLoader::frameHasLoaded() const -{ - return m_committedFirstRealDocumentLoad || (m_provisionalDocumentLoader && !m_creatingInitialEmptyDocument); -} - -void FrameLoader::setDocumentLoader(DocumentLoader* loader) -{ - if (!loader && !m_documentLoader) - return; - - ASSERT(loader != m_documentLoader); - ASSERT(!loader || loader->frameLoader() == this); - - m_client->prepareForDataSourceReplacement(); - detachChildren(); - if (m_documentLoader) - m_documentLoader->detachFromFrame(); - - m_documentLoader = loader; -} - -DocumentLoader* FrameLoader::documentLoader() const -{ - return m_documentLoader.get(); -} - -void FrameLoader::setPolicyDocumentLoader(DocumentLoader* loader) -{ - if (m_policyDocumentLoader == loader) - return; - - ASSERT(m_frame); - if (loader) - loader->setFrame(m_frame); - if (m_policyDocumentLoader - && m_policyDocumentLoader != m_provisionalDocumentLoader - && m_policyDocumentLoader != m_documentLoader) - m_policyDocumentLoader->detachFromFrame(); - - 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); - ASSERT(!loader || loader->frameLoader() == this); - - if (m_provisionalDocumentLoader && m_provisionalDocumentLoader != m_documentLoader) - m_provisionalDocumentLoader->detachFromFrame(); - - m_provisionalDocumentLoader = loader; -} - -FrameState FrameLoader::state() const -{ - return m_state; -} - -double FrameLoader::timeOfLastCompletedLoad() -{ - return storedTimeOfLastCompletedLoad; -} - -void FrameLoader::setState(FrameState newState) -{ - m_state = newState; - - if (newState == FrameStateProvisional) - provisionalLoadStarted(); - else if (newState == FrameStateComplete) { - frameLoadCompleted(); - storedTimeOfLastCompletedLoad = currentTime(); - if (m_documentLoader) - m_documentLoader->stopRecordingResponses(); - } -} - -void FrameLoader::clearProvisionalLoad() -{ - setProvisionalDocumentLoader(0); - if (Page* page = m_frame->page()) - page->progress()->progressCompleted(m_frame); - setState(FrameStateComplete); -} - -void FrameLoader::markLoadComplete() -{ - setState(FrameStateComplete); -} - -void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage) -{ - RefPtr<CachedPage> cachedPage = prpCachedPage; - RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader; - - // 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()) - cachePageForHistoryItem(m_currentHistoryItem.get()); - else if (m_frame->page() && m_frame == m_frame->page()->mainFrame()) { - // If the main frame installs a timeout late enough (for example in its onunload handler) - // it could sometimes fire when transitioning to a non-HTML document representation (such as the Mac bookmarks view). - // To avoid this, we clear all timeouts if the page is not to be cached in the back forward list. - // Cached pages have their timers paused so they are fine. - ScriptController* proxy = m_frame->script(); - if (proxy->haveWindowShell()) - proxy->windowShell()->window()->clearAllTimeouts(); - } - - if (m_loadType != FrameLoadTypeReplace) - closeOldDataSources(); - - if (!cachedPage && !m_creatingInitialEmptyDocument) - m_client->makeRepresentation(pdl.get()); - - transitionToCommitted(cachedPage); - - // Call clientRedirectCancelledOrFinished() here so that the frame load delegate is notified that the redirect's - // status has changed, if there was a redirect. The frame load delegate may have saved some state about - // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:. Since we are - // just about to commit a new page, there cannot possibly be a pending redirect at this point. - if (m_sentRedirectNotification) - clientRedirectCancelledOrFinished(false); - - if (cachedPage && cachedPage->document()) { - open(*cachedPage); - cachedPage->clear(); - } else { - KURL url = pdl->substituteData().responseURL(); - if (url.isEmpty()) - url = pdl->url(); - if (url.isEmpty()) - url = pdl->responseURL(); - if (url.isEmpty()) - url = blankURL(); - - didOpenURL(url); - } - opened(); -} - -void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) -{ - ASSERT(m_client->hasWebView()); - ASSERT(m_state == FrameStateProvisional); - - if (m_state != FrameStateProvisional) - return; - - m_client->setCopiesOnScroll(); - updateHistoryForCommit(); - - // The call to closeURL() invokes the unload event handler, which can execute arbitrary - // JavaScript. If the script initiates a new load, we need to abandon the current load, - // or the two will stomp each other. - DocumentLoader* pdl = m_provisionalDocumentLoader.get(); - if (m_documentLoader) - closeURL(); - if (pdl != m_provisionalDocumentLoader) - return; - - // Nothing else can interupt this commit - set the Provisional->Committed transition in stone - if (m_documentLoader) - m_documentLoader->stopLoadingSubresources(); - if (m_documentLoader) - m_documentLoader->stopLoadingPlugIns(); - - setDocumentLoader(m_provisionalDocumentLoader.get()); - setProvisionalDocumentLoader(0); - setState(FrameStateCommittedPage); - - // Handle adding the URL to the back/forward list. - DocumentLoader* dl = m_documentLoader.get(); - String ptitle = dl->title(); - - switch (m_loadType) { - case FrameLoadTypeForward: - case FrameLoadTypeBack: - case FrameLoadTypeIndexedBackForward: - if (Page* page = m_frame->page()) - if (page->backForwardList()) { - updateHistoryForBackForwardNavigation(); - - // 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->transitionToCommittedFromCachedPage(cachedPage.get()); - - } else - m_client->transitionToCommittedForNewPage(); - } - break; - - case FrameLoadTypeReload: - case FrameLoadTypeSame: - case FrameLoadTypeReplace: - updateHistoryForReload(); - 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 - // This code was originally added for a Leopard performance imporvement. We decided to - // ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>. - if (m_frame->view()) - m_frame->view()->setScrollbarsSuppressed(true); -#endif - m_client->transitionToCommittedForNewPage(); - break; - - case FrameLoadTypeRedirectWithLockedHistory: - updateHistoryForRedirectWithLockedHistory(); - m_client->transitionToCommittedForNewPage(); - break; - - // FIXME Remove this check when dummy ds is removed (whatever "dummy ds" is). - // An exception should be thrown if we're in the FrameLoadTypeUninitialized state. - default: - ASSERT_NOT_REACHED(); - } - - m_responseMIMEType = dl->responseMIMEType(); - - // Tell the client we've committed this URL. - ASSERT(m_frame->view()); - - if (m_creatingInitialEmptyDocument) - return; - - m_committedFirstRealDocumentLoad = true; - - // For non-cached HTML pages, these methods are called in FrameLoader::begin. - if (cachedPage || !m_client->hasHTMLView()) { - dispatchDidCommitLoad(); - - // If we have a title let the WebView know about it. - if (!ptitle.isNull()) - m_client->dispatchDidReceiveTitle(ptitle); - } -} - -void FrameLoader::clientRedirectCancelledOrFinished(bool cancelWithLoadInProgress) -{ - // Note that -webView:didCancelClientRedirectForFrame: is called on the frame load delegate even if - // the redirect succeeded. We should either rename this API, or add a new method, like - // -webView:didFinishClientRedirectForFrame: - m_client->dispatchDidCancelClientRedirect(); - - if (!cancelWithLoadInProgress) - m_quickRedirectComing = false; - - m_sentRedirectNotification = false; -} - -void FrameLoader::clientRedirected(const KURL& url, double seconds, double fireDate, bool lockHistory, bool isJavaScriptFormAction) -{ - m_client->dispatchWillPerformClientRedirect(url, seconds, fireDate); - - // Remember that we sent a redirect notification to the frame load delegate so that when we commit - // 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 - // no "original" load on which to base a redirect, so we treat the redirect as a normal load. - m_quickRedirectComing = lockHistory && m_documentLoader && !isJavaScriptFormAction; -} - -bool FrameLoader::shouldReload(const KURL& currentURL, const KURL& destinationURL) -{ - // This function implements the rule: "Don't reload if navigating by fragment within - // the same URL, but do reload if going to a new URL or to the same URL with no - // fragment identifier at all." - if (!destinationURL.hasRef()) - return true; - return !equalIgnoringRef(currentURL, destinationURL); -} - -void FrameLoader::closeOldDataSources() -{ - // FIXME: Is it important for this traversal to be postorder instead of preorder? - // If so, add helpers for postorder traversal, and use them. If not, then lets not - // use a recursive algorithm here. - for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) - child->loader()->closeOldDataSources(); - - if (m_documentLoader) - m_client->dispatchWillClose(); - - m_client->setMainFrameDocumentReady(false); // stop giving out the actual DOMDocument to observers -} - -void FrameLoader::open(CachedPage& cachedPage) -{ - ASSERT(m_frame->page()); - ASSERT(m_frame->page()->mainFrame() == m_frame); - - cancelRedirection(); - - // 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()) { - m_frame->setJSStatusBarText(String()); - m_frame->setJSDefaultStatusBarText(String()); - } - - KURL url = cachedPage.url(); - - if ((url.protocolIs("http") || url.protocolIs("https")) && !url.host().isEmpty() && url.path().isEmpty()) - url.setPath("/"); - - m_URL = url; - m_workingURL = url; - - started(); - - clear(); - - Document* document = cachedPage.document(); - ASSERT(document); - document->setInPageCache(false); - - m_needsClear = true; - m_isComplete = false; - m_didCallImplicitClose = false; - m_outgoingReferrer = url.string(); - - FrameView* view = cachedPage.view(); - if (view) - view->setWasScrolledByUser(false); - m_frame->setView(view); - - m_frame->setDocument(document); - m_frame->domWindow()->setURL(document->url()); - m_frame->domWindow()->setSecurityOrigin(document->securityOrigin()); - - m_decoder = document->decoder(); - - updatePolicyBaseURL(); - - cachedPage.restore(m_frame->page()); - - checkCompleted(); -} - -bool FrameLoader::isStopping() const -{ - return activeDocumentLoader()->isStopping(); -} - -void FrameLoader::finishedLoading() -{ - // Retain because the stop may release the last reference to it. - RefPtr<Frame> protect(m_frame); - - RefPtr<DocumentLoader> dl = activeDocumentLoader(); - dl->finishedLoading(); - if (!dl->mainDocumentError().isNull() || !dl->frameLoader()) - return; - dl->setPrimaryLoadComplete(true); - m_client->dispatchDidLoadMainResource(dl.get()); - checkLoadComplete(); -} - -bool FrameLoader::isHostedByObjectElement() const -{ - HTMLFrameOwnerElement* owner = m_frame->ownerElement(); - return owner && owner->hasTagName(objectTag); -} - -bool FrameLoader::isLoadingMainFrame() const -{ - Page* page = m_frame->page(); - return page && m_frame == page->mainFrame(); -} - -bool FrameLoader::canShowMIMEType(const String& MIMEType) const -{ - return m_client->canShowMIMEType(MIMEType); -} - -bool FrameLoader::representationExistsForURLScheme(const String& URLScheme) -{ - return m_client->representationExistsForURLScheme(URLScheme); -} - -String FrameLoader::generatedMIMETypeForURLScheme(const String& URLScheme) -{ - return m_client->generatedMIMETypeForURLScheme(URLScheme); -} - -void FrameLoader::cancelContentPolicyCheck() -{ - m_client->cancelPolicyCheck(); - m_policyCheck.clear(); -} - -void FrameLoader::didReceiveServerRedirectForProvisionalLoadForFrame() -{ - m_client->dispatchDidReceiveServerRedirectForProvisionalLoad(); -} - -void FrameLoader::finishedLoadingDocument(DocumentLoader* loader) -{ - // FIXME: Platforms shouldn't differ here! -#if PLATFORM(WIN) || PLATFORM(CHROMIUM) || defined(ANDROID) - if (m_creatingInitialEmptyDocument) - return; -#endif - -#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size - // If loading a webarchive, run through webarchive machinery - const String& responseMIMEType = loader->responseMIMEType(); - - // FIXME: Mac's FrameLoaderClient::finishedLoading() method does work that is required even with Archive loads - // so we still need to call it. Other platforms should only call finishLoading for non-archive loads - // That work should be factored out so this #ifdef can be removed -#if PLATFORM(MAC) - m_client->finishedLoading(loader); - if (!ArchiveFactory::isArchiveMimeType(responseMIMEType)) - return; -#else - if (!ArchiveFactory::isArchiveMimeType(responseMIMEType)) { - m_client->finishedLoading(loader); - return; - } -#endif - - RefPtr<Archive> archive(ArchiveFactory::create(loader->mainResourceData().get(), responseMIMEType)); - if (!archive) - return; - - loader->addAllArchiveResources(archive.get()); - - ArchiveResource* mainResource = archive->mainResource(); - loader->setParsedArchiveData(mainResource->data()); - continueLoadWithData(mainResource->data(), mainResource->mimeType(), mainResource->textEncoding(), mainResource->url()); -#else - m_client->finishedLoading(loader); -#endif -} - -bool FrameLoader::isReplacing() const -{ - return m_loadType == FrameLoadTypeReplace; -} - -void FrameLoader::setReplacing() -{ - m_loadType = FrameLoadTypeReplace; -} - -void FrameLoader::revertToProvisional(DocumentLoader* loader) -{ - m_client->revertToProvisionalState(loader); -} - -bool FrameLoader::subframeIsLoading() const -{ - // It's most likely that the last added frame is the last to load so we walk backwards. - for (Frame* child = m_frame->tree()->lastChild(); child; child = child->tree()->previousSibling()) { - FrameLoader* childLoader = child->loader(); - DocumentLoader* documentLoader = childLoader->documentLoader(); - if (documentLoader && documentLoader->isLoadingInAPISense()) - return true; - documentLoader = childLoader->provisionalDocumentLoader(); - if (documentLoader && documentLoader->isLoadingInAPISense()) - return true; - } - return false; -} - -void FrameLoader::willChangeTitle(DocumentLoader* loader) -{ - m_client->willChangeTitle(loader); -} - -FrameLoadType FrameLoader::loadType() const -{ - return m_loadType; -} - -void FrameLoader::stopPolicyCheck() -{ - m_client->cancelPolicyCheck(); - PolicyCheck check = m_policyCheck; - m_policyCheck.clear(); - check.cancel(); -} - -void FrameLoader::checkLoadCompleteForThisFrame() -{ - ASSERT(m_client->hasWebView()); - - switch (m_state) { - case FrameStateProvisional: { - if (m_delegateIsHandlingProvisionalLoadError) - return; - - RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader; - if (!pdl) - return; - - // If we've received any errors we may be stuck in the provisional state and actually complete. - const ResourceError& error = pdl->mainDocumentError(); - if (error.isNull()) - return; - - // Check all children first. - RefPtr<HistoryItem> item; - if (Page* page = m_frame->page()) - if (isBackForwardLoadType(loadType()) && m_frame == page->mainFrame()) - item = m_currentHistoryItem; - - bool shouldReset = true; - if (!pdl->isLoadingInAPISense()) { - m_delegateIsHandlingProvisionalLoadError = true; - m_client->dispatchDidFailProvisionalLoad(error); - m_delegateIsHandlingProvisionalLoadError = false; - - // FIXME: can stopping loading here possibly have any effect, if isLoading is false, - // which it must be to be in this branch of the if? And is it OK to just do a full-on - // stopAllLoaders instead of stopLoadingSubframes? - stopLoadingSubframes(); - pdl->stopLoading(); - - // Finish resetting the load state, but only if another load hasn't been started by the - // delegate callback. - if (pdl == m_provisionalDocumentLoader) - clearProvisionalLoad(); - else if (m_provisionalDocumentLoader) { - KURL unreachableURL = m_provisionalDocumentLoader->unreachableURL(); - if (!unreachableURL.isEmpty() && unreachableURL == pdl->request().url()) - shouldReset = false; - } - } - if (shouldReset && item) - if (Page* page = m_frame->page()) - page->backForwardList()->goToItem(item.get()); - return; - } - - case FrameStateCommittedPage: { - DocumentLoader* dl = m_documentLoader.get(); - if (!dl || dl->isLoadingInAPISense()) - return; - - markLoadComplete(); - - // FIXME: Is this subsequent work important if we already navigated away? - // Maybe there are bugs because of that, or extra work we can skip because - // the new page is ready. - - m_client->forceLayoutForNonHTML(); - - // If the user had a scroll point, scroll to it, overriding the anchor point if any. - if (Page* page = m_frame->page()) - if ((isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadTypeReload) && page->backForwardList()) - restoreScrollPositionAndViewState(); - - if (m_creatingInitialEmptyDocument || !m_committedFirstRealDocumentLoad) - return; - - const ResourceError& error = dl->mainDocumentError(); -#ifndef NDEBUG - m_didDispatchDidCommitLoad = false; -#endif - if (!error.isNull()) - m_client->dispatchDidFailLoad(error); - else - m_client->dispatchDidFinishLoad(); - - if (Page* page = m_frame->page()) - page->progress()->progressCompleted(m_frame); - -#ifdef ANDROID_INSTRUMENT - if (!m_frame->tree()->parent()) - android::TimeCounter::report(m_URL, cache()->getLiveSize(), cache()->getDeadSize()); -#endif - return; - } - - 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(); - return; - } - - ASSERT_NOT_REACHED(); -} - -void FrameLoader::continueAfterContentPolicy(PolicyAction policy) -{ - PolicyCheck check = m_policyCheck; - m_policyCheck.clear(); - check.call(policy); -} - -void FrameLoader::continueLoadAfterWillSubmitForm(PolicyAction) -{ - if (!m_provisionalDocumentLoader) - return; - - // DocumentLoader calls back to our prepareForLoadStart - m_provisionalDocumentLoader->prepareForLoadStart(); - - // The load might be cancelled inside of prepareForLoadStart(), nulling out the m_provisionalDocumentLoader, - // so we need to null check it again. - if (!m_provisionalDocumentLoader) - return; - - DocumentLoader* activeDocLoader = activeDocumentLoader(); - if (activeDocLoader && activeDocLoader->isLoadingMainResource()) - return; - - m_provisionalDocumentLoader->setLoadingFromCachedPage(false); - - unsigned long identifier = 0; - - if (Page* page = m_frame->page()) { - identifier = page->progress()->createUniqueIdentifier(); - dispatchAssignIdentifierToInitialRequest(identifier, m_provisionalDocumentLoader.get(), m_provisionalDocumentLoader->originalRequest()); - } - - if (!m_provisionalDocumentLoader->startLoadingMainResource(identifier)) - m_provisionalDocumentLoader->updateLoading(); -} - -void FrameLoader::didFirstLayout() -{ - if (Page* page = m_frame->page()) -#ifdef ANDROID_HISTORY_CLIENT - // this should match the logic in FrameStateCommittedPage, so that we - // can restore the scroll position and view state as early as possible - if ((isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadTypeReload) && page->backForwardList()) -#else - if (isBackForwardLoadType(m_loadType) && page->backForwardList()) -#endif - restoreScrollPositionAndViewState(); - - m_firstLayoutDone = true; - m_client->dispatchDidFirstLayout(); -} - -void FrameLoader::frameLoadCompleted() -{ - m_client->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; -} - -bool FrameLoader::isQuickRedirectComing() const -{ - return m_quickRedirectComing; -} - -void FrameLoader::detachChildren() -{ - // FIXME: Is it really necessary to do this in reverse order? - Frame* previous; - for (Frame* child = m_frame->tree()->lastChild(); child; child = previous) { - previous = child->tree()->previousSibling(); - child->loader()->detachFromParent(); - } -} - -void FrameLoader::recursiveCheckLoadComplete() -{ - Vector<RefPtr<Frame>, 10> frames; - - for (RefPtr<Frame> frame = m_frame->tree()->firstChild(); frame; frame = frame->tree()->nextSibling()) - frames.append(frame); - - unsigned size = frames.size(); - for (unsigned i = 0; i < size; i++) - frames[i]->loader()->recursiveCheckLoadComplete(); - - checkLoadCompleteForThisFrame(); -} - -// Called every time a resource is completely loaded, or an error is received. -void FrameLoader::checkLoadComplete() -{ - ASSERT(m_client->hasWebView()); - - // FIXME: Always traversing the entire frame tree is a bit inefficient, but - // is currently needed in order to null out the previous history item for all frames. - if (Page* page = m_frame->page()) - page->mainFrame()->loader()->recursiveCheckLoadComplete(); -} - -int FrameLoader::numPendingOrLoadingRequests(bool recurse) const -{ - if (!recurse) - return numRequests(m_frame->document()); - - int count = 0; - for (Frame* frame = m_frame; frame; frame = frame->tree()->traverseNext(m_frame)) - count += numRequests(frame->document()); - 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); -} - -void FrameLoader::tokenizerProcessedData() -{ -// ASSERT(m_frame->page()); -// ASSERT(m_frame->document()); - - checkCompleted(); -} - -void FrameLoader::didTellClientAboutLoad(const String& url) -{ - m_urlsClientKnowsAbout.add(url); -} - -bool FrameLoader::haveToldClientAboutLoad(const String& url) -{ - return m_urlsClientKnowsAbout.contains(url); -} - -void FrameLoader::handledOnloadEvents() -{ - m_client->dispatchDidHandleOnloadEvents(); -} - -void FrameLoader::frameDetached() -{ - stopAllLoaders(); - detachFromParent(); -} - -void FrameLoader::detachFromParent() -{ - RefPtr<Frame> protect(m_frame); - - closeURL(); - stopAllLoaders(); - saveScrollPositionAndViewStateToItem(currentHistoryItem()); - detachChildren(); - - if (Page* page = m_frame->page()) - page->inspectorController()->frameDetachedFromParent(m_frame); - - m_client->detachedFromParent2(); - setDocumentLoader(0); - m_client->detachedFromParent3(); - if (Frame* parent = m_frame->tree()->parent()) { - parent->tree()->removeChild(m_frame); - parent->loader()->scheduleCheckCompleted(); - } else { - m_frame->setView(0); - m_frame->pageDestroyed(); - } -} - -void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, bool mainResource, bool alwaysFromRequest) -{ - applyUserAgent(request); - - if (m_loadType == FrameLoadTypeReload) { - request.setCachePolicy(ReloadIgnoringCacheData); - request.setHTTPHeaderField("Cache-Control", "max-age=0"); - } - - // Don't set the cookie policy URL if it's already been set. - if (request.mainDocumentURL().isEmpty()) { - if (mainResource && (isLoadingMainFrame() || alwaysFromRequest)) - 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"); - - // Make sure we send the Origin header. - addHTTPOriginIfNeeded(request, String()); -} - -void FrameLoader::addHTTPOriginIfNeeded(ResourceRequest& request, String origin) -{ - if (!request.httpOrigin().isEmpty()) - return; // Request already has an Origin header. - - // Don't send an Origin header for GET or HEAD to avoid privacy issues. - // For example, if an intranet page has a hyperlink to an external web - // site, we don't want to include the Origin of the request because it - // will leak the internal host name. Similar privacy concerns have lead - // to the widespread suppression of the Referer header at the network - // layer. - if (request.httpMethod() == "GET" || request.httpMethod() == "HEAD") - return; - - // For non-GET and non-HEAD methods, always send an Origin header so the - // server knows we support this feature. - - if (origin.isEmpty()) { - // If we don't know what origin header to attach, we attach the value - // for an empty origin. - origin = SecurityOrigin::createEmpty()->toString(); - } - - request.setHTTPOrigin(origin); -} - -void FrameLoader::committedLoad(DocumentLoader* loader, const char* data, int length) -{ -#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size - if (ArchiveFactory::isArchiveMimeType(loader->response().mimeType())) - return; -#endif - m_client->committedLoad(loader, data, length); -} - -#ifdef ANDROID_USER_GESTURE -void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, - Event* event, PassRefPtr<FormState> prpFormState, bool userGesture) -#else -void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, - Event* event, PassRefPtr<FormState> prpFormState) -#endif -{ - RefPtr<FormState> formState = prpFormState; - - // When posting, use the NSURLRequestReloadIgnoringCacheData load flag. - // This prevents a potential bug which may cause a page with a form that uses itself - // as an action to be returned from the cache without submitting. - - // FIXME: Where's the code that implements what the comment above says? - - // Previously when this method was reached, the original FrameLoadRequest had been deconstructed to build a - // bunch of parameters that would come in here and then be built back up to a ResourceRequest. In case - // any caller depends on the immutability of the original ResourceRequest, I'm rebuilding a ResourceRequest - // from scratch as it did all along. - const KURL& url = inRequest.url(); - RefPtr<FormData> formData = inRequest.httpBody(); - const String& contentType = inRequest.httpContentType(); - String origin = inRequest.httpOrigin(); - - ResourceRequest workingResourceRequest(url); -#ifdef ANDROID_USER_GESTURE - workingResourceRequest.setUserGesture(userGesture); -#endif - - if (!referrer.isEmpty()) - workingResourceRequest.setHTTPReferrer(referrer); - workingResourceRequest.setHTTPOrigin(origin); - workingResourceRequest.setHTTPMethod("POST"); - workingResourceRequest.setHTTPBody(formData); - workingResourceRequest.setHTTPContentType(contentType); - addExtraFieldsToRequest(workingResourceRequest, true, true); - - NavigationAction action(url, FrameLoadTypeStandard, true, event); - - if (!frameName.isEmpty()) { - if (Frame* targetFrame = findFrameForNavigation(frameName)) - targetFrame->loader()->loadWithNavigationAction(workingResourceRequest, action, FrameLoadTypeStandard, formState.release()); - else - checkNewWindowPolicy(action, workingResourceRequest, formState.release(), frameName); - } else - loadWithNavigationAction(workingResourceRequest, action, FrameLoadTypeStandard, formState.release()); -} - -bool FrameLoader::isReloading() const -{ - return documentLoader()->request().cachePolicy() == ReloadIgnoringCacheData; -} - -void FrameLoader::loadEmptyDocumentSynchronously() -{ - ResourceRequest request(KURL("")); - load(request); -} - -unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, 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(); - - ResourceRequest initialRequest = request; - initialRequest.setTimeoutInterval(10); - - if (initialRequest.isConditional()) - initialRequest.setCachePolicy(ReloadIgnoringCacheData); - else - initialRequest.setCachePolicy(documentLoader()->request().cachePolicy()); - - if (!referrer.isEmpty()) - initialRequest.setHTTPReferrer(referrer); - addHTTPOriginIfNeeded(initialRequest, outgoingOrigin()); - - if (Page* page = m_frame->page()) - initialRequest.setMainDocumentURL(page->mainFrame()->loader()->documentLoader()->request().url()); - initialRequest.setHTTPUserAgent(client()->userAgent(request.url())); - - unsigned long identifier = 0; - ResourceRequest newRequest(initialRequest); - requestFromDelegate(newRequest, identifier, error); - - if (error.isNull()) { - ASSERT(!newRequest.isNull()); - didTellClientAboutLoad(newRequest.url().string()); - -#if ENABLE(OFFLINE_WEB_APPLICATIONS) - ApplicationCacheResource* resource; - if (documentLoader()->shouldLoadResourceFromApplicationCache(newRequest, resource)) { - if (resource) { - response = resource->response(); - data.append(resource->data()->data(), resource->data()->size()); - } else - error = cannotShowURLError(newRequest); - } else -#endif - ResourceHandle::loadResourceSynchronously(newRequest, error, response, data, m_frame); - } - - sendRemainingDelegateMessages(identifier, response, data.size(), error); - return identifier; -} - -void FrameLoader::assignIdentifierToInitialRequest(unsigned long identifier, const ResourceRequest& clientRequest) -{ - return dispatchAssignIdentifierToInitialRequest(identifier, activeDocumentLoader(), clientRequest); -} - -void FrameLoader::willSendRequest(ResourceLoader* loader, ResourceRequest& clientRequest, const ResourceResponse& redirectResponse) -{ - applyUserAgent(clientRequest); - dispatchWillSendRequest(loader->documentLoader(), loader->identifier(), clientRequest, redirectResponse); -} - -void FrameLoader::didReceiveResponse(ResourceLoader* loader, const ResourceResponse& r) -{ - activeDocumentLoader()->addResponse(r); - - if (Page* page = m_frame->page()) - page->progress()->incrementProgress(loader->identifier(), r); - dispatchDidReceiveResponse(loader->documentLoader(), loader->identifier(), r); -} - -void FrameLoader::didReceiveData(ResourceLoader* loader, const char* data, int length, int lengthReceived) -{ - if (Page* page = m_frame->page()) - page->progress()->incrementProgress(loader->identifier(), data, length); - dispatchDidReceiveContentLength(loader->documentLoader(), loader->identifier(), lengthReceived); -} - -void FrameLoader::didFailToLoad(ResourceLoader* loader, const ResourceError& error) -{ - if (Page* page = m_frame->page()) - page->progress()->completeProgress(loader->identifier()); - if (!error.isNull()) - m_client->dispatchDidFailLoading(loader->documentLoader(), loader->identifier(), error); -} - -const ResourceRequest& FrameLoader::originalRequest() const -{ - return activeDocumentLoader()->originalRequestCopy(); -} - -void FrameLoader::receivedMainResourceError(const ResourceError& error, bool isComplete) -{ - // Retain because the stop may release the last reference to it. - RefPtr<Frame> protect(m_frame); - - RefPtr<DocumentLoader> loader = activeDocumentLoader(); - - if (isComplete) { - // FIXME: Don't want to do this if an entirely new load is going, so should check - // that both data sources on the frame are either this or nil. - stop(); - if (m_client->shouldFallBack(error)) - handleFallbackContent(); - } - - if (m_state == FrameStateProvisional && m_provisionalDocumentLoader) { - KURL failedURL = m_provisionalDocumentLoader->originalRequestCopy().url(); - didNotOpenURL(failedURL); - - // 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 - // so that the existing view (that wenever got far enough to replace) can continue being used. - invalidateCurrentItemCachedPage(); - - // Call clientRedirectCancelledOrFinished here so that the frame load delegate is notified that the redirect's - // status has changed, if there was a redirect. The frame load delegate may have saved some state about - // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:. Since we are definitely - // not going to use this provisional resource, as it was cancelled, notify the frame load delegate that the redirect - // has ended. - if (m_sentRedirectNotification) - clientRedirectCancelledOrFinished(false); - } - - - loader->mainReceivedError(error, isComplete); -} - -void FrameLoader::callContinueFragmentScrollAfterNavigationPolicy(void* argument, - const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue) -{ - FrameLoader* loader = static_cast<FrameLoader*>(argument); - loader->continueFragmentScrollAfterNavigationPolicy(request, shouldContinue); -} - -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; - m_quickRedirectComing = false; - - if (!shouldContinue) - return; - - KURL url = request.url(); - - m_documentLoader->replaceRequestURLForAnchorScroll(url); - if (!isRedirect && !shouldTreatURLAsSameAsCurrent(url)) { - // NB: must happen after _setURL, since we add based on the current request. - // Must also happen before we openURL and displace the scroll position, since - // adding the BF item will save away scroll state. - - // NB2: If we were loading a long, slow doc, and the user anchor nav'ed before - // it was done, currItem is now set the that slow doc, and prevItem is whatever was - // before it. Adding the b/f item will bump the slow doc down to prevItem, even - // though its load is not yet done. I think this all works out OK, for one because - // we have already saved away the scroll and doc state for the long slow load, - // but it's not an obvious case. - - addHistoryItemForFragmentScroll(); - } - - scrollToAnchor(url); - - if (!isRedirect) - // This will clear previousItem from the rest of the frame tree that didn't - // doing any loading. We need to make a pass on this now, since for anchor nav - // we'll not go through a real load and reach Completed state. - checkLoadComplete(); - - m_client->dispatchDidChangeLocationWithinPage(); - m_client->didFinishLoad(); -} - -bool FrameLoader::shouldScrollToAnchor(bool isFormSubmission, FrameLoadType loadType, const KURL& url) -{ - // Should we do anchor navigation within the existing content? - - // We don't do this if we are submitting a form, explicitly reloading, - // currently displaying a frameset, or if the URL does not have a fragment. - // These rules were originally based on what KHTML was doing in KHTMLPart::openURL. - - // FIXME: What about load types other than Standard and Reload? - - return !isFormSubmission - && loadType != FrameLoadTypeReload - && loadType != FrameLoadTypeSame - && !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(); - } -} - -void FrameLoader::checkNewWindowPolicy(const NavigationAction& action, const ResourceRequest& request, - PassRefPtr<FormState> formState, const String& frameName) -{ - m_policyCheck.set(request, formState, frameName, - callContinueLoadAfterNewWindowPolicy, this); - m_client->dispatchDecidePolicyForNewWindowAction(&FrameLoader::continueAfterNewWindowPolicy, - action, request, formState, frameName); -} - -void FrameLoader::continueAfterNewWindowPolicy(PolicyAction policy) -{ - PolicyCheck check = m_policyCheck; - m_policyCheck.clear(); - - switch (policy) { - case PolicyIgnore: - check.clearRequest(); - break; - case PolicyDownload: - m_client->startDownload(check.request()); - check.clearRequest(); - break; - case PolicyUse: - break; - } - - check.call(policy == PolicyUse); -} - -void FrameLoader::checkNavigationPolicy(const ResourceRequest& request, DocumentLoader* loader, - PassRefPtr<FormState> formState, NavigationPolicyDecisionFunction function, void* argument) -{ - NavigationAction action = loader->triggeringAction(); - if (action.isEmpty()) { - action = NavigationAction(request.url(), NavigationTypeOther); - loader->setTriggeringAction(action); - } - - // Don't ask more than once for the same request or if we are loading an empty URL. - // This avoids confusion on the part of the client. - if (equalIgnoringHeaderFields(request, loader->lastCheckedRequest()) || (!request.isNull() && request.url().isEmpty())) { - function(argument, request, 0, true); - loader->setLastCheckedRequest(request); - return; - } - - // We are always willing to show alternate content for unreachable URLs; - // treat it like a reload so it maintains the right state for b/f list. - if (loader->substituteData().isValid() && !loader->substituteData().failingURL().isEmpty()) { - if (isBackForwardLoadType(m_policyLoadType)) - m_policyLoadType = FrameLoadTypeReload; - function(argument, request, 0, true); - return; - } - - loader->setLastCheckedRequest(request); - - m_policyCheck.set(request, formState.get(), function, argument); - - m_delegateIsDecidingNavigationPolicy = true; - m_client->dispatchDecidePolicyForNavigationAction(&FrameLoader::continueAfterNavigationPolicy, - action, request, formState); - m_delegateIsDecidingNavigationPolicy = false; -} - -void FrameLoader::continueAfterNavigationPolicy(PolicyAction policy) -{ - PolicyCheck check = m_policyCheck; - m_policyCheck.clear(); - - bool shouldContinue = policy == PolicyUse; - - switch (policy) { - case PolicyIgnore: - check.clearRequest(); - break; - case PolicyDownload: - m_client->startDownload(check.request()); - check.clearRequest(); - break; - case PolicyUse: { - ResourceRequest request(check.request()); - - if (!m_client->canHandleRequest(request)) { - handleUnimplementablePolicy(m_client->cannotShowURLError(check.request())); - check.clearRequest(); - shouldContinue = false; - } - break; - } - } - - check.call(shouldContinue); -} - -void FrameLoader::callContinueLoadAfterNavigationPolicy(void* argument, - const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue) -{ - FrameLoader* loader = static_cast<FrameLoader*>(argument); - loader->continueLoadAfterNavigationPolicy(request, formState, shouldContinue); -} - -void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue) -{ - // If we loaded an alternate page to replace an unreachableURL, we'll get in here with a - // nil policyDataSource because loading the alternate page will have passed - // through this method already, nested; otherwise, policyDataSource should still be set. - ASSERT(m_policyDocumentLoader || !m_provisionalDocumentLoader->unreachableURL().isEmpty()); - - bool isTargetItem = m_provisionalHistoryItem ? m_provisionalHistoryItem->isTargetItem() : false; - - // Two reasons we can't continue: - // 1) Navigation policy delegate said we can't so request is nil. A primary case of this - // 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()); - - if (!canContinue) { - // If we were waiting for a quick redirect, but the policy delegate decided to ignore it, then we - // need to report that the client redirect was cancelled. - if (m_quickRedirectComing) - clientRedirectCancelledOrFinished(false); - - setPolicyDocumentLoader(0); - - // If the navigation request came from the back/forward menu, and we punt on it, we have the - // problem that we have optimistically moved the b/f cursor already, so move it back. For sanity, - // we only do this when punting a navigation for the target frame or top-level frame. - if ((isTargetItem || isLoadingMainFrame()) && isBackForwardLoadType(m_policyLoadType)) - if (Page* page = m_frame->page()) { - Frame* mainFrame = page->mainFrame(); - if (HistoryItem* resetItem = mainFrame->loader()->m_currentHistoryItem.get()) - page->backForwardList()->goToItem(resetItem); - } - return; - } - - FrameLoadType type = m_policyLoadType; - stopAllLoaders(); - - // <rdar://problem/6250856> - In certain circumstances on pages with multiple frames, stopAllLoaders() - // might detach the current FrameLoader, in which case we should bail on this newly defunct load. - if (!m_frame->page()) - return; - - setProvisionalDocumentLoader(m_policyDocumentLoader.get()); - m_loadType = type; - setState(FrameStateProvisional); - - setPolicyDocumentLoader(0); - - if (isBackForwardLoadType(type) && loadProvisionalItemFromCachedPage()) - return; - - if (formState) - m_client->dispatchWillSubmitForm(&FrameLoader::continueLoadAfterWillSubmitForm, formState); - else - continueLoadAfterWillSubmitForm(); -} - - -void FrameLoader::callContinueLoadAfterNewWindowPolicy(void* argument, - const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, bool shouldContinue) -{ - FrameLoader* loader = static_cast<FrameLoader*>(argument); - loader->continueLoadAfterNewWindowPolicy(request, formState, frameName, shouldContinue); -} - -void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& request, - PassRefPtr<FormState> formState, const String& frameName, bool shouldContinue) -{ - if (!shouldContinue) - return; - - RefPtr<Frame> frame = m_frame; - RefPtr<Frame> mainFrame = m_client->dispatchCreatePage(); - if (!mainFrame) - return; - - if (frameName != "_blank") - mainFrame->tree()->setName(frameName); - - mainFrame->loader()->setOpenedByDOM(); - mainFrame->loader()->m_client->dispatchShow(); - mainFrame->loader()->setOpener(frame.get()); - mainFrame->loader()->loadWithNavigationAction(request, NavigationAction(), FrameLoadTypeStandard, formState); -} - -void FrameLoader::sendRemainingDelegateMessages(unsigned long identifier, const ResourceResponse& response, int length, const ResourceError& error) -{ - if (!response.isNull()) - dispatchDidReceiveResponse(m_documentLoader.get(), identifier, response); - - if (length > 0) - dispatchDidReceiveContentLength(m_documentLoader.get(), identifier, length); - - if (error.isNull()) - dispatchDidFinishLoading(m_documentLoader.get(), identifier); - else - m_client->dispatchDidFailLoading(m_documentLoader.get(), identifier, error); -} - -void FrameLoader::requestFromDelegate(ResourceRequest& request, unsigned long& identifier, ResourceError& error) -{ - ASSERT(!request.isNull()); - - identifier = 0; - if (Page* page = m_frame->page()) { - identifier = page->progress()->createUniqueIdentifier(); - dispatchAssignIdentifierToInitialRequest(identifier, m_documentLoader.get(), request); - } - - ResourceRequest newRequest(request); - dispatchWillSendRequest(m_documentLoader.get(), identifier, newRequest, ResourceResponse()); - - if (newRequest.isNull()) - error = cancelledError(request); - else - error = ResourceError(); - - request = newRequest; -} - -void FrameLoader::loadedResourceFromMemoryCache(const CachedResource* resource) -{ - ResourceRequest request(resource->url()); - const ResourceResponse& response = resource->response(); - SharedBuffer* data = resource->data(); - int length = data ? data->size() : 0; - - if (Page* page = m_frame->page()) - page->inspectorController()->didLoadResourceFromMemoryCache(m_documentLoader.get(), request, response, length); - - if (!resource->sendResourceLoadCallbacks() || haveToldClientAboutLoad(resource->url())) - return; - - if (m_client->dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), request, response, length)) { - didTellClientAboutLoad(resource->url()); - return; - } - - unsigned long identifier; - ResourceError error; - ResourceRequest r(request); - requestFromDelegate(r, identifier, error); - sendRemainingDelegateMessages(identifier, response, length, error); - - didTellClientAboutLoad(resource->url()); -} - -void FrameLoader::applyUserAgent(ResourceRequest& request) -{ - String userAgent = client()->userAgent(request.url()); - ASSERT(!userAgent.isNull()); - request.setHTTPUserAgent(userAgent); -} - -bool FrameLoader::canGoBackOrForward(int distance) const -{ - if (Page* page = m_frame->page()) { - if (distance == 0) - return true; - if (distance > 0 && distance <= page->backForwardList()->forwardListCount()) - return true; - if (distance < 0 && -distance <= page->backForwardList()->backListCount()) - return true; - } - return false; -} - -int FrameLoader::getHistoryLength() -{ - if (Page* page = m_frame->page()) - return page->backForwardList()->backListCount() + 1; - 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); -} - -bool FrameLoader::loadProvisionalItemFromCachedPage() -{ - RefPtr<CachedPage> cachedPage = pageCache()->get(m_provisionalHistoryItem.get()); - if (!cachedPage || !cachedPage->document()) - return false; - provisionalDocumentLoader()->loadFromCachedPage(cachedPage.release()); - return true; -} - -void FrameLoader::cachePageForHistoryItem(HistoryItem* item) -{ - 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()); - } -} - -bool FrameLoader::shouldTreatURLAsSameAsCurrent(const KURL& url) const -{ - if (!m_currentHistoryItem) - return false; - return url == m_currentHistoryItem->url() || url == m_currentHistoryItem->originalURL(); -} - -PassRefPtr<HistoryItem> FrameLoader::createHistoryItem(bool useOriginal) -{ - DocumentLoader* docLoader = documentLoader(); - - KURL unreachableURL = docLoader ? docLoader->unreachableURL() : KURL(); - - KURL url; - KURL originalURL; - - if (!unreachableURL.isEmpty()) { - url = unreachableURL; - originalURL = unreachableURL; - } else { - originalURL = docLoader ? docLoader->originalURL() : KURL(); - if (useOriginal) - url = originalURL; - else if (docLoader) - url = docLoader->requestURL(); - } - - LOG(History, "WebCoreHistory: Creating item for %s", url.string().ascii().data()); - - // Frames that have never successfully loaded any content - // may have no URL at all. Currently our history code can't - // deal with such things, so we nip that in the bud here. - // Later we may want to learn to live with nil for URL. - // See bug 3368236 and related bugs for more information. - if (url.isEmpty()) - url = blankURL(); - if (originalURL.isEmpty()) - originalURL = blankURL(); - - Frame* parentFrame = m_frame->tree()->parent(); - String parent = parentFrame ? parentFrame->tree()->name() : ""; - String title = docLoader ? docLoader->title() : ""; - - RefPtr<HistoryItem> item = HistoryItem::create(url, m_frame->tree()->name(), parent, title); - item->setOriginalURLString(originalURL.string()); - - // Save form state if this is a POST - if (docLoader) { - if (useOriginal) - item->setFormInfoFromRequest(docLoader->originalRequest()); - else - item->setFormInfoFromRequest(docLoader->request()); - } - - // Set the item for which we will save document state - m_previousHistoryItem = m_currentHistoryItem; - m_currentHistoryItem = item; - - return item.release(); -} - -void FrameLoader::addBackForwardItemClippedAtTarget(bool doClip) -{ - Page* page = m_frame->page(); - if (!page) - return; - - if (documentLoader()->urlForHistory().isEmpty()) - return; - - Frame* mainFrame = page->mainFrame(); - ASSERT(mainFrame); - FrameLoader* frameLoader = mainFrame->loader(); - - if (!frameLoader->m_didPerformFirstNavigation && page->backForwardList()->entries().size() == 1) { - frameLoader->m_didPerformFirstNavigation = true; - m_client->didPerformFirstNavigation(); - } - - RefPtr<HistoryItem> item = frameLoader->createHistoryItemTree(m_frame, doClip); - LOG(BackForward, "WebCoreBackForward - Adding backforward item %p for frame %s", item.get(), documentLoader()->url().string().ascii().data()); - page->backForwardList()->addItem(item); -} - -PassRefPtr<HistoryItem> FrameLoader::createHistoryItemTree(Frame* targetFrame, bool clipAtTarget) -{ - RefPtr<HistoryItem> bfItem = createHistoryItem(m_frame->tree()->parent() ? true : false); - if (m_previousHistoryItem) - saveScrollPositionAndViewStateToItem(m_previousHistoryItem.get()); - if (!(clipAtTarget && m_frame == targetFrame)) { - // save frame state for items that aren't loading (khtml doesn't save those) - saveDocumentState(); - for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) { - FrameLoader* childLoader = child->loader(); - bool hasChildLoaded = childLoader->frameHasLoaded(); - - // If the child is a frame corresponding to an <object> element that never loaded, - // we don't want to create a history item, because that causes fallback content - // to be ignored on reload. - - if (!(!hasChildLoaded && childLoader->isHostedByObjectElement())) - bfItem->addChildItem(childLoader->createHistoryItemTree(targetFrame, clipAtTarget)); - } - } - if (m_frame == targetFrame) - bfItem->setIsTargetItem(true); - return bfItem; -} - -Frame* FrameLoader::findFrameForNavigation(const AtomicString& name) -{ - Frame* frame = m_frame->tree()->find(name); - if (shouldAllowNavigation(frame)) - return frame; - return 0; -} - -void FrameLoader::saveScrollPositionAndViewStateToItem(HistoryItem* item) -{ - if (!item || !m_frame->view()) - return; - - item->setScrollPoint(m_frame->view()->scrollPosition()); - // FIXME: It would be great to work out a way to put this code in WebCore instead of calling through to the client. - m_client->saveViewStateToItem(item); -} - -/* - There is a race condition between the layout and load completion that affects restoring the scroll position. - We try to restore the scroll position at both the first layout and upon load completion. - - 1) If first layout happens before the load completes, we want to restore the scroll position then so that the - first time we draw the page is already scrolled to the right place, instead of starting at the top and later - jumping down. It is possible that the old scroll position is past the part of the doc laid out so far, in - which case the restore silent fails and we will fix it in when we try to restore on doc completion. - 2) If the layout happens after the load completes, the attempt to restore at load completion time silently - fails. We then successfully restore it when the layout happens. -*/ -void FrameLoader::restoreScrollPositionAndViewState() -{ - if (!m_committedFirstRealDocumentLoad) - return; - - ASSERT(m_currentHistoryItem); - - // FIXME: As the ASSERT attests, it seems we should always have a currentItem here. - // One counterexample is <rdar://problem/4917290> - // For now, to cover this issue in release builds, there is no technical harm to returning - // early and from a user standpoint - as in the above radar - the previous page load failed - // so there *is* no scroll or view state to restore! - if (!m_currentHistoryItem) - return; - - // FIXME: It would be great to work out a way to put this code in WebCore instead of calling - // through to the client. It's currently used only for the PDF view on Mac. - m_client->restoreViewState(); - - if (FrameView* view = m_frame->view()) - if (!view->wasScrolledByUser()) - view->setScrollPosition(m_currentHistoryItem->scrollPoint()); -} - -void FrameLoader::invalidateCurrentItemCachedPage() -{ - // When we are pre-commit, the currentItem is where the pageCache data resides - CachedPage* cachedPage = pageCache()->get(m_currentHistoryItem.get()); - - // FIXME: This is a grotesque hack to fix <rdar://problem/4059059> Crash in RenderFlow::detach - // Somehow the PageState object is not properly updated, and is holding onto a stale document. - // Both Xcode and FileMaker see this crash, Safari does not. - - ASSERT(!cachedPage || cachedPage->document() == m_frame->document()); - if (cachedPage && cachedPage->document() == m_frame->document()) { - cachedPage->document()->setInPageCache(false); - cachedPage->clear(); - } - - if (cachedPage) - pageCache()->remove(m_currentHistoryItem.get()); -} - -void FrameLoader::saveDocumentState() -{ - if (m_creatingInitialEmptyDocument) - return; - - // For a standard page load, we will have a previous item set, which will be used to - // store the form state. However, in some cases we will have no previous item, and - // the current item is the right place to save the state. One example is when we - // detach a bunch of frames because we are navigating from a site with frames to - // another site. Another is when saving the frame state of a frame that is not the - // target of the current navigation (if we even decide to save with that granularity). - - // Because of previousItem's "masking" of currentItem for this purpose, it's important - // that previousItem be cleared at the end of a page transition. We leverage the - // checkLoadComplete recursion to achieve this goal. - - HistoryItem* item = m_previousHistoryItem ? m_previousHistoryItem.get() : m_currentHistoryItem.get(); - if (!item) - return; - - Document* document = m_frame->document(); - ASSERT(document); - - if (document && item->isCurrentDocument(document)) { - LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree()->name().string().utf8().data(), item); - item->setDocumentState(document->formElementsState()); - } -} - -// Loads content into this frame, as specified by history item -void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) -{ - if (!m_frame->page()) - return; - - KURL itemURL = item->url(); - KURL itemOriginalURL = item->originalURL(); - KURL currentURL; - if (documentLoader()) - currentURL = documentLoader()->url(); - RefPtr<FormData> formData = item->formData(); - - // Are we navigating to an anchor within the page? - // 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. - - if (!formData && urlsMatchItem(item)) { - // Must do this maintenance here, since we don't go through a real page reload - saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get()); - - if (FrameView* view = m_frame->view()) - view->setWasScrolledByUser(false); - - m_currentHistoryItem = item; - - // FIXME: Form state might need to be saved here too. - - // We always call scrollToAnchor here, even if the URL doesn't have an - // anchor fragment. This is so we'll keep the WebCore Frame's URL up-to-date. - scrollToAnchor(item->url()); - - // must do this maintenance here, since we don't go through a real page reload - restoreScrollPositionAndViewState(); - - // Fake the URL change by updating the data source's request. This will no longer - // be necessary if we do the better fix described above. - documentLoader()->replaceRequestURLForAnchorScroll(itemURL); - - m_client->dispatchDidChangeLocationWithinPage(); - - // FrameLoaderClient::didFinishLoad() tells the internal load delegate the load finished with no error - m_client->didFinishLoad(); - } else { - // Remember this item so we can traverse any child items as child frames load - m_provisionalHistoryItem = item; - - bool inPageCache = false; - - // 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)) { - double interval = currentTime() - cachedPage->timeStamp(); - - // FIXME: 1800 should not be hardcoded, it should come from - // WebKitBackForwardCacheExpirationIntervalKey in WebKit. - // Or we should remove WebKitBackForwardCacheExpirationIntervalKey. - if (interval <= 1800) { - loadWithDocumentLoader(cachedPage->documentLoader(), loadType, 0); - inPageCache = true; - } else { - LOG(PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", m_provisionalHistoryItem->url().string().ascii().data()); - pageCache()->remove(item); - } - } - - if (!inPageCache) { - ResourceRequest request(itemURL); - - // 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()); - - request.setHTTPMethod("POST"); - request.setHTTPReferrer(item->formReferrer()); - request.setHTTPBody(formData); - request.setHTTPContentType(item->formContentType()); - RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::createFromString(item->formReferrer()); - addHTTPOriginIfNeeded(request, securityOrigin->toString()); - - // FIXME: Slight hack to test if the NSURL cache contains the page we're going to. - // We want to know this before talking to the policy delegate, since it affects whether - // we show the DoYouReallyWantToRepost nag. - // - // This trick has a small bug (3123893) where we might find a cache hit, but then - // have the item vanish when we try to use it in the ensuing nav. This should be - // extremely rare, but in that case the user will get an error on the navigation. - - if (ResourceHandle::willLoadFromCache(request)) - action = NavigationAction(itemURL, loadType, false); - else { - request.setCachePolicy(ReloadIgnoringCacheData); - action = NavigationAction(itemURL, NavigationTypeFormResubmitted); - } - } else { - switch (loadType) { - case FrameLoadTypeReload: - request.setCachePolicy(ReloadIgnoringCacheData); - break; - case FrameLoadTypeBack: - case FrameLoadTypeForward: - case FrameLoadTypeIndexedBackForward: - if (itemURL.protocol() != "https") - request.setCachePolicy(ReturnCacheDataElseLoad); - break; - case FrameLoadTypeStandard: - case FrameLoadTypeRedirectWithLockedHistory: - // no-op: leave as protocol default - // FIXME: I wonder if we ever hit this case - break; - case FrameLoadTypeSame: - case FrameLoadTypeReloadAllowingStaleData: - default: - ASSERT_NOT_REACHED(); - } - - action = NavigationAction(itemOriginalURL, loadType, false); - } - - addExtraFieldsToRequest(request, true, formData); - loadWithNavigationAction(request, action, loadType, 0); - } - } -} - -// Walk the frame tree and ensure that the URLs match the URLs in the item. -bool FrameLoader::urlsMatchItem(HistoryItem* item) const -{ - const KURL& currentURL = documentLoader()->url(); - if (!equalIgnoringRef(currentURL, item->url())) - return false; - - const HistoryItemVector& childItems = item->children(); - - unsigned size = childItems.size(); - for (unsigned i = 0; i < size; ++i) { - Frame* childFrame = m_frame->tree()->child(childItems[i]->target()); - if (childFrame && !childFrame->loader()->urlsMatchItem(childItems[i].get())) - return false; - } - - return true; -} - -// Main funnel for navigating to a previous location (back/forward, non-search snap-back) -// This includes recursion to handle loading into framesets properly -void FrameLoader::goToItem(HistoryItem* targetItem, FrameLoadType type) -{ - ASSERT(!m_frame->tree()->parent()); - - // shouldGoToHistoryItem is a private delegate method. This is needed to fix: - // <rdar://problem/3951283> can view pages from the back/forward cache that should be disallowed by Parental Controls - // Ultimately, history item navigations should go through the policy delegate. That's covered in: - // <rdar://problem/3979539> back/forward cache navigations should consult policy delegate - Page* page = m_frame->page(); - if (!page) - return; - if (!m_client->shouldGoToHistoryItem(targetItem)) - return; - - // Set the BF cursor before commit, which lets the user quickly click back/forward again. - // - plus, it only makes sense for the top level of the operation through the frametree, - // as opposed to happening for some/one of the page commits that might happen soon - BackForwardList* bfList = page->backForwardList(); - HistoryItem* currentItem = bfList->currentItem(); - bfList->goToItem(targetItem); - recursiveGoToItem(targetItem, currentItem, type); -} - -// The general idea here is to traverse the frame tree and the item tree in parallel, -// tracking whether each frame already has the content the item requests. If there is -// a match (by URL), we just restore scroll position and recurse. Otherwise we must -// reload that frame, and all its kids. -void FrameLoader::recursiveGoToItem(HistoryItem* item, HistoryItem* fromItem, FrameLoadType type) -{ - ASSERT(item); - ASSERT(fromItem); - - KURL itemURL = item->url(); - KURL currentURL; - if (documentLoader()) - currentURL = documentLoader()->url(); - - // Always reload the target frame of the item we're going to. This ensures that we will - // do -some- load for the transition, which means a proper notification will be posted - // to the app. - // The exact URL has to match, including fragment. We want to go through the _load - // method, even if to do a within-page navigation. - // The current frame tree and the frame tree snapshot in the item have to match. - if (!item->isTargetItem() && - itemURL == currentURL && - ((m_frame->tree()->name().isEmpty() && item->target().isEmpty()) || m_frame->tree()->name() == item->target()) && - childFramesMatchItem(item)) - { - // This content is good, so leave it alone and look for children that need reloading - // Save form state (works from currentItem, since prevItem is nil) - ASSERT(!m_previousHistoryItem); - saveDocumentState(); - saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get()); - - if (FrameView* view = m_frame->view()) - view->setWasScrolledByUser(false); - - m_currentHistoryItem = item; - - // Restore form state (works from currentItem) - restoreDocumentState(); - - // Restore the scroll position (we choose to do this rather than going back to the anchor point) - restoreScrollPositionAndViewState(); - - const HistoryItemVector& childItems = item->children(); - - int size = childItems.size(); - for (int i = 0; i < size; ++i) { - String childName = childItems[i]->target(); - HistoryItem* fromChildItem = fromItem->childItemWithName(childName); - ASSERT(fromChildItem || fromItem->isTargetItem()); - Frame* childFrame = m_frame->tree()->child(childName); - ASSERT(childFrame); - childFrame->loader()->recursiveGoToItem(childItems[i].get(), fromChildItem, type); - } - } else { - loadItem(item, type); - } -} - -// helper method that determines whether the subframes described by the item's subitems -// match our own current frameset -bool FrameLoader::childFramesMatchItem(HistoryItem* item) const -{ - const HistoryItemVector& childItems = item->children(); - if (childItems.size() != m_frame->tree()->childCount()) - return false; - - unsigned size = childItems.size(); - 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; -} - -// There are 3 things you might think of as "history", all of which are handled by these functions. -// -// 1) Back/forward: The m_currentHistoryItem is part of this mechanism. -// 2) Global history: Handled by the client. -// 3) Visited links: Handled by the PageGroup. - -void FrameLoader::updateHistoryForStandardLoad() -{ - LOG(History, "WebCoreHistory: Updating History for Standard Load in frame %s", documentLoader()->url().string().ascii().data()); - - Settings* settings = m_frame->settings(); - 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 (!historyURL.isEmpty()) { - addBackForwardItemClippedAtTarget(true); - if (!needPrivacy) - m_client->updateGlobalHistory(historyURL); - } - } else if (documentLoader()->unreachableURL().isEmpty() && m_currentHistoryItem) { - m_currentHistoryItem->setURL(documentLoader()->url()); - m_currentHistoryItem->setFormInfoFromRequest(documentLoader()->request()); - } - - if (!historyURL.isEmpty() && !needPrivacy) { - if (Page* page = m_frame->page()) - page->group().addVisitedLink(historyURL); - } -} - -void FrameLoader::updateHistoryForClientRedirect() -{ -#if !LOG_DISABLED - if (documentLoader()) - LOG(History, "WebCoreHistory: Updating History for client redirect in frame %s", documentLoader()->title().utf8().data()); -#endif - - // Clear out form data so we don't try to restore it into the incoming page. Must happen after - // webcore has closed the URL and saved away the form state. - if (m_currentHistoryItem) { - m_currentHistoryItem->clearDocumentState(); - m_currentHistoryItem->clearScrollPoint(); - } - - Settings* settings = m_frame->settings(); - bool needPrivacy = !settings || settings->privateBrowsingEnabled(); - const KURL& historyURL = documentLoader()->urlForHistory(); - - if (!historyURL.isEmpty() && !needPrivacy) { - if (Page* page = m_frame->page()) - page->group().addVisitedLink(historyURL); - } -} - -void FrameLoader::updateHistoryForBackForwardNavigation() -{ -#if !LOG_DISABLED - if (documentLoader()) - LOG(History, "WebCoreHistory: Updating History for back/forward navigation in frame %s", documentLoader()->title().utf8().data()); -#endif - - // Must grab the current scroll position before disturbing it - saveScrollPositionAndViewStateToItem(m_previousHistoryItem.get()); -} - -void FrameLoader::updateHistoryForReload() -{ -#if !LOG_DISABLED - if (documentLoader()) - LOG(History, "WebCoreHistory: Updating History for reload in frame %s", documentLoader()->title().utf8().data()); -#endif - - if (m_currentHistoryItem) { - pageCache()->remove(m_currentHistoryItem.get()); - - if (loadType() == FrameLoadTypeReload) - saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get()); - - // Sometimes loading a page again leads to a different result because of cookies. Bugzilla 4072 - if (documentLoader()->unreachableURL().isEmpty()) - m_currentHistoryItem->setURL(documentLoader()->requestURL()); - } -} - -void FrameLoader::updateHistoryForRedirectWithLockedHistory() -{ -#if !LOG_DISABLED - if (documentLoader()) - LOG(History, "WebCoreHistory: Updating History for internal load in frame %s", documentLoader()->title().utf8().data()); -#endif - - Settings* settings = m_frame->settings(); - bool needPrivacy = !settings || settings->privateBrowsingEnabled(); - const KURL& historyURL = documentLoader()->urlForHistory(); - - if (documentLoader()->isClientRedirect()) { - if (!m_currentHistoryItem && !m_frame->tree()->parent()) { - addBackForwardItemClippedAtTarget(true); - if (!needPrivacy && !historyURL.isEmpty()) - m_client->updateGlobalHistory(historyURL); - } - if (m_currentHistoryItem) { - m_currentHistoryItem->setURL(documentLoader()->url()); - m_currentHistoryItem->setFormInfoFromRequest(documentLoader()->request()); - } - } else { - Frame* parentFrame = m_frame->tree()->parent(); - if (parentFrame && parentFrame->loader()->m_currentHistoryItem) - parentFrame->loader()->m_currentHistoryItem->addChildItem(createHistoryItem(true)); - } - - if (!historyURL.isEmpty() && !needPrivacy) { - if (Page* page = m_frame->page()) - page->group().addVisitedLink(historyURL); - } -} - -void FrameLoader::updateHistoryForCommit() -{ -#if !LOG_DISABLED - if (documentLoader()) - LOG(History, "WebCoreHistory: Updating History for commit in frame %s", documentLoader()->title().utf8().data()); -#endif - FrameLoadType type = loadType(); - if (isBackForwardLoadType(type) || - (type == FrameLoadTypeReload && !provisionalDocumentLoader()->unreachableURL().isEmpty())) { - // Once committed, we want to use current item for saving DocState, and - // the provisional item for restoring state. - // Note previousItem must be set before we close the URL, which will - // happen when the data source is made non-provisional below - m_previousHistoryItem = m_currentHistoryItem; - ASSERT(m_provisionalHistoryItem); - m_currentHistoryItem = m_provisionalHistoryItem; - m_provisionalHistoryItem = 0; - } -} - -void FrameLoader::updateHistoryForAnchorScroll() -{ - if (m_URL.isEmpty()) - return; - - Settings* settings = m_frame->settings(); - if (!settings || settings->privateBrowsingEnabled()) - return; - - Page* page = m_frame->page(); - if (!page) - return; - - page->group().addVisitedLink(m_URL); -} - -// Walk the frame tree, telling all frames to save their form state into their current -// history item. -void FrameLoader::saveDocumentAndScrollState() -{ - for (Frame* frame = m_frame; frame; frame = frame->tree()->traverseNext(m_frame)) { - frame->loader()->saveDocumentState(); - frame->loader()->saveScrollPositionAndViewStateToItem(frame->loader()->currentHistoryItem()); - } -} - -// 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); -} - -void FrameLoader::mainReceivedCompleteError(DocumentLoader* loader, const ResourceError& error) -{ - loader->setPrimaryLoadComplete(true); - m_client->dispatchDidLoadMainResource(activeDocumentLoader()); - checkCompleted(); - if (m_frame->page()) - checkLoadComplete(); -} - -void FrameLoader::mainReceivedError(const ResourceError& error, bool isComplete) -{ - activeDocumentLoader()->mainReceivedError(error, isComplete); -} - -ResourceError FrameLoader::cancelledError(const ResourceRequest& request) const -{ - ResourceError error = m_client->cancelledError(request); - error.setIsCancellation(true); - return error; -} - -ResourceError FrameLoader::blockedError(const ResourceRequest& request) const -{ - return m_client->blockedError(request); -} - -ResourceError FrameLoader::cannotShowURLError(const ResourceRequest& request) const -{ - return m_client->cannotShowURLError(request); -} - -ResourceError FrameLoader::fileDoesNotExistError(const ResourceResponse& response) const -{ - return m_client->fileDoesNotExistError(response); -} - -void FrameLoader::didFinishLoad(ResourceLoader* loader) -{ - if (Page* page = m_frame->page()) - page->progress()->completeProgress(loader->identifier()); - dispatchDidFinishLoading(loader->documentLoader(), loader->identifier()); -} - -void FrameLoader::didReceiveAuthenticationChallenge(ResourceLoader* loader, const AuthenticationChallenge& currentWebChallenge) -{ - m_client->dispatchDidReceiveAuthenticationChallenge(loader->documentLoader(), loader->identifier(), currentWebChallenge); -} - -void FrameLoader::didCancelAuthenticationChallenge(ResourceLoader* loader, const AuthenticationChallenge& currentWebChallenge) -{ - m_client->dispatchDidCancelAuthenticationChallenge(loader->documentLoader(), loader->identifier(), currentWebChallenge); -} - -PolicyCheck::PolicyCheck() - : m_navigationFunction(0) - , m_newWindowFunction(0) - , m_contentFunction(0) -{ -} - -void PolicyCheck::clear() -{ - clearRequest(); - m_navigationFunction = 0; - m_newWindowFunction = 0; - m_contentFunction = 0; -} - -void PolicyCheck::set(const ResourceRequest& request, PassRefPtr<FormState> formState, - NavigationPolicyDecisionFunction function, void* argument) -{ - m_request = request; - m_formState = formState; - m_frameName = String(); - - m_navigationFunction = function; - m_newWindowFunction = 0; - m_contentFunction = 0; - m_argument = argument; -} - -void PolicyCheck::set(const ResourceRequest& request, PassRefPtr<FormState> formState, - const String& frameName, NewWindowPolicyDecisionFunction function, void* argument) -{ - m_request = request; - m_formState = formState; - m_frameName = frameName; - - m_navigationFunction = 0; - m_newWindowFunction = function; - m_contentFunction = 0; - m_argument = argument; -} - -void PolicyCheck::set(ContentPolicyDecisionFunction function, void* argument) -{ - m_request = ResourceRequest(); - m_formState = 0; - m_frameName = String(); - - m_navigationFunction = 0; - m_newWindowFunction = 0; - m_contentFunction = function; - m_argument = argument; -} - -void PolicyCheck::call(bool shouldContinue) -{ - if (m_navigationFunction) - m_navigationFunction(m_argument, m_request, m_formState.get(), shouldContinue); - if (m_newWindowFunction) - m_newWindowFunction(m_argument, m_request, m_formState.get(), m_frameName, shouldContinue); - ASSERT(!m_contentFunction); -} - -void PolicyCheck::call(PolicyAction action) -{ - ASSERT(!m_navigationFunction); - ASSERT(!m_newWindowFunction); - ASSERT(m_contentFunction); - m_contentFunction(m_argument, action); -} - -void PolicyCheck::clearRequest() -{ - m_request = ResourceRequest(); - m_formState = 0; - m_frameName = String(); -} - -void PolicyCheck::cancel() -{ - clearRequest(); - if (m_navigationFunction) - m_navigationFunction(m_argument, m_request, m_formState.get(), false); - if (m_newWindowFunction) - m_newWindowFunction(m_argument, m_request, m_formState.get(), m_frameName, false); - if (m_contentFunction) - m_contentFunction(m_argument, PolicyIgnore); -} - -void FrameLoader::setTitle(const String& title) -{ - documentLoader()->setTitle(title); -} - -KURL FrameLoader::originalRequestURL() const -{ - return activeDocumentLoader()->originalRequest().url(); -} - -String FrameLoader::referrer() const -{ - return documentLoader()->request().httpReferrer(); -} - -void FrameLoader::dispatchWindowObjectAvailable() -{ - if (!m_frame->script()->isEnabled() || !m_frame->script()->haveWindowShell()) - return; - - m_client->windowObjectCleared(); - - if (Page* page = m_frame->page()) { - if (InspectorController* inspector = page->inspectorController()) - inspector->inspectedWindowScriptObjectCleared(m_frame); - if (InspectorController* inspector = page->parentInspectorController()) - inspector->windowScriptObjectAvailable(); - } -} - -Widget* FrameLoader::createJavaAppletWidget(const IntSize& size, Element* element, const HashMap<String, String>& args) -{ - String baseURLString; - 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; - paramNames.append(it->first); - paramValues.append(it->second); - } - - 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; - - return widget; -} - -void FrameLoader::didChangeTitle(DocumentLoader* loader) -{ - m_client->didChangeTitle(loader); - - // The title doesn't get communicated to the WebView until we are committed. - if (loader->isCommitted()) { - // Must update the entries in the back-forward list too. - if (m_currentHistoryItem) - m_currentHistoryItem->setTitle(loader->title()); - // This must go through the WebFrame because it has the right notion of the current b/f item. - m_client->setTitle(loader->title(), loader->urlForHistory()); - m_client->setMainFrameDocumentReady(true); // update observers with new DOMDocument - m_client->dispatchDidReceiveTitle(loader->title()); - } -} - -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) - return; - -#ifndef NDEBUG - m_didDispatchDidCommitLoad = true; -#endif - - m_client->dispatchDidCommitLoad(); - - if (Page* page = m_frame->page()) - page->inspectorController()->didCommitLoad(m_documentLoader.get()); -} - -void FrameLoader::dispatchAssignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request) -{ - m_client->assignIdentifierToInitialRequest(identifier, loader, request); - - if (Page* page = m_frame->page()) - page->inspectorController()->identifierForInitialRequest(identifier, loader, request); -} - -void FrameLoader::dispatchWillSendRequest(DocumentLoader* loader, unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse) -{ - m_client->dispatchWillSendRequest(loader, identifier, request, redirectResponse); - - if (Page* page = m_frame->page()) - page->inspectorController()->willSendRequest(loader, identifier, request, redirectResponse); -} - -void FrameLoader::dispatchDidReceiveResponse(DocumentLoader* loader, unsigned long identifier, const ResourceResponse& r) -{ - m_client->dispatchDidReceiveResponse(loader, identifier, r); - - if (Page* page = m_frame->page()) - page->inspectorController()->didReceiveResponse(loader, identifier, r); -} - -void FrameLoader::dispatchDidReceiveContentLength(DocumentLoader* loader, unsigned long identifier, int length) -{ - m_client->dispatchDidReceiveContentLength(loader, identifier, length); - - if (Page* page = m_frame->page()) - page->inspectorController()->didReceiveContentLength(loader, identifier, length); -} - -void FrameLoader::dispatchDidFinishLoading(DocumentLoader* loader, unsigned long identifier) -{ - m_client->dispatchDidFinishLoading(loader, identifier); - - if (Page* page = m_frame->page()) - page->inspectorController()->didFinishLoading(loader, identifier); -} - -#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() -{ - 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()) { - // set cachePolicy to Cache to use the loaded resource - newDoc->docLoader()->setCachePolicy(CachePolicyCache); - 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; - } - } -} - -#endif - -} // namespace WebCore |