Tests:
[WebKit-https.git] / WebKit / WebView.subproj / WebView.m
1 /*      
2     WebView.m
3     Copyright 2001, 2002 Apple, Inc. All rights reserved.
4 */
5
6 #import <WebKit/WebViewPrivate.h>
7
8 #import <WebKit/WebAssertions.h>
9 #import <WebKit/WebBackForwardList.h>
10 #import <WebKit/WebBaseNetscapePluginView.h>
11 #import <WebKit/WebBridge.h>
12 #import <WebKit/WebControllerSets.h>
13 #import <WebKit/WebDataSourcePrivate.h>
14 #import <WebKit/WebDefaultFrameLoadDelegate.h>
15 #import <WebKit/WebDefaultPolicyDelegate.h>
16 #import <WebKit/WebDefaultResourceLoadDelegate.h>
17 #import <WebKit/WebDefaultUIDelegate.h>
18 #import <WebKit/WebDocument.h>
19 #import <WebKit/WebDocumentInternal.h>
20 #import <WebKit/WebDynamicScrollBarsView.h>
21 #import <WebKit/WebDownload.h>
22 #import <WebKit/WebException.h>
23 #import <WebKit/WebFormDelegatePrivate.h>
24 #import <WebKit/WebFramePrivate.h>
25 #import <WebKit/WebFrameViewPrivate.h>
26 #import <WebKit/WebHistoryItemPrivate.h>
27 #import <WebKit/WebHTMLView.h>
28 #import <WebKit/WebIconDatabase.h>
29 #import <WebKit/WebKitErrors.h>
30 #import <WebKit/WebKitLogging.h>
31 #import <WebKit/WebKitStatisticsPrivate.h>
32 #import <WebKit/WebNSPasteboardExtras.h>
33 #import "WebNSPrintOperationExtras.h"
34 #import <WebKit/WebNSURLExtras.h>
35 #import <WebKit/WebNSViewExtras.h>
36 #import <WebKit/WebPluginDatabase.h>
37 #import <WebKit/WebPolicyDelegate.h>
38 #import <WebKit/WebPreferencesPrivate.h>
39 #import <WebKit/WebResourceLoadDelegate.h>
40 #import <WebKit/WebTextView.h>
41 #import <WebKit/WebTextRepresentation.h>
42 #import <WebKit/WebTextRenderer.h>
43 #import <WebKit/WebUIDelegate.h>
44 #import <WebKit/WebUIDelegatePrivate.h>
45
46 #import <WebCore/WebCoreEncodings.h>
47 #import <WebCore/WebCoreSettings.h>
48
49 #import <Foundation/NSUserDefaults_NSURLExtras.h>
50 #import <Foundation/NSURLConnection.h>
51
52 #import <Foundation/NSData_NSURLExtras.h>
53 #import <Foundation/NSDictionary_NSURLExtras.h>
54 #import <Foundation/NSURLDownloadPrivate.h>
55 #import <Foundation/NSURLFileTypeMappings.h>
56 #import <Foundation/NSURLRequestPrivate.h>
57
58 static const struct UserAgentSpoofTableEntry *_web_findSpoofTableEntry(const char *, unsigned);
59
60 // Turn off inlining to avoid warning with newer gcc.
61 #undef __inline
62 #define __inline
63 #include "WebUserAgentSpoofTable.c"
64 #undef __inline
65
66 NSString *WebElementFrameKey =                  @"WebElementFrame";
67 NSString *WebElementImageKey =                  @"WebElementImage";
68 NSString *WebElementImageAltStringKey =         @"WebElementImageAltString";
69 NSString *WebElementImageRectKey =              @"WebElementImageRect";
70 NSString *WebElementImageURLKey =               @"WebElementImageURL";
71 NSString *WebElementIsSelectedKey =             @"WebElementIsSelected";
72 NSString *WebElementLinkURLKey =                @"WebElementLinkURL";
73 NSString *WebElementLinkTargetFrameKey =        @"WebElementTargetFrame";
74 NSString *WebElementLinkLabelKey =              @"WebElementLinkLabel";
75 NSString *WebElementLinkTitleKey =              @"WebElementLinkTitle";
76
77 NSString *WebViewProgressStartedNotification =          @"WebProgressStartedNotification";
78 NSString *WebViewProgressEstimateChangedNotification =  @"WebProgressEstimateChangedNotification";
79 NSString *WebViewProgressFinishedNotification =         @"WebProgressFinishedNotification";
80
81 enum { WebViewVersion = 2 };
82
83
84 static NSMutableSet *schemesWithRepresentationsSet;
85
86 NSString *_WebCanGoBackKey =            @"canGoBack";
87 NSString *_WebCanGoForwardKey =         @"canGoForward";
88 NSString *_WebEstimatedProgressKey =    @"estimatedProgress";
89 NSString *_WebIsLoadingKey =            @"isLoading";
90 NSString *_WebMainFrameIconKey =        @"mainFrameIcon";
91 NSString *_WebMainFrameTitleKey =       @"mainFrameTitle";
92 NSString *_WebMainFrameURLKey =         @"mainFrameURL";
93
94 @interface WebProgressItem : NSObject
95 {
96 @public
97     long long bytesReceived;
98     long long estimatedLength;
99 }
100 @end
101
102 @implementation WebProgressItem
103 @end
104
105 @implementation WebViewPrivate
106
107 - init 
108 {
109     backForwardList = [[WebBackForwardList alloc] init];
110     textSizeMultiplier = 1;
111     progressNotificationInterval = 0.02;
112     progressNotificationTimeInterval = 0.1;
113     settings = [[WebCoreSettings alloc] init];
114
115     return self;
116 }
117
118 - (void)dealloc
119 {
120     ASSERT(!mainFrame);
121     
122     [backForwardList release];
123     [applicationNameForUserAgent release];
124     [userAgentOverride release];
125     int i;
126     for (i = 0; i != NumUserAgentStringTypes; ++i) {
127         [userAgent[i] release];
128     }
129     
130     [preferences release];
131     [settings release];
132     [hostWindow release];
133     
134     [policyDelegateForwarder release];
135     [resourceProgressDelegateForwarder release];
136     [UIDelegateForwarder release];
137     [frameLoadDelegateForwarder release];
138     
139     [progressItems release];
140     
141     [super dealloc];
142 }
143
144 @end
145
146 @implementation WebView (WebPrivate)
147
148 + (NSArray *)_supportedMIMETypes
149 {
150     // Load the plug-in DB allowing plug-ins to install types.
151     [WebPluginDatabase installedPlugins];
152     return [[WebFrameView _viewTypesAllowImageTypeOmission:NO] allKeys];
153 }
154
155 + (NSArray *)_supportedFileExtensions
156 {
157     NSMutableSet *extensions = [[NSMutableSet alloc] init];
158     NSArray *MIMETypes = [self _supportedMIMETypes];
159     NSEnumerator *enumerator = [MIMETypes objectEnumerator];
160     NSString *MIMEType;
161     NSURLFileTypeMappings *mappings = [NSURLFileTypeMappings sharedMappings];
162     while ((MIMEType = [enumerator nextObject]) != nil) {
163         NSArray *extensionsForType = [mappings extensionsForMIMEType:MIMEType];
164         if (extensionsForType) {
165             [extensions addObjectsFromArray:extensionsForType];
166         }
167     }
168     NSArray *uniqueExtensions = [extensions allObjects];
169     [extensions release];
170     return uniqueExtensions;
171 }
172
173 + (BOOL)_viewClass:(Class *)vClass andRepresentationClass:(Class *)rClass forMIMEType:(NSString *)MIMEType;
174 {
175     MIMEType = [MIMEType lowercaseString];
176     Class viewClass;
177     Class repClass;
178     
179     // Simple optimization that avoids loading the plug-in DB and image types for the HTML case.
180     if ([self canShowMIMETypeAsHTML:MIMEType]) {
181         viewClass = [[WebFrameView _viewTypesAllowImageTypeOmission:YES] _web_objectForMIMEType:MIMEType];
182         repClass = [[WebDataSource _repTypesAllowImageTypeOmission:YES] _web_objectForMIMEType:MIMEType];
183         if (viewClass && repClass) {
184             if (vClass) {
185                 *vClass = viewClass;
186             }
187             if (rClass) {
188                 *rClass = repClass;
189             }
190             return YES;
191         }
192     }
193     
194     // Load the plug-in DB allowing plug-ins to install types.
195     [[WebPluginDatabase installedPlugins] loadPluginIfNeededForMIMEType:MIMEType];
196     
197     // Load the image types and get the view class and rep class. This should be the fullest picture of all handled types.
198     viewClass = [[WebFrameView _viewTypesAllowImageTypeOmission:NO] _web_objectForMIMEType:MIMEType];
199     repClass = [[WebDataSource _repTypesAllowImageTypeOmission:NO] _web_objectForMIMEType:MIMEType];
200     if (viewClass && repClass) {
201         // Special-case WebTextView for text types that shouldn't be shown.
202         if (viewClass == [WebTextView class] &&
203             repClass == [WebTextRepresentation class] &&
204             [[WebTextView unsupportedTextMIMETypes] containsObject:MIMEType]) {
205             return NO;
206         }
207         if (vClass) {
208             *vClass = viewClass;
209         }
210         if (rClass) {
211             *rClass = repClass;
212         }
213         return YES;
214     }
215     
216     return NO;
217 }
218
219 + (void)_setAlwaysUseATSU:(BOOL)f
220 {
221     [WebTextRenderer _setAlwaysUseATSU:f];
222 }
223
224
225 + (BOOL)canShowFile:(NSString *)path
226 {
227     NSString *MIMEType;
228
229     MIMEType = [WebView _MIMETypeForFile:path];
230     return [[self class] canShowMIMEType:MIMEType];
231 }
232
233 + (NSString *)suggestedFileExtensionForMIMEType: (NSString *)type
234 {
235     return [[NSURLFileTypeMappings sharedMappings] preferredExtensionForMIMEType:type];
236 }
237
238 - (void)_close
239 {
240     if (_private->setName != nil) {
241         [WebViewSets removeWebView:self fromSetNamed:_private->setName];
242         [_private->setName release];
243         _private->setName = nil;
244     }
245
246     [_private->mainFrame _detachFromParent];
247     [_private->mainFrame release];
248     _private->mainFrame = nil;
249     
250     // Clear the page cache so we call destroy on all the plug-ins in the page cache to break any retain cycles.
251     // See comment in [WebHistoryItem _releaseAllPendingPageCaches] for more information.
252     [_private->backForwardList _clearPageCache];
253 }
254
255 - (WebFrame *)_createFrameNamed:(NSString *)fname inParent:(WebFrame *)parent allowsScrolling:(BOOL)allowsScrolling
256 {
257     WebFrameView *childView = [[WebFrameView alloc] initWithFrame:NSMakeRect(0,0,0,0)];
258
259     [childView _setWebView:self];
260     [childView setAllowsScrolling:allowsScrolling];
261     
262     WebFrame *newFrame = [[WebFrame alloc] initWithName:fname webFrameView:childView webView:self];
263
264     [childView release];
265
266     [parent _addChild:newFrame];
267     
268     [newFrame release];
269         
270     return newFrame;
271 }
272
273 - (void)_finishedLoadingResourceFromDataSource: (WebDataSource *)dataSource
274 {
275     WebFrame *frame = [dataSource webFrame];
276     
277     ASSERT(dataSource != nil);
278     
279     // This resource has completed, so check if the load is complete for all frames.
280     if (frame != nil) {
281         [frame _transitionToLayoutAcceptable];
282         [frame _checkLoadComplete];
283     }
284 }
285
286 - (void)_mainReceivedBytesSoFar: (unsigned)bytesSoFar fromDataSource: (WebDataSource *)dataSource complete: (BOOL)isComplete
287 {
288     WebFrame *frame = [dataSource webFrame];
289     
290     ASSERT(dataSource != nil);
291
292     // The frame may be nil if a previously cancelled load is still making progress callbacks.
293     if (frame == nil)
294         return;
295         
296     // This resource has completed, so check if the load is complete for all frames.
297     if (isComplete){
298         // If the load is complete, mark the primary load as done.  The primary load is the load
299         // of the main document.  Other resources may still be arriving.
300         [dataSource _setPrimaryLoadComplete: YES];
301         [frame _checkLoadComplete];
302     }
303     else {
304         // If the frame isn't complete it might be ready for a layout.  Perform that check here.
305         // Note that transitioning a frame to this state doesn't guarantee a layout, rather it
306         // just indicates that an early layout can be performed.
307         int timedLayoutSize = [[WebPreferences standardPreferences] _initialTimedLayoutSize];
308         if ((int)bytesSoFar > timedLayoutSize)
309             [frame _transitionToLayoutAcceptable];
310     }
311 }
312
313
314 - (void)_receivedError: (NSError *)error fromDataSource: (WebDataSource *)dataSource
315 {
316     WebFrame *frame = [dataSource webFrame];
317
318     [frame _checkLoadComplete];
319 }
320
321
322 - (void)_mainReceivedError:(NSError *)error fromDataSource:(WebDataSource *)dataSource complete:(BOOL)isComplete
323 {
324     ASSERT(dataSource);
325 #ifndef NDEBUG
326     ASSERT([dataSource webFrame]);
327 #endif
328     
329     [dataSource _setMainDocumentError: error];
330
331     if (isComplete) {
332         [dataSource _setPrimaryLoadComplete:YES];
333         [[dataSource webFrame] _checkLoadComplete];
334     }
335 }
336
337 + (NSString *)_MIMETypeForFile:(NSString *)path
338 {
339     NSString *extension = [path pathExtension];
340     NSString *MIMEType = nil;
341
342     // Get the MIME type from the extension.
343     if ([extension length] != 0) {
344         MIMEType = [[NSURLFileTypeMappings sharedMappings] MIMETypeForExtension:extension];
345     }
346
347     // If we can't get a known MIME type from the extension, sniff.
348     if ([MIMEType length] == 0 || [MIMEType isEqualToString:@"application/octet-stream"]) {
349         NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path];
350         NSData *data = [handle readDataOfLength:GUESS_MIME_TYPE_PEEK_LENGTH];
351         [handle closeFile];
352         if ([data length] != 0) {
353             MIMEType = [data _web_guessedMIMEType];
354         }
355         if ([MIMEType length] == 0){
356             MIMEType = @"application/octet-stream";
357         }
358     }
359
360     return MIMEType;
361 }
362
363 - (void)_downloadURL:(NSURL *)URL
364 {
365     [self _downloadURL:URL toDirectory:nil];
366 }
367
368 - (void)_downloadURL:(NSURL *)URL toDirectory:(NSString *)directory
369 {
370     ASSERT(URL);
371     
372     NSURLRequest *request = [[NSURLRequest alloc] initWithURL:URL];
373     [WebDownload _downloadWithRequest:request
374                              delegate:_private->downloadDelegate
375                             directory:[directory isAbsolutePath] ? directory : nil];
376     [request release];
377 }
378
379 - (BOOL)defersCallbacks
380 {
381     return _private->defersCallbacks;
382 }
383
384 - (void)setDefersCallbacks:(BOOL)defers
385 {
386     if (defers == _private->defersCallbacks) {
387         return;
388     }
389
390     _private->defersCallbacks = defers;
391     [_private->mainFrame _defersCallbacksChanged];
392 }
393
394 - (void)_setTopLevelFrameName:(NSString *)name
395 {
396     [[self mainFrame] _setName:name];
397 }
398
399 - (WebFrame *)_findFrameInThisWindowNamed: (NSString *)name
400 {
401     return [[self mainFrame] _descendantFrameNamed:name];
402 }
403
404 - (WebFrame *)_findFrameNamed:(NSString *)name
405 {
406     // Try this WebView first.
407     WebFrame *frame = [self _findFrameInThisWindowNamed:name];
408
409     if (frame != nil) {
410         return frame;
411     }
412
413     // Try other WebViews in the same set
414     if (_private->setName != nil) {
415         NSEnumerator *enumerator = [WebViewSets webViewsInSetNamed:_private->setName];
416         WebView *webView;
417         while ((webView = [enumerator nextObject]) != nil && frame == nil) {
418             frame = [webView _findFrameInThisWindowNamed:name];
419         }
420     }
421
422     return frame;
423 }
424
425 - (WebView *)_openNewWindowWithRequest:(NSURLRequest *)request
426 {
427     id wd = [self UIDelegate];
428     WebView *newWindowWebView = nil;
429     if ([wd respondsToSelector:@selector(webView:createWebViewWithRequest:)])
430         newWindowWebView = [wd webView:self createWebViewWithRequest:request];
431     else {
432         newWindowWebView = [[WebDefaultUIDelegate sharedUIDelegate] webView:self createWebViewWithRequest: request];
433     }
434
435     [[newWindowWebView _UIDelegateForwarder] webViewShow: newWindowWebView];
436
437     return newWindowWebView;
438 }
439
440 - (NSMenu *)_menuForElement:(NSDictionary *)element
441 {
442     NSArray *defaultMenuItems = [[WebDefaultUIDelegate sharedUIDelegate]
443           webView:self contextMenuItemsForElement:element defaultMenuItems:nil];
444     NSArray *menuItems = defaultMenuItems;
445     NSMenu *menu = nil;
446     unsigned i;
447
448     if (_private->UIDelegate) {
449         id cd = _private->UIDelegate;
450         
451         if ([cd respondsToSelector:@selector(webView:contextMenuItemsForElement:defaultMenuItems:)])
452             menuItems = [cd webView:self contextMenuItemsForElement:element defaultMenuItems:defaultMenuItems];
453     } 
454
455     if (menuItems && [menuItems count] > 0) {
456         menu = [[[NSMenu alloc] init] autorelease];
457
458         for (i=0; i<[menuItems count]; i++) {
459             [menu addItem:[menuItems objectAtIndex:i]];
460         }
461     }
462
463     return menu;
464 }
465
466 - (void)_mouseDidMoveOverElement:(NSDictionary *)dictionary modifierFlags:(unsigned)modifierFlags
467 {
468     // When the mouse isn't over this view at all, we'll get called with a dictionary of nil over
469     // and over again. So it's a good idea to catch that here and not send multiple calls to the delegate
470     // for that case.
471     
472     if (dictionary && _private->lastElementWasNonNil) {
473         [[self _UIDelegateForwarder] webView:self mouseDidMoveOverElement:dictionary modifierFlags:modifierFlags];
474     }
475     _private->lastElementWasNonNil = dictionary != nil;
476 }
477
478 - (void)_goToItem:(WebHistoryItem *)item withLoadType:(WebFrameLoadType)type
479 {
480     // We never go back/forward on a per-frame basis, so the target must be the main frame
481     //ASSERT([item target] == nil || [self _findFrameNamed:[item target]] == [self mainFrame]);
482
483     // abort any current load if we're going back/forward
484     [[self mainFrame] stopLoading];
485     [[self mainFrame] _goToItem:item withLoadType:type];
486 }
487
488 // Not used now, but could be if we ever store frames in bookmarks or history
489 - (void)_loadItem:(WebHistoryItem *)item
490 {
491     WebHistoryItem *newItem = [item copy];      // Makes a deep copy, happily
492     [[self backForwardList] addItem:newItem];
493     [self _goToItem:newItem withLoadType:WebFrameLoadTypeIndexedBackForward];
494 }
495
496 - (void)_loadBackForwardListFromOtherView:(WebView *)otherView
497 {
498     // It turns out the right combination of behavior is done with the back/forward load
499     // type.  (See behavior matrix at the top of WebFramePrivate.)  So we copy all the items
500     // in the back forward list, and go to the current one.
501
502     WebBackForwardList *bfList = [self backForwardList];
503     ASSERT(![bfList currentItem]);      // destination list should be empty
504
505     WebBackForwardList *otherBFList = [otherView backForwardList];
506     if (![otherBFList currentItem]) {
507         return;         // empty back forward list, bail
508     }
509
510     WebHistoryItem *newItemToGoTo = nil;
511     int lastItemIndex = [otherBFList forwardListCount];
512     int i;
513     for (i = -[otherBFList backListCount]; i <= lastItemIndex; i++) {
514         if (i == 0) {
515             // If this item is showing , save away its current scroll and form state,
516             // since that might have changed since loading and it is normally not saved
517             // until we leave that page.
518             [[otherView mainFrame] _saveDocumentAndScrollState];
519         }
520         WebHistoryItem *newItem = [[otherBFList itemAtIndex:i] copy];
521         [bfList addItem:newItem];
522         if (i == 0) {
523             newItemToGoTo = newItem;
524         }
525     }
526     
527     [self _goToItem:newItemToGoTo withLoadType:WebFrameLoadTypeIndexedBackForward];
528 }
529
530 - (void)_setFormDelegate: (id<WebFormDelegate>)delegate
531 {
532     _private->formDelegate = delegate;
533 }
534
535 - (id<WebFormDelegate>)_formDelegate
536 {
537     if (!_private->formDelegate) {
538         // create lazily, to give the client a chance to set one before we bother to alloc the shared one
539         _private->formDelegate = [WebFormDelegate _sharedWebFormDelegate];
540     }
541     return _private->formDelegate;
542 }
543
544 - (WebCoreSettings *)_settings
545 {
546     return _private->settings;
547 }
548
549 - (void)_updateWebCoreSettingsFromPreferences: (WebPreferences *)preferences
550 {
551     [_private->settings setCursiveFontFamily:[preferences cursiveFontFamily]];
552     [_private->settings setDefaultFixedFontSize:[preferences defaultFixedFontSize]];
553     [_private->settings setDefaultFontSize:[preferences defaultFontSize]];
554     [_private->settings setDefaultTextEncoding:[preferences defaultTextEncodingName]];
555     [_private->settings setFantasyFontFamily:[preferences fantasyFontFamily]];
556     [_private->settings setFixedFontFamily:[preferences fixedFontFamily]];
557     [_private->settings setJavaEnabled:[preferences isJavaEnabled]];
558     [_private->settings setJavaScriptEnabled:[preferences isJavaScriptEnabled]];
559     [_private->settings setJavaScriptCanOpenWindowsAutomatically:[preferences javaScriptCanOpenWindowsAutomatically]];
560     [_private->settings setMinimumFontSize:[preferences minimumFontSize]];
561     [_private->settings setMinimumLogicalFontSize:[preferences minimumLogicalFontSize]];
562     [_private->settings setPluginsEnabled:[preferences arePlugInsEnabled]];
563     [_private->settings setSansSerifFontFamily:[preferences sansSerifFontFamily]];
564     [_private->settings setSerifFontFamily:[preferences serifFontFamily]];
565     [_private->settings setStandardFontFamily:[preferences standardFontFamily]];
566     [_private->settings setWillLoadImagesAutomatically:[preferences loadsImagesAutomatically]];
567
568     if ([preferences userStyleSheetEnabled]) {
569         [_private->settings setUserStyleSheetLocation:[[preferences userStyleSheetLocation] _web_originalDataAsString]];
570     } else {
571         [_private->settings setUserStyleSheetLocation:@""];
572     }
573     [_private->settings setShouldPrintBackgrounds:[preferences shouldPrintBackgrounds]];
574 }
575
576 - (void)_releaseUserAgentStrings
577 {
578     int i;
579     for (i = 0; i != NumUserAgentStringTypes; ++i) {
580         [_private->userAgent[i] release];
581         _private->userAgent[i] = nil;
582     }
583 }
584
585
586 - (void)_preferencesChangedNotification: (NSNotification *)notification
587 {
588     WebPreferences *preferences = (WebPreferences *)[notification object];
589     
590     ASSERT(preferences == [self preferences]);
591     [self _releaseUserAgentStrings];
592     [self _updateWebCoreSettingsFromPreferences: preferences];
593 }
594
595 - _frameLoadDelegateForwarder
596 {
597     if (!_private->frameLoadDelegateForwarder)
598         _private->frameLoadDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget: [self frameLoadDelegate]  defaultTarget: [WebDefaultFrameLoadDelegate sharedFrameLoadDelegate] templateClass: [WebDefaultFrameLoadDelegate class]];
599     return _private->frameLoadDelegateForwarder;
600 }
601
602 - _resourceLoadDelegateForwarder
603 {
604     if (!_private->resourceProgressDelegateForwarder)
605         _private->resourceProgressDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget: [self resourceLoadDelegate] defaultTarget: [WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] templateClass: [WebDefaultResourceLoadDelegate class]];
606     return _private->resourceProgressDelegateForwarder;
607 }
608
609 - (void)_cacheResourceLoadDelegateImplementations
610 {
611     if ([[self resourceLoadDelegate] respondsToSelector:@selector(webView:resource:didCancelAuthenticationChallenge:fromDataSource:)])
612         _private->resourceLoadDelegateImplementations.delegateImplementsDidCancelAuthenticationChallenge = YES;
613
614     if ([[self resourceLoadDelegate] respondsToSelector:@selector(webView:resource:didReceiveAuthenticationChallenge:fromDataSource:)])
615         _private->resourceLoadDelegateImplementations.delegateImplementsDidReceiveAuthenticationChallenge = YES;
616
617     if ([[self resourceLoadDelegate] respondsToSelector:@selector(webView:resource:didFinishLoadingFromDataSource:)])
618         _private->resourceLoadDelegateImplementations.delegateImplementsDidFinishLoadingFromDataSource = YES;
619     if ([[self resourceLoadDelegate] respondsToSelector:@selector(webView:resource:didReceiveContentLength:fromDataSource:)])
620         _private->resourceLoadDelegateImplementations.delegateImplementsDidReceiveContentLength = YES;
621     if ([[self resourceLoadDelegate] respondsToSelector:@selector(webView:resource:didReceiveResponse:fromDataSource:)])
622         _private->resourceLoadDelegateImplementations.delegateImplementsDidReceiveResponse = YES;
623     if ([[self resourceLoadDelegate] respondsToSelector:@selector(webView:resource:willSendRequest:redirectResponse:fromDataSource:)])
624         _private->resourceLoadDelegateImplementations.delegateImplementsWillSendRequest = YES;
625     if ([[self resourceLoadDelegate] respondsToSelector: @selector(webView:identifierForInitialRequest:fromDataSource:)])
626         _private->resourceLoadDelegateImplementations.delegateImplementsIdentifierForRequest = YES;
627 }
628
629 - (WebResourceDelegateImplementationCache)_resourceLoadDelegateImplementations
630 {
631     return _private->resourceLoadDelegateImplementations;
632 }
633
634 - _policyDelegateForwarder
635 {
636     if (!_private->policyDelegateForwarder)
637         _private->policyDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget: [self policyDelegate] defaultTarget: [WebDefaultPolicyDelegate sharedPolicyDelegate] templateClass: [WebDefaultPolicyDelegate class]];
638     return _private->policyDelegateForwarder;
639 }
640
641 - _UIDelegateForwarder
642 {
643     if (!_private->UIDelegateForwarder)
644         _private->UIDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget: [self UIDelegate] defaultTarget: [WebDefaultUIDelegate sharedUIDelegate] templateClass: [WebDefaultUIDelegate class]];
645     return _private->UIDelegateForwarder;
646 }
647
648
649 - (WebFrame *)_frameForDataSource: (WebDataSource *)dataSource fromFrame: (WebFrame *)frame
650 {
651     NSArray *frames;
652     int i, count;
653     WebFrame *result, *aFrame;
654
655     if ([frame dataSource] == dataSource)
656         return frame;
657
658     if ([frame provisionalDataSource] == dataSource)
659         return frame;
660
661     frames = [frame childFrames];
662     count = [frames count];
663     for (i = 0; i < count; i++){
664         aFrame = [frames objectAtIndex: i];
665         result = [self _frameForDataSource: dataSource fromFrame: aFrame];
666         if (result)
667             return result;
668     }
669
670     return nil;
671 }
672
673
674 - (WebFrame *)_frameForDataSource: (WebDataSource *)dataSource
675 {
676     WebFrame *frame = [self mainFrame];
677
678     return [self _frameForDataSource: dataSource fromFrame: frame];
679 }
680
681
682 - (WebFrame *)_frameForView: (WebFrameView *)aView fromFrame: (WebFrame *)frame
683 {
684     NSArray *frames;
685     int i, count;
686     WebFrame *result, *aFrame;
687
688     if ([frame frameView] == aView)
689         return frame;
690
691     frames = [frame childFrames];
692     count = [frames count];
693     for (i = 0; i < count; i++){
694         aFrame = [frames objectAtIndex: i];
695         result = [self _frameForView: aView fromFrame: aFrame];
696         if (result)
697             return result;
698     }
699
700     return nil;
701 }
702
703 - (WebFrame *)_frameForView: (WebFrameView *)aView
704 {
705     WebFrame *frame = [self mainFrame];
706
707     return [self _frameForView: aView fromFrame: frame];
708 }
709
710 - (void)_registerDraggedTypes
711 {
712     [self registerForDraggedTypes:[NSPasteboard _web_dragTypesForURL]];
713 }
714
715 - (void)_closeWindow
716 {
717     [[self _UIDelegateForwarder] webViewClose:self];
718 }
719
720 + (void)_unregisterViewClassAndRepresentationClassForMIMEType:(NSString *)MIMEType;
721 {
722     [[WebFrameView _viewTypesAllowImageTypeOmission:NO] removeObjectForKey:MIMEType];
723     [[WebDataSource _repTypesAllowImageTypeOmission:NO] removeObjectForKey:MIMEType];
724 }
725
726 + (void)_registerViewClass:(Class)viewClass representationClass:(Class)representationClass forURLScheme:(NSString *)URLScheme;
727 {
728     NSString *MIMEType = [self _generatedMIMETypeForURLScheme:URLScheme];
729     [self registerViewClass:viewClass representationClass:representationClass forMIMEType:MIMEType];
730
731     // This is used to make _representationExistsForURLScheme faster.
732     // Without this set, we'd have to create the MIME type each time.
733     if (schemesWithRepresentationsSet == nil) {
734         schemesWithRepresentationsSet = [[NSMutableSet alloc] init];
735     }
736     [schemesWithRepresentationsSet addObject:[[[URLScheme lowercaseString] copy] autorelease]];
737 }
738
739 + (NSString *)_generatedMIMETypeForURLScheme:(NSString *)URLScheme
740 {
741     return [@"x-apple-web-kit/" stringByAppendingString:[URLScheme lowercaseString]];
742 }
743
744 + (BOOL)_representationExistsForURLScheme:(NSString *)URLScheme
745 {
746     return [schemesWithRepresentationsSet containsObject:[URLScheme lowercaseString]];
747 }
748
749 + (BOOL)_canHandleRequest:(NSURLRequest *)request
750 {
751     if ([NSURLConnection canHandleRequest:request]) {
752         return YES;
753     }
754
755     return [self _representationExistsForURLScheme:[[request URL] scheme]];
756 }
757
758 + (NSString *)_decodeData:(NSData *)data
759 {
760     return [WebCoreEncodings decodeData:data];
761 }
762
763 - (void)_pushPerformingProgrammaticFocus
764 {
765     _private->programmaticFocusCount++;
766 }
767
768 - (void)_popPerformingProgrammaticFocus
769 {
770     _private->programmaticFocusCount--;
771 }
772
773 - (BOOL)_isPerformingProgrammaticFocus
774 {
775     return _private->programmaticFocusCount != 0;
776 }
777
778 #define UnknownTotalBytes -1
779 #define WebProgressItemDefaultEstimatedLength 1024*16
780
781 - (void)_didChangeValueForKey: (NSString *)key
782 {
783     LOG (Bindings, "calling didChangeValueForKey: %@", key);
784     [self didChangeValueForKey: key];
785 }
786
787 - (void)_willChangeValueForKey: (NSString *)key
788 {
789     LOG (Bindings, "calling willChangeValueForKey: %@", key);
790     [self willChangeValueForKey: key];
791 }
792
793 // Always start progress at INITIAL_PROGRESS_VALUE so it appears progress indicators
794 // will immediately show some progress.  This helps provide feedback as soon as a load
795 // starts.
796 #define INITIAL_PROGRESS_VALUE 0.1
797
798 - (void)_resetProgress
799 {
800     [_private->progressItems release];
801     _private->progressItems = nil;
802     _private->totalPageAndResourceBytesToLoad = 0;
803     _private->totalBytesReceived = 0;
804     _private->progressValue = 0;
805     _private->lastNotifiedProgressValue = 0;
806     _private->lastNotifiedProgressTime = 0;
807     _private->finalProgressChangedSent = NO;
808     _private->numProgressTrackedFrames = 0;
809     [_private->orginatingProgressFrame release];
810     _private->orginatingProgressFrame = nil;
811 }
812 - (void)_progressStarted:(WebFrame *)frame
813 {
814     LOG (Progress, "frame %p(%@), _private->numProgressTrackedFrames %d, _private->orginatingProgressFrame %p", frame, [frame name], _private->numProgressTrackedFrames, _private->orginatingProgressFrame);
815     [self _willChangeValueForKey: @"estimatedProgress"];
816     if (_private->numProgressTrackedFrames == 0 || _private->orginatingProgressFrame == frame){
817         [self _resetProgress];
818         _private->progressValue = INITIAL_PROGRESS_VALUE;
819         _private->orginatingProgressFrame = [frame retain];
820         [[NSNotificationCenter defaultCenter] postNotificationName:WebViewProgressStartedNotification object:self];
821     }
822     _private->numProgressTrackedFrames++;
823     [self _didChangeValueForKey: @"estimatedProgress"];
824 }
825
826 - (void)_finalProgressComplete
827 {
828     LOG (Progress, "");
829
830     // Before resetting progress value be sure to send client a least one notification
831     // with final progress value.
832     if (!_private->finalProgressChangedSent) {
833         _private->progressValue = 1;
834         [[NSNotificationCenter defaultCenter] postNotificationName:WebViewProgressEstimateChangedNotification object:self];
835     }
836     
837     [self _resetProgress];
838     
839     [[NSNotificationCenter defaultCenter] postNotificationName:WebViewProgressFinishedNotification object:self];
840 }
841
842 - (void)_progressCompleted:(WebFrame *)frame
843 {    
844     LOG (Progress, "frame %p(%@), _private->numProgressTrackedFrames %d, _private->orginatingProgressFrame %p", frame, [frame name], _private->numProgressTrackedFrames, _private->orginatingProgressFrame);
845
846     if (_private->numProgressTrackedFrames <= 0)
847         return;
848
849     [self _willChangeValueForKey: @"estimatedProgress"];
850
851     _private->numProgressTrackedFrames--;
852     if (_private->numProgressTrackedFrames == 0 ||
853         (frame == _private->orginatingProgressFrame && _private->numProgressTrackedFrames != 0)){
854         [self _finalProgressComplete];
855     }
856     [self _didChangeValueForKey: @"estimatedProgress"];
857 }
858
859 - (void)_incrementProgressForConnection:(NSURLConnection *)con response:(NSURLResponse *)response;
860 {
861     if (!con)
862         return;
863
864     LOG (Progress, "_private->numProgressTrackedFrames %d, _private->orginatingProgressFrame %p", _private->numProgressTrackedFrames, _private->orginatingProgressFrame);
865     
866     if (_private->numProgressTrackedFrames <= 0)
867         return;
868         
869     WebProgressItem *item = [[WebProgressItem alloc] init];
870
871     if (!item)
872         return;
873
874     long long length = [response expectedContentLength];
875     if (length < 0){
876         length = WebProgressItemDefaultEstimatedLength;
877     }
878     item->estimatedLength = length;
879     _private->totalPageAndResourceBytesToLoad += length;
880     
881     if (!_private->progressItems)
882         _private->progressItems = [[NSMutableDictionary alloc] init];
883         
884     [_private->progressItems _web_setObject:item forUncopiedKey:con];
885     [item release];
886 }
887
888 - (void)_incrementProgressForConnection:(NSURLConnection *)con data:(NSData *)data
889 {
890     if (!con)
891         return;
892
893     WebProgressItem *item = [_private->progressItems objectForKey:con];
894
895     if (!item)
896         return;
897
898     [self _willChangeValueForKey: @"estimatedProgress"];
899
900     unsigned bytesReceived = [data length];
901     double increment = 0, percentOfRemainingBytes;
902     long long remainingBytes, estimatedBytesForPendingRequests;
903
904     item->bytesReceived += bytesReceived;
905     if (item->bytesReceived > item->estimatedLength){
906         _private->totalPageAndResourceBytesToLoad += ((item->bytesReceived*2) - item->estimatedLength);
907         item->estimatedLength = item->bytesReceived*2;
908     }
909     
910     int numPendingOrLoadingRequests = [[self mainFrame] _numPendingOrLoadingRequests:YES];
911     estimatedBytesForPendingRequests = WebProgressItemDefaultEstimatedLength * numPendingOrLoadingRequests;
912     remainingBytes = ((_private->totalPageAndResourceBytesToLoad + estimatedBytesForPendingRequests) - _private->totalBytesReceived);
913     percentOfRemainingBytes = (double)bytesReceived / (double)remainingBytes;
914     increment = (1.0 - _private->progressValue) * percentOfRemainingBytes;
915     
916     _private->totalBytesReceived += bytesReceived;
917     
918     _private->progressValue += increment;
919
920     if (_private->progressValue < 0.0)
921         _private->progressValue = 0.0;
922
923     if (_private->progressValue > 1.0)
924         _private->progressValue = 1.0;
925
926     double now = CFAbsoluteTimeGetCurrent();
927     double notifiedProgressTimeDelta = CFAbsoluteTimeGetCurrent() - _private->lastNotifiedProgressTime;
928     _private->lastNotifiedProgressTime = now;
929     
930     LOG (Progress, "_private->progressValue %g, _private->numProgressTrackedFrames %d", _private->progressValue, _private->numProgressTrackedFrames);
931     double notificationProgressDelta = _private->progressValue - _private->lastNotifiedProgressValue;
932     if ((notificationProgressDelta >= _private->progressNotificationInterval ||
933             notifiedProgressTimeDelta >= _private->progressNotificationTimeInterval) &&
934             _private->numProgressTrackedFrames > 0) {
935         if (!_private->finalProgressChangedSent) {
936             if (_private->progressValue == 1)
937                 _private->finalProgressChangedSent = YES;
938             [[NSNotificationCenter defaultCenter] postNotificationName:WebViewProgressEstimateChangedNotification object:self];
939             _private->lastNotifiedProgressValue = _private->progressValue;
940         }
941     }
942
943     [self _didChangeValueForKey: @"estimatedProgress"];
944 }
945
946 - (void)_completeProgressForConnection:(NSURLConnection *)con
947 {
948     WebProgressItem *item = [_private->progressItems objectForKey:con];
949
950     if (!item)
951         return;
952         
953     // Adjust the total expected bytes to account for any overage/underage.
954     long long delta = item->bytesReceived - item->estimatedLength;
955     _private->totalPageAndResourceBytesToLoad += delta;
956     item->estimatedLength = item->bytesReceived;
957 }
958
959 // Required to prevent automatic observer notifications.
960 + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
961     return NO;
962 }
963
964 - (NSArray *)_declaredKeys {
965     static NSArray *declaredKeys = nil;
966     
967     if (!declaredKeys) {
968         declaredKeys = [[NSArray alloc] initWithObjects:_WebMainFrameURLKey, _WebIsLoadingKey, _WebEstimatedProgressKey, _WebCanGoBackKey, _WebCanGoForwardKey, _WebMainFrameTitleKey, _WebMainFrameIconKey, nil];
969     }
970
971     return declaredKeys;
972 }
973
974 - (void)setObservationInfo:(void *)info
975 {
976     _private->observationInfo = info;
977 }
978
979 - (void *)observationInfo
980 {
981     return _private->observationInfo;
982 }
983
984 - (void)_willChangeBackForwardKeys
985 {
986     [self _willChangeValueForKey: _WebCanGoBackKey];
987     [self _willChangeValueForKey: _WebCanGoForwardKey];
988 }
989
990 - (void)_didChangeBackForwardKeys
991 {
992     [self _didChangeValueForKey: _WebCanGoBackKey];
993     [self _didChangeValueForKey: _WebCanGoForwardKey];
994 }
995
996 - (void)_didStartProvisionalLoadForFrame:(WebFrame *)frame
997 {
998     [self _willChangeBackForwardKeys];
999     if (frame == [self mainFrame]){
1000         // Force an observer update by sending a will/did.
1001         [self _willChangeValueForKey: _WebIsLoadingKey];
1002         [self _didChangeValueForKey: _WebIsLoadingKey];
1003
1004         [self _willChangeValueForKey: _WebMainFrameURLKey];
1005     }
1006     [NSApp setWindowsNeedUpdate:YES];
1007 }
1008
1009 - (void)_didCommitLoadForFrame:(WebFrame *)frame
1010 {
1011     if (frame == [self mainFrame]){
1012         [self _didChangeValueForKey: _WebMainFrameURLKey];
1013     }
1014     [NSApp setWindowsNeedUpdate:YES];
1015 }
1016
1017 - (void)_didFinishLoadForFrame:(WebFrame *)frame
1018 {
1019     [self _didChangeBackForwardKeys];
1020     if (frame == [self mainFrame]){
1021         // Force an observer update by sending a will/did.
1022         [self _willChangeValueForKey: _WebIsLoadingKey];
1023         [self _didChangeValueForKey: _WebIsLoadingKey];
1024     }
1025     [NSApp setWindowsNeedUpdate:YES];
1026 }
1027
1028 - (void)_didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
1029 {
1030     [self _didChangeBackForwardKeys];
1031     if (frame == [self mainFrame]){
1032         // Force an observer update by sending a will/did.
1033         [self _willChangeValueForKey: _WebIsLoadingKey];
1034         [self _didChangeValueForKey: _WebIsLoadingKey];
1035     }
1036     [NSApp setWindowsNeedUpdate:YES];
1037 }
1038
1039 - (void)_didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
1040 {
1041     [self _didChangeBackForwardKeys];
1042     if (frame == [self mainFrame]){
1043         // Force an observer update by sending a will/did.
1044         [self _willChangeValueForKey: _WebIsLoadingKey];
1045         [self _didChangeValueForKey: _WebIsLoadingKey];
1046         
1047         [self _didChangeValueForKey: _WebMainFrameURLKey];
1048     }
1049     [NSApp setWindowsNeedUpdate:YES];
1050 }
1051
1052 - (void)_reloadForPluginChanges
1053 {
1054     [[self mainFrame] _reloadForPluginChanges];
1055 }
1056
1057 - (NSCachedURLResponse *)_cachedResponseForURL:(NSURL *)URL
1058 {
1059     NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];
1060     [request setHTTPUserAgent:[self userAgentForURL:URL]];
1061     NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
1062     [request release];
1063     return cachedResponse;
1064 }
1065
1066 - (NSFileWrapper *)_fileWrapperForURL:(NSURL *)URL
1067 {
1068     if ([URL isFileURL]) {
1069         NSString *path = [[URL path] stringByResolvingSymlinksInPath];
1070         return [[[NSFileWrapper alloc] initWithPath:path] autorelease];
1071     } else {
1072         NSCachedURLResponse *cachedResponse = [self _cachedResponseForURL:URL];
1073         if (cachedResponse) {
1074             NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:[cachedResponse data]] autorelease];
1075             [wrapper setPreferredFilename:[[cachedResponse response] suggestedFilename]];
1076             return wrapper;
1077         }
1078     }
1079     return nil;
1080 }
1081
1082 @end
1083
1084
1085 @implementation _WebSafeForwarder
1086
1087 - initWithTarget: t defaultTarget: dt templateClass: (Class)aClass
1088 {
1089     [super init];
1090     target = t;         // Non retained.
1091     defaultTarget = dt;
1092     templateClass = aClass;
1093     return self;
1094 }
1095
1096
1097 // Used to send messages to delegates that implement informal protocols.
1098 + safeForwarderWithTarget: t defaultTarget: dt templateClass: (Class)aClass;
1099 {
1100     return [[[_WebSafeForwarder alloc] initWithTarget: t defaultTarget: dt templateClass: aClass] autorelease];
1101 }
1102
1103 #ifndef NDEBUG
1104 NSMutableDictionary *countInvocations;
1105 #endif
1106
1107 - (void)forwardInvocation:(NSInvocation *)anInvocation
1108 {
1109 #ifndef NDEBUG
1110     if (!countInvocations){
1111         countInvocations = [[NSMutableDictionary alloc] init];
1112     }
1113     NSNumber *count = [countInvocations objectForKey: NSStringFromSelector([anInvocation selector])];
1114     if (!count)
1115         count = [NSNumber numberWithInt: 1];
1116     else
1117         count = [NSNumber numberWithInt: [count intValue] + 1];
1118     [countInvocations setObject: count forKey: NSStringFromSelector([anInvocation selector])];
1119 #endif
1120     if ([target respondsToSelector: [anInvocation selector]])
1121         [anInvocation invokeWithTarget: target];
1122     else if ([defaultTarget respondsToSelector: [anInvocation selector]])
1123         [anInvocation invokeWithTarget: defaultTarget];
1124     // Do nothing quietly if method not implemented.
1125 }
1126
1127 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
1128 {
1129     return [templateClass instanceMethodSignatureForSelector: aSelector];
1130 }
1131
1132 @end
1133
1134 @interface WebView (WebInternal)
1135 - (BOOL)_isLoading;
1136 @end
1137
1138 @implementation WebView
1139
1140 + (BOOL)canShowMIMEType:(NSString *)MIMEType
1141 {
1142     return [self _viewClass:nil andRepresentationClass:nil forMIMEType:MIMEType];
1143 }
1144
1145 + (BOOL)canShowMIMETypeAsHTML:(NSString *)MIMEType
1146 {
1147     return [WebFrameView _canShowMIMETypeAsHTML:MIMEType];
1148 }
1149
1150 - (void)_commonInitializationWithFrameName:(NSString *)frameName groupName:(NSString *)groupName
1151 {
1152     NSRect f = [self frame];
1153     WebFrameView *wv = [[WebFrameView alloc] initWithFrame: NSMakeRect(0,0,f.size.width,f.size.height)];
1154     [wv setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
1155     [self addSubview: wv];
1156     [wv release];
1157
1158     _private->mainFrame = [[WebFrame alloc] initWithName: frameName webFrameView: wv  webView: self];
1159     [self setGroupName:groupName];
1160     
1161     // If there's already a next key view (e.g., from a nib), wire it up to our
1162     // contained frame view. In any case, wire our next key view up to the our
1163     // contained frame view. This works together with our becomeFirstResponder 
1164     // and setNextKeyView overrides.
1165     NSView *nextKeyView = [self nextKeyView];
1166     if (nextKeyView != nil && nextKeyView != wv) {
1167         [wv setNextKeyView:nextKeyView];
1168     }
1169     [super setNextKeyView:wv];
1170
1171     ++WebViewCount;
1172
1173     [self _registerDraggedTypes];
1174
1175     // Update WebCore with preferences.  These values will either come from an archived WebPreferences,
1176     // or from the standard preferences, depending on whether this method was called from initWithCoder:
1177     // or initWithFrame, respectively.
1178     [self _updateWebCoreSettingsFromPreferences: [self preferences]];
1179     
1180     // Register to receive notifications whenever preference values change.
1181     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesChangedNotification:)
1182                                                  name:WebPreferencesChangedNotification object:[self preferences]];
1183 }
1184
1185 - init
1186 {
1187     return [self initWithFrame: NSZeroRect frameName: nil groupName: nil];
1188 }
1189
1190 - initWithFrame: (NSRect)f
1191 {
1192     [self initWithFrame: f frameName:nil groupName:nil];
1193     return self;
1194 }
1195
1196 - initWithFrame: (NSRect)f frameName: (NSString *)frameName groupName: (NSString *)groupName;
1197 {
1198     [super initWithFrame: f];
1199     _private = [[WebViewPrivate alloc] init];
1200     [self _commonInitializationWithFrameName:frameName groupName:groupName];
1201     [self setMaintainsBackForwardList: YES];
1202     return self;
1203 }
1204
1205 - (id)initWithCoder:(NSCoder *)decoder
1206 {
1207     WebView *result = nil;
1208
1209 NS_DURING
1210
1211     NSString *frameName;
1212     NSString *groupName;
1213     
1214     result = [super initWithCoder:decoder];
1215     result->_private = [[WebViewPrivate alloc] init];
1216     
1217     // We don't want any of the archived subviews.  The subviews will always
1218     // be created in _commonInitializationFrameName:groupName:.
1219     [[result subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
1220     
1221     if ([decoder allowsKeyedCoding]){
1222         frameName = [decoder decodeObjectForKey:@"FrameName"];
1223         groupName = [decoder decodeObjectForKey:@"GroupName"];
1224                 
1225         [result setPreferences: [decoder decodeObjectForKey:@"Preferences"]];
1226         result->_private->useBackForwardList = [decoder decodeBoolForKey:@"UseBackForwardList"];
1227
1228         LOG (Encoding, "FrameName = %@, GroupName = %@, useBackForwardList = %d\n", frameName, groupName, (int)_private->useBackForwardList);
1229     }
1230     else {
1231         int version;
1232     
1233         [decoder decodeValueOfObjCType:@encode(int) at:&version];
1234         frameName = [decoder decodeObject];
1235         groupName = [decoder decodeObject];
1236         [result setPreferences: [decoder decodeObject]];
1237         if (version > 1)
1238             [decoder decodeValuesOfObjCTypes:"c",&result->_private->useBackForwardList];
1239     }
1240     [result _commonInitializationWithFrameName:frameName groupName:groupName];
1241     
1242 NS_HANDLER
1243
1244     result = nil;
1245     [self release];
1246
1247 NS_ENDHANDLER
1248
1249     return result;
1250 }
1251
1252 - (void)encodeWithCoder:(NSCoder *)encoder
1253 {
1254     [super encodeWithCoder:encoder];
1255
1256     if ([encoder allowsKeyedCoding]){
1257         [encoder encodeObject:[[self mainFrame] name] forKey:@"FrameName"];
1258         [encoder encodeObject:[self groupName] forKey:@"GroupName"];
1259         [encoder encodeObject:[self preferences] forKey:@"Preferences"];
1260         [encoder encodeBool:_private->useBackForwardList forKey:@"UseBackForwardList"];
1261
1262         LOG (Encoding, "FrameName = %@, GroupName = %@, useBackForwardList = %d\n", [[self mainFrame] name], [self groupName], (int)_private->useBackForwardList);
1263     }
1264     else {
1265         int version = WebViewVersion;
1266         [encoder encodeValueOfObjCType:@encode(int) at:&version];
1267         [encoder encodeObject:[[self mainFrame] name]];
1268         [encoder encodeObject:[self groupName]];
1269         [encoder encodeObject:[self preferences]];
1270         [encoder encodeValuesOfObjCTypes:"c",&_private->useBackForwardList];
1271     }
1272 }
1273
1274 - (void)dealloc
1275 {
1276     [self _close];
1277     
1278     --WebViewCount;
1279     
1280     [[NSNotificationCenter defaultCenter] removeObserver:self];
1281     
1282     [WebPreferences _removeReferenceForIdentifier: [self preferencesIdentifier]];
1283     
1284     [_private release];
1285     // [super dealloc] can end up dispatching against _private (3466082)
1286     _private = nil;
1287
1288     [super dealloc];
1289 }
1290
1291 - (void)setPreferences: (WebPreferences *)prefs
1292 {
1293     if (_private->preferences != prefs){
1294         [[NSNotificationCenter defaultCenter] removeObserver: self name: WebPreferencesChangedNotification object: [self preferences]];
1295         [WebPreferences _removeReferenceForIdentifier: [_private->preferences identifier]];
1296         [_private->preferences release];
1297         _private->preferences = [prefs retain];
1298         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesChangedNotification:)
1299                                                     name:WebPreferencesChangedNotification object:[self preferences]];
1300     }
1301 }
1302
1303 - (WebPreferences *)preferences
1304 {
1305     return _private->preferences ? _private->preferences : [WebPreferences standardPreferences];
1306 }
1307
1308 - (void)setPreferencesIdentifier:(NSString *)anIdentifier
1309 {
1310     if (![anIdentifier isEqual: [[self preferences] identifier]]){
1311         [self setPreferences: [[WebPreferences alloc] initWithIdentifier:anIdentifier]];
1312     }
1313 }
1314
1315 - (NSString *)preferencesIdentifier
1316 {
1317     return [[self preferences] identifier];
1318 }
1319
1320
1321 - (void)setUIDelegate:delegate
1322 {
1323     _private->UIDelegate = delegate;
1324     [_private->UIDelegateForwarder release];
1325     _private->UIDelegateForwarder = nil;
1326 }
1327
1328 - UIDelegate
1329 {
1330     return _private->UIDelegate;
1331 }
1332
1333 - (void)setResourceLoadDelegate: delegate
1334 {
1335     _private->resourceProgressDelegate = delegate;
1336     [_private->resourceProgressDelegateForwarder release];
1337     _private->resourceProgressDelegateForwarder = nil;
1338     [self _cacheResourceLoadDelegateImplementations];
1339 }
1340
1341
1342 - resourceLoadDelegate
1343 {
1344     return _private->resourceProgressDelegate;
1345 }
1346
1347
1348 - (void)setDownloadDelegate: delegate
1349 {
1350     _private->downloadDelegate = delegate;
1351 }
1352
1353
1354 - downloadDelegate
1355 {
1356     return _private->downloadDelegate;
1357 }
1358
1359 - (void)setPolicyDelegate:delegate
1360 {
1361     _private->policyDelegate = delegate;
1362     [_private->policyDelegateForwarder release];
1363     _private->policyDelegateForwarder = nil;
1364 }
1365
1366 - policyDelegate
1367 {
1368     return _private->policyDelegate;
1369 }
1370
1371 - (void)setFrameLoadDelegate:delegate
1372 {
1373     _private->frameLoadDelegate = delegate;
1374     [_private->frameLoadDelegateForwarder release];
1375     _private->frameLoadDelegateForwarder = nil;
1376 }
1377
1378 - frameLoadDelegate
1379 {
1380     return _private->frameLoadDelegate;
1381 }
1382
1383 - (WebFrame *)mainFrame
1384 {
1385     // This can be called in initialization, before _private has been set up (3465613)
1386     if (_private != nil) {
1387         return _private->mainFrame;
1388     }
1389     return nil;
1390 }
1391
1392 - (WebBackForwardList *)backForwardList
1393 {
1394     if (_private->useBackForwardList)
1395         return _private->backForwardList;
1396     return nil;
1397 }
1398
1399 - (void)setMaintainsBackForwardList: (BOOL)flag
1400 {
1401     _private->useBackForwardList = flag;
1402 }
1403
1404 - (BOOL)goBack
1405 {
1406     WebHistoryItem *item = [[self backForwardList] backItem];
1407     
1408     if (item){
1409         [self _goToItem: item withLoadType: WebFrameLoadTypeBack];
1410         return YES;
1411     }
1412     return NO;
1413 }
1414
1415 - (BOOL)goForward
1416 {
1417     WebHistoryItem *item = [[self backForwardList] forwardItem];
1418     
1419     if (item){
1420         [self _goToItem: item withLoadType: WebFrameLoadTypeForward];
1421         return YES;
1422     }
1423     return NO;
1424 }
1425
1426 - (BOOL)goToBackForwardItem:(WebHistoryItem *)item
1427 {
1428     [self _goToItem: item withLoadType: WebFrameLoadTypeIndexedBackForward];
1429     return YES;
1430 }
1431
1432 - (void)setTextSizeMultiplier:(float)m
1433 {
1434     if (_private->textSizeMultiplier == m) {
1435         return;
1436     }
1437     _private->textSizeMultiplier = m;
1438     [[self mainFrame] _textSizeMultiplierChanged];
1439 }
1440
1441 - (float)textSizeMultiplier
1442 {
1443     return _private->textSizeMultiplier;
1444 }
1445
1446 - (void)setApplicationNameForUserAgent:(NSString *)applicationName
1447 {
1448     NSString *name = [applicationName copy];
1449     [_private->applicationNameForUserAgent release];
1450     _private->applicationNameForUserAgent = name;
1451     int i;
1452     for (i = 0; i != NumUserAgentStringTypes; ++i) {
1453         [_private->userAgent[i] release];
1454         _private->userAgent[i] = nil;
1455     }
1456 }
1457
1458 - (NSString *)applicationNameForUserAgent
1459 {
1460     return [[_private->applicationNameForUserAgent retain] autorelease];
1461 }
1462
1463 - (void)setCustomUserAgent:(NSString *)userAgentString
1464 {
1465     NSString *override = [userAgentString copy];
1466     [_private->userAgentOverride release];
1467     _private->userAgentOverride = override;
1468 }
1469
1470 - (NSString *)customUserAgent
1471 {
1472     return [[_private->userAgentOverride retain] autorelease];
1473 }
1474
1475 - (BOOL)supportsTextEncoding
1476 {
1477     id documentView = [[[self mainFrame] frameView] documentView];
1478     return [documentView conformsToProtocol:@protocol(WebDocumentText)]
1479         && [documentView supportsTextEncoding];
1480 }
1481
1482 - (void)setCustomTextEncodingName:(NSString *)encoding
1483 {
1484     NSString *oldEncoding = [self customTextEncodingName];
1485     if (encoding == oldEncoding || [encoding isEqualToString:oldEncoding]) {
1486         return;
1487     }
1488     [[self mainFrame] _reloadAllowingStaleDataWithOverrideEncoding:encoding];
1489 }
1490
1491 - (NSString *)_mainFrameOverrideEncoding
1492 {
1493     WebDataSource *dataSource = [[self mainFrame] provisionalDataSource];
1494     if (dataSource == nil) {
1495         dataSource = [[self mainFrame] dataSource];
1496     }
1497     if (dataSource == nil) {
1498         return nil;
1499     }
1500     return [dataSource _overrideEncoding];
1501 }
1502
1503 - (NSString *)customTextEncodingName
1504 {
1505     return [self _mainFrameOverrideEncoding];
1506 }
1507
1508 - (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script
1509 {
1510     return [[[self mainFrame] _bridge] stringByEvaluatingJavaScriptFromString:script];
1511 }
1512
1513 // Get the appropriate user-agent string for a particular URL.
1514 - (NSString *)userAgentForURL:(NSURL *)URL
1515 {
1516     if (_private->userAgentOverride) {
1517         return [[_private->userAgentOverride retain] autorelease];
1518     }
1519     
1520     // Look to see if we need to spoof.
1521     // First step is to get the host as a C-format string.
1522     UserAgentStringType type = Safari;
1523     NSString *host = [URL _web_hostString];
1524     char hostBuffer[256];
1525     if (host && CFStringGetCString((CFStringRef)host, hostBuffer, sizeof(hostBuffer), kCFStringEncodingASCII)) {
1526         // Next step is to find the last part of the name.
1527         // We get the part with only two dots, and the part with only one dot.
1528         const char *thirdToLastPeriod = NULL;
1529         const char *nextToLastPeriod = NULL;
1530         const char *lastPeriod = NULL;
1531         char c;
1532         char *p = hostBuffer;
1533         while ((c = *p)) {
1534             if (c == '.') {
1535                 thirdToLastPeriod = nextToLastPeriod;
1536                 nextToLastPeriod = lastPeriod;
1537                 lastPeriod = p;
1538             } else {
1539                 *p = tolower(c);
1540             }
1541             ++p;
1542         }
1543         // Now look up that last part in the gperf spoof table.
1544         if (lastPeriod) {
1545             const char *lastPart;
1546             const struct UserAgentSpoofTableEntry *entry = NULL;
1547             if (nextToLastPeriod) {
1548                 lastPart = thirdToLastPeriod ? thirdToLastPeriod + 1 : hostBuffer;
1549                 entry = _web_findSpoofTableEntry(lastPart, p - lastPart);
1550             }
1551             if (!entry) {
1552                 lastPart = nextToLastPeriod ? nextToLastPeriod + 1 : hostBuffer;
1553                 entry = _web_findSpoofTableEntry(lastPart, p - lastPart);
1554             }
1555             if (entry) {
1556                 type = entry->type;
1557             }
1558         }
1559     }
1560
1561     NSString **userAgentStorage = &_private->userAgent[type];
1562
1563     NSString *userAgent = *userAgentStorage;
1564     if (userAgent) {
1565         return [[userAgent retain] autorelease];
1566     }
1567     
1568     // FIXME: Some day we will start reporting the actual CPU here instead of hardcoding PPC.
1569
1570     NSString *language = [NSUserDefaults _web_preferredLanguageCode];
1571     id sourceVersion = [[NSBundle bundleForClass:[WebView class]]
1572         objectForInfoDictionaryKey:(id)kCFBundleVersionKey];
1573     NSString *applicationName = _private->applicationNameForUserAgent;
1574
1575     switch (type) {
1576         case Safari:
1577             if ([applicationName length]) {
1578                 userAgent = [NSString stringWithFormat:@"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; %@) AppleWebKit/%@ (KHTML, like Gecko) %@",
1579                     language, sourceVersion, applicationName];
1580             } else {
1581                 userAgent = [NSString stringWithFormat:@"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; %@) AppleWebKit/%@ (KHTML, like Gecko)",
1582                     language, sourceVersion];
1583             }
1584             break;
1585         case MacIE:
1586             if ([applicationName length]) {
1587                 userAgent = [NSString stringWithFormat:@"Mozilla/4.0 (compatible; MSIE 5.2; Mac_PowerPC) AppleWebKit/%@ %@",
1588                     language, sourceVersion, applicationName];
1589             } else {
1590                 userAgent = [NSString stringWithFormat:@"Mozilla/4.0 (compatible; MSIE 5.2; Mac_PowerPC) AppleWebKit/%@",
1591                     language, sourceVersion];
1592             }
1593             break;
1594         case WinIE:
1595             if ([applicationName length]) {
1596                 userAgent = [NSString stringWithFormat:@"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT) AppleWebKit/%@ %@",
1597                     language, sourceVersion, applicationName];
1598             } else {
1599                 userAgent = [NSString stringWithFormat:@"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT) AppleWebKit/%@",
1600                     language, sourceVersion];
1601             }
1602             break;
1603     }
1604
1605     *userAgentStorage = [userAgent retain];
1606     return userAgent;
1607 }
1608
1609 - (void)setHostWindow:(NSWindow *)hostWindow
1610 {
1611     if (hostWindow != _private->hostWindow) {
1612         [[self mainFrame] _viewWillMoveToHostWindow:hostWindow];
1613         [_private->hostWindow release];
1614         _private->hostWindow = [hostWindow retain];
1615         [[self mainFrame] _viewDidMoveToHostWindow];
1616     }
1617 }
1618
1619 - (NSWindow *)hostWindow
1620 {
1621     return _private->hostWindow;
1622 }
1623
1624
1625 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
1626 {
1627     return [self _web_dragOperationForDraggingInfo:sender];
1628 }
1629
1630 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
1631 {
1632     NSPoint point = [[self superview] convertPoint:[sender draggingLocation] toView:nil];
1633     if ([[self hitTest:point] isKindOfClass:[WebBaseNetscapePluginView class]]) {
1634         return NSDragOperationNone;
1635     }
1636     return [self _web_dragOperationForDraggingInfo:sender];
1637 }
1638
1639 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
1640 {
1641     return YES;
1642 }
1643
1644 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
1645 {
1646     return YES;
1647 }
1648
1649 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
1650 {
1651     NSURL *URL = [[sender draggingPasteboard] _web_bestURL];
1652
1653     if (URL) {
1654         NSURLRequest *request = [[NSURLRequest alloc] initWithURL:URL];
1655         [[self mainFrame] loadRequest:request];
1656         [request release];
1657     }
1658 }
1659
1660 - (BOOL)acceptsFirstResponder
1661 {
1662     return [[[self mainFrame] frameView] acceptsFirstResponder];
1663 }
1664
1665 - (BOOL)becomeFirstResponder
1666 {
1667     // This works together with setNextKeyView to splice the WebView into
1668     // the key loop similar to the way NSScrollView does this. Note that
1669     // WebFrameView has very similar code.
1670     NSWindow *window = [self window];
1671     WebFrameView *mainFrameView = [[self mainFrame] frameView];
1672     
1673     if ([window keyViewSelectionDirection] == NSSelectingPrevious) {
1674         NSView *previousValidKeyView = [self previousValidKeyView];
1675         if ((previousValidKeyView != self) && (previousValidKeyView != mainFrameView)) {
1676             [window makeFirstResponder:previousValidKeyView];
1677             return YES;
1678         } else {
1679             return NO;
1680         }
1681     }
1682     
1683     if ([mainFrameView acceptsFirstResponder]) {
1684         [window makeFirstResponder:mainFrameView];
1685         return YES;
1686     } 
1687     
1688     return NO;
1689 }
1690
1691 - (void)setNextKeyView:(NSView *)aView
1692 {
1693     // This works together with becomeFirstResponder to splice the WebView into
1694     // the key loop similar to the way NSScrollView does this. Note that
1695     // WebFrameView has very similar code.
1696     WebFrameView *mainFrameView = [[self mainFrame] frameView];
1697     if (mainFrameView != nil) {
1698         [mainFrameView setNextKeyView:aView];
1699     } else {
1700         [super setNextKeyView:aView];
1701     }
1702 }
1703
1704 // Return the frame holding first responder
1705 - (WebFrame *)_currentFrame
1706 {
1707     // Find the frame holding the first responder, or holding the first form in the doc
1708     NSResponder *resp = [[self window] firstResponder];
1709     if (!resp || ![resp isKindOfClass:[NSView class]] || ![(NSView *)resp isDescendantOf:self]) {
1710         return nil;     // first responder outside our view tree
1711     } else {
1712         WebFrameView *frameView = (WebFrameView *)[(NSView *)resp _web_superviewOfClass:[WebFrameView class]];
1713         return [frameView webFrame];
1714     }
1715 }
1716
1717 static WebFrame *incrementFrame(WebFrame *curr, BOOL forward, BOOL wrapFlag)
1718 {
1719     return forward ? [curr _nextFrameWithWrap:wrapFlag]
1720                    : [curr _previousFrameWithWrap:wrapFlag];
1721 }
1722
1723 // Search from the end of the currently selected location, or from the beginning of the
1724 // document if nothing is selected.  Deals with subframes.
1725 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
1726 {
1727     // Get the frame holding the selection, or start with the main frame
1728     WebFrame *startFrame = [self _currentFrame];
1729     if (!startFrame) {
1730         startFrame = [self mainFrame];
1731     }
1732
1733     // Search the first frame, then all the other frames, in order
1734     NSView <WebDocumentSearching> *startSearchView = nil;
1735     BOOL startHasSelection = NO;
1736     WebFrame *frame = startFrame;
1737     do {
1738         id <WebDocumentView> view = [[frame frameView] documentView];
1739         if ([view conformsToProtocol:@protocol(WebDocumentSearching)]) {
1740             NSView <WebDocumentSearching> *searchView = (NSView <WebDocumentSearching> *)view;
1741
1742             // first time through
1743             if (frame == startFrame) {
1744                 // Remember if start even has a selection, to know if we need to search more later
1745                 if ([searchView isKindOfClass:[WebHTMLView class]]) {
1746                     // optimization for the common case, to avoid making giant string for selection
1747                     startHasSelection = [[startFrame _bridge] selectionStart] != nil;
1748                 } else if ([searchView conformsToProtocol:@protocol(WebDocumentText)]) {
1749                     startHasSelection = [(id <WebDocumentText>)searchView selectedString] != nil;
1750                 }
1751                 startSearchView = searchView;
1752             }
1753             
1754             // Note at this point we are assuming the search will be done top-to-bottom,
1755             // not starting at any selection that exists.  See 3228554.
1756             BOOL success = [searchView searchFor:string direction:forward caseSensitive:caseFlag wrap:NO];
1757             if (success) {
1758                 [[self window] makeFirstResponder:searchView];
1759                 return YES;
1760             }
1761         }
1762         frame = incrementFrame(frame, forward, wrapFlag);
1763     } while (frame != nil && frame != startFrame);
1764
1765     // Search contents of startFrame, on the other side of the selection that we did earlier.
1766     // We cheat a bit and just research with wrap on
1767     if (wrapFlag && startHasSelection && startSearchView) {
1768         BOOL success = [startSearchView searchFor:string direction:forward caseSensitive:caseFlag wrap:YES];
1769         if (success) {
1770             [[self window] makeFirstResponder:startSearchView];
1771             return YES;
1772         }
1773     }
1774     return NO;
1775 }
1776
1777 + (void)registerViewClass:(Class)viewClass representationClass:(Class)representationClass forMIMEType:(NSString *)MIMEType
1778 {
1779     [[WebFrameView _viewTypesAllowImageTypeOmission:YES] setObject:viewClass forKey:MIMEType];
1780     [[WebDataSource _repTypesAllowImageTypeOmission:YES] setObject:representationClass forKey:MIMEType];
1781 }
1782
1783 - (void)setGroupName:(NSString *)groupName
1784 {
1785     if (groupName != _private->setName){
1786         [_private->setName release];
1787         _private->setName = [groupName copy];
1788         [WebViewSets addWebView:self toSetNamed:_private->setName];
1789     }
1790 }
1791
1792 - (NSString *)groupName
1793 {
1794     return _private->setName;
1795 }
1796
1797 - (double)estimatedProgress
1798 {
1799     return _private->progressValue;
1800 }
1801
1802 @end
1803
1804
1805 @implementation WebView (WebIBActions)
1806
1807 - (IBAction)takeStringURLFrom: sender
1808 {
1809     NSString *URLString = [sender stringValue];
1810     
1811     [[self mainFrame] loadRequest: [NSURLRequest requestWithURL: [NSURL _web_URLWithDataAsString: URLString]]];
1812 }
1813
1814 - (BOOL)canGoBack
1815 {
1816     return [[self backForwardList] backItem] != nil;
1817 }
1818
1819 - (BOOL)canGoForward
1820 {
1821     return [[self backForwardList] forwardItem] != nil;
1822 }
1823
1824 - (IBAction)goBack:(id)sender
1825 {
1826     [self goBack];
1827 }
1828
1829 - (IBAction)goForward:(id)sender
1830 {
1831     [self goForward];
1832 }
1833
1834 - (IBAction)stopLoading:(id)sender
1835 {
1836     [[self mainFrame] stopLoading];
1837 }
1838
1839 - (IBAction)reload:(id)sender
1840 {
1841     [[self mainFrame] reload];
1842 }
1843
1844 #define MinimumTextSizeMultiplier       0.5
1845 #define MaximumTextSizeMultiplier       3.0
1846 #define TextSizeMultiplierRatio         1.2
1847
1848 - (BOOL)canMakeTextSmaller
1849 {
1850     if ([[self mainFrame] dataSource] == nil) {
1851         return NO;
1852     }
1853     // FIXME: This will prevent text sizing in subframes if the main frame doesn't support it
1854     if (![[[[self mainFrame] frameView] documentView] conformsToProtocol:@protocol(_web_WebDocumentTextSizing)]) {
1855         return NO;
1856     }
1857     if ([self textSizeMultiplier]/TextSizeMultiplierRatio < MinimumTextSizeMultiplier) {
1858         return NO;
1859     }
1860     return YES;
1861 }
1862
1863 - (BOOL)canMakeTextLarger
1864 {
1865     if ([[self mainFrame] dataSource] == nil) {
1866         return NO;
1867     }
1868     // FIXME: This will prevent text sizing in subframes if the main frame doesn't support it
1869     if (![[[[self mainFrame] frameView] documentView] conformsToProtocol:@protocol(_web_WebDocumentTextSizing)]) {
1870         return NO;
1871     }
1872     if ([self textSizeMultiplier]*TextSizeMultiplierRatio > MaximumTextSizeMultiplier) {
1873         return NO;
1874     }
1875     return YES;
1876 }
1877
1878 - (IBAction)makeTextSmaller:(id)sender
1879 {
1880     if (![self canMakeTextSmaller]) {
1881         return;
1882     }
1883     [self setTextSizeMultiplier:[self textSizeMultiplier]/TextSizeMultiplierRatio];
1884 }
1885
1886 - (IBAction)makeTextLarger:(id)sender
1887 {
1888     if (![self canMakeTextLarger]) {
1889         return;
1890     }
1891     [self setTextSizeMultiplier:[self textSizeMultiplier]*TextSizeMultiplierRatio];
1892 }
1893
1894 - (BOOL)_isLoading
1895 {
1896     WebFrame *mainFrame = [self mainFrame];
1897     return [[mainFrame dataSource] isLoading]
1898         || [[mainFrame provisionalDataSource] isLoading];
1899 }
1900
1901 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
1902 {
1903     SEL action = [item action];
1904
1905     if (action == @selector(goBack:)) {
1906         return [self canGoBack];
1907     } else if (action == @selector(goForward:)) {
1908         return [self canGoForward];
1909     } else if (action == @selector(makeTextLarger:)) {
1910         return [self canMakeTextLarger];
1911     } else if (action == @selector(makeTextSmaller:)) {
1912         return [self canMakeTextSmaller];
1913     } else if (action == @selector(reload:)) {
1914         return [[self mainFrame] dataSource] != nil;
1915     } else if (action == @selector(stopLoading:)) {
1916         return [self _isLoading];
1917     }
1918
1919     return YES;
1920 }
1921
1922
1923 @end
1924
1925
1926 @implementation WebView (WebPendingPublic)
1927
1928 - (void)setMainFrameURL:(NSString *)URLString
1929 {
1930     [[self mainFrame] loadRequest: [NSURLRequest requestWithURL: [NSURL _web_URLWithDataAsString: URLString]]];
1931 }
1932
1933 - (NSString *)mainFrameURL
1934 {
1935     WebDataSource *ds;
1936     ds = [[self mainFrame] provisionalDataSource];
1937     if (!ds)
1938         ds = [[self mainFrame] dataSource];
1939     return [[[ds request] URL] _web_originalDataAsString];
1940 }
1941
1942 - (BOOL)isLoading
1943 {
1944     LOG (Bindings, "isLoading = %d", (int)[self _isLoading]);
1945     return [self _isLoading];
1946 }
1947
1948 - (NSString *)mainFrameTitle
1949 {
1950     NSString *mainFrameTitle = [[[self mainFrame] dataSource] pageTitle];
1951     return (mainFrameTitle != nil) ? mainFrameTitle : @"";
1952 }
1953
1954 - (NSImage *)mainFrameIcon
1955 {
1956     return [[WebIconDatabase sharedIconDatabase] iconForURL:[[[[self mainFrame] dataSource] _URL] _web_originalDataAsString] withSize:WebIconSmallSize];
1957 }
1958
1959 @end
1960
1961 @implementation WebView (WebViewPrintingPrivate)
1962
1963 - (float)_headerHeight
1964 {
1965     if ([[self UIDelegate] respondsToSelector:@selector(webViewHeaderHeight:)]) {
1966         return [[self UIDelegate] webViewHeaderHeight:self];
1967     }
1968     
1969 #ifdef DEBUG_HEADER_AND_FOOTER
1970     return 25;
1971 #else
1972     return 0;
1973 #endif
1974 }
1975
1976 - (float)_footerHeight
1977 {
1978     if ([[self UIDelegate] respondsToSelector:@selector(webViewFooterHeight:)]) {
1979         return [[self UIDelegate] webViewFooterHeight:self];
1980     }
1981     
1982 #ifdef DEBUG_HEADER_AND_FOOTER
1983     return 50;
1984 #else
1985     return 0;
1986 #endif
1987 }
1988
1989 - (void)_drawHeaderInRect:(NSRect)rect
1990 {
1991 #ifdef DEBUG_HEADER_AND_FOOTER
1992     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
1993     [currentContext saveGraphicsState];
1994     [[NSColor yellowColor] set];
1995     NSRectFill(rect);
1996     [currentContext restoreGraphicsState];
1997 #endif
1998     
1999     if ([[self UIDelegate] respondsToSelector:@selector(webView:drawHeaderInRect:)]) {
2000         NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
2001         [currentContext saveGraphicsState];
2002         NSRectClip(rect);
2003         [[self UIDelegate] webView:self drawHeaderInRect:rect]; 
2004         [currentContext restoreGraphicsState];
2005     }
2006 }
2007
2008 - (void)_drawFooterInRect:(NSRect)rect
2009 {
2010 #ifdef DEBUG_HEADER_AND_FOOTER
2011     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
2012     [currentContext saveGraphicsState];
2013     [[NSColor cyanColor] set];
2014     NSRectFill(rect);
2015     [currentContext restoreGraphicsState];
2016 #endif
2017     
2018     if ([[self UIDelegate] respondsToSelector:@selector(webView:drawFooterInRect:)]) {
2019         NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
2020         [currentContext saveGraphicsState];
2021         NSRectClip(rect);
2022         [[self UIDelegate] webView:self drawFooterInRect:rect];
2023         [currentContext restoreGraphicsState];
2024     }
2025 }
2026
2027 - (void)_adjustPrintingMarginsForHeaderAndFooter
2028 {
2029     NSPrintOperation *op = [NSPrintOperation currentOperation];
2030     NSPrintInfo *info = [op printInfo];
2031     float scale = [op _web_pageSetupScaleFactor];
2032     [info setTopMargin:[info topMargin] + [self _headerHeight]*scale];
2033     [info setBottomMargin:[info bottomMargin] + [self _footerHeight]*scale];
2034 }
2035
2036 - (void)_drawHeaderAndFooter
2037 {
2038     // The header and footer rect height scales with the page, but the width is always
2039     // all the way across the printed page (inset by printing margins).
2040     NSPrintOperation *op = [NSPrintOperation currentOperation];
2041     float scale = [op _web_pageSetupScaleFactor];
2042     NSPrintInfo *printInfo = [op printInfo];
2043     NSSize paperSize = [printInfo paperSize];
2044     float headerFooterLeft = [printInfo leftMargin]/scale;
2045     float headerFooterWidth = (paperSize.width - ([printInfo leftMargin] + [printInfo rightMargin]))/scale;
2046     NSRect footerRect = NSMakeRect(headerFooterLeft, [printInfo bottomMargin]/scale - [self _footerHeight] , 
2047                                    headerFooterWidth, [self _footerHeight]);
2048     NSRect headerRect = NSMakeRect(headerFooterLeft, (paperSize.height - [printInfo topMargin])/scale, 
2049                                    headerFooterWidth, [self _headerHeight]);
2050     
2051     [self _drawHeaderInRect:headerRect];
2052     [self _drawFooterInRect:footerRect];
2053 }
2054 @end
2055
2056 @implementation WebView (WebDebugBinding)
2057
2058 - (void)addObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
2059 {
2060     LOG (Bindings, "addObserver:%p forKeyPath:%@ options:%x context:%p", anObserver, keyPath, options, context);
2061     [super addObserver:anObserver forKeyPath:keyPath options:options context:context];
2062 }
2063
2064 - (void)removeObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath
2065 {
2066     LOG (Bindings, "removeObserver:%p forKeyPath:%@", anObserver, keyPath);
2067     [super removeObserver:anObserver forKeyPath:keyPath];
2068 }
2069
2070 @end
2071