From d8543bb6618c17b12da906afa77d216f58cf4058 Mon Sep 17 00:00:00 2001 From: Upstream Date: Mon, 12 Jan 1970 13:46:40 +0000 Subject: external/webkit r30707 --- WebCore/loader/FrameLoader.cpp | 4842 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4842 insertions(+) create mode 100644 WebCore/loader/FrameLoader.cpp (limited to 'WebCore/loader/FrameLoader.cpp') diff --git a/WebCore/loader/FrameLoader.cpp b/WebCore/loader/FrameLoader.cpp new file mode 100644 index 0000000..02376ba --- /dev/null +++ b/WebCore/loader/FrameLoader.cpp @@ -0,0 +1,4842 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Trolltech ASA + * + * 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" + +#include "CString.h" +#include "Cache.h" +#include "CachedPage.h" +#include "Chrome.h" +#include "DOMImplementation.h" +#include "DocLoader.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "EditCommand.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 "PluginInfoStore.h" +#include "ProgressTracker.h" +#include "RenderPart.h" +#include "RenderWidget.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 "kjs_binding.h" +#include "kjs_proxy.h" +#include "kjs_window.h" +#include +#include + +#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 + +using KJS::UString; +using KJS::JSLock; +using KJS::JSValue; + +namespace WebCore { + +#if ENABLE(SVG) +using namespace SVGNames; +#endif +using namespace HTMLNames; +using namespace EventNames; + +#if USE(LOW_BANDWIDTH_DISPLAY) +const unsigned int cMaxPendingSourceLengthInLowBandwidthDisplay = 128 * 1024; +#endif + +struct FormSubmission { + const char* action; + String url; + RefPtr data; + String target; + String contentType; + String boundary; + RefPtr event; + + FormSubmission(const char* a, const String& u, PassRefPtr d, const String& t, + const String& ct, const String& b, PassRefPtr 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 bool m_restrictAccessToLocal = true; + +static bool getString(JSValue* result, String& string) +{ + if (!result) + return false; + JSLock lock; + 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::iterator end = m_openedFrames.end(); + for (HashSet::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); + m_client->setDefersLoading(defers); +} + +Frame* FrameLoader::createWindow(const FrameLoadRequest& request, const WindowFeatures& features, bool& created) +{ + ASSERT(!features.dialog || request.frameName().isEmpty()); + + if (!request.frameName().isEmpty() && request.frameName() != "_blank") + if (Frame* frame = findFrameForNavigation(request.frameName())) { + if (!request.resourceRequest().url().isEmpty()) + frame->loader()->load(request, false, true, 0, 0, HashMap()); + 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); + + Page* page = m_frame->page(); + if (page) + page = page->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) +{ + ResourceRequestCachePolicy policy = (m_cachePolicy == CachePolicyReload) || (m_cachePolicy == CachePolicyRefresh) + ? ReloadIgnoringCacheData : UseProtocolCachePolicy; + ResourceRequest request(url, referrer, policy); + + if (executeIfJavaScriptURL(request.url(), userGesture)) + return; + + urlSelected(request, "_self", 0, lockHistory, userGesture); +} + +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); + + if (frameRequest.resourceRequest().httpReferrer().isEmpty()) + frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer); + + urlSelected(frameRequest, triggeringEvent, lockHistory, userGesture); +} + +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 + 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(ownerElement); + allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff; + marginWidth = o->getMarginWidth(); + marginHeight = o->getMarginHeight(); + } + + if (!canLoad(url, referrer)) { + FrameLoader::reportLocalLoadFailed(m_frame->page(), url.string()); + return 0; + } + + bool hideReferrer = shouldHideReferrer(url, referrer); + RefPtr frame = m_client->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer, + allowsScrolling, marginWidth, marginHeight); + + if (!frame) { + checkCallImplicitClose(); + return 0; + } + + frame->loader()->m_isComplete = false; + + if (ownerElement->renderer() && frame->view()) + static_cast(ownerElement->renderer())->setWidget(frame->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 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, + const String& target, const String& contentType, const String& boundary, Event* event) +{ + ASSERT(formData); + + 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; + } + + FrameLoadRequest frameRequest; + + if (!m_outgoingReferrer.isEmpty()) + frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer); + + frameRequest.setFrameName(target.isEmpty() ? m_frame->document()->baseTarget() : target); + + // Handle mailto: forms + bool isMailtoForm = equalIgnoringCase(u.protocol(), "mailto"); + if (isMailtoForm && strcmp(action, "GET") != 0) { + // Append body= for POST mailto, replace the whole query string for GET one. + String body = formData->flattenToString(); + String query = u.query(); + if (!query.isEmpty()) + query.append('&'); + u.setQuery(query + body); + } + + if (strcmp(action, "GET") == 0) { + 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); + + 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(unloadEvent, false, false); + if (m_frame->document()) + m_frame->document()->updateRendering(); + m_wasUnloadEventEmitted = true; + } + } + 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); + + XMLHttpRequest::cancelRequests(doc); + + doc->stopDatabases(); + } + + // 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 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()->setLastEditCommand(0); + 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(), 0, script); +} + +JSValue* FrameLoader::executeScript(const String& url, int baseLine, const String& script) +{ + if (!m_frame->scriptProxy()->isEnabled()) + return 0; + + bool wasRunningScript = m_isRunningScript; + m_isRunningScript = true; + + JSValue* result = m_frame->scriptProxy()->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 , 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->clearScriptProxy(); + m_frame->clearDOMWindow(); + } + + m_frame->selectionController()->clear(); + m_frame->eventHandler()->clear(); + if (m_frame->view()) + m_frame->view()->clear(); + + m_frame->setSelectionGranularity(CharacterGranularity); + + // Do not drop the document before the script proxy 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->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 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; + KURL baseURL; + if (!m_URL.isEmpty()) + baseURL = m_URL; + + RefPtr document = DOMImplementation::instance()->createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode()); + m_frame->setDocument(document); + + document->setURL(m_URL); + // We prefer m_baseURL over m_URL because m_URL changes when we are + // about to load a new page. + document->setBaseURL(baseURL); + if (m_decoder) + document->setDecoder(m_decoder.get()); + if (forcedSecurityOrigin) + document->setSecurityOrigin(forcedSecurityOrigin.get()); + + updatePolicyBaseURL(); + + Settings* settings = document->settings(); + document->docLoader()->setAutoLoadImages(settings && settings->loadsImagesAutomatically()); + +#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()->resizeContents(0, 0); + +#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 = new TextResourceDecoder(m_responseMIMEType, settings ? settings->defaultTextEncodingName() : String()); + if (!m_encoding.isNull()) + m_decoder->setEncoding(m_encoding, + m_encodingWasChosenByUser ? TextResourceDecoder::UserChosenEncoding : TextResourceDecoder::EncodingFromHTTPHeader); + if (m_frame->document()) + 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) + 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 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(); + + if (m_documentLoader && !m_documentLoader->isLoadingFromCachedPage()) + startIconLoader(); +} + +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(); +} + +bool FrameLoader::restrictAccessToLocal() +{ + return m_restrictAccessToLocal; +} + +void FrameLoader::setRestrictAccessToLocal(bool access) +{ + m_restrictAccessToLocal = access; +} + +static HashSet& localSchemes() +{ + static HashSet 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: + case FrameLoadTypeReloadAllowingStaleData: + case FrameLoadTypeSame: + case FrameLoadTypeReplace: + break; + case FrameLoadTypeBack: + case FrameLoadTypeForward: + case FrameLoadTypeIndexedBackForward: + case FrameLoadTypeRedirectWithLockedHistory: + case FrameLoadTypeStandard: + 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. + RefPtr protector = m_frame->refCount() > 0 ? 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 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*) +{ + checkCompleted(); +} + +void FrameLoader::scheduleCheckCompleted() +{ + if (!m_checkCompletedTimer.isActive()) + m_checkCompletedTimer.startOneShot(0); +} + +void FrameLoader::checkLoadCompleteTimerFired(Timer*) +{ + if (m_frame->page()) + 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; + + // We want a new history item if the refresh timeout is > 1 second. + if (!m_scheduledRedirection || delay <= m_scheduledRedirection->delay) + scheduleRedirection(new ScheduledRedirection(delay, url, delay <= 1, false)); +} + +void FrameLoader::scheduleLocationChange(const String& url, const String& referrer, bool lockHistory, bool wasUserGesture) +{ + // 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) +{ + // 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) +{ + // 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*) +{ + OwnPtr 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(); +} + +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) + 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 svg = static_cast(m_frame->document())->rootElement(); + if (!svg->currentView()->parseViewSpec(name)) + return false; + svg->setUseCurrentView(true); + } else { + if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) { + RefPtr viewElement = anchorNode->hasTagName(SVGNames::viewTag) ? static_cast(anchorNode) : 0; + if (viewElement.get()) { + RefPtr svg = static_cast(SVGLocatable::nearestViewportElement(viewElement.get())); + svg->inheritViewAttributes(viewElement.get()); + } + } + } + // FIXME: need to decide which 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->document()->renderer() && m_frame->document()->renderer()->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, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignTopAlways); + + return true; +} + +bool FrameLoader::requestObject(RenderPart* renderer, const String& url, const AtomicString& frameName, + const String& mimeType, const Vector& paramNames, const Vector& 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(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 ((mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) { + String pluginName = PluginInfoStore::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& paramNames, const Vector& paramValues, bool useFallback) +{ + Widget* widget = 0; + + if (renderer && !useFallback) { + Element* pluginElement = 0; + if (renderer->node() && renderer->node()->isElementNode()) + pluginElement = static_cast(renderer->node()); + + if (!canLoad(url, frame()->document())) { + FrameLoader::reportLocalLoadFailed(m_frame->page(), 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::recordFormValue(const String& name, const String& value, PassRefPtr element) +{ + m_formAboutToBeSubmitted = element; + m_formValuesAboutToBeSubmitted.set(name, value); +} + +void FrameLoader::parentCompleted() +{ + if (m_scheduledRedirection && !m_redirectionTimer.isActive()) + startRedirectionTimer(); +} + +String FrameLoader::outgoingReferrer() const +{ + return m_outgoingReferrer; +} + +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()->initSecurityOrigin(); +} + +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(owner)->renderFallbackContent(); +} + +void FrameLoader::provisionalLoadStarted() +{ + Page* page = m_frame->page(); + + // this is used to update the current history item + // in the event of a navigation aytime during loading + m_navigationDuringLoad = false; + if (page) { + Document *document = page->mainFrame()->document(); + m_navigationDuringLoad = !page->mainFrame()->loader()->isComplete() || (document && document->processingLoadEvent()); + } + + m_firstLayoutDone = false; + cancelRedirection(true); + m_client->provisionalLoadStarted(); +} + +bool FrameLoader::userGestureHint() +{ + Frame* rootFrame = m_frame; + while (rootFrame->tree()->parent()) + rootFrame = rootFrame->tree()->parent(); + + if (rootFrame->scriptProxy()->isEnabled()) + return rootFrame->scriptProxy()->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: - 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 for more information. + && !m_containsPlugIns + && !m_URL.protocolIs("https") + && m_frame->document() + && !m_frame->document()->applets()->length() + && !m_frame->document()->hasWindowEventListener(unloadEvent) +#if ENABLE(DATABASE) + && !m_frame->document()->hasOpenDatabases() +#endif + && 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(); +} + +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 FrameLoader::openURL 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; + 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) +{ + 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_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