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