1 /*
2 * Copyright (C) 2006, 2007, 2008, 2009 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 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 * its contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include "FrameLoader.h"
33
34 #include "ApplicationCacheHost.h"
35 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
36 #include "Archive.h"
37 #include "ArchiveFactory.h"
38 #endif
39 #include "CString.h"
40 #include "Cache.h"
41 #include "CachedPage.h"
42 #include "Chrome.h"
43 #include "DOMImplementation.h"
44 #include "DOMWindow.h"
45 #include "DocLoader.h"
46 #include "Document.h"
47 #include "DocumentLoader.h"
48 #include "Editor.h"
49 #include "EditorClient.h"
50 #include "Element.h"
51 #include "Event.h"
52 #include "EventNames.h"
53 #include "FloatRect.h"
54 #include "FormState.h"
55 #include "Frame.h"
56 #include "FrameLoadRequest.h"
57 #include "FrameLoaderClient.h"
58 #include "FrameTree.h"
59 #include "FrameView.h"
60 #include "HTMLAppletElement.h"
61 #include "HTMLFormElement.h"
62 #include "HTMLFrameElement.h"
63 #include "HTMLNames.h"
64 #include "HTMLObjectElement.h"
65 #include "HTTPParsers.h"
66 #include "HistoryItem.h"
67 #include "IconDatabase.h"
68 #include "IconLoader.h"
69 #include "InspectorController.h"
70 #include "Logging.h"
71 #include "MIMETypeRegistry.h"
72 #include "MainResourceLoader.h"
73 #include "Page.h"
74 #include "PageCache.h"
75 #include "PageGroup.h"
76 #include "PlaceholderDocument.h"
77 #include "PluginData.h"
78 #include "PluginDocument.h"
79 #include "ProgressTracker.h"
80 #include "RenderPart.h"
81 #include "RenderView.h"
82 #include "RenderWidget.h"
83 #include "ResourceHandle.h"
84 #include "ResourceRequest.h"
85 #include "ScriptController.h"
86 #include "ScriptSourceCode.h"
87 #include "ScriptString.h"
88 #include "ScriptValue.h"
89 #include "SecurityOrigin.h"
90 #include "SegmentedString.h"
91 #include "Settings.h"
92 #include "TextResourceDecoder.h"
93 #include "WindowFeatures.h"
94 #include "XMLHttpRequest.h"
95 #include "XMLTokenizer.h"
96 #include "XSSAuditor.h"
97 #include <wtf/CurrentTime.h>
98 #include <wtf/StdLibExtras.h>
99
100
101 #if ENABLE(SVG)
102 #include "SVGDocument.h"
103 #include "SVGLocatable.h"
104 #include "SVGNames.h"
105 #include "SVGPreserveAspectRatio.h"
106 #include "SVGSVGElement.h"
107 #include "SVGViewElement.h"
108 #include "SVGViewSpec.h"
109 #endif
110
111 #ifdef ANDROID_INSTRUMENT
112 #include "TimeCounter.h"
113 #include "RenderArena.h"
114 #endif
115
116 #if PLATFORM(ANDROID)
117 #include "WebCoreFrameBridge.h"
118 #endif
119
120 namespace WebCore {
121
122 #if ENABLE(SVG)
123 using namespace SVGNames;
124 #endif
125 using namespace HTMLNames;
126
127 struct ScheduledRedirection {
128 enum Type { redirection, locationChange, historyNavigation, formSubmission };
129
130 const Type type;
131 const double delay;
132 const String url;
133 const String referrer;
134 const FrameLoadRequest frameRequest;
135 const RefPtr<Event> event;
136 const RefPtr<FormState> formState;
137 const int historySteps;
138 const bool lockHistory;
139 const bool lockBackForwardList;
140 const bool wasUserGesture;
141 const bool wasRefresh;
142 const bool wasDuringLoad;
143
ScheduledRedirectionWebCore::ScheduledRedirection144 ScheduledRedirection(double delay, const String& url, bool lockHistory, bool lockBackForwardList, bool wasUserGesture, bool refresh)
145 : type(redirection)
146 , delay(delay)
147 , url(url)
148 , historySteps(0)
149 , lockHistory(lockHistory)
150 , lockBackForwardList(lockBackForwardList)
151 , wasUserGesture(wasUserGesture)
152 , wasRefresh(refresh)
153 , wasDuringLoad(false)
154 {
155 ASSERT(!url.isEmpty());
156 }
157
ScheduledRedirectionWebCore::ScheduledRedirection158 ScheduledRedirection(const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool wasUserGesture, bool refresh, bool duringLoad)
159 : type(locationChange)
160 , delay(0)
161 , url(url)
162 , referrer(referrer)
163 , historySteps(0)
164 , lockHistory(lockHistory)
165 , lockBackForwardList(lockBackForwardList)
166 , wasUserGesture(wasUserGesture)
167 , wasRefresh(refresh)
168 , wasDuringLoad(duringLoad)
169 {
170 ASSERT(!url.isEmpty());
171 }
172
ScheduledRedirectionWebCore::ScheduledRedirection173 explicit ScheduledRedirection(int historyNavigationSteps)
174 : type(historyNavigation)
175 , delay(0)
176 , historySteps(historyNavigationSteps)
177 , lockHistory(false)
178 , lockBackForwardList(false)
179 , wasUserGesture(false)
180 , wasRefresh(false)
181 , wasDuringLoad(false)
182 {
183 }
184
ScheduledRedirectionWebCore::ScheduledRedirection185 ScheduledRedirection(const FrameLoadRequest& frameRequest,
186 bool lockHistory, bool lockBackForwardList, PassRefPtr<Event> event, PassRefPtr<FormState> formState,
187 bool duringLoad)
188 : type(formSubmission)
189 , delay(0)
190 , frameRequest(frameRequest)
191 , event(event)
192 , formState(formState)
193 , historySteps(0)
194 , lockHistory(lockHistory)
195 , lockBackForwardList(lockBackForwardList)
196 , wasUserGesture(false)
197 , wasRefresh(false)
198 , wasDuringLoad(duringLoad)
199 {
200 ASSERT(!frameRequest.isEmpty());
201 ASSERT(this->formState);
202 }
203 };
204
205 #if ENABLE(XHTMLMP)
206 static const char defaultAcceptHeader[] = "application/xml,application/vnd.wap.xhtml+xml,application/xhtml+xml;profile='http://www.wapforum.org/xhtml',text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
207 #else
208 static const char defaultAcceptHeader[] = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
209 #endif
210 static double storedTimeOfLastCompletedLoad;
211 static FrameLoader::LocalLoadPolicy localLoadPolicy = FrameLoader::AllowLocalLoadsForLocalOnly;
212
isBackForwardLoadType(FrameLoadType type)213 bool isBackForwardLoadType(FrameLoadType type)
214 {
215 switch (type) {
216 case FrameLoadTypeStandard:
217 case FrameLoadTypeReload:
218 case FrameLoadTypeReloadFromOrigin:
219 case FrameLoadTypeSame:
220 case FrameLoadTypeRedirectWithLockedBackForwardList:
221 case FrameLoadTypeReplace:
222 return false;
223 case FrameLoadTypeBack:
224 case FrameLoadTypeBackWMLDeckNotAccessible:
225 case FrameLoadTypeForward:
226 case FrameLoadTypeIndexedBackForward:
227 return true;
228 }
229 ASSERT_NOT_REACHED();
230 return false;
231 }
232
numRequests(Document * document)233 static int numRequests(Document* document)
234 {
235 if (!document)
236 return 0;
237
238 return document->docLoader()->requestCount();
239 }
240
canReferToParentFrameEncoding(const Frame * frame,const Frame * parentFrame)241 static inline bool canReferToParentFrameEncoding(const Frame* frame, const Frame* parentFrame)
242 {
243 return parentFrame && parentFrame->document()->securityOrigin()->canAccess(frame->document()->securityOrigin());
244 }
245
FrameLoader(Frame * frame,FrameLoaderClient * client)246 FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client)
247 : m_frame(frame)
248 , m_client(client)
249 , m_state(FrameStateCommittedPage)
250 , m_loadType(FrameLoadTypeStandard)
251 , m_policyLoadType(FrameLoadTypeStandard)
252 , m_delegateIsHandlingProvisionalLoadError(false)
253 , m_delegateIsDecidingNavigationPolicy(false)
254 , m_delegateIsHandlingUnimplementablePolicy(false)
255 , m_firstLayoutDone(false)
256 , m_quickRedirectComing(false)
257 , m_sentRedirectNotification(false)
258 , m_inStopAllLoaders(false)
259 , m_isExecutingJavaScriptFormAction(false)
260 , m_isRunningScript(false)
261 , m_didCallImplicitClose(false)
262 , m_wasUnloadEventEmitted(false)
263 , m_unloadEventBeingDispatched(false)
264 , m_isComplete(false)
265 , m_isLoadingMainResource(false)
266 , m_cancellingWithLoadInProgress(false)
267 , m_needsClear(false)
268 , m_receivedData(false)
269 , m_encodingWasChosenByUser(false)
270 , m_containsPlugIns(false)
271 , m_redirectionTimer(this, &FrameLoader::redirectionTimerFired)
272 , m_checkCompletedTimer(this, &FrameLoader::checkCompletedTimerFired)
273 , m_checkLoadCompleteTimer(this, &FrameLoader::checkLoadCompleteTimerFired)
274 , m_opener(0)
275 , m_openedByDOM(false)
276 , m_creatingInitialEmptyDocument(false)
277 , m_isDisplayingInitialEmptyDocument(false)
278 , m_committedFirstRealDocumentLoad(false)
279 , m_didPerformFirstNavigation(false)
280 #ifndef NDEBUG
281 , m_didDispatchDidCommitLoad(false)
282 #endif
283 {
284 }
285
~FrameLoader()286 FrameLoader::~FrameLoader()
287 {
288 setOpener(0);
289
290 HashSet<Frame*>::iterator end = m_openedFrames.end();
291 for (HashSet<Frame*>::iterator it = m_openedFrames.begin(); it != end; ++it)
292 (*it)->loader()->m_opener = 0;
293
294 m_client->frameLoaderDestroyed();
295 }
296
init()297 void FrameLoader::init()
298 {
299 // this somewhat odd set of steps is needed to give the frame an initial empty document
300 m_isDisplayingInitialEmptyDocument = false;
301 m_creatingInitialEmptyDocument = true;
302 setPolicyDocumentLoader(m_client->createDocumentLoader(ResourceRequest(KURL("")), SubstituteData()).get());
303 setProvisionalDocumentLoader(m_policyDocumentLoader.get());
304 setState(FrameStateProvisional);
305 m_provisionalDocumentLoader->setResponse(ResourceResponse(KURL(), "text/html", 0, String(), String()));
306 m_provisionalDocumentLoader->finishedLoading();
307 begin(KURL(), false);
308 end();
309 m_frame->document()->cancelParsing();
310 m_creatingInitialEmptyDocument = false;
311 m_didCallImplicitClose = true;
312 }
313
setDefersLoading(bool defers)314 void FrameLoader::setDefersLoading(bool defers)
315 {
316 if (m_documentLoader)
317 m_documentLoader->setDefersLoading(defers);
318 if (m_provisionalDocumentLoader)
319 m_provisionalDocumentLoader->setDefersLoading(defers);
320 if (m_policyDocumentLoader)
321 m_policyDocumentLoader->setDefersLoading(defers);
322 }
323
createWindow(FrameLoader * frameLoaderForFrameLookup,const FrameLoadRequest & request,const WindowFeatures & features,bool & created)324 Frame* FrameLoader::createWindow(FrameLoader* frameLoaderForFrameLookup, const FrameLoadRequest& request, const WindowFeatures& features, bool& created)
325 {
326 ASSERT(!features.dialog || request.frameName().isEmpty());
327
328 if (!request.frameName().isEmpty() && request.frameName() != "_blank") {
329 Frame* frame = frameLoaderForFrameLookup->frame()->tree()->find(request.frameName());
330 if (frame && shouldAllowNavigation(frame)) {
331 if (!request.resourceRequest().url().isEmpty())
332 frame->loader()->loadFrameRequest(request, false, false, 0, 0);
333 if (Page* page = frame->page())
334 page->chrome()->focus();
335 created = false;
336 return frame;
337 }
338 }
339
340 // FIXME: Setting the referrer should be the caller's responsibility.
341 FrameLoadRequest requestWithReferrer = request;
342 requestWithReferrer.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
343 addHTTPOriginIfNeeded(requestWithReferrer.resourceRequest(), outgoingOrigin());
344
345 Page* oldPage = m_frame->page();
346 if (!oldPage)
347 return 0;
348
349 Page* page = oldPage->chrome()->createWindow(m_frame, requestWithReferrer, features);
350 if (!page)
351 return 0;
352
353 Frame* frame = page->mainFrame();
354 if (request.frameName() != "_blank")
355 frame->tree()->setName(request.frameName());
356
357 page->chrome()->setToolbarsVisible(features.toolBarVisible || features.locationBarVisible);
358 page->chrome()->setStatusbarVisible(features.statusBarVisible);
359 page->chrome()->setScrollbarsVisible(features.scrollbarsVisible);
360 page->chrome()->setMenubarVisible(features.menuBarVisible);
361 page->chrome()->setResizable(features.resizable);
362
363 // 'x' and 'y' specify the location of the window, while 'width' and 'height'
364 // specify the size of the page. We can only resize the window, so
365 // adjust for the difference between the window size and the page size.
366
367 FloatRect windowRect = page->chrome()->windowRect();
368 FloatSize pageSize = page->chrome()->pageRect().size();
369 if (features.xSet)
370 windowRect.setX(features.x);
371 if (features.ySet)
372 windowRect.setY(features.y);
373 if (features.widthSet)
374 windowRect.setWidth(features.width + (windowRect.width() - pageSize.width()));
375 if (features.heightSet)
376 windowRect.setHeight(features.height + (windowRect.height() - pageSize.height()));
377 page->chrome()->setWindowRect(windowRect);
378
379 page->chrome()->show();
380
381 created = true;
382 return frame;
383 }
384
canHandleRequest(const ResourceRequest & request)385 bool FrameLoader::canHandleRequest(const ResourceRequest& request)
386 {
387 return m_client->canHandleRequest(request);
388 }
389
changeLocation(const KURL & url,const String & referrer,bool lockHistory,bool lockBackForwardList,bool userGesture,bool refresh)390 void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool userGesture, bool refresh)
391 {
392 RefPtr<Frame> protect(m_frame);
393
394 ResourceRequest request(url, referrer, refresh ? ReloadIgnoringCacheData : UseProtocolCachePolicy);
395 #ifdef ANDROID_USER_GESTURE
396 request.setUserGesture(userGesture);
397 #endif
398
399 if (executeIfJavaScriptURL(request.url(), userGesture))
400 return;
401
402 urlSelected(request, "_self", 0, lockHistory, lockBackForwardList, userGesture);
403 }
404
urlSelected(const ResourceRequest & request,const String & passedTarget,PassRefPtr<Event> triggeringEvent,bool lockHistory,bool lockBackForwardList,bool userGesture)405 void FrameLoader::urlSelected(const ResourceRequest& request, const String& passedTarget, PassRefPtr<Event> triggeringEvent, bool lockHistory, bool lockBackForwardList, bool userGesture)
406 {
407 if (executeIfJavaScriptURL(request.url(), userGesture, false))
408 return;
409
410 String target = passedTarget;
411 if (target.isEmpty())
412 target = m_frame->document()->baseTarget();
413
414 FrameLoadRequest frameRequest(request, target);
415
416 if (frameRequest.resourceRequest().httpReferrer().isEmpty())
417 frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
418 addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin());
419
420 loadFrameRequest(frameRequest, lockHistory, lockBackForwardList, triggeringEvent, 0);
421 }
422
requestFrame(HTMLFrameOwnerElement * ownerElement,const String & urlString,const AtomicString & frameName)423 bool FrameLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName)
424 {
425 // Support for <frame src="javascript:string">
426 KURL scriptURL;
427 KURL url;
428 if (protocolIsJavaScript(urlString)) {
429 scriptURL = completeURL(urlString); // completeURL() encodes the URL.
430 url = blankURL();
431 } else
432 url = completeURL(urlString);
433
434 Frame* frame = ownerElement->contentFrame();
435 if (frame)
436 frame->loader()->scheduleLocationChange(url.string(), m_outgoingReferrer, true, true, isProcessingUserGesture());
437 else
438 frame = loadSubframe(ownerElement, url, frameName, m_outgoingReferrer);
439
440 if (!frame)
441 return false;
442
443 if (!scriptURL.isEmpty())
444 frame->loader()->executeIfJavaScriptURL(scriptURL);
445
446 return true;
447 }
448
loadSubframe(HTMLFrameOwnerElement * ownerElement,const KURL & url,const String & name,const String & referrer)449 Frame* FrameLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer)
450 {
451 bool allowsScrolling = true;
452 int marginWidth = -1;
453 int marginHeight = -1;
454 if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) {
455 HTMLFrameElementBase* o = static_cast<HTMLFrameElementBase*>(ownerElement);
456 allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff;
457 marginWidth = o->getMarginWidth();
458 marginHeight = o->getMarginHeight();
459 }
460
461 if (!canLoad(url, referrer)) {
462 FrameLoader::reportLocalLoadFailed(m_frame, url.string());
463 return 0;
464 }
465
466 bool hideReferrer = shouldHideReferrer(url, referrer);
467 RefPtr<Frame> frame = m_client->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer, allowsScrolling, marginWidth, marginHeight);
468
469 if (!frame) {
470 checkCallImplicitClose();
471 return 0;
472 }
473
474 frame->loader()->m_isComplete = false;
475
476 RenderObject* renderer = ownerElement->renderer();
477 FrameView* view = frame->view();
478 if (renderer && renderer->isWidget() && view)
479 toRenderWidget(renderer)->setWidget(view);
480
481 checkCallImplicitClose();
482
483 // In these cases, the synchronous load would have finished
484 // before we could connect the signals, so make sure to send the
485 // completed() signal for the child by hand
486 // FIXME: In this case the Frame will have finished loading before
487 // it's being added to the child list. It would be a good idea to
488 // create the child first, then invoke the loader separately.
489 if (url.isEmpty() || url == blankURL()) {
490 frame->loader()->completed();
491 frame->loader()->checkCompleted();
492 }
493
494 return frame.get();
495 }
496
submitForm(const char * action,const String & url,PassRefPtr<FormData> formData,const String & target,const String & contentType,const String & boundary,bool lockHistory,PassRefPtr<Event> event,PassRefPtr<FormState> formState)497 void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<FormData> formData,
498 const String& target, const String& contentType, const String& boundary,
499 bool lockHistory, PassRefPtr<Event> event, PassRefPtr<FormState> formState)
500 {
501 ASSERT(action);
502 ASSERT(strcmp(action, "GET") == 0 || strcmp(action, "POST") == 0);
503 ASSERT(formData);
504 ASSERT(formState);
505 ASSERT(formState->sourceFrame() == m_frame);
506
507 if (!m_frame->page())
508 return;
509
510 KURL u = completeURL(url.isNull() ? "" : url);
511 if (u.isEmpty())
512 return;
513
514 if (protocolIsJavaScript(u)) {
515 m_isExecutingJavaScriptFormAction = true;
516 executeIfJavaScriptURL(u, false, false);
517 m_isExecutingJavaScriptFormAction = false;
518 return;
519 }
520
521 FrameLoadRequest frameRequest;
522 #ifdef ANDROID_USER_GESTURE
523 frameRequest.resourceRequest().setUserGesture(isProcessingUserGesture());
524 #endif
525
526 String targetOrBaseTarget = target.isEmpty() ? m_frame->document()->baseTarget() : target;
527 Frame* targetFrame = findFrameForNavigation(targetOrBaseTarget);
528 if (!targetFrame) {
529 targetFrame = m_frame;
530 frameRequest.setFrameName(targetOrBaseTarget);
531 }
532 if (!targetFrame->page())
533 return;
534
535 // FIXME: We'd like to remove this altogether and fix the multiple form submission issue another way.
536
537 // We do not want to submit more than one form from the same page, nor do we want to submit a single
538 // form more than once. This flag prevents these from happening; not sure how other browsers prevent this.
539 // The flag is reset in each time we start handle a new mouse or key down event, and
540 // also in setView since this part may get reused for a page from the back/forward cache.
541 // The form multi-submit logic here is only needed when we are submitting a form that affects this frame.
542
543 // FIXME: Frame targeting is only one of the ways the submission could end up doing something other
544 // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly
545 // needed any more now that we reset m_submittedFormURL on each mouse or key down event.
546
547 if (m_frame->tree()->isDescendantOf(targetFrame)) {
548 if (m_submittedFormURL == u)
549 return;
550 m_submittedFormURL = u;
551 }
552
553 formData->generateFiles(m_frame->page()->chrome()->client());
554
555 if (!m_outgoingReferrer.isEmpty())
556 frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
557
558 if (strcmp(action, "GET") == 0)
559 u.setQuery(formData->flattenToString());
560 else {
561 frameRequest.resourceRequest().setHTTPMethod("POST");
562 frameRequest.resourceRequest().setHTTPBody(formData);
563
564 // construct some user headers if necessary
565 if (contentType.isNull() || contentType == "application/x-www-form-urlencoded")
566 frameRequest.resourceRequest().setHTTPContentType(contentType);
567 else // contentType must be "multipart/form-data"
568 frameRequest.resourceRequest().setHTTPContentType(contentType + "; boundary=" + boundary);
569 }
570
571 frameRequest.resourceRequest().setURL(u);
572 addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin());
573
574 targetFrame->loader()->scheduleFormSubmission(frameRequest, lockHistory, event, formState);
575 }
576
stopLoading(bool sendUnload,DatabasePolicy databasePolicy)577 void FrameLoader::stopLoading(bool sendUnload, DatabasePolicy databasePolicy)
578 {
579 if (m_frame->document() && m_frame->document()->tokenizer())
580 m_frame->document()->tokenizer()->stopParsing();
581
582 if (sendUnload) {
583 if (m_frame->document()) {
584 if (m_didCallImplicitClose && !m_wasUnloadEventEmitted) {
585 Node* currentFocusedNode = m_frame->document()->focusedNode();
586 if (currentFocusedNode)
587 currentFocusedNode->aboutToUnload();
588 m_unloadEventBeingDispatched = true;
589 if (m_frame->domWindow())
590 m_frame->domWindow()->dispatchUnloadEvent();
591 m_unloadEventBeingDispatched = false;
592 if (m_frame->document())
593 m_frame->document()->updateStyleIfNeeded();
594 m_wasUnloadEventEmitted = true;
595 }
596 }
597
598 // Dispatching the unload event could have made m_frame->document() null.
599 if (m_frame->document() && !m_frame->document()->inPageCache())
600 m_frame->document()->removeAllEventListeners();
601 }
602
603 m_isComplete = true; // to avoid calling completed() in finishedParsing() (David)
604 m_isLoadingMainResource = false;
605 m_didCallImplicitClose = true; // don't want that one either
606
607 if (m_frame->document() && m_frame->document()->parsing()) {
608 finishedParsing();
609 m_frame->document()->setParsing(false);
610 }
611
612 m_workingURL = KURL();
613
614 if (Document* doc = m_frame->document()) {
615 if (DocLoader* docLoader = doc->docLoader())
616 cache()->loader()->cancelRequests(docLoader);
617
618 #if ENABLE(DATABASE)
619 if (databasePolicy == DatabasePolicyStop)
620 doc->stopDatabases();
621 #endif
622 }
623
624 // tell all subframes to stop as well
625 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
626 child->loader()->stopLoading(sendUnload);
627
628 cancelRedirection();
629 }
630
stop()631 void FrameLoader::stop()
632 {
633 // http://bugs.webkit.org/show_bug.cgi?id=10854
634 // The frame's last ref may be removed and it will be deleted by checkCompleted().
635 RefPtr<Frame> protector(m_frame);
636
637 if (m_frame->document()->tokenizer())
638 m_frame->document()->tokenizer()->stopParsing();
639 m_frame->document()->finishParsing();
640
641 if (m_iconLoader)
642 m_iconLoader->stopLoading();
643 }
644
closeURL()645 bool FrameLoader::closeURL()
646 {
647 saveDocumentState();
648 stopLoading(true);
649 m_frame->editor()->clearUndoRedoOperations();
650 return true;
651 }
652
cancelRedirection(bool cancelWithLoadInProgress)653 void FrameLoader::cancelRedirection(bool cancelWithLoadInProgress)
654 {
655 m_cancellingWithLoadInProgress = cancelWithLoadInProgress;
656
657 stopRedirectionTimer();
658
659 m_scheduledRedirection.clear();
660 }
661
iconURL()662 KURL FrameLoader::iconURL()
663 {
664 // If this isn't a top level frame, return nothing
665 if (m_frame->tree() && m_frame->tree()->parent())
666 return KURL();
667
668 // If we have an iconURL from a Link element, return that
669 if (!m_frame->document()->iconURL().isEmpty())
670 return KURL(m_frame->document()->iconURL());
671
672 // Don't return a favicon iconURL unless we're http or https
673 if (!m_URL.protocolInHTTPFamily())
674 return KURL();
675
676 KURL url;
677 url.setProtocol(m_URL.protocol());
678 url.setHost(m_URL.host());
679 if (int port = m_URL.port())
680 url.setPort(port);
681 url.setPath("/favicon.ico");
682 return url;
683 }
684
didOpenURL(const KURL & url)685 bool FrameLoader::didOpenURL(const KURL& url)
686 {
687 if (m_scheduledRedirection && m_scheduledRedirection->wasDuringLoad) {
688 // A redirect was scheduled before the document was created.
689 // This can happen when one frame changes another frame's location.
690 return false;
691 }
692
693 cancelRedirection();
694 m_frame->editor()->clearLastEditCommand();
695 closeURL();
696
697 m_isComplete = false;
698 m_isLoadingMainResource = true;
699 m_didCallImplicitClose = false;
700
701 // If we are still in the process of initializing an empty document then
702 // its frame is not in a consistent state for rendering, so avoid setJSStatusBarText
703 // since it may cause clients to attempt to render the frame.
704 if (!m_creatingInitialEmptyDocument) {
705 m_frame->setJSStatusBarText(String());
706 m_frame->setJSDefaultStatusBarText(String());
707 }
708 m_URL = url;
709 if (m_URL.protocolInHTTPFamily() && !m_URL.host().isEmpty() && m_URL.path().isEmpty())
710 m_URL.setPath("/");
711 m_workingURL = m_URL;
712
713 started();
714
715 return true;
716 }
717
didExplicitOpen()718 void FrameLoader::didExplicitOpen()
719 {
720 m_isComplete = false;
721 m_didCallImplicitClose = false;
722
723 // Calling document.open counts as committing the first real document load.
724 m_committedFirstRealDocumentLoad = true;
725
726 // Prevent window.open(url) -- eg window.open("about:blank") -- from blowing away results
727 // from a subsequent window.document.open / window.document.write call.
728 // Cancelling redirection here works for all cases because document.open
729 // implicitly precedes document.write.
730 cancelRedirection();
731 if (m_frame->document()->url() != blankURL())
732 m_URL = m_frame->document()->url();
733 }
734
executeIfJavaScriptURL(const KURL & url,bool userGesture,bool replaceDocument)735 bool FrameLoader::executeIfJavaScriptURL(const KURL& url, bool userGesture, bool replaceDocument)
736 {
737 if (!protocolIsJavaScript(url))
738 return false;
739
740 if (m_frame->page() && !m_frame->page()->javaScriptURLsAreAllowed())
741 return true;
742
743 const int javascriptSchemeLength = sizeof("javascript:") - 1;
744
745 String script = decodeURLEscapeSequences(url.string().substring(javascriptSchemeLength));
746 ScriptValue result = executeScript(script, userGesture);
747
748 String scriptResult;
749 if (!result.getString(scriptResult))
750 return true;
751
752 SecurityOrigin* currentSecurityOrigin = m_frame->document()->securityOrigin();
753
754 // FIXME: We should always replace the document, but doing so
755 // synchronously can cause crashes:
756 // http://bugs.webkit.org/show_bug.cgi?id=16782
757 if (replaceDocument) {
758 stopAllLoaders();
759 begin(m_URL, true, currentSecurityOrigin);
760 write(scriptResult);
761 end();
762 }
763
764 return true;
765 }
766
executeScript(const String & script,bool forceUserGesture)767 ScriptValue FrameLoader::executeScript(const String& script, bool forceUserGesture)
768 {
769 return executeScript(ScriptSourceCode(script, forceUserGesture ? KURL() : m_URL));
770 }
771
executeScript(const ScriptSourceCode & sourceCode)772 ScriptValue FrameLoader::executeScript(const ScriptSourceCode& sourceCode)
773 {
774 if (!m_frame->script()->isEnabled() || m_frame->script()->isPaused())
775 return ScriptValue();
776
777 bool wasRunningScript = m_isRunningScript;
778 m_isRunningScript = true;
779
780 ScriptValue result = m_frame->script()->evaluate(sourceCode);
781
782 if (!wasRunningScript) {
783 m_isRunningScript = false;
784 Document::updateStyleForAllDocuments();
785 }
786
787 return result;
788 }
789
cancelAndClear()790 void FrameLoader::cancelAndClear()
791 {
792 cancelRedirection();
793
794 if (!m_isComplete)
795 closeURL();
796
797 clear(false);
798 m_frame->script()->updatePlatformScriptObjects();
799 }
800
clear(bool clearWindowProperties,bool clearScriptObjects)801 void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects)
802 {
803 m_frame->editor()->clear();
804
805 if (!m_needsClear)
806 return;
807 m_needsClear = false;
808
809 if (!m_frame->document()->inPageCache()) {
810 m_frame->document()->cancelParsing();
811 m_frame->document()->stopActiveDOMObjects();
812 if (m_frame->document()->attached()) {
813 m_frame->document()->willRemove();
814 m_frame->document()->detach();
815
816 m_frame->document()->removeFocusedNodeOfSubtree(m_frame->document());
817 }
818 }
819
820 // Do this after detaching the document so that the unload event works.
821 if (clearWindowProperties) {
822 m_frame->clearDOMWindow();
823 m_frame->script()->clearWindowShell();
824 }
825
826 m_frame->selection()->clear();
827 m_frame->eventHandler()->clear();
828 if (m_frame->view())
829 m_frame->view()->clear();
830
831 m_frame->setSelectionGranularity(CharacterGranularity);
832
833 // Do not drop the document before the ScriptController and view are cleared
834 // as some destructors might still try to access the document.
835 m_frame->setDocument(0);
836 m_decoder = 0;
837
838 m_containsPlugIns = false;
839
840 if (clearScriptObjects)
841 m_frame->script()->clearScriptObjects();
842
843 m_redirectionTimer.stop();
844 m_scheduledRedirection.clear();
845
846 m_checkCompletedTimer.stop();
847 m_checkLoadCompleteTimer.stop();
848
849 m_receivedData = false;
850 m_isDisplayingInitialEmptyDocument = false;
851
852 if (!m_encodingWasChosenByUser)
853 m_encoding = String();
854 }
855
receivedFirstData()856 void FrameLoader::receivedFirstData()
857 {
858 begin(m_workingURL, false);
859
860 dispatchDidCommitLoad();
861 dispatchWindowObjectAvailable();
862
863 if (m_documentLoader) {
864 String ptitle = m_documentLoader->title();
865 // If we have a title let the WebView know about it.
866 if (!ptitle.isNull())
867 m_client->dispatchDidReceiveTitle(ptitle);
868 }
869
870 m_workingURL = KURL();
871
872 double delay;
873 String url;
874 if (!m_documentLoader)
875 return;
876 if (!parseHTTPRefresh(m_documentLoader->response().httpHeaderField("Refresh"), false, delay, url))
877 return;
878
879 if (url.isEmpty())
880 url = m_URL.string();
881 else
882 url = m_frame->document()->completeURL(url).string();
883
884 scheduleHTTPRedirection(delay, url);
885 }
886
responseMIMEType() const887 const String& FrameLoader::responseMIMEType() const
888 {
889 return m_responseMIMEType;
890 }
891
setResponseMIMEType(const String & type)892 void FrameLoader::setResponseMIMEType(const String& type)
893 {
894 m_responseMIMEType = type;
895 }
896
begin()897 void FrameLoader::begin()
898 {
899 begin(KURL());
900 }
901
begin(const KURL & url,bool dispatch,SecurityOrigin * origin)902 void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin)
903 {
904 // We need to take a reference to the security origin because |clear|
905 // might destroy the document that owns it.
906 RefPtr<SecurityOrigin> forcedSecurityOrigin = origin;
907
908 RefPtr<Document> document;
909
910 // Create a new document before clearing the frame, because it may need to inherit an aliased security context.
911 if (!m_isDisplayingInitialEmptyDocument && m_client->shouldUsePluginDocument(m_responseMIMEType))
912 document = PluginDocument::create(m_frame);
913 else if (!m_client->hasHTMLView())
914 document = PlaceholderDocument::create(m_frame);
915 else
916 document = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode());
917
918 bool resetScripting = !(m_isDisplayingInitialEmptyDocument && m_frame->document()->securityOrigin()->isSecureTransitionTo(url));
919 clear(resetScripting, resetScripting);
920 if (resetScripting)
921 m_frame->script()->updatePlatformScriptObjects();
922
923 m_needsClear = true;
924 m_isComplete = false;
925 m_didCallImplicitClose = false;
926 m_isLoadingMainResource = true;
927 m_isDisplayingInitialEmptyDocument = m_creatingInitialEmptyDocument;
928
929 KURL ref(url);
930 ref.setUser(String());
931 ref.setPass(String());
932 ref.removeFragmentIdentifier();
933 m_outgoingReferrer = ref.string();
934 m_URL = url;
935
936 m_frame->setDocument(document);
937
938 if (dispatch)
939 dispatchWindowObjectAvailable();
940
941 document->setURL(m_URL);
942 if (m_decoder)
943 document->setDecoder(m_decoder.get());
944 if (forcedSecurityOrigin)
945 document->setSecurityOrigin(forcedSecurityOrigin.get());
946
947 m_frame->domWindow()->setURL(document->url());
948 m_frame->domWindow()->setSecurityOrigin(document->securityOrigin());
949
950 updateFirstPartyForCookies();
951
952 Settings* settings = document->settings();
953 document->docLoader()->setAutoLoadImages(settings && settings->loadsImagesAutomatically());
954 #ifdef ANDROID_BLOCK_NETWORK_IMAGE
955 document->docLoader()->setBlockNetworkImage(settings && settings->blockNetworkImage());
956 #endif
957
958 if (m_documentLoader) {
959 String dnsPrefetchControl = m_documentLoader->response().httpHeaderField("X-DNS-Prefetch-Control");
960 if (!dnsPrefetchControl.isEmpty())
961 document->parseDNSPrefetchControlHeader(dnsPrefetchControl);
962 }
963
964 #if FRAME_LOADS_USER_STYLESHEET
965 KURL userStyleSheet = settings ? settings->userStyleSheetLocation() : KURL();
966 if (!userStyleSheet.isEmpty())
967 m_frame->setUserStyleSheetLocation(userStyleSheet);
968 #endif
969
970 restoreDocumentState();
971
972 document->implicitOpen();
973
974 if (m_frame->view() && m_client->hasHTMLView())
975 m_frame->view()->setContentsSize(IntSize());
976 }
977
write(const char * str,int len,bool flush)978 void FrameLoader::write(const char* str, int len, bool flush)
979 {
980 if (len == 0 && !flush)
981 return;
982
983 if (len == -1)
984 len = strlen(str);
985
986 Tokenizer* tokenizer = m_frame->document()->tokenizer();
987 if (tokenizer && tokenizer->wantsRawData()) {
988 if (len > 0)
989 tokenizer->writeRawData(str, len);
990 return;
991 }
992
993 if (!m_decoder) {
994 if (Settings* settings = m_frame->settings()) {
995 m_decoder = TextResourceDecoder::create(m_responseMIMEType,
996 settings->defaultTextEncodingName(),
997 settings->usesEncodingDetector());
998 Frame* parentFrame = m_frame->tree()->parent();
999 // Set the hint encoding to the parent frame encoding only if
1000 // the parent and the current frames share the security origin.
1001 // We impose this condition because somebody can make a child frame
1002 // containing a carefully crafted html/javascript in one encoding
1003 // that can be mistaken for hintEncoding (or related encoding) by
1004 // an auto detector. When interpreted in the latter, it could be
1005 // an attack vector.
1006 // FIXME: This might be too cautious for non-7bit-encodings and
1007 // we may consider relaxing this later after testing.
1008 if (canReferToParentFrameEncoding(m_frame, parentFrame))
1009 m_decoder->setHintEncoding(parentFrame->document()->decoder());
1010 } else
1011 m_decoder = TextResourceDecoder::create(m_responseMIMEType, String());
1012 Frame* parentFrame = m_frame->tree()->parent();
1013 if (m_encoding.isEmpty()) {
1014 if (canReferToParentFrameEncoding(m_frame, parentFrame))
1015 m_decoder->setEncoding(parentFrame->document()->inputEncoding(), TextResourceDecoder::EncodingFromParentFrame);
1016 } else {
1017 m_decoder->setEncoding(m_encoding,
1018 m_encodingWasChosenByUser ? TextResourceDecoder::UserChosenEncoding : TextResourceDecoder::EncodingFromHTTPHeader);
1019 }
1020 m_frame->document()->setDecoder(m_decoder.get());
1021 }
1022
1023 String decoded = m_decoder->decode(str, len);
1024 if (flush)
1025 decoded += m_decoder->flush();
1026 if (decoded.isEmpty())
1027 return;
1028
1029 if (!m_receivedData) {
1030 m_receivedData = true;
1031 if (m_decoder->encoding().usesVisualOrdering())
1032 m_frame->document()->setVisuallyOrdered();
1033 m_frame->document()->recalcStyle(Node::Force);
1034 }
1035
1036 if (tokenizer) {
1037 ASSERT(!tokenizer->wantsRawData());
1038 tokenizer->write(decoded, true);
1039 }
1040 }
1041
write(const String & str)1042 void FrameLoader::write(const String& str)
1043 {
1044 if (str.isNull())
1045 return;
1046
1047 if (!m_receivedData) {
1048 m_receivedData = true;
1049 m_frame->document()->setParseMode(Document::Strict);
1050 }
1051
1052 if (Tokenizer* tokenizer = m_frame->document()->tokenizer())
1053 tokenizer->write(str, true);
1054 }
1055
end()1056 void FrameLoader::end()
1057 {
1058 m_isLoadingMainResource = false;
1059 endIfNotLoadingMainResource();
1060 }
1061
endIfNotLoadingMainResource()1062 void FrameLoader::endIfNotLoadingMainResource()
1063 {
1064 if (m_isLoadingMainResource || !m_frame->page() || !m_frame->document())
1065 return;
1066
1067 // http://bugs.webkit.org/show_bug.cgi?id=10854
1068 // The frame's last ref may be removed and it can be deleted by checkCompleted(),
1069 // so we'll add a protective refcount
1070 RefPtr<Frame> protector(m_frame);
1071
1072 // make sure nothing's left in there
1073 write(0, 0, true);
1074 m_frame->document()->finishParsing();
1075 }
1076
iconLoadDecisionAvailable()1077 void FrameLoader::iconLoadDecisionAvailable()
1078 {
1079 if (!m_mayLoadIconLater)
1080 return;
1081 LOG(IconDatabase, "FrameLoader %p was told a load decision is available for its icon", this);
1082 startIconLoader();
1083 m_mayLoadIconLater = false;
1084 }
1085
startIconLoader()1086 void FrameLoader::startIconLoader()
1087 {
1088 // FIXME: We kick off the icon loader when the frame is done receiving its main resource.
1089 // But we should instead do it when we're done parsing the head element.
1090 if (!isLoadingMainFrame())
1091 return;
1092
1093 if (!iconDatabase() || !iconDatabase()->isEnabled())
1094 return;
1095
1096 KURL url(iconURL());
1097 String urlString(url.string());
1098 if (urlString.isEmpty())
1099 return;
1100
1101 // If we're not reloading and the icon database doesn't say to load now then bail before we actually start the load
1102 if (loadType() != FrameLoadTypeReload && loadType() != FrameLoadTypeReloadFromOrigin) {
1103 IconLoadDecision decision = iconDatabase()->loadDecisionForIconURL(urlString, m_documentLoader.get());
1104 if (decision == IconLoadNo) {
1105 LOG(IconDatabase, "FrameLoader::startIconLoader() - Told not to load this icon, committing iconURL %s to database for pageURL mapping", urlString.ascii().data());
1106 commitIconURLToIconDatabase(url);
1107
1108 // We were told not to load this icon - that means this icon is already known by the database
1109 // If the icon data hasn't been read in from disk yet, kick off the read of the icon from the database to make sure someone
1110 // has done it. This is after registering for the notification so the WebView can call the appropriate delegate method.
1111 // Otherwise if the icon data *is* available, notify the delegate
1112 if (!iconDatabase()->iconDataKnownForIconURL(urlString)) {
1113 LOG(IconDatabase, "Told not to load icon %s but icon data is not yet available - registering for notification and requesting load from disk", urlString.ascii().data());
1114 m_client->registerForIconNotification();
1115 iconDatabase()->iconForPageURL(m_URL.string(), IntSize(0, 0));
1116 iconDatabase()->iconForPageURL(originalRequestURL().string(), IntSize(0, 0));
1117 } else
1118 m_client->dispatchDidReceiveIcon();
1119
1120 return;
1121 }
1122
1123 if (decision == IconLoadUnknown) {
1124 // In this case, we may end up loading the icon later, but we still want to commit the icon url mapping to the database
1125 // just in case we don't end up loading later - if we commit the mapping a second time after the load, that's no big deal
1126 // We also tell the client to register for the notification that the icon is received now so it isn't missed in case the
1127 // icon is later read in from disk
1128 LOG(IconDatabase, "FrameLoader %p might load icon %s later", this, urlString.ascii().data());
1129 m_mayLoadIconLater = true;
1130 m_client->registerForIconNotification();
1131 commitIconURLToIconDatabase(url);
1132 return;
1133 }
1134 }
1135
1136 // People who want to avoid loading images generally want to avoid loading all images.
1137 // Now that we've accounted for URL mapping, avoid starting the network load if images aren't set to display automatically.
1138 Settings* settings = m_frame->settings();
1139 if (settings && !settings->loadsImagesAutomatically())
1140 return;
1141
1142 // This is either a reload or the icon database said "yes, load the icon", so kick off the load!
1143 if (!m_iconLoader)
1144 m_iconLoader.set(IconLoader::create(m_frame).release());
1145
1146 m_iconLoader->startLoading();
1147 }
1148
setLocalLoadPolicy(LocalLoadPolicy policy)1149 void FrameLoader::setLocalLoadPolicy(LocalLoadPolicy policy)
1150 {
1151 localLoadPolicy = policy;
1152 }
1153
restrictAccessToLocal()1154 bool FrameLoader::restrictAccessToLocal()
1155 {
1156 return localLoadPolicy != FrameLoader::AllowLocalLoadsForAll;
1157 }
1158
allowSubstituteDataAccessToLocal()1159 bool FrameLoader::allowSubstituteDataAccessToLocal()
1160 {
1161 return localLoadPolicy != FrameLoader::AllowLocalLoadsForLocalOnly;
1162 }
1163
commitIconURLToIconDatabase(const KURL & icon)1164 void FrameLoader::commitIconURLToIconDatabase(const KURL& icon)
1165 {
1166 ASSERT(iconDatabase());
1167 LOG(IconDatabase, "Committing iconURL %s to database for pageURLs %s and %s", icon.string().ascii().data(), m_URL.string().ascii().data(), originalRequestURL().string().ascii().data());
1168 iconDatabase()->setIconURLForPageURL(icon.string(), m_URL.string());
1169 iconDatabase()->setIconURLForPageURL(icon.string(), originalRequestURL().string());
1170 }
1171
restoreDocumentState()1172 void FrameLoader::restoreDocumentState()
1173 {
1174 Document* doc = m_frame->document();
1175
1176 HistoryItem* itemToRestore = 0;
1177
1178 switch (loadType()) {
1179 case FrameLoadTypeReload:
1180 case FrameLoadTypeReloadFromOrigin:
1181 case FrameLoadTypeSame:
1182 case FrameLoadTypeReplace:
1183 break;
1184 case FrameLoadTypeBack:
1185 case FrameLoadTypeBackWMLDeckNotAccessible:
1186 case FrameLoadTypeForward:
1187 case FrameLoadTypeIndexedBackForward:
1188 case FrameLoadTypeRedirectWithLockedBackForwardList:
1189 case FrameLoadTypeStandard:
1190 itemToRestore = m_currentHistoryItem.get();
1191 }
1192
1193 if (!itemToRestore)
1194 return;
1195
1196 LOG(Loading, "WebCoreLoading %s: restoring form state from %p", m_frame->tree()->name().string().utf8().data(), itemToRestore);
1197 doc->setStateForNewFormElements(itemToRestore->documentState());
1198 }
1199
gotoAnchor()1200 void FrameLoader::gotoAnchor()
1201 {
1202 // If our URL has no ref, then we have no place we need to jump to.
1203 // OTOH If CSS target was set previously, we want to set it to 0, recalc
1204 // and possibly repaint because :target pseudo class may have been
1205 // set (see bug 11321).
1206 if (!m_URL.hasFragmentIdentifier() && !m_frame->document()->cssTarget())
1207 return;
1208
1209 String fragmentIdentifier = m_URL.fragmentIdentifier();
1210 if (gotoAnchor(fragmentIdentifier))
1211 return;
1212
1213 // Try again after decoding the ref, based on the document's encoding.
1214 if (m_decoder)
1215 gotoAnchor(decodeURLEscapeSequences(fragmentIdentifier, m_decoder->encoding()));
1216 }
1217
finishedParsing()1218 void FrameLoader::finishedParsing()
1219 {
1220 if (m_creatingInitialEmptyDocument)
1221 return;
1222
1223 // This can be called from the Frame's destructor, in which case we shouldn't protect ourselves
1224 // because doing so will cause us to re-enter the destructor when protector goes out of scope.
1225 // Null-checking the FrameView indicates whether or not we're in the destructor.
1226 RefPtr<Frame> protector = m_frame->view() ? m_frame : 0;
1227
1228 m_client->dispatchDidFinishDocumentLoad();
1229
1230 checkCompleted();
1231
1232 if (!m_frame->view())
1233 return; // We are being destroyed by something checkCompleted called.
1234
1235 // Check if the scrollbars are really needed for the content.
1236 // If not, remove them, relayout, and repaint.
1237 m_frame->view()->restoreScrollbar();
1238
1239 gotoAnchor();
1240 }
1241
loadDone()1242 void FrameLoader::loadDone()
1243 {
1244 checkCompleted();
1245 }
1246
checkCompleted()1247 void FrameLoader::checkCompleted()
1248 {
1249 if (m_frame->view())
1250 m_frame->view()->checkStopDelayingDeferredRepaints();
1251
1252 // Any frame that hasn't completed yet?
1253 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
1254 if (!child->loader()->m_isComplete)
1255 return;
1256
1257 // Have we completed before?
1258 if (m_isComplete)
1259 return;
1260
1261 // Are we still parsing?
1262 if (m_frame->document()->parsing())
1263 return;
1264
1265 // Still waiting for images/scripts?
1266 if (numRequests(m_frame->document()))
1267 return;
1268
1269 // OK, completed.
1270 m_isComplete = true;
1271
1272 RefPtr<Frame> protect(m_frame);
1273 checkCallImplicitClose(); // if we didn't do it before
1274
1275 // Do not start a redirection timer for subframes here.
1276 // That is deferred until the parent is completed.
1277 if (m_scheduledRedirection && !m_frame->tree()->parent())
1278 startRedirectionTimer();
1279
1280 completed();
1281 if (m_frame->page())
1282 checkLoadComplete();
1283 }
1284
checkCompletedTimerFired(Timer<FrameLoader> *)1285 void FrameLoader::checkCompletedTimerFired(Timer<FrameLoader>*)
1286 {
1287 checkCompleted();
1288 }
1289
scheduleCheckCompleted()1290 void FrameLoader::scheduleCheckCompleted()
1291 {
1292 if (!m_checkCompletedTimer.isActive())
1293 m_checkCompletedTimer.startOneShot(0);
1294 }
1295
checkLoadCompleteTimerFired(Timer<FrameLoader> *)1296 void FrameLoader::checkLoadCompleteTimerFired(Timer<FrameLoader>*)
1297 {
1298 if (!m_frame->page())
1299 return;
1300 checkLoadComplete();
1301 }
1302
scheduleCheckLoadComplete()1303 void FrameLoader::scheduleCheckLoadComplete()
1304 {
1305 if (!m_checkLoadCompleteTimer.isActive())
1306 m_checkLoadCompleteTimer.startOneShot(0);
1307 }
1308
checkCallImplicitClose()1309 void FrameLoader::checkCallImplicitClose()
1310 {
1311 if (m_didCallImplicitClose || m_frame->document()->parsing())
1312 return;
1313
1314 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
1315 if (!child->loader()->m_isComplete) // still got a frame running -> too early
1316 return;
1317
1318 m_didCallImplicitClose = true;
1319 m_wasUnloadEventEmitted = false;
1320 m_frame->document()->implicitClose();
1321 }
1322
baseURL() const1323 KURL FrameLoader::baseURL() const
1324 {
1325 ASSERT(m_frame->document());
1326 return m_frame->document()->baseURL();
1327 }
1328
completeURL(const String & url)1329 KURL FrameLoader::completeURL(const String& url)
1330 {
1331 ASSERT(m_frame->document());
1332 return m_frame->document()->completeURL(url);
1333 }
1334
scheduleHTTPRedirection(double delay,const String & url)1335 void FrameLoader::scheduleHTTPRedirection(double delay, const String& url)
1336 {
1337 if (delay < 0 || delay > INT_MAX / 1000)
1338 return;
1339
1340 if (!m_frame->page())
1341 return;
1342
1343 if (url.isEmpty())
1344 return;
1345
1346 // We want a new history item if the refresh timeout is > 1 second.
1347 if (!m_scheduledRedirection || delay <= m_scheduledRedirection->delay)
1348 scheduleRedirection(new ScheduledRedirection(delay, url, true, delay <= 1, false, false));
1349 }
1350
mustLockBackForwardList(Frame * targetFrame)1351 static bool mustLockBackForwardList(Frame* targetFrame)
1352 {
1353 // Navigation of a subframe during loading of an ancestor frame does not create a new back/forward item.
1354 // The definition of "during load" is any time before all handlers for the load event have been run.
1355 // See https://bugs.webkit.org/show_bug.cgi?id=14957 for the original motivation for this.
1356
1357 for (Frame* ancestor = targetFrame->tree()->parent(); ancestor; ancestor = ancestor->tree()->parent()) {
1358 Document* document = ancestor->document();
1359 if (!ancestor->loader()->isComplete() || document && document->processingLoadEvent())
1360 return true;
1361 }
1362 return false;
1363 }
1364
scheduleLocationChange(const String & url,const String & referrer,bool lockHistory,bool lockBackForwardList,bool wasUserGesture)1365 void FrameLoader::scheduleLocationChange(const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool wasUserGesture)
1366 {
1367 if (!m_frame->page())
1368 return;
1369
1370 if (url.isEmpty())
1371 return;
1372
1373 lockBackForwardList = lockBackForwardList || mustLockBackForwardList(m_frame);
1374
1375 // If the URL we're going to navigate to is the same as the current one, except for the
1376 // fragment part, we don't need to schedule the location change.
1377 KURL parsedURL(url);
1378 if (parsedURL.hasFragmentIdentifier() && equalIgnoringFragmentIdentifier(m_URL, parsedURL)) {
1379 changeLocation(completeURL(url), referrer, lockHistory, lockBackForwardList, wasUserGesture);
1380 return;
1381 }
1382
1383 // Handle a location change of a page with no document as a special case.
1384 // This may happen when a frame changes the location of another frame.
1385 bool duringLoad = !m_committedFirstRealDocumentLoad;
1386
1387 scheduleRedirection(new ScheduledRedirection(url, referrer, lockHistory, lockBackForwardList, wasUserGesture, false, duringLoad));
1388 }
1389
scheduleFormSubmission(const FrameLoadRequest & frameRequest,bool lockHistory,PassRefPtr<Event> event,PassRefPtr<FormState> formState)1390 void FrameLoader::scheduleFormSubmission(const FrameLoadRequest& frameRequest,
1391 bool lockHistory, PassRefPtr<Event> event, PassRefPtr<FormState> formState)
1392 {
1393 ASSERT(m_frame->page());
1394 ASSERT(!frameRequest.isEmpty());
1395
1396 // FIXME: Do we need special handling for form submissions where the URL is the same
1397 // as the current one except for the fragment part? See scheduleLocationChange above.
1398
1399 // Handle a location change of a page with no document as a special case.
1400 // This may happen when a frame changes the location of another frame.
1401 bool duringLoad = !m_committedFirstRealDocumentLoad;
1402
1403 scheduleRedirection(new ScheduledRedirection(frameRequest, lockHistory, mustLockBackForwardList(m_frame), event, formState, duringLoad));
1404 }
1405
scheduleRefresh(bool wasUserGesture)1406 void FrameLoader::scheduleRefresh(bool wasUserGesture)
1407 {
1408 if (!m_frame->page())
1409 return;
1410
1411 if (m_URL.isEmpty())
1412 return;
1413
1414 scheduleRedirection(new ScheduledRedirection(m_URL.string(), m_outgoingReferrer, true, true, wasUserGesture, true, false));
1415 }
1416
isLocationChange(const ScheduledRedirection & redirection)1417 bool FrameLoader::isLocationChange(const ScheduledRedirection& redirection)
1418 {
1419 switch (redirection.type) {
1420 case ScheduledRedirection::redirection:
1421 return false;
1422 case ScheduledRedirection::historyNavigation:
1423 case ScheduledRedirection::locationChange:
1424 case ScheduledRedirection::formSubmission:
1425 return true;
1426 }
1427 ASSERT_NOT_REACHED();
1428 return false;
1429 }
1430
scheduleHistoryNavigation(int steps)1431 void FrameLoader::scheduleHistoryNavigation(int steps)
1432 {
1433 if (!m_frame->page())
1434 return;
1435
1436 // navigation will always be allowed in the 0 steps case, which is OK because that's supposed to force a reload.
1437 if (!canGoBackOrForward(steps)) {
1438 cancelRedirection();
1439 return;
1440 }
1441
1442 scheduleRedirection(new ScheduledRedirection(steps));
1443 }
1444
goBackOrForward(int distance)1445 void FrameLoader::goBackOrForward(int distance)
1446 {
1447 if (distance == 0)
1448 return;
1449
1450 Page* page = m_frame->page();
1451 if (!page)
1452 return;
1453 BackForwardList* list = page->backForwardList();
1454 if (!list)
1455 return;
1456
1457 HistoryItem* item = list->itemAtIndex(distance);
1458 if (!item) {
1459 if (distance > 0) {
1460 int forwardListCount = list->forwardListCount();
1461 if (forwardListCount > 0)
1462 item = list->itemAtIndex(forwardListCount);
1463 } else {
1464 int backListCount = list->backListCount();
1465 if (backListCount > 0)
1466 item = list->itemAtIndex(-backListCount);
1467 }
1468 }
1469
1470 ASSERT(item); // we should not reach this line with an empty back/forward list
1471 if (item)
1472 page->goToItem(item, FrameLoadTypeIndexedBackForward);
1473 }
1474
redirectionTimerFired(Timer<FrameLoader> *)1475 void FrameLoader::redirectionTimerFired(Timer<FrameLoader>*)
1476 {
1477 ASSERT(m_frame->page());
1478
1479 OwnPtr<ScheduledRedirection> redirection(m_scheduledRedirection.release());
1480
1481 switch (redirection->type) {
1482 case ScheduledRedirection::redirection:
1483 case ScheduledRedirection::locationChange:
1484 changeLocation(KURL(redirection->url), redirection->referrer,
1485 redirection->lockHistory, redirection->lockBackForwardList, redirection->wasUserGesture, redirection->wasRefresh);
1486 return;
1487 case ScheduledRedirection::historyNavigation:
1488 if (redirection->historySteps == 0) {
1489 // Special case for go(0) from a frame -> reload only the frame
1490 urlSelected(m_URL, "", 0, redirection->lockHistory, redirection->lockBackForwardList, redirection->wasUserGesture);
1491 return;
1492 }
1493 // go(i!=0) from a frame navigates into the history of the frame only,
1494 // in both IE and NS (but not in Mozilla). We can't easily do that.
1495 goBackOrForward(redirection->historySteps);
1496 return;
1497 case ScheduledRedirection::formSubmission:
1498 // The submitForm function will find a target frame before using the redirection timer.
1499 // Now that the timer has fired, we need to repeat the security check which normally is done when
1500 // selecting a target, in case conditions have changed. Other code paths avoid this by targeting
1501 // without leaving a time window. If we fail the check just silently drop the form submission.
1502 if (!redirection->formState->sourceFrame()->loader()->shouldAllowNavigation(m_frame))
1503 return;
1504 loadFrameRequest(redirection->frameRequest, redirection->lockHistory, redirection->lockBackForwardList,
1505 redirection->event, redirection->formState);
1506 return;
1507 }
1508
1509 ASSERT_NOT_REACHED();
1510 }
1511
loadURLIntoChildFrame(const KURL & url,const String & referer,Frame * childFrame)1512 void FrameLoader::loadURLIntoChildFrame(const KURL& url, const String& referer, Frame* childFrame)
1513 {
1514 ASSERT(childFrame);
1515
1516 HistoryItem* parentItem = currentHistoryItem();
1517 FrameLoadType loadType = this->loadType();
1518 FrameLoadType childLoadType = FrameLoadTypeRedirectWithLockedBackForwardList;
1519
1520 KURL workingURL = url;
1521
1522 // If we're moving in the back/forward list, we might want to replace the content
1523 // of this child frame with whatever was there at that point.
1524 if (parentItem && parentItem->children().size() && isBackForwardLoadType(loadType)) {
1525 HistoryItem* childItem = parentItem->childItemWithTarget(childFrame->tree()->name());
1526 if (childItem) {
1527 // Use the original URL to ensure we get all the side-effects, such as
1528 // onLoad handlers, of any redirects that happened. An example of where
1529 // this is needed is Radar 3213556.
1530 workingURL = KURL(childItem->originalURLString());
1531 childLoadType = loadType;
1532 childFrame->loader()->m_provisionalHistoryItem = childItem;
1533 }
1534 }
1535
1536 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
1537 RefPtr<Archive> subframeArchive = activeDocumentLoader()->popArchiveForSubframe(childFrame->tree()->name());
1538
1539 if (subframeArchive)
1540 childFrame->loader()->loadArchive(subframeArchive.release());
1541 else
1542 #endif
1543 #ifdef ANDROID_USER_GESTURE
1544 childFrame->loader()->loadURL(workingURL, referer, String(), false, childLoadType, 0, 0, false);
1545 #else
1546 childFrame->loader()->loadURL(workingURL, referer, String(), false, childLoadType, 0, 0);
1547 #endif
1548 }
1549
1550 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
loadArchive(PassRefPtr<Archive> prpArchive)1551 void FrameLoader::loadArchive(PassRefPtr<Archive> prpArchive)
1552 {
1553 RefPtr<Archive> archive = prpArchive;
1554
1555 ArchiveResource* mainResource = archive->mainResource();
1556 ASSERT(mainResource);
1557 if (!mainResource)
1558 return;
1559
1560 SubstituteData substituteData(mainResource->data(), mainResource->mimeType(), mainResource->textEncoding(), KURL());
1561
1562 ResourceRequest request(mainResource->url());
1563 #if PLATFORM(MAC)
1564 request.applyWebArchiveHackForMail();
1565 #endif
1566
1567 RefPtr<DocumentLoader> documentLoader = m_client->createDocumentLoader(request, substituteData);
1568 documentLoader->addAllArchiveResources(archive.get());
1569 load(documentLoader.get());
1570 }
1571 #endif
1572
encoding() const1573 String FrameLoader::encoding() const
1574 {
1575 if (m_encodingWasChosenByUser && !m_encoding.isEmpty())
1576 return m_encoding;
1577 if (m_decoder && m_decoder->encoding().isValid())
1578 return m_decoder->encoding().name();
1579 Settings* settings = m_frame->settings();
1580 return settings ? settings->defaultTextEncodingName() : String();
1581 }
1582
gotoAnchor(const String & name)1583 bool FrameLoader::gotoAnchor(const String& name)
1584 {
1585 ASSERT(m_frame->document());
1586
1587 if (!m_frame->document()->haveStylesheetsLoaded()) {
1588 m_frame->document()->setGotoAnchorNeededAfterStylesheetsLoad(true);
1589 return false;
1590 }
1591
1592 m_frame->document()->setGotoAnchorNeededAfterStylesheetsLoad(false);
1593
1594 Element* anchorNode = m_frame->document()->findAnchor(name);
1595
1596 #if ENABLE(SVG)
1597 if (m_frame->document()->isSVGDocument()) {
1598 if (name.startsWith("xpointer(")) {
1599 // We need to parse the xpointer reference here
1600 } else if (name.startsWith("svgView(")) {
1601 RefPtr<SVGSVGElement> svg = static_cast<SVGDocument*>(m_frame->document())->rootElement();
1602 if (!svg->currentView()->parseViewSpec(name))
1603 return false;
1604 svg->setUseCurrentView(true);
1605 } else {
1606 if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) {
1607 RefPtr<SVGViewElement> viewElement = anchorNode->hasTagName(SVGNames::viewTag) ? static_cast<SVGViewElement*>(anchorNode) : 0;
1608 if (viewElement.get()) {
1609 RefPtr<SVGSVGElement> svg = static_cast<SVGSVGElement*>(SVGLocatable::nearestViewportElement(viewElement.get()));
1610 svg->inheritViewAttributes(viewElement.get());
1611 }
1612 }
1613 }
1614 // FIXME: need to decide which <svg> to focus on, and zoom to that one
1615 // FIXME: need to actually "highlight" the viewTarget(s)
1616 }
1617 #endif
1618
1619 m_frame->document()->setCSSTarget(anchorNode); // Setting to null will clear the current target.
1620
1621 // Implement the rule that "" and "top" both mean top of page as in other browsers.
1622 if (!anchorNode && !(name.isEmpty() || equalIgnoringCase(name, "top")))
1623 return false;
1624
1625 if (FrameView* view = m_frame->view())
1626 #ifdef ANDROID_SCROLL_ON_GOTO_ANCHOR
1627 {
1628 // TODO(andreip): check with Grace if this is correct.
1629 android::WebFrame::getWebFrame(m_frame)->setUserInitiatedClick(true);
1630 #endif
1631 view->maintainScrollPositionAtAnchor(anchorNode ? static_cast<Node*>(anchorNode) : m_frame->document());
1632 #ifdef ANDROID_SCROLL_ON_GOTO_ANCHOR
1633 android::WebFrame::getWebFrame(m_frame)->setUserInitiatedClick(false);
1634 }
1635 #endif
1636
1637 return true;
1638 }
1639
requestObject(RenderPart * renderer,const String & url,const AtomicString & frameName,const String & mimeType,const Vector<String> & paramNames,const Vector<String> & paramValues)1640 bool FrameLoader::requestObject(RenderPart* renderer, const String& url, const AtomicString& frameName,
1641 const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
1642 {
1643 if (url.isEmpty() && mimeType.isEmpty())
1644 return false;
1645
1646 if (!m_frame->script()->xssAuditor()->canLoadObject(url)) {
1647 // It is unsafe to honor the request for this object.
1648 return false;
1649 }
1650
1651 KURL completedURL;
1652 if (!url.isEmpty())
1653 completedURL = completeURL(url);
1654
1655 bool useFallback;
1656 if (shouldUsePlugin(completedURL, mimeType, renderer->hasFallbackContent(), useFallback)) {
1657 Settings* settings = m_frame->settings();
1658 if (!settings || !settings->arePluginsEnabled() ||
1659 (!settings->isJavaEnabled() && MIMETypeRegistry::isJavaAppletMIMEType(mimeType)))
1660 return false;
1661 return loadPlugin(renderer, completedURL, mimeType, paramNames, paramValues, useFallback);
1662 }
1663
1664 ASSERT(renderer->node()->hasTagName(objectTag) || renderer->node()->hasTagName(embedTag));
1665 HTMLPlugInElement* element = static_cast<HTMLPlugInElement*>(renderer->node());
1666
1667 // FIXME: OK to always make a new frame? When does the old frame get removed?
1668 return loadSubframe(element, completedURL, frameName, m_outgoingReferrer);
1669 }
1670
shouldUsePlugin(const KURL & url,const String & mimeType,bool hasFallback,bool & useFallback)1671 bool FrameLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool hasFallback, bool& useFallback)
1672 {
1673 if (m_client->shouldUsePluginDocument(mimeType)) {
1674 useFallback = false;
1675 return true;
1676 }
1677
1678 // Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that
1679 // can handle TIFF (which QuickTime can also handle) they probably intended to override QT.
1680 if (m_frame->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) {
1681 const PluginData* pluginData = m_frame->page()->pluginData();
1682 String pluginName = pluginData ? pluginData->pluginNameForMimeType(mimeType) : String();
1683 if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false))
1684 return true;
1685 }
1686
1687 ObjectContentType objectType = m_client->objectContentType(url, mimeType);
1688 // If an object's content can't be handled and it has no fallback, let
1689 // it be handled as a plugin to show the broken plugin icon.
1690 useFallback = objectType == ObjectContentNone && hasFallback;
1691 return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin;
1692 }
1693
toPlugInElement(Node * node)1694 static HTMLPlugInElement* toPlugInElement(Node* node)
1695 {
1696 if (!node)
1697 return 0;
1698
1699 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1700 ASSERT(node->hasTagName(objectTag) || node->hasTagName(embedTag)
1701 || node->hasTagName(videoTag) || node->hasTagName(audioTag)
1702 || node->hasTagName(appletTag));
1703 #else
1704 ASSERT(node->hasTagName(objectTag) || node->hasTagName(embedTag)
1705 || node->hasTagName(appletTag));
1706 #endif
1707
1708 return static_cast<HTMLPlugInElement*>(node);
1709 }
1710
loadPlugin(RenderPart * renderer,const KURL & url,const String & mimeType,const Vector<String> & paramNames,const Vector<String> & paramValues,bool useFallback)1711 bool FrameLoader::loadPlugin(RenderPart* renderer, const KURL& url, const String& mimeType,
1712 const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
1713 {
1714 RefPtr<Widget> widget;
1715
1716 if (renderer && !useFallback) {
1717 HTMLPlugInElement* element = toPlugInElement(renderer->node());
1718
1719 if (!canLoad(url, String(), frame()->document())) {
1720 FrameLoader::reportLocalLoadFailed(m_frame, url.string());
1721 return false;
1722 }
1723
1724 widget = m_client->createPlugin(IntSize(renderer->contentWidth(), renderer->contentHeight()),
1725 element, url, paramNames, paramValues, mimeType,
1726 m_frame->document()->isPluginDocument() && !m_containsPlugIns);
1727 if (widget) {
1728 renderer->setWidget(widget);
1729 m_containsPlugIns = true;
1730 }
1731 }
1732
1733 return widget != 0;
1734 }
1735
parentCompleted()1736 void FrameLoader::parentCompleted()
1737 {
1738 if (m_scheduledRedirection && !m_redirectionTimer.isActive())
1739 startRedirectionTimer();
1740 }
1741
outgoingReferrer() const1742 String FrameLoader::outgoingReferrer() const
1743 {
1744 return m_outgoingReferrer;
1745 }
1746
outgoingOrigin() const1747 String FrameLoader::outgoingOrigin() const
1748 {
1749 return m_frame->document()->securityOrigin()->toString();
1750 }
1751
opener()1752 Frame* FrameLoader::opener()
1753 {
1754 return m_opener;
1755 }
1756
setOpener(Frame * opener)1757 void FrameLoader::setOpener(Frame* opener)
1758 {
1759 if (m_opener)
1760 m_opener->loader()->m_openedFrames.remove(m_frame);
1761 if (opener)
1762 opener->loader()->m_openedFrames.add(m_frame);
1763 m_opener = opener;
1764
1765 if (m_frame->document()) {
1766 m_frame->document()->initSecurityContext();
1767 m_frame->domWindow()->setSecurityOrigin(m_frame->document()->securityOrigin());
1768 }
1769 }
1770
openedByDOM() const1771 bool FrameLoader::openedByDOM() const
1772 {
1773 return m_openedByDOM;
1774 }
1775
setOpenedByDOM()1776 void FrameLoader::setOpenedByDOM()
1777 {
1778 m_openedByDOM = true;
1779 }
1780
handleFallbackContent()1781 void FrameLoader::handleFallbackContent()
1782 {
1783 HTMLFrameOwnerElement* owner = m_frame->ownerElement();
1784 if (!owner || !owner->hasTagName(objectTag))
1785 return;
1786 static_cast<HTMLObjectElement*>(owner)->renderFallbackContent();
1787 }
1788
provisionalLoadStarted()1789 void FrameLoader::provisionalLoadStarted()
1790 {
1791 #ifdef ANDROID_INSTRUMENT
1792 if (!m_frame->tree()->parent())
1793 android::TimeCounter::reset();
1794 #endif
1795 m_firstLayoutDone = false;
1796 cancelRedirection(true);
1797 m_client->provisionalLoadStarted();
1798 }
1799
isProcessingUserGesture()1800 bool FrameLoader::isProcessingUserGesture()
1801 {
1802 Frame* frame = m_frame->tree()->top();
1803 if (!frame->script()->isEnabled())
1804 return true; // If JavaScript is disabled, a user gesture must have initiated the navigation.
1805 return frame->script()->processingUserGesture(); // FIXME: Use pageIsProcessingUserGesture.
1806 }
1807
resetMultipleFormSubmissionProtection()1808 void FrameLoader::resetMultipleFormSubmissionProtection()
1809 {
1810 m_submittedFormURL = KURL();
1811 }
1812
setEncoding(const String & name,bool userChosen)1813 void FrameLoader::setEncoding(const String& name, bool userChosen)
1814 {
1815 if (!m_workingURL.isEmpty())
1816 receivedFirstData();
1817 m_encoding = name;
1818 m_encodingWasChosenByUser = userChosen;
1819 }
1820
addData(const char * bytes,int length)1821 void FrameLoader::addData(const char* bytes, int length)
1822 {
1823 ASSERT(m_workingURL.isEmpty());
1824 ASSERT(m_frame->document());
1825 ASSERT(m_frame->document()->parsing());
1826 write(bytes, length);
1827 }
1828
1829 #if ENABLE(WML)
frameContainsWMLContent(Frame * frame)1830 static inline bool frameContainsWMLContent(Frame* frame)
1831 {
1832 Document* document = frame ? frame->document() : 0;
1833 if (!document)
1834 return false;
1835
1836 return document->containsWMLContent() || document->isWMLDocument();
1837 }
1838 #endif
1839
canCachePageContainingThisFrame()1840 bool FrameLoader::canCachePageContainingThisFrame()
1841 {
1842 return m_documentLoader
1843 && m_documentLoader->mainDocumentError().isNull()
1844 && !m_frame->tree()->childCount()
1845 // FIXME: If we ever change this so that frames with plug-ins will be cached,
1846 // we need to make sure that we don't cache frames that have outstanding NPObjects
1847 // (objects created by the plug-in). Since there is no way to pause/resume a Netscape plug-in,
1848 // they would need to be destroyed and then recreated, and there is no way that we can recreate
1849 // the right NPObjects. See <rdar://problem/5197041> for more information.
1850 && !m_containsPlugIns
1851 && !m_URL.protocolIs("https")
1852 && (!m_frame->domWindow() || !m_frame->domWindow()->hasEventListener(eventNames().unloadEvent))
1853 #if ENABLE(DATABASE)
1854 && !m_frame->document()->hasOpenDatabases()
1855 #endif
1856 && !m_frame->document()->usingGeolocation()
1857 && m_currentHistoryItem
1858 && !m_quickRedirectComing
1859 && !m_documentLoader->isLoadingInAPISense()
1860 && !m_documentLoader->isStopping()
1861 && m_frame->document()->canSuspendActiveDOMObjects()
1862 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
1863 // FIXME: We should investigating caching frames that have an associated
1864 // application cache. <rdar://problem/5917899> tracks that work.
1865 && m_documentLoader->applicationCacheHost()->canCacheInPageCache()
1866 #endif
1867 #if ENABLE(WML)
1868 && !frameContainsWMLContent(m_frame)
1869 #endif
1870 && m_client->canCachePage()
1871 ;
1872 }
1873
canCachePage()1874 bool FrameLoader::canCachePage()
1875 {
1876 #ifndef NDEBUG
1877 logCanCachePageDecision();
1878 #endif
1879
1880 // Cache the page, if possible.
1881 // Don't write to the cache if in the middle of a redirect, since we will want to
1882 // store the final page we end up on.
1883 // No point writing to the cache on a reload or loadSame, since we will just write
1884 // over it again when we leave that page.
1885 // FIXME: <rdar://problem/4886592> - We should work out the complexities of caching pages with frames as they
1886 // are the most interesting pages on the web, and often those that would benefit the most from caching!
1887 FrameLoadType loadType = this->loadType();
1888
1889 return !m_frame->tree()->parent()
1890 && canCachePageContainingThisFrame()
1891 && m_frame->page()
1892 && m_frame->page()->backForwardList()->enabled()
1893 && m_frame->page()->backForwardList()->capacity() > 0
1894 && m_frame->page()->settings()->usesPageCache()
1895 && loadType != FrameLoadTypeReload
1896 && loadType != FrameLoadTypeReloadFromOrigin
1897 && loadType != FrameLoadTypeSame
1898 ;
1899 }
1900
1901 #ifndef NDEBUG
pageCacheLogPrefix(int indentLevel)1902 static String& pageCacheLogPrefix(int indentLevel)
1903 {
1904 static int previousIndent = -1;
1905 DEFINE_STATIC_LOCAL(String, prefix, ());
1906
1907 if (indentLevel != previousIndent) {
1908 previousIndent = indentLevel;
1909 prefix.truncate(0);
1910 for (int i = 0; i < previousIndent; ++i)
1911 prefix += " ";
1912 }
1913
1914 return prefix;
1915 }
1916
pageCacheLog(const String & prefix,const String & message)1917 static void pageCacheLog(const String& prefix, const String& message)
1918 {
1919 LOG(PageCache, "%s%s", prefix.utf8().data(), message.utf8().data());
1920 }
1921
1922 #define PCLOG(...) pageCacheLog(pageCacheLogPrefix(indentLevel), String::format(__VA_ARGS__))
1923
logCanCachePageDecision()1924 void FrameLoader::logCanCachePageDecision()
1925 {
1926 // Only bother logging for main frames that have actually loaded and have content.
1927 if (m_creatingInitialEmptyDocument)
1928 return;
1929 KURL currentURL = m_documentLoader ? m_documentLoader->url() : KURL();
1930 if (currentURL.isEmpty())
1931 return;
1932
1933 int indentLevel = 0;
1934 PCLOG("--------\n Determining if page can be cached:");
1935
1936 bool cannotCache = !logCanCacheFrameDecision(1);
1937
1938 FrameLoadType loadType = this->loadType();
1939 do {
1940 if (m_frame->tree()->parent())
1941 { PCLOG(" -Frame has a parent frame"); cannotCache = true; }
1942 if (!m_frame->page()) {
1943 PCLOG(" -There is no Page object");
1944 cannotCache = true;
1945 break;
1946 }
1947 if (!m_frame->page()->backForwardList()->enabled())
1948 { PCLOG(" -The back/forward list is disabled"); cannotCache = true; }
1949 if (!(m_frame->page()->backForwardList()->capacity() > 0))
1950 { PCLOG(" -The back/forward list has a 0 capacity"); cannotCache = true; }
1951 if (!m_frame->page()->settings()->usesPageCache())
1952 { PCLOG(" -Page settings says b/f cache disabled"); cannotCache = true; }
1953 if (loadType == FrameLoadTypeReload)
1954 { PCLOG(" -Load type is: Reload"); cannotCache = true; }
1955 if (loadType == FrameLoadTypeReloadFromOrigin)
1956 { PCLOG(" -Load type is: Reload from origin"); cannotCache = true; }
1957 if (loadType == FrameLoadTypeSame)
1958 { PCLOG(" -Load type is: Same"); cannotCache = true; }
1959 } while (false);
1960
1961 PCLOG(cannotCache ? " Page CANNOT be cached\n--------" : " Page CAN be cached\n--------");
1962 }
1963
logCanCacheFrameDecision(int indentLevel)1964 bool FrameLoader::logCanCacheFrameDecision(int indentLevel)
1965 {
1966 // Only bother logging for frames that have actually loaded and have content.
1967 if (m_creatingInitialEmptyDocument)
1968 return false;
1969 KURL currentURL = m_documentLoader ? m_documentLoader->url() : KURL();
1970 if (currentURL.isEmpty())
1971 return false;
1972
1973 PCLOG("+---");
1974 KURL newURL = m_provisionalDocumentLoader ? m_provisionalDocumentLoader->url() : KURL();
1975 if (!newURL.isEmpty())
1976 PCLOG(" Determining if frame can be cached navigating from (%s) to (%s):", currentURL.string().utf8().data(), newURL.string().utf8().data());
1977 else
1978 PCLOG(" Determining if subframe with URL (%s) can be cached:", currentURL.string().utf8().data());
1979
1980 bool cannotCache = false;
1981
1982 do {
1983 if (!m_documentLoader) {
1984 PCLOG(" -There is no DocumentLoader object");
1985 cannotCache = true;
1986 break;
1987 }
1988 if (!m_documentLoader->mainDocumentError().isNull())
1989 { PCLOG(" -Main document has an error"); cannotCache = true; }
1990 if (m_frame->tree()->childCount())
1991 { PCLOG(" -Frame has child frames"); cannotCache = true; }
1992 if (m_containsPlugIns)
1993 { PCLOG(" -Frame contains plugins"); cannotCache = true; }
1994 if (m_URL.protocolIs("https"))
1995 { PCLOG(" -Frame is HTTPS"); cannotCache = true; }
1996 if (m_frame->domWindow() && m_frame->domWindow()->hasEventListener(eventNames().unloadEvent))
1997 { PCLOG(" -Frame has an unload event listener"); cannotCache = true; }
1998 #if ENABLE(DATABASE)
1999 if (m_frame->document()->hasOpenDatabases())
2000 { PCLOG(" -Frame has open database handles"); cannotCache = true; }
2001 #endif
2002 if (m_frame->document()->usingGeolocation())
2003 { PCLOG(" -Frame uses Geolocation"); cannotCache = true; }
2004 if (!m_currentHistoryItem)
2005 { PCLOG(" -No current history item"); cannotCache = true; }
2006 if (m_quickRedirectComing)
2007 { PCLOG(" -Quick redirect is coming"); cannotCache = true; }
2008 if (m_documentLoader->isLoadingInAPISense())
2009 { PCLOG(" -DocumentLoader is still loading in API sense"); cannotCache = true; }
2010 if (m_documentLoader->isStopping())
2011 { PCLOG(" -DocumentLoader is in the middle of stopping"); cannotCache = true; }
2012 if (!m_frame->document()->canSuspendActiveDOMObjects())
2013 { PCLOG(" -The document cannot suspect its active DOM Objects"); cannotCache = true; }
2014 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
2015 if (!m_documentLoader->applicationCacheHost()->canCacheInPageCache())
2016 { PCLOG(" -The DocumentLoader uses an application cache"); cannotCache = true; }
2017 #endif
2018 if (!m_client->canCachePage())
2019 { PCLOG(" -The client says this frame cannot be cached"); cannotCache = true; }
2020 } while (false);
2021
2022 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
2023 if (!child->loader()->logCanCacheFrameDecision(indentLevel + 1))
2024 cannotCache = true;
2025
2026 PCLOG(cannotCache ? " Frame CANNOT be cached" : " Frame CAN be cached");
2027 PCLOG("+---");
2028
2029 return !cannotCache;
2030 }
2031 #endif
2032
updateFirstPartyForCookies()2033 void FrameLoader::updateFirstPartyForCookies()
2034 {
2035 if (m_frame->tree()->parent())
2036 setFirstPartyForCookies(m_frame->tree()->parent()->document()->firstPartyForCookies());
2037 else
2038 setFirstPartyForCookies(m_URL);
2039 }
2040
setFirstPartyForCookies(const KURL & url)2041 void FrameLoader::setFirstPartyForCookies(const KURL& url)
2042 {
2043 m_frame->document()->setFirstPartyForCookies(url);
2044 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
2045 child->loader()->setFirstPartyForCookies(url);
2046 }
2047
2048 class HashChangeEventTask : public ScriptExecutionContext::Task {
2049 public:
create(PassRefPtr<Document> document)2050 static PassRefPtr<HashChangeEventTask> create(PassRefPtr<Document> document)
2051 {
2052 return adoptRef(new HashChangeEventTask(document));
2053 }
2054
performTask(ScriptExecutionContext * context)2055 virtual void performTask(ScriptExecutionContext* context)
2056 {
2057 ASSERT_UNUSED(context, context->isDocument());
2058 m_document->dispatchWindowEvent(eventNames().hashchangeEvent, false, false);
2059 }
2060
2061 private:
HashChangeEventTask(PassRefPtr<Document> document)2062 HashChangeEventTask(PassRefPtr<Document> document)
2063 : m_document(document)
2064 {
2065 ASSERT(m_document);
2066 }
2067
2068 RefPtr<Document> m_document;
2069 };
2070
2071 // This does the same kind of work that didOpenURL does, except it relies on the fact
2072 // that a higher level already checked that the URLs match and the scrolling is the right thing to do.
scrollToAnchor(const KURL & url)2073 void FrameLoader::scrollToAnchor(const KURL& url)
2074 {
2075 ASSERT(equalIgnoringFragmentIdentifier(url, m_URL));
2076 if (equalIgnoringFragmentIdentifier(url, m_URL) && !equalIgnoringNullity(url.fragmentIdentifier(), m_URL.fragmentIdentifier())) {
2077 Document* currentDocument = frame()->document();
2078 currentDocument->postTask(HashChangeEventTask::create(currentDocument));
2079 }
2080
2081 m_URL = url;
2082 updateHistoryForAnchorScroll();
2083
2084 // If we were in the autoscroll/panScroll mode we want to stop it before following the link to the anchor
2085 m_frame->eventHandler()->stopAutoscrollTimer();
2086 started();
2087 gotoAnchor();
2088
2089 // It's important to model this as a load that starts and immediately finishes.
2090 // Otherwise, the parent frame may think we never finished loading.
2091 m_isComplete = false;
2092 checkCompleted();
2093 }
2094
isComplete() const2095 bool FrameLoader::isComplete() const
2096 {
2097 return m_isComplete;
2098 }
2099
scheduleRedirection(ScheduledRedirection * redirection)2100 void FrameLoader::scheduleRedirection(ScheduledRedirection* redirection)
2101 {
2102 ASSERT(m_frame->page());
2103
2104 // If a redirect was scheduled during a load, then stop the current load.
2105 // Otherwise when the current load transitions from a provisional to a
2106 // committed state, pending redirects may be cancelled.
2107 if (redirection->wasDuringLoad) {
2108 if (m_provisionalDocumentLoader)
2109 m_provisionalDocumentLoader->stopLoading();
2110 stopLoading(true);
2111 }
2112
2113 stopRedirectionTimer();
2114 m_scheduledRedirection.set(redirection);
2115 if (!m_isComplete && redirection->type != ScheduledRedirection::redirection)
2116 completed();
2117 if (m_isComplete || redirection->type != ScheduledRedirection::redirection)
2118 startRedirectionTimer();
2119 }
2120
startRedirectionTimer()2121 void FrameLoader::startRedirectionTimer()
2122 {
2123 ASSERT(m_frame->page());
2124 ASSERT(m_scheduledRedirection);
2125
2126 m_redirectionTimer.stop();
2127 m_redirectionTimer.startOneShot(m_scheduledRedirection->delay);
2128
2129 switch (m_scheduledRedirection->type) {
2130 case ScheduledRedirection::locationChange:
2131 case ScheduledRedirection::redirection:
2132 clientRedirected(KURL(m_scheduledRedirection->url),
2133 m_scheduledRedirection->delay,
2134 currentTime() + m_redirectionTimer.nextFireInterval(),
2135 m_scheduledRedirection->lockBackForwardList);
2136 return;
2137 case ScheduledRedirection::formSubmission:
2138 // FIXME: It would make sense to report form submissions as client redirects too.
2139 // But we didn't do that in the past when form submission used a separate delay
2140 // mechanism, so doing it will be a behavior change.
2141 return;
2142 case ScheduledRedirection::historyNavigation:
2143 // Don't report history navigations.
2144 return;
2145 }
2146 ASSERT_NOT_REACHED();
2147 }
2148
stopRedirectionTimer()2149 void FrameLoader::stopRedirectionTimer()
2150 {
2151 if (!m_redirectionTimer.isActive())
2152 return;
2153
2154 m_redirectionTimer.stop();
2155
2156 if (m_scheduledRedirection) {
2157 switch (m_scheduledRedirection->type) {
2158 case ScheduledRedirection::locationChange:
2159 case ScheduledRedirection::redirection:
2160 clientRedirectCancelledOrFinished(m_cancellingWithLoadInProgress);
2161 return;
2162 case ScheduledRedirection::formSubmission:
2163 // FIXME: It would make sense to report form submissions as client redirects too.
2164 // But we didn't do that in the past when form submission used a separate delay
2165 // mechanism, so doing it will be a behavior change.
2166 return;
2167 case ScheduledRedirection::historyNavigation:
2168 // Don't report history navigations.
2169 return;
2170 }
2171 ASSERT_NOT_REACHED();
2172 }
2173 }
2174
completed()2175 void FrameLoader::completed()
2176 {
2177 RefPtr<Frame> protect(m_frame);
2178 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
2179 child->loader()->parentCompleted();
2180 if (Frame* parent = m_frame->tree()->parent())
2181 parent->loader()->checkCompleted();
2182 if (m_frame->view())
2183 m_frame->view()->maintainScrollPositionAtAnchor(0);
2184 }
2185
started()2186 void FrameLoader::started()
2187 {
2188 for (Frame* frame = m_frame; frame; frame = frame->tree()->parent())
2189 frame->loader()->m_isComplete = false;
2190 }
2191
containsPlugins() const2192 bool FrameLoader::containsPlugins() const
2193 {
2194 return m_containsPlugIns;
2195 }
2196
prepareForLoadStart()2197 void FrameLoader::prepareForLoadStart()
2198 {
2199 if (Page* page = m_frame->page())
2200 page->progress()->progressStarted(m_frame);
2201 m_client->dispatchDidStartProvisionalLoad();
2202 }
2203
setupForReplace()2204 void FrameLoader::setupForReplace()
2205 {
2206 setState(FrameStateProvisional);
2207 m_provisionalDocumentLoader = m_documentLoader;
2208 m_documentLoader = 0;
2209 detachChildren();
2210 }
2211
setupForReplaceByMIMEType(const String & newMIMEType)2212 void FrameLoader::setupForReplaceByMIMEType(const String& newMIMEType)
2213 {
2214 activeDocumentLoader()->setupForReplaceByMIMEType(newMIMEType);
2215 }
2216
2217 // This is a hack to allow keep navigation to http/https feeds working. To remove this
2218 // we need to introduce new API akin to registerURLSchemeAsLocal, that registers a
2219 // protocols navigation policy.
isFeedWithNestedProtocolInHTTPFamily(const KURL & url)2220 static bool isFeedWithNestedProtocolInHTTPFamily(const KURL& url)
2221 {
2222 const String& urlString = url.string();
2223 if (!urlString.startsWith("feed", false))
2224 return false;
2225
2226 return urlString.startsWith("feed://", false)
2227 || urlString.startsWith("feed:http:", false) || urlString.startsWith("feed:https:", false)
2228 || urlString.startsWith("feeds:http:", false) || urlString.startsWith("feeds:https:", false)
2229 || urlString.startsWith("feedsearch:http:", false) || urlString.startsWith("feedsearch:https:", false);
2230 }
2231
loadFrameRequest(const FrameLoadRequest & request,bool lockHistory,bool lockBackForwardList,PassRefPtr<Event> event,PassRefPtr<FormState> formState)2232 void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, bool lockHistory, bool lockBackForwardList,
2233 PassRefPtr<Event> event, PassRefPtr<FormState> formState)
2234 {
2235 KURL url = request.resourceRequest().url();
2236
2237 String referrer;
2238 String argsReferrer = request.resourceRequest().httpReferrer();
2239 if (!argsReferrer.isEmpty())
2240 referrer = argsReferrer;
2241 else
2242 referrer = m_outgoingReferrer;
2243
2244 ASSERT(frame()->document());
2245 if (SecurityOrigin::shouldTreatURLAsLocal(url.string()) && !isFeedWithNestedProtocolInHTTPFamily(url)) {
2246 if (!canLoad(url, String(), frame()->document()) && !canLoad(url, referrer)) {
2247 FrameLoader::reportLocalLoadFailed(m_frame, url.string());
2248 return;
2249 }
2250 }
2251
2252 if (shouldHideReferrer(url, referrer))
2253 referrer = String();
2254
2255 FrameLoadType loadType;
2256 if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData)
2257 loadType = FrameLoadTypeReload;
2258 else if (lockBackForwardList)
2259 loadType = FrameLoadTypeRedirectWithLockedBackForwardList;
2260 else
2261 loadType = FrameLoadTypeStandard;
2262
2263 #ifdef ANDROID_USER_GESTURE
2264 if (request.resourceRequest().httpMethod() == "POST")
2265 loadPostRequest(request.resourceRequest(), referrer, request.frameName(), lockHistory, loadType, event, formState.get(), request.resourceRequest().getUserGesture());
2266 else
2267 loadURL(request.resourceRequest().url(), referrer, request.frameName(), lockHistory, loadType, event, formState.get(), request.resourceRequest().getUserGesture());
2268 #else
2269 if (request.resourceRequest().httpMethod() == "POST")
2270 loadPostRequest(request.resourceRequest(), referrer, request.frameName(), lockHistory, loadType, event, formState.get());
2271 else
2272 loadURL(request.resourceRequest().url(), referrer, request.frameName(), lockHistory, loadType, event, formState.get());
2273 #endif
2274
2275 // FIXME: It's possible this targetFrame will not be the same frame that was targeted by the actual
2276 // load if frame names have changed.
2277 Frame* sourceFrame = formState ? formState->sourceFrame() : m_frame;
2278 Frame* targetFrame = sourceFrame->loader()->findFrameForNavigation(request.frameName());
2279 if (targetFrame && targetFrame != sourceFrame) {
2280 if (Page* page = targetFrame->page())
2281 page->chrome()->focus();
2282 }
2283 }
2284
2285 #ifdef ANDROID_USER_GESTURE
loadURL(const KURL & newURL,const String & referrer,const String & frameName,bool lockHistory,FrameLoadType newLoadType,PassRefPtr<Event> event,PassRefPtr<FormState> prpFormState,bool userGesture)2286 void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType newLoadType,
2287 PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState, bool userGesture)
2288 #else
2289 void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType newLoadType,
2290 PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState)
2291 #endif
2292 {
2293 RefPtr<FormState> formState = prpFormState;
2294 bool isFormSubmission = formState;
2295
2296 ResourceRequest request(newURL);
2297 #ifdef ANDROID_USER_GESTURE
2298 request.setUserGesture(userGesture);
2299 #endif
2300 if (!referrer.isEmpty()) {
2301 request.setHTTPReferrer(referrer);
2302 RefPtr<SecurityOrigin> referrerOrigin = SecurityOrigin::createFromString(referrer);
2303 addHTTPOriginIfNeeded(request, referrerOrigin->toString());
2304 }
2305 addExtraFieldsToRequest(request, newLoadType, true, event || isFormSubmission);
2306 if (newLoadType == FrameLoadTypeReload || newLoadType == FrameLoadTypeReloadFromOrigin)
2307 request.setCachePolicy(ReloadIgnoringCacheData);
2308
2309 ASSERT(newLoadType != FrameLoadTypeSame);
2310
2311 // The search for a target frame is done earlier in the case of form submission.
2312 Frame* targetFrame = isFormSubmission ? 0 : findFrameForNavigation(frameName);
2313 if (targetFrame && targetFrame != m_frame) {
2314 #ifdef ANDROID_USER_GESTURE
2315 targetFrame->loader()->loadURL(newURL, referrer, String(), lockHistory, newLoadType, event, formState.release(), userGesture);
2316 #else
2317 targetFrame->loader()->loadURL(newURL, referrer, String(), lockHistory, newLoadType, event, formState.release());
2318 #endif
2319 return;
2320 }
2321
2322 NavigationAction action(newURL, newLoadType, isFormSubmission, event);
2323
2324 if (!targetFrame && !frameName.isEmpty()) {
2325 checkNewWindowPolicy(action, request, formState.release(), frameName);
2326 return;
2327 }
2328
2329 RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader;
2330
2331 bool sameURL = shouldTreatURLAsSameAsCurrent(newURL);
2332
2333 // Make sure to do scroll to anchor processing even if the URL is
2334 // exactly the same so pages with '#' links and DHTML side effects
2335 // work properly.
2336 if (shouldScrollToAnchor(isFormSubmission, newLoadType, newURL)) {
2337 oldDocumentLoader->setTriggeringAction(action);
2338 stopPolicyCheck();
2339 checkNavigationPolicy(request, oldDocumentLoader.get(), formState.release(),
2340 callContinueFragmentScrollAfterNavigationPolicy, this);
2341 } else {
2342 // must grab this now, since this load may stop the previous load and clear this flag
2343 bool isRedirect = m_quickRedirectComing;
2344 loadWithNavigationAction(request, action, lockHistory, newLoadType, formState.release());
2345 if (isRedirect) {
2346 m_quickRedirectComing = false;
2347 if (m_provisionalDocumentLoader)
2348 m_provisionalDocumentLoader->setIsClientRedirect(true);
2349 } else if (sameURL)
2350 // Example of this case are sites that reload the same URL with a different cookie
2351 // driving the generated content, or a master frame with links that drive a target
2352 // frame, where the user has clicked on the same link repeatedly.
2353 m_loadType = FrameLoadTypeSame;
2354 }
2355 }
2356
load(const ResourceRequest & request,bool lockHistory)2357 void FrameLoader::load(const ResourceRequest& request, bool lockHistory)
2358 {
2359 load(request, SubstituteData(), lockHistory);
2360 }
2361
load(const ResourceRequest & request,const SubstituteData & substituteData,bool lockHistory)2362 void FrameLoader::load(const ResourceRequest& request, const SubstituteData& substituteData, bool lockHistory)
2363 {
2364 if (m_inStopAllLoaders)
2365 return;
2366
2367 // FIXME: is this the right place to reset loadType? Perhaps this should be done after loading is finished or aborted.
2368 m_loadType = FrameLoadTypeStandard;
2369 RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, substituteData);
2370 if (lockHistory && m_documentLoader)
2371 loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory() : m_documentLoader->clientRedirectSourceForHistory());
2372 load(loader.get());
2373 }
2374
load(const ResourceRequest & request,const String & frameName,bool lockHistory)2375 void FrameLoader::load(const ResourceRequest& request, const String& frameName, bool lockHistory)
2376 {
2377 if (frameName.isEmpty()) {
2378 load(request, lockHistory);
2379 return;
2380 }
2381
2382 Frame* frame = findFrameForNavigation(frameName);
2383 if (frame) {
2384 frame->loader()->load(request, lockHistory);
2385 return;
2386 }
2387
2388 checkNewWindowPolicy(NavigationAction(request.url(), NavigationTypeOther), request, 0, frameName);
2389 }
2390
loadWithNavigationAction(const ResourceRequest & request,const NavigationAction & action,bool lockHistory,FrameLoadType type,PassRefPtr<FormState> formState)2391 void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, bool lockHistory, FrameLoadType type, PassRefPtr<FormState> formState)
2392 {
2393 RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData());
2394 if (lockHistory && m_documentLoader)
2395 loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory() : m_documentLoader->clientRedirectSourceForHistory());
2396
2397 loader->setTriggeringAction(action);
2398 if (m_documentLoader)
2399 loader->setOverrideEncoding(m_documentLoader->overrideEncoding());
2400
2401 loadWithDocumentLoader(loader.get(), type, formState);
2402 }
2403
load(DocumentLoader * newDocumentLoader)2404 void FrameLoader::load(DocumentLoader* newDocumentLoader)
2405 {
2406 ResourceRequest& r = newDocumentLoader->request();
2407 addExtraFieldsToMainResourceRequest(r);
2408 FrameLoadType type;
2409
2410 if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->originalRequest().url())) {
2411 r.setCachePolicy(ReloadIgnoringCacheData);
2412 type = FrameLoadTypeSame;
2413 } else
2414 type = FrameLoadTypeStandard;
2415
2416 if (m_documentLoader)
2417 newDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding());
2418
2419 // When we loading alternate content for an unreachable URL that we're
2420 // visiting in the history list, we treat it as a reload so the history list
2421 // is appropriately maintained.
2422 //
2423 // FIXME: This seems like a dangerous overloading of the meaning of "FrameLoadTypeReload" ...
2424 // shouldn't a more explicit type of reload be defined, that means roughly
2425 // "load without affecting history" ?
2426 if (shouldReloadToHandleUnreachableURL(newDocumentLoader)) {
2427 ASSERT(type == FrameLoadTypeStandard);
2428 type = FrameLoadTypeReload;
2429 }
2430
2431 loadWithDocumentLoader(newDocumentLoader, type, 0);
2432 }
2433
loadWithDocumentLoader(DocumentLoader * loader,FrameLoadType type,PassRefPtr<FormState> prpFormState)2434 void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState)
2435 {
2436 ASSERT(m_client->hasWebView());
2437
2438 // Unfortunately the view must be non-nil, this is ultimately due
2439 // to parser requiring a FrameView. We should fix this dependency.
2440
2441 ASSERT(m_frame->view());
2442
2443 m_policyLoadType = type;
2444 RefPtr<FormState> formState = prpFormState;
2445 bool isFormSubmission = formState;
2446
2447 const KURL& newURL = loader->request().url();
2448
2449 if (shouldScrollToAnchor(isFormSubmission, m_policyLoadType, newURL)) {
2450 RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader;
2451 NavigationAction action(newURL, m_policyLoadType, isFormSubmission);
2452
2453 oldDocumentLoader->setTriggeringAction(action);
2454 stopPolicyCheck();
2455 checkNavigationPolicy(loader->request(), oldDocumentLoader.get(), formState,
2456 callContinueFragmentScrollAfterNavigationPolicy, this);
2457 } else {
2458 if (Frame* parent = m_frame->tree()->parent())
2459 loader->setOverrideEncoding(parent->loader()->documentLoader()->overrideEncoding());
2460
2461 stopPolicyCheck();
2462 setPolicyDocumentLoader(loader);
2463 if (loader->triggeringAction().isEmpty())
2464 loader->setTriggeringAction(NavigationAction(newURL, m_policyLoadType, isFormSubmission));
2465
2466 checkNavigationPolicy(loader->request(), loader, formState,
2467 callContinueLoadAfterNavigationPolicy, this);
2468 }
2469 }
2470
canLoad(const KURL & url,const String & referrer,const Document * doc)2471 bool FrameLoader::canLoad(const KURL& url, const String& referrer, const Document* doc)
2472 {
2473 return canLoad(url, referrer, doc ? doc->securityOrigin() : 0);
2474 }
2475
canLoad(const KURL & url,const String & referrer,const SecurityOrigin * securityOrigin)2476 bool FrameLoader::canLoad(const KURL& url, const String& referrer, const SecurityOrigin* securityOrigin)
2477 {
2478 // We can always load any URL that isn't considered local (e.g. http URLs).
2479 if (!SecurityOrigin::shouldTreatURLAsLocal(url.string()))
2480 return true;
2481
2482 // If we were provided a document, we let its local file policy dictate the result,
2483 // otherwise we allow local loads only if the supplied referrer is also local.
2484 if (securityOrigin)
2485 return securityOrigin->canLoadLocalResources();
2486 if (!referrer.isEmpty())
2487 return SecurityOrigin::shouldTreatURLAsLocal(referrer);
2488 return false;
2489 }
2490
reportLocalLoadFailed(Frame * frame,const String & url)2491 void FrameLoader::reportLocalLoadFailed(Frame* frame, const String& url)
2492 {
2493 ASSERT(!url.isEmpty());
2494 if (!frame)
2495 return;
2496
2497 frame->domWindow()->console()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Not allowed to load local resource: " + url, 0, String());
2498 }
2499
shouldHideReferrer(const KURL & url,const String & referrer)2500 bool FrameLoader::shouldHideReferrer(const KURL& url, const String& referrer)
2501 {
2502 bool referrerIsSecureURL = protocolIs(referrer, "https");
2503 bool referrerIsWebURL = referrerIsSecureURL || protocolIs(referrer, "http");
2504
2505 if (!referrerIsWebURL)
2506 return true;
2507
2508 if (!referrerIsSecureURL)
2509 return false;
2510
2511 bool URLIsSecureURL = url.protocolIs("https");
2512
2513 return !URLIsSecureURL;
2514 }
2515
initialRequest() const2516 const ResourceRequest& FrameLoader::initialRequest() const
2517 {
2518 return activeDocumentLoader()->originalRequest();
2519 }
2520
receivedData(const char * data,int length)2521 void FrameLoader::receivedData(const char* data, int length)
2522 {
2523 activeDocumentLoader()->receivedData(data, length);
2524 }
2525
handleUnimplementablePolicy(const ResourceError & error)2526 void FrameLoader::handleUnimplementablePolicy(const ResourceError& error)
2527 {
2528 m_delegateIsHandlingUnimplementablePolicy = true;
2529 m_client->dispatchUnableToImplementPolicy(error);
2530 m_delegateIsHandlingUnimplementablePolicy = false;
2531 }
2532
cannotShowMIMEType(const ResourceResponse & response)2533 void FrameLoader::cannotShowMIMEType(const ResourceResponse& response)
2534 {
2535 handleUnimplementablePolicy(m_client->cannotShowMIMETypeError(response));
2536 }
2537
interruptionForPolicyChangeError(const ResourceRequest & request)2538 ResourceError FrameLoader::interruptionForPolicyChangeError(const ResourceRequest& request)
2539 {
2540 return m_client->interruptForPolicyChangeError(request);
2541 }
2542
checkNavigationPolicy(const ResourceRequest & newRequest,NavigationPolicyDecisionFunction function,void * argument)2543 void FrameLoader::checkNavigationPolicy(const ResourceRequest& newRequest, NavigationPolicyDecisionFunction function, void* argument)
2544 {
2545 checkNavigationPolicy(newRequest, activeDocumentLoader(), 0, function, argument);
2546 }
2547
checkContentPolicy(const String & MIMEType,ContentPolicyDecisionFunction function,void * argument)2548 void FrameLoader::checkContentPolicy(const String& MIMEType, ContentPolicyDecisionFunction function, void* argument)
2549 {
2550 ASSERT(activeDocumentLoader());
2551
2552 // Always show content with valid substitute data.
2553 if (activeDocumentLoader()->substituteData().isValid()) {
2554 function(argument, PolicyUse);
2555 return;
2556 }
2557
2558 #if ENABLE(FTPDIR)
2559 // Respect the hidden FTP Directory Listing pref so it can be tested even if the policy delegate might otherwise disallow it
2560 Settings* settings = m_frame->settings();
2561 if (settings && settings->forceFTPDirectoryListings() && MIMEType == "application/x-ftp-directory") {
2562 function(argument, PolicyUse);
2563 return;
2564 }
2565 #endif
2566
2567 m_policyCheck.set(function, argument);
2568 m_client->dispatchDecidePolicyForMIMEType(&FrameLoader::continueAfterContentPolicy,
2569 MIMEType, activeDocumentLoader()->request());
2570 }
2571
shouldReloadToHandleUnreachableURL(DocumentLoader * docLoader)2572 bool FrameLoader::shouldReloadToHandleUnreachableURL(DocumentLoader* docLoader)
2573 {
2574 KURL unreachableURL = docLoader->unreachableURL();
2575
2576 if (unreachableURL.isEmpty())
2577 return false;
2578
2579 if (!isBackForwardLoadType(m_policyLoadType))
2580 return false;
2581
2582 // We only treat unreachableURLs specially during the delegate callbacks
2583 // for provisional load errors and navigation policy decisions. The former
2584 // case handles well-formed URLs that can't be loaded, and the latter
2585 // case handles malformed URLs and unknown schemes. Loading alternate content
2586 // at other times behaves like a standard load.
2587 DocumentLoader* compareDocumentLoader = 0;
2588 if (m_delegateIsDecidingNavigationPolicy || m_delegateIsHandlingUnimplementablePolicy)
2589 compareDocumentLoader = m_policyDocumentLoader.get();
2590 else if (m_delegateIsHandlingProvisionalLoadError)
2591 compareDocumentLoader = m_provisionalDocumentLoader.get();
2592
2593 return compareDocumentLoader && unreachableURL == compareDocumentLoader->request().url();
2594 }
2595
reloadWithOverrideEncoding(const String & encoding)2596 void FrameLoader::reloadWithOverrideEncoding(const String& encoding)
2597 {
2598 if (!m_documentLoader)
2599 return;
2600
2601 ResourceRequest request = m_documentLoader->request();
2602 KURL unreachableURL = m_documentLoader->unreachableURL();
2603 if (!unreachableURL.isEmpty())
2604 request.setURL(unreachableURL);
2605
2606 request.setCachePolicy(ReturnCacheDataElseLoad);
2607
2608 RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData());
2609 setPolicyDocumentLoader(loader.get());
2610
2611 loader->setOverrideEncoding(encoding);
2612
2613 loadWithDocumentLoader(loader.get(), FrameLoadTypeReload, 0);
2614 }
2615
reload(bool endToEndReload)2616 void FrameLoader::reload(bool endToEndReload)
2617 {
2618 if (!m_documentLoader)
2619 return;
2620
2621 // If a window is created by javascript, its main frame can have an empty but non-nil URL.
2622 // Reloading in this case will lose the current contents (see 4151001).
2623 if (m_documentLoader->request().url().isEmpty())
2624 return;
2625
2626 ResourceRequest initialRequest = m_documentLoader->request();
2627
2628 // Replace error-page URL with the URL we were trying to reach.
2629 KURL unreachableURL = m_documentLoader->unreachableURL();
2630 if (!unreachableURL.isEmpty())
2631 initialRequest.setURL(unreachableURL);
2632
2633 // Create a new document loader for the reload, this will become m_documentLoader eventually,
2634 // but first it has to be the "policy" document loader, and then the "provisional" document loader.
2635 RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(initialRequest, SubstituteData());
2636
2637 ResourceRequest& request = loader->request();
2638
2639 // FIXME: We don't have a mechanism to revalidate the main resource without reloading at the moment.
2640 request.setCachePolicy(ReloadIgnoringCacheData);
2641
2642 // If we're about to re-post, set up action so the application can warn the user.
2643 if (request.httpMethod() == "POST")
2644 loader->setTriggeringAction(NavigationAction(request.url(), NavigationTypeFormResubmitted));
2645
2646 loader->setOverrideEncoding(m_documentLoader->overrideEncoding());
2647
2648 loadWithDocumentLoader(loader.get(), endToEndReload ? FrameLoadTypeReloadFromOrigin : FrameLoadTypeReload, 0);
2649 }
2650
canAccessAncestor(const SecurityOrigin * activeSecurityOrigin,Frame * targetFrame)2651 static bool canAccessAncestor(const SecurityOrigin* activeSecurityOrigin, Frame* targetFrame)
2652 {
2653 // targetFrame can be NULL when we're trying to navigate a top-level frame
2654 // that has a NULL opener.
2655 if (!targetFrame)
2656 return false;
2657
2658 for (Frame* ancestorFrame = targetFrame; ancestorFrame; ancestorFrame = ancestorFrame->tree()->parent()) {
2659 Document* ancestorDocument = ancestorFrame->document();
2660 if (!ancestorDocument)
2661 return true;
2662
2663 const SecurityOrigin* ancestorSecurityOrigin = ancestorDocument->securityOrigin();
2664 if (activeSecurityOrigin->canAccess(ancestorSecurityOrigin))
2665 return true;
2666 }
2667
2668 return false;
2669 }
2670
shouldAllowNavigation(Frame * targetFrame) const2671 bool FrameLoader::shouldAllowNavigation(Frame* targetFrame) const
2672 {
2673 // The navigation change is safe if the active frame is:
2674 // - in the same security origin as the target or one of the target's
2675 // ancestors.
2676 //
2677 // Or the target frame is:
2678 // - a top-level frame in the frame hierarchy and the active frame can
2679 // navigate the target frame's opener per above.
2680
2681 if (!targetFrame)
2682 return true;
2683
2684 // Performance optimization.
2685 if (m_frame == targetFrame)
2686 return true;
2687
2688 // Let a frame navigate the top-level window that contains it. This is
2689 // important to allow because it lets a site "frame-bust" (escape from a
2690 // frame created by another web site).
2691 if (targetFrame == m_frame->tree()->top())
2692 return true;
2693
2694 Document* activeDocument = m_frame->document();
2695 ASSERT(activeDocument);
2696 const SecurityOrigin* activeSecurityOrigin = activeDocument->securityOrigin();
2697
2698 // For top-level windows, check the opener.
2699 if (!targetFrame->tree()->parent() && canAccessAncestor(activeSecurityOrigin, targetFrame->loader()->opener()))
2700 return true;
2701
2702 // In general, check the frame's ancestors.
2703 if (canAccessAncestor(activeSecurityOrigin, targetFrame))
2704 return true;
2705
2706 Settings* settings = targetFrame->settings();
2707 if (settings && !settings->privateBrowsingEnabled()) {
2708 Document* targetDocument = targetFrame->document();
2709 // FIXME: this error message should contain more specifics of why the navigation change is not allowed.
2710 String message = String::format("Unsafe JavaScript attempt to initiate a navigation change for frame with URL %s from frame with URL %s.\n",
2711 targetDocument->url().string().utf8().data(), activeDocument->url().string().utf8().data());
2712
2713 // FIXME: should we print to the console of the activeFrame as well?
2714 targetFrame->domWindow()->console()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, message, 1, String());
2715 }
2716
2717 return false;
2718 }
2719
stopLoadingSubframes()2720 void FrameLoader::stopLoadingSubframes()
2721 {
2722 for (RefPtr<Frame> child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
2723 child->loader()->stopAllLoaders();
2724 }
2725
stopAllLoaders(DatabasePolicy databasePolicy)2726 void FrameLoader::stopAllLoaders(DatabasePolicy databasePolicy)
2727 {
2728 if (m_unloadEventBeingDispatched)
2729 return;
2730
2731 // If this method is called from within this method, infinite recursion can occur (3442218). Avoid this.
2732 if (m_inStopAllLoaders)
2733 return;
2734
2735 m_inStopAllLoaders = true;
2736
2737 stopPolicyCheck();
2738
2739 stopLoadingSubframes();
2740 if (m_provisionalDocumentLoader)
2741 m_provisionalDocumentLoader->stopLoading(databasePolicy);
2742 if (m_documentLoader)
2743 m_documentLoader->stopLoading(databasePolicy);
2744
2745 setProvisionalDocumentLoader(0);
2746
2747 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
2748 if (m_documentLoader)
2749 m_documentLoader->clearArchiveResources();
2750 #endif
2751
2752 m_inStopAllLoaders = false;
2753 }
2754
stopForUserCancel(bool deferCheckLoadComplete)2755 void FrameLoader::stopForUserCancel(bool deferCheckLoadComplete)
2756 {
2757 stopAllLoaders();
2758
2759 if (deferCheckLoadComplete)
2760 scheduleCheckLoadComplete();
2761 else if (m_frame->page())
2762 checkLoadComplete();
2763 }
2764
activeDocumentLoader() const2765 DocumentLoader* FrameLoader::activeDocumentLoader() const
2766 {
2767 if (m_state == FrameStateProvisional)
2768 return m_provisionalDocumentLoader.get();
2769 return m_documentLoader.get();
2770 }
2771
isLoading() const2772 bool FrameLoader::isLoading() const
2773 {
2774 DocumentLoader* docLoader = activeDocumentLoader();
2775 if (!docLoader)
2776 return false;
2777 return docLoader->isLoadingMainResource() || docLoader->isLoadingSubresources() || docLoader->isLoadingPlugIns();
2778 }
2779
frameHasLoaded() const2780 bool FrameLoader::frameHasLoaded() const
2781 {
2782 return m_committedFirstRealDocumentLoad || (m_provisionalDocumentLoader && !m_creatingInitialEmptyDocument);
2783 }
2784
setDocumentLoader(DocumentLoader * loader)2785 void FrameLoader::setDocumentLoader(DocumentLoader* loader)
2786 {
2787 if (!loader && !m_documentLoader)
2788 return;
2789
2790 ASSERT(loader != m_documentLoader);
2791 ASSERT(!loader || loader->frameLoader() == this);
2792
2793 m_client->prepareForDataSourceReplacement();
2794 detachChildren();
2795 if (m_documentLoader)
2796 m_documentLoader->detachFromFrame();
2797
2798 m_documentLoader = loader;
2799 }
2800
setPolicyDocumentLoader(DocumentLoader * loader)2801 void FrameLoader::setPolicyDocumentLoader(DocumentLoader* loader)
2802 {
2803 if (m_policyDocumentLoader == loader)
2804 return;
2805
2806 ASSERT(m_frame);
2807 if (loader)
2808 loader->setFrame(m_frame);
2809 if (m_policyDocumentLoader
2810 && m_policyDocumentLoader != m_provisionalDocumentLoader
2811 && m_policyDocumentLoader != m_documentLoader)
2812 m_policyDocumentLoader->detachFromFrame();
2813
2814 m_policyDocumentLoader = loader;
2815 }
2816
setProvisionalDocumentLoader(DocumentLoader * loader)2817 void FrameLoader::setProvisionalDocumentLoader(DocumentLoader* loader)
2818 {
2819 ASSERT(!loader || !m_provisionalDocumentLoader);
2820 ASSERT(!loader || loader->frameLoader() == this);
2821
2822 if (m_provisionalDocumentLoader && m_provisionalDocumentLoader != m_documentLoader)
2823 m_provisionalDocumentLoader->detachFromFrame();
2824
2825 m_provisionalDocumentLoader = loader;
2826 }
2827
timeOfLastCompletedLoad()2828 double FrameLoader::timeOfLastCompletedLoad()
2829 {
2830 return storedTimeOfLastCompletedLoad;
2831 }
2832
setState(FrameState newState)2833 void FrameLoader::setState(FrameState newState)
2834 {
2835 m_state = newState;
2836
2837 if (newState == FrameStateProvisional)
2838 provisionalLoadStarted();
2839 else if (newState == FrameStateComplete) {
2840 frameLoadCompleted();
2841 storedTimeOfLastCompletedLoad = currentTime();
2842 if (m_documentLoader)
2843 m_documentLoader->stopRecordingResponses();
2844 }
2845 }
2846
clearProvisionalLoad()2847 void FrameLoader::clearProvisionalLoad()
2848 {
2849 setProvisionalDocumentLoader(0);
2850 if (Page* page = m_frame->page())
2851 page->progress()->progressCompleted(m_frame);
2852 setState(FrameStateComplete);
2853 }
2854
markLoadComplete()2855 void FrameLoader::markLoadComplete()
2856 {
2857 setState(FrameStateComplete);
2858 }
2859
commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage)2860 void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage)
2861 {
2862 RefPtr<CachedPage> cachedPage = prpCachedPage;
2863 RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader;
2864
2865 LOG(Loading, "WebCoreLoading %s: About to commit provisional load from previous URL %s", m_frame->tree()->name().string().utf8().data(), m_URL.string().utf8().data());
2866
2867 // Check to see if we need to cache the page we are navigating away from into the back/forward cache.
2868 // We are doing this here because we know for sure that a new page is about to be loaded.
2869 cachePageForHistoryItem(m_currentHistoryItem.get());
2870
2871 if (m_loadType != FrameLoadTypeReplace)
2872 closeOldDataSources();
2873
2874 if (!cachedPage && !m_creatingInitialEmptyDocument)
2875 m_client->makeRepresentation(pdl.get());
2876
2877 transitionToCommitted(cachedPage);
2878
2879 // Call clientRedirectCancelledOrFinished() here so that the frame load delegate is notified that the redirect's
2880 // status has changed, if there was a redirect. The frame load delegate may have saved some state about
2881 // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:. Since we are
2882 // just about to commit a new page, there cannot possibly be a pending redirect at this point.
2883 if (m_sentRedirectNotification)
2884 clientRedirectCancelledOrFinished(false);
2885
2886 if (cachedPage && cachedPage->document()) {
2887 open(*cachedPage);
2888 cachedPage->clear();
2889 } else {
2890 KURL url = pdl->substituteData().responseURL();
2891 if (url.isEmpty())
2892 url = pdl->url();
2893 if (url.isEmpty())
2894 url = pdl->responseURL();
2895 if (url.isEmpty())
2896 url = blankURL();
2897
2898 didOpenURL(url);
2899 }
2900
2901 LOG(Loading, "WebCoreLoading %s: Finished committing provisional load to URL %s", m_frame->tree()->name().string().utf8().data(), m_URL.string().utf8().data());
2902
2903 if (m_loadType == FrameLoadTypeStandard && m_documentLoader->isClientRedirect())
2904 updateHistoryForClientRedirect();
2905
2906 if (m_documentLoader->isLoadingFromCachedPage()) {
2907 m_frame->document()->documentDidBecomeActive();
2908
2909 // Force a layout to update view size and thereby update scrollbars.
2910 m_client->forceLayout();
2911
2912 const ResponseVector& responses = m_documentLoader->responses();
2913 size_t count = responses.size();
2914 for (size_t i = 0; i < count; i++) {
2915 const ResourceResponse& response = responses[i];
2916 // FIXME: If the WebKit client changes or cancels the request, this is not respected.
2917 ResourceError error;
2918 unsigned long identifier;
2919 ResourceRequest request(response.url());
2920 requestFromDelegate(request, identifier, error);
2921 // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
2922 // However, with today's computers and networking speeds, this won't happen in practice.
2923 // Could be an issue with a giant local file.
2924 sendRemainingDelegateMessages(identifier, response, static_cast<int>(response.expectedContentLength()), error);
2925 }
2926
2927 pageCache()->remove(m_currentHistoryItem.get());
2928
2929 m_documentLoader->setPrimaryLoadComplete(true);
2930
2931 // FIXME: Why only this frame and not parent frames?
2932 checkLoadCompleteForThisFrame();
2933 }
2934 }
2935
transitionToCommitted(PassRefPtr<CachedPage> cachedPage)2936 void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage)
2937 {
2938 ASSERT(m_client->hasWebView());
2939 ASSERT(m_state == FrameStateProvisional);
2940
2941 if (m_state != FrameStateProvisional)
2942 return;
2943
2944 m_client->setCopiesOnScroll();
2945 updateHistoryForCommit();
2946
2947 // The call to closeURL() invokes the unload event handler, which can execute arbitrary
2948 // JavaScript. If the script initiates a new load, we need to abandon the current load,
2949 // or the two will stomp each other.
2950 DocumentLoader* pdl = m_provisionalDocumentLoader.get();
2951 if (m_documentLoader)
2952 closeURL();
2953 if (pdl != m_provisionalDocumentLoader)
2954 return;
2955
2956 // Nothing else can interupt this commit - set the Provisional->Committed transition in stone
2957 if (m_documentLoader)
2958 m_documentLoader->stopLoadingSubresources();
2959 if (m_documentLoader)
2960 m_documentLoader->stopLoadingPlugIns();
2961
2962 setDocumentLoader(m_provisionalDocumentLoader.get());
2963 setProvisionalDocumentLoader(0);
2964 setState(FrameStateCommittedPage);
2965
2966 // Handle adding the URL to the back/forward list.
2967 DocumentLoader* dl = m_documentLoader.get();
2968 String ptitle = dl->title();
2969
2970 switch (m_loadType) {
2971 case FrameLoadTypeForward:
2972 case FrameLoadTypeBack:
2973 case FrameLoadTypeBackWMLDeckNotAccessible:
2974 case FrameLoadTypeIndexedBackForward:
2975 if (Page* page = m_frame->page())
2976 if (page->backForwardList()) {
2977 updateHistoryForBackForwardNavigation();
2978
2979 // Create a document view for this document, or used the cached view.
2980 if (cachedPage) {
2981 DocumentLoader* cachedDocumentLoader = cachedPage->documentLoader();
2982 ASSERT(cachedDocumentLoader);
2983 cachedDocumentLoader->setFrame(m_frame);
2984 m_client->transitionToCommittedFromCachedFrame(cachedPage->cachedMainFrame());
2985
2986 } else
2987 m_client->transitionToCommittedForNewPage();
2988 }
2989 break;
2990
2991 case FrameLoadTypeReload:
2992 case FrameLoadTypeReloadFromOrigin:
2993 case FrameLoadTypeSame:
2994 case FrameLoadTypeReplace:
2995 updateHistoryForReload();
2996 m_client->transitionToCommittedForNewPage();
2997 break;
2998
2999 case FrameLoadTypeStandard:
3000 updateHistoryForStandardLoad();
3001 #ifndef BUILDING_ON_TIGER
3002 // This code was originally added for a Leopard performance imporvement. We decided to
3003 // ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>.
3004 if (m_frame->view())
3005 m_frame->view()->setScrollbarsSuppressed(true);
3006 #endif
3007 m_client->transitionToCommittedForNewPage();
3008 break;
3009
3010 case FrameLoadTypeRedirectWithLockedBackForwardList:
3011 updateHistoryForRedirectWithLockedBackForwardList();
3012 m_client->transitionToCommittedForNewPage();
3013 break;
3014
3015 // FIXME Remove this check when dummy ds is removed (whatever "dummy ds" is).
3016 // An exception should be thrown if we're in the FrameLoadTypeUninitialized state.
3017 default:
3018 ASSERT_NOT_REACHED();
3019 }
3020
3021 m_responseMIMEType = dl->responseMIMEType();
3022
3023 // Tell the client we've committed this URL.
3024 ASSERT(m_frame->view());
3025
3026 if (m_creatingInitialEmptyDocument)
3027 return;
3028
3029 m_committedFirstRealDocumentLoad = true;
3030
3031 if (!m_client->hasHTMLView())
3032 receivedFirstData();
3033 else if (cachedPage) {
3034 // For non-cached HTML pages, these methods are called in receivedFirstData().
3035 dispatchDidCommitLoad();
3036
3037 // If we have a title let the WebView know about it.
3038 if (!ptitle.isNull())
3039 m_client->dispatchDidReceiveTitle(ptitle);
3040 }
3041 }
3042
clientRedirectCancelledOrFinished(bool cancelWithLoadInProgress)3043 void FrameLoader::clientRedirectCancelledOrFinished(bool cancelWithLoadInProgress)
3044 {
3045 // Note that -webView:didCancelClientRedirectForFrame: is called on the frame load delegate even if
3046 // the redirect succeeded. We should either rename this API, or add a new method, like
3047 // -webView:didFinishClientRedirectForFrame:
3048 m_client->dispatchDidCancelClientRedirect();
3049
3050 if (!cancelWithLoadInProgress)
3051 m_quickRedirectComing = false;
3052
3053 m_sentRedirectNotification = false;
3054 }
3055
clientRedirected(const KURL & url,double seconds,double fireDate,bool lockBackForwardList)3056 void FrameLoader::clientRedirected(const KURL& url, double seconds, double fireDate, bool lockBackForwardList)
3057 {
3058 m_client->dispatchWillPerformClientRedirect(url, seconds, fireDate);
3059
3060 // Remember that we sent a redirect notification to the frame load delegate so that when we commit
3061 // the next provisional load, we can send a corresponding -webView:didCancelClientRedirectForFrame:
3062 m_sentRedirectNotification = true;
3063
3064 // If a "quick" redirect comes in, we set a special mode so we treat the next
3065 // load as part of the original navigation. If we don't have a document loader, we have
3066 // no "original" load on which to base a redirect, so we treat the redirect as a normal load.
3067 // Loads triggered by JavaScript form submissions never count as quick redirects.
3068 m_quickRedirectComing = lockBackForwardList && m_documentLoader && !m_isExecutingJavaScriptFormAction;
3069 }
3070
shouldReload(const KURL & currentURL,const KURL & destinationURL)3071 bool FrameLoader::shouldReload(const KURL& currentURL, const KURL& destinationURL)
3072 {
3073 #if ENABLE(WML)
3074 // All WML decks are supposed to be reloaded, even within the same URL fragment
3075 if (frameContainsWMLContent(m_frame))
3076 return true;
3077 #endif
3078
3079 // This function implements the rule: "Don't reload if navigating by fragment within
3080 // the same URL, but do reload if going to a new URL or to the same URL with no
3081 // fragment identifier at all."
3082 if (!destinationURL.hasFragmentIdentifier())
3083 return true;
3084 return !equalIgnoringFragmentIdentifier(currentURL, destinationURL);
3085 }
3086
closeOldDataSources()3087 void FrameLoader::closeOldDataSources()
3088 {
3089 // FIXME: Is it important for this traversal to be postorder instead of preorder?
3090 // If so, add helpers for postorder traversal, and use them. If not, then lets not
3091 // use a recursive algorithm here.
3092 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
3093 child->loader()->closeOldDataSources();
3094
3095 if (m_documentLoader)
3096 m_client->dispatchWillClose();
3097
3098 m_client->setMainFrameDocumentReady(false); // stop giving out the actual DOMDocument to observers
3099 }
3100
open(CachedPage & cachedPage)3101 void FrameLoader::open(CachedPage& cachedPage)
3102 {
3103 ASSERT(!m_frame->tree()->parent());
3104 ASSERT(m_frame->page());
3105 ASSERT(m_frame->page()->mainFrame() == m_frame);
3106
3107 cancelRedirection();
3108
3109 // We still have to close the previous part page.
3110 closeURL();
3111
3112 // Delete old status bar messages (if it _was_ activated on last URL).
3113 if (m_frame->script()->isEnabled()) {
3114 m_frame->setJSStatusBarText(String());
3115 m_frame->setJSDefaultStatusBarText(String());
3116 }
3117
3118 open(*cachedPage.cachedMainFrame());
3119 cachedPage.restore(m_frame->page());
3120
3121 checkCompleted();
3122 }
3123
open(CachedFrame & cachedFrame)3124 void FrameLoader::open(CachedFrame& cachedFrame)
3125 {
3126 m_isComplete = false;
3127
3128 // Don't re-emit the load event.
3129 m_didCallImplicitClose = true;
3130
3131 KURL url = cachedFrame.url();
3132
3133 if (url.protocolInHTTPFamily() && !url.host().isEmpty() && url.path().isEmpty())
3134 url.setPath("/");
3135
3136 m_URL = url;
3137 m_workingURL = url;
3138
3139 started();
3140
3141 clear();
3142
3143 Document* document = cachedFrame.document();
3144 ASSERT(document);
3145 document->setInPageCache(false);
3146
3147 m_needsClear = true;
3148 m_isComplete = false;
3149 m_didCallImplicitClose = false;
3150 m_outgoingReferrer = url.string();
3151
3152 FrameView* view = cachedFrame.view();
3153
3154 // When navigating to a CachedFrame its FrameView should never be null. If it is we'll crash in creative ways downstream.
3155 ASSERT(view);
3156 if (view)
3157 view->setWasScrolledByUser(false);
3158 m_frame->setView(view);
3159
3160 m_frame->setDocument(document);
3161 m_frame->setDOMWindow(cachedFrame.domWindow());
3162 m_frame->domWindow()->setURL(document->url());
3163 m_frame->domWindow()->setSecurityOrigin(document->securityOrigin());
3164
3165 m_decoder = document->decoder();
3166
3167 updateFirstPartyForCookies();
3168
3169 cachedFrame.restore();
3170 }
3171
isStopping() const3172 bool FrameLoader::isStopping() const
3173 {
3174 return activeDocumentLoader()->isStopping();
3175 }
3176
finishedLoading()3177 void FrameLoader::finishedLoading()
3178 {
3179 // Retain because the stop may release the last reference to it.
3180 RefPtr<Frame> protect(m_frame);
3181
3182 RefPtr<DocumentLoader> dl = activeDocumentLoader();
3183 dl->finishedLoading();
3184 if (!dl->mainDocumentError().isNull() || !dl->frameLoader())
3185 return;
3186 dl->setPrimaryLoadComplete(true);
3187 m_client->dispatchDidLoadMainResource(dl.get());
3188 checkLoadComplete();
3189 }
3190
isHostedByObjectElement() const3191 bool FrameLoader::isHostedByObjectElement() const
3192 {
3193 HTMLFrameOwnerElement* owner = m_frame->ownerElement();
3194 return owner && owner->hasTagName(objectTag);
3195 }
3196
isLoadingMainFrame() const3197 bool FrameLoader::isLoadingMainFrame() const
3198 {
3199 Page* page = m_frame->page();
3200 return page && m_frame == page->mainFrame();
3201 }
3202
canShowMIMEType(const String & MIMEType) const3203 bool FrameLoader::canShowMIMEType(const String& MIMEType) const
3204 {
3205 return m_client->canShowMIMEType(MIMEType);
3206 }
3207
representationExistsForURLScheme(const String & URLScheme)3208 bool FrameLoader::representationExistsForURLScheme(const String& URLScheme)
3209 {
3210 return m_client->representationExistsForURLScheme(URLScheme);
3211 }
3212
generatedMIMETypeForURLScheme(const String & URLScheme)3213 String FrameLoader::generatedMIMETypeForURLScheme(const String& URLScheme)
3214 {
3215 return m_client->generatedMIMETypeForURLScheme(URLScheme);
3216 }
3217
cancelContentPolicyCheck()3218 void FrameLoader::cancelContentPolicyCheck()
3219 {
3220 m_client->cancelPolicyCheck();
3221 m_policyCheck.clear();
3222 }
3223
didReceiveServerRedirectForProvisionalLoadForFrame()3224 void FrameLoader::didReceiveServerRedirectForProvisionalLoadForFrame()
3225 {
3226 m_client->dispatchDidReceiveServerRedirectForProvisionalLoad();
3227 }
3228
finishedLoadingDocument(DocumentLoader * loader)3229 void FrameLoader::finishedLoadingDocument(DocumentLoader* loader)
3230 {
3231 // FIXME: Platforms shouldn't differ here!
3232 #if PLATFORM(WIN) || PLATFORM(CHROMIUM) || defined(ANDROID)
3233 if (m_creatingInitialEmptyDocument)
3234 return;
3235 #endif
3236
3237 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
3238 // If loading a webarchive, run through webarchive machinery
3239 const String& responseMIMEType = loader->responseMIMEType();
3240
3241 // FIXME: Mac's FrameLoaderClient::finishedLoading() method does work that is required even with Archive loads
3242 // so we still need to call it. Other platforms should only call finishLoading for non-archive loads
3243 // That work should be factored out so this #ifdef can be removed
3244 #if PLATFORM(MAC)
3245 m_client->finishedLoading(loader);
3246 if (!ArchiveFactory::isArchiveMimeType(responseMIMEType))
3247 return;
3248 #else
3249 if (!ArchiveFactory::isArchiveMimeType(responseMIMEType)) {
3250 m_client->finishedLoading(loader);
3251 return;
3252 }
3253 #endif
3254
3255 RefPtr<Archive> archive(ArchiveFactory::create(loader->mainResourceData().get(), responseMIMEType));
3256 if (!archive)
3257 return;
3258
3259 loader->addAllArchiveResources(archive.get());
3260
3261 ArchiveResource* mainResource = archive->mainResource();
3262 loader->setParsedArchiveData(mainResource->data());
3263
3264 m_responseMIMEType = mainResource->mimeType();
3265 didOpenURL(mainResource->url());
3266
3267 String userChosenEncoding = documentLoader()->overrideEncoding();
3268 bool encodingIsUserChosen = !userChosenEncoding.isNull();
3269 setEncoding(encodingIsUserChosen ? userChosenEncoding : mainResource->textEncoding(), encodingIsUserChosen);
3270
3271 ASSERT(m_frame->document());
3272
3273 addData(mainResource->data()->data(), mainResource->data()->size());
3274 #else
3275 m_client->finishedLoading(loader);
3276 #endif // ARCHIVE
3277 }
3278
isReplacing() const3279 bool FrameLoader::isReplacing() const
3280 {
3281 return m_loadType == FrameLoadTypeReplace;
3282 }
3283
setReplacing()3284 void FrameLoader::setReplacing()
3285 {
3286 m_loadType = FrameLoadTypeReplace;
3287 }
3288
revertToProvisional(DocumentLoader * loader)3289 void FrameLoader::revertToProvisional(DocumentLoader* loader)
3290 {
3291 m_client->revertToProvisionalState(loader);
3292 }
3293
subframeIsLoading() const3294 bool FrameLoader::subframeIsLoading() const
3295 {
3296 // It's most likely that the last added frame is the last to load so we walk backwards.
3297 for (Frame* child = m_frame->tree()->lastChild(); child; child = child->tree()->previousSibling()) {
3298 FrameLoader* childLoader = child->loader();
3299 DocumentLoader* documentLoader = childLoader->documentLoader();
3300 if (documentLoader && documentLoader->isLoadingInAPISense())
3301 return true;
3302 documentLoader = childLoader->provisionalDocumentLoader();
3303 if (documentLoader && documentLoader->isLoadingInAPISense())
3304 return true;
3305 }
3306 return false;
3307 }
3308
willChangeTitle(DocumentLoader * loader)3309 void FrameLoader::willChangeTitle(DocumentLoader* loader)
3310 {
3311 m_client->willChangeTitle(loader);
3312 }
3313
loadType() const3314 FrameLoadType FrameLoader::loadType() const
3315 {
3316 return m_loadType;
3317 }
3318
subresourceCachePolicy() const3319 CachePolicy FrameLoader::subresourceCachePolicy() const
3320 {
3321 if (m_isComplete)
3322 return CachePolicyVerify;
3323
3324 if (m_loadType == FrameLoadTypeReloadFromOrigin)
3325 return CachePolicyReload;
3326
3327 if (Frame* parentFrame = m_frame->tree()->parent()) {
3328 CachePolicy parentCachePolicy = parentFrame->loader()->subresourceCachePolicy();
3329 if (parentCachePolicy != CachePolicyVerify)
3330 return parentCachePolicy;
3331 }
3332
3333 // FIXME: POST documents are always Reloads, but their subresources should still be Revalidate.
3334 // If we bring the CachePolicy.h and ResourceRequest cache policy enums in sync with each other and
3335 // remember "Revalidate" in ResourceRequests, we can remove this "POST" check and return either "Reload"
3336 // or "Revalidate" if the DocumentLoader was requested with either.
3337 const ResourceRequest& request(documentLoader()->request());
3338 if (request.cachePolicy() == ReloadIgnoringCacheData && !equalIgnoringCase(request.httpMethod(), "post"))
3339 return CachePolicyRevalidate;
3340
3341 if (m_loadType == FrameLoadTypeReload)
3342 return CachePolicyRevalidate;
3343
3344 return CachePolicyVerify;
3345 }
3346
stopPolicyCheck()3347 void FrameLoader::stopPolicyCheck()
3348 {
3349 m_client->cancelPolicyCheck();
3350 PolicyCheck check = m_policyCheck;
3351 m_policyCheck.clear();
3352 check.cancel();
3353 }
3354
checkLoadCompleteForThisFrame()3355 void FrameLoader::checkLoadCompleteForThisFrame()
3356 {
3357 ASSERT(m_client->hasWebView());
3358
3359 switch (m_state) {
3360 case FrameStateProvisional: {
3361 if (m_delegateIsHandlingProvisionalLoadError)
3362 return;
3363
3364 RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader;
3365 if (!pdl)
3366 return;
3367
3368 // If we've received any errors we may be stuck in the provisional state and actually complete.
3369 const ResourceError& error = pdl->mainDocumentError();
3370 if (error.isNull())
3371 return;
3372
3373 // Check all children first.
3374 RefPtr<HistoryItem> item;
3375 if (Page* page = m_frame->page())
3376 if (isBackForwardLoadType(loadType()) && m_frame == page->mainFrame())
3377 item = m_currentHistoryItem;
3378
3379 bool shouldReset = true;
3380 if (!(pdl->isLoadingInAPISense() && !pdl->isStopping())) {
3381 m_delegateIsHandlingProvisionalLoadError = true;
3382 m_client->dispatchDidFailProvisionalLoad(error);
3383 m_delegateIsHandlingProvisionalLoadError = false;
3384
3385 // FIXME: can stopping loading here possibly have any effect, if isLoading is false,
3386 // which it must be to be in this branch of the if? And is it OK to just do a full-on
3387 // stopAllLoaders instead of stopLoadingSubframes?
3388 stopLoadingSubframes();
3389 pdl->stopLoading();
3390
3391 // Finish resetting the load state, but only if another load hasn't been started by the
3392 // delegate callback.
3393 if (pdl == m_provisionalDocumentLoader)
3394 clearProvisionalLoad();
3395 else if (m_provisionalDocumentLoader) {
3396 KURL unreachableURL = m_provisionalDocumentLoader->unreachableURL();
3397 if (!unreachableURL.isEmpty() && unreachableURL == pdl->request().url())
3398 shouldReset = false;
3399 }
3400 }
3401 if (shouldReset && item)
3402 if (Page* page = m_frame->page()) {
3403 page->backForwardList()->goToItem(item.get());
3404 Settings* settings = m_frame->settings();
3405 page->setGlobalHistoryItem((!settings || settings->privateBrowsingEnabled()) ? 0 : item.get());
3406 }
3407 return;
3408 }
3409
3410 case FrameStateCommittedPage: {
3411 DocumentLoader* dl = m_documentLoader.get();
3412 if (!dl || (dl->isLoadingInAPISense() && !dl->isStopping()))
3413 return;
3414
3415 markLoadComplete();
3416
3417 // FIXME: Is this subsequent work important if we already navigated away?
3418 // Maybe there are bugs because of that, or extra work we can skip because
3419 // the new page is ready.
3420
3421 m_client->forceLayoutForNonHTML();
3422
3423 // If the user had a scroll point, scroll to it, overriding the anchor point if any.
3424 if (Page* page = m_frame->page())
3425 if ((isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadTypeReload || m_loadType == FrameLoadTypeReloadFromOrigin) && page->backForwardList())
3426 restoreScrollPositionAndViewState();
3427
3428 if (m_creatingInitialEmptyDocument || !m_committedFirstRealDocumentLoad)
3429 return;
3430
3431 const ResourceError& error = dl->mainDocumentError();
3432 #ifndef NDEBUG
3433 m_didDispatchDidCommitLoad = false;
3434 #endif
3435 if (!error.isNull())
3436 m_client->dispatchDidFailLoad(error);
3437 else
3438 m_client->dispatchDidFinishLoad();
3439
3440 if (Page* page = m_frame->page())
3441 page->progress()->progressCompleted(m_frame);
3442
3443 #ifdef ANDROID_INSTRUMENT
3444 if (!m_frame->tree()->parent() && m_frame->document()->renderArena())
3445 android::TimeCounter::report(m_URL, cache()->getLiveSize(), cache()->getDeadSize(),
3446 m_frame->document()->renderArena()->reportPoolSize());
3447 #endif
3448 return;
3449 }
3450
3451 case FrameStateComplete:
3452 frameLoadCompleted();
3453 return;
3454 }
3455
3456 ASSERT_NOT_REACHED();
3457 }
3458
continueAfterContentPolicy(PolicyAction policy)3459 void FrameLoader::continueAfterContentPolicy(PolicyAction policy)
3460 {
3461 PolicyCheck check = m_policyCheck;
3462 m_policyCheck.clear();
3463 check.call(policy);
3464 }
3465
continueLoadAfterWillSubmitForm(PolicyAction)3466 void FrameLoader::continueLoadAfterWillSubmitForm(PolicyAction)
3467 {
3468 if (!m_provisionalDocumentLoader)
3469 return;
3470
3471 // DocumentLoader calls back to our prepareForLoadStart
3472 m_provisionalDocumentLoader->prepareForLoadStart();
3473
3474 // The load might be cancelled inside of prepareForLoadStart(), nulling out the m_provisionalDocumentLoader,
3475 // so we need to null check it again.
3476 if (!m_provisionalDocumentLoader)
3477 return;
3478
3479 DocumentLoader* activeDocLoader = activeDocumentLoader();
3480 if (activeDocLoader && activeDocLoader->isLoadingMainResource())
3481 return;
3482
3483 m_provisionalDocumentLoader->setLoadingFromCachedPage(false);
3484
3485 unsigned long identifier = 0;
3486
3487 if (Page* page = m_frame->page()) {
3488 identifier = page->progress()->createUniqueIdentifier();
3489 dispatchAssignIdentifierToInitialRequest(identifier, m_provisionalDocumentLoader.get(), m_provisionalDocumentLoader->originalRequest());
3490 }
3491
3492 if (!m_provisionalDocumentLoader->startLoadingMainResource(identifier))
3493 m_provisionalDocumentLoader->updateLoading();
3494 }
3495
didFirstLayout()3496 void FrameLoader::didFirstLayout()
3497 {
3498 if (Page* page = m_frame->page())
3499 if (isBackForwardLoadType(m_loadType) && page->backForwardList())
3500 restoreScrollPositionAndViewState();
3501
3502 m_firstLayoutDone = true;
3503 m_client->dispatchDidFirstLayout();
3504 }
3505
didFirstVisuallyNonEmptyLayout()3506 void FrameLoader::didFirstVisuallyNonEmptyLayout()
3507 {
3508 m_client->dispatchDidFirstVisuallyNonEmptyLayout();
3509 }
3510
frameLoadCompleted()3511 void FrameLoader::frameLoadCompleted()
3512 {
3513 // Note: Can be called multiple times.
3514
3515 m_client->frameLoadCompleted();
3516
3517 // Even if already complete, we might have set a previous item on a frame that
3518 // didn't do any data loading on the past transaction. Make sure to clear these out.
3519 m_previousHistoryItem = 0;
3520
3521 // After a canceled provisional load, firstLayoutDone is false.
3522 // Reset it to true if we're displaying a page.
3523 if (m_documentLoader)
3524 m_firstLayoutDone = true;
3525 }
3526
firstLayoutDone() const3527 bool FrameLoader::firstLayoutDone() const
3528 {
3529 return m_firstLayoutDone;
3530 }
3531
detachChildren()3532 void FrameLoader::detachChildren()
3533 {
3534 // FIXME: Is it really necessary to do this in reverse order?
3535 Frame* previous;
3536 for (Frame* child = m_frame->tree()->lastChild(); child; child = previous) {
3537 previous = child->tree()->previousSibling();
3538 child->loader()->detachFromParent();
3539 }
3540 }
3541
closeAndRemoveChild(Frame * child)3542 void FrameLoader::closeAndRemoveChild(Frame* child)
3543 {
3544 child->tree()->detachFromParent();
3545
3546 child->setView(0);
3547 if (child->ownerElement())
3548 child->page()->decrementFrameCount();
3549 child->pageDestroyed();
3550
3551 m_frame->tree()->removeChild(child);
3552 }
3553
recursiveCheckLoadComplete()3554 void FrameLoader::recursiveCheckLoadComplete()
3555 {
3556 Vector<RefPtr<Frame>, 10> frames;
3557
3558 for (RefPtr<Frame> frame = m_frame->tree()->firstChild(); frame; frame = frame->tree()->nextSibling())
3559 frames.append(frame);
3560
3561 unsigned size = frames.size();
3562 for (unsigned i = 0; i < size; i++)
3563 frames[i]->loader()->recursiveCheckLoadComplete();
3564
3565 checkLoadCompleteForThisFrame();
3566 }
3567
3568 // Called every time a resource is completely loaded, or an error is received.
checkLoadComplete()3569 void FrameLoader::checkLoadComplete()
3570 {
3571 ASSERT(m_client->hasWebView());
3572
3573 // FIXME: Always traversing the entire frame tree is a bit inefficient, but
3574 // is currently needed in order to null out the previous history item for all frames.
3575 if (Page* page = m_frame->page())
3576 page->mainFrame()->loader()->recursiveCheckLoadComplete();
3577 }
3578
numPendingOrLoadingRequests(bool recurse) const3579 int FrameLoader::numPendingOrLoadingRequests(bool recurse) const
3580 {
3581 if (!recurse)
3582 return numRequests(m_frame->document());
3583
3584 int count = 0;
3585 for (Frame* frame = m_frame; frame; frame = frame->tree()->traverseNext(m_frame))
3586 count += numRequests(frame->document());
3587 return count;
3588 }
3589
userAgent(const KURL & url) const3590 String FrameLoader::userAgent(const KURL& url) const
3591 {
3592 return m_client->userAgent(url);
3593 }
3594
tokenizerProcessedData()3595 void FrameLoader::tokenizerProcessedData()
3596 {
3597 checkCompleted();
3598 }
3599
handledOnloadEvents()3600 void FrameLoader::handledOnloadEvents()
3601 {
3602 m_client->dispatchDidHandleOnloadEvents();
3603 }
3604
frameDetached()3605 void FrameLoader::frameDetached()
3606 {
3607 stopAllLoaders();
3608 m_frame->document()->stopActiveDOMObjects();
3609 detachFromParent();
3610 }
3611
detachFromParent()3612 void FrameLoader::detachFromParent()
3613 {
3614 RefPtr<Frame> protect(m_frame);
3615
3616 closeURL();
3617 stopAllLoaders();
3618 saveScrollPositionAndViewStateToItem(currentHistoryItem());
3619 detachChildren();
3620
3621 if (Page* page = m_frame->page())
3622 page->inspectorController()->frameDetachedFromParent(m_frame);
3623
3624 m_client->detachedFromParent2();
3625 setDocumentLoader(0);
3626 m_client->detachedFromParent3();
3627 if (Frame* parent = m_frame->tree()->parent()) {
3628 parent->loader()->closeAndRemoveChild(m_frame);
3629 parent->loader()->scheduleCheckCompleted();
3630 } else {
3631 m_frame->setView(0);
3632 m_frame->pageDestroyed();
3633 }
3634 }
3635
addExtraFieldsToSubresourceRequest(ResourceRequest & request)3636 void FrameLoader::addExtraFieldsToSubresourceRequest(ResourceRequest& request)
3637 {
3638 addExtraFieldsToRequest(request, m_loadType, false, false);
3639 }
3640
addExtraFieldsToMainResourceRequest(ResourceRequest & request)3641 void FrameLoader::addExtraFieldsToMainResourceRequest(ResourceRequest& request)
3642 {
3643 addExtraFieldsToRequest(request, m_loadType, true, false);
3644 }
3645
addExtraFieldsToRequest(ResourceRequest & request,FrameLoadType loadType,bool mainResource,bool cookiePolicyURLFromRequest)3646 void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadType loadType, bool mainResource, bool cookiePolicyURLFromRequest)
3647 {
3648 // Don't set the cookie policy URL if it's already been set.
3649 // But make sure to set it on all requests, as it has significance beyond the cookie policy for all protocols (<rdar://problem/6616664>).
3650 if (request.firstPartyForCookies().isEmpty()) {
3651 if (mainResource && (isLoadingMainFrame() || cookiePolicyURLFromRequest))
3652 request.setFirstPartyForCookies(request.url());
3653 else if (Document* document = m_frame->document())
3654 request.setFirstPartyForCookies(document->firstPartyForCookies());
3655 }
3656
3657 // The remaining modifications are only necessary for HTTP and HTTPS.
3658 if (!request.url().isEmpty() && !request.url().protocolInHTTPFamily())
3659 return;
3660
3661 applyUserAgent(request);
3662
3663 if (loadType == FrameLoadTypeReload) {
3664 request.setCachePolicy(ReloadIgnoringCacheData);
3665 request.setHTTPHeaderField("Cache-Control", "max-age=0");
3666 } else if (loadType == FrameLoadTypeReloadFromOrigin) {
3667 request.setCachePolicy(ReloadIgnoringCacheData);
3668 request.setHTTPHeaderField("Cache-Control", "no-cache");
3669 request.setHTTPHeaderField("Pragma", "no-cache");
3670 }
3671
3672 if (mainResource)
3673 request.setHTTPAccept(defaultAcceptHeader);
3674
3675 // Make sure we send the Origin header.
3676 addHTTPOriginIfNeeded(request, String());
3677
3678 // Always try UTF-8. If that fails, try frame encoding (if any) and then the default.
3679 // For a newly opened frame with an empty URL, encoding() should not be used, because this methods asks decoder, which uses ISO-8859-1.
3680 Settings* settings = m_frame->settings();
3681 request.setResponseContentDispositionEncodingFallbackArray("UTF-8", m_URL.isEmpty() ? m_encoding : encoding(), settings ? settings->defaultTextEncodingName() : String());
3682 }
3683
addHTTPOriginIfNeeded(ResourceRequest & request,String origin)3684 void FrameLoader::addHTTPOriginIfNeeded(ResourceRequest& request, String origin)
3685 {
3686 if (!request.httpOrigin().isEmpty())
3687 return; // Request already has an Origin header.
3688
3689 // Don't send an Origin header for GET or HEAD to avoid privacy issues.
3690 // For example, if an intranet page has a hyperlink to an external web
3691 // site, we don't want to include the Origin of the request because it
3692 // will leak the internal host name. Similar privacy concerns have lead
3693 // to the widespread suppression of the Referer header at the network
3694 // layer.
3695 if (request.httpMethod() == "GET" || request.httpMethod() == "HEAD")
3696 return;
3697
3698 // For non-GET and non-HEAD methods, always send an Origin header so the
3699 // server knows we support this feature.
3700
3701 if (origin.isEmpty()) {
3702 // If we don't know what origin header to attach, we attach the value
3703 // for an empty origin.
3704 origin = SecurityOrigin::createEmpty()->toString();
3705 }
3706
3707 request.setHTTPOrigin(origin);
3708 }
3709
committedLoad(DocumentLoader * loader,const char * data,int length)3710 void FrameLoader::committedLoad(DocumentLoader* loader, const char* data, int length)
3711 {
3712 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
3713 if (ArchiveFactory::isArchiveMimeType(loader->response().mimeType()))
3714 return;
3715 #endif
3716 m_client->committedLoad(loader, data, length);
3717 }
3718
3719 #ifdef ANDROID_USER_GESTURE
loadPostRequest(const ResourceRequest & inRequest,const String & referrer,const String & frameName,bool lockHistory,FrameLoadType loadType,PassRefPtr<Event> event,PassRefPtr<FormState> prpFormState,bool userGesture)3720 void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType loadType, PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState, bool userGesture)
3721 #else
3722 void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType loadType, PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState)
3723 #endif
3724 {
3725 RefPtr<FormState> formState = prpFormState;
3726
3727 // When posting, use the NSURLRequestReloadIgnoringCacheData load flag.
3728 // This prevents a potential bug which may cause a page with a form that uses itself
3729 // as an action to be returned from the cache without submitting.
3730
3731 // FIXME: Where's the code that implements what the comment above says?
3732
3733 // Previously when this method was reached, the original FrameLoadRequest had been deconstructed to build a
3734 // bunch of parameters that would come in here and then be built back up to a ResourceRequest. In case
3735 // any caller depends on the immutability of the original ResourceRequest, I'm rebuilding a ResourceRequest
3736 // from scratch as it did all along.
3737 const KURL& url = inRequest.url();
3738 RefPtr<FormData> formData = inRequest.httpBody();
3739 const String& contentType = inRequest.httpContentType();
3740 String origin = inRequest.httpOrigin();
3741
3742 ResourceRequest workingResourceRequest(url);
3743 #ifdef ANDROID_USER_GESTURE
3744 workingResourceRequest.setUserGesture(userGesture);
3745 #endif
3746
3747 if (!referrer.isEmpty())
3748 workingResourceRequest.setHTTPReferrer(referrer);
3749 workingResourceRequest.setHTTPOrigin(origin);
3750 workingResourceRequest.setHTTPMethod("POST");
3751 workingResourceRequest.setHTTPBody(formData);
3752 workingResourceRequest.setHTTPContentType(contentType);
3753 addExtraFieldsToRequest(workingResourceRequest, loadType, true, true);
3754
3755 NavigationAction action(url, loadType, true, event);
3756
3757 if (!frameName.isEmpty()) {
3758 // The search for a target frame is done earlier in the case of form submission.
3759 if (Frame* targetFrame = formState ? 0 : findFrameForNavigation(frameName))
3760 targetFrame->loader()->loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release());
3761 else
3762 checkNewWindowPolicy(action, workingResourceRequest, formState.release(), frameName);
3763 } else
3764 loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release());
3765 }
3766
loadResourceSynchronously(const ResourceRequest & request,StoredCredentials storedCredentials,ResourceError & error,ResourceResponse & response,Vector<char> & data)3767 unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
3768 {
3769 String referrer = m_outgoingReferrer;
3770 if (shouldHideReferrer(request.url(), referrer))
3771 referrer = String();
3772
3773 ResourceRequest initialRequest = request;
3774 initialRequest.setTimeoutInterval(10);
3775
3776 if (initialRequest.isConditional())
3777 initialRequest.setCachePolicy(ReloadIgnoringCacheData);
3778 else
3779 initialRequest.setCachePolicy(documentLoader()->request().cachePolicy());
3780
3781 if (!referrer.isEmpty())
3782 initialRequest.setHTTPReferrer(referrer);
3783 addHTTPOriginIfNeeded(initialRequest, outgoingOrigin());
3784
3785 if (Page* page = m_frame->page())
3786 initialRequest.setFirstPartyForCookies(page->mainFrame()->loader()->documentLoader()->request().url());
3787 initialRequest.setHTTPUserAgent(client()->userAgent(request.url()));
3788
3789 unsigned long identifier = 0;
3790 ResourceRequest newRequest(initialRequest);
3791 requestFromDelegate(newRequest, identifier, error);
3792
3793 if (error.isNull()) {
3794 ASSERT(!newRequest.isNull());
3795
3796 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
3797 if (!documentLoader()->applicationCacheHost()->maybeLoadSynchronously(newRequest, error, response, data)) {
3798 #endif
3799 ResourceHandle::loadResourceSynchronously(newRequest, storedCredentials, error, response, data, m_frame);
3800 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
3801 documentLoader()->applicationCacheHost()->maybeLoadFallbackSynchronously(newRequest, error, response, data);
3802 }
3803 #endif
3804 }
3805
3806 sendRemainingDelegateMessages(identifier, response, data.size(), error);
3807 return identifier;
3808 }
3809
assignIdentifierToInitialRequest(unsigned long identifier,const ResourceRequest & clientRequest)3810 void FrameLoader::assignIdentifierToInitialRequest(unsigned long identifier, const ResourceRequest& clientRequest)
3811 {
3812 return dispatchAssignIdentifierToInitialRequest(identifier, activeDocumentLoader(), clientRequest);
3813 }
3814
willSendRequest(ResourceLoader * loader,ResourceRequest & clientRequest,const ResourceResponse & redirectResponse)3815 void FrameLoader::willSendRequest(ResourceLoader* loader, ResourceRequest& clientRequest, const ResourceResponse& redirectResponse)
3816 {
3817 applyUserAgent(clientRequest);
3818 dispatchWillSendRequest(loader->documentLoader(), loader->identifier(), clientRequest, redirectResponse);
3819 }
3820
didReceiveResponse(ResourceLoader * loader,const ResourceResponse & r)3821 void FrameLoader::didReceiveResponse(ResourceLoader* loader, const ResourceResponse& r)
3822 {
3823 activeDocumentLoader()->addResponse(r);
3824
3825 if (Page* page = m_frame->page())
3826 page->progress()->incrementProgress(loader->identifier(), r);
3827 dispatchDidReceiveResponse(loader->documentLoader(), loader->identifier(), r);
3828 }
3829
didReceiveData(ResourceLoader * loader,const char * data,int length,int lengthReceived)3830 void FrameLoader::didReceiveData(ResourceLoader* loader, const char* data, int length, int lengthReceived)
3831 {
3832 if (Page* page = m_frame->page())
3833 page->progress()->incrementProgress(loader->identifier(), data, length);
3834 dispatchDidReceiveContentLength(loader->documentLoader(), loader->identifier(), lengthReceived);
3835 }
3836
didFailToLoad(ResourceLoader * loader,const ResourceError & error)3837 void FrameLoader::didFailToLoad(ResourceLoader* loader, const ResourceError& error)
3838 {
3839 if (Page* page = m_frame->page())
3840 page->progress()->completeProgress(loader->identifier());
3841 if (!error.isNull())
3842 m_client->dispatchDidFailLoading(loader->documentLoader(), loader->identifier(), error);
3843 }
3844
didLoadResourceByXMLHttpRequest(unsigned long identifier,const ScriptString & sourceString)3845 void FrameLoader::didLoadResourceByXMLHttpRequest(unsigned long identifier, const ScriptString& sourceString)
3846 {
3847 m_client->dispatchDidLoadResourceByXMLHttpRequest(identifier, sourceString);
3848 }
3849
originalRequest() const3850 const ResourceRequest& FrameLoader::originalRequest() const
3851 {
3852 return activeDocumentLoader()->originalRequestCopy();
3853 }
3854
receivedMainResourceError(const ResourceError & error,bool isComplete)3855 void FrameLoader::receivedMainResourceError(const ResourceError& error, bool isComplete)
3856 {
3857 // Retain because the stop may release the last reference to it.
3858 RefPtr<Frame> protect(m_frame);
3859
3860 RefPtr<DocumentLoader> loader = activeDocumentLoader();
3861
3862 if (isComplete) {
3863 // FIXME: Don't want to do this if an entirely new load is going, so should check
3864 // that both data sources on the frame are either this or nil.
3865 stop();
3866 if (m_client->shouldFallBack(error))
3867 handleFallbackContent();
3868 }
3869
3870 if (m_state == FrameStateProvisional && m_provisionalDocumentLoader) {
3871 if (m_submittedFormURL == m_provisionalDocumentLoader->originalRequestCopy().url())
3872 m_submittedFormURL = KURL();
3873
3874 // We might have made a page cache item, but now we're bailing out due to an error before we ever
3875 // transitioned to the new page (before WebFrameState == commit). The goal here is to restore any state
3876 // so that the existing view (that wenever got far enough to replace) can continue being used.
3877 invalidateCurrentItemCachedPage();
3878
3879 // Call clientRedirectCancelledOrFinished here so that the frame load delegate is notified that the redirect's
3880 // status has changed, if there was a redirect. The frame load delegate may have saved some state about
3881 // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:. Since we are definitely
3882 // not going to use this provisional resource, as it was cancelled, notify the frame load delegate that the redirect
3883 // has ended.
3884 if (m_sentRedirectNotification)
3885 clientRedirectCancelledOrFinished(false);
3886 }
3887
3888
3889 loader->mainReceivedError(error, isComplete);
3890 }
3891
callContinueFragmentScrollAfterNavigationPolicy(void * argument,const ResourceRequest & request,PassRefPtr<FormState>,bool shouldContinue)3892 void FrameLoader::callContinueFragmentScrollAfterNavigationPolicy(void* argument,
3893 const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue)
3894 {
3895 FrameLoader* loader = static_cast<FrameLoader*>(argument);
3896 loader->continueFragmentScrollAfterNavigationPolicy(request, shouldContinue);
3897 }
3898
continueFragmentScrollAfterNavigationPolicy(const ResourceRequest & request,bool shouldContinue)3899 void FrameLoader::continueFragmentScrollAfterNavigationPolicy(const ResourceRequest& request, bool shouldContinue)
3900 {
3901 bool isRedirect = m_quickRedirectComing || m_policyLoadType == FrameLoadTypeRedirectWithLockedBackForwardList;
3902 m_quickRedirectComing = false;
3903
3904 if (!shouldContinue)
3905 return;
3906
3907 KURL url = request.url();
3908
3909 m_documentLoader->replaceRequestURLForAnchorScroll(url);
3910 if (!isRedirect && !shouldTreatURLAsSameAsCurrent(url)) {
3911 // NB: must happen after _setURL, since we add based on the current request.
3912 // Must also happen before we openURL and displace the scroll position, since
3913 // adding the BF item will save away scroll state.
3914
3915 // NB2: If we were loading a long, slow doc, and the user anchor nav'ed before
3916 // it was done, currItem is now set the that slow doc, and prevItem is whatever was
3917 // before it. Adding the b/f item will bump the slow doc down to prevItem, even
3918 // though its load is not yet done. I think this all works out OK, for one because
3919 // we have already saved away the scroll and doc state for the long slow load,
3920 // but it's not an obvious case.
3921
3922 addHistoryItemForFragmentScroll();
3923 }
3924
3925 scrollToAnchor(url);
3926
3927 if (!isRedirect)
3928 // This will clear previousItem from the rest of the frame tree that didn't
3929 // doing any loading. We need to make a pass on this now, since for anchor nav
3930 // we'll not go through a real load and reach Completed state.
3931 checkLoadComplete();
3932
3933 m_client->dispatchDidChangeLocationWithinPage();
3934 m_client->didFinishLoad();
3935 }
3936
shouldScrollToAnchor(bool isFormSubmission,FrameLoadType loadType,const KURL & url)3937 bool FrameLoader::shouldScrollToAnchor(bool isFormSubmission, FrameLoadType loadType, const KURL& url)
3938 {
3939 // Should we do anchor navigation within the existing content?
3940
3941 // We don't do this if we are submitting a form, explicitly reloading,
3942 // currently displaying a frameset, or if the URL does not have a fragment.
3943 // These rules were originally based on what KHTML was doing in KHTMLPart::openURL.
3944
3945 // FIXME: What about load types other than Standard and Reload?
3946
3947 return !isFormSubmission
3948 && loadType != FrameLoadTypeReload
3949 && loadType != FrameLoadTypeReloadFromOrigin
3950 && loadType != FrameLoadTypeSame
3951 && !shouldReload(this->url(), url)
3952 // We don't want to just scroll if a link from within a
3953 // frameset is trying to reload the frameset into _top.
3954 && !m_frame->document()->isFrameSet();
3955 }
3956
checkNewWindowPolicy(const NavigationAction & action,const ResourceRequest & request,PassRefPtr<FormState> formState,const String & frameName)3957 void FrameLoader::checkNewWindowPolicy(const NavigationAction& action, const ResourceRequest& request,
3958 PassRefPtr<FormState> formState, const String& frameName)
3959 {
3960 m_policyCheck.set(request, formState, frameName,
3961 callContinueLoadAfterNewWindowPolicy, this);
3962 m_client->dispatchDecidePolicyForNewWindowAction(&FrameLoader::continueAfterNewWindowPolicy,
3963 action, request, formState, frameName);
3964 }
3965
continueAfterNewWindowPolicy(PolicyAction policy)3966 void FrameLoader::continueAfterNewWindowPolicy(PolicyAction policy)
3967 {
3968 PolicyCheck check = m_policyCheck;
3969 m_policyCheck.clear();
3970
3971 switch (policy) {
3972 case PolicyIgnore:
3973 check.clearRequest();
3974 break;
3975 case PolicyDownload:
3976 m_client->startDownload(check.request());
3977 check.clearRequest();
3978 break;
3979 case PolicyUse:
3980 break;
3981 }
3982
3983 check.call(policy == PolicyUse);
3984 }
3985
checkNavigationPolicy(const ResourceRequest & request,DocumentLoader * loader,PassRefPtr<FormState> formState,NavigationPolicyDecisionFunction function,void * argument)3986 void FrameLoader::checkNavigationPolicy(const ResourceRequest& request, DocumentLoader* loader,
3987 PassRefPtr<FormState> formState, NavigationPolicyDecisionFunction function, void* argument)
3988 {
3989 NavigationAction action = loader->triggeringAction();
3990 if (action.isEmpty()) {
3991 action = NavigationAction(request.url(), NavigationTypeOther);
3992 loader->setTriggeringAction(action);
3993 }
3994
3995 // Don't ask more than once for the same request or if we are loading an empty URL.
3996 // This avoids confusion on the part of the client.
3997 if (equalIgnoringHeaderFields(request, loader->lastCheckedRequest()) || (!request.isNull() && request.url().isEmpty())) {
3998 function(argument, request, 0, true);
3999 loader->setLastCheckedRequest(request);
4000 return;
4001 }
4002
4003 // We are always willing to show alternate content for unreachable URLs;
4004 // treat it like a reload so it maintains the right state for b/f list.
4005 if (loader->substituteData().isValid() && !loader->substituteData().failingURL().isEmpty()) {
4006 if (isBackForwardLoadType(m_policyLoadType))
4007 m_policyLoadType = FrameLoadTypeReload;
4008 function(argument, request, 0, true);
4009 return;
4010 }
4011
4012 loader->setLastCheckedRequest(request);
4013
4014 m_policyCheck.set(request, formState.get(), function, argument);
4015
4016 m_delegateIsDecidingNavigationPolicy = true;
4017 m_client->dispatchDecidePolicyForNavigationAction(&FrameLoader::continueAfterNavigationPolicy,
4018 action, request, formState);
4019 m_delegateIsDecidingNavigationPolicy = false;
4020 }
4021
continueAfterNavigationPolicy(PolicyAction policy)4022 void FrameLoader::continueAfterNavigationPolicy(PolicyAction policy)
4023 {
4024 PolicyCheck check = m_policyCheck;
4025 m_policyCheck.clear();
4026
4027 bool shouldContinue = policy == PolicyUse;
4028
4029 switch (policy) {
4030 case PolicyIgnore:
4031 check.clearRequest();
4032 break;
4033 case PolicyDownload:
4034 m_client->startDownload(check.request());
4035 check.clearRequest();
4036 break;
4037 case PolicyUse: {
4038 ResourceRequest request(check.request());
4039
4040 if (!m_client->canHandleRequest(request)) {
4041 handleUnimplementablePolicy(m_client->cannotShowURLError(check.request()));
4042 check.clearRequest();
4043 shouldContinue = false;
4044 }
4045 break;
4046 }
4047 }
4048
4049 check.call(shouldContinue);
4050 }
4051
callContinueLoadAfterNavigationPolicy(void * argument,const ResourceRequest & request,PassRefPtr<FormState> formState,bool shouldContinue)4052 void FrameLoader::callContinueLoadAfterNavigationPolicy(void* argument,
4053 const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue)
4054 {
4055 FrameLoader* loader = static_cast<FrameLoader*>(argument);
4056 loader->continueLoadAfterNavigationPolicy(request, formState, shouldContinue);
4057 }
4058
continueLoadAfterNavigationPolicy(const ResourceRequest &,PassRefPtr<FormState> formState,bool shouldContinue)4059 void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState> formState, bool shouldContinue)
4060 {
4061 // If we loaded an alternate page to replace an unreachableURL, we'll get in here with a
4062 // nil policyDataSource because loading the alternate page will have passed
4063 // through this method already, nested; otherwise, policyDataSource should still be set.
4064 ASSERT(m_policyDocumentLoader || !m_provisionalDocumentLoader->unreachableURL().isEmpty());
4065
4066 bool isTargetItem = m_provisionalHistoryItem ? m_provisionalHistoryItem->isTargetItem() : false;
4067
4068 // Two reasons we can't continue:
4069 // 1) Navigation policy delegate said we can't so request is nil. A primary case of this
4070 // is the user responding Cancel to the form repost nag sheet.
4071 // 2) User responded Cancel to an alert popped up by the before unload event handler.
4072 // The "before unload" event handler runs only for the main frame.
4073 bool canContinue = shouldContinue && (!isLoadingMainFrame() || m_frame->shouldClose());
4074
4075 if (!canContinue) {
4076 // If we were waiting for a quick redirect, but the policy delegate decided to ignore it, then we
4077 // need to report that the client redirect was cancelled.
4078 if (m_quickRedirectComing)
4079 clientRedirectCancelledOrFinished(false);
4080
4081 setPolicyDocumentLoader(0);
4082
4083 // If the navigation request came from the back/forward menu, and we punt on it, we have the
4084 // problem that we have optimistically moved the b/f cursor already, so move it back. For sanity,
4085 // we only do this when punting a navigation for the target frame or top-level frame.
4086 if ((isTargetItem || isLoadingMainFrame()) && isBackForwardLoadType(m_policyLoadType))
4087 if (Page* page = m_frame->page()) {
4088 Frame* mainFrame = page->mainFrame();
4089 if (HistoryItem* resetItem = mainFrame->loader()->m_currentHistoryItem.get()) {
4090 page->backForwardList()->goToItem(resetItem);
4091 Settings* settings = m_frame->settings();
4092 page->setGlobalHistoryItem((!settings || settings->privateBrowsingEnabled()) ? 0 : resetItem);
4093 }
4094 }
4095 return;
4096 }
4097
4098 FrameLoadType type = m_policyLoadType;
4099 stopAllLoaders();
4100
4101 // <rdar://problem/6250856> - In certain circumstances on pages with multiple frames, stopAllLoaders()
4102 // might detach the current FrameLoader, in which case we should bail on this newly defunct load.
4103 if (!m_frame->page())
4104 return;
4105
4106 #if ENABLE(JAVASCRIPT_DEBUGGER)
4107 if (Page* page = m_frame->page()) {
4108 if (page->mainFrame() == m_frame)
4109 page->inspectorController()->resumeDebugger();
4110 }
4111 #endif
4112
4113 setProvisionalDocumentLoader(m_policyDocumentLoader.get());
4114 m_loadType = type;
4115 setState(FrameStateProvisional);
4116
4117 setPolicyDocumentLoader(0);
4118
4119 if (isBackForwardLoadType(type) && loadProvisionalItemFromCachedPage())
4120 return;
4121
4122 if (formState)
4123 m_client->dispatchWillSubmitForm(&FrameLoader::continueLoadAfterWillSubmitForm, formState);
4124 else
4125 continueLoadAfterWillSubmitForm();
4126 }
4127
4128
callContinueLoadAfterNewWindowPolicy(void * argument,const ResourceRequest & request,PassRefPtr<FormState> formState,const String & frameName,bool shouldContinue)4129 void FrameLoader::callContinueLoadAfterNewWindowPolicy(void* argument,
4130 const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, bool shouldContinue)
4131 {
4132 FrameLoader* loader = static_cast<FrameLoader*>(argument);
4133 loader->continueLoadAfterNewWindowPolicy(request, formState, frameName, shouldContinue);
4134 }
4135
continueLoadAfterNewWindowPolicy(const ResourceRequest & request,PassRefPtr<FormState> formState,const String & frameName,bool shouldContinue)4136 void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& request,
4137 PassRefPtr<FormState> formState, const String& frameName, bool shouldContinue)
4138 {
4139 if (!shouldContinue)
4140 return;
4141
4142 RefPtr<Frame> frame = m_frame;
4143 RefPtr<Frame> mainFrame = m_client->dispatchCreatePage();
4144 if (!mainFrame)
4145 return;
4146
4147 if (frameName != "_blank")
4148 mainFrame->tree()->setName(frameName);
4149
4150 mainFrame->loader()->setOpenedByDOM();
4151 mainFrame->loader()->m_client->dispatchShow();
4152 mainFrame->loader()->setOpener(frame.get());
4153 mainFrame->loader()->loadWithNavigationAction(request, NavigationAction(), false, FrameLoadTypeStandard, formState);
4154 }
4155
sendRemainingDelegateMessages(unsigned long identifier,const ResourceResponse & response,int length,const ResourceError & error)4156 void FrameLoader::sendRemainingDelegateMessages(unsigned long identifier, const ResourceResponse& response, int length, const ResourceError& error)
4157 {
4158 if (!response.isNull())
4159 dispatchDidReceiveResponse(m_documentLoader.get(), identifier, response);
4160
4161 if (length > 0)
4162 dispatchDidReceiveContentLength(m_documentLoader.get(), identifier, length);
4163
4164 if (error.isNull())
4165 dispatchDidFinishLoading(m_documentLoader.get(), identifier);
4166 else
4167 m_client->dispatchDidFailLoading(m_documentLoader.get(), identifier, error);
4168 }
4169
requestFromDelegate(ResourceRequest & request,unsigned long & identifier,ResourceError & error)4170 void FrameLoader::requestFromDelegate(ResourceRequest& request, unsigned long& identifier, ResourceError& error)
4171 {
4172 ASSERT(!request.isNull());
4173
4174 identifier = 0;
4175 if (Page* page = m_frame->page()) {
4176 identifier = page->progress()->createUniqueIdentifier();
4177 dispatchAssignIdentifierToInitialRequest(identifier, m_documentLoader.get(), request);
4178 }
4179
4180 ResourceRequest newRequest(request);
4181 dispatchWillSendRequest(m_documentLoader.get(), identifier, newRequest, ResourceResponse());
4182
4183 if (newRequest.isNull())
4184 error = cancelledError(request);
4185 else
4186 error = ResourceError();
4187
4188 request = newRequest;
4189 }
4190
loadedResourceFromMemoryCache(const CachedResource * resource)4191 void FrameLoader::loadedResourceFromMemoryCache(const CachedResource* resource)
4192 {
4193 Page* page = m_frame->page();
4194 if (!page)
4195 return;
4196
4197 page->inspectorController()->didLoadResourceFromMemoryCache(m_documentLoader.get(), resource);
4198
4199 if (!resource->sendResourceLoadCallbacks() || m_documentLoader->haveToldClientAboutLoad(resource->url()))
4200 return;
4201
4202 if (!page->areMemoryCacheClientCallsEnabled()) {
4203 m_documentLoader->recordMemoryCacheLoadForFutureClientNotification(resource->url());
4204 m_documentLoader->didTellClientAboutLoad(resource->url());
4205 return;
4206 }
4207
4208 ResourceRequest request(resource->url());
4209 if (m_client->dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), request, resource->response(), resource->encodedSize())) {
4210 m_documentLoader->didTellClientAboutLoad(resource->url());
4211 return;
4212 }
4213
4214 unsigned long identifier;
4215 ResourceError error;
4216 requestFromDelegate(request, identifier, error);
4217 sendRemainingDelegateMessages(identifier, resource->response(), resource->encodedSize(), error);
4218 }
4219
applyUserAgent(ResourceRequest & request)4220 void FrameLoader::applyUserAgent(ResourceRequest& request)
4221 {
4222 String userAgent = client()->userAgent(request.url());
4223 ASSERT(!userAgent.isNull());
4224 request.setHTTPUserAgent(userAgent);
4225 }
4226
shouldInterruptLoadForXFrameOptions(const String & content,const KURL & url)4227 bool FrameLoader::shouldInterruptLoadForXFrameOptions(const String& content, const KURL& url)
4228 {
4229 Frame* topFrame = m_frame->tree()->top();
4230 if (m_frame == topFrame)
4231 return false;
4232
4233 if (equalIgnoringCase(content, "deny"))
4234 return true;
4235
4236 if (equalIgnoringCase(content, "sameorigin")) {
4237 RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url);
4238 if (!origin->isSameSchemeHostPort(topFrame->document()->securityOrigin()))
4239 return true;
4240 }
4241
4242 return false;
4243 }
4244
canGoBackOrForward(int distance) const4245 bool FrameLoader::canGoBackOrForward(int distance) const
4246 {
4247 if (Page* page = m_frame->page()) {
4248 if (distance == 0)
4249 return true;
4250 if (distance > 0 && distance <= page->backForwardList()->forwardListCount())
4251 return true;
4252 if (distance < 0 && -distance <= page->backForwardList()->backListCount())
4253 return true;
4254 }
4255 return false;
4256 }
4257
getHistoryLength()4258 int FrameLoader::getHistoryLength()
4259 {
4260 if (Page* page = m_frame->page())
4261 return page->backForwardList()->backListCount() + 1;
4262 return 0;
4263 }
4264
addHistoryItemForFragmentScroll()4265 void FrameLoader::addHistoryItemForFragmentScroll()
4266 {
4267 addBackForwardItemClippedAtTarget(false);
4268 }
4269
loadProvisionalItemFromCachedPage()4270 bool FrameLoader::loadProvisionalItemFromCachedPage()
4271 {
4272 RefPtr<CachedPage> cachedPage = pageCache()->get(m_provisionalHistoryItem.get());
4273 if (!cachedPage || !cachedPage->document())
4274 return false;
4275 provisionalDocumentLoader()->loadFromCachedPage(cachedPage.release());
4276 return true;
4277 }
4278
cachePageForHistoryItem(HistoryItem * item)4279 void FrameLoader::cachePageForHistoryItem(HistoryItem* item)
4280 {
4281 if (!canCachePage() || item->isInPageCache())
4282 return;
4283
4284 if (Page* page = m_frame->page()) {
4285 RefPtr<CachedPage> cachedPage = CachedPage::create(page);
4286 pageCache()->add(item, cachedPage.release());
4287 }
4288 }
4289
shouldTreatURLAsSameAsCurrent(const KURL & url) const4290 bool FrameLoader::shouldTreatURLAsSameAsCurrent(const KURL& url) const
4291 {
4292 if (!m_currentHistoryItem)
4293 return false;
4294 return url == m_currentHistoryItem->url() || url == m_currentHistoryItem->originalURL();
4295 }
4296
createHistoryItem(bool useOriginal)4297 PassRefPtr<HistoryItem> FrameLoader::createHistoryItem(bool useOriginal)
4298 {
4299 DocumentLoader* docLoader = documentLoader();
4300
4301 KURL unreachableURL = docLoader ? docLoader->unreachableURL() : KURL();
4302
4303 KURL url;
4304 KURL originalURL;
4305
4306 if (!unreachableURL.isEmpty()) {
4307 url = unreachableURL;
4308 originalURL = unreachableURL;
4309 } else {
4310 originalURL = docLoader ? docLoader->originalURL() : KURL();
4311 if (useOriginal)
4312 url = originalURL;
4313 else if (docLoader)
4314 url = docLoader->requestURL();
4315 }
4316
4317 LOG(History, "WebCoreHistory: Creating item for %s", url.string().ascii().data());
4318
4319 // Frames that have never successfully loaded any content
4320 // may have no URL at all. Currently our history code can't
4321 // deal with such things, so we nip that in the bud here.
4322 // Later we may want to learn to live with nil for URL.
4323 // See bug 3368236 and related bugs for more information.
4324 if (url.isEmpty())
4325 url = blankURL();
4326 if (originalURL.isEmpty())
4327 originalURL = blankURL();
4328
4329 Frame* parentFrame = m_frame->tree()->parent();
4330 String parent = parentFrame ? parentFrame->tree()->name() : "";
4331 String title = docLoader ? docLoader->title() : "";
4332
4333 RefPtr<HistoryItem> item = HistoryItem::create(url, m_frame->tree()->name(), parent, title);
4334 item->setOriginalURLString(originalURL.string());
4335
4336 if (!unreachableURL.isEmpty() || !docLoader || docLoader->response().httpStatusCode() >= 400)
4337 item->setLastVisitWasFailure(true);
4338
4339 // Save form state if this is a POST
4340 if (docLoader) {
4341 if (useOriginal)
4342 item->setFormInfoFromRequest(docLoader->originalRequest());
4343 else
4344 item->setFormInfoFromRequest(docLoader->request());
4345 }
4346
4347 // Set the item for which we will save document state
4348 m_previousHistoryItem = m_currentHistoryItem;
4349 m_currentHistoryItem = item;
4350
4351 return item.release();
4352 }
4353
addBackForwardItemClippedAtTarget(bool doClip)4354 void FrameLoader::addBackForwardItemClippedAtTarget(bool doClip)
4355 {
4356 // In the case of saving state about a page with frames, we store a tree of items that mirrors the frame tree.
4357 // The item that was the target of the user's navigation is designated as the "targetItem".
4358 // When this function is called with doClip=true we're able to create the whole tree except for the target's children,
4359 // which will be loaded in the future. That part of the tree will be filled out as the child loads are committed.
4360
4361 Page* page = m_frame->page();
4362 if (!page)
4363 return;
4364
4365 if (documentLoader()->urlForHistory().isEmpty())
4366 return;
4367
4368 Frame* mainFrame = page->mainFrame();
4369 ASSERT(mainFrame);
4370 FrameLoader* frameLoader = mainFrame->loader();
4371
4372 if (!frameLoader->m_didPerformFirstNavigation && page->backForwardList()->entries().size() == 1) {
4373 frameLoader->m_didPerformFirstNavigation = true;
4374 m_client->didPerformFirstNavigation();
4375 }
4376
4377 RefPtr<HistoryItem> item = frameLoader->createHistoryItemTree(m_frame, doClip);
4378 LOG(BackForward, "WebCoreBackForward - Adding backforward item %p for frame %s", item.get(), documentLoader()->url().string().ascii().data());
4379 page->backForwardList()->addItem(item);
4380 }
4381
createHistoryItemTree(Frame * targetFrame,bool clipAtTarget)4382 PassRefPtr<HistoryItem> FrameLoader::createHistoryItemTree(Frame* targetFrame, bool clipAtTarget)
4383 {
4384 RefPtr<HistoryItem> bfItem = createHistoryItem(m_frame->tree()->parent() ? true : false);
4385 if (m_previousHistoryItem)
4386 saveScrollPositionAndViewStateToItem(m_previousHistoryItem.get());
4387 if (!(clipAtTarget && m_frame == targetFrame)) {
4388 // save frame state for items that aren't loading (khtml doesn't save those)
4389 saveDocumentState();
4390 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) {
4391 FrameLoader* childLoader = child->loader();
4392 bool hasChildLoaded = childLoader->frameHasLoaded();
4393
4394 // If the child is a frame corresponding to an <object> element that never loaded,
4395 // we don't want to create a history item, because that causes fallback content
4396 // to be ignored on reload.
4397
4398 if (!(!hasChildLoaded && childLoader->isHostedByObjectElement()))
4399 bfItem->addChildItem(childLoader->createHistoryItemTree(targetFrame, clipAtTarget));
4400 }
4401 }
4402 if (m_frame == targetFrame)
4403 bfItem->setIsTargetItem(true);
4404 return bfItem;
4405 }
4406
findFrameForNavigation(const AtomicString & name)4407 Frame* FrameLoader::findFrameForNavigation(const AtomicString& name)
4408 {
4409 Frame* frame = m_frame->tree()->find(name);
4410 if (!shouldAllowNavigation(frame))
4411 return 0;
4412 return frame;
4413 }
4414
saveScrollPositionAndViewStateToItem(HistoryItem * item)4415 void FrameLoader::saveScrollPositionAndViewStateToItem(HistoryItem* item)
4416 {
4417 if (!item || !m_frame->view())
4418 return;
4419
4420 item->setScrollPoint(m_frame->view()->scrollPosition());
4421 // FIXME: It would be great to work out a way to put this code in WebCore instead of calling through to the client.
4422 m_client->saveViewStateToItem(item);
4423 }
4424
4425 /*
4426 There is a race condition between the layout and load completion that affects restoring the scroll position.
4427 We try to restore the scroll position at both the first layout and upon load completion.
4428
4429 1) If first layout happens before the load completes, we want to restore the scroll position then so that the
4430 first time we draw the page is already scrolled to the right place, instead of starting at the top and later
4431 jumping down. It is possible that the old scroll position is past the part of the doc laid out so far, in
4432 which case the restore silent fails and we will fix it in when we try to restore on doc completion.
4433 2) If the layout happens after the load completes, the attempt to restore at load completion time silently
4434 fails. We then successfully restore it when the layout happens.
4435 */
restoreScrollPositionAndViewState()4436 void FrameLoader::restoreScrollPositionAndViewState()
4437 {
4438 if (!m_committedFirstRealDocumentLoad)
4439 return;
4440
4441 ASSERT(m_currentHistoryItem);
4442
4443 // FIXME: As the ASSERT attests, it seems we should always have a currentItem here.
4444 // One counterexample is <rdar://problem/4917290>
4445 // For now, to cover this issue in release builds, there is no technical harm to returning
4446 // early and from a user standpoint - as in the above radar - the previous page load failed
4447 // so there *is* no scroll or view state to restore!
4448 if (!m_currentHistoryItem)
4449 return;
4450
4451 // FIXME: It would be great to work out a way to put this code in WebCore instead of calling
4452 // through to the client. It's currently used only for the PDF view on Mac.
4453 m_client->restoreViewState();
4454
4455 if (FrameView* view = m_frame->view())
4456 if (!view->wasScrolledByUser())
4457 view->setScrollPosition(m_currentHistoryItem->scrollPoint());
4458 }
4459
invalidateCurrentItemCachedPage()4460 void FrameLoader::invalidateCurrentItemCachedPage()
4461 {
4462 // When we are pre-commit, the currentItem is where the pageCache data resides
4463 CachedPage* cachedPage = pageCache()->get(m_currentHistoryItem.get());
4464
4465 // FIXME: This is a grotesque hack to fix <rdar://problem/4059059> Crash in RenderFlow::detach
4466 // Somehow the PageState object is not properly updated, and is holding onto a stale document.
4467 // Both Xcode and FileMaker see this crash, Safari does not.
4468
4469 ASSERT(!cachedPage || cachedPage->document() == m_frame->document());
4470 if (cachedPage && cachedPage->document() == m_frame->document()) {
4471 cachedPage->document()->setInPageCache(false);
4472 cachedPage->clear();
4473 }
4474
4475 if (cachedPage)
4476 pageCache()->remove(m_currentHistoryItem.get());
4477 }
4478
saveDocumentState()4479 void FrameLoader::saveDocumentState()
4480 {
4481 if (m_creatingInitialEmptyDocument)
4482 return;
4483
4484 // For a standard page load, we will have a previous item set, which will be used to
4485 // store the form state. However, in some cases we will have no previous item, and
4486 // the current item is the right place to save the state. One example is when we
4487 // detach a bunch of frames because we are navigating from a site with frames to
4488 // another site. Another is when saving the frame state of a frame that is not the
4489 // target of the current navigation (if we even decide to save with that granularity).
4490
4491 // Because of previousItem's "masking" of currentItem for this purpose, it's important
4492 // that previousItem be cleared at the end of a page transition. We leverage the
4493 // checkLoadComplete recursion to achieve this goal.
4494
4495 HistoryItem* item = m_previousHistoryItem ? m_previousHistoryItem.get() : m_currentHistoryItem.get();
4496 if (!item)
4497 return;
4498
4499 Document* document = m_frame->document();
4500 ASSERT(document);
4501
4502 if (item->isCurrentDocument(document)) {
4503 LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree()->name().string().utf8().data(), item);
4504 item->setDocumentState(document->formElementsState());
4505 }
4506 }
4507
4508 // Loads content into this frame, as specified by history item
loadItem(HistoryItem * item,FrameLoadType loadType)4509 void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType)
4510 {
4511 if (!m_frame->page())
4512 return;
4513
4514 KURL itemURL = item->url();
4515 KURL itemOriginalURL = item->originalURL();
4516 KURL currentURL;
4517 if (documentLoader())
4518 currentURL = documentLoader()->url();
4519 RefPtr<FormData> formData = item->formData();
4520
4521 // Are we navigating to an anchor within the page?
4522 // Note if we have child frames we do a real reload, since the child frames might not
4523 // match our current frame structure, or they might not have the right content. We could
4524 // check for all that as an additional optimization.
4525 // We also do not do anchor-style navigation if we're posting a form or navigating from
4526 // a page that was resulted from a form post.
4527 bool shouldScroll = !formData && !(m_currentHistoryItem && m_currentHistoryItem->formData()) && urlsMatchItem(item);
4528
4529 #if ENABLE(WML)
4530 // All WML decks should go through the real load mechanism, not the scroll-to-anchor code
4531 if (frameContainsWMLContent(m_frame))
4532 shouldScroll = false;
4533 #endif
4534
4535 if (shouldScroll) {
4536 // Must do this maintenance here, since we don't go through a real page reload
4537 saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get());
4538
4539 if (FrameView* view = m_frame->view())
4540 view->setWasScrolledByUser(false);
4541
4542 m_currentHistoryItem = item;
4543
4544 // FIXME: Form state might need to be saved here too.
4545
4546 // We always call scrollToAnchor here, even if the URL doesn't have an
4547 // anchor fragment. This is so we'll keep the WebCore Frame's URL up-to-date.
4548 scrollToAnchor(item->url());
4549
4550 // must do this maintenance here, since we don't go through a real page reload
4551 restoreScrollPositionAndViewState();
4552
4553 // Fake the URL change by updating the data source's request. This will no longer
4554 // be necessary if we do the better fix described above.
4555 documentLoader()->replaceRequestURLForAnchorScroll(itemURL);
4556
4557 m_client->dispatchDidChangeLocationWithinPage();
4558
4559 // FrameLoaderClient::didFinishLoad() tells the internal load delegate the load finished with no error
4560 m_client->didFinishLoad();
4561 } else {
4562 // Remember this item so we can traverse any child items as child frames load
4563 m_provisionalHistoryItem = item;
4564
4565 bool inPageCache = false;
4566
4567 // Check if we'll be using the page cache. We only use the page cache
4568 // if one exists and it is less than _backForwardCacheExpirationInterval
4569 // seconds old. If the cache is expired it gets flushed here.
4570 if (RefPtr<CachedPage> cachedPage = pageCache()->get(item)) {
4571 double interval = currentTime() - cachedPage->timeStamp();
4572
4573 // FIXME: 1800 should not be hardcoded, it should come from
4574 // WebKitBackForwardCacheExpirationIntervalKey in WebKit.
4575 // Or we should remove WebKitBackForwardCacheExpirationIntervalKey.
4576 if (interval <= 1800) {
4577 loadWithDocumentLoader(cachedPage->documentLoader(), loadType, 0);
4578 inPageCache = true;
4579 } else {
4580 LOG(PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", m_provisionalHistoryItem->url().string().ascii().data());
4581 pageCache()->remove(item);
4582 }
4583 }
4584
4585 if (!inPageCache) {
4586 bool addedExtraFields = false;
4587 ResourceRequest request(itemURL);
4588
4589 if (!item->referrer().isNull())
4590 request.setHTTPReferrer(item->referrer());
4591
4592 // If this was a repost that failed the page cache, we might try to repost the form.
4593 NavigationAction action;
4594 if (formData) {
4595
4596 formData->generateFiles(m_frame->page()->chrome()->client());
4597
4598 request.setHTTPMethod("POST");
4599 request.setHTTPBody(formData);
4600 request.setHTTPContentType(item->formContentType());
4601 RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::createFromString(item->referrer());
4602 addHTTPOriginIfNeeded(request, securityOrigin->toString());
4603
4604 // Make sure to add extra fields to the request after the Origin header is added for the FormData case.
4605 // See https://bugs.webkit.org/show_bug.cgi?id=22194 for more discussion.
4606 addExtraFieldsToRequest(request, m_loadType, true, formData);
4607 addedExtraFields = true;
4608
4609 // FIXME: Slight hack to test if the NSURL cache contains the page we're going to.
4610 // We want to know this before talking to the policy delegate, since it affects whether
4611 // we show the DoYouReallyWantToRepost nag.
4612 //
4613 // This trick has a small bug (3123893) where we might find a cache hit, but then
4614 // have the item vanish when we try to use it in the ensuing nav. This should be
4615 // extremely rare, but in that case the user will get an error on the navigation.
4616
4617 if (ResourceHandle::willLoadFromCache(request, m_frame))
4618 action = NavigationAction(itemURL, loadType, false);
4619 else {
4620 request.setCachePolicy(ReloadIgnoringCacheData);
4621 action = NavigationAction(itemURL, NavigationTypeFormResubmitted);
4622 }
4623 } else {
4624 switch (loadType) {
4625 case FrameLoadTypeReload:
4626 case FrameLoadTypeReloadFromOrigin:
4627 request.setCachePolicy(ReloadIgnoringCacheData);
4628 break;
4629 case FrameLoadTypeBack:
4630 case FrameLoadTypeBackWMLDeckNotAccessible:
4631 case FrameLoadTypeForward:
4632 case FrameLoadTypeIndexedBackForward:
4633 if (itemURL.protocol() != "https")
4634 request.setCachePolicy(ReturnCacheDataElseLoad);
4635 break;
4636 case FrameLoadTypeStandard:
4637 case FrameLoadTypeRedirectWithLockedBackForwardList:
4638 break;
4639 case FrameLoadTypeSame:
4640 default:
4641 ASSERT_NOT_REACHED();
4642 }
4643
4644 action = NavigationAction(itemOriginalURL, loadType, false);
4645 }
4646
4647 if (!addedExtraFields)
4648 addExtraFieldsToRequest(request, m_loadType, true, formData);
4649
4650 loadWithNavigationAction(request, action, false, loadType, 0);
4651 }
4652 }
4653 }
4654
4655 // Walk the frame tree and ensure that the URLs match the URLs in the item.
urlsMatchItem(HistoryItem * item) const4656 bool FrameLoader::urlsMatchItem(HistoryItem* item) const
4657 {
4658 const KURL& currentURL = documentLoader()->url();
4659 if (!equalIgnoringFragmentIdentifier(currentURL, item->url()))
4660 return false;
4661
4662 const HistoryItemVector& childItems = item->children();
4663
4664 unsigned size = childItems.size();
4665 for (unsigned i = 0; i < size; ++i) {
4666 Frame* childFrame = m_frame->tree()->child(childItems[i]->target());
4667 if (childFrame && !childFrame->loader()->urlsMatchItem(childItems[i].get()))
4668 return false;
4669 }
4670
4671 return true;
4672 }
4673
4674 // Main funnel for navigating to a previous location (back/forward, non-search snap-back)
4675 // This includes recursion to handle loading into framesets properly
goToItem(HistoryItem * targetItem,FrameLoadType type)4676 void FrameLoader::goToItem(HistoryItem* targetItem, FrameLoadType type)
4677 {
4678 ASSERT(!m_frame->tree()->parent());
4679
4680 // shouldGoToHistoryItem is a private delegate method. This is needed to fix:
4681 // <rdar://problem/3951283> can view pages from the back/forward cache that should be disallowed by Parental Controls
4682 // Ultimately, history item navigations should go through the policy delegate. That's covered in:
4683 // <rdar://problem/3979539> back/forward cache navigations should consult policy delegate
4684 Page* page = m_frame->page();
4685 if (!page)
4686 return;
4687 if (!m_client->shouldGoToHistoryItem(targetItem))
4688 return;
4689
4690 // Set the BF cursor before commit, which lets the user quickly click back/forward again.
4691 // - plus, it only makes sense for the top level of the operation through the frametree,
4692 // as opposed to happening for some/one of the page commits that might happen soon
4693 BackForwardList* bfList = page->backForwardList();
4694 HistoryItem* currentItem = bfList->currentItem();
4695 bfList->goToItem(targetItem);
4696 Settings* settings = m_frame->settings();
4697 page->setGlobalHistoryItem((!settings || settings->privateBrowsingEnabled()) ? 0 : targetItem);
4698 recursiveGoToItem(targetItem, currentItem, type);
4699 }
4700
4701 // The general idea here is to traverse the frame tree and the item tree in parallel,
4702 // tracking whether each frame already has the content the item requests. If there is
4703 // a match (by URL), we just restore scroll position and recurse. Otherwise we must
4704 // reload that frame, and all its kids.
recursiveGoToItem(HistoryItem * item,HistoryItem * fromItem,FrameLoadType type)4705 void FrameLoader::recursiveGoToItem(HistoryItem* item, HistoryItem* fromItem, FrameLoadType type)
4706 {
4707 ASSERT(item);
4708 ASSERT(fromItem);
4709
4710 KURL itemURL = item->url();
4711 KURL currentURL;
4712 if (documentLoader())
4713 currentURL = documentLoader()->url();
4714
4715 // Always reload the target frame of the item we're going to. This ensures that we will
4716 // do -some- load for the transition, which means a proper notification will be posted
4717 // to the app.
4718 // The exact URL has to match, including fragment. We want to go through the _load
4719 // method, even if to do a within-page navigation.
4720 // The current frame tree and the frame tree snapshot in the item have to match.
4721 if (!item->isTargetItem() &&
4722 itemURL == currentURL &&
4723 ((m_frame->tree()->name().isEmpty() && item->target().isEmpty()) || m_frame->tree()->name() == item->target()) &&
4724 childFramesMatchItem(item))
4725 {
4726 // This content is good, so leave it alone and look for children that need reloading
4727 // Save form state (works from currentItem, since prevItem is nil)
4728 ASSERT(!m_previousHistoryItem);
4729 saveDocumentState();
4730 saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get());
4731
4732 if (FrameView* view = m_frame->view())
4733 view->setWasScrolledByUser(false);
4734
4735 m_currentHistoryItem = item;
4736
4737 // Restore form state (works from currentItem)
4738 restoreDocumentState();
4739
4740 // Restore the scroll position (we choose to do this rather than going back to the anchor point)
4741 restoreScrollPositionAndViewState();
4742
4743 const HistoryItemVector& childItems = item->children();
4744
4745 int size = childItems.size();
4746 for (int i = 0; i < size; ++i) {
4747 String childFrameName = childItems[i]->target();
4748 HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFrameName);
4749 ASSERT(fromChildItem || fromItem->isTargetItem());
4750 Frame* childFrame = m_frame->tree()->child(childFrameName);
4751 ASSERT(childFrame);
4752 childFrame->loader()->recursiveGoToItem(childItems[i].get(), fromChildItem, type);
4753 }
4754 } else {
4755 loadItem(item, type);
4756 }
4757 }
4758
4759 // helper method that determines whether the subframes described by the item's subitems
4760 // match our own current frameset
childFramesMatchItem(HistoryItem * item) const4761 bool FrameLoader::childFramesMatchItem(HistoryItem* item) const
4762 {
4763 const HistoryItemVector& childItems = item->children();
4764 if (childItems.size() != m_frame->tree()->childCount())
4765 return false;
4766
4767 unsigned size = childItems.size();
4768 for (unsigned i = 0; i < size; ++i) {
4769 if (!m_frame->tree()->child(childItems[i]->target()))
4770 return false;
4771 }
4772
4773 // Found matches for all item targets
4774 return true;
4775 }
4776
4777 // There are 3 things you might think of as "history", all of which are handled by these functions.
4778 //
4779 // 1) Back/forward: The m_currentHistoryItem is part of this mechanism.
4780 // 2) Global history: Handled by the client.
4781 // 3) Visited links: Handled by the PageGroup.
4782
updateHistoryForStandardLoad()4783 void FrameLoader::updateHistoryForStandardLoad()
4784 {
4785 LOG(History, "WebCoreHistory: Updating History for Standard Load in frame %s", documentLoader()->url().string().ascii().data());
4786
4787 Settings* settings = m_frame->settings();
4788 bool needPrivacy = !settings || settings->privateBrowsingEnabled();
4789 const KURL& historyURL = documentLoader()->urlForHistory();
4790
4791 if (!documentLoader()->isClientRedirect()) {
4792 if (!historyURL.isEmpty()) {
4793 addBackForwardItemClippedAtTarget(true);
4794 if (!needPrivacy) {
4795 m_client->updateGlobalHistory();
4796 m_documentLoader->setDidCreateGlobalHistoryEntry(true);
4797 if (m_documentLoader->unreachableURL().isEmpty())
4798 m_client->updateGlobalHistoryRedirectLinks();
4799 }
4800 if (Page* page = m_frame->page())
4801 page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForwardList()->currentItem());
4802 }
4803 } else if (documentLoader()->unreachableURL().isEmpty() && m_currentHistoryItem) {
4804 m_currentHistoryItem->setURL(documentLoader()->url());
4805 m_currentHistoryItem->setFormInfoFromRequest(documentLoader()->request());
4806 }
4807
4808 if (!historyURL.isEmpty() && !needPrivacy) {
4809 if (Page* page = m_frame->page())
4810 page->group().addVisitedLink(historyURL);
4811
4812 if (!m_documentLoader->didCreateGlobalHistoryEntry() && documentLoader()->unreachableURL().isEmpty() && !url().isEmpty())
4813 m_client->updateGlobalHistoryRedirectLinks();
4814 }
4815 }
4816
updateHistoryForClientRedirect()4817 void FrameLoader::updateHistoryForClientRedirect()
4818 {
4819 #if !LOG_DISABLED
4820 if (documentLoader())
4821 LOG(History, "WebCoreHistory: Updating History for client redirect in frame %s", documentLoader()->title().utf8().data());
4822 #endif
4823
4824 // Clear out form data so we don't try to restore it into the incoming page. Must happen after
4825 // webcore has closed the URL and saved away the form state.
4826 if (m_currentHistoryItem) {
4827 m_currentHistoryItem->clearDocumentState();
4828 m_currentHistoryItem->clearScrollPoint();
4829 }
4830
4831 Settings* settings = m_frame->settings();
4832 bool needPrivacy = !settings || settings->privateBrowsingEnabled();
4833 const KURL& historyURL = documentLoader()->urlForHistory();
4834
4835 if (!historyURL.isEmpty() && !needPrivacy) {
4836 if (Page* page = m_frame->page())
4837 page->group().addVisitedLink(historyURL);
4838 }
4839 }
4840
updateHistoryForBackForwardNavigation()4841 void FrameLoader::updateHistoryForBackForwardNavigation()
4842 {
4843 #if !LOG_DISABLED
4844 if (documentLoader())
4845 LOG(History, "WebCoreHistory: Updating History for back/forward navigation in frame %s", documentLoader()->title().utf8().data());
4846 #endif
4847
4848 // Must grab the current scroll position before disturbing it
4849 saveScrollPositionAndViewStateToItem(m_previousHistoryItem.get());
4850 }
4851
updateHistoryForReload()4852 void FrameLoader::updateHistoryForReload()
4853 {
4854 #if !LOG_DISABLED
4855 if (documentLoader())
4856 LOG(History, "WebCoreHistory: Updating History for reload in frame %s", documentLoader()->title().utf8().data());
4857 #endif
4858
4859 if (m_currentHistoryItem) {
4860 pageCache()->remove(m_currentHistoryItem.get());
4861
4862 if (loadType() == FrameLoadTypeReload || loadType() == FrameLoadTypeReloadFromOrigin)
4863 saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get());
4864
4865 // Sometimes loading a page again leads to a different result because of cookies. Bugzilla 4072
4866 if (documentLoader()->unreachableURL().isEmpty())
4867 m_currentHistoryItem->setURL(documentLoader()->requestURL());
4868 }
4869 }
4870
updateHistoryForRedirectWithLockedBackForwardList()4871 void FrameLoader::updateHistoryForRedirectWithLockedBackForwardList()
4872 {
4873 #if !LOG_DISABLED
4874 if (documentLoader())
4875 LOG(History, "WebCoreHistory: Updating History for redirect load in frame %s", documentLoader()->title().utf8().data());
4876 #endif
4877
4878 Settings* settings = m_frame->settings();
4879 bool needPrivacy = !settings || settings->privateBrowsingEnabled();
4880 const KURL& historyURL = documentLoader()->urlForHistory();
4881
4882 if (documentLoader()->isClientRedirect()) {
4883 if (!m_currentHistoryItem && !m_frame->tree()->parent()) {
4884 if (!historyURL.isEmpty()) {
4885 addBackForwardItemClippedAtTarget(true);
4886 if (!needPrivacy) {
4887 m_client->updateGlobalHistory();
4888 m_documentLoader->setDidCreateGlobalHistoryEntry(true);
4889 if (m_documentLoader->unreachableURL().isEmpty())
4890 m_client->updateGlobalHistoryRedirectLinks();
4891 }
4892 if (Page* page = m_frame->page())
4893 page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForwardList()->currentItem());
4894 }
4895 }
4896 if (m_currentHistoryItem) {
4897 m_currentHistoryItem->setURL(documentLoader()->url());
4898 m_currentHistoryItem->setFormInfoFromRequest(documentLoader()->request());
4899 }
4900 } else {
4901 Frame* parentFrame = m_frame->tree()->parent();
4902 if (parentFrame && parentFrame->loader()->m_currentHistoryItem)
4903 parentFrame->loader()->m_currentHistoryItem->setChildItem(createHistoryItem(true));
4904 }
4905
4906 if (!historyURL.isEmpty() && !needPrivacy) {
4907 if (Page* page = m_frame->page())
4908 page->group().addVisitedLink(historyURL);
4909
4910 if (!m_documentLoader->didCreateGlobalHistoryEntry() && documentLoader()->unreachableURL().isEmpty() && !url().isEmpty())
4911 m_client->updateGlobalHistoryRedirectLinks();
4912 }
4913 }
4914
updateHistoryForCommit()4915 void FrameLoader::updateHistoryForCommit()
4916 {
4917 #if !LOG_DISABLED
4918 if (documentLoader())
4919 LOG(History, "WebCoreHistory: Updating History for commit in frame %s", documentLoader()->title().utf8().data());
4920 #endif
4921 FrameLoadType type = loadType();
4922 if (isBackForwardLoadType(type) ||
4923 ((type == FrameLoadTypeReload || type == FrameLoadTypeReloadFromOrigin) && !provisionalDocumentLoader()->unreachableURL().isEmpty())) {
4924 // Once committed, we want to use current item for saving DocState, and
4925 // the provisional item for restoring state.
4926 // Note previousItem must be set before we close the URL, which will
4927 // happen when the data source is made non-provisional below
4928 m_previousHistoryItem = m_currentHistoryItem;
4929 ASSERT(m_provisionalHistoryItem);
4930 m_currentHistoryItem = m_provisionalHistoryItem;
4931 m_provisionalHistoryItem = 0;
4932 }
4933 }
4934
updateHistoryForAnchorScroll()4935 void FrameLoader::updateHistoryForAnchorScroll()
4936 {
4937 if (m_URL.isEmpty())
4938 return;
4939
4940 Settings* settings = m_frame->settings();
4941 if (!settings || settings->privateBrowsingEnabled())
4942 return;
4943
4944 Page* page = m_frame->page();
4945 if (!page)
4946 return;
4947
4948 page->group().addVisitedLink(m_URL);
4949 }
4950
4951 // Walk the frame tree, telling all frames to save their form state into their current
4952 // history item.
saveDocumentAndScrollState()4953 void FrameLoader::saveDocumentAndScrollState()
4954 {
4955 for (Frame* frame = m_frame; frame; frame = frame->tree()->traverseNext(m_frame)) {
4956 frame->loader()->saveDocumentState();
4957 frame->loader()->saveScrollPositionAndViewStateToItem(frame->loader()->currentHistoryItem());
4958 }
4959 }
4960
currentHistoryItem()4961 HistoryItem* FrameLoader::currentHistoryItem()
4962 {
4963 return m_currentHistoryItem.get();
4964 }
4965
setCurrentHistoryItem(PassRefPtr<HistoryItem> item)4966 void FrameLoader::setCurrentHistoryItem(PassRefPtr<HistoryItem> item)
4967 {
4968 m_currentHistoryItem = item;
4969 }
4970
setMainDocumentError(DocumentLoader * loader,const ResourceError & error)4971 void FrameLoader::setMainDocumentError(DocumentLoader* loader, const ResourceError& error)
4972 {
4973 m_client->setMainDocumentError(loader, error);
4974 }
4975
mainReceivedCompleteError(DocumentLoader * loader,const ResourceError &)4976 void FrameLoader::mainReceivedCompleteError(DocumentLoader* loader, const ResourceError&)
4977 {
4978 loader->setPrimaryLoadComplete(true);
4979 m_client->dispatchDidLoadMainResource(activeDocumentLoader());
4980 checkCompleted();
4981 if (m_frame->page())
4982 checkLoadComplete();
4983 }
4984
mainReceivedError(const ResourceError & error,bool isComplete)4985 void FrameLoader::mainReceivedError(const ResourceError& error, bool isComplete)
4986 {
4987 activeDocumentLoader()->mainReceivedError(error, isComplete);
4988 }
4989
cancelledError(const ResourceRequest & request) const4990 ResourceError FrameLoader::cancelledError(const ResourceRequest& request) const
4991 {
4992 ResourceError error = m_client->cancelledError(request);
4993 error.setIsCancellation(true);
4994 return error;
4995 }
4996
blockedError(const ResourceRequest & request) const4997 ResourceError FrameLoader::blockedError(const ResourceRequest& request) const
4998 {
4999 return m_client->blockedError(request);
5000 }
5001
cannotShowURLError(const ResourceRequest & request) const5002 ResourceError FrameLoader::cannotShowURLError(const ResourceRequest& request) const
5003 {
5004 return m_client->cannotShowURLError(request);
5005 }
5006
fileDoesNotExistError(const ResourceResponse & response) const5007 ResourceError FrameLoader::fileDoesNotExistError(const ResourceResponse& response) const
5008 {
5009 return m_client->fileDoesNotExistError(response);
5010 }
5011
didFinishLoad(ResourceLoader * loader)5012 void FrameLoader::didFinishLoad(ResourceLoader* loader)
5013 {
5014 if (Page* page = m_frame->page())
5015 page->progress()->completeProgress(loader->identifier());
5016 dispatchDidFinishLoading(loader->documentLoader(), loader->identifier());
5017 }
5018
shouldUseCredentialStorage(ResourceLoader * loader)5019 bool FrameLoader::shouldUseCredentialStorage(ResourceLoader* loader)
5020 {
5021 return m_client->shouldUseCredentialStorage(loader->documentLoader(), loader->identifier());
5022 }
5023
didReceiveAuthenticationChallenge(ResourceLoader * loader,const AuthenticationChallenge & currentWebChallenge)5024 void FrameLoader::didReceiveAuthenticationChallenge(ResourceLoader* loader, const AuthenticationChallenge& currentWebChallenge)
5025 {
5026 m_client->dispatchDidReceiveAuthenticationChallenge(loader->documentLoader(), loader->identifier(), currentWebChallenge);
5027 }
5028
didCancelAuthenticationChallenge(ResourceLoader * loader,const AuthenticationChallenge & currentWebChallenge)5029 void FrameLoader::didCancelAuthenticationChallenge(ResourceLoader* loader, const AuthenticationChallenge& currentWebChallenge)
5030 {
5031 m_client->dispatchDidCancelAuthenticationChallenge(loader->documentLoader(), loader->identifier(), currentWebChallenge);
5032 }
5033
PolicyCheck()5034 PolicyCheck::PolicyCheck()
5035 : m_navigationFunction(0)
5036 , m_newWindowFunction(0)
5037 , m_contentFunction(0)
5038 {
5039 }
5040
clear()5041 void PolicyCheck::clear()
5042 {
5043 clearRequest();
5044 m_navigationFunction = 0;
5045 m_newWindowFunction = 0;
5046 m_contentFunction = 0;
5047 }
5048
set(const ResourceRequest & request,PassRefPtr<FormState> formState,NavigationPolicyDecisionFunction function,void * argument)5049 void PolicyCheck::set(const ResourceRequest& request, PassRefPtr<FormState> formState,
5050 NavigationPolicyDecisionFunction function, void* argument)
5051 {
5052 m_request = request;
5053 m_formState = formState;
5054 m_frameName = String();
5055
5056 m_navigationFunction = function;
5057 m_newWindowFunction = 0;
5058 m_contentFunction = 0;
5059 m_argument = argument;
5060 }
5061
set(const ResourceRequest & request,PassRefPtr<FormState> formState,const String & frameName,NewWindowPolicyDecisionFunction function,void * argument)5062 void PolicyCheck::set(const ResourceRequest& request, PassRefPtr<FormState> formState,
5063 const String& frameName, NewWindowPolicyDecisionFunction function, void* argument)
5064 {
5065 m_request = request;
5066 m_formState = formState;
5067 m_frameName = frameName;
5068
5069 m_navigationFunction = 0;
5070 m_newWindowFunction = function;
5071 m_contentFunction = 0;
5072 m_argument = argument;
5073 }
5074
set(ContentPolicyDecisionFunction function,void * argument)5075 void PolicyCheck::set(ContentPolicyDecisionFunction function, void* argument)
5076 {
5077 m_request = ResourceRequest();
5078 m_formState = 0;
5079 m_frameName = String();
5080
5081 m_navigationFunction = 0;
5082 m_newWindowFunction = 0;
5083 m_contentFunction = function;
5084 m_argument = argument;
5085 }
5086
call(bool shouldContinue)5087 void PolicyCheck::call(bool shouldContinue)
5088 {
5089 if (m_navigationFunction)
5090 m_navigationFunction(m_argument, m_request, m_formState.get(), shouldContinue);
5091 if (m_newWindowFunction)
5092 m_newWindowFunction(m_argument, m_request, m_formState.get(), m_frameName, shouldContinue);
5093 ASSERT(!m_contentFunction);
5094 }
5095
call(PolicyAction action)5096 void PolicyCheck::call(PolicyAction action)
5097 {
5098 ASSERT(!m_navigationFunction);
5099 ASSERT(!m_newWindowFunction);
5100 ASSERT(m_contentFunction);
5101 m_contentFunction(m_argument, action);
5102 }
5103
clearRequest()5104 void PolicyCheck::clearRequest()
5105 {
5106 m_request = ResourceRequest();
5107 m_formState = 0;
5108 m_frameName = String();
5109 }
5110
cancel()5111 void PolicyCheck::cancel()
5112 {
5113 clearRequest();
5114 if (m_navigationFunction)
5115 m_navigationFunction(m_argument, m_request, m_formState.get(), false);
5116 if (m_newWindowFunction)
5117 m_newWindowFunction(m_argument, m_request, m_formState.get(), m_frameName, false);
5118 if (m_contentFunction)
5119 m_contentFunction(m_argument, PolicyIgnore);
5120 }
5121
setTitle(const String & title)5122 void FrameLoader::setTitle(const String& title)
5123 {
5124 documentLoader()->setTitle(title);
5125 }
5126
originalRequestURL() const5127 KURL FrameLoader::originalRequestURL() const
5128 {
5129 return activeDocumentLoader()->originalRequest().url();
5130 }
5131
referrer() const5132 String FrameLoader::referrer() const
5133 {
5134 return documentLoader()->request().httpReferrer();
5135 }
5136
dispatchDocumentElementAvailable()5137 void FrameLoader::dispatchDocumentElementAvailable()
5138 {
5139 m_client->documentElementAvailable();
5140 }
5141
dispatchWindowObjectAvailable()5142 void FrameLoader::dispatchWindowObjectAvailable()
5143 {
5144 if (!m_frame->script()->isEnabled() || !m_frame->script()->haveWindowShell())
5145 return;
5146
5147 m_client->windowObjectCleared();
5148
5149 if (Page* page = m_frame->page()) {
5150 if (InspectorController* inspector = page->inspectorController())
5151 inspector->inspectedWindowScriptObjectCleared(m_frame);
5152 if (InspectorController* inspector = page->parentInspectorController())
5153 inspector->windowScriptObjectAvailable();
5154 }
5155 }
5156
createJavaAppletWidget(const IntSize & size,HTMLAppletElement * element,const HashMap<String,String> & args)5157 PassRefPtr<Widget> FrameLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement* element, const HashMap<String, String>& args)
5158 {
5159 String baseURLString;
5160 String codeBaseURLString;
5161 Vector<String> paramNames;
5162 Vector<String> paramValues;
5163 HashMap<String, String>::const_iterator end = args.end();
5164 for (HashMap<String, String>::const_iterator it = args.begin(); it != end; ++it) {
5165 if (equalIgnoringCase(it->first, "baseurl"))
5166 baseURLString = it->second;
5167 else if (equalIgnoringCase(it->first, "codebase"))
5168 codeBaseURLString = it->second;
5169 paramNames.append(it->first);
5170 paramValues.append(it->second);
5171 }
5172
5173 if (!codeBaseURLString.isEmpty()) {
5174 KURL codeBaseURL = completeURL(codeBaseURLString);
5175 if (!canLoad(codeBaseURL, String(), element->document())) {
5176 FrameLoader::reportLocalLoadFailed(m_frame, codeBaseURL.string());
5177 return 0;
5178 }
5179 }
5180
5181 if (baseURLString.isEmpty())
5182 baseURLString = m_frame->document()->baseURL().string();
5183 KURL baseURL = completeURL(baseURLString);
5184
5185 RefPtr<Widget> widget = m_client->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues);
5186 if (!widget)
5187 return 0;
5188
5189 m_containsPlugIns = true;
5190 return widget;
5191 }
5192
didChangeTitle(DocumentLoader * loader)5193 void FrameLoader::didChangeTitle(DocumentLoader* loader)
5194 {
5195 m_client->didChangeTitle(loader);
5196
5197 if (loader == m_documentLoader) {
5198 // Must update the entries in the back-forward list too.
5199 if (m_currentHistoryItem)
5200 m_currentHistoryItem->setTitle(loader->title());
5201 // This must go through the WebFrame because it has the right notion of the current b/f item.
5202 m_client->setTitle(loader->title(), loader->urlForHistory());
5203 m_client->setMainFrameDocumentReady(true); // update observers with new DOMDocument
5204 m_client->dispatchDidReceiveTitle(loader->title());
5205 }
5206 }
5207
dispatchDidCommitLoad()5208 void FrameLoader::dispatchDidCommitLoad()
5209 {
5210 if (m_creatingInitialEmptyDocument)
5211 return;
5212
5213 #ifndef NDEBUG
5214 m_didDispatchDidCommitLoad = true;
5215 #endif
5216
5217 m_client->dispatchDidCommitLoad();
5218
5219 if (Page* page = m_frame->page())
5220 page->inspectorController()->didCommitLoad(m_documentLoader.get());
5221 }
5222
dispatchAssignIdentifierToInitialRequest(unsigned long identifier,DocumentLoader * loader,const ResourceRequest & request)5223 void FrameLoader::dispatchAssignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request)
5224 {
5225 m_client->assignIdentifierToInitialRequest(identifier, loader, request);
5226
5227 if (Page* page = m_frame->page())
5228 page->inspectorController()->identifierForInitialRequest(identifier, loader, request);
5229 }
5230
dispatchWillSendRequest(DocumentLoader * loader,unsigned long identifier,ResourceRequest & request,const ResourceResponse & redirectResponse)5231 void FrameLoader::dispatchWillSendRequest(DocumentLoader* loader, unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse)
5232 {
5233 StringImpl* oldRequestURL = request.url().string().impl();
5234 m_documentLoader->didTellClientAboutLoad(request.url());
5235
5236 m_client->dispatchWillSendRequest(loader, identifier, request, redirectResponse);
5237
5238 // If the URL changed, then we want to put that new URL in the "did tell client" set too.
5239 if (!request.isNull() && oldRequestURL != request.url().string().impl())
5240 m_documentLoader->didTellClientAboutLoad(request.url());
5241
5242 if (Page* page = m_frame->page())
5243 page->inspectorController()->willSendRequest(loader, identifier, request, redirectResponse);
5244 }
5245
dispatchDidReceiveResponse(DocumentLoader * loader,unsigned long identifier,const ResourceResponse & r)5246 void FrameLoader::dispatchDidReceiveResponse(DocumentLoader* loader, unsigned long identifier, const ResourceResponse& r)
5247 {
5248 m_client->dispatchDidReceiveResponse(loader, identifier, r);
5249
5250 if (Page* page = m_frame->page())
5251 page->inspectorController()->didReceiveResponse(loader, identifier, r);
5252 }
5253
dispatchDidReceiveContentLength(DocumentLoader * loader,unsigned long identifier,int length)5254 void FrameLoader::dispatchDidReceiveContentLength(DocumentLoader* loader, unsigned long identifier, int length)
5255 {
5256 m_client->dispatchDidReceiveContentLength(loader, identifier, length);
5257
5258 if (Page* page = m_frame->page())
5259 page->inspectorController()->didReceiveContentLength(loader, identifier, length);
5260 }
5261
dispatchDidFinishLoading(DocumentLoader * loader,unsigned long identifier)5262 void FrameLoader::dispatchDidFinishLoading(DocumentLoader* loader, unsigned long identifier)
5263 {
5264 m_client->dispatchDidFinishLoading(loader, identifier);
5265
5266 if (Page* page = m_frame->page())
5267 page->inspectorController()->didFinishLoading(loader, identifier);
5268 }
5269
tellClientAboutPastMemoryCacheLoads()5270 void FrameLoader::tellClientAboutPastMemoryCacheLoads()
5271 {
5272 ASSERT(m_frame->page());
5273 ASSERT(m_frame->page()->areMemoryCacheClientCallsEnabled());
5274
5275 if (!m_documentLoader)
5276 return;
5277
5278 Vector<String> pastLoads;
5279 m_documentLoader->takeMemoryCacheLoadsForClientNotification(pastLoads);
5280
5281 size_t size = pastLoads.size();
5282 for (size_t i = 0; i < size; ++i) {
5283 CachedResource* resource = cache()->resourceForURL(pastLoads[i]);
5284
5285 // FIXME: These loads, loaded from cache, but now gone from the cache by the time
5286 // Page::setMemoryCacheClientCallsEnabled(true) is called, will not be seen by the client.
5287 // Consider if there's some efficient way of remembering enough to deliver this client call.
5288 // We have the URL, but not the rest of the response or the length.
5289 if (!resource)
5290 continue;
5291
5292 ResourceRequest request(resource->url());
5293 m_client->dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), request, resource->response(), resource->encodedSize());
5294 }
5295 }
5296
hasHTMLView() const5297 bool FrameLoaderClient::hasHTMLView() const
5298 {
5299 return true;
5300 }
5301
5302 } // namespace WebCore
5303