1/* 2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. 3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) 4 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 5 * Copyright (C) 2008 Alp Toker <alp@atoker.com> 6 * Copyright (C) Research In Motion Limited 2009. All rights reserved. 7 * Copyright (C) 2011 Kris Jordan <krisjordan@gmail.com> 8 * Copyright (C) 2011 Google Inc. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of Apple Inc. ("Apple") nor the names of 20 * its contributors may be used to endorse or promote products derived 21 * from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 27 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35#include "config.h" 36#include "FrameLoader.h" 37 38#include "AXObjectCache.h" 39#include "ApplicationCacheHost.h" 40#include "BackForwardController.h" 41#include "BeforeUnloadEvent.h" 42#include "CachedPage.h" 43#include "CachedResourceLoader.h" 44#include "Chrome.h" 45#include "ChromeClient.h" 46#include "ContentSecurityPolicy.h" 47#include "DOMImplementation.h" 48#include "DOMWindow.h" 49#include "DatabaseManager.h" 50#include "Document.h" 51#include "DocumentLoadTiming.h" 52#include "DocumentLoader.h" 53#include "Editor.h" 54#include "EditorClient.h" 55#include "Element.h" 56#include "Event.h" 57#include "EventHandler.h" 58#include "EventNames.h" 59#include "FloatRect.h" 60#include "FormState.h" 61#include "FormSubmission.h" 62#include "FrameLoadRequest.h" 63#include "FrameLoaderClient.h" 64#include "FrameNetworkingContext.h" 65#include "FrameTree.h" 66#include "FrameView.h" 67#include "HTMLAnchorElement.h" 68#include "HTMLFormElement.h" 69#include "HTMLInputElement.h" 70#include "HTMLNames.h" 71#include "HTMLObjectElement.h" 72#include "HTMLParserIdioms.h" 73#include "HTTPHeaderNames.h" 74#include "HTTPParsers.h" 75#include "HistoryController.h" 76#include "HistoryItem.h" 77#include "IconController.h" 78#include "InspectorController.h" 79#include "InspectorInstrumentation.h" 80#include "LoaderStrategy.h" 81#include "Logging.h" 82#include "MIMETypeRegistry.h" 83#include "MainFrame.h" 84#include "MemoryCache.h" 85#include "Page.h" 86#include "PageActivityAssertionToken.h" 87#include "PageCache.h" 88#include "PageThrottler.h" 89#include "PageTransitionEvent.h" 90#include "PlatformStrategies.h" 91#include "PluginData.h" 92#include "PluginDatabase.h" 93#include "PluginDocument.h" 94#include "PolicyChecker.h" 95#include "ProgressTracker.h" 96#include "ResourceHandle.h" 97#include "ResourceRequest.h" 98#include "SVGDocument.h" 99#include "SVGLocatable.h" 100#include "SVGNames.h" 101#include "SVGPreserveAspectRatio.h" 102#include "SVGSVGElement.h" 103#include "SVGViewElement.h" 104#include "SVGViewSpec.h" 105#include "SchemeRegistry.h" 106#include "ScriptController.h" 107#include "ScriptSourceCode.h" 108#include "ScrollAnimator.h" 109#include "SecurityOrigin.h" 110#include "SecurityPolicy.h" 111#include "SegmentedString.h" 112#include "SerializedScriptValue.h" 113#include "Settings.h" 114#include "SubframeLoader.h" 115#include "TextResourceDecoder.h" 116#include "WindowFeatures.h" 117#include "XMLDocumentParser.h" 118#include <wtf/CurrentTime.h> 119#include <wtf/Ref.h> 120#include <wtf/StdLibExtras.h> 121#include <wtf/text/CString.h> 122#include <wtf/text/WTFString.h> 123 124#if ENABLE(SHARED_WORKERS) 125#include "SharedWorkerRepository.h" 126#endif 127 128#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) 129#include "Archive.h" 130#endif 131 132#if PLATFORM(IOS) 133#include "DocumentType.h" 134#include "MemoryPressureHandler.h" 135#include "ResourceLoader.h" 136#include "RuntimeApplicationChecksIOS.h" 137#include "SystemMemory.h" 138#include "WKContentObservation.h" 139#endif 140 141namespace WebCore { 142 143using namespace HTMLNames; 144using namespace SVGNames; 145 146static const char defaultAcceptHeader[] = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"; 147 148#if PLATFORM(IOS) 149const int memoryLevelThresholdToPrunePageCache = 20; 150#endif 151 152bool isBackForwardLoadType(FrameLoadType type) 153{ 154 switch (type) { 155 case FrameLoadType::Standard: 156 case FrameLoadType::Reload: 157 case FrameLoadType::ReloadFromOrigin: 158 case FrameLoadType::Same: 159 case FrameLoadType::RedirectWithLockedBackForwardList: 160 case FrameLoadType::Replace: 161 return false; 162 case FrameLoadType::Back: 163 case FrameLoadType::Forward: 164 case FrameLoadType::IndexedBackForward: 165 return true; 166 } 167 ASSERT_NOT_REACHED(); 168 return false; 169} 170 171// This is not in the FrameLoader class to emphasize that it does not depend on 172// private FrameLoader data, and to avoid increasing the number of public functions 173// with access to private data. Since only this .cpp file needs it, making it 174// non-member lets us exclude it from the header file, thus keeping FrameLoader.h's 175// API simpler. 176// 177static bool isDocumentSandboxed(Frame* frame, SandboxFlags mask) 178{ 179 return frame->document() && frame->document()->isSandboxed(mask); 180} 181 182class FrameLoader::FrameProgressTracker { 183public: 184 explicit FrameProgressTracker(Frame& frame) 185 : m_frame(frame) 186 , m_inProgress(false) 187 { 188 } 189 190 ~FrameProgressTracker() 191 { 192 ASSERT(!m_inProgress || m_frame.page()); 193 if (m_inProgress) 194 m_frame.page()->progress().progressCompleted(m_frame); 195 } 196 197 void progressStarted() 198 { 199 ASSERT(m_frame.page()); 200 if (!m_inProgress) 201 m_frame.page()->progress().progressStarted(m_frame); 202 m_inProgress = true; 203 } 204 205 void progressCompleted() 206 { 207 ASSERT(m_inProgress); 208 ASSERT(m_frame.page()); 209 m_inProgress = false; 210 m_frame.page()->progress().progressCompleted(m_frame); 211 } 212 213private: 214 Frame& m_frame; 215 bool m_inProgress; 216}; 217 218FrameLoader::FrameLoader(Frame& frame, FrameLoaderClient& client) 219 : m_frame(frame) 220 , m_client(client) 221 , m_policyChecker(std::make_unique<PolicyChecker>(frame)) 222 , m_history(std::make_unique<HistoryController>(frame)) 223 , m_notifier(frame) 224 , m_subframeLoader(std::make_unique<SubframeLoader>(frame)) 225 , m_icon(std::make_unique<IconController>(frame)) 226 , m_mixedContentChecker(frame) 227 , m_state(FrameStateProvisional) 228 , m_loadType(FrameLoadType::Standard) 229 , m_delegateIsHandlingProvisionalLoadError(false) 230 , m_quickRedirectComing(false) 231 , m_sentRedirectNotification(false) 232 , m_inStopAllLoaders(false) 233 , m_isExecutingJavaScriptFormAction(false) 234 , m_didCallImplicitClose(true) 235 , m_wasUnloadEventEmitted(false) 236 , m_pageDismissalEventBeingDispatched(NoDismissal) 237 , m_isComplete(false) 238 , m_needsClear(false) 239 , m_checkTimer(this, &FrameLoader::checkTimerFired) 240 , m_shouldCallCheckCompleted(false) 241 , m_shouldCallCheckLoadComplete(false) 242 , m_opener(nullptr) 243 , m_loadingFromCachedPage(false) 244 , m_suppressOpenerInNewFrame(false) 245 , m_currentNavigationHasShownBeforeUnloadConfirmPanel(false) 246 , m_loadsSynchronously(false) 247 , m_forcedSandboxFlags(SandboxNone) 248{ 249} 250 251FrameLoader::~FrameLoader() 252{ 253 setOpener(nullptr); 254 255 HashSet<Frame*>::iterator end = m_openedFrames.end(); 256 for (HashSet<Frame*>::iterator it = m_openedFrames.begin(); it != end; ++it) 257 (*it)->loader().m_opener = 0; 258 259 m_client.frameLoaderDestroyed(); 260 261 if (m_networkingContext) 262 m_networkingContext->invalidate(); 263} 264 265void FrameLoader::init() 266{ 267 // This somewhat odd set of steps gives the frame an initial empty document. 268 setPolicyDocumentLoader(m_client.createDocumentLoader(ResourceRequest(URL(ParsedURLString, emptyString())), SubstituteData()).get()); 269 setProvisionalDocumentLoader(m_policyDocumentLoader.get()); 270 m_provisionalDocumentLoader->startLoadingMainResource(); 271 272 Ref<Frame> protect(m_frame); 273 m_frame.document()->cancelParsing(); 274 m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocument); 275 276 m_networkingContext = m_client.createNetworkingContext(); 277 m_progressTracker = std::make_unique<FrameProgressTracker>(m_frame); 278} 279 280#if PLATFORM(IOS) 281void FrameLoader::initForSynthesizedDocument(const URL&) 282{ 283 // FIXME: We need to initialize the document URL to the specified URL. Currently the URL is empty and hence 284 // FrameLoader::checkCompleted() will overwrite the URL of the document to be activeDocumentLoader()->documentURL(). 285 286 RefPtr<DocumentLoader> loader = m_client.createDocumentLoader(ResourceRequest(URL(ParsedURLString, emptyString())), SubstituteData()); 287 loader->setFrame(&m_frame); 288 loader->setResponse(ResourceResponse(URL(), ASCIILiteral("text/html"), 0, String(), String())); 289 loader->setCommitted(true); 290 setDocumentLoader(loader.get()); 291 292 m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocument); 293 m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocumentPostCommit); 294 m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad); 295 m_client.transitionToCommittedForNewPage(); 296 297 m_didCallImplicitClose = true; 298 m_isComplete = true; 299 m_state = FrameStateComplete; 300 m_needsClear = true; 301 302 m_networkingContext = m_client.createNetworkingContext(); 303 m_progressTracker = std::make_unique<FrameProgressTracker>(m_frame); 304} 305#endif 306 307void FrameLoader::setDefersLoading(bool defers) 308{ 309 if (m_documentLoader) 310 m_documentLoader->setDefersLoading(defers); 311 if (m_provisionalDocumentLoader) 312 m_provisionalDocumentLoader->setDefersLoading(defers); 313 if (m_policyDocumentLoader) 314 m_policyDocumentLoader->setDefersLoading(defers); 315 history().setDefersLoading(defers); 316 317 if (!defers) { 318 m_frame.navigationScheduler().startTimer(); 319 startCheckCompleteTimer(); 320 } 321} 322 323void FrameLoader::changeLocation(SecurityOrigin* securityOrigin, const URL& url, const String& referrer, LockHistory lockHistory, LockBackForwardList lockBackForwardList, bool refresh, AllowNavigationToInvalidURL allowNavigationToInvalidURL) 324{ 325 urlSelected(FrameLoadRequest(securityOrigin, ResourceRequest(url, referrer, refresh ? ReloadIgnoringCacheData : UseProtocolCachePolicy), "_self"), 326 0, lockHistory, lockBackForwardList, MaybeSendReferrer, ReplaceDocumentIfJavaScriptURL, allowNavigationToInvalidURL); 327} 328 329void FrameLoader::urlSelected(const URL& url, const String& passedTarget, PassRefPtr<Event> triggeringEvent, LockHistory lockHistory, LockBackForwardList lockBackForwardList, ShouldSendReferrer shouldSendReferrer) 330{ 331 urlSelected(FrameLoadRequest(m_frame.document()->securityOrigin(), ResourceRequest(url), passedTarget), 332 triggeringEvent, lockHistory, lockBackForwardList, shouldSendReferrer, DoNotReplaceDocumentIfJavaScriptURL, AllowNavigationToInvalidURL::Yes); 333} 334 335// The shouldReplaceDocumentIfJavaScriptURL parameter will go away when the FIXME to eliminate the 336// corresponding parameter from ScriptController::executeIfJavaScriptURL() is addressed. 337void FrameLoader::urlSelected(const FrameLoadRequest& passedRequest, PassRefPtr<Event> triggeringEvent, LockHistory lockHistory, LockBackForwardList lockBackForwardList, ShouldSendReferrer shouldSendReferrer, ShouldReplaceDocumentIfJavaScriptURL shouldReplaceDocumentIfJavaScriptURL, AllowNavigationToInvalidURL allowNavigationToInvalidURL) 338{ 339 ASSERT(!m_suppressOpenerInNewFrame); 340 341 Ref<Frame> protect(m_frame); 342 FrameLoadRequest frameRequest(passedRequest); 343 344 if (m_frame.script().executeIfJavaScriptURL(frameRequest.resourceRequest().url(), shouldReplaceDocumentIfJavaScriptURL)) 345 return; 346 347 if (frameRequest.frameName().isEmpty()) 348 frameRequest.setFrameName(m_frame.document()->baseTarget()); 349 350 if (shouldSendReferrer == NeverSendReferrer) 351 m_suppressOpenerInNewFrame = true; 352 addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin()); 353 354 loadFrameRequest(frameRequest, lockHistory, lockBackForwardList, triggeringEvent, 0, shouldSendReferrer, allowNavigationToInvalidURL); 355 356 m_suppressOpenerInNewFrame = false; 357} 358 359void FrameLoader::submitForm(PassRefPtr<FormSubmission> submission) 360{ 361 ASSERT(submission->method() == FormSubmission::PostMethod || submission->method() == FormSubmission::GetMethod); 362 363 // FIXME: Find a good spot for these. 364 ASSERT(submission->data()); 365 ASSERT(submission->state()); 366 ASSERT(!submission->state()->sourceDocument()->frame() || submission->state()->sourceDocument()->frame() == &m_frame); 367 368 if (!m_frame.page()) 369 return; 370 371 if (submission->action().isEmpty()) 372 return; 373 374 if (isDocumentSandboxed(&m_frame, SandboxForms)) { 375 // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists. 376 m_frame.document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Blocked form submission to '" + submission->action().stringCenterEllipsizedToLength() + "' because the form's frame is sandboxed and the 'allow-forms' permission is not set."); 377 return; 378 } 379 380 if (protocolIsJavaScript(submission->action())) { 381 if (!m_frame.document()->contentSecurityPolicy()->allowFormAction(URL(submission->action()))) 382 return; 383 m_isExecutingJavaScriptFormAction = true; 384 Ref<Frame> protect(m_frame); 385 m_frame.script().executeIfJavaScriptURL(submission->action(), DoNotReplaceDocumentIfJavaScriptURL); 386 m_isExecutingJavaScriptFormAction = false; 387 return; 388 } 389 390 Frame* targetFrame = findFrameForNavigation(submission->target(), submission->state()->sourceDocument()); 391 if (!targetFrame) { 392 if (!DOMWindow::allowPopUp(&m_frame) && !ScriptController::processingUserGesture()) 393 return; 394 395 // FIXME: targetFrame can be 0 for two distinct reasons: 396 // 1. The frame was not found by name, so we should try opening a new window. 397 // 2. The frame was found, but navigating it was not allowed, e.g. by HTML5 sandbox or by origin checks. 398 // Continuing form submission makes no sense in the latter case. 399 // There is a repeat check after timer fires, so this is not a correctness issue. 400 401 targetFrame = &m_frame; 402 } else 403 submission->clearTarget(); 404 405 if (!targetFrame->page()) 406 return; 407 408 // FIXME: We'd like to remove this altogether and fix the multiple form submission issue another way. 409 410 // We do not want to submit more than one form from the same page, nor do we want to submit a single 411 // form more than once. This flag prevents these from happening; not sure how other browsers prevent this. 412 // The flag is reset in each time we start handle a new mouse or key down event, and 413 // also in setView since this part may get reused for a page from the back/forward cache. 414 // The form multi-submit logic here is only needed when we are submitting a form that affects this frame. 415 416 // FIXME: Frame targeting is only one of the ways the submission could end up doing something other 417 // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly 418 // needed any more now that we reset m_submittedFormURL on each mouse or key down event. 419 420 if (m_frame.tree().isDescendantOf(targetFrame)) { 421 if (m_submittedFormURL == submission->requestURL()) 422 return; 423 m_submittedFormURL = submission->requestURL(); 424 } 425 426 submission->data()->generateFiles(m_frame.document()); 427 submission->setReferrer(outgoingReferrer()); 428 submission->setOrigin(outgoingOrigin()); 429 430 targetFrame->navigationScheduler().scheduleFormSubmission(submission); 431} 432 433void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy) 434{ 435 if (m_frame.document() && m_frame.document()->parser()) 436 m_frame.document()->parser()->stopParsing(); 437 438 if (unloadEventPolicy != UnloadEventPolicyNone) { 439 if (m_frame.document()) { 440 if (m_didCallImplicitClose && !m_wasUnloadEventEmitted) { 441 Element* currentFocusedElement = m_frame.document()->focusedElement(); 442 if (currentFocusedElement && currentFocusedElement->toInputElement()) 443 currentFocusedElement->toInputElement()->endEditing(); 444 if (m_pageDismissalEventBeingDispatched == NoDismissal) { 445 if (unloadEventPolicy == UnloadEventPolicyUnloadAndPageHide) { 446 m_pageDismissalEventBeingDispatched = PageHideDismissal; 447 m_frame.document()->domWindow()->dispatchEvent(PageTransitionEvent::create(eventNames().pagehideEvent, m_frame.document()->inPageCache()), m_frame.document()); 448 } 449 450 // FIXME: update Page Visibility state here. 451 // https://bugs.webkit.org/show_bug.cgi?id=116770 452 453 if (!m_frame.document()->inPageCache()) { 454 RefPtr<Event> unloadEvent(Event::create(eventNames().unloadEvent, false, false)); 455 // The DocumentLoader (and thus its DocumentLoadTiming) might get destroyed 456 // while dispatching the event, so protect it to prevent writing the end 457 // time into freed memory. 458 RefPtr<DocumentLoader> documentLoader = m_provisionalDocumentLoader; 459 m_pageDismissalEventBeingDispatched = UnloadDismissal; 460 if (documentLoader && !documentLoader->timing()->unloadEventStart() && !documentLoader->timing()->unloadEventEnd()) { 461 DocumentLoadTiming* timing = documentLoader->timing(); 462 ASSERT(timing->navigationStart()); 463 timing->markUnloadEventStart(); 464 m_frame.document()->domWindow()->dispatchEvent(unloadEvent, m_frame.document()); 465 timing->markUnloadEventEnd(); 466 } else 467 m_frame.document()->domWindow()->dispatchEvent(unloadEvent, m_frame.document()); 468 } 469 } 470 m_pageDismissalEventBeingDispatched = NoDismissal; 471 if (m_frame.document()) 472 m_frame.document()->updateStyleIfNeeded(); 473 m_wasUnloadEventEmitted = true; 474 } 475 } 476 477 // Dispatching the unload event could have made m_frame.document() null. 478 if (m_frame.document() && !m_frame.document()->inPageCache()) { 479 // Don't remove event listeners from a transitional empty document (see bug 28716 for more information). 480 bool keepEventListeners = m_stateMachine.isDisplayingInitialEmptyDocument() && m_provisionalDocumentLoader 481 && m_frame.document()->isSecureTransitionTo(m_provisionalDocumentLoader->url()); 482 483 if (!keepEventListeners) 484 m_frame.document()->removeAllEventListeners(); 485 } 486 } 487 488 m_isComplete = true; // to avoid calling completed() in finishedParsing() 489 m_didCallImplicitClose = true; // don't want that one either 490 491 if (m_frame.document() && m_frame.document()->parsing()) { 492 finishedParsing(); 493 m_frame.document()->setParsing(false); 494 } 495 496 if (Document* doc = m_frame.document()) { 497 // FIXME: HTML5 doesn't tell us to set the state to complete when aborting, but we do anyway to match legacy behavior. 498 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10537 499 doc->setReadyState(Document::Complete); 500 501#if ENABLE(SQL_DATABASE) 502 // FIXME: Should the DatabaseManager watch for something like ActiveDOMObject::stop() rather than being special-cased here? 503 DatabaseManager::manager().stopDatabases(doc, 0); 504#endif 505 } 506 507 // FIXME: This will cancel redirection timer, which really needs to be restarted when restoring the frame from b/f cache. 508 m_frame.navigationScheduler().cancel(); 509} 510 511void FrameLoader::stop() 512{ 513 // http://bugs.webkit.org/show_bug.cgi?id=10854 514 // The frame's last ref may be removed and it will be deleted by checkCompleted(). 515 Ref<Frame> protect(m_frame); 516 517 if (DocumentParser* parser = m_frame.document()->parser()) { 518 parser->stopParsing(); 519 parser->finish(); 520 } 521 522 icon().stopLoader(); 523} 524 525void FrameLoader::willTransitionToCommitted() 526{ 527 // This function is called when a frame is still fully in place (not cached, not detached), but will be replaced. 528 529 if (m_frame.editor().hasComposition()) { 530 // The text was already present in DOM, so it's better to confirm than to cancel the composition. 531 m_frame.editor().confirmComposition(); 532 if (EditorClient* editorClient = m_frame.editor().client()) 533 editorClient->respondToChangedSelection(&m_frame); 534 } 535} 536 537bool FrameLoader::closeURL() 538{ 539 history().saveDocumentState(); 540 541 // Should only send the pagehide event here if the current document exists and has not been placed in the page cache. 542 Document* currentDocument = m_frame.document(); 543 stopLoading(currentDocument && !currentDocument->inPageCache() ? UnloadEventPolicyUnloadAndPageHide : UnloadEventPolicyUnloadOnly); 544 545 m_frame.editor().clearUndoRedoOperations(); 546 return true; 547} 548 549bool FrameLoader::didOpenURL() 550{ 551 if (m_frame.navigationScheduler().redirectScheduledDuringLoad()) { 552 // A redirect was scheduled before the document was created. 553 // This can happen when one frame changes another frame's location. 554 return false; 555 } 556 557 m_frame.navigationScheduler().cancel(); 558 m_frame.editor().clearLastEditCommand(); 559 560 m_isComplete = false; 561 m_didCallImplicitClose = false; 562 563 // If we are still in the process of initializing an empty document then 564 // its frame is not in a consistent state for rendering, so avoid setJSStatusBarText 565 // since it may cause clients to attempt to render the frame. 566 if (!m_stateMachine.creatingInitialEmptyDocument()) { 567 DOMWindow* window = m_frame.document()->domWindow(); 568 window->setStatus(String()); 569 window->setDefaultStatus(String()); 570 } 571 572 started(); 573 574 return true; 575} 576 577void FrameLoader::didExplicitOpen() 578{ 579 m_isComplete = false; 580 m_didCallImplicitClose = false; 581 582 // Calling document.open counts as committing the first real document load. 583 if (!m_stateMachine.committedFirstRealDocumentLoad()) 584 m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocumentPostCommit); 585 586 // Prevent window.open(url) -- eg window.open("about:blank") -- from blowing away results 587 // from a subsequent window.document.open / window.document.write call. 588 // Canceling redirection here works for all cases because document.open 589 // implicitly precedes document.write. 590 m_frame.navigationScheduler().cancel(); 591} 592 593 594void FrameLoader::cancelAndClear() 595{ 596 m_frame.navigationScheduler().cancel(); 597 598 if (!m_isComplete) 599 closeURL(); 600 601 clear(m_frame.document(), false); 602 m_frame.script().updatePlatformScriptObjects(); 603} 604 605void FrameLoader::clear(Document* newDocument, bool clearWindowProperties, bool clearScriptObjects, bool clearFrameView) 606{ 607 m_frame.editor().clear(); 608 609 if (!m_needsClear) 610 return; 611 m_needsClear = false; 612 613 if (!m_frame.document()->inPageCache()) { 614 m_frame.document()->cancelParsing(); 615 m_frame.document()->stopActiveDOMObjects(); 616 bool hadLivingRenderTree = m_frame.document()->hasLivingRenderTree(); 617 m_frame.document()->prepareForDestruction(); 618 if (hadLivingRenderTree) 619 m_frame.document()->removeFocusedNodeOfSubtree(m_frame.document()); 620 } 621 622 // Do this after detaching the document so that the unload event works. 623 if (clearWindowProperties) { 624 InspectorInstrumentation::frameWindowDiscarded(&m_frame, m_frame.document()->domWindow()); 625 m_frame.document()->domWindow()->resetUnlessSuspendedForPageCache(); 626 m_frame.script().clearWindowShell(newDocument->domWindow(), m_frame.document()->inPageCache()); 627 } 628 629 m_frame.selection().prepareForDestruction(); 630 m_frame.eventHandler().clear(); 631 if (clearFrameView && m_frame.view()) 632 m_frame.view()->clear(); 633 634 // Do not drop the document before the ScriptController and view are cleared 635 // as some destructors might still try to access the document. 636 m_frame.setDocument(0); 637 638 subframeLoader().clear(); 639 640 if (clearScriptObjects) 641 m_frame.script().clearScriptObjects(); 642 643 m_frame.script().enableEval(); 644 645 m_frame.navigationScheduler().clear(); 646 647 m_checkTimer.stop(); 648 m_shouldCallCheckCompleted = false; 649 m_shouldCallCheckLoadComplete = false; 650 651 if (m_stateMachine.isDisplayingInitialEmptyDocument() && m_stateMachine.committedFirstRealDocumentLoad()) 652 m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad); 653} 654 655void FrameLoader::receivedFirstData() 656{ 657 dispatchDidCommitLoad(); 658 dispatchDidClearWindowObjectsInAllWorlds(); 659 dispatchGlobalObjectAvailableInAllWorlds(); 660 661 if (m_documentLoader) { 662 StringWithDirection ptitle = m_documentLoader->title(); 663 // If we have a title let the WebView know about it. 664 if (!ptitle.isNull()) 665 m_client.dispatchDidReceiveTitle(ptitle); 666 } 667 668 if (!m_documentLoader) 669 return; 670 671 double delay; 672 String urlString; 673 if (!parseHTTPRefresh(m_documentLoader->response().httpHeaderField(HTTPHeaderName::Refresh), false, delay, urlString)) 674 return; 675 URL completedURL; 676 if (urlString.isEmpty()) 677 completedURL = m_frame.document()->url(); 678 else 679 completedURL = m_frame.document()->completeURL(urlString); 680 681 if (!protocolIsJavaScript(completedURL)) 682 m_frame.navigationScheduler().scheduleRedirect(delay, completedURL); 683 else { 684 String message = "Refused to refresh " + m_frame.document()->url().stringCenterEllipsizedToLength() + " to a javascript: URL"; 685 m_frame.document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message); 686 } 687} 688 689void FrameLoader::setOutgoingReferrer(const URL& url) 690{ 691 m_outgoingReferrer = url.strippedForUseAsReferrer(); 692} 693 694void FrameLoader::didBeginDocument(bool dispatch) 695{ 696 m_needsClear = true; 697 m_isComplete = false; 698 m_didCallImplicitClose = false; 699 m_frame.document()->setReadyState(Document::Loading); 700 701 if (m_pendingStateObject) { 702 m_frame.document()->statePopped(m_pendingStateObject.get()); 703 m_pendingStateObject.clear(); 704 } 705 706 if (dispatch) 707 dispatchDidClearWindowObjectsInAllWorlds(); 708 709 updateFirstPartyForCookies(); 710 m_frame.document()->initContentSecurityPolicy(); 711 712 const Settings& settings = m_frame.settings(); 713 m_frame.document()->cachedResourceLoader()->setImagesEnabled(settings.areImagesEnabled()); 714 m_frame.document()->cachedResourceLoader()->setAutoLoadImages(settings.loadsImagesAutomatically()); 715 716 if (m_documentLoader) { 717 String dnsPrefetchControl = m_documentLoader->response().httpHeaderField(HTTPHeaderName::XDNSPrefetchControl); 718 if (!dnsPrefetchControl.isEmpty()) 719 m_frame.document()->parseDNSPrefetchControlHeader(dnsPrefetchControl); 720 721 String policyValue = m_documentLoader->response().httpHeaderField(HTTPHeaderName::ContentSecurityPolicy); 722 if (!policyValue.isEmpty()) 723 m_frame.document()->contentSecurityPolicy()->didReceiveHeader(policyValue, ContentSecurityPolicy::Enforce); 724 725 policyValue = m_documentLoader->response().httpHeaderField(HTTPHeaderName::ContentSecurityPolicyReportOnly); 726 if (!policyValue.isEmpty()) 727 m_frame.document()->contentSecurityPolicy()->didReceiveHeader(policyValue, ContentSecurityPolicy::Report); 728 729 policyValue = m_documentLoader->response().httpHeaderField(HTTPHeaderName::XWebKitCSP); 730 if (!policyValue.isEmpty()) 731 m_frame.document()->contentSecurityPolicy()->didReceiveHeader(policyValue, ContentSecurityPolicy::PrefixedEnforce); 732 733 policyValue = m_documentLoader->response().httpHeaderField(HTTPHeaderName::XWebKitCSPReportOnly); 734 if (!policyValue.isEmpty()) 735 m_frame.document()->contentSecurityPolicy()->didReceiveHeader(policyValue, ContentSecurityPolicy::PrefixedReport); 736 737 String headerContentLanguage = m_documentLoader->response().httpHeaderField(HTTPHeaderName::ContentLanguage); 738 if (!headerContentLanguage.isEmpty()) { 739 size_t commaIndex = headerContentLanguage.find(','); 740 headerContentLanguage.truncate(commaIndex); // notFound == -1 == don't truncate 741 headerContentLanguage = headerContentLanguage.stripWhiteSpace(isHTMLSpace); 742 if (!headerContentLanguage.isEmpty()) 743 m_frame.document()->setContentLanguage(headerContentLanguage); 744 } 745 } 746 747 history().restoreDocumentState(); 748} 749 750void FrameLoader::finishedParsing() 751{ 752 m_frame.injectUserScripts(InjectAtDocumentEnd); 753 754 if (m_stateMachine.creatingInitialEmptyDocument()) 755 return; 756 757 // This can be called from the Frame's destructor, in which case we shouldn't protect ourselves 758 // because doing so will cause us to re-enter the destructor when protector goes out of scope. 759 // Null-checking the FrameView indicates whether or not we're in the destructor. 760 RefPtr<Frame> protector = m_frame.view() ? &m_frame : 0; 761 762 m_client.dispatchDidFinishDocumentLoad(); 763 764 checkCompleted(); 765 766 if (!m_frame.view()) 767 return; // We are being destroyed by something checkCompleted called. 768 769 // Check if the scrollbars are really needed for the content. 770 // If not, remove them, relayout, and repaint. 771 m_frame.view()->restoreScrollbar(); 772 scrollToFragmentWithParentBoundary(m_frame.document()->url()); 773} 774 775void FrameLoader::loadDone() 776{ 777 checkCompleted(); 778} 779 780bool FrameLoader::allChildrenAreComplete() const 781{ 782 for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) { 783 if (!child->loader().m_isComplete) 784 return false; 785 } 786 return true; 787} 788 789bool FrameLoader::allAncestorsAreComplete() const 790{ 791 for (Frame* ancestor = &m_frame; ancestor; ancestor = ancestor->tree().parent()) { 792 if (!ancestor->loader().m_isComplete) 793 return false; 794 } 795 return true; 796} 797 798void FrameLoader::checkCompleted() 799{ 800 m_shouldCallCheckCompleted = false; 801 802 // Have we completed before? 803 if (m_isComplete) 804 return; 805 806 // Are we still parsing? 807 if (m_frame.document()->parsing()) 808 return; 809 810 // Still waiting for images/scripts? 811 if (m_frame.document()->cachedResourceLoader()->requestCount()) 812 return; 813 814 // Still waiting for elements that don't go through a FrameLoader? 815 if (m_frame.document()->isDelayingLoadEvent()) 816 return; 817 818 // Any frame that hasn't completed yet? 819 if (!allChildrenAreComplete()) 820 return; 821 822 // Important not to protect earlier in this function, because earlier parts 823 // of this function can be called in the frame's destructor, and it's not legal 824 // to ref an object while it's being destroyed. 825 Ref<Frame> protect(m_frame); 826 827 // OK, completed. 828 m_isComplete = true; 829 m_requestedHistoryItem = 0; 830 m_frame.document()->setReadyState(Document::Complete); 831 832#if PLATFORM(IOS) 833 if (m_frame.document()->url().isEmpty()) { 834 // We need to update the document URL of a PDF document to be non-empty so that both back/forward history navigation 835 // between PDF pages and fragment navigation works. See <rdar://problem/9544769> for more details. 836 // FIXME: Is there a better place for this code, say DocumentLoader? Also, we should explicitly only update the URL 837 // of the document when it's a PDFDocument object instead of assuming that a Document object with an empty URL is a PDFDocument. 838 // FIXME: This code is incorrect for a synthesized document (which also has an empty URL). The URL for a synthesized 839 // document should be the URL specified to FrameLoader::initForSynthesizedDocument(). 840 m_frame.document()->setURL(activeDocumentLoader()->documentURL()); 841 } 842#endif 843 844 checkCallImplicitClose(); // if we didn't do it before 845 846 m_frame.navigationScheduler().startTimer(); 847 848 completed(); 849 if (m_frame.page()) 850 checkLoadComplete(); 851} 852 853void FrameLoader::checkTimerFired(Timer<FrameLoader>&) 854{ 855 Ref<Frame> protect(m_frame); 856 857 if (Page* page = m_frame.page()) { 858 if (page->defersLoading()) 859 return; 860 } 861 if (m_shouldCallCheckCompleted) 862 checkCompleted(); 863 if (m_shouldCallCheckLoadComplete) 864 checkLoadComplete(); 865} 866 867void FrameLoader::startCheckCompleteTimer() 868{ 869 if (!(m_shouldCallCheckCompleted || m_shouldCallCheckLoadComplete)) 870 return; 871 if (m_checkTimer.isActive()) 872 return; 873 m_checkTimer.startOneShot(0); 874} 875 876void FrameLoader::scheduleCheckCompleted() 877{ 878 m_shouldCallCheckCompleted = true; 879 startCheckCompleteTimer(); 880} 881 882void FrameLoader::scheduleCheckLoadComplete() 883{ 884 m_shouldCallCheckLoadComplete = true; 885 startCheckCompleteTimer(); 886} 887 888void FrameLoader::checkCallImplicitClose() 889{ 890 if (m_didCallImplicitClose || m_frame.document()->parsing() || m_frame.document()->isDelayingLoadEvent()) 891 return; 892 893 if (!allChildrenAreComplete()) 894 return; // still got a frame running -> too early 895 896 m_didCallImplicitClose = true; 897 m_wasUnloadEventEmitted = false; 898 m_frame.document()->implicitClose(); 899} 900 901void FrameLoader::loadURLIntoChildFrame(const URL& url, const String& referer, Frame* childFrame) 902{ 903 ASSERT(childFrame); 904 905#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) 906 RefPtr<Archive> subframeArchive = activeDocumentLoader()->popArchiveForSubframe(childFrame->tree().uniqueName(), url); 907 if (subframeArchive) { 908 childFrame->loader().loadArchive(subframeArchive.release()); 909 return; 910 } 911#endif // ENABLE(WEB_ARCHIVE) 912 913 HistoryItem* parentItem = history().currentItem(); 914 // If we're moving in the back/forward list, we might want to replace the content 915 // of this child frame with whatever was there at that point. 916 if (parentItem && parentItem->children().size() && isBackForwardLoadType(loadType()) 917 && !m_frame.document()->loadEventFinished()) { 918 HistoryItem* childItem = parentItem->childItemWithTarget(childFrame->tree().uniqueName()); 919 if (childItem) { 920 childFrame->loader().m_requestedHistoryItem = childItem; 921 childFrame->loader().loadDifferentDocumentItem(childItem, loadType(), MayAttemptCacheOnlyLoadForFormSubmissionItem); 922 return; 923 } 924 } 925 926 childFrame->loader().loadURL(url, referer, "_self", LockHistory::No, FrameLoadType::RedirectWithLockedBackForwardList, 0, 0, AllowNavigationToInvalidURL::Yes); 927} 928 929#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) 930void FrameLoader::loadArchive(PassRefPtr<Archive> archive) 931{ 932 ArchiveResource* mainResource = archive->mainResource(); 933 ASSERT(mainResource); 934 if (!mainResource) 935 return; 936 937 SubstituteData substituteData(mainResource->data(), mainResource->mimeType(), mainResource->textEncoding(), URL()); 938 939 ResourceRequest request(mainResource->url()); 940#if PLATFORM(MAC) 941 request.applyWebArchiveHackForMail(); 942#endif 943 944 RefPtr<DocumentLoader> documentLoader = m_client.createDocumentLoader(request, substituteData); 945 documentLoader->setArchive(archive.get()); 946 load(documentLoader.get()); 947} 948#endif // ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) 949 950ObjectContentType FrameLoader::defaultObjectContentType(const URL& url, const String& mimeTypeIn, bool shouldPreferPlugInsForImages) 951{ 952 String mimeType = mimeTypeIn; 953 954 if (mimeType.isEmpty()) 955 mimeType = mimeTypeFromURL(url); 956 957#if !PLATFORM(COCOA) && !PLATFORM(EFL) // Mac has no PluginDatabase, nor does EFL 958 if (mimeType.isEmpty()) { 959 String decodedPath = decodeURLEscapeSequences(url.path()); 960 mimeType = PluginDatabase::installedPlugins()->MIMETypeForExtension(decodedPath.substring(decodedPath.reverseFind('.') + 1)); 961 } 962#endif 963 964 if (mimeType.isEmpty()) 965 return ObjectContentFrame; // Go ahead and hope that we can display the content. 966 967#if !PLATFORM(COCOA) && !PLATFORM(EFL) // Mac has no PluginDatabase, nor does EFL 968 bool plugInSupportsMIMEType = PluginDatabase::installedPlugins()->isMIMETypeRegistered(mimeType); 969#else 970 bool plugInSupportsMIMEType = false; 971#endif 972 973 if (MIMETypeRegistry::isSupportedImageMIMEType(mimeType)) 974 return shouldPreferPlugInsForImages && plugInSupportsMIMEType ? WebCore::ObjectContentNetscapePlugin : WebCore::ObjectContentImage; 975 976 if (plugInSupportsMIMEType) 977 return WebCore::ObjectContentNetscapePlugin; 978 979 if (MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType)) 980 return WebCore::ObjectContentFrame; 981 982 return WebCore::ObjectContentNone; 983} 984 985String FrameLoader::outgoingReferrer() const 986{ 987 // See http://www.whatwg.org/specs/web-apps/current-work/#fetching-resources 988 // for why we walk the parent chain for srcdoc documents. 989 Frame* frame = &m_frame; 990 while (frame->document()->isSrcdocDocument()) { 991 frame = frame->tree().parent(); 992 // Srcdoc documents cannot be top-level documents, by definition, 993 // because they need to be contained in iframes with the srcdoc. 994 ASSERT(frame); 995 } 996 return frame->loader().m_outgoingReferrer; 997} 998 999String FrameLoader::outgoingOrigin() const 1000{ 1001 return m_frame.document()->securityOrigin()->toString(); 1002} 1003 1004bool FrameLoader::checkIfFormActionAllowedByCSP(const URL& url) const 1005{ 1006 if (m_submittedFormURL.isEmpty()) 1007 return true; 1008 1009 return m_frame.document()->contentSecurityPolicy()->allowFormAction(url); 1010} 1011 1012Frame* FrameLoader::opener() 1013{ 1014 return m_opener; 1015} 1016 1017void FrameLoader::setOpener(Frame* opener) 1018{ 1019 if (m_opener && !opener) 1020 m_client.didDisownOpener(); 1021 1022 if (m_opener) 1023 m_opener->loader().m_openedFrames.remove(&m_frame); 1024 if (opener) 1025 opener->loader().m_openedFrames.add(&m_frame); 1026 m_opener = opener; 1027 1028 if (m_frame.document()) 1029 m_frame.document()->initSecurityContext(); 1030} 1031 1032// FIXME: This does not belong in FrameLoader! 1033void FrameLoader::handleFallbackContent() 1034{ 1035 HTMLFrameOwnerElement* owner = m_frame.ownerElement(); 1036 if (!owner || !owner->hasTagName(objectTag)) 1037 return; 1038 toHTMLObjectElement(owner)->renderFallbackContent(); 1039} 1040 1041void FrameLoader::provisionalLoadStarted() 1042{ 1043 if (m_stateMachine.firstLayoutDone()) 1044 m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad); 1045 m_frame.navigationScheduler().cancel(true); 1046 m_client.provisionalLoadStarted(); 1047} 1048 1049void FrameLoader::resetMultipleFormSubmissionProtection() 1050{ 1051 m_submittedFormURL = URL(); 1052} 1053 1054void FrameLoader::updateFirstPartyForCookies() 1055{ 1056 if (m_frame.tree().parent()) 1057 setFirstPartyForCookies(m_frame.tree().parent()->document()->firstPartyForCookies()); 1058 else 1059 setFirstPartyForCookies(m_frame.document()->url()); 1060} 1061 1062void FrameLoader::setFirstPartyForCookies(const URL& url) 1063{ 1064 for (Frame* frame = &m_frame; frame; frame = frame->tree().traverseNext(&m_frame)) 1065 frame->document()->setFirstPartyForCookies(url); 1066} 1067 1068// This does the same kind of work that didOpenURL does, except it relies on the fact 1069// that a higher level already checked that the URLs match and the scrolling is the right thing to do. 1070void FrameLoader::loadInSameDocument(const URL& url, PassRefPtr<SerializedScriptValue> stateObject, bool isNewNavigation) 1071{ 1072 // If we have a state object, we cannot also be a new navigation. 1073 ASSERT(!stateObject || (stateObject && !isNewNavigation)); 1074 1075 // Update the data source's request with the new URL to fake the URL change 1076 URL oldURL = m_frame.document()->url(); 1077 m_frame.document()->setURL(url); 1078 setOutgoingReferrer(url); 1079 documentLoader()->replaceRequestURLForSameDocumentNavigation(url); 1080 if (isNewNavigation && !shouldTreatURLAsSameAsCurrent(url) && !stateObject) { 1081 // NB: must happen after replaceRequestURLForSameDocumentNavigation(), since we add 1082 // based on the current request. Must also happen before we openURL and displace the 1083 // scroll position, since adding the BF item will save away scroll state. 1084 1085 // NB2: If we were loading a long, slow doc, and the user fragment navigated before 1086 // it was done, currItem is now set the that slow doc, and prevItem is whatever was 1087 // before it. Adding the b/f item will bump the slow doc down to prevItem, even 1088 // though its load is not yet done. I think this all works out OK, for one because 1089 // we have already saved away the scroll and doc state for the long slow load, 1090 // but it's not an obvious case. 1091 1092 history().updateBackForwardListForFragmentScroll(); 1093 } 1094 1095 bool hashChange = equalIgnoringFragmentIdentifier(url, oldURL) && url.fragmentIdentifier() != oldURL.fragmentIdentifier(); 1096 1097 history().updateForSameDocumentNavigation(); 1098 1099 // If we were in the autoscroll/panScroll mode we want to stop it before following the link to the anchor 1100 if (hashChange) 1101 m_frame.eventHandler().stopAutoscrollTimer(); 1102 1103 // It's important to model this as a load that starts and immediately finishes. 1104 // Otherwise, the parent frame may think we never finished loading. 1105 started(); 1106 1107 // We need to scroll to the fragment whether or not a hash change occurred, since 1108 // the user might have scrolled since the previous navigation. 1109 scrollToFragmentWithParentBoundary(url); 1110 1111 m_isComplete = false; 1112 checkCompleted(); 1113 1114 if (isNewNavigation) { 1115 // This will clear previousItem from the rest of the frame tree that didn't 1116 // doing any loading. We need to make a pass on this now, since for fragment 1117 // navigation we'll not go through a real load and reach Completed state. 1118 checkLoadComplete(); 1119 } 1120 1121 m_client.dispatchDidNavigateWithinPage(); 1122 1123 m_frame.document()->statePopped(stateObject ? stateObject : SerializedScriptValue::nullValue()); 1124 m_client.dispatchDidPopStateWithinPage(); 1125 1126 if (hashChange) { 1127 m_frame.document()->enqueueHashchangeEvent(oldURL, url); 1128 m_client.dispatchDidChangeLocationWithinPage(); 1129 } 1130 1131 // FrameLoaderClient::didFinishLoad() tells the internal load delegate the load finished with no error 1132 m_client.didFinishLoad(); 1133} 1134 1135bool FrameLoader::isComplete() const 1136{ 1137 return m_isComplete; 1138} 1139 1140void FrameLoader::completed() 1141{ 1142 Ref<Frame> protect(m_frame); 1143 1144 for (Frame* descendant = m_frame.tree().traverseNext(&m_frame); descendant; descendant = descendant->tree().traverseNext(&m_frame)) 1145 descendant->navigationScheduler().startTimer(); 1146 1147 if (Frame* parent = m_frame.tree().parent()) 1148 parent->loader().checkCompleted(); 1149 1150 if (m_frame.view()) 1151 m_frame.view()->maintainScrollPositionAtAnchor(0); 1152 m_activityAssertion = nullptr; 1153} 1154 1155void FrameLoader::started() 1156{ 1157 if (m_frame.page() && m_frame.page()->pageThrottler()) 1158 m_activityAssertion = m_frame.page()->pageThrottler()->pageLoadActivityToken(); 1159 for (Frame* frame = &m_frame; frame; frame = frame->tree().parent()) 1160 frame->loader().m_isComplete = false; 1161} 1162 1163void FrameLoader::prepareForHistoryNavigation() 1164{ 1165 // If there is no currentItem, but we still want to engage in 1166 // history navigation we need to manufacture one, and update 1167 // the state machine of this frame to impersonate having 1168 // loaded it. 1169 RefPtr<HistoryItem> currentItem = history().currentItem(); 1170 if (!currentItem) { 1171 currentItem = HistoryItem::create(); 1172 currentItem->setLastVisitWasFailure(true); 1173 history().setCurrentItem(currentItem.get()); 1174 m_frame.page()->backForward().setCurrentItem(currentItem.get()); 1175 1176 ASSERT(stateMachine().isDisplayingInitialEmptyDocument()); 1177 stateMachine().advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocumentPostCommit); 1178 stateMachine().advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad); 1179 } 1180} 1181 1182void FrameLoader::prepareForLoadStart() 1183{ 1184 m_progressTracker->progressStarted(); 1185 m_client.dispatchDidStartProvisionalLoad(); 1186 1187 if (AXObjectCache::accessibilityEnabled()) { 1188 if (AXObjectCache* cache = m_frame.document()->existingAXObjectCache()) { 1189 AXObjectCache::AXLoadingEvent loadingEvent = loadType() == FrameLoadType::Reload ? AXObjectCache::AXLoadingReloaded : AXObjectCache::AXLoadingStarted; 1190 cache->frameLoadingEventNotification(&m_frame, loadingEvent); 1191 } 1192 } 1193} 1194 1195void FrameLoader::setupForReplace() 1196{ 1197 m_client.revertToProvisionalState(m_documentLoader.get()); 1198 setState(FrameStateProvisional); 1199 m_provisionalDocumentLoader = m_documentLoader; 1200 m_documentLoader = 0; 1201 detachChildren(); 1202} 1203 1204void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, LockHistory lockHistory, LockBackForwardList lockBackForwardList, 1205 PassRefPtr<Event> event, PassRefPtr<FormState> formState, ShouldSendReferrer shouldSendReferrer, AllowNavigationToInvalidURL allowNavigationToInvalidURL) 1206{ 1207 // Protect frame from getting blown away inside dispatchBeforeLoadEvent in loadWithDocumentLoader. 1208 Ref<Frame> protect(m_frame); 1209 1210 URL url = request.resourceRequest().url(); 1211 1212 ASSERT(m_frame.document()); 1213 if (!request.requester()->canDisplay(url)) { 1214 reportLocalLoadFailed(&m_frame, url.stringCenterEllipsizedToLength()); 1215 return; 1216 } 1217 1218 String argsReferrer = request.resourceRequest().httpReferrer(); 1219 if (argsReferrer.isEmpty()) 1220 argsReferrer = outgoingReferrer(); 1221 1222 String referrer = SecurityPolicy::generateReferrerHeader(m_frame.document()->referrerPolicy(), url, argsReferrer); 1223 if (shouldSendReferrer == NeverSendReferrer) 1224 referrer = String(); 1225 1226 FrameLoadType loadType; 1227 if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData) 1228 loadType = FrameLoadType::Reload; 1229 else if (lockBackForwardList == LockBackForwardList::Yes) 1230 loadType = FrameLoadType::RedirectWithLockedBackForwardList; 1231 else 1232 loadType = FrameLoadType::Standard; 1233 1234 if (request.resourceRequest().httpMethod() == "POST") 1235 loadPostRequest(request.resourceRequest(), referrer, request.frameName(), lockHistory, loadType, event, formState.get(), allowNavigationToInvalidURL); 1236 else 1237 loadURL(request.resourceRequest().url(), referrer, request.frameName(), lockHistory, loadType, event, formState.get(), allowNavigationToInvalidURL); 1238 1239 // FIXME: It's possible this targetFrame will not be the same frame that was targeted by the actual 1240 // load if frame names have changed. 1241 Frame* sourceFrame = formState ? formState->sourceDocument()->frame() : &m_frame; 1242 if (!sourceFrame) 1243 sourceFrame = &m_frame; 1244 Frame* targetFrame = sourceFrame->loader().findFrameForNavigation(request.frameName()); 1245 if (targetFrame && targetFrame != sourceFrame) { 1246 if (Page* page = targetFrame->page()) 1247 page->chrome().focus(); 1248 } 1249} 1250 1251void FrameLoader::loadURL(const URL& newURL, const String& referrer, const String& frameName, LockHistory lockHistory, FrameLoadType newLoadType, 1252 PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState, AllowNavigationToInvalidURL allowNavigationToInvalidURL) 1253{ 1254 if (m_inStopAllLoaders) 1255 return; 1256 1257 Ref<Frame> protect(m_frame); 1258 1259 RefPtr<FormState> formState = prpFormState; 1260 bool isFormSubmission = formState; 1261 1262 ResourceRequest request(newURL); 1263 if (!referrer.isEmpty()) { 1264 request.setHTTPReferrer(referrer); 1265 RefPtr<SecurityOrigin> referrerOrigin = SecurityOrigin::createFromString(referrer); 1266 addHTTPOriginIfNeeded(request, referrerOrigin->toString()); 1267 } 1268#if ENABLE(CACHE_PARTITIONING) 1269 if (&m_frame.tree().top() != &m_frame) 1270 request.setCachePartition(m_frame.tree().top().document()->securityOrigin()->cachePartition()); 1271#endif 1272 addExtraFieldsToRequest(request, newLoadType, true); 1273 if (newLoadType == FrameLoadType::Reload || newLoadType == FrameLoadType::ReloadFromOrigin) 1274 request.setCachePolicy(ReloadIgnoringCacheData); 1275 1276 ASSERT(newLoadType != FrameLoadType::Same); 1277 1278 // The search for a target frame is done earlier in the case of form submission. 1279 Frame* targetFrame = isFormSubmission ? 0 : findFrameForNavigation(frameName); 1280 if (targetFrame && targetFrame != &m_frame) { 1281 targetFrame->loader().loadURL(newURL, referrer, "_self", lockHistory, newLoadType, event, formState.release(), allowNavigationToInvalidURL); 1282 return; 1283 } 1284 1285 if (m_pageDismissalEventBeingDispatched != NoDismissal) 1286 return; 1287 1288 NavigationAction action(request, newLoadType, isFormSubmission, event); 1289 1290 if (!targetFrame && !frameName.isEmpty()) { 1291 policyChecker().checkNewWindowPolicy(action, request, formState.release(), frameName, [this, allowNavigationToInvalidURL](const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, const NavigationAction& action, bool shouldContinue) { 1292 continueLoadAfterNewWindowPolicy(request, formState, frameName, action, shouldContinue, allowNavigationToInvalidURL); 1293 }); 1294 return; 1295 } 1296 1297 RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader; 1298 1299 bool sameURL = shouldTreatURLAsSameAsCurrent(newURL); 1300 const String& httpMethod = request.httpMethod(); 1301 1302 // Make sure to do scroll to fragment processing even if the URL is 1303 // exactly the same so pages with '#' links and DHTML side effects 1304 // work properly. 1305 if (shouldPerformFragmentNavigation(isFormSubmission, httpMethod, newLoadType, newURL)) { 1306 oldDocumentLoader->setTriggeringAction(action); 1307 oldDocumentLoader->setLastCheckedRequest(ResourceRequest()); 1308 policyChecker().stopCheck(); 1309 policyChecker().setLoadType(newLoadType); 1310 policyChecker().checkNavigationPolicy(request, oldDocumentLoader.get(), formState.release(), [this](const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue) { 1311 continueFragmentScrollAfterNavigationPolicy(request, shouldContinue); 1312 }); 1313 return; 1314 } 1315 1316 // must grab this now, since this load may stop the previous load and clear this flag 1317 bool isRedirect = m_quickRedirectComing; 1318 loadWithNavigationAction(request, action, lockHistory, newLoadType, formState.release(), allowNavigationToInvalidURL); 1319 if (isRedirect) { 1320 m_quickRedirectComing = false; 1321 if (m_provisionalDocumentLoader) 1322 m_provisionalDocumentLoader->setIsClientRedirect(true); 1323 } else if (sameURL && newLoadType != FrameLoadType::Reload && newLoadType != FrameLoadType::ReloadFromOrigin) { 1324 // Example of this case are sites that reload the same URL with a different cookie 1325 // driving the generated content, or a master frame with links that drive a target 1326 // frame, where the user has clicked on the same link repeatedly. 1327 m_loadType = FrameLoadType::Same; 1328 } 1329} 1330 1331SubstituteData FrameLoader::defaultSubstituteDataForURL(const URL& url) 1332{ 1333 if (!shouldTreatURLAsSrcdocDocument(url)) 1334 return SubstituteData(); 1335 String srcdoc = m_frame.ownerElement()->fastGetAttribute(srcdocAttr); 1336 ASSERT(!srcdoc.isNull()); 1337 CString encodedSrcdoc = srcdoc.utf8(); 1338 return SubstituteData(SharedBuffer::create(encodedSrcdoc.data(), encodedSrcdoc.length()), "text/html", "UTF-8", URL()); 1339} 1340 1341void FrameLoader::load(const FrameLoadRequest& passedRequest) 1342{ 1343 FrameLoadRequest request(passedRequest); 1344 1345 if (m_inStopAllLoaders) 1346 return; 1347 1348 if (!request.frameName().isEmpty()) { 1349 Frame* frame = findFrameForNavigation(request.frameName()); 1350 if (frame) { 1351 request.setShouldCheckNewWindowPolicy(false); 1352 if (&frame->loader() != this) { 1353 frame->loader().load(request); 1354 return; 1355 } 1356 } 1357 } 1358 1359 if (request.shouldCheckNewWindowPolicy()) { 1360 policyChecker().checkNewWindowPolicy(NavigationAction(request.resourceRequest(), NavigationTypeOther), request.resourceRequest(), nullptr, request.frameName(), [this](const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, const NavigationAction& action, bool shouldContinue) { 1361 continueLoadAfterNewWindowPolicy(request, formState, frameName, action, shouldContinue, AllowNavigationToInvalidURL::Yes); 1362 }); 1363 1364 return; 1365 } 1366 1367 if (!request.hasSubstituteData()) 1368 request.setSubstituteData(defaultSubstituteDataForURL(request.resourceRequest().url())); 1369 1370 RefPtr<DocumentLoader> loader = m_client.createDocumentLoader(request.resourceRequest(), request.substituteData()); 1371 load(loader.get()); 1372} 1373 1374void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, LockHistory lockHistory, FrameLoadType type, PassRefPtr<FormState> formState, AllowNavigationToInvalidURL allowNavigationToInvalidURL) 1375{ 1376 RefPtr<DocumentLoader> loader = m_client.createDocumentLoader(request, defaultSubstituteDataForURL(request.url())); 1377 if (lockHistory == LockHistory::Yes && m_documentLoader) 1378 loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory().string() : m_documentLoader->clientRedirectSourceForHistory()); 1379 1380 loader->setTriggeringAction(action); 1381 if (m_documentLoader) 1382 loader->setOverrideEncoding(m_documentLoader->overrideEncoding()); 1383 1384 loadWithDocumentLoader(loader.get(), type, formState, allowNavigationToInvalidURL); 1385} 1386 1387void FrameLoader::load(DocumentLoader* newDocumentLoader) 1388{ 1389 ResourceRequest& r = newDocumentLoader->request(); 1390 addExtraFieldsToMainResourceRequest(r); 1391 FrameLoadType type; 1392 1393 if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->originalRequest().url())) { 1394 r.setCachePolicy(ReloadIgnoringCacheData); 1395 type = FrameLoadType::Same; 1396 } else if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->unreachableURL()) && m_loadType == FrameLoadType::Reload) 1397 type = FrameLoadType::Reload; 1398 else 1399 type = FrameLoadType::Standard; 1400 1401 if (m_documentLoader) 1402 newDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding()); 1403 1404 // When we loading alternate content for an unreachable URL that we're 1405 // visiting in the history list, we treat it as a reload so the history list 1406 // is appropriately maintained. 1407 // 1408 // FIXME: This seems like a dangerous overloading of the meaning of "FrameLoadType::Reload" ... 1409 // shouldn't a more explicit type of reload be defined, that means roughly 1410 // "load without affecting history" ? 1411 if (shouldReloadToHandleUnreachableURL(newDocumentLoader)) { 1412 // shouldReloadToHandleUnreachableURL() returns true only when the original load type is back-forward. 1413 // In this case we should save the document state now. Otherwise the state can be lost because load type is 1414 // changed and updateForBackForwardNavigation() will not be called when loading is committed. 1415 history().saveDocumentAndScrollState(); 1416 1417 ASSERT(type == FrameLoadType::Standard); 1418 type = FrameLoadType::Reload; 1419 } 1420 1421 loadWithDocumentLoader(newDocumentLoader, type, 0, AllowNavigationToInvalidURL::Yes); 1422} 1423 1424void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState, AllowNavigationToInvalidURL allowNavigationToInvalidURL) 1425{ 1426 // Retain because dispatchBeforeLoadEvent may release the last reference to it. 1427 Ref<Frame> protect(m_frame); 1428 1429 ASSERT(m_client.hasWebView()); 1430 1431 // Unfortunately the view must be non-nil, this is ultimately due 1432 // to parser requiring a FrameView. We should fix this dependency. 1433 1434 ASSERT(m_frame.view()); 1435 1436 if (m_pageDismissalEventBeingDispatched != NoDismissal) 1437 return; 1438 1439 if (m_frame.document()) 1440 m_previousURL = m_frame.document()->url(); 1441 1442 policyChecker().setLoadType(type); 1443 RefPtr<FormState> formState = prpFormState; 1444 bool isFormSubmission = formState; 1445 1446 const URL& newURL = loader->request().url(); 1447 const String& httpMethod = loader->request().httpMethod(); 1448 1449 if (shouldPerformFragmentNavigation(isFormSubmission, httpMethod, policyChecker().loadType(), newURL)) { 1450 RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader; 1451 NavigationAction action(loader->request(), policyChecker().loadType(), isFormSubmission); 1452 1453 oldDocumentLoader->setTriggeringAction(action); 1454 oldDocumentLoader->setLastCheckedRequest(ResourceRequest()); 1455 policyChecker().stopCheck(); 1456 policyChecker().checkNavigationPolicy(loader->request(), oldDocumentLoader.get(), formState, [this](const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue) { 1457 continueFragmentScrollAfterNavigationPolicy(request, shouldContinue); 1458 }); 1459 return; 1460 } 1461 1462 if (Frame* parent = m_frame.tree().parent()) 1463 loader->setOverrideEncoding(parent->loader().documentLoader()->overrideEncoding()); 1464 1465 policyChecker().stopCheck(); 1466 setPolicyDocumentLoader(loader); 1467 if (loader->triggeringAction().isEmpty()) 1468 loader->setTriggeringAction(NavigationAction(loader->request(), policyChecker().loadType(), isFormSubmission)); 1469 1470 if (Element* ownerElement = m_frame.ownerElement()) { 1471 // We skip dispatching the beforeload event if we've already 1472 // committed a real document load because the event would leak 1473 // subsequent activity by the frame which the parent frame isn't 1474 // supposed to learn. For example, if the child frame navigated to 1475 // a new URL, the parent frame shouldn't learn the URL. 1476 if (!m_stateMachine.committedFirstRealDocumentLoad() 1477 && !ownerElement->dispatchBeforeLoadEvent(loader->request().url().string())) { 1478 continueLoadAfterNavigationPolicy(loader->request(), formState, false, allowNavigationToInvalidURL); 1479 return; 1480 } 1481 } 1482 1483 policyChecker().checkNavigationPolicy(loader->request(), loader, formState, [this, allowNavigationToInvalidURL](const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue) { 1484 continueLoadAfterNavigationPolicy(request, formState, shouldContinue, allowNavigationToInvalidURL); 1485 }); 1486} 1487 1488void FrameLoader::reportLocalLoadFailed(Frame* frame, const String& url) 1489{ 1490 ASSERT(!url.isEmpty()); 1491 if (!frame) 1492 return; 1493 1494 frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Not allowed to load local resource: " + url); 1495} 1496 1497const ResourceRequest& FrameLoader::initialRequest() const 1498{ 1499 return activeDocumentLoader()->originalRequest(); 1500} 1501 1502bool FrameLoader::willLoadMediaElementURL(URL& url) 1503{ 1504#if PLATFORM(IOS) 1505 // MobileStore depends on the iOS 4.0 era client delegate method because webView:resource:willSendRequest:redirectResponse:fromDataSource 1506 // doesn't let them tell when a load request is coming from a media element. See <rdar://problem/8266916> for more details. 1507 if (applicationIsMobileStore()) 1508 return m_client.shouldLoadMediaElementURL(url); 1509#endif 1510 1511 ResourceRequest request(url); 1512 1513 unsigned long identifier; 1514 ResourceError error; 1515 requestFromDelegate(request, identifier, error); 1516 notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, request, ResourceResponse(url, String(), -1, String(), String()), 0, -1, -1, error); 1517 1518 url = request.url(); 1519 1520 return error.isNull(); 1521} 1522 1523bool FrameLoader::shouldReloadToHandleUnreachableURL(DocumentLoader* docLoader) 1524{ 1525 URL unreachableURL = docLoader->unreachableURL(); 1526 1527 if (unreachableURL.isEmpty()) 1528 return false; 1529 1530 if (!isBackForwardLoadType(policyChecker().loadType())) 1531 return false; 1532 1533 // We only treat unreachableURLs specially during the delegate callbacks 1534 // for provisional load errors and navigation policy decisions. The former 1535 // case handles well-formed URLs that can't be loaded, and the latter 1536 // case handles malformed URLs and unknown schemes. Loading alternate content 1537 // at other times behaves like a standard load. 1538 DocumentLoader* compareDocumentLoader = 0; 1539 if (policyChecker().delegateIsDecidingNavigationPolicy() || policyChecker().delegateIsHandlingUnimplementablePolicy()) 1540 compareDocumentLoader = m_policyDocumentLoader.get(); 1541 else if (m_delegateIsHandlingProvisionalLoadError) 1542 compareDocumentLoader = m_provisionalDocumentLoader.get(); 1543 1544 return compareDocumentLoader && unreachableURL == compareDocumentLoader->request().url(); 1545} 1546 1547void FrameLoader::reloadWithOverrideEncoding(const String& encoding) 1548{ 1549 if (!m_documentLoader) 1550 return; 1551 1552 ResourceRequest request = m_documentLoader->request(); 1553 URL unreachableURL = m_documentLoader->unreachableURL(); 1554 if (!unreachableURL.isEmpty()) 1555 request.setURL(unreachableURL); 1556 1557 // FIXME: If the resource is a result of form submission and is not cached, the form will be silently resubmitted. 1558 // We should ask the user for confirmation in this case. 1559 request.setCachePolicy(ReturnCacheDataElseLoad); 1560 1561 RefPtr<DocumentLoader> loader = m_client.createDocumentLoader(request, defaultSubstituteDataForURL(request.url())); 1562 setPolicyDocumentLoader(loader.get()); 1563 1564 loader->setOverrideEncoding(encoding); 1565 1566 loadWithDocumentLoader(loader.get(), FrameLoadType::Reload, 0, AllowNavigationToInvalidURL::Yes); 1567} 1568 1569void FrameLoader::reload(bool endToEndReload) 1570{ 1571 if (!m_documentLoader) 1572 return; 1573 1574 // If a window is created by javascript, its main frame can have an empty but non-nil URL. 1575 // Reloading in this case will lose the current contents (see 4151001). 1576 if (m_documentLoader->request().url().isEmpty()) 1577 return; 1578 1579 // Replace error-page URL with the URL we were trying to reach. 1580 ResourceRequest initialRequest = m_documentLoader->request(); 1581 URL unreachableURL = m_documentLoader->unreachableURL(); 1582 if (!unreachableURL.isEmpty()) 1583 initialRequest.setURL(unreachableURL); 1584 1585 // Create a new document loader for the reload, this will become m_documentLoader eventually, 1586 // but first it has to be the "policy" document loader, and then the "provisional" document loader. 1587 RefPtr<DocumentLoader> loader = m_client.createDocumentLoader(initialRequest, defaultSubstituteDataForURL(initialRequest.url())); 1588 1589 ResourceRequest& request = loader->request(); 1590 1591 // FIXME: We don't have a mechanism to revalidate the main resource without reloading at the moment. 1592 request.setCachePolicy(ReloadIgnoringCacheData); 1593 1594 // If we're about to re-post, set up action so the application can warn the user. 1595 if (request.httpMethod() == "POST") 1596 loader->setTriggeringAction(NavigationAction(request, NavigationTypeFormResubmitted)); 1597 1598 loader->setOverrideEncoding(m_documentLoader->overrideEncoding()); 1599 1600 loadWithDocumentLoader(loader.get(), endToEndReload ? FrameLoadType::ReloadFromOrigin : FrameLoadType::Reload, 0, AllowNavigationToInvalidURL::Yes); 1601} 1602 1603void FrameLoader::stopAllLoaders(ClearProvisionalItemPolicy clearProvisionalItemPolicy) 1604{ 1605 ASSERT(!m_frame.document() || !m_frame.document()->inPageCache()); 1606 if (m_pageDismissalEventBeingDispatched != NoDismissal) 1607 return; 1608 1609 // If this method is called from within this method, infinite recursion can occur (3442218). Avoid this. 1610 if (m_inStopAllLoaders) 1611 return; 1612 1613 // Calling stopLoading() on the provisional document loader can blow away 1614 // the frame from underneath. 1615 Ref<Frame> protect(m_frame); 1616 1617 m_inStopAllLoaders = true; 1618 1619 policyChecker().stopCheck(); 1620 1621 // If no new load is in progress, we should clear the provisional item from history 1622 // before we call stopLoading. 1623 if (clearProvisionalItemPolicy == ShouldClearProvisionalItem) 1624 history().setProvisionalItem(0); 1625 1626 for (RefPtr<Frame> child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) 1627 child->loader().stopAllLoaders(clearProvisionalItemPolicy); 1628 if (m_provisionalDocumentLoader) 1629 m_provisionalDocumentLoader->stopLoading(); 1630 if (m_documentLoader) 1631 m_documentLoader->stopLoading(); 1632 1633 setProvisionalDocumentLoader(0); 1634 1635 m_checkTimer.stop(); 1636 1637 m_inStopAllLoaders = false; 1638} 1639 1640void FrameLoader::stopForUserCancel(bool deferCheckLoadComplete) 1641{ 1642 stopAllLoaders(); 1643 1644#if PLATFORM(IOS) 1645 // Lay out immediately when stopping to immediately clear the old page if we just committed this one 1646 // but haven't laid out/painted yet. 1647 // FIXME: Is this behavior specific to iOS? Or should we expose a setting to toggle this behavior? 1648 if (m_frame.view() && !m_frame.view()->didFirstLayout()) 1649 m_frame.view()->layout(); 1650#endif 1651 1652 if (deferCheckLoadComplete) 1653 scheduleCheckLoadComplete(); 1654 else if (m_frame.page()) 1655 checkLoadComplete(); 1656} 1657 1658DocumentLoader* FrameLoader::activeDocumentLoader() const 1659{ 1660 if (m_state == FrameStateProvisional) 1661 return m_provisionalDocumentLoader.get(); 1662 return m_documentLoader.get(); 1663} 1664 1665bool FrameLoader::isLoading() const 1666{ 1667 DocumentLoader* docLoader = activeDocumentLoader(); 1668 if (!docLoader) 1669 return false; 1670 return docLoader->isLoading(); 1671} 1672 1673bool FrameLoader::frameHasLoaded() const 1674{ 1675 return m_stateMachine.committedFirstRealDocumentLoad() || (m_provisionalDocumentLoader && !m_stateMachine.creatingInitialEmptyDocument()); 1676} 1677 1678void FrameLoader::setDocumentLoader(DocumentLoader* loader) 1679{ 1680 if (!loader && !m_documentLoader) 1681 return; 1682 1683 ASSERT(loader != m_documentLoader); 1684 ASSERT(!loader || loader->frameLoader() == this); 1685 1686 m_client.prepareForDataSourceReplacement(); 1687 detachChildren(); 1688 1689 // detachChildren() can trigger this frame's unload event, and therefore 1690 // script can run and do just about anything. For example, an unload event that calls 1691 // document.write("") on its parent frame can lead to a recursive detachChildren() 1692 // invocation for this frame. In that case, we can end up at this point with a 1693 // loader that hasn't been deleted but has been detached from its frame. Such a 1694 // DocumentLoader has been sufficiently detached that we'll end up in an inconsistent 1695 // state if we try to use it. 1696 if (loader && !loader->frame()) 1697 return; 1698 1699 if (m_documentLoader) 1700 m_documentLoader->detachFromFrame(); 1701 1702 m_documentLoader = loader; 1703} 1704 1705void FrameLoader::setPolicyDocumentLoader(DocumentLoader* loader) 1706{ 1707 if (m_policyDocumentLoader == loader) 1708 return; 1709 1710 if (loader) 1711 loader->setFrame(&m_frame); 1712 if (m_policyDocumentLoader 1713 && m_policyDocumentLoader != m_provisionalDocumentLoader 1714 && m_policyDocumentLoader != m_documentLoader) 1715 m_policyDocumentLoader->detachFromFrame(); 1716 1717 m_policyDocumentLoader = loader; 1718} 1719 1720void FrameLoader::setProvisionalDocumentLoader(DocumentLoader* loader) 1721{ 1722 ASSERT(!loader || !m_provisionalDocumentLoader); 1723 ASSERT(!loader || loader->frameLoader() == this); 1724 1725 if (m_provisionalDocumentLoader && m_provisionalDocumentLoader != m_documentLoader) 1726 m_provisionalDocumentLoader->detachFromFrame(); 1727 1728 m_provisionalDocumentLoader = loader; 1729} 1730 1731void FrameLoader::setState(FrameState newState) 1732{ 1733 m_state = newState; 1734 1735 if (newState == FrameStateProvisional) 1736 provisionalLoadStarted(); 1737 else if (newState == FrameStateComplete) { 1738 frameLoadCompleted(); 1739 if (m_documentLoader) 1740 m_documentLoader->stopRecordingResponses(); 1741 } 1742} 1743 1744void FrameLoader::clearProvisionalLoad() 1745{ 1746 setProvisionalDocumentLoader(0); 1747 m_progressTracker->progressCompleted(); 1748 setState(FrameStateComplete); 1749} 1750 1751void FrameLoader::commitProvisionalLoad() 1752{ 1753 RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader; 1754 Ref<Frame> protect(m_frame); 1755 1756 std::unique_ptr<CachedPage> cachedPage; 1757 if (m_loadingFromCachedPage) 1758 cachedPage = pageCache()->take(history().provisionalItem()); 1759 1760 LOG(PageCache, "WebCoreLoading %s: About to commit provisional load from previous URL '%s' to new URL '%s'", m_frame.tree().uniqueName().string().utf8().data(), 1761 m_frame.document() ? m_frame.document()->url().stringCenterEllipsizedToLength().utf8().data() : "", 1762 pdl ? pdl->url().stringCenterEllipsizedToLength().utf8().data() : "<no provisional DocumentLoader>"); 1763 1764#if PLATFORM(IOS) 1765 // In the case where we are not navigating to a cached page, and the system is under (speculative) memory pressure, 1766 // we can try to preemptively release some of the pages in the cache. 1767 // FIXME: Right now the capacity is 1 on iOS devices with 256 MB of RAM, so this will always blow away the whole 1768 // page cache. We could still preemptively prune the page cache while navigating to a cached page if capacity > 1. 1769 // See <rdar://problem/11779846> for more details. 1770 if (!cachedPage) { 1771 if (memoryPressureHandler().isUnderMemoryPressure()) { 1772 LOG(MemoryPressure, "Pruning page cache because under memory pressure at: %s", __PRETTY_FUNCTION__); 1773 LOG(PageCache, "Pruning page cache to 0 due to memory pressure"); 1774 // Don't cache any page if we are under memory pressure. 1775 pageCache()->pruneToCapacityNow(0); 1776 } else if (systemMemoryLevel() <= memoryLevelThresholdToPrunePageCache) { 1777 LOG(MemoryPressure, "Pruning page cache because system memory level is %d at: %s", systemMemoryLevel(), __PRETTY_FUNCTION__); 1778 LOG(PageCache, "Pruning page cache to %d due to low memory (level %d less or equal to %d threshold)", pageCache()->capacity() / 2, systemMemoryLevel(), memoryLevelThresholdToPrunePageCache); 1779 pageCache()->pruneToCapacityNow(pageCache()->capacity() / 2); 1780 } 1781 } 1782#endif 1783 1784 willTransitionToCommitted(); 1785 1786 // Check to see if we need to cache the page we are navigating away from into the back/forward cache. 1787 // We are doing this here because we know for sure that a new page is about to be loaded. 1788 HistoryItem* item = history().currentItem(); 1789 if (!m_frame.tree().parent() && pageCache()->canCache(m_frame.page()) && !item->isInPageCache()) 1790 pageCache()->add(item, *m_frame.page()); 1791 1792 if (m_loadType != FrameLoadType::Replace) 1793 closeOldDataSources(); 1794 1795 if (!cachedPage && !m_stateMachine.creatingInitialEmptyDocument()) 1796 m_client.makeRepresentation(pdl.get()); 1797 1798 transitionToCommitted(cachedPage.get()); 1799 1800 if (pdl && m_documentLoader) { 1801 // Check if the destination page is allowed to access the previous page's timing information. 1802 RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(pdl->request().url()); 1803 m_documentLoader->timing()->setHasSameOriginAsPreviousDocument(securityOrigin->canRequest(m_previousURL)); 1804 } 1805 1806 // Call clientRedirectCancelledOrFinished() here so that the frame load delegate is notified that the redirect's 1807 // status has changed, if there was a redirect. The frame load delegate may have saved some state about 1808 // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:. Since we are 1809 // just about to commit a new page, there cannot possibly be a pending redirect at this point. 1810 if (m_sentRedirectNotification) 1811 clientRedirectCancelledOrFinished(false); 1812 1813 if (cachedPage && cachedPage->document()) { 1814#if PLATFORM(IOS) 1815 // FIXME: CachedPage::restore() would dispatch viewport change notification. However UIKit expects load 1816 // commit to happen before any changes to viewport arguments and dealing with this there is difficult. 1817 m_frame.page()->chrome().setDispatchViewportDataDidChangeSuppressed(true); 1818#endif 1819 prepareForCachedPageRestore(); 1820 1821 // FIXME: This API should be turned around so that we ground CachedPage into the Page. 1822 cachedPage->restore(*m_frame.page()); 1823 1824 dispatchDidCommitLoad(); 1825#if PLATFORM(IOS) 1826 m_frame.page()->chrome().setDispatchViewportDataDidChangeSuppressed(false); 1827 m_frame.page()->chrome().dispatchViewportPropertiesDidChange(m_frame.page()->viewportArguments()); 1828#endif 1829 // If we have a title let the WebView know about it. 1830 StringWithDirection title = m_documentLoader->title(); 1831 if (!title.isNull()) 1832 m_client.dispatchDidReceiveTitle(title); 1833 1834 checkCompleted(); 1835 } else 1836 didOpenURL(); 1837 1838 LOG(Loading, "WebCoreLoading %s: Finished committing provisional load to URL %s", m_frame.tree().uniqueName().string().utf8().data(), 1839 m_frame.document() ? m_frame.document()->url().stringCenterEllipsizedToLength().utf8().data() : ""); 1840 1841 if (m_loadType == FrameLoadType::Standard && m_documentLoader->isClientRedirect()) 1842 history().updateForClientRedirect(); 1843 1844 if (m_loadingFromCachedPage) { 1845#if PLATFORM(IOS) 1846 // Note, didReceiveDocType is expected to be called for cached pages. See <rdar://problem/5906758> for more details. 1847 if (m_frame.document()->doctype() && m_frame.page()) 1848 m_frame.page()->chrome().didReceiveDocType(&m_frame); 1849#endif 1850 m_frame.document()->documentDidResumeFromPageCache(); 1851 1852 // Force a layout to update view size and thereby update scrollbars. 1853#if PLATFORM(IOS) 1854 if (!m_client.forceLayoutOnRestoreFromPageCache()) 1855 m_frame.view()->forceLayout(); 1856#else 1857 m_frame.view()->forceLayout(); 1858#endif 1859 1860 const ResponseVector& responses = m_documentLoader->responses(); 1861 size_t count = responses.size(); 1862 for (size_t i = 0; i < count; i++) { 1863 const ResourceResponse& response = responses[i]; 1864 // FIXME: If the WebKit client changes or cancels the request, this is not respected. 1865 ResourceError error; 1866 unsigned long identifier; 1867 ResourceRequest request(response.url()); 1868 requestFromDelegate(request, identifier, error); 1869 // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing. 1870 // However, with today's computers and networking speeds, this won't happen in practice. 1871 // Could be an issue with a giant local file. 1872 notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, request, response, 0, static_cast<int>(response.expectedContentLength()), 0, error); 1873 } 1874 1875 // FIXME: Why only this frame and not parent frames? 1876 checkLoadCompleteForThisFrame(); 1877 } 1878} 1879 1880void FrameLoader::transitionToCommitted(CachedPage* cachedPage) 1881{ 1882 ASSERT(m_client.hasWebView()); 1883 ASSERT(m_state == FrameStateProvisional); 1884 1885 if (m_state != FrameStateProvisional) 1886 return; 1887 1888 if (FrameView* view = m_frame.view()) { 1889 if (ScrollAnimator* scrollAnimator = view->existingScrollAnimator()) 1890 scrollAnimator->cancelAnimations(); 1891 } 1892 1893 m_client.setCopiesOnScroll(); 1894 history().updateForCommit(); 1895 1896 // The call to closeURL() invokes the unload event handler, which can execute arbitrary 1897 // JavaScript. If the script initiates a new load, we need to abandon the current load, 1898 // or the two will stomp each other. 1899 DocumentLoader* pdl = m_provisionalDocumentLoader.get(); 1900 if (m_documentLoader) 1901 closeURL(); 1902 if (pdl != m_provisionalDocumentLoader) 1903 return; 1904 1905 // Nothing else can interupt this commit - set the Provisional->Committed transition in stone 1906 if (m_documentLoader) 1907 m_documentLoader->stopLoadingSubresources(); 1908 if (m_documentLoader) 1909 m_documentLoader->stopLoadingPlugIns(); 1910 1911 setDocumentLoader(m_provisionalDocumentLoader.get()); 1912 setProvisionalDocumentLoader(0); 1913 1914 if (pdl != m_documentLoader) { 1915 ASSERT(m_state == FrameStateComplete); 1916 return; 1917 } 1918 1919 setState(FrameStateCommittedPage); 1920 1921#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) 1922 if (m_frame.isMainFrame()) 1923 m_frame.page()->chrome().client().needTouchEvents(false); 1924#endif 1925 1926 // Handle adding the URL to the back/forward list. 1927 DocumentLoader* dl = m_documentLoader.get(); 1928 1929 switch (m_loadType) { 1930 case FrameLoadType::Forward: 1931 case FrameLoadType::Back: 1932 case FrameLoadType::IndexedBackForward: 1933 if (m_frame.page()) { 1934 // If the first load within a frame is a navigation within a back/forward list that was attached 1935 // without any of the items being loaded then we need to update the history in a similar manner as 1936 // for a standard load with the exception of updating the back/forward list (<rdar://problem/8091103>). 1937 if (!m_stateMachine.committedFirstRealDocumentLoad() && m_frame.isMainFrame()) 1938 history().updateForStandardLoad(HistoryController::UpdateAllExceptBackForwardList); 1939 1940 history().updateForBackForwardNavigation(); 1941 1942 // For cached pages, CachedFrame::restore will take care of firing the popstate event with the history item's state object 1943 if (history().currentItem() && !cachedPage) 1944 m_pendingStateObject = history().currentItem()->stateObject(); 1945 1946 // Create a document view for this document, or used the cached view. 1947 if (cachedPage) { 1948 DocumentLoader* cachedDocumentLoader = cachedPage->documentLoader(); 1949 ASSERT(cachedDocumentLoader); 1950 cachedDocumentLoader->setFrame(&m_frame); 1951 m_client.transitionToCommittedFromCachedFrame(cachedPage->cachedMainFrame()); 1952 } else 1953 m_client.transitionToCommittedForNewPage(); 1954 } 1955 break; 1956 1957 case FrameLoadType::Reload: 1958 case FrameLoadType::ReloadFromOrigin: 1959 case FrameLoadType::Same: 1960 case FrameLoadType::Replace: 1961 history().updateForReload(); 1962 m_client.transitionToCommittedForNewPage(); 1963 break; 1964 1965 case FrameLoadType::Standard: 1966 history().updateForStandardLoad(); 1967 if (m_frame.view()) 1968 m_frame.view()->setScrollbarsSuppressed(true); 1969 m_client.transitionToCommittedForNewPage(); 1970 break; 1971 1972 case FrameLoadType::RedirectWithLockedBackForwardList: 1973 history().updateForRedirectWithLockedBackForwardList(); 1974 m_client.transitionToCommittedForNewPage(); 1975 break; 1976 1977 // FIXME Remove this check when dummy ds is removed (whatever "dummy ds" is). 1978 // An exception should be thrown if we're in the FrameLoadTypeUninitialized state. 1979 default: 1980 ASSERT_NOT_REACHED(); 1981 } 1982 1983 m_documentLoader->writer().setMIMEType(dl->responseMIMEType()); 1984 1985 // Tell the client we've committed this URL. 1986 ASSERT(m_frame.view()); 1987 1988 if (m_stateMachine.creatingInitialEmptyDocument()) 1989 return; 1990 1991 if (!m_stateMachine.committedFirstRealDocumentLoad()) 1992 m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocumentPostCommit); 1993} 1994 1995void FrameLoader::clientRedirectCancelledOrFinished(bool cancelWithLoadInProgress) 1996{ 1997 // Note that -webView:didCancelClientRedirectForFrame: is called on the frame load delegate even if 1998 // the redirect succeeded. We should either rename this API, or add a new method, like 1999 // -webView:didFinishClientRedirectForFrame: 2000 m_client.dispatchDidCancelClientRedirect(); 2001 2002 if (!cancelWithLoadInProgress) 2003 m_quickRedirectComing = false; 2004 2005 m_sentRedirectNotification = false; 2006} 2007 2008void FrameLoader::clientRedirected(const URL& url, double seconds, double fireDate, LockBackForwardList lockBackForwardList) 2009{ 2010 m_client.dispatchWillPerformClientRedirect(url, seconds, fireDate); 2011 2012 // Remember that we sent a redirect notification to the frame load delegate so that when we commit 2013 // the next provisional load, we can send a corresponding -webView:didCancelClientRedirectForFrame: 2014 m_sentRedirectNotification = true; 2015 2016 // If a "quick" redirect comes in, we set a special mode so we treat the next 2017 // load as part of the original navigation. If we don't have a document loader, we have 2018 // no "original" load on which to base a redirect, so we treat the redirect as a normal load. 2019 // Loads triggered by JavaScript form submissions never count as quick redirects. 2020 m_quickRedirectComing = (lockBackForwardList == LockBackForwardList::Yes || history().currentItemShouldBeReplaced()) && m_documentLoader && !m_isExecutingJavaScriptFormAction; 2021} 2022 2023bool FrameLoader::shouldReload(const URL& currentURL, const URL& destinationURL) 2024{ 2025 // This function implements the rule: "Don't reload if navigating by fragment within 2026 // the same URL, but do reload if going to a new URL or to the same URL with no 2027 // fragment identifier at all." 2028 if (!destinationURL.hasFragmentIdentifier()) 2029 return true; 2030 return !equalIgnoringFragmentIdentifier(currentURL, destinationURL); 2031} 2032 2033void FrameLoader::closeOldDataSources() 2034{ 2035 // FIXME: Is it important for this traversal to be postorder instead of preorder? 2036 // If so, add helpers for postorder traversal, and use them. If not, then lets not 2037 // use a recursive algorithm here. 2038 for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) 2039 child->loader().closeOldDataSources(); 2040 2041 if (m_documentLoader) 2042 m_client.dispatchWillClose(); 2043 2044 m_client.setMainFrameDocumentReady(false); // stop giving out the actual DOMDocument to observers 2045} 2046 2047void FrameLoader::prepareForCachedPageRestore() 2048{ 2049 ASSERT(!m_frame.tree().parent()); 2050 ASSERT(m_frame.page()); 2051 ASSERT(m_frame.isMainFrame()); 2052 2053 m_frame.navigationScheduler().cancel(); 2054 2055 // We still have to close the previous part page. 2056 closeURL(); 2057 2058 // Delete old status bar messages (if it _was_ activated on last URL). 2059 if (m_frame.script().canExecuteScripts(NotAboutToExecuteScript)) { 2060 DOMWindow* window = m_frame.document()->domWindow(); 2061 window->setStatus(String()); 2062 window->setDefaultStatus(String()); 2063 } 2064} 2065 2066void FrameLoader::open(CachedFrameBase& cachedFrame) 2067{ 2068 m_isComplete = false; 2069 2070 // Don't re-emit the load event. 2071 m_didCallImplicitClose = true; 2072 2073 URL url = cachedFrame.url(); 2074 2075 // FIXME: I suspect this block of code doesn't do anything. 2076 if (url.protocolIsInHTTPFamily() && !url.host().isEmpty() && url.path().isEmpty()) 2077 url.setPath("/"); 2078 2079 started(); 2080 Document* document = cachedFrame.document(); 2081 ASSERT(document); 2082 ASSERT(document->domWindow()); 2083 2084 clear(document, true, true, cachedFrame.isMainFrame()); 2085 2086 document->setInPageCache(false); 2087 2088 m_needsClear = true; 2089 m_isComplete = false; 2090 m_didCallImplicitClose = false; 2091 m_outgoingReferrer = url.string(); 2092 2093 FrameView* view = cachedFrame.view(); 2094 2095 // When navigating to a CachedFrame its FrameView should never be null. If it is we'll crash in creative ways downstream. 2096 ASSERT(view); 2097 view->setWasScrolledByUser(false); 2098 2099 // Use the current ScrollView's frame rect. 2100 if (m_frame.view()) 2101 view->setFrameRect(m_frame.view()->frameRect()); 2102 m_frame.setView(view); 2103 2104 m_frame.setDocument(document); 2105 document->domWindow()->resumeFromPageCache(); 2106 2107 updateFirstPartyForCookies(); 2108 2109 cachedFrame.restore(); 2110} 2111 2112bool FrameLoader::isHostedByObjectElement() const 2113{ 2114 HTMLFrameOwnerElement* owner = m_frame.ownerElement(); 2115 return owner && owner->hasTagName(objectTag); 2116} 2117 2118bool FrameLoader::isReplacing() const 2119{ 2120 return m_loadType == FrameLoadType::Replace; 2121} 2122 2123void FrameLoader::setReplacing() 2124{ 2125 m_loadType = FrameLoadType::Replace; 2126} 2127 2128bool FrameLoader::subframeIsLoading() const 2129{ 2130 // It's most likely that the last added frame is the last to load so we walk backwards. 2131 for (Frame* child = m_frame.tree().lastChild(); child; child = child->tree().previousSibling()) { 2132 FrameLoader& childLoader = child->loader(); 2133 DocumentLoader* documentLoader = childLoader.documentLoader(); 2134 if (documentLoader && documentLoader->isLoadingInAPISense()) 2135 return true; 2136 documentLoader = childLoader.provisionalDocumentLoader(); 2137 if (documentLoader && documentLoader->isLoadingInAPISense()) 2138 return true; 2139 documentLoader = childLoader.policyDocumentLoader(); 2140 if (documentLoader) 2141 return true; 2142 } 2143 return false; 2144} 2145 2146void FrameLoader::willChangeTitle(DocumentLoader* loader) 2147{ 2148 m_client.willChangeTitle(loader); 2149} 2150 2151FrameLoadType FrameLoader::loadType() const 2152{ 2153 return m_loadType; 2154} 2155 2156CachePolicy FrameLoader::subresourceCachePolicy() const 2157{ 2158 if (m_isComplete) 2159 return CachePolicyVerify; 2160 2161 if (m_loadType == FrameLoadType::ReloadFromOrigin) 2162 return CachePolicyReload; 2163 2164 if (Frame* parentFrame = m_frame.tree().parent()) { 2165 CachePolicy parentCachePolicy = parentFrame->loader().subresourceCachePolicy(); 2166 if (parentCachePolicy != CachePolicyVerify) 2167 return parentCachePolicy; 2168 } 2169 2170 if (m_loadType == FrameLoadType::Reload) 2171 return CachePolicyRevalidate; 2172 2173 const ResourceRequest& request(documentLoader()->request()); 2174#if PLATFORM(COCOA) 2175 if (request.cachePolicy() == ReloadIgnoringCacheData && !equalIgnoringCase(request.httpMethod(), "post") && ResourceRequest::useQuickLookResourceCachingQuirks()) 2176 return CachePolicyRevalidate; 2177#endif 2178 2179 if (request.cachePolicy() == ReturnCacheDataElseLoad) 2180 return CachePolicyHistoryBuffer; 2181 2182 return CachePolicyVerify; 2183} 2184 2185void FrameLoader::checkLoadCompleteForThisFrame() 2186{ 2187 ASSERT(m_client.hasWebView()); 2188 2189 switch (m_state) { 2190 case FrameStateProvisional: { 2191 if (m_delegateIsHandlingProvisionalLoadError) 2192 return; 2193 2194 RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader; 2195 if (!pdl) 2196 return; 2197 2198 // If we've received any errors we may be stuck in the provisional state and actually complete. 2199 const ResourceError& error = pdl->mainDocumentError(); 2200 if (error.isNull()) 2201 return; 2202 2203 // Check all children first. 2204 RefPtr<HistoryItem> item; 2205 if (Page* page = m_frame.page()) 2206 if (isBackForwardLoadType(loadType())) 2207 // Reset the back forward list to the last committed history item at the top level. 2208 item = page->mainFrame().loader().history().currentItem(); 2209 2210 // Only reset if we aren't already going to a new provisional item. 2211 bool shouldReset = !history().provisionalItem(); 2212 if (!pdl->isLoadingInAPISense() || pdl->isStopping()) { 2213 m_delegateIsHandlingProvisionalLoadError = true; 2214 m_client.dispatchDidFailProvisionalLoad(error); 2215 m_delegateIsHandlingProvisionalLoadError = false; 2216 2217 ASSERT(!pdl->isLoading()); 2218 2219 // If we're in the middle of loading multipart data, we need to restore the document loader. 2220 if (isReplacing() && !m_documentLoader.get()) 2221 setDocumentLoader(m_provisionalDocumentLoader.get()); 2222 2223 // Finish resetting the load state, but only if another load hasn't been started by the 2224 // delegate callback. 2225 if (pdl == m_provisionalDocumentLoader) 2226 clearProvisionalLoad(); 2227 else if (activeDocumentLoader()) { 2228 URL unreachableURL = activeDocumentLoader()->unreachableURL(); 2229 if (!unreachableURL.isEmpty() && unreachableURL == pdl->request().url()) 2230 shouldReset = false; 2231 } 2232 } 2233 if (shouldReset && item) 2234 if (Page* page = m_frame.page()) { 2235 page->backForward().setCurrentItem(item.get()); 2236 m_frame.loader().client().updateGlobalHistoryItemForPage(); 2237 } 2238 return; 2239 } 2240 2241 case FrameStateCommittedPage: { 2242 DocumentLoader* dl = m_documentLoader.get(); 2243 if (!dl || (dl->isLoadingInAPISense() && !dl->isStopping())) 2244 return; 2245 2246 setState(FrameStateComplete); 2247 2248 // FIXME: Is this subsequent work important if we already navigated away? 2249 // Maybe there are bugs because of that, or extra work we can skip because 2250 // the new page is ready. 2251 2252 m_client.forceLayoutForNonHTML(); 2253 2254 // If the user had a scroll point, scroll to it, overriding the anchor point if any. 2255 if (m_frame.page()) { 2256 if (isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadType::Reload || m_loadType == FrameLoadType::ReloadFromOrigin) 2257 history().restoreScrollPositionAndViewState(); 2258 } 2259 2260 if (m_stateMachine.creatingInitialEmptyDocument() || !m_stateMachine.committedFirstRealDocumentLoad()) 2261 return; 2262 2263 m_progressTracker->progressCompleted(); 2264 if (Page* page = m_frame.page()) { 2265 if (m_frame.isMainFrame()) 2266 page->resetRelevantPaintedObjectCounter(); 2267 } 2268 2269 const ResourceError& error = dl->mainDocumentError(); 2270 2271 AXObjectCache::AXLoadingEvent loadingEvent; 2272 if (!error.isNull()) { 2273 m_client.dispatchDidFailLoad(error); 2274 loadingEvent = AXObjectCache::AXLoadingFailed; 2275 } else { 2276 m_client.dispatchDidFinishLoad(); 2277 loadingEvent = AXObjectCache::AXLoadingFinished; 2278 } 2279 2280 // Notify accessibility. 2281 if (AXObjectCache* cache = m_frame.document()->existingAXObjectCache()) 2282 cache->frameLoadingEventNotification(&m_frame, loadingEvent); 2283 2284 return; 2285 } 2286 2287 case FrameStateComplete: 2288 m_loadType = FrameLoadType::Standard; 2289 frameLoadCompleted(); 2290 return; 2291 } 2292 2293 ASSERT_NOT_REACHED(); 2294} 2295 2296void FrameLoader::continueLoadAfterWillSubmitForm() 2297{ 2298 if (!m_provisionalDocumentLoader) 2299 return; 2300 2301 prepareForLoadStart(); 2302 2303 // The load might be cancelled inside of prepareForLoadStart(), nulling out the m_provisionalDocumentLoader, 2304 // so we need to null check it again. 2305 if (!m_provisionalDocumentLoader) 2306 return; 2307 2308 DocumentLoader* activeDocLoader = activeDocumentLoader(); 2309 if (activeDocLoader && activeDocLoader->isLoadingMainResource()) 2310 return; 2311 2312 m_loadingFromCachedPage = false; 2313 m_provisionalDocumentLoader->startLoadingMainResource(); 2314} 2315 2316static URL originatingURLFromBackForwardList(Page* page) 2317{ 2318 // FIXME: Can this logic be replaced with m_frame.document()->firstPartyForCookies()? 2319 // It has the same meaning of "page a user thinks is the current one". 2320 2321 URL originalURL; 2322 int backCount = page->backForward().backCount(); 2323 for (int backIndex = 0; backIndex <= backCount; backIndex++) { 2324 // FIXME: At one point we had code here to check a "was user gesture" flag. 2325 // Do we need to restore that logic? 2326 HistoryItem* historyItem = page->backForward().itemAtIndex(-backIndex); 2327 if (!historyItem) 2328 continue; 2329 2330 originalURL = historyItem->originalURL(); 2331 if (!originalURL.isNull()) 2332 return originalURL; 2333 } 2334 2335 return URL(); 2336} 2337 2338void FrameLoader::setOriginalURLForDownloadRequest(ResourceRequest& request) 2339{ 2340 URL originalURL; 2341 2342 // If there is no referrer, assume that the download was initiated directly, so current document is 2343 // completely unrelated to it. See <rdar://problem/5294691>. 2344 // FIXME: Referrer is not sent in many other cases, so we will often miss this important information. 2345 // Find a better way to decide whether the download was unrelated to current document. 2346 if (!request.httpReferrer().isNull()) { 2347 // find the first item in the history that was originated by the user 2348 originalURL = originatingURLFromBackForwardList(m_frame.page()); 2349 } 2350 2351 if (originalURL.isNull()) 2352 originalURL = request.url(); 2353 2354 if (!originalURL.protocol().isEmpty() && !originalURL.host().isEmpty()) { 2355 unsigned port = originalURL.port(); 2356 2357 // Original URL is needed to show the user where a file was downloaded from. We should make a URL that won't result in downloading the file again. 2358 // FIXME: Using host-only URL is a very heavy-handed approach. We should attempt to provide the actual page where the download was initiated from, as a reminder to the user. 2359 String hostOnlyURLString; 2360 if (port) 2361 hostOnlyURLString = makeString(originalURL.protocol(), "://", originalURL.host(), ":", String::number(port)); 2362 else 2363 hostOnlyURLString = makeString(originalURL.protocol(), "://", originalURL.host()); 2364 2365 // FIXME: Rename firstPartyForCookies back to mainDocumentURL. It was a mistake to think that it was only used for cookies. 2366 request.setFirstPartyForCookies(URL(URL(), hostOnlyURLString)); 2367 } 2368} 2369 2370void FrameLoader::didLayout(LayoutMilestones milestones) 2371{ 2372 ASSERT(m_frame.isMainFrame()); 2373 2374 m_client.dispatchDidLayout(milestones); 2375} 2376 2377void FrameLoader::didFirstLayout() 2378{ 2379#if PLATFORM(IOS) 2380 // Only send layout-related delegate callbacks synchronously for the main frame to 2381 // avoid reentering layout for the main frame while delivering a layout-related delegate 2382 // callback for a subframe. 2383 if (&m_frame != &m_frame.page()->mainFrame()) 2384 return; 2385#endif 2386 if (m_frame.page() && isBackForwardLoadType(m_loadType)) 2387 history().restoreScrollPositionAndViewState(); 2388 2389 if (m_stateMachine.committedFirstRealDocumentLoad() && !m_stateMachine.isDisplayingInitialEmptyDocument() && !m_stateMachine.firstLayoutDone()) 2390 m_stateMachine.advanceTo(FrameLoaderStateMachine::FirstLayoutDone); 2391} 2392 2393void FrameLoader::frameLoadCompleted() 2394{ 2395 // Note: Can be called multiple times. 2396 2397 m_client.frameLoadCompleted(); 2398 2399 history().updateForFrameLoadCompleted(); 2400 2401 // After a canceled provisional load, firstLayoutDone is false. 2402 // Reset it to true if we're displaying a page. 2403 if (m_documentLoader && m_stateMachine.committedFirstRealDocumentLoad() && !m_stateMachine.isDisplayingInitialEmptyDocument() && !m_stateMachine.firstLayoutDone()) 2404 m_stateMachine.advanceTo(FrameLoaderStateMachine::FirstLayoutDone); 2405} 2406 2407void FrameLoader::detachChildren() 2408{ 2409 Vector<Ref<Frame>, 16> childrenToDetach; 2410 childrenToDetach.reserveInitialCapacity(m_frame.tree().childCount()); 2411 for (Frame* child = m_frame.tree().lastChild(); child; child = child->tree().previousSibling()) 2412 childrenToDetach.uncheckedAppend(*child); 2413 for (unsigned i = 0; i < childrenToDetach.size(); ++i) 2414 childrenToDetach[i]->loader().detachFromParent(); 2415} 2416 2417void FrameLoader::closeAndRemoveChild(Frame* child) 2418{ 2419 child->tree().detachFromParent(); 2420 2421 child->setView(0); 2422 if (child->ownerElement() && child->page()) 2423 child->page()->decrementSubframeCount(); 2424 child->willDetachPage(); 2425 child->detachFromPage(); 2426 2427 m_frame.tree().removeChild(child); 2428} 2429 2430// Called every time a resource is completely loaded or an error is received. 2431void FrameLoader::checkLoadComplete() 2432{ 2433 ASSERT(m_client.hasWebView()); 2434 2435 m_shouldCallCheckLoadComplete = false; 2436 2437 if (!m_frame.page()) 2438 return; 2439 2440 // FIXME: Always traversing the entire frame tree is a bit inefficient, but 2441 // is currently needed in order to null out the previous history item for all frames. 2442 Vector<Ref<Frame>, 16> frames; 2443 for (Frame* frame = &m_frame.mainFrame(); frame; frame = frame->tree().traverseNext()) 2444 frames.append(*frame); 2445 2446 // To process children before their parents, iterate the vector backwards. 2447 for (unsigned i = frames.size(); i; --i) 2448 frames[i - 1]->loader().checkLoadCompleteForThisFrame(); 2449} 2450 2451int FrameLoader::numPendingOrLoadingRequests(bool recurse) const 2452{ 2453 if (!recurse) 2454 return m_frame.document()->cachedResourceLoader()->requestCount(); 2455 2456 int count = 0; 2457 for (Frame* frame = &m_frame; frame; frame = frame->tree().traverseNext(&m_frame)) 2458 count += frame->document()->cachedResourceLoader()->requestCount(); 2459 return count; 2460} 2461 2462String FrameLoader::userAgent(const URL& url) const 2463{ 2464 return m_client.userAgent(url); 2465} 2466 2467void FrameLoader::handledOnloadEvents() 2468{ 2469 m_client.dispatchDidHandleOnloadEvents(); 2470 2471 if (documentLoader()) 2472 documentLoader()->handledOnloadEvents(); 2473} 2474 2475void FrameLoader::frameDetached() 2476{ 2477 stopAllLoaders(); 2478 m_frame.document()->stopActiveDOMObjects(); 2479 detachFromParent(); 2480} 2481 2482void FrameLoader::detachFromParent() 2483{ 2484 Ref<Frame> protect(m_frame); 2485 2486 closeURL(); 2487 history().saveScrollPositionAndViewStateToItem(history().currentItem()); 2488 detachChildren(); 2489 // stopAllLoaders() needs to be called after detachChildren(), because detachedChildren() 2490 // will trigger the unload event handlers of any child frames, and those event 2491 // handlers might start a new subresource load in this frame. 2492 stopAllLoaders(); 2493 2494 InspectorInstrumentation::frameDetachedFromParent(&m_frame); 2495 2496 detachViewsAndDocumentLoader(); 2497 2498 m_progressTracker = nullptr; 2499 2500 if (Frame* parent = m_frame.tree().parent()) { 2501 parent->loader().closeAndRemoveChild(&m_frame); 2502 parent->loader().scheduleCheckCompleted(); 2503 } else { 2504 m_frame.setView(0); 2505 m_frame.willDetachPage(); 2506 m_frame.detachFromPage(); 2507 } 2508} 2509 2510void FrameLoader::detachViewsAndDocumentLoader() 2511{ 2512 m_client.detachedFromParent2(); 2513 setDocumentLoader(0); 2514 m_client.detachedFromParent3(); 2515} 2516 2517void FrameLoader::addExtraFieldsToSubresourceRequest(ResourceRequest& request) 2518{ 2519 addExtraFieldsToRequest(request, m_loadType, false); 2520} 2521 2522void FrameLoader::addExtraFieldsToMainResourceRequest(ResourceRequest& request) 2523{ 2524 // FIXME: Using m_loadType seems wrong for some callers. 2525 // If we are only preparing to load the main resource, that is previous load's load type! 2526 addExtraFieldsToRequest(request, m_loadType, true); 2527} 2528 2529void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadType loadType, bool mainResource) 2530{ 2531 // Don't set the cookie policy URL if it's already been set. 2532 // But make sure to set it on all requests regardless of protocol, as it has significance beyond the cookie policy (<rdar://problem/6616664>). 2533 if (request.firstPartyForCookies().isEmpty()) { 2534 if (mainResource && m_frame.isMainFrame()) 2535 request.setFirstPartyForCookies(request.url()); 2536 else if (Document* document = m_frame.document()) 2537 request.setFirstPartyForCookies(document->firstPartyForCookies()); 2538 } 2539 2540 // The remaining modifications are only necessary for HTTP and HTTPS. 2541 if (!request.url().isEmpty() && !request.url().protocolIsInHTTPFamily()) 2542 return; 2543 2544 applyUserAgent(request); 2545 2546 if (!mainResource) { 2547 if (request.isConditional()) 2548 request.setCachePolicy(ReloadIgnoringCacheData); 2549 else if (documentLoader()->isLoadingInAPISense()) { 2550 // If we inherit cache policy from a main resource, we use the DocumentLoader's 2551 // original request cache policy for two reasons: 2552 // 1. For POST requests, we mutate the cache policy for the main resource, 2553 // but we do not want this to apply to subresources 2554 // 2. Delegates that modify the cache policy using willSendRequest: should 2555 // not affect any other resources. Such changes need to be done 2556 // per request. 2557 ResourceRequestCachePolicy mainDocumentOriginalCachePolicy = documentLoader()->originalRequest().cachePolicy(); 2558 // Back-forward navigations try to load main resource from cache only to avoid re-submitting form data, and start over (with a warning dialog) if that fails. 2559 // This policy is set on initial request too, but should not be inherited. 2560 ResourceRequestCachePolicy subresourceCachePolicy = (mainDocumentOriginalCachePolicy == ReturnCacheDataDontLoad) ? ReturnCacheDataElseLoad : mainDocumentOriginalCachePolicy; 2561 request.setCachePolicy(subresourceCachePolicy); 2562 } else 2563 request.setCachePolicy(UseProtocolCachePolicy); 2564 2565 // FIXME: Other FrameLoader functions have duplicated code for setting cache policy of main request when reloading. 2566 // It seems better to manage it explicitly than to hide the logic inside addExtraFieldsToRequest(). 2567 } else if (loadType == FrameLoadType::Reload || loadType == FrameLoadType::ReloadFromOrigin || request.isConditional()) 2568 request.setCachePolicy(ReloadIgnoringCacheData); 2569 2570 if (request.cachePolicy() == ReloadIgnoringCacheData) { 2571 if (loadType == FrameLoadType::Reload) 2572 request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "max-age=0"); 2573 else if (loadType == FrameLoadType::ReloadFromOrigin) { 2574 request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "no-cache"); 2575 request.setHTTPHeaderField(HTTPHeaderName::Pragma, "no-cache"); 2576 } 2577 } 2578 2579 if (mainResource) 2580 request.setHTTPAccept(defaultAcceptHeader); 2581 2582 // Make sure we send the Origin header. 2583 addHTTPOriginIfNeeded(request, String()); 2584 2585 // Only set fallback array if it's still empty (later attempts may be incorrect, see bug 117818). 2586 if (request.responseContentDispositionEncodingFallbackArray().isEmpty()) { 2587 // Always try UTF-8. If that fails, try frame encoding (if any) and then the default. 2588 request.setResponseContentDispositionEncodingFallbackArray("UTF-8", m_frame.document()->encoding(), m_frame.settings().defaultTextEncodingName()); 2589 } 2590} 2591 2592void FrameLoader::addHTTPOriginIfNeeded(ResourceRequest& request, const String& origin) 2593{ 2594 if (!request.httpOrigin().isEmpty()) 2595 return; // Request already has an Origin header. 2596 2597 // Don't send an Origin header for GET or HEAD to avoid privacy issues. 2598 // For example, if an intranet page has a hyperlink to an external web 2599 // site, we don't want to include the Origin of the request because it 2600 // will leak the internal host name. Similar privacy concerns have lead 2601 // to the widespread suppression of the Referer header at the network 2602 // layer. 2603 if (request.httpMethod() == "GET" || request.httpMethod() == "HEAD") 2604 return; 2605 2606 // For non-GET and non-HEAD methods, always send an Origin header so the 2607 // server knows we support this feature. 2608 2609 if (origin.isEmpty()) { 2610 // If we don't know what origin header to attach, we attach the value 2611 // for an empty origin. 2612 request.setHTTPOrigin(SecurityOrigin::createUnique()->toString()); 2613 return; 2614 } 2615 2616 request.setHTTPOrigin(origin); 2617} 2618 2619void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, LockHistory lockHistory, FrameLoadType loadType, PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState, AllowNavigationToInvalidURL allowNavigationToInvalidURL) 2620{ 2621 RefPtr<FormState> formState = prpFormState; 2622 2623 // Previously when this method was reached, the original FrameLoadRequest had been deconstructed to build a 2624 // bunch of parameters that would come in here and then be built back up to a ResourceRequest. In case 2625 // any caller depends on the immutability of the original ResourceRequest, I'm rebuilding a ResourceRequest 2626 // from scratch as it did all along. 2627 const URL& url = inRequest.url(); 2628 RefPtr<FormData> formData = inRequest.httpBody(); 2629 const String& contentType = inRequest.httpContentType(); 2630 String origin = inRequest.httpOrigin(); 2631 2632 ResourceRequest workingResourceRequest(url); 2633 2634 if (!referrer.isEmpty()) 2635 workingResourceRequest.setHTTPReferrer(referrer); 2636 workingResourceRequest.setHTTPOrigin(origin); 2637 workingResourceRequest.setHTTPMethod("POST"); 2638 workingResourceRequest.setHTTPBody(formData); 2639 workingResourceRequest.setHTTPContentType(contentType); 2640 addExtraFieldsToRequest(workingResourceRequest, loadType, true); 2641 2642 NavigationAction action(workingResourceRequest, loadType, true, event); 2643 2644 if (!frameName.isEmpty()) { 2645 // The search for a target frame is done earlier in the case of form submission. 2646 if (Frame* targetFrame = formState ? 0 : findFrameForNavigation(frameName)) { 2647 targetFrame->loader().loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release(), allowNavigationToInvalidURL); 2648 return; 2649 } 2650 2651 policyChecker().checkNewWindowPolicy(action, workingResourceRequest, formState.release(), frameName, [this, allowNavigationToInvalidURL](const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, const NavigationAction& action, bool shouldContinue) { 2652 continueLoadAfterNewWindowPolicy(request, formState, frameName, action, shouldContinue, allowNavigationToInvalidURL); 2653 }); 2654 return; 2655 } 2656 2657 // must grab this now, since this load may stop the previous load and clear this flag 2658 bool isRedirect = m_quickRedirectComing; 2659 loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release(), allowNavigationToInvalidURL); 2660 if (isRedirect) { 2661 m_quickRedirectComing = false; 2662 if (m_provisionalDocumentLoader) 2663 m_provisionalDocumentLoader->setIsClientRedirect(true); 2664 } 2665} 2666 2667unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ClientCredentialPolicy clientCredentialPolicy, ResourceError& error, ResourceResponse& response, Vector<char>& data) 2668{ 2669 ASSERT(m_frame.document()); 2670 String referrer = SecurityPolicy::generateReferrerHeader(m_frame.document()->referrerPolicy(), request.url(), outgoingReferrer()); 2671 2672 ResourceRequest initialRequest = request; 2673 initialRequest.setTimeoutInterval(10); 2674 2675 if (!referrer.isEmpty()) 2676 initialRequest.setHTTPReferrer(referrer); 2677 addHTTPOriginIfNeeded(initialRequest, outgoingOrigin()); 2678 2679 initialRequest.setFirstPartyForCookies(m_frame.mainFrame().loader().documentLoader()->request().url()); 2680 2681 addExtraFieldsToSubresourceRequest(initialRequest); 2682 2683 unsigned long identifier = 0; 2684 ResourceRequest newRequest(initialRequest); 2685 requestFromDelegate(newRequest, identifier, error); 2686 2687 if (error.isNull()) { 2688 ASSERT(!newRequest.isNull()); 2689 2690 if (!documentLoader()->applicationCacheHost()->maybeLoadSynchronously(newRequest, error, response, data)) { 2691 platformStrategies()->loaderStrategy()->loadResourceSynchronously(networkingContext(), identifier, newRequest, storedCredentials, clientCredentialPolicy, error, response, data); 2692 documentLoader()->applicationCacheHost()->maybeLoadFallbackSynchronously(newRequest, error, response, data); 2693 } 2694 } 2695 notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, request, response, data.data(), data.size(), -1, error); 2696 return identifier; 2697} 2698 2699const ResourceRequest& FrameLoader::originalRequest() const 2700{ 2701 return activeDocumentLoader()->originalRequestCopy(); 2702} 2703 2704void FrameLoader::receivedMainResourceError(const ResourceError& error) 2705{ 2706 // Retain because the stop may release the last reference to it. 2707 Ref<Frame> protect(m_frame); 2708 2709 RefPtr<DocumentLoader> loader = activeDocumentLoader(); 2710 // FIXME: Don't want to do this if an entirely new load is going, so should check 2711 // that both data sources on the frame are either this or nil. 2712 stop(); 2713 if (m_client.shouldFallBack(error)) 2714 handleFallbackContent(); 2715 2716 if (m_state == FrameStateProvisional && m_provisionalDocumentLoader) { 2717 if (m_submittedFormURL == m_provisionalDocumentLoader->originalRequestCopy().url()) 2718 m_submittedFormURL = URL(); 2719 2720 // We might have made a page cache item, but now we're bailing out due to an error before we ever 2721 // transitioned to the new page (before WebFrameState == commit). The goal here is to restore any state 2722 // so that the existing view (that wenever got far enough to replace) can continue being used. 2723 history().invalidateCurrentItemCachedPage(); 2724 2725 // Call clientRedirectCancelledOrFinished here so that the frame load delegate is notified that the redirect's 2726 // status has changed, if there was a redirect. The frame load delegate may have saved some state about 2727 // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:. Since we are definitely 2728 // not going to use this provisional resource, as it was cancelled, notify the frame load delegate that the redirect 2729 // has ended. 2730 if (m_sentRedirectNotification) 2731 clientRedirectCancelledOrFinished(false); 2732 } 2733 2734 checkCompleted(); 2735 if (m_frame.page()) 2736 checkLoadComplete(); 2737} 2738 2739void FrameLoader::continueFragmentScrollAfterNavigationPolicy(const ResourceRequest& request, bool shouldContinue) 2740{ 2741 m_quickRedirectComing = false; 2742 2743 if (!shouldContinue) 2744 return; 2745 2746 // If we have a provisional request for a different document, a fragment scroll should cancel it. 2747 if (m_provisionalDocumentLoader && !equalIgnoringFragmentIdentifier(m_provisionalDocumentLoader->request().url(), request.url())) { 2748 m_provisionalDocumentLoader->stopLoading(); 2749 setProvisionalDocumentLoader(0); 2750 } 2751 2752 bool isRedirect = m_quickRedirectComing || policyChecker().loadType() == FrameLoadType::RedirectWithLockedBackForwardList; 2753 loadInSameDocument(request.url(), 0, !isRedirect); 2754} 2755 2756bool FrameLoader::shouldPerformFragmentNavigation(bool isFormSubmission, const String& httpMethod, FrameLoadType loadType, const URL& url) 2757{ 2758 // We don't do this if we are submitting a form with method other than "GET", explicitly reloading, 2759 // currently displaying a frameset, or if the URL does not have a fragment. 2760 // These rules were originally based on what KHTML was doing in KHTMLPart::openURL. 2761 2762 // FIXME: What about load types other than Standard and Reload? 2763 2764 return (!isFormSubmission || equalIgnoringCase(httpMethod, "GET")) 2765 && loadType != FrameLoadType::Reload 2766 && loadType != FrameLoadType::ReloadFromOrigin 2767 && loadType != FrameLoadType::Same 2768 && !shouldReload(m_frame.document()->url(), url) 2769 // We don't want to just scroll if a link from within a 2770 // frameset is trying to reload the frameset into _top. 2771 && !m_frame.document()->isFrameSet(); 2772} 2773 2774void FrameLoader::scrollToFragmentWithParentBoundary(const URL& url) 2775{ 2776 FrameView* view = m_frame.view(); 2777 if (!view) 2778 return; 2779 2780 // Leaking scroll position to a cross-origin ancestor would permit the so-called "framesniffing" attack. 2781 RefPtr<Frame> boundaryFrame(url.hasFragmentIdentifier() ? m_frame.document()->findUnsafeParentScrollPropagationBoundary() : 0); 2782 2783 if (boundaryFrame) 2784 boundaryFrame->view()->setSafeToPropagateScrollToParent(false); 2785 2786 view->scrollToFragment(url); 2787 2788 if (boundaryFrame) 2789 boundaryFrame->view()->setSafeToPropagateScrollToParent(true); 2790} 2791 2792bool FrameLoader::shouldClose() 2793{ 2794 Page* page = m_frame.page(); 2795 if (!page) 2796 return true; 2797 if (!page->chrome().canRunBeforeUnloadConfirmPanel()) 2798 return true; 2799 2800 // Store all references to each subframe in advance since beforeunload's event handler may modify frame 2801 Vector<Ref<Frame>, 16> targetFrames; 2802 targetFrames.append(m_frame); 2803 for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().traverseNext(&m_frame)) 2804 targetFrames.append(*child); 2805 2806 bool shouldClose = false; 2807 { 2808 NavigationDisablerForBeforeUnload navigationDisabler; 2809 size_t i; 2810 2811 for (i = 0; i < targetFrames.size(); i++) { 2812 if (!targetFrames[i]->tree().isDescendantOf(&m_frame)) 2813 continue; 2814 if (!targetFrames[i]->loader().handleBeforeUnloadEvent(page->chrome(), this)) 2815 break; 2816 } 2817 2818 if (i == targetFrames.size()) 2819 shouldClose = true; 2820 } 2821 2822 if (!shouldClose) 2823 m_submittedFormURL = URL(); 2824 2825 m_currentNavigationHasShownBeforeUnloadConfirmPanel = false; 2826 return shouldClose; 2827} 2828 2829bool FrameLoader::handleBeforeUnloadEvent(Chrome& chrome, FrameLoader* frameLoaderBeingNavigated) 2830{ 2831 DOMWindow* domWindow = m_frame.document()->domWindow(); 2832 if (!domWindow) 2833 return true; 2834 2835 RefPtr<Document> document = m_frame.document(); 2836 if (!document->body()) 2837 return true; 2838 2839 RefPtr<BeforeUnloadEvent> beforeUnloadEvent = BeforeUnloadEvent::create(); 2840 m_pageDismissalEventBeingDispatched = BeforeUnloadDismissal; 2841 2842 // We store the frame's page in a local variable because the frame might get detached inside dispatchEvent. 2843 Page* page = m_frame.page(); 2844 page->incrementFrameHandlingBeforeUnloadEventCount(); 2845 domWindow->dispatchEvent(beforeUnloadEvent.get(), domWindow->document()); 2846 page->decrementFrameHandlingBeforeUnloadEventCount(); 2847 2848 m_pageDismissalEventBeingDispatched = NoDismissal; 2849 2850 if (!beforeUnloadEvent->defaultPrevented()) 2851 document->defaultEventHandler(beforeUnloadEvent.get()); 2852 if (beforeUnloadEvent->returnValue().isNull()) 2853 return true; 2854 2855 // If the navigating FrameLoader has already shown a beforeunload confirmation panel for the current navigation attempt, 2856 // this frame is not allowed to cause another one to be shown. 2857 if (frameLoaderBeingNavigated->m_currentNavigationHasShownBeforeUnloadConfirmPanel) { 2858 document->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Blocked attempt to show multiple beforeunload confirmation dialogs for the same navigation.")); 2859 return true; 2860 } 2861 2862 // We should only display the beforeunload dialog for an iframe if its SecurityOrigin matches all 2863 // ancestor frame SecurityOrigins up through the navigating FrameLoader. 2864 if (frameLoaderBeingNavigated != this) { 2865 Frame* parentFrame = m_frame.tree().parent(); 2866 while (parentFrame) { 2867 Document* parentDocument = parentFrame->document(); 2868 if (!parentDocument) 2869 return true; 2870 if (!m_frame.document() || !m_frame.document()->securityOrigin()->canAccess(parentDocument->securityOrigin())) { 2871 document->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Blocked attempt to show beforeunload confirmation dialog on behalf of a frame with different security origin. Protocols, domains, and ports must match.")); 2872 return true; 2873 } 2874 2875 if (&parentFrame->loader() == frameLoaderBeingNavigated) 2876 break; 2877 2878 parentFrame = parentFrame->tree().parent(); 2879 } 2880 2881 // The navigatingFrameLoader should always be in our ancestory. 2882 ASSERT(parentFrame); 2883 ASSERT(&parentFrame->loader() == frameLoaderBeingNavigated); 2884 } 2885 2886 frameLoaderBeingNavigated->m_currentNavigationHasShownBeforeUnloadConfirmPanel = true; 2887 2888 String text = document->displayStringModifiedByEncoding(beforeUnloadEvent->returnValue()); 2889 return chrome.runBeforeUnloadConfirmPanel(text, &m_frame); 2890} 2891 2892void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue, AllowNavigationToInvalidURL allowNavigationToInvalidURL) 2893{ 2894 // If we loaded an alternate page to replace an unreachableURL, we'll get in here with a 2895 // nil policyDataSource because loading the alternate page will have passed 2896 // through this method already, nested; otherwise, policyDataSource should still be set. 2897 ASSERT(m_policyDocumentLoader || !m_provisionalDocumentLoader->unreachableURL().isEmpty()); 2898 2899 bool isTargetItem = history().provisionalItem() ? history().provisionalItem()->isTargetItem() : false; 2900 2901 bool urlIsDisallowed = allowNavigationToInvalidURL == AllowNavigationToInvalidURL::No && !request.url().isValid(); 2902 2903 // Three reasons we can't continue: 2904 // 1) Navigation policy delegate said we can't so request is nil. A primary case of this 2905 // is the user responding Cancel to the form repost nag sheet. 2906 // 2) User responded Cancel to an alert popped up by the before unload event handler. 2907 // 3) The request's URL is invalid and navigation to invalid URLs is disallowed. 2908 bool canContinue = shouldContinue && shouldClose() && !urlIsDisallowed; 2909 2910 if (!canContinue) { 2911 // If we were waiting for a quick redirect, but the policy delegate decided to ignore it, then we 2912 // need to report that the client redirect was cancelled. 2913 // FIXME: The client should be told about ignored non-quick redirects, too. 2914 if (m_quickRedirectComing) 2915 clientRedirectCancelledOrFinished(false); 2916 2917 setPolicyDocumentLoader(0); 2918 2919 // If the navigation request came from the back/forward menu, and we punt on it, we have the 2920 // problem that we have optimistically moved the b/f cursor already, so move it back. For sanity, 2921 // we only do this when punting a navigation for the target frame or top-level frame. 2922 if ((isTargetItem || m_frame.isMainFrame()) && isBackForwardLoadType(policyChecker().loadType())) { 2923 if (Page* page = m_frame.page()) { 2924 if (HistoryItem* resetItem = m_frame.mainFrame().loader().history().currentItem()) { 2925 page->backForward().setCurrentItem(resetItem); 2926 m_frame.loader().client().updateGlobalHistoryItemForPage(); 2927 } 2928 } 2929 } 2930 return; 2931 } 2932 2933 FrameLoadType type = policyChecker().loadType(); 2934 // A new navigation is in progress, so don't clear the history's provisional item. 2935 stopAllLoaders(ShouldNotClearProvisionalItem); 2936 2937 // <rdar://problem/6250856> - In certain circumstances on pages with multiple frames, stopAllLoaders() 2938 // might detach the current FrameLoader, in which case we should bail on this newly defunct load. 2939 if (!m_frame.page()) 2940 return; 2941 2942#if ENABLE(INSPECTOR) 2943 if (Page* page = m_frame.page()) { 2944 if (m_frame.isMainFrame()) 2945 page->inspectorController().resume(); 2946 } 2947#endif 2948 2949 setProvisionalDocumentLoader(m_policyDocumentLoader.get()); 2950 m_loadType = type; 2951 setState(FrameStateProvisional); 2952 2953 setPolicyDocumentLoader(0); 2954 2955 if (isBackForwardLoadType(type) && history().provisionalItem()->isInPageCache()) { 2956 loadProvisionalItemFromCachedPage(); 2957 return; 2958 } 2959 2960 if (!formState) { 2961 continueLoadAfterWillSubmitForm(); 2962 return; 2963 } 2964 2965 m_client.dispatchWillSubmitForm(formState, [this](PolicyAction action) { 2966 policyChecker().continueLoadAfterWillSubmitForm(action); 2967 }); 2968} 2969 2970void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& request, 2971 PassRefPtr<FormState> formState, const String& frameName, const NavigationAction& action, bool shouldContinue, AllowNavigationToInvalidURL allowNavigationToInvalidURL) 2972{ 2973 if (!shouldContinue) 2974 return; 2975 2976 Ref<Frame> frame(m_frame); 2977 RefPtr<Frame> mainFrame = m_client.dispatchCreatePage(action); 2978 if (!mainFrame) 2979 return; 2980 2981 if (frameName != "_blank") 2982 mainFrame->tree().setName(frameName); 2983 2984 mainFrame->page()->setOpenedByDOM(); 2985 mainFrame->loader().m_client.dispatchShow(); 2986 if (!m_suppressOpenerInNewFrame) { 2987 mainFrame->loader().setOpener(&frame.get()); 2988 mainFrame->document()->setReferrerPolicy(frame->document()->referrerPolicy()); 2989 } 2990 mainFrame->loader().loadWithNavigationAction(request, NavigationAction(request), LockHistory::No, FrameLoadType::Standard, formState, allowNavigationToInvalidURL); 2991} 2992 2993void FrameLoader::requestFromDelegate(ResourceRequest& request, unsigned long& identifier, ResourceError& error) 2994{ 2995 ASSERT(!request.isNull()); 2996 2997 identifier = 0; 2998 if (Page* page = m_frame.page()) { 2999 identifier = page->progress().createUniqueIdentifier(); 3000 notifier().assignIdentifierToInitialRequest(identifier, m_documentLoader.get(), request); 3001 } 3002 3003 ResourceRequest newRequest(request); 3004 notifier().dispatchWillSendRequest(m_documentLoader.get(), identifier, newRequest, ResourceResponse()); 3005 3006 if (newRequest.isNull()) 3007 error = cancelledError(request); 3008 else 3009 error = ResourceError(); 3010 3011 request = newRequest; 3012} 3013 3014void FrameLoader::loadedResourceFromMemoryCache(CachedResource* resource, ResourceRequest& newRequest) 3015{ 3016 Page* page = m_frame.page(); 3017 if (!page) 3018 return; 3019 3020 if (!resource->shouldSendResourceLoadCallbacks() || m_documentLoader->haveToldClientAboutLoad(resource->url())) 3021 return; 3022 3023 // Main resource delegate messages are synthesized in MainResourceLoader, so we must not send them here. 3024 if (resource->type() == CachedResource::MainResource) 3025 return; 3026 3027 if (!page->areMemoryCacheClientCallsEnabled()) { 3028 InspectorInstrumentation::didLoadResourceFromMemoryCache(page, m_documentLoader.get(), resource); 3029 m_documentLoader->recordMemoryCacheLoadForFutureClientNotification(resource->resourceRequest()); 3030 m_documentLoader->didTellClientAboutLoad(resource->url()); 3031 return; 3032 } 3033 3034 if (m_client.dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), newRequest, resource->response(), resource->encodedSize())) { 3035 InspectorInstrumentation::didLoadResourceFromMemoryCache(page, m_documentLoader.get(), resource); 3036 m_documentLoader->didTellClientAboutLoad(resource->url()); 3037 return; 3038 } 3039 3040 unsigned long identifier; 3041 ResourceError error; 3042 requestFromDelegate(newRequest, identifier, error); 3043 InspectorInstrumentation::markResourceAsCached(page, identifier); 3044 notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, newRequest, resource->response(), 0, resource->encodedSize(), 0, error); 3045} 3046 3047void FrameLoader::applyUserAgent(ResourceRequest& request) 3048{ 3049 String userAgent = this->userAgent(request.url()); 3050 ASSERT(!userAgent.isNull()); 3051 request.setHTTPUserAgent(userAgent); 3052} 3053 3054bool FrameLoader::shouldInterruptLoadForXFrameOptions(const String& content, const URL& url, unsigned long requestIdentifier) 3055{ 3056 Frame& topFrame = m_frame.tree().top(); 3057 if (&m_frame == &topFrame) 3058 return false; 3059 3060 XFrameOptionsDisposition disposition = parseXFrameOptionsHeader(content); 3061 3062 switch (disposition) { 3063 case XFrameOptionsSameOrigin: { 3064 RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url); 3065 if (!origin->isSameSchemeHostPort(topFrame.document()->securityOrigin())) 3066 return true; 3067 for (Frame* frame = m_frame.tree().parent(); frame; frame = frame->tree().parent()) { 3068 if (!origin->isSameSchemeHostPort(frame->document()->securityOrigin())) 3069 break; 3070 } 3071 return false; 3072 } 3073 case XFrameOptionsDeny: 3074 return true; 3075 case XFrameOptionsAllowAll: 3076 return false; 3077 case XFrameOptionsConflict: 3078 m_frame.document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Multiple 'X-Frame-Options' headers with conflicting values ('" + content + "') encountered when loading '" + url.stringCenterEllipsizedToLength() + "'. Falling back to 'DENY'.", requestIdentifier); 3079 return true; 3080 case XFrameOptionsInvalid: 3081 m_frame.document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Invalid 'X-Frame-Options' header encountered when loading '" + url.stringCenterEllipsizedToLength() + "': '" + content + "' is not a recognized directive. The header will be ignored.", requestIdentifier); 3082 return false; 3083 case XFrameOptionsNone: 3084 return false; 3085 } 3086 ASSERT_NOT_REACHED(); 3087 return false; 3088} 3089 3090void FrameLoader::loadProvisionalItemFromCachedPage() 3091{ 3092 DocumentLoader* provisionalLoader = provisionalDocumentLoader(); 3093 LOG(PageCache, "WebCorePageCache: Loading provisional DocumentLoader %p with URL '%s' from CachedPage", provisionalDocumentLoader(), provisionalDocumentLoader()->url().stringCenterEllipsizedToLength().utf8().data()); 3094 3095 prepareForLoadStart(); 3096 3097 m_loadingFromCachedPage = true; 3098 3099 // Should have timing data from previous time(s) the page was shown. 3100 ASSERT(provisionalLoader->timing()->navigationStart()); 3101 provisionalLoader->resetTiming(); 3102 provisionalLoader->timing()->markNavigationStart(); 3103 3104 provisionalLoader->setCommitted(true); 3105 commitProvisionalLoad(); 3106} 3107 3108bool FrameLoader::shouldTreatURLAsSameAsCurrent(const URL& url) const 3109{ 3110 if (!history().currentItem()) 3111 return false; 3112 return url == history().currentItem()->url() || url == history().currentItem()->originalURL(); 3113} 3114 3115bool FrameLoader::shouldTreatURLAsSrcdocDocument(const URL& url) const 3116{ 3117 if (!equalIgnoringCase(url.string(), "about:srcdoc")) 3118 return false; 3119 HTMLFrameOwnerElement* ownerElement = m_frame.ownerElement(); 3120 if (!ownerElement) 3121 return false; 3122 if (!ownerElement->hasTagName(iframeTag)) 3123 return false; 3124 return ownerElement->fastHasAttribute(srcdocAttr); 3125} 3126 3127Frame* FrameLoader::findFrameForNavigation(const AtomicString& name, Document* activeDocument) 3128{ 3129 Frame* frame = m_frame.tree().find(name); 3130 3131 // FIXME: Eventually all callers should supply the actual activeDocument so we can call canNavigate with the right document. 3132 if (!activeDocument) 3133 activeDocument = m_frame.document(); 3134 3135 if (!activeDocument->canNavigate(frame)) 3136 return 0; 3137 3138 return frame; 3139} 3140 3141void FrameLoader::loadSameDocumentItem(HistoryItem* item) 3142{ 3143 ASSERT(item->documentSequenceNumber() == history().currentItem()->documentSequenceNumber()); 3144 3145 // Save user view state to the current history item here since we don't do a normal load. 3146 // FIXME: Does form state need to be saved here too? 3147 history().saveScrollPositionAndViewStateToItem(history().currentItem()); 3148 if (FrameView* view = m_frame.view()) 3149 view->setWasScrolledByUser(false); 3150 3151 history().setCurrentItem(item); 3152 3153 // loadInSameDocument() actually changes the URL and notifies load delegates of a "fake" load 3154 loadInSameDocument(item->url(), item->stateObject(), false); 3155 3156 // Restore user view state from the current history item here since we don't do a normal load. 3157 history().restoreScrollPositionAndViewState(); 3158} 3159 3160// FIXME: This function should really be split into a couple pieces, some of 3161// which should be methods of HistoryController and some of which should be 3162// methods of FrameLoader. 3163void FrameLoader::loadDifferentDocumentItem(HistoryItem* item, FrameLoadType loadType, FormSubmissionCacheLoadPolicy cacheLoadPolicy) 3164{ 3165 // Remember this item so we can traverse any child items as child frames load 3166 history().setProvisionalItem(item); 3167 3168 if (CachedPage* cachedPage = pageCache()->get(item)) { 3169 loadWithDocumentLoader(cachedPage->documentLoader(), loadType, 0, AllowNavigationToInvalidURL::Yes); 3170 return; 3171 } 3172 3173 URL itemURL = item->url(); 3174 URL itemOriginalURL = item->originalURL(); 3175 URL currentURL; 3176 if (documentLoader()) 3177 currentURL = documentLoader()->url(); 3178 RefPtr<FormData> formData = item->formData(); 3179 3180 ResourceRequest request(itemURL); 3181 3182 if (!item->referrer().isNull()) 3183 request.setHTTPReferrer(item->referrer()); 3184 3185 // If this was a repost that failed the page cache, we might try to repost the form. 3186 NavigationAction action; 3187 if (formData) { 3188 formData->generateFiles(m_frame.document()); 3189 3190 request.setHTTPMethod("POST"); 3191 request.setHTTPBody(formData); 3192 request.setHTTPContentType(item->formContentType()); 3193 RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::createFromString(item->referrer()); 3194 addHTTPOriginIfNeeded(request, securityOrigin->toString()); 3195 3196 // Make sure to add extra fields to the request after the Origin header is added for the FormData case. 3197 // See https://bugs.webkit.org/show_bug.cgi?id=22194 for more discussion. 3198 addExtraFieldsToRequest(request, loadType, true); 3199 3200 // FIXME: Slight hack to test if the NSURL cache contains the page we're going to. 3201 // We want to know this before talking to the policy delegate, since it affects whether 3202 // we show the DoYouReallyWantToRepost nag. 3203 // 3204 // This trick has a small bug (3123893) where we might find a cache hit, but then 3205 // have the item vanish when we try to use it in the ensuing nav. This should be 3206 // extremely rare, but in that case the user will get an error on the navigation. 3207 3208 if (cacheLoadPolicy == MayAttemptCacheOnlyLoadForFormSubmissionItem) { 3209 request.setCachePolicy(ReturnCacheDataDontLoad); 3210 action = NavigationAction(request, loadType, false); 3211 } else { 3212 request.setCachePolicy(ReturnCacheDataElseLoad); 3213 action = NavigationAction(request, NavigationTypeFormResubmitted); 3214 } 3215 } else { 3216 switch (loadType) { 3217 case FrameLoadType::Reload: 3218 case FrameLoadType::ReloadFromOrigin: 3219 request.setCachePolicy(ReloadIgnoringCacheData); 3220 break; 3221 case FrameLoadType::Back: 3222 case FrameLoadType::Forward: 3223 case FrameLoadType::IndexedBackForward: 3224 // If the first load within a frame is a navigation within a back/forward list that was attached 3225 // without any of the items being loaded then we should use the default caching policy (<rdar://problem/8131355>). 3226 if (m_stateMachine.committedFirstRealDocumentLoad()) 3227 request.setCachePolicy(ReturnCacheDataElseLoad); 3228 break; 3229 case FrameLoadType::Standard: 3230 case FrameLoadType::RedirectWithLockedBackForwardList: 3231 break; 3232 case FrameLoadType::Same: 3233 default: 3234 ASSERT_NOT_REACHED(); 3235 } 3236 3237 addExtraFieldsToRequest(request, loadType, true); 3238 3239 ResourceRequest requestForOriginalURL(request); 3240 requestForOriginalURL.setURL(itemOriginalURL); 3241 action = NavigationAction(requestForOriginalURL, loadType, false); 3242 } 3243 3244 loadWithNavigationAction(request, action, LockHistory::No, loadType, 0, AllowNavigationToInvalidURL::Yes); 3245} 3246 3247// Loads content into this frame, as specified by history item 3248void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) 3249{ 3250 m_requestedHistoryItem = item; 3251 HistoryItem* currentItem = history().currentItem(); 3252 bool sameDocumentNavigation = currentItem && item->shouldDoSameDocumentNavigationTo(currentItem); 3253 3254 if (sameDocumentNavigation) 3255 loadSameDocumentItem(item); 3256 else 3257 loadDifferentDocumentItem(item, loadType, MayAttemptCacheOnlyLoadForFormSubmissionItem); 3258} 3259 3260void FrameLoader::retryAfterFailedCacheOnlyMainResourceLoad() 3261{ 3262 ASSERT(m_state == FrameStateProvisional); 3263 ASSERT(!m_loadingFromCachedPage); 3264 // We only use cache-only loads to avoid resubmitting forms. 3265 ASSERT(isBackForwardLoadType(m_loadType)); 3266 ASSERT(history().provisionalItem()->formData()); 3267 ASSERT(history().provisionalItem() == m_requestedHistoryItem.get()); 3268 3269 FrameLoadType loadType = m_loadType; 3270 HistoryItem* item = history().provisionalItem(); 3271 3272 stopAllLoaders(ShouldNotClearProvisionalItem); 3273 loadDifferentDocumentItem(item, loadType, MayNotAttemptCacheOnlyLoadForFormSubmissionItem); 3274} 3275 3276ResourceError FrameLoader::cancelledError(const ResourceRequest& request) const 3277{ 3278 ResourceError error = m_client.cancelledError(request); 3279 error.setIsCancellation(true); 3280 return error; 3281} 3282 3283#if PLATFORM(IOS) 3284RetainPtr<CFDictionaryRef> FrameLoader::connectionProperties(ResourceLoader* loader) 3285{ 3286 return m_client.connectionProperties(loader->documentLoader(), loader->identifier()); 3287} 3288#endif 3289 3290String FrameLoader::referrer() const 3291{ 3292 return m_documentLoader ? m_documentLoader->request().httpReferrer() : ""; 3293} 3294 3295void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds() 3296{ 3297 if (!m_frame.script().canExecuteScripts(NotAboutToExecuteScript)) 3298 return; 3299 3300 Vector<Ref<DOMWrapperWorld>> worlds; 3301 ScriptController::getAllWorlds(worlds); 3302 for (size_t i = 0; i < worlds.size(); ++i) 3303 dispatchDidClearWindowObjectInWorld(worlds[i].get()); 3304} 3305 3306void FrameLoader::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld& world) 3307{ 3308 if (!m_frame.script().canExecuteScripts(NotAboutToExecuteScript) || !m_frame.script().existingWindowShell(world)) 3309 return; 3310 3311 m_client.dispatchDidClearWindowObjectInWorld(world); 3312 3313#if ENABLE(INSPECTOR) 3314 if (Page* page = m_frame.page()) 3315 page->inspectorController().didClearWindowObjectInWorld(&m_frame, world); 3316#endif 3317 3318 InspectorInstrumentation::didClearWindowObjectInWorld(&m_frame, world); 3319} 3320 3321void FrameLoader::dispatchGlobalObjectAvailableInAllWorlds() 3322{ 3323 Vector<Ref<DOMWrapperWorld>> worlds; 3324 ScriptController::getAllWorlds(worlds); 3325 for (size_t i = 0; i < worlds.size(); ++i) 3326 m_client.dispatchGlobalObjectAvailable(worlds[i].get()); 3327} 3328 3329SandboxFlags FrameLoader::effectiveSandboxFlags() const 3330{ 3331 SandboxFlags flags = m_forcedSandboxFlags; 3332 if (Frame* parentFrame = m_frame.tree().parent()) 3333 flags |= parentFrame->document()->sandboxFlags(); 3334 if (HTMLFrameOwnerElement* ownerElement = m_frame.ownerElement()) 3335 flags |= ownerElement->sandboxFlags(); 3336 return flags; 3337} 3338 3339void FrameLoader::didChangeTitle(DocumentLoader* loader) 3340{ 3341 m_client.didChangeTitle(loader); 3342 3343 if (loader == m_documentLoader) { 3344 // Must update the entries in the back-forward list too. 3345 history().setCurrentItemTitle(loader->title()); 3346 // This must go through the WebFrame because it has the right notion of the current b/f item. 3347 m_client.setTitle(loader->title(), loader->urlForHistory()); 3348 m_client.setMainFrameDocumentReady(true); // update observers with new DOMDocument 3349 m_client.dispatchDidReceiveTitle(loader->title()); 3350 } 3351 3352#if ENABLE(REMOTE_INSPECTOR) 3353 if (m_frame.isMainFrame()) 3354 m_frame.page()->remoteInspectorInformationDidChange(); 3355#endif 3356} 3357 3358void FrameLoader::didChangeIcons(IconType type) 3359{ 3360 m_client.dispatchDidChangeIcons(type); 3361} 3362 3363void FrameLoader::dispatchDidCommitLoad() 3364{ 3365 if (m_stateMachine.creatingInitialEmptyDocument()) 3366 return; 3367 3368 m_client.dispatchDidCommitLoad(); 3369 3370 if (m_frame.isMainFrame()) { 3371 m_frame.page()->resetSeenPlugins(); 3372 m_frame.page()->resetSeenMediaEngines(); 3373 } 3374 3375 InspectorInstrumentation::didCommitLoad(&m_frame, m_documentLoader.get()); 3376 3377#if ENABLE(REMOTE_INSPECTOR) 3378 if (m_frame.isMainFrame()) 3379 m_frame.page()->remoteInspectorInformationDidChange(); 3380#endif 3381} 3382 3383void FrameLoader::tellClientAboutPastMemoryCacheLoads() 3384{ 3385 ASSERT(m_frame.page()); 3386 ASSERT(m_frame.page()->areMemoryCacheClientCallsEnabled()); 3387 3388 if (!m_documentLoader) 3389 return; 3390 3391 Vector<ResourceRequest> pastLoads; 3392 m_documentLoader->takeMemoryCacheLoadsForClientNotification(pastLoads); 3393 3394 size_t size = pastLoads.size(); 3395 for (size_t i = 0; i < size; ++i) { 3396 CachedResource* resource = memoryCache()->resourceForRequest(pastLoads[i], m_frame.page()->sessionID()); 3397 3398 // FIXME: These loads, loaded from cache, but now gone from the cache by the time 3399 // Page::setMemoryCacheClientCallsEnabled(true) is called, will not be seen by the client. 3400 // Consider if there's some efficient way of remembering enough to deliver this client call. 3401 // We have the URL, but not the rest of the response or the length. 3402 if (!resource) 3403 continue; 3404 3405 ResourceRequest request(resource->url()); 3406 m_client.dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), request, resource->response(), resource->encodedSize()); 3407 } 3408} 3409 3410NetworkingContext* FrameLoader::networkingContext() const 3411{ 3412 return m_networkingContext.get(); 3413} 3414 3415void FrameLoader::loadProgressingStatusChanged() 3416{ 3417 if (auto* view = m_frame.mainFrame().view()) 3418 view->loadProgressingStatusChanged(); 3419} 3420 3421void FrameLoader::forcePageTransitionIfNeeded() 3422{ 3423 m_client.forcePageTransitionIfNeeded(); 3424} 3425 3426bool FrameLoaderClient::hasHTMLView() const 3427{ 3428 return true; 3429} 3430 3431PassRefPtr<Frame> createWindow(Frame* openerFrame, Frame* lookupFrame, const FrameLoadRequest& request, const WindowFeatures& features, bool& created) 3432{ 3433 ASSERT(!features.dialog || request.frameName().isEmpty()); 3434 3435 created = false; 3436 3437 if (!request.frameName().isEmpty() && request.frameName() != "_blank") { 3438 if (RefPtr<Frame> frame = lookupFrame->loader().findFrameForNavigation(request.frameName(), openerFrame->document())) { 3439 if (request.frameName() != "_self") { 3440 if (Page* page = frame->page()) 3441 page->chrome().focus(); 3442 } 3443 return frame.release(); 3444 } 3445 } 3446 3447 // Sandboxed frames cannot open new auxiliary browsing contexts. 3448 if (isDocumentSandboxed(openerFrame, SandboxPopups)) { 3449 // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists. 3450 openerFrame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Blocked opening '" + request.resourceRequest().url().stringCenterEllipsizedToLength() + "' in a new window because the request was made in a sandboxed frame whose 'allow-popups' permission is not set."); 3451 return nullptr; 3452 } 3453 3454 // FIXME: Setting the referrer should be the caller's responsibility. 3455 FrameLoadRequest requestWithReferrer = request; 3456 String referrer = SecurityPolicy::generateReferrerHeader(openerFrame->document()->referrerPolicy(), request.resourceRequest().url(), openerFrame->loader().outgoingReferrer()); 3457 if (!referrer.isEmpty()) 3458 requestWithReferrer.resourceRequest().setHTTPReferrer(referrer); 3459 FrameLoader::addHTTPOriginIfNeeded(requestWithReferrer.resourceRequest(), openerFrame->loader().outgoingOrigin()); 3460 3461 Page* oldPage = openerFrame->page(); 3462 if (!oldPage) 3463 return nullptr; 3464 3465 Page* page = oldPage->chrome().createWindow(openerFrame, requestWithReferrer, features, NavigationAction(requestWithReferrer.resourceRequest())); 3466 if (!page) 3467 return nullptr; 3468 3469 RefPtr<Frame> frame = &page->mainFrame(); 3470 3471 frame->loader().forceSandboxFlags(openerFrame->document()->sandboxFlags()); 3472 3473 if (request.frameName() != "_blank") 3474 frame->tree().setName(request.frameName()); 3475 3476 page->chrome().setToolbarsVisible(features.toolBarVisible || features.locationBarVisible); 3477 3478 if (!frame->page()) 3479 return nullptr; 3480 page->chrome().setStatusbarVisible(features.statusBarVisible); 3481 3482 if (!frame->page()) 3483 return nullptr; 3484 page->chrome().setScrollbarsVisible(features.scrollbarsVisible); 3485 3486 if (!frame->page()) 3487 return nullptr; 3488 page->chrome().setMenubarVisible(features.menuBarVisible); 3489 3490 if (!frame->page()) 3491 return nullptr; 3492 page->chrome().setResizable(features.resizable); 3493 3494 // 'x' and 'y' specify the location of the window, while 'width' and 'height' 3495 // specify the size of the viewport. We can only resize the window, so adjust 3496 // for the difference between the window size and the viewport size. 3497 3498 // FIXME: We should reconcile the initialization of viewport arguments between iOS and non-IOS. 3499#if !PLATFORM(IOS) 3500 FloatSize viewportSize = page->chrome().pageRect().size(); 3501 FloatRect windowRect = page->chrome().windowRect(); 3502 if (features.xSet) 3503 windowRect.setX(features.x); 3504 if (features.ySet) 3505 windowRect.setY(features.y); 3506 // Zero width and height mean using default size, not minumum one. 3507 if (features.widthSet && features.width) 3508 windowRect.setWidth(features.width + (windowRect.width() - viewportSize.width())); 3509 if (features.heightSet && features.height) 3510 windowRect.setHeight(features.height + (windowRect.height() - viewportSize.height())); 3511 3512 // Ensure non-NaN values, minimum size as well as being within valid screen area. 3513 FloatRect newWindowRect = DOMWindow::adjustWindowRect(page, windowRect); 3514 3515 if (!frame->page()) 3516 return nullptr; 3517 page->chrome().setWindowRect(newWindowRect); 3518#else 3519 // On iOS, width and height refer to the viewport dimensions. 3520 ViewportArguments arguments; 3521 // Zero width and height mean using default size, not minimum one. 3522 if (features.widthSet && features.width) 3523 arguments.width = features.width; 3524 if (features.heightSet && features.height) 3525 arguments.height = features.height; 3526 frame->setViewportArguments(arguments); 3527#endif 3528 3529 if (!frame->page()) 3530 return nullptr; 3531 page->chrome().show(); 3532 3533 created = true; 3534 return frame.release(); 3535} 3536 3537} // namespace WebCore 3538