8f6770ea3cd14b28332998f81103001cd3fe2788
[WebKit-https.git] / WebKit / mac / WebView / WebView.mm
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
3  * Copyright (C) 2006 David Smith (catfish.man@gmail.com)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer. 
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution. 
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission. 
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #import "WebViewInternal.h"
31
32 #import "DOMRangeInternal.h"
33 #import "WebBackForwardListInternal.h"
34 #import "WebBaseNetscapePluginView.h"
35 #import "WebChromeClient.h"
36 #import "WebContextMenuClient.h"
37 #import "WebDOMOperationsPrivate.h"
38 #import "WebDataSourceInternal.h"
39 #import "WebDatabaseManagerInternal.h"
40 #import "WebDefaultEditingDelegate.h"
41 #import "WebDefaultPolicyDelegate.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 "WebDragClient.h"
49 #import "WebDynamicScrollBarsViewInternal.h"
50 #import "WebEditingDelegate.h"
51 #import "WebEditorClient.h"
52 #import "WebFormDelegatePrivate.h"
53 #import "WebFrameInternal.h"
54 #import "WebFrameViewInternal.h"
55 #import "WebHTMLRepresentation.h"
56 #import "WebHTMLViewInternal.h"
57 #import "WebHistoryItemInternal.h"
58 #import "WebIconDatabaseInternal.h"
59 #import "WebInspector.h"
60 #import "WebInspectorClient.h"
61 #import "WebKitErrors.h"
62 #import "WebKitLogging.h"
63 #import "WebKitNSStringExtras.h"
64 #import "WebKitStatisticsPrivate.h"
65 #import "WebKitSystemBits.h"
66 #import "WebKitVersionChecks.h"
67 #import "WebLocalizableStrings.h"
68 #import "WebNSDataExtras.h"
69 #import "WebNSDataExtrasPrivate.h"
70 #import "WebNSDictionaryExtras.h"
71 #import "WebNSEventExtras.h"
72 #import "WebNSObjectExtras.h"
73 #import "WebNSPasteboardExtras.h"
74 #import "WebNSPrintOperationExtras.h"
75 #import "WebNSURLExtras.h"
76 #import "WebNSURLRequestExtras.h"
77 #import "WebNSUserDefaultsExtras.h"
78 #import "WebNSViewExtras.h"
79 #import "WebPDFView.h"
80 #import "WebPanelAuthenticationHandler.h"
81 #import "WebPasteboardHelper.h"
82 #import "WebPluginDatabase.h"
83 #import "WebPolicyDelegate.h"
84 #import "WebPreferenceKeysPrivate.h"
85 #import "WebPreferencesPrivate.h"
86 #import "WebScriptDebugServerPrivate.h"
87 #import "WebUIDelegate.h"
88 #import "WebUIDelegatePrivate.h"
89 #import <CoreFoundation/CFSet.h>
90 #import <Foundation/NSURLConnection.h>
91 #import <JavaScriptCore/Assertions.h>
92 #import <JavaScriptCore/HashTraits.h>
93 #import <JavaScriptCore/RefPtr.h>
94 #import <JavaScriptCore/array_object.h>
95 #import <JavaScriptCore/date_object.h>
96 #import <WebCore/ApplicationCacheStorage.h>
97 #import <WebCore/Cache.h>
98 #import <WebCore/ColorMac.h>
99 #import <WebCore/Document.h>
100 #import <WebCore/DocumentLoader.h>
101 #import <WebCore/DragController.h>
102 #import <WebCore/DragData.h>
103 #import <WebCore/Editor.h>
104 #import <WebCore/ExceptionHandlers.h>
105 #import <WebCore/Frame.h>
106 #import <WebCore/FrameLoader.h>
107 #import <WebCore/FrameTree.h>
108 #import <WebCore/HTMLNames.h>
109 #import <WebCore/HistoryItem.h>
110 #import <WebCore/Logging.h>
111 #import <WebCore/MIMETypeRegistry.h>
112 #import <WebCore/Page.h>
113 #import <WebCore/PageCache.h>
114 #import <WebCore/PlatformMouseEvent.h>
115 #import <WebCore/ProgressTracker.h>
116 #import <WebCore/SelectionController.h>
117 #import <WebCore/Settings.h>
118 #import <WebCore/TextResourceDecoder.h>
119 #import <WebCore/WebCoreObjCExtras.h>
120 #import <WebCore/WebCoreTextRenderer.h>
121 #import <WebCore/WebCoreView.h>
122 #import <WebCore/kjs_proxy.h>
123 #import <WebKit/DOM.h>
124 #import <WebKit/DOMExtensions.h>
125 #import <WebKit/DOMPrivate.h>
126 #import <WebKitSystemInterface.h>
127 #import <kjs/InitializeThreading.h>
128 #import <mach-o/dyld.h>
129 #import <objc/objc-auto.h>
130 #import <objc/objc-runtime.h>
131 #import <sys/param.h>
132
133 #if ENABLE(DASHBOARD_SUPPORT)
134 #import <WebKit/WebDashboardRegion.h>
135 #endif
136
137 using namespace WebCore;
138 using namespace KJS;
139
140 #if defined(__ppc__) || defined(__ppc64__)
141 #define PROCESSOR "PPC"
142 #elif defined(__i386__) || defined(__x86_64__)
143 #define PROCESSOR "Intel"
144 #else
145 #error Unknown architecture
146 #endif
147
148 #define FOR_EACH_RESPONDER_SELECTOR(macro) \
149 macro(alignCenter) \
150 macro(alignJustified) \
151 macro(alignLeft) \
152 macro(alignRight) \
153 macro(capitalizeWord) \
154 macro(centerSelectionInVisibleArea) \
155 macro(changeAttributes) \
156 macro(changeBaseWritingDirection) \
157 macro(changeBaseWritingDirectionToLTR) \
158 macro(changeBaseWritingDirectionToRTL) \
159 macro(changeColor) \
160 macro(changeDocumentBackgroundColor) \
161 macro(changeFont) \
162 macro(changeSpelling) \
163 macro(checkSpelling) \
164 macro(complete) \
165 macro(copy) \
166 macro(copyFont) \
167 macro(cut) \
168 macro(delete) \
169 macro(deleteBackward) \
170 macro(deleteBackwardByDecomposingPreviousCharacter) \
171 macro(deleteForward) \
172 macro(deleteToBeginningOfLine) \
173 macro(deleteToBeginningOfParagraph) \
174 macro(deleteToEndOfLine) \
175 macro(deleteToEndOfParagraph) \
176 macro(deleteToMark) \
177 macro(deleteWordBackward) \
178 macro(deleteWordForward) \
179 macro(ignoreSpelling) \
180 macro(indent) \
181 macro(insertBacktab) \
182 macro(insertLineBreak) \
183 macro(insertNewline) \
184 macro(insertNewlineIgnoringFieldEditor) \
185 macro(insertParagraphSeparator) \
186 macro(insertTab) \
187 macro(insertTabIgnoringFieldEditor) \
188 macro(lowercaseWord) \
189 macro(moveBackward) \
190 macro(moveBackwardAndModifySelection) \
191 macro(moveDown) \
192 macro(moveDownAndModifySelection) \
193 macro(moveForward) \
194 macro(moveForwardAndModifySelection) \
195 macro(moveLeft) \
196 macro(moveLeftAndModifySelection) \
197 macro(moveParagraphBackwardAndModifySelection) \
198 macro(moveParagraphForwardAndModifySelection) \
199 macro(moveRight) \
200 macro(moveRightAndModifySelection) \
201 macro(moveToBeginningOfDocument) \
202 macro(moveToBeginningOfDocumentAndModifySelection) \
203 macro(moveToBeginningOfLine) \
204 macro(moveToBeginningOfLineAndModifySelection) \
205 macro(moveToBeginningOfParagraph) \
206 macro(moveToBeginningOfParagraphAndModifySelection) \
207 macro(moveToBeginningOfSentence) \
208 macro(moveToBeginningOfSentenceAndModifySelection) \
209 macro(moveToEndOfDocument) \
210 macro(moveToEndOfDocumentAndModifySelection) \
211 macro(moveToEndOfLine) \
212 macro(moveToEndOfLineAndModifySelection) \
213 macro(moveToEndOfParagraph) \
214 macro(moveToEndOfParagraphAndModifySelection) \
215 macro(moveToEndOfSentence) \
216 macro(moveToEndOfSentenceAndModifySelection) \
217 macro(moveUp) \
218 macro(moveUpAndModifySelection) \
219 macro(moveWordBackward) \
220 macro(moveWordBackwardAndModifySelection) \
221 macro(moveWordForward) \
222 macro(moveWordForwardAndModifySelection) \
223 macro(moveWordLeft) \
224 macro(moveWordLeftAndModifySelection) \
225 macro(moveWordRight) \
226 macro(moveWordRightAndModifySelection) \
227 macro(outdent) \
228 macro(pageDown) \
229 macro(pageDownAndModifySelection) \
230 macro(pageUp) \
231 macro(pageUpAndModifySelection) \
232 macro(paste) \
233 macro(pasteAsPlainText) \
234 macro(pasteAsRichText) \
235 macro(pasteFont) \
236 macro(performFindPanelAction) \
237 macro(scrollLineDown) \
238 macro(scrollLineUp) \
239 macro(scrollPageDown) \
240 macro(scrollPageUp) \
241 macro(scrollToBeginningOfDocument) \
242 macro(scrollToEndOfDocument) \
243 macro(selectAll) \
244 macro(selectLine) \
245 macro(selectParagraph) \
246 macro(selectSentence) \
247 macro(selectToMark) \
248 macro(selectWord) \
249 macro(setMark) \
250 macro(showGuessPanel) \
251 macro(startSpeaking) \
252 macro(stopSpeaking) \
253 macro(subscript) \
254 macro(superscript) \
255 macro(swapWithMark) \
256 macro(takeFindStringFromSelection) \
257 macro(toggleBaseWritingDirection) \
258 macro(transpose) \
259 macro(underline) \
260 macro(unscript) \
261 macro(uppercaseWord) \
262 macro(yank) \
263 macro(yankAndSelect) \
264
265 #define WebKitOriginalTopPrintingMarginKey @"WebKitOriginalTopMargin"
266 #define WebKitOriginalBottomPrintingMarginKey @"WebKitOriginalBottomMargin"
267
268 #define KeyboardUIModeDidChangeNotification @"com.apple.KeyboardUIModeDidChange"
269 #define AppleKeyboardUIMode CFSTR("AppleKeyboardUIMode")
270 #define UniversalAccessDomain CFSTR("com.apple.universalaccess")
271
272 static BOOL s_didSetCacheModel;
273 static WebCacheModel s_cacheModel = WebCacheModelDocumentViewer;
274
275 static BOOL applicationIsTerminating;
276 static int pluginDatabaseClientCount = 0;
277
278 @interface NSSpellChecker (AppKitSecretsIKnow)
279 - (void)_preflightChosenSpellServer;
280 @end
281
282 @interface NSView (AppKitSecretsIKnow)
283 - (NSView *)_hitTest:(NSPoint *)aPoint dragTypes:(NSSet *)types;
284 - (void)_autoscrollForDraggingInfo:(id)dragInfo timeDelta:(NSTimeInterval)repeatDelta;
285 - (BOOL)_shouldAutoscrollForDraggingInfo:(id)dragInfo;
286 @end
287
288 @interface NSWindow (AppKitSecretsIKnow) 
289 - (id)_oldFirstResponderBeforeBecoming;
290 @end
291
292 @interface NSObject (ValidateWithoutDelegate)
293 - (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item;
294 @end
295
296 @interface _WebSafeForwarder : NSObject
297 {
298     id target; // Non-retained. Don't retain delegates.
299     id defaultTarget;
300     BOOL catchExceptions;
301 }
302 - (id)initWithTarget:(id)target defaultTarget:(id)defaultTarget catchExceptions:(BOOL)catchExceptions;
303 @end
304
305 @interface WebViewPrivate : NSObject {
306 @public
307     Page* page;
308     
309     id UIDelegate;
310     id UIDelegateForwarder;
311     id resourceProgressDelegate;
312     id downloadDelegate;
313     id policyDelegate;
314     id policyDelegateForwarder;
315     id frameLoadDelegate;
316     id frameLoadDelegateForwarder;
317     id <WebFormDelegate> formDelegate;
318     id editingDelegate;
319     id editingDelegateForwarder;
320     id scriptDebugDelegate;
321     id scriptDebugDelegateForwarder;
322
323     WebInspector *inspector;
324
325     BOOL allowsUndo;
326         
327     float zoomMultiplier;
328     BOOL zoomMultiplierIsTextOnly;
329
330     NSString *applicationNameForUserAgent;
331     String* userAgent;
332     BOOL userAgentOverridden;
333     
334     WebPreferences *preferences;
335     BOOL useSiteSpecificSpoofing;
336
337     NSWindow *hostWindow;
338
339     int programmaticFocusCount;
340     
341     WebResourceDelegateImplementationCache resourceLoadDelegateImplementations;
342     WebFrameLoadDelegateImplementationCache frameLoadDelegateImplementations;
343
344     void *observationInfo;
345     
346     BOOL closed;
347     BOOL shouldCloseWithWindow;
348     BOOL mainFrameDocumentReady;
349     BOOL drawsBackground;
350     BOOL editable;
351     BOOL tabKeyCyclesThroughElementsChanged;
352     BOOL becomingFirstResponder;
353     BOOL becomingFirstResponderFromOutside;
354     BOOL hoverFeedbackSuspended;
355     BOOL usesPageCache;
356     BOOL catchesDelegateExceptions;
357
358     NSColor *backgroundColor;
359
360     NSString *mediaStyle;
361     
362     BOOL hasSpellCheckerDocumentTag;
363     NSInteger spellCheckerDocumentTag;
364
365     BOOL smartInsertDeleteEnabled;
366         
367 #if ENABLE(DASHBOARD_SUPPORT)
368     BOOL dashboardBehaviorAlwaysSendMouseEventsToAllWindows;
369     BOOL dashboardBehaviorAlwaysSendActiveNullEventsToPlugIns;
370     BOOL dashboardBehaviorAlwaysAcceptsFirstMouse;
371     BOOL dashboardBehaviorAllowWheelScrolling;
372 #endif
373     
374     // WebKit has both a global plug-in database and a separate, per WebView plug-in database. Dashboard uses the per WebView database.
375     WebPluginDatabase *pluginDatabase;
376     
377     HashMap<unsigned long, RetainPtr<id> >* identifierMap;
378
379     BOOL _keyboardUIModeAccessed;
380     KeyboardUIMode _keyboardUIMode;
381 }
382 @end
383
384 @interface WebView (WebFileInternal)
385 - (WebFrame *)_selectedOrMainFrame;
386 - (BOOL)_isLoading;
387 - (WebFrameView *)_frameViewAtWindowPoint:(NSPoint)point;
388 - (WebFrame *)_focusedFrame;
389 + (void)_preflightSpellChecker;
390 - (BOOL)_continuousCheckingAllowed;
391 - (NSResponder *)_responderForResponderOperations;
392 @end
393
394 @interface WebView (WebCallDelegateFunctions)
395 @end
396
397 NSString *WebElementDOMNodeKey =            @"WebElementDOMNode";
398 NSString *WebElementFrameKey =              @"WebElementFrame";
399 NSString *WebElementImageKey =              @"WebElementImage";
400 NSString *WebElementImageAltStringKey =     @"WebElementImageAltString";
401 NSString *WebElementImageRectKey =          @"WebElementImageRect";
402 NSString *WebElementImageURLKey =           @"WebElementImageURL";
403 NSString *WebElementIsSelectedKey =         @"WebElementIsSelected";
404 NSString *WebElementLinkLabelKey =          @"WebElementLinkLabel";
405 NSString *WebElementLinkTargetFrameKey =    @"WebElementTargetFrame";
406 NSString *WebElementLinkTitleKey =          @"WebElementLinkTitle";
407 NSString *WebElementLinkURLKey =            @"WebElementLinkURL";
408 NSString *WebElementSpellingToolTipKey =    @"WebElementSpellingToolTip";
409 NSString *WebElementTitleKey =              @"WebElementTitle";
410 NSString *WebElementLinkIsLiveKey =         @"WebElementLinkIsLive";
411 NSString *WebElementIsContentEditableKey =  @"WebElementIsContentEditableKey";
412
413 NSString *WebViewProgressStartedNotification =          @"WebProgressStartedNotification";
414 NSString *WebViewProgressEstimateChangedNotification =  @"WebProgressEstimateChangedNotification";
415 NSString *WebViewProgressFinishedNotification =         @"WebProgressFinishedNotification";
416
417 NSString * const WebViewDidBeginEditingNotification =         @"WebViewDidBeginEditingNotification";
418 NSString * const WebViewDidChangeNotification =               @"WebViewDidChangeNotification";
419 NSString * const WebViewDidEndEditingNotification =           @"WebViewDidEndEditingNotification";
420 NSString * const WebViewDidChangeTypingStyleNotification =    @"WebViewDidChangeTypingStyleNotification";
421 NSString * const WebViewDidChangeSelectionNotification =      @"WebViewDidChangeSelectionNotification";
422
423 enum { WebViewVersion = 4 };
424
425 #define timedLayoutSize 4096
426
427 static NSMutableSet *schemesWithRepresentationsSet;
428
429 NSString *_WebCanGoBackKey =            @"canGoBack";
430 NSString *_WebCanGoForwardKey =         @"canGoForward";
431 NSString *_WebEstimatedProgressKey =    @"estimatedProgress";
432 NSString *_WebIsLoadingKey =            @"isLoading";
433 NSString *_WebMainFrameIconKey =        @"mainFrameIcon";
434 NSString *_WebMainFrameTitleKey =       @"mainFrameTitle";
435 NSString *_WebMainFrameURLKey =         @"mainFrameURL";
436 NSString *_WebMainFrameDocumentKey =    @"mainFrameDocument";
437
438 @interface WebProgressItem : NSObject
439 {
440 @public
441     long long bytesReceived;
442     long long estimatedLength;
443 }
444 @end
445
446 @implementation WebProgressItem
447 @end
448
449 static BOOL continuousSpellCheckingEnabled;
450 #ifndef BUILDING_ON_TIGER
451 static BOOL grammarCheckingEnabled;
452 #endif
453
454 @implementation WebViewPrivate
455
456 #ifndef BUILDING_ON_TIGER
457 + (void)initialize
458 {
459     WebCoreObjCFinalizeOnMainThread(self);
460 }
461 #endif
462
463 - init 
464 {
465     self = [super init];
466     if (!self)
467         return nil;
468     KJS::initializeThreading();
469     allowsUndo = YES;
470     zoomMultiplier = 1;
471     zoomMultiplierIsTextOnly = YES;
472 #if ENABLE(DASHBOARD_SUPPORT)
473     dashboardBehaviorAllowWheelScrolling = YES;
474 #endif
475     shouldCloseWithWindow = objc_collecting_enabled();
476     continuousSpellCheckingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebContinuousSpellCheckingEnabled];
477
478 #ifndef BUILDING_ON_TIGER
479     grammarCheckingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebGrammarCheckingEnabled];
480 #endif
481     userAgent = new String;
482     
483     usesPageCache = YES;
484     
485     identifierMap = new HashMap<unsigned long, RetainPtr<id> >();
486     pluginDatabaseClientCount++;
487
488     return self;
489 }
490
491 - (void)dealloc
492 {
493     ASSERT(!page);
494     ASSERT(!preferences);
495
496     delete userAgent;
497     delete identifierMap;
498     
499     [applicationNameForUserAgent release];
500     [backgroundColor release];
501     
502     [inspector release];
503     [hostWindow release];
504
505     [policyDelegateForwarder release];
506     [UIDelegateForwarder release];
507     [frameLoadDelegateForwarder release];
508     [editingDelegateForwarder release];
509     [scriptDebugDelegateForwarder release];
510     
511     [mediaStyle release];
512     
513     [super dealloc];
514 }
515
516 - (void)finalize
517 {
518     ASSERT_MAIN_THREAD();
519
520     delete userAgent;
521     delete identifierMap;
522
523     [super finalize];
524 }
525
526 @end
527
528 @implementation WebView (AllWebViews)
529
530 static CFSetCallBacks NonRetainingSetCallbacks = {
531     0,
532     NULL,
533     NULL,
534     CFCopyDescription,
535     CFEqual,
536     CFHash
537 };
538
539 static CFMutableSetRef allWebViewsSet;
540
541 + (void)_makeAllWebViewsPerformSelector:(SEL)selector
542 {
543     if (!allWebViewsSet)
544         return;
545
546     [(NSMutableSet *)allWebViewsSet makeObjectsPerformSelector:selector];
547 }
548
549 - (void)_removeFromAllWebViewsSet
550 {
551     if (allWebViewsSet)
552         CFSetRemoveValue(allWebViewsSet, self);
553 }
554
555 - (void)_addToAllWebViewsSet
556 {
557     if (!allWebViewsSet)
558         allWebViewsSet = CFSetCreateMutable(NULL, 0, &NonRetainingSetCallbacks);
559
560     CFSetSetValue(allWebViewsSet, self);
561 }
562
563 @end
564
565 @implementation WebView (WebPrivate)
566
567 #ifdef DEBUG_WIDGET_DRAWING
568 static bool debugWidget = true;
569 - (void)drawRect:(NSRect)rect
570 {
571     [[NSColor blueColor] set];
572     NSRectFill (rect);
573     
574     NSRect htmlViewRect = [[[[self mainFrame] frameView] documentView] frame];
575
576     if (debugWidget) {
577         while (debugWidget) {
578             sleep (1);
579         }
580     }
581
582     NSLog (@"%s:   rect:  (%0.f,%0.f) %0.f %0.f, htmlViewRect:  (%0.f,%0.f) %0.f %0.f\n", 
583         __PRETTY_FUNCTION__, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height,
584         htmlViewRect.origin.x, htmlViewRect.origin.y, htmlViewRect.size.width, htmlViewRect.size.height
585     );
586
587     [super drawRect:rect];
588 }
589 #endif
590
591 + (BOOL)_scriptDebuggerEnabled
592 {
593 #ifdef NDEBUG
594     return [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitScriptDebuggerEnabled"];
595 #else
596     return YES; // always enable in debug builds
597 #endif
598 }
599
600 + (NSArray *)_supportedMIMETypes
601 {
602     // Load the plug-in DB allowing plug-ins to install types.
603     [WebPluginDatabase sharedDatabase];
604     return [[WebFrameView _viewTypesAllowImageTypeOmission:NO] allKeys];
605 }
606
607 + (NSArray *)_supportedFileExtensions
608 {
609     NSMutableSet *extensions = [[NSMutableSet alloc] init];
610     NSArray *MIMETypes = [self _supportedMIMETypes];
611     NSEnumerator *enumerator = [MIMETypes objectEnumerator];
612     NSString *MIMEType;
613     while ((MIMEType = [enumerator nextObject]) != nil) {
614         NSArray *extensionsForType = WKGetExtensionsForMIMEType(MIMEType);
615         if (extensionsForType) {
616             [extensions addObjectsFromArray:extensionsForType];
617         }
618     }
619     NSArray *uniqueExtensions = [extensions allObjects];
620     [extensions release];
621     return uniqueExtensions;
622 }
623
624 + (BOOL)_viewClass:(Class *)vClass andRepresentationClass:(Class *)rClass forMIMEType:(NSString *)MIMEType;
625 {
626     MIMEType = [MIMEType lowercaseString];
627     Class viewClass = [[WebFrameView _viewTypesAllowImageTypeOmission:YES] _webkit_objectForMIMEType:MIMEType];
628     Class repClass = [[WebDataSource _repTypesAllowImageTypeOmission:YES] _webkit_objectForMIMEType:MIMEType];
629     
630     if (!viewClass || !repClass || [[WebPDFView supportedMIMETypes] containsObject:MIMEType]) {
631         // Our optimization to avoid loading the plug-in DB and image types for the HTML case failed.
632         // Load the plug-in DB allowing plug-ins to install types.
633         [WebPluginDatabase sharedDatabase];
634             
635         // Load the image types and get the view class and rep class. This should be the fullest picture of all handled types.
636         viewClass = [[WebFrameView _viewTypesAllowImageTypeOmission:NO] _webkit_objectForMIMEType:MIMEType];
637         repClass = [[WebDataSource _repTypesAllowImageTypeOmission:NO] _webkit_objectForMIMEType:MIMEType];
638     }
639     
640     if (viewClass && repClass) {
641         // Special-case WebHTMLView for text types that shouldn't be shown.
642         if (viewClass == [WebHTMLView class] &&
643             repClass == [WebHTMLRepresentation class] &&
644             [[WebHTMLView unsupportedTextMIMETypes] containsObject:MIMEType]) {
645             return NO;
646         }
647         if (vClass)
648             *vClass = viewClass;
649         if (rClass)
650             *rClass = repClass;
651         return YES;
652     }
653     
654     return NO;
655 }
656
657 - (BOOL)_viewClass:(Class *)vClass andRepresentationClass:(Class *)rClass forMIMEType:(NSString *)MIMEType;
658 {
659     if ([[self class] _viewClass:vClass andRepresentationClass:rClass forMIMEType:MIMEType])
660         return YES;
661
662     if (_private->pluginDatabase) {
663         WebBasePluginPackage *pluginPackage = [_private->pluginDatabase pluginForMIMEType:MIMEType];
664         if (pluginPackage) {
665             if (vClass)
666                 *vClass = [WebHTMLView class];
667             if (rClass)
668                 *rClass = [WebHTMLRepresentation class];
669             return YES;
670         }
671     }
672     
673     return NO;
674 }
675
676 + (void)_setAlwaysUseATSU:(BOOL)f
677 {
678     WebCoreSetAlwaysUseATSU(f);
679 }
680
681 + (BOOL)canShowFile:(NSString *)path
682 {
683     return [[self class] canShowMIMEType:[WebView _MIMETypeForFile:path]];
684 }
685
686 + (NSString *)suggestedFileExtensionForMIMEType:(NSString *)type
687 {
688     return WKGetPreferredExtensionForMIMEType(type);
689 }
690
691 - (BOOL)_isClosed
692 {
693     if (!_private || _private->closed)
694         return YES;
695     return NO;
696 }
697
698 - (void)_close
699 {
700     if (!_private || _private->closed)
701         return;
702
703     if (Frame* mainFrame = core([self mainFrame]))
704         mainFrame->loader()->detachFromParent();
705
706     [self _removeFromAllWebViewsSet];
707     [self setHostWindow:nil];
708
709     [self setDownloadDelegate:nil];
710     [self setEditingDelegate:nil];
711     [self setFrameLoadDelegate:nil];
712     [self setPolicyDelegate:nil];
713     [self setResourceLoadDelegate:nil];
714     [self setScriptDebugDelegate:nil];
715     [self setUIDelegate:nil];
716
717     [_private->inspector webViewClosed];
718
719     // setHostWindow:nil must be called before this value is set (see 5408186)
720     _private->closed = YES;
721
722     // To avoid leaks, call removeDragCaret in case it wasn't called after moveDragCaretToPoint.
723     [self removeDragCaret];
724
725     // Deleteing the WebCore::Page will clear the page cache so we call destroy on 
726     // all the plug-ins in the page cache to break any retain cycles.
727     // See comment in HistoryItem::releaseAllPendingPageCaches() for more information.
728     delete _private->page;
729     _private->page = 0;
730
731     if (_private->hasSpellCheckerDocumentTag) {
732         [[NSSpellChecker sharedSpellChecker] closeSpellDocumentWithTag:_private->spellCheckerDocumentTag];
733         _private->hasSpellCheckerDocumentTag = NO;
734     }
735     
736     [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
737     [[NSNotificationCenter defaultCenter] removeObserver:self];
738
739     [WebPreferences _removeReferenceForIdentifier:[self preferencesIdentifier]];
740
741     WebPreferences *preferences = _private->preferences;
742     _private->preferences = nil;
743     [preferences didRemoveFromWebView];
744     [preferences release];
745
746     pluginDatabaseClientCount--;
747     
748     // Make sure to close both sets of plug-ins databases because plug-ins need an opportunity to clean up files, etc.
749     
750     // Unload the WebView local plug-in database. 
751     if (_private->pluginDatabase) {
752         [_private->pluginDatabase close];
753         [_private->pluginDatabase release];
754         _private->pluginDatabase = nil;
755     }
756     
757     // Keep the global plug-in database active until the app terminates to avoid having to reload plug-in bundles.
758     if (!pluginDatabaseClientCount && applicationIsTerminating)
759         [WebPluginDatabase closeSharedDatabase];
760 }
761
762 + (NSString *)_MIMETypeForFile:(NSString *)path
763 {
764     NSString *extension = [path pathExtension];
765     NSString *MIMEType = nil;
766
767     // Get the MIME type from the extension.
768     if ([extension length] != 0) {
769         MIMEType = WKGetMIMETypeForExtension(extension);
770     }
771
772     // If we can't get a known MIME type from the extension, sniff.
773     if ([MIMEType length] == 0 || [MIMEType isEqualToString:@"application/octet-stream"]) {
774         NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path];
775         NSData *data = [handle readDataOfLength:WEB_GUESS_MIME_TYPE_PEEK_LENGTH];
776         [handle closeFile];
777         if ([data length] != 0) {
778             MIMEType = [data _webkit_guessedMIMEType];
779         }
780         if ([MIMEType length] == 0) {
781             MIMEType = @"application/octet-stream";
782         }
783     }
784
785     return MIMEType;
786 }
787
788 - (WebDownload *)_downloadURL:(NSURL *)URL
789 {
790     ASSERT(URL);
791     
792     NSURLRequest *request = [[NSURLRequest alloc] initWithURL:URL];
793     WebDownload *download = [WebDownload _downloadWithRequest:request
794                                                      delegate:_private->downloadDelegate
795                                                     directory:nil];
796     [request release];
797     
798     return download;
799 }
800
801 - (WebView *)_openNewWindowWithRequest:(NSURLRequest *)request
802 {
803     NSDictionary *features = [[NSDictionary alloc] init];
804     WebView *newWindowWebView = [[self _UIDelegateForwarder] webView:self
805                                             createWebViewWithRequest:nil
806                                                       windowFeatures:features];
807     [features release];
808     if (!newWindowWebView)
809         return nil;
810
811     CallUIDelegate(newWindowWebView, @selector(webViewShow:));
812     return newWindowWebView;
813 }
814
815 - (WebInspector *)inspector
816 {
817     if (!_private->inspector)
818         _private->inspector = [[WebInspector alloc] initWithWebView:self];
819     return _private->inspector;
820 }
821
822 - (WebCore::Page*)page
823 {
824     return _private->page;
825 }
826
827 - (NSMenu *)_menuForElement:(NSDictionary *)element defaultItems:(NSArray *)items
828 {
829     NSArray *defaultMenuItems = [[WebDefaultUIDelegate sharedUIDelegate] webView:self contextMenuItemsForElement:element defaultMenuItems:items];
830
831     NSArray *menuItems = CallUIDelegate(self, @selector(webView:contextMenuItemsForElement:defaultMenuItems:), element, defaultMenuItems);
832     if (!menuItems)
833         return nil;
834
835     unsigned count = [menuItems count];
836     if (!count)
837         return nil;
838
839     NSMenu *menu = [[NSMenu alloc] init];
840     for (unsigned i = 0; i < count; i++)
841         [menu addItem:[menuItems objectAtIndex:i]];
842
843     return [menu autorelease];
844 }
845
846 - (void)_mouseDidMoveOverElement:(NSDictionary *)dictionary modifierFlags:(NSUInteger)modifierFlags
847 {
848     // We originally intended to call this delegate method sometimes with a nil dictionary, but due to
849     // a bug dating back to WebKit 1.0 this delegate was never called with nil! Unfortunately we can't
850     // start calling this with nil since it will break Adobe Help Viewer, and possibly other clients.
851     if (!dictionary)
852         return;
853     CallUIDelegate(self, @selector(webView:mouseDidMoveOverElement:modifierFlags:), dictionary, modifierFlags);
854 }
855
856 - (void)_loadBackForwardListFromOtherView:(WebView *)otherView
857 {
858     if (!_private->page)
859         return;
860     
861     if (!otherView->_private->page)
862         return;
863     
864     // It turns out the right combination of behavior is done with the back/forward load
865     // type.  (See behavior matrix at the top of WebFramePrivate.)  So we copy all the items
866     // in the back forward list, and go to the current one.
867
868     BackForwardList* backForwardList = _private->page->backForwardList();
869     ASSERT(!backForwardList->currentItem()); // destination list should be empty
870
871     BackForwardList* otherBackForwardList = otherView->_private->page->backForwardList();
872     if (!otherBackForwardList->currentItem())
873         return; // empty back forward list, bail
874     
875     HistoryItem* newItemToGoTo = 0;
876
877     int lastItemIndex = otherBackForwardList->forwardListCount();
878     for (int i = -otherBackForwardList->backListCount(); i <= lastItemIndex; ++i) {
879         if (i == 0) {
880             // If this item is showing , save away its current scroll and form state,
881             // since that might have changed since loading and it is normally not saved
882             // until we leave that page.
883             otherView->_private->page->mainFrame()->loader()->saveDocumentAndScrollState();
884         }
885         RefPtr<HistoryItem> newItem = otherBackForwardList->itemAtIndex(i)->copy();
886         if (i == 0) 
887             newItemToGoTo = newItem.get();
888         backForwardList->addItem(newItem.release());
889     }
890     
891     ASSERT(newItemToGoTo);
892     _private->page->goToItem(newItemToGoTo, FrameLoadTypeIndexedBackForward);
893 }
894
895 - (void)_setFormDelegate: (id<WebFormDelegate>)delegate
896 {
897     _private->formDelegate = delegate;
898 }
899
900 - (id<WebFormDelegate>)_formDelegate
901 {
902     return _private->formDelegate;
903 }
904
905 - (BOOL)_needsAdobeFrameReloadingQuirk
906 {
907     static BOOL checked = NO;
908     static BOOL needsQuirk = NO;
909
910     if (checked)
911         return needsQuirk;
912
913     needsQuirk = WKAppVersionCheckLessThan(@"com.adobe.Acrobat", -1, 9.0)
914         || WKAppVersionCheckLessThan(@"com.adobe.Acrobat.Pro", -1, 9.0)
915         || WKAppVersionCheckLessThan(@"com.adobe.Reader", -1, 9.0)
916         || WKAppVersionCheckLessThan(@"com.adobe.distiller", -1, 9.0)
917         || WKAppVersionCheckLessThan(@"com.adobe.Contribute", -1, 4.2)
918         || WKAppVersionCheckLessThan(@"com.adobe.dreamweaver-9.0", -1, 9.1)
919         || WKAppVersionCheckLessThan(@"com.macromedia.fireworks", -1, 9.1)
920         || WKAppVersionCheckLessThan(@"com.adobe.InCopy", -1, 5.1)
921         || WKAppVersionCheckLessThan(@"com.adobe.InDesign", -1, 5.1)
922         || WKAppVersionCheckLessThan(@"com.adobe.Soundbooth", -1, 2);
923     checked = YES;
924
925     return needsQuirk;
926 }
927
928 - (BOOL)_needsKeyboardEventDisambiguationQuirks
929 {
930     static BOOL checked = NO;
931     static BOOL needsQuirks = NO;
932
933     if (checked)
934         return needsQuirks;
935
936     needsQuirks = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_IE_COMPATIBLE_KEYBOARD_EVENT_DISPATCH)
937         && ![[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.apple.Safari"];
938     checked = YES;
939
940     return needsQuirks;
941 }
942
943 - (void)_preferencesChangedNotification:(NSNotification *)notification
944 {
945     WebPreferences *preferences = (WebPreferences *)[notification object];
946     ASSERT(preferences == [self preferences]);
947     
948     if (!_private->userAgentOverridden)
949         *_private->userAgent = String();
950
951     // Cache this value so we don't have to read NSUserDefaults on each page load
952     _private->useSiteSpecificSpoofing = [preferences _useSiteSpecificSpoofing];
953
954     // Update corresponding WebCore Settings object.
955     if (!_private->page)
956         return;
957     
958     Settings* settings = _private->page->settings();
959     
960     settings->setCursiveFontFamily([preferences cursiveFontFamily]);
961     settings->setDefaultFixedFontSize([preferences defaultFixedFontSize]);
962     settings->setDefaultFontSize([preferences defaultFontSize]);
963     settings->setDefaultTextEncodingName([preferences defaultTextEncodingName]);
964     settings->setFantasyFontFamily([preferences fantasyFontFamily]);
965     settings->setFixedFontFamily([preferences fixedFontFamily]);
966     settings->setForceFTPDirectoryListings([preferences _forceFTPDirectoryListings]);
967     settings->setFTPDirectoryTemplatePath([preferences _ftpDirectoryTemplatePath]);
968     settings->setLocalStorageDatabasePath([preferences _localStorageDatabasePath]);
969     settings->setJavaEnabled([preferences isJavaEnabled]);
970     settings->setJavaScriptEnabled([preferences isJavaScriptEnabled]);
971     settings->setJavaScriptCanOpenWindowsAutomatically([preferences javaScriptCanOpenWindowsAutomatically]);
972     settings->setMinimumFontSize([preferences minimumFontSize]);
973     settings->setMinimumLogicalFontSize([preferences minimumLogicalFontSize]);
974     settings->setPluginsEnabled([preferences arePlugInsEnabled]);
975     settings->setPrivateBrowsingEnabled([preferences privateBrowsingEnabled]);
976     settings->setSansSerifFontFamily([preferences sansSerifFontFamily]);
977     settings->setSerifFontFamily([preferences serifFontFamily]);
978     settings->setStandardFontFamily([preferences standardFontFamily]);
979     settings->setLoadsImagesAutomatically([preferences loadsImagesAutomatically]);
980     settings->setShouldPrintBackgrounds([preferences shouldPrintBackgrounds]);
981     settings->setTextAreasAreResizable([preferences textAreasAreResizable]);
982     settings->setShrinksStandaloneImagesToFit([preferences shrinksStandaloneImagesToFit]);
983     settings->setEditableLinkBehavior(core([preferences editableLinkBehavior]));
984     settings->setDOMPasteAllowed([preferences isDOMPasteAllowed]);
985     settings->setUsesPageCache([self usesPageCache]);
986     settings->setShowsURLsInToolTips([preferences showsURLsInToolTips]);
987     settings->setDeveloperExtrasEnabled([preferences developerExtrasEnabled]);
988     settings->setAuthorAndUserStylesEnabled([preferences authorAndUserStylesEnabled]);
989     if ([preferences userStyleSheetEnabled]) {
990         NSString* location = [[preferences userStyleSheetLocation] _web_originalDataAsString];
991         settings->setUserStyleSheetLocation([NSURL URLWithString:(location ? location : @"")]);
992     } else
993         settings->setUserStyleSheetLocation([NSURL URLWithString:@""]);
994     settings->setNeedsAdobeFrameReloadingQuirk([self _needsAdobeFrameReloadingQuirk]);
995     settings->setNeedsKeyboardEventDisambiguationQuirks([self _needsKeyboardEventDisambiguationQuirks]);
996     settings->setNeedsSiteSpecificQuirks(_private->useSiteSpecificSpoofing);
997     settings->setWebArchiveDebugModeEnabled([preferences webArchiveDebugModeEnabled]);
998     settings->disableRangeMutationForOldAppleMail(WKAppVersionCheckLessThan(@"com.apple.mail", -1, 4.0));
999     settings->setOfflineWebApplicationCacheEnabled([preferences offlineWebApplicationCacheEnabled]);
1000 }
1001
1002 static inline IMP getMethod(id o, SEL s)
1003 {
1004     return [o respondsToSelector:s] ? [o methodForSelector:s] : 0;
1005 }
1006
1007 - (void)_cacheResourceLoadDelegateImplementations
1008 {
1009     WebResourceDelegateImplementationCache *cache = &_private->resourceLoadDelegateImplementations;
1010     id delegate = _private->resourceProgressDelegate;
1011
1012     if (!delegate) {
1013         bzero(cache, sizeof(WebResourceDelegateImplementationCache));
1014         return;
1015     }
1016
1017     cache->didCancelAuthenticationChallengeFunc = getMethod(delegate, @selector(webView:resource:didReceiveAuthenticationChallenge:fromDataSource:));
1018     cache->didFailLoadingWithErrorFromDataSourceFunc = getMethod(delegate, @selector(webView:resource:didFailLoadingWithError:fromDataSource:));
1019     cache->didFinishLoadingFromDataSourceFunc = getMethod(delegate, @selector(webView:resource:didFinishLoadingFromDataSource:));
1020     cache->didLoadResourceFromMemoryCacheFunc = getMethod(delegate, @selector(webView:didLoadResourceFromMemoryCache:response:length:fromDataSource:));
1021     cache->didReceiveAuthenticationChallengeFunc = getMethod(delegate, @selector(webView:resource:didReceiveAuthenticationChallenge:fromDataSource:));
1022     cache->didReceiveContentLengthFunc = getMethod(delegate, @selector(webView:resource:didReceiveContentLength:fromDataSource:));
1023     cache->didReceiveResponseFunc = getMethod(delegate, @selector(webView:resource:didReceiveResponse:fromDataSource:));
1024     cache->identifierForRequestFunc = getMethod(delegate, @selector(webView:identifierForInitialRequest:fromDataSource:));
1025     cache->plugInFailedWithErrorFunc = getMethod(delegate, @selector(webView:plugInFailedWithError:dataSource:));
1026     cache->willCacheResponseFunc = getMethod(delegate, @selector(webView:resource:willCacheResponse:fromDataSource:));
1027     cache->willSendRequestFunc = getMethod(delegate, @selector(webView:resource:willSendRequest:redirectResponse:fromDataSource:));
1028 }
1029
1030 WebResourceDelegateImplementationCache* WebViewGetResourceLoadDelegateImplementations(WebView *webView)
1031 {
1032     static WebResourceDelegateImplementationCache empty;
1033     if (!webView)
1034         return &empty;
1035     return &webView->_private->resourceLoadDelegateImplementations;
1036 }
1037
1038 - (void)_cacheFrameLoadDelegateImplementations
1039 {
1040     WebFrameLoadDelegateImplementationCache *cache = &_private->frameLoadDelegateImplementations;
1041     id delegate = _private->frameLoadDelegate;
1042
1043     if (!delegate) {
1044         bzero(cache, sizeof(WebFrameLoadDelegateImplementationCache));
1045         return;
1046     }
1047
1048     cache->didCancelClientRedirectForFrameFunc = getMethod(delegate, @selector(webView:didCancelClientRedirectForFrame:));
1049     cache->didChangeLocationWithinPageForFrameFunc = getMethod(delegate, @selector(webView:didChangeLocationWithinPageForFrame:));
1050     cache->didClearWindowObjectForFrameFunc = getMethod(delegate, @selector(webView:didClearWindowObject:forFrame:));
1051     cache->didCommitLoadForFrameFunc = getMethod(delegate, @selector(webView:didCommitLoadForFrame:));
1052     cache->didFailLoadWithErrorForFrameFunc = getMethod(delegate, @selector(webView:didFailLoadWithError:forFrame:));
1053     cache->didFailProvisionalLoadWithErrorForFrameFunc = getMethod(delegate, @selector(webView:didFailProvisionalLoadWithError:forFrame:));
1054     cache->didFinishDocumentLoadForFrameFunc = getMethod(delegate, @selector(webView:didFinishDocumentLoadForFrame:));
1055     cache->didFinishLoadForFrameFunc = getMethod(delegate, @selector(webView:didFinishLoadForFrame:));
1056     cache->didFirstLayoutInFrameFunc = getMethod(delegate, @selector(webView:didFirstLayoutInFrame:));
1057     cache->didHandleOnloadEventsForFrameFunc = getMethod(delegate, @selector(webView:didHandleOnloadEventsForFrame:));
1058     cache->didReceiveIconForFrameFunc = getMethod(delegate, @selector(webView:didReceiveIcon:forFrame:));
1059     cache->didReceiveServerRedirectForProvisionalLoadForFrameFunc = getMethod(delegate, @selector(webView:didReceiveServerRedirectForProvisionalLoadForFrame:));
1060     cache->didReceiveTitleForFrameFunc = getMethod(delegate, @selector(webView:didReceiveTitle:forFrame:));
1061     cache->didStartProvisionalLoadForFrameFunc = getMethod(delegate, @selector(webView:didStartProvisionalLoadForFrame:));
1062     cache->willCloseFrameFunc = getMethod(delegate, @selector(webView:willCloseFrame:));
1063     cache->willPerformClientRedirectToURLDelayFireDateForFrameFunc = getMethod(delegate, @selector(webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:));
1064     cache->windowScriptObjectAvailableFunc = getMethod(delegate, @selector(webView:windowScriptObjectAvailable:));
1065 }
1066
1067 WebFrameLoadDelegateImplementationCache* WebViewGetFrameLoadDelegateImplementations(WebView *webView)
1068 {
1069     static WebFrameLoadDelegateImplementationCache empty;
1070     if (!webView)
1071         return &empty;
1072     return &webView->_private->frameLoadDelegateImplementations;
1073 }
1074
1075 - (id)_policyDelegateForwarder
1076 {
1077     if (!_private->policyDelegateForwarder)
1078         _private->policyDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget:_private->policyDelegate defaultTarget:[WebDefaultPolicyDelegate sharedPolicyDelegate] catchExceptions:_private->catchesDelegateExceptions];
1079     return _private->policyDelegateForwarder;
1080 }
1081
1082 - (id)_UIDelegateForwarder
1083 {
1084     if (!_private->UIDelegateForwarder)
1085         _private->UIDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget:_private->UIDelegate defaultTarget:[WebDefaultUIDelegate sharedUIDelegate] catchExceptions:_private->catchesDelegateExceptions];
1086     return _private->UIDelegateForwarder;
1087 }
1088
1089 - (id)_editingDelegateForwarder
1090 {
1091     // This can be called during window deallocation by QTMovieView in the QuickTime Cocoa Plug-in.
1092     // Not sure if that is a bug or not.
1093     if (!_private)
1094         return nil;
1095
1096     if (!_private->editingDelegateForwarder)
1097         _private->editingDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget:_private->editingDelegate defaultTarget:[WebDefaultEditingDelegate sharedEditingDelegate] catchExceptions:_private->catchesDelegateExceptions];
1098     return _private->editingDelegateForwarder;
1099 }
1100
1101 - (id)_scriptDebugDelegateForwarder
1102 {
1103     if (!_private->scriptDebugDelegateForwarder)
1104         _private->scriptDebugDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget:_private->scriptDebugDelegate defaultTarget:[WebDefaultScriptDebugDelegate sharedScriptDebugDelegate] catchExceptions:_private->catchesDelegateExceptions];
1105     return _private->scriptDebugDelegateForwarder;
1106 }
1107
1108 - (void)_closeWindow
1109 {
1110     [[self _UIDelegateForwarder] webViewClose:self];
1111 }
1112
1113 + (void)_unregisterViewClassAndRepresentationClassForMIMEType:(NSString *)MIMEType;
1114 {
1115     [[WebFrameView _viewTypesAllowImageTypeOmission:NO] removeObjectForKey:MIMEType];
1116     [[WebDataSource _repTypesAllowImageTypeOmission:NO] removeObjectForKey:MIMEType];
1117     
1118     // FIXME: We also need to maintain MIMEType registrations (which can be dynamically changed)
1119     // in the WebCore MIMEType registry.  For now we're doing this in a safe, limited manner
1120     // to fix <rdar://problem/5372989> - a future revamping of the entire system is neccesary for future robustness
1121     MIMETypeRegistry::getSupportedNonImageMIMETypes().remove(MIMEType);
1122 }
1123
1124 + (void)_registerViewClass:(Class)viewClass representationClass:(Class)representationClass forURLScheme:(NSString *)URLScheme;
1125 {
1126     NSString *MIMEType = [self _generatedMIMETypeForURLScheme:URLScheme];
1127     [self registerViewClass:viewClass representationClass:representationClass forMIMEType:MIMEType];
1128
1129     // FIXME: We also need to maintain MIMEType registrations (which can be dynamically changed)
1130     // in the WebCore MIMEType registry.  For now we're doing this in a safe, limited manner
1131     // to fix <rdar://problem/5372989> - a future revamping of the entire system is neccesary for future robustness
1132     if ([viewClass class] == [WebHTMLView class])
1133         MIMETypeRegistry::getSupportedNonImageMIMETypes().add(MIMEType);
1134     
1135     // This is used to make _representationExistsForURLScheme faster.
1136     // Without this set, we'd have to create the MIME type each time.
1137     if (schemesWithRepresentationsSet == nil) {
1138         schemesWithRepresentationsSet = [[NSMutableSet alloc] init];
1139     }
1140     [schemesWithRepresentationsSet addObject:[[[URLScheme lowercaseString] copy] autorelease]];
1141 }
1142
1143 + (NSString *)_generatedMIMETypeForURLScheme:(NSString *)URLScheme
1144 {
1145     return [@"x-apple-web-kit/" stringByAppendingString:[URLScheme lowercaseString]];
1146 }
1147
1148 + (BOOL)_representationExistsForURLScheme:(NSString *)URLScheme
1149 {
1150     return [schemesWithRepresentationsSet containsObject:[URLScheme lowercaseString]];
1151 }
1152
1153 + (BOOL)_canHandleRequest:(NSURLRequest *)request
1154 {
1155     // FIXME: If <rdar://problem/5217309> gets fixed, this check can be removed
1156     if (!request)
1157         return NO;
1158
1159     if ([NSURLConnection canHandleRequest:request])
1160         return YES;
1161
1162     NSString *scheme = [[request URL] scheme];
1163
1164     if ([self _representationExistsForURLScheme:scheme])
1165         return YES;
1166         
1167     return ([scheme _webkit_isCaseInsensitiveEqualToString:@"applewebdata"]);
1168 }
1169
1170 + (NSString *)_decodeData:(NSData *)data
1171 {
1172     HTMLNames::init(); // this method is used for importing bookmarks at startup, so HTMLNames are likely to be uninitialized yet
1173     RefPtr<TextResourceDecoder> decoder = new TextResourceDecoder("text/html"); // bookmark files are HTML
1174     String result = decoder->decode(static_cast<const char*>([data bytes]), [data length]);
1175     result += decoder->flush();
1176     return result;
1177 }
1178
1179 - (void)_pushPerformingProgrammaticFocus
1180 {
1181     _private->programmaticFocusCount++;
1182 }
1183
1184 - (void)_popPerformingProgrammaticFocus
1185 {
1186     _private->programmaticFocusCount--;
1187 }
1188
1189 - (BOOL)_isPerformingProgrammaticFocus
1190 {
1191     return _private->programmaticFocusCount != 0;
1192 }
1193
1194 - (void)_didChangeValueForKey: (NSString *)key
1195 {
1196     LOG (Bindings, "calling didChangeValueForKey: %@", key);
1197     [self didChangeValueForKey: key];
1198 }
1199
1200 - (void)_willChangeValueForKey: (NSString *)key
1201 {
1202     LOG (Bindings, "calling willChangeValueForKey: %@", key);
1203     [self willChangeValueForKey: key];
1204 }
1205
1206 + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
1207     static NSSet *manualNotifyKeys = nil;
1208     if (!manualNotifyKeys)
1209         manualNotifyKeys = [[NSSet alloc] initWithObjects:_WebMainFrameURLKey, _WebIsLoadingKey, _WebEstimatedProgressKey,
1210             _WebCanGoBackKey, _WebCanGoForwardKey, _WebMainFrameTitleKey, _WebMainFrameIconKey, _WebMainFrameDocumentKey, nil];
1211     if ([manualNotifyKeys containsObject:key])
1212         return NO;
1213     return YES;
1214 }
1215
1216 - (NSArray *)_declaredKeys {
1217     static NSArray *declaredKeys = nil;
1218     if (!declaredKeys)
1219         declaredKeys = [[NSArray alloc] initWithObjects:_WebMainFrameURLKey, _WebIsLoadingKey, _WebEstimatedProgressKey,
1220             _WebCanGoBackKey, _WebCanGoForwardKey, _WebMainFrameTitleKey, _WebMainFrameIconKey, _WebMainFrameDocumentKey, nil];
1221     return declaredKeys;
1222 }
1223
1224 - (void)setObservationInfo:(void *)info
1225 {
1226     _private->observationInfo = info;
1227 }
1228
1229 - (void *)observationInfo
1230 {
1231     return _private->observationInfo;
1232 }
1233
1234 - (void)_willChangeBackForwardKeys
1235 {
1236     [self _willChangeValueForKey: _WebCanGoBackKey];
1237     [self _willChangeValueForKey: _WebCanGoForwardKey];
1238 }
1239
1240 - (void)_didChangeBackForwardKeys
1241 {
1242     [self _didChangeValueForKey: _WebCanGoBackKey];
1243     [self _didChangeValueForKey: _WebCanGoForwardKey];
1244 }
1245
1246 - (void)_didStartProvisionalLoadForFrame:(WebFrame *)frame
1247 {
1248     [self _willChangeBackForwardKeys];
1249     if (frame == [self mainFrame]){
1250         // Force an observer update by sending a will/did.
1251         [self _willChangeValueForKey: _WebIsLoadingKey];
1252         [self _didChangeValueForKey: _WebIsLoadingKey];
1253
1254         [self _willChangeValueForKey: _WebMainFrameURLKey];
1255     }
1256
1257     [NSApp setWindowsNeedUpdate:YES];
1258 }
1259
1260 - (void)_didCommitLoadForFrame:(WebFrame *)frame
1261 {
1262     if (frame == [self mainFrame])
1263         [self _didChangeValueForKey: _WebMainFrameURLKey];
1264     [NSApp setWindowsNeedUpdate:YES];
1265 }
1266
1267 - (void)_didFinishLoadForFrame:(WebFrame *)frame
1268 {
1269     [self _didChangeBackForwardKeys];
1270     if (frame == [self mainFrame]){
1271         // Force an observer update by sending a will/did.
1272         [self _willChangeValueForKey: _WebIsLoadingKey];
1273         [self _didChangeValueForKey: _WebIsLoadingKey];
1274     }
1275     [NSApp setWindowsNeedUpdate:YES];
1276 }
1277
1278 - (void)_didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
1279 {
1280     [self _didChangeBackForwardKeys];
1281     if (frame == [self mainFrame]){
1282         // Force an observer update by sending a will/did.
1283         [self _willChangeValueForKey: _WebIsLoadingKey];
1284         [self _didChangeValueForKey: _WebIsLoadingKey];
1285     }
1286     [NSApp setWindowsNeedUpdate:YES];
1287 }
1288
1289 - (void)_didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
1290 {
1291     [self _didChangeBackForwardKeys];
1292     if (frame == [self mainFrame]){
1293         // Force an observer update by sending a will/did.
1294         [self _willChangeValueForKey: _WebIsLoadingKey];
1295         [self _didChangeValueForKey: _WebIsLoadingKey];
1296         
1297         [self _didChangeValueForKey: _WebMainFrameURLKey];
1298     }
1299     [NSApp setWindowsNeedUpdate:YES];
1300 }
1301
1302 - (NSCachedURLResponse *)_cachedResponseForURL:(NSURL *)URL
1303 {
1304     NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];
1305     [request _web_setHTTPUserAgent:[self userAgentForURL:URL]];
1306     NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
1307     [request release];
1308     return cachedResponse;
1309 }
1310
1311 - (void)_writeImageForElement:(NSDictionary *)element withPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
1312 {
1313     NSURL *linkURL = [element objectForKey:WebElementLinkURLKey];
1314     DOMElement *domElement = [element objectForKey:WebElementDOMNodeKey];
1315     [pasteboard _web_writeImage:(NSImage *)(domElement ? nil : [element objectForKey:WebElementImageKey])
1316                         element:domElement
1317                             URL:linkURL ? linkURL : (NSURL *)[element objectForKey:WebElementImageURLKey]
1318                           title:[element objectForKey:WebElementImageAltStringKey] 
1319                         archive:[[element objectForKey:WebElementDOMNodeKey] webArchive]
1320                           types:types
1321                          source:nil];
1322 }
1323
1324 - (void)_writeLinkElement:(NSDictionary *)element withPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
1325 {
1326     [pasteboard _web_writeURL:[element objectForKey:WebElementLinkURLKey]
1327                      andTitle:[element objectForKey:WebElementLinkLabelKey]
1328                         types:types];
1329 }
1330
1331 - (void)_setInitiatedDrag:(BOOL)initiatedDrag
1332 {
1333     if (!_private->page)
1334         return;
1335     _private->page->dragController()->setDidInitiateDrag(initiatedDrag);
1336 }
1337
1338 #if ENABLE(DASHBOARD_SUPPORT)
1339
1340 #define DASHBOARD_CONTROL_LABEL @"control"
1341
1342 - (void)_addScrollerDashboardRegions:(NSMutableDictionary *)regions from:(NSArray *)views
1343 {
1344     // Add scroller regions for NSScroller and KWQScrollBar
1345     int i, count = [views count];
1346     
1347     for (i = 0; i < count; i++) {
1348         NSView *aView = [views objectAtIndex:i];
1349         
1350         if ([aView isKindOfClass:[NSScroller class]] ||
1351             [aView isKindOfClass:NSClassFromString (@"KWQScrollBar")]) {
1352             NSRect bounds = [aView bounds];
1353             NSRect adjustedBounds;
1354             adjustedBounds.origin = [self convertPoint:bounds.origin fromView:aView];
1355             adjustedBounds.origin.y = [self bounds].size.height - adjustedBounds.origin.y;
1356             
1357             // AppKit has horrible hack of placing absent scrollers at -100,-100
1358             if (adjustedBounds.origin.y == -100)
1359                 continue;
1360             adjustedBounds.size = bounds.size;
1361             NSRect clip = [aView visibleRect];
1362             NSRect adjustedClip;
1363             adjustedClip.origin = [self convertPoint:clip.origin fromView:aView];
1364             adjustedClip.origin.y = [self bounds].size.height - adjustedClip.origin.y;
1365             adjustedClip.size = clip.size;
1366             WebDashboardRegion *aRegion = 
1367                         [[[WebDashboardRegion alloc] initWithRect:adjustedBounds 
1368                                     clip:adjustedClip type:WebDashboardRegionTypeScrollerRectangle] autorelease];
1369             NSMutableArray *scrollerRegions;
1370             scrollerRegions = [regions objectForKey:DASHBOARD_CONTROL_LABEL];
1371             if (!scrollerRegions) {
1372                 scrollerRegions = [NSMutableArray array];
1373                 [regions setObject:scrollerRegions forKey:DASHBOARD_CONTROL_LABEL];
1374             }
1375             [scrollerRegions addObject:aRegion];
1376         }
1377         [self _addScrollerDashboardRegions:regions from:[aView subviews]];
1378     }
1379 }
1380
1381 - (void)_addScrollerDashboardRegions:(NSMutableDictionary *)regions
1382 {
1383     [self _addScrollerDashboardRegions:regions from:[self subviews]];
1384 }
1385
1386 - (NSDictionary *)_dashboardRegions
1387 {
1388     // Only return regions from main frame.
1389     Frame* mainFrame = core([self mainFrame]);
1390     if (!mainFrame)
1391         return nil;
1392     NSMutableDictionary *regions = mainFrame->dashboardRegionsDictionary();
1393     [self _addScrollerDashboardRegions:regions];
1394     return regions;
1395 }
1396
1397 - (void)_setDashboardBehavior:(WebDashboardBehavior)behavior to:(BOOL)flag
1398 {
1399     // FIXME: Remove this blanket assignment once Dashboard and Dashcode implement 
1400     // specific support for the backward compatibility mode flag.
1401     if (behavior == WebDashboardBehaviorAllowWheelScrolling && flag == NO && _private->page)
1402         _private->page->settings()->setUsesDashboardBackwardCompatibilityMode(true);
1403     
1404     switch (behavior) {
1405         case WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows: {
1406             _private->dashboardBehaviorAlwaysSendMouseEventsToAllWindows = flag;
1407             break;
1408         }
1409         case WebDashboardBehaviorAlwaysSendActiveNullEventsToPlugIns: {
1410             _private->dashboardBehaviorAlwaysSendActiveNullEventsToPlugIns = flag;
1411             break;
1412         }
1413         case WebDashboardBehaviorAlwaysAcceptsFirstMouse: {
1414             _private->dashboardBehaviorAlwaysAcceptsFirstMouse = flag;
1415             break;
1416         }
1417         case WebDashboardBehaviorAllowWheelScrolling: {
1418             _private->dashboardBehaviorAllowWheelScrolling = flag;
1419             break;
1420         }
1421         case WebDashboardBehaviorUseBackwardCompatibilityMode: {
1422             if (_private->page)
1423                 _private->page->settings()->setUsesDashboardBackwardCompatibilityMode(flag);
1424             break;
1425         }
1426     }
1427 }
1428
1429 - (BOOL)_dashboardBehavior:(WebDashboardBehavior)behavior
1430 {
1431     switch (behavior) {
1432         case WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows: {
1433             return _private->dashboardBehaviorAlwaysSendMouseEventsToAllWindows;
1434         }
1435         case WebDashboardBehaviorAlwaysSendActiveNullEventsToPlugIns: {
1436             return _private->dashboardBehaviorAlwaysSendActiveNullEventsToPlugIns;
1437         }
1438         case WebDashboardBehaviorAlwaysAcceptsFirstMouse: {
1439             return _private->dashboardBehaviorAlwaysAcceptsFirstMouse;
1440         }
1441         case WebDashboardBehaviorAllowWheelScrolling: {
1442             return _private->dashboardBehaviorAllowWheelScrolling;
1443         }
1444         case WebDashboardBehaviorUseBackwardCompatibilityMode: {
1445             return _private->page && _private->page->settings()->usesDashboardBackwardCompatibilityMode();
1446         }
1447     }
1448     return NO;
1449 }
1450
1451 #endif /* ENABLE(DASHBOARD_SUPPORT) */
1452
1453 + (void)_setShouldUseFontSmoothing:(BOOL)f
1454 {
1455     WebCoreSetShouldUseFontSmoothing(f);
1456 }
1457
1458 + (BOOL)_shouldUseFontSmoothing
1459 {
1460     return WebCoreShouldUseFontSmoothing();
1461 }
1462
1463 + (void)_setUsesTestModeFocusRingColor:(BOOL)f
1464 {
1465     setUsesTestModeFocusRingColor(f);
1466 }
1467
1468 + (BOOL)_usesTestModeFocusRingColor
1469 {
1470     return usesTestModeFocusRingColor();
1471 }
1472
1473 // This is only used by versions of Safari up to and including 3.0 and should be removed in a future release. 
1474 + (NSString *)_minimumRequiredSafariBuildNumber
1475 {
1476     return @"420+";
1477 }
1478
1479 - (void)setAlwaysShowVerticalScroller:(BOOL)flag
1480 {
1481     WebDynamicScrollBarsView *scrollview = [[[self mainFrame] frameView] _scrollView];
1482     if (flag) {
1483         [scrollview setVerticalScrollingMode:ScrollbarAlwaysOn andLock:YES];
1484     } else {
1485         [scrollview setVerticalScrollingModeLocked:NO];
1486         [scrollview setVerticalScrollingMode:ScrollbarAuto];
1487     }
1488 }
1489
1490 - (BOOL)alwaysShowVerticalScroller
1491 {
1492     WebDynamicScrollBarsView *scrollview = [[[self mainFrame] frameView] _scrollView];
1493     return [scrollview verticalScrollingModeLocked] && [scrollview verticalScrollingMode] == ScrollbarAlwaysOn;
1494 }
1495
1496 - (void)setAlwaysShowHorizontalScroller:(BOOL)flag
1497 {
1498     WebDynamicScrollBarsView *scrollview = [[[self mainFrame] frameView] _scrollView];
1499     if (flag) {
1500         [scrollview setHorizontalScrollingMode:ScrollbarAlwaysOn andLock:YES];
1501     } else {
1502         [scrollview setHorizontalScrollingModeLocked:NO];
1503         [scrollview setHorizontalScrollingMode:ScrollbarAuto];
1504     }
1505 }
1506
1507 - (void)setProhibitsMainFrameScrolling:(BOOL)prohibits
1508 {
1509     Frame* mainFrame = core([self mainFrame]);
1510     if (mainFrame)
1511         mainFrame->setProhibitsScrolling(prohibits);
1512 }
1513
1514 - (BOOL)alwaysShowHorizontalScroller
1515 {
1516     WebDynamicScrollBarsView *scrollview = [[[self mainFrame] frameView] _scrollView];
1517     return [scrollview horizontalScrollingModeLocked] && [scrollview horizontalScrollingMode] == ScrollbarAlwaysOn;
1518 }
1519
1520 - (void)_setInViewSourceMode:(BOOL)flag
1521 {
1522     Frame* mainFrame = core([self mainFrame]);
1523     if (mainFrame)
1524         mainFrame->setInViewSourceMode(flag);
1525 }
1526
1527 - (BOOL)_inViewSourceMode
1528 {
1529     Frame* mainFrame = core([self mainFrame]);
1530     return mainFrame && mainFrame->inViewSourceMode();
1531 }
1532
1533 - (void)_setUseFastImageScalingMode:(BOOL)flag
1534 {
1535     if (_private->page && _private->page->inLowQualityImageInterpolationMode() != flag) {
1536         _private->page->setInLowQualityImageInterpolationMode(flag);
1537         [self setNeedsDisplay:YES];
1538     }
1539 }
1540
1541 - (BOOL)_inFastImageScalingMode
1542 {
1543     if (_private->page)
1544         return _private->page->inLowQualityImageInterpolationMode();
1545     return NO;
1546 }
1547
1548 - (void)_setAdditionalWebPlugInPaths:(NSArray *)newPaths
1549 {
1550     if (!_private->pluginDatabase)
1551         _private->pluginDatabase = [[WebPluginDatabase alloc] init];
1552         
1553     [_private->pluginDatabase setPlugInPaths:newPaths];
1554     [_private->pluginDatabase refresh];
1555 }
1556
1557 - (void)_attachScriptDebuggerToAllFrames
1558 {
1559     for (Frame* frame = core([self mainFrame]); frame; frame = frame->tree()->traverseNext())
1560         [kit(frame) _attachScriptDebugger];
1561 }
1562
1563 - (void)_detachScriptDebuggerFromAllFrames
1564 {
1565     for (Frame* frame = core([self mainFrame]); frame; frame = frame->tree()->traverseNext())
1566         [kit(frame) _detachScriptDebugger];
1567 }
1568
1569 - (void)setBackgroundColor:(NSColor *)backgroundColor
1570 {
1571     if ([_private->backgroundColor isEqual:backgroundColor])
1572         return;
1573
1574     id old = _private->backgroundColor;
1575     _private->backgroundColor = [backgroundColor retain];
1576     [old release];
1577
1578     [[self mainFrame] _updateBackground];
1579 }
1580
1581 - (NSColor *)backgroundColor
1582 {
1583     return _private->backgroundColor;
1584 }
1585
1586 - (BOOL)defersCallbacks
1587 {
1588     if (!_private->page)
1589         return NO;
1590     return _private->page->defersLoading();
1591 }
1592
1593 - (void)setDefersCallbacks:(BOOL)defer
1594 {
1595     if (!_private->page)
1596         return;
1597     return _private->page->setDefersLoading(defer);
1598 }
1599
1600 // For backwards compatibility with the WebBackForwardList API, we honor both
1601 // a per-WebView and a per-preferences setting for whether to use the page cache.
1602
1603 - (BOOL)usesPageCache
1604 {
1605     return _private->usesPageCache && [[self preferences] usesPageCache];
1606 }
1607
1608 - (void)setUsesPageCache:(BOOL)usesPageCache
1609 {
1610     _private->usesPageCache = usesPageCache;
1611
1612     // Post a notification so the WebCore settings update.
1613     [[self preferences] _postPreferencesChangesNotification];
1614 }
1615
1616 - (void)handleAuthenticationForResource:(id)identifier challenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)dataSource 
1617 {
1618     NSWindow *window = [self hostWindow] ? [self hostWindow] : [self window]; 
1619     [[WebPanelAuthenticationHandler sharedHandler] startAuthentication:challenge window:window]; 
1620
1621
1622 - (void)_clearUndoRedoOperations
1623 {
1624     if (!_private->page)
1625         return;
1626     _private->page->clearUndoRedoOperations();
1627 }
1628
1629 - (void)_setCatchesDelegateExceptions:(BOOL)f
1630 {
1631     _private->catchesDelegateExceptions = f;
1632 }
1633
1634 - (BOOL)_catchesDelegateExceptions
1635 {
1636     return _private->catchesDelegateExceptions;
1637 }
1638
1639 - (void)_executeCoreCommandByName:(NSString *)name value:(NSString *)value
1640 {
1641     Frame* coreFrame = core([self mainFrame]);
1642     if (!coreFrame)
1643         return;
1644     coreFrame->editor()->command(name).execute(value);
1645 }
1646
1647 @end
1648
1649 @implementation _WebSafeForwarder
1650
1651 // Used to send messages to delegates that implement informal protocols.
1652
1653 - (id)initWithTarget:(id)t defaultTarget:(id)dt catchExceptions:(BOOL)c
1654 {
1655     self = [super init];
1656     if (!self)
1657         return nil;
1658     target = t; // Non retained.
1659     defaultTarget = dt;
1660     catchExceptions = c;
1661     return self;
1662 }
1663
1664 - (void)forwardInvocation:(NSInvocation *)invocation
1665 {
1666     if ([target respondsToSelector:[invocation selector]]) {
1667         if (catchExceptions) {
1668             @try {
1669                 [invocation invokeWithTarget:target];
1670             } @catch(id exception) {
1671                 ReportDiscardedDelegateException([invocation selector], exception);
1672             }
1673         } else
1674             [invocation invokeWithTarget:target];
1675         return;
1676     }
1677
1678     if ([defaultTarget respondsToSelector:[invocation selector]])
1679         [invocation invokeWithTarget:defaultTarget];
1680
1681     // Do nothing quietly if method not implemented.
1682 }
1683
1684 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
1685 {
1686     return [defaultTarget methodSignatureForSelector:aSelector];
1687 }
1688
1689 @end
1690
1691 @implementation WebView
1692
1693 + (void)initialize
1694 {
1695     static BOOL initialized = NO;
1696     if (initialized)
1697         return;
1698     initialized = YES;
1699
1700     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationWillTerminate) name:NSApplicationWillTerminateNotification object:NSApp];
1701     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesChangedNotification:) name:WebPreferencesChangedNotification object:nil];
1702     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesRemovedNotification:) name:WebPreferencesRemovedNotification object:nil];
1703 }
1704
1705 + (void)_applicationWillTerminate
1706 {   
1707     applicationIsTerminating = YES;
1708     if (!pluginDatabaseClientCount)
1709         [WebPluginDatabase closeSharedDatabase];
1710 }
1711
1712 + (BOOL)canShowMIMEType:(NSString *)MIMEType
1713 {
1714     return [self _viewClass:nil andRepresentationClass:nil forMIMEType:MIMEType];
1715 }
1716
1717 - (WebBasePluginPackage *)_pluginForMIMEType:(NSString *)MIMEType
1718 {
1719     WebBasePluginPackage *pluginPackage = [[WebPluginDatabase sharedDatabase] pluginForMIMEType:MIMEType];
1720     if (pluginPackage)
1721         return pluginPackage;
1722     
1723     if (_private->pluginDatabase)
1724         return [_private->pluginDatabase pluginForMIMEType:MIMEType];
1725     
1726     return nil;
1727 }
1728
1729 - (WebBasePluginPackage *)_pluginForExtension:(NSString *)extension
1730 {
1731     WebBasePluginPackage *pluginPackage = [[WebPluginDatabase sharedDatabase] pluginForExtension:extension];
1732     if (pluginPackage)
1733         return pluginPackage;
1734     
1735     if (_private->pluginDatabase)
1736         return [_private->pluginDatabase pluginForExtension:extension];
1737     
1738     return nil;
1739 }
1740
1741 - (BOOL)_isMIMETypeRegisteredAsPlugin:(NSString *)MIMEType
1742 {
1743     if ([[WebPluginDatabase sharedDatabase] isMIMETypeRegistered:MIMEType])
1744         return YES;
1745         
1746     if (_private->pluginDatabase && [_private->pluginDatabase isMIMETypeRegistered:MIMEType])
1747         return YES;
1748     
1749     return NO;
1750 }
1751
1752 + (BOOL)canShowMIMETypeAsHTML:(NSString *)MIMEType
1753 {
1754     return [WebFrameView _canShowMIMETypeAsHTML:MIMEType];
1755 }
1756
1757 + (NSArray *)MIMETypesShownAsHTML
1758 {
1759     NSMutableDictionary *viewTypes = [WebFrameView _viewTypesAllowImageTypeOmission:YES];
1760     NSEnumerator *enumerator = [viewTypes keyEnumerator];
1761     id key;
1762     NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease];
1763     
1764     while ((key = [enumerator nextObject])) {
1765         if ([viewTypes objectForKey:key] == [WebHTMLView class])
1766             [array addObject:key];
1767     }
1768     
1769     return array;
1770 }
1771
1772 + (void)setMIMETypesShownAsHTML:(NSArray *)MIMETypes
1773 {
1774     NSDictionary *viewTypes = [[WebFrameView _viewTypesAllowImageTypeOmission:YES] copy];
1775     NSEnumerator *enumerator = [viewTypes keyEnumerator];
1776     id key;
1777     while ((key = [enumerator nextObject])) {
1778         if ([viewTypes objectForKey:key] == [WebHTMLView class])
1779             [WebView _unregisterViewClassAndRepresentationClassForMIMEType:key];
1780     }
1781     
1782     int i, count = [MIMETypes count];
1783     for (i = 0; i < count; i++) {
1784         [WebView registerViewClass:[WebHTMLView class] 
1785                 representationClass:[WebHTMLRepresentation class] 
1786                 forMIMEType:[MIMETypes objectAtIndex:i]];
1787     }
1788     [viewTypes release];
1789 }
1790
1791 + (NSURL *)URLFromPasteboard:(NSPasteboard *)pasteboard
1792 {
1793     return [pasteboard _web_bestURL];
1794 }
1795
1796 + (NSString *)URLTitleFromPasteboard:(NSPasteboard *)pasteboard
1797 {
1798     return [pasteboard stringForType:WebURLNamePboardType];
1799 }
1800
1801 + (void)registerURLSchemeAsLocal:(NSString *)protocol
1802 {
1803     FrameLoader::registerURLSchemeAsLocal(protocol);
1804 }
1805
1806 - (void)_registerDraggedTypes
1807 {
1808     NSArray *editableTypes = [WebHTMLView _insertablePasteboardTypes];
1809     NSArray *URLTypes = [NSPasteboard _web_dragTypesForURL];
1810     NSMutableSet *types = [[NSMutableSet alloc] initWithArray:editableTypes];
1811     [types addObjectsFromArray:URLTypes];
1812     [self registerForDraggedTypes:[types allObjects]];
1813     [types release];
1814 }
1815
1816 static void WebKitInitializeApplicationCachePathIfNecessary()
1817 {
1818     static BOOL initialized = NO;
1819     if (initialized)
1820         return;
1821
1822     NSString *appName = [[NSBundle mainBundle] bundleIdentifier];
1823     if (!appName)
1824         appName = [[NSProcessInfo processInfo] processName];
1825     
1826     ASSERT(appName);
1827
1828     NSString* cacheDir = nil;
1829     
1830 #ifdef BUILDING_ON_TIGER
1831     cacheDir = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Caches"];
1832 #else
1833     char cacheDirectory[MAXPATHLEN];
1834     size_t cacheDirectoryLen = confstr(_CS_DARWIN_USER_CACHE_DIR, cacheDirectory, MAXPATHLEN);
1835     
1836     if (cacheDirectoryLen)
1837         cacheDir = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:cacheDirectory length:cacheDirectoryLen - 1];
1838 #endif
1839
1840     cacheDir = [cacheDir stringByAppendingPathComponent:appName];    
1841
1842     cacheStorage().setCacheDirectory(cacheDir);
1843     initialized = YES;
1844 }
1845
1846 - (void)_commonInitializationWithFrameName:(NSString *)frameName groupName:(NSString *)groupName
1847 {
1848     WebPreferences *standardPreferences = [WebPreferences standardPreferences];
1849     [standardPreferences willAddToWebView];
1850
1851     _private->preferences = [standardPreferences retain];
1852     _private->catchesDelegateExceptions = YES;
1853     _private->mainFrameDocumentReady = NO;
1854     _private->drawsBackground = YES;
1855     _private->smartInsertDeleteEnabled = YES;
1856     _private->backgroundColor = [[NSColor whiteColor] retain];
1857
1858     NSRect f = [self frame];
1859     WebFrameView *frameView = [[WebFrameView alloc] initWithFrame: NSMakeRect(0,0,f.size.width,f.size.height)];
1860     [frameView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
1861     [self addSubview:frameView];
1862     [frameView release];
1863
1864     WebKitInitializeLoggingChannelsIfNecessary();
1865     WebCore::InitializeLoggingChannelsIfNecessary();
1866     [WebHistoryItem initWindowWatcherIfNecessary];
1867     WebKitInitializeDatabasesIfNecessary();
1868     WebKitInitializeApplicationCachePathIfNecessary();
1869     
1870     _private->page = new Page(new WebChromeClient(self), new WebContextMenuClient(self), new WebEditorClient(self), new WebDragClient(self), new WebInspectorClient(self));
1871
1872     [WebFrame _createMainFrameWithPage:_private->page frameName:frameName frameView:frameView];
1873
1874 #ifndef BUILDING_ON_TIGER
1875     if (WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_LOADING_DURING_COMMON_RUNLOOP_MODES))
1876         [self scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
1877     else
1878         [self scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
1879 #endif
1880
1881     [self _addToAllWebViewsSet];
1882     [self setGroupName:groupName];
1883     
1884     // If there's already a next key view (e.g., from a nib), wire it up to our
1885     // contained frame view. In any case, wire our next key view up to the our
1886     // contained frame view. This works together with our becomeFirstResponder 
1887     // and setNextKeyView overrides.
1888     NSView *nextKeyView = [self nextKeyView];
1889     if (nextKeyView && nextKeyView != frameView)
1890         [frameView setNextKeyView:nextKeyView];
1891     [super setNextKeyView:frameView];
1892
1893     ++WebViewCount;
1894
1895     [self _registerDraggedTypes];
1896
1897     // initialize WebScriptDebugServer here so listeners can register before any pages are loaded.
1898     if ([WebView _scriptDebuggerEnabled])
1899         [WebScriptDebugServer sharedScriptDebugServer];
1900
1901     WebPreferences *prefs = [self preferences];
1902     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesChangedNotification:)
1903                                                  name:WebPreferencesChangedNotification object:prefs];
1904
1905     // Post a notification so the WebCore settings update.
1906     [[self preferences] _postPreferencesChangesNotification];
1907
1908
1909     if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_LOCAL_RESOURCE_SECURITY_RESTRICTION))
1910         FrameLoader::setRestrictAccessToLocal(false);
1911 }
1912
1913 - (id)initWithFrame:(NSRect)f
1914 {
1915     return [self initWithFrame:f frameName:nil groupName:nil];
1916 }
1917
1918 - (id)initWithFrame:(NSRect)f frameName:(NSString *)frameName groupName:(NSString *)groupName;
1919 {
1920     self = [super initWithFrame:f];
1921     if (!self)
1922         return nil;
1923
1924 #ifdef ENABLE_WEBKIT_UNSET_DYLD_FRAMEWORK_PATH
1925     // DYLD_FRAMEWORK_PATH is used so Safari will load the development version of WebKit, which
1926     // may not work with other WebKit applications.  Unsetting DYLD_FRAMEWORK_PATH removes the
1927     // need for Safari to unset it to prevent it from being passed to applications it launches.
1928     // Unsetting it when a WebView is first created is as good a place as any.
1929     // See <http://bugs.webkit.org/show_bug.cgi?id=4286> for more details.
1930     if (getenv("WEBKIT_UNSET_DYLD_FRAMEWORK_PATH")) {
1931         unsetenv("DYLD_FRAMEWORK_PATH");
1932         unsetenv("WEBKIT_UNSET_DYLD_FRAMEWORK_PATH");
1933     }
1934 #endif
1935
1936     _private = [[WebViewPrivate alloc] init];
1937     [self _commonInitializationWithFrameName:frameName groupName:groupName];
1938     [self setMaintainsBackForwardList: YES];
1939     return self;
1940 }
1941
1942 - (id)initWithCoder:(NSCoder *)decoder
1943 {
1944     WebView *result = nil;
1945
1946     @try {
1947         NSString *frameName;
1948         NSString *groupName;
1949         WebPreferences *preferences;
1950         BOOL useBackForwardList = NO;
1951         BOOL allowsUndo = YES;
1952         
1953         result = [super initWithCoder:decoder];
1954         result->_private = [[WebViewPrivate alloc] init];
1955
1956         // We don't want any of the archived subviews. The subviews will always
1957         // be created in _commonInitializationFrameName:groupName:.
1958         [[result subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
1959
1960         if ([decoder allowsKeyedCoding]) {
1961             frameName = [decoder decodeObjectForKey:@"FrameName"];
1962             groupName = [decoder decodeObjectForKey:@"GroupName"];
1963             preferences = [decoder decodeObjectForKey:@"Preferences"];
1964             useBackForwardList = [decoder decodeBoolForKey:@"UseBackForwardList"];
1965             if ([decoder containsValueForKey:@"AllowsUndo"])
1966                 allowsUndo = [decoder decodeBoolForKey:@"AllowsUndo"];
1967         } else {
1968             int version;
1969             [decoder decodeValueOfObjCType:@encode(int) at:&version];
1970             frameName = [decoder decodeObject];
1971             groupName = [decoder decodeObject];
1972             preferences = [decoder decodeObject];
1973             if (version > 1)
1974                 [decoder decodeValuesOfObjCTypes:"c", &useBackForwardList];
1975             // The allowsUndo field is no longer written out in encodeWithCoder, but since there are
1976             // version 3 NIBs that have this field encoded, we still need to read it in.
1977             if (version == 3)
1978                 [decoder decodeValuesOfObjCTypes:"c", &allowsUndo];
1979         }
1980
1981         if (![frameName isKindOfClass:[NSString class]])
1982             frameName = nil;
1983         if (![groupName isKindOfClass:[NSString class]])
1984             groupName = nil;
1985         if (![preferences isKindOfClass:[WebPreferences class]])
1986             preferences = nil;
1987
1988         LOG(Encoding, "FrameName = %@, GroupName = %@, useBackForwardList = %d\n", frameName, groupName, (int)useBackForwardList);
1989         [result _commonInitializationWithFrameName:frameName groupName:groupName];
1990         [result page]->backForwardList()->setEnabled(useBackForwardList);
1991         result->_private->allowsUndo = allowsUndo;
1992         if (preferences)
1993             [result setPreferences:preferences];
1994     } @catch (NSException *localException) {
1995         result = nil;
1996         [self release];
1997     }
1998
1999     return result;
2000 }
2001
2002 - (void)encodeWithCoder:(NSCoder *)encoder
2003 {
2004     // Set asside the subviews before we archive. We don't want to archive any subviews.
2005     // The subviews will always be created in _commonInitializationFrameName:groupName:.
2006     id originalSubviews = _subviews;
2007     _subviews = nil;
2008
2009     [super encodeWithCoder:encoder];
2010
2011     // Restore the subviews we set aside.
2012     _subviews = originalSubviews;
2013
2014     BOOL useBackForwardList = _private->page && _private->page->backForwardList()->enabled();
2015     if ([encoder allowsKeyedCoding]) {
2016         [encoder encodeObject:[[self mainFrame] name] forKey:@"FrameName"];
2017         [encoder encodeObject:[self groupName] forKey:@"GroupName"];
2018         [encoder encodeObject:[self preferences] forKey:@"Preferences"];
2019         [encoder encodeBool:useBackForwardList forKey:@"UseBackForwardList"];
2020         [encoder encodeBool:_private->allowsUndo forKey:@"AllowsUndo"];
2021     } else {
2022         int version = WebViewVersion;
2023         [encoder encodeValueOfObjCType:@encode(int) at:&version];
2024         [encoder encodeObject:[[self mainFrame] name]];
2025         [encoder encodeObject:[self groupName]];
2026         [encoder encodeObject:[self preferences]];
2027         [encoder encodeValuesOfObjCTypes:"c", &useBackForwardList];
2028         // DO NOT encode any new fields here, doing so will break older WebKit releases.
2029     }
2030
2031     LOG(Encoding, "FrameName = %@, GroupName = %@, useBackForwardList = %d\n", [[self mainFrame] name], [self groupName], (int)useBackForwardList);
2032 }
2033
2034 - (void)dealloc
2035 {
2036     // call close to ensure we tear-down completely
2037     // this maintains our old behavior for existing applications
2038     [self _close];
2039
2040     --WebViewCount;
2041     
2042     [_private release];
2043     // [super dealloc] can end up dispatching against _private (3466082)
2044     _private = nil;
2045
2046     [super dealloc];
2047 }
2048
2049 - (void)finalize
2050 {
2051     ASSERT(_private->closed);
2052
2053     --WebViewCount;
2054
2055     [super finalize];
2056 }
2057
2058 - (void)close
2059 {
2060     [self _close];
2061 }
2062
2063 - (void)setShouldCloseWithWindow:(BOOL)close
2064 {
2065     _private->shouldCloseWithWindow = close;
2066 }
2067
2068 - (BOOL)shouldCloseWithWindow
2069 {
2070     return _private->shouldCloseWithWindow;
2071 }
2072
2073 - (void)viewWillMoveToWindow:(NSWindow *)window
2074 {
2075     // Don't do anything if the WebView isn't initialized.
2076     // This happens when decoding a WebView in a nib.
2077     // FIXME: What sets up the observer of NSWindowWillCloseNotification in this case?
2078     if (!_private)
2079         return;
2080
2081     if (_private->closed)
2082         return;
2083     
2084     if ([self window] && [self window] != [self hostWindow])
2085         [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowWillCloseNotification object:[self window]];
2086
2087     if (window) {
2088         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowWillClose:) name:NSWindowWillCloseNotification object:window];
2089
2090         // Ensure that we will receive the events that WebHTMLView (at least) needs.
2091         // The following are expensive enough that we don't want to call them over
2092         // and over, so do them when we move into a window.
2093         [window setAcceptsMouseMovedEvents:YES];
2094         WKSetNSWindowShouldPostEventNotifications(window, YES);
2095     }
2096 }
2097
2098 - (void)_windowWillClose:(NSNotification *)notification
2099 {
2100     if ([self shouldCloseWithWindow] && ([self window] == [self hostWindow] || ([self window] && ![self hostWindow]) || (![self window] && [self hostWindow])))
2101         [self _close];
2102 }
2103
2104 - (void)setPreferences:(WebPreferences *)prefs
2105 {
2106     if (!prefs)
2107         prefs = [WebPreferences standardPreferences];
2108
2109     if (_private->preferences == prefs)
2110         return;
2111
2112     [prefs willAddToWebView];
2113
2114     WebPreferences *oldPrefs = _private->preferences;
2115
2116     [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:[self preferences]];
2117     [WebPreferences _removeReferenceForIdentifier:[oldPrefs identifier]];
2118
2119     _private->preferences = [prefs retain];
2120
2121     // After registering for the notification, post it so the WebCore settings update.
2122     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesChangedNotification:)
2123         name:WebPreferencesChangedNotification object:[self preferences]];
2124     [[self preferences] _postPreferencesChangesNotification];
2125
2126     [oldPrefs didRemoveFromWebView];
2127     [oldPrefs release];
2128 }
2129
2130 - (WebPreferences *)preferences
2131 {
2132     return _private->preferences;
2133 }
2134
2135 - (void)setPreferencesIdentifier:(NSString *)anIdentifier
2136 {
2137     if (!_private->closed && ![anIdentifier isEqual:[[self preferences] identifier]]) {
2138         WebPreferences *prefs = [[WebPreferences alloc] initWithIdentifier:anIdentifier];
2139         [self setPreferences:prefs];
2140         [prefs release];
2141     }
2142 }
2143
2144 - (NSString *)preferencesIdentifier
2145 {
2146     return [[self preferences] identifier];
2147 }
2148
2149
2150 - (void)setUIDelegate:delegate
2151 {
2152     _private->UIDelegate = delegate;
2153     [_private->UIDelegateForwarder release];
2154     _private->UIDelegateForwarder = nil;
2155 }
2156
2157 - UIDelegate
2158 {
2159     return _private->UIDelegate;
2160 }
2161
2162 - (void)setResourceLoadDelegate: delegate
2163 {
2164     _private->resourceProgressDelegate = delegate;
2165     [self _cacheResourceLoadDelegateImplementations];
2166 }
2167
2168 - resourceLoadDelegate
2169 {
2170     return _private->resourceProgressDelegate;
2171 }
2172
2173 - (void)setDownloadDelegate: delegate
2174 {
2175     _private->downloadDelegate = delegate;
2176 }
2177
2178
2179 - downloadDelegate
2180 {
2181     return _private->downloadDelegate;
2182 }
2183
2184 - (void)setPolicyDelegate:delegate
2185 {
2186     _private->policyDelegate = delegate;
2187     [_private->policyDelegateForwarder release];
2188     _private->policyDelegateForwarder = nil;
2189 }
2190
2191 - policyDelegate
2192 {
2193     return _private->policyDelegate;
2194 }
2195
2196 - (void)setFrameLoadDelegate:delegate
2197 {
2198     _private->frameLoadDelegate = delegate;
2199     [self _cacheFrameLoadDelegateImplementations];
2200
2201     // If this delegate wants callbacks for icons, fire up the icon database.
2202     if (_private->frameLoadDelegateImplementations.didReceiveIconForFrameFunc)
2203         [WebIconDatabase sharedIconDatabase];
2204 }
2205
2206 - frameLoadDelegate
2207 {
2208     return _private->frameLoadDelegate;
2209 }
2210
2211 - (WebFrame *)mainFrame
2212 {
2213     // This can be called in initialization, before _private has been set up (3465613)
2214     if (!_private)
2215         return nil;
2216     if (!_private->page)
2217         return nil;
2218     return kit(_private->page->mainFrame());
2219 }
2220
2221 - (WebFrame *)selectedFrame
2222 {
2223     // If the first responder is a view in our tree, we get the frame containing the first responder.
2224     // This is faster than searching the frame hierarchy, and will give us a result even in the case
2225     // where the focused frame doesn't actually contain a selection.
2226     WebFrame *focusedFrame = [self _focusedFrame];
2227     if (focusedFrame)
2228         return focusedFrame;
2229     
2230     // If the first responder is outside of our view tree, we search for a frame containing a selection.
2231     // There should be at most only one of these.
2232     return [[self mainFrame] _findFrameWithSelection];
2233 }
2234
2235 - (WebBackForwardList *)backForwardList
2236 {
2237     if (!_private->page)
2238         return nil;
2239     if (!_private->page->backForwardList()->enabled())
2240         return nil;
2241     return kit(_private->page->backForwardList());
2242 }
2243
2244 - (void)setMaintainsBackForwardList: (BOOL)flag
2245 {
2246     if (!_private->page)
2247         return;
2248     _private->page->backForwardList()->setEnabled(flag);
2249 }
2250
2251 - (BOOL)goBack
2252 {
2253     if (!_private->page)
2254         return NO;
2255     
2256     return _private->page->goBack();
2257 }
2258
2259 - (BOOL)goForward
2260 {
2261     if (!_private->page)
2262         return NO;
2263
2264     return _private->page->goForward();
2265 }
2266
2267 - (BOOL)goToBackForwardItem:(WebHistoryItem *)item
2268 {
2269     if (!_private->page)
2270         return NO;
2271
2272     _private->page->goToItem(core(item), FrameLoadTypeIndexedBackForward);
2273     return YES;
2274 }
2275
2276 - (void)setTextSizeMultiplier:(float)m
2277 {
2278     [self _setZoomMultiplier:m isTextOnly:![[NSUserDefaults standardUserDefaults] boolForKey:WebKitDebugFullPageZoomPreferenceKey]];
2279 }
2280
2281 - (float)textSizeMultiplier
2282 {
2283     return _private->zoomMultiplierIsTextOnly ? _private->zoomMultiplier : 1.0f;
2284 }
2285
2286 - (void)_setZoomMultiplier:(float)m isTextOnly:(BOOL)isTextOnly
2287 {
2288     // NOTE: This has no visible effect when viewing a PDF (see <rdar://problem/4737380>)
2289     _private->zoomMultiplier = m;
2290     _private->zoomMultiplierIsTextOnly = isTextOnly;
2291     Frame* coreFrame = core([self mainFrame]);
2292     if (coreFrame)
2293         coreFrame->setZoomFactor(m, isTextOnly);
2294 }
2295
2296 - (float)_zoomMultiplier:(BOOL)isTextOnly
2297 {
2298     if (isTextOnly != _private->zoomMultiplierIsTextOnly)
2299         return 1.0f;
2300     return _private->zoomMultiplier;
2301 }
2302
2303 - (float)_realZoomMultiplier
2304 {
2305     return _private->zoomMultiplier;
2306 }
2307
2308 - (BOOL)_realZoomMultiplierIsTextOnly
2309 {
2310     return _private->zoomMultiplierIsTextOnly;
2311 }
2312
2313 #define MinimumZoomMultiplier       0.5f
2314 #define MaximumZoomMultiplier       3.0f
2315 #define ZoomMultiplierRatio         1.2f
2316
2317 - (BOOL)_canZoomOut:(BOOL)isTextOnly
2318 {
2319     id docView = [[[self mainFrame] frameView] documentView];
2320     if ([docView conformsToProtocol:@protocol(_WebDocumentZooming)]) {
2321         id <_WebDocumentZooming> zoomingDocView = (id <_WebDocumentZooming>)docView;
2322         return [zoomingDocView _canZoomOut];
2323     }
2324     return [self _zoomMultiplier:isTextOnly] / ZoomMultiplierRatio > MinimumZoomMultiplier;
2325 }
2326
2327
2328 - (BOOL)_canZoomIn:(BOOL)isTextOnly
2329 {
2330     id docView = [[[self mainFrame] frameView] documentView];
2331     if ([docView conformsToProtocol:@protocol(_WebDocumentZooming)]) {
2332         id <_WebDocumentZooming> zoomingDocView = (id <_WebDocumentZooming>)docView;
2333         return [zoomingDocView _canZoomIn];
2334     }
2335     return [self _zoomMultiplier:isTextOnly] * ZoomMultiplierRatio < MaximumZoomMultiplier;
2336 }
2337
2338 - (IBAction)_zoomOut:(id)sender isTextOnly:(BOOL)isTextOnly
2339 {
2340     id docView = [[[self mainFrame] frameView] documentView];
2341     if ([docView conformsToProtocol:@protocol(_WebDocumentZooming)]) {
2342         id <_WebDocumentZooming> zoomingDocView = (id <_WebDocumentZooming>)docView;
2343         return [zoomingDocView _zoomOut:sender];
2344     }
2345     float newScale = [self _zoomMultiplier:isTextOnly] / ZoomMultiplierRatio;
2346     if (newScale > MinimumZoomMultiplier)
2347         [self _setZoomMultiplier:newScale isTextOnly:isTextOnly];
2348 }
2349
2350 - (IBAction)_zoomIn:(id)sender isTextOnly:(BOOL)isTextOnly
2351 {
2352     id docView = [[[self mainFrame] frameView] documentView];
2353     if ([docView conformsToProtocol:@protocol(_WebDocumentZooming)]) {
2354         id <_WebDocumentZooming> zoomingDocView = (id <_WebDocumentZooming>)docView;
2355         return [zoomingDocView _zoomIn:sender];
2356     }
2357     float newScale = [self _zoomMultiplier:isTextOnly] * ZoomMultiplierRatio;
2358     if (newScale < MaximumZoomMultiplier)
2359         [self _setZoomMultiplier:newScale isTextOnly:isTextOnly];
2360 }
2361
2362 - (BOOL)_canResetZoom:(BOOL)isTextOnly
2363 {
2364     id docView = [[[self mainFrame] frameView] documentView];
2365     if ([docView conformsToProtocol:@protocol(_WebDocumentZooming)]) {
2366         id <_WebDocumentZooming> zoomingDocView = (id <_WebDocumentZooming>)docView;
2367         return [zoomingDocView _canResetZoom];
2368     }
2369     return [self _zoomMultiplier:isTextOnly] != 1.0f;
2370 }
2371
2372 - (IBAction)_resetZoom:(id)sender isTextOnly:(BOOL)isTextOnly
2373 {
2374     id docView = [[[self mainFrame] frameView] documentView];
2375     if ([docView conformsToProtocol:@protocol(_WebDocumentZooming)]) {
2376         id <_WebDocumentZooming> zoomingDocView = (id <_WebDocumentZooming>)docView;
2377         return [zoomingDocView _resetZoom:sender];
2378     }
2379     if ([self _zoomMultiplier:isTextOnly] != 1.0f)
2380         [self _setZoomMultiplier:1.0f isTextOnly:isTextOnly];
2381 }
2382
2383 - (void)setApplicationNameForUserAgent:(NSString *)applicationName
2384 {
2385     NSString *name = [applicationName copy];
2386     [_private->applicationNameForUserAgent release];
2387     _private->applicationNameForUserAgent = name;
2388     if (!_private->userAgentOverridden)
2389         *_private->userAgent = String();
2390 }
2391
2392 - (NSString *)applicationNameForUserAgent
2393 {
2394     return [[_private->applicationNameForUserAgent retain] autorelease];
2395 }
2396
2397 - (void)setCustomUserAgent:(NSString *)userAgentString
2398 {
2399     *_private->userAgent = userAgentString;
2400     _private->userAgentOverridden = userAgentString != nil;
2401 }
2402
2403 - (NSString *)customUserAgent
2404 {
2405     if (!_private->userAgentOverridden)
2406         return nil;
2407     return *_private->userAgent;
2408 }
2409
2410 - (void)setMediaStyle:(NSString *)mediaStyle
2411 {
2412     if (_private->mediaStyle != mediaStyle) {
2413         [_private->mediaStyle release];
2414         _private->mediaStyle = [mediaStyle copy];
2415     }
2416 }
2417
2418 - (NSString *)mediaStyle
2419 {
2420     return _private->mediaStyle;
2421 }
2422
2423 - (BOOL)supportsTextEncoding
2424 {
2425     id documentView = [[[self mainFrame] frameView] documentView];
2426     return [documentView conformsToProtocol:@protocol(WebDocumentText)]
2427         && [documentView supportsTextEncoding];
2428 }
2429
2430 - (void)setCustomTextEncodingName:(NSString *)encoding
2431 {
2432     NSString *oldEncoding = [self customTextEncodingName];
2433     if (encoding == oldEncoding || [encoding isEqualToString:oldEncoding])
2434         return;
2435     if (Frame* mainFrame = core([self mainFrame]))
2436         mainFrame->loader()->reloadAllowingStaleData(encoding);
2437 }
2438
2439 - (NSString *)_mainFrameOverrideEncoding
2440 {
2441     WebDataSource *dataSource = [[self mainFrame] provisionalDataSource];
2442     if (dataSource == nil)
2443         dataSource = [[self mainFrame] _dataSource];
2444     if (dataSource == nil)
2445         return nil;
2446     return nsStringNilIfEmpty([dataSource _documentLoader]->overrideEncoding());
2447 }
2448
2449 - (NSString *)customTextEncodingName
2450 {
2451     return [self _mainFrameOverrideEncoding];
2452 }
2453
2454 - (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script
2455 {
2456     // Return statements are only valid in a function but some applications pass in scripts
2457     // prefixed with return (<rdar://problems/5103720&4616860>) since older WebKit versions
2458     // silently ignored the return. If the application is linked against an earlier version
2459     // of WebKit we will strip the return so the script wont fail.
2460     if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_JAVASCRIPT_RETURN_QUIRK)) {
2461         NSRange returnStringRange = [script rangeOfString:@"return "];
2462         if (returnStringRange.length && !returnStringRange.location)
2463             script = [script substringFromIndex:returnStringRange.location + returnStringRange.length];
2464     }
2465
2466     NSString *result = [[self mainFrame] _stringByEvaluatingJavaScriptFromString:script];
2467     // The only way stringByEvaluatingJavaScriptFromString can return nil is if the frame was removed by the script
2468     // Since there's no way to get rid of the main frame, result will never ever be nil here.
2469     ASSERT(result);
2470
2471     return result;
2472 }
2473
2474 - (WebScriptObject *)windowScriptObject
2475 {
2476     Frame* coreFrame = core([self mainFrame]);
2477     if (!coreFrame)
2478         return nil;
2479     return coreFrame->windowScriptObject();
2480 }
2481
2482 // Get the appropriate user-agent string for a particular URL.
2483 - (NSString *)userAgentForURL:(NSURL *)url
2484 {
2485     return [self _userAgentForURL:KURL([url absoluteURL])];
2486 }
2487
2488 - (void)setHostWindow:(NSWindow *)hostWindow
2489 {
2490     if (_private->closed)
2491         return;
2492     if (hostWindow == _private->hostWindow)
2493         return;
2494
2495     Frame* coreFrame = core([self mainFrame]);
2496     for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame))
2497         [[[kit(frame) frameView] documentView] viewWillMoveToHostWindow:hostWindow];
2498     if (_private->hostWindow && [self window] != _private->hostWindow)
2499         [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowWillCloseNotification object:_private->hostWindow];
2500     if (hostWindow)
2501         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowWillClose:) name:NSWindowWillCloseNotification object:hostWindow];
2502     [_private->hostWindow release];
2503     _private->hostWindow = [hostWindow retain];
2504     for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame))
2505         [[[kit(frame) frameView] documentView] viewDidMoveToHostWindow];
2506 }
2507
2508 - (NSWindow *)hostWindow
2509 {
2510     return _private->hostWindow;
2511 }
2512
2513 - (NSView <WebDocumentView> *)documentViewAtWindowPoint:(NSPoint)point
2514 {
2515     return [[self _frameViewAtWindowPoint:point] documentView];
2516 }
2517
2518 - (NSDictionary *)_elementAtWindowPoint:(NSPoint)windowPoint
2519 {
2520     WebFrameView *frameView = [self _frameViewAtWindowPoint:windowPoint];
2521     if (!frameView)
2522         return nil;
2523     NSView <WebDocumentView> *documentView = [frameView documentView];
2524     if ([documentView conformsToProtocol:@protocol(WebDocumentElement)]) {
2525         NSPoint point = [documentView convertPoint:windowPoint fromView:nil];
2526         return [(NSView <WebDocumentElement> *)documentView elementAtPoint:point];
2527     }
2528     return [NSDictionary dictionaryWithObject:[frameView webFrame] forKey:WebElementFrameKey];
2529 }
2530
2531 - (NSDictionary *)elementAtPoint:(NSPoint)point
2532 {
2533     return [self _elementAtWindowPoint:[self convertPoint:point toView:nil]];
2534 }
2535
2536 // The following 2 internal NSView methods are called on the drag destination by make scrolling while dragging work.
2537 // Scrolling while dragging will only work if the drag destination is in a scroll view. The WebView is the drag destination. 
2538 // When dragging to a WebView, the document subview should scroll, but it doesn't because it is not the drag destination. 
2539 // Forward these calls to the document subview to make its scroll view scroll.
2540 - (void)_autoscrollForDraggingInfo:(id)draggingInfo timeDelta:(NSTimeInterval)repeatDelta
2541 {
2542     NSView <WebDocumentView> *documentView = [self documentViewAtWindowPoint:[draggingInfo draggingLocation]];
2543     [documentView _autoscrollForDraggingInfo:draggingInfo timeDelta:repeatDelta];
2544 }
2545
2546 - (BOOL)_shouldAutoscrollForDraggingInfo:(id)draggingInfo
2547 {
2548     NSView <WebDocumentView> *documentView = [self documentViewAtWindowPoint:[draggingInfo draggingLocation]];
2549     return [documentView _shouldAutoscrollForDraggingInfo:draggingInfo];
2550 }
2551
2552 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)draggingInfo
2553 {
2554     NSView <WebDocumentView>* view = [self documentViewAtWindowPoint:[draggingInfo draggingLocation]];
2555     WebPasteboardHelper helper([view isKindOfClass:[WebHTMLView class]] ? (WebHTMLView*)view : nil);
2556     IntPoint client([draggingInfo draggingLocation]);
2557     IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
2558     DragData dragData(draggingInfo, client, global, (DragOperation)[draggingInfo draggingSourceOperationMask], &helper);
2559     return core(self)->dragController()->dragEntered(&dragData);
2560 }
2561
2562 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)draggingInfo
2563 {
2564     Page* page = core(self);
2565     if (!page)
2566         return NSDragOperationNone;
2567
2568     NSView <WebDocumentView>* view = [self documentViewAtWindowPoint:[draggingInfo draggingLocation]];
2569     WebPasteboardHelper helper([view isKindOfClass:[WebHTMLView class]] ? (WebHTMLView*)view : nil);
2570     IntPoint client([draggingInfo draggingLocation]);
2571     IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
2572     DragData dragData(draggingInfo, client, global, (DragOperation)[draggingInfo draggingSourceOperationMask], &helper);
2573     return page->dragController()->dragUpdated(&dragData);
2574 }
2575
2576 - (void)draggingExited:(id <NSDraggingInfo>)draggingInfo
2577 {
2578     Page* page = core(self);
2579     if (!page)
2580         return;
2581
2582     NSView <WebDocumentView>* view = [self documentViewAtWindowPoint:[draggingInfo draggingLocation]];
2583     WebPasteboardHelper helper([view isKindOfClass:[WebHTMLView class]] ? (WebHTMLView*)view : nil);
2584     IntPoint client([draggingInfo draggingLocation]);
2585     IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
2586     DragData dragData(draggingInfo, client, global, (DragOperation)[draggingInfo draggingSourceOperationMask], &helper);
2587     page->dragController()->dragExited(&dragData);
2588 }
2589
2590 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)draggingInfo
2591 {
2592     return YES;
2593 }
2594
2595 - (BOOL)performDragOperation:(id <NSDraggingInfo>)draggingInfo
2596 {
2597     NSView <WebDocumentView>* view = [self documentViewAtWindowPoint:[draggingInfo draggingLocation]];
2598     WebPasteboardHelper helper([view isKindOfClass:[WebHTMLView class]]? (WebHTMLView*)view : nil);
2599     IntPoint client([draggingInfo draggingLocation]);
2600     IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
2601     DragData dragData(draggingInfo, client, global, (DragOperation)[draggingInfo draggingSourceOperationMask], &helper);
2602     return core(self)->dragController()->performDrag(&dragData);
2603 }
2604
2605 - (NSView *)_hitTest:(NSPoint *)aPoint dragTypes:(NSSet *)types
2606 {
2607     NSView *hitView = [super _hitTest:aPoint dragTypes:types];
2608     if (!hitView && [[self superview] mouse:*aPoint inRect:[self frame]]) {
2609         return self;
2610     } else {
2611         return hitView;
2612     }
2613 }
2614
2615 - (BOOL)acceptsFirstResponder
2616 {
2617     return [[[self mainFrame] frameView] acceptsFirstResponder];
2618 }
2619
2620 - (BOOL)becomeFirstResponder
2621 {
2622     if (_private->becomingFirstResponder) {
2623         // Fix for unrepro infinite recursion reported in radar 4448181. If we hit this assert on
2624         // a debug build, we should figure out what causes the problem and do a better fix.
2625         ASSERT_NOT_REACHED();
2626         return NO;
2627     }
2628     
2629     // This works together with setNextKeyView to splice the WebView into
2630     // the key loop similar to the way NSScrollView does this. Note that
2631     // WebFrameView has very similar code.
2632     NSWindow *window = [self window];
2633     WebFrameView *mainFrameView = [[self mainFrame] frameView];
2634
2635     NSResponder *previousFirstResponder = [[self window] _oldFirstResponderBeforeBecoming];
2636     BOOL fromOutside = ![previousFirstResponder isKindOfClass:[NSView class]] || (![(NSView *)previousFirstResponder isDescendantOf:self] && previousFirstResponder != self);
2637     
2638     if ([window keyViewSelectionDirection] == NSSelectingPrevious) {
2639         NSView *previousValidKeyView = [self previousValidKeyView];
2640         if ((previousValidKeyView != self) && (previousValidKeyView != mainFrameView)) {
2641             _private->becomingFirstResponder = YES;
2642             _private->becomingFirstResponderFromOutside = fromOutside;
2643             [window makeFirstResponder:previousValidKeyView];
2644             _private->becomingFirstResponderFromOutside = NO;
2645             _private->becomingFirstResponder = NO;
2646             return YES;
2647         } else {
2648             return NO;
2649         }
2650     }
2651     
2652     if ([mainFrameView acceptsFirstResponder]) {
2653         _private->becomingFirstResponder = YES;
2654         _private->becomingFirstResponderFromOutside = fromOutside;
2655         [window makeFirstResponder:mainFrameView];
2656         _private->becomingFirstResponderFromOutside = NO;
2657         _private->becomingFirstResponder = NO;
2658         return YES;
2659     } 
2660     
2661     return NO;
2662 }
2663
2664 - (NSView *)_webcore_effectiveFirstResponder
2665 {
2666     WebFrameView *frameView = [[self mainFrame] frameView];
2667     return frameView ? [frameView _webcore_effectiveFirstResponder] : [super _webcore_effectiveFirstResponder];
2668 }
2669
2670 - (void)setNextKeyView:(NSView *)aView
2671 {
2672     // This works together with becomeFirstResponder to splice the WebView into
2673     // the key loop similar to the way NSScrollView does this. Note that
2674     // WebFrameView has very similar code.
2675     WebFrameView *mainFrameView = [[self mainFrame] frameView];
2676     if (mainFrameView != nil) {
2677         [mainFrameView setNextKeyView:aView];
2678     } else {
2679         [super setNextKeyView:aView];
2680     }
2681 }
2682
2683 static WebFrame *incrementFrame(WebFrame *curr, BOOL forward, BOOL wrapFlag)
2684 {
2685     Frame* coreFrame = core(curr);
2686     return kit(forward
2687         ? coreFrame->tree()->traverseNextWithWrap(wrapFlag)
2688         : coreFrame->tree()->traversePreviousWithWrap(wrapFlag));
2689 }
2690
2691 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
2692 {
2693     return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO];
2694 }
2695
2696 + (void)registerViewClass:(Class)viewClass representationClass:(Class)representationClass forMIMEType:(NSString *)MIMEType
2697 {
2698     [[WebFrameView _viewTypesAllowImageTypeOmission:YES] setObject:viewClass forKey:MIMEType];
2699     [[WebDataSource _repTypesAllowImageTypeOmission:YES] setObject:representationClass forKey:MIMEType];
2700     
2701     // FIXME: We also need to maintain MIMEType registrations (which can be dynamically changed)
2702     // in the WebCore MIMEType registry.  For now we're doing this in a safe, limited manner
2703     // to fix <rdar://problem/5372989> - a future revamping of the entire system is neccesary for future robustness
2704     if ([viewClass class] == [WebHTMLView class])
2705         MIMETypeRegistry::getSupportedNonImageMIMETypes().add(MIMEType);
2706 }
2707
2708 - (void)setGroupName:(NSString *)groupName
2709 {
2710     if (!_private->page)
2711         return;
2712     _private->page->setGroupName(groupName);
2713 }
2714
2715 - (NSString *)groupName
2716 {
2717     if (!_private->page)
2718         return nil;
2719     return _private->page->groupName();
2720 }
2721
2722 - (double)estimatedProgress
2723 {
2724     if (!_private->page)
2725         return 0.0;
2726
2727     return _private->page->progress()->estimatedProgress();
2728 }
2729
2730 - (NSArray *)pasteboardTypesForSelection
2731 {
2732     NSView <WebDocumentView> *documentView = [[[self _selectedOrMainFrame] frameView] documentView];
2733     if ([documentView conformsToProtocol:@protocol(WebDocumentSelection)]) {
2734         return [(NSView <WebDocumentSelection> *)documentView pasteboardTypesForSelection];
2735     }
2736     return [NSArray array];
2737 }
2738
2739 - (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
2740 {
2741     WebFrame *frame = [self _selectedOrMainFrame];
2742     if (frame && [frame _hasSelection]) {
2743         NSView <WebDocumentView> *documentView = [[frame frameView] documentView];
2744         if ([documentView conformsToProtocol:@protocol(WebDocumentSelection)])
2745             [(NSView <WebDocumentSelection> *)documentView writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2746     }
2747 }
2748
2749 - (NSArray *)pasteboardTypesForElement:(NSDictionary *)element
2750 {
2751     if ([element objectForKey:WebElementImageURLKey] != nil) {
2752         return [NSPasteboard _web_writableTypesForImageIncludingArchive:([element objectForKey:WebElementDOMNodeKey] != nil)];
2753     } else if ([element objectForKey:WebElementLinkURLKey] != nil) {
2754         return [NSPasteboard _web_writableTypesForURL];
2755     } else if ([[element objectForKey:WebElementIsSelectedKey] boolValue]) {
2756         return [self pasteboardTypesForSelection];
2757     }
2758     return [NSArray array];
2759 }
2760
2761 - (void)writeElement:(NSDictionary *)element withPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
2762 {
2763     if ([element objectForKey:WebElementImageURLKey] != nil) {
2764         [self _writeImageForElement:element withPasteboardTypes:types toPasteboard:pasteboard];
2765     } else if ([element objectForKey:WebElementLinkURLKey] != nil) {
2766         [self _writeLinkElement:element withPasteboardTypes:types toPasteboard:pasteboard];
2767     } else if ([[element objectForKey:WebElementIsSelectedKey] boolValue]) {
2768         [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2769     }
2770 }
2771
2772 - (void)moveDragCaretToPoint:(NSPoint)point
2773 {
2774     if (Page* page = core(self))
2775         page->dragController()->placeDragCaret(IntPoint([self convertPoint:point toView:nil]));
2776 }
2777
2778 - (void)removeDragCaret
2779 {
2780     if (Page* page = core(self))
2781         page->dragController()->dragEnded();
2782 }
2783
2784 - (void)setMainFrameURL:(NSString *)URLString
2785 {
2786     [[self mainFrame] loadRequest: [NSURLRequest requestWithURL: [NSURL _web_URLWithDataAsString: URLString]]];
2787 }
2788
2789 - (NSString *)mainFrameURL
2790 {
2791     WebDataSource *ds;
2792     ds = [[self mainFrame] provisionalDataSource];
2793     if (!ds)
2794         ds = [[self mainFrame] _dataSource];
2795     return [[[ds request] URL] _web_originalDataAsString];
2796 }
2797
2798 - (BOOL)isLoading
2799 {
2800     LOG (Bindings, "isLoading = %d", (int)[self _isLoading]);
2801     return [self _isLoading];
2802 }
2803
2804 - (NSString *)mainFrameTitle
2805 {
2806     NSString *mainFrameTitle = [[[self mainFrame] _dataSource] pageTitle];
2807     return (mainFrameTitle != nil) ? mainFrameTitle : (NSString *)@"";
2808 }
2809
2810 - (NSImage *)mainFrameIcon
2811 {
2812     return [[WebIconDatabase sharedIconDatabase] iconForURL:[[[[self mainFrame] _dataSource] _URL] _web_originalDataAsString] withSize:WebIconSmallSize];
2813 }
2814
2815 - (DOMDocument *)mainFrameDocument
2816 {
2817     // only return the actual value if the state we're in gives NSTreeController
2818     // enough time to release its observers on the old model
2819     if (_private->mainFrameDocumentReady)
2820         return [[self mainFrame] DOMDocument];
2821     return nil;
2822 }
2823
2824 - (void)setDrawsBackground:(BOOL)drawsBackground
2825 {
2826     if (_private->drawsBackground == drawsBackground)
2827         return;
2828     _private->drawsBackground = drawsBackground;
2829     [[self mainFrame] _updateBackground];
2830 }
2831
2832 - (BOOL)drawsBackground
2833 {
2834     return _private->drawsBackground;
2835 }
2836
2837 @end
2838
2839 @implementation WebView (WebIBActions)
2840
2841 - (IBAction)takeStringURLFrom: sender
2842 {
2843     NSString *URLString = [sender stringValue];
2844     
2845     [[self mainFrame] loadRequest: [NSURLRequest requestWithURL: [NSURL _web_URLWithDataAsString: URLString]]];
2846 }
2847
2848 - (BOOL)canGoBack
2849 {
2850     if (!_private->page)
2851         return NO;
2852
2853     return !!_private->page->backForwardList()->backItem();
2854 }
2855
2856 - (BOOL)canGoForward
2857 {
2858     if (!_private->page)
2859         return NO;
2860
2861     return !!_private->page->backForwardList()->forwardItem();
2862 }
2863
2864 - (IBAction)goBack:(id)sender
2865 {
2866     [self goBack];
2867 }
2868
2869 - (IBAction)goForward:(id)sender
2870 {
2871     [self goForward];
2872 }
2873
2874 - (IBAction)stopLoading:(id)sender
2875 {
2876     [[self mainFrame] stopLoading];
2877 }
2878
2879 - (IBAction)reload:(id)sender
2880 {
2881     [[self mainFrame] reload];
2882 }
2883
2884 // FIXME: This code should move into WebCore so that it is not duplicated in each WebKit.
2885 // (This includes canMakeTextSmaller/Larger, makeTextSmaller/Larger, and canMakeTextStandardSize/makeTextStandardSize)
2886 - (BOOL)canMakeTextSmaller
2887 {
2888     return [self _canZoomOut:![[NSUserDefaults standardUserDefaults] boolForKey:WebKitDebugFullPageZoomPreferenceKey]];
2889 }
2890
2891 - (IBAction)makeTextSmaller:(id)sender
2892 {
2893     return [self _zoomOut:sender isTextOnly:![[NSUserDefaults standardUserDefaults] boolForKey:WebKitDebugFullPageZoomPreferenceKey]];
2894 }
2895
2896 - (BOOL)canMakeTextLarger
2897 {
2898     return [self _canZoomIn:![[NSUserDefaults standardUserDefaults] boolForKey:WebKitDebugFullPageZoomPreferenceKey]];
2899 }
2900
2901 - (IBAction)makeTextLarger:(id)sender
2902 {
2903     return [self _zoomIn:sender isTextOnly:![[NSUserDefaults standardUserDefaults] boolForKey:WebKitDebugFullPageZoomPreferenceKey]];
2904 }
2905
2906 - (BOOL)canMakeTextStandardSize
2907 {
2908     return [self _canResetZoom:![[NSUserDefaults standardUserDefaults] boolForKey:WebKitDebugFullPageZoomPreferenceKey]];
2909 }
2910
2911 - (IBAction)makeTextStandardSize:(id)sender
2912 {
2913    return [self _resetZoom:sender isTextOnly:![[NSUserDefaults standardUserDefaults] boolForKey:WebKitDebugFullPageZoomPreferenceKey]];
2914 }
2915
2916 - (IBAction)toggleSmartInsertDelete:(id)sender
2917 {
2918     [self setSmartInsertDeleteEnabled:![self smartInsertDeleteEnabled]];
2919 }
2920
2921 - (IBAction)toggleContinuousSpellChecking:(id)sender
2922 {
2923     [self setContinuousSpellCheckingEnabled:![self isContinuousSpellCheckingEnabled]];
2924 }
2925
2926 - (BOOL)_responderValidateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2927 {
2928     id responder = [self _responderForResponderOperations];
2929     if (responder != self && [responder respondsToSelector:[item action]]) {
2930         if ([responder respondsToSelector:@selector(validateUserInterfaceItemWithoutDelegate:)])
2931             return [responder validateUserInterfaceItemWithoutDelegate:item];
2932         if ([responder respondsToSelector:@selector(validateUserInterfaceItem:)])
2933             return [responder validateUserInterfaceItem:item];
2934         return YES;
2935     }
2936     return NO;
2937 }
2938
2939 #define VALIDATE(name) \
2940     else if (action == @selector(name:)) { return [self _responderValidateUserInterfaceItem:item]; }
2941
2942 - (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item
2943 {
2944     SEL action = [item action];
2945
2946     if (action == @selector(goBack:)) {
2947         return [self canGoBack];
2948     } else if (action == @selector(goForward:)) {
2949         return [self canGoForward];
2950     } else if (action == @selector(makeTextLarger:)) {
2951         return [self canMakeTextLarger];
2952     } else if (action == @selector(makeTextSmaller:)) {
2953         return [self canMakeTextSmaller];
2954     } else if (action == @selector(makeTextStandardSize:)) {
2955         return [self canMakeTextStandardSize];
2956     } else if (action == @selector(reload:)) {
2957         return [[self mainFrame] _dataSource] != nil;
2958     } else if (action == @selector(stopLoading:)) {
2959         return [self _isLoading];
2960     } else if (action == @selector(toggleContinuousSpellChecking:)) {
2961         BOOL checkMark = NO;
2962         BOOL retVal = NO;
2963         if ([self _continuousCheckingAllowed]) {
2964             checkMark = [self isContinuousSpellCheckingEnabled];
2965             retVal = YES;
2966         }
2967         if ([(NSObject *)item isKindOfClass:[NSMenuItem class]]) {
2968             NSMenuItem *menuItem = (NSMenuItem *)item;
2969             [menuItem setState:checkMark ? NSOnState : NSOffState];
2970         }
2971         return retVal;
2972 #ifndef BUILDING_ON_TIGER
2973     } else if (action == @selector(toggleGrammarChecking:)) {
2974         BOOL checkMark = [self isGrammarCheckingEnabled];
2975         if ([(NSObject *)item isKindOfClass:[NSMenuItem class]]) {
2976             NSMenuItem *menuItem = (NSMenuItem *)item;
2977             [menuItem setState:checkMark ? NSOnState : NSOffState];
2978         }
2979         return YES;
2980 #endif
2981     }
2982     FOR_EACH_RESPONDER_SELECTOR(VALIDATE)
2983
2984     return YES;
2985 }
2986
2987 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2988 {
2989     BOOL result = [self validateUserInterfaceItemWithoutDelegate:item];
2990     return CallUIDelegateReturningBoolean(result, self, @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result);
2991 }
2992
2993 @end
2994
2995 @implementation WebView (WebPendingPublic)
2996
2997 - (void)scheduleInRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode
2998 {
2999 #ifndef BUILDING_ON_TIGER
3000     if (runLoop && mode)
3001         core(self)->addSchedulePair(SchedulePair::create(runLoop, (CFStringRef)mode));
3002 #endif
3003 }
3004
3005 - (void)unscheduleFromRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode
3006 {
3007 #ifndef BUILDING_ON_TIGER
3008     if (runLoop && mode)
3009         core(self)->removeSchedulePair(SchedulePair::create(runLoop, (CFStringRef)mode));
3010 #endif
3011 }
3012
3013 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection
3014 {
3015     if (_private->closed)
3016         return NO;
3017     
3018     // Get the frame holding the selection, or start with the main frame
3019     WebFrame *startFrame = [self _selectedOrMainFrame];
3020     
3021     // Search the first frame, then all the other frames, in order
3022     NSView <WebDocumentSearching> *startSearchView = nil;
3023     WebFrame *frame = startFrame;
3024     do {
3025         WebFrame *nextFrame = incrementFrame(frame, forward, wrapFlag);
3026         
3027         BOOL onlyOneFrame = (frame == nextFrame);
3028         ASSERT(!onlyOneFrame || frame == startFrame);
3029         
3030         id <WebDocumentView> view = [[frame frameView] documentView];
3031         if ([view conformsToProtocol:@protocol(WebDocumentSearching)]) {
3032             NSView <WebDocumentSearching> *searchView = (NSView <WebDocumentSearching> *)view;
3033             
3034             if (frame == startFrame)
3035                 startSearchView = searchView;
3036             
3037             BOOL foundString;
3038             // In some cases we have to search some content twice; see comment later in this method.
3039             // We can avoid ever doing this in the common one-frame case by passing YES for wrapFlag 
3040             // here, and then bailing out before we get to the code that would search again in the
3041             // same content.
3042             BOOL wrapOnThisPass = wrapFlag && onlyOneFrame;
3043             if ([searchView conformsToProtocol:@protocol(WebDocumentIncrementalSearching)])
3044                 foundString = [(NSView <WebDocumentIncrementalSearching> *)searchView searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapOnThisPass startInSelection:startInSelection];
3045             else
3046                 foundString = [searchView searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapOnThisPass];
3047             
3048             if (foundString) {
3049                 if (frame != startFrame)
3050                     [startFrame _clearSelection];
3051                 [[self window] makeFirstResponder:searchView];
3052                 return YES;
3053             }
3054             
3055             if (onlyOneFrame)
3056                 return NO;
3057         }
3058         frame = nextFrame;
3059     } while (frame && frame != startFrame);
3060     
3061     // If there are multiple frames and wrapFlag is true and we've visited each one without finding a result, we still need to search in the 
3062     // first-searched frame up to the selection. However, the API doesn't provide a way to search only up to a particular point. The only 
3063     // way to make sure the entire frame is searched is to pass YES for the wrapFlag. When there are no matches, this will search again
3064     // some content that we already searched on the first pass. In the worst case, we could search the entire contents of this frame twice.
3065     // To fix this, we'd need to add a mechanism to specify a range in which to search.
3066     if (wrapFlag && startSearchView) {
3067         BOOL foundString;
3068         if ([startSearchView conformsToProtocol:@protocol(WebDocumentIncrementalSearching)])
3069             foundString = [(NSView <WebDocumentIncrementalSearching> *)startSearchView searchFor:string direction:forward caseSensitive:caseFlag wrap:YES startInSelection:startInSelection];
3070         else
3071             foundString = [startSearchView searchFor:string direction:forward caseSensitive:caseFlag wrap:YES];
3072         if (foundString) {
3073             [[self window] makeFirstResponder:startSearchView];
3074             return YES;
3075         }
3076     }
3077     return NO;
3078 }
3079
3080 - (void)setHoverFeedbackSuspended:(BOOL)newValue
3081 {
3082     if (_private->hoverFeedbackSuspended == newValue)
3083         return;
3084     
3085     _private->hoverFeedbackSuspended = newValue;
3086     id <WebDocumentView> documentView = [[[self mainFrame] frameView] documentView];
3087     // FIXME: in a perfect world we'd do this in a general way that worked with any document view,
3088     // such as by calling a protocol method or using respondsToSelector or sending a notification.
3089     // But until there is any need for these more general solutions, we'll just hardwire it to work
3090     // with WebHTMLView.
3091     // Note that _hoverFeedbackSuspendedChanged needs to be called only on the main WebHTMLView, not
3092     // on each subframe separately.
3093     if ([documentView isKindOfClass:[WebHTMLView class]])
3094         [(WebHTMLView *)documentView _hoverFeedbackSuspendedChanged];
3095 }
3096
3097 - (BOOL)isHoverFeedbackSuspended
3098 {
3099     return _private->hoverFeedbackSuspended;
3100 }
3101
3102 - (void)setMainFrameDocumentReady:(BOOL)mainFrameDocumentReady
3103 {
3104     // by setting this to NO, calls to mainFrameDocument are forced to return nil
3105     // setting this to YES lets it return the actual DOMDocument value
3106     // we use this to tell NSTreeController to reset its observers and clear its state
3107     if (_private->mainFrameDocumentReady == mainFrameDocumentReady)
3108         return;
3109     [self _willChangeValueForKey:_WebMainFrameDocumentKey];
3110     _private->mainFrameDocumentReady = mainFrameDocumentReady;
3111     [self _didChangeValueForKey:_WebMainFrameDocumentKey];
3112     // this will cause observers to call mainFrameDocument where this flag will be checked
3113 }
3114
3115 // This method name is used by Mail on Tiger (but not post-Tiger), so we shouldn't delete it 
3116 // until the day comes when we're no longer supporting Mail on Tiger.
3117 - (WebFrame *)_frameForCurrentSelection
3118 {
3119     return [self _selectedOrMainFrame];
3120 }
3121
3122 - (void)setTabKeyCyclesThroughElements:(BOOL)cyclesElements
3123 {
3124     _private->tabKeyCyclesThroughElementsChanged = YES;
3125     if (_private->page)
3126         _private->page->setTabKeyCyclesThroughElements(cyclesElements);
3127 }
3128
3129 - (BOOL)tabKeyCyclesThroughElements
3130 {
3131     return _private->page && _private->page->tabKeyCyclesThroughElements();
3132 }
3133
3134 - (void)setScriptDebugDelegate:(id)delegate
3135 {
3136     _private->scriptDebugDelegate = delegate;
3137     [_private->scriptDebugDelegateForwarder release];
3138     _private->scriptDebugDelegateForwarder = nil;
3139     if (delegate)
3140         [self _attachScriptDebuggerToAllFrames];
3141     else
3142         [self _detachScriptDebuggerFromAllFrames];
3143 }
3144
3145 - (id)scriptDebugDelegate
3146 {
3147     return _private->scriptDebugDelegate;
3148 }
3149
3150 - (BOOL)shouldClose
3151 {
3152     Frame* coreFrame = core([self mainFrame]);
3153     if (!coreFrame)
3154         return YES;
3155     return coreFrame->shouldClose();
3156 }
3157
3158 static NSAppleEventDescriptor* aeDescFromJSValue(ExecState* exec, JSValue* jsValue)
3159 {
3160     NSAppleEventDescriptor* aeDesc = 0;
3161     switch (jsValue->type()) {
3162         case BooleanType:
3163             aeDesc = [NSAppleEventDescriptor descriptorWithBoolean:jsValue->getBoolean()];
3164             break;
3165         case StringType:
3166             aeDesc = [NSAppleEventDescriptor descriptorWithString:String(jsValue->getString())];
3167             break;
3168         case NumberType: {
3169             double value = jsValue->getNumber();
3170             int intValue = (int)value;
3171             if (value == intValue)
3172                 aeDesc = [NSAppleEventDescriptor descriptorWithDescriptorType:typeSInt32 bytes:&intValue length:sizeof(intValue)];
3173             else
3174                 aeDesc = [NSAppleEventDescriptor descriptorWithDescriptorType:typeIEEE64BitFloatingPoint bytes:&value length:sizeof(value)];
3175             break;
3176         }
3177         case ObjectType: {
3178             JSObject* object = jsValue->getObject();
3179             if (object->inherits(&DateInstance::info)) {
3180                 DateInstance* date = static_cast<DateInstance*>(object);
3181                 double ms = 0;
3182                 int tzOffset = 0;
3183                 if (date->getTime(ms, tzOffset)) {
3184                     CFAbsoluteTime utcSeconds = ms / 1000 - kCFAbsoluteTimeIntervalSince1970;
3185                     LongDateTime ldt;
3186                     if (noErr == UCConvertCFAbsoluteTimeToLongDateTime(utcSeconds, &ldt))
3187                         aeDesc = [NSAppleEventDescriptor descriptorWithDescriptorType:typeLongDateTime bytes:&ldt length:sizeof(ldt)];
3188                 }
3189             }
3190             else if (object->inherits(&ArrayInstance::info)) {
3191                 static HashSet<JSObject*> visitedElems;
3192                 if (!visitedElems.contains(object)) {
3193                     visitedElems.add(object);
3194                     
3195                     ArrayInstance* array = static_cast<ArrayInstance*>(object);
3196                     aeDesc = [NSAppleEventDescriptor listDescriptor];
3197                     unsigned numItems = array->getLength();
3198                     for (unsigned i = 0; i < numItems; ++i)
3199                         [aeDesc insertDescriptor:aeDescFromJSValue(exec, array->getItem(i)) atIndex:0];
3200                     
3201                     visitedElems.remove(object);
3202                 }
3203             }
3204             if (!aeDesc) {
3205                 JSValue* primitive = object->toPrimitive(exec);
3206                 if (exec->hadException()) {
3207                     exec->clearException();
3208                     return [NSAppleEventDescriptor nullDescriptor];
3209                 }
3210                 return aeDescFromJSValue(exec, primitive);
3211             }
3212             break;
3213         }
3214         case UndefinedType:
3215             aeDesc = [NSAppleEventDescriptor descriptorWithTypeCode:cMissingValue];
3216             break;
3217         default:
3218             LOG_ERROR("Unknown JavaScript type: %d", jsValue->type());
3219             // no break;
3220         case UnspecifiedType:
3221         case NullType:
3222         case GetterSetterType:
3223             aeDesc = [NSAppleEventDescriptor nullDescriptor];
3224             break;
3225     }
3226     
3227     return aeDesc;
3228 }
3229
3230 - (NSAppleEventDescriptor *)aeDescByEvaluatingJavaScriptFromString:(NSString *)script
3231 {
3232     Frame* coreFrame = core([self mainFrame]);
3233     if (!coreFrame)
3234         return nil;
3235     if (!coreFrame->document())
3236         return nil;
3237     JSValue* result = coreFrame->loader()->executeScript(script, true);
3238     if (!result) // FIXME: pass errors
3239         return 0;
3240     JSLock lock;
3241     return aeDescFromJSValue(coreFrame->scriptProxy()->globalObject()->globalExec(), result);
3242 }
3243
3244 - (BOOL)canMarkAllTextMatches
3245 {
3246     WebFrame *frame = [self mainFrame];
3247     do {
3248         id <WebDocumentView> view = [[frame frameView] documentView];
3249         if (view && ![view conformsToProtocol:@protocol(WebMultipleTextMatches)])
3250             return NO;
3251         
3252         frame = incrementFrame(frame, YES, NO);
3253     } while (frame);
3254     
3255     return YES;
3256 }
3257
3258 - (NSUInteger)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag highlight:(BOOL)highlight limit:(NSUInteger)limit
3259 {
3260     WebFrame *frame = [self mainFrame];
3261     unsigned matchCount = 0;
3262     do {
3263         id <WebDocumentView> view = [[frame frameView] documentView];
3264         if ([view conformsToProtocol:@protocol(WebMultipleTextMatches)]) {
3265             [(NSView <WebMultipleTextMatches>*)view  setMarkedTextMatchesAreHighlighted:highlight];
3266         
3267             ASSERT(limit == 0 || matchCount < limit);
3268             matchCount += [(NSView <WebMultipleTextMatches>*)view markAllMatchesForText:string caseSensitive:caseFlag limit:limit == 0 ? 0 : limit - matchCount];
3269
3270             // Stop looking if we've reached the limit. A limit of 0 means no limit.
3271             if (limit > 0 && matchCount >= limit)
3272                 break;
3273         }
3274         
3275         frame = incrementFrame(frame, YES, NO);
3276     } while (frame);
3277     
3278     return matchCount;
3279 }
3280
3281 - (void)unmarkAllTextMatches
3282 {
3283     WebFrame *frame = [self mainFrame];
3284     do {
3285         id <WebDocumentView> view = [[frame frameView] documentView];
3286         if ([view conformsToProtocol:@protocol(WebMultipleTextMatches)])
3287             [(NSView <WebMultipleTextMatches>*)view unmarkAllTextMatches];
3288         
3289         frame = incrementFrame(frame, YES, NO);
3290     } while (frame);
3291 }
3292
3293 - (NSArray *)rectsForTextMatches
3294 {
3295     NSMutableArray *result = [NSMutableArray array];
3296     WebFrame *frame = [self mainFrame];
3297     do {
3298         id <WebDocumentView> view = [[frame frameView] documentView];
3299         if ([view conformsToProtocol:@protocol(WebMultipleTextMatches)]) {
3300             NSView <WebMultipleTextMatches> *documentView = (NSView <WebMultipleTextMatches> *)view;
3301             NSRect documentViewVisibleRect = [documentView visibleRect];
3302             NSArray *originalRects = [documentView rectsForTextMatches];
3303             unsigned rectCount = [originalRects count];
3304             unsigned rectIndex;
3305             NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3306             for (rectIndex = 0; rectIndex < rectCount; ++rectIndex) {
3307                 NSRect r = [[originalRects objectAtIndex:rectIndex] rectValue];
3308                 // Clip rect to document view's visible rect so rect is confined to subframe
3309                 r = NSIntersectionRect(r, documentViewVisibleRect);
3310                 if (NSIsEmptyRect(r))
3311                     continue;
3312                 
3313                 // Convert rect to our coordinate system
3314                 r = [documentView convertRect:r toView:self];
3315                 [result addObject:[NSValue valueWithRect:r]];
3316                 if (rectIndex % 10 == 0) {
3317                     [pool drain];
3318                     pool = [[NSAutoreleasePool alloc] init];
3319                 }
3320             }
3321             [pool drain];
3322         }
3323         
3324         frame = incrementFrame(frame, YES, NO);
3325     } while (frame);
3326     
3327     return result;
3328 }
3329
3330 - (void)scrollDOMRangeToVisible:(DOMRange *)range
3331 {
3332     [[[[range startContainer] ownerDocument] webFrame] _scrollDOMRangeToVisible:range];
3333 }
3334
3335 - (BOOL)allowsUndo
3336 {
3337     return _private->allowsUndo;
3338 }
3339
3340 - (void)setAllowsUndo:(BOOL)flag
3341 {
3342     _private->allowsUndo = flag;
3343 }
3344
3345 - (void)setPageSizeMultiplier:(float)m
3346 {
3347     [self _setZoomMultiplier:m isTextOnly:NO];
3348 }
3349
3350 - (float)pageSizeMultiplier
3351 {
3352     return !_private->zoomMultiplierIsTextOnly ? _private->zoomMultiplier : 1.0f;
3353 }
3354
3355 - (BOOL)canZoomPageIn
3356 {
3357     return [self _canZoomIn:NO];
3358 }
3359
3360 - (IBAction)zoomPageIn:(id)sender
3361 {
3362     return [self _zoomIn:sender isTextOnly:NO];
3363 }
3364
3365 - (BOOL)canZoomPageOut
3366 {
3367     return [self _canZoomOut:NO];
3368 }
3369
3370 - (IBAction)zoomPageOut:(id)sender
3371 {
3372     return [self _zoomOut:sender isTextOnly:NO];
3373 }
3374
3375 - (BOOL)canResetPageZoom
3376 {
3377     return [self _canResetZoom:NO];
3378 }
3379
3380 - (IBAction)resetPageZoom:(id)sender
3381 {
3382     return [self _resetZoom:sender isTextOnly:NO];
3383 }
3384
3385 @end
3386
3387 @implementation WebView (WebViewPrintingPrivate)
3388
3389 - (float)_headerHeight
3390 {
3391     return CallUIDelegateReturningFloat(self, @selector(webViewHeaderHeight:));
3392 }
3393
3394 - (float)_footerHeight
3395 {
3396     return CallUIDelegateReturningFloat(self, @selector(webViewFooterHeight:));
3397 }
3398
3399 - (void)_drawHeaderInRect:(NSRect)rect
3400 {
3401 #ifdef DEBUG_HEADER_AND_FOOTER
3402     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
3403     [currentContext saveGraphicsState];
3404     [[NSColor yellowColor] set];
3405     NSRectFill(rect);
3406     [currentContext restoreGraphicsState];
3407 #endif
3408
3409     SEL selector = @selector(webView:drawHeaderInRect:);
3410     if (![_private->UIDelegate respondsToSelector:selector])
3411         return;
3412
3413     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
3414     [currentContext saveGraphicsState];
3415
3416     NSRectClip(rect);
3417     CallUIDelegate(self, selector, rect);
3418
3419     [currentContext restoreGraphicsState];
3420 }
3421
3422 - (void)_drawFooterInRect:(NSRect)rect
3423 {
3424 #ifdef DEBUG_HEADER_AND_FOOTER
3425     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
3426     [currentContext saveGraphicsState];
3427     [[NSColor cyanColor] set];
3428     NSRectFill(rect);
3429     [currentContext restoreGraphicsState];
3430 #endif
3431     
3432     SEL selector = @selector(webView:drawFooterInRect:);
3433     if (![_private->UIDelegate respondsToSelector:selector])
3434         return;
3435
3436     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
3437     [currentContext saveGraphicsState];
3438
3439     NSRectClip(rect);
3440     CallUIDelegate(self, selector, rect);
3441
3442     [currentContext restoreGraphicsState];
3443 }
3444
3445 - (void)_adjustPrintingMarginsForHeaderAndFooter
3446 {
3447     NSPrintOperation *op = [NSPrintOperation currentOperation];
3448     NSPrintInfo *info = [op printInfo];
3449     NSMutableDictionary *infoDictionary = [info dictionary];
3450     
3451     // We need to modify the top and bottom margins in the NSPrintInfo to account for the space needed by the
3452     // header and footer. Because this method can be called more than once on the same NSPrintInfo (see 5038087),
3453     // we stash away the unmodified top and bottom margins the first time this method is called, and we read from
3454     // those stashed-away values on subsequent calls.
3455     float originalTopMargin;
3456     float originalBottomMargin;
3457     NSNumber *originalTopMarginNumber = [infoDictionary objectForKey:WebKitOriginalTopPrintingMarginKey];
3458     if (!originalTopMarginNumber) {
3459         ASSERT(![infoDictionary objectForKey:WebKitOriginalBottomPrintingMarginKey]);
3460         originalTopMargin = [info topMargin];
3461         originalBottomMargin = [info bottomMargin];
3462         [infoDictionary setObject:[NSNumber numberWithFloat:originalTopMargin] forKey:WebKitOriginalTopPrintingMarginKey];
3463         [infoDictionary setObject:[NSNumber numberWithFloat:originalBottomMargin] forKey:WebKitOriginalBottomPrintingMarginKey];
3464     } else {
3465         ASSERT([originalTopMarginNumber isKindOfClass:[NSNumber class]]);
3466         ASSERT([[infoDictionary objectForKey:WebKitOriginalBottomPrintingMarginKey] isKindOfClass:[NSNumber class]]);
3467         originalTopMargin = [originalTopMarginNumber floatValue];
3468         originalBottomMargin = [[infoDictionary objectForKey:WebKitOriginalBottomPrintingMarginKey] floatValue];
3469     }
3470     
3471     float scale = [op _web_pageSetupScaleFactor];
3472     [info setTopMargin:originalTopMargin + [self _headerHeight] * scale];
3473     [info setBottomMargin:originalBottomMargin + [self _footerHeight] * scale];
3474 }
3475
3476 - (void)_drawHeaderAndFooter
3477 {
3478     // The header and footer rect height scales with the page, but the width is always
3479     // all the way across the printed page (inset by printing margins).
3480     NSPrintOperation *op = [NSPrintOperation currentOperation];
3481     float scale = [op _web_pageSetupScaleFactor];
3482     NSPrintInfo *printInfo = [op printInfo];
3483     NSSize paperSize = [printInfo paperSize];
3484     float headerFooterLeft = [printInfo leftMargin]/scale;
3485     float headerFooterWidth = (paperSize.width - ([printInfo leftMargin] + [printInfo rightMargin]))/scale;
3486     NSRect footerRect = NSMakeRect(headerFooterLeft, [printInfo bottomMargin]/scale - [self _footerHeight] , 
3487                                    headerFooterWidth, [self _footerHeight]);
3488     NSRect headerRect = NSMakeRect(headerFooterLeft, (paperSize.height - [printInfo topMargin])/scale, 
3489                                    headerFooterWidth, [self _headerHeight]);
3490     
3491     [self _drawHeaderInRect:headerRect];
3492     [self _drawFooterInRect:footerRect];
3493 }
3494 @end
3495
3496 @implementation WebView (WebDebugBinding)
3497
3498 - (void)addObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
3499 {
3500     LOG (Bindings, "addObserver:%p forKeyPath:%@ options:%x context:%p", anObserver, keyPath, options, context);
3501     [super addObserver:anObserver forKeyPath:keyPath options:options context:context];
3502 }
3503
3504 - (void)removeObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath
3505 {
3506     LOG (Bindings, "removeObserver:%p forKeyPath:%@", anObserver, keyPath);
3507     [super removeObserver:anObserver forKeyPath:keyPath];
3508 }
3509
3510 @end
3511
3512 //==========================================================================================
3513 // Editing
3514
3515 @implementation WebView (WebViewCSS)
3516
3517 - (DOMCSSStyleDeclaration *)computedStyleForElement:(DOMElement *)element pseudoElement:(NSString *)pseudoElement
3518 {
3519     // FIXME: is this the best level for this conversion?
3520     if (pseudoElement == nil)
3521         pseudoElement = @"";
3522
3523     return [[element ownerDocument] getComputedStyle:element pseudoElement:pseudoElement];
3524 }
3525
3526 @end
3527
3528 @implementation WebView (WebViewEditing)
3529
3530 - (DOMRange *)editableDOMRangeForPoint:(NSPoint)point
3531 {
3532     Page* page = core(self);
3533     if (!page)
3534         return nil;
3535     return kit(page->mainFrame()->editor()->rangeForPoint(IntPoint([self convertPoint:point toView:nil])).get());
3536 }
3537
3538 - (BOOL)_shouldChangeSelectedDOMRange:(DOMRange *)currentRange toDOMRange:(DOMRange *)proposedRange affinity:(NSSelectionAffinity)selectionAffinity stillSelecting:(BOOL)flag;
3539 {
3540     // FIXME: This quirk is needed due to <rdar://problem/4985321> - We can phase it out once Aperture can adopt the new behavior on their end
3541     if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_APERTURE_QUIRK) && [[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.apple.Aperture"])
3542         return YES;
3543     return [[self _editingDelegateForwarder] webView:self shouldChangeSelectedDOMRange:currentRange toDOMRange:proposedRange affinity:selectionAffinity stillSelecting:flag];
3544 }
3545
3546 - (BOOL)maintainsInactiveSelection
3547 {
3548     return NO;
3549 }
3550
3551 - (void)setSelectedDOMRange:(DOMRange *)range affinity:(NSSelectionAffinity)selectionAffinity
3552 {
3553     Frame* coreFrame = core([self _selectedOrMainFrame]);
3554     if (!coreFrame)
3555         return;
3556
3557     if (range == nil)
3558         coreFrame->selectionController()->clear();
3559     else {
3560         // Derive the frame to use from the range passed in.
3561         // Using _selectedOrMainFrame could give us a different document than
3562         // the one the range uses.
3563         coreFrame = core([range startContainer])->document()->frame();
3564         if (!coreFrame)
3565             return;
3566
3567         coreFrame->selectionController()->setSelectedRange([range _range], core(selectionAffinity), true);
3568     }
3569 }
3570
3571 - (DOMRange *)selectedDOMRange
3572 {
3573     Frame* coreFrame = core([self _selectedOrMainFrame]);
3574     if (!coreFrame)
3575         return nil;
3576     return kit(coreFrame->selectionController()->toRange().get());
3577 }
3578
3579 - (NSSelectionAffinity)selectionAffinity
3580 {
3581     Frame* coreFrame = core([self _selectedOrMainFrame]);
3582     if (!coreFrame)
3583         return NSSelectionAffinityDownstream;
3584     return kit(coreFrame->selectionController()->affinity());
3585 }
3586
3587 - (void)setEditable:(BOOL)flag
3588 {
3589     if (_private->editable != flag) {
3590         _private->editable = flag;
3591         if (!_private->tabKeyCyclesThroughElementsChanged && _private->page)
3592             _private->page->setTabKeyCyclesThroughElements(!flag);
3593         Frame* mainFrame = core([self mainFrame]);
3594         if (mainFrame) {
3595             if (flag) {
3596                 mainFrame->applyEditingStyleToBodyElement();
3597                 // If the WebView is made editable and the selection is empty, set it to something.
3598                 if (![self selectedDOMRange])
3599                     mainFrame->setSelectionFromNone();
3600             } else
3601                 mainFrame->removeEditingStyleFromBodyElement();
3602         }
3603     }
3604 }
3605
3606 - (BOOL)isEditable
3607 {
3608     return _private->editable;
3609 }
3610
3611 - (void)setTypingStyle:(DOMCSSStyleDeclaration *)style
3612 {
3613     // We don't know enough at thls level to pass in a relevant WebUndoAction; we'd have to
3614     // change the API to allow this.
3615     [[self _selectedOrMainFrame] _setTypingStyle:style withUndoAction:EditActionUnspecified];
3616 }
3617
3618 - (DOMCSSStyleDeclaration *)typingStyle
3619 {
3620     return [[self _selectedOrMainFrame] _typingStyle];
3621 }
3622
3623 - (void)setSmartInsertDeleteEnabled:(BOOL)flag
3624 {
3625     _private->smartInsertDeleteEnabled = flag;
3626 }
3627
3628 - (BOOL)smartInsertDeleteEnabled
3629 {
3630     return _private->smartInsertDeleteEnabled;
3631 }
3632
3633 - (void)setContinuousSpellCheckingEnabled:(BOOL)flag
3634 {
3635     if (continuousSpellCheckingEnabled != flag) {
3636         continuousSpellCheckingEnabled = flag;
3637         [[NSUserDefaults standardUserDefaults] setBool:continuousSpellCheckingEnabled forKey:WebContinuousSpellCheckingEnabled];
3638     }
3639     
3640     if ([self isContinuousSpellCheckingEnabled]) {
3641         [[self class] _preflightSpellChecker];
3642     } else {
3643         [[self mainFrame] _unmarkAllMisspellings];
3644     }
3645 }
3646
3647 - (BOOL)isContinuousSpellCheckingEnabled
3648 {
3649     return (continuousSpellCheckingEnabled && [self _continuousCheckingAllowed]);
3650 }
3651
3652 - (NSInteger)spellCheckerDocumentTag
3653 {
3654     if (!_private->hasSpellCheckerDocumentTag) {
3655         _private->spellCheckerDocumentTag = [NSSpellChecker uniqueSpellDocumentTag];
3656         _private->hasSpellCheckerDocumentTag = YES;
3657     }
3658     return _private->spellCheckerDocumentTag;
3659 }
3660
3661 - (NSUndoManager *)undoManager
3662 {
3663     if (!_private->allowsUndo)
3664         return nil;
3665
3666     NSUndoManager *undoManager = [[self _editingDelegateForwarder] undoManagerForWebView:self];
3667     if (undoManager)
3668         return undoManager;
3669
3670     return [super undoManager];
3671 }
3672
3673 - (void)registerForEditingDelegateNotification:(NSString *)name selector:(SEL)selector
3674 {
3675     NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
3676     if ([_private->editingDelegate respondsToSelector:selector])
3677         [defaultCenter addObserver:_private->editingDelegate selector:selector name:name object:self];
3678 }
3679
3680 - (void)setEditingDelegate:(id)delegate
3681 {
3682     if (_private->editingDelegate == delegate)
3683         return;
3684
3685     NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
3686
3687     // remove notifications from current delegate
3688     [defaultCenter removeObserver:_private->editingDelegate name:WebViewDidBeginEditingNotification object:self];
3689     [defaultCenter removeObserver:_private->editingDelegate name:WebViewDidChangeNotification object:self];
3690     [defaultCenter removeObserver:_private->editingDelegate name:WebViewDidEndEditingNotification object:self];
3691     [defaultCenter removeObserver:_private->editingDelegate name:WebViewDidChangeTypingStyleNotification object:self];
3692     [defaultCenter removeObserver:_private->editingDelegate name:WebViewDidChangeSelectionNotification object:self];
3693     
3694     _private->editingDelegate = delegate;
3695     [_private->editingDelegateForwarder release];
3696     _private->editingDelegateForwarder = nil;
3697     
3698     // add notifications for new delegate
3699     [self registerForEditingDelegateNotification:WebViewDidBeginEditingNotification selector:@selector(webViewDidBeginEditing:)];
3700     [self registerForEditingDelegateNotification:WebViewDidChangeNotification selector:@selector(webViewDidChange:)];
3701     [self registerForEditingDelegateNotification:WebViewDidEndEditingNotification selector:@selector(webViewDidEndEditing:)];
3702     [self registerForEditingDelegateNotification:WebViewDidChangeTypingStyleNotification selector:@selector(webViewDidChangeTypingStyle:)];
3703     [self registerForEditingDelegateNotification:WebViewDidChangeSelectionNotification selector:@selector(webViewDidChangeSelection:)];
3704 }
3705
3706 - (id)editingDelegate
3707 {
3708     return _private->editingDelegate;
3709 }
3710
3711 - (DOMCSSStyleDeclaration *)styleDeclarationWithText:(NSString *)text
3712 {
3713     // FIXME: Should this really be attached to the document with the current selection?
3714     DOMCSSStyleDeclaration *decl = [[[self _selectedOrMainFrame] DOMDocument] createCSSStyleDeclaration];
3715     [decl setCssText:text];
3716     return decl;
3717 }
3718
3719 @end
3720
3721 @implementation WebView (WebViewGrammarChecking)
3722
3723 // FIXME: This method should be merged into WebViewEditing when we're not in API freeze
3724 - (BOOL)isGrammarCheckingEnabled
3725 {
3726 #ifdef BUILDING_ON_TIGER
3727     return NO;
3728 #else
3729     return grammarCheckingEnabled;
3730 #endif
3731 }
3732
3733 #ifndef BUILDING_ON_TIGER
3734 // FIXME: This method should be merged into WebViewEditing when we're not in API freeze
3735 - (void)setGrammarCheckingEnabled:(BOOL)flag
3736 {
3737     if (grammarCheckingEnabled == flag)
3738         return;
3739     
3740     grammarCheckingEnabled = flag;
3741     [[NSUserDefaults standardUserDefaults] setBool:grammarCheckingEnabled forKey:WebGrammarCheckingEnabled];    
3742     
3743     // FIXME 4811447: workaround for lack of API
3744     NSSpellChecker *spellChecker = [NSSpellChecker sharedSpellChecker];
3745     if ([spellChecker respondsToSelector:@selector(_updateGrammar)])
3746         [spellChecker performSelector:@selector(_updateGrammar)];
3747     
3748     // We call _preflightSpellChecker when turning continuous spell checking on, but we don't need to do that here
3749     // because grammar checking only occurs on code paths that already preflight spell checking appropriately.
3750     
3751     if (![self isGrammarCheckingEnabled])
3752         [[self mainFrame] _unmarkAllBadGrammar];
3753 }
3754
3755 // FIXME: This method should be merged into WebIBActions when we're not in API freeze
3756 - (void)toggleGrammarChecking:(id)sender
3757 {
3758     [self setGrammarCheckingEnabled:![self isGrammarCheckingEnabled]];
3759 }
3760 #endif
3761
3762 @end
3763
3764 @implementation WebView (WebViewUndoableEditing)
3765
3766 - (void)replaceSelectionWithNode:(DOMNode *)node
3767 {
3768     [[self _selectedOrMainFrame] _replaceSelectionWithNode:node selectReplacement:YES smartReplace:NO matchStyle:NO];
3769 }    
3770
3771 - (void)replaceSelectionWithText:(NSString *)text
3772 {
3773     [[self _selectedOrMainFrame] _replaceSelectionWithText:text selectReplacement:YES smartReplace:NO];
3774 }
3775
3776 - (void)replaceSelectionWithMarkupString:(NSString *)markupString
3777 {
3778     [[self _selectedOrMainFrame] _replaceSelectionWithMarkupString:markupString baseURLString:nil selectReplacement:YES smartReplace:NO];
3779 }
3780
3781 - (void)replaceSelectionWithArchive:(WebArchive *)archive
3782 {
3783     [[[self _selectedOrMainFrame] _dataSource] _replaceSelectionWithArchive:archive selectReplacement:YES];
3784 }
3785
3786 - (void)deleteSelection
3787 {
3788     WebFrame *webFrame = [self _selectedOrMainFrame];
3789     Frame* coreFrame = core(webFrame);
3790     if (coreFrame)
3791         coreFrame->editor()->deleteSelectionWithSmartDelete([(WebHTMLView *)[[webFrame frameView] documentView] _canSmartCopyOrDelete]);
3792 }
3793     
3794 - (void)applyStyle:(DOMCSSStyleDeclaration *)style
3795 {
3796     // We don't know enough at thls level to pass in a relevant WebUndoAction; we'd have to
3797     // change the API to allow this.
3798     WebFrame *webFrame = [self _selectedOrMainFrame];
3799     Frame* coreFrame = core(webFrame);
3800     if (coreFrame)
3801         coreFrame->editor()->applyStyle(core(style));
3802 }
3803
3804 @end
3805
3806 @implementation WebView (WebViewEditingActions)
3807
3808 - (void)_performResponderOperation:(SEL)selector with:(id)parameter
3809 {
3810     static BOOL reentered = NO;
3811     if (reentered) {
3812         [[self nextResponder] tryToPerform:selector with:parameter];
3813         return;
3814     }
3815
3816     // There are two possibilities here.
3817     //
3818     // One is that WebView has been called in its role as part of the responder chain.
3819     // In that case, it's fine to call the first responder and end up calling down the
3820     // responder chain again. Later we will return here with reentered = YES and continue
3821     // past the WebView.
3822     //
3823     // The other is that we are being called directly, in which case we want to pass the
3824     // selector down to the view inside us that can handle it, and continue down the
3825     // responder chain as usual.
3826
3827     // Pass this selector down to the first responder.
3828     NSResponder *responder = [self _responderForResponderOperations];
3829     reentered = YES;
3830     [responder tryToPerform:selector with:parameter];
3831     reentered = NO;
3832 }
3833
3834 #define FORWARD(name) \
3835     - (void)name:(id)sender { [self _performResponderOperation:_cmd with:sender]; }
3836
3837 FOR_EACH_RESPONDER_SELECTOR(FORWARD)
3838
3839 - (void)insertText:(NSString *)text
3840 {
3841     [self _performResponderOperation:_cmd with:text];
3842 }
3843
3844 @end
3845
3846 @implementation WebView (WebViewEditingInMail)
3847
3848 - (void)_insertNewlineInQuotedContent;
3849 {
3850     [[self _selectedOrMainFrame] _insertParagraphSeparatorInQuotedContent];
3851 }
3852
3853 - (void)_replaceSelectionWithNode:(DOMNode *)node matchStyle:(BOOL)matchStyle
3854 {
3855     [[self _selectedOrMainFrame] _replaceSelectionWithNode:node selectReplacement:YES smartReplace:NO matchStyle:matchStyle];
3856 }
3857
3858 @end
3859
3860 static WebFrameView *containingFrameView(NSView *view)
3861 {
3862     while (view && ![view isKindOfClass:[WebFrameView class]])
3863         view = [view superview];
3864     return (WebFrameView *)view;    
3865 }
3866
3867 @implementation WebView (WebFileInternal)
3868
3869 + (void)_setCacheModel:(WebCacheModel)cacheModel
3870 {
3871     if (s_didSetCacheModel && cacheModel == s_cacheModel)
3872         return;
3873
3874     NSString *nsurlCacheDirectory = [(NSString *)WKCopyFoundationCacheDirectory() autorelease];
3875     if (!nsurlCacheDirectory)
3876         nsurlCacheDirectory = NSHomeDirectory();
3877
3878     // As a fudge factor, use 1000 instead of 1024, in case the reported byte 
3879     // count doesn't align exactly to a megabyte boundary.
3880     vm_size_t memSize = WebMemorySize() / 1024 / 1000;
3881     unsigned long long diskFreeSize = WebVolumeFreeSize(nsurlCacheDirectory) / 1024 / 1000;
3882     NSURLCache *nsurlCache = [NSURLCache sharedURLCache];
3883
3884     unsigned cacheTotalCapacity = 0;
3885     unsigned cacheMinDeadCapacity = 0;
3886     unsigned cacheMaxDeadCapacity = 0;
3887
3888     unsigned pageCacheCapacity = 0;
3889
3890     NSUInteger nsurlCacheMemoryCapacity = 0;
3891     NSUInteger nsurlCacheDiskCapacity = 0;
3892
3893     switch (cacheModel) {
3894     case WebCacheModelDocumentViewer: {
3895         // Page cache capacity (in pages)
3896         pageCacheCapacity = 0;
3897
3898         // Object cache capacities (in bytes)
3899         if (memSize >= 4096)
3900             cacheTotalCapacity = 256 * 1024 * 1024;
3901         else if (memSize >= 3072)
3902             cacheTotalCapacity = 192 * 1024 * 1024;
3903         else if (memSize >= 2048)
3904             cacheTotalCapacity = 128 * 1024 * 1024;
3905         else if (memSize >= 1536)
3906             cacheTotalCapacity = 86 * 1024 * 1024;
3907         else if (memSize >= 1024)
3908             cacheTotalCapacity = 64 * 1024 * 1024;
3909         else if (memSize >= 512)
3910             cacheTotalCapacity = 32 * 1024 * 1024;
3911         else if (memSize >= 256)
3912             cacheTotalCapacity = 16 * 1024 * 1024; 
3913
3914         cacheMinDeadCapacity = 0;
3915         cacheMaxDeadCapacity = 0;
3916
3917         // Foundation memory cache capacity (in bytes)
3918         nsurlCacheMemoryCapacity = 0;
3919
3920         // Foundation disk cache capacity (in bytes)
3921         nsurlCacheDiskCapacity = [nsurlCache diskCapacity];
3922
3923         break;
3924     }
3925     case WebCacheModelDocumentBrowser: {
3926         // Page cache capacity (in pages)
3927         if (memSize >= 1024)
3928             pageCacheCapacity = 3;
3929         else if (memSize >= 512)
3930             pageCacheCapacity = 2;
3931         else if (memSize >= 256)
3932             pageCacheCapacity = 1;
3933         else
3934             pageCacheCapacity = 0;
3935
3936         // Object cache capacities (in bytes)
3937         if (memSize >= 4096)
3938             cacheTotalCapacity = 256 * 1024 * 1024;
3939         else if (memSize >= 3072)
3940             cacheTotalCapacity = 192 * 1024 * 1024;
3941         else if (memSize >= 2048)
3942             cacheTotalCapacity = 128 * 1024 * 1024;
3943         else if (memSize >= 1536)
3944             cacheTotalCapacity = 86 * 1024 * 1024;
3945         else if (memSize >= 1024)
3946             cacheTotalCapacity = 64 * 1024 * 1024;
3947         else if (memSize >= 512)
3948             cacheTotalCapacity = 32 * 1024 * 1024;
3949         else if (memSize >= 256)
3950             cacheTotalCapacity = 16 * 1024 * 1024; 
3951
3952         cacheMinDeadCapacity = cacheTotalCapacity / 8;
3953         cacheMaxDeadCapacity = cacheTotalCapacity / 4;
3954
3955         // Foundation memory cache capacity (in bytes)
3956         if (memSize >= 2048)
3957             nsurlCacheMemoryCapacity = 4 * 1024 * 1024;
3958         else if (memSize >= 1024)
3959             nsurlCacheMemoryCapacity = 2 * 1024 * 1024;
3960         else if (memSize >= 512)
3961             nsurlCacheMemoryCapacity = 1 * 1024 * 1024;
3962         else
3963             nsurlCacheMemoryCapacity =      512 * 1024; 
3964
3965         // Foundation disk cache capacity (in bytes)
3966         if (diskFreeSize >= 16384)
3967             nsurlCacheDiskCapacity = 50 * 1024 * 1024;
3968         else if (diskFreeSize >= 8192)
3969             nsurlCacheDiskCapacity = 40 * 1024 * 1024;
3970         else if (diskFreeSize >= 4096)
3971             nsurlCacheDiskCapacity = 30 * 1024 * 1024;
3972         else
3973             nsurlCacheDiskCapacity = 20 * 1024 * 1024;
3974
3975         break;
3976     }
3977     case WebCacheModelPrimaryWebBrowser: {
3978         // Page cache capacity (in pages)
3979         // (Research indicates that value / page drops substantially after 3 pages.)
3980         if (memSize >= 8192)
3981             pageCacheCapacity = 7;
3982         if (memSize >= 4096)
3983             pageCacheCapacity = 6;
3984         else if (memSize >= 2048)
3985             pageCacheCapacity = 5;
3986         else if (memSize >= 1024)
3987             pageCacheCapacity = 4;
3988         else if (memSize >= 512)
3989             pageCacheCapacity = 3;
3990         else if (memSize >= 256)
3991             pageCacheCapacity = 2;
3992         else
3993             pageCacheCapacity = 1;
3994
3995         // Object cache capacities (in bytes)
3996         // (Testing indicates that value / MB depends heavily on content and
3997         // browsing pattern. Even growth above 128MB can have substantial 
3998         // value / MB for some content / browsing patterns.)
3999         if (memSize >= 4096)
4000             cacheTotalCapacity = 512 * 1024 * 1024;
4001         else if (memSize >= 3072)
4002             cacheTotalCapacity = 384 * 1024 * 1024;
4003         else if (memSize >= 2048)
4004             cacheTotalCapacity = 256 * 1024 * 1024;
4005         else if (memSize >= 1536)
4006             cacheTotalCapacity = 172 * 1024 * 1024;
4007         else if (memSize >= 1024)
4008             cacheTotalCapacity = 128 * 1024 * 1024;
4009         else if (memSize >= 512)
4010             cacheTotalCapacity = 64 * 1024 * 1024;
4011         else if (memSize >= 256)
4012             cacheTotalCapacity = 32 * 1024 * 1024; 
4013
4014         cacheMinDeadCapacity = cacheTotalCapacity / 4;
4015         cacheMaxDeadCapacity = cacheTotalCapacity / 2;
4016
4017         // This code is here to avoid a PLT regression. We can remove it if we
4018         // can prove that the overall system gain would justify the regression.
4019         cacheMaxDeadCapacity = max(24u, cacheMaxDeadCapacity);
4020
4021         // Foundation memory cache capacity (in bytes)
4022         // (These values are small because WebCore does most caching itself.)
4023         if (memSize >= 1024)
4024             nsurlCacheMemoryCapacity = 4 * 1024 * 1024;
4025         else if (memSize >= 512)
4026             nsurlCacheMemoryCapacity = 2 * 1024 * 1024;
4027         else if (memSize >= 256)
4028             nsurlCacheMemoryCapacity = 1 * 1024 * 1024;
4029         else
4030             nsurlCacheMemoryCapacity =      512 * 1024; 
4031
4032         // Foundation disk cache capacity (in bytes)
4033         if (diskFreeSize >= 16384)
4034             nsurlCacheDiskCapacity = 175 * 1024 * 1024;
4035         else if (diskFreeSize >= 8192)
4036             nsurlCacheDiskCapacity = 150 * 1024 * 1024;
4037         else if (diskFreeSize >= 4096)
4038             nsurlCacheDiskCapacity = 125 * 1024 * 1024;
4039         else if (diskFreeSize >= 2048)
4040             nsurlCacheDiskCapacity = 100 * 1024 * 1024;
4041         else if (diskFreeSize >= 1024)
4042             nsurlCacheDiskCapacity = 75 * 1024 * 1024;
4043         else
4044             nsurlCacheDiskCapacity = 50 * 1024 * 1024;
4045
4046         break;
4047     }
4048     default:
4049         ASSERT_NOT_REACHED();
4050     };
4051
4052 #ifdef BUILDING_ON_TIGER
4053     // Don't use a big Foundation disk cache on Tiger because, according to the 
4054     // PLT, the Foundation disk cache on Tiger is slower than the network. 
4055     nsurlCacheDiskCapacity = [nsurlCache diskCapacity];
4056 #endif
4057
4058     // Don't shrink a big disk cache, since that would cause churn.
4059     nsurlCacheDiskCapacity = max(nsurlCacheDiskCapacity, [nsurlCache diskCapacity]);
4060
4061     cache()->setCapacities(cacheMinDeadCapacity, cacheMaxDeadCapacity, cacheTotalCapacity);
4062     pageCache()->setCapacity(pageCacheCapacity);
4063     [nsurlCache setMemoryCapacity:nsurlCacheMemoryCapacity];
4064     [nsurlCache setDiskCapacity:nsurlCacheDiskCapacity];
4065
4066     s_cacheModel = cacheModel;
4067     s_didSetCacheModel = YES;
4068 }
4069
4070 + (WebCacheModel)_cacheModel
4071 {
4072     return s_cacheModel;
4073 }
4074
4075 + (WebCacheModel)_didSetCacheModel
4076 {
4077     return s_didSetCacheModel;
4078 }
4079
4080 + (WebCacheModel)_maxCacheModelInAnyInstance
4081 {
4082     WebCacheModel cacheModel = WebCacheModelDocumentViewer;
4083     NSEnumerator *enumerator = [(NSMutableSet *)allWebViewsSet objectEnumerator];
4084     while (WebPreferences *preferences = [[enumerator nextObject] preferences])
4085         cacheModel = max(cacheModel, [preferences cacheModel]);
4086     return cacheModel;
4087 }
4088
4089 + (void)_preferencesChangedNotification:(NSNotification *)notification
4090 {
4091     WebPreferences *preferences = (WebPreferences *)[notification object];
4092     ASSERT([preferences isKindOfClass:[WebPreferences class]]);
4093
4094     WebCacheModel cacheModel = [preferences cacheModel];
4095     if (![self _didSetCacheModel] || cacheModel > [self _cacheModel])
4096         [self _setCacheModel:cacheModel];
4097     else if (cacheModel < [self _cacheModel])
4098         [self _setCacheModel:max([[WebPreferences standardPreferences] cacheModel], [self _maxCacheModelInAnyInstance])];
4099 }
4100
4101 + (void)_preferencesRemovedNotification:(NSNotification *)notification
4102 {
4103     WebPreferences *preferences = (WebPreferences *)[notification object];
4104     ASSERT([preferences isKindOfClass:[WebPreferences class]]);
4105
4106     if ([preferences cacheModel] == [self _cacheModel])
4107         [self _setCacheModel:max([[WebPreferences standardPreferences] cacheModel], [self _maxCacheModelInAnyInstance])];
4108 }
4109
4110 - (WebFrame *)_focusedFrame
4111 {
4112     NSResponder *resp = [[self window] firstResponder];
4113     if (resp && [resp isKindOfClass:[NSView class]] && [(NSView *)resp isDescendantOf:[[self mainFrame] frameView]]) {
4114         WebFrameView *frameView = containingFrameView((NSView *)resp);
4115         ASSERT(frameView != nil);
4116         return [frameView webFrame];
4117     }
4118     
4119     return nil;
4120 }
4121
4122 - (WebFrame *)_selectedOrMainFrame
4123 {
4124     WebFrame *result = [self selectedFrame];
4125     if (result == nil)
4126         result = [self mainFrame];
4127     return result;
4128 }
4129
4130 - (BOOL)_isLoading
4131 {
4132     WebFrame *mainFrame = [self mainFrame];
4133     return [[mainFrame _dataSource] isLoading]
4134         || [[mainFrame provisionalDataSource] isLoading];
4135 }
4136
4137 - (WebFrameView *)_frameViewAtWindowPoint:(NSPoint)point
4138 {
4139     if (_private->closed)
4140         return nil;
4141     NSView *view = [self hitTest:[[self superview] convertPoint:point fromView:nil]];
4142     if (![view isDescendantOf:[[self mainFrame] frameView]])
4143         return nil;
4144     WebFrameView *frameView = containingFrameView(view);
4145     ASSERT(frameView);
4146     return frameView;
4147 }
4148
4149 + (void)_preflightSpellCheckerNow:(id)sender
4150 {
4151     [[NSSpellChecker sharedSpellChecker] _preflightChosenSpellServer];
4152 }
4153
4154 + (void)_preflightSpellChecker
4155 {
4156     // As AppKit does, we wish to delay tickling the shared spellchecker into existence on application launch.
4157     if ([NSSpellChecker sharedSpellCheckerExists]) {
4158         [self _preflightSpellCheckerNow:self];
4159     } else {
4160         [self performSelector:@selector(_preflightSpellCheckerNow:) withObject:self afterDelay:2.0];
4161     }
4162 }
4163
4164 - (BOOL)_continuousCheckingAllowed
4165 {
4166     static BOOL allowContinuousSpellChecking = YES;
4167     static BOOL readAllowContinuousSpellCheckingDefault = NO;
4168     if (!readAllowContinuousSpellCheckingDefault) {
4169         if ([[NSUserDefaults standardUserDefaults] objectForKey:@"NSAllowContinuousSpellChecking"]) {
4170             allowContinuousSpellChecking = [[NSUserDefaults standardUserDefaults] boolForKey:@"NSAllowContinuousSpellChecking"];
4171         }
4172         readAllowContinuousSpellCheckingDefault = YES;
4173     }
4174     return allowContinuousSpellChecking;
4175 }
4176
4177 - (NSResponder *)_responderForResponderOperations
4178 {
4179     NSResponder *responder = [[self window] firstResponder];
4180     WebFrameView *mainFrameView = [[self mainFrame] frameView];
4181     
4182     // If the current responder is outside of the webview, use our main frameView or its
4183     // document view. We also do this for subviews of self that are siblings of the main
4184     // frameView since clients might insert non-webview-related views there (see 4552713).
4185     if (responder != self && ![mainFrameView _web_firstResponderIsSelfOrDescendantView]) {
4186         responder = [mainFrameView documentView];
4187         if (!responder)
4188             responder = mainFrameView;
4189     }
4190     return responder;
4191 }
4192
4193 - (void)_openFrameInNewWindowFromMenu:(NSMenuItem *)sender
4194 {
4195     ASSERT_ARG(sender, [sender isKindOfClass:[NSMenuItem class]]);
4196
4197     NSDictionary *element = [sender representedObject];
4198     ASSERT([element isKindOfClass:[NSDictionary class]]);
4199     
4200     NSURLRequest *request = [[[[element objectForKey:WebElementFrameKey] dataSource] request] copy];
4201     ASSERT(request);
4202     
4203     [self _openNewWindowWithRequest:request];
4204     [request release];
4205 }
4206
4207 - (void)_searchWithGoogleFromMenu:(id)sender
4208 {
4209     id documentView = [[[self selectedFrame] frameView] documentView];
4210     if (![documentView conformsToProtocol:@protocol(WebDocumentText)]) {
4211         return;
4212     }
4213     
4214     NSString *selectedString = [(id <WebDocumentText>)documentView selectedString];
4215     if ([selectedString length] == 0) {
4216         return;
4217     }
4218     
4219     NSPasteboard *pasteboard = [NSPasteboard pasteboardWithUniqueName];
4220     [pasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
4221     NSMutableString *s = [selectedString mutableCopy];
4222     const unichar nonBreakingSpaceCharacter = 0xA0;
4223     NSString *nonBreakingSpaceString = [NSString stringWithCharacters:&nonBreakingSpaceCharacter length:1];
4224     [s replaceOccurrencesOfString:nonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
4225     [pasteboard setString:s forType:NSStringPboardType];
4226     [s release];
4227     
4228     // FIXME: seems fragile to use the service by name, but this is what AppKit does
4229     NSPerformService(@"Search With Google", pasteboard);
4230 }
4231
4232 - (void)_searchWithSpotlightFromMenu:(id)sender
4233 {
4234     id documentView = [[[self selectedFrame] frameView] documentView];
4235     if (![documentView conformsToProtocol:@protocol(WebDocumentText)])
4236         return;
4237     
4238     NSString *selectedString = [(id <WebDocumentText>)documentView selectedString];
4239     if ([selectedString length] == 0) {
4240         return;
4241     }
4242
4243     (void)HISearchWindowShow((CFStringRef)selectedString, kNilOptions);
4244 }
4245 @end
4246
4247 @implementation WebView (WebViewInternal)
4248
4249 - (BOOL)_becomingFirstResponderFromOutside
4250 {
4251     return _private->becomingFirstResponderFromOutside;
4252 }
4253
4254 - (void)_receivedIconChangedNotification:(NSNotification *)notification
4255 {
4256     // Get the URL for this notification
4257     NSDictionary *userInfo = [notification userInfo];
4258     ASSERT([userInfo isKindOfClass:[NSDictionary class]]);
4259     NSString *urlString = [userInfo objectForKey:WebIconNotificationUserInfoURLKey];
4260     ASSERT([urlString isKindOfClass:[NSString class]]);
4261     
4262     // If that URL matches the current main frame, dispatch the delegate call, which will also unregister
4263     // us for this notification
4264     if ([[self mainFrameURL] isEqualTo:urlString])
4265         [self _dispatchDidReceiveIconFromWebFrame:[self mainFrame]];
4266 }
4267
4268 - (void)_registerForIconNotification:(BOOL)listen
4269 {
4270     if (listen)
4271         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_receivedIconChangedNotification:) name:WebIconDatabaseDidAddIconNotification object:nil];        
4272     else
4273         [[NSNotificationCenter defaultCenter] removeObserver:self name:WebIconDatabaseDidAddIconNotification object:nil];
4274 }
4275
4276 - (void)_dispatchDidReceiveIconFromWebFrame:(WebFrame *)webFrame
4277 {
4278     // FIXME: This willChangeValueForKey call is too late, because the icon has already changed by now.
4279     [self _willChangeValueForKey:_WebMainFrameIconKey];
4280     
4281     // Since we definitely have an icon and are about to send out the delegate call for that, this WebView doesn't need to listen for the general
4282     // notification any longer
4283     [self _registerForIconNotification:NO];
4284
4285     WebFrameLoadDelegateImplementationCache* cache = &_private->frameLoadDelegateImplementations;
4286     if (cache->didReceiveIconForFrameFunc) {
4287         Image* image = iconDatabase()->iconForPageURL(core(webFrame)->loader()->url().string(), IntSize(16, 16));
4288         if (NSImage *icon = webGetNSImage(image, NSMakeSize(16, 16)))
4289             CallFrameLoadDelegate(cache->didReceiveIconForFrameFunc, self, @selector(webView:didReceiveIcon:forFrame:), icon, webFrame);
4290     }
4291
4292     [self _didChangeValueForKey:_WebMainFrameIconKey];
4293 }
4294
4295 - (NSString *)_userVisibleBundleVersionFromFullVersion:(NSString *)fullVersion
4296 {
4297     // If the version is 4 digits long or longer, then the first digit represents
4298     // the version of the OS. Our user agent string should not include this first digit,
4299     // so strip it off and report the rest as the version. <rdar://problem/4997547>
4300     NSRange nonDigitRange = [fullVersion rangeOfCharacterFromSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]];
4301     if (nonDigitRange.location == NSNotFound && [fullVersion length] >= 4)
4302         return [fullVersion substringFromIndex:1];
4303     if (nonDigitRange.location != NSNotFound && nonDigitRange.location >= 4)
4304         return [fullVersion substringFromIndex:1];
4305     return fullVersion;
4306 }
4307
4308 static inline int callGestalt(OSType selector)
4309 {
4310     SInt32 value = 0;
4311     Gestalt(selector, &value);
4312     return value;
4313 }
4314
4315 // Uses underscores instead of dots because if "4." ever appears in a user agent string, old DHTML libraries treat it as Netscape 4.
4316 static NSString *createMacOSXVersionString()
4317 {
4318     // Can't use -[NSProcessInfo operatingSystemVersionString] because it has too much stuff we don't want.
4319     int major = callGestalt(gestaltSystemVersionMajor);
4320     ASSERT(major);
4321
4322     int minor = callGestalt(gestaltSystemVersionMinor);
4323     int bugFix = callGestalt(gestaltSystemVersionBugFix);
4324     if (bugFix)
4325         return [[NSString alloc] initWithFormat:@"%d_%d_%d", major, minor, bugFix];
4326     if (minor)
4327         return [[NSString alloc] initWithFormat:@"%d_%d", major, minor];
4328     return [[NSString alloc] initWithFormat:@"%d", major];
4329 }
4330
4331 - (NSString *)_userAgentWithApplicationName:(NSString *)applicationName andWebKitVersion:(NSString *)version
4332 {
4333     // Note: Do *not* move the initialization of osVersion into the declaration.
4334     // Garbage collection won't correctly mark the global variable in that case <rdar://problem/5733674>.
4335     static NSString *osVersion;
4336     if (!osVersion)
4337         osVersion = createMacOSXVersionString();
4338     NSString *language = [NSUserDefaults _webkit_preferredLanguageCode];
4339     if ([applicationName length])
4340         return [NSString stringWithFormat:@"Mozilla/5.0 (Maci