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