2 * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #import "WebFrameInternal.h"
31 #import "WebArchive.h"
32 #import "WebBackForwardList.h"
33 #import "WebDataProtocol.h"
34 #import "WebDataSourceInternal.h"
35 #import "WebDefaultResourceLoadDelegate.h"
36 #import "WebDefaultUIDelegate.h"
37 #import "WebDocumentInternal.h"
38 #import "WebDocumentLoaderMac.h"
39 #import "WebFormDataStream.h"
40 #import "WebFrameBridge.h"
41 #import "WebFrameLoadDelegate.h"
42 #import "WebFrameLoader.h"
43 #import "WebFrameViewInternal.h"
44 #import "WebHTMLRepresentationPrivate.h"
45 #import "WebHTMLViewInternal.h"
46 #import "WebHTMLViewPrivate.h"
47 #import "WebHistoryItemPrivate.h"
48 #import "WebHistoryPrivate.h"
49 #import "WebKitErrorsPrivate.h"
50 #import "WebKitLogging.h"
51 #import "WebKitNSStringExtras.h"
52 #import "WebKitStatisticsPrivate.h"
53 #import "WebNSObjectExtras.h"
54 #import "WebNSURLExtras.h"
55 #import "WebNSURLRequestExtras.h"
56 #import "WebNetscapePluginEmbeddedView.h"
57 #import "WebNullPluginView.h"
59 #import "WebPluginController.h"
60 #import "WebPreferencesPrivate.h"
61 #import "WebResourceLoadDelegate.h"
62 #import "WebResourcePrivate.h"
63 #import "WebScriptDebugDelegatePrivate.h"
64 #import "WebUIDelegate.h"
65 #import "WebViewInternal.h"
66 #import <WebKit/DOM.h>
67 #import <WebKitSystemInterface.h>
68 #import <objc/objc-runtime.h>
71 Here is the current behavior matrix for four types of navigations:
75 Restore form state: YES
76 Restore scroll and focus state: YES
77 WF Cache policy: NSURLRequestUseProtocolCachePolicy
78 Add to back/forward list: YES
82 Restore form state: YES
83 Restore scroll and focus state: YES
84 WF Cache policy: NSURLRequestReturnCacheDataElseLoad
85 Add to back/forward list: NO
87 Reload (meaning only the reload button):
89 Restore form state: NO
90 Restore scroll and focus state: YES
91 WF Cache policy: NSURLRequestReloadIgnoringCacheData
92 Add to back/forward list: NO
94 Repeat load of the same URL (by any other means of navigation other than the reload button, including hitting return in the location field):
96 Restore form state: NO
97 Restore scroll and focus state: NO, reset to initial conditions
98 WF Cache policy: NSURLRequestReloadIgnoringCacheData
99 Add to back/forward list: NO
102 NSString *WebPageCacheEntryDateKey = @"WebPageCacheEntryDateKey";
103 NSString *WebPageCacheDataSourceKey = @"WebPageCacheDataSourceKey";
104 NSString *WebPageCacheDocumentViewKey = @"WebPageCacheDocumentViewKey";
106 @interface WebFrame (ForwardDecls)
107 - (void)_loadHTMLString:(NSString *)string baseURL:(NSURL *)URL unreachableURL:(NSURL *)unreachableURL;
108 - (NSDictionary *)_actionInformationForLoadType:(FrameLoadType)loadType isFormSubmission:(BOOL)isFormSubmission event:(NSEvent *)event originalURL:(NSURL *)URL;
110 - (void)_saveScrollPositionAndViewStateToItem:(WebHistoryItem *)item;
112 - (WebHistoryItem *)_createItem:(BOOL)useOriginal;
113 - (WebHistoryItem *)_createItemTreeWithTargetFrame:(WebFrame *)targetFrame clippedAtTarget:(BOOL)doClip;
114 - (WebHistoryItem *)_currentBackForwardListItemToResetTo;
117 @interface WebFrame (FrameTraversal)
118 - (WebFrame *)_firstChildFrame;
119 - (WebFrame *)_lastChildFrame;
120 - (unsigned)_childFrameCount;
121 - (WebFrame *)_previousSiblingFrame;
122 - (WebFrame *)_nextSiblingFrame;
123 - (WebFrame *)_traverseNextFrameStayWithin:(WebFrame *)stayWithin;
126 @interface NSView (WebFramePluginHosting)
127 - (void)setWebFrame:(WebFrame *)webFrame;
130 @interface WebFramePrivate : NSObject
133 WebFrameView *webFrameView;
135 WebFrameBridge *bridge;
136 WebHistoryItem *currentItem; // BF item for our current content
137 WebHistoryItem *provisionalItem; // BF item for where we're trying to go
138 // (only known when navigating to a pre-existing BF item)
139 WebHistoryItem *previousItem; // BF item for previous content, see _itemForSavingDocState
141 WebScriptDebugger *scriptDebugger;
142 id internalLoadDelegate;
144 NSMutableSet *plugInViews;
145 NSMutableSet *inspectors;
148 - (void)setWebFrameView:(WebFrameView *)v;
150 - (void)setProvisionalItem:(WebHistoryItem *)item;
151 - (void)setPreviousItem:(WebHistoryItem *)item;
152 - (void)setCurrentItem:(WebHistoryItem *)item;
156 @implementation WebFramePrivate
160 [webFrameView release];
162 [currentItem release];
163 [provisionalItem release];
164 [previousItem release];
166 [scriptDebugger release];
168 [inspectors release];
170 ASSERT(plugInViews == nil);
175 - (void)setWebFrameView:(WebFrameView *)v
178 [webFrameView release];
182 - (void)setProvisionalItem:(WebHistoryItem *)item
185 [provisionalItem release];
186 provisionalItem = item;
189 - (void)setPreviousItem:(WebHistoryItem *)item
192 [previousItem release];
196 - (void)setCurrentItem:(WebHistoryItem *)item
199 [currentItem release];
205 static inline WebFrame *Frame(WebCoreFrameBridge *bridge)
207 return [(WebFrameBridge *)bridge webFrame];
210 @implementation WebFrame (FrameTraversal)
212 - (WebFrame *)_firstChildFrame
214 return Frame([[self _bridge] firstChild]);
217 - (WebFrame *)_lastChildFrame
219 return Frame([[self _bridge] lastChild]);
222 - (unsigned)_childFrameCount
224 return [[self _bridge] childCount];
227 - (WebFrame *)_previousSiblingFrame;
229 return Frame([[self _bridge] previousSibling]);
232 - (WebFrame *)_nextSiblingFrame;
234 return Frame([[self _bridge] nextSibling]);
237 - (WebFrame *)_traverseNextFrameStayWithin:(WebFrame *)stayWithin
239 return Frame([[self _bridge] traverseNextFrameStayWithin:[stayWithin _bridge]]);
244 @implementation WebFrame (WebInternal)
246 - (NSURLRequest *)_webDataRequestForData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)URL unreachableURL:(NSURL *)unreachableURL
248 NSURL *fakeURL = [NSURL _web_uniqueWebDataURL];
249 NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:fakeURL] autorelease];
250 [request _webDataRequestSetData:data];
251 [request _webDataRequestSetEncoding:encodingName];
252 [request _webDataRequestSetBaseURL:URL];
253 [request _webDataRequestSetUnreachableURL:unreachableURL];
254 [request _webDataRequestSetMIMEType: MIMEType ? MIMEType : (NSString *)@"text/html"];
258 // helper method used in various nav cases below
259 - (void)_addBackForwardItemClippedAtTarget:(BOOL)doClip
261 if ([[self dataSource] _URLForHistory] != nil) {
262 WebHistoryItem *bfItem = [[[self webView] mainFrame] _createItemTreeWithTargetFrame:self clippedAtTarget:doClip];
263 LOG (BackForward, "for frame %@, adding item %@\n", [self name], bfItem);
264 [[[self webView] backForwardList] addItem:bfItem];
268 - (void)_addHistoryItemForFragmentScroll
270 [self _addBackForwardItemClippedAtTarget:NO];
273 - (void)_didFinishLoad
275 [_private->internalLoadDelegate webFrame:self didFinishLoadWithError:nil];
278 - (WebHistoryItem *)_createItem:(BOOL)useOriginal
280 WebDataSource *dataSrc = [self dataSource];
281 NSURLRequest *request;
282 NSURL *unreachableURL = [dataSrc unreachableURL];
285 WebHistoryItem *bfItem;
288 request = [[dataSrc _documentLoader] originalRequestCopy];
290 request = [dataSrc request];
292 if (unreachableURL != nil) {
293 URL = unreachableURL;
294 originalURL = unreachableURL;
297 originalURL = [[[dataSrc _documentLoader] originalRequestCopy] URL];
300 LOG (History, "creating item for %@", request);
302 // Frames that have never successfully loaded any content
303 // may have no URL at all. Currently our history code can't
304 // deal with such things, so we nip that in the bud here.
305 // Later we may want to learn to live with nil for URL.
306 // See bug 3368236 and related bugs for more information.
308 URL = [NSURL URLWithString:@"about:blank"];
310 if (originalURL == nil) {
311 originalURL = [NSURL URLWithString:@"about:blank"];
314 bfItem = [[[WebHistoryItem alloc] initWithURL:URL target:[self name] parent:[[self parentFrame] name] title:[dataSrc pageTitle]] autorelease];
315 [bfItem setOriginalURLString:[originalURL _web_originalDataAsString]];
317 // save form state if this is a POST
318 [bfItem _setFormInfoFromRequest:request];
320 // Set the item for which we will save document state
321 [_private setPreviousItem:_private->currentItem];
322 [_private setCurrentItem:bfItem];
328 In the case of saving state about a page with frames, we store a tree of items that mirrors the frame tree.
329 The item that was the target of the user's navigation is designated as the "targetItem".
330 When this method is called with doClip=YES we're able to create the whole tree except for the target's children,
331 which will be loaded in the future. That part of the tree will be filled out as the child loads are committed.
333 - (WebHistoryItem *)_createItemTreeWithTargetFrame:(WebFrame *)targetFrame clippedAtTarget:(BOOL)doClip
335 WebHistoryItem *bfItem = [self _createItem:[self parentFrame] ? YES : NO];
337 [self _saveScrollPositionAndViewStateToItem:_private->previousItem];
338 if (!(doClip && self == targetFrame)) {
339 // save frame state for items that aren't loading (khtml doesn't save those)
340 [_private->bridge saveDocumentState];
342 for (WebFrame *child = [self _firstChildFrame]; child; child = [child _nextSiblingFrame])
343 [bfItem addChildItem:[child _createItemTreeWithTargetFrame:targetFrame clippedAtTarget:doClip]];
345 if (self == targetFrame)
346 [bfItem setIsTargetItem:YES];
351 - (WebFrame *)_immediateChildFrameNamed:(NSString *)name
353 return Frame([[self _bridge] childFrameNamed:name]);
356 - (void)_detachChildren
358 // FIXME: is it really necessary to do this in reverse order any more?
359 WebFrame *child = [self _lastChildFrame];
360 WebFrame *prev = [child _previousSiblingFrame];
361 for (; child; child = prev, prev = [child _previousSiblingFrame])
362 [child _detachFromParent];
365 - (void)_detachFromParent
367 WebFrameBridge *bridge = [_private->bridge retain];
372 [self _saveScrollPositionAndViewStateToItem:_private->currentItem];
373 [self _detachChildren];
374 [_private->inspectors makeObjectsPerformSelector:@selector(_webFrameDetached:) withObject:self];
376 [_private->webFrameView _setWebFrame:nil]; // needed for now to be compatible w/ old behavior
378 [[self _frameLoader] clearDataSource];
379 [_private setWebFrameView:nil];
381 [self retain]; // retain self temporarily because dealloc can re-enter this method
383 [[[self parentFrame] _bridge] removeChild:bridge];
389 _private->bridge = nil;
394 - (BOOL)_canCachePage
396 return [[[self webView] backForwardList] _usesPageCache];
399 - (void)_purgePageCache
401 // This method implements the rule for purging the page cache.
402 unsigned sizeLimit = [[[self webView] backForwardList] pageCacheSize];
403 unsigned pagesCached = 0;
404 WebBackForwardList *backForwardList = [[self webView] backForwardList];
405 NSArray *backList = [backForwardList backListWithLimit: 999999];
406 WebHistoryItem *oldestNonSnapbackItem = nil;
409 for (i = 0; i < [backList count]; i++){
410 WebHistoryItem *item = [backList objectAtIndex: i];
411 if ([item hasPageCache]){
412 if (oldestNonSnapbackItem == nil && ![item alwaysAttemptToUsePageCache])
413 oldestNonSnapbackItem = item;
418 // Snapback items are never directly purged here.
419 if (pagesCached >= sizeLimit) {
420 LOG(PageCache, "Purging back/forward cache, %@\n", [oldestNonSnapbackItem URL]);
421 [oldestNonSnapbackItem setHasPageCache:NO];
425 + (CFAbsoluteTime)_timeOfLastCompletedLoad
427 return [WebFrameLoader timeOfLastCompletedLoad];
430 - (BOOL)_createPageCacheForItem:(WebHistoryItem *)item
432 NSMutableDictionary *pageCache;
434 [item setHasPageCache: YES];
436 if (![_private->bridge saveDocumentToPageCache]){
437 [item setHasPageCache: NO];
441 pageCache = [item pageCache];
442 [pageCache setObject:[NSDate date] forKey: WebPageCacheEntryDateKey];
443 [pageCache setObject:[self dataSource] forKey: WebPageCacheDataSourceKey];
444 [pageCache setObject:[[self frameView] documentView] forKey: WebPageCacheDocumentViewKey];
449 - (void)_provisionalLoadStarted
451 FrameLoadType loadType = [[self _frameLoader] loadType];
453 // FIXME: This is OK as long as no one resizes the window,
454 // but in the case where someone does, it means garbage outside
455 // the occupied part of the scroll view.
456 [[[self frameView] _scrollView] setDrawsBackground:NO];
458 // Cache the page, if possible.
459 // Don't write to the cache if in the middle of a redirect, since we will want to
460 // store the final page we end up on.
461 // No point writing to the cache on a reload or loadSame, since we will just write
462 // over it again when we leave that page.
463 WebHistoryItem *item = _private->currentItem;
464 if ([self _canCachePage]
465 && [_private->bridge canCachePage]
467 && ![[self _frameLoader] isQuickRedirectComing]
468 && loadType != FrameLoadTypeReload
469 && loadType != FrameLoadTypeReloadAllowingStaleData
470 && loadType != FrameLoadTypeSame
471 && ![[self dataSource] isLoading]
472 && ![[[self _frameLoader] documentLoader] isStopping]) {
473 if ([[[self dataSource] representation] isKindOfClass:[WebHTMLRepresentation class]]) {
474 if (![item pageCache]){
475 // Add the items to this page's cache.
476 if ([self _createPageCacheForItem:item]) {
477 LOG(PageCache, "Saving page to back/forward cache, %@\n", [[self dataSource] _URL]);
479 // See if any page caches need to be purged after the addition of this
481 [self _purgePageCache];
484 LOG(PageCache, "NOT saving page to back/forward cache, unable to create items, %@\n", [[self dataSource] _URL]);
487 // Put the document into a null state, so it can be restored correctly.
488 [_private->bridge clear];
490 LOG(PageCache, "NOT saving page to back/forward cache, %@\n", [[self dataSource] _URL]);
493 // Called after we send an openURL:... down to WebCore.
496 if ([[self _frameLoader] loadType] == FrameLoadTypeStandard && [[[self dataSource] _documentLoader] isClientRedirect]) {
497 // Clear out form data so we don't try to restore it into the incoming page. Must happen after
498 // khtml has closed the URL and saved away the form state.
499 WebHistoryItem *item = _private->currentItem;
500 [item setDocumentState:nil];
501 [item setScrollPoint:NSZeroPoint];
504 if ([[self dataSource] _loadingFromPageCache]){
505 // Force a layout to update view size and thereby update scrollbars.
506 NSView <WebDocumentView> *view = [[self frameView] documentView];
507 if ([view isKindOfClass:[WebHTMLView class]]) {
508 [(WebHTMLView *)view setNeedsToApplyStyles:YES];
510 [view setNeedsLayout: YES];
513 NSArray *responses = [[[self _frameLoader] documentLoader] responses];
514 NSURLResponse *response;
515 int i, count = [responses count];
516 for (i = 0; i < count; i++){
517 response = [responses objectAtIndex: i];
518 // FIXME: If the WebKit client changes or cancels the request, this is not respected.
521 NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[response URL]];
522 [[self _frameLoader] requestFromDelegateForRequest:request identifier:&identifier error:&error];
523 [[self _frameLoader] sendRemainingDelegateMessagesWithIdentifier:identifier response:response length:(unsigned)[response expectedContentLength] error:error];
527 // Release the resources kept in the page cache. They will be
528 // reset when we leave this page. The core side of the page cache
529 // will have already been invalidated by the bridge to prevent
530 // premature release.
531 [_private->currentItem setHasPageCache:NO];
533 [[[self _frameLoader] documentLoader] setPrimaryLoadComplete:YES];
534 // why only this frame and not parent frames?
535 [[self _frameLoader] checkLoadCompleteForThisFrame];
539 - (void)_handledOnloadEvents
541 [[[self webView] _frameLoadDelegateForwarder] webView:[self webView] didHandleOnloadEventsForFrame:self];
544 // Called every time a resource is completely loaded, or an error is received.
545 - (void)_checkLoadComplete
547 ASSERT([self webView] != nil);
550 for (WebFrame *frame = self; frame; frame = parent) {
552 [[frame _frameLoader] checkLoadCompleteForThisFrame];
553 parent = [frame parentFrame];
558 - (WebFrameBridge *)_bridge
560 return _private->bridge;
563 // helper method that determines whether the subframes described by the item's subitems
564 // match our own current frameset
565 - (BOOL)_childFramesMatchItem:(WebHistoryItem *)item
567 NSArray *childItems = [item children];
568 int numChildItems = [childItems count];
569 int numChildFrames = [self _childFrameCount];
570 if (numChildFrames != numChildItems)
574 for (i = 0; i < numChildItems; i++) {
575 NSString *itemTargetName = [[childItems objectAtIndex:i] target];
576 //Search recursive here?
577 if (![self _immediateChildFrameNamed:itemTargetName])
578 return NO; // couldn't match the i'th itemTarget
581 return YES; // found matches for all itemTargets
584 // Walk the frame tree and ensure that the URLs match the URLs in the item.
585 - (BOOL)_URLsMatchItem:(WebHistoryItem *)item
587 NSURL *currentURL = [[[self dataSource] request] URL];
589 if (![[[item URL] _webkit_URLByRemovingFragment] isEqual:[currentURL _webkit_URLByRemovingFragment]])
592 NSArray *childItems = [item children];
593 WebHistoryItem *childItem;
594 WebFrame *childFrame;
595 int i, count = [childItems count];
596 for (i = 0; i < count; i++){
597 childItem = [childItems objectAtIndex:i];
598 childFrame = [self _immediateChildFrameNamed:[childItem target]];
599 if (![childFrame _URLsMatchItem: childItem])
606 // loads content into this frame, as specified by item
607 - (void)_loadItem:(WebHistoryItem *)item withLoadType:(FrameLoadType)loadType
609 NSURL *itemURL = [item URL];
610 NSURL *itemOriginalURL = [NSURL _web_URLWithDataAsString:[item originalURLString]];
611 NSURL *currentURL = [[[self dataSource] request] URL];
612 NSArray *formData = [item formData];
614 // Are we navigating to an anchor within the page?
615 // Note if we have child frames we do a real reload, since the child frames might not
616 // match our current frame structure, or they might not have the right content. We could
617 // check for all that as an additional optimization.
618 // We also do not do anchor-style navigation if we're posting a form.
620 // FIXME: These checks don't match the ones in _loadURL:referrer:loadType:target:triggeringEvent:isFormSubmission:
621 // Perhaps they should.
622 if (!formData && ![[self _frameLoader] shouldReloadForCurrent:itemURL andDestination:currentURL] && [self _URLsMatchItem:item] )
625 // FIXME: We need to normalize the code paths for anchor navigation. Something
626 // like the following line of code should be done, but also accounting for correct
627 // updates to the back/forward list and scroll position.
628 // rjw 4/9/03 See 3223929.
629 [self _loadURL:itemURL referrer:[[[self dataSource] request] HTTPReferrer] loadType:loadType target:nil triggeringEvent:nil form:nil formValues:nil];
631 // must do this maintenance here, since we don't go through a real page reload
632 [self _saveScrollPositionAndViewStateToItem:_private->currentItem];
633 // FIXME: form state might want to be saved here too
635 // We always call scrollToAnchorWithURL here, even if the URL doesn't have an
636 // anchor fragment. This is so we'll keep the WebCore Frame's URL up-to-date.
637 [_private->bridge scrollToAnchorWithURL:[item URL]];
639 // must do this maintenance here, since we don't go through a real page reload
640 [_private setCurrentItem:item];
641 [self _restoreScrollPositionAndViewState];
643 // Fake the URL change by updating the data source's request. This will no longer
644 // be necessary if we do the better fix described above.
645 [[[self _frameLoader] documentLoader] replaceRequestURLForAnchorScrollWithURL:itemURL];
647 [[[self webView] _frameLoadDelegateForwarder] webView:[self webView]
648 didChangeLocationWithinPageForFrame:self];
649 [_private->internalLoadDelegate webFrame:self didFinishLoadWithError:nil];
651 // Remember this item so we can traverse any child items as child frames load
652 [_private setProvisionalItem:item];
654 WebDataSource *newDataSource;
655 BOOL inPageCache = NO;
657 // Check if we'll be using the page cache. We only use the page cache
658 // if one exists and it is less than _backForwardCacheExpirationInterval
659 // seconds old. If the cache is expired it gets flushed here.
660 if ([item hasPageCache]){
661 NSDictionary *pageCache = [item pageCache];
662 NSDate *cacheDate = [pageCache objectForKey: WebPageCacheEntryDateKey];
663 NSTimeInterval delta = [[NSDate date] timeIntervalSinceDate: cacheDate];
665 if (delta <= [[[self webView] preferences] _backForwardCacheExpirationInterval]){
666 newDataSource = [pageCache objectForKey: WebPageCacheDataSourceKey];
667 [[self _frameLoader] loadDataSource:newDataSource withLoadType:loadType formState:nil];
671 LOG (PageCache, "Not restoring page from back/forward cache because cache entry has expired, %@ (%3.5f > %3.5f seconds)\n", [_private->provisionalItem URL], delta, [[[self webView] preferences] _backForwardCacheExpirationInterval]);
672 [item setHasPageCache: NO];
677 NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:itemURL];
678 [self _addExtraFieldsToRequest:request mainResource:YES alwaysFromRequest:(formData != nil) ? YES : NO];
680 // If this was a repost that failed the page cache, we might try to repost the form.
681 NSDictionary *action;
683 [request setHTTPMethod:@"POST"];
684 [request _web_setHTTPReferrer:[item formReferrer]];
685 webSetHTTPBody(request, formData);
686 [request _web_setHTTPContentType:[item formContentType]];
688 // Slight hack to test if the WF cache contains the page we're going to. We want
689 // to know this before talking to the policy delegate, since it affects whether we
690 // show the DoYouReallyWantToRepost nag.
692 // This trick has a small bug (3123893) where we might find a cache hit, but then
693 // have the item vanish when we try to use it in the ensuing nav. This should be
694 // extremely rare, but in that case the user will get an error on the navigation.
695 [request setCachePolicy:NSURLRequestReturnCacheDataDontLoad];
696 NSURLResponse *synchResponse = nil;
697 [NSURLConnection sendSynchronousRequest:request returningResponse:&synchResponse error:nil];
698 if (synchResponse == nil) {
700 [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
701 action = [self _actionInformationForNavigationType:WebNavigationTypeFormResubmitted event:nil originalURL:itemURL];
703 // We can use the cache, don't use navType=resubmit
704 action = [self _actionInformationForLoadType:loadType isFormSubmission:NO event:nil originalURL:itemURL];
708 case FrameLoadTypeReload:
709 [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
711 case FrameLoadTypeBack:
712 case FrameLoadTypeForward:
713 case FrameLoadTypeIndexedBackForward:
714 if (![[itemURL scheme] isEqual:@"https"])
715 [request setCachePolicy:NSURLRequestReturnCacheDataElseLoad];
717 case FrameLoadTypeStandard:
718 case FrameLoadTypeInternal:
719 // no-op: leave as protocol default
720 // FIXME: I wonder if we ever hit this case
722 case FrameLoadTypeSame:
723 case FrameLoadTypeReloadAllowingStaleData:
725 ASSERT_NOT_REACHED();
728 action = [self _actionInformationForLoadType:loadType isFormSubmission:NO event:nil originalURL:itemOriginalURL];
731 [[self _frameLoader] _loadRequest:request triggeringAction:action loadType:loadType formState:nil];
737 // The general idea here is to traverse the frame tree and the item tree in parallel,
738 // tracking whether each frame already has the content the item requests. If there is
739 // a match (by URL), we just restore scroll position and recurse. Otherwise we must
740 // reload that frame, and all its kids.
741 - (void)_recursiveGoToItem:(WebHistoryItem *)item fromItem:(WebHistoryItem *)fromItem withLoadType:(FrameLoadType)type
743 NSURL *itemURL = [item URL];
744 NSURL *currentURL = [[[self dataSource] request] URL];
746 // Always reload the target frame of the item we're going to. This ensures that we will
747 // do -some- load for the transition, which means a proper notification will be posted
749 // The exact URL has to match, including fragment. We want to go through the _load
750 // method, even if to do a within-page navigation.
751 // The current frame tree and the frame tree snapshot in the item have to match.
752 if (![item isTargetItem] &&
753 [itemURL isEqual:currentURL] &&
754 (([self name] == nil && [item target] == nil) ||[[self name] isEqualToString:[item target]]) &&
755 [self _childFramesMatchItem:item])
757 // This content is good, so leave it alone and look for children that need reloading
759 // Save form state (works from currentItem, since prevItem is nil)
760 ASSERT(!_private->previousItem);
761 [_private->bridge saveDocumentState];
762 [self _saveScrollPositionAndViewStateToItem:_private->currentItem];
764 [_private setCurrentItem:item];
766 // Restore form state (works from currentItem)
767 [_private->bridge restoreDocumentState];
768 // Restore the scroll position (taken in favor of going back to the anchor)
769 [self _restoreScrollPositionAndViewState];
771 NSArray *childItems = [item children];
772 int numChildItems = childItems ? [childItems count] : 0;
774 for (i = numChildItems - 1; i >= 0; i--) {
775 WebHistoryItem *childItem = [childItems objectAtIndex:i];
776 NSString *childName = [childItem target];
777 WebHistoryItem *fromChildItem = [fromItem childItemWithName:childName];
778 ASSERT(fromChildItem || [fromItem isTargetItem]);
779 WebFrame *childFrame = [self _immediateChildFrameNamed:childName];
781 [childFrame _recursiveGoToItem:childItem fromItem:fromChildItem withLoadType:type];
784 // We need to reload the content
785 [self _loadItem:item withLoadType:type];
789 // Main funnel for navigating to a previous location (back/forward, non-search snap-back)
790 // This includes recursion to handle loading into framesets properly
791 - (void)_goToItem:(WebHistoryItem *)item withLoadType:(FrameLoadType)type
793 ASSERT(![self parentFrame]);
794 // shouldGoToHistoryItem is a private delegate method. This is needed to fix:
795 // <rdar://problem/3951283> can view pages from the back/forward cache that should be disallowed by Parental Controls
796 // Ultimately, history item navigations should go through the policy delegate. That's covered in:
797 // <rdar://problem/3979539> back/forward cache navigations should consult policy delegate
798 if ([[[self webView] _policyDelegateForwarder] webView:[self webView] shouldGoToHistoryItem:item]) {
799 WebBackForwardList *backForwardList = [[self webView] backForwardList];
800 WebHistoryItem *currItem = [backForwardList currentItem];
801 // Set the BF cursor before commit, which lets the user quickly click back/forward again.
802 // - plus, it only makes sense for the top level of the operation through the frametree,
803 // as opposed to happening for some/one of the page commits that might happen soon
804 [backForwardList goToItem:item];
805 [self _recursiveGoToItem:item fromItem:currItem withLoadType:type];
809 -(NSDictionary *)_actionInformationForNavigationType:(WebNavigationType)navigationType event:(NSEvent *)event originalURL:(NSURL *)URL
811 switch ([event type]) {
812 case NSLeftMouseDown:
813 case NSRightMouseDown:
814 case NSOtherMouseDown:
819 NSView *topViewInEventWindow = [[event window] contentView];
820 NSView *viewContainingPoint = [topViewInEventWindow hitTest:[topViewInEventWindow convertPoint:[event locationInWindow] fromView:nil]];
821 while (viewContainingPoint != nil) {
822 if ([viewContainingPoint isKindOfClass:[WebHTMLView class]]) {
825 viewContainingPoint = [viewContainingPoint superview];
827 if (viewContainingPoint != nil) {
828 NSPoint point = [viewContainingPoint convertPoint:[event locationInWindow] fromView:nil];
829 NSDictionary *elementInfo = [(WebHTMLView *)viewContainingPoint elementAtPoint:point];
831 return [NSDictionary dictionaryWithObjectsAndKeys:
832 [NSNumber numberWithInt:navigationType], WebActionNavigationTypeKey,
833 elementInfo, WebActionElementKey,
834 [NSNumber numberWithInt:[event buttonNumber]], WebActionButtonKey,
835 [NSNumber numberWithInt:[event modifierFlags]], WebActionModifierFlagsKey,
836 URL, WebActionOriginalURLKey,
844 return [NSDictionary dictionaryWithObjectsAndKeys:
845 [NSNumber numberWithInt:navigationType], WebActionNavigationTypeKey,
846 [NSNumber numberWithInt:[event modifierFlags]], WebActionModifierFlagsKey,
847 URL, WebActionOriginalURLKey,
852 -(NSDictionary *)_actionInformationForLoadType:(FrameLoadType)loadType isFormSubmission:(BOOL)isFormSubmission event:(NSEvent *)event originalURL:(NSURL *)URL
854 WebNavigationType navType;
855 if (isFormSubmission) {
856 navType = WebNavigationTypeFormSubmitted;
857 } else if (event == nil) {
858 if (loadType == FrameLoadTypeReload)
859 navType = WebNavigationTypeReload;
860 else if (isBackForwardLoadType(loadType))
861 navType = WebNavigationTypeBackForward;
863 navType = WebNavigationTypeOther;
865 navType = WebNavigationTypeLinkClicked;
867 return [self _actionInformationForNavigationType:navType event:event originalURL:URL];
870 - (void)_loadURL:(NSURL *)URL referrer:(NSString *)referrer intoChild:(WebFrame *)childFrame
872 WebHistoryItem *parentItem = _private->currentItem;
873 NSArray *childItems = [parentItem children];
874 FrameLoadType loadType = [[self _frameLoader] loadType];
875 FrameLoadType childLoadType = FrameLoadTypeInternal;
876 WebHistoryItem *childItem = nil;
878 // If we're moving in the backforward list, we might want to replace the content
879 // of this child frame with whatever was there at that point.
880 // Reload will maintain the frame contents, LoadSame will not.
882 (isBackForwardLoadType(loadType)
883 || loadType == FrameLoadTypeReload
884 || loadType == FrameLoadTypeReloadAllowingStaleData))
886 childItem = [parentItem childItemWithName:[childFrame name]];
888 // Use the original URL to ensure we get all the side-effects, such as
889 // onLoad handlers, of any redirects that happened. An example of where
890 // this is needed is Radar 3213556.
891 URL = [NSURL _web_URLWithDataAsString:[childItem originalURLString]];
892 // These behaviors implied by these loadTypes should apply to the child frames
893 childLoadType = loadType;
895 if (isBackForwardLoadType(loadType))
896 // For back/forward, remember this item so we can traverse any child items as child frames load
897 [childFrame->_private setProvisionalItem:childItem];
899 // For reload, just reinstall the current item, since a new child frame was created but we won't be creating a new BF item
900 [childFrame->_private setCurrentItem:childItem];
904 WebArchive *archive = [[self dataSource] _popSubframeArchiveWithName:[childFrame name]];
906 [childFrame loadArchive:archive];
908 [[childFrame _frameLoader] loadURL:URL referrer:referrer loadType:childLoadType target:nil triggeringEvent:nil form:nil formValues:nil];
912 - (void)_setTitle:(NSString *)title
914 [_private->currentItem setTitle:title];
917 - (void)_saveScrollPositionAndViewStateToItem:(WebHistoryItem *)item
920 NSView <WebDocumentView> *docView = [[self frameView] documentView];
921 NSView *parent = [docView superview];
922 // we might already be detached when this is called from detachFromParent, in which
923 // case we don't want to override real data earlier gathered with (0,0)
926 if ([docView conformsToProtocol:@protocol(_WebDocumentViewState)]) {
927 // The view has it's own idea of where it is scrolled to, perhaps because it contains its own
928 // ScrollView instead of using the one provided by the WebFrame
929 point = [(id <_WebDocumentViewState>)docView scrollPoint];
930 [item setViewState:[(id <_WebDocumentViewState>)docView viewState]];
932 // Parent is the clipview of the DynamicScrollView the WebFrame installs
933 ASSERT([parent isKindOfClass:[NSClipView class]]);
934 point = [parent bounds].origin;
936 [item setScrollPoint:point];
941 - (void)_defersCallbacksChanged
943 for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self])
944 [[frame _frameLoader] defersCallbacksChanged];
947 - (void)_viewWillMoveToHostWindow:(NSWindow *)hostWindow
949 for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self])
950 [[[frame frameView] documentView] viewWillMoveToHostWindow:hostWindow];
953 - (void)_viewDidMoveToHostWindow
955 for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self])
956 [[[frame frameView] documentView] viewDidMoveToHostWindow];
959 - (void)_addChild:(WebFrame *)child
961 [[self _bridge] appendChild:[child _bridge]];
962 [[[child dataSource] _documentLoader] setOverrideEncoding:[[[self dataSource] _documentLoader] overrideEncoding]];
965 // If we bailed out of a b/f navigation, we might need to set the b/f cursor back to the current
966 // item, because we optimistically move it right away at the start of the operation. But when
967 // alternate content is loaded for an unreachableURL, we don't want to reset the b/f cursor.
968 // Return the item that we would reset to, so we can decide later whether to actually reset.
969 - (WebHistoryItem *)_currentBackForwardListItemToResetTo
971 if (isBackForwardLoadType([[self _frameLoader] loadType]) && self == [[self webView] mainFrame])
972 return _private->currentItem;
976 - (WebHistoryItem *)_itemForSavingDocState
978 // For a standard page load, we will have a previous item set, which will be used to
979 // store the form state. However, in some cases we will have no previous item, and
980 // the current item is the right place to save the state. One example is when we
981 // detach a bunch of frames because we are navigating from a site with frames to
982 // another site. Another is when saving the frame state of a frame that is not the
983 // target of the current navigation (if we even decide to save with that granularity).
985 // Because of previousItem's "masking" of currentItem for this purpose, it's important
986 // that previousItem be cleared at the end of a page transition. We leverage the
987 // checkLoadComplete recursion to achieve this goal.
989 return _private->previousItem ? _private->previousItem : _private->currentItem;
992 - (WebHistoryItem *)_itemForRestoringDocState
994 switch ([[self _frameLoader] loadType]) {
995 case FrameLoadTypeReload:
996 case FrameLoadTypeReloadAllowingStaleData:
997 case FrameLoadTypeSame:
998 case FrameLoadTypeReplace:
999 // Don't restore any form state on reload or loadSame
1001 case FrameLoadTypeBack:
1002 case FrameLoadTypeForward:
1003 case FrameLoadTypeIndexedBackForward:
1004 case FrameLoadTypeInternal:
1005 case FrameLoadTypeStandard:
1006 return _private->currentItem;
1008 ASSERT_NOT_REACHED();
1012 // Walk the frame tree, telling all frames to save their form state into their current
1014 - (void)_saveDocumentAndScrollState
1016 for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self]) {
1017 [[frame _bridge] saveDocumentState];
1018 [frame _saveScrollPositionAndViewStateToItem:frame->_private->currentItem];
1022 // used to decide to use loadType=Same
1023 - (BOOL)_shouldTreatURLAsSameAsCurrent:(NSURL *)URL
1025 WebHistoryItem *item = _private->currentItem;
1026 NSString* URLString = [URL _web_originalDataAsString];
1027 return [URLString isEqual:[item URLString]] || [URLString isEqual:[item originalURLString]];
1030 // Return next frame to be traversed, visiting children after parent
1031 - (WebFrame *)_nextFrameWithWrap:(BOOL)wrapFlag
1033 return Frame([[self _bridge] nextFrameWithWrap:wrapFlag]);
1036 // Return previous frame to be traversed, exact reverse order of _nextFrame
1037 - (WebFrame *)_previousFrameWithWrap:(BOOL)wrapFlag
1039 return Frame([[self _bridge] previousFrameWithWrap:wrapFlag]);
1042 - (BOOL)_shouldCreateRenderers
1044 return [_private->bridge shouldCreateRenderers];
1047 - (int)_numPendingOrLoadingRequests:(BOOL)recurse
1050 return [[self _bridge] numPendingOrLoadingRequests];
1053 for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self])
1054 num += [[frame _bridge] numPendingOrLoadingRequests];
1059 - (void)_reloadForPluginChanges
1061 for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self]) {
1062 NSView <WebDocumentView> *documentView = [[frame frameView] documentView];
1063 if (([documentView isKindOfClass:[WebHTMLView class]] && [_private->bridge containsPlugins]))
1068 - (void)_attachScriptDebugger
1070 if (!_private->scriptDebugger)
1071 _private->scriptDebugger = [[WebScriptDebugger alloc] initWithWebFrame:self];
1074 - (void)_detachScriptDebugger
1076 if (_private->scriptDebugger) {
1077 id old = _private->scriptDebugger;
1078 _private->scriptDebugger = nil;
1083 - (void)_recursive_pauseNullEventsForAllNetscapePlugins
1085 for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self]) {
1086 NSView <WebDocumentView> *documentView = [[frame frameView] documentView];
1087 if ([documentView isKindOfClass:[WebHTMLView class]])
1088 [(WebHTMLView *)documentView _pauseNullEventsForAllNetscapePlugins];
1092 - (void)_recursive_resumeNullEventsForAllNetscapePlugins
1094 for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self]) {
1095 NSView <WebDocumentView> *documentView = [[frame frameView] documentView];
1096 if ([documentView isKindOfClass:[WebHTMLView class]])
1097 [(WebHTMLView *)documentView _resumeNullEventsForAllNetscapePlugins];
1101 - (void)_didReceiveServerRedirectForProvisionalLoadForFrame
1103 [[[self webView] _frameLoadDelegateForwarder] webView:[self webView]
1104 didReceiveServerRedirectForProvisionalLoadForFrame:self];
1107 - (id)_initWithWebFrameView:(WebFrameView *)fv webView:(WebView *)v bridge:(WebFrameBridge *)bridge
1109 self = [super init];
1113 _private = [[WebFramePrivate alloc] init];
1115 _private->bridge = bridge;
1118 [_private setWebFrameView:fv];
1119 [fv _setWebFrame:self];
1127 - (NSArray *)_documentViews
1129 NSMutableArray *result = [NSMutableArray array];
1130 for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self]) {
1131 id docView = [[frame frameView] documentView];
1133 [result addObject:docView];
1139 - (void)_updateBackground
1141 BOOL drawsBackground = [[self webView] drawsBackground];
1142 NSColor *backgroundColor = [[self webView] backgroundColor];
1144 for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self]) {
1145 // Never call setDrawsBackground:YES here on the scroll view or the background color will
1146 // flash between pages loads. setDrawsBackground:YES will be called in _frameLoadCompleted.
1147 if (!drawsBackground)
1148 [[[frame frameView] _scrollView] setDrawsBackground:NO];
1149 [[[frame frameView] _scrollView] setBackgroundColor:backgroundColor];
1150 id documentView = [[frame frameView] documentView];
1151 if ([documentView respondsToSelector:@selector(setDrawsBackground:)])
1152 [documentView setDrawsBackground:drawsBackground];
1153 if ([documentView respondsToSelector:@selector(setBackgroundColor:)])
1154 [documentView setBackgroundColor:backgroundColor];
1155 [[frame _bridge] setDrawsBackground:drawsBackground];
1156 [[frame _bridge] setBaseBackgroundColor:backgroundColor];
1160 - (void)_setInternalLoadDelegate:(id)internalLoadDelegate
1162 _private->internalLoadDelegate = internalLoadDelegate;
1165 - (id)_internalLoadDelegate
1167 return _private->internalLoadDelegate;
1170 - (void)_safeLoadURL:(NSURL *)URL
1172 // Call the bridge because this is where our security checks are made.
1173 [[self _bridge] loadURL:URL
1174 referrer:[[[[self dataSource] request] URL] _web_originalDataAsString]
1178 triggeringEvent:[NSApp currentEvent]
1183 - (void)_unmarkAllMisspellings
1185 for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self])
1186 [[frame _bridge] unmarkAllMisspellings];
1189 - (BOOL)_hasSelection
1191 id documentView = [[self frameView] documentView];
1193 // optimization for common case to avoid creating potentially large selection string
1194 if ([documentView isKindOfClass:[WebHTMLView class]]) {
1195 DOMRange *selectedDOMRange = [[self _bridge] selectedDOMRange];
1196 return selectedDOMRange && ![selectedDOMRange collapsed];
1199 if ([documentView conformsToProtocol:@protocol(WebDocumentText)])
1200 return [[documentView selectedString] length] > 0;
1205 - (void)_clearSelection
1207 id documentView = [[self frameView] documentView];
1208 if ([documentView conformsToProtocol:@protocol(WebDocumentText)])
1209 [documentView deselectAll];
1214 - (BOOL)_atMostOneFrameHasSelection;
1216 // FIXME: 4186050 is one known case that makes this debug check fail
1218 for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self]) {
1219 if ([frame _hasSelection]) {
1230 - (WebFrame *)_findFrameWithSelection
1232 for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self])
1233 if ([frame _hasSelection])
1239 - (void)_clearSelectionInOtherFrames
1241 // We rely on WebDocumentSelection protocol implementors to call this method when they become first
1242 // responder. It would be nicer to just notice first responder changes here instead, but there's no
1243 // notification sent when the first responder changes in general (Radar 2573089).
1244 WebFrame *frameWithSelection = [[[self webView] mainFrame] _findFrameWithSelection];
1245 if (frameWithSelection != self)
1246 [frameWithSelection _clearSelection];
1248 // While we're in the general area of selection and frames, check that there is only one now.
1249 ASSERT([[[self webView] mainFrame] _atMostOneFrameHasSelection]);
1252 - (BOOL)_subframeIsLoading
1254 // It's most likely that the last added frame is the last to load so we walk backwards.
1255 for (WebFrame *frame = [self _lastChildFrame]; frame; frame = [frame _previousSiblingFrame])
1256 if ([[frame dataSource] isLoading] || [[frame provisionalDataSource] isLoading])
1261 - (void)_addPlugInView:(NSView *)plugInView
1263 ASSERT([plugInView respondsToSelector:@selector(setWebFrame:)]);
1264 ASSERT(![_private->plugInViews containsObject:plugInView]);
1266 if (!_private->plugInViews)
1267 _private->plugInViews = [[NSMutableSet alloc] init];
1269 [plugInView setWebFrame:self];
1270 [_private->plugInViews addObject:plugInView];
1273 - (void)_removeAllPlugInViews
1275 if (!_private->plugInViews)
1278 [_private->plugInViews makeObjectsPerformSelector:@selector(setWebFrame:) withObject:nil];
1279 [_private->plugInViews release];
1280 _private->plugInViews = nil;
1283 // This is called when leaving a page or closing the WebView
1284 - (void)_willCloseURL
1286 [self _removeAllPlugInViews];
1289 - (void)_addExtraFieldsToRequest:(NSMutableURLRequest *)request mainResource:(BOOL)mainResource alwaysFromRequest:(BOOL)f
1291 [request _web_setHTTPUserAgent:[[self webView] userAgentForURL:[request URL]]];
1293 if ([[self _frameLoader] loadType] == FrameLoadTypeReload)
1294 [request setValue:@"max-age=0" forHTTPHeaderField:@"Cache-Control"];
1296 // Don't set the cookie policy URL if it's already been set.
1297 if ([request mainDocumentURL] == nil) {
1298 if (mainResource && (self == [[self webView] mainFrame] || f))
1299 [request setMainDocumentURL:[request URL]];
1301 [request setMainDocumentURL:[[[[self webView] mainFrame] dataSource] _URL]];
1305 [request setValue:@"text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" forHTTPHeaderField:@"Accept"];
1308 - (BOOL)_isMainFrame
1310 return self == [[self webView] mainFrame];
1313 - (void)_addInspector:(WebInspector *)inspector
1315 if (!_private->inspectors)
1316 _private->inspectors = [[NSMutableSet alloc] init];
1317 ASSERT(![_private->inspectors containsObject:inspector]);
1318 [_private->inspectors addObject:inspector];
1321 - (void)_removeInspector:(WebInspector *)inspector
1323 ASSERT([_private->inspectors containsObject:inspector]);
1324 [_private->inspectors removeObject:inspector];
1327 - (WebFrameLoader *)_frameLoader
1329 return [_private->bridge frameLoader];
1332 - (void)_prepareForDataSourceReplacement
1334 if (![[self _frameLoader] dataSource]) {
1335 ASSERT(![self _childFrameCount]);
1339 // Make sure that any work that is triggered by resigning first reponder can get done.
1340 // The main example where this came up is the textDidEndEditing that is sent to the
1341 // FormsDelegate (3223413). We need to do this before _detachChildren, since that will
1342 // remove the views as a side-effect of freeing the bridge, at which point we can't
1343 // post the FormDelegate messages.
1345 // Note that this can also take FirstResponder away from a child of our frameView that
1346 // is not in a child frame's view. This is OK because we are in the process
1347 // of loading new content, which will blow away all editors in this top frame, and if
1348 // a non-editor is firstReponder it will not be affected by endEditingFor:.
1349 // Potentially one day someone could write a DocView whose editors were not all
1350 // replaced by loading new content, but that does not apply currently.
1351 NSView *frameView = [self frameView];
1352 NSWindow *window = [frameView window];
1353 NSResponder *firstResp = [window firstResponder];
1354 if ([firstResp isKindOfClass:[NSView class]]
1355 && [(NSView *)firstResp isDescendantOf:frameView])
1357 [window endEditingFor:firstResp];
1360 [self _detachChildren];
1363 - (void)_frameLoadCompleted
1365 // Note: Can be called multiple times.
1366 // Even if already complete, we might have set a previous item on a frame that
1367 // didn't do any data loading on the past transaction. Make sure to clear these out.
1368 NSScrollView *sv = [[self frameView] _scrollView];
1369 if ([[self webView] drawsBackground])
1370 [sv setDrawsBackground:YES];
1371 [_private setPreviousItem:nil];
1374 - (WebDataSource *)_dataSourceForDocumentLoader:(WebDocumentLoader *)loader
1376 return [(WebDocumentLoaderMac *)loader dataSource];
1379 - (WebDocumentLoader *)_createDocumentLoaderWithRequest:(NSURLRequest *)request
1381 WebDocumentLoaderMac *loader = [[WebDocumentLoaderMac alloc] initWithRequest:request];
1383 WebDataSource *dataSource = [[WebDataSource alloc] _initWithDocumentLoader:loader];
1384 [loader setDataSource:dataSource];
1385 [dataSource release];
1391 There is a race condition between the layout and load completion that affects restoring the scroll position.
1392 We try to restore the scroll position at both the first layout and upon load completion.
1394 1) If first layout happens before the load completes, we want to restore the scroll position then so that the
1395 first time we draw the page is already scrolled to the right place, instead of starting at the top and later
1396 jumping down. It is possible that the old scroll position is past the part of the doc laid out so far, in
1397 which case the restore silent fails and we will fix it in when we try to restore on doc completion.
1398 2) If the layout happens after the load completes, the attempt to restore at load completion time silently
1399 fails. We then successfully restore it when the layout happens.
1402 - (void)_restoreScrollPositionAndViewState
1404 ASSERT(_private->currentItem);
1405 NSView <WebDocumentView> *docView = [[self frameView] documentView];
1406 NSPoint point = [_private->currentItem scrollPoint];
1407 if ([docView conformsToProtocol:@protocol(_WebDocumentViewState)]) {
1408 id state = [_private->currentItem viewState];
1410 [(id <_WebDocumentViewState>)docView setViewState:state];
1413 [(id <_WebDocumentViewState>)docView setScrollPoint:point];
1415 [docView scrollPoint:point];
1421 @implementation WebFrame (WebPrivate)
1423 // FIXME: this exists only as a convenience for Safari, consider moving there
1424 - (BOOL)_isDescendantOfFrame:(WebFrame *)ancestor
1426 return [[self _bridge] isDescendantOfFrame:[ancestor _bridge]];
1429 - (void)_setShouldCreateRenderers:(BOOL)f
1431 [_private->bridge setShouldCreateRenderers:f];
1434 - (NSColor *)_bodyBackgroundColor
1436 return [_private->bridge bodyBackgroundColor];
1441 return [_private->bridge isFrameSet];
1444 - (BOOL)_firstLayoutDone
1446 return [[self _frameLoader] firstLayoutDone];
1449 - (WebFrameLoadType)_loadType
1451 return (WebFrameLoadType)[[self _frameLoader] loadType];
1456 @implementation WebFormState : NSObject
1458 - (id)initWithForm:(DOMElement *)form values:(NSDictionary *)values sourceFrame:(WebFrame *)sourceFrame
1460 self = [super init];
1464 _form = [form retain];
1465 _values = [values copy];
1466 _sourceFrame = [sourceFrame retain];
1474 [_sourceFrame release];
1478 - (DOMElement *)form
1483 - (NSDictionary *)values
1488 - (WebFrame *)sourceFrame
1490 return _sourceFrame;
1495 @implementation WebFrame
1499 return [self initWithName:nil webFrameView:nil webView:nil];
1502 // FIXME: this method can't work any more and should be marked deprecated
1503 - (id)initWithName:(NSString *)n webFrameView:(WebFrameView *)fv webView:(WebView *)v
1505 return [self _initWithWebFrameView:fv webView:v bridge:nil];
1510 ASSERT(_private->bridge == nil);
1520 ASSERT(_private->bridge == nil);
1529 return [[self _bridge] name];
1532 - (WebFrameView *)frameView
1534 return _private->webFrameView;
1537 - (WebView *)webView
1539 return [[[self _bridge] page] webView];
1542 - (DOMDocument *)DOMDocument
1544 return [[self dataSource] _isDocumentHTML] ? [_private->bridge DOMDocument] : nil;
1547 - (DOMHTMLElement *)frameElement
1549 return [[self webView] mainFrame] != self ? [_private->bridge frameElement] : nil;
1552 - (WebDataSource *)provisionalDataSource
1554 return [[self _frameLoader] provisionalDataSource];
1557 - (WebDataSource *)dataSource
1559 return [[self _frameLoader] dataSource];
1562 - (void)loadRequest:(NSURLRequest *)request
1564 // FIXME: is this the right place to reset loadType? Perhaps this should be done
1565 // after loading is finished or aborted.
1566 [[self _frameLoader] setLoadType:FrameLoadTypeStandard];
1567 [[self _frameLoader] _loadRequest:request archive:nil];
1570 - (void)_loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)URL unreachableURL:(NSURL *)unreachableURL
1572 NSURLRequest *request = [self _webDataRequestForData:data
1574 textEncodingName:encodingName
1576 unreachableURL:unreachableURL];
1577 [self loadRequest:request];
1581 - (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)URL
1583 [self _loadData:data MIMEType:MIMEType textEncodingName:encodingName baseURL:URL unreachableURL:nil];
1586 - (void)_loadHTMLString:(NSString *)string baseURL:(NSURL *)URL unreachableURL:(NSURL *)unreachableURL
1588 NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
1589 [self _loadData:data MIMEType:nil textEncodingName:@"UTF-8" baseURL:URL unreachableURL:unreachableURL];
1592 - (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)URL
1594 [self _loadHTMLString:string baseURL:URL unreachableURL:nil];
1597 - (void)loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)URL forUnreachableURL:(NSURL *)unreachableURL
1599 [self _loadHTMLString:string baseURL:URL unreachableURL:unreachableURL];
1602 - (void)loadArchive:(WebArchive *)archive
1604 WebResource *mainResource = [archive mainResource];
1606 NSURLRequest *request = [self _webDataRequestForData:[mainResource data]
1607 MIMEType:[mainResource MIMEType]
1608 textEncodingName:[mainResource textEncodingName]
1609 baseURL:[mainResource URL]
1610 unreachableURL:nil];
1611 [[self _frameLoader] _loadRequest:request archive:archive];
1617 [[self _frameLoader] stopLoading];
1622 [[self _frameLoader] reload];
1625 - (WebFrame *)findFrameNamed:(NSString *)name
1627 return Frame([[self _bridge] findFrameNamed:name]);
1630 - (WebFrame *)parentFrame
1632 return [[Frame([[self _bridge] parent]) retain] autorelease];
1635 - (NSArray *)childFrames
1637 NSMutableArray *children = [NSMutableArray arrayWithCapacity:[self _childFrameCount]];
1638 for (WebFrame *child = [self _firstChildFrame]; child; child = [child _nextSiblingFrame])
1639 [children addObject:child];
1646 @implementation WebFrame (WebFrameLoaderClient)
1648 - (void)_resetBackForwardList
1650 // Note this doesn't verify the current load type as a b/f operation because it is called from
1651 // a subframe in the case of a delegate bailing out of the nav before it even gets to provisional state.
1652 ASSERT(self == [[self webView] mainFrame]);
1653 WebHistoryItem *resetItem = _private->currentItem;
1655 [[[self webView] backForwardList] goToItem:resetItem];
1658 - (void)_invalidateCurrentItemPageCache
1660 // When we are pre-commit, the currentItem is where the pageCache data resides
1661 NSDictionary *pageCache = [_private->currentItem pageCache];
1663 [[self _bridge] invalidatePageCache:pageCache];
1665 // We're assuming that WebCore invalidates its pageCache state in didNotOpen:pageCache:
1666 [_private->currentItem setHasPageCache:NO];
1669 - (BOOL)_provisionalItemIsTarget
1671 return [_private->provisionalItem isTargetItem];
1674 - (BOOL)_loadProvisionalItemFromPageCache
1676 WebHistoryItem *item = _private->provisionalItem;
1677 if (![item hasPageCache])
1679 NSDictionary *pageCache = [item pageCache];
1680 if (![pageCache objectForKey:WebCorePageCacheStateKey])
1682 LOG(PageCache, "Restoring page from back/forward cache, %@", [item URL]);
1683 [[self provisionalDataSource] _loadFromPageCache:pageCache];
1687 - (BOOL)_privateBrowsingEnabled
1689 return [[[self webView] preferences] privateBrowsingEnabled];
1692 - (void)_makeDocumentView
1694 NSView <WebDocumentView> *documentView = [_private->webFrameView _makeDocumentViewForDataSource:[[self _frameLoader] dataSource]];
1698 // FIXME: We could save work and not do this for a top-level view that is not a WebHTMLView.
1699 WebFrameView *v = _private->webFrameView;
1700 [_private->bridge createFrameViewWithNSView:documentView marginWidth:[v _marginWidth] marginHeight:[v _marginHeight]];
1701 [self _updateBackground];
1702 [_private->bridge installInFrame:[v _scrollView]];
1704 // Call setDataSource on the document view after it has been placed in the view hierarchy.
1705 // This what we for the top-level view, so should do this for views in subframes as well.
1706 [documentView setDataSource:[[self _frameLoader] dataSource]];
1709 - (void)_updateHistoryForCommit
1711 WebFrameLoadType type = [[self _frameLoader] loadType];
1712 if (isBackForwardLoadType(type) ||
1713 (type == WebFrameLoadTypeReload && [[self provisionalDataSource] unreachableURL] != nil)) {
1714 // Once committed, we want to use current item for saving DocState, and
1715 // the provisional item for restoring state.
1716 // Note previousItem must be set before we close the URL, which will
1717 // happen when the data source is made non-provisional below
1718 [_private setPreviousItem:_private->currentItem];
1719 ASSERT(_private->provisionalItem);
1720 [_private setCurrentItem:_private->provisionalItem];
1721 [_private setProvisionalItem:nil];
1725 - (void)_updateHistoryForReload
1727 WebHistoryItem *currItem = _private->currentItem;
1728 LOG(PageCache, "Clearing back/forward cache, %@\n", [currItem URL]);
1729 [currItem setHasPageCache:NO];
1730 if ([[self _frameLoader] loadType] == WebFrameLoadTypeReload)
1731 [self _saveScrollPositionAndViewStateToItem:currItem];
1732 WebDataSource *dataSource = [[self _frameLoader] dataSource];
1733 NSURLRequest *request = [dataSource request];
1734 // Sometimes loading a page again leads to a different result because of cookies. Bugzilla 4072
1735 if ([request _webDataRequestUnreachableURL] == nil)
1736 [currItem setURL:[request URL]];
1737 // Update the last visited time. Mostly interesting for URL autocompletion statistics.
1738 NSURL *URL = [[[[dataSource _documentLoader] originalRequestCopy] URL] _webkit_canonicalize];
1739 WebHistory *sharedHistory = [WebHistory optionalSharedHistory];
1740 WebHistoryItem *oldItem = [sharedHistory itemForURL:URL];
1742 [sharedHistory setLastVisitedTimeInterval:[NSDate timeIntervalSinceReferenceDate] forItem:oldItem];
1745 - (void)_updateHistoryForStandardLoad
1747 WebDataSource *dataSource = [[self _frameLoader] dataSource];
1748 if (![[dataSource _documentLoader] isClientRedirect]) {
1749 NSURL *URL = [dataSource _URLForHistory];
1750 if (URL && ![URL _web_isEmpty]) {
1751 ASSERT([self webView]);
1752 if (![[[self webView] preferences] privateBrowsingEnabled]) {
1753 WebHistoryItem *entry = [[WebHistory optionalSharedHistory] addItemForURL:URL];
1754 if ([dataSource pageTitle])
1755 [entry setTitle:[dataSource pageTitle]];
1757 [self _addBackForwardItemClippedAtTarget:YES];
1760 NSURLRequest *request = [dataSource request];
1762 // Update the URL in the BF list that we made before the redirect, unless
1763 // this is alternate content for an unreachable URL (we want the BF list
1764 // item to remember the unreachable URL in case it becomes reachable later).
1765 if ([request _webDataRequestUnreachableURL] == nil) {
1766 [_private->currentItem setURL:[request URL]];
1768 // clear out the form data so we don't repost it to the wrong place if we
1769 // ever go back/forward to this item
1770 [_private->currentItem _setFormInfoFromRequest:request];
1772 // We must also clear out form data so we don't try to restore it into the incoming page,
1778 - (void)_updateHistoryForBackForwardNavigation
1780 // Must grab the current scroll position before disturbing it
1781 [self _saveScrollPositionAndViewStateToItem:_private->previousItem];
1784 - (void)_updateHistoryForInternalLoad
1786 WebDataSource *dataSource = [[self _frameLoader] dataSource];
1788 // Add an item to the item tree for this frame
1789 ASSERT(![[dataSource _documentLoader] isClientRedirect]);
1790 WebFrame *parentFrame = [self parentFrame];
1792 WebHistoryItem *parentItem = parentFrame->_private->currentItem;
1793 // The only case where parentItem==nil should be when a parent frame loaded an
1794 // empty URL, which doesn't set up a current item in that parent.
1796 [parentItem addChildItem:[self _createItem:YES]];
1798 // See 3556159. It's not clear if it's valid to be in WebFrameLoadTypeOnLoadEvent
1799 // for a top-level frame, but that was a likely explanation for those crashes,
1800 // so let's guard against it.
1801 // ...and all WebFrameLoadTypeOnLoadEvent uses were folded to WebFrameLoadTypeInternal
1802 LOG_ERROR("no parent frame in transitionToCommitted:, WebFrameLoadTypeInternal");
1806 - (LoadErrorResetToken *)_tokenForLoadErrorReset
1808 return (LoadErrorResetToken*)[[self _currentBackForwardListItemToResetTo] retain];
1811 - (void)_resetAfterLoadError:(LoadErrorResetToken *)token
1813 WebHistoryItem *item = (WebHistoryItem *)token;
1815 [[[self webView] backForwardList] goToItem:item];
1819 - (void)_doNotResetAfterLoadError:(LoadErrorResetToken *)token
1821 WebHistoryItem *item = (WebHistoryItem *)token;