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