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