Reviewed by Darin.
[WebKit-https.git] / WebKit / WebView / WebFrame.m
1 /*
2  * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
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. 
16  *
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.
27  */
28
29 #import "WebFrameInternal.h"
30
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 "WebFrameLoaderClient.h"
44 #import "WebFrameViewInternal.h"
45 #import "WebHTMLRepresentationPrivate.h"
46 #import "WebHTMLViewInternal.h"
47 #import "WebHTMLViewPrivate.h"
48 #import "WebHistoryItemPrivate.h"
49 #import "WebHistoryPrivate.h"
50 #import "WebKitErrorsPrivate.h"
51 #import "WebKitLogging.h"
52 #import "WebKitNSStringExtras.h"
53 #import "WebKitStatisticsPrivate.h"
54 #import "WebNSObjectExtras.h"
55 #import "WebNSURLExtras.h"
56 #import "WebNSURLRequestExtras.h"
57 #import "WebNetscapePluginEmbeddedView.h"
58 #import "WebNullPluginView.h"
59 #import "WebPlugin.h"
60 #import "WebPluginController.h"
61 #import "WebPreferencesPrivate.h"
62 #import "WebResourceLoadDelegate.h"
63 #import "WebResourcePrivate.h"
64 #import "WebScriptDebugDelegatePrivate.h"
65 #import "WebUIDelegate.h"
66 #import "WebViewInternal.h"
67 #import <WebKit/DOM.h>
68 #import <WebKitSystemInterface.h>
69 #import <objc/objc-runtime.h>
70
71 /*
72 Here is the current behavior matrix for four types of navigations:
73
74 Standard Nav:
75
76  Restore form state:   YES
77  Restore scroll and focus state:  YES
78  WF Cache policy: NSURLRequestUseProtocolCachePolicy
79  Add to back/forward list: YES
80  
81 Back/Forward:
82
83  Restore form state:   YES
84  Restore scroll and focus state:  YES
85  WF Cache policy: NSURLRequestReturnCacheDataElseLoad
86  Add to back/forward list: NO
87
88 Reload (meaning only the reload button):
89
90  Restore form state:   NO
91  Restore scroll and focus state:  YES
92  WF Cache policy: NSURLRequestReloadIgnoringCacheData
93  Add to back/forward list: NO
94
95 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
97  Restore form state:   NO
98  Restore scroll and focus state:  NO, reset to initial conditions
99  WF Cache policy: NSURLRequestReloadIgnoringCacheData
100  Add to back/forward list: NO
101 */
102
103 NSString *WebPageCacheEntryDateKey = @"WebPageCacheEntryDateKey";
104 NSString *WebPageCacheDataSourceKey = @"WebPageCacheDataSourceKey";
105 NSString *WebPageCacheDocumentViewKey = @"WebPageCacheDocumentViewKey";
106
107 @interface WebFrame (ForwardDecls)
108 - (void)_loadHTMLString:(NSString *)string baseURL:(NSURL *)URL unreachableURL:(NSURL *)unreachableURL;
109 - (NSDictionary *)_actionInformationForLoadType:(WebFrameLoadType)loadType isFormSubmission:(BOOL)isFormSubmission event:(NSEvent *)event originalURL:(NSURL *)URL;
110
111 - (void)_saveScrollPositionAndViewStateToItem:(WebHistoryItem *)item;
112
113 - (WebHistoryItem *)_createItem: (BOOL)useOriginal;
114 - (WebHistoryItem *)_createItemTreeWithTargetFrame:(WebFrame *)targetFrame clippedAtTarget:(BOOL)doClip;
115 - (WebHistoryItem *)_currentBackForwardListItemToResetTo;
116 @end
117
118 @interface WebFrame (FrameTraversal)
119 - (WebFrame *)_firstChildFrame;
120 - (WebFrame *)_lastChildFrame;
121 - (unsigned)_childFrameCount;
122 - (WebFrame *)_previousSiblingFrame;
123 - (WebFrame *)_nextSiblingFrame;
124 - (WebFrame *)_traverseNextFrameStayWithin:(WebFrame *)stayWithin;
125 @end
126
127 @interface WebFrame (WebFrameLoaderClient) <WebFrameLoaderClient>
128 @end
129
130 @interface NSView (WebFramePluginHosting)
131 - (void)setWebFrame:(WebFrame *)webFrame;
132 @end
133
134 @interface WebFramePrivate : NSObject
135 {
136 @public
137     WebFrameView *webFrameView;
138     WebFrameLoader *frameLoader;
139
140     WebFrameBridge *bridge;
141     WebHistoryItem *currentItem;        // BF item for our current content
142     WebHistoryItem *provisionalItem;    // BF item for where we're trying to go
143                                         // (only known when navigating to a pre-existing BF item)
144     WebHistoryItem *previousItem;       // BF item for previous content, see _itemForSavingDocState
145
146     WebScriptDebugger *scriptDebugger;
147     id internalLoadDelegate;
148     
149     NSMutableSet *plugInViews;
150     NSMutableSet *inspectors;
151 }
152
153 - (void)setWebFrameView:(WebFrameView *)v;
154 - (WebFrameView *)webFrameView;
155
156 - (void)setProvisionalItem:(WebHistoryItem *)item;
157 - (WebHistoryItem *)provisionalItem;
158 - (void)setPreviousItem:(WebHistoryItem *)item;
159 - (WebHistoryItem *)previousItem;
160 - (void)setCurrentItem:(WebHistoryItem *)item;
161 - (WebHistoryItem *)currentItem;
162
163 @end
164
165 @implementation WebFramePrivate
166
167 - (void)dealloc
168 {
169     [webFrameView release];
170     [frameLoader release];
171
172     [currentItem release];
173     [provisionalItem release];
174     [previousItem release];
175     
176     [scriptDebugger release];
177     
178     [inspectors release];
179
180     ASSERT(plugInViews == nil);
181     
182     [super dealloc];
183 }
184
185 - (WebFrameView *)webFrameView { return webFrameView; }
186 - (void)setWebFrameView: (WebFrameView *)v 
187
188     [v retain];
189     [webFrameView release];
190     webFrameView = v;
191 }
192
193 - (WebHistoryItem *)provisionalItem { return provisionalItem; }
194 - (void)setProvisionalItem: (WebHistoryItem *)item
195 {
196     [item retain];
197     [provisionalItem release];
198     provisionalItem = item;
199 }
200
201 - (WebHistoryItem *)previousItem { return previousItem; }
202 - (void)setPreviousItem:(WebHistoryItem *)item
203 {
204     [item retain];
205     [previousItem release];
206     previousItem = item;
207 }
208
209 - (WebHistoryItem *)currentItem { return currentItem; }
210 - (void)setCurrentItem:(WebHistoryItem *)item
211 {
212     [item retain];
213     [currentItem release];
214     currentItem = item;
215 }
216
217 @end
218 static inline WebFrame *Frame(WebCoreFrameBridge *bridge)
219 {
220     return [(WebFrameBridge *)bridge webFrame];
221 }
222
223 @implementation WebFrame (FrameTraversal)
224 - (WebFrame *)_firstChildFrame
225 {
226     return Frame([[self _bridge] firstChild]);
227 }
228
229 - (WebFrame *)_lastChildFrame
230 {
231     return Frame([[self _bridge] lastChild]);
232 }
233
234 - (unsigned)_childFrameCount
235 {
236     return [[self _bridge] childCount];
237 }
238
239 - (WebFrame *)_previousSiblingFrame;
240 {
241     return Frame([[self _bridge] previousSibling]);
242 }
243
244 - (WebFrame *)_nextSiblingFrame;
245 {
246     return Frame([[self _bridge] nextSibling]);
247 }
248
249 - (WebFrame *)_traverseNextFrameStayWithin:(WebFrame *)stayWithin
250 {
251     return Frame([[self _bridge] traverseNextFrameStayWithin:[stayWithin _bridge]]);
252 }
253
254 @end
255
256 @implementation WebFrame (WebInternal)
257
258 - (NSURLRequest *)_webDataRequestForData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName: (NSString *)encodingName baseURL:(NSURL *)URL unreachableURL:(NSURL *)unreachableURL
259 {
260     NSURL *fakeURL = [NSURL _web_uniqueWebDataURL];
261     NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:fakeURL] autorelease];
262     [request _webDataRequestSetData:data];
263     [request _webDataRequestSetEncoding:encodingName];
264     [request _webDataRequestSetBaseURL:URL];
265     [request _webDataRequestSetUnreachableURL:unreachableURL];
266     [request _webDataRequestSetMIMEType: MIMEType ? MIMEType : (NSString *)@"text/html"];
267     return request;
268 }
269
270 // helper method used in various nav cases below
271 - (void)_addBackForwardItemClippedAtTarget:(BOOL)doClip
272 {
273     if ([[self dataSource] _URLForHistory] != nil) {
274         WebHistoryItem *bfItem = [[[self webView] mainFrame] _createItemTreeWithTargetFrame:self clippedAtTarget:doClip];
275         LOG (BackForward, "for frame %@, adding item  %@\n", [self name], bfItem);
276         [[[self webView] backForwardList] addItem:bfItem];
277     }
278 }
279
280 - (void)_addHistoryItemForFragmentScroll
281 {
282     [self _addBackForwardItemClippedAtTarget:NO];
283 }
284
285 - (void)_didFinishLoad
286 {
287     [_private->internalLoadDelegate webFrame:self didFinishLoadWithError:nil];    
288 }
289
290 - (WebHistoryItem *)_createItem:(BOOL)useOriginal
291 {
292     WebDataSource *dataSrc = [self dataSource];
293     NSURLRequest *request;
294     NSURL *unreachableURL = [dataSrc unreachableURL];
295     NSURL *URL;
296     NSURL *originalURL;
297     WebHistoryItem *bfItem;
298
299     if (useOriginal)
300         request = [[dataSrc _documentLoader] originalRequestCopy];
301     else
302         request = [dataSrc request];
303
304     if (unreachableURL != nil) {
305         URL = unreachableURL;
306         originalURL = unreachableURL;
307     } else {
308         URL = [request URL];
309         originalURL = [[[dataSrc _documentLoader] originalRequestCopy] URL];
310     }
311
312     LOG (History, "creating item for %@", request);
313     
314     // Frames that have never successfully loaded any content
315     // may have no URL at all. Currently our history code can't
316     // deal with such things, so we nip that in the bud here.
317     // Later we may want to learn to live with nil for URL.
318     // See bug 3368236 and related bugs for more information.
319     if (URL == nil) {
320         URL = [NSURL URLWithString:@"about:blank"];
321     }
322     if (originalURL == nil) {
323         originalURL = [NSURL URLWithString:@"about:blank"];
324     }
325     
326     bfItem = [[[WebHistoryItem alloc] initWithURL:URL target:[self name] parent:[[self parentFrame] name] title:[dataSrc pageTitle]] autorelease];
327     [bfItem setOriginalURLString:[originalURL _web_originalDataAsString]];
328
329     // save form state if this is a POST
330     [bfItem _setFormInfoFromRequest:request];
331
332     // Set the item for which we will save document state
333     [_private setPreviousItem:[_private currentItem]];
334     [_private setCurrentItem:bfItem];
335
336     return bfItem;
337 }
338
339 /*
340     In the case of saving state about a page with frames, we store a tree of items that mirrors the frame tree.  
341     The item that was the target of the user's navigation is designated as the "targetItem".  
342     When this method is called with doClip=YES we're able to create the whole tree except for the target's children, 
343     which will be loaded in the future.  That part of the tree will be filled out as the child loads are committed.
344 */
345 - (WebHistoryItem *)_createItemTreeWithTargetFrame:(WebFrame *)targetFrame clippedAtTarget:(BOOL)doClip
346 {
347     WebHistoryItem *bfItem = [self _createItem:[self parentFrame] ? YES : NO];
348
349     [self _saveScrollPositionAndViewStateToItem:[_private previousItem]];
350     if (!(doClip && self == targetFrame)) {
351         // save frame state for items that aren't loading (khtml doesn't save those)
352         [_private->bridge saveDocumentState];
353
354         for (WebFrame *child = [self _firstChildFrame]; child; child = [child _nextSiblingFrame])
355             [bfItem addChildItem:[child _createItemTreeWithTargetFrame:targetFrame clippedAtTarget:doClip]];
356     }
357     if (self == targetFrame)
358         [bfItem setIsTargetItem:YES];
359
360     return bfItem;
361 }
362
363 - (WebFrame *)_immediateChildFrameNamed:(NSString *)name
364 {
365     return Frame([[self _bridge] childFrameNamed:name]);
366 }
367
368 - (void)_detachChildren
369 {
370     // FIXME: is it really necessary to do this in reverse order any more?
371     WebFrame *child = [self _lastChildFrame];
372     WebFrame *prev = [child _previousSiblingFrame];
373     for (; child; child = prev, prev = [child _previousSiblingFrame])
374         [child _detachFromParent];
375 }
376
377 - (void)_detachFromParent
378 {
379     WebFrameBridge *bridge = [_private->bridge retain];
380
381     [bridge closeURL];
382     [self stopLoading];
383
384     [self _saveScrollPositionAndViewStateToItem:[_private currentItem]];
385     [self _detachChildren];
386     [_private->inspectors makeObjectsPerformSelector:@selector(_webFrameDetached:) withObject:self];
387
388     [_private->webFrameView _setWebFrame:nil]; // needed for now to be compatible w/ old behavior
389
390     [_private->frameLoader clearDataSource];
391     [_private setWebFrameView:nil];
392
393     [self retain]; // retain self temporarily because dealloc can re-enter this method
394
395     [[[self parentFrame] _bridge] removeChild:bridge];
396
397     [bridge close];
398     [bridge release];
399
400     bridge = nil;
401     _private->bridge = nil;
402
403     [self release];
404 }
405
406 - (void)_makeDocumentView
407 {
408     NSView <WebDocumentView> *documentView = [_private->webFrameView _makeDocumentViewForDataSource:[_private->frameLoader dataSource]];
409     if (!documentView)
410         return;
411
412     // FIXME: We could save work and not do this for a top-level view that is not a WebHTMLView.
413     WebFrameView *v = _private->webFrameView;
414     [_private->bridge createFrameViewWithNSView:documentView marginWidth:[v _marginWidth] marginHeight:[v _marginHeight]];
415     [self _updateBackground];
416     [_private->bridge installInFrame:[v _scrollView]];
417
418     // Call setDataSource on the document view after it has been placed in the view hierarchy.
419     // This what we for the top-level view, so should do this for views in subframes as well.
420     [documentView setDataSource:[_private->frameLoader dataSource]];
421 }
422
423 - (void)_transitionToCommitted:(NSDictionary *)pageCache
424 {
425     ASSERT([self webView] != nil);
426     
427     switch ([_private->frameLoader state]) {
428         case WebFrameStateProvisional:
429         {
430             [[[[self frameView] _scrollView] contentView] setCopiesOnScroll:YES];
431
432             WebFrameLoadType loadType = [_private->frameLoader loadType];
433             if (loadType == WebFrameLoadTypeForward ||
434                 loadType == WebFrameLoadTypeBack ||
435                 loadType == WebFrameLoadTypeIndexedBackForward ||
436                 (loadType == WebFrameLoadTypeReload && [[_private->frameLoader provisionalDataSource] unreachableURL] != nil))
437             {
438                 // Once committed, we want to use current item for saving DocState, and
439                 // the provisional item for restoring state.
440                 // Note previousItem must be set before we close the URL, which will
441                 // happen when the data source is made non-provisional below
442                 [_private setPreviousItem:[_private currentItem]];
443                 ASSERT([_private provisionalItem]);
444                 [_private setCurrentItem:[_private provisionalItem]];
445                 [_private setProvisionalItem:nil];
446             }
447
448             // The call to closeURL invokes the unload event handler, which can execute arbitrary
449             // JavaScript. If the script initiates a new load, we need to abandon the current load,
450             // or the two will stomp each other.
451             WebDataSource *pd = [_private->frameLoader provisionalDataSource];
452             [[self _bridge] closeURL];
453             if (pd != [_private->frameLoader provisionalDataSource])
454                 return;
455
456             [_private->frameLoader commitProvisionalLoad];
457
458             // Handle adding the URL to the back/forward list.
459             WebDataSource *ds = [self dataSource];
460             NSString *ptitle = [ds pageTitle];
461
462             switch (loadType) {
463             case WebFrameLoadTypeForward:
464             case WebFrameLoadTypeBack:
465             case WebFrameLoadTypeIndexedBackForward:
466                 if ([[self webView] backForwardList]) {
467                     // Must grab the current scroll position before disturbing it
468                     [self _saveScrollPositionAndViewStateToItem:[_private previousItem]];
469                     
470                     // Create a document view for this document, or used the cached view.
471                     if (pageCache){
472                         NSView <WebDocumentView> *cachedView = [pageCache objectForKey: WebPageCacheDocumentViewKey];
473                         ASSERT(cachedView != nil);
474                         [[self frameView] _setDocumentView: cachedView];
475                     }
476                     else
477                         [self _makeDocumentView];
478                 }
479                 break;
480
481             case WebFrameLoadTypeReload:
482             case WebFrameLoadTypeSame:
483             case WebFrameLoadTypeReplace:
484             {
485                 WebHistoryItem *currItem = [_private currentItem];
486                 LOG(PageCache, "Clearing back/forward cache, %@\n", [currItem URL]);
487                 [currItem setHasPageCache:NO];
488                 if (loadType == WebFrameLoadTypeReload) {
489                     [self _saveScrollPositionAndViewStateToItem:currItem];
490                 }
491                 NSURLRequest *request = [ds request];
492                 if ([request _webDataRequestUnreachableURL] == nil) {
493                     // Sometimes loading a page again leads to a different result because of cookies.  Bugzilla 4072
494                     [currItem setURL:[request URL]];
495                 }
496                 // Update the last visited time.  Mostly interesting for URL autocompletion
497                 // statistics.
498                 NSURL *URL = [[[[ds _documentLoader] originalRequestCopy] URL] _webkit_canonicalize];
499                 WebHistory *sharedHistory = [WebHistory optionalSharedHistory];
500                 WebHistoryItem *oldItem = [sharedHistory itemForURL:URL];
501                 if (oldItem)
502                     [sharedHistory setLastVisitedTimeInterval:[NSDate timeIntervalSinceReferenceDate] forItem:oldItem];
503                 
504                 [self _makeDocumentView];
505                 break;
506             }
507
508             // FIXME - just get rid of this case, and merge WebFrameLoadTypeReloadAllowingStaleData with the above case
509             case WebFrameLoadTypeReloadAllowingStaleData:
510                 [self _makeDocumentView];
511                 break;
512                 
513             case WebFrameLoadTypeStandard:
514                 if (![[ds _documentLoader] isClientRedirect]) {
515                     // Add item to history and BF list
516                     NSURL *URL = [ds _URLForHistory];
517                     if (URL && ![URL _web_isEmpty]){
518                         ASSERT([self webView]);
519                         if (![[[self webView] preferences] privateBrowsingEnabled]) {
520                             WebHistoryItem *entry = [[WebHistory optionalSharedHistory] addItemForURL:URL];
521                             if (ptitle)
522                                 [entry setTitle: ptitle];                            
523                         }
524                         [self _addBackForwardItemClippedAtTarget:YES];
525                     }
526
527                 } else {
528                     NSURLRequest *request = [ds request];
529                     
530                     // update the URL in the BF list that we made before the redirect, unless
531                     // this is alternate content for an unreachable URL (we want the BF list
532                     // item to remember the unreachable URL in case it becomes reachable later)
533                     if ([request _webDataRequestUnreachableURL] == nil) {
534                         [[_private currentItem] setURL:[request URL]];
535
536                         // clear out the form data so we don't repost it to the wrong place if we
537                         // ever go back/forward to this item
538                         [[_private currentItem] _setFormInfoFromRequest:request];
539
540                         // We must also clear out form data so we don't try to restore it into the incoming page,
541                         // see -_opened
542                     }
543                 }
544                 [self _makeDocumentView];
545                 break;
546                 
547             case WebFrameLoadTypeInternal:
548                 // Add an item to the item tree for this frame
549                 ASSERT(![[ds _documentLoader] isClientRedirect]);
550                 WebFrame *parentFrame = [self parentFrame];
551                 if (parentFrame) {
552                     WebHistoryItem *parentItem = [parentFrame->_private currentItem];
553                     // The only case where parentItem==nil should be when a parent frame loaded an
554                     // empty URL, which doesn't set up a current item in that parent.
555                     if (parentItem)
556                         [parentItem addChildItem:[self _createItem: YES]];
557                 } else {
558                     // See 3556159.  It's not clear if it's valid to be in WebFrameLoadTypeOnLoadEvent
559                     // for a top-level frame, but that was a likely explanation for those crashes,
560                     // so let's guard against it.
561                     // ...and all WebFrameLoadTypeOnLoadEvent uses were folded to WebFrameLoadTypeInternal
562                     LOG_ERROR("no parent frame in _transitionToCommitted:, loadType=%d", loadType);
563                 }
564                 [self _makeDocumentView];
565                 break;
566
567             // FIXME Remove this check when dummy ds is removed.  An exception should be thrown
568             // if we're in the WebFrameLoadTypeUninitialized state.
569             default:
570                 ASSERT_NOT_REACHED();
571             }
572
573             
574             // Tell the client we've committed this URL.
575             ASSERT([[self frameView] documentView] != nil);
576             [[self webView] _didCommitLoadForFrame: self];
577             [[[self webView] _frameLoadDelegateForwarder] webView:[self webView] didCommitLoadForFrame:self];
578             
579             // If we have a title let the WebView know about it.
580             if (ptitle) {
581                 [[[self webView] _frameLoadDelegateForwarder] webView:[self webView]
582                                                            didReceiveTitle:ptitle
583                                                                   forFrame:self];
584             }
585             break;
586         }
587         
588         case WebFrameStateCommittedPage:
589         case WebFrameStateComplete:
590         default:
591         {
592             ASSERT_NOT_REACHED();
593         }
594     }
595 }
596
597 - (BOOL)_canCachePage
598 {
599     return [[[self webView] backForwardList] _usesPageCache];
600 }
601
602 - (void)_purgePageCache
603 {
604     // This method implements the rule for purging the page cache.
605     unsigned sizeLimit = [[[self webView] backForwardList] pageCacheSize];
606     unsigned pagesCached = 0;
607     WebBackForwardList *backForwardList = [[self webView] backForwardList];
608     NSArray *backList = [backForwardList backListWithLimit: 999999];
609     WebHistoryItem *oldestNonSnapbackItem = nil;
610     
611     unsigned i;
612     for (i = 0; i < [backList count]; i++){
613         WebHistoryItem *item = [backList objectAtIndex: i];
614         if ([item hasPageCache]){
615             if (oldestNonSnapbackItem == nil && ![item alwaysAttemptToUsePageCache])
616                 oldestNonSnapbackItem = item;
617             pagesCached++;
618         }
619     }
620
621     // Snapback items are never directly purged here.
622     if (pagesCached >= sizeLimit) {
623         LOG(PageCache, "Purging back/forward cache, %@\n", [oldestNonSnapbackItem URL]);
624         [oldestNonSnapbackItem setHasPageCache:NO];
625     }
626 }
627
628 + (CFAbsoluteTime)_timeOfLastCompletedLoad
629 {
630     return [WebFrameLoader timeOfLastCompletedLoad];
631 }
632
633 - (BOOL)_createPageCacheForItem:(WebHistoryItem *)item
634 {
635     NSMutableDictionary *pageCache;
636
637     [item setHasPageCache: YES];
638
639     if (![_private->bridge saveDocumentToPageCache]){
640         [item setHasPageCache: NO];
641         return NO;
642     }
643     else {
644         pageCache = [item pageCache];
645         [pageCache setObject:[NSDate date]  forKey: WebPageCacheEntryDateKey];
646         [pageCache setObject:[self dataSource] forKey: WebPageCacheDataSourceKey];
647         [pageCache setObject:[[self frameView] documentView] forKey: WebPageCacheDocumentViewKey];
648     }
649     return YES;
650 }
651
652 - (void)_provisionalLoadStarted
653 {
654     WebFrameLoadType loadType = [_private->frameLoader loadType];
655     
656     // FIXME: This is OK as long as no one resizes the window,
657     // but in the case where someone does, it means garbage outside
658     // the occupied part of the scroll view.
659     [[[self frameView] _scrollView] setDrawsBackground:NO];
660     
661     // Cache the page, if possible.
662     // Don't write to the cache if in the middle of a redirect, since we will want to
663     // store the final page we end up on.
664     // No point writing to the cache on a reload or loadSame, since we will just write
665     // over it again when we leave that page.
666     WebHistoryItem *item = [_private currentItem];
667     if ([self _canCachePage]
668         && [_private->bridge canCachePage]
669         && item
670         && ![_private->frameLoader isQuickRedirectComing]
671         && loadType != WebFrameLoadTypeReload 
672         && loadType != WebFrameLoadTypeReloadAllowingStaleData
673         && loadType != WebFrameLoadTypeSame
674         && ![[self dataSource] isLoading]
675         && ![[_private->frameLoader documentLoader] isStopping]) {
676         if ([[[self dataSource] representation] isKindOfClass:[WebHTMLRepresentation class]]) {
677             if (![item pageCache]){
678                 // Add the items to this page's cache.
679                 if ([self _createPageCacheForItem:item]) {
680                     LOG(PageCache, "Saving page to back/forward cache, %@\n", [[self dataSource] _URL]);
681                     
682                     // See if any page caches need to be purged after the addition of this
683                     // new page cache.
684                     [self _purgePageCache];
685                 }
686                 else
687                     LOG(PageCache, "NOT saving page to back/forward cache, unable to create items, %@\n", [[self dataSource] _URL]);
688             }
689         } else
690             // Put the document into a null state, so it can be restored correctly.
691             [_private->bridge clear];
692     } else
693         LOG(PageCache, "NOT saving page to back/forward cache, %@\n", [[self dataSource] _URL]);
694 }
695
696 // Called after we send an openURL:... down to WebCore.
697 - (void)_opened
698 {
699     if ([_private->frameLoader loadType] == WebFrameLoadTypeStandard && [[[self dataSource] _documentLoader] isClientRedirect]) {
700         // Clear out form data so we don't try to restore it into the incoming page.  Must happen after
701         // khtml has closed the URL and saved away the form state.
702         WebHistoryItem *item = [_private currentItem];
703         [item setDocumentState:nil];
704         [item setScrollPoint:NSZeroPoint];
705     }
706
707     if ([[self dataSource] _loadingFromPageCache]){
708         // Force a layout to update view size and thereby update scrollbars.
709         NSView <WebDocumentView> *view = [[self frameView] documentView];
710         if ([view isKindOfClass:[WebHTMLView class]]) {
711             [(WebHTMLView *)view setNeedsToApplyStyles:YES];
712         }
713         [view setNeedsLayout: YES];
714         [view layout];
715
716         NSArray *responses = [[_private->frameLoader documentLoader] responses];
717         NSURLResponse *response;
718         int i, count = [responses count];
719         for (i = 0; i < count; i++){
720             response = [responses objectAtIndex: i];
721             // FIXME: If the WebKit client changes or cancels the request, this is not respected.
722             NSError *error;
723             id identifier;
724             NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[response URL]];
725             [self _requestFromDelegateForRequest:request identifier:&identifier error:&error];
726             [self _sendRemainingDelegateMessagesWithIdentifier:identifier response:response length:(unsigned)[response expectedContentLength] error:error];
727             [request release];
728         }
729         
730         // Release the resources kept in the page cache.  They will be
731         // reset when we leave this page.  The core side of the page cache
732         // will have already been invalidated by the bridge to prevent
733         // premature release.
734         [[_private currentItem] setHasPageCache:NO];
735
736         [[_private->frameLoader documentLoader] setPrimaryLoadComplete:YES];
737         // why only this frame and not parent frames?
738         [self _checkLoadCompleteForThisFrame];
739     }
740 }
741
742 - (void)_checkLoadCompleteForThisFrame
743 {
744     ASSERT([self webView] != nil);
745
746     switch ([_private->frameLoader state]) {
747         case WebFrameStateProvisional:
748         {
749             if ([_private->frameLoader delegateIsHandlingProvisionalLoadError])
750                 return;
751
752             WebDataSource *pd = [self provisionalDataSource];
753             
754             LOG(Loading, "%@:  checking complete in WebFrameStateProvisional", [self name]);
755             // If we've received any errors we may be stuck in the provisional state and actually
756             // complete.
757             NSError *error = [pd _mainDocumentError];
758             if (error != nil) {
759                 // Check all children first.
760                 LOG(Loading, "%@:  checking complete, current state WebFrameStateProvisional", [self name]);
761                 WebHistoryItem *resetItem = [self _currentBackForwardListItemToResetTo];
762                 BOOL shouldReset = YES;
763                 if (![pd isLoading]) {
764                     LOG(Loading, "%@:  checking complete in WebFrameStateProvisional, load done", [self name]);
765                     [[self webView] _didFailProvisionalLoadWithError:error forFrame:self];
766                     [_private->frameLoader setDelegateIsHandlingProvisionalLoadError:YES];
767                     [[[self webView] _frameLoadDelegateForwarder] webView:[self webView]
768                                           didFailProvisionalLoadWithError:error
769                                                                  forFrame:self];
770                     [_private->frameLoader setDelegateIsHandlingProvisionalLoadError:NO];
771                     [_private->internalLoadDelegate webFrame:self didFinishLoadWithError:error];
772
773                     // FIXME: can stopping loading here possibly have
774                     // any effect, if isLoading is false, which it
775                     // must be, to be in this branch of the if? And is it ok to just do 
776                     // a full-on stopLoading?
777                     [_private->frameLoader stopLoadingSubframes];
778                     [[pd _documentLoader] stopLoading];
779
780                     // Finish resetting the load state, but only if another load hasn't been started by the
781                     // delegate callback.
782                     if (pd == [_private->frameLoader provisionalDataSource])
783                         [_private->frameLoader clearProvisionalLoad];
784                     else {
785                         NSURL *unreachableURL = [[_private->frameLoader provisionalDataSource] unreachableURL];
786                         if (unreachableURL != nil && [unreachableURL isEqual:[[pd request] URL]]) {
787                             shouldReset = NO;
788                         }
789                     }
790                 }
791                 if (shouldReset && resetItem != nil) {
792                     [[[self webView] backForwardList] goToItem:resetItem];
793                 }
794             }
795             return;
796         }
797         
798         case WebFrameStateCommittedPage:
799         {
800             WebDataSource *ds = [self dataSource];
801             
802             //LOG(Loading, "%@:  checking complete, current state WEBFRAMESTATE_COMMITTED", [self name]);
803             if (![ds isLoading]) {
804                 WebFrameView *thisView = [self frameView];
805                 NSView <WebDocumentView> *thisDocumentView = [thisView documentView];
806                 ASSERT(thisDocumentView != nil);
807
808                 [_private->frameLoader markLoadComplete];
809
810                 // FIXME: Is this subsequent work important if we already navigated away?
811                 // Maybe there are bugs because of that, or extra work we can skip because
812                 // the new page is ready.
813
814                 // Tell the just loaded document to layout.  This may be necessary
815                 // for non-html content that needs a layout message.
816                 if (!([[self dataSource] _isDocumentHTML])) {
817                     [thisDocumentView setNeedsLayout:YES];
818                     [thisDocumentView layout];
819                     [thisDocumentView setNeedsDisplay:YES];
820                 }
821                  
822                 // If the user had a scroll point scroll to it.  This will override
823                 // the anchor point.  After much discussion it was decided by folks
824                 // that the user scroll point should override the anchor point.
825                 if ([[self webView] backForwardList]) {
826                     switch ([_private->frameLoader loadType]) {
827                     case WebFrameLoadTypeForward:
828                     case WebFrameLoadTypeBack:
829                     case WebFrameLoadTypeIndexedBackForward:
830                     case WebFrameLoadTypeReload:
831                         [self _restoreScrollPositionAndViewState];
832                         break;
833
834                     case WebFrameLoadTypeStandard:
835                     case WebFrameLoadTypeInternal:
836                     case WebFrameLoadTypeReloadAllowingStaleData:
837                     case WebFrameLoadTypeSame:
838                     case WebFrameLoadTypeReplace:
839                         // Do nothing.
840                         break;
841
842                     default:
843                         ASSERT_NOT_REACHED();
844                         break;
845                     }
846                 }
847
848                 NSError *error = [ds _mainDocumentError];
849                 if (error != nil) {
850                     [[self webView] _didFailLoadWithError:error forFrame:self];
851                     [[[self webView] _frameLoadDelegateForwarder] webView:[self webView]
852                                                      didFailLoadWithError:error
853                                                                  forFrame:self];
854                     [_private->internalLoadDelegate webFrame:self didFinishLoadWithError:error];
855                 } else {
856                     [[self webView] _didFinishLoadForFrame:self];
857                     [[[self webView] _frameLoadDelegateForwarder] webView:[self webView]
858                                                     didFinishLoadForFrame:self];
859                     [_private->internalLoadDelegate webFrame:self didFinishLoadWithError:nil];
860                 }
861                 
862                 [[self webView] _progressCompleted: self];
863  
864                 return;
865             }
866             return;
867         }
868         
869         case WebFrameStateComplete:
870         {
871             LOG(Loading, "%@:  checking complete, current state WebFrameStateComplete", [self name]);
872             // Even if already complete, we might have set a previous item on a frame that
873             // didn't do any data loading on the past transaction.  Make sure to clear these out.
874             [_private setPreviousItem:nil];
875             return;
876         }
877     }
878     
879     // Yikes!  Serious horkage.
880     ASSERT_NOT_REACHED();
881 }
882
883 - (void)_handledOnloadEvents
884 {
885     [[[self webView] _frameLoadDelegateForwarder] webView:[self webView] didHandleOnloadEventsForFrame:self];
886 }
887
888 // Called every time a resource is completely loaded, or an error is received.
889 - (void)_checkLoadComplete
890 {
891     ASSERT([self webView] != nil);
892
893     WebFrame *parent;
894     for (WebFrame *frame = self; frame; frame = parent) {
895         [frame retain];
896         [frame _checkLoadCompleteForThisFrame];
897         parent = [frame parentFrame];
898         [frame release];
899     }
900 }
901
902 - (WebFrameBridge *)_bridge
903 {
904     return _private->bridge;
905 }
906
907 // helper method that determines whether the subframes described by the item's subitems
908 // match our own current frameset
909 - (BOOL)_childFramesMatchItem:(WebHistoryItem *)item
910 {
911     NSArray *childItems = [item children];
912     int numChildItems = [childItems count];
913     int numChildFrames = [self _childFrameCount];
914     if (numChildFrames != numChildItems)
915         return NO;
916
917     int i;
918     for (i = 0; i < numChildItems; i++) {
919         NSString *itemTargetName = [[childItems objectAtIndex:i] target];
920         //Search recursive here?
921         if (![self _immediateChildFrameNamed:itemTargetName])
922             return NO; // couldn't match the i'th itemTarget
923     }
924
925     return YES; // found matches for all itemTargets
926 }
927
928 // Walk the frame tree and ensure that the URLs match the URLs in the item.
929 - (BOOL)_URLsMatchItem:(WebHistoryItem *)item
930 {
931     NSURL *currentURL = [[[self dataSource] request] URL];
932
933     if (![[[item URL] _webkit_URLByRemovingFragment] isEqual:[currentURL _webkit_URLByRemovingFragment]])
934         return NO;
935     
936     NSArray *childItems = [item children];
937     WebHistoryItem *childItem;
938     WebFrame *childFrame;
939     int i, count = [childItems count];
940     for (i = 0; i < count; i++){
941         childItem = [childItems objectAtIndex:i];
942         childFrame = [self _immediateChildFrameNamed:[childItem target]];
943         if (![childFrame _URLsMatchItem: childItem])
944             return NO;
945     }
946     
947     return YES;
948 }
949
950 // loads content into this frame, as specified by item
951 - (void)_loadItem:(WebHistoryItem *)item withLoadType:(WebFrameLoadType)loadType
952 {
953     NSURL *itemURL = [item URL];
954     NSURL *itemOriginalURL = [NSURL _web_URLWithDataAsString:[item originalURLString]];
955     NSURL *currentURL = [[[self dataSource] request] URL];
956     NSArray *formData = [item formData];
957
958     // Are we navigating to an anchor within the page?
959     // Note if we have child frames we do a real reload, since the child frames might not
960     // match our current frame structure, or they might not have the right content.  We could
961     // check for all that as an additional optimization.
962     // We also do not do anchor-style navigation if we're posting a form.
963     
964     // FIXME: These checks don't match the ones in _loadURL:referrer:loadType:target:triggeringEvent:isFormSubmission:
965     // Perhaps they should.
966     if (!formData && ![_private->frameLoader shouldReloadForCurrent:itemURL andDestination:currentURL] && [self _URLsMatchItem:item] )
967     {
968 #if 0
969         // FIXME:  We need to normalize the code paths for anchor navigation.  Something
970         // like the following line of code should be done, but also accounting for correct
971         // updates to the back/forward list and scroll position.
972         // rjw 4/9/03 See 3223929.
973         [self _loadURL:itemURL referrer:[[[self dataSource] request] HTTPReferrer] loadType:loadType target:nil triggeringEvent:nil form:nil formValues:nil];
974 #endif
975         // must do this maintenance here, since we don't go through a real page reload
976         [self _saveScrollPositionAndViewStateToItem:[_private currentItem]];
977         // FIXME: form state might want to be saved here too
978
979         // We always call scrollToAnchorWithURL here, even if the URL doesn't have an
980         // anchor fragment. This is so we'll keep the WebCore Frame's URL up-to-date.
981         [_private->bridge scrollToAnchorWithURL:[item URL]];
982     
983         // must do this maintenance here, since we don't go through a real page reload
984         [_private setCurrentItem:item];
985         [self _restoreScrollPositionAndViewState];
986
987         // Fake the URL change by updating the data source's request.  This will no longer
988         // be necessary if we do the better fix described above.
989         [[_private->frameLoader documentLoader] replaceRequestURLForAnchorScrollWithURL:itemURL];
990         
991         [[[self webView] _frameLoadDelegateForwarder] webView:[self webView]
992                                didChangeLocationWithinPageForFrame:self];
993         [_private->internalLoadDelegate webFrame:self didFinishLoadWithError:nil];
994     } else {
995         // Remember this item so we can traverse any child items as child frames load
996         [_private setProvisionalItem:item];
997
998         WebDataSource *newDataSource;
999         BOOL inPageCache = NO;
1000         
1001         // Check if we'll be using the page cache.  We only use the page cache
1002         // if one exists and it is less than _backForwardCacheExpirationInterval
1003         // seconds old.  If the cache is expired it gets flushed here.
1004         if ([item hasPageCache]){
1005             NSDictionary *pageCache = [item pageCache];
1006             NSDate *cacheDate = [pageCache objectForKey: WebPageCacheEntryDateKey];
1007             NSTimeInterval delta = [[NSDate date] timeIntervalSinceDate: cacheDate];
1008
1009             if (delta <= [[[self webView] preferences] _backForwardCacheExpirationInterval]){
1010                 newDataSource = [pageCache objectForKey: WebPageCacheDataSourceKey];
1011                 [_private->frameLoader loadDataSource:newDataSource withLoadType:loadType formState:nil];   
1012                 inPageCache = YES;
1013             }
1014             else {
1015                 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]);
1016                 [item setHasPageCache: NO];
1017             }
1018         }
1019         
1020         if (!inPageCache) {
1021             NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:itemURL];
1022             [self _addExtraFieldsToRequest:request mainResource:YES alwaysFromRequest:(formData != nil) ? YES : NO];
1023
1024             // If this was a repost that failed the page cache, we might try to repost the form.
1025             NSDictionary *action;
1026             if (formData) {
1027                 [request setHTTPMethod:@"POST"];
1028                 [request _web_setHTTPReferrer:[item formReferrer]];
1029                 webSetHTTPBody(request, formData);
1030                 [request _web_setHTTPContentType:[item formContentType]];
1031
1032                 // Slight hack to test if the WF cache contains the page we're going to.  We want
1033                 // to know this before talking to the policy delegate, since it affects whether we
1034                 // show the DoYouReallyWantToRepost nag.
1035                 //
1036                 // This trick has a small bug (3123893) where we might find a cache hit, but then
1037                 // have the item vanish when we try to use it in the ensuing nav.  This should be
1038                 // extremely rare, but in that case the user will get an error on the navigation.
1039                 [request setCachePolicy:NSURLRequestReturnCacheDataDontLoad];
1040                 NSURLResponse *synchResponse = nil;
1041                 [NSURLConnection sendSynchronousRequest:request returningResponse:&synchResponse error:nil];
1042                 if (synchResponse == nil) { 
1043                     // Not in WF cache
1044                     [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
1045                     action = [self _actionInformationForNavigationType:WebNavigationTypeFormResubmitted event:nil originalURL:itemURL];
1046                 } else {
1047                     // We can use the cache, don't use navType=resubmit
1048                     action = [self _actionInformationForLoadType:loadType isFormSubmission:NO event:nil originalURL:itemURL];
1049                 }
1050             } else {
1051                 switch (loadType) {
1052                     case WebFrameLoadTypeReload:
1053                         [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
1054                         break;
1055                     case WebFrameLoadTypeBack:
1056                     case WebFrameLoadTypeForward:
1057                     case WebFrameLoadTypeIndexedBackForward:
1058                         if (![[itemURL scheme] isEqual:@"https"]) {
1059                             [request setCachePolicy:NSURLRequestReturnCacheDataElseLoad];
1060                         }
1061                         break;
1062                     case WebFrameLoadTypeStandard:
1063                     case WebFrameLoadTypeInternal:
1064                         // no-op: leave as protocol default
1065                         // FIXME:  I wonder if we ever hit this case
1066                         break;
1067                     case WebFrameLoadTypeSame:
1068                     case WebFrameLoadTypeReloadAllowingStaleData:
1069                     default:
1070                         ASSERT_NOT_REACHED();
1071                 }
1072
1073                 action = [self _actionInformationForLoadType:loadType isFormSubmission:NO event:nil originalURL:itemOriginalURL];
1074             }
1075
1076             [_private->frameLoader _loadRequest:request triggeringAction:action loadType:loadType formState:nil];
1077             [request release];
1078         }
1079     }
1080 }
1081
1082 // The general idea here is to traverse the frame tree and the item tree in parallel,
1083 // tracking whether each frame already has the content the item requests.  If there is
1084 // a match (by URL), we just restore scroll position and recurse.  Otherwise we must
1085 // reload that frame, and all its kids.
1086 - (void)_recursiveGoToItem:(WebHistoryItem *)item fromItem:(WebHistoryItem *)fromItem withLoadType:(WebFrameLoadType)type
1087 {
1088     NSURL *itemURL = [item URL];
1089     NSURL *currentURL = [[[self dataSource] request] URL];
1090
1091     // Always reload the target frame of the item we're going to.  This ensures that we will
1092     // do -some- load for the transition, which means a proper notification will be posted
1093     // to the app.
1094     // The exact URL has to match, including fragment.  We want to go through the _load
1095     // method, even if to do a within-page navigation.
1096     // The current frame tree and the frame tree snapshot in the item have to match.
1097     if (![item isTargetItem] &&
1098         [itemURL isEqual:currentURL] &&
1099         (([self name] == nil && [item target] == nil) ||[[self name] isEqualToString:[item target]]) &&
1100         [self _childFramesMatchItem:item])
1101     {
1102         // This content is good, so leave it alone and look for children that need reloading
1103
1104         // Save form state (works from currentItem, since prevItem is nil)
1105         ASSERT(![_private previousItem]);
1106         [_private->bridge saveDocumentState];
1107         [self _saveScrollPositionAndViewStateToItem:[_private currentItem]];
1108         
1109         [_private setCurrentItem:item];
1110
1111         // Restore form state (works from currentItem)
1112         [_private->bridge restoreDocumentState];
1113         // Restore the scroll position (taken in favor of going back to the anchor)
1114         [self _restoreScrollPositionAndViewState];
1115         
1116         NSArray *childItems = [item children];
1117         int numChildItems = childItems ? [childItems count] : 0;
1118         int i;
1119         for (i = numChildItems - 1; i >= 0; i--) {
1120             WebHistoryItem *childItem = [childItems objectAtIndex:i];
1121             NSString *childName = [childItem target];
1122             WebHistoryItem *fromChildItem = [fromItem childItemWithName:childName];
1123             ASSERT(fromChildItem || [fromItem isTargetItem]);
1124             WebFrame *childFrame = [self _immediateChildFrameNamed:childName];
1125             ASSERT(childFrame);
1126             [childFrame _recursiveGoToItem:childItem fromItem:fromChildItem withLoadType:type];
1127         }
1128     } else {
1129         // We need to reload the content
1130         [self _loadItem:item withLoadType:type];
1131     }
1132 }
1133
1134 // Main funnel for navigating to a previous location (back/forward, non-search snap-back)
1135 // This includes recursion to handle loading into framesets properly
1136 - (void)_goToItem:(WebHistoryItem *)item withLoadType:(WebFrameLoadType)type
1137 {
1138     ASSERT(![self parentFrame]);
1139     // shouldGoToHistoryItem is a private delegate method. This is needed to fix:
1140     // <rdar://problem/3951283> can view pages from the back/forward cache that should be disallowed by Parental Controls
1141     // Ultimately, history item navigations should go through the policy delegate. That's covered in:
1142     // <rdar://problem/3979539> back/forward cache navigations should consult policy delegate
1143     if ([[[self webView] _policyDelegateForwarder] webView:[self webView] shouldGoToHistoryItem:item]) {    
1144         WebBackForwardList *backForwardList = [[self webView] backForwardList];
1145         WebHistoryItem *currItem = [backForwardList currentItem];
1146         // Set the BF cursor before commit, which lets the user quickly click back/forward again.
1147         // - plus, it only makes sense for the top level of the operation through the frametree,
1148         // as opposed to happening for some/one of the page commits that might happen soon
1149         [backForwardList goToItem:item];
1150         [self _recursiveGoToItem:item fromItem:currItem withLoadType:type];
1151     }
1152 }
1153
1154 -(NSDictionary *)_actionInformationForNavigationType:(WebNavigationType)navigationType event:(NSEvent *)event originalURL:(NSURL *)URL
1155 {
1156     switch ([event type]) {
1157         case NSLeftMouseDown:
1158         case NSRightMouseDown:
1159         case NSOtherMouseDown:
1160         case NSLeftMouseUp:
1161         case NSRightMouseUp:
1162         case NSOtherMouseUp:
1163         {
1164             NSView *topViewInEventWindow = [[event window] contentView];
1165             NSView *viewContainingPoint = [topViewInEventWindow hitTest:[topViewInEventWindow convertPoint:[event locationInWindow] fromView:nil]];
1166             while (viewContainingPoint != nil) {
1167                 if ([viewContainingPoint isKindOfClass:[WebHTMLView class]]) {
1168                     break;
1169                 }
1170                 viewContainingPoint = [viewContainingPoint superview];
1171             }
1172             if (viewContainingPoint != nil) {
1173                 NSPoint point = [viewContainingPoint convertPoint:[event locationInWindow] fromView:nil];
1174                 NSDictionary *elementInfo = [(WebHTMLView *)viewContainingPoint elementAtPoint:point];
1175         
1176                 return [NSDictionary dictionaryWithObjectsAndKeys:
1177                     [NSNumber numberWithInt:navigationType], WebActionNavigationTypeKey,
1178                     elementInfo, WebActionElementKey,
1179                     [NSNumber numberWithInt:[event buttonNumber]], WebActionButtonKey,
1180                     [NSNumber numberWithInt:[event modifierFlags]], WebActionModifierFlagsKey,
1181                     URL, WebActionOriginalURLKey,
1182                     nil];
1183             }
1184         }
1185             
1186         // fall through
1187         
1188         default:
1189             return [NSDictionary dictionaryWithObjectsAndKeys:
1190                 [NSNumber numberWithInt:navigationType], WebActionNavigationTypeKey,
1191                 [NSNumber numberWithInt:[event modifierFlags]], WebActionModifierFlagsKey,
1192                 URL, WebActionOriginalURLKey,
1193                 nil];
1194     }
1195 }
1196
1197 -(NSDictionary *)_actionInformationForLoadType:(WebFrameLoadType)loadType isFormSubmission:(BOOL)isFormSubmission event:(NSEvent *)event originalURL:(NSURL *)URL
1198 {
1199     WebNavigationType navType;
1200     if (isFormSubmission) {
1201         navType = WebNavigationTypeFormSubmitted;
1202     } else if (event == nil) {
1203         if (loadType == WebFrameLoadTypeReload) {
1204             navType = WebNavigationTypeReload;
1205         } else if (loadType == WebFrameLoadTypeForward
1206                    || loadType == WebFrameLoadTypeBack
1207                    || loadType == WebFrameLoadTypeIndexedBackForward) {
1208             navType = WebNavigationTypeBackForward;
1209         } else {
1210             navType = WebNavigationTypeOther;
1211         }
1212     } else {
1213         navType = WebNavigationTypeLinkClicked;
1214     }
1215     return [self _actionInformationForNavigationType:navType event:event originalURL:URL];
1216 }
1217
1218 - (void)_continueLoadRequestAfterNewWindowPolicy:(NSURLRequest *)request frameName:(NSString *)frameName formState:(WebFormState *)formState
1219 {
1220     if (!request)
1221         return;
1222
1223     [self retain];
1224
1225     WebView *webView = nil;
1226     WebView *currentWebView = [self webView];
1227     id wd = [currentWebView UIDelegate];
1228     if ([wd respondsToSelector:@selector(webView:createWebViewWithRequest:)])
1229         webView = [wd webView:currentWebView createWebViewWithRequest:nil];
1230     else
1231         webView = [[WebDefaultUIDelegate sharedUIDelegate] webView:currentWebView createWebViewWithRequest:nil];
1232     if (!webView)
1233         goto exit;
1234
1235     WebFrame *frame = [webView mainFrame];
1236     if (!frame)
1237         goto exit;
1238
1239     [frame retain];
1240
1241     [[frame _bridge] setName:frameName];
1242
1243     [[webView _UIDelegateForwarder] webViewShow:webView];
1244
1245     [[frame _bridge] setOpener:[self _bridge]];
1246     [frame->_private->frameLoader _loadRequest:request triggeringAction:nil loadType:WebFrameLoadTypeStandard formState:formState];
1247
1248     [frame release];
1249
1250 exit:
1251     [self release];
1252 }
1253
1254 - (void)_loadURL:(NSURL *)URL referrer:(NSString *)referrer intoChild:(WebFrame *)childFrame
1255 {
1256     WebHistoryItem *parentItem = [_private currentItem];
1257     NSArray *childItems = [parentItem children];
1258     WebFrameLoadType loadType = [_private->frameLoader loadType];
1259     WebFrameLoadType childLoadType = WebFrameLoadTypeInternal;
1260     WebHistoryItem *childItem = nil;
1261
1262     // If we're moving in the backforward list, we might want to replace the content
1263     // of this child frame with whatever was there at that point.
1264     // Reload will maintain the frame contents, LoadSame will not.
1265     if (childItems &&
1266         (loadType == WebFrameLoadTypeForward
1267          || loadType == WebFrameLoadTypeBack
1268          || loadType == WebFrameLoadTypeIndexedBackForward
1269          || loadType == WebFrameLoadTypeReload
1270          || loadType == WebFrameLoadTypeReloadAllowingStaleData))
1271     {
1272         childItem = [parentItem childItemWithName:[childFrame name]];
1273         if (childItem) {
1274             // Use the original URL to ensure we get all the side-effects, such as
1275             // onLoad handlers, of any redirects that happened. An example of where
1276             // this is needed is Radar 3213556.
1277             URL = [NSURL _web_URLWithDataAsString:[childItem originalURLString]];
1278             // These behaviors implied by these loadTypes should apply to the child frames
1279             childLoadType = loadType;
1280
1281             if (loadType == WebFrameLoadTypeForward
1282                 || loadType == WebFrameLoadTypeBack
1283                 || loadType == WebFrameLoadTypeIndexedBackForward)
1284             {
1285                 // For back/forward, remember this item so we can traverse any child items as child frames load
1286                 [childFrame->_private setProvisionalItem:childItem];
1287             } else {
1288                 // For reload, just reinstall the current item, since a new child frame was created but we won't be creating a new BF item
1289                 [childFrame->_private setCurrentItem:childItem];
1290             }
1291         }
1292     }
1293
1294     WebArchive *archive = [[self dataSource] _popSubframeArchiveWithName:[childFrame name]];
1295     if (archive) {
1296         [childFrame loadArchive:archive];
1297     } else {
1298         [[childFrame _frameLoader] loadURL:URL referrer:referrer loadType:childLoadType target:nil triggeringEvent:nil form:nil formValues:nil];
1299     }
1300 }
1301
1302 - (void)_postWithURL:(NSURL *)URL referrer:(NSString *)referrer target:(NSString *)target data:(NSArray *)postData contentType:(NSString *)contentType triggeringEvent:(NSEvent *)event form:(DOMElement *)form formValues:(NSDictionary *)values
1303 {
1304     // When posting, use the NSURLRequestReloadIgnoringCacheData load flag.
1305     // This prevents a potential bug which may cause a page with a form that uses itself
1306     // as an action to be returned from the cache without submitting.
1307
1308     // FIXME: Where's the code that implements what the comment above says?
1309
1310     NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];
1311     [self _addExtraFieldsToRequest:request mainResource:YES alwaysFromRequest:YES];
1312     [request _web_setHTTPReferrer:referrer];
1313     [request setHTTPMethod:@"POST"];
1314     webSetHTTPBody(request, postData);
1315     [request _web_setHTTPContentType:contentType];
1316
1317     NSDictionary *action = [self _actionInformationForLoadType:WebFrameLoadTypeStandard isFormSubmission:YES event:event originalURL:URL];
1318     WebFormState *formState = nil;
1319     if (form && values) {
1320         formState = [[WebFormState alloc] initWithForm:form values:values sourceFrame:self];
1321     }
1322
1323     if (target != nil) {
1324         WebFrame *targetFrame = [self findFrameNamed:target];
1325
1326         if (targetFrame != nil) {
1327             [[targetFrame _frameLoader] _loadRequest:request triggeringAction:action loadType:WebFrameLoadTypeStandard formState:formState];
1328         } else {
1329             [_private->frameLoader checkNewWindowPolicyForRequest:request action:action frameName:target formState:formState andCall:self
1330                 withSelector:@selector(_continueLoadRequestAfterNewWindowPolicy:frameName:formState:)];
1331         }
1332         [request release];
1333         [formState release];
1334         return;
1335     }
1336
1337     [_private->frameLoader _loadRequest:request triggeringAction:action loadType:WebFrameLoadTypeStandard formState:formState];
1338
1339     [request release];
1340     [formState release];
1341 }
1342
1343 - (void)_setTitle:(NSString *)title
1344 {
1345     [[_private currentItem] setTitle:title];
1346 }
1347
1348 - (void)_saveScrollPositionAndViewStateToItem:(WebHistoryItem *)item
1349 {
1350     if (item) {
1351         NSView <WebDocumentView> *docView = [[self frameView] documentView];
1352         NSView *parent = [docView superview];
1353         // we might already be detached when this is called from detachFromParent, in which
1354         // case we don't want to override real data earlier gathered with (0,0)
1355         if (parent) {
1356             NSPoint point;
1357             if ([docView conformsToProtocol:@protocol(_WebDocumentViewState)]) {
1358                 // The view has it's own idea of where it is scrolled to, perhaps because it contains its own
1359                 // ScrollView instead of using the one provided by the WebFrame
1360                 point = [(id <_WebDocumentViewState>)docView scrollPoint];
1361                 [item setViewState:[(id <_WebDocumentViewState>)docView viewState]];
1362             } else {
1363                 // Parent is the clipview of the DynamicScrollView the WebFrame installs
1364                 ASSERT([parent isKindOfClass:[NSClipView class]]);
1365                 point = [parent bounds].origin;
1366             }
1367             [item setScrollPoint:point];
1368         }
1369     }
1370 }
1371
1372 - (void)_defersCallbacksChanged
1373 {
1374     for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self])
1375         [frame->_private->frameLoader defersCallbacksChanged];
1376 }
1377
1378 - (void)_viewWillMoveToHostWindow:(NSWindow *)hostWindow
1379 {
1380     for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self])
1381         [[[frame frameView] documentView] viewWillMoveToHostWindow:hostWindow];
1382 }
1383
1384 - (void)_viewDidMoveToHostWindow
1385 {
1386     for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self])
1387         [[[frame frameView] documentView] viewDidMoveToHostWindow];
1388 }
1389
1390 - (void)_addChild:(WebFrame *)child
1391 {
1392     [[self _bridge] appendChild:[child _bridge]];
1393     [[[child dataSource] _documentLoader] setOverrideEncoding:[[[self dataSource] _documentLoader] overrideEncoding]];  
1394 }
1395
1396 // If we bailed out of a b/f navigation, we might need to set the b/f cursor back to the current
1397 // item, because we optimistically move it right away at the start of the operation. But when
1398 // alternate content is loaded for an unreachableURL, we don't want to reset the b/f cursor.
1399 // Return the item that we would reset to, so we can decide later whether to actually reset.
1400 - (WebHistoryItem *)_currentBackForwardListItemToResetTo
1401 {
1402     WebFrameLoadType loadType = [_private->frameLoader loadType];
1403     if ((loadType == WebFrameLoadTypeForward
1404          || loadType == WebFrameLoadTypeBack
1405          || loadType == WebFrameLoadTypeIndexedBackForward)
1406         && self == [[self webView] mainFrame]) {
1407         return [_private currentItem];
1408     }
1409     return nil;
1410 }
1411
1412 - (WebHistoryItem *)_itemForSavingDocState
1413 {
1414     // For a standard page load, we will have a previous item set, which will be used to
1415     // store the form state.  However, in some cases we will have no previous item, and
1416     // the current item is the right place to save the state.  One example is when we
1417     // detach a bunch of frames because we are navigating from a site with frames to
1418     // another site.  Another is when saving the frame state of a frame that is not the
1419     // target of the current navigation (if we even decide to save with that granularity).
1420
1421     // Because of previousItem's "masking" of currentItem for this purpose, it's important
1422     // that previousItem be cleared at the end of a page transition.  We leverage the
1423     // checkLoadComplete recursion to achieve this goal.
1424
1425     WebHistoryItem *result = [_private previousItem] ? [_private previousItem] : [_private currentItem];
1426     return result;
1427 }
1428
1429 - (WebHistoryItem *)_itemForRestoringDocState
1430 {
1431     switch ([_private->frameLoader loadType]) {
1432         case WebFrameLoadTypeReload:
1433         case WebFrameLoadTypeReloadAllowingStaleData:
1434         case WebFrameLoadTypeSame:
1435         case WebFrameLoadTypeReplace:
1436             // Don't restore any form state on reload or loadSame
1437             return nil;
1438         case WebFrameLoadTypeBack:
1439         case WebFrameLoadTypeForward:
1440         case WebFrameLoadTypeIndexedBackForward:
1441         case WebFrameLoadTypeInternal:
1442         case WebFrameLoadTypeStandard:
1443             return [_private currentItem];
1444     }
1445     ASSERT_NOT_REACHED();
1446     return nil;
1447 }
1448
1449 // Walk the frame tree, telling all frames to save their form state into their current
1450 // history item.
1451 - (void)_saveDocumentAndScrollState
1452 {
1453     for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self]) {
1454         [[frame _bridge] saveDocumentState];
1455         [frame _saveScrollPositionAndViewStateToItem:[frame->_private currentItem]];
1456     }
1457 }
1458
1459 // used to decide to use loadType=Same
1460 - (BOOL)_shouldTreatURLAsSameAsCurrent:(NSURL *)URL
1461 {
1462     WebHistoryItem *item = [_private currentItem];
1463     NSString* URLString = [URL _web_originalDataAsString];
1464     return [URLString isEqual:[item URLString]] || [URLString isEqual:[item originalURLString]];
1465 }    
1466
1467 - (void)_loadRequest:(NSURLRequest *)request inFrameNamed:(NSString *)frameName
1468 {
1469     if (frameName == nil) {
1470         [self loadRequest:request];
1471         return;
1472     }
1473
1474     WebFrame *frame = [self findFrameNamed:frameName];
1475     
1476     if (frame != nil) {
1477         [frame loadRequest:request];
1478         return;
1479     }
1480
1481     NSDictionary *action = [self _actionInformationForNavigationType:WebNavigationTypeOther event:nil originalURL:[request URL]];
1482     [_private->frameLoader checkNewWindowPolicyForRequest:request action:action frameName:frameName formState:nil andCall:self withSelector:@selector(_continueLoadRequestAfterNewWindowPolicy:frameName:formState:)];
1483 }
1484
1485 // Return next frame to be traversed, visiting children after parent
1486 - (WebFrame *)_nextFrameWithWrap:(BOOL)wrapFlag
1487 {
1488     return Frame([[self _bridge] nextFrameWithWrap:wrapFlag]);
1489 }
1490
1491 // Return previous frame to be traversed, exact reverse order of _nextFrame
1492 - (WebFrame *)_previousFrameWithWrap:(BOOL)wrapFlag
1493 {
1494     return Frame([[self _bridge] previousFrameWithWrap:wrapFlag]);
1495 }
1496
1497 - (BOOL)_shouldCreateRenderers
1498 {
1499     return [_private->bridge shouldCreateRenderers];
1500 }
1501
1502 - (int)_numPendingOrLoadingRequests:(BOOL)recurse
1503 {
1504     if (!recurse)
1505         return [[self _bridge] numPendingOrLoadingRequests];
1506
1507     int num = 0;
1508     for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self])
1509         num += [[frame _bridge] numPendingOrLoadingRequests];
1510
1511     return num;
1512 }
1513
1514 - (void)_reloadForPluginChanges
1515 {
1516     for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self]) {
1517         NSView <WebDocumentView> *documentView = [[frame frameView] documentView];
1518         if (([documentView isKindOfClass:[WebHTMLView class]] && [_private->bridge containsPlugins]))
1519             [frame reload];
1520     }
1521 }
1522
1523 - (void)_attachScriptDebugger
1524 {
1525     if (!_private->scriptDebugger)
1526         _private->scriptDebugger = [[WebScriptDebugger alloc] initWithWebFrame:self];
1527 }
1528
1529 - (void)_detachScriptDebugger
1530 {
1531     if (_private->scriptDebugger) {
1532         id old = _private->scriptDebugger;
1533         _private->scriptDebugger = nil;
1534         [old release];
1535     }
1536 }
1537
1538 - (void)_recursive_pauseNullEventsForAllNetscapePlugins
1539 {
1540     for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self]) {
1541         NSView <WebDocumentView> *documentView = [[frame frameView] documentView];
1542         if ([documentView isKindOfClass:[WebHTMLView class]])
1543             [(WebHTMLView *)documentView _pauseNullEventsForAllNetscapePlugins];
1544     }
1545 }
1546
1547 - (void)_recursive_resumeNullEventsForAllNetscapePlugins
1548 {
1549     for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self]) {
1550         NSView <WebDocumentView> *documentView = [[frame frameView] documentView];
1551         if ([documentView isKindOfClass:[WebHTMLView class]])
1552             [(WebHTMLView *)documentView _resumeNullEventsForAllNetscapePlugins];
1553     }
1554 }
1555
1556 - (void)_didReceiveServerRedirectForProvisionalLoadForFrame
1557 {
1558     [[[self webView] _frameLoadDelegateForwarder] webView:[self webView]
1559         didReceiveServerRedirectForProvisionalLoadForFrame:self];
1560 }
1561
1562 - (id)_initWithWebFrameView:(WebFrameView *)fv webView:(WebView *)v bridge:(WebFrameBridge *)bridge
1563 {
1564     self = [super init];
1565     if (!self)
1566         return nil;
1567
1568     _private = [[WebFramePrivate alloc] init];
1569
1570     _private->bridge = bridge;
1571
1572     if (fv) {
1573         [_private setWebFrameView:fv];
1574         [fv _setWebFrame:self];
1575     }
1576     
1577     _private->frameLoader = [[WebFrameLoader alloc] initWithClient:self];
1578     
1579     ++WebFrameCount;
1580     
1581     return self;
1582 }
1583
1584 - (NSArray *)_documentViews
1585 {
1586     NSMutableArray *result = [NSMutableArray array];
1587     for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self]) {
1588         id docView = [[frame frameView] documentView];
1589         if (docView)
1590             [result addObject:docView];
1591     }
1592         
1593     return result;
1594 }
1595
1596 - (void)_updateBackground
1597 {
1598     BOOL drawsBackground = [[self webView] drawsBackground];
1599     NSColor *backgroundColor = [[self webView] backgroundColor];
1600
1601     for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self]) {
1602         // Never call setDrawsBackground:YES here on the scroll view or the background color will
1603         // flash between pages loads. setDrawsBackground:YES will be called in WebFrame's _frameLoadCompleted.
1604         if (!drawsBackground)
1605             [[[frame frameView] _scrollView] setDrawsBackground:NO];
1606         [[[frame frameView] _scrollView] setBackgroundColor:backgroundColor];
1607         id documentView = [[frame frameView] documentView];
1608         if ([documentView respondsToSelector:@selector(setDrawsBackground:)])
1609             [documentView setDrawsBackground:drawsBackground];
1610         if ([documentView respondsToSelector:@selector(setBackgroundColor:)])
1611             [documentView setBackgroundColor:backgroundColor];
1612         [[frame _bridge] setDrawsBackground:drawsBackground];
1613         [[frame _bridge] setBaseBackgroundColor:backgroundColor];
1614     }
1615 }
1616
1617 - (void)_setInternalLoadDelegate:(id)internalLoadDelegate
1618 {
1619     _private->internalLoadDelegate = internalLoadDelegate;
1620 }
1621
1622 - (id)_internalLoadDelegate
1623 {
1624     return _private->internalLoadDelegate;
1625 }
1626
1627 - (NSURLRequest *)_requestFromDelegateForRequest:(NSURLRequest *)request identifier:(id *)identifier error:(NSError **)error
1628 {
1629     ASSERT(request != nil);
1630     
1631     WebView *wv = [self webView];
1632     id delegate = [wv resourceLoadDelegate];
1633     id sharedDelegate = [WebDefaultResourceLoadDelegate sharedResourceLoadDelegate];
1634     WebResourceDelegateImplementationCache implementations = [wv _resourceLoadDelegateImplementations];
1635     WebDataSource *dataSource = [self dataSource];
1636     
1637     if (implementations.delegateImplementsIdentifierForRequest) {
1638         *identifier = [delegate webView:wv identifierForInitialRequest:request fromDataSource:dataSource];
1639     } else {
1640         *identifier = [sharedDelegate webView:wv identifierForInitialRequest:request fromDataSource:dataSource];
1641     }
1642         
1643     NSURLRequest *newRequest;
1644     if (implementations.delegateImplementsWillSendRequest) {
1645         newRequest = [delegate webView:wv resource:*identifier willSendRequest:request redirectResponse:nil fromDataSource:dataSource];
1646     } else {
1647         newRequest = [sharedDelegate webView:wv resource:*identifier willSendRequest:request redirectResponse:nil fromDataSource:dataSource];
1648     }
1649     
1650     if (newRequest == nil) {
1651         *error = [NSError _webKitErrorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled URL:[request URL]];
1652     } else {
1653         *error = nil;
1654     }
1655     
1656     return newRequest;
1657 }
1658
1659 - (void)_sendRemainingDelegateMessagesWithIdentifier:(id)identifier response:(NSURLResponse *)response length:(unsigned)length error:(NSError *)error 
1660 {    
1661     WebView *wv = [self webView];
1662     id delegate = [wv resourceLoadDelegate];
1663     id sharedDelegate = [WebDefaultResourceLoadDelegate sharedResourceLoadDelegate];
1664     WebResourceDelegateImplementationCache implementations = [wv _resourceLoadDelegateImplementations];
1665     WebDataSource *dataSource = [self dataSource];
1666         
1667     if (response != nil) {
1668         if (implementations.delegateImplementsDidReceiveResponse) {
1669             [delegate webView:wv resource:identifier didReceiveResponse:response fromDataSource:dataSource];
1670         } else {
1671             [sharedDelegate webView:wv resource:identifier didReceiveResponse:response fromDataSource:dataSource];
1672         }
1673     }
1674     
1675     if (length > 0) {
1676         if (implementations.delegateImplementsDidReceiveContentLength) {
1677             [delegate webView:wv resource:identifier didReceiveContentLength:(WebNSUInteger)length fromDataSource:dataSource];
1678         } else {
1679             [sharedDelegate webView:wv resource:identifier didReceiveContentLength:(WebNSUInteger)length fromDataSource:dataSource];
1680         }
1681     }
1682     
1683     if (error == nil) {
1684         if (implementations.delegateImplementsDidFinishLoadingFromDataSource) {
1685             [delegate webView:wv resource:identifier didFinishLoadingFromDataSource:dataSource];
1686         } else {
1687             [sharedDelegate webView:wv resource:identifier didFinishLoadingFromDataSource:dataSource];
1688         }
1689         [self _checkLoadComplete];
1690     } else {
1691         [[wv _resourceLoadDelegateForwarder] webView:wv resource:identifier didFailLoadingWithError:error fromDataSource:dataSource];
1692     }
1693 }
1694
1695 - (void)_safeLoadURL:(NSURL *)URL
1696 {
1697    // Call the bridge because this is where our security checks are made.
1698     [[self _bridge] loadURL:URL 
1699                     referrer:[[[[self dataSource] request] URL] _web_originalDataAsString]
1700                       reload:NO
1701                  userGesture:YES       
1702                       target:nil
1703              triggeringEvent:[NSApp currentEvent]
1704                         form:nil 
1705                   formValues:nil];
1706 }
1707
1708 - (void)_unmarkAllMisspellings
1709 {
1710     for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self])
1711         [[frame _bridge] unmarkAllMisspellings];
1712 }
1713
1714 - (BOOL)_hasSelection
1715 {
1716     id documentView = [[self frameView] documentView];    
1717     
1718     // optimization for common case to avoid creating potentially large selection string
1719     if ([documentView isKindOfClass:[WebHTMLView class]]) {
1720         DOMRange *selectedDOMRange = [[self _bridge] selectedDOMRange];
1721         return selectedDOMRange && ![selectedDOMRange collapsed];
1722     }
1723     
1724     if ([documentView conformsToProtocol:@protocol(WebDocumentText)])
1725         return [[documentView selectedString] length] > 0;
1726     
1727     return NO;
1728 }
1729
1730 - (void)_clearSelection
1731 {
1732     id documentView = [[self frameView] documentView];    
1733     if ([documentView conformsToProtocol:@protocol(WebDocumentText)])
1734         [documentView deselectAll];
1735 }
1736
1737 #ifndef NDEBUG
1738
1739 - (BOOL)_atMostOneFrameHasSelection;
1740 {
1741     // FIXME: 4186050 is one known case that makes this debug check fail
1742     BOOL found = NO;
1743     for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self]) {
1744         if ([frame _hasSelection]) {
1745             if (found)
1746                 return NO;
1747             found = YES;
1748         }
1749     }
1750
1751     return YES;
1752 }
1753 #endif
1754
1755 - (WebFrame *)_findFrameWithSelection
1756 {
1757     for (WebFrame *frame = self; frame; frame = [frame _traverseNextFrameStayWithin:self])
1758         if ([frame _hasSelection])
1759             return frame;
1760
1761     return nil;
1762 }
1763
1764 - (void)_clearSelectionInOtherFrames
1765 {
1766     // We rely on WebDocumentSelection protocol implementors to call this method when they become first 
1767     // responder. It would be nicer to just notice first responder changes here instead, but there's no 
1768     // notification sent when the first responder changes in general (Radar 2573089).
1769     WebFrame *frameWithSelection = [[[self webView] mainFrame] _findFrameWithSelection];
1770     if (frameWithSelection != self)
1771         [frameWithSelection _clearSelection];
1772
1773     // While we're in the general area of selection and frames, check that there is only one now.
1774     ASSERT([[[self webView] mainFrame] _atMostOneFrameHasSelection]);
1775 }
1776
1777 - (BOOL)_subframeIsLoading
1778 {
1779     // It's most likely that the last added frame is the last to load so we walk backwards.
1780     for (WebFrame *frame = [self _lastChildFrame]; frame; frame = [frame _previousSiblingFrame])
1781         if ([[frame dataSource] isLoading] || [[frame provisionalDataSource] isLoading])
1782             return YES;
1783     return NO;
1784 }
1785
1786 - (void)_addPlugInView:(NSView *)plugInView
1787 {
1788     ASSERT([plugInView respondsToSelector:@selector(setWebFrame:)]);
1789     ASSERT(![_private->plugInViews containsObject:plugInView]);
1790     
1791     if (!_private->plugInViews)
1792         _private->plugInViews = [[NSMutableSet alloc] init];
1793         
1794     [plugInView setWebFrame:self];
1795     [_private->plugInViews addObject:plugInView];
1796 }
1797
1798 - (void)_removeAllPlugInViews
1799 {
1800     if (!_private->plugInViews)
1801         return;
1802     
1803     [_private->plugInViews makeObjectsPerformSelector:@selector(setWebFrame:) withObject:nil];
1804     [_private->plugInViews release];
1805     _private->plugInViews = nil;
1806 }
1807
1808 // This is called when leaving a page or closing the WebView
1809 - (void)_willCloseURL
1810 {
1811     [self _removeAllPlugInViews];
1812 }
1813
1814 - (void)_addExtraFieldsToRequest:(NSMutableURLRequest *)request mainResource:(BOOL)mainResource alwaysFromRequest:(BOOL)f
1815 {
1816     [request _web_setHTTPUserAgent:[[self webView] userAgentForURL:[request URL]]];
1817     
1818     if ([_private->frameLoader loadType] == WebFrameLoadTypeReload)
1819         [request setValue:@"max-age=0" forHTTPHeaderField:@"Cache-Control"];
1820     
1821     // Don't set the cookie policy URL if it's already been set.
1822     if ([request mainDocumentURL] == nil) {
1823         if (mainResource && (self == [[self webView] mainFrame] || f))
1824             [request setMainDocumentURL:[request URL]];
1825         else
1826             [request setMainDocumentURL:[[[[self webView] mainFrame] dataSource] _URL]];
1827     }
1828     
1829     if (mainResource)
1830         [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"];
1831 }
1832
1833 - (BOOL)_isMainFrame
1834 {
1835     return self == [[self webView] mainFrame];
1836 }
1837
1838 - (void)_addInspector:(WebInspector *)inspector
1839 {
1840     if (!_private->inspectors)
1841         _private->inspectors = [[NSMutableSet alloc] init];
1842     ASSERT(![_private->inspectors containsObject:inspector]);
1843     [_private->inspectors addObject:inspector];
1844 }
1845
1846 - (void)_removeInspector:(WebInspector *)inspector
1847 {
1848     ASSERT([_private->inspectors containsObject:inspector]);
1849     [_private->inspectors removeObject:inspector];
1850 }
1851
1852 - (WebFrameLoader *)_frameLoader
1853 {
1854     return _private->frameLoader;
1855 }
1856
1857 - (void)_prepareForDataSourceReplacement
1858 {
1859     if (![_private->frameLoader dataSource]) {
1860         ASSERT(![self _childFrameCount]);
1861         return;
1862     }
1863     
1864     // Make sure that any work that is triggered by resigning first reponder can get done.
1865     // The main example where this came up is the textDidEndEditing that is sent to the
1866     // FormsDelegate (3223413).  We need to do this before _detachChildren, since that will
1867     // remove the views as a side-effect of freeing the bridge, at which point we can't
1868     // post the FormDelegate messages.
1869     //
1870     // Note that this can also take FirstResponder away from a child of our frameView that
1871     // is not in a child frame's view.  This is OK because we are in the process
1872     // of loading new content, which will blow away all editors in this top frame, and if
1873     // a non-editor is firstReponder it will not be affected by endEditingFor:.
1874     // Potentially one day someone could write a DocView whose editors were not all
1875     // replaced by loading new content, but that does not apply currently.
1876     NSView *frameView = [self frameView];
1877     NSWindow *window = [frameView window];
1878     NSResponder *firstResp = [window firstResponder];
1879     if ([firstResp isKindOfClass:[NSView class]]
1880         && [(NSView *)firstResp isDescendantOf:frameView])
1881     {
1882         [window endEditingFor:firstResp];
1883     }
1884     
1885     [self _detachChildren];
1886 }
1887
1888 - (void)_frameLoadCompleted
1889 {
1890     NSScrollView *sv = [[self frameView] _scrollView];
1891     if ([[self webView] drawsBackground])
1892         [sv setDrawsBackground:YES];
1893     [_private setPreviousItem:nil];
1894 }
1895
1896 - (WebDataSource *)_dataSourceForDocumentLoader:(WebDocumentLoader *)loader
1897 {
1898     return [(WebDocumentLoaderMac *)loader dataSource];
1899 }
1900
1901 - (WebDocumentLoader *)_createDocumentLoaderWithRequest:(NSURLRequest *)request
1902 {
1903     WebDocumentLoaderMac *loader = [[WebDocumentLoaderMac alloc] initWithRequest:request];
1904     
1905     WebDataSource *dataSource = [[WebDataSource alloc] _initWithDocumentLoader:loader];
1906     [loader setDataSource:dataSource];
1907     [dataSource release];
1908
1909     return loader;
1910 }
1911
1912 /*
1913     There is a race condition between the layout and load completion that affects restoring the scroll position.
1914     We try to restore the scroll position at both the first layout and upon load completion.
1915
1916     1) If first layout happens before the load completes, we want to restore the scroll position then so that the
1917        first time we draw the page is already scrolled to the right place, instead of starting at the top and later
1918        jumping down.  It is possible that the old scroll position is past the part of the doc laid out so far, in
1919        which case the restore silent fails and we will fix it in when we try to restore on doc completion.
1920     2) If the layout happens after the load completes, the attempt to restore at load completion time silently
1921        fails.  We then successfully restore it when the layout happens.
1922  */
1923
1924 - (void)_restoreScrollPositionAndViewState
1925 {
1926     ASSERT([_private currentItem]);
1927     NSView <WebDocumentView> *docView = [[self frameView] documentView];
1928     NSPoint point = [[_private currentItem] scrollPoint];
1929     if ([docView conformsToProtocol:@protocol(_WebDocumentViewState)]) {        
1930         id state = [[_private currentItem] viewState];
1931         if (state) {
1932             [(id <_WebDocumentViewState>)docView setViewState:state];
1933         }
1934         
1935         [(id <_WebDocumentViewState>)docView setScrollPoint:point];
1936     } else {
1937         [docView scrollPoint:point];
1938     }
1939 }
1940
1941 @end
1942
1943 @implementation WebFrame (WebPrivate)
1944
1945 // FIXME: this exists only as a convenience for Safari, consider moving there
1946 - (BOOL)_isDescendantOfFrame:(WebFrame *)ancestor
1947 {
1948     return [[self _bridge] isDescendantOfFrame:[ancestor _bridge]];
1949 }
1950
1951 - (void)_setShouldCreateRenderers:(BOOL)f
1952 {
1953     [_private->bridge setShouldCreateRenderers:f];
1954 }
1955
1956 - (NSColor *)_bodyBackgroundColor
1957 {
1958     return [_private->bridge bodyBackgroundColor];
1959 }
1960
1961 - (BOOL)_isFrameSet
1962 {
1963     return [_private->bridge isFrameSet];
1964 }
1965
1966 - (BOOL)_firstLayoutDone
1967 {
1968     return [_private->frameLoader firstLayoutDone];
1969 }
1970
1971 - (WebFrameLoadType)_loadType
1972 {
1973     return [_private->frameLoader loadType];
1974 }
1975
1976 @end
1977
1978 @implementation WebFormState : NSObject
1979
1980 - (id)initWithForm:(DOMElement *)form values:(NSDictionary *)values sourceFrame:(WebFrame *)sourceFrame
1981 {
1982     self = [super init];
1983     if (!self)
1984         return nil;
1985     
1986     _form = [form retain];
1987     _values = [values copy];
1988     _sourceFrame = [sourceFrame retain];
1989     return self;
1990 }
1991
1992 - (void)dealloc
1993 {
1994     [_form release];
1995     [_values release];
1996     [_sourceFrame release];
1997     [super dealloc];
1998 }
1999
2000 - (DOMElement *)form
2001 {
2002     return _form;
2003 }
2004
2005 - (NSDictionary *)values
2006 {
2007     return _values;
2008 }
2009
2010 - (WebFrame *)sourceFrame
2011 {
2012     return _sourceFrame;
2013 }
2014
2015 @end
2016
2017 @implementation WebFrame
2018
2019 - (id)init
2020 {
2021     return [self initWithName:nil webFrameView:nil webView:nil];
2022 }
2023
2024 // FIXME: this method can't work any more and should be marked deprecated
2025 - (id)initWithName:(NSString *)n webFrameView:(WebFrameView *)fv webView:(WebView *)v
2026 {
2027     return [self _initWithWebFrameView:fv webView:v bridge:nil];
2028 }
2029
2030 - (void)dealloc
2031 {
2032     ASSERT(_private->bridge == nil);
2033     [_private release];
2034
2035     --WebFrameCount;
2036
2037     [super dealloc];
2038 }
2039
2040 - (void)finalize
2041 {
2042     ASSERT(_private->bridge == nil);
2043
2044     --WebFrameCount;
2045
2046     [super finalize];
2047 }
2048
2049 - (NSString *)name
2050 {
2051     return [[self _bridge] name];
2052 }
2053
2054 - (WebFrameView *)frameView
2055 {
2056     return [_private webFrameView];
2057 }
2058
2059 - (WebView *)webView
2060 {
2061     return [[[self _bridge] page] webView];
2062 }
2063
2064 - (DOMDocument *)DOMDocument
2065 {
2066     return [[self dataSource] _isDocumentHTML] ? [_private->bridge DOMDocument] : nil;
2067 }
2068
2069 - (DOMHTMLElement *)frameElement
2070 {
2071     return [[self webView] mainFrame] != self ? [_private->bridge frameElement] : nil;
2072 }
2073
2074 - (WebDataSource *)provisionalDataSource
2075 {
2076     return [_private->frameLoader provisionalDataSource];
2077 }
2078
2079 - (WebDataSource *)dataSource
2080 {
2081     return [_private->frameLoader dataSource];
2082 }
2083
2084 - (void)loadRequest:(NSURLRequest *)request
2085 {
2086     // FIXME: is this the right place to reset loadType? Perhaps this should be done
2087     // after loading is finished or aborted.
2088     [_private->frameLoader setLoadType:WebFrameLoadTypeStandard];
2089     [_private->frameLoader _loadRequest:request archive:nil];
2090 }
2091
2092 - (void)_loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)URL unreachableURL:(NSURL *)unreachableURL
2093 {
2094     NSURLRequest *request = [self _webDataRequestForData:data 
2095                                                 MIMEType:MIMEType 
2096                                         textEncodingName:encodingName 
2097                                                  baseURL:URL
2098                                           unreachableURL:unreachableURL];
2099     [self loadRequest:request];
2100 }
2101
2102
2103 - (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)URL
2104 {
2105     [self _loadData:data MIMEType:MIMEType textEncodingName:encodingName baseURL:URL unreachableURL:nil];
2106 }
2107
2108 - (void)_loadHTMLString:(NSString *)string baseURL:(NSURL *)URL unreachableURL:(NSURL *)unreachableURL
2109 {
2110     NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
2111     [self _loadData:data MIMEType:nil textEncodingName:@"UTF-8" baseURL:URL unreachableURL:unreachableURL];
2112 }
2113
2114 - (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)URL
2115 {
2116     [self _loadHTMLString:string baseURL:URL unreachableURL:nil];
2117 }
2118
2119 - (void)loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)URL forUnreachableURL:(NSURL *)unreachableURL
2120 {
2121     [self _loadHTMLString:string baseURL:URL unreachableURL:unreachableURL];
2122 }
2123
2124 - (void)loadArchive:(WebArchive *)archive
2125 {
2126     WebResource *mainResource = [archive mainResource];
2127     if (mainResource) {
2128         NSURLRequest *request = [self _webDataRequestForData:[mainResource data] 
2129                                                     MIMEType:[mainResource MIMEType]
2130                                             textEncodingName:[mainResource textEncodingName]
2131                                                      baseURL:[mainResource URL]
2132                                               unreachableURL:nil];
2133         [_private->frameLoader _loadRequest:request archive:archive];
2134     }
2135 }
2136
2137 - (void)stopLoading
2138 {
2139     [_private->frameLoader stopLoading];
2140 }
2141
2142 - (void)reload
2143 {
2144     [_private->frameLoader reload];
2145 }
2146
2147 - (WebFrame *)findFrameNamed:(NSString *)name
2148 {
2149     return Frame([[self _bridge] findFrameNamed:name]);
2150 }
2151
2152 - (WebFrame *)parentFrame
2153 {
2154     return [[Frame([[self _bridge] parent]) retain] autorelease];
2155 }
2156
2157 - (NSArray *)childFrames
2158 {
2159     NSMutableArray *children = [NSMutableArray arrayWithCapacity:[self _childFrameCount]];
2160     for (WebFrame *child = [self _firstChildFrame]; child; child = [child _nextSiblingFrame])
2161         [children addObject:child];
2162
2163     return children;
2164 }
2165
2166 @end
2167
2168 @implementation WebFrame (WebFrameLoaderClient)
2169
2170 - (void)_resetBackForwardList
2171 {
2172     // Note this doesn't verify the current load type as a b/f operation because it is called from
2173     // a subframe in the case of a delegate bailing out of the nav before it even gets to provisional state.
2174     ASSERT(self == [[self webView] mainFrame]);
2175     WebHistoryItem *resetItem = [_private currentItem];
2176     if (resetItem)
2177         [[[self webView] backForwardList] goToItem:resetItem];
2178 }
2179
2180 - (void)_invalidateCurrentItemPageCache
2181 {
2182     // When we are pre-commit, the currentItem is where the pageCache data resides
2183     NSDictionary *pageCache = [[_private currentItem] pageCache];
2184
2185     [[self _bridge] invalidatePageCache:pageCache];
2186     
2187     // We're assuming that WebCore invalidates its pageCache state in didNotOpen:pageCache:
2188     [[_private currentItem] setHasPageCache:NO];
2189 }
2190
2191 - (BOOL)_provisionalItemIsTarget
2192 {
2193     return [[_private provisionalItem] isTargetItem];
2194 }
2195
2196 - (BOOL)_loadProvisionalItemFromPageCache
2197 {
2198     WebHistoryItem *item = [_private provisionalItem];
2199     if (![item hasPageCache])
2200         return NO;
2201     NSDictionary *pageCache = [item pageCache];
2202     if (![pageCache objectForKey:WebCorePageCacheStateKey])
2203         return NO;
2204     LOG(PageCache, "Restoring page from back/forward cache, %@", [item URL]);
2205     [[self provisionalDataSource] _loadFromPageCache:pageCache];
2206     return YES;
2207 }
2208
2209 @end