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