e5b385c13a5c2f9d0044e6a3e71a5e388a055137
[WebKit-https.git] / WebKit / WebView / WebView.m
1 /*
2  * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #import "WebViewInternal.h"
30
31 #import <JavaScriptCore/Assertions.h>
32 #import "WebBackForwardList.h"
33 #import "WebBaseNetscapePluginView.h"
34 #import "WebDOMOperationsPrivate.h"
35 #import "WebDashboardRegion.h"
36 #import "WebDataProtocol.h"
37 #import "WebDataSourceInternal.h"
38 #import "WebDefaultEditingDelegate.h"
39 #import "WebDefaultFrameLoadDelegate.h"
40 #import "WebDefaultPolicyDelegate.h"
41 #import "WebDefaultResourceLoadDelegate.h"
42 #import "WebDefaultScriptDebugDelegate.h"
43 #import "WebDefaultUIDelegate.h"
44 #import "WebDocument.h"
45 #import "WebDocumentInternal.h"
46 #import "WebDownload.h"
47 #import "WebDownloadInternal.h"
48 #import "WebDynamicScrollBarsView.h"
49 #import "WebEditingDelegate.h"
50 #import "WebFormDelegatePrivate.h"
51 #import "WebFrameBridge.h"
52 #import "WebFrameInternal.h"
53 #import "WebFramePrivate.h"
54 #import "WebFrameViewInternal.h"
55 #import "WebHTMLRepresentation.h"
56 #import "WebHTMLViewInternal.h"
57 #import "WebHistoryItemPrivate.h"
58 #import "WebIconDatabase.h"
59 #import "WebInspector.h"
60 #import "WebKitErrors.h"
61 #import "WebKitLogging.h"
62 #import "WebKitNSStringExtras.h"
63 #import "WebKitStatisticsPrivate.h"
64 #import "WebLocalizableStrings.h"
65 #import "WebNSDataExtras.h"
66 #import "WebNSDataExtrasPrivate.h"
67 #import "WebNSDictionaryExtras.h"
68 #import "WebNSEventExtras.h"
69 #import "WebNSObjectExtras.h"
70 #import "WebNSPasteboardExtras.h"
71 #import "WebNSPrintOperationExtras.h"
72 #import "WebNSURLExtras.h"
73 #import "WebNSURLRequestExtras.h"
74 #import "WebNSUserDefaultsExtras.h"
75 #import "WebNSViewExtras.h"
76 #import "WebPageBridge.h"
77 #import "WebPluginDatabase.h"
78 #import "WebPolicyDelegate.h"
79 #import "WebPreferencesPrivate.h"
80 #import "WebResourceLoadDelegate.h"
81 #import "WebScriptDebugDelegatePrivate.h"
82 #import "WebScriptDebugServerPrivate.h"
83 #import "WebUIDelegate.h"
84 #import "WebUIDelegatePrivate.h"
85 #import <CoreFoundation/CFSet.h>
86 #import <Foundation/NSURLConnection.h>
87 #import <WebCore/WebCoreEncodings.h>
88 #import <WebCore/WebCoreFrameBridge.h>
89 #import <WebCore/WebCoreSettings.h>
90 #import <WebCore/WebCoreView.h>
91 #import <WebKit/DOM.h>
92 #import <WebKit/DOMPrivate.h>
93 #import <WebKit/DOMExtensions.h>
94 #import <WebKitSystemInterface.h>
95 #import <objc/objc-runtime.h>
96
97 #import <WebCore/WebCoreTextRenderer.h>
98
99 #if __ppc__ || __ppc64__
100 #define PROCESSOR "PPC"
101 #elif __i386__ || __x86_64__
102 #define PROCESSOR "Intel"
103 #else
104 #error Unknown architecture
105 #endif
106
107 #define FOR_EACH_RESPONDER_SELECTOR(macro) \
108 macro(alignCenter) \
109 macro(alignJustified) \
110 macro(alignLeft) \
111 macro(alignRight) \
112 macro(capitalizeWord) \
113 macro(centerSelectionInVisibleArea) \
114 macro(changeAttributes) \
115 macro(changeColor) \
116 macro(changeDocumentBackgroundColor) \
117 macro(changeFont) \
118 macro(checkSpelling) \
119 macro(complete) \
120 macro(copy) \
121 macro(copyFont) \
122 macro(cut) \
123 macro(delete) \
124 macro(deleteBackward) \
125 macro(deleteBackwardByDecomposingPreviousCharacter) \
126 macro(deleteForward) \
127 macro(deleteToBeginningOfLine) \
128 macro(deleteToBeginningOfParagraph) \
129 macro(deleteToEndOfLine) \
130 macro(deleteToEndOfParagraph) \
131 macro(deleteWordBackward) \
132 macro(deleteWordForward) \
133 macro(ignoreSpelling) \
134 macro(indent) \
135 macro(insertBacktab) \
136 macro(insertNewline) \
137 macro(insertNewlineIgnoringFieldEditor) \
138 macro(insertParagraphSeparator) \
139 macro(insertTab) \
140 macro(insertTabIgnoringFieldEditor) \
141 macro(lowercaseWord) \
142 macro(moveBackward) \
143 macro(moveBackwardAndModifySelection) \
144 macro(moveDown) \
145 macro(moveDownAndModifySelection) \
146 macro(moveForward) \
147 macro(moveForwardAndModifySelection) \
148 macro(moveLeft) \
149 macro(moveLeftAndModifySelection) \
150 macro(moveRight) \
151 macro(moveRightAndModifySelection) \
152 macro(moveToBeginningOfDocument) \
153 macro(moveToBeginningOfDocumentAndModifySelection) \
154 macro(moveToBeginningOfSentence) \
155 macro(moveToBeginningOfSentenceAndModifySelection) \
156 macro(moveToBeginningOfLine) \
157 macro(moveToBeginningOfLineAndModifySelection) \
158 macro(moveToBeginningOfParagraph) \
159 macro(moveToBeginningOfParagraphAndModifySelection) \
160 macro(moveToEndOfDocument) \
161 macro(moveToEndOfDocumentAndModifySelection) \
162 macro(moveToEndOfLine) \
163 macro(moveToEndOfLineAndModifySelection) \
164 macro(moveToEndOfParagraph) \
165 macro(moveToEndOfParagraphAndModifySelection) \
166 macro(moveToEndOfSentence) \
167 macro(moveToEndOfSentenceAndModifySelection) \
168 macro(moveUp) \
169 macro(moveUpAndModifySelection) \
170 macro(moveWordBackward) \
171 macro(moveWordBackwardAndModifySelection) \
172 macro(moveWordForward) \
173 macro(moveWordForwardAndModifySelection) \
174 macro(moveWordLeft) \
175 macro(moveWordLeftAndModifySelection) \
176 macro(moveWordRight) \
177 macro(moveWordRightAndModifySelection) \
178 macro(pageDown) \
179 macro(pageUp) \
180 macro(paste) \
181 macro(pasteAsPlainText) \
182 macro(pasteAsRichText) \
183 macro(pasteFont) \
184 macro(performFindPanelAction) \
185 macro(scrollLineDown) \
186 macro(scrollLineUp) \
187 macro(scrollPageDown) \
188 macro(scrollPageUp) \
189 macro(scrollToBeginningOfDocument) \
190 macro(scrollToEndOfDocument) \
191 macro(selectAll) \
192 macro(selectWord) \
193 macro(selectSentence) \
194 macro(selectLine) \
195 macro(selectParagraph) \
196 macro(showGuessPanel) \
197 macro(startSpeaking) \
198 macro(stopSpeaking) \
199 macro(subscript) \
200 macro(superscript) \
201 macro(underline) \
202 macro(unscript) \
203 macro(uppercaseWord) \
204 macro(yank) \
205 macro(yankAndSelect) \
206
207 @interface NSSpellChecker (AppKitSecretsIKnow)
208 - (void)_preflightChosenSpellServer;
209 @end
210
211 @interface NSView (AppKitSecretsIKnow)
212 - (NSView *)_hitTest:(NSPoint *)aPoint dragTypes:(NSSet *)types;
213 - (void)_autoscrollForDraggingInfo:(id)dragInfo timeDelta:(NSTimeInterval)repeatDelta;
214 - (BOOL)_shouldAutoscrollForDraggingInfo:(id)dragInfo;
215 @end
216
217 @interface WebViewPrivate : NSObject
218 {
219 @public
220     WebPageBridge *_pageBridge;
221     
222     id UIDelegate;
223     id UIDelegateForwarder;
224     id resourceProgressDelegate;
225     id resourceProgressDelegateForwarder;
226     id downloadDelegate;
227     id policyDelegate;
228     id policyDelegateForwarder;
229     id frameLoadDelegate;
230     id frameLoadDelegateForwarder;
231     id <WebFormDelegate> formDelegate;
232     id editingDelegate;
233     id editingDelegateForwarder;
234     id scriptDebugDelegate;
235     id scriptDebugDelegateForwarder;
236     
237     WebBackForwardList *backForwardList;
238     BOOL useBackForwardList;
239     
240     float textSizeMultiplier;
241
242     NSString *applicationNameForUserAgent;
243     NSString *userAgent;
244     BOOL userAgentOverridden;
245     
246     BOOL defersCallbacks;
247
248     WebPreferences *preferences;
249     WebCoreSettings *settings;
250         
251     BOOL lastElementWasNonNil;
252
253     NSWindow *hostWindow;
254
255     int programmaticFocusCount;
256     
257     WebResourceDelegateImplementationCache resourceLoadDelegateImplementations;
258
259     long long totalPageAndResourceBytesToLoad;
260     long long totalBytesReceived;
261     double progressValue;
262     double lastNotifiedProgressValue;
263     double lastNotifiedProgressTime;
264     double progressNotificationInterval;
265     double progressNotificationTimeInterval;
266     BOOL finalProgressChangedSent;
267     WebFrame *originatingProgressFrame;
268     
269     int numProgressTrackedFrames;
270     NSMutableDictionary *progressItems;
271     
272     void *observationInfo;
273     
274     BOOL mainFrameDocumentReady;
275     BOOL drawsBackground;
276     BOOL editable;
277     BOOL initiatedDrag;
278         
279     NSString *mediaStyle;
280     
281     NSView <WebDocumentDragging> *draggingDocumentView;
282     unsigned int dragDestinationActionMask;
283     
284     BOOL hasSpellCheckerDocumentTag;
285     WebNSInteger spellCheckerDocumentTag;
286
287     BOOL continuousSpellCheckingEnabled;
288     BOOL smartInsertDeleteEnabled;
289     
290     BOOL dashboardBehaviorAlwaysSendMouseEventsToAllWindows;
291     BOOL dashboardBehaviorAlwaysSendActiveNullEventsToPlugIns;
292     BOOL dashboardBehaviorAlwaysAcceptsFirstMouse;
293     BOOL dashboardBehaviorAllowWheelScrolling;
294     
295     BOOL selectWordBeforeMenuEvent;
296     
297     WebPluginDatabase *pluginDatabase;
298 }
299 @end
300
301 @interface WebView (WebFileInternal)
302 - (WebFrame *)_selectedOrMainFrame;
303 - (WebFrameBridge *)_bridgeForSelectedOrMainFrame;
304 - (BOOL)_isLoading;
305 - (WebFrameView *)_frameViewAtWindowPoint:(NSPoint)point;
306 - (WebFrameBridge *)_bridgeAtPoint:(NSPoint)point;
307 - (WebFrame *)_focusedFrame;
308 + (void)_preflightSpellChecker;
309 - (BOOL)_continuousCheckingAllowed;
310 - (NSResponder *)_responderForResponderOperations;
311 - (BOOL)_performTextSizingSelector:(SEL)sel withObject:(id)arg onTrackingDocs:(BOOL)doTrackingViews selForNonTrackingDocs:(SEL)testSel newScaleFactor:(float)newScaleFactor;
312 @end
313
314 NSString *WebElementDOMNodeKey =            @"WebElementDOMNode";
315 NSString *WebElementFrameKey =              @"WebElementFrame";
316 NSString *WebElementImageKey =              @"WebElementImage";
317 NSString *WebElementImageAltStringKey =     @"WebElementImageAltString";
318 NSString *WebElementImageRectKey =          @"WebElementImageRect";
319 NSString *WebElementImageURLKey =           @"WebElementImageURL";
320 NSString *WebElementIsSelectedKey =         @"WebElementIsSelected";
321 NSString *WebElementTitleKey =              @"WebElementTitle";
322 NSString *WebElementLinkURLKey =            @"WebElementLinkURL";
323 NSString *WebElementLinkTargetFrameKey =    @"WebElementTargetFrame";
324 NSString *WebElementLinkLabelKey =          @"WebElementLinkLabel";
325 NSString *WebElementLinkTitleKey =          @"WebElementLinkTitle";
326
327 NSString *WebViewProgressStartedNotification =          @"WebProgressStartedNotification";
328 NSString *WebViewProgressEstimateChangedNotification =  @"WebProgressEstimateChangedNotification";
329 NSString *WebViewProgressFinishedNotification =         @"WebProgressFinishedNotification";
330
331 NSString * const WebViewDidBeginEditingNotification =         @"WebViewDidBeginEditingNotification";
332 NSString * const WebViewDidChangeNotification =               @"WebViewDidChangeNotification";
333 NSString * const WebViewDidEndEditingNotification =           @"WebViewDidEndEditingNotification";
334 NSString * const WebViewDidChangeTypingStyleNotification =    @"WebViewDidChangeTypingStyleNotification";
335 NSString * const WebViewDidChangeSelectionNotification =      @"WebViewDidChangeSelectionNotification";
336
337 enum { WebViewVersion = 2 };
338
339 #define timedLayoutSize 4096
340
341 static NSMutableSet *schemesWithRepresentationsSet;
342
343 NSString *_WebCanGoBackKey =            @"canGoBack";
344 NSString *_WebCanGoForwardKey =         @"canGoForward";
345 NSString *_WebEstimatedProgressKey =    @"estimatedProgress";
346 NSString *_WebIsLoadingKey =            @"isLoading";
347 NSString *_WebMainFrameIconKey =        @"mainFrameIcon";
348 NSString *_WebMainFrameTitleKey =       @"mainFrameTitle";
349 NSString *_WebMainFrameURLKey =         @"mainFrameURL";
350 NSString *_WebMainFrameDocumentKey =    @"mainFrameDocument";
351
352 @interface WebProgressItem : NSObject
353 {
354 @public
355     long long bytesReceived;
356     long long estimatedLength;
357 }
358 @end
359
360 @implementation WebProgressItem
361 @end
362
363 @implementation WebViewPrivate
364
365 - init 
366 {
367     self = [super init];
368     if (!self)
369         return nil;
370     
371     backForwardList = [[WebBackForwardList alloc] init];
372     textSizeMultiplier = 1;
373     progressNotificationInterval = 0.02;
374     progressNotificationTimeInterval = 0.1;
375     settings = [[WebCoreSettings alloc] init];
376     dashboardBehaviorAllowWheelScrolling = YES;
377
378     return self;
379 }
380
381 - (void)dealloc
382 {
383     ASSERT(!_pageBridge);
384     ASSERT(draggingDocumentView == nil);
385     
386     [backForwardList release];
387     [applicationNameForUserAgent release];
388     [userAgent release];
389     
390     [preferences release];
391     [settings release];
392     [hostWindow release];
393     
394     [policyDelegateForwarder release];
395     [resourceProgressDelegateForwarder release];
396     [UIDelegateForwarder release];
397     [frameLoadDelegateForwarder release];
398     [editingDelegateForwarder release];
399     [scriptDebugDelegateForwarder release];
400     
401     [progressItems release];
402         
403     [mediaStyle release];
404     
405     [super dealloc];
406 }
407
408 @end
409
410 @implementation WebView (AllWebViews)
411
412 static CFSetCallBacks NonRetainingSetCallbacks = {
413     0,
414     NULL,
415     NULL,
416     CFCopyDescription,
417     CFEqual,
418     CFHash
419 };
420
421 static CFMutableSetRef allWebViewsSet;
422
423 + (void)_makeAllWebViewsPerformSelector:(SEL)selector
424 {
425     if (!allWebViewsSet)
426         return;
427
428     [(NSMutableSet *)allWebViewsSet makeObjectsPerformSelector:selector];
429 }
430
431 - (void)_removeFromAllWebViewsSet
432 {
433     if (allWebViewsSet)
434         CFSetRemoveValue(allWebViewsSet, self);
435 }
436
437 - (void)_addToAllWebViewsSet
438 {
439     if (!allWebViewsSet)
440         allWebViewsSet = CFSetCreateMutable(NULL, 0, &NonRetainingSetCallbacks);
441
442     CFSetSetValue(allWebViewsSet, self);
443 }
444
445 @end
446
447 @implementation WebView (WebPrivate)
448
449 #ifdef DEBUG_WIDGET_DRAWING
450 static bool debugWidget = true;
451 - (void)drawRect:(NSRect)rect
452 {
453     [[NSColor blueColor] set];
454     NSRectFill (rect);
455     
456     NSRect htmlViewRect = [[[[self mainFrame] frameView] documentView] frame];
457
458     if (debugWidget) {
459         while (debugWidget) {
460             sleep (1);
461         }
462     }
463
464     NSLog (@"%s:   rect:  (%0.f,%0.f) %0.f %0.f, htmlViewRect:  (%0.f,%0.f) %0.f %0.f\n", 
465         __PRETTY_FUNCTION__, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height,
466         htmlViewRect.origin.x, htmlViewRect.origin.y, htmlViewRect.size.width, htmlViewRect.size.height
467         );
468
469     [super drawRect:rect];
470 }
471 #endif
472
473 + (BOOL)_developerExtrasEnabled
474 {
475 #ifdef NDEBUG
476     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
477     BOOL enableDebugger = [defaults boolForKey:@"WebKitDeveloperExtras"];
478     if (!enableDebugger)
479         enableDebugger = [defaults boolForKey:@"IncludeDebugMenu"];
480     return enableDebugger;
481 #else
482     return YES; // always enable in debug builds
483 #endif
484 }
485
486 + (BOOL)_scriptDebuggerEnabled
487 {
488 #ifdef NDEBUG
489     return [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitScriptDebuggerEnabled"];
490 #else
491     return YES; // always enable in debug builds
492 #endif
493 }
494
495 + (NSArray *)_supportedMIMETypes
496 {
497     // Load the plug-in DB allowing plug-ins to install types.
498     [WebPluginDatabase installedPlugins];
499     return [[WebFrameView _viewTypesAllowImageTypeOmission:NO] allKeys];
500 }
501
502 + (NSArray *)_supportedFileExtensions
503 {
504     NSMutableSet *extensions = [[NSMutableSet alloc] init];
505     NSArray *MIMETypes = [self _supportedMIMETypes];
506     NSEnumerator *enumerator = [MIMETypes objectEnumerator];
507     NSString *MIMEType;
508     while ((MIMEType = [enumerator nextObject]) != nil) {
509         NSArray *extensionsForType = WKGetExtensionsForMIMEType(MIMEType);
510         if (extensionsForType) {
511             [extensions addObjectsFromArray:extensionsForType];
512         }
513     }
514     NSArray *uniqueExtensions = [extensions allObjects];
515     [extensions release];
516     return uniqueExtensions;
517 }
518
519 + (BOOL)_viewClass:(Class *)vClass andRepresentationClass:(Class *)rClass forMIMEType:(NSString *)MIMEType;
520 {
521     MIMEType = [MIMEType lowercaseString];
522     Class viewClass = [[WebFrameView _viewTypesAllowImageTypeOmission:YES] _webkit_objectForMIMEType:MIMEType];
523     Class repClass = [[WebDataSource _repTypesAllowImageTypeOmission:YES] _webkit_objectForMIMEType:MIMEType];
524     
525     if (!viewClass || !repClass) {
526         // Our optimization to avoid loading the plug-in DB and image types for the HTML case failed.
527         // Load the plug-in DB allowing plug-ins to install types.
528         [WebPluginDatabase installedPlugins];
529             
530         // Load the image types and get the view class and rep class. This should be the fullest picture of all handled types.
531         viewClass = [[WebFrameView _viewTypesAllowImageTypeOmission:NO] _webkit_objectForMIMEType:MIMEType];
532         repClass = [[WebDataSource _repTypesAllowImageTypeOmission:NO] _webkit_objectForMIMEType:MIMEType];
533     }
534     
535     if (viewClass && repClass) {
536         // Special-case WebHTMLView for text types that shouldn't be shown.
537         if (viewClass == [WebHTMLView class] &&
538             repClass == [WebHTMLRepresentation class] &&
539             [[WebHTMLView unsupportedTextMIMETypes] containsObject:MIMEType]) {
540             return NO;
541         }
542         if (vClass)
543             *vClass = viewClass;
544         if (rClass)
545             *rClass = repClass;
546         return YES;
547     }
548     
549     return NO;
550 }
551
552 - (BOOL)_viewClass:(Class *)vClass andRepresentationClass:(Class *)rClass forMIMEType:(NSString *)MIMEType;
553 {
554     if ([[self class] _viewClass:vClass andRepresentationClass:rClass forMIMEType:MIMEType])
555         return YES;
556
557     if (_private->pluginDatabase) {
558         WebBasePluginPackage *pluginPackage = [_private->pluginDatabase pluginForMIMEType:MIMEType];
559         if (pluginPackage) {
560             if (vClass)
561                 *vClass = [WebHTMLView class];
562             if (rClass)
563                 *rClass = [WebHTMLRepresentation class];
564             return YES;
565         }
566     }
567     
568     return NO;
569 }
570
571 + (void)_setAlwaysUseATSU:(BOOL)f
572 {
573     WebCoreSetAlwaysUseATSU(f);
574 }
575
576 + (BOOL)canShowFile:(NSString *)path
577 {
578     return [[self class] canShowMIMEType:[WebView _MIMETypeForFile:path]];
579 }
580
581 + (NSString *)suggestedFileExtensionForMIMEType:(NSString *)type
582 {
583     return WKGetPreferredExtensionForMIMEType(type);
584 }
585
586 - (void)_close
587 {
588     [self _removeFromAllWebViewsSet];
589     [self setGroupName:nil];
590
591     // To avoid leaks, call removeDragCaret in case it wasn't called after moveDragCaretToPoint.
592     [self removeDragCaret];
593     
594     [[self mainFrame] _detachFromParent];
595     [_private->_pageBridge release];
596     _private->_pageBridge = nil;
597     
598     // Clear the page cache so we call destroy on all the plug-ins in the page cache to break any retain cycles.
599     // See comment in [WebHistoryItem _releaseAllPendingPageCaches] for more information.
600     [_private->backForwardList _clearPageCache];
601     
602     if (_private->hasSpellCheckerDocumentTag) {
603         [[NSSpellChecker sharedSpellChecker] closeSpellDocumentWithTag:_private->spellCheckerDocumentTag];
604         _private->hasSpellCheckerDocumentTag = NO;
605     }
606     
607     if (_private->pluginDatabase) {
608         [_private->pluginDatabase close];
609         [_private->pluginDatabase release];
610         _private->pluginDatabase = nil;
611     }
612 }
613
614 + (NSString *)_MIMETypeForFile:(NSString *)path
615 {
616     NSString *extension = [path pathExtension];
617     NSString *MIMEType = nil;
618
619     // Get the MIME type from the extension.
620     if ([extension length] != 0) {
621         MIMEType = WKGetMIMETypeForExtension(extension);
622     }
623
624     // If we can't get a known MIME type from the extension, sniff.
625     if ([MIMEType length] == 0 || [MIMEType isEqualToString:@"application/octet-stream"]) {
626         NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path];
627         NSData *data = [handle readDataOfLength:WEB_GUESS_MIME_TYPE_PEEK_LENGTH];
628         [handle closeFile];
629         if ([data length] != 0) {
630             MIMEType = [data _webkit_guessedMIMEType];
631         }
632         if ([MIMEType length] == 0) {
633             MIMEType = @"application/octet-stream";
634         }
635     }
636
637     return MIMEType;
638 }
639
640 - (void)_downloadURL:(NSURL *)URL
641 {
642     ASSERT(URL);
643     
644     NSURLRequest *request = [[NSURLRequest alloc] initWithURL:URL];
645     [WebDownload _downloadWithRequest:request
646                              delegate:_private->downloadDelegate
647                             directory:nil];
648     [request release];
649 }
650
651 - (BOOL)defersCallbacks
652 {
653     return _private->defersCallbacks;
654 }
655
656 - (void)setDefersCallbacks:(BOOL)defers
657 {
658     if (defers == _private->defersCallbacks) {
659         return;
660     }
661
662     _private->defersCallbacks = defers;
663     [[self mainFrame] _defersCallbacksChanged];
664 }
665
666 - (WebView *)_openNewWindowWithRequest:(NSURLRequest *)request
667 {
668     id wd = [self UIDelegate];
669     WebView *newWindowWebView = nil;
670     if ([wd respondsToSelector:@selector(webView:createWebViewWithRequest:)])
671         newWindowWebView = [wd webView:self createWebViewWithRequest:request];
672     else {
673         newWindowWebView = [[WebDefaultUIDelegate sharedUIDelegate] webView:self createWebViewWithRequest: request];
674     }
675
676     [[newWindowWebView _UIDelegateForwarder] webViewShow: newWindowWebView];
677
678     return newWindowWebView;
679 }
680
681 - (NSMenu *)_menuForElement:(NSDictionary *)element defaultItems:(NSArray *)items
682 {
683     NSArray *defaultMenuItems = [[WebDefaultUIDelegate sharedUIDelegate]
684           webView:self contextMenuItemsForElement:element defaultMenuItems:items];
685     NSArray *menuItems = defaultMenuItems;
686     NSMenu *menu = nil;
687     unsigned i;
688
689     DOMNode* node = [element objectForKey:WebElementDOMNodeKey];
690     BOOL elementIsTextField = [node isKindOfClass:[DOMHTMLInputElement class]] && [(DOMHTMLInputElement*)node _isTextField];
691     BOOL elementIsTextArea = [node isKindOfClass:[DOMHTMLTextAreaElement class]];
692     if (_private->UIDelegate && !elementIsTextField && !elementIsTextArea) {
693         id cd = _private->UIDelegate;
694         
695         if ([cd respondsToSelector:@selector(webView:contextMenuItemsForElement:defaultMenuItems:)]) {
696             menuItems = [cd webView:self contextMenuItemsForElement:element defaultMenuItems:defaultMenuItems];
697
698             // Versions of Mail compiled with older WebKits will end up without three context menu items, though
699             // with the separators between them. Here we check for that problem and reinsert the three missing
700             // items. This shouldn't affect any clients other than Mail since the tags for the three items
701             // were not public API. We can remove this code when we no longer support previously-built versions
702             // of Mail on Tiger. See 4498606 for more details.
703             if (([[defaultMenuItems objectAtIndex:0] tag] == WebMenuItemTagSearchInSpotlight) && ([[menuItems objectAtIndex:0] isSeparatorItem])) {
704                 ASSERT([[menuItems objectAtIndex:1] isSeparatorItem]);
705                 ASSERT([[defaultMenuItems objectAtIndex:1] tag] == WebMenuItemTagSearchWeb);
706                 ASSERT([[defaultMenuItems objectAtIndex:2] isSeparatorItem]);
707                 ASSERT([[defaultMenuItems objectAtIndex:3] tag] == WebMenuItemTagLookUpInDictionary);
708                 ASSERT([[defaultMenuItems objectAtIndex:4] isSeparatorItem]);
709                 NSMutableArray *mutableMenuItems = [NSMutableArray arrayWithArray:menuItems];
710                 [mutableMenuItems insertObject:[defaultMenuItems objectAtIndex:0] atIndex:0];
711                 [mutableMenuItems insertObject:[defaultMenuItems objectAtIndex:1] atIndex:1];
712                 [mutableMenuItems insertObject:[defaultMenuItems objectAtIndex:3] atIndex:3];
713                 menuItems = mutableMenuItems;
714             }
715         }
716     } 
717
718     if (menuItems && [menuItems count] > 0) {
719         menu = [[[NSMenu alloc] init] autorelease];
720
721         for (i=0; i<[menuItems count]; i++) {
722             [menu addItem:[menuItems objectAtIndex:i]];
723         }
724     }
725
726     // optionally add the Inspect Element menu item it if preference is set or in debug builds
727     // and only showing the menu item if we are working with a WebHTMLView
728     WebFrame *webFrame = [element objectForKey:WebElementFrameKey];
729     if ([WebView _developerExtrasEnabled] && [[[webFrame frameView] documentView] isKindOfClass:[WebHTMLView class]]) {
730         if (!menu)
731             menu = [[[NSMenu alloc] init] autorelease];
732         else if ([menu numberOfItems])
733             [menu addItem:[NSMenuItem separatorItem]];
734         NSMenuItem *menuItem = [[[NSMenuItem alloc] init] autorelease];
735         [menuItem setAction:@selector(_inspectElement:)];
736         [menuItem setTitle:UI_STRING("Inspect Element", "Inspect Element context menu item")];
737         [menuItem setRepresentedObject:element];
738         [menu addItem:menuItem];
739     }
740
741     return menu;
742 }
743
744 - (void)_mouseDidMoveOverElement:(NSDictionary *)dictionary modifierFlags:(WebNSUInteger)modifierFlags
745 {
746     // When the mouse isn't over this view at all, we'll get called with a dictionary of nil over
747     // and over again. So it's a good idea to catch that here and not send multiple calls to the delegate
748     // for that case.
749     
750     if (dictionary && _private->lastElementWasNonNil) {
751         [[self _UIDelegateForwarder] webView:self mouseDidMoveOverElement:dictionary modifierFlags:modifierFlags];
752     }
753     _private->lastElementWasNonNil = dictionary != nil;
754 }
755
756 - (void)_goToItem:(WebHistoryItem *)item withLoadType:(WebFrameLoadType)type
757 {
758     // We never go back/forward on a per-frame basis, so the target must be the main frame
759     //ASSERT([item target] == nil || [self _findFrameNamed:[item target]] == [self mainFrame]);
760
761     // abort any current load if we're going back/forward
762     [[self mainFrame] stopLoading];
763     [[self mainFrame] _goToItem:item withLoadType:type];
764 }
765
766 - (void)_loadBackForwardListFromOtherView:(WebView *)otherView
767 {
768     // It turns out the right combination of behavior is done with the back/forward load
769     // type.  (See behavior matrix at the top of WebFramePrivate.)  So we copy all the items
770     // in the back forward list, and go to the current one.
771
772     WebBackForwardList *bfList = [self backForwardList];
773     ASSERT(![bfList currentItem]);      // destination list should be empty
774
775     WebBackForwardList *otherBFList = [otherView backForwardList];
776     if (![otherBFList currentItem]) {
777         return;         // empty back forward list, bail
778     }
779
780     WebHistoryItem *newItemToGoTo = nil;
781     int lastItemIndex = [otherBFList forwardListCount];
782     int i;
783     for (i = -[otherBFList backListCount]; i <= lastItemIndex; i++) {
784         if (i == 0) {
785             // If this item is showing , save away its current scroll and form state,
786             // since that might have changed since loading and it is normally not saved
787             // until we leave that page.
788             [[otherView mainFrame] _saveDocumentAndScrollState];
789         }
790         WebHistoryItem *newItem = [[otherBFList itemAtIndex:i] copy];
791         [bfList addItem:newItem];
792         if (i == 0) {
793             newItemToGoTo = newItem;
794         }
795     }
796     
797     [self _goToItem:newItemToGoTo withLoadType:WebFrameLoadTypeIndexedBackForward];
798 }
799
800 - (void)_setFormDelegate: (id<WebFormDelegate>)delegate
801 {
802     _private->formDelegate = delegate;
803 }
804
805 - (id<WebFormDelegate>)_formDelegate
806 {
807     if (!_private->formDelegate) {
808         // create lazily, to give the client a chance to set one before we bother to alloc the shared one
809         _private->formDelegate = [WebFormDelegate _sharedWebFormDelegate];
810     }
811     return _private->formDelegate;
812 }
813
814 - (WebCoreSettings *)_settings
815 {
816     return _private->settings;
817 }
818
819 - (void)_updateWebCoreSettingsFromPreferences:(WebPreferences *)preferences
820 {
821     [_private->settings setCursiveFontFamily:[preferences cursiveFontFamily]];
822     [_private->settings setDefaultFixedFontSize:[preferences defaultFixedFontSize]];
823     [_private->settings setDefaultFontSize:[preferences defaultFontSize]];
824     [_private->settings setDefaultTextEncoding:[preferences defaultTextEncodingName]];
825     [_private->settings setFantasyFontFamily:[preferences fantasyFontFamily]];
826     [_private->settings setFixedFontFamily:[preferences fixedFontFamily]];
827     [_private->settings setJavaEnabled:[preferences isJavaEnabled]];
828     [_private->settings setJavaScriptEnabled:[preferences isJavaScriptEnabled]];
829     [_private->settings setJavaScriptCanOpenWindowsAutomatically:[preferences javaScriptCanOpenWindowsAutomatically]];
830     [_private->settings setMinimumFontSize:[preferences minimumFontSize]];
831     [_private->settings setMinimumLogicalFontSize:[preferences minimumLogicalFontSize]];
832     [_private->settings setPluginsEnabled:[preferences arePlugInsEnabled]];
833     [_private->settings setPrivateBrowsingEnabled:[preferences privateBrowsingEnabled]];
834     [_private->settings setSansSerifFontFamily:[preferences sansSerifFontFamily]];
835     [_private->settings setSerifFontFamily:[preferences serifFontFamily]];
836     [_private->settings setStandardFontFamily:[preferences standardFontFamily]];
837     [_private->settings setWillLoadImagesAutomatically:[preferences loadsImagesAutomatically]];
838
839     if ([preferences userStyleSheetEnabled]) {
840         [_private->settings setUserStyleSheetLocation:[[preferences userStyleSheetLocation] _web_originalDataAsString]];
841     } else {
842         [_private->settings setUserStyleSheetLocation:@""];
843     }
844     [_private->settings setShouldPrintBackgrounds:[preferences shouldPrintBackgrounds]];
845     [_private->settings setTextAreasAreResizable:[preferences textAreasAreResizable]];
846 }
847
848 - (void)_preferencesChangedNotification: (NSNotification *)notification
849 {
850     WebPreferences *preferences = (WebPreferences *)[notification object];
851     
852     ASSERT(preferences == [self preferences]);
853     if (!_private->userAgentOverridden) {
854         [_private->userAgent release];
855         _private->userAgent = nil;
856     }
857     [self _updateWebCoreSettingsFromPreferences: preferences];
858 }
859
860 - _frameLoadDelegateForwarder
861 {
862     if (!_private->frameLoadDelegateForwarder)
863         _private->frameLoadDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget: [self frameLoadDelegate]  defaultTarget: [WebDefaultFrameLoadDelegate sharedFrameLoadDelegate] templateClass: [WebDefaultFrameLoadDelegate class]];
864     return _private->frameLoadDelegateForwarder;
865 }
866
867 - _resourceLoadDelegateForwarder
868 {
869     if (!_private->resourceProgressDelegateForwarder)
870         _private->resourceProgressDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget: [self resourceLoadDelegate] defaultTarget: [WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] templateClass: [WebDefaultResourceLoadDelegate class]];
871     return _private->resourceProgressDelegateForwarder;
872 }
873
874 - (void)_cacheResourceLoadDelegateImplementations
875 {
876     WebResourceDelegateImplementationCache *cache = &_private->resourceLoadDelegateImplementations;
877     id delegate = [self resourceLoadDelegate];
878
879     cache->delegateImplementsDidCancelAuthenticationChallenge = [delegate respondsToSelector:@selector(webView:resource:didCancelAuthenticationChallenge:fromDataSource:)];
880     cache->delegateImplementsDidReceiveAuthenticationChallenge = [delegate respondsToSelector:@selector(webView:resource:didReceiveAuthenticationChallenge:fromDataSource:)];
881     cache->delegateImplementsDidFinishLoadingFromDataSource = [delegate respondsToSelector:@selector(webView:resource:didFinishLoadingFromDataSource:)];
882     cache->delegateImplementsDidReceiveContentLength = [delegate respondsToSelector:@selector(webView:resource:didReceiveContentLength:fromDataSource:)];
883     cache->delegateImplementsDidReceiveResponse = [delegate respondsToSelector:@selector(webView:resource:didReceiveResponse:fromDataSource:)];
884     cache->delegateImplementsWillSendRequest = [delegate respondsToSelector:@selector(webView:resource:willSendRequest:redirectResponse:fromDataSource:)];
885     cache->delegateImplementsIdentifierForRequest = [delegate respondsToSelector:@selector(webView:identifierForInitialRequest:fromDataSource:)];
886 }
887
888 - (WebResourceDelegateImplementationCache)_resourceLoadDelegateImplementations
889 {
890     return _private->resourceLoadDelegateImplementations;
891 }
892
893 - _policyDelegateForwarder
894 {
895     if (!_private->policyDelegateForwarder)
896         _private->policyDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget: [self policyDelegate] defaultTarget: [WebDefaultPolicyDelegate sharedPolicyDelegate] templateClass: [WebDefaultPolicyDelegate class]];
897     return _private->policyDelegateForwarder;
898 }
899
900 - _UIDelegateForwarder
901 {
902     if (!_private->UIDelegateForwarder)
903         _private->UIDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget: [self UIDelegate] defaultTarget: [WebDefaultUIDelegate sharedUIDelegate] templateClass: [WebDefaultUIDelegate class]];
904     return _private->UIDelegateForwarder;
905 }
906
907 - _editingDelegateForwarder
908 {
909     // This can be called during window deallocation by QTMovieView in the QuickTime Cocoa Plug-in.
910     // Not sure if that is a bug or not.
911     if (!_private)
912         return nil;
913     if (!_private->editingDelegateForwarder)
914         _private->editingDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget: [self editingDelegate] defaultTarget: [WebDefaultEditingDelegate sharedEditingDelegate] templateClass: [WebDefaultEditingDelegate class]];
915     return _private->editingDelegateForwarder;
916 }
917
918 - _scriptDebugDelegateForwarder
919 {
920     if (!_private->scriptDebugDelegateForwarder)
921         _private->scriptDebugDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget: [self scriptDebugDelegate] defaultTarget: [WebDefaultScriptDebugDelegate sharedScriptDebugDelegate] templateClass: [WebDefaultScriptDebugDelegate class]];
922     return _private->scriptDebugDelegateForwarder;
923 }
924
925 - (void)_closeWindow
926 {
927     [[self _UIDelegateForwarder] webViewClose:self];
928 }
929
930 + (void)_unregisterViewClassAndRepresentationClassForMIMEType:(NSString *)MIMEType;
931 {
932     [[WebFrameView _viewTypesAllowImageTypeOmission:NO] removeObjectForKey:MIMEType];
933     [[WebDataSource _repTypesAllowImageTypeOmission:NO] removeObjectForKey:MIMEType];
934 }
935
936 + (void)_registerViewClass:(Class)viewClass representationClass:(Class)representationClass forURLScheme:(NSString *)URLScheme;
937 {
938     NSString *MIMEType = [self _generatedMIMETypeForURLScheme:URLScheme];
939     [self registerViewClass:viewClass representationClass:representationClass forMIMEType:MIMEType];
940
941     // This is used to make _representationExistsForURLScheme faster.
942     // Without this set, we'd have to create the MIME type each time.
943     if (schemesWithRepresentationsSet == nil) {
944         schemesWithRepresentationsSet = [[NSMutableSet alloc] init];
945     }
946     [schemesWithRepresentationsSet addObject:[[[URLScheme lowercaseString] copy] autorelease]];
947 }
948
949 + (NSString *)_generatedMIMETypeForURLScheme:(NSString *)URLScheme
950 {
951     return [@"x-apple-web-kit/" stringByAppendingString:[URLScheme lowercaseString]];
952 }
953
954 + (BOOL)_representationExistsForURLScheme:(NSString *)URLScheme
955 {
956     return [schemesWithRepresentationsSet containsObject:[URLScheme lowercaseString]];
957 }
958
959 + (BOOL)_canHandleRequest:(NSURLRequest *)request
960 {
961     if ([NSURLConnection canHandleRequest:request]) {
962         return YES;
963     }
964     
965     // We're always willing to load alternate content for unreachable URLs
966     if ([request _webDataRequestUnreachableURL]) {
967         return YES;
968     }
969
970     return [self _representationExistsForURLScheme:[[request URL] scheme]];
971 }
972
973 + (NSString *)_decodeData:(NSData *)data
974 {
975     return [WebCoreEncodings decodeData:data];
976 }
977
978 - (void)_pushPerformingProgrammaticFocus
979 {
980     _private->programmaticFocusCount++;
981 }
982
983 - (void)_popPerformingProgrammaticFocus
984 {
985     _private->programmaticFocusCount--;
986 }
987
988 - (BOOL)_isPerformingProgrammaticFocus
989 {
990     return _private->programmaticFocusCount != 0;
991 }
992
993 #define UnknownTotalBytes -1
994 #define WebProgressItemDefaultEstimatedLength 1024*16
995
996 - (void)_didChangeValueForKey: (NSString *)key
997 {
998     LOG (Bindings, "calling didChangeValueForKey: %@", key);
999     [self didChangeValueForKey: key];
1000 }
1001
1002 - (void)_willChangeValueForKey: (NSString *)key
1003 {
1004     LOG (Bindings, "calling willChangeValueForKey: %@", key);
1005     [self willChangeValueForKey: key];
1006 }
1007
1008 // Always start progress at INITIAL_PROGRESS_VALUE. This helps provide feedback as 
1009 // soon as a load starts.
1010 #define INITIAL_PROGRESS_VALUE 0.1
1011 // Similarly, always leave space at the end. This helps show the user that we're not done
1012 // until we're done.
1013 #define FINAL_PROGRESS_VALUE 1.0 - INITIAL_PROGRESS_VALUE
1014
1015 - (void)_resetProgress
1016 {
1017     [_private->progressItems release];
1018     _private->progressItems = nil;
1019     _private->totalPageAndResourceBytesToLoad = 0;
1020     _private->totalBytesReceived = 0;
1021     _private->progressValue = 0;
1022     _private->lastNotifiedProgressValue = 0;
1023     _private->lastNotifiedProgressTime = 0;
1024     _private->finalProgressChangedSent = NO;
1025     _private->numProgressTrackedFrames = 0;
1026     [_private->originatingProgressFrame release];
1027     _private->originatingProgressFrame = nil;
1028 }
1029 - (void)_progressStarted:(WebFrame *)frame
1030 {
1031     LOG (Progress, "frame %p(%@), _private->numProgressTrackedFrames %d, _private->originatingProgressFrame %p", frame, [frame name], _private->numProgressTrackedFrames, _private->originatingProgressFrame);
1032     [self _willChangeValueForKey: @"estimatedProgress"];
1033     if (_private->numProgressTrackedFrames == 0 || _private->originatingProgressFrame == frame){
1034         [self _resetProgress];
1035         _private->progressValue = INITIAL_PROGRESS_VALUE;
1036         _private->originatingProgressFrame = [frame retain];
1037         [[NSNotificationCenter defaultCenter] postNotificationName:WebViewProgressStartedNotification object:self];
1038     }
1039     _private->numProgressTrackedFrames++;
1040     [self _didChangeValueForKey: @"estimatedProgress"];
1041 }
1042
1043 - (void)_finalProgressComplete
1044 {
1045     LOG (Progress, "");
1046
1047     // Before resetting progress value be sure to send client a least one notification
1048     // with final progress value.
1049     if (!_private->finalProgressChangedSent) {
1050         _private->progressValue = 1;
1051         [[NSNotificationCenter defaultCenter] postNotificationName:WebViewProgressEstimateChangedNotification object:self];
1052     }
1053     
1054     [self _resetProgress];
1055     
1056     [self setMainFrameDocumentReady:YES];   // make sure this is turned on at this point
1057     [[NSNotificationCenter defaultCenter] postNotificationName:WebViewProgressFinishedNotification object:self];
1058 }
1059
1060 - (void)_progressCompleted:(WebFrame *)frame
1061 {    
1062     LOG (Progress, "frame %p(%@), _private->numProgressTrackedFrames %d, _private->originatingProgressFrame %p", frame, [frame name], _private->numProgressTrackedFrames, _private->originatingProgressFrame);
1063
1064     if (_private->numProgressTrackedFrames <= 0)
1065         return;
1066
1067     [self _willChangeValueForKey: @"estimatedProgress"];
1068
1069     _private->numProgressTrackedFrames--;
1070     if (_private->numProgressTrackedFrames == 0 ||
1071         (frame == _private->originatingProgressFrame && _private->numProgressTrackedFrames != 0)){
1072         [self _finalProgressComplete];
1073     }
1074     [self _didChangeValueForKey: @"estimatedProgress"];
1075 }
1076
1077 - (void)_incrementProgressForIdentifier:(id)identifier response:(NSURLResponse *)response;
1078 {
1079     if (!identifier)
1080         return;
1081
1082     LOG (Progress, "_private->numProgressTrackedFrames %d, _private->originatingProgressFrame %p", _private->numProgressTrackedFrames, _private->originatingProgressFrame);
1083     
1084     if (_private->numProgressTrackedFrames <= 0)
1085         return;
1086         
1087     WebProgressItem *item = [[WebProgressItem alloc] init];
1088
1089     if (!item)
1090         return;
1091
1092     long long length = [response expectedContentLength];
1093     if (length < 0){
1094         length = WebProgressItemDefaultEstimatedLength;
1095     }
1096     item->estimatedLength = length;
1097     _private->totalPageAndResourceBytesToLoad += length;
1098     
1099     if (!_private->progressItems)
1100         _private->progressItems = [[NSMutableDictionary alloc] init];
1101         
1102     [_private->progressItems _webkit_setObject:item forUncopiedKey:identifier];
1103     [item release];
1104 }
1105
1106 - (void)_incrementProgressForIdentifier:(id)identifier data:(NSData *)data
1107 {
1108     if (!identifier)
1109         return;
1110
1111     WebProgressItem *item = [_private->progressItems objectForKey:identifier];
1112
1113     if (!item)
1114         return;
1115
1116     [self _willChangeValueForKey: @"estimatedProgress"];
1117
1118     unsigned bytesReceived = [data length];
1119     double increment, percentOfRemainingBytes;
1120     long long remainingBytes, estimatedBytesForPendingRequests;
1121
1122     item->bytesReceived += bytesReceived;
1123     if (item->bytesReceived > item->estimatedLength){
1124         _private->totalPageAndResourceBytesToLoad += ((item->bytesReceived*2) - item->estimatedLength);
1125         item->estimatedLength = item->bytesReceived*2;
1126     }
1127     
1128     int numPendingOrLoadingRequests = [_private->originatingProgressFrame _numPendingOrLoadingRequests:YES];
1129     estimatedBytesForPendingRequests = WebProgressItemDefaultEstimatedLength * numPendingOrLoadingRequests;
1130     remainingBytes = ((_private->totalPageAndResourceBytesToLoad + estimatedBytesForPendingRequests) - _private->totalBytesReceived);
1131     percentOfRemainingBytes = (double)bytesReceived / (double)remainingBytes;
1132
1133     // Treat the first layout as the half-way point.
1134     double maxProgressValue = [_private->originatingProgressFrame _firstLayoutDone]
1135         ? FINAL_PROGRESS_VALUE
1136         : .5;
1137     increment = (maxProgressValue - _private->progressValue) * percentOfRemainingBytes;
1138     _private->progressValue += increment;
1139     if (_private->progressValue > maxProgressValue)
1140         _private->progressValue = maxProgressValue;
1141     ASSERT(_private->progressValue >= INITIAL_PROGRESS_VALUE);
1142
1143     _private->totalBytesReceived += bytesReceived;
1144
1145     double now = CFAbsoluteTimeGetCurrent();
1146     double notifiedProgressTimeDelta = now - _private->lastNotifiedProgressTime;
1147     
1148     LOG (Progress, "_private->progressValue %g, _private->numProgressTrackedFrames %d", _private->progressValue, _private->numProgressTrackedFrames);
1149     double notificationProgressDelta = _private->progressValue - _private->lastNotifiedProgressValue;
1150     if ((notificationProgressDelta >= _private->progressNotificationInterval ||
1151             notifiedProgressTimeDelta >= _private->progressNotificationTimeInterval) &&
1152             _private->numProgressTrackedFrames > 0) {
1153         if (!_private->finalProgressChangedSent) {
1154             if (_private->progressValue == 1)
1155                 _private->finalProgressChangedSent = YES;
1156             [[NSNotificationCenter defaultCenter] postNotificationName:WebViewProgressEstimateChangedNotification object:self];
1157             _private->lastNotifiedProgressValue = _private->progressValue;
1158             _private->lastNotifiedProgressTime = now;
1159         }
1160     }
1161
1162     [self _didChangeValueForKey: @"estimatedProgress"];
1163 }
1164
1165 - (void)_completeProgressForIdentifier:(id)identifier
1166 {
1167     WebProgressItem *item = [_private->progressItems objectForKey:identifier];
1168
1169     if (!item)
1170         return;
1171         
1172     // Adjust the total expected bytes to account for any overage/underage.
1173     long long delta = item->bytesReceived - item->estimatedLength;
1174     _private->totalPageAndResourceBytesToLoad += delta;
1175     item->estimatedLength = item->bytesReceived;
1176 }
1177
1178 // Required to prevent automatic observer notifications.
1179 + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
1180     return NO;
1181 }
1182
1183 - (NSArray *)_declaredKeys {
1184     static NSArray *declaredKeys = nil;
1185     
1186     if (!declaredKeys) {
1187         declaredKeys = [[NSArray alloc] initWithObjects:_WebMainFrameURLKey, _WebIsLoadingKey, _WebEstimatedProgressKey, _WebCanGoBackKey, _WebCanGoForwardKey, _WebMainFrameTitleKey, _WebMainFrameIconKey, _WebMainFrameDocumentKey, nil];
1188     }
1189
1190     return declaredKeys;
1191 }
1192
1193 - (void)setObservationInfo:(void *)info
1194 {
1195     _private->observationInfo = info;
1196 }
1197
1198 - (void *)observationInfo
1199 {
1200     return _private->observationInfo;
1201 }
1202
1203 - (void)_willChangeBackForwardKeys
1204 {
1205     [self _willChangeValueForKey: _WebCanGoBackKey];
1206     [self _willChangeValueForKey: _WebCanGoForwardKey];
1207 }
1208
1209 - (void)_didChangeBackForwardKeys
1210 {
1211     [self _didChangeValueForKey: _WebCanGoBackKey];
1212     [self _didChangeValueForKey: _WebCanGoForwardKey];
1213 }
1214
1215 - (void)_didStartProvisionalLoadForFrame:(WebFrame *)frame
1216 {
1217     [self _willChangeBackForwardKeys];
1218     if (frame == [self mainFrame]){
1219         // Force an observer update by sending a will/did.
1220         [self _willChangeValueForKey: _WebIsLoadingKey];
1221         [self _didChangeValueForKey: _WebIsLoadingKey];
1222
1223         [self _willChangeValueForKey: _WebMainFrameURLKey];
1224     }
1225     [NSApp setWindowsNeedUpdate:YES];
1226 }
1227
1228 - (void)_didCommitLoadForFrame:(WebFrame *)frame
1229 {
1230     if (frame == [self mainFrame]){
1231         [self _didChangeValueForKey: _WebMainFrameURLKey];
1232     }
1233     [NSApp setWindowsNeedUpdate:YES];
1234 }
1235
1236 - (void)_didFinishLoadForFrame:(WebFrame *)frame
1237 {
1238     [self _didChangeBackForwardKeys];
1239     if (frame == [self mainFrame]){
1240         // Force an observer update by sending a will/did.
1241         [self _willChangeValueForKey: _WebIsLoadingKey];
1242         [self _didChangeValueForKey: _WebIsLoadingKey];
1243     }
1244     [NSApp setWindowsNeedUpdate:YES];
1245 }
1246
1247 - (void)_didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
1248 {
1249     [self _didChangeBackForwardKeys];
1250     if (frame == [self mainFrame]){
1251         // Force an observer update by sending a will/did.
1252         [self _willChangeValueForKey: _WebIsLoadingKey];
1253         [self _didChangeValueForKey: _WebIsLoadingKey];
1254     }
1255     [NSApp setWindowsNeedUpdate:YES];
1256 }
1257
1258 - (void)_didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
1259 {
1260     [self _didChangeBackForwardKeys];
1261     if (frame == [self mainFrame]){
1262         // Force an observer update by sending a will/did.
1263         [self _willChangeValueForKey: _WebIsLoadingKey];
1264         [self _didChangeValueForKey: _WebIsLoadingKey];
1265         
1266         [self _didChangeValueForKey: _WebMainFrameURLKey];
1267     }
1268     [NSApp setWindowsNeedUpdate:YES];
1269 }
1270
1271 - (void)_reloadForPluginChanges
1272 {
1273     [[self mainFrame] _reloadForPluginChanges];
1274 }
1275
1276 - (NSCachedURLResponse *)_cachedResponseForURL:(NSURL *)URL
1277 {
1278     NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];
1279     [request _web_setHTTPUserAgent:[self userAgentForURL:URL]];
1280     NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
1281     [request release];
1282     return cachedResponse;
1283 }
1284
1285 - (void)_writeImageForElement:(NSDictionary *)element withPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
1286 {
1287     NSURL *linkURL = [element objectForKey:WebElementLinkURLKey];
1288     DOMElement *domElement = [element objectForKey:WebElementDOMNodeKey];
1289     [pasteboard _web_writeImage:(NSImage *)(domElement ? nil : [element objectForKey:WebElementImageKey])
1290                         element:domElement
1291                             URL:linkURL ? linkURL : (NSURL *)[element objectForKey:WebElementImageURLKey]
1292                           title:[element objectForKey:WebElementImageAltStringKey] 
1293                         archive:[[element objectForKey:WebElementDOMNodeKey] webArchive]
1294                           types:types];
1295 }
1296
1297 - (void)_writeLinkElement:(NSDictionary *)element withPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
1298 {
1299     [pasteboard _web_writeURL:[element objectForKey:WebElementLinkURLKey]
1300                      andTitle:[element objectForKey:WebElementLinkLabelKey]
1301                         types:types];
1302 }
1303
1304 - (void)_setInitiatedDrag:(BOOL)initiatedDrag
1305 {
1306     _private->initiatedDrag = initiatedDrag;
1307 }
1308
1309 #define DASHBOARD_CONTROL_LABEL @"control"
1310
1311 - (void)_addScrollerDashboardRegions:(NSMutableDictionary *)regions from:(NSArray *)views
1312 {
1313     // Add scroller regions for NSScroller and KWQScrollBar
1314     int i, count = [views count];
1315     
1316     for (i = 0; i < count; i++) {
1317         NSView *aView = [views objectAtIndex:i];
1318         
1319         if ([aView isKindOfClass:[NSScroller class]] ||
1320             [aView isKindOfClass:NSClassFromString (@"KWQScrollBar")]) {
1321             NSRect bounds = [aView bounds];
1322             NSRect adjustedBounds;
1323             adjustedBounds.origin = [self convertPoint:bounds.origin fromView:aView];
1324             adjustedBounds.origin.y = [self bounds].size.height - adjustedBounds.origin.y;
1325             
1326             // AppKit has horrible hack of placing absent scrollers at -100,-100
1327             if (adjustedBounds.origin.y == -100)
1328                 continue;
1329             adjustedBounds.size = bounds.size;
1330             NSRect clip = [aView visibleRect];
1331             NSRect adjustedClip;
1332             adjustedClip.origin = [self convertPoint:clip.origin fromView:aView];
1333             adjustedClip.origin.y = [self bounds].size.height - adjustedClip.origin.y;
1334             adjustedClip.size = clip.size;
1335             WebDashboardRegion *aRegion = 
1336                         [[[WebDashboardRegion alloc] initWithRect:adjustedBounds 
1337                                     clip:adjustedClip type:WebDashboardRegionTypeScrollerRectangle] autorelease];
1338             NSMutableArray *scrollerRegions;
1339             scrollerRegions = [regions objectForKey:DASHBOARD_CONTROL_LABEL];
1340             if (!scrollerRegions) {
1341                 scrollerRegions = [NSMutableArray array];
1342                 [regions setObject:scrollerRegions forKey:DASHBOARD_CONTROL_LABEL];
1343             }
1344             [scrollerRegions addObject:aRegion];
1345         }
1346         [self _addScrollerDashboardRegions:regions from:[aView subviews]];
1347     }
1348 }
1349
1350 - (void)_addScrollerDashboardRegions:(NSMutableDictionary *)regions
1351 {
1352     [self _addScrollerDashboardRegions:regions from:[self subviews]];
1353 }
1354
1355 - (NSDictionary *)_dashboardRegions
1356 {
1357     // Only return regions from main frame.
1358     NSMutableDictionary *regions = [[[self mainFrame] _bridge] dashboardRegions];
1359     [self _addScrollerDashboardRegions:regions];
1360     return regions;
1361 }
1362
1363 - (void)_setDashboardBehavior:(WebDashboardBehavior)behavior to:(BOOL)flag;
1364 {
1365     switch (behavior) {
1366         case WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows: {
1367             _private->dashboardBehaviorAlwaysSendMouseEventsToAllWindows = flag;
1368             break;
1369         }
1370         case WebDashboardBehaviorAlwaysSendActiveNullEventsToPlugIns: {
1371             _private->dashboardBehaviorAlwaysSendActiveNullEventsToPlugIns = flag;
1372             break;
1373         }
1374         case WebDashboardBehaviorAlwaysAcceptsFirstMouse: {
1375             _private->dashboardBehaviorAlwaysAcceptsFirstMouse = flag;
1376             break;
1377         }
1378         case WebDashboardBehaviorAllowWheelScrolling: {
1379             _private->dashboardBehaviorAllowWheelScrolling = flag;
1380             break;
1381         }
1382     }
1383 }
1384
1385 - (BOOL)_dashboardBehavior:(WebDashboardBehavior)behavior
1386 {
1387     switch (behavior) {
1388         case WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows: {
1389             return _private->dashboardBehaviorAlwaysSendMouseEventsToAllWindows;
1390         }
1391         case WebDashboardBehaviorAlwaysSendActiveNullEventsToPlugIns: {
1392             return _private->dashboardBehaviorAlwaysSendActiveNullEventsToPlugIns;
1393         }
1394         case WebDashboardBehaviorAlwaysAcceptsFirstMouse: {
1395             return _private->dashboardBehaviorAlwaysAcceptsFirstMouse;
1396         }
1397         case WebDashboardBehaviorAllowWheelScrolling: {
1398             return _private->dashboardBehaviorAllowWheelScrolling;
1399         }
1400     }
1401     return NO;
1402 }
1403
1404 - (void)handleAuthenticationForResource:(id)identifier challenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)dataSource
1405 {
1406     [[WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] webView:self resource:identifier didReceiveAuthenticationChallenge:challenge fromDataSource:dataSource];
1407 }
1408
1409 + (void)_setShouldUseFontSmoothing:(BOOL)f
1410 {
1411     WebCoreSetShouldUseFontSmoothing(f);
1412 }
1413
1414 + (BOOL)_shouldUseFontSmoothing
1415 {
1416     return WebCoreShouldUseFontSmoothing();
1417 }
1418
1419 + (NSString *)_minimumRequiredSafariBuildNumber
1420 {
1421     return @"420+";
1422 }
1423
1424 - (void)setAlwaysShowVerticalScroller:(BOOL)flag
1425 {
1426     WebDynamicScrollBarsView *scrollview = (WebDynamicScrollBarsView *)[[[self mainFrame] frameView] _scrollView];
1427     if (flag) {
1428         [scrollview setVerticalScrollingMode:WebCoreScrollBarAlwaysOn];
1429         [scrollview setVerticalScrollingModeLocked:YES];
1430     } else {
1431         [scrollview setVerticalScrollingModeLocked:NO];
1432         [scrollview setVerticalScrollingMode:WebCoreScrollBarAuto];
1433     }
1434 }
1435
1436 - (BOOL)alwaysShowVerticalScroller
1437 {
1438     WebDynamicScrollBarsView *scrollview = (WebDynamicScrollBarsView *)[[[self mainFrame] frameView] _scrollView];
1439     return [scrollview verticalScrollingModeLocked] && [scrollview verticalScrollingMode] == WebCoreScrollBarAlwaysOn;
1440 }
1441
1442 - (void)setAlwaysShowHorizontalScroller:(BOOL)flag
1443 {
1444     WebDynamicScrollBarsView *scrollview = (WebDynamicScrollBarsView *)[[[self mainFrame] frameView] _scrollView];
1445     if (flag) {
1446         [scrollview setHorizontalScrollingMode:WebCoreScrollBarAlwaysOn];
1447         [scrollview setHorizontalScrollingModeLocked:YES];
1448     } else {
1449         [scrollview setHorizontalScrollingModeLocked:NO];
1450         [scrollview setHorizontalScrollingMode:WebCoreScrollBarAuto];
1451     }
1452 }
1453
1454 - (BOOL)alwaysShowHorizontalScroller
1455 {
1456     WebDynamicScrollBarsView *scrollview = (WebDynamicScrollBarsView *)[[[self mainFrame] frameView] _scrollView];
1457     return [scrollview horizontalScrollingModeLocked] && [scrollview horizontalScrollingMode] == WebCoreScrollBarAlwaysOn;
1458 }
1459
1460 - (void)_setAdditionalWebPlugInPaths:(NSArray *)newPaths
1461 {
1462     if (!_private->pluginDatabase)
1463         _private->pluginDatabase = [[WebPluginDatabase alloc] init];
1464         
1465     [_private->pluginDatabase setPlugInPaths:newPaths];
1466     [_private->pluginDatabase refresh];
1467 }
1468
1469 @end
1470
1471
1472 @implementation _WebSafeForwarder
1473
1474 - initWithTarget: t defaultTarget: dt templateClass: (Class)aClass
1475 {
1476     self = [super init];
1477     if (!self)
1478         return nil;
1479     
1480     target = t;         // Non retained.
1481     defaultTarget = dt;
1482     templateClass = aClass;
1483     return self;
1484 }
1485
1486
1487 // Used to send messages to delegates that implement informal protocols.
1488 + safeForwarderWithTarget: t defaultTarget: dt templateClass: (Class)aClass;
1489 {
1490     return [[[_WebSafeForwarder alloc] initWithTarget: t defaultTarget: dt templateClass: aClass] autorelease];
1491 }
1492
1493 #ifndef NDEBUG
1494 NSMutableDictionary *countInvocations;
1495 #endif
1496
1497 - (void)forwardInvocation:(NSInvocation *)anInvocation
1498 {
1499 #ifndef NDEBUG
1500     if (!countInvocations){
1501         countInvocations = [[NSMutableDictionary alloc] init];
1502     }
1503     NSNumber *count = [countInvocations objectForKey: NSStringFromSelector([anInvocation selector])];
1504     if (!count)
1505         count = [NSNumber numberWithInt: 1];
1506     else
1507         count = [NSNumber numberWithInt: [count intValue] + 1];
1508     [countInvocations setObject: count forKey: NSStringFromSelector([anInvocation selector])];
1509 #endif
1510     if ([target respondsToSelector: [anInvocation selector]])
1511         [anInvocation invokeWithTarget: target];
1512     else if ([defaultTarget respondsToSelector: [anInvocation selector]])
1513         [anInvocation invokeWithTarget: defaultTarget];
1514     // Do nothing quietly if method not implemented.
1515 }
1516
1517 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
1518 {
1519     return [templateClass instanceMethodSignatureForSelector: aSelector];
1520 }
1521
1522 @end
1523
1524 @implementation WebView
1525
1526 #if REMOVE_SAFARI_DOM_TREE_DEBUG_ITEM
1527 // this prevents open source users from crashing when using the Show DOM Tree menu item in Safari
1528 // FIXME: remove this when it is no longer needed to prevent Safari from crashing
1529 +(void)initialize
1530 {
1531     static BOOL tooLate = NO;
1532     if (!tooLate) {
1533         if ([[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.apple.Safari"] && [[NSUserDefaults standardUserDefaults] boolForKey:@"IncludeDebugMenu"])
1534             [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_finishedLaunching) name:NSApplicationDidFinishLaunchingNotification object:NSApp];
1535         tooLate = YES;
1536     }
1537 }
1538
1539 +(void)_finishedLaunching
1540 {
1541     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_removeDOMTreeMenuItem:) name:NSMenuDidAddItemNotification object:[NSApp mainMenu]];
1542     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationDidFinishLaunchingNotification object:NSApp];
1543 }
1544
1545 +(void)_removeDOMTreeMenuItem:(NSNotification *)notification
1546 {
1547     NSMenu *debugMenu = [[[[NSApp mainMenu] itemArray] lastObject] submenu];
1548     NSMenuItem *domTree = [debugMenu itemWithTitle:@"Show DOM Tree"];
1549     if (domTree)
1550         [debugMenu removeItem:domTree];
1551     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSMenuDidAddItemNotification object:[NSApp mainMenu]];
1552 }
1553 #endif
1554
1555 + (BOOL)canShowMIMEType:(NSString *)MIMEType
1556 {
1557     return [self _viewClass:nil andRepresentationClass:nil forMIMEType:MIMEType];
1558 }
1559
1560 - (WebBasePluginPackage *)_pluginForMIMEType:(NSString *)MIMEType
1561 {
1562     WebBasePluginPackage *pluginPackage = [[WebPluginDatabase installedPlugins] pluginForMIMEType:MIMEType];
1563     if (pluginPackage)
1564         return pluginPackage;
1565     
1566     if (_private->pluginDatabase)
1567         return [_private->pluginDatabase pluginForMIMEType:MIMEType];
1568     
1569     return nil;
1570 }
1571
1572 - (WebBasePluginPackage *)_pluginForExtension:(NSString *)extension
1573 {
1574     WebBasePluginPackage *pluginPackage = [[WebPluginDatabase installedPlugins] pluginForExtension:extension];
1575     if (pluginPackage)
1576         return pluginPackage;
1577     
1578     if (_private->pluginDatabase)
1579         return [_private->pluginDatabase pluginForExtension:extension];
1580     
1581     return nil;
1582 }
1583
1584 - (BOOL)_isMIMETypeRegisteredAsPlugin:(NSString *)MIMEType
1585 {
1586     if ([[WebPluginDatabase installedPlugins] isMIMETypeRegistered:MIMEType])
1587         return YES;
1588         
1589     if (_private->pluginDatabase && [_private->pluginDatabase isMIMETypeRegistered:MIMEType])
1590         return YES;
1591     
1592     return nil;
1593 }
1594
1595 + (BOOL)canShowMIMETypeAsHTML:(NSString *)MIMEType
1596 {
1597     return [WebFrameView _canShowMIMETypeAsHTML:MIMEType];
1598 }
1599
1600 + (NSArray *)MIMETypesShownAsHTML
1601 {
1602     NSMutableDictionary *viewTypes = [WebFrameView _viewTypesAllowImageTypeOmission:YES];
1603     NSEnumerator *enumerator = [viewTypes keyEnumerator];
1604     id key;
1605     NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease];
1606     
1607     while ((key = [enumerator nextObject])) {
1608         if ([viewTypes objectForKey:key] == [WebHTMLView class])
1609             [array addObject:key];
1610     }
1611     
1612     return array;
1613 }
1614
1615 + (void)setMIMETypesShownAsHTML:(NSArray *)MIMETypes
1616 {
1617     NSMutableDictionary *viewTypes = [WebFrameView _viewTypesAllowImageTypeOmission:YES];
1618     NSEnumerator *enumerator = [viewTypes keyEnumerator];
1619     id key;
1620     while ((key = [enumerator nextObject])) {
1621         if ([viewTypes objectForKey:key] == [WebHTMLView class])
1622             [WebView _unregisterViewClassAndRepresentationClassForMIMEType:key];
1623     }
1624     
1625     int i, count = [MIMETypes count];
1626     for (i = 0; i < count; i++) {
1627         [WebView registerViewClass:[WebHTMLView class] 
1628                 representationClass:[WebHTMLRepresentation class] 
1629                 forMIMEType:[MIMETypes objectAtIndex:i]];
1630     }
1631 }
1632
1633 + (NSURL *)URLFromPasteboard:(NSPasteboard *)pasteboard
1634 {
1635     return [pasteboard _web_bestURL];
1636 }
1637
1638 + (NSString *)URLTitleFromPasteboard:(NSPasteboard *)pasteboard
1639 {
1640     return [pasteboard stringForType:WebURLNamePboardType];
1641 }
1642
1643 - (void)_registerDraggedTypes
1644 {
1645     NSArray *editableTypes = [WebHTMLView _insertablePasteboardTypes];
1646     NSArray *URLTypes = [NSPasteboard _web_dragTypesForURL];
1647     NSMutableSet *types = [[NSMutableSet alloc] initWithArray:editableTypes];
1648     [types addObjectsFromArray:URLTypes];
1649     [self registerForDraggedTypes:[types allObjects]];
1650     [types release];
1651 }
1652
1653 - (void)_commonInitializationWithFrameName:(NSString *)frameName groupName:(NSString *)groupName
1654 {
1655     _private->mainFrameDocumentReady = NO;
1656     _private->drawsBackground = YES;
1657     _private->smartInsertDeleteEnabled = YES;
1658
1659     NSRect f = [self frame];
1660     WebFrameView *frameView = [[WebFrameView alloc] initWithFrame: NSMakeRect(0,0,f.size.width,f.size.height)];
1661     [frameView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
1662     [self addSubview:frameView];
1663     [frameView release];
1664
1665     WebKitInitializeLoggingChannelsIfNecessary();
1666     _private->_pageBridge = [[WebPageBridge alloc] initWithMainFrameName:frameName webView:self frameView:frameView];
1667
1668     [self _addToAllWebViewsSet];
1669     [self setGroupName:groupName];
1670     
1671     // If there's already a next key view (e.g., from a nib), wire it up to our
1672     // contained frame view. In any case, wire our next key view up to the our
1673     // contained frame view. This works together with our becomeFirstResponder 
1674     // and setNextKeyView overrides.
1675     NSView *nextKeyView = [self nextKeyView];
1676     if (nextKeyView != nil && nextKeyView != frameView) {
1677         [frameView setNextKeyView:nextKeyView];
1678     }
1679     [super setNextKeyView:frameView];
1680
1681     ++WebViewCount;
1682
1683     [self _registerDraggedTypes];
1684
1685     // initialize WebScriptDebugServer here so listeners can register before any pages are loaded.
1686     if ([WebView _scriptDebuggerEnabled])
1687         [WebScriptDebugServer sharedScriptDebugServer];
1688
1689     // Update WebCore with preferences.  These values will either come from an archived WebPreferences,
1690     // or from the standard preferences, depending on whether this method was called from initWithCoder:
1691     // or initWithFrame, respectively.
1692     [self _updateWebCoreSettingsFromPreferences: [self preferences]];
1693     
1694     // Register to receive notifications whenever preference values change.
1695     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesChangedNotification:)
1696                                                  name:WebPreferencesChangedNotification object:[self preferences]];
1697 }
1698
1699 - (id)initWithFrame:(NSRect)f
1700 {
1701     return [self initWithFrame:f frameName:nil groupName:nil];
1702 }
1703
1704 - (id)initWithFrame:(NSRect)f frameName:(NSString *)frameName groupName:(NSString *)groupName;
1705 {
1706     self = [super initWithFrame:f];
1707     if (!self)
1708         return nil;
1709
1710 #if ENABLE_WEBKIT_UNSET_DYLD_FRAMEWORK_PATH
1711     // DYLD_FRAMEWORK_PATH is used so Safari will load the development version of WebKit, which
1712     // may not work with other WebKit applications.  Unsetting DYLD_FRAMEWORK_PATH removes the
1713     // need for Safari to unset it to prevent it from being passed to applications it launches.
1714     // Unsetting it when a WebView is first created is as good a place as any.
1715     // See <http://bugzilla.opendarwin.org/show_bug.cgi?id=4286> for more details.
1716     if (getenv("WEBKIT_UNSET_DYLD_FRAMEWORK_PATH")) {
1717         unsetenv("DYLD_FRAMEWORK_PATH");
1718         unsetenv("WEBKIT_UNSET_DYLD_FRAMEWORK_PATH");
1719     }
1720 #endif
1721
1722     _private = [[WebViewPrivate alloc] init];
1723     [self _commonInitializationWithFrameName:frameName groupName:groupName];
1724     [self setMaintainsBackForwardList: YES];
1725     return self;
1726 }
1727
1728 - (id)initWithCoder:(NSCoder *)decoder
1729 {
1730     WebView *result = nil;
1731
1732 NS_DURING
1733
1734     NSString *frameName;
1735     NSString *groupName;
1736     WebPreferences *preferences;
1737     BOOL useBackForwardList;
1738
1739     result = [super initWithCoder:decoder];
1740     result->_private = [[WebViewPrivate alloc] init];
1741
1742     // We don't want any of the archived subviews. The subviews will always
1743     // be created in _commonInitializationFrameName:groupName:.
1744     [[result subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
1745
1746     if ([decoder allowsKeyedCoding]) {
1747         frameName = [decoder decodeObjectForKey:@"FrameName"];
1748         groupName = [decoder decodeObjectForKey:@"GroupName"];
1749         preferences = [decoder decodeObjectForKey:@"Preferences"];
1750         useBackForwardList = [decoder decodeBoolForKey:@"UseBackForwardList"];
1751     } else {
1752         int version;
1753         [decoder decodeValueOfObjCType:@encode(int) at:&version];
1754         frameName = [decoder decodeObject];
1755         groupName = [decoder decodeObject];
1756         preferences = [decoder decodeObject];
1757         if (version > 1)
1758             [decoder decodeValuesOfObjCTypes:"c", &useBackForwardList];
1759     }
1760
1761     if (![frameName isKindOfClass:[NSString class]])
1762         frameName = nil;
1763     if (![groupName isKindOfClass:[NSString class]])
1764         groupName = nil;
1765     if (![preferences isKindOfClass:[WebPreferences class]])
1766         preferences = nil;
1767
1768     LOG(Encoding, "FrameName = %@, GroupName = %@, useBackForwardList = %d\n", frameName, groupName, (int)useBackForwardList);
1769
1770     if (preferences)
1771         [result setPreferences:preferences];
1772     result->_private->useBackForwardList = useBackForwardList;
1773     [result _commonInitializationWithFrameName:frameName groupName:groupName];
1774
1775 NS_HANDLER
1776
1777     result = nil;
1778     [self release];
1779
1780 NS_ENDHANDLER
1781
1782     return result;
1783 }
1784
1785 - (void)encodeWithCoder:(NSCoder *)encoder
1786 {
1787     [super encodeWithCoder:encoder];
1788
1789     if ([encoder allowsKeyedCoding]) {
1790         [encoder encodeObject:[[self mainFrame] name] forKey:@"FrameName"];
1791         [encoder encodeObject:[self groupName] forKey:@"GroupName"];
1792         [encoder encodeObject:[self preferences] forKey:@"Preferences"];
1793         [encoder encodeBool:_private->useBackForwardList forKey:@"UseBackForwardList"];
1794     } else {
1795         int version = WebViewVersion;
1796         [encoder encodeValueOfObjCType:@encode(int) at:&version];
1797         [encoder encodeObject:[[self mainFrame] name]];
1798         [encoder encodeObject:[self groupName]];
1799         [encoder encodeObject:[self preferences]];
1800         [encoder encodeValuesOfObjCTypes:"c", &_private->useBackForwardList];
1801     }
1802
1803     LOG(Encoding, "FrameName = %@, GroupName = %@, useBackForwardList = %d\n", [[self mainFrame] name], [self groupName], (int)_private->useBackForwardList);
1804 }
1805
1806 - (void)dealloc
1807 {
1808     [self _close];
1809     
1810     --WebViewCount;
1811     
1812     [[NSNotificationCenter defaultCenter] removeObserver:self];
1813     
1814     [WebPreferences _removeReferenceForIdentifier: [self preferencesIdentifier]];
1815     
1816     [_private release];
1817     // [super dealloc] can end up dispatching against _private (3466082)
1818     _private = nil;
1819
1820     [super dealloc];
1821 }
1822
1823 - (void)finalize
1824 {
1825     [self _close];
1826
1827     --WebViewCount;
1828
1829     [[NSNotificationCenter defaultCenter] removeObserver:self];
1830
1831     [WebPreferences _removeReferenceForIdentifier: [self preferencesIdentifier]];
1832
1833     [super finalize];
1834 }
1835
1836 - (void)setPreferences:(WebPreferences *)prefs
1837 {
1838     if (_private->preferences != prefs) {
1839         [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:[self preferences]];
1840         [WebPreferences _removeReferenceForIdentifier:[_private->preferences identifier]];
1841         [_private->preferences release];
1842         _private->preferences = [prefs retain];
1843         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesChangedNotification:)
1844             name:WebPreferencesChangedNotification object:[self preferences]];
1845         [[NSNotificationCenter defaultCenter]
1846             postNotificationName:WebPreferencesChangedNotification object:prefs userInfo:nil];
1847     }
1848 }
1849
1850 - (WebPreferences *)preferences
1851 {
1852     return _private->preferences ? _private->preferences : [WebPreferences standardPreferences];
1853 }
1854
1855 - (void)setPreferencesIdentifier:(NSString *)anIdentifier
1856 {
1857     if (![anIdentifier isEqual:[[self preferences] identifier]]) {
1858         WebPreferences *prefs = [[WebPreferences alloc] initWithIdentifier:anIdentifier];
1859         [self setPreferences:prefs];
1860         [prefs release];
1861     }
1862 }
1863
1864 - (NSString *)preferencesIdentifier
1865 {
1866     return [[self preferences] identifier];
1867 }
1868
1869
1870 - (void)setUIDelegate:delegate
1871 {
1872     _private->UIDelegate = delegate;
1873     [_private->UIDelegateForwarder release];
1874     _private->UIDelegateForwarder = nil;
1875 }
1876
1877 - UIDelegate
1878 {
1879     return _private->UIDelegate;
1880 }
1881
1882 - (void)setResourceLoadDelegate: delegate
1883 {
1884     _private->resourceProgressDelegate = delegate;
1885     [_private->resourceProgressDelegateForwarder release];
1886     _private->resourceProgressDelegateForwarder = nil;
1887     [self _cacheResourceLoadDelegateImplementations];
1888 }
1889
1890
1891 - resourceLoadDelegate
1892 {
1893     return _private->resourceProgressDelegate;
1894 }
1895
1896
1897 - (void)setDownloadDelegate: delegate
1898 {
1899     _private->downloadDelegate = delegate;
1900 }
1901
1902
1903 - downloadDelegate
1904 {
1905     return _private->downloadDelegate;
1906 }
1907
1908 - (void)setPolicyDelegate:delegate
1909 {
1910     _private->policyDelegate = delegate;
1911     [_private->policyDelegateForwarder release];
1912     _private->policyDelegateForwarder = nil;
1913 }
1914
1915 - policyDelegate
1916 {
1917     return _private->policyDelegate;
1918 }
1919
1920 - (void)setFrameLoadDelegate:delegate
1921 {
1922     _private->frameLoadDelegate = delegate;
1923     [_private->frameLoadDelegateForwarder release];
1924     _private->frameLoadDelegateForwarder = nil;
1925 }
1926
1927 - frameLoadDelegate
1928 {
1929     return _private->frameLoadDelegate;
1930 }
1931
1932 - (WebFrame *)mainFrame
1933 {
1934     // This can be called in initialization, before _private has been set up (3465613)
1935     if (!_private)
1936         return nil;
1937
1938     return [(WebFrameBridge *)[_private->_pageBridge mainFrame] webFrame];
1939 }
1940
1941 - (WebBackForwardList *)backForwardList
1942 {
1943     if (_private->useBackForwardList)
1944         return _private->backForwardList;
1945     return nil;
1946 }
1947
1948 - (void)setMaintainsBackForwardList: (BOOL)flag
1949 {
1950     _private->useBackForwardList = flag;
1951 }
1952
1953 - (BOOL)goBack
1954 {
1955     WebHistoryItem *item = [[self backForwardList] backItem];
1956     
1957     if (item){
1958         [self _goToItem: item withLoadType: WebFrameLoadTypeBack];
1959         return YES;
1960     }
1961     return NO;
1962 }
1963
1964 - (BOOL)goForward
1965 {
1966     WebHistoryItem *item = [[self backForwardList] forwardItem];
1967     
1968     if (item){
1969         [self _goToItem: item withLoadType: WebFrameLoadTypeForward];
1970         return YES;
1971     }
1972     return NO;
1973 }
1974
1975 - (BOOL)goToBackForwardItem:(WebHistoryItem *)item
1976 {
1977     [self _goToItem: item withLoadType: WebFrameLoadTypeIndexedBackForward];
1978     return YES;
1979 }
1980
1981 - (void)setTextSizeMultiplier:(float)m
1982 {
1983     if (_private->textSizeMultiplier == m) {
1984         return;
1985     }
1986     _private->textSizeMultiplier = m;
1987 }
1988
1989 - (float)textSizeMultiplier
1990 {
1991     return _private->textSizeMultiplier;
1992 }
1993
1994 - (void)setApplicationNameForUserAgent:(NSString *)applicationName
1995 {
1996     NSString *name = [applicationName copy];
1997     [_private->applicationNameForUserAgent release];
1998     _private->applicationNameForUserAgent = name;
1999     if (!_private->userAgentOverridden) {
2000         [_private->userAgent release];
2001         _private->userAgent = nil;
2002     }
2003 }
2004
2005 - (NSString *)applicationNameForUserAgent
2006 {
2007     return [[_private->applicationNameForUserAgent retain] autorelease];
2008 }
2009
2010 - (void)setCustomUserAgent:(NSString *)userAgentString
2011 {
2012     NSString *override = [userAgentString copy];
2013     [_private->userAgent release];
2014     _private->userAgent = override;
2015     _private->userAgentOverridden = override != nil;
2016 }
2017
2018 - (NSString *)customUserAgent
2019 {
2020     return _private->userAgentOverridden ? [[_private->userAgent retain] autorelease] : nil;
2021 }
2022
2023 - (void)setMediaStyle:(NSString *)mediaStyle
2024 {
2025     if (_private->mediaStyle != mediaStyle) {
2026         [_private->mediaStyle release];
2027         _private->mediaStyle = [mediaStyle copy];
2028     }
2029 }
2030
2031 - (NSString *)mediaStyle
2032 {
2033     return _private->mediaStyle;
2034 }
2035
2036 - (BOOL)supportsTextEncoding
2037 {
2038     id documentView = [[[self mainFrame] frameView] documentView];
2039     return [documentView conformsToProtocol:@protocol(WebDocumentText)]
2040         && [documentView supportsTextEncoding];
2041 }
2042
2043 - (void)setCustomTextEncodingName:(NSString *)encoding
2044 {
2045     NSString *oldEncoding = [self customTextEncodingName];
2046     if (encoding == oldEncoding || [encoding isEqualToString:oldEncoding]) {
2047         return;
2048     }
2049     [[self mainFrame] _reloadAllowingStaleDataWithOverrideEncoding:encoding];
2050 }
2051
2052 - (NSString *)_mainFrameOverrideEncoding
2053 {
2054     WebDataSource *dataSource = [[self mainFrame] provisionalDataSource];
2055     if (dataSource == nil) {
2056         dataSource = [[self mainFrame] dataSource];
2057     }
2058     if (dataSource == nil) {
2059         return nil;
2060     }
2061     return [dataSource _overrideEncoding];
2062 }
2063
2064 - (NSString *)customTextEncodingName
2065 {
2066     return [self _mainFrameOverrideEncoding];
2067 }
2068
2069 - (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script
2070 {
2071     return [[[self mainFrame] _bridge] stringByEvaluatingJavaScriptFromString:script];
2072 }
2073
2074 - (WebScriptObject *)windowScriptObject
2075 {
2076     return [[[self mainFrame] _bridge] windowScriptObject];
2077 }
2078
2079         
2080 // Get the appropriate user-agent string for a particular URL.
2081 // Since we no longer automatically spoof, this no longer requires looking at the URL.
2082 - (NSString *)userAgentForURL:(NSURL *)URL
2083 {
2084     NSString *userAgent = _private->userAgent;
2085     if (userAgent) {
2086         return [[userAgent retain] autorelease];
2087     }
2088     
2089     NSString *language = [NSUserDefaults _webkit_preferredLanguageCode];
2090     id sourceVersion = [[NSBundle bundleForClass:[WebView class]]
2091         objectForInfoDictionaryKey:(id)kCFBundleVersionKey];
2092     NSString *applicationName = _private->applicationNameForUserAgent;
2093
2094     if ([applicationName length]) {
2095         userAgent = [NSString stringWithFormat:@"Mozilla/5.0 (Macintosh; U; " PROCESSOR " Mac OS X; %@) AppleWebKit/%@ (KHTML, like Gecko) %@",
2096             language, sourceVersion, applicationName];
2097     } else {
2098         userAgent = [NSString stringWithFormat:@"Mozilla/5.0 (Macintosh; U; " PROCESSOR " Mac OS X; %@) AppleWebKit/%@ (KHTML, like Gecko)",
2099             language, sourceVersion];
2100     }
2101
2102     _private->userAgent = [userAgent retain];
2103     return userAgent;
2104 }
2105
2106 - (void)setHostWindow:(NSWindow *)hostWindow
2107 {
2108     if (hostWindow != _private->hostWindow) {
2109         [[self mainFrame] _viewWillMoveToHostWindow:hostWindow];
2110         [_private->hostWindow release];
2111         _private->hostWindow = [hostWindow retain];
2112         [[self mainFrame] _viewDidMoveToHostWindow];
2113     }
2114 }
2115
2116 - (NSWindow *)hostWindow
2117 {
2118     return _private->hostWindow;
2119 }
2120
2121 - (NSView <WebDocumentView> *)documentViewAtWindowPoint:(NSPoint)point
2122 {
2123     return [[self _frameViewAtWindowPoint:point] documentView];
2124 }
2125
2126 - (NSView <WebDocumentDragging> *)_draggingDocumentViewAtWindowPoint:(NSPoint)point
2127 {
2128     NSView <WebDocumentView> *documentView = [self documentViewAtWindowPoint:point];
2129     if ([documentView conformsToProtocol:@protocol(WebDocumentDragging)]) {
2130         return (NSView <WebDocumentDragging> *)documentView;
2131     }
2132     return nil;
2133 }
2134
2135 - (NSDictionary *)_elementAtWindowPoint:(NSPoint)windowPoint
2136 {
2137     WebFrameView *frameView = [self _frameViewAtWindowPoint:windowPoint];
2138     if (!frameView)
2139         return nil;
2140     NSView <WebDocumentView> *documentView = [frameView documentView];
2141     if ([documentView conformsToProtocol:@protocol(WebDocumentElement)]) {
2142         NSPoint point = [documentView convertPoint:windowPoint fromView:nil];
2143         return [(NSView <WebDocumentElement> *)documentView elementAtPoint:point];
2144     }
2145     return [NSDictionary dictionaryWithObject:[frameView webFrame] forKey:WebElementFrameKey];
2146 }
2147
2148 - (NSDictionary *)elementAtPoint:(NSPoint)point
2149 {
2150     return [self _elementAtWindowPoint:[self convertPoint:point toView:nil]];
2151 }
2152
2153 - (void)_setDraggingDocumentView:(NSView <WebDocumentDragging> *)newDraggingView
2154 {
2155     if (_private->draggingDocumentView != newDraggingView) {
2156         [_private->draggingDocumentView release];
2157         _private->draggingDocumentView = [newDraggingView retain];
2158     }
2159 }
2160
2161 - (NSDragOperation)_loadingDragOperationForDraggingInfo:(id <NSDraggingInfo>)draggingInfo
2162 {
2163     if (_private->dragDestinationActionMask & WebDragDestinationActionLoad) {
2164         NSPoint windowPoint = [draggingInfo draggingLocation];
2165         NSView *view = [self hitTest:[[self superview] convertPoint:windowPoint toView:nil]];
2166         // Don't accept the drag over a plug-in since plug-ins may want to handle it.
2167         if (![view isKindOfClass:[WebBaseNetscapePluginView class]] && !_private->editable && !_private->initiatedDrag) {
2168             // If not editing or dragging, use _web_dragOperationForDraggingInfo to find a URL to load on the pasteboard.
2169             return [self _web_dragOperationForDraggingInfo:draggingInfo];
2170         }
2171     }
2172     return NSDragOperationNone;
2173 }
2174
2175 - (NSDragOperation)_delegateDragOperationForDraggingInfo:(id <NSDraggingInfo>)draggingInfo
2176 {
2177     NSPoint windowPoint = [draggingInfo draggingLocation];
2178     NSView <WebDocumentDragging> *newDraggingView = [self _draggingDocumentViewAtWindowPoint:windowPoint];
2179     if (_private->draggingDocumentView != newDraggingView) {
2180         [_private->draggingDocumentView draggingCancelledWithDraggingInfo:draggingInfo];
2181         [self _setDraggingDocumentView:newDraggingView];
2182     }
2183     
2184     _private->dragDestinationActionMask = [[self _UIDelegateForwarder] webView:self dragDestinationActionMaskForDraggingInfo:draggingInfo];
2185     NSDragOperation operation = NSDragOperationNone;
2186     
2187     if (_private->dragDestinationActionMask == WebDragDestinationActionNone) {
2188         [_private->draggingDocumentView draggingCancelledWithDraggingInfo:draggingInfo];
2189     } else {
2190         operation = [_private->draggingDocumentView draggingUpdatedWithDraggingInfo:draggingInfo actionMask:_private->dragDestinationActionMask];
2191         if (operation == NSDragOperationNone) {
2192             return [self _loadingDragOperationForDraggingInfo:draggingInfo];
2193         }
2194     }
2195     
2196     return operation;
2197 }
2198
2199 // The following 2 internal NSView methods are called on the drag destination by make scrolling while dragging work.
2200 // Scrolling while dragging will only work if the drag destination is in a scroll view. The WebView is the drag destination. 
2201 // When dragging to a WebView, the document subview should scroll, but it doesn't because it is not the drag destination. 
2202 // Forward these calls to the document subview to make its scroll view scroll.
2203 - (void)_autoscrollForDraggingInfo:(id)draggingInfo timeDelta:(NSTimeInterval)repeatDelta
2204 {
2205     if (![self isEditable])
2206         return;
2207     
2208     NSView <WebDocumentView> *documentView = [self documentViewAtWindowPoint:[draggingInfo draggingLocation]];
2209     [documentView _autoscrollForDraggingInfo:draggingInfo timeDelta:repeatDelta];
2210 }
2211
2212 - (BOOL)_shouldAutoscrollForDraggingInfo:(id)draggingInfo
2213 {
2214     if (![self isEditable])
2215         return NO;
2216     
2217     NSView <WebDocumentView> *documentView = [self documentViewAtWindowPoint:[draggingInfo draggingLocation]];
2218     return [documentView _shouldAutoscrollForDraggingInfo:draggingInfo];
2219 }
2220
2221 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)draggingInfo
2222 {
2223     return [self _delegateDragOperationForDraggingInfo:draggingInfo];
2224 }
2225
2226 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)draggingInfo
2227 {
2228     return [self _delegateDragOperationForDraggingInfo:draggingInfo];
2229 }
2230
2231 - (void)draggingExited:(id <NSDraggingInfo>)draggingInfo
2232 {
2233     [_private->draggingDocumentView draggingCancelledWithDraggingInfo:draggingInfo];
2234     [self _setDraggingDocumentView:nil];
2235 }
2236
2237 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)draggingInfo
2238 {
2239     return YES;
2240 }
2241
2242 - (BOOL)performDragOperation:(id <NSDraggingInfo>)draggingInfo
2243 {
2244     ASSERT(_private->draggingDocumentView == [self _draggingDocumentViewAtWindowPoint:[draggingInfo draggingLocation]]);
2245     
2246     if ([_private->draggingDocumentView concludeDragForDraggingInfo:draggingInfo actionMask:_private->dragDestinationActionMask]) {
2247         [self _setDraggingDocumentView:nil];
2248         return YES;
2249     }
2250     
2251     [self _setDraggingDocumentView:nil];
2252         
2253     if ([self _loadingDragOperationForDraggingInfo:draggingInfo] != NSDragOperationNone) {    
2254         NSURL *URL = [[self class] URLFromPasteboard:[draggingInfo draggingPasteboard]];
2255         if (URL != nil) {
2256             [[self _UIDelegateForwarder] webView:self willPerformDragDestinationAction:WebDragDestinationActionLoad forDraggingInfo:draggingInfo];
2257             NSURLRequest *request = [[NSURLRequest alloc] initWithURL:URL];
2258             [[self mainFrame] loadRequest:request];
2259             [request release];
2260             return YES;
2261         }
2262     }
2263     
2264     return NO;
2265 }
2266
2267 - (NSView *)_hitTest:(NSPoint *)aPoint dragTypes:(NSSet *)types
2268 {
2269     NSView *hitView = [super _hitTest:aPoint dragTypes:types];
2270     if (!hitView && [[self superview] mouse:*aPoint inRect:[self frame]]) {
2271         return self;
2272     } else {
2273         return hitView;
2274     }
2275 }
2276
2277 - (BOOL)acceptsFirstResponder
2278 {
2279     return [[[self mainFrame] frameView] acceptsFirstResponder];
2280 }
2281
2282 - (BOOL)becomeFirstResponder
2283 {
2284     // This works together with setNextKeyView to splice the WebView into
2285     // the key loop similar to the way NSScrollView does this. Note that
2286     // WebFrameView has very similar code.
2287     NSWindow *window = [self window];
2288     WebFrameView *mainFrameView = [[self mainFrame] frameView];
2289     
2290     if ([window keyViewSelectionDirection] == NSSelectingPrevious) {
2291         NSView *previousValidKeyView = [self previousValidKeyView];
2292         if ((previousValidKeyView != self) && (previousValidKeyView != mainFrameView)) {
2293             [window makeFirstResponder:previousValidKeyView];
2294             return YES;
2295         } else {
2296             return NO;
2297         }
2298     }
2299     
2300     if ([mainFrameView acceptsFirstResponder]) {
2301         [window makeFirstResponder:mainFrameView];
2302         return YES;
2303     } 
2304     
2305     return NO;
2306 }
2307
2308 - (NSView *)_webcore_effectiveFirstResponder
2309 {
2310     WebFrameView *frameView = [[self mainFrame] frameView];
2311     return frameView ? [frameView _webcore_effectiveFirstResponder] : [super _webcore_effectiveFirstResponder];
2312 }
2313
2314 - (void)setNextKeyView:(NSView *)aView
2315 {
2316     // This works together with becomeFirstResponder to splice the WebView into
2317     // the key loop similar to the way NSScrollView does this. Note that
2318     // WebFrameView has very similar code.
2319     WebFrameView *mainFrameView = [[self mainFrame] frameView];
2320     if (mainFrameView != nil) {
2321         [mainFrameView setNextKeyView:aView];
2322     } else {
2323         [super setNextKeyView:aView];
2324     }
2325 }
2326
2327 static WebFrame *incrementFrame(WebFrame *curr, BOOL forward, BOOL wrapFlag)
2328 {
2329     return forward ? [curr _nextFrameWithWrap:wrapFlag]
2330                    : [curr _previousFrameWithWrap:wrapFlag];
2331 }
2332
2333 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
2334 {
2335     // Get the frame holding the selection, or start with the main frame
2336     WebFrame *startFrame = [self _selectedOrMainFrame];
2337
2338     // Search the first frame, then all the other frames, in order
2339     NSView <WebDocumentSearching> *startSearchView = nil;
2340     BOOL startHasSelection = NO;
2341     WebFrame *frame = startFrame;
2342     do {
2343         id <WebDocumentView> view = [[frame frameView] documentView];
2344         if ([view conformsToProtocol:@protocol(WebDocumentSearching)]) {
2345             NSView <WebDocumentSearching> *searchView = (NSView <WebDocumentSearching> *)view;
2346
2347             // first time through
2348             if (frame == startFrame) {
2349                 // Remember if start even has a selection, to know if we need to search more later
2350                 if ([searchView isKindOfClass:[WebHTMLView class]]) {
2351                     // optimization for the common case, to avoid making giant string for selection
2352                     startHasSelection = [[startFrame _bridge] selectedDOMRange] != nil;
2353                 } else if ([searchView conformsToProtocol:@protocol(WebDocumentText)]) {
2354                     startHasSelection = [(id <WebDocumentText>)searchView selectedString] != nil;
2355                 }
2356                 startSearchView = searchView;
2357             }
2358             
2359             if ([searchView searchFor:string direction:forward caseSensitive:caseFlag wrap:NO]) {
2360                 if (frame != startFrame)
2361                     [startFrame _clearSelection];
2362                 [[self window] makeFirstResponder:searchView];
2363                 return YES;
2364             }
2365         }
2366         frame = incrementFrame(frame, forward, wrapFlag);
2367     } while (frame != nil && frame != startFrame);
2368
2369     // Search contents of startFrame, on the other side of the selection that we did earlier.
2370     // We cheat a bit and just research with wrap on
2371     if (wrapFlag && startHasSelection && startSearchView) {
2372         if ([startSearchView searchFor:string direction:forward caseSensitive:caseFlag wrap:YES]) {
2373             [[self window] makeFirstResponder:startSearchView];
2374             return YES;
2375         }
2376     }
2377     return NO;
2378 }
2379
2380 + (void)registerViewClass:(Class)viewClass representationClass:(Class)representationClass forMIMEType:(NSString *)MIMEType
2381 {
2382     [[WebFrameView _viewTypesAllowImageTypeOmission:YES] setObject:viewClass forKey:MIMEType];
2383     [[WebDataSource _repTypesAllowImageTypeOmission:YES] setObject:representationClass forKey:MIMEType];
2384 }
2385
2386 - (void)setGroupName:(NSString *)groupName
2387 {
2388     [[self _pageBridge] setGroupName:groupName];
2389 }
2390
2391 - (NSString *)groupName
2392 {
2393     return [[self _pageBridge] groupName];
2394 }
2395
2396 - (double)estimatedProgress
2397 {
2398     return _private->progressValue;
2399 }
2400
2401 - (NSArray *)pasteboardTypesForSelection
2402 {
2403     NSView <WebDocumentView> *documentView = [[[self _selectedOrMainFrame] frameView] documentView];
2404     if ([documentView conformsToProtocol:@protocol(WebDocumentSelection)]) {
2405         return [(NSView <WebDocumentSelection> *)documentView pasteboardTypesForSelection];
2406     }
2407     return [NSArray array];
2408 }
2409
2410 - (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
2411 {
2412     WebFrameBridge *bridge = [self _bridgeForSelectedOrMainFrame];
2413     if ([bridge selectionState] != WebSelectionStateRange) {
2414         NSView <WebDocumentView> *documentView = [[[bridge webFrame] frameView] documentView];
2415         if ([documentView conformsToProtocol:@protocol(WebDocumentSelection)]) {
2416             [(NSView <WebDocumentSelection> *)documentView writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2417         }
2418     }
2419 }
2420
2421 - (NSArray *)pasteboardTypesForElement:(NSDictionary *)element
2422 {
2423     if ([element objectForKey:WebElementImageURLKey] != nil) {
2424         return [NSPasteboard _web_writableTypesForImageIncludingArchive:([element objectForKey:WebElementDOMNodeKey] != nil)];
2425     } else if ([element objectForKey:WebElementLinkURLKey] != nil) {
2426         return [NSPasteboard _web_writableTypesForURL];
2427     } else if ([[element objectForKey:WebElementIsSelectedKey] boolValue]) {
2428         return [self pasteboardTypesForSelection];
2429     }
2430     return [NSArray array];
2431 }
2432
2433 - (void)writeElement:(NSDictionary *)element withPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
2434 {
2435     if ([element objectForKey:WebElementImageURLKey] != nil) {
2436         [self _writeImageForElement:element withPasteboardTypes:types toPasteboard:pasteboard];
2437     } else if ([element objectForKey:WebElementLinkURLKey] != nil) {
2438         [self _writeLinkElement:element withPasteboardTypes:types toPasteboard:pasteboard];
2439     } else if ([[element objectForKey:WebElementIsSelectedKey] boolValue]) {
2440         [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2441     }
2442 }
2443
2444 - (void)moveDragCaretToPoint:(NSPoint)point
2445 {
2446     [[[self mainFrame] _bridge] moveDragCaretToPoint:[self convertPoint:point toView:[[[self mainFrame] frameView] documentView]]];
2447 }
2448
2449 - (void)removeDragCaret
2450 {
2451     [[[self mainFrame] _bridge] removeDragCaret];
2452 }
2453
2454 - (void)_inspectElement:(id)sender
2455 {
2456     NSDictionary *element = [sender representedObject];
2457     WebFrame *frame = [element objectForKey:WebElementFrameKey];
2458     DOMNode *node = [element objectForKey:WebElementDOMNodeKey];
2459     if (!node || !frame)
2460         return;
2461
2462     if ([node nodeType] != DOM_ELEMENT_NODE || [node nodeType] != DOM_DOCUMENT_NODE)
2463         node = [node parentNode];
2464
2465     WebInspector *inspector = [WebInspector sharedWebInspector];
2466     [inspector setWebFrame:frame];
2467     [inspector setFocusedDOMNode:node];
2468
2469     node = [node parentNode];
2470     node = [node parentNode];
2471     if (node) // set the root node to something relatively close to the focused node
2472         [inspector setRootDOMNode:node];
2473
2474     [inspector showWindow:nil];
2475 }
2476 @end
2477
2478 @implementation WebView (WebIBActions)
2479
2480 - (IBAction)takeStringURLFrom: sender
2481 {
2482     NSString *URLString = [sender stringValue];
2483     
2484     [[self mainFrame] loadRequest: [NSURLRequest requestWithURL: [NSURL _web_URLWithDataAsString: URLString]]];
2485 }
2486
2487 - (BOOL)canGoBack
2488 {
2489     return [[self backForwardList] backItem] != nil;
2490 }
2491
2492 - (BOOL)canGoForward
2493 {
2494     return [[self backForwardList] forwardItem] != nil;
2495 }
2496
2497 - (IBAction)goBack:(id)sender
2498 {
2499     [self goBack];
2500 }
2501
2502 - (IBAction)goForward:(id)sender
2503 {
2504     [self goForward];
2505 }
2506
2507 - (IBAction)stopLoading:(id)sender
2508 {
2509     [[self mainFrame] stopLoading];
2510 }
2511
2512 - (IBAction)reload:(id)sender
2513 {
2514     [[self mainFrame] reload];
2515 }
2516
2517 #define MinimumTextSizeMultiplier       0.5
2518 #define MaximumTextSizeMultiplier       3.0
2519 #define TextSizeMultiplierRatio         1.2
2520
2521 - (BOOL)canMakeTextSmaller
2522 {
2523     BOOL canShrinkMore = _private->textSizeMultiplier/TextSizeMultiplierRatio > MinimumTextSizeMultiplier;
2524     return [self _performTextSizingSelector:(SEL)0 withObject:nil onTrackingDocs:canShrinkMore selForNonTrackingDocs:@selector(_canMakeTextSmaller) newScaleFactor:0];
2525 }
2526
2527 - (BOOL)canMakeTextLarger
2528 {
2529     BOOL canGrowMore = _private->textSizeMultiplier*TextSizeMultiplierRatio < MaximumTextSizeMultiplier;
2530     return [self _performTextSizingSelector:(SEL)0 withObject:nil onTrackingDocs:canGrowMore selForNonTrackingDocs:@selector(_canMakeTextLarger) newScaleFactor:0];
2531 }
2532
2533 - (IBAction)makeTextSmaller:(id)sender
2534 {
2535     float newScale = _private->textSizeMultiplier/TextSizeMultiplierRatio;
2536     BOOL canShrinkMore = newScale > MinimumTextSizeMultiplier;
2537     [self _performTextSizingSelector:@selector(_makeTextSmaller:) withObject:sender onTrackingDocs:canShrinkMore selForNonTrackingDocs:@selector(_canMakeTextSmaller) newScaleFactor:newScale];
2538 }
2539
2540 - (IBAction)makeTextLarger:(id)sender
2541 {
2542     float newScale = _private->textSizeMultiplier*TextSizeMultiplierRatio;
2543     BOOL canGrowMore = newScale < MaximumTextSizeMultiplier;
2544     [self _performTextSizingSelector:@selector(_makeTextLarger:) withObject:sender onTrackingDocs:canGrowMore selForNonTrackingDocs:@selector(_canMakeTextLarger) newScaleFactor:newScale];
2545 }
2546
2547 - (BOOL)_responderValidateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2548 {
2549     id responder = [self _responderForResponderOperations];
2550     if (responder != self && [responder respondsToSelector:[item action]]) {
2551         if ([responder respondsToSelector:@selector(validateUserInterfaceItem:)]) {
2552             return [responder validateUserInterfaceItem:item];
2553         }
2554         return YES;
2555     }
2556     return NO;
2557 }
2558
2559 #define VALIDATE(name) \
2560     else if (action == @selector(name:)) { return [self _responderValidateUserInterfaceItem:item]; }
2561
2562 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2563 {
2564     SEL action = [item action];
2565
2566     if (action == @selector(goBack:)) {
2567         return [self canGoBack];
2568     } else if (action == @selector(goForward:)) {
2569         return [self canGoForward];
2570     } else if (action == @selector(makeTextLarger:)) {
2571         return [self canMakeTextLarger];
2572     } else if (action == @selector(makeTextSmaller:)) {
2573         return [self canMakeTextSmaller];
2574     } else if (action == @selector(makeTextStandardSize:)) {
2575         return [self canMakeTextStandardSize];
2576     } else if (action == @selector(reload:)) {
2577         return [[self mainFrame] dataSource] != nil;
2578     } else if (action == @selector(stopLoading:)) {
2579         return [self _isLoading];
2580     } else if (action == @selector(toggleContinuousSpellChecking:)) {
2581         BOOL checkMark = NO;
2582         BOOL retVal = NO;
2583         if ([self isEditable] && [self _continuousCheckingAllowed]) {
2584             checkMark = [self isContinuousSpellCheckingEnabled];
2585             retVal = YES;
2586         }
2587         if ([(NSObject *)item isKindOfClass:[NSMenuItem class]]) {
2588             NSMenuItem *menuItem = (NSMenuItem *)item;
2589             [menuItem setState:checkMark ? NSOnState : NSOffState];
2590         }
2591         return retVal;
2592     }
2593     FOR_EACH_RESPONDER_SELECTOR(VALIDATE)
2594
2595     return YES;
2596 }
2597
2598 @end
2599
2600 @implementation WebView (WebPendingPublic)
2601
2602 - (void)setMainFrameURL:(NSString *)URLString
2603 {
2604     [[self mainFrame] loadRequest: [NSURLRequest requestWithURL: [NSURL _web_URLWithDataAsString: URLString]]];
2605 }
2606
2607 - (NSString *)mainFrameURL
2608 {
2609     WebDataSource *ds;
2610     ds = [[self mainFrame] provisionalDataSource];
2611     if (!ds)
2612         ds = [[self mainFrame] dataSource];
2613     return [[[ds request] URL] _web_originalDataAsString];
2614 }
2615
2616 - (BOOL)isLoading
2617 {
2618     LOG (Bindings, "isLoading = %d", (int)[self _isLoading]);
2619     return [self _isLoading];
2620 }
2621
2622 - (NSString *)mainFrameTitle
2623 {
2624     NSString *mainFrameTitle = [[[self mainFrame] dataSource] pageTitle];
2625     return (mainFrameTitle != nil) ? mainFrameTitle : (NSString *)@"";
2626 }
2627
2628 - (NSImage *)mainFrameIcon
2629 {
2630     return [[WebIconDatabase sharedIconDatabase] iconForURL:[[[[self mainFrame] dataSource] _URL] _web_originalDataAsString] withSize:WebIconSmallSize];
2631 }
2632
2633 - (void)setMainFrameDocumentReady:(BOOL)mainFrameDocumentReady
2634 {
2635     // by setting this to NO, calls to mainFrameDocument are forced to return nil
2636     // setting this to YES lets it return the actual DOMDocument value
2637     // we use this to tell NSTreeController to reset its observers and clear its state
2638     if (_private->mainFrameDocumentReady == mainFrameDocumentReady)
2639         return;
2640     [self _willChangeValueForKey:_WebMainFrameDocumentKey];
2641     _private->mainFrameDocumentReady = mainFrameDocumentReady;
2642     [self _didChangeValueForKey:_WebMainFrameDocumentKey];
2643     // this will cause observers to call mainFrameDocument where this flag will be checked
2644 }
2645
2646 - (DOMDocument *)mainFrameDocument
2647 {
2648     // only return the actual value if the state we're in gives NSTreeController
2649     // enough time to release its observers on the old model
2650     if (_private->mainFrameDocumentReady)
2651         return [[self mainFrame] DOMDocument];
2652     return nil;
2653 }
2654
2655 - (void)setDrawsBackground:(BOOL)drawsBackground
2656 {
2657     if (_private->drawsBackground == drawsBackground)
2658         return;
2659     _private->drawsBackground = drawsBackground;
2660     [[self mainFrame] _updateDrawsBackground];
2661 }
2662
2663 - (BOOL)drawsBackground
2664 {
2665     return _private->drawsBackground;
2666 }
2667
2668 - (void)toggleSmartInsertDelete:(id)sender
2669 {
2670     if ([self isEditable]) {
2671         [self setSmartInsertDeleteEnabled:![self smartInsertDeleteEnabled]];
2672     }
2673 }
2674
2675 - (IBAction)toggleContinuousSpellChecking:(id)sender
2676 {
2677     if ([self isEditable]) {
2678         [self setContinuousSpellCheckingEnabled:![self isContinuousSpellCheckingEnabled]];
2679     }
2680 }
2681
2682 - (BOOL)maintainsInactiveSelection
2683 {
2684     return [self isEditable];
2685 }
2686
2687 // This method name is used by Mail on Tiger (but not post-Tiger), so we shouldn't delete it 
2688 // until the day comes when we're no longer supporting Mail on Tiger.
2689 - (WebFrame *)_frameForCurrentSelection
2690 {
2691     return [self _selectedOrMainFrame];
2692 }
2693
2694 - (WebFrame *)selectedFrame
2695 {
2696     // If the first responder is a view in our tree, we get the frame containing the first responder.
2697     // This is faster than searching the frame hierarchy, and will give us a result even in the case
2698     // where the focused frame doesn't actually contain a selection.
2699     WebFrame *focusedFrame = [self _focusedFrame];
2700     if (focusedFrame)
2701         return focusedFrame;
2702     
2703     // If the first responder is outside of our view tree, we search for a frame containing a selection.
2704     // There should be at most only one of these.
2705     return [[self mainFrame] _findFrameWithSelection];
2706     
2707     return nil;
2708 }
2709
2710 - (BOOL)canMakeTextStandardSize
2711 {
2712     BOOL notAlreadyStandard = _private->textSizeMultiplier != 1.0;
2713     return [self _performTextSizingSelector:(SEL)0 withObject:nil onTrackingDocs:notAlreadyStandard selForNonTrackingDocs:@selector(_canMakeTextStandardSize) newScaleFactor:0];
2714 }
2715
2716 - (IBAction)makeTextStandardSize:(id)sender
2717 {
2718     BOOL notAlreadyStandard = _private->textSizeMultiplier != 1.0;
2719     [self _performTextSizingSelector:@selector(_makeTextStandardSize:) withObject:sender onTrackingDocs:notAlreadyStandard selForNonTrackingDocs:@selector(_canMakeTextStandardSize) newScaleFactor:1.0];
2720 }
2721
2722 - (void)setScriptDebugDelegate:delegate
2723 {
2724     _private->scriptDebugDelegate = delegate;
2725     [_private->scriptDebugDelegateForwarder release];
2726     _private->scriptDebugDelegateForwarder = nil;
2727 }
2728
2729 - scriptDebugDelegate
2730 {
2731     return _private->scriptDebugDelegate;
2732 }
2733
2734 - (BOOL)shouldClose
2735 {
2736     WebFrameBridge *bridge = [[self mainFrame] _bridge];
2737     if (!bridge)
2738         return YES;
2739     return [bridge shouldClose];
2740 }
2741
2742 - (NSAppleEventDescriptor *)aeDescByEvaluatingJavaScriptFromString:(NSString *)script
2743 {
2744     return [[[self mainFrame] _bridge] aeDescByEvaluatingJavaScriptFromString:script];
2745 }
2746
2747 - (unsigned)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag highlight:(BOOL)highlight
2748 {
2749     WebFrame *frame = [self mainFrame];
2750     unsigned matchCount = 0;
2751     do {
2752         id <WebDocumentView> view = [[frame frameView] documentView];
2753         // FIXME: introduce a protocol, or otherwise make this work with other types
2754         if ([view isKindOfClass:[WebHTMLView class]])
2755             [(WebHTMLView *)view setMarkedTextMatchesAreHighlighted:highlight];
2756             matchCount += [(WebHTMLView *)view markAllMatchesForText:string caseSensitive:caseFlag];
2757
2758         frame = incrementFrame(frame, YES, NO);
2759     } while (frame);
2760     
2761     return matchCount;
2762 }
2763
2764 - (void)unmarkAllTextMatches
2765 {
2766     WebFrame *frame = [self mainFrame];
2767     do {
2768         id <WebDocumentView> view = [[frame frameView] documentView];
2769         // FIXME: introduce a protocol, or otherwise make this work with other types
2770         if ([view isKindOfClass:[WebHTMLView class]])
2771             [(WebHTMLView *)view unmarkAllTextMatches];
2772         
2773         frame = incrementFrame(frame, YES, NO);
2774     } while (frame);
2775 }
2776
2777 - (NSArray *)rectsForTextMatches
2778 {
2779     NSMutableArray *result = [NSMutableArray array];
2780     WebFrame *frame = [self mainFrame];
2781     do {
2782         id <WebDocumentView> view = [[frame frameView] documentView];
2783         // FIXME: introduce a protocol, or otherwise make this work with other types
2784         if ([view isKindOfClass:[WebHTMLView class]]) {
2785             WebHTMLView *htmlView = (WebHTMLView *)view;
2786             NSArray *originalRects = [htmlView rectsForTextMatches];
2787             unsigned rectCount = [originalRects count];
2788             unsigned rectIndex;
2789             NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2790             for (rectIndex = 0; rectIndex < rectCount; ++rectIndex) {
2791                 NSRect r = [[originalRects objectAtIndex:rectIndex] rectValue];
2792                 if (NSIsEmptyRect(r))
2793                     continue;
2794                 
2795                 // Convert rect to our coordinate system
2796                 r = [htmlView convertRect:r toView:self];
2797                 [result addObject:[NSValue valueWithRect:r]];
2798                 if (rectIndex % 10 == 0)
2799                     [pool drain];
2800             }
2801             [pool release];
2802         }
2803         
2804         frame = incrementFrame(frame, YES, NO);
2805     } while (frame);
2806     
2807     return result;
2808 }
2809
2810 @end
2811
2812 @implementation WebView (WebViewPrintingPrivate)
2813
2814 - (float)_headerHeight
2815 {
2816     if ([[self UIDelegate] respondsToSelector:@selector(webViewHeaderHeight:)]) {
2817         return [[self UIDelegate] webViewHeaderHeight:self];
2818     }
2819     
2820 #ifdef DEBUG_HEADER_AND_FOOTER
2821     return 25;
2822 #else
2823     return 0;
2824 #endif
2825 }
2826
2827 - (float)_footerHeight
2828 {
2829     if ([[self UIDelegate] respondsToSelector:@selector(webViewFooterHeight:)]) {
2830         return [[self UIDelegate] webViewFooterHeight:self];
2831     }
2832     
2833 #ifdef DEBUG_HEADER_AND_FOOTER
2834     return 50;
2835 #else
2836     return 0;
2837 #endif
2838 }
2839
2840 - (void)_drawHeaderInRect:(NSRect)rect
2841 {
2842 #ifdef DEBUG_HEADER_AND_FOOTER
2843     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
2844     [currentContext saveGraphicsState];
2845     [[NSColor yellowColor] set];
2846     NSRectFill(rect);
2847     [currentContext restoreGraphicsState];
2848 #endif
2849     
2850     if ([[self UIDelegate] respondsToSelector:@selector(webView:drawHeaderInRect:)]) {
2851         NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
2852         [currentContext saveGraphicsState];
2853         NSRectClip(rect);
2854         [[self UIDelegate] webView:self drawHeaderInRect:rect]; 
2855         [currentContext restoreGraphicsState];
2856     }
2857 }
2858
2859 - (void)_drawFooterInRect:(NSRect)rect
2860 {
2861 #ifdef DEBUG_HEADER_AND_FOOTER
2862     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
2863     [currentContext saveGraphicsState];
2864     [[NSColor cyanColor] set];
2865     NSRectFill(rect);
2866     [currentContext restoreGraphicsState];
2867 #endif
2868     
2869     if ([[self UIDelegate] respondsToSelector:@selector(webView:drawFooterInRect:)]) {
2870         NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
2871         [currentContext saveGraphicsState];
2872         NSRectClip(rect);
2873         [[self UIDelegate] webView:self drawFooterInRect:rect];
2874         [currentContext restoreGraphicsState];
2875     }
2876 }
2877
2878 - (void)_adjustPrintingMarginsForHeaderAndFooter
2879 {
2880     NSPrintOperation *op = [NSPrintOperation currentOperation];
2881     NSPrintInfo *info = [op printInfo];
2882     float scale = [op _web_pageSetupScaleFactor];
2883     [info setTopMargin:[info topMargin] + [self _headerHeight]*scale];
2884     [info setBottomMargin:[info bottomMargin] + [self _footerHeight]*scale];
2885 }
2886
2887 - (void)_drawHeaderAndFooter
2888 {
2889     // The header and footer rect height scales with the page, but the width is always
2890     // all the way across the printed page (inset by printing margins).
2891     NSPrintOperation *op = [NSPrintOperation currentOperation];
2892     float scale = [op _web_pageSetupScaleFactor];
2893     NSPrintInfo *printInfo = [op printInfo];
2894     NSSize paperSize = [printInfo paperSize];
2895     float headerFooterLeft = [printInfo leftMargin]/scale;
2896     float headerFooterWidth = (paperSize.width - ([printInfo leftMargin] + [printInfo rightMargin]))/scale;
2897     NSRect footerRect = NSMakeRect(headerFooterLeft, [printInfo bottomMargin]/scale - [self _footerHeight] , 
2898                                    headerFooterWidth, [self _footerHeight]);
2899     NSRect headerRect = NSMakeRect(headerFooterLeft, (paperSize.height - [printInfo topMargin])/scale, 
2900                                    headerFooterWidth, [self _headerHeight]);
2901     
2902     [self _drawHeaderInRect:headerRect];
2903     [self _drawFooterInRect:footerRect];
2904 }
2905 @end
2906
2907 @implementation WebView (WebDebugBinding)
2908
2909 - (void)addObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
2910 {
2911     LOG (Bindings, "addObserver:%p forKeyPath:%@ options:%x context:%p", anObserver, keyPath, options, context);
2912     [super addObserver:anObserver forKeyPath:keyPath options:options context:context];
2913 }
2914
2915 - (void)removeObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath
2916 {
2917     LOG (Bindings, "removeObserver:%p forKeyPath:%@", anObserver, keyPath);
2918     [super removeObserver:anObserver forKeyPath:keyPath];
2919 }
2920
2921 @end
2922
2923 //==========================================================================================
2924 // Editing
2925
2926 @implementation WebView (WebViewCSS)
2927
2928 - (DOMCSSStyleDeclaration *)computedStyleForElement:(DOMElement *)element pseudoElement:(NSString *)pseudoElement
2929 {
2930     // FIXME: is this the best level for this conversion?
2931     if (pseudoElement == nil) {
2932         pseudoElement = @"";
2933     }
2934     return [[element ownerDocument] getComputedStyle:element :pseudoElement];
2935 }
2936
2937 @end
2938
2939 @implementation WebView (WebViewEditing)
2940
2941 - (DOMRange *)editableDOMRangeForPoint:(NSPoint)point
2942 {
2943     WebFrameBridge *bridge = [self _bridgeAtPoint:point];
2944     return [bridge editableDOMRangeForPoint:[self convertPoint:point toView:[[[bridge webFrame] frameView] documentView]]];
2945 }
2946
2947 - (BOOL)_shouldChangeSelectedDOMRange:(DOMRange *)currentRange toDOMRange:(DOMRange *)proposedRange affinity:(NSSelectionAffinity)selectionAffinity stillSelecting:(BOOL)flag;
2948 {
2949     return [[self _editingDelegateForwarder] webView:self shouldChangeSelectedDOMRange:currentRange toDOMRange:proposedRange affinity:selectionAffinity stillSelecting:flag];
2950 }
2951
2952 - (BOOL)_shouldBeginEditingInDOMRange:(DOMRange *)range
2953 {
2954     return [[self _editingDelegateForwarder] webView:self shouldBeginEditingInDOMRange:range];
2955 }
2956
2957 - (BOOL)_shouldEndEditingInDOMRange:(DOMRange *)range
2958 {
2959     return [[self _editingDelegateForwarder] webView:self shouldEndEditingInDOMRange:range];
2960 }
2961
2962 - (BOOL)_canPaste
2963 {
2964     id documentView = [[[self mainFrame] frameView] documentView];
2965     return [documentView respondsToSelector:@selector(_canPaste)] && [documentView _canPaste];
2966 }
2967
2968 - (void)setSelectedDOMRange:(DOMRange *)range affinity:(NSSelectionAffinity)selectionAffinity
2969 {
2970     if (range == nil) {
2971         [[self _bridgeForSelectedOrMainFrame] deselectText];
2972     } else {
2973         // Derive the bridge to use from the range passed in.
2974         // Using _bridgeForSelectedOrMainFrame could give us a different document than
2975         // the one the range uses.
2976         [[[range startContainer] _bridge] setSelectedDOMRange:range affinity:selectionAffinity closeTyping:YES];
2977     }
2978 }
2979
2980 - (DOMRange *)selectedDOMRange
2981 {
2982     return [[self _bridgeForSelectedOrMainFrame] selectedDOMRange];
2983 }
2984
2985 - (NSSelectionAffinity)selectionAffinity
2986 {
2987     return [[self _bridgeForSelectedOrMainFrame] selectionAffinity];
2988 }
2989
2990 - (void)setEditable:(BOOL)flag
2991 {
2992     if (_private->editable != flag) {
2993         _private->editable = flag;
2994         WebFrameBridge *bridge = [[self mainFrame] _bridge];
2995         if (flag) {
2996             [bridge applyEditingStyleToBodyElement];
2997             // If the WebView is made editable and the selection is empty, set it to something.
2998             if ([self selectedDOMRange] == nil)
2999                 [bridge setSelectionFromNone];
3000         }
3001         else {
3002             [bridge removeEditingStyleFromBodyElement];
3003         }
3004     }
3005 }
3006
3007 - (BOOL)isEditable
3008 {
3009     return _private->editable;
3010 }
3011
3012 - (void)setTypingStyle:(DOMCSSStyleDeclaration *)style
3013 {
3014     // We don't know enough at thls level to pass in a relevant WebUndoAction; we'd have to
3015     // change the API to allow this.
3016     [[self _bridgeForSelectedOrMainFrame] setTypingStyle:style withUndoAction:WebUndoActionUnspecified];
3017 }
3018
3019 - (DOMCSSStyleDeclaration *)typingStyle
3020 {
3021     return [[self _bridgeForSelectedOrMainFrame] typingStyle];
3022 }
3023
3024 - (void)setSmartInsertDeleteEnabled:(BOOL)flag
3025 {
3026     _private->smartInsertDeleteEnabled = flag;
3027 }
3028
3029 - (BOOL)smartInsertDeleteEnabled
3030 {
3031     return _private->smartInsertDeleteEnabled;
3032 }
3033
3034 - (void)setContinuousSpellCheckingEnabled:(BOOL)flag
3035 {
3036     _private->continuousSpellCheckingEnabled = flag;
3037     if ([self isContinuousSpellCheckingEnabled]) {
3038         [[self class] _preflightSpellChecker];
3039     } else {
3040         [[self mainFrame] _unmarkAllMisspellings];
3041     }
3042 }
3043
3044 - (BOOL)isContinuousSpellCheckingEnabled
3045 {
3046     return _private->continuousSpellCheckingEnabled && [self _continuousCheckingAllowed];
3047 }
3048
3049 - (WebNSInteger)spellCheckerDocumentTag
3050 {
3051     if (!_private->hasSpellCheckerDocumentTag) {
3052         _private->spellCheckerDocumentTag = [NSSpellChecker uniqueSpellDocumentTag];
3053         _private->hasSpellCheckerDocumentTag = YES;
3054     }
3055     return _private->spellCheckerDocumentTag;
3056 }
3057
3058 - (NSUndoManager *)undoManager
3059 {
3060     NSUndoManager *undoManager = [[self _editingDelegateForwarder] undoManagerForWebView:self];
3061     if (undoManager) {
3062         return undoManager;
3063     }
3064     return [super undoManager];
3065 }
3066
3067 - (void)registerForEditingDelegateNotification:(NSString *)name selector:(SEL)selector
3068 {
3069     NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
3070     if ([_private->editingDelegate respondsToSelector:selector])
3071         [defaultCenter addObserver:_private->editingDelegate selector:selector name:name object:self];
3072 }
3073
3074 - (void)setEditingDelegate:(id)delegate
3075 {
3076     if (_private->editingDelegate == delegate)
3077         return;
3078
3079     NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
3080
3081     // remove notifications from current delegate
3082     [defaultCenter removeObserver:_private->editingDelegate name:WebViewDidBeginEditingNotification object:self];
3083     [defaultCenter removeObserver:_private->editingDelegate name:WebViewDidChangeNotification object:self];
3084     [defaultCenter removeObserver:_private->editingDelegate name:WebViewDidEndEditingNotification object:self];
3085     [defaultCenter removeObserver:_private->editingDelegate name:WebViewDidChangeTypingStyleNotification object:self];
3086     [defaultCenter removeObserver:_private->editingDelegate name:WebViewDidChangeSelectionNotification object:self];
3087     
3088     _private->editingDelegate = delegate;
3089     [_private->editingDelegateForwarder release];
3090     _private->editingDelegateForwarder = nil;
3091     
3092     // add notifications for new delegate
3093     [self registerForEditingDelegateNotification:WebViewDidBeginEditingNotification selector:@selector(webViewDidBeginEditing:)];
3094     [self registerForEditingDelegateNotification:WebViewDidChangeNotification selector:@selector(webViewDidChange:)];
3095     [self registerForEditingDelegateNotification:WebViewDidEndEditingNotification selector:@selector(webViewDidEndEditing:)];
3096     [self registerForEditingDelegateNotification:WebViewDidChangeTypingStyleNotification selector:@selector(webViewDidChangeTypingStyle:)];
3097     [self registerForEditingDelegateNotification:WebViewDidChangeSelectionNotification selector:@selector(webViewDidChangeSelection:)];
3098 }
3099
3100 - (id)editingDelegate
3101 {
3102     return _private->editingDelegate;
3103 }
3104
3105 - (DOMCSSStyleDeclaration *)styleDeclarationWithText:(NSString *)text
3106 {
3107     // FIXME: Should this really be attached to the document with the current selection?
3108     DOMCSSStyleDeclaration *decl = [[[self _bridgeForSelectedOrMainFrame] DOMDocument] createCSSStyleDeclaration];
3109     [decl setCssText:text];
3110     return decl;
3111 }
3112
3113 @end
3114
3115 @implementation WebView (WebViewUndoableEditing)
3116
3117 - (void)replaceSelectionWithNode:(DOMNode *)node
3118 {
3119     [[self _bridgeForSelectedOrMainFrame] replaceSelectionWithNode:node selectReplacement:YES smartReplace:NO];
3120 }    
3121
3122 - (void)replaceSelectionWithText:(NSString *)text
3123 {
3124     [[self _bridgeForSelectedOrMainFrame] replaceSelectionWithText:text selectReplacement:YES smartReplace:NO];
3125 }
3126
3127 - (void)replaceSelectionWithMarkupString:(NSString *)markupString
3128 {
3129     [[self _bridgeForSelectedOrMainFrame] replaceSelectionWithMarkupString:markupString baseURLString:nil selectReplacement:YES smartReplace:NO];
3130 }
3131
3132 - (void)replaceSelectionWithArchive:(WebArchive *)archive
3133 {
3134     [[[[self _bridgeForSelectedOrMainFrame] webFrame] dataSource] _replaceSelectionWithArchive:archive selectReplacement:YES];
3135 }
3136
3137 - (void)deleteSelection
3138 {
3139     WebFrameBridge *bridge = [self _bridgeForSelectedOrMainFrame];
3140     [bridge deleteSelectionWithSmartDelete:[(WebHTMLView *)[[[bridge webFrame] frameView] documentView] _canSmartCopyOrDelete]];
3141 }
3142     
3143 - (void)applyStyle:(DOMCSSStyleDeclaration *)style
3144 {
3145     // We don't know enough at thls level to pass in a relevant WebUndoAction; we'd have to
3146     // change the API to allow this.
3147     [[self _bridgeForSelectedOrMainFrame] applyStyle:style withUndoAction:WebUndoActionUnspecified];
3148 }
3149
3150 @end
3151
3152 @implementation WebView (WebViewEditingActions)
3153
3154 - (void)_performResponderOperation:(SEL)selector with:(id)parameter
3155 {
3156     static BOOL reentered = NO;
3157     if (reentered) {
3158         [[self nextResponder] tryToPerform:selector with:parameter];
3159         return;
3160     }
3161
3162     // There are two possibilities here.
3163     //
3164     // One is that WebView has been called in its role as part of the responder chain.
3165     // In that case, it's fine to call the first responder and end up calling down the
3166     // responder chain again. Later we will return here with reentered = YES and continue
3167     // past the WebView.
3168     //
3169     // The other is that we are being called directly, in which case we want to pass the
3170     // selector down to the view inside us that can handle it, and continue down the
3171     // responder chain as usual.
3172
3173     // Pass this selector down to the first responder.
3174     NSResponder *responder = [self _responderForResponderOperations];
3175     reentered = YES;
3176     [responder tryToPerform:selector with:parameter];
3177     reentered = NO;
3178 }
3179
3180 #define FORWARD(name) \
3181     - (void)name:(id)sender { [self _performResponderOperation:_cmd with:sender]; }
3182
3183 FOR_EACH_RESPONDER_SELECTOR(FORWARD)
3184
3185 - (void)insertText:(NSString *)text
3186 {
3187     [self _performResponderOperation:_cmd with:text];
3188 }
3189
3190 @end
3191
3192 @implementation WebView (WebViewEditingInMail)
3193
3194 - (void)_insertNewlineInQuotedContent;
3195 {
3196     [[self _bridgeForSelectedOrMainFrame] insertParagraphSeparatorInQuotedContent];
3197 }
3198
3199 - (BOOL)_selectWordBeforeMenuEvent
3200 {
3201     return _private->selectWordBeforeMenuEvent;
3202 }
3203
3204 - (void)_setSelectWordBeforeMenuEvent:(BOOL)flag
3205 {
3206     _private->selectWordBeforeMenuEvent = flag;
3207 }
3208
3209 @end
3210
3211 static WebFrameView *containingFrameView(NSView *view)
3212 {
3213     while (view && ![view isKindOfClass:[WebFrameView class]])
3214         view = [view superview];
3215     return (WebFrameView *)view;    
3216 }
3217
3218 @implementation WebView (WebFileInternal)
3219
3220 - (WebFrame *)_focusedFrame
3221 {
3222     NSResponder *resp = [[self window] firstResponder];
3223     if (resp && [resp isKindOfClass:[NSView class]] && [(NSView *)resp isDescendantOf:[[self mainFrame] frameView]]) {
3224         WebFrameView *frameView = containingFrameView((NSView *)resp);
3225         ASSERT(frameView != nil);
3226         return [frameView webFrame];
3227     }
3228     
3229     return nil;
3230 }
3231
3232 - (WebFrame *)_selectedOrMainFrame
3233 {
3234     WebFrame *result = [self selectedFrame];
3235     if (result == nil)
3236         result = [self mainFrame];
3237     return result;
3238 }
3239
3240 - (WebFrameBridge *)_bridgeForSelectedOrMainFrame
3241 {
3242     return [[self _selectedOrMainFrame] _bridge];
3243 }
3244
3245 - (BOOL)_isLoading
3246 {
3247     WebFrame *mainFrame = [self mainFrame];
3248     return [[mainFrame dataSource] isLoading]
3249         || [[mainFrame provisionalDataSource] isLoading];
3250 }
3251
3252 - (WebFrameView *)_frameViewAtWindowPoint:(NSPoint)point
3253 {
3254     NSView *view = [self hitTest:[[self superview] convertPoint:point fromView:nil]];
3255     if (![view isDescendantOf:[[self mainFrame] frameView]])
3256         return nil;
3257     WebFrameView *frameView = containingFrameView(view);
3258     ASSERT(frameView);
3259     return frameView;
3260 }
3261
3262 - (WebFrameBridge *)_bridgeAtPoint:(NSPoint)point
3263 {
3264     return [[[self _frameViewAtWindowPoint:[self convertPoint:point toView:nil]] webFrame] _bridge];
3265 }
3266
3267 + (void)_preflightSpellCheckerNow:(id)sender
3268 {
3269     [[NSSpellChecker sharedSpellChecker] _preflightChosenSpellServer];
3270 }
3271
3272 + (void)_preflightSpellChecker
3273 {
3274     // As AppKit does, we wish to delay tickling the shared spellchecker into existence on application launch.
3275     if ([NSSpellChecker sharedSpellCheckerExists]) {
3276         [self _preflightSpellCheckerNow:self];
3277     } else {
3278         [self performSelector:@selector(_preflightSpellCheckerNow:) withObject:self afterDelay:2.0];
3279     }
3280 }
3281
3282 - (BOOL)_continuousCheckingAllowed
3283 {
3284     static BOOL allowContinuousSpellChecking = YES;
3285     static BOOL readAllowContinuousSpellCheckingDefault = NO;
3286     if (!readAllowContinuousSpellCheckingDefault) {
3287         if ([[NSUserDefaults standardUserDefaults] objectForKey:@"NSAllowContinuousSpellChecking"]) {
3288             allowContinuousSpellChecking = [[NSUserDefaults standardUserDefaults] boolForKey:@"NSAllowContinuousSpellChecking"];
3289         }
3290         readAllowContinuousSpellCheckingDefault = YES;
3291     }
3292     return allowContinuousSpellChecking;
3293 }
3294
3295 - (NSResponder *)_responderForResponderOperations
3296 {
3297     NSResponder *responder = [[self window] firstResponder];
3298     WebFrameView *mainFrameView = [[self mainFrame] frameView];
3299     
3300     // If the current responder is outside of the webview, use our main frameView or its
3301     // document view. We also do this for subviews of self that are siblings of the main
3302     // frameView since clients might insert non-webview-related views there (see 4552713).
3303     if (responder != self && ![mainFrameView _web_firstResponderIsSelfOrDescendantView]) {
3304         responder = [mainFrameView documentView];
3305         if (!responder)
3306             responder = mainFrameView;
3307     }
3308     return responder;
3309 }
3310
3311 - (void)_searchWithGoogleFromMenu:(id)sender
3312 {
3313     id documentView = [[[self mainFrame] frameView] documentView];
3314     if (![documentView conformsToProtocol:@protocol(WebDocumentText)]) {
3315         return;
3316     }
3317     
3318     NSString *selectedString = [(id <WebDocumentText>)documentView selectedString];
3319     if ([selectedString length] == 0) {
3320         return;
3321     }
3322     
3323     NSPasteboard *pasteboard = [NSPasteboard pasteboardWithUniqueName];
3324     [pasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
3325     NSMutableString *s = [selectedString mutableCopy];
3326     const unichar nonBreakingSpaceCharacter = 0xA0;
3327     NSString *nonBreakingSpaceString = [NSString stringWithCharacters:&nonBreakingSpaceCharacter length:1];
3328     [s replaceOccurrencesOfString:nonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
3329     [pasteboard setString:s forType:NSStringPboardType];
3330     [s release];
3331     
3332     // FIXME: seems fragile to use the service by name, but this is what AppKit does
3333     NSPerformService(@"Search With Google", pasteboard);
3334 }
3335
3336 - (void)_searchWithSpotlightFromMenu:(id)sender
3337 {
3338     id documentView = [[[self mainFrame] frameView] documentView];
3339     if (![documentView conformsToProtocol:@protocol(WebDocumentText)]) {
3340         return;
3341     }
3342     
3343     NSString *selectedString = [(id <WebDocumentText>)documentView selectedString];
3344     if ([selectedString length] == 0) {
3345         return;
3346     }
3347
3348     (void)HISearchWindowShow((CFStringRef)selectedString, kNilOptions);
3349 }
3350
3351 // Slightly funky method that lets us have one copy of the logic for finding docViews that can do
3352 // text sizing.  It returns whether it found any "suitable" doc views.  It sends sel to any suitable
3353 // doc views, or if sel==0 we do nothing to them.  For doc views that track our size factor, they are
3354 // suitable if doTrackingViews==YES (which in practice means that our size factor isn't at its max or
3355 // min).  For doc views that don't track it, we send them testSel to determine suitablility.  If we
3356 // do find any suitable tracking doc views and newScaleFactor!=0, we will set the common scale factor
3357 // to that new factor before we send sel to any of them. 
3358 - (BOOL)_performTextSizingSelector:(SEL)sel withObject:(id)arg onTrackingDocs:(BOOL)doTrackingViews selForNonTrackingDocs:(SEL)testSel newScaleFactor:(float)newScaleFactor
3359 {
3360     if ([[self mainFrame] dataSource] == nil) {
3361         return NO;
3362     }
3363     
3364     BOOL foundSome = NO;
3365     NSArray *docViews = [[self mainFrame] _documentViews];
3366     int i;
3367     for (i = [docViews count]-1; i >= 0; i--) {
3368         id docView = [docViews objectAtIndex:i];
3369         if ([docView conformsToProtocol:@protocol(_WebDocumentTextSizing)]) {
3370             id <_WebDocumentTextSizing> sizingDocView = (id <_WebDocumentTextSizing>)docView;
3371             BOOL isSuitable;
3372             if ([sizingDocView _tracksCommonSizeFactor]) {
3373                 isSuitable = doTrackingViews;
3374                 if (isSuitable && newScaleFactor != 0) {
3375                     [self setTextSizeMultiplier:newScaleFactor];
3376                 }
3377             } else {
3378                 // Incantation to perform a selector returning a BOOL.
3379                 isSuitable = ((BOOL(*)(id, SEL))objc_msgSend)(sizingDocView, testSel);
3380             }
3381             
3382             if (isSuitable) {
3383                 if (sel != 0) {
3384                     foundSome = YES;
3385                     [sizingDocView performSelector:sel withObject:arg];
3386                 } else {
3387                     // if we're just called for the benefit of the return value, we can return at first match
3388                     return YES;
3389                 }
3390             }
3391         }
3392     }
3393     
3394     return foundSome;
3395 }
3396
3397 @end
3398
3399 @implementation WebView (WebViewBridge)
3400
3401 - (WebPageBridge *)_pageBridge
3402 {
3403     return _private->_pageBridge;
3404 }
3405
3406 @end