• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 Computer, 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 "core/loader/FrameLoader.h"
37 
38 #include "bindings/v8/DOMWrapperWorld.h"
39 #include "bindings/v8/ScriptController.h"
40 #include "bindings/v8/SerializedScriptValue.h"
41 #include "core/HTMLNames.h"
42 #include "core/dom/Document.h"
43 #include "core/dom/Element.h"
44 #include "core/dom/ViewportDescription.h"
45 #include "core/editing/Editor.h"
46 #include "core/editing/UndoStack.h"
47 #include "core/events/PageTransitionEvent.h"
48 #include "core/fetch/FetchContext.h"
49 #include "core/fetch/ResourceFetcher.h"
50 #include "core/fetch/ResourceLoader.h"
51 #include "core/frame/LocalDOMWindow.h"
52 #include "core/frame/FrameHost.h"
53 #include "core/frame/FrameView.h"
54 #include "core/frame/LocalFrame.h"
55 #include "core/frame/PinchViewport.h"
56 #include "core/frame/csp/ContentSecurityPolicy.h"
57 #include "core/html/HTMLFormElement.h"
58 #include "core/html/HTMLFrameOwnerElement.h"
59 #include "core/html/parser/HTMLParserIdioms.h"
60 #include "core/inspector/InspectorController.h"
61 #include "core/inspector/InspectorInstrumentation.h"
62 #include "core/loader/DocumentLoadTiming.h"
63 #include "core/loader/DocumentLoader.h"
64 #include "core/loader/FormState.h"
65 #include "core/loader/FormSubmission.h"
66 #include "core/loader/FrameFetchContext.h"
67 #include "core/loader/FrameLoadRequest.h"
68 #include "core/loader/FrameLoaderClient.h"
69 #include "core/loader/ProgressTracker.h"
70 #include "core/loader/UniqueIdentifier.h"
71 #include "core/loader/appcache/ApplicationCacheHost.h"
72 #include "core/page/BackForwardClient.h"
73 #include "core/page/Chrome.h"
74 #include "core/page/ChromeClient.h"
75 #include "core/page/CreateWindow.h"
76 #include "core/page/EventHandler.h"
77 #include "core/page/FrameTree.h"
78 #include "core/page/Page.h"
79 #include "core/frame/Settings.h"
80 #include "core/page/WindowFeatures.h"
81 #include "core/page/scrolling/ScrollingCoordinator.h"
82 #include "core/xml/parser/XMLDocumentParser.h"
83 #include "platform/Logging.h"
84 #include "platform/UserGestureIndicator.h"
85 #include "platform/geometry/FloatRect.h"
86 #include "platform/network/ContentSecurityPolicyResponseHeaders.h"
87 #include "platform/network/HTTPParsers.h"
88 #include "platform/network/ResourceRequest.h"
89 #include "platform/scroll/ScrollAnimator.h"
90 #include "platform/weborigin/SecurityOrigin.h"
91 #include "platform/weborigin/SecurityPolicy.h"
92 #include "wtf/TemporaryChange.h"
93 #include "wtf/text/CString.h"
94 #include "wtf/text/WTFString.h"
95 
96 namespace WebCore {
97 
98 using namespace HTMLNames;
99 
isBackForwardLoadType(FrameLoadType type)100 bool isBackForwardLoadType(FrameLoadType type)
101 {
102     return type == FrameLoadTypeBackForward;
103 }
104 
needsHistoryItemRestore(FrameLoadType type)105 static bool needsHistoryItemRestore(FrameLoadType type)
106 {
107     return type == FrameLoadTypeBackForward || type == FrameLoadTypeReload || type == FrameLoadTypeReloadFromOrigin;
108 }
109 
FrameLoader(LocalFrame * frame)110 FrameLoader::FrameLoader(LocalFrame* frame)
111     : m_frame(frame)
112     , m_mixedContentChecker(frame)
113     , m_progressTracker(ProgressTracker::create(frame))
114     , m_state(FrameStateProvisional)
115     , m_loadType(FrameLoadTypeStandard)
116     , m_fetchContext(FrameFetchContext::create(frame))
117     , m_inStopAllLoaders(false)
118     , m_isComplete(false)
119     , m_checkTimer(this, &FrameLoader::checkTimerFired)
120     , m_shouldCallCheckCompleted(false)
121     , m_didAccessInitialDocument(false)
122     , m_didAccessInitialDocumentTimer(this, &FrameLoader::didAccessInitialDocumentTimerFired)
123     , m_forcedSandboxFlags(SandboxNone)
124     , m_willDetachClient(false)
125 {
126 }
127 
~FrameLoader()128 FrameLoader::~FrameLoader()
129 {
130 }
131 
init()132 void FrameLoader::init()
133 {
134     m_provisionalDocumentLoader = client()->createDocumentLoader(m_frame, ResourceRequest(KURL(ParsedURLString, emptyString())), SubstituteData());
135     m_provisionalDocumentLoader->startLoadingMainResource();
136     m_frame->document()->cancelParsing();
137     m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocument);
138 }
139 
client() const140 FrameLoaderClient* FrameLoader::client() const
141 {
142     return static_cast<FrameLoaderClient*>(m_frame->client());
143 }
144 
setDefersLoading(bool defers)145 void FrameLoader::setDefersLoading(bool defers)
146 {
147     if (m_documentLoader)
148         m_documentLoader->setDefersLoading(defers);
149     if (m_provisionalDocumentLoader)
150         m_provisionalDocumentLoader->setDefersLoading(defers);
151     if (m_policyDocumentLoader)
152         m_policyDocumentLoader->setDefersLoading(defers);
153 
154     if (!defers) {
155         if (m_deferredHistoryLoad.isValid()) {
156             loadHistoryItem(m_deferredHistoryLoad.m_item.get(), m_deferredHistoryLoad.m_type, m_deferredHistoryLoad.m_cachePolicy);
157             m_deferredHistoryLoad = DeferredHistoryLoad();
158         }
159         m_frame->navigationScheduler().startTimer();
160         startCheckCompleteTimer();
161     }
162 }
163 
stopLoading()164 void FrameLoader::stopLoading()
165 {
166     m_isComplete = true; // to avoid calling completed() in finishedParsing()
167 
168     if (m_frame->document() && m_frame->document()->parsing()) {
169         finishedParsing();
170         m_frame->document()->setParsing(false);
171     }
172 
173     if (Document* doc = m_frame->document()) {
174         // FIXME: HTML5 doesn't tell us to set the state to complete when aborting, but we do anyway to match legacy behavior.
175         // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10537
176         doc->setReadyState(Document::Complete);
177     }
178 
179     // FIXME: This will cancel redirection timer, which really needs to be restarted when restoring the frame from b/f cache.
180     m_frame->navigationScheduler().cancel();
181 }
182 
saveScrollState()183 void FrameLoader::saveScrollState()
184 {
185     if (!m_currentItem || !m_frame->view())
186         return;
187 
188     // Shouldn't clobber anything if we might still restore later.
189     if (needsHistoryItemRestore(m_loadType) && !m_frame->view()->wasScrolledByUser())
190         return;
191 
192     m_currentItem->setScrollPoint(m_frame->view()->scrollPosition());
193 
194     if (m_frame->settings()->pinchVirtualViewportEnabled())
195         m_currentItem->setPinchViewportScrollPoint(m_frame->host()->pinchViewport().visibleRect().location());
196     else
197         m_currentItem->setPinchViewportScrollPoint(FloatPoint(-1, -1));
198 
199     if (m_frame->isMainFrame() && !m_frame->page()->inspectorController().deviceEmulationEnabled())
200         m_currentItem->setPageScaleFactor(m_frame->page()->pageScaleFactor());
201 
202     client()->didUpdateCurrentHistoryItem();
203 }
204 
clearScrollPositionAndViewState()205 void FrameLoader::clearScrollPositionAndViewState()
206 {
207     ASSERT(m_frame->isMainFrame());
208     if (!m_currentItem)
209         return;
210     m_currentItem->clearScrollPoint();
211     m_currentItem->setPageScaleFactor(0);
212 }
213 
closeURL()214 bool FrameLoader::closeURL()
215 {
216     saveScrollState();
217 
218     // Should only send the pagehide event here if the current document exists.
219     if (m_frame->document())
220         m_frame->document()->dispatchUnloadEvents();
221     stopLoading();
222 
223     if (Page* page = m_frame->page())
224         page->undoStack().didUnloadFrame(*m_frame);
225     return true;
226 }
227 
didExplicitOpen()228 void FrameLoader::didExplicitOpen()
229 {
230     m_isComplete = false;
231 
232     // Calling document.open counts as committing the first real document load.
233     if (!m_stateMachine.committedFirstRealDocumentLoad())
234         m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad);
235 
236     // Prevent window.open(url) -- eg window.open("about:blank") -- from blowing away results
237     // from a subsequent window.document.open / window.document.write call.
238     // Canceling redirection here works for all cases because document.open
239     // implicitly precedes document.write.
240     m_frame->navigationScheduler().cancel();
241 }
242 
clear()243 void FrameLoader::clear()
244 {
245     if (m_stateMachine.creatingInitialEmptyDocument())
246         return;
247 
248     m_frame->editor().clear();
249     m_frame->document()->cancelParsing();
250     m_frame->document()->prepareForDestruction();
251     m_frame->document()->removeFocusedElementOfSubtree(m_frame->document());
252 
253     m_frame->selection().prepareForDestruction();
254     m_frame->eventHandler().clear();
255     if (m_frame->view())
256         m_frame->view()->clear();
257 
258     m_frame->script().enableEval();
259 
260     m_frame->navigationScheduler().clear();
261 
262     m_checkTimer.stop();
263     m_shouldCallCheckCompleted = false;
264 
265     if (m_stateMachine.isDisplayingInitialEmptyDocument())
266         m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad);
267 }
268 
setHistoryItemStateForCommit(HistoryCommitType historyCommitType,bool isPushOrReplaceState,PassRefPtr<SerializedScriptValue> stateObject)269 void FrameLoader::setHistoryItemStateForCommit(HistoryCommitType historyCommitType, bool isPushOrReplaceState, PassRefPtr<SerializedScriptValue> stateObject)
270 {
271     if (m_provisionalItem)
272         m_currentItem = m_provisionalItem.release();
273 
274     if (!m_currentItem || historyCommitType == StandardCommit) {
275         m_currentItem = HistoryItem::create();
276     } else if (!isPushOrReplaceState && m_documentLoader->url() != m_currentItem->url()) {
277         m_currentItem->generateNewItemSequenceNumber();
278         if (!equalIgnoringFragmentIdentifier(m_documentLoader->url(), m_currentItem->url()))
279             m_currentItem->generateNewDocumentSequenceNumber();
280     }
281 
282     m_currentItem->setURL(m_documentLoader->urlForHistory());
283     m_currentItem->setDocumentState(m_frame->document()->formElementsState());
284     m_currentItem->setTarget(m_frame->tree().uniqueName());
285     if (isPushOrReplaceState)
286         m_currentItem->setStateObject(stateObject);
287     m_currentItem->setReferrer(Referrer(m_documentLoader->request().httpReferrer(), m_documentLoader->request().referrerPolicy()));
288     m_currentItem->setFormInfoFromRequest(m_documentLoader->request());
289 }
290 
loadTypeToCommitType(FrameLoadType type)291 static HistoryCommitType loadTypeToCommitType(FrameLoadType type)
292 {
293     switch (type) {
294     case FrameLoadTypeStandard:
295         return StandardCommit;
296     case FrameLoadTypeInitialInChildFrame:
297         return InitialCommitInChildFrame;
298     case FrameLoadTypeBackForward:
299         return BackForwardCommit;
300     default:
301         break;
302     }
303     return HistoryInertCommit;
304 }
305 
receivedFirstData()306 void FrameLoader::receivedFirstData()
307 {
308     if (m_stateMachine.creatingInitialEmptyDocument())
309         return;
310 
311     HistoryCommitType historyCommitType = loadTypeToCommitType(m_loadType);
312     if (historyCommitType == StandardCommit && (m_documentLoader->urlForHistory().isEmpty() || (opener() && !m_currentItem && m_documentLoader->originalRequest().url().isEmpty())))
313         historyCommitType = HistoryInertCommit;
314     else if (historyCommitType == InitialCommitInChildFrame && (!m_frame->tree().top()->isLocalFrame() || MixedContentChecker::isMixedContent(toLocalFrame(m_frame->tree().top())->document()->securityOrigin(), m_documentLoader->url())))
315         historyCommitType = HistoryInertCommit;
316     setHistoryItemStateForCommit(historyCommitType);
317 
318     if (!m_stateMachine.committedMultipleRealLoads() && m_loadType == FrameLoadTypeStandard)
319         m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedMultipleRealLoads);
320 
321     client()->dispatchDidCommitLoad(m_frame, m_currentItem.get(), historyCommitType);
322 
323     InspectorInstrumentation::didCommitLoad(m_frame, m_documentLoader.get());
324     m_frame->page()->didCommitLoad(m_frame);
325     dispatchDidClearDocumentOfWindowObject();
326 }
327 
didFailContentSecurityPolicyCheck(FrameLoader * loader)328 static void didFailContentSecurityPolicyCheck(FrameLoader* loader)
329 {
330     // load event and stopAllLoaders can detach the LocalFrame, so protect it.
331     RefPtr<LocalFrame> frame(loader->frame());
332 
333     // Move the page to a unique origin, and cancel the load.
334     frame->document()->enforceSandboxFlags(SandboxOrigin);
335     loader->stopAllLoaders();
336 
337     // Fire a load event, as timing attacks would otherwise reveal that the
338     // frame was blocked. This way, it looks like every other cross-origin
339     // page.
340     if (FrameOwner* frameOwner = frame->owner())
341         frameOwner->dispatchLoad();
342 }
343 
didBeginDocument(bool dispatch)344 void FrameLoader::didBeginDocument(bool dispatch)
345 {
346     m_isComplete = false;
347     m_frame->document()->setReadyState(Document::Loading);
348 
349     if (m_provisionalItem && m_loadType == FrameLoadTypeBackForward)
350         m_frame->domWindow()->statePopped(m_provisionalItem->stateObject());
351 
352     if (dispatch)
353         dispatchDidClearDocumentOfWindowObject();
354 
355     m_frame->document()->initContentSecurityPolicy(m_documentLoader ? ContentSecurityPolicyResponseHeaders(m_documentLoader->response()) : ContentSecurityPolicyResponseHeaders());
356 
357     if (!m_frame->document()->contentSecurityPolicy()->allowAncestors(m_frame)) {
358         didFailContentSecurityPolicyCheck(this);
359         return;
360     }
361 
362     Settings* settings = m_frame->document()->settings();
363     if (settings) {
364         m_frame->document()->fetcher()->setImagesEnabled(settings->imagesEnabled());
365         m_frame->document()->fetcher()->setAutoLoadImages(settings->loadsImagesAutomatically());
366     }
367 
368     if (m_documentLoader) {
369         const AtomicString& dnsPrefetchControl = m_documentLoader->response().httpHeaderField("X-DNS-Prefetch-Control");
370         if (!dnsPrefetchControl.isEmpty())
371             m_frame->document()->parseDNSPrefetchControlHeader(dnsPrefetchControl);
372 
373         String headerContentLanguage = m_documentLoader->response().httpHeaderField("Content-Language");
374         if (!headerContentLanguage.isEmpty()) {
375             size_t commaIndex = headerContentLanguage.find(',');
376             headerContentLanguage.truncate(commaIndex); // kNotFound == -1 == don't truncate
377             headerContentLanguage = headerContentLanguage.stripWhiteSpace(isHTMLSpace<UChar>);
378             if (!headerContentLanguage.isEmpty())
379                 m_frame->document()->setContentLanguage(AtomicString(headerContentLanguage));
380         }
381     }
382 
383     if (m_provisionalItem && m_loadType == FrameLoadTypeBackForward)
384         m_frame->document()->setStateForNewFormElements(m_provisionalItem->documentState());
385 }
386 
finishedParsing()387 void FrameLoader::finishedParsing()
388 {
389     if (m_stateMachine.creatingInitialEmptyDocument())
390         return;
391 
392     // This can be called from the LocalFrame's destructor, in which case we shouldn't protect ourselves
393     // because doing so will cause us to re-enter the destructor when protector goes out of scope.
394     // Null-checking the FrameView indicates whether or not we're in the destructor.
395     RefPtr<LocalFrame> protector = m_frame->view() ? m_frame : 0;
396 
397     if (client())
398         client()->dispatchDidFinishDocumentLoad();
399 
400     checkCompleted();
401 
402     if (!m_frame->view())
403         return; // We are being destroyed by something checkCompleted called.
404 
405     // Check if the scrollbars are really needed for the content.
406     // If not, remove them, relayout, and repaint.
407     m_frame->view()->restoreScrollbar();
408     scrollToFragmentWithParentBoundary(m_frame->document()->url());
409 }
410 
loadDone()411 void FrameLoader::loadDone()
412 {
413     checkCompleted();
414 }
415 
allChildrenAreComplete() const416 bool FrameLoader::allChildrenAreComplete() const
417 {
418     for (Frame* child = m_frame->tree().firstChild(); child; child = child->tree().nextSibling()) {
419         if (child->isLocalFrame() && !toLocalFrame(child)->loader().m_isComplete)
420             return false;
421     }
422     return true;
423 }
424 
allAncestorsAreComplete() const425 bool FrameLoader::allAncestorsAreComplete() const
426 {
427     for (Frame* ancestor = m_frame; ancestor; ancestor = ancestor->tree().parent()) {
428         if (ancestor->isLocalFrame() && !toLocalFrame(ancestor)->document()->loadEventFinished())
429             return false;
430     }
431     return true;
432 }
433 
checkCompleted()434 void FrameLoader::checkCompleted()
435 {
436     RefPtr<LocalFrame> protect(m_frame);
437     m_shouldCallCheckCompleted = false;
438 
439     if (m_frame->view())
440         m_frame->view()->handleLoadCompleted();
441 
442     // Have we completed before?
443     if (m_isComplete)
444         return;
445 
446     // Are we still parsing?
447     if (m_frame->document()->parsing())
448         return;
449 
450     // Still waiting imports?
451     if (!m_frame->document()->haveImportsLoaded())
452         return;
453 
454     // Still waiting for images/scripts?
455     if (m_frame->document()->fetcher()->requestCount())
456         return;
457 
458     // Still waiting for elements that don't go through a FrameLoader?
459     if (m_frame->document()->isDelayingLoadEvent())
460         return;
461 
462     // Any frame that hasn't completed yet?
463     if (!allChildrenAreComplete())
464         return;
465 
466     // OK, completed.
467     m_isComplete = true;
468     m_frame->document()->setReadyState(Document::Complete);
469     if (m_frame->document()->loadEventStillNeeded())
470         m_frame->document()->implicitClose();
471 
472     m_frame->navigationScheduler().startTimer();
473 
474     completed();
475     if (m_frame->page())
476         checkLoadComplete();
477 
478     if (m_frame->view())
479         m_frame->view()->handleLoadCompleted();
480 }
481 
checkTimerFired(Timer<FrameLoader> *)482 void FrameLoader::checkTimerFired(Timer<FrameLoader>*)
483 {
484     RefPtr<LocalFrame> protect(m_frame);
485 
486     if (Page* page = m_frame->page()) {
487         if (page->defersLoading())
488             return;
489     }
490     if (m_shouldCallCheckCompleted)
491         checkCompleted();
492 }
493 
startCheckCompleteTimer()494 void FrameLoader::startCheckCompleteTimer()
495 {
496     if (!m_shouldCallCheckCompleted)
497         return;
498     if (m_checkTimer.isActive())
499         return;
500     m_checkTimer.startOneShot(0, FROM_HERE);
501 }
502 
scheduleCheckCompleted()503 void FrameLoader::scheduleCheckCompleted()
504 {
505     m_shouldCallCheckCompleted = true;
506     startCheckCompleteTimer();
507 }
508 
opener()509 LocalFrame* FrameLoader::opener()
510 {
511     // FIXME: Temporary hack to stage converting locations that really should be Frame.
512     return client() ? toLocalFrame(client()->opener()) : 0;
513 }
514 
setOpener(LocalFrame * opener)515 void FrameLoader::setOpener(LocalFrame* opener)
516 {
517     // If the frame is already detached, the opener has already been cleared.
518     if (client())
519         client()->setOpener(opener);
520 }
521 
allowPlugins(ReasonForCallingAllowPlugins reason)522 bool FrameLoader::allowPlugins(ReasonForCallingAllowPlugins reason)
523 {
524     Settings* settings = m_frame->settings();
525     bool allowed = client()->allowPlugins(settings && settings->pluginsEnabled());
526     if (!allowed && reason == AboutToInstantiatePlugin)
527         client()->didNotAllowPlugins();
528     return allowed;
529 }
530 
updateForSameDocumentNavigation(const KURL & newURL,SameDocumentNavigationSource sameDocumentNavigationSource,PassRefPtr<SerializedScriptValue> data,FrameLoadType type)531 void FrameLoader::updateForSameDocumentNavigation(const KURL& newURL, SameDocumentNavigationSource sameDocumentNavigationSource, PassRefPtr<SerializedScriptValue> data, FrameLoadType type)
532 {
533     // Update the data source's request with the new URL to fake the URL change
534     m_frame->document()->setURL(newURL);
535     documentLoader()->updateForSameDocumentNavigation(newURL, sameDocumentNavigationSource);
536 
537     // Generate start and stop notifications only when loader is completed so that we
538     // don't fire them for fragment redirection that happens in window.onload handler.
539     // See https://bugs.webkit.org/show_bug.cgi?id=31838
540     if (m_frame->document()->loadEventFinished())
541         client()->didStartLoading(NavigationWithinSameDocument);
542 
543     HistoryCommitType historyCommitType = loadTypeToCommitType(type);
544     if (!m_currentItem)
545         historyCommitType = HistoryInertCommit;
546 
547     setHistoryItemStateForCommit(historyCommitType, sameDocumentNavigationSource == SameDocumentNavigationHistoryApi, data);
548     client()->dispatchDidNavigateWithinPage(m_currentItem.get(), historyCommitType);
549     client()->dispatchDidReceiveTitle(m_frame->document()->title());
550     if (m_frame->document()->loadEventFinished())
551         client()->didStopLoading();
552 }
553 
loadInSameDocument(const KURL & url,PassRefPtr<SerializedScriptValue> stateObject,FrameLoadType type,ClientRedirectPolicy clientRedirect)554 void FrameLoader::loadInSameDocument(const KURL& url, PassRefPtr<SerializedScriptValue> stateObject, FrameLoadType type, ClientRedirectPolicy clientRedirect)
555 {
556     // If we have a state object, we cannot also be a new navigation.
557     ASSERT(!stateObject || type == FrameLoadTypeBackForward);
558 
559     // If we have a provisional request for a different document, a fragment scroll should cancel it.
560     if (m_provisionalDocumentLoader) {
561         m_provisionalDocumentLoader->stopLoading();
562         if (m_provisionalDocumentLoader)
563             m_provisionalDocumentLoader->detachFromFrame();
564         m_provisionalDocumentLoader = nullptr;
565         if (!m_frame->host())
566             return;
567     }
568     m_loadType = type;
569     saveScrollState();
570 
571     KURL oldURL = m_frame->document()->url();
572     // If we were in the autoscroll/panScroll mode we want to stop it before following the link to the anchor
573     bool hashChange = equalIgnoringFragmentIdentifier(url, oldURL) && url.fragmentIdentifier() != oldURL.fragmentIdentifier();
574     if (hashChange) {
575         m_frame->eventHandler().stopAutoscroll();
576         m_frame->domWindow()->enqueueHashchangeEvent(oldURL, url);
577     }
578     m_documentLoader->setIsClientRedirect(clientRedirect == ClientRedirect);
579     m_documentLoader->setReplacesCurrentHistoryItem(m_loadType == FrameLoadTypeStandard);
580     updateForSameDocumentNavigation(url, SameDocumentNavigationDefault, nullptr, type);
581 
582     m_frame->view()->setWasScrolledByUser(false);
583 
584     // It's important to model this as a load that starts and immediately finishes.
585     // Otherwise, the parent frame may think we never finished loading.
586     started();
587 
588     // We need to scroll to the fragment whether or not a hash change occurred, since
589     // the user might have scrolled since the previous navigation.
590     scrollToFragmentWithParentBoundary(url);
591 
592     m_isComplete = false;
593     checkCompleted();
594 
595     m_frame->domWindow()->statePopped(stateObject ? stateObject : SerializedScriptValue::nullValue());
596 }
597 
completed()598 void FrameLoader::completed()
599 {
600     RefPtr<LocalFrame> protect(m_frame);
601 
602     for (Frame* descendant = m_frame->tree().traverseNext(m_frame); descendant; descendant = descendant->tree().traverseNext(m_frame)) {
603         if (descendant->isLocalFrame())
604             toLocalFrame(descendant)->navigationScheduler().startTimer();
605     }
606 
607     Frame* parent = m_frame->tree().parent();
608     if (parent && parent->isLocalFrame())
609         toLocalFrame(parent)->loader().checkCompleted();
610 
611     if (m_frame->view())
612         m_frame->view()->maintainScrollPositionAtAnchor(0);
613 }
614 
started()615 void FrameLoader::started()
616 {
617     for (Frame* frame = m_frame; frame; frame = frame->tree().parent()) {
618         if (frame->isLocalFrame())
619             toLocalFrame(frame)->loader().m_isComplete = false;
620     }
621 }
622 
setReferrerForFrameRequest(ResourceRequest & request,ShouldSendReferrer shouldSendReferrer,Document * originDocument)623 void FrameLoader::setReferrerForFrameRequest(ResourceRequest& request, ShouldSendReferrer shouldSendReferrer, Document* originDocument)
624 {
625     if (shouldSendReferrer == NeverSendReferrer) {
626         request.clearHTTPReferrer();
627         return;
628     }
629 
630     // Always use the initiating document to generate the referrer.
631     // We need to generateReferrerHeader(), because we might not have enforced ReferrerPolicy or https->http
632     // referrer suppression yet.
633     String argsReferrer(request.httpReferrer());
634     if (argsReferrer.isEmpty())
635         argsReferrer = originDocument->outgoingReferrer();
636     String referrer = SecurityPolicy::generateReferrerHeader(originDocument->referrerPolicy(), request.url(), argsReferrer);
637 
638     request.setHTTPReferrer(Referrer(referrer, originDocument->referrerPolicy()));
639     RefPtr<SecurityOrigin> referrerOrigin = SecurityOrigin::createFromString(referrer);
640     addHTTPOriginIfNeeded(request, referrerOrigin->toAtomicString());
641 }
642 
isScriptTriggeredFormSubmissionInChildFrame(const FrameLoadRequest & request) const643 bool FrameLoader::isScriptTriggeredFormSubmissionInChildFrame(const FrameLoadRequest& request) const
644 {
645     // If this is a child frame and the form submission was triggered by a script, lock the back/forward list
646     // to match IE and Opera.
647     // See https://bugs.webkit.org/show_bug.cgi?id=32383 for the original motivation for this.
648     if (!m_frame->tree().parent() || UserGestureIndicator::processingUserGesture())
649         return false;
650     return request.formState() && request.formState()->formSubmissionTrigger() == SubmittedByJavaScript;
651 }
652 
determineFrameLoadType(const FrameLoadRequest & request)653 FrameLoadType FrameLoader::determineFrameLoadType(const FrameLoadRequest& request)
654 {
655     if (m_frame->tree().parent() && !m_stateMachine.committedFirstRealDocumentLoad())
656         return FrameLoadTypeInitialInChildFrame;
657     if (!m_frame->tree().parent() && !m_frame->page()->backForward().backForwardListCount())
658         return FrameLoadTypeStandard;
659     if (m_provisionalDocumentLoader && request.substituteData().failingURL() == m_provisionalDocumentLoader->url() && m_loadType == FrameLoadTypeBackForward)
660         return FrameLoadTypeBackForward;
661     if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData)
662         return FrameLoadTypeReload;
663     if (request.resourceRequest().cachePolicy() == ReloadBypassingCache)
664         return FrameLoadTypeReloadFromOrigin;
665     if (request.lockBackForwardList() || isScriptTriggeredFormSubmissionInChildFrame(request))
666         return FrameLoadTypeRedirectWithLockedBackForwardList;
667     if (!request.originDocument() && request.resourceRequest().url() == m_documentLoader->urlForHistory())
668         return FrameLoadTypeSame;
669     if (request.substituteData().failingURL() == m_documentLoader->urlForHistory() && m_loadType == FrameLoadTypeReload)
670         return FrameLoadTypeReload;
671     return FrameLoadTypeStandard;
672 }
673 
prepareRequestForThisFrame(FrameLoadRequest & request)674 bool FrameLoader::prepareRequestForThisFrame(FrameLoadRequest& request)
675 {
676     // If no origin Document* was specified, skip security checks and assume the caller has fully initialized the FrameLoadRequest.
677     if (!request.originDocument())
678         return true;
679 
680     KURL url = request.resourceRequest().url();
681     if (m_frame->script().executeScriptIfJavaScriptURL(url))
682         return false;
683 
684     if (!request.originDocument()->securityOrigin()->canDisplay(url)) {
685         reportLocalLoadFailed(m_frame, url.elidedString());
686         return false;
687     }
688 
689     if (!request.formState() && request.frameName().isEmpty())
690         request.setFrameName(m_frame->document()->baseTarget());
691 
692     setReferrerForFrameRequest(request.resourceRequest(), request.shouldSendReferrer(), request.originDocument());
693     return true;
694 }
695 
shouldOpenInNewWindow(LocalFrame * targetFrame,const FrameLoadRequest & request,const NavigationAction & action)696 static bool shouldOpenInNewWindow(LocalFrame* targetFrame, const FrameLoadRequest& request, const NavigationAction& action)
697 {
698     if (!targetFrame && !request.frameName().isEmpty())
699         return true;
700     // FIXME: This case is a workaround for the fact that ctrl+clicking a form submission incorrectly
701     // sends as a GET rather than a POST if it creates a new window in a different process.
702     return request.formState() && action.shouldOpenInNewWindow();
703 }
704 
load(const FrameLoadRequest & passedRequest)705 void FrameLoader::load(const FrameLoadRequest& passedRequest)
706 {
707     ASSERT(m_frame->document());
708 
709     RefPtr<LocalFrame> protect(m_frame);
710 
711     if (m_inStopAllLoaders)
712         return;
713 
714     FrameLoadRequest request(passedRequest);
715     if (!prepareRequestForThisFrame(request))
716         return;
717 
718     RefPtr<LocalFrame> targetFrame = request.formState() ? 0 : findFrameForNavigation(AtomicString(request.frameName()), request.formState() ? request.formState()->sourceDocument() : m_frame->document());
719     if (targetFrame && targetFrame != m_frame) {
720         request.setFrameName("_self");
721         targetFrame->loader().load(request);
722         if (Page* page = targetFrame->page())
723             page->chrome().focus();
724         return;
725     }
726 
727     FrameLoadType newLoadType = determineFrameLoadType(request);
728     NavigationAction action(request.resourceRequest(), newLoadType, request.formState(), request.triggeringEvent());
729     if (shouldOpenInNewWindow(targetFrame.get(), request, action)) {
730         if (action.policy() == NavigationPolicyDownload)
731             client()->loadURLExternally(action.resourceRequest(), NavigationPolicyDownload);
732         else
733             createWindowForRequest(request, *m_frame, action.policy(), request.shouldSendReferrer());
734         return;
735     }
736 
737     const KURL& url = request.resourceRequest().url();
738     if (!action.shouldOpenInNewWindow() && shouldPerformFragmentNavigation(request.formState(), request.resourceRequest().httpMethod(), newLoadType, url)) {
739         m_documentLoader->setTriggeringAction(action);
740         if (shouldTreatURLAsSameAsCurrent(url))
741             newLoadType = FrameLoadTypeRedirectWithLockedBackForwardList;
742         loadInSameDocument(url, nullptr, newLoadType, request.clientRedirect());
743         return;
744     }
745     bool sameURL = url == m_documentLoader->urlForHistory();
746     loadWithNavigationAction(action, newLoadType, request.formState(), request.substituteData(), request.clientRedirect());
747     // Example of this case are sites that reload the same URL with a different cookie
748     // driving the generated content, or a master frame with links that drive a target
749     // frame, where the user has clicked on the same link repeatedly.
750     if (sameURL && newLoadType != FrameLoadTypeReload && newLoadType != FrameLoadTypeReloadFromOrigin && request.resourceRequest().httpMethod() != "POST")
751         m_loadType = FrameLoadTypeSame;
752 }
753 
defaultSubstituteDataForURL(const KURL & url)754 SubstituteData FrameLoader::defaultSubstituteDataForURL(const KURL& url)
755 {
756     if (!shouldTreatURLAsSrcdocDocument(url))
757         return SubstituteData();
758     String srcdoc = m_frame->deprecatedLocalOwner()->fastGetAttribute(srcdocAttr);
759     ASSERT(!srcdoc.isNull());
760     CString encodedSrcdoc = srcdoc.utf8();
761     return SubstituteData(SharedBuffer::create(encodedSrcdoc.data(), encodedSrcdoc.length()), "text/html", "UTF-8", KURL());
762 }
763 
reportLocalLoadFailed(LocalFrame * frame,const String & url)764 void FrameLoader::reportLocalLoadFailed(LocalFrame* frame, const String& url)
765 {
766     ASSERT(!url.isEmpty());
767     if (!frame)
768         return;
769 
770     frame->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Not allowed to load local resource: " + url);
771 }
772 
requestFromHistoryItem(HistoryItem * item,ResourceRequestCachePolicy cachePolicy)773 static ResourceRequest requestFromHistoryItem(HistoryItem* item, ResourceRequestCachePolicy cachePolicy)
774 {
775     RefPtr<FormData> formData = item->formData();
776     ResourceRequest request(item->url(), item->referrer());
777     request.setCachePolicy(cachePolicy);
778     if (formData) {
779         request.setHTTPMethod("POST");
780         request.setHTTPBody(formData);
781         request.setHTTPContentType(item->formContentType());
782         RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::createFromString(item->referrer().referrer);
783         FrameLoader::addHTTPOriginIfNeeded(request, securityOrigin->toAtomicString());
784     }
785     return request;
786 }
787 
reload(ReloadPolicy reloadPolicy,const KURL & overrideURL,const AtomicString & overrideEncoding)788 void FrameLoader::reload(ReloadPolicy reloadPolicy, const KURL& overrideURL, const AtomicString& overrideEncoding)
789 {
790     if (!m_currentItem)
791         return;
792 
793     ResourceRequestCachePolicy cachePolicy = reloadPolicy == EndToEndReload ? ReloadBypassingCache : ReloadIgnoringCacheData;
794     ResourceRequest request = requestFromHistoryItem(m_currentItem.get(), cachePolicy);
795     if (!overrideURL.isEmpty()) {
796         request.setURL(overrideURL);
797         request.clearHTTPReferrer();
798     }
799 
800     FrameLoadType type = reloadPolicy == EndToEndReload ? FrameLoadTypeReloadFromOrigin : FrameLoadTypeReload;
801     loadWithNavigationAction(NavigationAction(request, type), type, nullptr, SubstituteData(), NotClientRedirect, overrideEncoding);
802 }
803 
stopAllLoaders()804 void FrameLoader::stopAllLoaders()
805 {
806     if (m_frame->document()->pageDismissalEventBeingDispatched() != Document::NoDismissal)
807         return;
808 
809     // If this method is called from within this method, infinite recursion can occur (3442218). Avoid this.
810     if (m_inStopAllLoaders)
811         return;
812 
813     // Calling stopLoading() on the provisional document loader can blow away
814     // the frame from underneath.
815     RefPtr<LocalFrame> protect(m_frame);
816 
817     m_inStopAllLoaders = true;
818 
819     for (RefPtr<Frame> child = m_frame->tree().firstChild(); child; child = child->tree().nextSibling()) {
820         if (child->isLocalFrame())
821             toLocalFrame(child.get())->loader().stopAllLoaders();
822     }
823     if (m_provisionalDocumentLoader)
824         m_provisionalDocumentLoader->stopLoading();
825     if (m_documentLoader)
826         m_documentLoader->stopLoading();
827 
828     if (m_provisionalDocumentLoader)
829         m_provisionalDocumentLoader->detachFromFrame();
830     m_provisionalDocumentLoader = nullptr;
831 
832     m_checkTimer.stop();
833 
834     m_inStopAllLoaders = false;
835 
836     // detachFromParent() can be called multiple times on same LocalFrame, which
837     // means we may no longer have a FrameLoaderClient to talk to.
838     if (client())
839         client()->didStopAllLoaders();
840 }
841 
didAccessInitialDocument()842 void FrameLoader::didAccessInitialDocument()
843 {
844     // We only need to notify the client once, and only for the main frame.
845     if (isLoadingMainFrame() && !m_didAccessInitialDocument) {
846         m_didAccessInitialDocument = true;
847         // Notify asynchronously, since this is called within a JavaScript security check.
848         m_didAccessInitialDocumentTimer.startOneShot(0, FROM_HERE);
849     }
850 }
851 
didAccessInitialDocumentTimerFired(Timer<FrameLoader> *)852 void FrameLoader::didAccessInitialDocumentTimerFired(Timer<FrameLoader>*)
853 {
854     client()->didAccessInitialDocument();
855 }
856 
notifyIfInitialDocumentAccessed()857 void FrameLoader::notifyIfInitialDocumentAccessed()
858 {
859     if (m_didAccessInitialDocumentTimer.isActive()) {
860         m_didAccessInitialDocumentTimer.stop();
861         didAccessInitialDocumentTimerFired(0);
862     }
863 }
864 
isLoading() const865 bool FrameLoader::isLoading() const
866 {
867     if (m_provisionalDocumentLoader)
868         return true;
869     return m_documentLoader && m_documentLoader->isLoading();
870 }
871 
commitProvisionalLoad()872 void FrameLoader::commitProvisionalLoad()
873 {
874     ASSERT(client()->hasWebView());
875     ASSERT(m_state == FrameStateProvisional);
876     RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader;
877     RefPtr<LocalFrame> protect(m_frame);
878 
879     // Check if the destination page is allowed to access the previous page's timing information.
880     if (m_frame->document()) {
881         RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(pdl->request().url());
882         pdl->timing()->setHasSameOriginAsPreviousDocument(securityOrigin->canRequest(m_frame->document()->url()));
883     }
884 
885     // The call to closeURL() invokes the unload event handler, which can execute arbitrary
886     // JavaScript. If the script initiates a new load, we need to abandon the current load,
887     // or the two will stomp each other.
888     // detachChildren will similarly trigger child frame unload event handlers.
889     if (m_documentLoader) {
890         client()->dispatchWillClose();
891         closeURL();
892     }
893     detachChildren();
894     if (pdl != m_provisionalDocumentLoader)
895         return;
896     if (m_documentLoader)
897         m_documentLoader->detachFromFrame();
898     m_documentLoader = m_provisionalDocumentLoader.release();
899     m_state = FrameStateCommittedPage;
900 
901     if (isLoadingMainFrame())
902         m_frame->page()->chrome().client().needTouchEvents(false);
903 
904     client()->transitionToCommittedForNewPage();
905     m_frame->navigationScheduler().cancel();
906     m_frame->editor().clearLastEditCommand();
907 
908     // If we are still in the process of initializing an empty document then
909     // its frame is not in a consistent state for rendering, so avoid setJSStatusBarText
910     // since it may cause clients to attempt to render the frame.
911     if (!m_stateMachine.creatingInitialEmptyDocument()) {
912         LocalDOMWindow* window = m_frame->domWindow();
913         window->setStatus(String());
914         window->setDefaultStatus(String());
915     }
916     started();
917 }
918 
isLoadingMainFrame() const919 bool FrameLoader::isLoadingMainFrame() const
920 {
921     return m_frame->isMainFrame();
922 }
923 
loadType() const924 FrameLoadType FrameLoader::loadType() const
925 {
926     return m_loadType;
927 }
928 
929 // This function is an incomprehensible mess and is only used in checkLoadCompleteForThisFrame.
930 // If you're thinking of using it elsewhere, stop right now and reconsider your life.
isDocumentDoneLoading(Document * document)931 static bool isDocumentDoneLoading(Document* document)
932 {
933     if (!document->loader())
934         return true;
935     if (document->loader()->isLoadingMainResource())
936         return false;
937     if (!document->loadEventFinished()) {
938         if (document->loader()->isLoading() || document->isDelayingLoadEvent())
939             return false;
940     }
941     if (document->fetcher()->requestCount())
942         return false;
943     if (document->processingLoadEvent())
944         return false;
945     if (document->hasActiveParser())
946         return false;
947     return true;
948 }
949 
checkLoadCompleteForThisFrame()950 bool FrameLoader::checkLoadCompleteForThisFrame()
951 {
952     ASSERT(client()->hasWebView());
953     RefPtr<LocalFrame> protect(m_frame);
954 
955     bool allChildrenAreDoneLoading = true;
956     for (RefPtr<Frame> child = m_frame->tree().firstChild(); child; child = child->tree().nextSibling()) {
957         if (child->isLocalFrame())
958             allChildrenAreDoneLoading &= toLocalFrame(child.get())->loader().checkLoadCompleteForThisFrame();
959     }
960 
961     if (m_state == FrameStateProvisional && m_provisionalDocumentLoader) {
962         const ResourceError& error = m_provisionalDocumentLoader->mainDocumentError();
963         if (error.isNull())
964             return false;
965         RefPtr<DocumentLoader> loader = m_provisionalDocumentLoader;
966         client()->dispatchDidFailProvisionalLoad(error);
967         if (loader != m_provisionalDocumentLoader)
968             return false;
969         m_provisionalDocumentLoader->detachFromFrame();
970         m_provisionalDocumentLoader = nullptr;
971         m_progressTracker->progressCompleted();
972         m_state = FrameStateComplete;
973         return true;
974     }
975 
976     if (!allChildrenAreDoneLoading)
977         return false;
978 
979     if (m_state == FrameStateComplete)
980         return true;
981     if (m_provisionalDocumentLoader || !m_documentLoader)
982         return false;
983     if (!isDocumentDoneLoading(m_frame->document()) && !m_inStopAllLoaders)
984         return false;
985 
986     m_state = FrameStateComplete;
987 
988     // FIXME: Is this subsequent work important if we already navigated away?
989     // Maybe there are bugs because of that, or extra work we can skip because
990     // the new page is ready.
991 
992     // Retry restoring scroll offset since FrameStateComplete disables content
993     // size clamping.
994     restoreScrollPositionAndViewState();
995 
996     if (!m_stateMachine.committedFirstRealDocumentLoad())
997         return true;
998 
999     m_progressTracker->progressCompleted();
1000     m_frame->domWindow()->finishedLoading();
1001 
1002     const ResourceError& error = m_documentLoader->mainDocumentError();
1003     if (!error.isNull()) {
1004         client()->dispatchDidFailLoad(error);
1005     } else {
1006         // Report mobile vs. desktop page statistics. This will only report on Android.
1007         if (m_frame->isMainFrame())
1008             m_frame->document()->viewportDescription().reportMobilePageStats(m_frame);
1009 
1010         client()->dispatchDidFinishLoad();
1011     }
1012     m_loadType = FrameLoadTypeStandard;
1013     return true;
1014 }
1015 
restoreScrollPositionAndViewState()1016 void FrameLoader::restoreScrollPositionAndViewState()
1017 {
1018     FrameView* view = m_frame->view();
1019     if (!m_frame->page() || !view || !m_currentItem || !m_stateMachine.committedFirstRealDocumentLoad())
1020         return;
1021 
1022     if (!needsHistoryItemRestore(m_loadType))
1023         return;
1024 
1025     // This tries to balance 1. restoring as soon as possible, 2. detecting
1026     // clamping to avoid repeatedly popping the scroll position down as the
1027     // page height increases, 3. ignore clamp detection after load completes
1028     // because that may be because the page will never reach its previous
1029     // height.
1030     float mainFrameScale = m_frame->settings()->pinchVirtualViewportEnabled() ? 1 : m_currentItem->pageScaleFactor();
1031     bool canRestoreWithoutClamping = view->clampOffsetAtScale(m_currentItem->scrollPoint(), mainFrameScale) == m_currentItem->scrollPoint();
1032     bool canRestoreWithoutAnnoyingUser = !view->wasScrolledByUser() && (canRestoreWithoutClamping || m_state == FrameStateComplete);
1033     if (!canRestoreWithoutAnnoyingUser)
1034         return;
1035 
1036     if (m_frame->isMainFrame() && m_currentItem->pageScaleFactor()) {
1037         FloatPoint pinchViewportOffset(m_currentItem->pinchViewportScrollPoint());
1038         IntPoint frameScrollOffset(m_currentItem->scrollPoint());
1039 
1040         m_frame->page()->setPageScaleFactor(m_currentItem->pageScaleFactor(), frameScrollOffset);
1041 
1042         if (m_frame->settings()->pinchVirtualViewportEnabled()) {
1043             // If the pinch viewport's offset is (-1, -1) it means the history item
1044             // is an old version of HistoryItem so distribute the scroll between
1045             // the main frame and the pinch viewport as best as we can.
1046             // FIXME(bokan): This legacy distribution can be removed once the virtual viewport
1047             // pinch path is enabled on all platforms for at least one release.
1048             if (pinchViewportOffset.x() == -1 && pinchViewportOffset.y() == -1)
1049                 pinchViewportOffset = FloatPoint(frameScrollOffset - view->scrollPosition());
1050 
1051             m_frame->host()->pinchViewport().setLocation(pinchViewportOffset);
1052         }
1053     } else {
1054         view->setScrollPositionNonProgrammatically(m_currentItem->scrollPoint());
1055     }
1056 
1057     if (m_frame->isMainFrame()) {
1058         if (ScrollingCoordinator* scrollingCoordinator = m_frame->page()->scrollingCoordinator())
1059             scrollingCoordinator->frameViewRootLayerDidChange(view);
1060     }
1061 }
1062 
detachChildren()1063 void FrameLoader::detachChildren()
1064 {
1065     typedef Vector<RefPtr<LocalFrame> > FrameVector;
1066     FrameVector childrenToDetach;
1067     childrenToDetach.reserveCapacity(m_frame->tree().childCount());
1068     for (Frame* child = m_frame->tree().lastChild(); child; child = child->tree().previousSibling()) {
1069         if (child->isLocalFrame())
1070             childrenToDetach.append(toLocalFrame(child));
1071     }
1072     FrameVector::iterator end = childrenToDetach.end();
1073     for (FrameVector::iterator it = childrenToDetach.begin(); it != end; ++it)
1074         (*it)->loader().detachFromParent();
1075 }
1076 
1077 // Called every time a resource is completely loaded or an error is received.
checkLoadComplete()1078 void FrameLoader::checkLoadComplete()
1079 {
1080     ASSERT(client()->hasWebView());
1081     if (Page* page = m_frame->page()) {
1082         if (page->mainFrame()->isLocalFrame())
1083             page->deprecatedLocalMainFrame()->loader().checkLoadCompleteForThisFrame();
1084     }
1085 }
1086 
userAgent(const KURL & url) const1087 String FrameLoader::userAgent(const KURL& url) const
1088 {
1089     String userAgent = client()->userAgent(url);
1090     InspectorInstrumentation::applyUserAgentOverride(m_frame, &userAgent);
1091     return userAgent;
1092 }
1093 
frameDetached()1094 void FrameLoader::frameDetached()
1095 {
1096     // stopAllLoaders can detach the LocalFrame, so protect it.
1097     RefPtr<LocalFrame> protect(m_frame);
1098     stopAllLoaders();
1099     detachFromParent();
1100 }
1101 
detachFromParent()1102 void FrameLoader::detachFromParent()
1103 {
1104     // Temporary explosions. We should never re-enter this code when this condition is true.
1105     RELEASE_ASSERT(!m_willDetachClient);
1106 
1107     // stopAllLoaders can detach the LocalFrame, so protect it.
1108     RefPtr<LocalFrame> protect(m_frame);
1109 
1110     closeURL();
1111     detachChildren();
1112     // stopAllLoaders() needs to be called after detachChildren(), because detachedChildren()
1113     // will trigger the unload event handlers of any child frames, and those event
1114     // handlers might start a new subresource load in this frame.
1115     stopAllLoaders();
1116 
1117     InspectorInstrumentation::frameDetachedFromParent(m_frame);
1118 
1119     if (m_documentLoader)
1120         m_documentLoader->detachFromFrame();
1121     m_documentLoader = nullptr;
1122 
1123     if (!client())
1124         return;
1125 
1126     TemporaryChange<bool> willDetachClient(m_willDetachClient, true);
1127 
1128     // FIXME: All this code belongs up in Page.
1129     Frame* parent = m_frame->tree().parent();
1130     if (parent && parent->isLocalFrame()) {
1131         m_frame->setView(nullptr);
1132         // FIXME: Shouldn't need to check if page() is null here.
1133         if (m_frame->owner() && m_frame->page())
1134             m_frame->page()->decrementSubframeCount();
1135         m_frame->willDetachFrameHost();
1136         detachClient();
1137         toLocalFrame(parent)->loader().scheduleCheckCompleted();
1138     } else {
1139         m_frame->setView(nullptr);
1140         m_frame->willDetachFrameHost();
1141         detachClient();
1142     }
1143     m_frame->detachFromFrameHost();
1144 }
1145 
detachClient()1146 void FrameLoader::detachClient()
1147 {
1148     ASSERT(client());
1149 
1150     // Finish all cleanup work that might require talking to the embedder.
1151     m_progressTracker.clear();
1152     setOpener(0);
1153     // Notify ScriptController that the frame is closing, since its cleanup ends up calling
1154     // back to FrameLoaderClient via V8WindowShell.
1155     m_frame->script().clearForClose();
1156 
1157     // client() should never be null because that means we somehow re-entered
1158     // the frame detach code... but it is sometimes.
1159     // FIXME: Understand why this is happening so we can document this insanity.
1160     if (client()) {
1161         // After this, we must no longer talk to the client since this clears
1162         // its owning reference back to our owning LocalFrame.
1163         client()->detachedFromParent();
1164         m_frame->clearClient();
1165     }
1166 }
1167 
addHTTPOriginIfNeeded(ResourceRequest & request,const AtomicString & origin)1168 void FrameLoader::addHTTPOriginIfNeeded(ResourceRequest& request, const AtomicString& origin)
1169 {
1170     if (!request.httpOrigin().isEmpty())
1171         return;  // Request already has an Origin header.
1172 
1173     // Don't send an Origin header for GET or HEAD to avoid privacy issues.
1174     // For example, if an intranet page has a hyperlink to an external web
1175     // site, we don't want to include the Origin of the request because it
1176     // will leak the internal host name. Similar privacy concerns have lead
1177     // to the widespread suppression of the Referer header at the network
1178     // layer.
1179     if (request.httpMethod() == "GET" || request.httpMethod() == "HEAD")
1180         return;
1181 
1182     // For non-GET and non-HEAD methods, always send an Origin header so the
1183     // server knows we support this feature.
1184 
1185     if (origin.isEmpty()) {
1186         // If we don't know what origin header to attach, we attach the value
1187         // for an empty origin.
1188         request.setHTTPOrigin(SecurityOrigin::createUnique()->toAtomicString());
1189         return;
1190     }
1191 
1192     request.setHTTPOrigin(origin);
1193 }
1194 
receivedMainResourceError(const ResourceError & error)1195 void FrameLoader::receivedMainResourceError(const ResourceError& error)
1196 {
1197     // Retain because the stop may release the last reference to it.
1198     RefPtr<LocalFrame> protect(m_frame);
1199 
1200     if (m_frame->document()->parser())
1201         m_frame->document()->parser()->stopParsing();
1202 
1203     // FIXME: We really ought to be able to just check for isCancellation() here, but there are some
1204     // ResourceErrors that setIsCancellation() but aren't created by ResourceError::cancelledError().
1205     ResourceError c(ResourceError::cancelledError(KURL()));
1206     if ((error.errorCode() != c.errorCode() || error.domain() != c.domain()) && m_frame->owner()) {
1207         // FIXME: For now, fallback content doesn't work cross process.
1208         ASSERT(m_frame->owner()->isLocal());
1209         m_frame->deprecatedLocalOwner()->renderFallbackContent();
1210     }
1211 
1212     checkCompleted();
1213     if (m_frame->page())
1214         checkLoadComplete();
1215 }
1216 
shouldPerformFragmentNavigation(bool isFormSubmission,const String & httpMethod,FrameLoadType loadType,const KURL & url)1217 bool FrameLoader::shouldPerformFragmentNavigation(bool isFormSubmission, const String& httpMethod, FrameLoadType loadType, const KURL& url)
1218 {
1219     ASSERT(loadType != FrameLoadTypeReloadFromOrigin);
1220     // We don't do this if we are submitting a form with method other than "GET", explicitly reloading,
1221     // currently displaying a frameset, or if the URL does not have a fragment.
1222     return (!isFormSubmission || equalIgnoringCase(httpMethod, "GET"))
1223         && loadType != FrameLoadTypeReload
1224         && loadType != FrameLoadTypeSame
1225         && loadType != FrameLoadTypeBackForward
1226         && url.hasFragmentIdentifier()
1227         && equalIgnoringFragmentIdentifier(m_frame->document()->url(), url)
1228         // We don't want to just scroll if a link from within a
1229         // frameset is trying to reload the frameset into _top.
1230         && !m_frame->document()->isFrameSet();
1231 }
1232 
scrollToFragmentWithParentBoundary(const KURL & url)1233 void FrameLoader::scrollToFragmentWithParentBoundary(const KURL& url)
1234 {
1235     FrameView* view = m_frame->view();
1236     if (!view)
1237         return;
1238 
1239     // Leaking scroll position to a cross-origin ancestor would permit the so-called "framesniffing" attack.
1240     RefPtr<LocalFrame> boundaryFrame(url.hasFragmentIdentifier() ? m_frame->document()->findUnsafeParentScrollPropagationBoundary() : 0);
1241 
1242     if (boundaryFrame)
1243         boundaryFrame->view()->setSafeToPropagateScrollToParent(false);
1244 
1245     view->scrollToFragment(url);
1246 
1247     if (boundaryFrame)
1248         boundaryFrame->view()->setSafeToPropagateScrollToParent(true);
1249 }
1250 
shouldClose()1251 bool FrameLoader::shouldClose()
1252 {
1253     Page* page = m_frame->page();
1254     if (!page || !page->chrome().canRunBeforeUnloadConfirmPanel())
1255         return true;
1256 
1257     // Store all references to each subframe in advance since beforeunload's event handler may modify frame
1258     Vector<RefPtr<LocalFrame> > targetFrames;
1259     targetFrames.append(m_frame);
1260     for (Frame* child = m_frame->tree().firstChild(); child; child = child->tree().traverseNext(m_frame)) {
1261         // FIXME: There is not yet any way to dispatch events to out-of-process frames.
1262         if (child->isLocalFrame())
1263             targetFrames.append(toLocalFrame(child));
1264     }
1265 
1266     bool shouldClose = false;
1267     {
1268         NavigationDisablerForBeforeUnload navigationDisabler;
1269         size_t i;
1270 
1271         bool didAllowNavigation = false;
1272         for (i = 0; i < targetFrames.size(); i++) {
1273             if (!targetFrames[i]->tree().isDescendantOf(m_frame))
1274                 continue;
1275             if (!targetFrames[i]->document()->dispatchBeforeUnloadEvent(page->chrome(), didAllowNavigation))
1276                 break;
1277         }
1278 
1279         if (i == targetFrames.size())
1280             shouldClose = true;
1281     }
1282     return shouldClose;
1283 }
1284 
loadWithNavigationAction(const NavigationAction & action,FrameLoadType type,PassRefPtrWillBeRawPtr<FormState> formState,const SubstituteData & substituteData,ClientRedirectPolicy clientRedirect,const AtomicString & overrideEncoding)1285 void FrameLoader::loadWithNavigationAction(const NavigationAction& action, FrameLoadType type, PassRefPtrWillBeRawPtr<FormState> formState, const SubstituteData& substituteData, ClientRedirectPolicy clientRedirect, const AtomicString& overrideEncoding)
1286 {
1287     ASSERT(client()->hasWebView());
1288     if (m_frame->document()->pageDismissalEventBeingDispatched() != Document::NoDismissal)
1289         return;
1290 
1291     const ResourceRequest& request = action.resourceRequest();
1292 
1293     // The current load should replace the history item if it is the first real
1294     // load of the frame.
1295     bool replacesCurrentHistoryItem = false;
1296     if (type == FrameLoadTypeRedirectWithLockedBackForwardList
1297         || !m_stateMachine.committedFirstRealDocumentLoad()) {
1298         replacesCurrentHistoryItem = true;
1299     }
1300 
1301     m_policyDocumentLoader = client()->createDocumentLoader(m_frame, request, substituteData.isValid() ? substituteData : defaultSubstituteDataForURL(request.url()));
1302     m_policyDocumentLoader->setTriggeringAction(action);
1303     m_policyDocumentLoader->setReplacesCurrentHistoryItem(replacesCurrentHistoryItem);
1304     m_policyDocumentLoader->setIsClientRedirect(clientRedirect == ClientRedirect);
1305 
1306     Frame* parent = m_frame->tree().parent();
1307     if (parent && parent->isLocalFrame())
1308         m_policyDocumentLoader->setOverrideEncoding(toLocalFrame(parent)->loader().documentLoader()->overrideEncoding());
1309     else if (!overrideEncoding.isEmpty())
1310         m_policyDocumentLoader->setOverrideEncoding(overrideEncoding);
1311     else if (m_documentLoader)
1312         m_policyDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding());
1313 
1314     // stopAllLoaders can detach the LocalFrame, so protect it.
1315     RefPtr<LocalFrame> protect(m_frame);
1316     if ((!m_policyDocumentLoader->shouldContinueForNavigationPolicy(request) || !shouldClose()) && m_policyDocumentLoader) {
1317         m_policyDocumentLoader->detachFromFrame();
1318         m_policyDocumentLoader = nullptr;
1319         return;
1320     }
1321 
1322     if (m_provisionalDocumentLoader) {
1323         m_provisionalDocumentLoader->stopLoading();
1324         if (m_provisionalDocumentLoader)
1325             m_provisionalDocumentLoader->detachFromFrame();
1326         m_provisionalDocumentLoader = nullptr;
1327     }
1328     m_checkTimer.stop();
1329 
1330     // <rdar://problem/6250856> - In certain circumstances on pages with multiple frames, stopAllLoaders()
1331     // might detach the current FrameLoader, in which case we should bail on this newly defunct load.
1332     if (!m_frame->page() || !m_policyDocumentLoader)
1333         return;
1334 
1335     if (isLoadingMainFrame())
1336         m_frame->page()->inspectorController().resume();
1337     m_frame->navigationScheduler().cancel();
1338 
1339     m_provisionalDocumentLoader = m_policyDocumentLoader.release();
1340     m_loadType = type;
1341     m_state = FrameStateProvisional;
1342 
1343     if (formState)
1344         client()->dispatchWillSubmitForm(formState->form());
1345 
1346     m_progressTracker->progressStarted();
1347     if (m_provisionalDocumentLoader->isClientRedirect())
1348         m_provisionalDocumentLoader->appendRedirect(m_frame->document()->url());
1349     m_provisionalDocumentLoader->appendRedirect(m_provisionalDocumentLoader->request().url());
1350     client()->dispatchDidStartProvisionalLoad();
1351     ASSERT(m_provisionalDocumentLoader);
1352     m_provisionalDocumentLoader->startLoadingMainResource();
1353 }
1354 
applyUserAgent(ResourceRequest & request)1355 void FrameLoader::applyUserAgent(ResourceRequest& request)
1356 {
1357     String userAgent = this->userAgent(request.url());
1358     ASSERT(!userAgent.isNull());
1359     request.setHTTPUserAgent(AtomicString(userAgent));
1360 }
1361 
shouldInterruptLoadForXFrameOptions(const String & content,const KURL & url,unsigned long requestIdentifier)1362 bool FrameLoader::shouldInterruptLoadForXFrameOptions(const String& content, const KURL& url, unsigned long requestIdentifier)
1363 {
1364     UseCounter::count(m_frame->domWindow()->document(), UseCounter::XFrameOptions);
1365 
1366     Frame* topFrame = m_frame->tree().top();
1367     if (m_frame == topFrame)
1368         return false;
1369 
1370     XFrameOptionsDisposition disposition = parseXFrameOptionsHeader(content);
1371 
1372     switch (disposition) {
1373     case XFrameOptionsSameOrigin: {
1374         UseCounter::count(m_frame->domWindow()->document(), UseCounter::XFrameOptionsSameOrigin);
1375         RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url);
1376         // Out-of-process ancestors are always a different origin.
1377         if (!topFrame->isLocalFrame() || !origin->isSameSchemeHostPort(toLocalFrame(topFrame)->document()->securityOrigin()))
1378             return true;
1379         for (Frame* frame = m_frame->tree().parent(); frame; frame = frame->tree().parent()) {
1380             if (!frame->isLocalFrame() || !origin->isSameSchemeHostPort(toLocalFrame(frame)->document()->securityOrigin())) {
1381                 UseCounter::count(m_frame->domWindow()->document(), UseCounter::XFrameOptionsSameOriginWithBadAncestorChain);
1382                 break;
1383             }
1384         }
1385         return false;
1386     }
1387     case XFrameOptionsDeny:
1388         return true;
1389     case XFrameOptionsAllowAll:
1390         return false;
1391     case XFrameOptionsConflict:
1392         m_frame->document()->addConsoleMessageWithRequestIdentifier(JSMessageSource, ErrorMessageLevel, "Multiple 'X-Frame-Options' headers with conflicting values ('" + content + "') encountered when loading '" + url.elidedString() + "'. Falling back to 'DENY'.", requestIdentifier);
1393         return true;
1394     case XFrameOptionsInvalid:
1395         m_frame->document()->addConsoleMessageWithRequestIdentifier(JSMessageSource, ErrorMessageLevel, "Invalid 'X-Frame-Options' header encountered when loading '" + url.elidedString() + "': '" + content + "' is not a recognized directive. The header will be ignored.", requestIdentifier);
1396         return false;
1397     default:
1398         ASSERT_NOT_REACHED();
1399         return false;
1400     }
1401 }
1402 
shouldTreatURLAsSameAsCurrent(const KURL & url) const1403 bool FrameLoader::shouldTreatURLAsSameAsCurrent(const KURL& url) const
1404 {
1405     return m_currentItem && url == m_currentItem->url();
1406 }
1407 
shouldTreatURLAsSrcdocDocument(const KURL & url) const1408 bool FrameLoader::shouldTreatURLAsSrcdocDocument(const KURL& url) const
1409 {
1410     if (!equalIgnoringCase(url.string(), "about:srcdoc"))
1411         return false;
1412     HTMLFrameOwnerElement* ownerElement = m_frame->deprecatedLocalOwner();
1413     if (!isHTMLIFrameElement(ownerElement))
1414         return false;
1415     return ownerElement->fastHasAttribute(srcdocAttr);
1416 }
1417 
findFrameForNavigation(const AtomicString & name,Document * activeDocument)1418 LocalFrame* FrameLoader::findFrameForNavigation(const AtomicString& name, Document* activeDocument)
1419 {
1420     ASSERT(activeDocument);
1421     Frame* frame = m_frame->tree().find(name);
1422     if (!frame || !frame->isLocalFrame() || !activeDocument->canNavigate(toLocalFrame(*frame)))
1423         return 0;
1424     return toLocalFrame(frame);
1425 }
1426 
loadHistoryItem(HistoryItem * item,HistoryLoadType historyLoadType,ResourceRequestCachePolicy cachePolicy)1427 void FrameLoader::loadHistoryItem(HistoryItem* item, HistoryLoadType historyLoadType, ResourceRequestCachePolicy cachePolicy)
1428 {
1429     RefPtr<LocalFrame> protect(m_frame);
1430     if (m_frame->page()->defersLoading()) {
1431         m_deferredHistoryLoad = DeferredHistoryLoad(item, historyLoadType, cachePolicy);
1432         return;
1433     }
1434 
1435     m_provisionalItem = item;
1436     if (historyLoadType == HistorySameDocumentLoad) {
1437         loadInSameDocument(item->url(), item->stateObject(), FrameLoadTypeBackForward, NotClientRedirect);
1438         restoreScrollPositionAndViewState();
1439         return;
1440     }
1441     loadWithNavigationAction(NavigationAction(requestFromHistoryItem(item, cachePolicy), FrameLoadTypeBackForward), FrameLoadTypeBackForward, nullptr, SubstituteData());
1442 }
1443 
dispatchDocumentElementAvailable()1444 void FrameLoader::dispatchDocumentElementAvailable()
1445 {
1446     client()->documentElementAvailable();
1447 }
1448 
dispatchDidClearDocumentOfWindowObject()1449 void FrameLoader::dispatchDidClearDocumentOfWindowObject()
1450 {
1451     if (!m_frame->script().canExecuteScripts(NotAboutToExecuteScript))
1452         return;
1453 
1454     if (Page* page = m_frame->page())
1455         page->inspectorController().didClearDocumentOfWindowObject(m_frame);
1456     InspectorInstrumentation::didClearDocumentOfWindowObject(m_frame);
1457 
1458     // We just cleared the document, not the entire window object, but for the
1459     // embedder that's close enough.
1460     client()->dispatchDidClearWindowObjectInMainWorld();
1461 }
1462 
dispatchDidClearWindowObjectInMainWorld()1463 void FrameLoader::dispatchDidClearWindowObjectInMainWorld()
1464 {
1465     if (!m_frame->script().canExecuteScripts(NotAboutToExecuteScript))
1466         return;
1467 
1468     client()->dispatchDidClearWindowObjectInMainWorld();
1469 }
1470 
effectiveSandboxFlags() const1471 SandboxFlags FrameLoader::effectiveSandboxFlags() const
1472 {
1473     SandboxFlags flags = m_forcedSandboxFlags;
1474     // FIXME: We need a way to propagate sandbox flags to out-of-process frames.
1475     Frame* parentFrame = m_frame->tree().parent();
1476     if (parentFrame && parentFrame->isLocalFrame())
1477         flags |= toLocalFrame(parentFrame)->document()->sandboxFlags();
1478     if (FrameOwner* frameOwner = m_frame->owner())
1479         flags |= frameOwner->sandboxFlags();
1480     return flags;
1481 }
1482 
1483 } // namespace WebCore
1484